//
// tardy - a tar post-processor
// Copyright (C) 1991-1993, 1995, 1998, 1999, 2008, 2009, 2011 Peter Miller
//
// 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 3 of the License, 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, see <http://www.gnu.org/licenses/>.
//

#ifndef LIBTARDY_ARGLEX_H
#define LIBTARDY_ARGLEX_H

#include <libtardy/main.h>

/**
  * The arglex class represemts the processing necessary to perform
  * lexical analysis on command line arguments.
  */
class arglex
{
public:
    /**
      * The destructor.
      */
    virtual ~arglex();

    enum
    {
        token_eoln = -20,
        token_help,
        token_number,
        token_option,
        token_stdio,
        token_string,
        token_trace,
        token_version
    };

    typedef int token_t;

    /**
      * The table_t struct is used to hold mapping tables, from strings
      * to token numbers.
      */
    struct table_t
    {
        const char *name;
        int token;
    };

    /**
      * The lex method is used to perfom lexical analysis on the command
      * line arguments.
      *
      * Unrecognised options are returned as arglex_token_option for
      * anything starting with a '-', or arglex_token_string otherwise.
      *
      * @returns
      *      The next token in the token stream.  When the end is
      *      reached, arglex::token_eoln is returned forever.
      */
    token_t lex(void);

    /**
      * The compare method is used to compare a command line
      * string with a formal spec of the option, to see if they compare
      * equal.
      *
      * The actual is case-insensitive.  Uppercase in the formal means
      * a mandatory character, while lower case means optional.  Any
      * number of consecutive optional characters may be supplied by
      * actual, but none may be skipped, unless all are skipped to the
      * next non-lower-case letter.
      *
      * The underscore (_) is like a lower-case minus, it matches "",
      * "-" and "_".
      *
      * The "*" in a pattern matches everything to the end of the
      * argument, anything after the "*" is ignored.  The rest of
      * the argument is pointed to by the "partial" variable as a
      * side-effect (else it will be 0).  This rather ugly feature is to
      * support "-I./dir" type options.
      *
      * A backslash in a pattern nominates an exact match required,
      * case must match excatly here.  This rather ugly feature is to
      * support "-I./dir" type options.
      *
      * For example: "-project" and "-P' both match "-Project", as does
      * "-proJ", but "-prj" does not.
      *
      * For example: "-devDir" and "-d_d' both match
      * "-Development_Directory", but "-dvlpmnt_drctry" does not.
      *
      * For example: to match include path specifications, use a pattern
      * such as "-\\I*", and the partial global variable will have the
      * path in it on return.
      *
      * The gnu --arg=value convention is understood.  In essence, the
      * extra minus character is discarded.
      *
      * @param formal
      *     The pattern to match against.
      * @param actual
      *     The candidate string to be matched.
      * @returns
      *     true if the actual string matchs the formal pattern
      */
    bool compare(const char *formal, const char *actual);

    virtual void usage(void) const = 0;
    virtual void help(void) = 0;
    void version(void);

    token_t get_token(void) const { return token; }
    long get_value_number(void) const { return value.number; }
    const char *get_value_string(void) const { return value.string; }

protected:
    /**
      * The constructor.
      * Only for use by derived classes.
      *
      * @param ac
      *     argument count, from main
      * @param av
      *     argument values, from main
      * @param tp
      *     pointer to table of options
      */
    arglex(int ac, char **av, const table_t *tp);

    void help_f(const char **text, size_t text_size);

private:
    /**
      * The value_t struct is used to hold the value associated with a token.
      */
    struct value_t
    {
        const char *string;
        long number;
        value_t() : string(0), number(0) { }
    };

    token_t token;
    value_t value;
    int argc;
    char **argv;
    const table_t *utable;
    const char *partial;
    const char *pushback;

    /**
      * The is_a_number function is used to determine if the argument is
      * a number.
      *
      * The value is placed in arglex::value.number as a side effect.
      *
      * Negative and positive signs are accepted.  The C conventions for
      * decimal, octal and hexadecimal are understood.
      *
      * There may be no white space anywhere in the string, and the
      * string must end after the last digit.  Trailing garbage will be
      * interpreted to mean it is not a string.
      *
      * @param text
      *     string to be tested and evaluated
      * @returns
      *      bool; false if not a number, true if is a number.
      */
    bool is_a_number(const char *text);

    void generic_argument(void);

    void bad_argument(void);

    void version_main(void);

    void version_help(void);

    /**
      * The default constructor.  Do not use.
      */
    arglex();

    /**
      * The copy constructor.  Do not use.
      *
      * @param rhs
      *     The right hand side of the initialisation.
      */
    arglex(const arglex &rhs);

    /**
      * The assignment operator.  Do not use.
      *
      * @param rhs
      *     The right hand side of the assignment.
      */
    arglex &operator=(const arglex &rhs);
};

#endif // LIBTARDY_ARGLEX_H
