1 /* $NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $ */
4 * Copyright (c) 2000 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Dieter Baron and Thomas Klausner.
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 /* Ported to HelenOS August 2008 by Tim Post <echo@echoreply.us> */
43 /* HelenOS Port : We're incorporating only the modern getopt_long with wrappers
44 * to keep legacy getopt() usage from breaking. All references to REPLACE_GETOPT
45 * are dropped, we just include the code */
47 int opterr
= 1; /* if error message should be printed */
48 int optind
= 1; /* index into parent argv vector */
49 int optopt
= '?'; /* character checked for validity */
50 int optreset
; /* reset getopt */
51 const char *optarg
; /* argument associated with option */
54 #define IGNORE_FIRST (*options == '-' || *options == '+')
55 #define PRINT_ERROR ((opterr) && ((*options != ':') \
56 || (IGNORE_FIRST && options[1] != ':')))
57 /*HelenOS Port - POSIXLY_CORRECT is always false */
58 #define IS_POSIXLY_CORRECT 0
59 #define PERMUTE (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
60 /* XXX: GNU ignores PC if *options == '-' */
61 #define IN_ORDER (!IS_POSIXLY_CORRECT && *options == '-')
64 #define BADCH (int)'?'
65 #define BADARG ((IGNORE_FIRST && options[1] == ':') \
66 || (*options == ':') ? (int)':' : (int)'?')
67 #define INORDER (int)1
71 static int getopt_internal(int, char **, const char *);
72 static int gcd(int, int);
73 static void permute_args(int, int, int, char **);
75 static const char *place
= EMSG
; /* option letter processing */
77 /* XXX: set optreset to 1 rather than these two */
78 static int nonopt_start
= -1; /* first non option argument (for permute) */
79 static int nonopt_end
= -1; /* first option after non options (for permute) */
83 /* HelenOS Port: Calls to warnx() were eliminated (as we have no stderr that
84 * may be redirected) and replaced with printf. As such, error messages now
87 static const char recargchar
[] = "option requires an argument -- %c\n";
88 static const char recargstring
[] = "option requires an argument -- %s\n";
89 static const char ambig
[] = "ambiguous option -- %.*s\n";
90 static const char noarg
[] = "option doesn't take an argument -- %.*s\n";
91 static const char illoptchar
[] = "unknown option -- %c\n";
92 static const char illoptstring
[] = "unknown option -- %s\n";
96 * Compute the greatest common divisor of a and b.
98 static int gcd(int a
, int b
)
113 * Exchange the block from nonopt_start to nonopt_end with the block
114 * from nonopt_end to opt_end (keeping the same order of arguments
117 static void permute_args(int panonopt_start
, int panonopt_end
, int opt_end
,
120 int cstart
, cyclelen
, i
, j
, ncycle
, nnonopts
, nopts
, pos
;
123 assert(nargv
!= NULL
);
126 * compute lengths of blocks and number and size of cycles
128 nnonopts
= panonopt_end
- panonopt_start
;
129 nopts
= opt_end
- panonopt_end
;
130 ncycle
= gcd(nnonopts
, nopts
);
131 cyclelen
= (opt_end
- panonopt_start
) / ncycle
;
133 for (i
= 0; i
< ncycle
; i
++) {
134 cstart
= panonopt_end
+i
;
136 for (j
= 0; j
< cyclelen
; j
++) {
137 if (pos
>= panonopt_end
)
142 nargv
[pos
] = nargv
[cstart
];
143 nargv
[cstart
] = swap
;
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).
153 static int getopt_internal(int nargc
, char **nargv
, const char *options
)
155 const char *oli
; /* option letter list index */
158 assert(nargv
!= NULL
);
159 assert(options
!= NULL
);
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.
172 nonopt_start
= nonopt_end
= -1;
174 if (optreset
|| !*place
) { /* update scanning pointer */
176 if (optind
>= nargc
) { /* end of argument vector */
178 if (nonopt_end
!= -1) {
179 /* do permutation, if we have to */
180 permute_args(nonopt_start
, nonopt_end
,
182 optind
-= nonopt_end
- nonopt_start
;
184 else if (nonopt_start
!= -1) {
186 * If we skipped non-options, set optind
187 * to the first of them.
189 optind
= nonopt_start
;
191 nonopt_start
= nonopt_end
= -1;
194 if ((*(place
= nargv
[optind
]) != '-')
195 || (place
[1] == '\0')) { /* found non-option */
200 * return non-option as argument to option 1
202 optarg
= nargv
[optind
++];
207 * if no permutation wanted, stop parsing
208 * at first non-option
213 if (nonopt_start
== -1)
214 nonopt_start
= optind
;
215 else if (nonopt_end
!= -1) {
216 permute_args(nonopt_start
, nonopt_end
,
218 nonopt_start
= optind
-
219 (nonopt_end
- nonopt_start
);
223 /* process next argument */
226 if (nonopt_start
!= -1 && nonopt_end
== -1)
228 if (place
[1] && *++place
== '-') { /* found "--" */
233 if ((optchar
= (int)*place
++) == (int)':' ||
234 (oli
= str_chr(options
+ (IGNORE_FIRST
? 1 : 0), optchar
)) == NULL
) {
235 /* option letter unknown or ':' */
239 printf(illoptchar
, optchar
);
243 if (optchar
== 'W' && oli
[1] == ';') { /* -W long-option */
244 /* XXX: what if no long options provided (called by getopt)? */
248 if (++optind
>= nargc
) { /* no arg */
251 printf(recargchar
, optchar
);
254 } else /* white space */
255 place
= nargv
[optind
];
257 * Handle -W arg the same as --arg (which causes getopt to
262 if (*++oli
!= ':') { /* doesn't take argument */
265 } else { /* takes (optional) argument */
267 if (*place
) /* no white space */
269 /* XXX: disable test for :: if PC? (GNU doesn't) */
270 else if (oli
[1] != ':') { /* arg not optional */
271 if (++optind
>= nargc
) { /* no arg */
274 printf(recargchar
, optchar
);
278 optarg
= nargv
[optind
];
283 /* dump back option letter */
289 * Parse argc/argv argument vector.
291 int getopt(int nargc
, char * const *nargv
, const char *options
)
295 assert(nargv
!= NULL
);
296 assert(options
!= NULL
);
298 retval
= getopt_internal(nargc
, (char **)nargv
, options
);
302 * We found an option (--), so if we skipped non-options,
303 * we have to permute.
305 if (nonopt_end
!= -1) {
306 permute_args(nonopt_start
, nonopt_end
, optind
,
308 optind
-= nonopt_end
- nonopt_start
;
310 nonopt_start
= nonopt_end
= -1;
318 * Parse argc/argv argument vector.
320 int getopt_long(int nargc
, char * const *nargv
, const char *options
,
321 const struct option
*long_options
, int *idx
)
325 #define IDENTICAL_INTERPRETATION(_x, _y) \
326 (long_options[(_x)].has_arg == long_options[(_y)].has_arg && \
327 long_options[(_x)].flag == long_options[(_y)].flag && \
328 long_options[(_x)].val == long_options[(_y)].val)
330 assert(nargv
!= NULL
);
331 assert(options
!= NULL
);
332 assert(long_options
!= NULL
);
333 /* idx may be NULL */
335 retval
= getopt_internal(nargc
, (char **)nargv
, options
);
338 const char *has_equal
;
339 size_t current_argv_len
;
340 int i
, ambiguous
, match
;
342 current_argv
= (char *)place
;
349 if (*current_argv
== '\0') { /* found "--" */
351 * We found an option (--), so if we skipped
352 * non-options, we have to permute.
354 if (nonopt_end
!= -1) {
355 permute_args(nonopt_start
, nonopt_end
,
356 optind
, (char **)nargv
);
357 optind
-= nonopt_end
- nonopt_start
;
359 nonopt_start
= nonopt_end
= -1;
362 if ((has_equal
= str_chr(current_argv
, '=')) != NULL
) {
363 /* argument found (--option=arg) */
364 current_argv_len
= has_equal
- current_argv
;
367 current_argv_len
= str_size(current_argv
);
369 for (i
= 0; long_options
[i
].name
; i
++) {
370 /* find matching long option */
371 if (str_lcmp(current_argv
, long_options
[i
].name
,
372 str_nlength(current_argv
, current_argv_len
)))
375 if (str_size(long_options
[i
].name
) ==
376 (unsigned)current_argv_len
) {
382 if (match
== -1) /* partial match */
384 else if (!IDENTICAL_INTERPRETATION(i
, match
))
388 /* ambiguous abbreviation */
390 printf(ambig
, (int)current_argv_len
,
395 if (match
!= -1) { /* option found */
396 if (long_options
[match
].has_arg
== no_argument
399 printf(noarg
, (int)current_argv_len
,
402 * XXX: GNU sets optopt to val regardless of
405 if (long_options
[match
].flag
== NULL
)
406 optopt
= long_options
[match
].val
;
411 if (long_options
[match
].has_arg
== required_argument
||
412 long_options
[match
].has_arg
== optional_argument
) {
415 else if (long_options
[match
].has_arg
==
418 * optional argument doesn't use
421 optarg
= nargv
[optind
++];
424 if ((long_options
[match
].has_arg
== required_argument
)
425 && (optarg
== NULL
)) {
427 * Missing argument; leading ':'
428 * indicates no error should be generated
431 printf(recargstring
, current_argv
);
433 * XXX: GNU sets optopt to val regardless
436 if (long_options
[match
].flag
== NULL
)
437 optopt
= long_options
[match
].val
;
443 } else { /* unknown option */
445 printf(illoptstring
, current_argv
);
449 if (long_options
[match
].flag
) {
450 *long_options
[match
].flag
= long_options
[match
].val
;
453 retval
= long_options
[match
].val
;
458 #undef IDENTICAL_INTERPRETATION