getopt_long.c revision 1.5 1 /* $NetBSD: getopt_long.c,v 1.5 2000/04/02 22:04:06 christos 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 2000/04/02 22:04:06 christos 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 recarg[] = "option requires an argument -- %c";
96 static const char ambig[] = "ambiguous option -- %.*s";
97 static const char noarg[] = "option doesn't take an argument -- %.*s";
98 static const char illopt[] = "illegal option -- %s";
99
100
101 extern char *__progname;
102
103 /*
104 * Compute the greatest common divisor of a and b.
105 */
106 static int
107 gcd(a, b)
108 int a;
109 int b;
110 {
111 int c;
112
113 c = a % b;
114 while (c != 0) {
115 a = b;
116 b = c;
117 c = a % b;
118 }
119
120 return b;
121 }
122
123 /*
124 * Exchange the block from nonopt_start to nonopt_end with the block
125 * from nonopt_end to opt_end (keeping the same order of arguments
126 * in each block).
127 */
128 static void
129 permute_args(nonopt_start, nonopt_end, opt_end, nargv)
130 int nonopt_start;
131 int nonopt_end;
132 int opt_end;
133 char * const *nargv;
134 {
135 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
136 char *swap;
137
138 /*
139 * compute lengths of blocks and number and size of cycles
140 */
141 nnonopts = nonopt_end - nonopt_start;
142 nopts = opt_end - nonopt_end;
143 ncycle = gcd(nnonopts, nopts);
144 cyclelen = (opt_end - nonopt_start) / ncycle;
145
146 for (i = 0; i < ncycle; i++) {
147 cstart = nonopt_end+i;
148 pos = cstart;
149 for (j = 0; j < cyclelen; j++) {
150 if (pos >= nonopt_end)
151 pos -= nnonopts;
152 else
153 pos += nopts;
154 swap = nargv[pos];
155 /* LINTED const cast */
156 ((char **) nargv)[pos] = nargv[cstart];
157 /* LINTED const cast */
158 ((char **)nargv)[cstart] = swap;
159 }
160 }
161 }
162
163 /*
164 * getopt_internal --
165 * Parse argc/argv argument vector. Called by user level routines.
166 * Returns -2 if -- is found (can be long option or end of options marker).
167 */
168 static int
169 getopt_internal(nargc, nargv, options)
170 int nargc;
171 char * const *nargv;
172 const char *options;
173 {
174 char *oli; /* option letter list index */
175 int optchar;
176
177 _DIAGASSERT(nargv != NULL);
178 _DIAGASSERT(options != NULL);
179
180 optarg = NULL;
181
182 if (optreset)
183 nonopt_start = nonopt_end = -1;
184 start:
185 if (optreset || !*place) { /* update scanning pointer */
186 optreset = 0;
187 if (optind >= nargc) { /* end of argument vector */
188 place = EMSG;
189 if (nonopt_end != -1) {
190 /* do permutation, if we have to */
191 permute_args(nonopt_start, nonopt_end,
192 optind, nargv);
193 optind -= nonopt_end - nonopt_start;
194 }
195 else if (nonopt_start != -1) {
196 /*
197 * If we skipped non-options, set optind
198 * to the first of them.
199 */
200 optind = nonopt_start;
201 }
202 nonopt_start = nonopt_end = -1;
203 return -1;
204 }
205 if (*(place = nargv[optind]) != '-') { /* 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 = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
245 /* option letter unknown or ':' */
246 if (!*place)
247 ++optind;
248 if (PRINT_ERROR)
249 warnx(illopt, 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 warnx(recarg, optchar);
262 optopt = optchar;
263 /* XXX: GNU returns '?' if options[0] != ':' */
264 return BADARG;
265 } else /* white space */
266 place = nargv[optind];
267 /*
268 * Handle -W arg the same as --arg (which causes getopt to
269 * stop parsing).
270 */
271 return -2;
272 }
273 if (*++oli != ':') { /* doesn't take argument */
274 if (!*place)
275 ++optind;
276 } else { /* takes (optional) argument */
277 optarg = NULL;
278 if (*place) /* no white space */
279 optarg = place;
280 /* XXX: disable test for :: if PC? (GNU doesn't) */
281 else if (oli[1] != ':') { /* arg not optional */
282 if (++optind >= nargc) { /* no arg */
283 place = EMSG;
284 if (PRINT_ERROR)
285 warnx(recarg, optchar);
286 optopt = optchar;
287 /* XXX: GNU returns '?' if options[0] != ':' */
288 return BADARG;
289 } else
290 optarg = nargv[optind];
291 }
292 place = EMSG;
293 ++optind;
294 }
295 /* dump back option letter */
296 return optchar;
297 }
298
299 #ifdef REPLACE_GETOPT
300 /*
301 * getopt --
302 * Parse argc/argv argument vector.
303 *
304 * [eventually this will replace the real getopt]
305 */
306 int
307 getopt(nargc, nargv, options)
308 int nargc;
309 char * const *nargv;
310 const char *options;
311 {
312 int retval;
313
314 if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
315 ++optind;
316 /*
317 * We found an option (--), so if we skipped non-options,
318 * we have to permute.
319 */
320 if (nonopt_end != -1) {
321 permute_args(nonopt_start, nonopt_end, optind,
322 nargv);
323 optind -= nonopt_end - nonopt_start;
324 }
325 nonopt_start = nonopt_end = -1;
326 retval = -1;
327 }
328 return retval;
329 }
330 #endif
331
332 /*
333 * getopt_long --
334 * Parse argc/argv argument vector.
335 */
336 int
337 getopt_long(nargc, nargv, options, long_options, idx)
338 int nargc;
339 char * const *nargv;
340 const char *options;
341 const struct option *long_options;
342 int *idx;
343 {
344 int retval;
345
346 _DIAGASSERT(nargv != NULL);
347 _DIAGASSERT(options != NULL);
348 _DIAGASSERT(long_options != NULL);
349 /* idx may be NULL */
350
351 if ((retval = getopt_internal(nargc, nargv, options)) == -2) {
352 char *current_argv, *has_equal;
353 size_t current_argv_len;
354 int i, match;
355
356 current_argv = place;
357 match = -1;
358
359 optind++;
360 place = EMSG;
361
362 if (*current_argv == '\0') { /* found "--" */
363 /*
364 * We found an option (--), so if we skipped
365 * non-options, we have to permute.
366 */
367 if (nonopt_end != -1) {
368 permute_args(nonopt_start, nonopt_end,
369 optind, nargv);
370 optind -= nonopt_end - nonopt_start;
371 }
372 nonopt_start = nonopt_end = -1;
373 return -1;
374 }
375 if ((has_equal = strchr(current_argv, '=')) != NULL) {
376 /* argument found (--option=arg) */
377 current_argv_len = has_equal - current_argv;
378 has_equal++;
379 } else
380 current_argv_len = strlen(current_argv);
381
382 for (i = 0; long_options[i].name; i++) {
383 /* find matching long option */
384 if (strncmp(current_argv, long_options[i].name,
385 current_argv_len))
386 continue;
387
388 if (strlen(long_options[i].name) ==
389 (unsigned)current_argv_len) {
390 /* exact match */
391 match = i;
392 break;
393 }
394 if (match == -1) /* partial match */
395 match = i;
396 else {
397 /* ambiguous abbreviation */
398 if (PRINT_ERROR)
399 warnx(ambig, current_argv_len,
400 current_argv);
401 optopt = 0;
402 return BADCH;
403 }
404 }
405 if (match != -1) { /* option found */
406 if (long_options[match].has_arg == no_argument
407 && has_equal) {
408 if (PRINT_ERROR)
409 warnx(noarg, current_argv_len,
410 current_argv);
411 /*
412 * XXX: GNU sets optopt to val regardless of
413 * flag
414 */
415 if (long_options[match].flag == NULL)
416 optopt = long_options[match].val;
417 else
418 optopt = 0;
419 /* XXX: GNU returns '?' if options[0] != ':' */
420 return BADARG;
421 }
422 if (long_options[match].has_arg == required_argument ||
423 long_options[match].has_arg == optional_argument) {
424 if (has_equal)
425 optarg = has_equal;
426 else if (long_options[match].has_arg ==
427 required_argument) {
428 /*
429 * optional argument doesn't use
430 * next nargv
431 */
432 optarg = nargv[optind++];
433 }
434 }
435 if ((long_options[match].has_arg == required_argument)
436 && (optarg == NULL)) {
437 /*
438 * Missing argument; leading ':'
439 * indicates no error should be generated
440 */
441 if (PRINT_ERROR)
442 warnx(recarg, current_argv);
443 /*
444 * XXX: GNU sets optopt to val regardless
445 * of flag
446 */
447 if (long_options[match].flag == NULL)
448 optopt = long_options[match].val;
449 else
450 optopt = 0;
451 /* XXX: GNU returns '?' if options[0] != ':' */
452 --optind;
453 return BADARG;
454 }
455 } else { /* unknown option */
456 if (PRINT_ERROR)
457 warnx(illopt, current_argv);
458 optopt = 0;
459 return BADCH;
460 }
461 if (long_options[match].flag) {
462 *long_options[match].flag = long_options[match].val;
463 retval = 0;
464 } else
465 retval = long_options[match].val;
466 if (idx)
467 *idx = match;
468 }
469 return retval;
470 }
471