00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #define QT_NO_CAST_ASCII
00021 #define QT_NO_ASCII_CAST
00022
00023 #include "optionparser.h"
00024
00025 #include <QCoreApplication>
00026 #include <QFileInfo>
00027 #include <QStack>
00028 #include <cstdlib>
00029 #include <cassert>
00030
00031 namespace farsa {
00032
00033 OptionParser::OptionParser() {
00034 QCoreApplication* qApp1 = QCoreApplication::instance();
00035 if ( !qApp1 ) {
00036 qFatal( "OptionParser: requires a QCoreApplication instance to be constructed first" );
00037 }
00038 init( qApp1->argc(), qApp1->argv(), 1 );
00039 }
00040
00041 OptionParser::OptionParser( int offset ) {
00042 QCoreApplication* qApp1 = QCoreApplication::instance();
00043 if ( !qApp1 ) {
00044 qFatal( "OptionParser: requires a QApplication instance to be constructed first" );
00045 }
00046 init( qApp1->argc(), qApp1->argv(), offset );
00047 }
00048
00049 OptionParser::OptionParser( int argc, char *argv[] ) {
00050 init( argc, argv );
00051 }
00052
00053 OptionParser::OptionParser( const QStringList &a )
00054 : args( a ) {
00055 init( 0, 0 );
00056 }
00057
00058 void OptionParser::init( int argc, char *argv[], int offset ) {
00059 numReqArgs = numOptArgs = 0;
00060 currArg = 1;
00061 if ( argc ) {
00062
00063 aname = QFileInfo( QString::fromUtf8( argv[0] ) ).fileName();
00064
00065 for ( int i = offset; i < argc; ++i ) {
00066 args.append( QString::fromUtf8( argv[i] ) );
00067 }
00068 }
00069 }
00070
00071
00072 bool OptionParser::parse( bool untilFirstSwitchOnly ) {
00073
00074
00075
00076
00077 QStack<QString> stack;
00078 {
00079 QStringListIterator it(args);
00080 it.toBack();
00081 while( it.hasPrevious() ) {
00082 stack.push( it.previous() );
00083 }
00084 }
00085
00086 const OptionConstIterator obegin = options.begin();
00087 const OptionConstIterator oend = options.end();
00088 enum { StartState, ExpectingState, OptionalState } state = StartState;
00089 Option currOpt;
00090 enum TokenType { LongOpt, ShortOpt, Arg, End } t, currType = End;
00091 bool extraLoop = true;
00092 while ( !stack.isEmpty() || extraLoop ) {
00093 QString a;
00094 QString origA;
00095
00096 if ( !stack.isEmpty() ) {
00097 a = stack.pop();
00098 currArg++;
00099 origA = a;
00100
00101 if ( a.startsWith( QString::fromLatin1( "--" ) ) ) {
00102
00103 a = a.mid( 2 );
00104 if ( a.isEmpty() ) {
00105 qWarning( "'--' feature not supported, yet" );
00106 exit( 2 );
00107 }
00108 t = LongOpt;
00109
00110 int equal = a.indexOf( '=' );
00111 if ( equal >= 0 ) {
00112 stack.push( a.mid( equal + 1 ) );
00113 currArg--;
00114 a = a.left( equal );
00115 }
00116 } else if ( a.length() == 1 ) {
00117 t = Arg;
00118 } else if ( a[0] == '-' ) {
00119 #if 0 // compat mode for -long style options
00120 if ( a.length() == 2 ) {
00121 t = ShortOpt;
00122 a = a[1];
00123 } else {
00124 a = a.mid( 1 );
00125 t = LongOpt;
00126
00127 int equal = a.find( '=' );
00128 if ( equal >= 0 ) {
00129 stack.push( a.mid( equal + 1 ) );
00130 currArg--;
00131 a = a.left( equal );
00132 }
00133 }
00134 #else
00135
00136 t = ShortOpt;
00137
00138 if ( a.length() > 2 ) {
00139 stack.push( a.mid( 2 ) );
00140 currArg--;
00141 }
00142 a = a[1];
00143 #endif
00144 } else {
00145 t = Arg;
00146 }
00147 } else {
00148
00149 t = End;
00150 }
00151
00152 Option opt;
00153 if ( t != End ) {
00154 OptionConstIterator oit = obegin;
00155 while ( oit != oend ) {
00156 const Option &o = *oit;
00157 if ( ( t == LongOpt && a == o.lname ) ||
00158 ( t == ShortOpt && a[0].unicode() == o.sname ) ) {
00159 opt = o;
00160 break;
00161 }
00162 ++oit;
00163 }
00164 if ( t == LongOpt && opt.type == OUnknown ) {
00165 if ( currOpt.type != OVarLen ) {
00166 qWarning( "Unknown option --%s", a.toAscii().data() );
00167 return false;
00168 } else {
00169
00170 t = Arg;
00171 }
00172 } else if ( t == ShortOpt && opt.type == OUnknown ) {
00173 if ( currOpt.type != OVarLen ) {
00174 qWarning( "Unknown option -%c", a[0].unicode() );
00175 return false;
00176 } else {
00177
00178 t = Arg;
00179 }
00180 }
00181 } else {
00182 opt = Option( OEnd );
00183 }
00184
00185
00186 switch ( state ) {
00187 case StartState:
00188 if ( opt.type == OSwitch ) {
00189 setSwitch( opt );
00190 setOptions.insert( opt.lname, 1 );
00191 setOptions.insert( QString( QChar( opt.sname ) ), 1 );
00192 } else if ( opt.type == OArg1 || opt.type == ORepeat ) {
00193 state = ExpectingState;
00194 currOpt = opt;
00195 currType = t;
00196 setOptions.insert( opt.lname, 1 );
00197 setOptions.insert( QString( QChar( opt.sname ) ), 1 );
00198 } else if ( opt.type == OOpt || opt.type == OVarLen ) {
00199 state = OptionalState;
00200 currOpt = opt;
00201 currType = t;
00202 setOptions.insert( opt.lname, 1 );
00203 setOptions.insert( QString( QChar( opt.sname ) ), 1 );
00204 } else if ( opt.type == OEnd ) {
00205
00206 } else if ( opt.type == OUnknown && t == Arg ) {
00207 if ( numReqArgs > 0 ) {
00208 if ( reqArg.stringValue->isNull() ) {
00209 *reqArg.stringValue = a;
00210 } else {
00211 qWarning( "Too many arguments" );
00212 return false;
00213 }
00214 } else if ( numOptArgs > 0 ) {
00215 if ( optArg.stringValue->isNull() ) {
00216 *optArg.stringValue = a;
00217 } else {
00218 qWarning( "Too many arguments" );
00219 return false;
00220 }
00221 }
00222 } else {
00223 qFatal( "unhandled StartState case %d", opt.type );
00224 }
00225 break;
00226 case ExpectingState:
00227 if ( t == Arg ) {
00228 if ( currOpt.type == OArg1 ) {
00229 *currOpt.stringValue = a;
00230 state = StartState;
00231 } else if ( currOpt.type == ORepeat ) {
00232 currOpt.listValue->append( a );
00233 state = StartState;
00234 } else {
00235 abort();
00236 }
00237 } else {
00238 QString n = currType == LongOpt ?
00239 currOpt.lname : QString( QChar( currOpt.sname ) );
00240 qWarning( "Expected an argument after '%s' option", n.toAscii().data() );
00241 return false;
00242 }
00243 break;
00244 case OptionalState:
00245 if ( t == Arg ) {
00246 if ( currOpt.type == OOpt ) {
00247 *currOpt.stringValue = a;
00248 state = StartState;
00249 } else if ( currOpt.type == OVarLen ) {
00250 currOpt.listValue->append( origA );
00251
00252 } else {
00253 abort();
00254 }
00255 } else {
00256
00257 if ( currOpt.type == OOpt )
00258 *currOpt.stringValue = currOpt.def;
00259 if ( t != End ) {
00260
00261 stack.push( origA );
00262 currArg--;
00263 }
00264 state = StartState;
00265 }
00266 break;
00267 }
00268
00269 if ( untilFirstSwitchOnly && opt.type == OSwitch )
00270 return true;
00271
00272
00273 if ( t == End )
00274 extraLoop = false;
00275 }
00276
00277 if ( numReqArgs > 0 && reqArg.stringValue->isNull() ) {
00278 qWarning( "Lacking required argument" );
00279 return false;
00280 }
00281
00282 return true;
00283 }
00284
00285 void OptionParser::addOption( Option o ) {
00286
00287 options.append( o );
00288 }
00289
00290 void OptionParser::addSwitch( const QString &lname, bool *b ) {
00291 Option opt( OSwitch, 0, lname );
00292 opt.boolValue = b;
00293 addOption( opt );
00294
00295 *b = false;
00296 }
00297
00298 void OptionParser::setSwitch( const Option &o ) {
00299 assert( o.type == OSwitch );
00300 *o.boolValue = true;
00301 }
00302
00303 void OptionParser::addOption( char s, const QString &l, QString *v ) {
00304 Option opt( OArg1, s, l );
00305 opt.stringValue = v;
00306 addOption( opt );
00307 *v = QString::null;
00308 }
00309
00310 void OptionParser::addVarLengthOption( const QString &l, QStringList *v ) {
00311 Option opt( OVarLen, 0, l );
00312 opt.listValue = v;
00313 addOption( opt );
00314 *v = QStringList();
00315 }
00316
00317 void OptionParser::addRepeatableOption( char s, QStringList *v ) {
00318 Option opt( ORepeat, s, QString::null );
00319 opt.listValue = v;
00320 addOption( opt );
00321 *v = QStringList();
00322 }
00323
00324 void OptionParser::addRepeatableOption( const QString &l, QStringList *v ) {
00325 Option opt( ORepeat, 0, l );
00326 opt.listValue = v;
00327 addOption( opt );
00328 *v = QStringList();
00329 }
00330
00331 void OptionParser::addOptionalOption( const QString &l, QString *v, const QString &def ) {
00332 addOptionalOption( 0, l, v, def );
00333 }
00334
00335 void OptionParser::addOptionalOption( char s, const QString &l, QString *v, const QString &def ) {
00336 Option opt( OOpt, s, l );
00337 opt.stringValue = v;
00338 opt.def = def;
00339 addOption( opt );
00340 *v = QString::null;
00341 }
00342
00343 void OptionParser::addArgument( const QString &name, QString *v ) {
00344 Option opt( OUnknown, 0, name );
00345 opt.stringValue = v;
00346 reqArg = opt;
00347 ++numReqArgs;
00348 *v = QString::null;
00349 }
00350
00351 void OptionParser::addOptionalArgument( const QString &name, QString *v ) {
00352 Option opt( OUnknown, 0, name );
00353 opt.stringValue = v;
00354 optArg = opt;
00355 ++numOptArgs;
00356 *v = QString::null;
00357 }
00358
00359
00360 bool OptionParser::isSet( const QString &name ) const {
00361 return setOptions.find( name ) != setOptions.end();
00362 }
00363
00364 }
00365