VirtualBox

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

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

IPRT: Added support for short option lists (ls -latrT4). This fixes a bug in the short option without values, where we didn't check that the following char was the string terminator.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.1 KB
Line 
1/* $Id: getopt.cpp 17141 2009-02-25 17:09:22Z 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 return VINF_SUCCESS;
65}
66
67
68/**
69 * Searches for a long option.
70 *
71 * @returns Pointer to a matching option.
72 * @param pszOption The alleged long option.
73 * @param paOptions Option array.
74 * @param cOptions Number of items in the array.
75 */
76static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions)
77{
78 PCRTGETOPTDEF pOpt = paOptions;
79 while (cOptions-- > 0)
80 {
81 if (pOpt->pszLong)
82 {
83 if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
84 {
85 /*
86 * A value is required with the argument. We're trying to be very
87 * understanding here and will permit any of the following:
88 * --long:value, --long=value, --long value,
89 * --long: value, --long= value
90 */
91 size_t cchLong = strlen(pOpt->pszLong);
92 if ( !strncmp(pszOption, pOpt->pszLong, cchLong)
93 && ( pszOption[cchLong] == '\0'
94 || pszOption[cchLong] == ':'
95 || pszOption[cchLong] == '='))
96 return pOpt;
97 }
98 else if (!strcmp(pszOption, pOpt->pszLong))
99 return pOpt;
100 }
101 pOpt++;
102 }
103 return NULL;
104}
105
106
107/**
108 * Searches for a matching short option.
109 *
110 * @returns Pointer to a matching option.
111 * @param chOption The option char.
112 * @param paOptions Option array.
113 * @param cOptions Number of items in the array.
114 */
115static PCRTGETOPTDEF rtGetOptSearchShort(int chOption, PCRTGETOPTDEF paOptions, size_t cOptions)
116{
117 PCRTGETOPTDEF pOpt = paOptions;
118 while (cOptions-- > 0)
119 {
120 if (pOpt->iShort == chOption)
121 return pOpt;
122 pOpt++;
123 }
124 return NULL;
125}
126
127
128RTDECL(int) RTGetOpt(PRTGETOPTSTATE pState, PRTGETOPTUNION pValueUnion)
129{
130 /*
131 * Make sure the union is completely cleared out, whatever happens below.
132 */
133 pValueUnion->u64 = 0;
134 pValueUnion->pDef = NULL;
135
136 /*
137 * The next option.
138 */
139 bool fShort;
140 int iThis;
141 const char *pszArgThis;
142 PCRTGETOPTDEF pOpt;
143
144 if (pState->pszNextShort)
145 {
146 /*
147 * We've got short options left over from the previous call.
148 */
149 pOpt = rtGetOptSearchShort(*pState->pszNextShort, pState->paOptions, pState->cOptions);
150 if (!pOpt)
151 {
152 pValueUnion->psz = pState->pszNextShort;
153 return VERR_GETOPT_UNKNOWN_OPTION;
154 }
155 pState->pszNextShort++;
156 pszArgThis = pState->pszNextShort - 2;
157 iThis = pState->iNext;
158 fShort = true;
159 }
160 else
161 {
162 /*
163 * Pop off the next argument.
164 */
165 if (pState->iNext >= pState->argc)
166 return 0;
167 iThis = pState->iNext++;
168 pszArgThis = pState->argv[iThis];
169
170 /*
171 * Do a long option search first and the a short option one.
172 * This way we can make sure single dash long options doesn't
173 * get mixed up with short ones.
174 */
175 pOpt = rtGetOptSearchLong(pszArgThis, pState->paOptions, pState->cOptions);
176 if ( !pOpt
177 && pszArgThis[0] == '-'
178 && pszArgThis[1] != '-'
179 && pszArgThis[1] != '\0')
180 {
181 pOpt = rtGetOptSearchShort(pszArgThis[1], pState->paOptions, pState->cOptions);
182 fShort = pOpt != NULL;
183 }
184 else
185 fShort = false;
186 }
187
188
189 /** @todo Handle '--' and possibly implement an RTGetOptInit that lets us
190 * optionally sort the stuff and set other policeis sorts the result. */
191 /** @todo Implement short form short option like in:
192 * ls -latr .
193 * tar -xzvf foobar.tar.gz
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