/*****************************************************************************
 *
 *  MODULE NAME : GETOPT.C
 *
 *  COPYRIGHTS:
 *             This module contains code made available by IBM
 *             Corporation on an AS IS basis.  Any one receiving the
 *             module is considered to be licensed under IBM copyrights
 *             to use the IBM-provided source code in any way he or she
 *             deems fit, including copying it, compiling it, modifying
 *             it, and redistributing it, with or without
 *             modifications.  No license under any IBM patents or
 *             patent applications is to be implied from this copyright
 *             license.
 *
 *             A user of the module should understand that IBM cannot
 *             provide technical support for the module and will not be
 *             responsible for any consequences of use of the program.
 *
 *             Any notices, including this one, are not to be removed
 *             from the module without the prior written consent of
 *             IBM.
 *
 *  AUTHOR:   Original author:
 *                 G. R. Blair (BOBBLAIR at AUSVM1)
 *                 Internet: bobblair@bobblair.austin.ibm.com
 *
 *            Extensively revised by:
 *                 John Q. Walker II, Ph.D. (JOHHQ at RALVM6)
 *                 Internet: johnq@ralvm6.vnet.ibm.com
 *
 *            Tweaked by Kern Sibbald for use in Bacula September 2007
 *
 *****************************************************************************/

/******************************************************************************
 * getopt()
 *
 * The getopt() function is a command line parser.  It returns the next
 * option character in argv that matches an option character in opstring.
 *
 * The argv argument points to an array of argc+1 elements containing argc
 * pointers to character strings followed by a null pointer.
 *
 * The opstring argument points to a string of option characters; if an
 * option character is followed by a colon, the option is expected to have
 * an argument that may or may not be separated from it by white space.
 * The external variable optarg is set to point to the start of the option
 * argument on return from getopt().
 *
 * The getopt() function places in optind the argv index of the next argument
 * to be processed.  The system initializes the external variable optind to
 * 1 before the first call to getopt().
 *
 * When all options have been processed (that is, up to the first nonoption
 * argument), getopt() returns EOF.  The special option "--" may be used to
 * delimit the end of the options; EOF will be returned, and "--" will be
 * skipped.
 *
 * The getopt() function returns a question mark (?) when it encounters an
 * option character not included in opstring.  This error message can be
 * disabled by setting opterr to zero.  Otherwise, it returns the option
 * character that was detected.
 *
 * If the special option "--" is detected, or all options have been
 * processed, EOF is returned.
 *
 * Options are marked by either a minus sign (-) or a slash (/) if
 * GETOPT_USE_SLASH is defined.
 *
 * No errors are defined.
 *****************************************************************************/

#include <stdio.h>              /* for EOF */
#include <string.h>             /* for strchr() */
#include "getopt.h"


/* static (global) variables that are specified as exported by getopt() */
char *optarg = NULL;            /* pointer to the start of the option argument  */
int optind = 1;                 /* number of the next argv[] to be evaluated    */
int opterr = 1;                 /* non-zero if a question mark should be returned
                                   when a non-valid option character is detected */
int optopt = '?';               /* Not used */

/* handle possible future character set concerns by putting this in a macro */
#define _next_char(string)  (char)(*(string+1))

int getopt(int argc, char *const argv[], const char *opstring)
{
   static char *pIndexPosition = NULL;  /* place inside current argv string */
   char *pArgString = NULL;     /* where to start from next */
   char *pOptString;            /* the string in our program */


   if (pIndexPosition != NULL) {
      /* we last left off inside an argv string */
      if (*(++pIndexPosition)) {
         /* there is more to come in the most recent argv */
         pArgString = pIndexPosition;
      }
   }

   if (pArgString == NULL) {
      /* we didn't leave off in the middle of an argv string */
      if (optind >= argc) {
         /* more command-line arguments than the argument count */
         pIndexPosition = NULL; /* not in the middle of anything */
         return EOF;            /* used up all command-line arguments */
      }

      /*---------------------------------------------------------------------
       * If the next argv[] is not an option, there can be no more options.
       *-------------------------------------------------------------------*/
      pArgString = argv[optind++];      /* set this to the next argument ptr */

#ifdef GETOPT_USE_SLASH
      if (('/' != *pArgString) &&       /* doesn't start with a slash or a dash? */
          ('-' != *pArgString)) {
         --optind;              /* point to current arg once we're done */
         optarg = NULL;         /* no argument follows the option */
         pIndexPosition = NULL; /* not in the middle of anything */
         return EOF;            /* used up all the command-line flags */
      }
#else
      if ('-' != *pArgString) { /* doesn't start with a dash? */
         --optind;              /* point to current arg once we're done */
         optarg = NULL;         /* no argument follows the option */
         pIndexPosition = NULL; /* not in the middle of anything */
         return EOF;            /* used up all the command-line flags */
      }
#endif

      /* check for special end-of-flags markers */
      if ((strcmp(pArgString, "-") == 0) ||
          (strcmp(pArgString, "--") == 0)) {
         optarg = NULL;         /* no argument follows the option */
         pIndexPosition = NULL; /* not in the middle of anything */
         return EOF;            /* encountered the special flag */
      }

      pArgString++;             /* look past the / or - */
   }

   if (':' == *pArgString) {    /* is it a colon? */
      /*---------------------------------------------------------------------
       * Rare case: if opterr is non-zero, return a question mark;
       * otherwise, just return the colon we're on.
       *-------------------------------------------------------------------*/
      return (opterr ? (int) '?' : (int) ':');
   } else if ((pOptString = strchr(opstring, *pArgString)) == 0) {
      /*---------------------------------------------------------------------
       * The letter on the command-line wasn't any good.
       *-------------------------------------------------------------------*/
      optarg = NULL;            /* no argument follows the option */
      pIndexPosition = NULL;    /* not in the middle of anything */
      return (opterr ? (int) '?' : (int) *pArgString);
   } else {
      /*---------------------------------------------------------------------
       * The letter on the command-line matches one we expect to see
       *-------------------------------------------------------------------*/
      if (':' == _next_char(pOptString)) {      /* is the next letter a colon? */
         /* It is a colon.  Look for an argument string. */
         if ('\0' != _next_char(pArgString)) {  /* argument in this argv? */
            optarg = &pArgString[1];    /* Yes, it is */
         } else {
        /*-------------------------------------------------------------
         * The argument string must be in the next argv.
         * But, what if there is none (bad input from the user)?
         * In that case, return the letter, and optarg as NULL.
         *-----------------------------------------------------------*/
            if (optind < argc)
               optarg = argv[optind++];
            else {
               optarg = NULL;
               return (opterr ? (int) '?' : (int) *pArgString);
            }
         }
         pIndexPosition = NULL; /* not in the middle of anything */
      } else {
         /* it's not a colon, so just return the letter */
         optarg = NULL;         /* no argument follows the option */
         pIndexPosition = pArgString;   /* point to the letter we're on */
      }
      return (int) *pArgString; /* return the letter that matched */
   }
}
