source: mainline/uspace/lib/c/generic/getopt.c@ fcab7ef

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fcab7ef was 193d280c, checked in by Martin Decky <martin@…>, 10 years ago

cstyle improvements
replace traditional K&R-style function declarations and definitions

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/* $NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $ */
2
3/*-
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Dieter Baron and Thomas Klausner.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32/* Ported to HelenOS August 2008 by Tim Post <echo@echoreply.us> */
33
34#include <assert.h>
35#include <stdarg.h>
36#include <err.h>
37#include <errno.h>
38#include <getopt.h>
39#include <stdlib.h>
40#include <str.h>
41
42/* HelenOS Port : We're incorporating only the modern getopt_long with wrappers
43 * to keep legacy getopt() usage from breaking. All references to REPLACE_GETOPT
44 * are dropped, we just include the code */
45
46int opterr = 1; /* if error message should be printed */
47int optind = 1; /* index into parent argv vector */
48int optopt = '?'; /* character checked for validity */
49int optreset; /* reset getopt */
50const char *optarg; /* argument associated with option */
51
52
53#define IGNORE_FIRST (*options == '-' || *options == '+')
54#define PRINT_ERROR ((opterr) && ((*options != ':') \
55 || (IGNORE_FIRST && options[1] != ':')))
56/*HelenOS Port - POSIXLY_CORRECT is always false */
57#define IS_POSIXLY_CORRECT 0
58#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
59/* XXX: GNU ignores PC if *options == '-' */
60#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
61
62/* return values */
63#define BADCH (int)'?'
64#define BADARG ((IGNORE_FIRST && options[1] == ':') \
65 || (*options == ':') ? (int)':' : (int)'?')
66#define INORDER (int)1
67
68#define EMSG ""
69
70static int getopt_internal(int, char **, const char *);
71static int gcd(int, int);
72static void permute_args(int, int, int, char **);
73
74static const char *place = EMSG; /* option letter processing */
75
76/* XXX: set optreset to 1 rather than these two */
77static int nonopt_start = -1; /* first non option argument (for permute) */
78static int nonopt_end = -1; /* first option after non options (for permute) */
79
80/* Error messages */
81
82/* HelenOS Port: Calls to warnx() were eliminated (as we have no stderr that
83 * may be redirected) and replaced with printf. As such, error messages now
84 * end in a newline */
85
86static const char recargchar[] = "option requires an argument -- %c\n";
87static const char recargstring[] = "option requires an argument -- %s\n";
88static const char ambig[] = "ambiguous option -- %.*s\n";
89static const char noarg[] = "option doesn't take an argument -- %.*s\n";
90static const char illoptchar[] = "unknown option -- %c\n";
91static const char illoptstring[] = "unknown option -- %s\n";
92
93
94/*
95 * Compute the greatest common divisor of a and b.
96 */
97static int gcd(int a, int b)
98{
99 int c;
100
101 c = a % b;
102 while (c != 0) {
103 a = b;
104 b = c;
105 c = a % b;
106 }
107
108 return b;
109}
110
111/*
112 * Exchange the block from nonopt_start to nonopt_end with the block
113 * from nonopt_end to opt_end (keeping the same order of arguments
114 * in each block).
115 */
116static void permute_args(int panonopt_start, int panonopt_end, int opt_end,
117 char **nargv)
118{
119 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
120 char *swap;
121
122 assert(nargv != NULL);
123
124 /*
125 * compute lengths of blocks and number and size of cycles
126 */
127 nnonopts = panonopt_end - panonopt_start;
128 nopts = opt_end - panonopt_end;
129 ncycle = gcd(nnonopts, nopts);
130 cyclelen = (opt_end - panonopt_start) / ncycle;
131
132 for (i = 0; i < ncycle; i++) {
133 cstart = panonopt_end+i;
134 pos = cstart;
135 for (j = 0; j < cyclelen; j++) {
136 if (pos >= panonopt_end)
137 pos -= nnonopts;
138 else
139 pos += nopts;
140 swap = nargv[pos];
141 nargv[pos] = nargv[cstart];
142 nargv[cstart] = swap;
143 }
144 }
145}
146
147/*
148 * getopt_internal --
149 * Parse argc/argv argument vector. Called by user level routines.
150 * Returns -2 if -- is found (can be long option or end of options marker).
151 */
152static int getopt_internal(int nargc, char **nargv, const char *options)
153{
154 const char *oli; /* option letter list index */
155 int optchar;
156
157 assert(nargv != NULL);
158 assert(options != NULL);
159
160 optarg = NULL;
161
162 /*
163 * XXX Some programs (like rsyncd) expect to be able to
164 * XXX re-initialize optind to 0 and have getopt_long(3)
165 * XXX properly function again. Work around this braindamage.
166 */
167 if (optind == 0)
168 optind = 1;
169
170 if (optreset)
171 nonopt_start = nonopt_end = -1;
172start:
173 if (optreset || !*place) { /* update scanning pointer */
174 optreset = 0;
175 if (optind >= nargc) { /* end of argument vector */
176 place = EMSG;
177 if (nonopt_end != -1) {
178 /* do permutation, if we have to */
179 permute_args(nonopt_start, nonopt_end,
180 optind, nargv);
181 optind -= nonopt_end - nonopt_start;
182 }
183 else if (nonopt_start != -1) {
184 /*
185 * If we skipped non-options, set optind
186 * to the first of them.
187 */
188 optind = nonopt_start;
189 }
190 nonopt_start = nonopt_end = -1;
191 return -1;
192 }
193 if ((*(place = nargv[optind]) != '-')
194 || (place[1] == '\0')) { /* found non-option */
195 place = EMSG;
196 if (IN_ORDER) {
197 /*
198 * GNU extension:
199 * return non-option as argument to option 1
200 */
201 optarg = nargv[optind++];
202 return INORDER;
203 }
204 if (!PERMUTE) {
205 /*
206 * if no permutation wanted, stop parsing
207 * at first non-option
208 */
209 return -1;
210 }
211 /* do permutation */
212 if (nonopt_start == -1)
213 nonopt_start = optind;
214 else if (nonopt_end != -1) {
215 permute_args(nonopt_start, nonopt_end,
216 optind, nargv);
217 nonopt_start = optind -
218 (nonopt_end - nonopt_start);
219 nonopt_end = -1;
220 }
221 optind++;
222 /* process next argument */
223 goto start;
224 }
225 if (nonopt_start != -1 && nonopt_end == -1)
226 nonopt_end = optind;
227 if (place[1] && *++place == '-') { /* found "--" */
228 place++;
229 return -2;
230 }
231 }
232 if ((optchar = (int)*place++) == (int)':' ||
233 (oli = str_chr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
234 /* option letter unknown or ':' */
235 if (!*place)
236 ++optind;
237 if (PRINT_ERROR)
238 printf(illoptchar, optchar);
239 optopt = optchar;
240 return BADCH;
241 }
242 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
243 /* XXX: what if no long options provided (called by getopt)? */
244 if (*place)
245 return -2;
246
247 if (++optind >= nargc) { /* no arg */
248 place = EMSG;
249 if (PRINT_ERROR)
250 printf(recargchar, optchar);
251 optopt = optchar;
252 return BADARG;
253 } else /* white space */
254 place = nargv[optind];
255 /*
256 * Handle -W arg the same as --arg (which causes getopt to
257 * stop parsing).
258 */
259 return -2;
260 }
261 if (*++oli != ':') { /* doesn't take argument */
262 if (!*place)
263 ++optind;
264 } else { /* takes (optional) argument */
265 optarg = NULL;
266 if (*place) /* no white space */
267 optarg = place;
268 /* XXX: disable test for :: if PC? (GNU doesn't) */
269 else if (oli[1] != ':') { /* arg not optional */
270 if (++optind >= nargc) { /* no arg */
271 place = EMSG;
272 if (PRINT_ERROR)
273 printf(recargchar, optchar);
274 optopt = optchar;
275 return BADARG;
276 } else
277 optarg = nargv[optind];
278 }
279 place = EMSG;
280 ++optind;
281 }
282 /* dump back option letter */
283 return optchar;
284}
285
286/*
287 * getopt --
288 * Parse argc/argv argument vector.
289 */
290int getopt(int nargc, char * const *nargv, const char *options)
291{
292 int retval;
293
294 assert(nargv != NULL);
295 assert(options != NULL);
296
297 retval = getopt_internal(nargc, (char **)nargv, options);
298 if (retval == -2) {
299 ++optind;
300 /*
301 * We found an option (--), so if we skipped non-options,
302 * we have to permute.
303 */
304 if (nonopt_end != -1) {
305 permute_args(nonopt_start, nonopt_end, optind,
306 (char **)nargv);
307 optind -= nonopt_end - nonopt_start;
308 }
309 nonopt_start = nonopt_end = -1;
310 retval = -1;
311 }
312 return retval;
313}
314
315/*
316 * getopt_long --
317 * Parse argc/argv argument vector.
318 */
319int getopt_long(int nargc, char * const *nargv, const char *options,
320 const struct option *long_options, int *idx)
321{
322 int retval;
323
324#define IDENTICAL_INTERPRETATION(_x, _y) \
325 (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
326 long_options[(_x)].flag == long_options[(_y)].flag && \
327 long_options[(_x)].val == long_options[(_y)].val)
328
329 assert(nargv != NULL);
330 assert(options != NULL);
331 assert(long_options != NULL);
332 /* idx may be NULL */
333
334 retval = getopt_internal(nargc, (char **)nargv, options);
335 if (retval == -2) {
336 char *current_argv;
337 const char *has_equal;
338 size_t current_argv_len;
339 int i, ambiguous, match;
340
341 current_argv = (char *)place;
342 match = -1;
343 ambiguous = 0;
344
345 optind++;
346 place = EMSG;
347
348 if (*current_argv == '\0') { /* found "--" */
349 /*
350 * We found an option (--), so if we skipped
351 * non-options, we have to permute.
352 */
353 if (nonopt_end != -1) {
354 permute_args(nonopt_start, nonopt_end,
355 optind, (char **)nargv);
356 optind -= nonopt_end - nonopt_start;
357 }
358 nonopt_start = nonopt_end = -1;
359 return -1;
360 }
361 if ((has_equal = str_chr(current_argv, '=')) != NULL) {
362 /* argument found (--option=arg) */
363 current_argv_len = has_equal - current_argv;
364 has_equal++;
365 } else
366 current_argv_len = str_size(current_argv);
367
368 for (i = 0; long_options[i].name; i++) {
369 /* find matching long option */
370 if (str_lcmp(current_argv, long_options[i].name,
371 str_nlength(current_argv, current_argv_len)))
372 continue;
373
374 if (str_size(long_options[i].name) ==
375 (unsigned)current_argv_len) {
376 /* exact match */
377 match = i;
378 ambiguous = 0;
379 break;
380 }
381 if (match == -1) /* partial match */
382 match = i;
383 else if (!IDENTICAL_INTERPRETATION(i, match))
384 ambiguous = 1;
385 }
386 if (ambiguous) {
387 /* ambiguous abbreviation */
388 if (PRINT_ERROR)
389 printf(ambig, (int)current_argv_len,
390 current_argv);
391 optopt = 0;
392 return BADCH;
393 }
394 if (match != -1) { /* option found */
395 if (long_options[match].has_arg == no_argument
396 && has_equal) {
397 if (PRINT_ERROR)
398 printf(noarg, (int)current_argv_len,
399 current_argv);
400 /*
401 * XXX: GNU sets optopt to val regardless of
402 * flag
403 */
404 if (long_options[match].flag == NULL)
405 optopt = long_options[match].val;
406 else
407 optopt = 0;
408 return BADARG;
409 }
410 if (long_options[match].has_arg == required_argument ||
411 long_options[match].has_arg == optional_argument) {
412 if (has_equal)
413 optarg = has_equal;
414 else if (long_options[match].has_arg ==
415 required_argument) {
416 /*
417 * optional argument doesn't use
418 * next nargv
419 */
420 optarg = nargv[optind++];
421 }
422 }
423 if ((long_options[match].has_arg == required_argument)
424 && (optarg == NULL)) {
425 /*
426 * Missing argument; leading ':'
427 * indicates no error should be generated
428 */
429 if (PRINT_ERROR)
430 printf(recargstring, current_argv);
431 /*
432 * XXX: GNU sets optopt to val regardless
433 * of flag
434 */
435 if (long_options[match].flag == NULL)
436 optopt = long_options[match].val;
437 else
438 optopt = 0;
439 --optind;
440 return BADARG;
441 }
442 } else { /* unknown option */
443 if (PRINT_ERROR)
444 printf(illoptstring, current_argv);
445 optopt = 0;
446 return BADCH;
447 }
448 if (long_options[match].flag) {
449 *long_options[match].flag = long_options[match].val;
450 retval = 0;
451 } else
452 retval = long_options[match].val;
453 if (idx)
454 *idx = match;
455 }
456 return retval;
457#undef IDENTICAL_INTERPRETATION
458}
459
Note: See TracBrowser for help on using the repository browser.