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