VirtualBox

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

Last change on this file since 18188 was 18100, checked in by vboxsync, 16 years ago

typo

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