busybox/libbb/getopt32.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * universal getopt32 implementation for busybox
   4 *
   5 * Copyright (C) 2003-2005  Vladimir Oleynik  <dzo@simtreas.ru>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9#if ENABLE_LONG_OPTS
  10# include <getopt.h>
  11#endif
  12#include "libbb.h"
  13
  14//kbuild:lib-y += getopt32.o
  15
  16/*      Documentation
  17
  18uint32_t
  19getopt32(char **argv, const char *applet_opts, ...)
  20
  21        The command line options are passed as the applet_opts string.
  22
  23        If one of the given options is found, a flag value is added to
  24        the return value.
  25
  26        The flag value is determined by the position of the char in
  27        applet_opts string.  For example:
  28
  29        flags = getopt32(argv, "rnug");
  30
  31        "r" will set 1    (bit 0)
  32        "n" will set 2    (bit 1)
  33        "u" will set 4    (bit 2)
  34        "g" will set 8    (bit 3)
  35
  36        and so on.  You can also look at the return value as a bit
  37        field and each option sets one bit.
  38
  39        On exit, global variable optind is set so that if you
  40        will do argc -= optind; argv += optind; then
  41        argc will be equal to number of remaining non-option
  42        arguments, first one would be in argv[0], next in argv[1] and so on
  43        (options and their parameters will be moved into argv[]
  44        positions prior to argv[optind]).
  45
  46 "o:"   If one of the options requires an argument, then add a ":"
  47        after the char in applet_opts and provide a pointer to store
  48        the argument.  For example:
  49
  50        char *pointer_to_arg_for_a;
  51        char *pointer_to_arg_for_b;
  52        char *pointer_to_arg_for_c;
  53        char *pointer_to_arg_for_d;
  54
  55        flags = getopt32(argv, "a:b:c:d:",
  56                        &pointer_to_arg_for_a, &pointer_to_arg_for_b,
  57                        &pointer_to_arg_for_c, &pointer_to_arg_for_d);
  58
  59        The type of the pointer may be controlled by "o::" or "o+" in
  60        the external string opt_complementary (see below for more info).
  61
  62 "o::"  If option can have an *optional* argument, then add a "::"
  63        after its char in applet_opts and provide a pointer to store
  64        the argument.  Note that optional arguments _must_
  65        immediately follow the option: -oparam, not -o param.
  66
  67 "o:+"  This means that the parameter for this option is a nonnegative integer.
  68        It will be processed with xatoi_positive() - allowed range
  69        is 0..INT_MAX.
  70
  71        int param;  // "unsigned param;" will also work
  72        getopt32(argv, "p:+", &param);
  73
  74 "o:*"  This means that the option can occur multiple times. Each occurrence
  75        will be saved as a llist_t element instead of char*.
  76
  77        For example:
  78        The grep applet can have one or more "-e pattern" arguments.
  79        In this case you should use getopt32() as follows:
  80
  81        llist_t *patterns = NULL;
  82
  83        (this pointer must be initializated to NULL if the list is empty
  84        as required by llist_add_to_end(llist_t **old_head, char *new_item).)
  85
  86        getopt32(argv, "e:*", &patterns);
  87
  88        $ grep -e user -e root /etc/passwd
  89        root:x:0:0:root:/root:/bin/bash
  90        user:x:500:500::/home/user:/bin/bash
  91
  92 "+"    If the first character in the applet_opts string is a plus,
  93        then option processing will stop as soon as a non-option is
  94        encountered in the argv array.  Useful for applets like env
  95        which should not process arguments to subprograms:
  96        env -i ls -d /
  97        Here we want env to process just the '-i', not the '-d'.
  98
  99 "!"    Report bad options, missing required options,
 100        inconsistent options with all-ones return value (instead of abort).
 101
 102 "^"    options string is "^optchars""\0""opt_complementary".
 103
 104uint32_t
 105getopt32long(char **argv, const char *applet_opts, const char *logopts...)
 106
 107        This allows you to define long options:
 108
 109        static const char applet_longopts[] ALIGN1 =
 110                //"name\0"  has_arg     val
 111                "verbose\0" No_argument "v"
 112                ;
 113        opt = getopt32long(argv, applet_opts, applet_longopts, ...);
 114
 115        The last element (val) typically is set to
 116        matching short option from applet_opts. If there is no matching
 117        char in applet_opts, then:
 118        - return bit has next position after short options
 119        - if has_arg is not "No_argument", use ptr for arg also
 120        - opt_complementary affects it too
 121
 122        Note: a good applet will make long options configurable via the
 123        config process and not a required feature.  The current standard
 124        is to name the config option CONFIG_FEATURE_<applet>_LONG_OPTIONS.
 125
 126opt_complementary - option modifiers.
 127
 128 ":"    The colon (":") is used to separate groups of two or more chars
 129        and/or groups of chars and special characters (stating some
 130        conditions to be checked).
 131
 132 "abc"  If groups of two or more chars are specified, the first char
 133        is the main option and the other chars are secondary options.
 134        Their flags will be turned on if the main option is found even
 135        if they are not specified on the command line.  For example:
 136
 137        flags = getopt32(argv, "^abcd""\0""abc")
 138
 139        If getopt() finds "-a" on the command line, then
 140        getopt32's return value will be as if "-a -b -c" were
 141        found.
 142
 143 "ww"   Adjacent double options have a counter associated which indicates
 144        the number of occurrences of the option.
 145        For example the ps applet needs:
 146        if w is given once, GNU ps sets the width to 132,
 147        if w is given more than once, it is "unlimited"
 148
 149        int w_counter = 0; // must be initialized!
 150        getopt32(argv, "^w""\0""ww", &w_counter);
 151        if (w_counter)
 152                width = (w_counter == 1) ? 132 : INT_MAX;
 153        else
 154                get_terminal_width(...&width...);
 155
 156        w_counter is a pointer to an integer. It has to be passed to
 157        getopt32() after all other option argument sinks.
 158
 159        For example: accept multiple -v to indicate the level of verbosity
 160        and for each -b optarg, add optarg to my_b. Finally, if b is given,
 161        turn off c and vice versa:
 162
 163        llist_t *my_b = NULL;
 164        int verbose_level = 0;
 165        f = getopt32(argv, "^vb:*c"
 166                        "\0""vv:b-c:c-b"
 167                        , &my_b, &verbose_level);
 168        if (f & 2)       // -c after -b unsets -b flag
 169                while (my_b) dosomething_with(llist_pop(&my_b));
 170        if (my_b)        // but llist is stored if -b is specified
 171                free_llist(my_b);
 172        if (verbose_level) printf("verbose level is %d\n", verbose_level);
 173
 174Special characters:
 175
 176 "-N"   A dash as the first char in a opt_complementary group followed
 177        by a single digit (0-9) means that at least N non-option
 178        arguments must be present on the command line
 179
 180 "=N"   An equal sign as the first char in a opt_complementary group followed
 181        by a single digit (0-9) means that exactly N non-option
 182        arguments must be present on the command line
 183
 184 "?N"   A "?" as the first char in a opt_complementary group followed
 185        by a single digit (0-9) means that at most N arguments must be present
 186        on the command line.
 187
 188 "V-"   An option with dash before colon or end-of-line results in
 189        bb_show_usage() being called if this option is encountered.
 190        This is typically used to implement "print verbose usage message
 191        and exit" option.
 192
 193 "a-b"  A dash between two options causes the second of the two
 194        to be unset (and ignored) if it is given on the command line.
 195
 196        [FIXME: what if they are the same? like "x-x"? Is it ever useful?]
 197
 198        For example:
 199        The du applet has the options "-s" and "-d depth".  If
 200        getopt32 finds -s, then -d is unset or if it finds -d
 201        then -s is unset.  (Note:  busybox implements the GNU
 202        "--max-depth" option as "-d".)  To obtain this behavior, you
 203        set opt_complementary to "s-d:d-s".  Only one flag value is
 204        added to getopt32's return value depending on the
 205        position of the options on the command line.  If one of the
 206        two options requires an argument pointer (":" in applet_opts
 207        as in "d:") optarg is set accordingly.
 208
 209        char *smax_print_depth;
 210
 211        opt = getopt32(argv, "^sd:x""\0""s-d:d-s:x-x", &smax_print_depth);
 212
 213        if (opt & 2)
 214                max_print_depth = atoi(smax_print_depth);
 215        if (opt & 4)
 216                printf("Detected odd -x usage\n");
 217
 218 "a--b" A double dash between two options, or between an option and a group
 219        of options, means that they are mutually exclusive.  Unlike
 220        the "-" case above, an error will be forced if the options
 221        are used together.
 222
 223        For example:
 224        The cut applet must have only one type of list specified, so
 225        -b, -c and -f are mutually exclusive and should raise an error
 226        if specified together.  In this case you must set
 227        opt_complementary to "b--cf:c--bf:f--bc".  If two of the
 228        mutually exclusive options are found, getopt32 will call
 229        bb_show_usage() and die.
 230
 231 "x--x" Variation of the above, it means that -x option should occur
 232        at most once.
 233
 234 "o+"   A plus after a char in opt_complementary means that the parameter
 235        for this option is a nonnegative integer. It will be processed
 236        with xatoi_positive() - allowed range is 0..INT_MAX.
 237
 238        int param;  // "unsigned param;" will also work
 239        getopt32(argv, "^p:""\0""p+", &param);
 240
 241 "o::"  A double colon after a char in opt_complementary means that the
 242        option can occur multiple times. Each occurrence will be saved as
 243        a llist_t element instead of char*.
 244
 245        For example:
 246        The grep applet can have one or more "-e pattern" arguments.
 247        In this case you should use getopt32() as follows:
 248
 249        llist_t *patterns = NULL;
 250
 251        (this pointer must be initializated to NULL if the list is empty
 252        as required by llist_add_to_end(llist_t **old_head, char *new_item).)
 253
 254        getopt32(argv, "^e:""\0""e::", &patterns);
 255
 256        $ grep -e user -e root /etc/passwd
 257        root:x:0:0:root:/root:/bin/bash
 258        user:x:500:500::/home/user:/bin/bash
 259
 260        "o+" and "o::" can be handled by "o:+" and "o:*" specifiers
 261        in option string (and it is preferred), but this does not work
 262        for "long options only" cases, such as tar --exclude=PATTERN,
 263        wget --header=HDR cases.
 264
 265 "a?b"  A "?" between an option and a group of options means that
 266        at least one of them is required to occur if the first option
 267        occurs in preceding command line arguments.
 268
 269        For example from "id" applet:
 270
 271        // Don't allow -n -r -rn -ug -rug -nug -rnug
 272        flags = getopt32(argv, "^rnug""\0""r?ug:n?ug:u--g:g--u");
 273
 274        This example allowed only:
 275        $ id; id -u; id -g; id -ru; id -nu; id -rg; id -ng; id -rnu; id -rng
 276
 277 "X"    A opt_complementary group with just a single letter means
 278        that this option is required. If more than one such group exists,
 279        at least one option is required to occur (not all of them).
 280        For example from "start-stop-daemon" applet:
 281
 282        // Don't allow -KS -SK, but -S or -K is required
 283        flags = getopt32(argv, "^KS...""\0""K:S:K--S:S--K");
 284
 285
 286        Don't forget to use ':'. For example, "?322-22-23X-x-a"
 287        is interpreted as "?3:22:-2:2-2:2-3Xa:2--x" -
 288        max 3 args; count uses of '-2'; min 2 args; if there is
 289        a '-2' option then unset '-3', '-X' and '-a'; if there is
 290        a '-2' and after it a '-x' then error out.
 291        But it's far too obfuscated. Use ':' to separate groups.
 292*/
 293
 294/* Code here assumes that 'unsigned' is at least 32 bits wide */
 295
 296const char *const bb_argv_dash[] = { "-", NULL };
 297
 298enum {
 299        PARAM_STRING,
 300        PARAM_LIST,
 301        PARAM_INT,
 302};
 303
 304typedef struct {
 305        unsigned char opt_char;
 306        smallint param_type;
 307        unsigned switch_on;
 308        unsigned switch_off;
 309        unsigned incongruously;
 310        unsigned requires;
 311        void **optarg;  /* char**, llist_t** or int *. */
 312        int *counter;
 313} t_complementary;
 314
 315uint32_t option_mask32;
 316
 317#if ENABLE_LONG_OPTS
 318static const struct option bb_null_long_options[1] = {
 319        { 0, 0, 0, 0 }
 320};
 321#else
 322#define vgetopt32(argv,applet_opts,applet_long_options,p) \
 323        vgetopt32(argv,applet_opts,p)
 324#endif
 325
 326/* Please keep getopt32 free from xmalloc */
 327
 328static uint32_t
 329vgetopt32(char **argv, const char *applet_opts, const char *applet_long_options, va_list p)
 330{
 331        int argc;
 332        unsigned flags = 0;
 333        unsigned requires = 0;
 334        unsigned len;
 335        t_complementary complementary[33]; /* last stays zero-filled */
 336        char dont_die_flag;
 337        int c;
 338        const unsigned char *s;
 339        const char *opt_complementary;
 340        t_complementary *on_off;
 341#if ENABLE_LONG_OPTS
 342        const struct option *l_o;
 343        struct option *long_options = (struct option *) &bb_null_long_options;
 344#endif
 345        unsigned trigger;
 346        int min_arg = 0;
 347        int max_arg = -1;
 348        int spec_flgs = 0;
 349
 350#define SHOW_USAGE_IF_ERROR     1
 351
 352        on_off = complementary;
 353        memset(on_off, 0, sizeof(complementary));
 354
 355        len = strlen(applet_opts);
 356
 357        /* skip bbox extension */
 358        opt_complementary = NULL;
 359        if (applet_opts[0] == '^') {
 360                applet_opts++;
 361                /* point it past terminating NUL */
 362                opt_complementary = applet_opts + len;
 363        }
 364
 365        /* skip another bbox extension */
 366        dont_die_flag = applet_opts[0];
 367        if (dont_die_flag == '!')
 368                applet_opts++;
 369
 370        applet_opts = strcpy(alloca(len + 1), applet_opts);
 371
 372        /* skip GNU extension */
 373        s = (const unsigned char *)applet_opts;
 374        if (*s == '+' || *s == '-')
 375                s++;
 376        c = 0;
 377        while (*s) {
 378                if (c >= 32)
 379                        break;
 380                on_off->opt_char = *s;
 381                on_off->switch_on = (1U << c);
 382                if (*++s == ':') {
 383                        on_off->optarg = va_arg(p, void **);
 384                        if (s[1] == '+' || s[1] == '*') {
 385                                /* 'o:+' or 'o:*' */
 386                                on_off->param_type = (s[1] == '+') ?
 387                                        PARAM_INT : PARAM_LIST;
 388                                overlapping_strcpy((char*)s + 1, (char*)s + 2);
 389                        }
 390                        /* skip possible 'o::' (or 'o:+:' !) */
 391                        while (*++s == ':')
 392                                continue;
 393                }
 394                on_off++;
 395                c++;
 396        }
 397
 398#if ENABLE_LONG_OPTS
 399        if (applet_long_options) {
 400                const char *optstr;
 401                unsigned i, count;
 402
 403                count = 1;
 404                optstr = applet_long_options;
 405                while (optstr[0]) {
 406                        optstr += strlen(optstr) + 3; /* skip NUL, has_arg, val */
 407                        count++;
 408                }
 409                /* count == no. of longopts + 1 */
 410                long_options = alloca(count * sizeof(*long_options));
 411                memset(long_options, 0, count * sizeof(*long_options));
 412                i = 0;
 413                optstr = applet_long_options;
 414                while (--count) {
 415                        long_options[i].name = optstr;
 416                        optstr += strlen(optstr) + 1;
 417                        long_options[i].has_arg = (unsigned char)(*optstr++);
 418                        /* long_options[i].flag = NULL; */
 419                        long_options[i].val = (unsigned char)(*optstr++);
 420                        i++;
 421                }
 422                for (l_o = long_options; l_o->name; l_o++) {
 423                        if (l_o->flag)
 424                                continue;
 425                        for (on_off = complementary; on_off->opt_char; on_off++)
 426                                if (on_off->opt_char == l_o->val)
 427                                        goto next_long;
 428                        if (c >= 32)
 429                                break;
 430                        on_off->opt_char = l_o->val;
 431                        on_off->switch_on = (1U << c);
 432                        if (l_o->has_arg != no_argument)
 433                                on_off->optarg = va_arg(p, void **);
 434                        c++;
 435 next_long: ;
 436                }
 437        }
 438#endif /* ENABLE_LONG_OPTS */
 439
 440        s = (const unsigned char *)opt_complementary;
 441        if (s) for (; *s; s++) {
 442                t_complementary *pair;
 443                unsigned *pair_switch;
 444
 445                if (*s == ':')
 446                        continue;
 447                c = s[1];
 448                if (*s == '?') {
 449                        if (c < '0' || c > '9') {
 450                                spec_flgs |= SHOW_USAGE_IF_ERROR;
 451                        } else {
 452                                max_arg = c - '0';
 453                                s++;
 454                        }
 455                        continue;
 456                }
 457                if (*s == '-') {
 458                        if (c >= '0' && c <= '9') {
 459                                min_arg = c - '0';
 460                                s++;
 461                        }
 462                        continue;
 463                }
 464                if (*s == '=') {
 465                        min_arg = max_arg = c - '0';
 466                        s++;
 467                        continue;
 468                }
 469                for (on_off = complementary; on_off->opt_char; on_off++)
 470                        if (on_off->opt_char == *s)
 471                                goto found_opt;
 472                /* Without this, diagnostic of such bugs is not easy */
 473                bb_error_msg_and_die("NO OPT %c!", *s);
 474 found_opt:
 475                if (c == ':' && s[2] == ':') {
 476                        on_off->param_type = PARAM_LIST;
 477                        continue;
 478                }
 479                if (c == '+' && (s[2] == ':' || s[2] == '\0')) {
 480                        on_off->param_type = PARAM_INT;
 481                        s++;
 482                        continue;
 483                }
 484                if (c == ':' || c == '\0') {
 485                        requires |= on_off->switch_on;
 486                        continue;
 487                }
 488                if (c == '-' && (s[2] == ':' || s[2] == '\0')) {
 489                        flags |= on_off->switch_on;
 490                        on_off->incongruously |= on_off->switch_on;
 491                        s++;
 492                        continue;
 493                }
 494                if (c == *s) {
 495                        on_off->counter = va_arg(p, int *);
 496                        s++;
 497                }
 498                pair = on_off;
 499                pair_switch = &pair->switch_on;
 500                for (s++; *s && *s != ':'; s++) {
 501                        if (*s == '?') {
 502                                pair_switch = &pair->requires;
 503                        } else if (*s == '-') {
 504                                if (pair_switch == &pair->switch_off)
 505                                        pair_switch = &pair->incongruously;
 506                                else
 507                                        pair_switch = &pair->switch_off;
 508                        } else {
 509                                for (on_off = complementary; on_off->opt_char; on_off++)
 510                                        if (on_off->opt_char == *s) {
 511                                                *pair_switch |= on_off->switch_on;
 512                                                break;
 513                                        }
 514                        }
 515                }
 516                s--;
 517        }
 518
 519        /* In case getopt32 was already called:
 520         * reset libc getopt() internal state.
 521         * run_nofork_applet() does this, but we might end up here
 522         * also via gunzip_main() -> gzip_main(). Play safe.
 523         */
 524        GETOPT_RESET();
 525
 526        /* skip 0: some applets cheat: they do not actually HAVE argv[0] */
 527        argc = 1 + string_array_len(argv + 1);
 528
 529        /* Note: just "getopt() <= 0" will not work well for
 530         * "fake" short options, like this one:
 531         * wget $'-\203' "Test: test" http://kernel.org/
 532         * (supposed to act as --header, but doesn't) */
 533#if ENABLE_LONG_OPTS
 534        while ((c = getopt_long(argc, argv, applet_opts,
 535                        long_options, NULL)) != -1) {
 536#else
 537        while ((c = getopt(argc, argv, applet_opts)) != -1) {
 538#endif
 539                /* getopt prints "option requires an argument -- X"
 540                 * and returns '?' if an option has no arg, but one is reqd */
 541                c &= 0xff; /* fight libc's sign extension */
 542                for (on_off = complementary; on_off->opt_char != c; on_off++) {
 543                        /* c can be NUL if long opt has non-NULL ->flag,
 544                         * but we construct long opts so that flag
 545                         * is always NULL (see above) */
 546                        if (on_off->opt_char == '\0' /* && c != '\0' */) {
 547                                /* c is probably '?' - "bad option" */
 548                                goto error;
 549                        }
 550                }
 551                if (flags & on_off->incongruously)
 552                        goto error;
 553                trigger = on_off->switch_on & on_off->switch_off;
 554                flags &= ~(on_off->switch_off ^ trigger);
 555                flags |= on_off->switch_on ^ trigger;
 556                flags ^= trigger;
 557                if (on_off->counter)
 558                        (*(on_off->counter))++;
 559                if (optarg) {
 560                        if (on_off->param_type == PARAM_LIST) {
 561                                llist_add_to_end((llist_t **)(on_off->optarg), optarg);
 562                        } else if (on_off->param_type == PARAM_INT) {
 563//TODO: xatoi_positive indirectly pulls in printf machinery
 564                                *(unsigned*)(on_off->optarg) = xatoi_positive(optarg);
 565                        } else if (on_off->optarg) {
 566                                *(char **)(on_off->optarg) = optarg;
 567                        }
 568                }
 569        }
 570
 571        /* check depending requires for given options */
 572        for (on_off = complementary; on_off->opt_char; on_off++) {
 573                if (on_off->requires
 574                 && (flags & on_off->switch_on)
 575                 && (flags & on_off->requires) == 0
 576                ) {
 577                        goto error;
 578                }
 579        }
 580        if (requires && (flags & requires) == 0)
 581                goto error;
 582        argc -= optind;
 583        if (argc < min_arg || (max_arg >= 0 && argc > max_arg))
 584                goto error;
 585
 586        option_mask32 = flags;
 587        return flags;
 588
 589 error:
 590        if (dont_die_flag != '!')
 591                bb_show_usage();
 592        return (int32_t)-1;
 593}
 594
 595uint32_t FAST_FUNC
 596getopt32(char **argv, const char *applet_opts, ...)
 597{
 598        uint32_t opt;
 599        va_list p;
 600
 601        va_start(p, applet_opts);
 602        opt = vgetopt32(argv, applet_opts, NULL, p);
 603        va_end(p);
 604        return opt;
 605}
 606
 607#if ENABLE_LONG_OPTS
 608uint32_t FAST_FUNC
 609getopt32long(char **argv, const char *applet_opts, const char *longopts, ...)
 610{
 611        uint32_t opt;
 612        va_list p;
 613
 614        va_start(p, longopts);
 615        opt = vgetopt32(argv, applet_opts, longopts, p);
 616        va_end(p);
 617        return opt;
 618}
 619#endif
 620