Subversion Repositories HelenOS

Rev

Rev 4327 | 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 : We're incorporating only the modern getopt_long with wrappers
  43.  * to keep legacy getopt() usage from breaking. All references to REPLACE_GETOPT
  44.  * are dropped, we just include the code */
  45.  
  46. int opterr = 1;     /* if error message should be printed */
  47. int optind = 1;     /* index into parent argv vector */
  48. int optopt = '?';       /* character checked for validity */
  49. int optreset;       /* reset getopt */
  50. const char *optarg;     /* argument associated with option */
  51.  
  52.  
  53. #define IGNORE_FIRST    (*options == '-' || *options == '+')
  54. #define PRINT_ERROR ((opterr) && ((*options != ':') \
  55.                       || (IGNORE_FIRST && options[1] != ':')))
  56. /*HelenOS Port - POSIXLY_CORRECT is always false */
  57. #define IS_POSIXLY_CORRECT 0
  58. #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
  59. /* XXX: GNU ignores PC if *options == '-' */
  60. #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
  61.  
  62. /* return values */
  63. #define BADCH   (int)'?'
  64. #define BADARG      ((IGNORE_FIRST && options[1] == ':') \
  65.              || (*options == ':') ? (int)':' : (int)'?')
  66. #define INORDER (int)1
  67.  
  68. #define EMSG    ""
  69.  
  70. static int getopt_internal(int, char **, const char *);
  71. static int gcd(int, int);
  72. static void permute_args(int, int, int, char **);
  73.  
  74. static const char *place = EMSG; /* option letter processing */
  75.  
  76. /* XXX: set optreset to 1 rather than these two */
  77. static int nonopt_start = -1; /* first non option argument (for permute) */
  78. static int nonopt_end = -1;   /* first option after non options (for permute) */
  79.  
  80. /* Error messages */
  81.  
  82. /* HelenOS Port: Calls to warnx() were eliminated (as we have no stderr that
  83.  * may be redirected) and replaced with printf. As such, error messages now
  84.  * end in a newline */
  85.  
  86. static const char recargchar[] = "option requires an argument -- %c\n";
  87. static const char recargstring[] = "option requires an argument -- %s\n";
  88. static const char ambig[] = "ambiguous option -- %.*s\n";
  89. static const char noarg[] = "option doesn't take an argument -- %.*s\n";
  90. static const char illoptchar[] = "unknown option -- %c\n";
  91. static const char illoptstring[] = "unknown option -- %s\n";
  92.  
  93.  
  94. /*
  95.  * Compute the greatest common divisor of a and b.
  96.  */
  97. static int
  98. gcd(a, b)
  99.     int a;
  100.     int b;
  101. {
  102.     int c;
  103.  
  104.     c = a % b;
  105.     while (c != 0) {
  106.         a = b;
  107.         b = c;
  108.         c = a % b;
  109.     }
  110.        
  111.     return b;
  112. }
  113.  
  114. /*
  115.  * Exchange the block from nonopt_start to nonopt_end with the block
  116.  * from nonopt_end to opt_end (keeping the same order of arguments
  117.  * in each block).
  118.  */
  119. static void
  120. permute_args(panonopt_start, panonopt_end, opt_end, nargv)
  121.     int panonopt_start;
  122.     int panonopt_end;
  123.     int opt_end;
  124.     char **nargv;
  125. {
  126.     int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
  127.     char *swap;
  128.  
  129.     assert(nargv != NULL);
  130.  
  131.     /*
  132.      * compute lengths of blocks and number and size of cycles
  133.      */
  134.     nnonopts = panonopt_end - panonopt_start;
  135.     nopts = opt_end - panonopt_end;
  136.     ncycle = gcd(nnonopts, nopts);
  137.     cyclelen = (opt_end - panonopt_start) / ncycle;
  138.  
  139.     for (i = 0; i < ncycle; i++) {
  140.         cstart = panonopt_end+i;
  141.         pos = cstart;
  142.         for (j = 0; j < cyclelen; j++) {
  143.             if (pos >= panonopt_end)
  144.                 pos -= nnonopts;
  145.             else
  146.                 pos += nopts;
  147.             swap = nargv[pos];
  148.             nargv[pos] = nargv[cstart];
  149.             nargv[cstart] = swap;
  150.         }
  151.     }
  152. }
  153.  
  154. /*
  155.  * getopt_internal --
  156.  *  Parse argc/argv argument vector.  Called by user level routines.
  157.  *  Returns -2 if -- is found (can be long option or end of options marker).
  158.  */
  159. static int
  160. getopt_internal(nargc, nargv, options)
  161.     int nargc;
  162.     char **nargv;
  163.     const char *options;
  164. {
  165.     const char *oli;                /* option letter list index */
  166.     int optchar;
  167.  
  168.     assert(nargv != NULL);
  169.     assert(options != NULL);
  170.  
  171.     optarg = NULL;
  172.  
  173.     /*
  174.      * XXX Some programs (like rsyncd) expect to be able to
  175.      * XXX re-initialize optind to 0 and have getopt_long(3)
  176.      * XXX properly function again.  Work around this braindamage.
  177.      */
  178.     if (optind == 0)
  179.         optind = 1;
  180.  
  181.     if (optreset)
  182.         nonopt_start = nonopt_end = -1;
  183. start:
  184.     if (optreset || !*place) {      /* update scanning pointer */
  185.         optreset = 0;
  186.         if (optind >= nargc) {          /* end of argument vector */
  187.             place = EMSG;
  188.             if (nonopt_end != -1) {
  189.                 /* do permutation, if we have to */
  190.                 permute_args(nonopt_start, nonopt_end,
  191.                     optind, nargv);
  192.                 optind -= nonopt_end - nonopt_start;
  193.             }
  194.             else if (nonopt_start != -1) {
  195.                 /*
  196.                  * If we skipped non-options, set optind
  197.                  * to the first of them.
  198.                  */
  199.                 optind = nonopt_start;
  200.             }
  201.             nonopt_start = nonopt_end = -1;
  202.             return -1;
  203.         }
  204.         if ((*(place = nargv[optind]) != '-')
  205.             || (place[1] == '\0')) {    /* found non-option */
  206.             place = EMSG;
  207.             if (IN_ORDER) {
  208.                 /*
  209.                  * GNU extension:
  210.                  * return non-option as argument to option 1
  211.                  */
  212.                 optarg = nargv[optind++];
  213.                 return INORDER;
  214.             }
  215.             if (!PERMUTE) {
  216.                 /*
  217.                  * if no permutation wanted, stop parsing
  218.                  * at first non-option
  219.                  */
  220.                 return -1;
  221.             }
  222.             /* do permutation */
  223.             if (nonopt_start == -1)
  224.                 nonopt_start = optind;
  225.             else if (nonopt_end != -1) {
  226.                 permute_args(nonopt_start, nonopt_end,
  227.                     optind, nargv);
  228.                 nonopt_start = optind -
  229.                     (nonopt_end - nonopt_start);
  230.                 nonopt_end = -1;
  231.             }
  232.             optind++;
  233.             /* process next argument */
  234.             goto start;
  235.         }
  236.         if (nonopt_start != -1 && nonopt_end == -1)
  237.             nonopt_end = optind;
  238.         if (place[1] && *++place == '-') {  /* found "--" */
  239.             place++;
  240.             return -2;
  241.         }
  242.     }
  243.     if ((optchar = (int)*place++) == (int)':' ||
  244.         (oli = str_chr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
  245.         /* option letter unknown or ':' */
  246.         if (!*place)
  247.             ++optind;
  248.         if (PRINT_ERROR)
  249.             printf(illoptchar, optchar);
  250.         optopt = optchar;
  251.         return BADCH;
  252.     }
  253.     if (optchar == 'W' && oli[1] == ';') {      /* -W long-option */
  254.         /* XXX: what if no long options provided (called by getopt)? */
  255.         if (*place)
  256.             return -2;
  257.  
  258.         if (++optind >= nargc) {    /* no arg */
  259.             place = EMSG;
  260.             if (PRINT_ERROR)
  261.                 printf(recargchar, optchar);
  262.             optopt = optchar;
  263.             return BADARG;
  264.         } else              /* white space */
  265.             place = nargv[optind];
  266.         /*
  267.          * Handle -W arg the same as --arg (which causes getopt to
  268.          * stop parsing).
  269.          */
  270.         return -2;
  271.     }
  272.     if (*++oli != ':') {            /* doesn't take argument */
  273.         if (!*place)
  274.             ++optind;
  275.     } else {                /* takes (optional) argument */
  276.         optarg = NULL;
  277.         if (*place)         /* no white space */
  278.             optarg = place;
  279.         /* XXX: disable test for :: if PC? (GNU doesn't) */
  280.         else if (oli[1] != ':') {   /* arg not optional */
  281.             if (++optind >= nargc) {    /* no arg */
  282.                 place = EMSG;
  283.                 if (PRINT_ERROR)
  284.                     printf(recargchar, optchar);
  285.                 optopt = optchar;
  286.                 return BADARG;
  287.             } else
  288.                 optarg = nargv[optind];
  289.         }
  290.         place = EMSG;
  291.         ++optind;
  292.     }
  293.     /* dump back option letter */
  294.     return optchar;
  295. }
  296.  
  297. /*
  298.  * getopt --
  299.  *  Parse argc/argv argument vector.
  300.  */
  301. int
  302. getopt(nargc, nargv, options)
  303.     int nargc;
  304.     char * const *nargv;
  305.     const char *options;
  306. {
  307.     int retval;
  308.  
  309.     assert(nargv != NULL);
  310.     assert(options != NULL);
  311.  
  312.     retval = getopt_internal(nargc, (char **)nargv, options);
  313.     if (retval == -2) {
  314.         ++optind;
  315.         /*
  316.          * We found an option (--), so if we skipped non-options,
  317.          * we have to permute.
  318.          */
  319.         if (nonopt_end != -1) {
  320.             permute_args(nonopt_start, nonopt_end, optind,
  321.                        (char **)nargv);
  322.             optind -= nonopt_end - nonopt_start;
  323.         }
  324.         nonopt_start = nonopt_end = -1;
  325.         retval = -1;
  326.     }
  327.     return retval;
  328. }
  329.  
  330. /*
  331.  * getopt_long --
  332.  *  Parse argc/argv argument vector.
  333.  */
  334. int
  335. getopt_long(nargc, nargv, options, long_options, idx)
  336.     int nargc;
  337.     char * const *nargv;
  338.     const char *options;
  339.     const struct option *long_options;
  340.     int *idx;
  341. {
  342.     int retval;
  343.  
  344. #define IDENTICAL_INTERPRETATION(_x, _y)                \
  345.     (long_options[(_x)].has_arg == long_options[(_y)].has_arg &&    \
  346.      long_options[(_x)].flag == long_options[(_y)].flag &&      \
  347.      long_options[(_x)].val == long_options[(_y)].val)
  348.  
  349.     assert(nargv != NULL);
  350.     assert(options != NULL);
  351.     assert(long_options != NULL);
  352.     /* idx may be NULL */
  353.  
  354.     retval = getopt_internal(nargc, (char **)nargv, options);
  355.     if (retval == -2) {
  356.         char *current_argv;
  357.         const char *has_equal;
  358.         size_t current_argv_len;
  359.         int i, ambiguous, match;
  360.  
  361.         current_argv = (char *)place;
  362.         match = -1;
  363.         ambiguous = 0;
  364.  
  365.         optind++;
  366.         place = EMSG;
  367.  
  368.         if (*current_argv == '\0') {        /* found "--" */
  369.             /*
  370.              * We found an option (--), so if we skipped
  371.              * non-options, we have to permute.
  372.              */
  373.             if (nonopt_end != -1) {
  374.                 permute_args(nonopt_start, nonopt_end,
  375.                     optind, (char **)nargv);
  376.                 optind -= nonopt_end - nonopt_start;
  377.             }
  378.             nonopt_start = nonopt_end = -1;
  379.             return -1;
  380.         }
  381.         if ((has_equal = str_chr(current_argv, '=')) != NULL) {
  382.             /* argument found (--option=arg) */
  383.             current_argv_len = has_equal - current_argv;
  384.             has_equal++;
  385.         } else
  386.             current_argv_len = str_size(current_argv);
  387.        
  388.         for (i = 0; long_options[i].name; i++) {
  389.             /* find matching long option */
  390.             if (str_lcmp(current_argv, long_options[i].name,
  391.                 str_nlength(current_argv, current_argv_len)))
  392.                 continue;
  393.  
  394.             if (str_size(long_options[i].name) ==
  395.                 (unsigned)current_argv_len) {
  396.                 /* exact match */
  397.                 match = i;
  398.                 ambiguous = 0;
  399.                 break;
  400.             }
  401.             if (match == -1)        /* partial match */
  402.                 match = i;
  403.             else if (!IDENTICAL_INTERPRETATION(i, match))
  404.                 ambiguous = 1;
  405.         }
  406.         if (ambiguous) {
  407.             /* ambiguous abbreviation */
  408.             if (PRINT_ERROR)
  409.                 printf(ambig, (int)current_argv_len,
  410.                      current_argv);
  411.             optopt = 0;
  412.             return BADCH;
  413.         }
  414.         if (match != -1) {          /* option found */
  415.                 if (long_options[match].has_arg == no_argument
  416.                 && has_equal) {
  417.                 if (PRINT_ERROR)
  418.                     printf(noarg, (int)current_argv_len,
  419.                          current_argv);
  420.                 /*
  421.                  * XXX: GNU sets optopt to val regardless of
  422.                  * flag
  423.                  */
  424.                 if (long_options[match].flag == NULL)
  425.                     optopt = long_options[match].val;
  426.                 else
  427.                     optopt = 0;
  428.                 return BADARG;
  429.             }
  430.             if (long_options[match].has_arg == required_argument ||
  431.                 long_options[match].has_arg == optional_argument) {
  432.                 if (has_equal)
  433.                     optarg = has_equal;
  434.                 else if (long_options[match].has_arg ==
  435.                     required_argument) {
  436.                     /*
  437.                      * optional argument doesn't use
  438.                      * next nargv
  439.                      */
  440.                     optarg = nargv[optind++];
  441.                 }
  442.             }
  443.             if ((long_options[match].has_arg == required_argument)
  444.                 && (optarg == NULL)) {
  445.                 /*
  446.                  * Missing argument; leading ':'
  447.                  * indicates no error should be generated
  448.                  */
  449.                 if (PRINT_ERROR)
  450.                     printf(recargstring, current_argv);
  451.                 /*
  452.                  * XXX: GNU sets optopt to val regardless
  453.                  * of flag
  454.                  */
  455.                 if (long_options[match].flag == NULL)
  456.                     optopt = long_options[match].val;
  457.                 else
  458.                     optopt = 0;
  459.                 --optind;
  460.                 return BADARG;
  461.             }
  462.         } else {            /* unknown option */
  463.             if (PRINT_ERROR)
  464.                 printf(illoptstring, current_argv);
  465.             optopt = 0;
  466.             return BADCH;
  467.         }
  468.         if (long_options[match].flag) {
  469.             *long_options[match].flag = long_options[match].val;
  470.             retval = 0;
  471.         } else
  472.             retval = long_options[match].val;
  473.         if (idx)
  474.             *idx = match;
  475.     }
  476.     return retval;
  477. #undef IDENTICAL_INTERPRETATION
  478. }
  479.  
  480.