| /**************************************************************************** |
| |
| 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 GETOPT_LONG_OPTION_T. 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> |
| #ifndef GETOPT_H |
| #include "getopt.h" |
| #endif |
| |
| /* 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 **argv, int len1, int len2) |
| { |
| reverse_argv_elements (argv, len1); |
| reverse_argv_elements (argv, len1 + len2); |
| reverse_argv_elements (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 **argv, char *shortopts, |
| GETOPT_LONG_OPTION_T * 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 = NULL; |
| int arg_next = 0; |
| |
| /* first, deal with silly parameters and easy stuff */ |
| if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL)) |
| return (optopt = '?'); |
| 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 == 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_argument) : no_argument); |
| 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_argument: |
| 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_argument: |
| 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; |
| } |
| |
| #ifndef _AIX |
| int |
| getopt (int argc, char **argv, char *optstring) |
| { |
| return getopt_internal (argc, argv, optstring, NULL, NULL, 0); |
| } |
| #endif |
| |
| int |
| getopt_long (int argc, char **argv, const char *shortopts, |
| const GETOPT_LONG_OPTION_T * longopts, int *longind) |
| { |
| return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0); |
| } |
| |
| int |
| getopt_long_only (int argc, char **argv, const char *shortopts, |
| const GETOPT_LONG_OPTION_T * longopts, int *longind) |
| { |
| return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1); |
| } |
| |
| /* end of file GETOPT.C */ |