VirtualBox

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

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

IPRT: Updated the RTGetOpt todos.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: getopt.cpp 17143 2009-02-25 17:14: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/getopt.h>
35#include <iprt/err.h>
36#include <iprt/string.h>
37#include <iprt/assert.h>
38#include <iprt/ctype.h>
39
40
41
42RTDECL(int) RTGetOptInit(PRTGETOPTSTATE pState, int argc, char **argv,
43 PCRTGETOPTDEF paOptions, size_t cOptions,
44 int iFirst, uint32_t fFlags)
45{
46 AssertReturn(!fFlags, VERR_INVALID_PARAMETER);
47
48 pState->argv = argv;
49 pState->argc = argc;
50 pState->paOptions = paOptions;
51 pState->cOptions = cOptions;
52 pState->iNext = iFirst;
53 pState->pszNextShort = NULL;
54
55 /* validate the options. */
56 for (size_t i = 0; i < cOptions; i++)
57 {
58 Assert(!(paOptions[i].fFlags & ~RTGETOPT_VALID_MASK));
59 Assert(paOptions[i].iShort > 0);
60 Assert(paOptions[i].iShort != VINF_GETOPT_NOT_OPTION);
61 Assert(paOptions[i].iShort != '-');
62 }
63
64 /** @todo Add an flag for sorting the arguments so that all the options comes
65 * first. */
66 return VINF_SUCCESS;
67}
68
69
70/**
71 * Searches for a long option.
72 *
73 * @returns Pointer to a matching option.
74 * @param pszOption The alleged long option.
75 * @param paOptions Option array.
76 * @param cOptions Number of items in the array.
77 */
78static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions)
79{
80 PCRTGETOPTDEF pOpt = paOptions;
81 while (cOptions-- > 0)
82 {
83 if (pOpt->pszLong)
84 {
85 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
86 {
87 /*
88 * A value is required with the argument. We're trying to be very
89 * understanding here and will permit any of the following:
90 * --long:value, --long=value, --long value,
91 * --long: value, --long= value
92 */
93 size_t cchLong = strlen(pOpt->pszLong);
94 if ( !strncmp(pszOption, pOpt->pszLong, cchLong)
95 && ( pszOption[cchLong] == '\0'
96 || pszOption[cchLong] == ':'
97 || pszOption[cchLong] == '='))
98 return pOpt;
99 }
100 else if (!strcmp(pszOption, pOpt->pszLong))
101 return pOpt;
102 }
103 pOpt++;
104 }
105 return NULL;
106}
107
108
109/**
110 * Searches for a matching short option.
111 *
112 * @returns Pointer to a matching option.
113 * @param chOption The option char.
114 * @param paOptions Option array.
115 * @param cOptions Number of items in the array.
116 */
117static PCRTGETOPTDEF rtGetOptSearchShort(int chOption, PCRTGETOPTDEF paOptions, size_t cOptions)
118{
119 PCRTGETOPTDEF pOpt = paOptions;
120 while (cOptions-- > 0)
121 {
122 if (pOpt->iShort == chOption)
123 return pOpt;
124 pOpt++;
125 }
126 return NULL;
127}
128
129
130RTDECL(int) RTGetOpt(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion)
131{
132 /*
133 * Make sure the union is completely cleared out, whatever happens below.
134 */
135 pValueUnion->u64 = 0;
136 pValueUnion->pDef = NULL;
137
138 /** @todo Handle '--' (end of options).*/
139 /** @todo Add a flag to RTGetOptInit for handling the various help options in
140 * a common way. (-?,-h,-help,--help,++) */
141 /** @todo Add a flag to RTGetOptInit for handling the standard version options
142 * in a common way. (-V,--version) */
143
144 /*
145 * The next option.
146 */
147 bool fShort;
148 int iThis;
149 const char *pszArgThis;
150 PCRTGETOPTDEF pOpt;
151
152 if (pState->pszNextShort)
153 {
154 /*
155 * We've got short options left over from the previous call.
156 */
157 pOpt = rtGetOptSearchShort(*pState->pszNextShort, pState->paOptions, pState->cOptions);
158 if (!pOpt)
159 {
160 pValueUnion->psz = pState->pszNextShort;
161 return VERR_GETOPT_UNKNOWN_OPTION;
162 }
163 pState->pszNextShort++;
164 pszArgThis = pState->pszNextShort - 2;
165 iThis = pState->iNext;
166 fShort = true;
167 }
168 else
169 {
170 /*
171 * Pop off the next argument.
172 */
173 if (pState->iNext >= pState->argc)
174 return 0;
175 iThis = pState->iNext++;
176 pszArgThis = pState->argv[iThis];
177
178 /*
179 * Do a long option search first and the a short option one.
180 * This way we can make sure single dash long options doesn't
181 * get mixed up with short ones.
182 */
183 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions);
184 if ( !pOpt
185 && pszArgThis[0] == '-'
186 && pszArgThis[1] != '-'
187 && pszArgThis[1] != '\0')
188 {
189 pOpt = rtGetOptSearchShort(pszArgThis[1], pState->paOptions, pState->cOptions);
190 fShort = pOpt != NULL;
191 }
192 else
193 fShort = false;
194 }
195
196 if (pOpt)
197 {
198 pValueUnion->pDef = pOpt; /* in case of no value or error. */
199
200 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
201 {
202 /*
203 * Find the argument value.
204 *
205 * A value is required with the argument. We're trying to be very
206 * understanding here and will permit any of the following:
207 * -svalue, -s:value, -s=value,
208 * -s value, -s: value, -s= value
209 * (Ditto for long options.)
210 */
211 const char *pszValue;
212 if (fShort)
213 {
214 if ( pszArgThis[2] == '\0'
215 || ( pszArgThis[3] == '\0'
216 && ( pszArgThis[2] == ':'
217 || pszArgThis[2] == '=')) )
218 {
219 if (iThis + 1 >= pState->argc)
220 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
221 pszValue = pState->argv[iThis + 1];
222 pState->iNext++;
223 }
224 else /* same argument. */
225 pszValue = &pszArgThis[2 + (pszArgThis[2] == ':' || pszArgThis[2] == '=')];
226 if (pState->pszNextShort)
227 {
228 pState->pszNextShort = NULL;
229 pState->iNext++;
230 }
231 }
232 else
233 {
234 size_t cchLong = strlen(pOpt->pszLong);
235 if ( pszArgThis[cchLong] == '\0'
236 || pszArgThis[cchLong + 1] == '\0')
237 {
238 if (iThis + 1 >= pState->argc)
239 return VERR_GETOPT_REQUIRED_ARGUMENT_MISSING;
240 pszValue = pState->argv[iThis + 1];
241 pState->iNext++;
242 }
243 else /* same argument. */
244 pszValue = &pszArgThis[cchLong + 1];
245 }
246
247 /*
248 * Transform into a option value as requested.
249 * If decimal conversion fails, we'll check for "0x<xdigit>" and
250 * try a 16 based conversion. We will not interpret any of the
251 * generic ints as octals.
252 */
253 switch (pOpt->fFlags & (RTGETOPT_REQ_MASK | RTGETOPT_FLAG_HEX | RTGETOPT_FLAG_OCT | RTGETOPT_FLAG_DEC))
254 {
255 case RTGETOPT_REQ_STRING:
256 pValueUnion->psz = pszValue;
257 break;
258
259#define MY_INT_CASE(req,type,memb,convfn) \
260 case req: \
261 { \
262 type Value; \
263 if ( convfn(pszValue, 10, &Value) != VINF_SUCCESS \
264 && ( pszValue[0] != '0' \
265 || (pszValue[1] != 'x' && pszValue[1] != 'X') \
266 || !RT_C_IS_XDIGIT(pszValue[2]) \
267 || convfn(pszValue, 16, &Value) != VINF_SUCCESS ) ) \
268 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
269 pValueUnion->memb = Value; \
270 break; \
271 }
272#define MY_BASE_INT_CASE(req,type,memb,convfn,base) \
273 case req: \
274 { \
275 type Value; \
276 if (convfn(pszValue, base, &Value) != VINF_SUCCESS) \
277 return VERR_GETOPT_INVALID_ARGUMENT_FORMAT; \
278 pValueUnion->memb = Value; \
279 break; \
280 }
281
282 MY_INT_CASE(RTGETOPT_REQ_INT8, int8_t, i, RTStrToInt8Full)
283 MY_INT_CASE(RTGETOPT_REQ_INT16, int16_t, i, RTStrToInt16Full)
284 MY_INT_CASE(RTGETOPT_REQ_INT32, int32_t, i, RTStrToInt32Full)
285 MY_INT_CASE(RTGETOPT_REQ_INT64, int64_t, i, RTStrToInt64Full)
286 MY_INT_CASE(RTGETOPT_REQ_UINT8, uint8_t, u, RTStrToUInt8Full)
287 MY_INT_CASE(RTGETOPT_REQ_UINT16, uint16_t, u, RTStrToUInt16Full)
288 MY_INT_CASE(RTGETOPT_REQ_UINT32, uint32_t, u, RTStrToUInt32Full)
289 MY_INT_CASE(RTGETOPT_REQ_UINT64, uint64_t, u, RTStrToUInt64Full)
290
291 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_HEX, int8_t, i, RTStrToInt8Full, 16)
292 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_HEX, int16_t, i, RTStrToInt16Full, 16)
293 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_HEX, int32_t, i, RTStrToInt32Full, 16)
294 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_HEX, int64_t, i, RTStrToInt64Full, 16)
295 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_HEX, uint8_t, u, RTStrToUInt8Full, 16)
296 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_HEX, uint16_t, u, RTStrToUInt16Full, 16)
297 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_HEX, uint32_t, u, RTStrToUInt32Full, 16)
298 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_HEX, uint64_t, u, RTStrToUInt64Full, 16)
299
300 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_DEC, int8_t, i, RTStrToInt8Full, 10)
301 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_DEC, int16_t, i, RTStrToInt16Full, 10)
302 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_DEC, int32_t, i, RTStrToInt32Full, 10)
303 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_DEC, int64_t, i, RTStrToInt64Full, 10)
304 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_DEC, uint8_t, u, RTStrToUInt8Full, 10)
305 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_DEC, uint16_t, u, RTStrToUInt16Full, 10)
306 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_DEC, uint32_t, u, RTStrToUInt32Full, 10)
307 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_DEC, uint64_t, u, RTStrToUInt64Full, 10)
308
309 MY_BASE_INT_CASE(RTGETOPT_REQ_INT8 | RTGETOPT_FLAG_OCT, int8_t, i, RTStrToInt8Full, 8)
310 MY_BASE_INT_CASE(RTGETOPT_REQ_INT16 | RTGETOPT_FLAG_OCT, int16_t, i, RTStrToInt16Full, 8)
311 MY_BASE_INT_CASE(RTGETOPT_REQ_INT32 | RTGETOPT_FLAG_OCT, int32_t, i, RTStrToInt32Full, 8)
312 MY_BASE_INT_CASE(RTGETOPT_REQ_INT64 | RTGETOPT_FLAG_OCT, int64_t, i, RTStrToInt64Full, 8)
313 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT8 | RTGETOPT_FLAG_OCT, uint8_t, u, RTStrToUInt8Full, 8)
314 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT16 | RTGETOPT_FLAG_OCT, uint16_t, u, RTStrToUInt16Full, 8)
315 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT, uint32_t, u, RTStrToUInt32Full, 8)
316 MY_BASE_INT_CASE(RTGETOPT_REQ_UINT64 | RTGETOPT_FLAG_OCT, uint64_t, u, RTStrToUInt64Full, 8)
317#undef MY_INT_CASE
318#undef MY_BASE_INT_CASE
319
320 default:
321 AssertMsgFailed(("i=%d f=%#x\n", pOpt - &pState->paOptions[0], pOpt->fFlags));
322 return VERR_INTERNAL_ERROR;
323 }
324 }
325 else if (fShort)
326 {
327 /*
328 * Deal with "compressed" short option lists, correcting the next
329 * state variables for the start and end cases.
330 */
331 if (pszArgThis[2])
332 {
333 if (!pState->pszNextShort)
334 {
335 /* start */
336 pState->pszNextShort = &pszArgThis[2];
337 pState->iNext--;
338 }
339 }
340 else if (pState->pszNextShort)
341 {
342 /* end */
343 pState->pszNextShort = NULL;
344 pState->iNext++;
345 }
346 }
347
348 return pOpt->iShort;
349 }
350
351 /*
352 * Not a known option argument. If it starts with a switch char (-) we'll
353 * fail with unkown option, and if it doesn't we'll return it as a non-option.
354 */
355
356 if (*pszArgThis == '-')
357 {
358 pValueUnion->psz = pszArgThis;
359 return VERR_GETOPT_UNKNOWN_OPTION;
360 }
361
362 pValueUnion->psz = pszArgThis;
363 return VINF_GETOPT_NOT_OPTION;
364}
365
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