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