407 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			407 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C
		
	
	
	
| /****************************************************************************
 | |
| 
 | |
| getopt.c - Read command line options
 | |
| 
 | |
| AUTHOR: Gregory Pietsch
 | |
| CREATED Fri Jan 10 21:13:05 1997
 | |
| 
 | |
| DESCRIPTION:
 | |
| 
 | |
| The getopt() function parses the command line arguments.  Its arguments argc
 | |
| and argv are the argument count and array as passed to the main() function
 | |
| on program invocation.  The argument optstring is a list of available option
 | |
| characters.  If such a character is followed by a colon (`:'), the option
 | |
| takes an argument, which is placed in optarg.  If such a character is
 | |
| followed by two colons, the option takes an optional argument, which is
 | |
| placed in optarg.  If the option does not take an argument, optarg is NULL.
 | |
| 
 | |
| The external variable optind is the index of the next array element of argv
 | |
| to be processed; it communicates from one call to the next which element to
 | |
| process.
 | |
| 
 | |
| The getopt_long() function works like getopt() except that it also accepts
 | |
| long options started by two dashes `--'.  If these take values, it is either
 | |
| in the form
 | |
| 
 | |
| --arg=value
 | |
| 
 | |
|  or
 | |
| 
 | |
| --arg value
 | |
| 
 | |
| It takes the additional arguments longopts which is a pointer to the first
 | |
| element of an array of type struct option.  The last element of the array
 | |
| has to be filled with NULL for the name field.
 | |
| 
 | |
| The longind pointer points to the index of the current long option relative
 | |
| to longopts if it is non-NULL.
 | |
| 
 | |
| The getopt() function returns the option character if the option was found
 | |
| successfully, `:' if there was a missing parameter for one of the options,
 | |
| `?' for an unknown option character, and EOF for the end of the option list.
 | |
| 
 | |
| The getopt_long() function's return value is described in the header file.
 | |
| 
 | |
| The function getopt_long_only() is identical to getopt_long(), except that a
 | |
| plus sign `+' can introduce long options as well as `--'.
 | |
| 
 | |
| The following describes how to deal with options that follow non-option
 | |
| argv-elements.
 | |
| 
 | |
| If the caller did not specify anything, the default is REQUIRE_ORDER if the
 | |
| environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise.
 | |
| 
 | |
| REQUIRE_ORDER means don't recognize them as options; stop option processing
 | |
| when the first non-option is seen.  This is what Unix does.  This mode of
 | |
| operation is selected by either setting the environment variable
 | |
| POSIXLY_CORRECT, or using `+' as the first character of the optstring
 | |
| parameter.
 | |
| 
 | |
| PERMUTE is the default.  We permute the contents of ARGV as we scan, so that
 | |
| eventually all the non-options are at the end.  This allows options to be
 | |
| given in any order, even with programs that were not written to expect this.
 | |
| 
 | |
| RETURN_IN_ORDER is an option available to programs that were written to
 | |
| expect options and other argv-elements in any order and that care about the
 | |
| ordering of the two.  We describe each non-option argv-element as if it were
 | |
| the argument of an option with character code 1.  Using `-' as the first
 | |
| character of the optstring parameter selects this mode of operation.
 | |
| 
 | |
| The special argument `--' forces an end of option-scanning regardless of the
 | |
| value of ordering.  In the case of RETURN_IN_ORDER, only `--' can cause
 | |
| getopt() and friends to return EOF with optind != argc.
 | |
| 
 | |
| COPYRIGHT NOTICE AND DISCLAIMER:
 | |
| 
 | |
| Copyright (C) 1997 Gregory Pietsch
 | |
| 
 | |
| This file and the accompanying getopt.h header file are hereby placed in the 
 | |
| public domain without restrictions.  Just give the author credit, don't
 | |
| claim you wrote it or prevent anyone else from using it.
 | |
| 
 | |
| Gregory Pietsch's current e-mail address:
 | |
| gpietsch@comcast.net
 | |
| ****************************************************************************/
 | |
| 
 | |
| /* include files */
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <getopt.h>
 | |
| 
 | |
| /* macros */
 | |
| 
 | |
| /* types */
 | |
| typedef enum GETOPT_ORDERING_T
 | |
| {
 | |
|   PERMUTE,
 | |
|   RETURN_IN_ORDER,
 | |
|   REQUIRE_ORDER
 | |
| } GETOPT_ORDERING_T;
 | |
| 
 | |
| /* globally-defined variables */
 | |
| char *optarg = NULL;
 | |
| int optind = 0;
 | |
| int opterr = 1;
 | |
| int optopt = '?';
 | |
| 
 | |
| /* functions */
 | |
| 
 | |
| /* reverse_argv_elements:  reverses num elements starting at argv */
 | |
| static void
 | |
| reverse_argv_elements (char ** argv, int num)
 | |
| {
 | |
|   int i;
 | |
|   char *tmp;
 | |
| 
 | |
|   for (i = 0; i < (num >> 1); i++)
 | |
|     {
 | |
|       tmp = argv[i];
 | |
|       argv[i] = argv[num - i - 1];
 | |
|       argv[num - i - 1] = tmp;
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* permute: swap two blocks of argv-elements given their lengths */
 | |
| static void
 | |
| permute (char *const argv[], int len1, int len2)
 | |
| {
 | |
|   reverse_argv_elements ((char **)argv, len1);
 | |
|   reverse_argv_elements ((char **)argv, len1 + len2);
 | |
|   reverse_argv_elements ((char **)argv, len2);
 | |
| }
 | |
| 
 | |
| /* is_option: is this argv-element an option or the end of the option list? */
 | |
| static int
 | |
| is_option (char *argv_element, int only)
 | |
| {
 | |
|   return ((argv_element == NULL)
 | |
|           || (argv_element[0] == '-') || (only && argv_element[0] == '+'));
 | |
| }
 | |
| 
 | |
| /* getopt_internal:  the function that does all the dirty work */
 | |
| static int
 | |
| getopt_internal (int argc, char *const argv[], const char *shortopts,
 | |
|                  const struct option *longopts, int *longind, int only)
 | |
| {
 | |
|   GETOPT_ORDERING_T ordering = PERMUTE;
 | |
|   static size_t optwhere = 0;
 | |
|   size_t permute_from = 0;
 | |
|   int num_nonopts = 0;
 | |
|   int optindex = 0;
 | |
|   size_t match_chars = 0;
 | |
|   char *possible_arg = NULL;
 | |
|   int longopt_match = -1;
 | |
|   int has_arg = -1;
 | |
|   char *cp;
 | |
|   int arg_next = 0;
 | |
| 
 | |
|   /* first, deal with silly parameters and easy stuff */
 | |
|   if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL))
 | |
|     return EOF;
 | |
|   if (optind >= argc || argv[optind] == NULL)
 | |
|     return EOF;
 | |
|   if (strcmp (argv[optind], "--") == 0)
 | |
|     {
 | |
|       optind++;
 | |
|       return EOF;
 | |
|     }
 | |
|   /* if this is our first time through */
 | |
|   if (optind == 0)
 | |
|     optind = optwhere = 1;
 | |
| 
 | |
|   /* define ordering */
 | |
|   if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+'))
 | |
|     {
 | |
|       ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER;
 | |
|       shortopts++;
 | |
|     }
 | |
|   else
 | |
|     ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE;
 | |
| 
 | |
|   /*
 | |
|    * based on ordering, find our next option, if we're at the beginning of
 | |
|    * one
 | |
|    */
 | |
|   if (optwhere == 1)
 | |
|     {
 | |
|       switch (ordering)
 | |
|         {
 | |
|         case PERMUTE:
 | |
|           permute_from = optind;
 | |
|           num_nonopts = 0;
 | |
|           while (!is_option (argv[optind], only))
 | |
|             {
 | |
|               optind++;
 | |
|               num_nonopts++;
 | |
|             }
 | |
|           if (argv[optind] == NULL)
 | |
|             {
 | |
|               /* no more options */
 | |
|               optind = permute_from;
 | |
|               return EOF;
 | |
|             }
 | |
|           else if (strcmp (argv[optind], "--") == 0)
 | |
|             {
 | |
|               /* no more options, but have to get `--' out of the way */
 | |
|               permute (argv + permute_from, num_nonopts, 1);
 | |
|               optind = permute_from + 1;
 | |
|               return EOF;
 | |
|             }
 | |
|           break;
 | |
|         case RETURN_IN_ORDER:
 | |
|           if (!is_option (argv[optind], only))
 | |
|             {
 | |
|               optarg = argv[optind++];
 | |
|               return (optopt = 1);
 | |
|             }
 | |
|           break;
 | |
|         case REQUIRE_ORDER:
 | |
|           if (!is_option (argv[optind], only))
 | |
|             return EOF;
 | |
|           break;
 | |
|         }
 | |
|     }
 | |
|   /* we've got an option, so parse it */
 | |
| 
 | |
|   /* first, is it a long option? */
 | |
|   if (longopts != NULL
 | |
|       && (memcmp (argv[optind], "--", 2) == 0
 | |
|           || (only && argv[optind][0] == '+')) && optwhere == 1)
 | |
|     {
 | |
|       /* handle long options */
 | |
|       if (memcmp (argv[optind], "--", 2) == 0)
 | |
|         optwhere = 2;
 | |
|       longopt_match = -1;
 | |
|       possible_arg = strchr (argv[optind] + optwhere, '=');
 | |
|       if (possible_arg == NULL)
 | |
|         {
 | |
|           /* no =, so next argv might be arg */
 | |
|           match_chars = strlen (argv[optind]);
 | |
|           possible_arg = argv[optind] + match_chars;
 | |
|           match_chars = match_chars - optwhere;
 | |
|         }
 | |
|       else
 | |
|         match_chars = (possible_arg - argv[optind]) - optwhere;
 | |
|       for (optindex = 0; longopts[optindex].name != NULL; optindex++)
 | |
|         {
 | |
|           if (memcmp (argv[optind] + optwhere,
 | |
|                       longopts[optindex].name, match_chars) == 0)
 | |
|             {
 | |
|               /* do we have an exact match? */
 | |
|               if (match_chars == (int) (strlen (longopts[optindex].name)))
 | |
|                 {
 | |
|                   longopt_match = optindex;
 | |
|                   break;
 | |
|                 }
 | |
|               /* do any characters match? */
 | |
|               else
 | |
|                 {
 | |
|                   if (longopt_match < 0)
 | |
|                     longopt_match = optindex;
 | |
|                   else
 | |
|                     {
 | |
|                       /* we have ambiguous options */
 | |
|                       if (opterr)
 | |
|                         fprintf (stderr, "%s: option `%s' is ambiguous "
 | |
|                                  "(could be `--%s' or `--%s')\n",
 | |
|                                  argv[0],
 | |
|                                  argv[optind],
 | |
|                                  longopts[longopt_match].name,
 | |
|                                  longopts[optindex].name);
 | |
|                       return (optopt = '?');
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|       if (longopt_match >= 0)
 | |
|         has_arg = longopts[longopt_match].has_arg;
 | |
|     }
 | |
|   /* if we didn't find a long option, is it a short option? */
 | |
|   if (longopt_match < 0 && shortopts != NULL)
 | |
|     {
 | |
|       cp = strchr (shortopts, argv[optind][optwhere]);
 | |
|       if (cp == NULL)
 | |
|         {
 | |
|           /* couldn't find option in shortopts */
 | |
|           if (opterr)
 | |
|             fprintf (stderr,
 | |
|                      "%s: invalid option -- `-%c'\n",
 | |
|                      argv[0], argv[optind][optwhere]);
 | |
|           optwhere++;
 | |
|           if (argv[optind][optwhere] == '\0')
 | |
|             {
 | |
|               optind++;
 | |
|               optwhere = 1;
 | |
|             }
 | |
|           return (optopt = '?');
 | |
|         }
 | |
|       has_arg = ((cp[1] == ':')
 | |
|                  ? ((cp[2] == ':') ? OPTIONAL_ARG : REQUIRED_ARG) : NO_ARG);
 | |
|       possible_arg = argv[optind] + optwhere + 1;
 | |
|       optopt = *cp;
 | |
|     }
 | |
|   /* get argument and reset optwhere */
 | |
|   arg_next = 0;
 | |
|   switch (has_arg)
 | |
|     {
 | |
|     case OPTIONAL_ARG:
 | |
|       if (*possible_arg == '=')
 | |
|         possible_arg++;
 | |
|       if (*possible_arg != '\0')
 | |
|         {
 | |
|           optarg = possible_arg;
 | |
|           optwhere = 1;
 | |
|         }
 | |
|       else
 | |
|         optarg = NULL;
 | |
|       break;
 | |
|     case REQUIRED_ARG:
 | |
|       if (*possible_arg == '=')
 | |
|         possible_arg++;
 | |
|       if (*possible_arg != '\0')
 | |
|         {
 | |
|           optarg = possible_arg;
 | |
|           optwhere = 1;
 | |
|         }
 | |
|       else if (optind + 1 >= argc)
 | |
|         {
 | |
|           if (opterr)
 | |
|             {
 | |
|               fprintf (stderr, "%s: argument required for option `", argv[0]);
 | |
|               if (longopt_match >= 0)
 | |
|                 fprintf (stderr, "--%s'\n", longopts[longopt_match].name);
 | |
|               else
 | |
|                 fprintf (stderr, "-%c'\n", *cp);
 | |
|             }
 | |
|           optind++;
 | |
|           return (optopt = ':');
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           optarg = argv[optind + 1];
 | |
|           arg_next = 1;
 | |
|           optwhere = 1;
 | |
|         }
 | |
|       break;
 | |
|     case NO_ARG:
 | |
|       if (longopt_match < 0)
 | |
|         {
 | |
|           optwhere++;
 | |
|           if (argv[optind][optwhere] == '\0')
 | |
|             optwhere = 1;
 | |
|         }
 | |
|       else
 | |
|         optwhere = 1;
 | |
|       optarg = NULL;
 | |
|       break;
 | |
|     }
 | |
| 
 | |
|   /* do we have to permute or otherwise modify optind? */
 | |
|   if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0)
 | |
|     {
 | |
|       permute (argv + permute_from, num_nonopts, 1 + arg_next);
 | |
|       optind = permute_from + 1 + arg_next;
 | |
|     }
 | |
|   else if (optwhere == 1)
 | |
|     optind = optind + 1 + arg_next;
 | |
| 
 | |
|   /* finally return */
 | |
|   if (longopt_match >= 0)
 | |
|     {
 | |
|       if (longind != NULL)
 | |
|         *longind = longopt_match;
 | |
|       if (longopts[longopt_match].flag != NULL)
 | |
|         {
 | |
|           *(longopts[longopt_match].flag) = longopts[longopt_match].val;
 | |
|           return 0;
 | |
|         }
 | |
|       else
 | |
|         return longopts[longopt_match].val;
 | |
|     }
 | |
|   else
 | |
|     return optopt;
 | |
| }
 | |
| 
 | |
| int
 | |
| getopt (int argc, char *const argv[], const char *optstring)
 | |
| {
 | |
|   return getopt_internal (argc, argv, optstring, NULL, NULL, 0);
 | |
| }
 | |
| 
 | |
| int
 | |
| getopt_long (int argc, char *const argv[], const char *shortopts,
 | |
|              const struct option *longopts, int *longind)
 | |
| {
 | |
|   return getopt_internal (argc, argv, shortopts, longopts, longind, 0);
 | |
| }
 | |
| 
 | |
| int
 | |
| getopt_long_only (int argc, char *const argv[], const char *shortopts,
 | |
|                   const struct option *longopts, int *longind)
 | |
| {
 | |
|   return getopt_internal (argc, argv, shortopts, longopts, longind, 1);
 | |
| }
 | |
| 
 | |
| /* end of file GETOPT.C */
 |