VirtualBox

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

Last change on this file since 42835 was 42129, checked in by vboxsync, 13 years ago

Main/VirtualBox+Machine: add support for VM groups (incomplete, saving of settings is not implemented), remaining code adjusted accordingly, use better parameter checking macros, make XSLT generators process setter attributes using safearrays correctly, various cleanups and warning fixes
Frontends/VBoxManage: first traces of groups support

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