
/* getopt.c - get command line options
 *
 * Parse the command line options, System V style.
 *
 * Standard option syntax is:
 *
 *         option = -[optLetter,...][argLetter argument]
 *
 * where
 *    - there is no space between the '-' and optLetters or argLetters.
 *    - optLetters and argLetters are alphabetic, not punctuation characters.
 *    - optLetters, if present, must be matched in options.
 *    - argLetters, if present, are found in options followed by ':'.
 *    - argument is any white-space delimited string.  Note that it can
 *      include the '-'.
 *    - upper and lower case letters are distinct.
 *
 * There may be multiple option clusters on a command line, each
 * beginning with a '-', but all must appear before any non-option
 * arguments (arguments not introduced by a '-'). optLetters and
 * argLetters may be repeated, it is up to the caller to decide
 * if that is an error.
 *
 * The character '-' appearing alone as the last argument is an error.
 * The lead-in sequence '--' causes itself and all the rest of the
 * line to be ignored (allowing non-options which begin with '-'.
 *
 * The string *options allows valid optLetters and argLetters to be
 * recognized. argLetters are followed with ':'.  getopt() returns the
 * value of the option character found, or EOF if no more options are in
 * the command line.    If option is an argLetter then the global optarg is
 * set to point to the argument string (having skipped any white-space).
 *
 * The global optind is initially 1 and is always left as the index
 * of the next argument of argv[] which getopt has not taken.      Note
 * that if '--' is used then optind is stepped to the next argument
 * before getopt() returns EOF.
 *
 * If an error occurs, that is '-' precedes an unknown letter, then
 * getopt() will return a '?' character and normally prints an error
 * message via perror().        If the global variable opterr is set to
 * false (zero) before calling getopt() then the error message is
 * not printed.
 *
 * For example, if
 *
 *         *options == "A:F:PuU:wXZ:"
 *
 * then 'P', 'u', 'w', and 'X' are option letters and 'F', 'U', 'Z'
 * are followed by arguments.     A valid command line may be:
 *
 *     command  -uPFPi -X -A L otherparameters
 *
 * where:
 *      - 'u' and 'P' will be returned as isolated option letters.
 *    - 'F' will return with "Pi" as its argument string.
 *    - 'X' is an isolated option.
 *    - 'A' will return with "L" as its argument.
 *    - "otherparameters" is not an option, and terminates getOpt.  The
 *      caller may collect remaining arguments using argv pointers.
*/

#include <errno.h>
#include <stdio.h>
#include <string.h>

int            optind  = 1;    /* index of which argument is next */
char    *optarg;         /* pointer to argument of current option */
int      opterr  = 1;    /* allow error message  */

static    char   *letP = NULL;    /* remember next option char's location */


int        getopt(int argc, char *argv[], char *options)
{
          unsigned char ch;
         char *optP;

          if (argc > optind) {
                 if (letP == NULL) {
                         if ((letP = argv[optind]) == NULL ||
                                 *(letP++) != '-')  goto gopEOF;
                         if (*letP == '-') {
                                 optind++;  goto gopEOF;
                         }
                 }
                 if (0 == (ch = *(letP++))) {
                         optind++;  goto gopEOF;
                 }
                 if (':' == ch  ||  (optP = strchr(options, ch)) == NULL)
                         goto gopError;
                 if (':' == *(++optP)) {
                         optind++;
                         if (0 == *letP) {
                                 if (argc <= optind)  goto  gopError;
                                 letP = argv[optind++];
                         }
                         optarg = letP;
                         letP = NULL;
                 } else {
                         if (0 == *letP) {
                                 optind++;
                                 letP = NULL;
                         }
                         optarg = NULL;
                 }
                 return ch;
         }
gopEOF:
          optarg = letP = NULL;
         return EOF;

gopError:
           optarg = NULL;
         errno  = EINVAL;
         if (opterr)
                 perror ("getopt()");
         return ('?');
}
