Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members

bsd-getopt-long.c

Go to the documentation of this file.
00001 /* $Id: bsd-getopt-long.c,v 1.2 2005/09/12 19:48:57 vern Exp $ */
00002 
00003 /*    $OpenBSD: getopt_long.c,v 1.17 2004/06/03 18:46:52 millert Exp $    */
00004 /*    $NetBSD: getopt_long.c,v 1.15 2002/01/31 22:43:40 tv Exp $    */
00005 
00006 /*
00007  * Copyright (c) 2002 Todd C. Miller <Todd.Miller@courtesan.com>
00008  *
00009  * Permission to use, copy, modify, and distribute this software for any
00010  * purpose with or without fee is hereby granted, provided that the above
00011  * copyright notice and this permission notice appear in all copies.
00012  *
00013  * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
00014  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
00015  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
00016  * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
00017  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
00018  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
00019  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
00020  */
00021 /*-
00022  * Copyright (c) 2000 The NetBSD Foundation, Inc.
00023  * All rights reserved.
00024  *
00025  * This code is derived from software contributed to The NetBSD Foundation
00026  * by Dieter Baron and Thomas Klausner.
00027  *
00028  * Redistribution and use in source and binary forms, with or without
00029  * modification, are permitted provided that the following conditions
00030  * are met:
00031  * 1. Redistributions of source code must retain the above copyright
00032  *    notice, this list of conditions and the following disclaimer.
00033  * 2. Redistributions in binary form must reproduce the above copyright
00034  *    notice, this list of conditions and the following disclaimer in the
00035  *    documentation and/or other materials provided with the distribution.
00036  * 3. All advertising materials mentioning features or use of this software
00037  *    must display the following acknowledgement:
00038  *        This product includes software developed by the NetBSD
00039  *        Foundation, Inc. and its contributors.
00040  * 4. Neither the name of The NetBSD Foundation nor the names of its
00041  *    contributors may be used to endorse or promote products derived
00042  *    from this software without specific prior written permission.
00043  *
00044  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
00045  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
00046  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00047  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
00048  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00049  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00050  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00051  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00052  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00053  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00054  * POSSIBILITY OF SUCH DAMAGE.
00055  */
00056 
00057 #define IN_GETOPT_LONG_C 1
00058 
00059 #include <config.h>
00060 #include <stdlib.h>
00061 #include <unistd.h>
00062 #include <stdio.h>
00063 #include <string.h>
00064 
00065 #ifndef HAVE_GETOPT_LONG
00066 
00067 # include "bsd-getopt-long.h"
00068 
00069 # ifdef WITH_DMALLOC
00070 #  include <dmalloc.h>
00071 # endif
00072 
00073 int    pure_opterr = 1;        /* if error message should be printed */
00074 int    pure_optind = 1;        /* index into parent argv vector */
00075 int    pure_optopt = '?';        /* character checked for validity */
00076 int    pure_optreset;        /* reset getopt */
00077 const char    *pure_optarg;        /* argument associated with option */
00078 
00079 # define PRINT_ERROR    ((pure_opterr) && (*options != ':'))
00080 
00081 # define FLAG_PERMUTE    0x01    /* permute non-options to the end of argv */
00082 # define FLAG_ALLARGS    0x02    /* treat non-options as args to option "-1" */
00083 # define FLAG_LONGONLY   0x04    /* operate as pure_getopt_long_only */
00084 
00085 /* return values */
00086 # define    BADCH       (int)'?'
00087 # define    BADARG      ((*options == ':') ? (int)':' : (int)'?')
00088 # define    INORDER     (int)1
00089 
00090 # define    EMSG        ""
00091 
00092 static int pure_getopt_internal(int, char * const *, const char *,
00093                                 const struct pure_option *, int *, int);
00094 static int pure_parse_long_options(char * const *, const char *,
00095                                    const struct pure_option *, int *, int);
00096 static int pure_gcd(int, int);
00097 static void pure_permute_args(int, int, int, char * const *);
00098 
00099 static const char *pure_place = EMSG; /* option letter processing */
00100 
00101 /* XXX: set pure_optreset to 1 rather than these two */
00102 static int nonopt_start = -1; /* first non option argument (for permute) */
00103 static int nonopt_end = -1;   /* first option after non options (for permute) */
00104 
00105 /* Error messages */
00106 static const char *recargchar = "option requires an argument -- %c\n";
00107 static const char *recargstring = "option requires an argument -- %s\n";
00108 static const char *ambig = "ambiguous option -- %.*s\n";
00109 static const char *noarg = "option doesn't take an argument -- %.*s\n";
00110 static const char *illoptchar = "unknown option -- %c\n";
00111 static const char *illoptstring = "unknown option -- %s\n";
00112 
00113 /*
00114  * Compute the greatest common divisor of a and b.
00115  */
00116 static int pure_gcd(int a, int b)
00117 {
00118     int c;
00119     
00120     c = a % b;
00121     while (c != 0) {
00122         a = b;
00123         b = c;
00124         c = a % b;
00125     }    
00126     return b;
00127 }
00128 
00129 /*
00130  * Exchange the block from nonopt_start to nonopt_end with the block
00131  * from nonopt_end to opt_end (keeping the same order of arguments
00132  * in each block).
00133  */
00134 static void pure_permute_args(int panonopt_start, int panonopt_end, 
00135                               int opt_end, char * const *nargv)
00136 {
00137     int cstart, cyclelen, i, j, ncycle, nnonopts, nopts, pos;
00138     char *swap;
00139     
00140     /*
00141      * compute lengths of blocks and number and size of cycles
00142      */
00143     nnonopts = panonopt_end - panonopt_start;
00144     nopts = opt_end - panonopt_end;
00145     ncycle = pure_gcd(nnonopts, nopts);
00146     cyclelen = (opt_end - panonopt_start) / ncycle;
00147     
00148     for (i = 0; i < ncycle; i++) {
00149         cstart = panonopt_end+i;
00150         pos = cstart;
00151         for (j = 0; j < cyclelen; j++) {
00152             if (pos >= panonopt_end)
00153                 pos -= nnonopts;
00154             else
00155                 pos += nopts;
00156             swap = nargv[pos];
00157             /* LINTED const cast */
00158             ((char **) nargv)[pos] = nargv[cstart];
00159             /* LINTED const cast */
00160             ((char **)nargv)[cstart] = swap;
00161         }
00162     }
00163 }
00164 
00165 /*
00166  * pure_parse_long_options --
00167  *    Parse long options in argc/argv argument vector.
00168  * Returns -1 if short_too is set and the option does not match long_options.
00169  */
00170 static int pure_parse_long_options(char * const *nargv, const char *options,
00171                                    const struct pure_option *long_options,
00172                                    int *idx, int short_too)
00173 {
00174     const char *current_argv, *has_equal;
00175     size_t current_argv_len;
00176     int i, match;
00177     
00178     current_argv = pure_place;
00179     match = -1;
00180     
00181     pure_optind++;
00182     
00183     if ((has_equal = strchr(current_argv, '=')) != NULL) {
00184         /* argument found (--option=arg) */
00185         current_argv_len = has_equal - current_argv;
00186         has_equal++;
00187     } else
00188         current_argv_len = strlen(current_argv);
00189     
00190     for (i = 0; long_options[i].name; i++) {
00191         /* find matching long option */
00192         if (strncmp(current_argv, long_options[i].name,
00193             current_argv_len))
00194             continue;
00195         
00196         if (strlen(long_options[i].name) == current_argv_len) {
00197             /* exact match */
00198             match = i;
00199             break;
00200         }
00201         /*
00202          * If this is a known short option, don't allow
00203          * a partial match of a single character.
00204          */
00205         if (short_too && current_argv_len == 1)
00206             continue;
00207         
00208         if (match == -1)    /* partial match */
00209             match = i;
00210         else {
00211             /* ambiguous abbreviation */
00212             if (PRINT_ERROR)
00213                 fprintf(stderr, ambig, (int)current_argv_len,
00214                         current_argv);
00215             pure_optopt = 0;
00216             return BADCH;
00217         }
00218     }
00219     if (match != -1) {        /* option found */
00220         if (long_options[match].has_arg == no_argument
00221             && has_equal) {
00222             if (PRINT_ERROR)
00223                 fprintf(stderr, noarg, (int)current_argv_len,
00224                         current_argv);
00225             /*
00226              * XXX: GNU sets pure_optopt to val regardless of flag
00227              */
00228             if (long_options[match].flag == NULL)
00229                 pure_optopt = long_options[match].val;
00230             else
00231                 pure_optopt = 0;
00232             return BADARG;
00233         }
00234         if (long_options[match].has_arg == required_argument ||
00235             long_options[match].has_arg == optional_argument) {
00236             if (has_equal)
00237                 pure_optarg = has_equal;
00238             else if (long_options[match].has_arg ==
00239                      required_argument) {
00240                 /*
00241                  * optional argument doesn't use next nargv
00242                  */
00243                 pure_optarg = nargv[pure_optind++];
00244             }
00245         }
00246         if ((long_options[match].has_arg == required_argument)
00247             && (pure_optarg == NULL)) {
00248             /*
00249              * Missing argument; leading ':' indicates no error
00250              * should be generated.
00251              */
00252             if (PRINT_ERROR)
00253                 fprintf(stderr, recargstring,
00254                         current_argv);
00255             /*
00256              * XXX: GNU sets pure_optopt to val regardless of flag
00257              */
00258             if (long_options[match].flag == NULL)
00259                 pure_optopt = long_options[match].val;
00260             else
00261                 pure_optopt = 0;
00262             --pure_optind;
00263             return BADARG;
00264         }
00265     } else {            /* unknown option */
00266         if (short_too) {
00267             --pure_optind;
00268             return -1;
00269         }
00270         if (PRINT_ERROR)
00271             fprintf(stderr, illoptstring, current_argv);
00272         pure_optopt = 0;
00273         return BADCH;
00274     }
00275     if (idx)
00276         *idx = match;
00277     if (long_options[match].flag) {
00278         *long_options[match].flag = long_options[match].val;
00279         return 0;
00280     } else
00281         return long_options[match].val;
00282 }
00283 
00284 /*
00285  * getopt_internal --
00286  *    Parse argc/argv argument vector.  Called by user level routines.
00287  */
00288 static int pure_getopt_internal(int nargc, char * const *nargv,
00289                                 const char *options,
00290                                 const struct pure_option *long_options,
00291                                 int *idx, int flags)
00292 {
00293     char *oli;                /* option letter list index */
00294     int optchar, short_too;
00295     static int posixly_correct = -1;
00296     
00297     if (options == NULL)
00298         return -1;
00299     
00300     /*
00301      * Disable GNU extensions if POSIXLY_CORRECT is set or options
00302      * string begins with a '+'.
00303      */
00304     if (posixly_correct == -1)
00305         posixly_correct = (getenv("POSIXLY_CORRECT") != NULL);
00306     if (posixly_correct || *options == '+')
00307         flags &= ~FLAG_PERMUTE;
00308     else if (*options == '-')
00309         flags |= FLAG_ALLARGS;
00310     if (*options == '+' || *options == '-')
00311         options++;
00312     
00313     /*
00314      * XXX Some GNU programs (like cvs) set pure_optind to 0 instead of
00315      * XXX using pure_optreset.  Work around this braindamage.
00316      */
00317     if (pure_optind == 0)
00318         pure_optind = pure_optreset = 1;
00319     
00320     pure_optarg = NULL;
00321     if (pure_optreset)
00322         nonopt_start = nonopt_end = -1;
00323     start:
00324     if (pure_optreset || !*pure_place) {        /* update scanning pointer */
00325         pure_optreset = 0;
00326         if (pure_optind >= nargc) {          /* end of argument vector */
00327             pure_place = EMSG;
00328             if (nonopt_end != -1) {
00329                 /* do permutation, if we have to */
00330                 pure_permute_args(nonopt_start, nonopt_end,
00331                                   pure_optind, nargv);
00332                 pure_optind -= nonopt_end - nonopt_start;
00333             }
00334             else if (nonopt_start != -1) {
00335                 /*
00336                  * If we skipped non-options, set pure_optind
00337                  * to the first of them.
00338                  */
00339                 pure_optind = nonopt_start;
00340             }
00341             nonopt_start = nonopt_end = -1;
00342             return -1;
00343         }
00344         if (*(pure_place = nargv[pure_optind]) != '-' ||
00345             (pure_place[1] == '\0' && strchr(options, '-') == NULL)) {
00346             pure_place = EMSG;        /* found non-option */
00347             if (flags & FLAG_ALLARGS) {
00348                 /*
00349                  * GNU extension:
00350                  * return non-option as argument to option 1
00351                  */
00352                 pure_optarg = nargv[pure_optind++];
00353                 return INORDER;
00354             }
00355             if (!(flags & FLAG_PERMUTE)) {
00356                 /*
00357                  * If no permutation wanted, stop parsing
00358                  * at first non-option.
00359                  */
00360                 return -1;
00361             }
00362             /* do permutation */
00363             if (nonopt_start == -1)
00364                 nonopt_start = pure_optind;
00365             else if (nonopt_end != -1) {
00366                 pure_permute_args(nonopt_start, nonopt_end,
00367                                   pure_optind, nargv);
00368                 nonopt_start = pure_optind -
00369                     (nonopt_end - nonopt_start);
00370                 nonopt_end = -1;
00371             }
00372             pure_optind++;
00373             /* process next argument */
00374             goto start;
00375         }
00376         if (nonopt_start != -1 && nonopt_end == -1)
00377             nonopt_end = pure_optind;
00378 
00379         /*
00380          * Check for "--" or "--foo" with no long options
00381          * but if pure_place is simply "-" leave it unmolested.
00382          */
00383 
00384         if (pure_place[1] != '\0' && *++pure_place == '-' &&
00385             (pure_place[1] == '\0' || long_options == NULL)) {
00386             pure_optind++;
00387             pure_place = EMSG;
00388             /*
00389              * We found an option (--), so if we skipped
00390              * non-options, we have to permute.
00391              */
00392             if (nonopt_end != -1) {
00393                 pure_permute_args(nonopt_start, nonopt_end,
00394                                   pure_optind, nargv);
00395                 pure_optind -= nonopt_end - nonopt_start;
00396             }
00397             nonopt_start = nonopt_end = -1;
00398             return -1;
00399         }
00400     }
00401     
00402     /*
00403      * Check long options if:
00404      *  1) we were passed some
00405      *  2) the arg is not just "-"
00406      *  3) either the arg starts with -- we are pure_getopt_long_only()
00407      */
00408     if (long_options != NULL && pure_place != nargv[pure_optind] &&
00409         (*pure_place == '-' || (flags & FLAG_LONGONLY))) {
00410         short_too = 0;
00411         if (*pure_place == '-')
00412             pure_place++;        /* --foo long option */
00413         else if (*pure_place != ':' && strchr(options, *pure_place) != NULL)
00414             short_too = 1;        /* could be short option too */
00415         
00416         optchar = pure_parse_long_options(nargv, options, long_options,
00417                                           idx, short_too);
00418         if (optchar != -1) {
00419             pure_place = EMSG;
00420             return optchar;
00421         }
00422     }
00423     
00424     if ((optchar = (int) *pure_place++) == ':' ||
00425         (optchar == '-' && *pure_place != '\0') ||        
00426         (oli = strchr(options, optchar)) == NULL) { 
00427          /*
00428           * If the user specified "-" and '-' isn't listed in
00429           * options, return -1 (non-option) as per POSIX.
00430           * Otherwise, it is an unknown option character (or :').
00431           */
00432         if (optchar == '-' && *pure_place == '\0')
00433             return -1;
00434         if (!*pure_place)
00435             ++pure_optind;
00436         if (PRINT_ERROR)
00437             fprintf(stderr, illoptchar, optchar);
00438         pure_optopt = optchar;
00439         return BADCH;
00440     }
00441     if (long_options != NULL && optchar == 'W' && oli[1] == ';') {
00442         /* -W long-option */
00443         if (*pure_place)            /* no space */
00444             /* NOTHING */;
00445         else if (++pure_optind >= nargc) {    /* no arg */
00446             pure_place = EMSG;
00447             if (PRINT_ERROR)
00448                 fprintf(stderr, recargchar, optchar);
00449             pure_optopt = optchar;
00450             return BADARG;
00451         } else                /* white space */
00452             pure_place = nargv[pure_optind];
00453         optchar = pure_parse_long_options(nargv, options, long_options,
00454                                           idx, 0);
00455         pure_place = EMSG;
00456         return optchar;
00457     }
00458     if (*++oli != ':') {            /* doesn't take argument */
00459         if (!*pure_place)
00460             ++pure_optind;
00461     } else {                /* takes (optional) argument */
00462         pure_optarg = NULL;
00463         if (*pure_place)            /* no white space */
00464             pure_optarg = pure_place;
00465         /* XXX: disable test for :: if PC? (GNU doesn't) */
00466         else if (oli[1] != ':') {    /* arg not optional */
00467             if (++pure_optind >= nargc) {    /* no arg */
00468                 pure_place = EMSG;
00469                 if (PRINT_ERROR)
00470                     fprintf(stderr, recargchar, optchar);
00471                 pure_optopt = optchar;
00472                 return BADARG;
00473             } else {
00474                 pure_optarg = nargv[pure_optind];
00475             }
00476         }
00477         pure_place = EMSG;
00478         ++pure_optind;
00479     }
00480     /* dump back option letter */
00481     return optchar;
00482 }
00483 
00484 /*
00485  * getopt --
00486  *    Parse argc/argv argument vector.
00487  */
00488 int pure_getopt(int nargc, char * const *nargv, const char *options)
00489 {
00490     
00491     /*
00492      * We dont' pass FLAG_PERMUTE to pure_getopt_internal() since
00493      * the BSD getopt(3) (unlike GNU) has never done this.
00494      *
00495      * Furthermore, since many privileged programs call getopt()
00496      * before dropping privileges it makes sense to keep things
00497      * as simple (and bug-free) as possible.
00498      */
00499     return pure_getopt_internal(nargc, nargv, options, NULL, NULL, 0);
00500 }
00501 
00502 /*
00503  * pure_getopt_long --
00504  *    Parse argc/argv argument vector.
00505  */
00506 int pure_getopt_long(int nargc, char * const *nargv, const char *options,
00507                      const struct pure_option *long_options, int *idx)
00508 {
00509     return pure_getopt_internal(nargc, nargv, options, long_options, idx,
00510                                 FLAG_PERMUTE);
00511 }
00512 
00513 /*
00514  * pure_getopt_long_only --
00515  *    Parse argc/argv argument vector.
00516  */
00517 int pure_getopt_long_only(int nargc, char * const *nargv,
00518                                const char *options,
00519                                const struct pure_option *long_options,
00520                                int *idx)
00521 {
00522     return pure_getopt_internal(nargc, nargv, options, long_options, idx,
00523                                 FLAG_PERMUTE|FLAG_LONGONLY);
00524 }
00525 
00526 #endif

Generated on Wed Sep 14 02:55:58 2005 for bro_docs by doxygen 1.3.5