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.
  */
 
be4bf7f4
 #if HAVE_CONFIG_H
 #include "clamav-config.h"
 #endif
 
b61db429
 #include <sys/types.h>
 #include <stdlib.h>
e3aaff8e
 #include <stdio.h>
b61db429
 #include <string.h>
e3aaff8e
 #include "getopt.h"
 
288057e9
 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
 {
288057e9
     char **argv        = (char **)argvc;
     static int charind = 0;
     const char *s;
     char mode, colon_mode;
     int off = 0, opt = -1;
b61db429
 
288057e9
     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++;
b61db429
         }
e3aaff8e
     }
288057e9
     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;
             }
         if (opterr) fprintf(stderr,
                             "%s: illegal option -- %c\n",
                             argv[0], optopt);
         opt = '?';
         if (argv[optind][++charind] == '\0') {
             optind++;
             charind = 0;
         }
     getopt_ok:
         if (charind && !argv[optind][charind]) {
             optind++;
             charind = 0;
         }
     } 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;
b61db429
 
288057e9
         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;
                     opt    = my_getopt(argc, argv, opts);
                     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;
b61db429
         }
288057e9
     } else {
         charind++;
         opt = my_getopt(argc, argv, opts);
e3aaff8e
     }
288057e9
     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
 
288057e9
 static int _getopt_internal(int argc, char *argv[], const char *shortopts,
                             const struct option *longopts, int *longind,
                             int long_only)
e3aaff8e
 {
288057e9
     char mode, colon_mode;
     int shortoff = 0, opt = -1;
b61db429
 
288057e9
     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
     }
288057e9
     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;
b61db429
 
288057e9
         opt = -1;
         if (mode == '+')
             return -1;
         else if (mode == '-') {
             optarg = argv[optind++];
             return 1;
         }
         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;
             }
     } else if ((!long_only) && (argv[optind][1] != '-'))
         opt = my_getopt(argc, argv, shortopts);
     else {
         int charind, offset;
         int found = 0, ind, hits = 0;
b61db429
 
288057e9
         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++;
                 if (optopt == c) return my_getopt(argc, argv, shortopts);
             }
         }
         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;
b61db429
 
288057e9
             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; // Never used again past here
                 }
             } 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;
             }
             optind++;
         } else if (!hits) {
             if (offset == 1)
                 opt = my_getopt(argc, argv, shortopts);
             else {
                 opt = '?';
                 if (opterr) fprintf(stderr,
                                     "%s: unrecognized option `%s'\n",
                                     argv[0], argv[optind++]);
             }
b61db429
         } else {
288057e9
             opt = '?';
             if (opterr) fprintf(stderr,
                                 "%s: option `%s' is ambiguous\n",
                                 argv[0], argv[optind++]);
b61db429
         }
     }
288057e9
     if (optind > argc) optind = argc;
     return opt;
e3aaff8e
 }
 
288057e9
 int my_getopt_long(int argc, char *argv[], const char *shortopts,
                    const struct option *longopts, int *longind)
e3aaff8e
 {
288057e9
     return _getopt_internal(argc, argv, shortopts, longopts, longind, 0);
e3aaff8e
 }
 
288057e9
 int my_getopt_long_only(int argc, char *argv[], const char *shortopts,
                         const struct option *longopts, int *longind)
e3aaff8e
 {
288057e9
     return _getopt_internal(argc, argv, shortopts, longopts, longind, 1);
e3aaff8e
 }