VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/misc/getopt.cpp@ 75482

Last change on this file since 75482 was 71009, checked in by vboxsync, 7 years ago

RTGetOpt: Fixed bug handling missing option values when RTGETOPTINIT_FLAGS_OPTS_FIRST is active.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.5 KB
Line 
1/* $Id: getopt.cpp 71009 2018-02-14 13:29:47Z vboxsync $ */
2/** @file
3 * IPRT - Command Line Parsing
4 */
5
6/*
7 * Copyright (C) 2007-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#include <iprt/cidr.h>
32#include <iprt/net.h> /* must come before getopt.h */
33#include <iprt/getopt.h>
34#include "internal/iprt.h"
35
36#include <iprt/assert.h>
37#include <iprt/ctype.h>
38#include <iprt/err.h>
39#include <iprt/message.h>
40#include <iprt/string.h>
41#include <iprt/uuid.h>
42
43
44/*********************************************************************************************************************************
45* Global Variables *
46*********************************************************************************************************************************/
47/**
48 * Standard options that gets included unless RTGETOPTINIT_FLAGS_NO_STD_OPTS is
49 * set.
50 */
51static RTGETOPTDEF const g_aStdOptions[] =
52{
53 { "--help", 'h', RTGETOPT_REQ_NOTHING },
54 { "-help", 'h', RTGETOPT_REQ_NOTHING },
55 { "--version", 'V', RTGETOPT_REQ_NOTHING },
56 { "-version", 'V', RTGETOPT_REQ_NOTHING },
57};
58/** The index of --help in g_aStdOptions. Used for some trickery. */
59#define RTGETOPT_STD_OPTIONS_HELP_IDX 0
60
61
62
63RTDECL(int) RTGetOptInit(PRTGETOPTSTATE pState, int argc, char **argv,
64 PCRTGETOPTDEF paOptions, size_t cOptions,
65 int iFirst, uint32_t fFlags)
66{
67 AssertReturn(!(fFlags & ~(RTGETOPTINIT_FLAGS_OPTS_FIRST | RTGETOPTINIT_FLAGS_NO_STD_OPTS)), VERR_INVALID_PARAMETER);
68
69 pState->argv = argv;
70 pState->argc = argc;
71 pState->paOptions = paOptions;
72 pState->cOptions = cOptions;
73 pState->iNext = iFirst;
74 pState->pszNextShort = NULL;
75 pState->pDef = NULL;
76 pState->uIndex = UINT32_MAX;
77 pState->fFlags = fFlags;
78 pState->cNonOptions = 0;
79
80 /* validate the options. */
81 for (size_t i = 0; i < cOptions; i++)
82 {
83 Assert(!(paOptions[i].fFlags & ~RTGETOPT_VALID_MASK));
84 Assert(paOptions[i].iShort > 0);
85 Assert(paOptions[i].iShort != VINF_GETOPT_NOT_OPTION);
86 Assert(paOptions[i].iShort != '-');
87 }
88
89 return VINF_SUCCESS;
90}
91RT_EXPORT_SYMBOL(RTGetOptInit);
92
93
94/**
95 * Converts an stringified IPv4 address into the RTNETADDRIPV4 representation.
96 *
97 * @returns VINF_SUCCESS on success, VERR_GETOPT_INVALID_ARGUMENT_FORMAT on
98 * failure.
99 *
100 * @param pszValue The value to convert.
101 * @param pAddr Where to store the result.
102 */
103static int rtgetoptConvertIPv4Addr(const char *pszValue, PRTNETADDRIPV4 pAddr)
104{
105 if (RT_FAILURE(RTNetStrToIPv4Addr(pszValue, pAddr)))
106 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
107 return VINF_SUCCESS;
108}
109
110
111/**
112 * Converts an stringified Ethernet MAC address into the RTMAC representation.
113 *
114 * @returns VINF_SUCCESS on success, VERR_GETOPT_INVALID_ARGUMENT_FORMAT on
115 * failure.
116 *
117 * @param pszValue The value to convert.
118 * @param pAddr Where to store the result.
119 */
120static int rtgetoptConvertMacAddr(const char *pszValue, PRTMAC pAddr)
121{
122
123 int rc = RTNetStrToMacAddr(pszValue, pAddr);
124 if (RT_FAILURE(rc))
125 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
126
127 return VINF_SUCCESS;
128}
129
130
131/**
132 * Searches for a long option.
133 *
134 * @returns Pointer to a matching option.
135 * @param pszOption The alleged long option.
136 * @param paOptions Option array.
137 * @param cOptions Number of items in the array.
138 * @param fFlags Init flags.
139 */
140static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions, uint32_t fFlags)
141{
142 PCRTGETOPTDEF pOpt = paOptions;
143 while (cOptions-- > 0)
144 {
145 if (pOpt->pszLong)
146 {
147 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
148 {
149 /*
150 * A value is required with the argument. We're trying to be
151 * understanding here and will permit any of the following:
152 * --long12:value, --long12=value, --long12 value,
153 * --long:value, --long=value, --long value,
154 *
155 * If the option is index, then all trailing chars must be
156 * digits. For error reporting reasons we also match where
157 * there is no index.
158 */
159 size_t cchLong = strlen(pOpt->pszLong);
160 if ( !strncmp(pszOption, pOpt->pszLong, cchLong)
161 || ( pOpt->fFlags & RTGETOPT_FLAG_ICASE
162 && !RTStrNICmp(pszOption, pOpt->pszLong, cchLong)))
163 {
164 if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
165 while (RT_C_IS_DIGIT(pszOption[cchLong]))
166 cchLong++;
167 if ( pszOption[cchLong] == '\0'
168 || pszOption[cchLong] == ':'
169 || pszOption[cchLong] == '=')
170 return pOpt;
171 }
172 }
173 else if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
174 {
175 /*
176 * The option takes an index but no value.
177 * As above, we also match where there is no index.
178 */
179 size_t cchLong = strlen(pOpt->pszLong);
180 if ( !strncmp(pszOption, pOpt->pszLong, cchLong)
181 || ( pOpt->fFlags & RTGETOPT_FLAG_ICASE
182 && !RTStrNICmp(pszOption, pOpt->pszLong, cchLong)))
183 {
184 while (RT_C_IS_DIGIT(pszOption[cchLong]))
185 cchLong++;
186 if (pszOption[cchLong] == '\0')
187 return pOpt;
188 }
189 }
190 else if ( !strcmp(pszOption, pOpt->pszLong)
191 || ( pOpt->fFlags & RTGETOPT_FLAG_ICASE
192 && !RTStrICmp(pszOption, pOpt->pszLong)))
193 return pOpt;
194 }
195 pOpt++;
196 }
197
198 if (!(fFlags & RTGETOPTINIT_FLAGS_NO_STD_OPTS))
199 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStdOptions); i++)
200 if ( !strcmp(pszOption, g_aStdOptions[i].pszLong)
201 || ( g_aStdOptions[i].fFlags & RTGETOPT_FLAG_ICASE
202 && !RTStrICmp(pszOption, g_aStdOptions[i].pszLong)))
203 return &g_aStdOptions[i];
204
205 return NULL;
206}
207
208
209/**
210 * Searches for a matching short option.
211 *
212 * @returns Pointer to a matching option.
213 * @param chOption The option char.
214 * @param paOptions Option array.
215 * @param cOptions Number of items in the array.
216 * @param fFlags Init flags.
217 */
218static PCRTGETOPTDEF rtGetOptSearchShort(int chOption, PCRTGETOPTDEF paOptions, size_t cOptions, uint32_t fFlags)
219{
220 PCRTGETOPTDEF pOpt = paOptions;
221 while (cOptions-- > 0)
222 {
223 if (pOpt->iShort == chOption)
224 return pOpt;
225 pOpt++;
226 }
227
228 if (!(fFlags & RTGETOPTINIT_FLAGS_NO_STD_OPTS))
229 {
230 for (uint32_t i = 0; i < RT_ELEMENTS(g_aStdOptions); i++)
231 if (g_aStdOptions[i].iShort == chOption)
232 return &g_aStdOptions[i];
233 if (chOption == '?')
234 return &g_aStdOptions[RTGETOPT_STD_OPTIONS_HELP_IDX];
235 }
236 return NULL;
237}
238
239
240/**
241 * Value string -> Value union.
242 *
243 * @returns IPRT status code.
244 * @param fFlags The value flags.
245 * @param pszValue The value string.
246 * @param pValueUnion Where to return the processed value.
247 */
248static int rtGetOptProcessValue(uint32_t fFlags, const char *pszValue, PRTGETOPTUNION pValueUnion)
249{
250 /*
251 * Transform into a option value as requested.
252 * If decimal conversion fails, we'll check for "0x<xdigit>" and
253 * try a 16 based conversion. We will not interpret any of the
254 * generic ints as octals.
255 */
256 uint32_t const fSwitchValue = fFlags & ( RTGETOPT_REQ_MASK
257 | RTGETOPT_FLAG_HEX
258 | RTGETOPT_FLAG_DEC
259 | RTGETOPT_FLAG_OCT);
260 switch (fSwitchValue)
261 {
262 case RTGETOPT_REQ_STRING:
263 pValueUnion->psz = pszValue;
264 break;
265
266 case RTGETOPT_REQ_BOOL:
267 if ( !RTStrICmp(pszValue, "true")
268 || !RTStrICmp(pszValue, "t")
269 || !RTStrICmp(pszValue, "yes")
270 || !RTStrICmp(pszValue, "y")
271 || !RTStrICmp(pszValue, "enabled")
272 || !RTStrICmp(pszValue, "enable")
273 || !RTStrICmp(pszValue, "en")
274 || !RTStrICmp(pszValue, "e")
275 || !RTStrICmp(pszValue, "on")
276 || !RTStrCmp(pszValue, "1")
277 )
278 pValueUnion->f = true;
279 else if ( !RTStrICmp(pszValue, "false")
280 || !RTStrICmp(pszValue, "f")
281 || !RTStrICmp(pszValue, "no")
282 || !RTStrICmp(pszValue, "n")
283 || !RTStrICmp(pszValue, "disabled")
284 || !RTStrICmp(pszValue, "disable")
285 || !RTStrICmp(pszValue, "dis")
286 || !RTStrICmp(pszValue, "d")
287 || !RTStrICmp(pszValue, "off")
288 || !RTStrCmp(pszValue, "0")
289 )
290 pValueUnion->f = false;
291 else
292 {
293 pValueUnion->psz = pszValue;
294 return VERR_GETOPT_UNKNOWN_OPTION;
295 }
296 break;
297
298 case RTGETOPT_REQ_BOOL_ONOFF:
299 if (!RTStrICmp(pszValue, "on"))
300 pValueUnion->f = true;
301 else if (!RTStrICmp(pszValue, "off"))
302 pValueUnion->f = false;
303 else
304 {
305 pValueUnion->psz = pszValue;
306 return VERR_GETOPT_UNKNOWN_OPTION;
307 }
308 break;
309
310#define MY_INT_CASE(req, type, memb, convfn) \
311 case req: \
312 { \
313 type Value; \
314 if ( convfn(pszValue, 10, &Value) != VINF_SUCCESS \
315 && ( pszValue[0] != '0' \
316 || (pszValue[1] != 'x' && pszValue[1] != 'X') \
317 || !RT_C_IS_XDIGIT(pszValue[2]) \
318 || convfn(pszValue, 16, &Value) != VINF_SUCCESS ) ) \
319 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
320 pValueUnion->memb = Value; \
321 break; \
322 }
323#define MY_BASE_INT_CASE(req, type, memb, convfn, base) \
324 case req: \
325 { \
326 type Value; \
327 if (convfn(pszValue, base, &Value) != VINF_SUCCESS) \
328 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
329 pValueUnion->memb = Value; \
330 break; \
331 }
332
333 MY_INT_CASE(RTGETOPT_REQ_INT8, int8_t, i8, RTStrToInt8Full)
334 MY_INT_CASE(RTGETOPT_REQ_INT16, int16_t, i16, RTStrToInt16Full)
335 MY_INT_CASE(RTGETOPT_REQ_INT32, int32_t, i32, RTStrToInt32Full)
336 MY_INT_CASE(RTGETOPT_REQ_INT64, int64_t, i64, RTStrToInt64Full)
337 MY_INT_CASE(RTGETOPT_REQ_UINT8, uint8_t, u8, RTStrToUInt8Full)
338 MY_INT_CASE(RTGETOPT_REQ_UINT16, uint16_t, u16, RTStrToUInt16Full)
339 MY_INT_CASE(RTGETOPT_REQ_UINT32, uint32_t, u32, RTStrToUInt32Full)
340 MY_INT_CASE(RTGETOPT_REQ_UINT64, uint64_t, u64, RTStrToUInt64Full)
341
342 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_HEX, int8_t, i8, RTStrToInt8Full, 16)
343 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_HEX, int16_t, i16, RTStrToInt16Full, 16)
344 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_HEX, int32_t, i32, RTStrToInt32Full, 16)
345 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_HEX, int64_t, i64, RTStrToInt64Full, 16)
346 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_HEX, uint8_t, u8, RTStrToUInt8Full, 16)
347 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_HEX, uint16_t, u16, RTStrToUInt16Full, 16)
348 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX, uint32_t, u32, RTStrToUInt32Full, 16)
349 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX, uint64_t, u64, RTStrToUInt64Full, 16)
350
351 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_DEC, int8_t, i8, RTStrToInt8Full, 10)
352 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_DEC, int16_t, i16, RTStrToInt16Full, 10)
353 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_DEC, int32_t, i32, RTStrToInt32Full, 10)
354 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_DEC, int64_t, i64, RTStrToInt64Full, 10)
355 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_DEC, uint8_t, u8, RTStrToUInt8Full, 10)
356 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_DEC, uint16_t, u16, RTStrToUInt16Full, 10)
357 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_DEC, uint32_t, u32, RTStrToUInt32Full, 10)
358 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_DEC, uint64_t, u64, RTStrToUInt64Full, 10)
359
360 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_OCT, int8_t, i8, RTStrToInt8Full, 8)
361 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_OCT, int16_t, i16, RTStrToInt16Full, 8)
362 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_OCT, int32_t, i32, RTStrToInt32Full, 8)
363 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_OCT, int64_t, i64, RTStrToInt64Full, 8)
364 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_OCT, uint8_t, u8, RTStrToUInt8Full, 8)
365 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_OCT, uint16_t, u16, RTStrToUInt16Full, 8)
366 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT, uint32_t, u32, RTStrToUInt32Full, 8)
367 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_OCT, uint64_t, u64, RTStrToUInt64Full, 8)
368
369#undef MY_INT_CASE
370#undef MY_BASE_INT_CASE
371
372 case RTGETOPT_REQ_IPV4ADDR:
373 {
374 RTNETADDRIPV4 Addr;
375 if (rtgetoptConvertIPv4Addr(pszValue, &Addr) != VINF_SUCCESS)
376 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
377 pValueUnion->IPv4Addr = Addr;
378 break;
379 }
380
381 case RTGETOPT_REQ_IPV4CIDR:
382 {
383 RTNETADDRIPV4 network;
384 RTNETADDRIPV4 netmask;
385 if (RT_FAILURE(RTCidrStrToIPv4(pszValue, &network, &netmask)))
386 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
387 pValueUnion->CidrIPv4.IPv4Network.u = network.u;
388 pValueUnion->CidrIPv4.IPv4Netmask.u = netmask.u;
389 break;
390 }
391
392 case RTGETOPT_REQ_MACADDR:
393 {
394 RTMAC Addr;
395 if (rtgetoptConvertMacAddr(pszValue, &Addr) != VINF_SUCCESS)
396 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
397 pValueUnion->MacAddr = Addr;
398 break;
399 }
400
401 case RTGETOPT_REQ_UUID:
402 {
403 RTUUID Uuid;
404 if (RTUuidFromStr(&Uuid, pszValue) != VINF_SUCCESS)
405 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
406 pValueUnion->Uuid = Uuid;
407 break;
408 }
409
410#define MY_INT_PAIR_CASE(a_fReqValue, a_fReqValueOptional, a_Type, a_MemberPrefix, a_fnConv, a_ConvBase, a_DefaultValue) \
411 case a_fReqValue: \
412 case a_fReqValueOptional: \
413 { \
414 /* First value: */ \
415 a_Type Value1; \
416 char *pszNext = NULL; \
417 unsigned uBase = pszValue[0] == '0' \
418 && (pszValue[1] == 'x' || pszValue[1] == 'X') \
419 && RT_C_IS_XDIGIT(pszValue[2]) \
420 ? 16 : a_ConvBase; \
421 int rc = a_fnConv(pszValue, &pszNext, uBase, &Value1); \
422 if (rc == VINF_SUCCESS || rc == VWRN_TRAILING_CHARS || rc == VWRN_TRAILING_SPACES) \
423 { \
424 /* The second value, could be optional: */ \
425 a_Type Value2 = a_DefaultValue; \
426 pszValue = pszNext;\
427 if (pszValue) \
428 { \
429 while (RT_C_IS_BLANK(*pszValue)) \
430 pszValue++; \
431 if (*pszValue == ':' || *pszValue == '/' || *pszValue == '|') \
432 do pszValue++; \
433 while (RT_C_IS_BLANK(*pszValue)); \
434 if (pszValue != pszNext) \
435 { \
436 uBase = pszValue[0] == '0' \
437 && (pszValue[1] == 'x' || pszValue[1] == 'X') \
438 && RT_C_IS_XDIGIT(pszValue[2]) \
439 ? 16 : a_ConvBase; \
440 rc = a_fnConv(pszValue, &pszNext, uBase, &Value2); \
441 if (rc == VINF_SUCCESS) \
442 { /* likely */ } \
443 else \
444 { RTAssertMsg2("z rc=%Rrc: '%s' '%s' uBase=%d\n", rc, pszValue, pszNext, uBase); return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; } \
445 } \
446 else if (fSwitchValue != (a_fReqValueOptional)) \
447 { RTAssertMsg2("x\n"); return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; } \
448 } \
449 else if (fSwitchValue != (a_fReqValueOptional)) \
450 { RTAssertMsg2("y\n"); return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; } \
451 pValueUnion->a_MemberPrefix##Second = Value2; \
452 pValueUnion->a_MemberPrefix##First = Value1; \
453 break; \
454 } \
455 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
456 }
457
458 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT32_PAIR, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR,
459 uint32_t, PairU32.u, RTStrToUInt32Ex, 10, UINT32_MAX)
460 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT32_PAIR | RTGETOPT_FLAG_DEC, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR | RTGETOPT_FLAG_DEC,
461 uint32_t, PairU32.u, RTStrToUInt32Ex, 10, UINT32_MAX)
462 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT32_PAIR | RTGETOPT_FLAG_HEX, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR | RTGETOPT_FLAG_HEX,
463 uint32_t, PairU32.u, RTStrToUInt32Ex, 16, UINT32_MAX)
464 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT32_PAIR | RTGETOPT_FLAG_OCT, RTGETOPT_REQ_UINT32_OPTIONAL_PAIR | RTGETOPT_FLAG_OCT,
465 uint32_t, PairU32.u, RTStrToUInt32Ex, 8, UINT32_MAX)
466
467 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT64_PAIR, RTGETOPT_REQ_UINT64_OPTIONAL_PAIR,
468 uint64_t, PairU64.u, RTStrToUInt64Ex, 10, UINT64_MAX)
469 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT64_PAIR | RTGETOPT_FLAG_DEC, RTGETOPT_REQ_UINT64_OPTIONAL_PAIR | RTGETOPT_FLAG_DEC,
470 uint64_t, PairU64.u, RTStrToUInt64Ex, 10, UINT64_MAX)
471 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT64_PAIR | RTGETOPT_FLAG_HEX, RTGETOPT_REQ_UINT64_OPTIONAL_PAIR | RTGETOPT_FLAG_HEX,
472 uint64_t, PairU64.u, RTStrToUInt64Ex, 16, UINT64_MAX)
473 MY_INT_PAIR_CASE(RTGETOPT_REQ_UINT64_PAIR | RTGETOPT_FLAG_OCT, RTGETOPT_REQ_UINT64_OPTIONAL_PAIR | RTGETOPT_FLAG_OCT,
474 uint64_t, PairU64.u, RTStrToUInt64Ex, 8, UINT64_MAX)
475
476 default:
477 AssertMsgFailed(("f=%#x\n", fFlags));
478 return VERR_INTERNAL_ERROR;
479 }
480
481 return VINF_SUCCESS;
482}
483
484
485/**
486 * Moves one argv option entries.
487 *
488 * @param papszTo Destination.
489 * @param papszFrom Source.
490 */
491static void rtGetOptMoveArgvEntries(char **papszTo, char **papszFrom)
492{
493 if (papszTo != papszFrom)
494 {
495 Assert((uintptr_t)papszTo < (uintptr_t)papszFrom);
496 char * const pszMoved = papszFrom[0];
497 memmove(&papszTo[1], &papszTo[0], (uintptr_t)papszFrom - (uintptr_t)papszTo);
498 papszTo[0] = pszMoved;
499 }
500}
501
502
503RTDECL(int) RTGetOpt(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion)
504{
505 /*
506 * Reset the variables kept in state.
507 */
508 pState->pDef = NULL;
509 pState->uIndex = UINT32_MAX;
510
511 /*
512 * Make sure the union is completely cleared out, whatever happens below.
513 */
514 pValueUnion->u64 = 0;
515 pValueUnion->pDef = NULL;
516
517 /*
518 * The next option.
519 */
520 bool fShort;
521 int iThis;
522 const char *pszArgThis;
523 PCRTGETOPTDEF pOpt;
524
525 if (pState->pszNextShort)
526 {
527 /*
528 * We've got short options left over from the previous call.
529 */
530 pOpt = rtGetOptSearchShort(*pState->pszNextShort, pState->paOptions, pState->cOptions, pState->fFlags);
531 if (!pOpt)
532 {
533 pValueUnion->psz = pState->pszNextShort;
534 return VERR_GETOPT_UNKNOWN_OPTION;
535 }
536 pState->pszNextShort++;
537 pszArgThis = pState->pszNextShort - 2;
538 iThis = pState->iNext;
539 fShort = true;
540 }
541 else
542 {
543 /*
544 * Pop off the next argument. Sorting options and dealing with the
545 * dash-dash makes this a little extra complicated.
546 */
547 for (;;)
548 {
549 if (pState->iNext >= pState->argc)
550 return 0;
551
552 if (pState->cNonOptions)
553 {
554 if (pState->cNonOptions == INT32_MAX)
555 {
556 pValueUnion->psz = pState->argv[pState->iNext++];
557 return VINF_GETOPT_NOT_OPTION;
558 }
559
560 if (pState->iNext + pState->cNonOptions >= pState->argc)
561 {
562 pState->cNonOptions = INT32_MAX;
563 continue;
564 }
565 }
566
567 iThis = pState->iNext++;
568 pszArgThis = pState->argv[iThis + pState->cNonOptions];
569
570 /*
571 * Do a long option search first and then a short option one.
572 * This way we can make sure single dash long options doesn't
573 * get mixed up with short ones.
574 */
575 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions, pState->fFlags);
576 if ( !pOpt
577 && pszArgThis[0] == '-'
578 && pszArgThis[1] != '-'
579 && pszArgThis[1] != '\0')
580 {
581 pOpt = rtGetOptSearchShort(pszArgThis[1], pState->paOptions, pState->cOptions, pState->fFlags);
582 fShort = pOpt != NULL;
583 }
584 else
585 fShort = false;
586
587 /* Look for dash-dash. */
588 if (!pOpt && !strcmp(pszArgThis, "--"))
589 {
590 rtGetOptMoveArgvEntries(&pState->argv[iThis], &pState->argv[iThis + pState->cNonOptions]);
591 pState->cNonOptions = INT32_MAX;
592 continue;
593 }
594
595 /* Options first hacks. */
596 if (pState->fFlags & RTGETOPTINIT_FLAGS_OPTS_FIRST)
597 {
598 if (pOpt)
599 rtGetOptMoveArgvEntries(&pState->argv[iThis], &pState->argv[iThis + pState->cNonOptions]);
600 else if (*pszArgThis == '-')
601 {
602 pValueUnion->psz = pszArgThis;
603 return VERR_GETOPT_UNKNOWN_OPTION;
604 }
605 else
606 {
607 /* not an option, add it to the non-options and try again. */
608 pState->iNext--;
609 pState->cNonOptions++;
610
611 /* Switch to returning non-options if we've reached the end. */
612 if (pState->iNext + pState->cNonOptions >= pState->argc)
613 pState->cNonOptions = INT32_MAX;
614 continue;
615 }
616 }
617
618 /* done */
619 break;
620 }
621 }
622
623 if (pOpt)
624 {
625 pValueUnion->pDef = pOpt; /* in case of no value or error. */
626
627 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
628 {
629 /*
630 * Find the argument value.
631 *
632 * A value is required with the argument. We're trying to be
633 * understanding here and will permit any of the following:
634 * -svalue, -s value, -s:value and -s=value
635 * (Ditto for long options.)
636 */
637 const char *pszValue;
638 if (fShort)
639 {
640 if (pszArgThis[2] == '\0')
641 {
642 if (iThis + 1 >= pState->argc)
643 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
644 pszValue = pState->argv[iThis + pState->cNonOptions + 1];
645 rtGetOptMoveArgvEntries(&pState->argv[iThis + 1], &pState->argv[iThis + pState->cNonOptions + 1]);
646 pState->iNext++;
647 }
648 else /* same argument. */
649 pszValue = &pszArgThis[2 + (pszArgThis[2] == ':' || pszArgThis[2] == '=')];
650 if (pState->pszNextShort)
651 {
652 pState->pszNextShort = NULL;
653 pState->iNext++;
654 }
655 }
656 else
657 {
658 size_t cchLong = strlen(pOpt->pszLong);
659 if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
660 {
661
662 if (pszArgThis[cchLong] == '\0')
663 return VERR_GETOPT_INDEX_MISSING;
664
665 uint32_t uIndex;
666 char *pszRet = NULL;
667 int rc = RTStrToUInt32Ex(&pszArgThis[cchLong], &pszRet, 10, &uIndex);
668 if (rc == VWRN_TRAILING_CHARS)
669 {
670 if ( pszRet[0] != ':'
671 && pszRet[0] != '=')
672 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
673 pState->uIndex = uIndex;
674 pszValue = pszRet + 1;
675 }
676 else if (rc == VINF_SUCCESS)
677 {
678 if (iThis + 1 + pState->cNonOptions >= pState->argc)
679 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
680 pState->uIndex = uIndex;
681 pszValue = pState->argv[iThis + pState->cNonOptions + 1];
682 rtGetOptMoveArgvEntries(&pState->argv[iThis + 1], &pState->argv[iThis + pState->cNonOptions + 1]);
683 pState->iNext++;
684 }
685 else
686 AssertMsgFailedReturn(("%s\n", pszArgThis), VERR_GETOPT_INVALID_ARGUMENT_FORMAT); /* search bug */
687 }
688 else
689 {
690 if (pszArgThis[cchLong] == '\0')
691 {
692 if (iThis + 1 + pState->cNonOptions >= pState->argc)
693 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
694 pszValue = pState->argv[iThis + pState->cNonOptions + 1];
695 rtGetOptMoveArgvEntries(&pState->argv[iThis + 1], &pState->argv[iThis + pState->cNonOptions + 1]);
696 pState->iNext++;
697 }
698 else /* same argument. */
699 pszValue = &pszArgThis[cchLong + 1];
700 }
701 }
702
703 /*
704 * Set up the ValueUnion.
705 */
706 int rc = rtGetOptProcessValue(pOpt->fFlags, pszValue, pValueUnion);
707 if (RT_FAILURE(rc))
708 return rc;
709 }
710 else if (fShort)
711 {
712 /*
713 * Deal with "compressed" short option lists, correcting the next
714 * state variables for the start and end cases.
715 */
716 if (pszArgThis[2])
717 {
718 if (!pState->pszNextShort)
719 {
720 /* start */
721 pState->pszNextShort = &pszArgThis[2];
722 pState->iNext--;
723 }
724 }
725 else if (pState->pszNextShort)
726 {
727 /* end */
728 pState->pszNextShort = NULL;
729 pState->iNext++;
730 }
731 }
732 else if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
733 {
734 size_t cchLong = strlen(pOpt->pszLong);
735 if (pszArgThis[cchLong] == '\0')
736 return VERR_GETOPT_INDEX_MISSING;
737
738 uint32_t uIndex;
739 if (RTStrToUInt32Full(&pszArgThis[cchLong], 10, &uIndex) == VINF_SUCCESS)
740 pState->uIndex = uIndex;
741 else
742 AssertMsgFailedReturn(("%s\n", pszArgThis), VERR_GETOPT_INVALID_ARGUMENT_FORMAT); /* search bug */
743 }
744
745 pState->pDef = pOpt;
746 return pOpt->iShort;
747 }
748
749 /*
750 * Not a known option argument. If it starts with a switch char (-) we'll
751 * fail with unknown option, and if it doesn't we'll return it as a non-option.
752 */
753 if (*pszArgThis == '-')
754 {
755 pValueUnion->psz = pszArgThis;
756 return VERR_GETOPT_UNKNOWN_OPTION;
757 }
758
759 pValueUnion->psz = pszArgThis;
760 return VINF_GETOPT_NOT_OPTION;
761}
762RT_EXPORT_SYMBOL(RTGetOpt);
763
764
765RTDECL(int) RTGetOptFetchValue(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion, uint32_t fFlags)
766{
767 /*
768 * Validate input.
769 */
770 PCRTGETOPTDEF pOpt = pState->pDef;
771 AssertReturn(!(fFlags & ~RTGETOPT_VALID_MASK), VERR_INVALID_PARAMETER);
772 AssertReturn((fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING, VERR_INVALID_PARAMETER);
773
774 /*
775 * Make sure the union is completely cleared out, whatever happens below.
776 */
777 pValueUnion->u64 = 0;
778 pValueUnion->pDef = NULL;
779
780 /*
781 * Pop off the next argument and convert it into a value union.
782 */
783 if (pState->iNext >= pState->argc)
784 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
785 int iThis = pState->iNext++;
786 const char *pszValue = pState->argv[iThis + (pState->cNonOptions != INT32_MAX ? pState->cNonOptions : 0)];
787 pValueUnion->pDef = pOpt; /* in case of no value or error. */
788
789 if (pState->cNonOptions && pState->cNonOptions != INT32_MAX)
790 rtGetOptMoveArgvEntries(&pState->argv[iThis], &pState->argv[iThis + pState->cNonOptions]);
791
792 return rtGetOptProcessValue(fFlags, pszValue, pValueUnion);
793}
794RT_EXPORT_SYMBOL(RTGetOptFetchValue);
795
796
797RTDECL(char **) RTGetOptNonOptionArrayPtr(PRTGETOPTSTATE pState)
798{
799 AssertReturn(pState->fFlags & RTGETOPTINIT_FLAGS_OPTS_FIRST, NULL);
800 return &pState->argv[pState->iNext - 1];
801}
802RT_EXPORT_SYMBOL(RTGetOptNonOptionArrayPtr);
803
804
805RTDECL(RTEXITCODE) RTGetOptPrintError(int ch, PCRTGETOPTUNION pValueUnion)
806{
807 if (ch == VINF_GETOPT_NOT_OPTION)
808 RTMsgError("Invalid parameter: %s", pValueUnion->psz);
809 else if (ch > 0)
810 {
811 if (RT_C_IS_GRAPH(ch))
812 RTMsgError("Unhandled option: -%c", ch);
813 else
814 RTMsgError("Unhandled option: %i (%#x)", ch, ch);
815 }
816 else if (ch == VERR_GETOPT_UNKNOWN_OPTION)
817 RTMsgError("Unknown option: '%s'", pValueUnion->psz);
818 else if (pValueUnion->pDef && ch == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
819 /** @todo r=klaus not really ideal, as the value isn't available */
820 RTMsgError("The value given '%s' has an invalid format.", pValueUnion->pDef->pszLong);
821 else if (pValueUnion->pDef)
822 RTMsgError("%s: %Rrs\n", pValueUnion->pDef->pszLong, ch);
823 else
824 RTMsgError("%Rrs\n", ch);
825
826 return RTEXITCODE_SYNTAX;
827}
828RT_EXPORT_SYMBOL(RTGetOptPrintError);
829
830
831RTDECL(ssize_t) RTGetOptFormatError(char *pszBuf, size_t cbBuf, int ch, PCRTGETOPTUNION pValueUnion)
832{
833 ssize_t cchRet;
834 if (ch == VINF_GETOPT_NOT_OPTION)
835 cchRet = RTStrPrintf2(pszBuf, cbBuf, "Invalid parameter: %s", pValueUnion->psz);
836 else if (ch > 0)
837 {
838 if (RT_C_IS_GRAPH(ch))
839 cchRet = RTStrPrintf2(pszBuf, cbBuf, "Unhandled option: -%c", ch);
840 else
841 cchRet = RTStrPrintf2(pszBuf, cbBuf, "Unhandled option: %i (%#x)", ch, ch);
842 }
843 else if (ch == VERR_GETOPT_UNKNOWN_OPTION)
844 cchRet = RTStrPrintf2(pszBuf, cbBuf, "Unknown option: '%s'", pValueUnion->psz);
845 else if (pValueUnion->pDef && ch == VERR_GETOPT_INVALID_ARGUMENT_FORMAT)
846 /** @todo r=klaus not really ideal, as the value isn't available */
847 cchRet = RTStrPrintf2(pszBuf, cbBuf, "The value given '%s' has an invalid format.", pValueUnion->pDef->pszLong);
848 else if (pValueUnion->pDef)
849 cchRet = RTStrPrintf2(pszBuf, cbBuf, "%s: %Rrs\n", pValueUnion->pDef->pszLong, ch);
850 else
851 cchRet = RTStrPrintf2(pszBuf, cbBuf, "%Rrs\n", ch);
852
853 return cchRet;
854}
855RT_EXPORT_SYMBOL(RTGetOptFormatError);
856
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette