VirtualBox

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

Last change on this file since 25617 was 25323, checked in by vboxsync, 15 years ago

iprt/getopt.h/cpp: Made the uIndex 32-bit instead of 64-bit as we don't need 64-bit and it only causes lots of warnings when compiling VBoxManage with MSC.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 23.7 KB
Line 
1/* $Id: getopt.cpp 25323 2009-12-11 12:27:17Z vboxsync $ */
2/** @file
3 * IPRT - Command Line Parsing
4 */
5
6/*
7 * Copyright (C) 2007 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31/*******************************************************************************
32* Header Files *
33*******************************************************************************/
34#include <iprt/net.h> /* must come before getopt.h */
35#include <iprt/getopt.h>
36#include "internal/iprt.h"
37
38#include <iprt/assert.h>
39#include <iprt/ctype.h>
40#include <iprt/err.h>
41#include <iprt/message.h>
42#include <iprt/string.h>
43#include <iprt/uuid.h>
44
45
46
47RTDECL(int) RTGetOptInit(PRTGETOPTSTATE pState, int argc, char **argv,
48 PCRTGETOPTDEF paOptions, size_t cOptions,
49 int iFirst, uint32_t fFlags)
50{
51 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
52
53 pState->argv = argv;
54 pState->argc = argc;
55 pState->paOptions = paOptions;
56 pState->cOptions = cOptions;
57 pState->iNext = iFirst;
58 pState->pszNextShort = NULL;
59 pState->pDef = NULL;
60
61 /* validate the options. */
62 for (size_t i = 0; i < cOptions; i++)
63 {
64 Assert(!(paOptions[i].fFlags & ~RTGETOPT_VALID_MASK));
65 Assert(paOptions[i].iShort > 0);
66 Assert(paOptions[i].iShort != VINF_GETOPT_NOT_OPTION);
67 Assert(paOptions[i].iShort != '-');
68 }
69
70 /** @todo Add an flag for sorting the arguments so that all the options comes
71 * first. */
72 return VINF_SUCCESS;
73}
74RT_EXPORT_SYMBOL(RTGetOptInit);
75
76
77/**
78 * Converts an stringified IPv4 address into the RTNETADDRIPV4 representation.
79 *
80 * @todo This should be move to some generic part of the runtime.
81 *
82 * @returns VINF_SUCCESS on success, VERR_GETOPT_INVALID_ARGUMENT_FORMAT on
83 * failure.
84 *
85 * @param pszValue The value to convert.
86 * @param pAddr Where to store the result.
87 */
88static int rtgetoptConvertIPv4Addr(const char *pszValue, PRTNETADDRIPV4 pAddr)
89{
90 char *pszNext;
91 int rc = RTStrToUInt8Ex(RTStrStripL(pszValue), &pszNext, 10, &pAddr->au8[0]);
92 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
93 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
94 if (*pszNext++ != '.')
95 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
96
97 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[1]);
98 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
99 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
100 if (*pszNext++ != '.')
101 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
102
103 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[2]);
104 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
105 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
106 if (*pszNext++ != '.')
107 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
108
109 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[3]);
110 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
111 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
112 pszNext = RTStrStripL(pszNext);
113 if (*pszNext)
114 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
115
116 return VINF_SUCCESS;
117}
118
119
120/**
121 * Converts an stringified Ethernet MAC address into the RTMAC representation.
122 *
123 * @todo This should be move to some generic part of the runtime.
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 * Not quite sure if I should accept stuff like "08::27:::1" here...
135 * The code is accepting "::" patterns now, except for for the first
136 * and last parts.
137 */
138
139 /* first */
140 char *pszNext;
141 int rc = RTStrToUInt8Ex(RTStrStripL(pszValue), &pszNext, 16, &pAddr->au8[0]);
142 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
143 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
144 if (*pszNext++ != ':')
145 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
146
147 /* middle */
148 for (unsigned i = 1; i < 5; i++)
149 {
150 if (*pszNext == ':')
151 pAddr->au8[i] = 0;
152 else
153 {
154 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &pAddr->au8[i]);
155 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
156 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
157 if (*pszNext != ':')
158 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
159 }
160 pszNext++;
161 }
162
163 /* last */
164 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &pAddr->au8[5]);
165 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
166 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
167 pszNext = RTStrStripL(pszNext);
168 if (*pszNext)
169 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
170
171 return VINF_SUCCESS;
172}
173
174
175/**
176 * Searches for a long option.
177 *
178 * @returns Pointer to a matching option.
179 * @param pszOption The alleged long option.
180 * @param paOptions Option array.
181 * @param cOptions Number of items in the array.
182 */
183static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions)
184{
185 PCRTGETOPTDEF pOpt = paOptions;
186 while (cOptions-- > 0)
187 {
188 if (pOpt->pszLong)
189 {
190 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
191 {
192 /*
193 * A value is required with the argument. We're trying to be very
194 * understanding here and will permit any of the following:
195 * --long12:value, --long12=value, --long12 value,
196 * --long:value, --long=value, --long value,
197 * --long: value, --long= value
198 *
199 * If the option is index, then all trailing chars must be
200 * digits. For error reporting reasons we also match where
201 * there is no index.
202 */
203 size_t cchLong = strlen(pOpt->pszLong);
204 if (!strncmp(pszOption, pOpt->pszLong, cchLong))
205 {
206 if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
207 while (RT_C_IS_DIGIT(pszOption[cchLong]))
208 cchLong++;
209 if ( pszOption[cchLong] == '\0'
210 || pszOption[cchLong] == ':'
211 || pszOption[cchLong] == '=')
212 return pOpt;
213 }
214 }
215 else if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
216 {
217 /*
218 * The option takes an index but no value.
219 * As above, we also match where there is no index.
220 */
221 size_t cchLong = strlen(pOpt->pszLong);
222 if (!strncmp(pszOption, pOpt->pszLong, cchLong))
223 {
224 while (RT_C_IS_DIGIT(pszOption[cchLong]))
225 cchLong++;
226 if (pszOption[cchLong] == '\0')
227 return pOpt;
228 }
229 }
230 else if (!strcmp(pszOption, pOpt->pszLong))
231 return pOpt;
232 }
233 pOpt++;
234 }
235 return NULL;
236}
237
238
239/**
240 * Searches for a matching short option.
241 *
242 * @returns Pointer to a matching option.
243 * @param chOption The option char.
244 * @param paOptions Option array.
245 * @param cOptions Number of items in the array.
246 */
247static PCRTGETOPTDEF rtGetOptSearchShort(int chOption, PCRTGETOPTDEF paOptions, size_t cOptions)
248{
249 PCRTGETOPTDEF pOpt = paOptions;
250 while (cOptions-- > 0)
251 {
252 if (pOpt->iShort == chOption)
253 return pOpt;
254 pOpt++;
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 switch (fFlags & ( RTGETOPT_REQ_MASK
277 | RTGETOPT_FLAG_HEX
278 | RTGETOPT_FLAG_DEC
279 | RTGETOPT_FLAG_OCT))
280 {
281 case RTGETOPT_REQ_STRING:
282 pValueUnion->psz = pszValue;
283 break;
284
285 case RTGETOPT_REQ_BOOL_ONOFF:
286 if (!RTStrICmp(pszValue, "on"))
287 pValueUnion->f = true;
288 else if (!RTStrICmp(pszValue, "off"))
289 pValueUnion->f = false;
290 else
291 {
292 pValueUnion->psz = pszValue;
293 return VERR_GETOPT_UNKNOWN_OPTION;
294 }
295 break;
296
297#define MY_INT_CASE(req,type,memb,convfn) \
298 case req: \
299 { \
300 type Value; \
301 if ( convfn(pszValue, 10, &Value) != VINF_SUCCESS \
302 && ( pszValue[0] != '0' \
303 || (pszValue[1] != 'x' && pszValue[1] != 'X') \
304 || !RT_C_IS_XDIGIT(pszValue[2]) \
305 || convfn(pszValue, 16, &Value) != VINF_SUCCESS ) ) \
306 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
307 pValueUnion->memb = Value; \
308 break; \
309 }
310#define MY_BASE_INT_CASE(req,type,memb,convfn,base) \
311 case req: \
312 { \
313 type Value; \
314 if (convfn(pszValue, base, &Value) != VINF_SUCCESS) \
315 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
316 pValueUnion->memb = Value; \
317 break; \
318 }
319
320 MY_INT_CASE(RTGETOPT_REQ_INT8, int8_t, i, RTStrToInt8Full)
321 MY_INT_CASE(RTGETOPT_REQ_INT16, int16_t, i, RTStrToInt16Full)
322 MY_INT_CASE(RTGETOPT_REQ_INT32, int32_t, i, RTStrToInt32Full)
323 MY_INT_CASE(RTGETOPT_REQ_INT64, int64_t, i, RTStrToInt64Full)
324 MY_INT_CASE(RTGETOPT_REQ_UINT8, uint8_t, u, RTStrToUInt8Full)
325 MY_INT_CASE(RTGETOPT_REQ_UINT16, uint16_t, u, RTStrToUInt16Full)
326 MY_INT_CASE(RTGETOPT_REQ_UINT32, uint32_t, u, RTStrToUInt32Full)
327 MY_INT_CASE(RTGETOPT_REQ_UINT64, uint64_t, u, RTStrToUInt64Full)
328
329 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_HEX, int8_t, i, RTStrToInt8Full, 16)
330 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_HEX, int16_t, i, RTStrToInt16Full, 16)
331 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_HEX, int32_t, i, RTStrToInt32Full, 16)
332 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_HEX, int64_t, i, RTStrToInt64Full, 16)
333 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_HEX, uint8_t, u, RTStrToUInt8Full, 16)
334 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_HEX, uint16_t, u, RTStrToUInt16Full, 16)
335 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX, uint32_t, u, RTStrToUInt32Full, 16)
336 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX, uint64_t, u, RTStrToUInt64Full, 16)
337
338 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_DEC, int8_t, i, RTStrToInt8Full, 10)
339 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_DEC, int16_t, i, RTStrToInt16Full, 10)
340 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_DEC, int32_t, i, RTStrToInt32Full, 10)
341 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_DEC, int64_t, i, RTStrToInt64Full, 10)
342 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_DEC, uint8_t, u, RTStrToUInt8Full, 10)
343 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_DEC, uint16_t, u, RTStrToUInt16Full, 10)
344 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_DEC, uint32_t, u, RTStrToUInt32Full, 10)
345 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_DEC, uint64_t, u, RTStrToUInt64Full, 10)
346
347 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_OCT, int8_t, i, RTStrToInt8Full, 8)
348 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_OCT, int16_t, i, RTStrToInt16Full, 8)
349 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_OCT, int32_t, i, RTStrToInt32Full, 8)
350 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_OCT, int64_t, i, RTStrToInt64Full, 8)
351 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_OCT, uint8_t, u, RTStrToUInt8Full, 8)
352 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_OCT, uint16_t, u, RTStrToUInt16Full, 8)
353 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT, uint32_t, u, RTStrToUInt32Full, 8)
354 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_OCT, uint64_t, u, RTStrToUInt64Full, 8)
355
356#undef MY_INT_CASE
357#undef MY_BASE_INT_CASE
358
359 case RTGETOPT_REQ_IPV4ADDR:
360 {
361 RTNETADDRIPV4 Addr;
362 if (rtgetoptConvertIPv4Addr(pszValue, &Addr) != VINF_SUCCESS)
363 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
364 pValueUnion->IPv4Addr = Addr;
365 break;
366 }
367#if 0 /** @todo CIDR */
368#endif
369
370 case RTGETOPT_REQ_MACADDR:
371 {
372 RTMAC Addr;
373 if (rtgetoptConvertMacAddr(pszValue, &Addr) != VINF_SUCCESS)
374 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
375 pValueUnion->MacAddr = Addr;
376 break;
377 }
378
379 case RTGETOPT_REQ_UUID:
380 {
381 RTUUID Uuid;
382 if (RTUuidFromStr(&Uuid, pszValue) != VINF_SUCCESS)
383 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
384 pValueUnion->Uuid = Uuid;
385 break;
386 }
387
388 default:
389 AssertMsgFailed(("f=%#x\n", fFlags));
390 return VERR_INTERNAL_ERROR;
391 }
392
393 return VINF_SUCCESS;
394}
395
396
397RTDECL(int) RTGetOpt(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion)
398{
399 /*
400 * Reset the variables kept in state.
401 */
402 pState->pDef = NULL;
403 pState->uIndex = UINT32_MAX;
404
405 /*
406 * Make sure the union is completely cleared out, whatever happens below.
407 */
408 pValueUnion->u64 = 0;
409 pValueUnion->pDef = NULL;
410
411 /** @todo Handle '--' (end of options).*/
412 /** @todo Add a flag to RTGetOptInit for handling the various help options in
413 * a common way. (-?,-h,-help,--help,++) */
414 /** @todo Add a flag to RTGetOptInit for handling the standard version options
415 * in a common way. (-V,--version) */
416
417 /*
418 * The next option.
419 */
420 bool fShort;
421 int iThis;
422 const char *pszArgThis;
423 PCRTGETOPTDEF pOpt;
424
425 if (pState->pszNextShort)
426 {
427 /*
428 * We've got short options left over from the previous call.
429 */
430 pOpt = rtGetOptSearchShort(*pState->pszNextShort, pState->paOptions, pState->cOptions);
431 if (!pOpt)
432 {
433 pValueUnion->psz = pState->pszNextShort;
434 return VERR_GETOPT_UNKNOWN_OPTION;
435 }
436 pState->pszNextShort++;
437 pszArgThis = pState->pszNextShort - 2;
438 iThis = pState->iNext;
439 fShort = true;
440 }
441 else
442 {
443 /*
444 * Pop off the next argument.
445 */
446 if (pState->iNext >= pState->argc)
447 return 0;
448 iThis = pState->iNext++;
449 pszArgThis = pState->argv[iThis];
450
451 /*
452 * Do a long option search first and then a short option one.
453 * This way we can make sure single dash long options doesn't
454 * get mixed up with short ones.
455 */
456 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions);
457 if ( !pOpt
458 && pszArgThis[0] == '-'
459 && pszArgThis[1] != '-'
460 && pszArgThis[1] != '\0')
461 {
462 pOpt = rtGetOptSearchShort(pszArgThis[1], pState->paOptions, pState->cOptions);
463 fShort = pOpt != NULL;
464 }
465 else
466 fShort = false;
467 }
468
469 if (pOpt)
470 {
471 pValueUnion->pDef = pOpt; /* in case of no value or error. */
472
473 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
474 {
475 /*
476 * Find the argument value.
477 *
478 * A value is required with the argument. We're trying to be very
479 * understanding here and will permit any of the following:
480 * -svalue, -s:value, -s=value,
481 * -s value, -s: value, -s= value
482 * (Ditto for long options.)
483 */
484 const char *pszValue;
485 if (fShort)
486 {
487 if ( pszArgThis[2] == '\0'
488 || ( pszArgThis[3] == '\0'
489 && ( pszArgThis[2] == ':'
490 || pszArgThis[2] == '=')) )
491 {
492 if (iThis + 1 >= pState->argc)
493 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
494 pszValue = pState->argv[iThis + 1];
495 pState->iNext++;
496 }
497 else /* same argument. */
498 pszValue = &pszArgThis[2 + (pszArgThis[2] == ':' || pszArgThis[2] == '=')];
499 if (pState->pszNextShort)
500 {
501 pState->pszNextShort = NULL;
502 pState->iNext++;
503 }
504 }
505 else
506 {
507 size_t cchLong = strlen(pOpt->pszLong);
508 if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
509 {
510
511 if (pszArgThis[cchLong] == '\0')
512 return VERR_GETOPT_INDEX_MISSING;
513
514 uint32_t uIndex;
515 char *pszRet = NULL;
516 int rc = RTStrToUInt32Ex(&pszArgThis[cchLong], &pszRet, 10, &uIndex);
517 if (rc == VWRN_TRAILING_CHARS)
518 {
519 if ( pszRet[0] != ':'
520 && pszRet[0] != '=')
521 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
522 pState->uIndex = uIndex;
523 pszValue = pszRet + 1;
524 }
525 else if (rc == VINF_SUCCESS)
526 {
527 if (iThis + 1 >= pState->argc)
528 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
529 pState->uIndex = uIndex;
530 pszValue = pState->argv[iThis + 1];
531 pState->iNext++;
532 }
533 else
534 AssertMsgFailedReturn(("%s\n", pszArgThis), VERR_GETOPT_INVALID_ARGUMENT_FORMAT); /* search bug */
535 }
536 else
537 {
538 if ( pszArgThis[cchLong] == '\0'
539 || pszArgThis[cchLong + 1] == '\0')
540 {
541 if (iThis + 1 >= pState->argc)
542 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
543 pszValue = pState->argv[iThis + 1];
544 pState->iNext++;
545 }
546 else /* same argument. */
547 pszValue = &pszArgThis[cchLong + 1];
548 }
549 }
550
551 /*
552 * Set up the ValueUnion.
553 */
554 int rc = rtGetOptProcessValue(pOpt->fFlags, pszValue, pValueUnion);
555 if (RT_FAILURE(rc))
556 return rc;
557 }
558 else if (fShort)
559 {
560 /*
561 * Deal with "compressed" short option lists, correcting the next
562 * state variables for the start and end cases.
563 */
564 if (pszArgThis[2])
565 {
566 if (!pState->pszNextShort)
567 {
568 /* start */
569 pState->pszNextShort = &pszArgThis[2];
570 pState->iNext--;
571 }
572 }
573 else if (pState->pszNextShort)
574 {
575 /* end */
576 pState->pszNextShort = NULL;
577 pState->iNext++;
578 }
579 }
580 else if (pOpt->fFlags & RTGETOPT_FLAG_INDEX)
581 {
582 size_t cchLong = strlen(pOpt->pszLong);
583 if (pszArgThis[cchLong] == '\0')
584 return VERR_GETOPT_INDEX_MISSING;
585
586 uint32_t uIndex;
587 char *pszRet = NULL;
588 if (RTStrToUInt32Full(&pszArgThis[cchLong], 10, &uIndex) == VINF_SUCCESS)
589 pState->uIndex = uIndex;
590 else
591 AssertMsgFailedReturn(("%s\n", pszArgThis), VERR_GETOPT_INVALID_ARGUMENT_FORMAT); /* search bug */
592 }
593
594 pState->pDef = pOpt;
595 return pOpt->iShort;
596 }
597
598 /*
599 * Not a known option argument. If it starts with a switch char (-) we'll
600 * fail with unkown option, and if it doesn't we'll return it as a non-option.
601 */
602
603 if (*pszArgThis == '-')
604 {
605 pValueUnion->psz = pszArgThis;
606 return VERR_GETOPT_UNKNOWN_OPTION;
607 }
608
609 pValueUnion->psz = pszArgThis;
610 return VINF_GETOPT_NOT_OPTION;
611}
612RT_EXPORT_SYMBOL(RTGetOpt);
613
614
615RTDECL(int) RTGetOptFetchValue(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion, uint32_t fFlags)
616{
617 /*
618 * Validate input.
619 */
620 PCRTGETOPTDEF pOpt = pState->pDef;
621 AssertReturn(pOpt, VERR_GETOPT_UNKNOWN_OPTION);
622 AssertReturn(!(fFlags & ~RTGETOPT_VALID_MASK), VERR_INVALID_PARAMETER);
623 AssertReturn((fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING, VERR_INVALID_PARAMETER);
624
625 /*
626 * Make sure the union is completely cleared out, whatever happens below.
627 */
628 pValueUnion->u64 = 0;
629 pValueUnion->pDef = NULL;
630
631 /*
632 * Pop off the next argument and convert it into a value union.
633 */
634 if (pState->iNext >= pState->argc)
635 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
636 int iThis = pState->iNext++;
637 const char *pszValue = pState->argv[iThis];
638 pValueUnion->pDef = pOpt; /* in case of no value or error. */
639
640 return rtGetOptProcessValue(fFlags, pszValue, pValueUnion);
641}
642RT_EXPORT_SYMBOL(RTGetOptFetchValue);
643
644
645RTDECL(int) RTGetOptPrintError(int ch, PCRTGETOPTUNION pValueUnion)
646{
647 if (ch == VINF_GETOPT_NOT_OPTION)
648 RTMsgError("Invalid parameter: %s", pValueUnion->psz);
649 else if (ch > 0)
650 {
651 if (RT_C_IS_GRAPH(ch))
652 RTMsgError("Unhandled option: -%c", ch);
653 else
654 RTMsgError("Unhandled option: %i (%#x)", ch, ch);
655 }
656 else if (ch == VERR_GETOPT_UNKNOWN_OPTION)
657 RTMsgError("Unknown option: '%s'", pValueUnion->psz);
658 else if (pValueUnion->pDef)
659 RTMsgError("%s: %Rrs\n", pValueUnion->pDef->pszLong, ch);
660 else
661 RTMsgError("%Rrs\n", ch);
662
663 return 2; /** @todo add defines for EXIT_SUCCESS, EXIT_FAILURE, EXIT_INVAL, etc... */
664}
665RT_EXPORT_SYMBOL(RTGetOptPrintError);
666
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