VirtualBox

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

Last change on this file since 49329 was 49326, checked in by vboxsync, 11 years ago

IPRT: exposes RTNetStrToMAC (resolves todo in misc/getopt.cpp).

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