VirtualBox

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

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

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 29.3 KB
Line 
1/* $Id: getopt.cpp 44528 2013-02-04 14:27: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.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 value isn't available */
795 RTMsgError("The value given '%s' has an invalid format.", pValueUnion->pDef->pszLong);
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