/*
 * Getopt for GNU. Copyright (C) 1987, 1989, 1990, 1991 Free Software
 * Foundation, Inc.
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 675
 * Mass Ave, Cambridge, MA 02139, USA.
 */

#ifdef __STDC__
#define CONST const
#else
#define CONST
#endif

/*
 * This version of `getopt' appears to the caller like standard Unix `getopt'
 * but it behaves differently for the user, since it allows the user to
 * intersperse the options with the other arguments.
 * 
 * As `getopt' works, it permutes the elements of `argv' so that, when it is
 * done, all the options precede everything else.  Thus all application
 * programs are extended to handle flexible argument order.
 * 
 * Setting the environment variable _POSIX_OPTION_ORDER disables permutation.
 * Then the behavior is completely standard.
 * 
 * GNU application programs can use a third alternative mode in which they can
 * distinguish the relative order of options and other arguments.
 */

#include <stdio.h>

/* If compiled with GNU C, use the built-in alloca */
#ifdef __GNUC__
#define alloca __builtin_alloca
#else /* not __GNUC__ */
#if defined (sparc) || defined (_DCC)
#include <alloca.h>

#define index(p1, p2)   strchr(p1, p2)
#define rindex(p1, p2)  strrchr(p1, p2)

#else
#ifdef _AIX
#pragma alloca
#else
char *alloca ();

#endif
#endif /* sparc */
#endif /* not __GNUC__ */

#if defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
#include <stdlib.h>
#else /* STDC_HEADERS or __GNU_LIBRARY__ */
char *getenv ();
char *malloc ();

#endif /* STDC_HEADERS or __GNU_LIBRARY__ */

#if defined(USG) || defined(STDC_HEADERS) || defined(__GNU_LIBRARY__)
#include <string.h>
#define bcopy(s, d, n) memcpy ((d), (s), (n))
#define index strchr
#else /* USG or STDC_HEADERS or __GNU_LIBRARY__ */
#ifdef VMS
#include <string.h>
#else /* VMS */
#include <strings.h>
#endif /* VMS */
/*
 * Declaring bcopy causes errors on systems whose declarations are different.
 * If the declaration is omitted, everything works fine.
 */
#endif /* USG or STDC_HEADERS or __GNU_LIBRARY__ */

/*
 * For communication from `getopt' to the caller. When `getopt' finds an
 * option that takes an argument, the argument value is returned here. Also,
 * when `ordering' is RETURN_IN_ORDER, each non-option ARGV-element is
 * returned here.
 */

char *optarg = 0;

/*
 * Index in ARGV of the next element to be scanned. This is used for
 * communication to and from the caller and for communication between
 * successive calls to `getopt'.
 * 
 * On entry to `getopt', zero means this is the first call; initialize.
 * 
 * When `getopt' returns EOF, this is the index of the first of the non-option
 * elements that the caller should itself scan.
 * 
 * Otherwise, `optind' communicates from one call to the next how much of ARGV
 * has been scanned so far.
 */

int optind = 0;

/*
 * The next char to be scanned in the option-element in which the last option
 * character we returned was found. This allows us to pick up the scan where
 * we left off.
 * 
 * If this is zero, or a null string, it means resume the scan by advancing to
 * the next ARGV-element.
 */

static char *nextchar;

/*
 * Callers store zero here to inhibit the error message for unrecognized
 * options.
 */

int opterr = 1;

/*
 * Describe 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 _POSIX_OPTION_ORDER 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
 * _POSIX_OPTION_ORDER, or using `+' as the first character of the list of
 * option characters.
 * 
 * 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 list of option characters 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' to return EOF with `optind' != ARGC.
 */

static enum
{
   REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
} ordering;

/*
 * Describe the long-named options requested by the application.
 * _GETOPT_LONG_OPTIONS is a vector of `struct option' terminated by an
 * element containing a name which is zero. The field `has_arg' is 1 if the
 * option takes an argument, 2 if it takes an optional argument.
 */

struct option
{
   char *name;
   int has_arg;
   int *flag;
   int val;
};

CONST struct option *_getopt_long_options;

int _getopt_long_only = 0;

/*
 * Index in _GETOPT_LONG_OPTIONS of the long-named option actually found.
 * Only valid when a long-named option was found.
 */

int option_index;

/* Handle permutation of arguments.  */

/*
 * Describe the part of ARGV that contains non-options that have been
 * skipped.  `first_nonopt' is the index in ARGV of the first of them;
 * `last_nonopt' is the index after the last of them.
 */

static int first_nonopt;
static int last_nonopt;

/*
 * Exchange two adjacent subsequences of ARGV. One subsequence is elements
 * [first_nonopt,last_nonopt) which contains all the non-options that have
 * been skipped so far. The other is elements [last_nonopt,optind), which
 * contains all the options processed since those non-options were skipped.
 * 
 * `first_nonopt' and `last_nonopt' are relocated so that they describe the new
 * indices of the non-options in ARGV after they are moved.
 */

static void
 exchange (argv)
char **argv;
{
   int nonopts_size = (last_nonopt - first_nonopt) * sizeof (char *);
   char **temp = (char **) alloca (nonopts_size);

   /* Interchange the two blocks of data in ARGV.  */

   bcopy (&argv[first_nonopt], temp, nonopts_size);
   bcopy (&argv[last_nonopt], &argv[first_nonopt],
	  (optind - last_nonopt) * sizeof (char *));

   bcopy (temp, &argv[first_nonopt + optind - last_nonopt], nonopts_size);

   /* Update records for the slots the non-options now occupy.  */

   first_nonopt += (optind - last_nonopt);
   last_nonopt = optind;
}

/*
 * Scan elements of ARGV (whose length is ARGC) for option characters given
 * in OPTSTRING.
 * 
 * If an element of ARGV starts with '-', and is not exactly "-" or "--", then
 * it is an option element.  The characters of this element (aside from the
 * initial '-') are option characters.  If `getopt' is called repeatedly, it
 * returns successively each of the option characters from each of the option
 * elements.
 * 
 * If `getopt' finds another option character, it returns that character,
 * updating `optind' and `nextchar' so that the next call to `getopt' can
 * resume the scan with the following option character or ARGV-element.
 * 
 * If there are no more option characters, `getopt' returns `EOF'. Then `optind'
 * is the index in ARGV of the first ARGV-element that is not an option.
 * (The ARGV-elements have been permuted so that those that are not options
 * now come last.)
 * 
 * OPTSTRING is a string containing the legitimate option characters. If an
 * option character is seen that is not listed in OPTSTRING, return '?' after
 * printing an error message.  If you set `opterr' to zero, the error message
 * is suppressed but we still return '?'.
 * 
 * If a char in OPTSTRING is followed by a colon, that means it wants an arg, so
 * the following text in the same ARGV-element, or the text of the following
 * ARGV-element, is returned in `optarg'.  Two colons mean an option that
 * wants an optional arg; if there is text in the current ARGV-element, it is
 * returned in `optarg', otherwise `optarg' is set to zero.
 * 
 * If OPTSTRING starts with `-' or `+', it requests different methods of
 * handling the non-option ARGV-elements. See the comments about
 * RETURN_IN_ORDER and REQUIRE_ORDER, above.
 * 
 * Long-named options begin with `+' instead of `-'. Their names may be
 * abbreviated as long as the abbreviation is unique or is an exact match for
 * some defined option.  If they have an argument, it follows the option name
 * in the same ARGV-element, separated from the option name by a `=', or else
 * the in next ARGV-element. When `getopt' finds a long-named option, it
 * returns 0 if that option's `flag' field is nonzero, the value of the
 * option's `val' field otherwise.
 */

int
 getopt (argc, argv, optstring)
int argc;
char **argv;
CONST char *optstring;
{
   optarg = 0;

   /*
    * Initialize the internal data when the first call is made. Start
    * processing options with ARGV-element 1 (since ARGV-element 0 is the
    * program name); the sequence of previously skipped non-option
    * ARGV-elements is empty.
    */

   if (optind == 0)
   {
      first_nonopt = last_nonopt = optind = 1;

      nextchar = 0;

      /* Determine how to handle the ordering of options and nonoptions.  */

      if (optstring[0] == '-')
      {
	 ordering = RETURN_IN_ORDER;
	 ++optstring;
      }
      else if (optstring[0] == '+')
      {
	 ordering = REQUIRE_ORDER;
	 ++optstring;
      }
      else if (getenv ("_POSIX_OPTION_ORDER") != 0)
	 ordering = REQUIRE_ORDER;
      else
	 ordering = PERMUTE;
   }

   if (nextchar == 0 || *nextchar == 0)
   {
      if (ordering == PERMUTE)
      {
	 /*
	  * If we have just processed some options following some
	  * non-options, exchange them so that the options come first.
	  */

	 if (first_nonopt != last_nonopt && last_nonopt != optind)
	    exchange (argv);
	 else if (last_nonopt != optind)
	    first_nonopt = optind;

	 /*
	  * Now skip any additional non-options and extend the range of
	  * non-options previously skipped.
	  */

	 while (optind < argc
		&& (argv[optind][0] != '-'
		    || argv[optind][1] == 0)
		&& (_getopt_long_options == 0
		    || argv[optind][0] != '+'
		    || argv[optind][1] == 0))
	    optind++;
	 last_nonopt = optind;
      }

      /*
       * Special ARGV-element `--' means premature end of options. Skip it
       * like a null option, then exchange with previous non-options as if it
       * were an option, then skip everything else like a non-option.
       */

      if (optind != argc && !strcmp (argv[optind], "--"))
      {
	 optind++;

	 if (first_nonopt != last_nonopt && last_nonopt != optind)
	    exchange (argv);
	 else if (first_nonopt == last_nonopt)
	    first_nonopt = optind;
	 last_nonopt = argc;

	 optind = argc;
      }

      /*
       * If we have done all the ARGV-elements, stop the scan and back over
       * any non-options that we skipped and permuted.
       */

      if (optind == argc)
      {
	 /*
	  * Set the next-arg-index to point at the non-options that we
	  * previously skipped, so the caller will digest them.
	  */
	 if (first_nonopt != last_nonopt)
	    optind = first_nonopt;
	 return EOF;
      }

      /*
       * If we have come to a non-option and did not permute it, either stop
       * the scan or describe it to the caller and pass it by.
       */

      if ((argv[optind][0] != '-' || argv[optind][1] == 0)
	  && (_getopt_long_options == 0
	      || argv[optind][0] != '+' || argv[optind][1] == 0))
      {
	 if (ordering == REQUIRE_ORDER)
	    return EOF;
	 optarg = argv[optind++];
	 return 1;
      }

      /*
       * We have found another option-ARGV-element. Start decoding its
       * characters.
       */

      nextchar = argv[optind] + 1;
   }

   if (_getopt_long_options != 0
       && (argv[optind][0] == '+'
	   || (_getopt_long_only && argv[optind][0] == '-'))
      )
   {
      CONST struct option *p;
      char *s = nextchar;
      int exact = 0;
      int ambig = 0;
      CONST struct option *pfound = 0;
      int indfound;

      while (*s && *s != '=')
	 s++;

      /* Test all options for either exact match or abbreviated matches.  */
      for (p = _getopt_long_options, option_index = 0; p->name;
	   p++, option_index++)
	 if (!strncmp (p->name, nextchar, s - nextchar))
	 {
	    if (s - nextchar == strlen (p->name))
	    {
	       /* Exact match found.  */
	       pfound = p;
	       indfound = option_index;
	       exact = 1;
	       break;
	    }
	    else if (pfound == 0)
	    {
	       /* First nonexact match found.  */
	       pfound = p;
	       indfound = option_index;
	    }
	    else
	       /* Second nonexact match found.  */
	       ambig = 1;
	 }

      if (ambig && !exact)
      {
	 fprintf (stderr, "%s: option `%s' is ambiguous\n",
		  argv[0], argv[optind]);
	 nextchar += strlen (nextchar);
	 optind++;
	 return '?';
      }

      if (pfound != 0)
      {
	 option_index = indfound;
	 optind++;
	 if (*s)
	 {
	    if (pfound->has_arg > 0)
	       optarg = s + 1;
	    else
	    {
	       fprintf (stderr,
			"%s: option `%c%s' doesn't allow an argument\n",
			argv[0], argv[optind - 1][0], pfound->name);
	       nextchar += strlen (nextchar);
	       return '?';
	    }
	 }
	 else if (pfound->has_arg == 1)
	 {
	    if (optind < argc)
	       optarg = argv[optind++];
	    else
	    {
	       fprintf (stderr, "%s: option `%s' requires an argument\n",
			argv[0], argv[optind - 1]);
	       nextchar += strlen (nextchar);
	       return '?';
	    }
	 }
	 nextchar += strlen (nextchar);
	 if (pfound->flag)
	 {
	    *(pfound->flag) = pfound->val;
	    return 0;
	 }
	 return pfound->val;
      }
      /*
       * Can't find it as a long option.  If this is getopt_long_only, and
       * the option starts with '-' and is a valid short option, then
       * interpret it as a short option.  Otherwise it's an error.
       */
      if (_getopt_long_only == 0 || argv[optind][0] == '+' ||
	  index (optstring, *nextchar) == 0)
      {
	 if (opterr != 0)
	    fprintf (stderr, "%s: unrecognized option `%c%s'\n",
		     argv[0], argv[optind][0], nextchar);
	 nextchar += strlen (nextchar);
	 optind++;
	 return '?';
      }
   }

   /* Look at and handle the next option-character.  */

   {
      char c = *nextchar++;
      char *temp = index (optstring, c);

      /* Increment `optind' when we start to process its last character.  */
      if (*nextchar == 0)
	 optind++;

      if (temp == 0 || c == ':')
      {
	 if (opterr != 0)
	 {
	    if (c < 040 || c >= 0177)
	       fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
			argv[0], c);
	    else
	       fprintf (stderr, "%s: unrecognized option `-%c'\n",
			argv[0], c);
	 }
	 return '?';
      }
      if (temp[1] == ':')
      {
	 if (temp[2] == ':')
	 {
	    /* This is an option that accepts an argument optionally.  */
	    if (*nextchar != 0)
	    {
	       optarg = nextchar;
	       optind++;
	    }
	    else
	       optarg = 0;
	    nextchar = 0;
	 }
	 else
	 {
	    /* This is an option that requires an argument.  */
	    if (*nextchar != 0)
	    {
	       optarg = nextchar;
	       /*
	        * If we end this ARGV-element by taking the rest as an arg,
	        * we must advance to the next element now.
	        */
	       optind++;
	    }
	    else if (optind == argc)
	    {
	       if (opterr != 0)
		  fprintf (stderr, "%s: option `-%c' requires an argument\n",
			   argv[0], c);
	       c = '?';
	    }
	    else
	       /*
	        * We already incremented `optind' once; increment it again
	        * when taking next ARGV-elt as argument.
	        */
	       optarg = argv[optind++];
	    nextchar = 0;
	 }
      }
      return c;
   }
}

#ifdef TEST

/*
 * Compile with -DTEST to make an executable for use in testing the above
 * definition of `getopt'.
 */

int
 main (argc, argv)
int argc;
char **argv;
{
   int c;
   int digit_optind = 0;

   while (1)
   {
      int this_option_optind = optind ? optind : 1;

      c = getopt (argc, argv, "abc:d:0123456789");
      if (c == EOF)
	 break;

      switch (c)
      {
	 case '0':
	 case '1':
	 case '2':
	 case '3':
	 case '4':
	 case '5':
	 case '6':
	 case '7':
	 case '8':
	 case '9':
	    if (digit_optind != 0 && digit_optind != this_option_optind)
	       printf ("digits occur in two different argv-elements.\n");
	    digit_optind = this_option_optind;
	    printf ("option %c\n", c);
	    break;

	 case 'a':
	    printf ("option a\n");
	    break;

	 case 'b':
	    printf ("option b\n");
	    break;

	 case 'c':
	    printf ("option c with value `%s'\n", optarg);
	    break;

	 case '?':
	    break;

	 default:
	    printf ("?? getopt returned character code 0%o ??\n", c);
      }
   }

   if (optind < argc)
   {
      printf ("non-option ARGV-elements: ");
      while (optind < argc)
	 printf ("%s ", argv[optind++]);
      printf ("\n");
   }

   exit (0);
}

#endif /* TEST */
