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