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 tarball for details. 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 in 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#include <getopt.h> 35#include "libbb.h" 36 37/* NON_OPT is the code that is returned when a non-option is found in '+' 38 mode */ 39enum { 40 NON_OPT = 1, 41#if ENABLE_FEATURE_GETOPT_LONG 42/* LONG_OPT is the code that is returned when a long option is found. */ 43 LONG_OPT = 2 44#endif 45}; 46 47/* For finding activated option flags. Must match getopt32 call! */ 48enum { 49 OPT_o = 0x1, // -o 50 OPT_n = 0x2, // -n 51 OPT_q = 0x4, // -q 52 OPT_Q = 0x8, // -Q 53 OPT_s = 0x10, // -s 54 OPT_T = 0x20, // -T 55 OPT_u = 0x40, // -u 56#if ENABLE_FEATURE_GETOPT_LONG 57 OPT_a = 0x80, // -a 58 OPT_l = 0x100, // -l 59#endif 60 SHELL_IS_TCSH = 0x8000, /* hijack this bit for other purposes */ 61}; 62 63/* 0 is getopt_long, 1 is getopt_long_only */ 64#define alternative (option_mask32 & OPT_a) 65 66#define quiet_errors (option_mask32 & OPT_q) 67#define quiet_output (option_mask32 & OPT_Q) 68#define quote (!(option_mask32 & OPT_u)) 69#define shell_TCSH (option_mask32 & SHELL_IS_TCSH) 70 71/* 72 * This function 'normalizes' a single argument: it puts single quotes around 73 * it and escapes other special characters. If quote is false, it just 74 * returns its argument. 75 * Bash only needs special treatment for single quotes; tcsh also recognizes 76 * exclamation marks within single quotes, and nukes whitespace. 77 * This function returns a pointer to a buffer that is overwritten by 78 * each call. 79 */ 80static const char *normalize(const char *arg) 81{ 82 char *bufptr; 83#if ENABLE_FEATURE_CLEAN_UP 84 static char *BUFFER = NULL; 85 free(BUFFER); 86#else 87 char *BUFFER; 88#endif 89 90 if (!quote) { /* Just copy arg */ 91 BUFFER = xstrdup(arg); 92 return BUFFER; 93 } 94 95 /* Each character in arg may take up to four characters in the result: 96 For a quote we need a closing quote, a backslash, a quote and an 97 opening quote! We need also the global opening and closing quote, 98 and one extra character for '\0'. */ 99 BUFFER = xmalloc(strlen(arg)*4 + 3); 100 101 bufptr = BUFFER; 102 *bufptr ++= '\''; 103 104 while (*arg) { 105 if (*arg == '\'') { 106 /* Quote: replace it with: '\'' */ 107 *bufptr ++= '\''; 108 *bufptr ++= '\\'; 109 *bufptr ++= '\''; 110 *bufptr ++= '\''; 111 } else if (shell_TCSH && *arg == '!') { 112 /* Exclamation mark: replace it with: \! */ 113 *bufptr ++= '\''; 114 *bufptr ++= '\\'; 115 *bufptr ++= '!'; 116 *bufptr ++= '\''; 117 } else if (shell_TCSH && *arg == '\n') { 118 /* Newline: replace it with: \n */ 119 *bufptr ++= '\\'; 120 *bufptr ++= 'n'; 121 } else if (shell_TCSH && isspace(*arg)) { 122 /* Non-newline whitespace: replace it with \<ws> */ 123 *bufptr ++= '\''; 124 *bufptr ++= '\\'; 125 *bufptr ++= *arg; 126 *bufptr ++= '\''; 127 } else 128 /* Just copy */ 129 *bufptr ++= *arg; 130 arg++; 131 } 132 *bufptr ++= '\''; 133 *bufptr ++= '\0'; 134 return BUFFER; 135} 136 137/* 138 * Generate the output. argv[0] is the program name (used for reporting errors). 139 * argv[1..] contains the options to be parsed. argc must be the number of 140 * elements in argv (ie. 1 if there are no options, only the program name), 141 * optstr must contain the short options, and longopts the long options. 142 * Other settings are found in global variables. 143 */ 144#if !ENABLE_FEATURE_GETOPT_LONG 145#define generate_output(argv,argc,optstr,longopts) \ 146 generate_output(argv,argc,optstr) 147#endif 148static int generate_output(char **argv, int argc, const char *optstr, const struct option *longopts) 149{ 150 int exit_code = 0; /* We assume everything will be OK */ 151 int opt; 152#if ENABLE_FEATURE_GETOPT_LONG 153 int longindex; 154#endif 155 const char *charptr; 156 157 if (quiet_errors) /* No error reporting from getopt(3) */ 158 opterr = 0; 159 160 /* We used it already in main() in getopt32(), 161 * we *must* reset getopt(3): */ 162#ifdef __GLIBC__ 163 optind = 0; 164#else /* BSD style */ 165 optind = 1; 166 /* optreset = 1; */ 167#endif 168 169 while (1) { 170 opt = 171#if ENABLE_FEATURE_GETOPT_LONG 172 alternative ? 173 getopt_long_only(argc, argv, optstr, longopts, &longindex) : 174 getopt_long(argc, argv, optstr, longopts, &longindex); 175#else 176 getopt(argc, argv, optstr); 177#endif 178 if (opt == -1) 179 break; 180 if (opt == '?' || opt == ':' ) 181 exit_code = 1; 182 else if (!quiet_output) { 183#if ENABLE_FEATURE_GETOPT_LONG 184 if (opt == LONG_OPT) { 185 printf(" --%s", longopts[longindex].name); 186 if (longopts[longindex].has_arg) 187 printf(" %s", 188 normalize(optarg ? optarg : "")); 189 } else 190#endif 191 if (opt == NON_OPT) 192 printf(" %s", normalize(optarg)); 193 else { 194 printf(" -%c", opt); 195 charptr = strchr(optstr, opt); 196 if (charptr != NULL && *++charptr == ':') 197 printf(" %s", 198 normalize(optarg ? optarg : "")); 199 } 200 } 201 } 202 203 if (!quiet_output) { 204 printf(" --"); 205 while (optind < argc) 206 printf(" %s", normalize(argv[optind++])); 207 bb_putchar('\n'); 208 } 209 return exit_code; 210} 211 212#if ENABLE_FEATURE_GETOPT_LONG 213/* 214 * Register several long options. options is a string of long options, 215 * separated by commas or whitespace. 216 * This nukes options! 217 */ 218static struct option *add_long_options(struct option *long_options, char *options) 219{ 220 int long_nr = 0; 221 int arg_opt, tlen; 222 char *tokptr = strtok(options, ", \t\n"); 223 224 if (long_options) 225 while (long_options[long_nr].name) 226 long_nr++; 227 228 while (tokptr) { 229 arg_opt = no_argument; 230 tlen = strlen(tokptr); 231 if (tlen) { 232 tlen--; 233 if (tokptr[tlen] == ':') { 234 arg_opt = required_argument; 235 if (tlen && tokptr[tlen-1] == ':') { 236 tlen--; 237 arg_opt = optional_argument; 238 } 239 tokptr[tlen] = '\0'; 240 if (tlen == 0) 241 bb_error_msg_and_die("empty long option specified"); 242 } 243 long_options = xrealloc_vector(long_options, 4, long_nr); 244 long_options[long_nr].has_arg = arg_opt; 245 /*long_options[long_nr].flag = NULL; - xrealloc_vector did it */ 246 long_options[long_nr].val = LONG_OPT; 247 long_options[long_nr].name = xstrdup(tokptr); 248 long_nr++; 249 /*memset(&long_options[long_nr], 0, sizeof(long_options[0])); - xrealloc_vector did it */ 250 } 251 tokptr = strtok(NULL, ", \t\n"); 252 } 253 return long_options; 254} 255#endif 256 257static void set_shell(const char *new_shell) 258{ 259 if (!strcmp(new_shell, "bash") || !strcmp(new_shell, "sh")) 260 return; 261 if (!strcmp(new_shell, "tcsh") || !strcmp(new_shell, "csh")) 262 option_mask32 |= SHELL_IS_TCSH; 263 else 264 bb_error_msg("unknown shell '%s', assuming bash", new_shell); 265} 266 267 268/* Exit codes: 269 * 0) No errors, successful operation. 270 * 1) getopt(3) returned an error. 271 * 2) A problem with parameter parsing for getopt(1). 272 * 3) Internal error, out of memory 273 * 4) Returned for -T 274 */ 275 276#if ENABLE_FEATURE_GETOPT_LONG 277static const char getopt_longopts[] ALIGN1 = 278 "options\0" Required_argument "o" 279 "longoptions\0" Required_argument "l" 280 "quiet\0" No_argument "q" 281 "quiet-output\0" No_argument "Q" 282 "shell\0" Required_argument "s" 283 "test\0" No_argument "T" 284 "unquoted\0" No_argument "u" 285 "alternative\0" No_argument "a" 286 "name\0" Required_argument "n" 287 ; 288#endif 289 290int getopt_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 291int getopt_main(int argc, char **argv) 292{ 293 char *optstr = NULL; 294 char *name = NULL; 295 unsigned opt; 296 const char *compatible; 297 char *s_arg; 298#if ENABLE_FEATURE_GETOPT_LONG 299 struct option *long_options = NULL; 300 llist_t *l_arg = NULL; 301#endif 302 303 compatible = getenv("GETOPT_COMPATIBLE"); /* used as yes/no flag */ 304 305 if (argc == 1) { 306 if (compatible) { 307 /* For some reason, the original getopt gave no error 308 when there were no arguments. */ 309 printf(" --\n"); 310 return 0; 311 } 312 bb_error_msg_and_die("missing optstring argument"); 313 } 314 315 if (argv[1][0] != '-' || compatible) { 316 char *s; 317 318 option_mask32 |= OPT_u; /* quoting off */ 319 s = xstrdup(argv[1] + strspn(argv[1], "-+")); 320 argv[1] = argv[0]; 321 return generate_output(argv+1, argc-1, s, long_options); 322 } 323 324#if !ENABLE_FEATURE_GETOPT_LONG 325 opt = getopt32(argv, "+o:n:qQs:Tu", &optstr, &name, &s_arg); 326#else 327 applet_long_options = getopt_longopts; 328 opt_complementary = "l::"; 329 opt = getopt32(argv, "+o:n:qQs:Tual:", 330 &optstr, &name, &s_arg, &l_arg); 331 /* Effectuate the read options for the applet itself */ 332 while (l_arg) { 333 long_options = add_long_options(long_options, llist_pop(&l_arg)); 334 } 335#endif 336 337 if (opt & OPT_s) { 338 set_shell(s_arg); 339 } 340 341 if (opt & OPT_T) { 342 return 4; 343 } 344 345 /* All options controlling the applet have now been parsed */ 346 if (!optstr) { 347 if (optind >= argc) 348 bb_error_msg_and_die("missing optstring argument"); 349 optstr = argv[optind++]; 350 } 351 352 argv[optind-1] = name ? name : argv[0]; 353 return generate_output(argv+optind-1, argc-optind+1, optstr, long_options); 354} 355