VirtualBox

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

Last change on this file since 30651 was 28800, checked in by vboxsync, 15 years ago

Automated rebranding to Oracle copyright/license strings via filemuncher

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