Subversion Repositories HelenOS

Rev

Rev 3317 | Rev 3336 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*  $NetBSD: getopt_long.c,v 1.21.4.1 2008/01/09 01:34:14 matt Exp $    */
  2.  
  3. /*-
  4.  * Copyright (c) 2000 The NetBSD Foundation, Inc.
  5.  * All rights reserved.
  6.  *
  7.  * This code is derived from software contributed to The NetBSD Foundation
  8.  * by Dieter Baron and Thomas Klausner.
  9.  *
  10.  * Redistribution and use in source and binary forms, with or without
  11.  * modification, are permitted provided that the following conditions
  12.  * are met:
  13.  * 1. Redistributions of source code must retain the above copyright
  14.  *    notice, this list of conditions and the following disclaimer.
  15.  * 2. Redistributions in binary form must reproduce the above copyright
  16.  *    notice, this list of conditions and the following disclaimer in the
  17.  *    documentation and/or other materials provided with the distribution.
  18.  *
  19.  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
  20.  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
  21.  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  22.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
  23.  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  24.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  25.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  26.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  27.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  28.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  29.  * POSSIBILITY OF SUCH DAMAGE.
  30.  */
  31.  
  32. /* Ported to HelenOS August 2008 by Tim Post <echo@echoreply.us> */
  33.  
  34. #include <assert.h>
  35. #include <stdarg.h>
  36. #include <err.h>
  37. #include <errno.h>
  38. #include <getopt.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41.  
  42. /* HelenOS Port: Something similar to warnx(), however we just print
  43.  * to the console */
  44.  
  45. void warnx(const char *fmt, ...);
  46.  
  47. void warnx(const char *fmt, ...)
  48. {
  49.     va_list vargs;
  50.     va_start(vargs, fmt);
  51.     vprintf(fmt, vargs);
  52.     va_end(vargs);
  53.     printf("\n");
  54.     return;
  55. }
  56.  
  57. /* HelenOS Port : We're incorporating only the modern getopt_long with wrappers
  58.  * to keep legacy getopt() usage from breaking. All references to REPLACE_GETOPT
  59.  * are dropped, we just include the code */
  60.  
  61. int opterr = 1;     /* if error message should be printed */
  62. int optind = 1;     /* index into parent argv vector */
  63. int optopt = '?';       /* character checked for validity */
  64. int optreset;       /* reset getopt */
  65. char *optarg;       /* argument associated with option */
  66.  
  67.  
  68. #define IGNORE_FIRST    (*options == '-' || *options == '+')
  69. #define PRINT_ERROR ((opterr) && ((*options != ':') \
  70.                       || (IGNORE_FIRST && options[1] != ':')))
  71. /*HelenOS Port - POSIXLY_CORRECT is always false */
  72. #define IS_POSIXLY_CORRECT 0
  73. #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
  74. /* XXX: GNU ignores PC if *options == '-' */
  75. #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
  76.  
  77. /* return values */
  78. #define BADCH   (int)'?'
  79. #define BADARG      ((IGNORE_FIRST && options[1] == ':') \
  80.              || (*options == ':') ? (int)':' : (int)'?')
  81. #define INORDER (int)1
  82.  
  83. #define EMSG    ""
  84.  
  85. static int getopt_internal(int, char **, const char *);
  86. static int gcd(int, int);
  87. static void permute_args(int, int, int, char **);
  88.  
  89. static const char *place = EMSG; /* option letter processing */
  90.  
  91. /* XXX: set optreset to 1 rather than these two */
  92. static int nonopt_start = -1; /* first non option argument (for permute) */
  93. static int nonopt_end = -1;   /* first option after non options (for permute) */
  94.  
  95. /* Error messages */
  96. static const char recargchar[] = "option requires an argument -- %c";
  97. static const char recargstring[] = "option requires an argument -- %s";
  98. static const char ambig[] = "ambiguous option -- %.*s";
  99. static const char noarg[] = "option doesn't take an argument -- %.*s";
  100. static const char illoptchar[] = "unknown option -- %c";
  101. static const char illoptstring[] = "unknown option -- %s";
  102.  
  103.  
  104. /*
  105.  * Compute the greatest common divisor of a and b.
  106.  */
  107. static int
  108. gcd(a, b)
  109.     int a;
  110.     int b;
  111. {
  112.     int c;
  113.  
  114.     c = a % b;
  115.     while (c != 0) {
  116.         a = b;
  117.         b = c;
  118.         c = a % b;
  119.     }
  120.        
  121.     return b;
  122. }
  123.  
  124. /*
  125.  * Exchange the block from nonopt_start to nonopt_end with the block
  126.  * from nonopt_end to opt_end (keeping the same order of arguments
  127.  * in each block).
  128.  */
  129. static void
  130. permute_args(panonopt_start, panonopt_end, opt_end, nargv)
  131.     int panonopt_start;
  132.     int panonopt_end;
  133.     int opt_end;
  134.     char **nargv;
  135. {
  136.     int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
  137.     char *swap;
  138.  
  139.     assert(nargv != NULL);
  140.  
  141.     /*
  142.      * compute lengths of blocks and number and size of cycles
  143.      */
  144.     nnonopts = panonopt_end - panonopt_start;
  145.     nopts = opt_end - panonopt_end;
  146.     ncycle = gcd(nnonopts, nopts);
  147.     cyclelen = (opt_end - panonopt_start) / ncycle;
  148.  
  149.     for (i = 0; i < ncycle; i++) {
  150.         cstart = panonopt_end+i;
  151.         pos = cstart;
  152.         for (j = 0; j < cyclelen; j++) {
  153.             if (pos >= panonopt_end)
  154.                 pos -= nnonopts;
  155.             else
  156.                 pos += nopts;
  157.             swap = nargv[pos];
  158.             nargv[pos] = nargv[cstart];
  159.             nargv[cstart] = swap;
  160.         }
  161.     }
  162. }
  163.  
  164. /*
  165.  * getopt_internal --
  166.  *  Parse argc/argv argument vector.  Called by user level routines.
  167.  *  Returns -2 if -- is found (can be long option or end of options marker).
  168.  */
  169. static int
  170. getopt_internal(nargc, nargv, options)
  171.     int nargc;
  172.     char **nargv;
  173.     const char *options;
  174. {
  175.     char *oli;              /* option letter list index */
  176.     int optchar;
  177.  
  178.     assert(nargv != NULL);
  179.     assert(options != NULL);
  180.  
  181.     optarg = NULL;
  182.  
  183.     /*
  184.      * XXX Some programs (like rsyncd) expect to be able to
  185.      * XXX re-initialize optind to 0 and have getopt_long(3)
  186.      * XXX properly function again.  Work around this braindamage.
  187.      */
  188.     if (optind == 0)
  189.         optind = 1;
  190.  
  191.     if (optreset)
  192.         nonopt_start = nonopt_end = -1;
  193. start:
  194.     if (optreset || !*place) {      /* update scanning pointer */
  195.         optreset = 0;
  196.         if (optind >= nargc) {          /* end of argument vector */
  197.             place = EMSG;
  198.             if (nonopt_end != -1) {
  199.                 /* do permutation, if we have to */
  200.                 permute_args(nonopt_start, nonopt_end,
  201.                     optind, nargv);
  202.                 optind -= nonopt_end - nonopt_start;
  203.             }
  204.             else if (nonopt_start != -1) {
  205.                 /*
  206.                  * If we skipped non-options, set optind
  207.                  * to the first of them.
  208.                  */
  209.                 optind = nonopt_start;
  210.             }
  211.             nonopt_start = nonopt_end = -1;
  212.             return -1;
  213.         }
  214.         if ((*(place = nargv[optind]) != '-')
  215.             || (place[1] == '\0')) {    /* found non-option */
  216.             place = EMSG;
  217.             if (IN_ORDER) {
  218.                 /*
  219.                  * GNU extension:
  220.                  * return non-option as argument to option 1
  221.                  */
  222.                 optarg = nargv[optind++];
  223.                 return INORDER;
  224.             }
  225.             if (!PERMUTE) {
  226.                 /*
  227.                  * if no permutation wanted, stop parsing
  228.                  * at first non-option
  229.                  */
  230.                 return -1;
  231.             }
  232.             /* do permutation */
  233.             if (nonopt_start == -1)
  234.                 nonopt_start = optind;
  235.             else if (nonopt_end != -1) {
  236.                 permute_args(nonopt_start, nonopt_end,
  237.                     optind, nargv);
  238.                 nonopt_start = optind -
  239.                     (nonopt_end - nonopt_start);
  240.                 nonopt_end = -1;
  241.             }
  242.             optind++;
  243.             /* process next argument */
  244.             goto start;
  245.         }
  246.         if (nonopt_start != -1 && nonopt_end == -1)
  247.             nonopt_end = optind;
  248.         if (place[1] && *++place == '-') {  /* found "--" */
  249.             place++;
  250.             return -2;
  251.         }
  252.     }
  253.     if ((optchar = (int)*place++) == (int)':' ||
  254.         (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
  255.         /* option letter unknown or ':' */
  256.         if (!*place)
  257.             ++optind;
  258.         if (PRINT_ERROR)
  259.             warnx(illoptchar, optchar);
  260.         optopt = optchar;
  261.         return BADCH;
  262.     }
  263.     if (optchar == 'W' && oli[1] == ';') {      /* -W long-option */
  264.         /* XXX: what if no long options provided (called by getopt)? */
  265.         if (*place)
  266.             return -2;
  267.  
  268.         if (++optind >= nargc) {    /* no arg */
  269.             place = EMSG;
  270.             if (PRINT_ERROR)
  271.                 warnx(recargchar, optchar);
  272.             optopt = optchar;
  273.             return BADARG;
  274.         } else              /* white space */
  275.             place = nargv[optind];
  276.         /*
  277.          * Handle -W arg the same as --arg (which causes getopt to
  278.          * stop parsing).
  279.          */
  280.         return -2;
  281.     }
  282.     if (*++oli != ':') {            /* doesn't take argument */
  283.         if (!*place)
  284.             ++optind;
  285.     } else {                /* takes (optional) argument */
  286.         optarg = NULL;
  287.         if (*place)         /* no white space */
  288.             optarg = *place;
  289.         /* XXX: disable test for :: if PC? (GNU doesn't) */
  290.         else if (oli[1] != ':') {   /* arg not optional */
  291.             if (++optind >= nargc) {    /* no arg */
  292.                 place = EMSG;
  293.                 if (PRINT_ERROR)
  294.                     warnx(recargchar, optchar);
  295.                 optopt = optchar;
  296.                 return BADARG;
  297.             } else
  298.                 optarg = nargv[optind];
  299.         }
  300.         place = EMSG;
  301.         ++optind;
  302.     }
  303.     /* dump back option letter */
  304.     return optchar;
  305. }
  306.  
  307. /*
  308.  * getopt --
  309.  *  Parse argc/argv argument vector.
  310.  */
  311. int
  312. getopt(nargc, nargv, options)
  313.     int nargc;
  314.     char * const *nargv;
  315.     const char *options;
  316. {
  317.     int retval;
  318.  
  319.     assert(nargv != NULL);
  320.     assert(options != NULL);
  321.  
  322.     retval = getopt_internal(nargc, (char **)nargv, options);
  323.     if (retval == -2) {
  324.         ++optind;
  325.         /*
  326.          * We found an option (--), so if we skipped non-options,
  327.          * we have to permute.
  328.          */
  329.         if (nonopt_end != -1) {
  330.             permute_args(nonopt_start, nonopt_end, optind,
  331.                        (char **)nargv);
  332.             optind -= nonopt_end - nonopt_start;
  333.         }
  334.         nonopt_start = nonopt_end = -1;
  335.         retval = -1;
  336.     }
  337.     return retval;
  338. }
  339.  
  340. /*
  341.  * getopt_long --
  342.  *  Parse argc/argv argument vector.
  343.  */
  344. int
  345. getopt_long(nargc, nargv, options, long_options, idx)
  346.     int nargc;
  347.     char * const *nargv;
  348.     const char *options;
  349.     const struct option *long_options;
  350.     int *idx;
  351. {
  352.     int retval;
  353.  
  354. #define IDENTICAL_INTERPRETATION(_x, _y)                \
  355.     (long_options[(_x)].has_arg == long_options[(_y)].has_arg &&    \
  356.      long_options[(_x)].flag == long_options[(_y)].flag &&      \
  357.      long_options[(_x)].val == long_options[(_y)].val)
  358.  
  359.     assert(nargv != NULL);
  360.     assert(options != NULL);
  361.     assert(long_options != NULL);
  362.     /* idx may be NULL */
  363.  
  364.     retval = getopt_internal(nargc, (char **)nargv, options);
  365.     if (retval == -2) {
  366.         char *current_argv, *has_equal;
  367.         size_t current_argv_len;
  368.         int i, ambiguous, match;
  369.  
  370.         current_argv = (char *)place;
  371.         match = -1;
  372.         ambiguous = 0;
  373.  
  374.         optind++;
  375.         place = EMSG;
  376.  
  377.         if (*current_argv == '\0') {        /* found "--" */
  378.             /*
  379.              * We found an option (--), so if we skipped
  380.              * non-options, we have to permute.
  381.              */
  382.             if (nonopt_end != -1) {
  383.                 permute_args(nonopt_start, nonopt_end,
  384.                     optind, (char **)nargv);
  385.                 optind -= nonopt_end - nonopt_start;
  386.             }
  387.             nonopt_start = nonopt_end = -1;
  388.             return -1;
  389.         }
  390.         if ((has_equal = strchr(current_argv, '=')) != NULL) {
  391.             /* argument found (--option=arg) */
  392.             current_argv_len = has_equal - current_argv;
  393.             has_equal++;
  394.         } else
  395.             current_argv_len = strlen(current_argv);
  396.        
  397.         for (i = 0; long_options[i].name; i++) {
  398.             /* find matching long option */
  399.             if (strncmp(current_argv, long_options[i].name,
  400.                 current_argv_len))
  401.                 continue;
  402.  
  403.             if (strlen(long_options[i].name) ==
  404.                 (unsigned)current_argv_len) {
  405.                 /* exact match */
  406.                 match = i;
  407.                 ambiguous = 0;
  408.                 break;
  409.             }
  410.             if (match == -1)        /* partial match */
  411.                 match = i;
  412.             else if (!IDENTICAL_INTERPRETATION(i, match))
  413.                 ambiguous = 1;
  414.         }
  415.         if (ambiguous) {
  416.             /* ambiguous abbreviation */
  417.             if (PRINT_ERROR)
  418.                 warnx(ambig, (int)current_argv_len,
  419.                      current_argv);
  420.             optopt = 0;
  421.             return BADCH;
  422.         }
  423.         if (match != -1) {          /* option found */
  424.                 if (long_options[match].has_arg == no_argument
  425.                 && has_equal) {
  426.                 if (PRINT_ERROR)
  427.                     warnx(noarg, (int)current_argv_len,
  428.                          current_argv);
  429.                 /*
  430.                  * XXX: GNU sets optopt to val regardless of
  431.                  * flag
  432.                  */
  433.                 if (long_options[match].flag == NULL)
  434.                     optopt = long_options[match].val;
  435.                 else
  436.                     optopt = 0;
  437.                 return BADARG;
  438.             }
  439.             if (long_options[match].has_arg == required_argument ||
  440.                 long_options[match].has_arg == optional_argument) {
  441.                 if (has_equal)
  442.                     optarg = has_equal;
  443.                 else if (long_options[match].has_arg ==
  444.                     required_argument) {
  445.                     /*
  446.                      * optional argument doesn't use
  447.                      * next nargv
  448.                      */
  449.                     optarg = nargv[optind++];
  450.                 }
  451.             }
  452.             if ((long_options[match].has_arg == required_argument)
  453.                 && (optarg == NULL)) {
  454.                 /*
  455.                  * Missing argument; leading ':'
  456.                  * indicates no error should be generated
  457.                  */
  458.                 if (PRINT_ERROR)
  459.                     warnx(recargstring, current_argv);
  460.                 /*
  461.                  * XXX: GNU sets optopt to val regardless
  462.                  * of flag
  463.                  */
  464.                 if (long_options[match].flag == NULL)
  465.                     optopt = long_options[match].val;
  466.                 else
  467.                     optopt = 0;
  468.                 --optind;
  469.                 return BADARG;
  470.             }
  471.         } else {            /* unknown option */
  472.             if (PRINT_ERROR)
  473.                 warnx(illoptstring, current_argv);
  474.             optopt = 0;
  475.             return BADCH;
  476.         }
  477.         if (long_options[match].flag) {
  478.             *long_options[match].flag = long_options[match].val;
  479.             retval = 0;
  480.         } else
  481.             retval = long_options[match].val;
  482.         if (idx)
  483.             *idx = match;
  484.     }
  485.     return retval;
  486. #undef IDENTICAL_INTERPRETATION
  487. }
  488.  
  489.