[821] | 1 | /*
|
---|
| 2 | * getopt.c - Enhanced implementation of BSD getopt(1)
|
---|
| 3 | * Copyright (c) 1997, 1998, 1999, 2000 Frodo Looijaard <frodol@dds.nl>
|
---|
| 4 | *
|
---|
| 5 | * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
|
---|
| 6 | */
|
---|
| 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
|
---|
| 28 | * Removed parse_error(), using bb_error_msg() from Busybox instead
|
---|
| 29 | * 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 |
|
---|
| 40 | #include "busybox.h"
|
---|
| 41 |
|
---|
| 42 | /* NON_OPT is the code that is returned when a non-option is found in '+'
|
---|
| 43 | mode */
|
---|
| 44 | enum {
|
---|
| 45 | NON_OPT = 1,
|
---|
| 46 | /* LONG_OPT is the code that is returned when a long option is found. */
|
---|
| 47 | LONG_OPT = 2
|
---|
| 48 | };
|
---|
| 49 |
|
---|
| 50 | /* The shells recognized. */
|
---|
| 51 | typedef enum {BASH,TCSH} shell_t;
|
---|
| 52 |
|
---|
| 53 |
|
---|
| 54 | /* Some global variables that tells us how to parse. */
|
---|
| 55 | static shell_t shell=BASH; /* The shell we generate output for. */
|
---|
| 56 | static int quiet_errors; /* 0 is not quiet. */
|
---|
| 57 | static int quiet_output; /* 0 is not quiet. */
|
---|
| 58 | static int quote=1; /* 1 is do quote. */
|
---|
| 59 | static int alternative; /* 0 is getopt_long, 1 is getopt_long_only */
|
---|
| 60 |
|
---|
| 61 | /* Function prototypes */
|
---|
| 62 | static const char *normalize(const char *arg);
|
---|
| 63 | static int generate_output(char * argv[],int argc,const char *optstr,
|
---|
| 64 | const struct option *longopts);
|
---|
| 65 | static void add_long_options(char *options);
|
---|
| 66 | static void add_longopt(const char *name,int has_arg);
|
---|
| 67 | static void set_shell(const char *new_shell);
|
---|
| 68 |
|
---|
| 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 | */
|
---|
| 79 | const char *normalize(const char *arg)
|
---|
| 80 | {
|
---|
| 81 | static char *BUFFER=NULL;
|
---|
| 82 | const char *argptr=arg;
|
---|
| 83 | char *bufptr;
|
---|
| 84 |
|
---|
| 85 | free(BUFFER);
|
---|
| 86 |
|
---|
| 87 | if (!quote) { /* Just copy arg */
|
---|
| 88 | BUFFER=bb_xstrdup(arg);
|
---|
| 89 | return BUFFER;
|
---|
| 90 | }
|
---|
| 91 |
|
---|
| 92 | /* 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);
|
---|
| 97 |
|
---|
| 98 | bufptr=BUFFER;
|
---|
| 99 | *bufptr++='\'';
|
---|
| 100 |
|
---|
| 101 | 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;
|
---|
| 132 | }
|
---|
| 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 | */
|
---|
| 141 | int generate_output(char * argv[],int argc,const char *optstr,
|
---|
| 142 | const struct option *longopts)
|
---|
| 143 | {
|
---|
| 144 | int exit_code = 0; /* We assume everything will be OK */
|
---|
| 145 | int opt;
|
---|
| 146 | int longindex;
|
---|
| 147 | const char *charptr;
|
---|
| 148 |
|
---|
| 149 | if (quiet_errors) /* No error reporting from getopt(3) */
|
---|
| 150 | opterr=0;
|
---|
| 151 | optind=0; /* Reset getopt(3) */
|
---|
| 152 |
|
---|
| 153 | 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 | }
|
---|
| 175 |
|
---|
| 176 | if (! quiet_output) {
|
---|
| 177 | printf(" --");
|
---|
| 178 | while (optind < argc)
|
---|
| 179 | printf(" %s",normalize(argv[optind++]));
|
---|
| 180 | printf("\n");
|
---|
| 181 | }
|
---|
| 182 | return exit_code;
|
---|
| 183 | }
|
---|
| 184 |
|
---|
| 185 | static struct option *long_options;
|
---|
| 186 | static int long_options_length; /* Length of array */
|
---|
| 187 | static int long_options_nr; /* Nr of used elements in array */
|
---|
| 188 | enum { LONG_OPTIONS_INCR = 10 };
|
---|
| 189 | #define init_longopt() add_longopt(NULL,0)
|
---|
| 190 |
|
---|
| 191 | /* Register a long option. The contents of name is copied. */
|
---|
| 192 | void add_longopt(const char *name,int has_arg)
|
---|
| 193 | {
|
---|
| 194 | if (!name) { /* init */
|
---|
| 195 | free(long_options);
|
---|
| 196 | long_options=NULL;
|
---|
| 197 | long_options_length=0;
|
---|
| 198 | long_options_nr=0;
|
---|
| 199 | }
|
---|
| 200 |
|
---|
| 201 | 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 | }
|
---|
| 207 |
|
---|
| 208 | 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;
|
---|
| 212 |
|
---|
| 213 | 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++;
|
---|
| 220 | }
|
---|
| 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 | */
|
---|
| 228 | void add_long_options(char *options)
|
---|
| 229 | {
|
---|
| 230 | int arg_opt, tlen;
|
---|
| 231 | char *tokptr=strtok(options,", \t\n");
|
---|
| 232 | while (tokptr) {
|
---|
| 233 | arg_opt=no_argument;
|
---|
| 234 | tlen=strlen(tokptr);
|
---|
| 235 | if (tlen > 0) {
|
---|
| 236 | if (tokptr[tlen-1] == ':') {
|
---|
| 237 | if (tlen > 1 && tokptr[tlen-2] == ':') {
|
---|
| 238 | tokptr[tlen-2]='\0';
|
---|
| 239 | tlen -= 2;
|
---|
| 240 | arg_opt=optional_argument;
|
---|
| 241 | } else {
|
---|
| 242 | tokptr[tlen-1]='\0';
|
---|
| 243 | tlen -= 1;
|
---|
| 244 | 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 | }
|
---|
| 253 | }
|
---|
| 254 |
|
---|
| 255 | void set_shell(const char *new_shell)
|
---|
| 256 | {
|
---|
| 257 | 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");
|
---|
| 267 | }
|
---|
| 268 |
|
---|
| 269 |
|
---|
| 270 | /* Exit codes:
|
---|
| 271 | * 0) No errors, successful operation.
|
---|
| 272 | * 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 |
|
---|
| 278 | static const struct option longopts[]=
|
---|
| 279 | {
|
---|
| 280 | {"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}
|
---|
| 290 | };
|
---|
| 291 |
|
---|
| 292 | /* Stop scanning as soon as a non-option argument is found! */
|
---|
| 293 | static const char shortopts[]="+ao:l:n:qQs:Tu";
|
---|
| 294 |
|
---|
| 295 |
|
---|
| 296 | int getopt_main(int argc, char *argv[])
|
---|
| 297 | {
|
---|
| 298 | const char *optstr = NULL;
|
---|
| 299 | char *name = NULL;
|
---|
| 300 | int opt;
|
---|
| 301 | int compatible=0;
|
---|
| 302 |
|
---|
| 303 | init_longopt();
|
---|
| 304 |
|
---|
| 305 | if (getenv("GETOPT_COMPATIBLE"))
|
---|
| 306 | compatible=1;
|
---|
| 307 |
|
---|
| 308 | 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 | }
|
---|
| 317 |
|
---|
| 318 | if (argv[1][0] != '-' || compatible) {
|
---|
| 319 | char *s;
|
---|
| 320 |
|
---|
| 321 | 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 | }
|
---|
| 327 |
|
---|
| 328 | 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];
|
---|
| 369 | return (generate_output(argv+optind-1,argc-optind+1,optstr,long_options));
|
---|
| 370 | }
|
---|