VirtualBox

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

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

IPRT: Added RTGETOPT_REQ_IPV4ADDR to RTGetOpt.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 16.1 KB
Line 
1/* $Id: getopt.cpp 17319 2009-03-04 03:18:37Z 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.215389.xyz. 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 * 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 * Searches for a long option.
116 *
117 * @returns Pointer to a matching option.
118 * @param pszOption The alleged long option.
119 * @param paOptions Option array.
120 * @param cOptions Number of items in the array.
121 */
122static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions)
123{
124 PCRTGETOPTDEF pOpt = paOptions;
125 while (cOptions-- > 0)
126 {
127 if (pOpt->pszLong)
128 {
129 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
130 {
131 /*
132 * A value is required with the argument. We're trying to be very
133 * understanding here and will permit any of the following:
134 * --long:value, --long=value, --long value,
135 * --long: value, --long= value
136 */
137 size_t cchLong = strlen(pOpt->pszLong);
138 if ( !strncmp(pszOption, pOpt->pszLong, cchLong)
139 && ( pszOption[cchLong] == '\0'
140 || pszOption[cchLong] == ':'
141 || pszOption[cchLong] == '='))
142 return pOpt;
143 }
144 else if (!strcmp(pszOption, pOpt->pszLong))
145 return pOpt;
146 }
147 pOpt++;
148 }
149 return NULL;
150}
151
152
153/**
154 * Searches for a matching short option.
155 *
156 * @returns Pointer to a matching option.
157 * @param chOption The option char.
158 * @param paOptions Option array.
159 * @param cOptions Number of items in the array.
160 */
161static PCRTGETOPTDEF rtGetOptSearchShort(int chOption, PCRTGETOPTDEF paOptions, size_t cOptions)
162{
163 PCRTGETOPTDEF pOpt = paOptions;
164 while (cOptions-- > 0)
165 {
166 if (pOpt->iShort == chOption)
167 return pOpt;
168 pOpt++;
169 }
170 return NULL;
171}
172
173
174RTDECL(int) RTGetOpt(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion)
175{
176 /*
177 * Make sure the union is completely cleared out, whatever happens below.
178 */
179 pValueUnion->u64 = 0;
180 pValueUnion->pDef = NULL;
181
182 /** @todo Handle '--' (end of options).*/
183 /** @todo Add a flag to RTGetOptInit for handling the various help options in
184 * a common way. (-?,-h,-help,--help,++) */
185 /** @todo Add a flag to RTGetOptInit for handling the standard version options
186 * in a common way. (-V,--version) */
187
188 /*
189 * The next option.
190 */
191 bool fShort;
192 int iThis;
193 const char *pszArgThis;
194 PCRTGETOPTDEF pOpt;
195
196 if (pState->pszNextShort)
197 {
198 /*
199 * We've got short options left over from the previous call.
200 */
201 pOpt = rtGetOptSearchShort(*pState->pszNextShort, pState->paOptions, pState->cOptions);
202 if (!pOpt)
203 {
204 pValueUnion->psz = pState->pszNextShort;
205 return VERR_GETOPT_UNKNOWN_OPTION;
206 }
207 pState->pszNextShort++;
208 pszArgThis = pState->pszNextShort - 2;
209 iThis = pState->iNext;
210 fShort = true;
211 }
212 else
213 {
214 /*
215 * Pop off the next argument.
216 */
217 if (pState->iNext >= pState->argc)
218 return 0;
219 iThis = pState->iNext++;
220 pszArgThis = pState->argv[iThis];
221
222 /*
223 * Do a long option search first and the a short option one.
224 * This way we can make sure single dash long options doesn't
225 * get mixed up with short ones.
226 */
227 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions);
228 if ( !pOpt
229 && pszArgThis[0] == '-'
230 && pszArgThis[1] != '-'
231 && pszArgThis[1] != '\0')
232 {
233 pOpt = rtGetOptSearchShort(pszArgThis[1], pState->paOptions, pState->cOptions);
234 fShort = pOpt != NULL;
235 }
236 else
237 fShort = false;
238 }
239
240 if (pOpt)
241 {
242 pValueUnion->pDef = pOpt; /* in case of no value or error. */
243
244 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
245 {
246 /*
247 * Find the argument value.
248 *
249 * A value is required with the argument. We're trying to be very
250 * understanding here and will permit any of the following:
251 * -svalue, -s:value, -s=value,
252 * -s value, -s: value, -s= value
253 * (Ditto for long options.)
254 */
255 const char *pszValue;
256 if (fShort)
257 {
258 if ( pszArgThis[2] == '\0'
259 || ( pszArgThis[3] == '\0'
260 && ( pszArgThis[2] == ':'
261 || pszArgThis[2] == '=')) )
262 {
263 if (iThis + 1 >= pState->argc)
264 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
265 pszValue = pState->argv[iThis + 1];
266 pState->iNext++;
267 }
268 else /* same argument. */
269 pszValue = &pszArgThis[2 + (pszArgThis[2] == ':' || pszArgThis[2] == '=')];
270 if (pState->pszNextShort)
271 {
272 pState->pszNextShort = NULL;
273 pState->iNext++;
274 }
275 }
276 else
277 {
278 size_t cchLong = strlen(pOpt->pszLong);
279 if ( pszArgThis[cchLong] == '\0'
280 || pszArgThis[cchLong + 1] == '\0')
281 {
282 if (iThis + 1 >= pState->argc)
283 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
284 pszValue = pState->argv[iThis + 1];
285 pState->iNext++;
286 }
287 else /* same argument. */
288 pszValue = &pszArgThis[cchLong + 1];
289 }
290
291 /*
292 * Transform into a option value as requested.
293 * If decimal conversion fails, we'll check for "0x<xdigit>" and
294 * try a 16 based conversion. We will not interpret any of the
295 * generic ints as octals.
296 */
297 switch (pOpt->fFlags & (RTGETOPT_REQ_MASK | RTGETOPT_FLAG_HEX | RTGETOPT_FLAG_OCT | RTGETOPT_FLAG_DEC))
298 {
299 case RTGETOPT_REQ_STRING:
300 pValueUnion->psz = pszValue;
301 break;
302
303#define MY_INT_CASE(req,type,memb,convfn) \
304 case req: \
305 { \
306 type Value; \
307 if ( convfn(pszValue, 10, &Value) != VINF_SUCCESS \
308 && ( pszValue[0] != '0' \
309 || (pszValue[1] != 'x' && pszValue[1] != 'X') \
310 || !RT_C_IS_XDIGIT(pszValue[2]) \
311 || convfn(pszValue, 16, &Value) != VINF_SUCCESS ) ) \
312 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
313 pValueUnion->memb = Value; \
314 break; \
315 }
316#define MY_BASE_INT_CASE(req,type,memb,convfn,base) \
317 case req: \
318 { \
319 type Value; \
320 if (convfn(pszValue, base, &Value) != VINF_SUCCESS) \
321 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
322 pValueUnion->memb = Value; \
323 break; \
324 }
325
326 MY_INT_CASE(RTGETOPT_REQ_INT8, int8_t, i, RTStrToInt8Full)
327 MY_INT_CASE(RTGETOPT_REQ_INT16, int16_t, i, RTStrToInt16Full)
328 MY_INT_CASE(RTGETOPT_REQ_INT32, int32_t, i, RTStrToInt32Full)
329 MY_INT_CASE(RTGETOPT_REQ_INT64, int64_t, i, RTStrToInt64Full)
330 MY_INT_CASE(RTGETOPT_REQ_UINT8, uint8_t, u, RTStrToUInt8Full)
331 MY_INT_CASE(RTGETOPT_REQ_UINT16, uint16_t, u, RTStrToUInt16Full)
332 MY_INT_CASE(RTGETOPT_REQ_UINT32, uint32_t, u, RTStrToUInt32Full)
333 MY_INT_CASE(RTGETOPT_REQ_UINT64, uint64_t, u, RTStrToUInt64Full)
334
335 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_HEX, int8_t, i, RTStrToInt8Full, 16)
336 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_HEX, int16_t, i, RTStrToInt16Full, 16)
337 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_HEX, int32_t, i, RTStrToInt32Full, 16)
338 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_HEX, int64_t, i, RTStrToInt64Full, 16)
339 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_HEX, uint8_t, u, RTStrToUInt8Full, 16)
340 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_HEX, uint16_t, u, RTStrToUInt16Full, 16)
341 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX, uint32_t, u, RTStrToUInt32Full, 16)
342 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX, uint64_t, u, RTStrToUInt64Full, 16)
343
344 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_DEC, int8_t, i, RTStrToInt8Full, 10)
345 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_DEC, int16_t, i, RTStrToInt16Full, 10)
346 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_DEC, int32_t, i, RTStrToInt32Full, 10)
347 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_DEC, int64_t, i, RTStrToInt64Full, 10)
348 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_DEC, uint8_t, u, RTStrToUInt8Full, 10)
349 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_DEC, uint16_t, u, RTStrToUInt16Full, 10)
350 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_DEC, uint32_t, u, RTStrToUInt32Full, 10)
351 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_DEC, uint64_t, u, RTStrToUInt64Full, 10)
352
353 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_OCT, int8_t, i, RTStrToInt8Full, 8)
354 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_OCT, int16_t, i, RTStrToInt16Full, 8)
355 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_OCT, int32_t, i, RTStrToInt32Full, 8)
356 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_OCT, int64_t, i, RTStrToInt64Full, 8)
357 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_OCT, uint8_t, u, RTStrToUInt8Full, 8)
358 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_OCT, uint16_t, u, RTStrToUInt16Full, 8)
359 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT, uint32_t, u, RTStrToUInt32Full, 8)
360 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_OCT, uint64_t, u, RTStrToUInt64Full, 8)
361
362#undef MY_INT_CASE
363#undef MY_BASE_INT_CASE
364
365 case RTGETOPT_REQ_IPV4ADDR:
366 {
367 RTNETADDRIPV4 Addr;
368 if (rtgetoptConvertIPv4Addr(pszValue, &Addr) != VINF_SUCCESS)
369 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT;
370 pValueUnion->IPv4Addr = Addr;
371 break;
372 }
373
374 default:
375 AssertMsgFailed(("i=%d f=%#x\n", pOpt - &pState->paOptions[0], pOpt->fFlags));
376 return VERR_INTERNAL_ERROR;
377 }
378 }
379 else if (fShort)
380 {
381 /*
382 * Deal with "compressed" short option lists, correcting the next
383 * state variables for the start and end cases.
384 */
385 if (pszArgThis[2])
386 {
387 if (!pState->pszNextShort)
388 {
389 /* start */
390 pState->pszNextShort = &pszArgThis[2];
391 pState->iNext--;
392 }
393 }
394 else if (pState->pszNextShort)
395 {
396 /* end */
397 pState->pszNextShort = NULL;
398 pState->iNext++;
399 }
400 }
401
402 return pOpt->iShort;
403 }
404
405 /*
406 * Not a known option argument. If it starts with a switch char (-) we'll
407 * fail with unkown option, and if it doesn't we'll return it as a non-option.
408 */
409
410 if (*pszArgThis == '-')
411 {
412 pValueUnion->psz = pszArgThis;
413 return VERR_GETOPT_UNKNOWN_OPTION;
414 }
415
416 pValueUnion->psz = pszArgThis;
417 return VINF_GETOPT_NOT_OPTION;
418}
419
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette