source: MondoRescue/branches/3.3/mindi-busybox/util-linux/getopt.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 12.3 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * getopt.c - Enhanced implementation of BSD getopt(1)
4 * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>
5 *
6 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
7 */
8
9/*
10 * Version 1.0-b4: Tue Sep 23 1997. First public release.
11 * Version 1.0: Wed Nov 19 1997.
12 * Bumped up the version number to 1.0
13 * Fixed minor typo (CSH instead of TCSH)
14 * Version 1.0.1: Tue Jun 3 1998
15 * Fixed sizeof instead of strlen bug
16 * Bumped up the version number to 1.0.1
17 * Version 1.0.2: Thu Jun 11 1998 (not present)
18 * Fixed gcc-2.8.1 warnings
19 * Fixed --version/-V option (not present)
20 * Version 1.0.5: Tue Jun 22 1999
21 * Make -u option work (not present)
22 * Version 1.0.6: Tue Jun 27 2000
23 * No important changes
24 * Version 1.1.0: Tue Jun 30 2000
25 * Added NLS support (partly written by Arkadiusz Mickiewicz
26 * <misiek@misiek.eu.org>)
27 * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
28 * Removed --version/-V and --help/-h
29 * Removed parse_error(), using bb_error_msg() from Busybox instead
30 * Replaced our_malloc with xmalloc and our_realloc with xrealloc
31 *
32 */
33
34//usage:#define getopt_trivial_usage
35//usage: "[OPTIONS] [--] OPTSTRING PARAMS"
36//usage:#define getopt_full_usage "\n\n"
37//usage: IF_LONG_OPTS(
38//usage: IF_FEATURE_GETOPT_LONG(
39//usage: " -a,--alternative Allow long options starting with single -\n"
40//usage: " -l,--longoptions=LOPT[,...] Long options to recognize\n"
41//usage: )
42//usage: " -n,--name=PROGNAME The name under which errors are reported"
43//usage: "\n -o,--options=OPTSTRING Short options to recognize"
44//usage: "\n -q,--quiet No error messages on unrecognized options"
45//usage: "\n -Q,--quiet-output No normal output"
46//usage: "\n -s,--shell=SHELL Set shell quoting conventions"
47//usage: "\n -T,--test Version test (exits with 4)"
48//usage: "\n -u,--unquoted Don't quote output"
49//usage: )
50//usage: IF_NOT_LONG_OPTS(
51//usage: IF_FEATURE_GETOPT_LONG(
52//usage: " -a Allow long options starting with single -\n"
53//usage: " -l LOPT[,...] Long options to recognize\n"
54//usage: )
55//usage: " -n PROGNAME The name under which errors are reported"
56//usage: "\n -o OPTSTRING Short options to recognize"
57//usage: "\n -q No error messages on unrecognized options"
58//usage: "\n -Q No normal output"
59//usage: "\n -s SHELL Set shell quoting conventions"
60//usage: "\n -T Version test (exits with 4)"
61//usage: "\n -u Don't quote output"
62//usage: )
63//usage: IF_FEATURE_GETOPT_LONG( /* example uses -l, needs FEATURE_GETOPT_LONG */
64//usage: "\n"
65//usage: "\nExample:"
66//usage: "\n"
67//usage: "\nO=`getopt -l bb: -- ab:c:: \"$@\"` || exit 1"
68//usage: "\neval set -- \"$O\""
69//usage: "\nwhile true; do"
70//usage: "\n case \"$1\" in"
71//usage: "\n -a) echo A; shift;;"
72//usage: "\n -b|--bb) echo \"B:'$2'\"; shift 2;;"
73//usage: "\n -c) case \"$2\" in"
74//usage: "\n \"\") echo C; shift 2;;"
75//usage: "\n *) echo \"C:'$2'\"; shift 2;;"
76//usage: "\n esac;;"
77//usage: "\n --) shift; break;;"
78//usage: "\n *) echo Error; exit 1;;"
79//usage: "\n esac"
80//usage: "\ndone"
81//usage: )
82//usage:
83//usage:#define getopt_example_usage
84//usage: "$ cat getopt.test\n"
85//usage: "#!/bin/sh\n"
86//usage: "GETOPT=`getopt -o ab:c:: --long a-long,b-long:,c-long:: \\\n"
87//usage: " -n 'example.busybox' -- \"$@\"`\n"
88//usage: "if [ $? != 0 ]; then exit 1; fi\n"
89//usage: "eval set -- \"$GETOPT\"\n"
90//usage: "while true; do\n"
91//usage: " case $1 in\n"
92//usage: " -a|--a-long) echo \"Option a\"; shift;;\n"
93//usage: " -b|--b-long) echo \"Option b, argument '$2'\"; shift 2;;\n"
94//usage: " -c|--c-long)\n"
95//usage: " case \"$2\" in\n"
96//usage: " \"\") echo \"Option c, no argument\"; shift 2;;\n"
97//usage: " *) echo \"Option c, argument '$2'\"; shift 2;;\n"
98//usage: " esac;;\n"
99//usage: " --) shift; break;;\n"
100//usage: " *) echo \"Internal error!\"; exit 1;;\n"
101//usage: " esac\n"
102//usage: "done\n"
103
104#if ENABLE_FEATURE_GETOPT_LONG
105# include <getopt.h>
106#endif
107#include "libbb.h"
108
109/* NON_OPT is the code that is returned when a non-option is found in '+'
110 mode */
111enum {
112 NON_OPT = 1,
113#if ENABLE_FEATURE_GETOPT_LONG
114/* LONG_OPT is the code that is returned when a long option is found. */
115 LONG_OPT = 2
116#endif
117};
118
119/* For finding activated option flags. Must match getopt32 call! */
120enum {
121 OPT_o = 0x1, // -o
122 OPT_n = 0x2, // -n
123 OPT_q = 0x4, // -q
124 OPT_Q = 0x8, // -Q
125 OPT_s = 0x10, // -s
126 OPT_T = 0x20, // -T
127 OPT_u = 0x40, // -u
128#if ENABLE_FEATURE_GETOPT_LONG
129 OPT_a = 0x80, // -a
130 OPT_l = 0x100, // -l
131#endif
132 SHELL_IS_TCSH = 0x8000, /* hijack this bit for other purposes */
133};
134
135/* 0 is getopt_long, 1 is getopt_long_only */
136#define alternative (option_mask32 & OPT_a)
137
138#define quiet_errors (option_mask32 & OPT_q)
139#define quiet_output (option_mask32 & OPT_Q)
140#define quote (!(option_mask32 & OPT_u))
141#define shell_TCSH (option_mask32 & SHELL_IS_TCSH)
142
143/*
144 * This function 'normalizes' a single argument: it puts single quotes around
145 * it and escapes other special characters. If quote is false, it just
146 * returns its argument.
147 * Bash only needs special treatment for single quotes; tcsh also recognizes
148 * exclamation marks within single quotes, and nukes whitespace.
149 * This function returns a pointer to a buffer that is overwritten by
150 * each call.
151 */
152static const char *normalize(const char *arg)
153{
154 char *bufptr;
155#if ENABLE_FEATURE_CLEAN_UP
156 static char *BUFFER = NULL;
157 free(BUFFER);
158#else
159 char *BUFFER;
160#endif
161
162 if (!quote) { /* Just copy arg */
163 BUFFER = xstrdup(arg);
164 return BUFFER;
165 }
166
167 /* Each character in arg may take up to four characters in the result:
168 For a quote we need a closing quote, a backslash, a quote and an
169 opening quote! We need also the global opening and closing quote,
170 and one extra character for '\0'. */
171 BUFFER = xmalloc(strlen(arg)*4 + 3);
172
173 bufptr = BUFFER;
174 *bufptr ++= '\'';
175
176 while (*arg) {
177 if (*arg == '\'') {
178 /* Quote: replace it with: '\'' */
179 *bufptr ++= '\'';
180 *bufptr ++= '\\';
181 *bufptr ++= '\'';
182 *bufptr ++= '\'';
183 } else if (shell_TCSH && *arg == '!') {
184 /* Exclamation mark: replace it with: \! */
185 *bufptr ++= '\'';
186 *bufptr ++= '\\';
187 *bufptr ++= '!';
188 *bufptr ++= '\'';
189 } else if (shell_TCSH && *arg == '\n') {
190 /* Newline: replace it with: \n */
191 *bufptr ++= '\\';
192 *bufptr ++= 'n';
193 } else if (shell_TCSH && isspace(*arg)) {
194 /* Non-newline whitespace: replace it with \<ws> */
195 *bufptr ++= '\'';
196 *bufptr ++= '\\';
197 *bufptr ++= *arg;
198 *bufptr ++= '\'';
199 } else
200 /* Just copy */
201 *bufptr ++= *arg;
202 arg++;
203 }
204 *bufptr ++= '\'';
205 *bufptr ++= '\0';
206 return BUFFER;
207}
208
209/*
210 * Generate the output. argv[0] is the program name (used for reporting errors).
211 * argv[1..] contains the options to be parsed. argc must be the number of
212 * elements in argv (ie. 1 if there are no options, only the program name),
213 * optstr must contain the short options, and longopts the long options.
214 * Other settings are found in global variables.
215 */
216#if !ENABLE_FEATURE_GETOPT_LONG
217#define generate_output(argv,argc,optstr,longopts) \
218 generate_output(argv,argc,optstr)
219#endif
220static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts)
221{
222 int exit_code = 0; /* We assume everything will be OK */
223
224 if (quiet_errors) /* No error reporting from getopt(3) */
225 opterr = 0;
226
227 /* We used it already in main() in getopt32(),
228 * we *must* reset getopt(3): */
229#ifdef __GLIBC__
230 optind = 0;
231#else /* BSD style */
232 optind = 1;
233 /* optreset = 1; */
234#endif
235
236 while (1) {
237#if ENABLE_FEATURE_GETOPT_LONG
238 int longindex;
239 int opt = alternative
240 ? getopt_long_only(argc, argv, optstr, longopts, &longindex)
241 : getopt_long(argc, argv, optstr, longopts, &longindex)
242 ;
243#else
244 int opt = getopt(argc, argv, optstr);
245#endif
246 if (opt == -1)
247 break;
248 if (opt == '?' || opt == ':' )
249 exit_code = 1;
250 else if (!quiet_output) {
251#if ENABLE_FEATURE_GETOPT_LONG
252 if (opt == LONG_OPT) {
253 printf(" --%s", longopts[longindex].name);
254 if (longopts[longindex].has_arg)
255 printf(" %s",
256 normalize(optarg ? optarg : ""));
257 } else
258#endif
259 if (opt == NON_OPT)
260 printf(" %s", normalize(optarg));
261 else {
262 const char *charptr;
263 printf(" -%c", opt);
264 charptr = strchr(optstr, opt);
265 if (charptr && *++charptr == ':')
266 printf(" %s",
267 normalize(optarg ? optarg : ""));
268 }
269 }
270 }
271
272 if (!quiet_output) {
273 unsigned idx;
274 printf(" --");
275 idx = optind;
276 while (argv[idx])
277 printf(" %s", normalize(argv[idx++]));
278 bb_putchar('\n');
279 }
280 return exit_code;
281}
282
283#if ENABLE_FEATURE_GETOPT_LONG
284/*
285 * Register several long options. options is a string of long options,
286 * separated by commas or whitespace.
287 * This nukes options!
288 */
289static struct option *add_long_options(struct option *long_options, char *options)
290{
291 int long_nr = 0;
292 int arg_opt, tlen;
293 char *tokptr = strtok(options, ", \t\n");
294
295 if (long_options)
296 while (long_options[long_nr].name)
297 long_nr++;
298
299 while (tokptr) {
300 arg_opt = no_argument;
301 tlen = strlen(tokptr);
302 if (tlen) {
303 tlen--;
304 if (tokptr[tlen] == ':') {
305 arg_opt = required_argument;
306 if (tlen && tokptr[tlen-1] == ':') {
307 tlen--;
308 arg_opt = optional_argument;
309 }
310 tokptr[tlen] = '\0';
311 if (tlen == 0)
312 bb_error_msg_and_die("empty long option specified");
313 }
314 long_options = xrealloc_vector(long_options, 4, long_nr);
315 long_options[long_nr].has_arg = arg_opt;
316 /*long_options[long_nr].flag = NULL; - xrealloc_vector did it */
317 long_options[long_nr].val = LONG_OPT;
318 long_options[long_nr].name = xstrdup(tokptr);
319 long_nr++;
320 /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */
321 }
322 tokptr = strtok(NULL, ", \t\n");
323 }
324 return long_options;
325}
326#endif
327
328static void set_shell(const char *new_shell)
329{
330 if (!strcmp(new_shell, "bash") || !strcmp(new_shell, "sh"))
331 return;
332 if (!strcmp(new_shell, "tcsh") || !strcmp(new_shell, "csh"))
333 option_mask32 |= SHELL_IS_TCSH;
334 else
335 bb_error_msg("unknown shell '%s', assuming bash", new_shell);
336}
337
338
339/* Exit codes:
340 * 0) No errors, successful operation.
341 * 1) getopt(3) returned an error.
342 * 2) A problem with parameter parsing for getopt(1).
343 * 3) Internal error, out of memory
344 * 4) Returned for -T
345 */
346
347#if ENABLE_FEATURE_GETOPT_LONG
348static const char getopt_longopts[] ALIGN1 =
349 "options\0" Required_argument "o"
350 "longoptions\0" Required_argument "l"
351 "quiet\0" No_argument "q"
352 "quiet-output\0" No_argument "Q"
353 "shell\0" Required_argument "s"
354 "test\0" No_argument "T"
355 "unquoted\0" No_argument "u"
356 "alternative\0" No_argument "a"
357 "name\0" Required_argument "n"
358 ;
359#endif
360
361int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
362int getopt_main(int argc, char **argv)
363{
364 int n;
365 char *optstr = NULL;
366 char *name = NULL;
367 unsigned opt;
368 const char *compatible;
369 char *s_arg;
370#if ENABLE_FEATURE_GETOPT_LONG
371 struct option *long_options = NULL;
372 llist_t *l_arg = NULL;
373#endif
374
375 compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */
376
377 if (!argv[1]) {
378 if (compatible) {
379 /* For some reason, the original getopt gave no error
380 * when there were no arguments. */
381 puts(" --");
382 return 0;
383 }
384 bb_error_msg_and_die("missing optstring argument");
385 }
386
387 if (argv[1][0] != '-' || compatible) {
388 char *s = argv[1];
389
390 option_mask32 |= OPT_u; /* quoting off */
391 s = xstrdup(s + strspn(s, "-+"));
392 argv[1] = argv[0];
393 return generate_output(argv+1, argc-1, s, long_options);
394 }
395
396#if !ENABLE_FEATURE_GETOPT_LONG
397 opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg);
398#else
399 applet_long_options = getopt_longopts;
400 opt_complementary = "l::";
401 opt = getopt32(argv, "+o:n:qQs:Tual:",
402 &optstr, &name, &s_arg, &l_arg);
403 /* Effectuate the read options for the applet itself */
404 while (l_arg) {
405 long_options = add_long_options(long_options, llist_pop(&l_arg));
406 }
407#endif
408
409 if (opt & OPT_s) {
410 set_shell(s_arg);
411 }
412
413 if (opt & OPT_T) {
414 return 4;
415 }
416
417 /* All options controlling the applet have now been parsed */
418 n = optind - 1;
419 if (!optstr) {
420 optstr = argv[++n];
421 if (!optstr)
422 bb_error_msg_and_die("missing optstring argument");
423 }
424
425 argv[n] = name ? name : argv[0];
426 return generate_output(argv + n, argc - n, optstr, long_options);
427}
Note: See TracBrowser for help on using the repository browser.