VirtualBox

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

Last change on this file since 45115 was 45115, checked in by vboxsync, 12 years ago

RT/net: rt-network-str2ipv4.patch from xTRacker/5894 with applyed recommendations in comment 90.
introduces RTNetStrToIPv4Addr: char* -> RTNETADDRIP4.

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

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