VirtualBox

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

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

Add/Nt/Installer: Simple installer program for NT 3.x.

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