summaryrefslogtreecommitdiff
path: root/quackleio/froggetopt.cpp
diff options
context:
space:
mode:
authorJason Katz-Brown <jason@airbnb.com>2013-08-25 02:17:13 -0700
committerJason Katz-Brown <jason@airbnb.com>2013-08-25 02:17:13 -0700
commit9306cb60c32082c5403931de0823a9fd5daa196c (patch)
treeca1b6eb695fdf3f0c2294e92416b272164bae642 /quackleio/froggetopt.cpp
parent8fb2c681cecc01b46b0f4ba02d5cc177c4747b1c (diff)
Initial git commit.
Diffstat (limited to 'quackleio/froggetopt.cpp')
-rw-r--r--quackleio/froggetopt.cpp660
1 files changed, 660 insertions, 0 deletions
diff --git a/quackleio/froggetopt.cpp b/quackleio/froggetopt.cpp
new file mode 100644
index 0000000..68f20fe
--- /dev/null
+++ b/quackleio/froggetopt.cpp
@@ -0,0 +1,660 @@
+/**********************************************************************
+ * Copyright (c) 2003, 2004, froglogic Porten & Stadlbauer GbR
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * * Neither the name of the froglogic nor the names of its
+ * contributors may be used to endorse or promote products
+ * derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ **********************************************************************/
+
+#define QT_NO_CAST_ASCII
+#define QT_NO_ASCII_CAST
+
+#include <QtCore>
+
+#include <stdlib.h>
+#include <assert.h>
+
+#include "froggetopt.h"
+
+/**
+ \class GetOpt
+
+ \brief A command line option parser.
+
+ This class helps to overcome the repetitive, tedious and
+ error-prone task of parsing the command line options passed to your
+ application by the user. Specify the acceptable syntax with a
+ minimum of statements in a readable way, check it against the
+ actual arguments passed and find the retrieved values in variables
+ of your program. The name \em GetOpt is based on similar utilities
+ build into the Unix shell and other languages.
+
+ A command line that a user might have entered is:
+
+ \code
+ app -v --config=my.cnf -Wall input.dat
+ \endcode
+
+ The typical usage has three stages:
+
+ -# Construct a parser specifying what arguments to parse
+ -# Set up the list of allowed and required options
+ -# Run the parser
+
+ For the first step there are three different constructors that
+ either take arguments directly from \c main(), \c QApplication or a
+ user specified list. Setting up the accepted syntax is done by a
+ set of \c add functions like addSwitch(). The final step of running
+ the parser is simply done by calling parse().
+
+ A short example implementing a \c --verbose switch:
+
+ \code
+ int main(int argc, char **argv)
+ {
+ GetOpt opts(argc, argv);
+ bool verbose;
+ opts.addSwitch("verbose", &verbose);
+ if (!opts.parse())
+ return 1;
+ if (verbose)
+ cout << "VERBOSE mode on" << endl;
+ ...
+ \endcode
+
+ For a better understanding of the function names we'll better
+ define some terms used in the API and its documentation:
+
+ - \em Argument An argument is a plain text token like e.g. a file
+ name one typically passes to an editor when invoking it.
+ - \em Switch A switch is an on/off kind of argument without the need
+ of additional information. Example: \c --debug.
+ - \em Option An option is a normally optional argument with a key-value
+ syntax like \c --output=out.txt or \c -I/usr/include.
+ - \em Short \em Option A short option is a one letter option with a
+ preceding dash. Like \c -v.
+ - \em Long \em Option A long option has a more verbose,
+ multi-letter name like \c --debug.
+ .
+
+ \author froglogic GbR <contact@froglogic.com>
+ */
+
+
+/**
+ Constructs a command line parser from the arguments stored in a
+ previously created QApplication instance.
+
+ Example usage:
+ \code
+ QApplication a(argc, argv);
+
+ GetOpt opt;
+ \endcode
+
+ This constructor is probably the most convenient one to use in a
+ regular Qt application. Note that QApplication may already have
+ removed Qt (or X11) specific arguments. Also see
+ QApplication::argv() and QApplication::argc().
+ */
+GetOpt::GetOpt()
+{
+ if ( !QCoreApplication::instance() )
+ qFatal( "GetOpt: requires a QApplication instance to be constructed first" );
+
+ init( QCoreApplication::instance()->argc(), QCoreApplication::instance()->argv(), 1 );
+}
+
+/**
+ \internal
+ */
+GetOpt::GetOpt( int offset )
+{
+ if ( !QCoreApplication::instance() )
+ qFatal( "GetOpt: requires a QApplication instance to be constructed first" );
+
+ init( QCoreApplication::instance()->argc(), QCoreApplication::instance()->argv(), offset );
+}
+
+/**
+ Construct a command line parser from the array \a argv of string
+ pointers with the size \a argc. Those parameters have the form
+ typically found in the \c main() function. That means that you can
+ simply pass on the arguments specified by the user of your
+ application.
+
+ Example usage:
+
+ \code
+ int main(int argc, char **argv) {
+ GetOpt opt(argc, argv);
+ ...
+ }
+ \endcode
+ */
+GetOpt::GetOpt( int argc, char *argv[] )
+{
+ init( argc, argv );
+}
+
+/**
+ Construct a command line parser from the arguments specified in the
+ list of arguments \a a. This constructor is convenient in those
+ cases where you want to parse a command line assembled on-the-fly
+ instead of relying on the \c argc and \c arg parameters passed to
+ the \c main() function.
+ */
+ GetOpt::GetOpt( const QStringList &a )
+: args( a )
+{
+ init( 0, 0 );
+}
+
+/**
+ \internal
+ */
+void GetOpt::init( int argc, char *argv[], int offset )
+{
+ numReqArgs = numOptArgs = 0;
+ currArg = 1; // appname is not part of the arguments
+ if ( argc ) {
+ // application name
+ aname = QFileInfo( QString::fromUtf8( argv[0] ) ).fileName();
+ // arguments
+ for ( int i = offset; i < argc; ++i )
+ args.append( QString::fromUtf8( argv[i] ) );
+ }
+}
+
+/**
+ \fn bool GetOpt::parse()
+
+ Parse the command line arguments specified in the constructor under
+ the conditions set by the various \c add*() functions. On success,
+ the given variable reference will be initialized with their
+ respective values and true will be returned. Returns false
+ otherwise.
+
+ In the future there'll be a way to retrieve an error message. In
+ the current version the message will be printed to \c stderr.
+ */
+
+/**
+ \internal
+ */
+bool GetOpt::parse( bool untilFirstSwitchOnly )
+{
+ // qDebug( "parse(%s)", args.join( QString( "," ) ).toLocal8Bit().constData() );
+ // push all arguments as we got them on a stack
+ // more pushes might following when parsing condensed arguments
+ // like --key=value.
+ QStack<QString> stack;
+
+ if (!args.empty())
+ {
+ QStringList::const_iterator it = args.end();
+ while ( it != args.begin() ) {
+ --it;
+ stack.push( *it );
+ }
+ }
+
+ const OptionConstIterator obegin = options.begin();
+ const OptionConstIterator oend = options.end();
+ enum { StartState, ExpectingState, OptionalState } state = StartState;
+ Option currOpt;
+ enum TokenType { LongOpt, ShortOpt, Arg, End } t, currType = End;
+ bool extraLoop = true; // we'll do an extra round. fake an End argument
+ while ( !stack.isEmpty() || extraLoop ) {
+ QString a;
+ QString origA;
+ // identify argument type
+ if ( !stack.isEmpty() ) {
+ a = stack.pop();
+ currArg++;
+ origA = a;
+ // qDebug( "popped %s", a.toLocal8Bit().constData() );
+ if ( a.startsWith( QString::fromLatin1( "--" ) ) ) {
+ // recognized long option
+ a = a.mid( 2 );
+ if ( a.isEmpty() ) {
+ qWarning( "'--' feature not supported, yet" );
+ exit( 2 );
+ }
+ t = LongOpt;
+ // split key=value style arguments
+ int equal = a.indexOf( '=' );
+ if ( equal >= 0 ) {
+ stack.push( a.mid( equal + 1 ) );
+ currArg--;
+ a = a.left( equal );
+ }
+ } else if ( a.length() == 1 ) {
+ t = Arg;
+ } else if ( a[0] == '-' ) {
+#if 0 // compat mode for -long style options
+ if ( a.length() == 2 ) {
+ t = ShortOpt;
+ a = a[1];
+ } else {
+ a = a.mid( 1 );
+ t = LongOpt;
+ // split key=value style arguments
+ int equal = a.find( '=' );
+ if ( equal >= 0 ) {
+ stack.push( a.mid( equal + 1 ) );
+ currArg--;
+ a = a.left( equal );
+ }
+ }
+#else
+ // short option
+ t = ShortOpt;
+ // followed by an argument ? push it for later processing.
+ if ( a.length() > 2 ) {
+ stack.push( a.mid( 2 ) );
+ currArg--;
+ }
+ a = a[1];
+#endif
+ } else {
+ t = Arg;
+ }
+ } else {
+ // faked closing argument
+ t = End;
+ }
+ // look up among known list of options
+ Option opt;
+ if ( t != End ) {
+ OptionConstIterator oit = obegin;
+ while ( oit != oend ) {
+ const Option &o = *oit;
+ if ( ( t == LongOpt && a == o.lname ) || // ### check state
+ ( t == ShortOpt && a[0].unicode() == o.sname ) ) {
+ opt = o;
+ break;
+ }
+ ++oit;
+ }
+ if ( t == LongOpt && opt.type == OUnknown ) {
+ if ( currOpt.type != OVarLen ) {
+ qWarning( "Unknown option --%s", a.toLocal8Bit().constData() );
+ return false;
+ } else {
+ // VarLength options support arguments starting with '-'
+ t = Arg;
+ }
+ } else if ( t == ShortOpt && opt.type == OUnknown ) {
+ if ( currOpt.type != OVarLen ) {
+ qWarning( "Unknown option -%c", a[0].unicode() );
+ return false;
+ } else {
+ // VarLength options support arguments starting with '-'
+ t = Arg;
+ }
+ }
+
+ } else {
+ opt = Option( OEnd );
+ }
+
+ // interpret result
+ switch ( state ) {
+ case StartState:
+ if ( opt.type == OSwitch ) {
+ setSwitch( opt );
+ setOptions.insert( opt.lname, 1 );
+ setOptions.insert( QString( QChar( opt.sname ) ), 1 );
+ } else if ( opt.type == OArg1 || opt.type == ORepeat ) {
+ state = ExpectingState;
+ currOpt = opt;
+ currType = t;
+ setOptions.insert( opt.lname, 1 );
+ setOptions.insert( QString( QChar( opt.sname ) ), 1 );
+ } else if ( opt.type == OOpt || opt.type == OVarLen ) {
+ state = OptionalState;
+ currOpt = opt;
+ currType = t;
+ setOptions.insert( opt.lname, 1 );
+ setOptions.insert( QString( QChar( opt.sname ) ), 1 );
+ } else if ( opt.type == OEnd ) {
+ // we're done
+ } else if ( opt.type == OUnknown && t == Arg ) {
+ if ( numReqArgs > 0 ) {
+ if ( reqArg.stringValue->isNull() ) { // ###
+ *reqArg.stringValue = a;
+ } else {
+ qWarning( "Too many arguments" );
+ return false;
+ }
+ } else if ( numOptArgs > 0 ) {
+ if ( optArg.stringValue->isNull() ) { // ###
+ *optArg.stringValue = a;
+ } else {
+ qWarning( "Too many arguments" );
+ return false;
+ }
+ }
+ } else {
+ qFatal( "unhandled StartState case %d", opt.type );
+ }
+ break;
+ case ExpectingState:
+ if ( t == Arg ) {
+ if ( currOpt.type == OArg1 ) {
+ *currOpt.stringValue = a;
+ state = StartState;
+ } else if ( currOpt.type == ORepeat ) {
+ currOpt.listValue->append( a );
+ state = StartState;
+ } else {
+ abort();
+ }
+ } else {
+ QString n = currType == LongOpt ?
+ currOpt.lname : QString( QChar( currOpt.sname ) );
+ qWarning( "Expected an argument after '%s' option", n.toLocal8Bit().constData() );
+ return false;
+ }
+ break;
+ case OptionalState:
+ if ( t == Arg ) {
+ if ( currOpt.type == OOpt ) {
+ *currOpt.stringValue = a;
+ state = StartState;
+ } else if ( currOpt.type == OVarLen ) {
+ currOpt.listValue->append( origA );
+ // remain in this state
+ } else {
+ abort();
+ }
+ } else {
+ // optional argument not specified
+ if ( currOpt.type == OOpt )
+ *currOpt.stringValue = currOpt.def;
+ if ( t != End ) {
+ // re-evaluate current argument
+ stack.push( origA );
+ currArg--;
+ }
+ state = StartState;
+ }
+ break;
+ }
+
+ if ( untilFirstSwitchOnly && opt.type == OSwitch )
+ return true;
+
+ // are we in the extra loop ? if so, flag the final end
+ if ( t == End )
+ extraLoop = false;
+ }
+
+ if ( numReqArgs > 0 && reqArg.stringValue->isNull() ) {
+ qWarning( "Lacking required argument" );
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ \internal
+ */
+void GetOpt::addOption( Option o )
+{
+ // ### check for conflicts
+ options.append( o );
+}
+
+/**
+ Adds a switch with the long name \a lname. If the switch is found
+ during parsing the bool \a *b will bet set to true. Otherwise the
+ bool will be initialized to false.
+
+Example:
+
+\code
+GetOpt opt;
+bool verbose;
+opt.addSwitch("verbose", &verbose);
+\endcode
+
+The boolean flag \c verbose will be set to true if \c --verbose has
+been specified in the command line; false otherwise.
+ */
+void GetOpt::addSwitch( const QString &lname, bool *b )
+{
+ Option opt( OSwitch, 0, lname );
+ opt.boolValue = b;
+ addOption( opt );
+ // ### could do all inits at the beginning of parse()
+ *b = false;
+}
+
+/**
+ \internal
+ */
+void GetOpt::setSwitch( const Option &o )
+{
+ assert( o.type == OSwitch );
+ *o.boolValue = true;
+}
+
+/**
+ Registers an option with the short name \a s and long name \a l to
+ the parser. If this option is found during parsing the value will
+ be stored in the string pointed to by \a v. By default \a *v will
+ be initialized to \c QString::null.
+ */
+void GetOpt::addOption( char s, const QString &l, QString *v )
+{
+ Option opt( OArg1, s, l );
+ opt.stringValue = v;
+ addOption( opt );
+ *v = QString::null;
+}
+
+/**
+ Registers a long option \a l that can have a variable number of
+ corresponding value parameters. As there currently is no way to
+ tell the end of the value list the only sensible use of this option
+ is at the end of the command line.
+
+Example:
+
+\code
+QStringList args;
+opt.addVarLengthOption("exec", &args);
+\endcode
+
+Above code will lead to "-f" and "test.txt" being stored in \a args
+upon
+
+\code
+myapp --exec otherapp -f test.txt
+\endcode
+ */
+void GetOpt::addVarLengthOption( const QString &l, QStringList *v )
+{
+ Option opt( OVarLen, 0, l );
+ opt.listValue = v;
+ addOption( opt );
+ *v = QStringList();
+}
+
+/**
+ Registers an option with the short name \a s that can be specified
+ repeatedly in the command line. The option values will be stored in
+ the list pointed to by \a v. If no \a s option is found \a *v will
+ remain at its default value of an empty QStringList instance.
+
+Example:
+
+To parse the \c -I options in a command line like
+\code
+myapp -I/usr/include -I/usr/local/include
+\endcode
+
+you can use code like this:
+
+\code
+GetOpt opt;
+QStringList includes;
+opt.addRepeatableOption('I', &includes);
+opt.parse();
+\endcode
+ */
+void GetOpt::addRepeatableOption( char s, QStringList *v )
+{
+ Option opt( ORepeat, s, QString::null );
+ opt.listValue = v;
+ addOption( opt );
+ *v = QStringList();
+}
+
+/**
+ Registers an option with the long name \a l that can be specified
+ repeatedly in the command line.
+
+ \sa addRepeatableOption( char, QStringList* )
+ */
+void GetOpt::addRepeatableOption( const QString &l, QStringList *v )
+{
+ Option opt( ORepeat, 0, l );
+ opt.listValue = v;
+ addOption( opt );
+ *v = QStringList();
+}
+
+/**
+ Adds a long option \a l that has an optional value parameter. If
+ the value is not specified by the user it will be set to \a def.
+
+Example:
+
+\code
+GetOpt opt;
+QString file;
+opt.addOptionalOption("dump", &file, "<stdout>");
+\endcode
+
+\sa addOption
+ */
+void GetOpt::addOptionalOption( const QString &l, QString *v,
+ const QString &def )
+{
+ addOptionalOption( 0, l, v, def );
+}
+
+/**
+ Adds a short option \a s that has an optional value parameter. If
+ the value is not specified by the user it will be set to \a def.
+ */
+void GetOpt::addOptionalOption( char s, const QString &l,
+ QString *v, const QString &def )
+{
+ Option opt( OOpt, s, l );
+ opt.stringValue = v;
+ opt.def = def;
+ addOption( opt );
+ *v = QString::null;
+}
+
+/**
+ Registers a required command line argument \a name. If the argument
+ is missing parse() will return false to indicate an error and \a *v
+ will remain with its default QString::null value. Otherwise \a *v
+ will be set to the value of the argument.
+
+Example:
+
+To accept simple arguments like
+
+\code
+myeditor letter.txt
+\endcode
+
+use a call like:
+
+\code
+QString &file;
+opt.addArgument("file", &file);
+\endcode
+
+Note: the \a name parameter has a rather descriptive meaning for
+now. It might be used for generating a usage or error message in
+the future. Right now, the only current use is in relation with the
+isSet() function.
+ */
+void GetOpt::addArgument( const QString &name, QString *v )
+{
+ Option opt( OUnknown, 0, name );
+ opt.stringValue = v;
+ reqArg = opt;
+ ++numReqArgs;
+ *v = QString::null;
+}
+
+/**
+ Registers an optional command line argument \a name. For a more
+ detailed description see the addArgument() documentation.
+
+ */
+void GetOpt::addOptionalArgument( const QString &name, QString *v )
+{
+ Option opt( OUnknown, 0, name );
+ opt.stringValue = v;
+ optArg = opt;
+ ++numOptArgs;
+ *v = QString::null;
+}
+
+/**
+ Returns true if the (long) option or switch \a name has been found
+ in the command line; returns false otherwise. Leading hyphens are
+ not part of the name.
+
+ As the set/not set decision can also be made depending on the value
+ of the variable reference used in the respective \c add*() call
+ there's generally little use for this function.
+ */
+
+bool GetOpt::isSet( const QString &name ) const
+{
+ return setOptions.find( name ) != setOptions.end();
+}
+
+/**
+ \fn int GetOpt::currentArgument() const
+ \internal
+ */