VirtualBox

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

Last change on this file since 83982 was 83982, checked in by vboxsync, 5 years ago

IPRT/getopt.cpp: Assume options are 7-bit ASCII / basic latin, so we don't need to drag in the case tables in static builds.

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