// -*- C++ -*-

/* Simple argument-parsing class
 * Copyright (C) 2001 Patrick E. Pelletier <ppelleti@users.sourceforge.net>
 *
 * 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 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef	WIN32
#pragma warning( disable : 4786 )
#endif	// WIN32

#include <string>
#include <vector>

class ArgParseInternalData;	// hide implementation details

/*
 * The ArgParse class allows you to specify names of options
 * that you want parsed, along with a usage message for them.
 * Options come in four flavors: flag, int, float, and string.
 * Flags don't take arguments, but the other kinds do.  For
 * an option that takes an argument, it can be specified with
 * an equals sign, with a colon, or by putting it in the next
 * element of argv.  ("--foo=stuff", "--foo:stuff", or
 * "--foo stuff", respectively)
 *
 * The flavors that take arguments also come in array flavors.
 * With an array, you specify a pointer to a vector of the basic
 * type, instead of just a pointer to a basic type.  This allows
 * the option to appear more than once, and the new values are
 * appended to the array.  Optionally, you can also specify a
 * separator character, so that multiple array elements can be
 * parsed up from a single instance of the option.
 *
 * Options can start with either a single dash or a double dash,
 * but see allowOneCharOptionsToBeCombined() for more information.
 *
 * The argument "--" all by itself signals the end of the options,
 * and everything after it will go into the leftovers (see leftovers())
 * even if it starts with a dash.
 *
 * The usage text for an option gets appended immediately after the
 * name of the option and its aliases.  The ASCII BEL character (\a)
 * can be used as a special kind of tab, which moves the cursor to
 * the current indent level (see usageHeader()), to make it easy to
 * make things line up in columns.
 */

/*
 * Rationale for some design decisions:
 *
 * Things which need to be modified are passed as pointers, not as
 * references, because some people feel that non-const references make
 * what's going on less clear, and I kind of agree with them.
 *
 * Arrays are explicitly passed as std::vector (or whatever you change
 * the typedefs below to) instead of passing around iterators and
 * allowing any STL container to be used, just because unnecessary
 * templatization can be less clear and sometimes leads to portability
 * and debugability issues, and doesn't seem justified for this simple
 * application.
 */

class ArgParse {
public:
    // Basic types: ArgParse always uses these typedefs, so that there's
    // just one place to change things if you want to use float instead
    // of double, or CqString instead of std::string, etc.
    typedef bool apflag;
    typedef int apint;
    typedef double apfloat;
    typedef std::string apstring;

    // Array types: ditto
    typedef std::vector<apint> apintvec;
    typedef std::vector<apfloat> apfloatvec;
    typedef std::vector<apstring> apstringvec;

    // These two constants are valid values for the "separator"
    // argument to argInts, argFloats, and argStrings.  (Any ASCII
    // character is also a valid argument.)  SEP_NONE means that the
    // only way to get more than one thing in the array is to specify
    // the option more than once.  (e. g. "--foo one --foo two")
    // SEP_ARGV means that each element of argv, up to the next option,
    // will be put into the array.  (e. g. "--foo one two")  Using an
    // ASCII character means that only one argv element is used, but
    // it can become multiple elements in the array by being split
    // on the given character.  (e. g. "--foo one,two")
    enum {
        SEP_NONE = -1,
        SEP_ARGV = -2
    };

    ArgParse();
    ~ArgParse();

    // Calling this method means that "-bar" will be treated as if
    // it was "-b -a -r", and to specify a multi-character option,
    // you need to use a double dash.  (e. g. "--bar")  If you don't
    // call this method, then single dash and double dash are treated
    // the same.
    void allowOneCharOptionsToBeCombined(); // long but descriptive :)

    // Normally, parse() will return an error if there are any
    // unrecognized options.  But if you call this method before
    // calling parse(), then unrecognized options will go into
    // the leftovers, without causing an error.
    void allowUnrecognizedOptions();

    // "--foo" will set *value to true.  If allow_negation is true,
    // then "--nofoo" will set *value to false.
    void argFlag(apstring name, apstring usage,
                 apflag* value, bool allow_negation = true);

    void argInt(apstring name, apstring usage, apint* value);
    void argInts(apstring name, apstring usage,
                 apintvec* values, int separator = SEP_NONE, int count = -1);

    void argFloat(apstring name, apstring usage, apfloat* value);
    void argFloats(apstring name, apstring usage,
                   apfloatvec* values, int separator = SEP_NONE, int count = -1);

    void argString(apstring name, apstring usage, apstring* value);
    void argStrings(apstring name, apstring usage,
                    apstringvec* values, int separator = SEP_NONE, int count = -1);

    // Makes "aliasname" work just like "realname".  Note that
    // "realname" can be a negated flag name (if the flag allows
    // negation), so you can make "--fooless" mean "--nofoo", for
    // example.
    void alias(apstring realname, apstring aliasname);

    // This inserts literal text into the usage message.  The order
    // is significant with respect to calls to argFlag, argInt, etc.
    // The most common use of this would be to add the
    // "Usage: blech [options] files" at the top, but it can also be
    // used to make different "sections" of options (give the --help
    // option to GNU tar to see what I mean) or to make a footer at the
    // end of the usage.  Your string can contain newlines, but it
    // shouldn't end in a newline unless you want an extra blank line.
    // The "indent" value specifies how to line up
    // columns of individual usage messages in the follow section.
    // (Again, refer to the GNU tar usage message to see what I mean.)
    void usageHeader(apstring text, int indent = 25);

    // This makes the parsing actually happen.  Returns true on
    // success, or false on failure.
    bool parse(int argc, const char** argv);

    // If parse() returns false, this method will give you an
    // error message that describes what's wrong.  Note that
    // although it's legal to call parse() more than once on the
    // same ArgParse object, only the most recent error message is
    // retained.
    apstring errmsg() const;

    // Returns a usage string made up of the usage messages of
    // the individual arguments, with things from usageHeader()
    // interspersed at the appropriate places.  The string returned
    // contains embedded newlines, and also a trailing newline.
    apstring usagemsg() const;

    // Returns any leftover arguments.  (arguments which didn't start
    // with a dash and didn't get eaten by another option, or any
    // arguments which appeared after "--")
    // Again, this is only valid until the next call to parse() on
    // this ArgParse object.
    const apstringvec& leftovers() const;

private:
    ArgParseInternalData* d;
};
