VirtualBox

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

Last change on this file since 64602 was 64602, checked in by vboxsync, 8 years ago

IPRT: Added RTGetOptNonOptionArrayPtr.

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