VirtualBox

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

Last change on this file since 28142 was 27802, checked in by vboxsync, 15 years ago

getopt fix (2nd part).

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