VirtualBox

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

Last change on this file since 45343 was 45116, checked in by vboxsync, 12 years ago

patch getopt-cidr.patch from xTracker/5894 with applied recommendations from comment 91.

introduces RTGETOPT_REQ_IPV4CIDR in getop.h

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