Complete Citrus import. Import message catalog implement from
[dragonfly.git] / lib / libc / stdlib / getopt_long.c
CommitLineData
984168ec 1/* $NetBSD: getopt_long.c,v 1.16 2003/10/27 00:12:42 lukem Exp $ */
32a7b49a 2/* $DragonFly: src/lib/libc/stdlib/getopt_long.c,v 1.13 2005/04/21 16:36:35 joerg Exp $ */
984168ec
JS
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#include <sys/cdefs.h>
41
42#include <err.h>
43#include <errno.h>
44#include <getopt.h>
45#include <stdlib.h>
46#include <string.h>
47
6cb1bb24
JS
48/* XXX BOOTSTRAPPING */
49#ifndef __DECONST
50#define __DECONST(type, var) ((type)(uintptr_t)(const void *)(var))
51#endif
52
6cc517f2 53#ifdef REPLACE_GETOPT
984168ec
JS
54int opterr = 1; /* if error message should be printed */
55int optind = 1; /* index into parent argv vector */
56int optopt = '?'; /* character checked for validity */
57int optreset; /* reset getopt */
58char *optarg; /* argument associated with option */
6cc517f2 59#endif
984168ec
JS
60
61#define IGNORE_FIRST (*options == '-' || *options == '+')
62#define PRINT_ERROR ((opterr) && ((*options != ':') \
63 || (IGNORE_FIRST && options[1] != ':')))
64#define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
65#define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
66/* XXX: GNU ignores PC if *options == '-' */
67#define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
68
69/* return values */
70#define BADCH (int)'?'
71#define BADARG ((IGNORE_FIRST && options[1] == ':') \
72 || (*options == ':') ? (int)':' : (int)'?')
73#define INORDER (int)1
74
9e4b0859
JS
75static int getopt_internal(int, char * const *, const char *, int);
76static int getopt_internal_short(int, char * const *, const char *, int);
6fb80e22
JS
77static int getopt_long_internal(int, char * const *, const char *,
78 const struct option *, int *, int);
984168ec
JS
79static int gcd(int, int);
80static void permute_args(int, int, int, char * const *);
81
82static char EMSG[] = {0};
83static char *place = EMSG; /* option letter processing */
84
85/* XXX: set optreset to 1 rather than these two */
86static int nonopt_start = -1; /* first non option argument (for permute) */
87static int nonopt_end = -1; /* first option after non options (for permute) */
88
89/* Error messages */
90static const char recargchar[] = "option requires an argument -- %c";
91static const char recargstring[] = "option requires an argument -- %s";
92static const char ambig[] = "ambiguous option -- %.*s";
93static const char noarg[] = "option doesn't take an argument -- %.*s";
94static const char illoptchar[] = "unknown option -- %c";
95static const char illoptstring[] = "unknown option -- %s";
96
97
98/*
99 * Compute the greatest common divisor of a and b.
100 */
101static int
102gcd(int a, int b)
103{
104 int c;
105
106 c = a % b;
107 while (c != 0) {
108 a = b;
109 b = c;
110 c = a % b;
111 }
112
113 return b;
114}
115
116/*
117 * Exchange the block from nonopt_start to nonopt_end with the block
118 * from nonopt_end to opt_end (keeping the same order of arguments
119 * in each block).
120 */
121static void
122permute_args(int panonopt_start, int panonopt_end, int opt_end,
123 char * const *nargv)
124{
125 int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
126 char *swap;
127
128 /*
129 * compute lengths of blocks and number and size of cycles
130 */
131 nnonopts = panonopt_end - panonopt_start;
132 nopts = opt_end - panonopt_end;
133 ncycle = gcd(nnonopts, nopts);
134 cyclelen = (opt_end - panonopt_start) / ncycle;
135
136 for (i = 0; i < ncycle; i++) {
137 cstart = panonopt_end+i;
138 pos = cstart;
139 for (j = 0; j < cyclelen; j++) {
140 if (pos >= panonopt_end)
141 pos -= nnonopts;
142 else
143 pos += nopts;
144 swap = nargv[pos];
145 /* LINTED const cast */
f76844ff 146 (__DECONST(char **, nargv))[pos] = nargv[cstart];
984168ec 147 /* LINTED const cast */
f76844ff 148 (__DECONST(char **, nargv))[cstart] = swap;
984168ec
JS
149 }
150 }
151}
152
153/*
154 * getopt_internal --
155 * Parse argc/argv argument vector. Called by user level routines.
156 * Returns -2 if -- is found (can be long option or end of options marker).
157 */
158static int
9e4b0859
JS
159getopt_internal(int nargc, char * const *nargv, const char *options,
160 int long_support)
984168ec 161{
984168ec
JS
162 optarg = NULL;
163
164 /*
165 * XXX Some programs (like rsyncd) expect to be able to
166 * XXX re-initialize optind to 0 and have getopt_long(3)
167 * XXX properly function again. Work around this braindamage.
168 */
169 if (optind == 0)
170 optind = 1;
171
172 if (optreset)
173 nonopt_start = nonopt_end = -1;
174start:
175 if (optreset || !*place) { /* update scanning pointer */
176 optreset = 0;
177 if (optind >= nargc) { /* end of argument vector */
178 place = EMSG;
179 if (nonopt_end != -1) {
180 /* do permutation, if we have to */
181 permute_args(nonopt_start, nonopt_end,
182 optind, nargv);
183 optind -= nonopt_end - nonopt_start;
184 }
185 else if (nonopt_start != -1) {
186 /*
187 * If we skipped non-options, set optind
188 * to the first of them.
189 */
190 optind = nonopt_start;
191 }
192 nonopt_start = nonopt_end = -1;
193 return -1;
194 }
e66f201f 195 place = nargv[optind];
32a7b49a 196 if ((*place == '-') && (place[1] == '\0') && long_support == 0)
e66f201f 197 return -1;
32a7b49a
JS
198 if ((*place != '-') ||
199 ((*place == '-') && (place[1] == '\0') && long_support != 0)) {
e66f201f 200 /* found non-option */
984168ec
JS
201 place = EMSG;
202 if (IN_ORDER) {
203 /*
204 * GNU extension:
205 * return non-option as argument to option 1
206 */
207 optarg = nargv[optind++];
208 return INORDER;
209 }
210 if (!PERMUTE) {
211 /*
212 * if no permutation wanted, stop parsing
213 * at first non-option
214 */
215 return -1;
216 }
217 /* do permutation */
218 if (nonopt_start == -1)
219 nonopt_start = optind;
220 else if (nonopt_end != -1) {
221 permute_args(nonopt_start, nonopt_end,
222 optind, nargv);
223 nonopt_start = optind -
224 (nonopt_end - nonopt_start);
225 nonopt_end = -1;
226 }
227 optind++;
228 /* process next argument */
229 goto start;
230 }
231 if (nonopt_start != -1 && nonopt_end == -1)
232 nonopt_end = optind;
233 if (place[1] && *++place == '-') { /* found "--" */
9e4b0859
JS
234 if (place[1] == '\0') {
235 ++optind;
236 /*
237 * We found an option (--), so if we skipped
238 * non-options, we have to permute.
239 */
240 if (nonopt_end != -1) {
241 permute_args(nonopt_start, nonopt_end,
242 optind, nargv);
243 optind -= nonopt_end - nonopt_start;
244 }
245 nonopt_start = nonopt_end = -1;
246 return -1;
247 } else if (long_support) {
248 place++;
249 return -2;
250 }
984168ec
JS
251 }
252 }
c892a7a1
JS
253 if (long_support == 2 && (place[1] || strchr(options, *place) == NULL))
254 return -3;
9e4b0859 255 return getopt_internal_short(nargc, nargv, options, long_support);
ca92595e
JS
256}
257
258static int
9e4b0859
JS
259getopt_internal_short(int nargc, char * const *nargv, const char *options,
260 int long_support)
ca92595e
JS
261{
262 const char *oli; /* option letter list index */
263 int optchar;
264
984168ec
JS
265 if ((optchar = (int)*place++) == (int)':' ||
266 (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
267 /* option letter unknown or ':' */
a3fea3cf
JS
268 if (PRINT_ERROR) {
269 if (long_support == 2)
270 warnx(illoptstring, --place);
271 else
272 warnx(illoptchar, optchar);
273 }
2186fbc1
JS
274 if (long_support == 2)
275 place = EMSG;
e66f201f 276 if (*place == 0)
984168ec 277 ++optind;
984168ec
JS
278 optopt = optchar;
279 return BADCH;
280 }
9e4b0859
JS
281 if (long_support && optchar == 'W' && oli[1] == ';') {
282 /* -W long-option */
984168ec
JS
283 if (*place)
284 return -2;
285
286 if (++optind >= nargc) { /* no arg */
287 place = EMSG;
288 if (PRINT_ERROR)
289 warnx(recargchar, optchar);
290 optopt = optchar;
291 return BADARG;
292 } else /* white space */
293 place = nargv[optind];
294 /*
295 * Handle -W arg the same as --arg (which causes getopt to
296 * stop parsing).
297 */
298 return -2;
299 }
300 if (*++oli != ':') { /* doesn't take argument */
301 if (!*place)
302 ++optind;
303 } else { /* takes (optional) argument */
304 optarg = NULL;
305 if (*place) /* no white space */
306 optarg = place;
307 /* XXX: disable test for :: if PC? (GNU doesn't) */
308 else if (oli[1] != ':') { /* arg not optional */
309 if (++optind >= nargc) { /* no arg */
310 place = EMSG;
311 if (PRINT_ERROR)
312 warnx(recargchar, optchar);
313 optopt = optchar;
314 return BADARG;
315 } else
316 optarg = nargv[optind];
317 }
318 place = EMSG;
319 ++optind;
320 }
321 /* dump back option letter */
322 return optchar;
323}
c892a7a1 324
6fb80e22
JS
325static int
326getopt_long_internal(int nargc, char * const *nargv, const char *options,
327 const struct option *long_options, int *idx, int long_only)
984168ec
JS
328{
329 int retval;
330
331 /* idx may be NULL */
332
6fb80e22
JS
333 retval = getopt_internal(nargc, nargv, options, long_only ? 2 : 1);
334recheck:
335 if (retval == -2 || retval == -3) {
984168ec
JS
336 char *current_argv, *has_equal;
337 size_t current_argv_len;
338 int i, match;
339
340 current_argv = place;
341 match = -1;
342
343 optind++;
344 place = EMSG;
345
984168ec
JS
346 if ((has_equal = strchr(current_argv, '=')) != NULL) {
347 /* argument found (--option=arg) */
348 current_argv_len = has_equal - current_argv;
349 has_equal++;
350 } else
351 current_argv_len = strlen(current_argv);
352
353 for (i = 0; long_options[i].name; i++) {
354 /* find matching long option */
355 if (strncmp(current_argv, long_options[i].name,
356 current_argv_len))
357 continue;
358
359 if (strlen(long_options[i].name) ==
360 (unsigned)current_argv_len) {
361 /* exact match */
362 match = i;
363 break;
364 }
365 if (match == -1) /* partial match */
366 match = i;
367 else {
368 /* ambiguous abbreviation */
369 if (PRINT_ERROR)
370 warnx(ambig, (int)current_argv_len,
371 current_argv);
372 optopt = 0;
373 return BADCH;
374 }
375 }
376 if (match != -1) { /* option found */
377 if (long_options[match].has_arg == no_argument
378 && has_equal) {
379 if (PRINT_ERROR)
380 warnx(noarg, (int)current_argv_len,
381 current_argv);
382 /*
383 * XXX: GNU sets optopt to val regardless of
384 * flag
385 */
386 if (long_options[match].flag == NULL)
387 optopt = long_options[match].val;
388 else
389 optopt = 0;
390 return BADARG;
391 }
392 if (long_options[match].has_arg == required_argument ||
393 long_options[match].has_arg == optional_argument) {
394 if (has_equal)
395 optarg = has_equal;
396 else if (long_options[match].has_arg ==
397 required_argument) {
398 /*
399 * optional argument doesn't use
400 * next nargv
401 */
402 optarg = nargv[optind++];
403 }
404 }
405 if ((long_options[match].has_arg == required_argument)
406 && (optarg == NULL)) {
407 /*
408 * Missing argument; leading ':'
409 * indicates no error should be generated
410 */
411 if (PRINT_ERROR)
412 warnx(recargstring, current_argv);
413 /*
414 * XXX: GNU sets optopt to val regardless
415 * of flag
416 */
417 if (long_options[match].flag == NULL)
418 optopt = long_options[match].val;
419 else
420 optopt = 0;
421 --optind;
422 return BADARG;
423 }
6fb80e22
JS
424 } else if (retval == -3) {
425 --optind;
c892a7a1 426 place = current_argv;
6fb80e22
JS
427 retval = getopt_internal_short(nargc, nargv,
428 options, long_only ? 2 : 1);
429 goto recheck;
984168ec
JS
430 } else { /* unknown option */
431 if (PRINT_ERROR)
432 warnx(illoptstring, current_argv);
433 optopt = 0;
434 return BADCH;
435 }
436 if (long_options[match].flag) {
437 *long_options[match].flag = long_options[match].val;
438 retval = 0;
439 } else
440 retval = long_options[match].val;
441 if (idx)
442 *idx = match;
443 }
444 return retval;
445}
6fb80e22 446
6cc517f2 447#ifdef REPLACE_GETOPT
6fb80e22
JS
448/*
449 * getopt --
450 * Parse argc/argv argument vector.
451 *
452 * [eventually this will replace the real getopt]
453 */
454int
455getopt(int nargc, char * const *nargv, const char *options)
456{
6fb80e22
JS
457 return getopt_internal(nargc, nargv, options, 0);
458}
6cc517f2 459#endif
6fb80e22
JS
460
461/*
462 * getopt_long --
463 * Parse argc/argv argument vector.
464 */
465
466int
467getopt_long(int nargc, char * const *nargv, const char *options,
468 const struct option *long_options, int *idx)
469{
470 return getopt_long_internal(nargc, nargv, options, long_options,
471 idx, 0);
472}
473
474/*
475 * getopt_long_only --
476 * Parse argc/argv argument vector.
477 * Prefers long options over short options for single dash arguments.
478 */
479
480int getopt_long_only(int nargc, char * const *nargv, const char *options,
481 const struct option *long_options, int *idx)
482{
483 return getopt_long_internal(nargc, nargv, options, long_options,
484 idx, 1);
485}