"Fossies" - the Fresh Open Source Software Archive

Member "scponly-20110526/netbsd_getopt_long.c" (20 Nov 2010, 10885 Bytes) of package /linux/privat/old/scponly-20110526.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 /*  $NetBSD: getopt_long.c,v 1.11 2009/04/14 17:34:41 joerg 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 #include "scponly_getopt.h"
   33 
   34 #include <errno.h>
   35 #include <stdlib.h>
   36 #include <string.h>
   37 #include <stdio.h>
   38 #include <stdarg.h>
   39 
   40 #undef __UNCONST
   41 #define __UNCONST(a)    ((void *)(size_t)(const void *)(a))
   42 
   43 #define IGNORE_FIRST    (*options == '-' || *options == '+')
   44 #define IS_POSIXLY_CORRECT (getenv("POSIXLY_CORRECT") != NULL)
   45 #define PERMUTE         (!IS_POSIXLY_CORRECT && !IGNORE_FIRST)
   46 /* XXX: GNU ignores PC if *options == '-' */
   47 #define IN_ORDER        (!IS_POSIXLY_CORRECT && *options == '-')
   48 
   49 /* return values */
   50 #define BADCH   (int)'?'
   51 #define BADARG      ((IGNORE_FIRST && options[1] == ':') \
   52              || (*options == ':') ? (int)':' : (int)'?')
   53 #define INORDER (int)1
   54 
   55 #define EMSG    ""
   56 
   57 #ifndef _DIAGASSERT
   58 #define _DIAGASSERT(X)
   59 #endif
   60 
   61 int     optreset;               /* reset getopt */
   62 
   63 static int netbsd_getopt_internal(int, char **, const char *);
   64 static int gcd(int, int);
   65 static void permute_args(int, int, int, char **);
   66 
   67 static const char *place = EMSG; /* option letter processing */
   68 
   69 /* XXX: set optreset to 1 rather than these two */
   70 static int nonopt_start = -1; /* first non option argument (for permute) */
   71 static int nonopt_end = -1;   /* first option after non options (for permute) */
   72 
   73 /* Error messages */
   74 static const char recargchar[] = "option requires an argument -- %c";
   75 static const char recargstring[] = "option requires an argument -- %s";
   76 static const char ambig[] = "ambiguous option -- %.*s";
   77 static const char noarg[] = "option doesn't take an argument -- %.*s";
   78 static const char illoptchar[] = "unknown option -- %c";
   79 static const char illoptstring[] = "unknown option -- %s";
   80 
   81 char opterrmsg[128];
   82 
   83 /*
   84  * Compute the greatest common divisor of a and b.
   85  */
   86 static int
   87 gcd(int a, int b)
   88 {
   89     int c;
   90 
   91     c = a % b;
   92     while (c != 0) {
   93         a = b;
   94         b = c;
   95         c = a % b;
   96     }
   97        
   98     return b;
   99 }
  100 
  101 /*
  102  * Exchange the block from nonopt_start to nonopt_end with the block
  103  * from nonopt_end to opt_end (keeping the same order of arguments
  104  * in each block).
  105  */
  106 static void
  107 permute_args(int panonopt_start, int panonopt_end, int opt_end, char **nargv)
  108 {
  109     int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
  110     char *swap;
  111 
  112     _DIAGASSERT(nargv != NULL);
  113 
  114     /*
  115      * compute lengths of blocks and number and size of cycles
  116      */
  117     nnonopts = panonopt_end - panonopt_start;
  118     nopts = opt_end - panonopt_end;
  119     ncycle = gcd(nnonopts, nopts);
  120     cyclelen = (opt_end - panonopt_start) / ncycle;
  121 
  122     for (i = 0; i < ncycle; i++) {
  123         cstart = panonopt_end+i;
  124         pos = cstart;
  125         for (j = 0; j < cyclelen; j++) {
  126             if (pos >= panonopt_end)
  127                 pos -= nnonopts;
  128             else
  129                 pos += nopts;
  130             swap = nargv[pos];
  131             nargv[pos] = nargv[cstart];
  132             nargv[cstart] = swap;
  133         }
  134     }
  135 }
  136 
  137 /*
  138  * netbsd_getopt_internal --
  139  *  Parse argc/argv argument vector.  Called by user level routines.
  140  *  Returns -2 if -- is found (can be long option or end of options marker).
  141  */
  142 static int
  143 netbsd_getopt_internal(int nargc, char **nargv, const char *options)
  144 {
  145     char *oli;              /* option letter list index */
  146     int optchar;
  147 
  148     _DIAGASSERT(nargv != NULL);
  149     _DIAGASSERT(options != NULL);
  150 
  151     optarg = NULL;
  152 
  153     /*
  154      * XXX Some programs (like rsyncd) expect to be able to
  155      * XXX re-initialize optind to 0 and have getopt_long(3)
  156      * XXX properly function again.  Work around this braindamage.
  157      */
  158     if (optind == 0)
  159         optind = 1;
  160 
  161     if (optreset)
  162         nonopt_start = nonopt_end = -1;
  163 start:
  164     if (optreset || !*place) {      /* update scanning pointer */
  165         optreset = 0;
  166         if (optind >= nargc) {          /* end of argument vector */
  167             place = EMSG;
  168             if (nonopt_end != -1) {
  169                 /* do permutation, if we have to */
  170                 permute_args(nonopt_start, nonopt_end,
  171                     optind, nargv);
  172                 optind -= nonopt_end - nonopt_start;
  173             }
  174             else if (nonopt_start != -1) {
  175                 /*
  176                  * If we skipped non-options, set optind
  177                  * to the first of them.
  178                  */
  179                 optind = nonopt_start;
  180             }
  181             nonopt_start = nonopt_end = -1;
  182             return -1;
  183         }
  184         if ((*(place = nargv[optind]) != '-')
  185             || (place[1] == '\0')) {    /* found non-option */
  186             place = EMSG;
  187             if (IN_ORDER) {
  188                 /*
  189                  * GNU extension: 
  190                  * return non-option as argument to option 1
  191                  */
  192                 optarg = nargv[optind++];
  193                 return INORDER;
  194             }
  195             if (!PERMUTE) {
  196                 /*
  197                  * if no permutation wanted, stop parsing
  198                  * at first non-option
  199                  */
  200                 return -1;
  201             }
  202             /* do permutation */
  203             if (nonopt_start == -1)
  204                 nonopt_start = optind;
  205             else if (nonopt_end != -1) {
  206                 permute_args(nonopt_start, nonopt_end,
  207                     optind, nargv);
  208                 nonopt_start = optind -
  209                     (nonopt_end - nonopt_start);
  210                 nonopt_end = -1;
  211             }
  212             optind++;
  213             /* process next argument */
  214             goto start;
  215         }
  216         if (nonopt_start != -1 && nonopt_end == -1)
  217             nonopt_end = optind;
  218         if (place[1] && *++place == '-') {  /* found "--" */
  219             place++;
  220             return -2;
  221         }
  222     }
  223     if ((optchar = (int)*place++) == (int)':' ||
  224         (oli = strchr(options + (IGNORE_FIRST ? 1 : 0), optchar)) == NULL) {
  225         /* option letter unknown or ':' */
  226         if (!*place)
  227             ++optind;
  228         optopt = optchar;
  229         return BADCH;
  230     }
  231     if (optchar == 'W' && oli[1] == ';') {      /* -W long-option */
  232         /* XXX: what if no long options provided (called by getopt)? */
  233         if (*place) 
  234             return -2;
  235 
  236         if (++optind >= nargc) {    /* no arg */
  237             place = EMSG;
  238             optopt = optchar;
  239             return BADARG;
  240         } else              /* white space */
  241             place = nargv[optind];
  242         /*
  243          * Handle -W arg the same as --arg (which causes getopt to
  244          * stop parsing).
  245          */
  246         return -2;
  247     }
  248     if (*++oli != ':') {            /* doesn't take argument */
  249         if (!*place)
  250             ++optind;
  251     } else {                /* takes (optional) argument */
  252         optarg = NULL;
  253         if (*place)         /* no white space */
  254             optarg = __UNCONST(place);
  255         /* XXX: disable test for :: if PC? (GNU doesn't) */
  256         else if (oli[1] != ':') {   /* arg not optional */
  257             if (++optind >= nargc) {    /* no arg */
  258                 place = EMSG;
  259                 optopt = optchar;
  260                 return BADARG;
  261             } else
  262                 optarg = nargv[optind];
  263         }
  264         place = EMSG;
  265         ++optind;
  266     }
  267     /* dump back option letter */
  268     return optchar;
  269 }
  270 
  271 
  272 /*
  273  * getopt_long --
  274  *  Parse argc/argv argument vector.
  275  */
  276 int
  277 netbsd_getopt_long(int nargc, char * const *nargv, const char *options,
  278     const struct option *long_options, int *idx)
  279 {
  280     int retval;
  281 
  282 #define IDENTICAL_INTERPRETATION(_x, _y)                \
  283     (long_options[(_x)].has_arg == long_options[(_y)].has_arg &&    \
  284      long_options[(_x)].flag == long_options[(_y)].flag &&      \
  285      long_options[(_x)].val == long_options[(_y)].val)
  286 
  287     if (nargv == NULL || options == NULL || long_options == NULL)
  288     {
  289         printf("getopt_long assertions failed!");
  290         exit(42);
  291     }
  292     
  293     /* idx may be NULL */
  294 
  295     retval = netbsd_getopt_internal(nargc, __UNCONST(nargv), options);
  296     if (retval == -2) {
  297         char *current_argv, *has_equal;
  298         size_t current_argv_len;
  299         int i, ambiguous, match;
  300 
  301         current_argv = __UNCONST(place);
  302         match = -1;
  303         ambiguous = 0;
  304 
  305         optind++;
  306         place = EMSG;
  307 
  308         if (*current_argv == '\0') {        /* found "--" */
  309             /*
  310              * We found an option (--), so if we skipped
  311              * non-options, we have to permute.
  312              */
  313             if (nonopt_end != -1) {
  314                 permute_args(nonopt_start, nonopt_end,
  315                     optind, __UNCONST(nargv));
  316                 optind -= nonopt_end - nonopt_start;
  317             }
  318             nonopt_start = nonopt_end = -1;
  319             return -1;
  320         }
  321         if ((has_equal = strchr(current_argv, '=')) != NULL) {
  322             /* argument found (--option=arg) */
  323             current_argv_len = has_equal - current_argv;
  324             has_equal++;
  325         } else
  326             current_argv_len = strlen(current_argv);
  327         
  328         for (i = 0; long_options[i].name; i++) {
  329             /* find matching long option */
  330             if (strncmp(current_argv, long_options[i].name,
  331                 current_argv_len))
  332                 continue;
  333 
  334             if (strlen(long_options[i].name) ==
  335                 (unsigned)current_argv_len) {
  336                 /* exact match */
  337                 match = i;
  338                 ambiguous = 0;
  339                 break;
  340             }
  341             if (match == -1)        /* partial match */
  342                 match = i;
  343             else if (!IDENTICAL_INTERPRETATION(i, match))
  344                 ambiguous = 1;
  345         }
  346         if (ambiguous) {
  347             /* ambiguous abbreviation */
  348             optopt = 0;
  349             return BADCH;
  350         }
  351         if (match != -1) {          /* option found */
  352                 if (long_options[match].has_arg == no_argument
  353                 && has_equal) {
  354                 /*
  355                  * XXX: GNU sets optopt to val regardless of
  356                  * flag
  357                  */
  358                 if (long_options[match].flag == NULL)
  359                     optopt = long_options[match].val;
  360                 else
  361                     optopt = 0;
  362                 return BADARG;
  363             }
  364             if (long_options[match].has_arg == required_argument ||
  365                 long_options[match].has_arg == optional_argument) {
  366                 if (has_equal)
  367                     optarg = has_equal;
  368                 else if (long_options[match].has_arg ==
  369                     required_argument) {
  370                     /*
  371                      * optional argument doesn't use
  372                      * next nargv
  373                      */
  374                     optarg = nargv[optind++];
  375                 }
  376             }
  377             if ((long_options[match].has_arg == required_argument)
  378                 && (optarg == NULL)) {
  379                 /*
  380                  * Missing argument; leading ':'
  381                  * indicates no error should be generated
  382                  */
  383                 /*
  384                  * XXX: GNU sets optopt to val regardless
  385                  * of flag
  386                  */
  387                 if (long_options[match].flag == NULL)
  388                     optopt = long_options[match].val;
  389                 else
  390                     optopt = 0;
  391                 --optind;
  392                 return BADARG;
  393             }
  394         } else {            /* unknown option */
  395             optopt = 0;
  396             return BADCH;
  397         }
  398         if (long_options[match].flag) {
  399             *long_options[match].flag = long_options[match].val;
  400             retval = 0;
  401         } else 
  402             retval = long_options[match].val;
  403         if (idx)
  404             *idx = match;
  405     }
  406     return retval;
  407 #undef IDENTICAL_INTERPRETATION
  408 }
  409