source: mainline/uspace/lib/c/generic/getopt.c@ 76ca3f7

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 76ca3f7 was 63f8966, checked in by Martin Decky <martin@…>, 15 years ago

rename library directories (the common "lib" prefix is already in the upper directory)

  • Property mode set to 100644
File size: 12.4 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
98gcd(a, b)
99 int a;
100 int b;
101{
102 int c;
103
104 c = a % b;
105 while (c != 0) {
106 a = b;
107 b = c;
108 c = a % b;
109 }
110
111 return b;
112}
113
114/*
115 * Exchange the block from nonopt_start to nonopt_end with the block
116 * from nonopt_end to opt_end (keeping the same order of arguments
117 * in each block).
118 */
119static void
120permute_args(panonopt_start, panonopt_end, opt_end, nargv)
121 int panonopt_start;
122 int panonopt_end;
123 int opt_end;
124 char **nargv;
125{
126 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
127 char *swap;
128
129 assert(nargv != NULL);
130
131 /*
132 * compute lengths of blocks and number and size of cycles
133 */
134 nnonopts = panonopt_end - panonopt_start;
135 nopts = opt_end - panonopt_end;
136 ncycle = gcd(nnonopts, nopts);
137 cyclelen = (opt_end - panonopt_start) / ncycle;
138
139 for (i = 0; i < ncycle; i++) {
140 cstart = panonopt_end+i;
141 pos = cstart;
142 for (j = 0; j < cyclelen; j++) {
143 if (pos >= panonopt_end)
144 pos -= nnonopts;
145 else
146 pos += nopts;
147 swap = nargv[pos];
148 nargv[pos] = nargv[cstart];
149 nargv[cstart] = swap;
150 }
151 }
152}
153
154/*
155 * getopt_internal --
156 * Parse argc/argv argument vector. Called by user level routines.
157 * Returns -2 if -- is found (can be long option or end of options marker).
158 */
159static int
160getopt_internal(nargc, nargv, options)
161 int nargc;
162 char **nargv;
163 const char *options;
164{
165 const char *oli; /* option letter list index */
166 int optchar;
167
168 assert(nargv != NULL);
169 assert(options != NULL);
170
171 optarg = NULL;
172
173 /*
174 * XXX Some programs (like rsyncd) expect to be able to
175 * XXX re-initialize optind to 0 and have getopt_long(3)
176 * XXX properly function again. Work around this braindamage.
177 */
178 if (optind == 0)
179 optind = 1;
180
181 if (optreset)
182 nonopt_start = nonopt_end = -1;
183start:
184 if (optreset || !*place) { /* update scanning pointer */
185 optreset = 0;
186 if (optind >= nargc) { /* end of argument vector */
187 place = EMSG;
188 if (nonopt_end != -1) {
189 /* do permutation, if we have to */
190 permute_args(nonopt_start, nonopt_end,
191 optind, nargv);
192 optind -= nonopt_end - nonopt_start;
193 }
194 else if (nonopt_start != -1) {
195 /*
196 * If we skipped non-options, set optind
197 * to the first of them.
198 */
199 optind = nonopt_start;
200 }
201 nonopt_start = nonopt_end = -1;
202 return -1;
203 }
204 if ((*(place = nargv[optind]) != '-')
205 || (place[1] == '\0')) { /* found non-option */
206 place = EMSG;
207 if (IN_ORDER) {
208 /*
209 * GNU extension:
210 * return non-option as argument to option 1
211 */
212 optarg = nargv[optind++];
213 return INORDER;
214 }
215 if (!PERMUTE) {
216 /*
217 * if no permutation wanted, stop parsing
218 * at first non-option
219 */
220 return -1;
221 }
222 /* do permutation */
223 if (nonopt_start == -1)
224 nonopt_start = optind;
225 else if (nonopt_end != -1) {
226 permute_args(nonopt_start, nonopt_end,
227 optind, nargv);
228 nonopt_start = optind -
229 (nonopt_end - nonopt_start);
230 nonopt_end = -1;
231 }
232 optind++;
233 /* process next argument */
234 goto start;
235 }
236 if (nonopt_start != -1 && nonopt_end == -1)
237 nonopt_end = optind;
238 if (place[1] && *++place == '-') { /* found "--" */
239 place++;
240 return -2;
241 }
242 }
243 if ((optchar = (int)*place++) == (int)':' ||
244 (oli = str_chr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
245 /* option letter unknown or ':' */
246 if (!*place)
247 ++optind;
248 if (PRINT_ERROR)
249 printf(illoptchar, optchar);
250 optopt = optchar;
251 return BADCH;
252 }
253 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
254 /* XXX: what if no long options provided (called by getopt)? */
255 if (*place)
256 return -2;
257
258 if (++optind >= nargc) { /* no arg */
259 place = EMSG;
260 if (PRINT_ERROR)
261 printf(recargchar, optchar);
262 optopt = optchar;
263 return BADARG;
264 } else /* white space */
265 place = nargv[optind];
266 /*
267 * Handle -W arg the same as --arg (which causes getopt to
268 * stop parsing).
269 */
270 return -2;
271 }
272 if (*++oli != ':') { /* doesn't take argument */
273 if (!*place)
274 ++optind;
275 } else { /* takes (optional) argument */
276 optarg = NULL;
277 if (*place) /* no white space */
278 optarg = place;
279 /* XXX: disable test for :: if PC? (GNU doesn't) */
280 else if (oli[1] != ':') { /* arg not optional */
281 if (++optind >= nargc) { /* no arg */
282 place = EMSG;
283 if (PRINT_ERROR)
284 printf(recargchar, optchar);
285 optopt = optchar;
286 return BADARG;
287 } else
288 optarg = nargv[optind];
289 }
290 place = EMSG;
291 ++optind;
292 }
293 /* dump back option letter */
294 return optchar;
295}
296
297/*
298 * getopt --
299 * Parse argc/argv argument vector.
300 */
301int
302getopt(nargc, nargv, options)
303 int nargc;
304 char * const *nargv;
305 const char *options;
306{
307 int retval;
308
309 assert(nargv != NULL);
310 assert(options != NULL);
311
312 retval = getopt_internal(nargc, (char **)nargv, options);
313 if (retval == -2) {
314 ++optind;
315 /*
316 * We found an option (--), so if we skipped non-options,
317 * we have to permute.
318 */
319 if (nonopt_end != -1) {
320 permute_args(nonopt_start, nonopt_end, optind,
321 (char **)nargv);
322 optind -= nonopt_end - nonopt_start;
323 }
324 nonopt_start = nonopt_end = -1;
325 retval = -1;
326 }
327 return retval;
328}
329
330/*
331 * getopt_long --
332 * Parse argc/argv argument vector.
333 */
334int
335getopt_long(nargc, nargv, options, long_options, idx)
336 int nargc;
337 char * const *nargv;
338 const char *options;
339 const struct option *long_options;
340 int *idx;
341{
342 int retval;
343
344#define IDENTICAL_INTERPRETATION(_x, _y) \
345 (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
346 long_options[(_x)].flag == long_options[(_y)].flag && \
347 long_options[(_x)].val == long_options[(_y)].val)
348
349 assert(nargv != NULL);
350 assert(options != NULL);
351 assert(long_options != NULL);
352 /* idx may be NULL */
353
354 retval = getopt_internal(nargc, (char **)nargv, options);
355 if (retval == -2) {
356 char *current_argv;
357 const char *has_equal;
358 size_t current_argv_len;
359 int i, ambiguous, match;
360
361 current_argv = (char *)place;
362 match = -1;
363 ambiguous = 0;
364
365 optind++;
366 place = EMSG;
367
368 if (*current_argv == '\0') { /* found "--" */
369 /*
370 * We found an option (--), so if we skipped
371 * non-options, we have to permute.
372 */
373 if (nonopt_end != -1) {
374 permute_args(nonopt_start, nonopt_end,
375 optind, (char **)nargv);
376 optind -= nonopt_end - nonopt_start;
377 }
378 nonopt_start = nonopt_end = -1;
379 return -1;
380 }
381 if ((has_equal = str_chr(current_argv, '=')) != NULL) {
382 /* argument found (--option=arg) */
383 current_argv_len = has_equal - current_argv;
384 has_equal++;
385 } else
386 current_argv_len = str_size(current_argv);
387
388 for (i = 0; long_options[i].name; i++) {
389 /* find matching long option */
390 if (str_lcmp(current_argv, long_options[i].name,
391 str_nlength(current_argv, current_argv_len)))
392 continue;
393
394 if (str_size(long_options[i].name) ==
395 (unsigned)current_argv_len) {
396 /* exact match */
397 match = i;
398 ambiguous = 0;
399 break;
400 }
401 if (match == -1) /* partial match */
402 match = i;
403 else if (!IDENTICAL_INTERPRETATION(i, match))
404 ambiguous = 1;
405 }
406 if (ambiguous) {
407 /* ambiguous abbreviation */
408 if (PRINT_ERROR)
409 printf(ambig, (int)current_argv_len,
410 current_argv);
411 optopt = 0;
412 return BADCH;
413 }
414 if (match != -1) { /* option found */
415 if (long_options[match].has_arg == no_argument
416 && has_equal) {
417 if (PRINT_ERROR)
418 printf(noarg, (int)current_argv_len,
419 current_argv);
420 /*
421 * XXX: GNU sets optopt to val regardless of
422 * flag
423 */
424 if (long_options[match].flag == NULL)
425 optopt = long_options[match].val;
426 else
427 optopt = 0;
428 return BADARG;
429 }
430 if (long_options[match].has_arg == required_argument ||
431 long_options[match].has_arg == optional_argument) {
432 if (has_equal)
433 optarg = has_equal;
434 else if (long_options[match].has_arg ==
435 required_argument) {
436 /*
437 * optional argument doesn't use
438 * next nargv
439 */
440 optarg = nargv[optind++];
441 }
442 }
443 if ((long_options[match].has_arg == required_argument)
444 && (optarg == NULL)) {
445 /*
446 * Missing argument; leading ':'
447 * indicates no error should be generated
448 */
449 if (PRINT_ERROR)
450 printf(recargstring, current_argv);
451 /*
452 * XXX: GNU sets optopt to val regardless
453 * of flag
454 */
455 if (long_options[match].flag == NULL)
456 optopt = long_options[match].val;
457 else
458 optopt = 0;
459 --optind;
460 return BADARG;
461 }
462 } else { /* unknown option */
463 if (PRINT_ERROR)
464 printf(illoptstring, current_argv);
465 optopt = 0;
466 return BADCH;
467 }
468 if (long_options[match].flag) {
469 *long_options[match].flag = long_options[match].val;
470 retval = 0;
471 } else
472 retval = long_options[match].val;
473 if (idx)
474 *idx = match;
475 }
476 return retval;
477#undef IDENTICAL_INTERPRETATION
478}
479
Note: See TracBrowser for help on using the repository browser.