blob: 005af8fa8b9b5606d1252ffb2318d54e238261d8 [file] [log] [blame]
Eric Andersena1f16bb2000-08-21 22:02:34 +00001/*
2 * getopt.c - Enhanced implementation of BSD getopt(1)
3 * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>
4 *
Bernhard Reutner-Fischerb1629b12006-05-19 19:29:19 +00005 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersena1f16bb2000-08-21 22:02:34 +00006 */
7
8/*
9 * Version 1.0-b4: Tue Sep 23 1997. First public release.
10 * Version 1.0: Wed Nov 19 1997.
11 * Bumped up the version number to 1.0
12 * Fixed minor typo (CSH instead of TCSH)
13 * Version 1.0.1: Tue Jun 3 1998
14 * Fixed sizeof instead of strlen bug
15 * Bumped up the version number to 1.0.1
16 * Version 1.0.2: Thu Jun 11 1998 (not present)
17 * Fixed gcc-2.8.1 warnings
18 * Fixed --version/-V option (not present)
19 * Version 1.0.5: Tue Jun 22 1999
20 * Make -u option work (not present)
21 * Version 1.0.6: Tue Jun 27 2000
22 * No important changes
23 * Version 1.1.0: Tue Jun 30 2000
24 * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
25 * <misiek@misiek.eu.org>)
26 * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
27 * Removed --version/-V and --help/-h in
Eric Andersenaff114c2004-04-14 17:51:38 +000028 * Removed parse_error(), using bb_error_msg() from Busybox instead
Eric Andersena1f16bb2000-08-21 22:02:34 +000029 * Replaced our_malloc with xmalloc and our_realloc with xrealloc
30 *
31 */
32
33#include <stdio.h>
34#include <stdlib.h>
35#include <string.h>
36#include <unistd.h>
37#include <ctype.h>
38#include <getopt.h>
39
Eric Andersen3570a342000-09-25 21:45:58 +000040#include "busybox.h"
Eric Andersena1f16bb2000-08-21 22:02:34 +000041
42/* NON_OPT is the code that is returned when a non-option is found in '+'
43 mode */
Rob Landleybc68cd12006-03-10 19:22:06 +000044enum {
45 NON_OPT = 1,
Eric Andersena1f16bb2000-08-21 22:02:34 +000046/* LONG_OPT is the code that is returned when a long option is found. */
Rob Landleybc68cd12006-03-10 19:22:06 +000047 LONG_OPT = 2
48};
Eric Andersena1f16bb2000-08-21 22:02:34 +000049
50/* The shells recognized. */
51typedef enum {BASH,TCSH} shell_t;
52
53
54/* Some global variables that tells us how to parse. */
Eric Andersen3e6ff902001-03-09 21:24:12 +000055static shell_t shell=BASH; /* The shell we generate output for. */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +000056static int quiet_errors; /* 0 is not quiet. */
57static int quiet_output; /* 0 is not quiet. */
Eric Andersen3e6ff902001-03-09 21:24:12 +000058static int quote=1; /* 1 is do quote. */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +000059static int alternative; /* 0 is getopt_long, 1 is getopt_long_only */
Eric Andersena1f16bb2000-08-21 22:02:34 +000060
61/* Function prototypes */
Eric Andersen3e6ff902001-03-09 21:24:12 +000062static const char *normalize(const char *arg);
63static int generate_output(char * argv[],int argc,const char *optstr,
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000064 const struct option *longopts);
Eric Andersen3e6ff902001-03-09 21:24:12 +000065static void add_long_options(char *options);
66static void add_longopt(const char *name,int has_arg);
67static void set_shell(const char *new_shell);
Eric Andersena1f16bb2000-08-21 22:02:34 +000068
69
70/*
71 * This function 'normalizes' a single argument: it puts single quotes around
72 * it and escapes other special characters. If quote is false, it just
73 * returns its argument.
74 * Bash only needs special treatment for single quotes; tcsh also recognizes
75 * exclamation marks within single quotes, and nukes whitespace.
76 * This function returns a pointer to a buffer that is overwritten by
77 * each call.
78 */
79const char *normalize(const char *arg)
80{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000081 static char *BUFFER=NULL;
82 const char *argptr=arg;
83 char *bufptr;
Eric Andersena1f16bb2000-08-21 22:02:34 +000084
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000085 free(BUFFER);
Eric Andersena1f16bb2000-08-21 22:02:34 +000086
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000087 if (!quote) { /* Just copy arg */
88 BUFFER=bb_xstrdup(arg);
89 return BUFFER;
90 }
Eric Andersena1f16bb2000-08-21 22:02:34 +000091
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000092 /* Each character in arg may take up to four characters in the result:
93 For a quote we need a closing quote, a backslash, a quote and an
94 opening quote! We need also the global opening and closing quote,
95 and one extra character for '\0'. */
96 BUFFER=xmalloc(strlen(arg)*4+3);
Eric Andersena1f16bb2000-08-21 22:02:34 +000097
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000098 bufptr=BUFFER;
99 *bufptr++='\'';
Eric Andersena1f16bb2000-08-21 22:02:34 +0000100
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000101 while (*argptr) {
102 if (*argptr == '\'') {
103 /* Quote: replace it with: '\'' */
104 *bufptr++='\'';
105 *bufptr++='\\';
106 *bufptr++='\'';
107 *bufptr++='\'';
108 } else if (shell==TCSH && *argptr=='!') {
109 /* Exclamation mark: replace it with: \! */
110 *bufptr++='\'';
111 *bufptr++='\\';
112 *bufptr++='!';
113 *bufptr++='\'';
114 } else if (shell==TCSH && *argptr=='\n') {
115 /* Newline: replace it with: \n */
116 *bufptr++='\\';
117 *bufptr++='n';
118 } else if (shell==TCSH && isspace(*argptr)) {
119 /* Non-newline whitespace: replace it with \<ws> */
120 *bufptr++='\'';
121 *bufptr++='\\';
122 *bufptr++=*argptr;
123 *bufptr++='\'';
124 } else
125 /* Just copy */
126 *bufptr++=*argptr;
127 argptr++;
128 }
129 *bufptr++='\'';
130 *bufptr++='\0';
131 return BUFFER;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000132}
133
134/*
135 * Generate the output. argv[0] is the program name (used for reporting errors).
136 * argv[1..] contains the options to be parsed. argc must be the number of
137 * elements in argv (ie. 1 if there are no options, only the program name),
138 * optstr must contain the short options, and longopts the long options.
139 * Other settings are found in global variables.
140 */
141int generate_output(char * argv[],int argc,const char *optstr,
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000142 const struct option *longopts)
Eric Andersena1f16bb2000-08-21 22:02:34 +0000143{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000144 int exit_code = 0; /* We assume everything will be OK */
145 int opt;
146 int longindex;
147 const char *charptr;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000148
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000149 if (quiet_errors) /* No error reporting from getopt(3) */
150 opterr=0;
151 optind=0; /* Reset getopt(3) */
Eric Andersena1f16bb2000-08-21 22:02:34 +0000152
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000153 while ((opt = (alternative?
154 getopt_long_only(argc,argv,optstr,longopts,&longindex):
155 getopt_long(argc,argv,optstr,longopts,&longindex)))
156 != EOF)
157 if (opt == '?' || opt == ':' )
158 exit_code = 1;
159 else if (!quiet_output) {
160 if (opt == LONG_OPT) {
161 printf(" --%s",longopts[longindex].name);
162 if (longopts[longindex].has_arg)
163 printf(" %s",
164 normalize(optarg?optarg:""));
165 } else if (opt == NON_OPT)
166 printf(" %s",normalize(optarg));
167 else {
168 printf(" -%c",opt);
169 charptr = strchr(optstr,opt);
170 if (charptr != NULL && *++charptr == ':')
171 printf(" %s",
172 normalize(optarg?optarg:""));
173 }
174 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000175
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000176 if (! quiet_output) {
177 printf(" --");
178 while (optind < argc)
179 printf(" %s",normalize(argv[optind++]));
180 printf("\n");
181 }
182 return exit_code;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000183}
184
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000185static struct option *long_options;
186static int long_options_length; /* Length of array */
187static int long_options_nr; /* Nr of used elements in array */
Rob Landleybc68cd12006-03-10 19:22:06 +0000188enum { LONG_OPTIONS_INCR = 10 };
Eric Andersena1f16bb2000-08-21 22:02:34 +0000189#define init_longopt() add_longopt(NULL,0)
190
191/* Register a long option. The contents of name is copied. */
192void add_longopt(const char *name,int has_arg)
193{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000194 if (!name) { /* init */
195 free(long_options);
196 long_options=NULL;
197 long_options_length=0;
198 long_options_nr=0;
199 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000200
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000201 if (long_options_nr == long_options_length) {
202 long_options_length += LONG_OPTIONS_INCR;
203 long_options=xrealloc(long_options,
204 sizeof(struct option) *
205 long_options_length);
206 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000207
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000208 long_options[long_options_nr].name=NULL;
209 long_options[long_options_nr].has_arg=0;
210 long_options[long_options_nr].flag=NULL;
211 long_options[long_options_nr].val=0;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000212
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000213 if (long_options_nr) { /* Not for init! */
214 long_options[long_options_nr-1].has_arg=has_arg;
215 long_options[long_options_nr-1].flag=NULL;
216 long_options[long_options_nr-1].val=LONG_OPT;
217 long_options[long_options_nr-1].name=bb_xstrdup(name);
218 }
219 long_options_nr++;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000220}
221
222
223/*
224 * Register several long options. options is a string of long options,
225 * separated by commas or whitespace.
226 * This nukes options!
227 */
228void add_long_options(char *options)
229{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000230 int arg_opt, tlen;
231 char *tokptr=strtok(options,", \t\n");
232 while (tokptr) {
233 arg_opt=no_argument;
Mark Whitleyaf030492001-04-23 23:16:20 +0000234 tlen=strlen(tokptr);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000235 if (tlen > 0) {
236 if (tokptr[tlen-1] == ':') {
237 if (tlen > 1 && tokptr[tlen-2] == ':') {
238 tokptr[tlen-2]='\0';
Mark Whitleyaf030492001-04-23 23:16:20 +0000239 tlen -= 2;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000240 arg_opt=optional_argument;
241 } else {
242 tokptr[tlen-1]='\0';
Mark Whitleyaf030492001-04-23 23:16:20 +0000243 tlen -= 1;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000244 arg_opt=required_argument;
245 }
246 if (tlen == 0)
247 bb_error_msg("empty long option after -l or --long argument");
248 }
249 add_longopt(tokptr,arg_opt);
250 }
251 tokptr=strtok(NULL,", \t\n");
252 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000253}
254
255void set_shell(const char *new_shell)
256{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000257 if (!strcmp(new_shell,"bash"))
258 shell=BASH;
259 else if (!strcmp(new_shell,"tcsh"))
260 shell=TCSH;
261 else if (!strcmp(new_shell,"sh"))
262 shell=BASH;
263 else if (!strcmp(new_shell,"csh"))
264 shell=TCSH;
265 else
266 bb_error_msg("unknown shell after -s or --shell argument");
Eric Andersena1f16bb2000-08-21 22:02:34 +0000267}
268
269
270/* Exit codes:
Eric Andersenaff114c2004-04-14 17:51:38 +0000271 * 0) No errors, successful operation.
Eric Andersena1f16bb2000-08-21 22:02:34 +0000272 * 1) getopt(3) returned an error.
273 * 2) A problem with parameter parsing for getopt(1).
274 * 3) Internal error, out of memory
275 * 4) Returned for -T
276 */
277
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000278static const struct option longopts[]=
Eric Andersena1f16bb2000-08-21 22:02:34 +0000279{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000280 {"options",required_argument,NULL,'o'},
281 {"longoptions",required_argument,NULL,'l'},
282 {"quiet",no_argument,NULL,'q'},
283 {"quiet-output",no_argument,NULL,'Q'},
284 {"shell",required_argument,NULL,'s'},
285 {"test",no_argument,NULL,'T'},
286 {"unquoted",no_argument,NULL,'u'},
287 {"alternative",no_argument,NULL,'a'},
288 {"name",required_argument,NULL,'n'},
289 {NULL,0,NULL,0}
Eric Andersena1f16bb2000-08-21 22:02:34 +0000290};
291
292/* Stop scanning as soon as a non-option argument is found! */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000293static const char shortopts[]="+ao:l:n:qQs:Tu";
Eric Andersena1f16bb2000-08-21 22:02:34 +0000294
Eric Andersena1f16bb2000-08-21 22:02:34 +0000295
296int getopt_main(int argc, char *argv[])
297{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000298 const char *optstr = NULL;
299 char *name = NULL;
300 int opt;
301 int compatible=0;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000302
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000303 init_longopt();
Eric Andersena1f16bb2000-08-21 22:02:34 +0000304
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000305 if (getenv("GETOPT_COMPATIBLE"))
306 compatible=1;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000307
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000308 if (argc == 1) {
309 if (compatible) {
310 /* For some reason, the original getopt gave no error
311 when there were no arguments. */
312 printf(" --\n");
313 return 0;
314 } else
315 bb_error_msg_and_die("missing optstring argument");
316 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000317
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000318 if (argv[1][0] != '-' || compatible) {
Rob Landleydbaf97e2005-09-05 06:16:53 +0000319 char *s;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000320
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000321 quote=0;
322 s=xmalloc(strlen(argv[1])+1);
323 strcpy(s,argv[1]+strspn(argv[1],"-+"));
324 argv[1]=argv[0];
325 return (generate_output(argv+1,argc-1,s,long_options));
326 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000327
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000328 while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
329 switch (opt) {
330 case 'a':
331 alternative=1;
332 break;
333 case 'o':
334 optstr = optarg;
335 break;
336 case 'l':
337 add_long_options(optarg);
338 break;
339 case 'n':
340 name = optarg;
341 break;
342 case 'q':
343 quiet_errors=1;
344 break;
345 case 'Q':
346 quiet_output=1;
347 break;
348 case 's':
349 set_shell(optarg);
350 break;
351 case 'T':
352 return 4;
353 case 'u':
354 quote=0;
355 break;
356 default:
357 bb_show_usage();
358 }
359
360 if (!optstr) {
361 if (optind >= argc)
362 bb_error_msg_and_die("missing optstring argument");
363 else optstr=argv[optind++];
364 }
365 if (name)
366 argv[optind-1]=name;
367 else
368 argv[optind-1]=argv[0];
Robert Griebld378c312002-07-19 00:05:54 +0000369 return (generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
Eric Andersena1f16bb2000-08-21 22:02:34 +0000370}