summaryrefslogtreecommitdiff
path: root/src/lib/libc/stdlib/getopt_long.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/libc/stdlib/getopt_long.c')
-rw-r--r--src/lib/libc/stdlib/getopt_long.c510
1 files changed, 510 insertions, 0 deletions
diff --git a/src/lib/libc/stdlib/getopt_long.c b/src/lib/libc/stdlib/getopt_long.c
new file mode 100644
index 0000000000..1256e5d655
--- /dev/null
+++ b/src/lib/libc/stdlib/getopt_long.c
@@ -0,0 +1,510 @@
1/* $OpenBSD: getopt_long.c,v 1.1 2002/12/03 20:24:30 millert Exp $ */
2/* $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $ */
3
4/*-
5 * Copyright (c) 2000 The NetBSD Foundation, Inc.
6 * All rights reserved.
7 *
8 * This code is derived from software contributed to The NetBSD Foundation
9 * by Dieter Baron and Thomas Klausner.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40#if defined(LIBC_SCCS) && !defined(lint)
41static char *rcsid = "$OpenBSD: getopt_long.c,v 1.1 2002/12/03 20:24:30 millert Exp $";
42#endif /* LIBC_SCCS and not lint */
43
44#include <err.h>
45#include <errno.h>
46#include <getopt.h>
47#include <stdlib.h>
48#include <string.h>
49
50int opterr = 1; /* if error message should be printed */
51int optind = 1; /* index into parent argv vector */
52int optopt = '?'; /* character checked for validity */
53int optreset; /* reset getopt */
54char *optarg; /* argument associated with option */
55
56#define PRINT_ERROR ((opterr) && (*options != ':'))
57
58#define FLAG_PERMUTE 0x01
59#define FLAG_ALLARGS 0x02
60#define FLAG_LONGONLY 0x04
61
62/* return values */
63#define BADCH (int)'?'
64#define BADARG ((*options == ':') ? (int)':' : (int)'?')
65#define INORDER (int)1
66
67#define EMSG ""
68
69static int getopt_internal(int, char * const *, const char *, int);
70static int getopt_long_internal(int, char * const *, const char *,
71 const struct option *, int *, int);
72static int gcd(int, int);
73static void permute_args(int, int, int, char * const *);
74
75static char *place = EMSG; /* option letter processing */
76
77/* XXX: set optreset to 1 rather than these two */
78static int nonopt_start = -1; /* first non option argument (for permute) */
79static int nonopt_end = -1; /* first option after non options (for permute) */
80
81/* Error messages */
82static const char recargchar[] = "option requires an argument -- %c";
83static const char recargstring[] = "option requires an argument -- %s";
84static const char ambig[] = "ambiguous option -- %.*s";
85static const char noarg[] = "option doesn't take an argument -- %.*s";
86static const char illoptchar[] = "unknown option -- %c";
87static const char illoptstring[] = "unknown option -- %s";
88
89/*
90 * Compute the greatest common divisor of a and b.
91 */
92static int
93gcd(a, b)
94 int a;
95 int b;
96{
97 int c;
98
99 c = a % b;
100 while (c != 0) {
101 a = b;
102 b = c;
103 c = a % b;
104 }
105
106 return b;
107}
108
109/*
110 * Exchange the block from nonopt_start to nonopt_end with the block
111 * from nonopt_end to opt_end (keeping the same order of arguments
112 * in each block).
113 */
114static void
115permute_args(panonopt_start, panonopt_end, opt_end, nargv)
116 int panonopt_start;
117 int panonopt_end;
118 int opt_end;
119 char * const *nargv;
120{
121 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
122 char *swap;
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 /* LINTED const cast */
142 ((char **) nargv)[pos] = nargv[cstart];
143 /* LINTED const cast */
144 ((char **)nargv)[cstart] = swap;
145 }
146 }
147}
148
149/*
150 * getopt_internal --
151 * Parse argc/argv argument vector. Called by user level routines.
152 * Returns -2 if -- is found (can be long option or end of options marker).
153 */
154static int
155getopt_internal(nargc, nargv, options, flags)
156 int nargc;
157 char * const *nargv;
158 const char *options;
159 int flags;
160{
161 char *oli; /* option letter list index */
162 int optchar;
163
164 optarg = NULL;
165
166 /*
167 * XXX Some programs (like rsyncd) expect to be able to
168 * XXX re-initialize optind to 0 and have getopt_long(3)
169 * XXX properly function again. Work around this braindamage.
170 */
171 if (optind == 0)
172 optind = 1;
173
174 if (optreset)
175 nonopt_start = nonopt_end = -1;
176start:
177 if (optreset || !*place) { /* update scanning pointer */
178 optreset = 0;
179 if (optind >= nargc) { /* end of argument vector */
180 place = EMSG;
181 if (nonopt_end != -1) {
182 /* do permutation, if we have to */
183 permute_args(nonopt_start, nonopt_end,
184 optind, nargv);
185 optind -= nonopt_end - nonopt_start;
186 }
187 else if (nonopt_start != -1) {
188 /*
189 * If we skipped non-options, set optind
190 * to the first of them.
191 */
192 optind = nonopt_start;
193 }
194 nonopt_start = nonopt_end = -1;
195 return -1;
196 }
197 if ((*(place = nargv[optind]) != '-')
198 || (place[1] == '\0')) { /* found non-option */
199 place = EMSG;
200 if (flags & FLAG_ALLARGS) {
201 /*
202 * GNU extension:
203 * return non-option as argument to option 1
204 */
205 optarg = nargv[optind++];
206 return INORDER;
207 }
208 if (!(flags & FLAG_PERMUTE)) {
209 /*
210 * If no permutation wanted, stop parsing
211 * at first non-option.
212 */
213 return -1;
214 }
215 /* do permutation */
216 if (nonopt_start == -1)
217 nonopt_start = optind;
218 else if (nonopt_end != -1) {
219 permute_args(nonopt_start, nonopt_end,
220 optind, nargv);
221 nonopt_start = optind -
222 (nonopt_end - nonopt_start);
223 nonopt_end = -1;
224 }
225 optind++;
226 /* process next argument */
227 goto start;
228 }
229 if (nonopt_start != -1 && nonopt_end == -1)
230 nonopt_end = optind;
231 if (place[1] && *++place == '-') { /* found "--" */
232 place++;
233 return -2;
234 }
235 }
236 if ((optchar = (int)*place++) == (int)':' ||
237 (oli = strchr(options, optchar)) == NULL) {
238 /* could it be a long option with a single '-'? */
239 if (flags & FLAG_LONGONLY)
240 return -2;
241 /* option letter unknown or ':' */
242 if (!*place)
243 ++optind;
244 if (PRINT_ERROR)
245 warnx(illoptchar, optchar);
246 optopt = optchar;
247 return BADCH;
248 }
249 if (optchar == 'W' && oli[1] == ';') { /* -W long-option */
250 /* XXX: what if no long options provided (called by getopt)? */
251 if (*place)
252 return -2;
253
254 if (++optind >= nargc) { /* no arg */
255 place = EMSG;
256 if (PRINT_ERROR)
257 warnx(recargchar, optchar);
258 optopt = optchar;
259 return BADARG;
260 } else /* white space */
261 place = nargv[optind];
262 /*
263 * Handle -W arg the same as --arg (which causes getopt to
264 * stop parsing).
265 */
266 return -2;
267 }
268 if (*++oli != ':') { /* doesn't take argument */
269 if (!*place)
270 ++optind;
271 } else { /* takes (optional) argument */
272 optarg = NULL;
273 if (*place) /* no white space */
274 optarg = place;
275 /* XXX: disable test for :: if PC? (GNU doesn't) */
276 else if (oli[1] != ':') { /* arg not optional */
277 if (++optind >= nargc) { /* no arg */
278 place = EMSG;
279 if (PRINT_ERROR)
280 warnx(recargchar, optchar);
281 optopt = optchar;
282 return BADARG;
283 } else
284 optarg = nargv[optind];
285 }
286 place = EMSG;
287 ++optind;
288 }
289 /* dump back option letter */
290 return optchar;
291}
292
293#ifdef REPLACE_GETOPT
294/*
295 * getopt --
296 * Parse argc/argv argument vector.
297 *
298 * [eventually this will replace the BSD getopt]
299 */
300int
301getopt(nargc, nargv, options)
302 int nargc;
303 char * const *nargv;
304 const char *options;
305{
306 int retval;
307
308 if ((retval = getopt_internal(nargc, nargv, options, 0)) == -2) {
309 ++optind;
310 /*
311 * We found an option (--), so if we skipped non-options,
312 * we have to permute.
313 */
314 if (nonopt_end != -1) {
315 permute_args(nonopt_start, nonopt_end, optind,
316 nargv);
317 optind -= nonopt_end - nonopt_start;
318 }
319 nonopt_start = nonopt_end = -1;
320 retval = -1;
321 }
322 return retval;
323}
324#endif /* REPLACE_GETOPT */
325
326/*
327 * getopt_long_internal --
328 * Parse argc/argv argument vector.
329 */
330static int
331getopt_long_internal(nargc, nargv, options, long_options, idx, flags)
332 int nargc;
333 char * const *nargv;
334 const char *options;
335 const struct option *long_options;
336 int *idx;
337 int flags;
338{
339 int retval;
340
341 /*
342 * Disable GNU extensions if POSIXLY_CORRECT is set or options
343 * string begins with a '+'.
344 */
345 if (getenv("POSIXLY_CORRECT")) {
346 if (*options == '+' || *options == '-')
347 options++;
348 } else {
349 if (*options == '+') {
350 options++;
351 } else {
352 flags |= FLAG_PERMUTE;
353 if (*options == '-') {
354 flags |= FLAG_ALLARGS;
355 options++;
356 }
357 }
358 }
359
360 if ((retval = getopt_internal(nargc, nargv, options, flags)) == -2) {
361 char *current_argv, *has_equal;
362 size_t current_argv_len;
363 int i, match;
364
365 current_argv = place;
366 match = -1;
367
368 optind++;
369 place = EMSG;
370
371 if (*current_argv == '\0') { /* found "--" */
372 /*
373 * We found an option (--), so if we skipped
374 * non-options, we have to permute.
375 */
376 if (nonopt_end != -1) {
377 permute_args(nonopt_start, nonopt_end,
378 optind, nargv);
379 optind -= nonopt_end - nonopt_start;
380 }
381 nonopt_start = nonopt_end = -1;
382 return -1;
383 }
384 if ((has_equal = strchr(current_argv, '=')) != NULL) {
385 /* argument found (--option=arg) */
386 current_argv_len = has_equal - current_argv;
387 has_equal++;
388 } else
389 current_argv_len = strlen(current_argv);
390
391 for (i = 0; long_options[i].name; i++) {
392 /* find matching long option */
393 if (strncmp(current_argv, long_options[i].name,
394 current_argv_len))
395 continue;
396
397 if (strlen(long_options[i].name) ==
398 (unsigned)current_argv_len) {
399 /* exact match */
400 match = i;
401 break;
402 }
403 if (match == -1) /* partial match */
404 match = i;
405 else {
406 /* ambiguous abbreviation */
407 if (PRINT_ERROR)
408 warnx(ambig, (int)current_argv_len,
409 current_argv);
410 optopt = 0;
411 return BADCH;
412 }
413 }
414 if (match != -1) { /* option found */
415 if (long_options[match].has_arg == no_argument
416 && has_equal) {
417 if (PRINT_ERROR)
418 warnx(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 warnx(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 warnx(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}
478
479/*
480 * getopt_long --
481 * Parse argc/argv argument vector.
482 */
483int
484getopt_long(nargc, nargv, options, long_options, idx)
485 int nargc;
486 char * const *nargv;
487 const char *options;
488 const struct option *long_options;
489 int *idx;
490{
491
492 return getopt_long_internal(nargc, nargv, options, long_options, idx, 0);
493}
494
495/*
496 * getopt_long_only --
497 * Parse argc/argv argument vector.
498 */
499int
500getopt_long_only(nargc, nargv, options, long_options, idx)
501 int nargc;
502 char * const *nargv;
503 const char *options;
504 const struct option *long_options;
505 int *idx;
506{
507
508 return getopt_long_internal(nargc, nargv, options, long_options, idx,
509 FLAG_LONGONLY);
510}