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