VirtualBox

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

Last change on this file since 21848 was 21337, checked in by vboxsync, 15 years ago

IPRT,HostDrv,AddDrv: Export public IPRT symbols for the linux kernel (pain).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.7 KB
Line 
1/* $Id: getopt.cpp 21337 2009-07-07 14:58:27Z 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/err.h>
39#include <iprt/string.h>
40#include <iprt/assert.h>
41#include <iprt/ctype.h>
42#include <iprt/uuid.h>
43
44
45
46RTDECL(int) RTGetOptInit(PRTGETOPTSTATE pState, int argc, char **argv,
47 PCRTGETOPTDEF paOptions, size_t cOptions,
48 int iFirst, uint32_t fFlags)
49{
50 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
51
52 pState->argv = argv;
53 pState->argc = argc;
54 pState->paOptions = paOptions;
55 pState->cOptions = cOptions;
56 pState->iNext = iFirst;
57 pState->pszNextShort = NULL;
58 pState->pDef = NULL;
59
60 /* validate the options. */
61 for (size_t i = 0; i < cOptions; i++)
62 {
63 Assert(!(paOptions[i].fFlags & ~RTGETOPT_VALID_MASK));
64 Assert(paOptions[i].iShort > 0);
65 Assert(paOptions[i].iShort != VINF_GETOPT_NOT_OPTION);
66 Assert(paOptions[i].iShort != '-');
67 }
68
69 /** @todo Add an flag for sorting the arguments so that all the options comes
70 * first. */
71 return VINF_SUCCESS;
72}
73RT_EXPORT_SYMBOL(RTGetOptInit);
74
75
76/**
77 * Converts an stringified IPv4 address into the RTNETADDRIPV4 representation.
78 *
79 * @todo This should be move to some generic part of the runtime.
80 *
81 * @returns VINF_SUCCESS on success, VERR_GETOPT_INVALID_ARGUMENT_FORMAT on
82 * failure.
83 *
84 * @param pszValue The value to convert.
85 * @param pAddr Where to store the result.
86 */
87static int rtgetoptConvertIPv4Addr(const char *pszValue, PRTNETADDRIPV4 pAddr)
88{
89 char *pszNext;
90 int rc = RTStrToUInt8Ex(RTStrStripL(pszValue), &pszNext, 10, &pAddr->au8[0]);
91 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
92 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
93 if (*pszNext++ != '.')
94 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
95
96 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[1]);
97 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
98 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
99 if (*pszNext++ != '.')
100 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
101
102 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[2]);
103 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
104 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
105 if (*pszNext++ != '.')
106 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
107
108 rc = RTStrToUInt8Ex(pszNext, &pszNext, 10, &pAddr->au8[3]);
109 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
110 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
111 pszNext = RTStrStripL(pszNext);
112 if (*pszNext)
113 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
114
115 return VINF_SUCCESS;
116}
117
118
119/**
120 * Converts an stringified Ethernet MAC address into the RTMAC representation.
121 *
122 * @todo This should be move to some generic part of the runtime.
123 *
124 * @returns VINF_SUCCESS on success, VERR_GETOPT_INVALID_ARGUMENT_FORMAT on
125 * failure.
126 *
127 * @param pszValue The value to convert.
128 * @param pAddr Where to store the result.
129 */
130static int rtgetoptConvertMacAddr(const char *pszValue, PRTMAC pAddr)
131{
132 /*
133 * Not quite sure if I should accept stuff like "08::27:::1" here...
134 * The code is accepting "::" patterns now, except for for the first
135 * and last parts.
136 */
137
138 /* first */
139 char *pszNext;
140 int rc = RTStrToUInt8Ex(RTStrStripL(pszValue), &pszNext, 16, &pAddr->au8[0]);
141 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
142 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
143 if (*pszNext++ != ':')
144 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
145
146 /* middle */
147 for (unsigned i = 1; i < 5; i++)
148 {
149 if (*pszNext == ':')
150 pAddr->au8[i] = 0;
151 else
152 {
153 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &pAddr->au8[i]);
154 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_CHARS)
155 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
156 if (*pszNext != ':')
157 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
158 }
159 pszNext++;
160 }
161
162 /* last */
163 rc = RTStrToUInt8Ex(pszNext, &pszNext, 16, &pAddr->au8[5]);
164 if (rc != VINF_SUCCESS && rc != VWRN_TRAILING_SPACES)
165 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
166 pszNext = RTStrStripL(pszNext);
167 if (*pszNext)
168 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
169
170 return VINF_SUCCESS;
171}
172
173
174/**
175 * Searches for a long option.
176 *
177 * @returns Pointer to a matching option.
178 * @param pszOption The alleged long option.
179 * @param paOptions Option array.
180 * @param cOptions Number of items in the array.
181 */
182static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions)
183{
184 PCRTGETOPTDEF pOpt = paOptions;
185 while (cOptions-- > 0)
186 {
187 if (pOpt->pszLong)
188 {
189 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
190 {
191 /*
192 * A value is required with the argument. We're trying to be very
193 * understanding here and will permit any of the following:
194 * --long:value, --long=value, --long value,
195 * --long: value, --long= value
196 */
197 size_t cchLong = strlen(pOpt->pszLong);
198 if ( !strncmp(pszOption, pOpt->pszLong, cchLong)
199 && ( pszOption[cchLong] == '\0'
200 || pszOption[cchLong] == ':'
201 || pszOption[cchLong] == '='))
202 return pOpt;
203 }
204 else if (!strcmp(pszOption, pOpt->pszLong))
205 return pOpt;
206 }
207 pOpt++;
208 }
209 return NULL;
210}
211
212
213/**
214 * Searches for a matching short option.
215 *
216 * @returns Pointer to a matching option.
217 * @param chOption The option char.
218 * @param paOptions Option array.
219 * @param cOptions Number of items in the array.
220 */
221static PCRTGETOPTDEF rtGetOptSearchShort(int chOption, PCRTGETOPTDEF paOptions, size_t cOptions)
222{
223 PCRTGETOPTDEF pOpt = paOptions;
224 while (cOptions-- > 0)
225 {
226 if (pOpt->iShort == chOption)
227 return pOpt;
228 pOpt++;
229 }
230 return NULL;
231}
232
233
234RTDECL(int) RTGetOpt(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion)
235{
236 pState->pDef = NULL;
237 /*
238 * Make sure the union is completely cleared out, whatever happens below.
239 */
240 pValueUnion->u64 = 0;
241 pValueUnion->pDef = NULL;
242
243 /** @todo Handle '--' (end of options).*/
244 /** @todo Add a flag to RTGetOptInit for handling the various help options in
245 * a common way. (-?,-h,-help,--help,++) */
246 /** @todo Add a flag to RTGetOptInit for handling the standard version options
247 * in a common way. (-V,--version) */
248
249 /*
250 * The next option.
251 */
252 bool fShort;
253 int iThis;
254 const char *pszArgThis;
255 PCRTGETOPTDEF pOpt;
256
257 if (pState->pszNextShort)
258 {
259 /*
260 * We've got short options left over from the previous call.
261 */
262 pOpt = rtGetOptSearchShort(*pState->pszNextShort, pState->paOptions, pState->cOptions);
263 if (!pOpt)
264 {
265 pValueUnion->psz = pState->pszNextShort;
266 return VERR_GETOPT_UNKNOWN_OPTION;
267 }
268 pState->pszNextShort++;
269 pszArgThis = pState->pszNextShort - 2;
270 iThis = pState->iNext;
271 fShort = true;
272 }
273 else
274 {
275 /*
276 * Pop off the next argument.
277 */
278 if (pState->iNext >= pState->argc)
279 return 0;
280 iThis = pState->iNext++;
281 pszArgThis = pState->argv[iThis];
282
283 /*
284 * Do a long option search first and then a short option one.
285 * This way we can make sure single dash long options doesn't
286 * get mixed up with short ones.
287 */
288 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions);
289 if ( !pOpt
290 && pszArgThis[0] == '-'
291 && pszArgThis[1] != '-'
292 && pszArgThis[1] != '\0')
293 {
294 pOpt = rtGetOptSearchShort(pszArgThis[1], pState->paOptions, pState->cOptions);
295 fShort = pOpt != NULL;
296 }
297 else
298 fShort = false;
299 }
300
301 if (pOpt)
302 {
303 pValueUnion->pDef = pOpt; /* in case of no value or error. */
304
305 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
306 {
307 /*
308 * Find the argument value.
309 *
310 * A value is required with the argument. We're trying to be very
311 * understanding here and will permit any of the following:
312 * -svalue, -s:value, -s=value,
313 * -s value, -s: value, -s= value
314 * (Ditto for long options.)
315 */
316 const char *pszValue;
317 if (fShort)
318 {
319 if ( pszArgThis[2] == '\0'
320 || ( pszArgThis[3] == '\0'
321 && ( pszArgThis[2] == ':'
322 || pszArgThis[2] == '=')) )
323 {
324 if (iThis + 1 >= pState->argc)
325 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
326 pszValue = pState->argv[iThis + 1];
327 pState->iNext++;
328 }
329 else /* same argument. */
330 pszValue = &pszArgThis[2 + (pszArgThis[2] == ':' || pszArgThis[2] == '=')];
331 if (pState->pszNextShort)
332 {
333 pState->pszNextShort = NULL;
334 pState->iNext++;
335 }
336 }
337 else
338 {
339 size_t cchLong = strlen(pOpt->pszLong);
340 if ( pszArgThis[cchLong] == '\0'
341 || pszArgThis[cchLong + 1] == '\0')
342 {
343 if (iThis + 1 >= pState->argc)
344 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
345 pszValue = pState->argv[iThis + 1];
346 pState->iNext++;
347 }
348 else /* same argument. */
349 pszValue = &pszArgThis[cchLong + 1];
350 }
351
352 /*
353 * Transform into a option value as requested.
354 * If decimal conversion fails, we'll check for "0x<xdigit>" and
355 * try a 16 based conversion. We will not interpret any of the
356 * generic ints as octals.
357 */
358 switch (pOpt->fFlags & (RTGETOPT_REQ_MASK | RTGETOPT_FLAG_HEX | RTGETOPT_FLAG_OCT | RTGETOPT_FLAG_DEC))
359 {
360 case RTGETOPT_REQ_STRING:
361 pValueUnion->psz = pszValue;
362 break;
363
364#define MY_INT_CASE(req,type,memb,convfn) \
365 case req: \
366 { \
367 type Value; \
368 if ( convfn(pszValue, 10, &Value) != VINF_SUCCESS \
369 && ( pszValue[0] != '0' \
370 || (pszValue[1] != 'x' && pszValue[1] != 'X') \
371 || !RT_C_IS_XDIGIT(pszValue[2]) \
372 || convfn(pszValue, 16, &Value) != VINF_SUCCESS ) ) \
373 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
374 pValueUnion->memb = Value; \
375 break; \
376 }
377#define MY_BASE_INT_CASE(req,type,memb,convfn,base) \
378 case req: \
379 { \
380 type Value; \
381 if (convfn(pszValue, base, &Value) != VINF_SUCCESS) \
382 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
383 pValueUnion->memb = Value; \
384 break; \
385 }
386
387 MY_INT_CASE(RTGETOPT_REQ_INT8, int8_t, i, RTStrToInt8Full)
388 MY_INT_CASE(RTGETOPT_REQ_INT16, int16_t, i, RTStrToInt16Full)
389 MY_INT_CASE(RTGETOPT_REQ_INT32, int32_t, i, RTStrToInt32Full)
390 MY_INT_CASE(RTGETOPT_REQ_INT64, int64_t, i, RTStrToInt64Full)
391 MY_INT_CASE(RTGETOPT_REQ_UINT8, uint8_t, u, RTStrToUInt8Full)
392 MY_INT_CASE(RTGETOPT_REQ_UINT16, uint16_t, u, RTStrToUInt16Full)
393 MY_INT_CASE(RTGETOPT_REQ_UINT32, uint32_t, u, RTStrToUInt32Full)
394 MY_INT_CASE(RTGETOPT_REQ_UINT64, uint64_t, u, RTStrToUInt64Full)
395
396 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_HEX, int8_t, i, RTStrToInt8Full, 16)
397 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_HEX, int16_t, i, RTStrToInt16Full, 16)
398 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_HEX, int32_t, i, RTStrToInt32Full, 16)
399 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_HEX, int64_t, i, RTStrToInt64Full, 16)
400 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_HEX, uint8_t, u, RTStrToUInt8Full, 16)
401 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_HEX, uint16_t, u, RTStrToUInt16Full, 16)
402 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX, uint32_t, u, RTStrToUInt32Full, 16)
403 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX, uint64_t, u, RTStrToUInt64Full, 16)
404
405 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_DEC, int8_t, i, RTStrToInt8Full, 10)
406 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_DEC, int16_t, i, RTStrToInt16Full, 10)
407 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_DEC, int32_t, i, RTStrToInt32Full, 10)
408 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_DEC, int64_t, i, RTStrToInt64Full, 10)
409 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_DEC, uint8_t, u, RTStrToUInt8Full, 10)
410 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_DEC, uint16_t, u, RTStrToUInt16Full, 10)
411 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_DEC, uint32_t, u, RTStrToUInt32Full, 10)
412 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_DEC, uint64_t, u, RTStrToUInt64Full, 10)
413
414 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_OCT, int8_t, i, RTStrToInt8Full, 8)
415 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_OCT, int16_t, i, RTStrToInt16Full, 8)
416 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_OCT, int32_t, i, RTStrToInt32Full, 8)
417 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_OCT, int64_t, i, RTStrToInt64Full, 8)
418 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_OCT, uint8_t, u, RTStrToUInt8Full, 8)
419 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_OCT, uint16_t, u, RTStrToUInt16Full, 8)
420 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT, uint32_t, u, RTStrToUInt32Full, 8)
421 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_OCT, uint64_t, u, RTStrToUInt64Full, 8)
422
423#undef MY_INT_CASE
424#undef MY_BASE_INT_CASE
425
426 case RTGETOPT_REQ_IPV4ADDR:
427 {
428 RTNETADDRIPV4 Addr;
429 if (rtgetoptConvertIPv4Addr(pszValue, &Addr) != VINF_SUCCESS)
430 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
431 pValueUnion->IPv4Addr = Addr;
432 break;
433 }
434#if 0 /** @todo CIDR */
435#endif
436
437 case RTGETOPT_REQ_MACADDR:
438 {
439 RTMAC Addr;
440 if (rtgetoptConvertMacAddr(pszValue, &Addr) != VINF_SUCCESS)
441 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
442 pValueUnion->MacAddr = Addr;
443 break;
444 }
445
446 case RTGETOPT_REQ_UUID:
447 {
448 RTUUID Uuid;
449 if (RTUuidFromStr(&Uuid, pszValue) != VINF_SUCCESS)
450 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
451 pValueUnion->Uuid = Uuid;
452 break;
453 }
454
455 default:
456 AssertMsgFailed(("i=%d f=%#x\n", pOpt - &pState->paOptions[0], pOpt->fFlags));
457 return VERR_INTERNAL_ERROR;
458 }
459 }
460 else if (fShort)
461 {
462 /*
463 * Deal with "compressed" short option lists, correcting the next
464 * state variables for the start and end cases.
465 */
466 if (pszArgThis[2])
467 {
468 if (!pState->pszNextShort)
469 {
470 /* start */
471 pState->pszNextShort = &pszArgThis[2];
472 pState->iNext--;
473 }
474 }
475 else if (pState->pszNextShort)
476 {
477 /* end */
478 pState->pszNextShort = NULL;
479 pState->iNext++;
480 }
481 }
482
483 pState->pDef = pOpt;
484 return pOpt->iShort;
485 }
486
487 /*
488 * Not a known option argument. If it starts with a switch char (-) we'll
489 * fail with unkown option, and if it doesn't we'll return it as a non-option.
490 */
491
492 if (*pszArgThis == '-')
493 {
494 pValueUnion->psz = pszArgThis;
495 return VERR_GETOPT_UNKNOWN_OPTION;
496 }
497
498 pValueUnion->psz = pszArgThis;
499 return VINF_GETOPT_NOT_OPTION;
500}
501RT_EXPORT_SYMBOL(RTGetOpt);
502
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