/*
Getopt for GNU.
Copyright (C) 1987, 1989 Free Software Foundation, Inc.

(Modified by Douglas C. Schmidt for use with GNU G++.)
This file is part of the GNU C++ Library.  This library is free
software; you can redistribute it and/or modify it under the terms of
the GNU Library General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.  This library 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 Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free Software
Foundation, 59 Temple Place - Fifth Floor, Boston, MA 02110-1301, USA.
*/

#include "config.h"

/* AIX requires the alloca decl to be the first thing in the file. */
#ifdef __GNUC__
#define alloca __builtin_alloca
#elif defined(sparc)
#include <alloca.h>
#elif defined(_AIX)
#pragma alloca
#elif defined(WIN32)
#include <malloc.h>
#else
char *alloca();
#endif

#include <vector>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <cstring> // Added these. 10/20/98 jhrg

#include "GetOpt.h"

// #include <string.h>

char *GetOpt::nextchar = 0;
int GetOpt::first_nonopt = 0;
int GetOpt::last_nonopt = 0;

GetOpt::GetOpt(int argc, char **argv, const char *optstring)
    : opterr(1), nargc(argc), nargv(argv), noptstring(optstring) {
    /* 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.  */

    first_nonopt = last_nonopt = optind = 1;
    optarg = nextchar = 0;

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

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

void GetOpt::exchange(char **argv) {
    int nonopts_size = (last_nonopt - first_nonopt) * sizeof(char *);
    /* char **temp = (char **) alloca (nonopts_size); */
    /* char **temp = (char **)malloc(nonopts_size); */
    std::vector<char> temp(nonopts_size);

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

    memcpy(temp.data(), &argv[first_nonopt], nonopts_size);

    /* valgrind complains about this because in some cases the memory areas
       overlap. I switched to memmove. See the memcpy & memmove man pages.
       02/12/04 jhrg */
#if 0
  memcpy (&argv[first_nonopt], &argv[last_nonopt],
         (optind - last_nonopt) * sizeof (char *));
#endif
    memmove(&argv[first_nonopt], &argv[last_nonopt], (optind - last_nonopt) * sizeof(char *));

    memcpy(&argv[first_nonopt + optind - last_nonopt], temp.data(), nonopts_size);

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

    first_nonopt += (optind - last_nonopt);
    last_nonopt = optind;

    // free(temp);
}

/* 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 theoption 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.
   A colon in OPTSTRING means that the previous character is an option
   that wants an argument.  The argument is taken from the rest of the
   current ARGV-element, or from the following ARGV-element,
   and returned in `optarg'.

   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'.

   If OPTSTRING starts with `-', it requests a different method of handling the
   non-option ARGV-elements.  See the comments about RETURN_IN_ORDER, above.  */

int GetOpt::operator()(void) {
    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(nargv);
            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 < nargc && (nargv[optind][0] != '-' || nargv[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 != nargc && !strcmp(nargv[optind], "--")) {
            optind++;

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

            optind = nargc;
        }

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

        if (optind == nargc) {
            /* 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 (nargv[optind][0] != '-' || nargv[optind][1] == 0) {
            if (ordering == REQUIRE_ORDER)
                return EOF;
            optarg = nargv[optind++];
            return 0;
        }

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

        nextchar = nargv[optind] + 1;
    }

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

    {
        char c = *nextchar++;
        char *temp = (char *)strchr(noptstring, 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", nargv[0], c);
                else
                    fprintf(stderr, "%s: unrecognized option `-%c'\n", nargv[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 == nargc) {
                    if (opterr != 0)
                        fprintf(stderr, "%s: no argument for `-%c' option\n", nargv[0], c);
                    c = '?';
                } else
                    /* We already incremented `optind' once;
                       increment it again when taking next ARGV-elt as argument.  */
                    optarg = nargv[optind++];
                nextchar = 0;
            }
        }
        return c;
    }
}
