Subversion Repositories HelenOS

Rev

Rev 3318 | 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.  * [eventually this will replace the real getopt]
  312.  */
  313. int
  314. getopt(nargc, nargv, options)
  315.     int nargc;
  316.     char * const *nargv;
  317.     const char *options;
  318. {
  319.     int retval;
  320.  
  321.     assert(nargv != NULL);
  322.     assert(options != NULL);
  323.  
  324.     retval = getopt_internal(nargc, (char **)nargv, options);
  325.     if (retval == -2) {
  326.         ++optind;
  327.         /*
  328.          * We found an option (--), so if we skipped non-options,
  329.          * we have to permute.
  330.          */
  331.         if (nonopt_end != -1) {
  332.             permute_args(nonopt_start, nonopt_end, optind,
  333.                        (char **)nargv);
  334.             optind -= nonopt_end - nonopt_start;
  335.         }
  336.         nonopt_start = nonopt_end = -1;
  337.         retval = -1;
  338.     }
  339.     return retval;
  340. }
  341.  
  342. /*
  343.  * getopt_long --
  344.  *  Parse argc/argv argument vector.
  345.  */
  346. int
  347. getopt_long(nargc, nargv, options, long_options, idx)
  348.     int nargc;
  349.     char * const *nargv;
  350.     const char *options;
  351.     const struct option *long_options;
  352.     int *idx;
  353. {
  354.     int retval;
  355.  
  356. #define IDENTICAL_INTERPRETATION(_x, _y)                \
  357.     (long_options[(_x)].has_arg == long_options[(_y)].has_arg &&    \
  358.      long_options[(_x)].flag == long_options[(_y)].flag &&      \
  359.      long_options[(_x)].val == long_options[(_y)].val)
  360.  
  361.     assert(nargv != NULL);
  362.     assert(options != NULL);
  363.     assert(long_options != NULL);
  364.     /* idx may be NULL */
  365.  
  366.     retval = getopt_internal(nargc, (char **)nargv, options);
  367.     if (retval == -2) {
  368.         char *current_argv, *has_equal;
  369.         size_t current_argv_len;
  370.         int i, ambiguous, match;
  371.  
  372.         current_argv = (char *)place;
  373.         match = -1;
  374.         ambiguous = 0;
  375.  
  376.         optind++;
  377.         place = EMSG;
  378.  
  379.         if (*current_argv == '\0') {        /* found "--" */
  380.             /*
  381.              * We found an option (--), so if we skipped
  382.              * non-options, we have to permute.
  383.              */
  384.             if (nonopt_end != -1) {
  385.                 permute_args(nonopt_start, nonopt_end,
  386.                     optind, (char **)nargv);
  387.                 optind -= nonopt_end - nonopt_start;
  388.             }
  389.             nonopt_start = nonopt_end = -1;
  390.             return -1;
  391.         }
  392.         if ((has_equal = strchr(current_argv, '=')) != NULL) {
  393.             /* argument found (--option=arg) */
  394.             current_argv_len = has_equal - current_argv;
  395.             has_equal++;
  396.         } else
  397.             current_argv_len = strlen(current_argv);
  398.        
  399.         for (i = 0; long_options[i].name; i++) {
  400.             /* find matching long option */
  401.             if (strncmp(current_argv, long_options[i].name,
  402.                 current_argv_len))
  403.                 continue;
  404.  
  405.             if (strlen(long_options[i].name) ==
  406.                 (unsigned)current_argv_len) {
  407.                 /* exact match */
  408.                 match = i;
  409.                 ambiguous = 0;
  410.                 break;
  411.             }
  412.             if (match == -1)        /* partial match */
  413.                 match = i;
  414.             else if (!IDENTICAL_INTERPRETATION(i, match))
  415.                 ambiguous = 1;
  416.         }
  417.         if (ambiguous) {
  418.             /* ambiguous abbreviation */
  419.             if (PRINT_ERROR)
  420.                 warnx(ambig, (int)current_argv_len,
  421.                      current_argv);
  422.             optopt = 0;
  423.             return BADCH;
  424.         }
  425.         if (match != -1) {          /* option found */
  426.                 if (long_options[match].has_arg == no_argument
  427.                 && has_equal) {
  428.                 if (PRINT_ERROR)
  429.                     warnx(noarg, (int)current_argv_len,
  430.                          current_argv);
  431.                 /*
  432.                  * XXX: GNU sets optopt to val regardless of
  433.                  * flag
  434.                  */
  435.                 if (long_options[match].flag == NULL)
  436.                     optopt = long_options[match].val;
  437.                 else
  438.                     optopt = 0;
  439.                 return BADARG;
  440.             }
  441.             if (long_options[match].has_arg == required_argument ||
  442.                 long_options[match].has_arg == optional_argument) {
  443.                 if (has_equal)
  444.                     optarg = has_equal;
  445.                 else if (long_options[match].has_arg ==
  446.                     required_argument) {
  447.                     /*
  448.                      * optional argument doesn't use
  449.                      * next nargv
  450.                      */
  451.                     optarg = nargv[optind++];
  452.                 }
  453.             }
  454.             if ((long_options[match].has_arg == required_argument)
  455.                 && (optarg == NULL)) {
  456.                 /*
  457.                  * Missing argument; leading ':'
  458.                  * indicates no error should be generated
  459.                  */
  460.                 if (PRINT_ERROR)
  461.                     warnx(recargstring, current_argv);
  462.                 /*
  463.                  * XXX: GNU sets optopt to val regardless
  464.                  * of flag
  465.                  */
  466.                 if (long_options[match].flag == NULL)
  467.                     optopt = long_options[match].val;
  468.                 else
  469.                     optopt = 0;
  470.                 --optind;
  471.                 return BADARG;
  472.             }
  473.         } else {            /* unknown option */
  474.             if (PRINT_ERROR)
  475.                 warnx(illoptstring, current_argv);
  476.             optopt = 0;
  477.             return BADCH;
  478.         }
  479.         if (long_options[match].flag) {
  480.             *long_options[match].flag = long_options[match].val;
  481.             retval = 0;
  482.         } else
  483.             retval = long_options[match].val;
  484.         if (idx)
  485.             *idx = match;
  486.     }
  487.     return retval;
  488. #undef IDENTICAL_INTERPRETATION
  489. }
  490.  
  491.