busybox/util-linux/getopt.c
<<
>>
Prefs
   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