Subversion Repositories f9daq

Rev

Blame | Last modification | View Log | RSS feed

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