shared/getopt.c
b61db429
 /*
  *  getopt.c - my re-implementation of getopt.
  *  Copyright 1997, 2000, 2001, 2002, 2006, Benjamin Sittler
  *
  *  Permission is hereby granted, free of charge, to any person
  *  obtaining a copy of this software and associated documentation
  *  files (the "Software"), to deal in the Software without
  *  restriction, including without limitation the rights to use, copy,
  *  modify, merge, publish, distribute, sublicense, and/or sell copies
  *  of the Software, and to permit persons to whom the Software is
  *  furnished to do so, subject to the following conditions:
  *  
  *  The above copyright notice and this permission notice shall be
  *  included in all copies or substantial portions of the Software.
  *  
  *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  *  EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  *  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  *  NONINFRINGEMENT.  IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  *  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  *  DEALINGS IN THE SOFTWARE.
  */
 
 #include <sys/types.h>
 #include <stdlib.h>
e3aaff8e
 #include <stdio.h>
b61db429
 #include <string.h>
e3aaff8e
 #include "getopt.h"
 
b61db429
 int optind=1, opterr=1, optopt=0;
 char *optarg=0;
e3aaff8e
 
b61db429
 /* reset argument parser to start-up values */
fe5a1daa
 /*
b61db429
 int getopt_reset(void)
e3aaff8e
 {
b61db429
     optind = 1;
     opterr = 1;
     optopt = 0;
     optarg = 0;
     return 0;
e3aaff8e
 }
fe5a1daa
 */
e3aaff8e
 
b61db429
 /* this is the plain old UNIX getopt, with GNU-style extensions. */
 /* if you're porting some piece of UNIX software, this is all you need. */
 /* this supports GNU-style permution and optional arguments */
e3aaff8e
 
fe5a1daa
 int my_getopt(int argc, char *argvc[], const char *opts)
e3aaff8e
 {
08c9ac85
   char **argv = (char**)argvc;
b61db429
   static int charind=0;
   const char *s;
   char mode, colon_mode;
   int off = 0, opt = -1;
 
   if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
   else {
     if((colon_mode = *opts) == ':') off ++;
     if(((mode = opts[off]) == '+') || (mode == '-')) {
       off++;
       if((colon_mode != ':') && ((colon_mode = opts[off]) == ':'))
         off ++;
e3aaff8e
     }
b61db429
   }
   optarg = 0;
   if(charind) {
     optopt = argv[optind][charind];
     for(s=opts+off; *s; s++) if(optopt == *s) {
       charind++;
       if((*(++s) == ':') || ((optopt == 'W') && (*s == ';'))) {
         if(argv[optind][charind]) {
           optarg = &(argv[optind++][charind]);
           charind = 0;
         } else if(*(++s) != ':') {
           charind = 0;
           if(++optind >= argc) {
             if(opterr) fprintf(stderr,
                                 "%s: option requires an argument -- %c\n",
                                 argv[0], optopt);
             opt = (colon_mode == ':') ? ':' : '?';
             goto getopt_ok;
           }
           optarg = argv[optind++];
         }
       }
       opt = optopt;
       goto getopt_ok;
e3aaff8e
     }
b61db429
     if(opterr) fprintf(stderr,
                         "%s: illegal option -- %c\n",
                         argv[0], optopt);
     opt = '?';
     if(argv[optind][++charind] == '\0') {
       optind++;
       charind = 0;
e3aaff8e
     }
b61db429
   getopt_ok:
     if(charind && ! argv[optind][charind]) {
       optind++;
       charind = 0;
e3aaff8e
     }
b61db429
   } else if((optind >= argc) ||
              ((argv[optind][0] == '-') &&
               (argv[optind][1] == '-') &&
               (argv[optind][2] == '\0'))) {
     optind++;
     opt = -1;
   } else if((argv[optind][0] != '-') ||
              (argv[optind][1] == '\0')) {
     char *tmp;
     int i, j, k;
 
     if(mode == '+') opt = -1;
     else if(mode == '-') {
       optarg = argv[optind++];
       charind = 0;
       opt = 1;
     } else {
       for(i=j=optind; i<argc; i++) if((argv[i][0] == '-') &&
                                         (argv[i][1] != '\0')) {
         optind=i;
fe5a1daa
         opt=my_getopt(argc, argv, opts);
b61db429
         while(i > j) {
           tmp=argv[--i];
           for(k=i; k+1<optind; k++) argv[k]=argv[k+1];
           argv[--optind]=tmp;
         }
         break;
       }
       if(i == argc) opt = -1;
e3aaff8e
     }
b61db429
   } else {
     charind++;
fe5a1daa
     opt = my_getopt(argc, argv, opts);
b61db429
   }
   if (optind > argc) optind = argc;
   return opt;
e3aaff8e
 }
 
b61db429
 /* this is the extended getopt_long{,_only}, with some GNU-like
  * extensions. Implements _getopt_internal in case any programs
  * expecting GNU libc getopt call it.
  */
e3aaff8e
 
fe5a1daa
 static int _getopt_internal(int argc, char * argv[], const char *shortopts,
b61db429
                      const struct option *longopts, int *longind,
                      int long_only)
e3aaff8e
 {
b61db429
   char mode, colon_mode = *shortopts;
   int shortoff = 0, opt = -1;
 
   if(getenv("POSIXLY_CORRECT")) colon_mode = mode = '+';
   else {
     if((colon_mode = *shortopts) == ':') shortoff ++;
     if(((mode = shortopts[shortoff]) == '+') || (mode == '-')) {
       shortoff++;
       if((colon_mode != ':') && ((colon_mode = shortopts[shortoff]) == ':'))
         shortoff ++;
e3aaff8e
     }
b61db429
   }
   optarg = 0;
   if((optind >= argc) ||
       ((argv[optind][0] == '-') &&
        (argv[optind][1] == '-') &&
        (argv[optind][2] == '\0'))) {
     optind++;
     opt = -1;
   } else if((argv[optind][0] != '-') ||
             (argv[optind][1] == '\0')) {
     char *tmp;
     int i, j, k;
 
     opt = -1;
     if(mode == '+') return -1;
     else if(mode == '-') {
       optarg = argv[optind++];
       return 1;
e3aaff8e
     }
b61db429
     for(i=j=optind; i<argc; i++) if((argv[i][0] == '-') &&
                                     (argv[i][1] != '\0')) {
       optind=i;
       opt=_getopt_internal(argc, argv, shortopts,
                               longopts, longind,
                               long_only);
       while(i > j) {
         tmp=argv[--i];
         for(k=i; k+1<optind; k++)
           argv[k]=argv[k+1];
         argv[--optind]=tmp;
       }
       break;
e3aaff8e
     }
b61db429
   } else if((!long_only) && (argv[optind][1] != '-'))
fe5a1daa
     opt = my_getopt(argc, argv, shortopts);
b61db429
   else {
     int charind, offset;
     int found = 0, ind, hits = 0;
 
     if(((optopt = argv[optind][1]) != '-') && ! argv[optind][2]) {
       int c;
       
       ind = shortoff;
       while((c = shortopts[ind++])) {
         if(((shortopts[ind] == ':') ||
             ((c == 'W') && (shortopts[ind] == ';'))) &&
            (shortopts[++ind] == ':'))
           ind ++;
fe5a1daa
         if(optopt == c) return my_getopt(argc, argv, shortopts);
e3aaff8e
       }
b61db429
     }
     offset = 2 - (argv[optind][1] != '-');
     for(charind = offset;
         (argv[optind][charind] != '\0') &&
           (argv[optind][charind] != '=');
         charind++);
     for(ind = 0; longopts[ind].name && !hits; ind++)
       if((strlen(longopts[ind].name) == (size_t) (charind - offset)) &&
          (strncmp(longopts[ind].name,
                   argv[optind] + offset, charind - offset) == 0))
         found = ind, hits++;
     if(!hits) for(ind = 0; longopts[ind].name; ind++)
       if(strncmp(longopts[ind].name,
                  argv[optind] + offset, charind - offset) == 0)
         found = ind, hits++;
     if(hits == 1) {
       opt = 0;
 
       if(argv[optind][charind] == '=') {
         if(longopts[found].has_arg == 0) {
           opt = '?';
           if(opterr) fprintf(stderr,
                              "%s: option `--%s' doesn't allow an argument\n",
                              argv[0], longopts[found].name);
         } else {
           optarg = argv[optind] + ++charind;
           charind = 0;
         }
       } else if(longopts[found].has_arg == 1) {
         if(++optind >= argc) {
           opt = (colon_mode == ':') ? ':' : '?';
           if(opterr) fprintf(stderr,
                              "%s: option `--%s' requires an argument\n",
                              argv[0], longopts[found].name);
         } else optarg = argv[optind];
       }
       if(!opt) {
         if (longind) *longind = found;
         if(!longopts[found].flag) opt = longopts[found].val;
         else *(longopts[found].flag) = longopts[found].val;
e3aaff8e
       }
b61db429
       optind++;
     } else if(!hits) {
fe5a1daa
       if(offset == 1) opt = my_getopt(argc, argv, shortopts);
b61db429
       else {
         opt = '?';
         if(opterr) fprintf(stderr,
                            "%s: unrecognized option `%s'\n",
                            argv[0], argv[optind++]);
e3aaff8e
       }
b61db429
     } else {
       opt = '?';
       if(opterr) fprintf(stderr,
                          "%s: option `%s' is ambiguous\n",
                          argv[0], argv[optind++]);
     }
e3aaff8e
   }
b61db429
   if (optind > argc) optind = argc;
   return opt;
e3aaff8e
 }
 
fe5a1daa
 int my_getopt_long(int argc, char * argv[], const char *shortopts,
b61db429
                 const struct option *longopts, int *longind)
e3aaff8e
 {
b61db429
   return _getopt_internal(argc, argv, shortopts, longopts, longind, 0);
e3aaff8e
 }
 
fe5a1daa
 int my_getopt_long_only(int argc, char * argv[], const char *shortopts,
b61db429
                 const struct option *longopts, int *longind)
e3aaff8e
 {
b61db429
   return _getopt_internal(argc, argv, shortopts, longopts, longind, 1);
e3aaff8e
 }