blob: 9e33b79bf6ab0c28569804e42940c4ec4d6ca64d [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 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20/*
21 * Version 1.0-b4: Tue Sep 23 1997. First public release.
22 * Version 1.0: Wed Nov 19 1997.
23 * Bumped up the version number to 1.0
24 * Fixed minor typo (CSH instead of TCSH)
25 * Version 1.0.1: Tue Jun 3 1998
26 * Fixed sizeof instead of strlen bug
27 * Bumped up the version number to 1.0.1
28 * Version 1.0.2: Thu Jun 11 1998 (not present)
29 * Fixed gcc-2.8.1 warnings
30 * Fixed --version/-V option (not present)
31 * Version 1.0.5: Tue Jun 22 1999
32 * Make -u option work (not present)
33 * Version 1.0.6: Tue Jun 27 2000
34 * No important changes
35 * Version 1.1.0: Tue Jun 30 2000
36 * Added NLS support (partly written by Arkadiusz Mi<B6>kiewicz
37 * <misiek@misiek.eu.org>)
38 * Ported to Busybox - Alfred M. Szmidt <ams@trillian.itslinux.org>
39 * Removed --version/-V and --help/-h in
Eric Andersenaff114c2004-04-14 17:51:38 +000040 * Removed parse_error(), using bb_error_msg() from Busybox instead
Eric Andersena1f16bb2000-08-21 22:02:34 +000041 * Replaced our_malloc with xmalloc and our_realloc with xrealloc
42 *
43 */
44
45#include <stdio.h>
46#include <stdlib.h>
47#include <string.h>
48#include <unistd.h>
49#include <ctype.h>
50#include <getopt.h>
51
Eric Andersen3570a342000-09-25 21:45:58 +000052#include "busybox.h"
Eric Andersena1f16bb2000-08-21 22:02:34 +000053
54/* NON_OPT is the code that is returned when a non-option is found in '+'
55 mode */
Rob Landleybc68cd12006-03-10 19:22:06 +000056enum {
57 NON_OPT = 1,
Eric Andersena1f16bb2000-08-21 22:02:34 +000058/* LONG_OPT is the code that is returned when a long option is found. */
Rob Landleybc68cd12006-03-10 19:22:06 +000059 LONG_OPT = 2
60};
Eric Andersena1f16bb2000-08-21 22:02:34 +000061
62/* The shells recognized. */
63typedef enum {BASH,TCSH} shell_t;
64
65
66/* Some global variables that tells us how to parse. */
Eric Andersen3e6ff902001-03-09 21:24:12 +000067static shell_t shell=BASH; /* The shell we generate output for. */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +000068static int quiet_errors; /* 0 is not quiet. */
69static int quiet_output; /* 0 is not quiet. */
Eric Andersen3e6ff902001-03-09 21:24:12 +000070static int quote=1; /* 1 is do quote. */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +000071static int alternative; /* 0 is getopt_long, 1 is getopt_long_only */
Eric Andersena1f16bb2000-08-21 22:02:34 +000072
73/* Function prototypes */
Eric Andersen3e6ff902001-03-09 21:24:12 +000074static const char *normalize(const char *arg);
75static int generate_output(char * argv[],int argc,const char *optstr,
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000076 const struct option *longopts);
Eric Andersen3e6ff902001-03-09 21:24:12 +000077static void add_long_options(char *options);
78static void add_longopt(const char *name,int has_arg);
79static void set_shell(const char *new_shell);
Eric Andersena1f16bb2000-08-21 22:02:34 +000080
81
82/*
83 * This function 'normalizes' a single argument: it puts single quotes around
84 * it and escapes other special characters. If quote is false, it just
85 * returns its argument.
86 * Bash only needs special treatment for single quotes; tcsh also recognizes
87 * exclamation marks within single quotes, and nukes whitespace.
88 * This function returns a pointer to a buffer that is overwritten by
89 * each call.
90 */
91const char *normalize(const char *arg)
92{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000093 static char *BUFFER=NULL;
94 const char *argptr=arg;
95 char *bufptr;
Eric Andersena1f16bb2000-08-21 22:02:34 +000096
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000097 free(BUFFER);
Eric Andersena1f16bb2000-08-21 22:02:34 +000098
Tim Rikerc1ef7bd2006-01-25 00:08:53 +000099 if (!quote) { /* Just copy arg */
100 BUFFER=bb_xstrdup(arg);
101 return BUFFER;
102 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000103
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000104 /* Each character in arg may take up to four characters in the result:
105 For a quote we need a closing quote, a backslash, a quote and an
106 opening quote! We need also the global opening and closing quote,
107 and one extra character for '\0'. */
108 BUFFER=xmalloc(strlen(arg)*4+3);
Eric Andersena1f16bb2000-08-21 22:02:34 +0000109
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000110 bufptr=BUFFER;
111 *bufptr++='\'';
Eric Andersena1f16bb2000-08-21 22:02:34 +0000112
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000113 while (*argptr) {
114 if (*argptr == '\'') {
115 /* Quote: replace it with: '\'' */
116 *bufptr++='\'';
117 *bufptr++='\\';
118 *bufptr++='\'';
119 *bufptr++='\'';
120 } else if (shell==TCSH && *argptr=='!') {
121 /* Exclamation mark: replace it with: \! */
122 *bufptr++='\'';
123 *bufptr++='\\';
124 *bufptr++='!';
125 *bufptr++='\'';
126 } else if (shell==TCSH && *argptr=='\n') {
127 /* Newline: replace it with: \n */
128 *bufptr++='\\';
129 *bufptr++='n';
130 } else if (shell==TCSH && isspace(*argptr)) {
131 /* Non-newline whitespace: replace it with \<ws> */
132 *bufptr++='\'';
133 *bufptr++='\\';
134 *bufptr++=*argptr;
135 *bufptr++='\'';
136 } else
137 /* Just copy */
138 *bufptr++=*argptr;
139 argptr++;
140 }
141 *bufptr++='\'';
142 *bufptr++='\0';
143 return BUFFER;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000144}
145
146/*
147 * Generate the output. argv[0] is the program name (used for reporting errors).
148 * argv[1..] contains the options to be parsed. argc must be the number of
149 * elements in argv (ie. 1 if there are no options, only the program name),
150 * optstr must contain the short options, and longopts the long options.
151 * Other settings are found in global variables.
152 */
153int generate_output(char * argv[],int argc,const char *optstr,
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000154 const struct option *longopts)
Eric Andersena1f16bb2000-08-21 22:02:34 +0000155{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000156 int exit_code = 0; /* We assume everything will be OK */
157 int opt;
158 int longindex;
159 const char *charptr;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000160
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000161 if (quiet_errors) /* No error reporting from getopt(3) */
162 opterr=0;
163 optind=0; /* Reset getopt(3) */
Eric Andersena1f16bb2000-08-21 22:02:34 +0000164
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000165 while ((opt = (alternative?
166 getopt_long_only(argc,argv,optstr,longopts,&longindex):
167 getopt_long(argc,argv,optstr,longopts,&longindex)))
168 != EOF)
169 if (opt == '?' || opt == ':' )
170 exit_code = 1;
171 else if (!quiet_output) {
172 if (opt == LONG_OPT) {
173 printf(" --%s",longopts[longindex].name);
174 if (longopts[longindex].has_arg)
175 printf(" %s",
176 normalize(optarg?optarg:""));
177 } else if (opt == NON_OPT)
178 printf(" %s",normalize(optarg));
179 else {
180 printf(" -%c",opt);
181 charptr = strchr(optstr,opt);
182 if (charptr != NULL && *++charptr == ':')
183 printf(" %s",
184 normalize(optarg?optarg:""));
185 }
186 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000187
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000188 if (! quiet_output) {
189 printf(" --");
190 while (optind < argc)
191 printf(" %s",normalize(argv[optind++]));
192 printf("\n");
193 }
194 return exit_code;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000195}
196
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000197static struct option *long_options;
198static int long_options_length; /* Length of array */
199static int long_options_nr; /* Nr of used elements in array */
Rob Landleybc68cd12006-03-10 19:22:06 +0000200enum { LONG_OPTIONS_INCR = 10 };
Eric Andersena1f16bb2000-08-21 22:02:34 +0000201#define init_longopt() add_longopt(NULL,0)
202
203/* Register a long option. The contents of name is copied. */
204void add_longopt(const char *name,int has_arg)
205{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000206 if (!name) { /* init */
207 free(long_options);
208 long_options=NULL;
209 long_options_length=0;
210 long_options_nr=0;
211 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000212
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000213 if (long_options_nr == long_options_length) {
214 long_options_length += LONG_OPTIONS_INCR;
215 long_options=xrealloc(long_options,
216 sizeof(struct option) *
217 long_options_length);
218 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000219
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000220 long_options[long_options_nr].name=NULL;
221 long_options[long_options_nr].has_arg=0;
222 long_options[long_options_nr].flag=NULL;
223 long_options[long_options_nr].val=0;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000224
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000225 if (long_options_nr) { /* Not for init! */
226 long_options[long_options_nr-1].has_arg=has_arg;
227 long_options[long_options_nr-1].flag=NULL;
228 long_options[long_options_nr-1].val=LONG_OPT;
229 long_options[long_options_nr-1].name=bb_xstrdup(name);
230 }
231 long_options_nr++;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000232}
233
234
235/*
236 * Register several long options. options is a string of long options,
237 * separated by commas or whitespace.
238 * This nukes options!
239 */
240void add_long_options(char *options)
241{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000242 int arg_opt, tlen;
243 char *tokptr=strtok(options,", \t\n");
244 while (tokptr) {
245 arg_opt=no_argument;
Mark Whitleyaf030492001-04-23 23:16:20 +0000246 tlen=strlen(tokptr);
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000247 if (tlen > 0) {
248 if (tokptr[tlen-1] == ':') {
249 if (tlen > 1 && tokptr[tlen-2] == ':') {
250 tokptr[tlen-2]='\0';
Mark Whitleyaf030492001-04-23 23:16:20 +0000251 tlen -= 2;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000252 arg_opt=optional_argument;
253 } else {
254 tokptr[tlen-1]='\0';
Mark Whitleyaf030492001-04-23 23:16:20 +0000255 tlen -= 1;
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000256 arg_opt=required_argument;
257 }
258 if (tlen == 0)
259 bb_error_msg("empty long option after -l or --long argument");
260 }
261 add_longopt(tokptr,arg_opt);
262 }
263 tokptr=strtok(NULL,", \t\n");
264 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000265}
266
267void set_shell(const char *new_shell)
268{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000269 if (!strcmp(new_shell,"bash"))
270 shell=BASH;
271 else if (!strcmp(new_shell,"tcsh"))
272 shell=TCSH;
273 else if (!strcmp(new_shell,"sh"))
274 shell=BASH;
275 else if (!strcmp(new_shell,"csh"))
276 shell=TCSH;
277 else
278 bb_error_msg("unknown shell after -s or --shell argument");
Eric Andersena1f16bb2000-08-21 22:02:34 +0000279}
280
281
282/* Exit codes:
Eric Andersenaff114c2004-04-14 17:51:38 +0000283 * 0) No errors, successful operation.
Eric Andersena1f16bb2000-08-21 22:02:34 +0000284 * 1) getopt(3) returned an error.
285 * 2) A problem with parameter parsing for getopt(1).
286 * 3) Internal error, out of memory
287 * 4) Returned for -T
288 */
289
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000290static const struct option longopts[]=
Eric Andersena1f16bb2000-08-21 22:02:34 +0000291{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000292 {"options",required_argument,NULL,'o'},
293 {"longoptions",required_argument,NULL,'l'},
294 {"quiet",no_argument,NULL,'q'},
295 {"quiet-output",no_argument,NULL,'Q'},
296 {"shell",required_argument,NULL,'s'},
297 {"test",no_argument,NULL,'T'},
298 {"unquoted",no_argument,NULL,'u'},
299 {"alternative",no_argument,NULL,'a'},
300 {"name",required_argument,NULL,'n'},
301 {NULL,0,NULL,0}
Eric Andersena1f16bb2000-08-21 22:02:34 +0000302};
303
304/* Stop scanning as soon as a non-option argument is found! */
"Vladimir N. Oleynik"5cf9a032005-10-19 09:21:51 +0000305static const char shortopts[]="+ao:l:n:qQs:Tu";
Eric Andersena1f16bb2000-08-21 22:02:34 +0000306
Eric Andersena1f16bb2000-08-21 22:02:34 +0000307
308int getopt_main(int argc, char *argv[])
309{
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000310 const char *optstr = NULL;
311 char *name = NULL;
312 int opt;
313 int compatible=0;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000314
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000315 init_longopt();
Eric Andersena1f16bb2000-08-21 22:02:34 +0000316
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000317 if (getenv("GETOPT_COMPATIBLE"))
318 compatible=1;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000319
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000320 if (argc == 1) {
321 if (compatible) {
322 /* For some reason, the original getopt gave no error
323 when there were no arguments. */
324 printf(" --\n");
325 return 0;
326 } else
327 bb_error_msg_and_die("missing optstring argument");
328 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000329
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000330 if (argv[1][0] != '-' || compatible) {
Rob Landleydbaf97e2005-09-05 06:16:53 +0000331 char *s;
Eric Andersena1f16bb2000-08-21 22:02:34 +0000332
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000333 quote=0;
334 s=xmalloc(strlen(argv[1])+1);
335 strcpy(s,argv[1]+strspn(argv[1],"-+"));
336 argv[1]=argv[0];
337 return (generate_output(argv+1,argc-1,s,long_options));
338 }
Eric Andersena1f16bb2000-08-21 22:02:34 +0000339
Tim Rikerc1ef7bd2006-01-25 00:08:53 +0000340 while ((opt=getopt_long(argc,argv,shortopts,longopts,NULL)) != EOF)
341 switch (opt) {
342 case 'a':
343 alternative=1;
344 break;
345 case 'o':
346 optstr = optarg;
347 break;
348 case 'l':
349 add_long_options(optarg);
350 break;
351 case 'n':
352 name = optarg;
353 break;
354 case 'q':
355 quiet_errors=1;
356 break;
357 case 'Q':
358 quiet_output=1;
359 break;
360 case 's':
361 set_shell(optarg);
362 break;
363 case 'T':
364 return 4;
365 case 'u':
366 quote=0;
367 break;
368 default:
369 bb_show_usage();
370 }
371
372 if (!optstr) {
373 if (optind >= argc)
374 bb_error_msg_and_die("missing optstring argument");
375 else optstr=argv[optind++];
376 }
377 if (name)
378 argv[optind-1]=name;
379 else
380 argv[optind-1]=argv[0];
Robert Griebld378c312002-07-19 00:05:54 +0000381 return (generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
Eric Andersena1f16bb2000-08-21 22:02:34 +0000382}
383
384/*
385 Local Variables:
386 c-file-style: "linux"
387 c-basic-offset: 4
388 tab-width: 4
389 End:
390*/