// // The developer of the original code and/or files is Tripwire, Inc. // Portions created by Tripwire, Inc. are copyright (C) 2000 Tripwire, // Inc. Tripwire is a registered trademark of Tripwire, Inc. All rights // reserved. // // This program is free software. The contents of this file are subject // to 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. You may redistribute it and/or modify it // only in compliance with the GNU General Public License. // // This program is distributed in the hope that it will be useful. // However, this program is distributed AS-IS WITHOUT ANY // WARRANTY; INCLUDING THE IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS // FOR A PARTICULAR PURPOSE. Please 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. // // Nothing in the GNU General Public License or any other license to use // the code or files shall permit you to use Tripwire's trademarks, // service marks, or other intellectual property without Tripwire's // prior written consent. // // If you have any questions, please contact Tripwire, Inc. at either // info@tripwire.org or www.tripwire.org. // // cmdlineparser.cpp #include "stdcore.h" #include "cmdlineparser.h" #include "corestrings.h" /////////////////////////////////////////////////////////////////////////////// // ctor, dotr /////////////////////////////////////////////////////////////////////////////// cCmdLineParser::cCmdLineParser() : mArgTable(HASH_VERY_SMALL), mLastArgInfo(-1, PARAM_NONE) { } cCmdLineParser::~cCmdLineParser() { } /////////////////////////////////////////////////////////////////////////////// // AddArg /////////////////////////////////////////////////////////////////////////////// void cCmdLineParser::AddArg(int argId, const TSTRING& arg, const TSTRING& alias, ParamCount numParams, bool multipleAllowed) { if(arg.empty() && alias.empty()) { // this refers to the list of parameters that comes after all the cmd line switches mLastArgInfo.mId = argId; mLastArgInfo.mNumParams = numParams; return ; } if(! arg.empty()) mArgTable.Insert(arg, cArgInfo(argId, numParams)); if(! alias.empty()) { // put the alias in the table with a '-' prepended to it so it matches '--' TSTRING str(_T("-")); str += alias; mArgTable.Insert(str, cArgInfo(argId, numParams)); } // This argument can appear more than once on the command line. if( multipleAllowed ) mMultipleAllowed.insert( argId ); } /////////////////////////////////////////////////////////////////////////////// // Clear /////////////////////////////////////////////////////////////////////////////// void cCmdLineParser::Clear() { mLastArgInfo.mId = -1; mLastArgInfo.mNumParams = PARAM_INVALID; mArgTable.Clear(); mArgData.clear(); mMutExList.clear(); } /////////////////////////////////////////////////////////////////////////////// // Parse /////////////////////////////////////////////////////////////////////////////// void cCmdLineParser::Parse(int argc, const TCHAR *const * argv) { // clear out any existing data mArgData.clear(); const TCHAR* pCurArg = 0; bool bProcessedFinalParams = false; // gets set to true when the parameters to the command line are processed // I assume argv[0] is the executable name... for(int i=1; i < argc; i++) { if(argv[i][0] == _T('-')) { pCurArg = argv[i]; // this is a switch; find it in the table... cArgInfo argInfo; if ( !mArgTable.Lookup( TSTRING(&argv[i][1] ), argInfo ) ) { // unknown switch! throw eCmdLineInvalidArg( TSS_GetString( cCore, core::STR_ERR2_BAD_ARG_PARAMS ) + pCurArg ); } // // make sure this hasn't been specified yet... // if( ArgInList( argInfo.mId ) ) { // Make sure it isn't okay for this one to appear more than once... std::set::iterator it = mMultipleAllowed.find( argInfo.mId ); if( it == mMultipleAllowed.end() ) { // It wasn't in our list of allowed params, so error. throw eCmdLineMultiArg( TSS_GetString( cCore, core::STR_ERR2_BAD_ARG_PARAMS ) + argv[i] ); } } // // add it to the list.. // mArgData.push_back(cArgData(argInfo.mId, TSTRING(argv[i]))); cArgData& curArg = mArgData.back(); switch( argInfo.mNumParams ) { case PARAM_NONE: // make sure there are no parameters to this, but be careful because // it is legal to start the parameters to the executable here. if((i+1 < argc) && (argv[i+1][0] != _T('-'))) { // search for any more parameters // TODO: In the future we may want to support a '--' switch that specifies the start // of parameters to the executable. for (int j = i + 2; j < argc; ++j ) { if (argv[j][0] == _T('-')) { // >0 parameter passed ! throw eCmdLineBadParam( TSS_GetString( cCore, core::STR_ERR2_BAD_ARG_PARAMS ) + pCurArg ); } } } break; case PARAM_ONE: // get the next parameter... i++; if ( (i >= argc) || (argv[i][0] == _T('-')) ) { // zero parameters passed to something that needed one param throw eCmdLineBadParam( TSS_GetString( cCore, core::STR_ERR2_BAD_ARG_PARAMS ) + pCurArg ); } curArg.mParams.push_back( TSTRING(argv[i]) ); break; case PARAM_MANY: i++; while((i < argc) && (argv[i][0] != _T('-'))) { curArg.mParams.push_back(TSTRING(argv[i])); i++; } i--; // since we have gone too far at this point break; default: ASSERTMSG( false, "Unknown number of arguments to parser" ); } } else { bProcessedFinalParams = true; // this must be the final "unnamed" arg // first, make sure it is consistent with the current info... bool bResult = true; switch(mLastArgInfo.mNumParams) { case PARAM_NONE: // this is an error; they didn't want any command line parameters... bResult = false; break; case PARAM_ONE: if(i+1 != argc) // there is >1 final parameter; it is an error bResult = false; break; case PARAM_MANY: // we'll catch errors below break; default: ASSERT(false); } if(! bResult) { throw eCmdLineBadParam( ); } // ok, we can push the final parameter info onto the list... mArgData.push_back(cArgData(mLastArgInfo.mId)); cArgData& curArg = mArgData.back(); while ( i < argc ) { if ( argv[i][0] == _T('-') ) { if ( ! pCurArg ) { throw eCmdLineBadSwitchPos( TSS_GetString( cCore, core::STR_ERR2_BAD_ARG_PARAMS ) + argv[i] ); } else { // there was an extra parameter passed somewhere! throw eCmdLineBadArgParam( TSS_GetString( cCore, core::STR_ERR2_BAD_ARG_PARAMS ) + pCurArg ); } } // add this param to the list curArg.mParams.push_back(TSTRING(argv[i])); i++; } } } // it is possible not to process the final command line parameters in the "else" case above // (this only occurs if there are no command line parameters specified) so let's make sure that // is consistent with what we are configured with... // NOTE -- it is ok to have no cmd line parameters if they specified PARAM_NONE or PARAM_MANY if(! bProcessedFinalParams) { if(mLastArgInfo.mNumParams == PARAM_ONE) { throw eCmdLineBadParam( ); } } // Check for "relationship errors": TestMutEx(); TestDependency(); } /////////////////////////////////////////////////////////////////////////////// // TestMutEx /////////////////////////////////////////////////////////////////////////////// void cCmdLineParser::TestMutEx() { std::list >::const_iterator i; cCmdLineIter iter1(*this), iter2(*this); for(i = mMutExList.begin(); i != mMutExList.end(); i++) { //TODO -- there is a much more efficent way to do this (using cFCOPropVector, for example) // the command line is presumably small enough, tho, that it probably isn't a big // deal to do it this way. iter1.SeekToArg(i->first); if(! iter1.Done()) { iter2.SeekToArg(i->second); if(! iter2.Done()) { // we have a mutual exclusion violation! throw eCmdLineMutEx( iter1.ActualParam() + _T(", ") + iter2.ActualParam() ); } } } } /////////////////////////////////////////////////////////////////////////////// // TestDependency /////////////////////////////////////////////////////////////////////////////// void cCmdLineParser::TestDependency() { std::list< std::pair< std::pair< int, int>, bool > >::const_iterator i; cCmdLineIter iter1(*this), iter2(*this); for( i = mDependencyList.begin(); i != mDependencyList.end(); ++i) { iter1.SeekToArg( i->first.first ); // was it on the command line? if( !iter1.Done() ) { // it was, is the corresponding arg on the command line? iter2.SeekToArg( i->first.second ); if( iter2.Done() ) // it wasn't, dependency error { TSTRING arg1, arg2, alias1, alias2; cCmdLineParser::LookupArgInfo( i->first.first, arg1, alias1 ); cCmdLineParser::LookupArgInfo( i->first.second, arg2, alias2 ); // determine in which form the user passed the arguments, // and construct the error message in the same form if ( iter1.ActualParam().length() == 2 ) throw eCmdLineDependency( _T("The switch -") + arg1 + _T(" requires -") + arg2 +_T(".") ); else throw eCmdLineDependency( _T("The switch --") + alias1 + _T(" requires --") + alias2 + _T(".") ); } } else if( i->second ) // only make this second check if the dependencies are MUTUAL, // as indicated (or not) by the bool value. { iter2.SeekToArg( i->first.second ); // the first arg in the pair was not on the command line, // so just make sure the second isn't there... if( !iter2.Done() ) { // arg2 appeared without arg1, so dependency error. TSTRING arg1, arg2, alias1, alias2; cCmdLineParser::LookupArgInfo( i->first.first, arg1, alias1 ); cCmdLineParser::LookupArgInfo( i->first.second, arg2, alias2 ); // determine in which form the user passed the arguments, // and construct the error message in the same form if ( iter1.ActualParam().length() == 2 ) throw eCmdLineDependency( _T("The switch -") + arg2 + _T(" requires -") + arg1 +_T(".") ); else throw eCmdLineDependency( _T("The switch --") + alias2 + _T(" requires --") + alias1 + _T(".") ); } } } //end for } /////////////////////////////////////////////////////////////////////////////// // AddMutEx /////////////////////////////////////////////////////////////////////////////// void cCmdLineParser::AddMutEx(int argId1, int argId2) { // note that I do no checking for duplicates here... std::pair mutEx(argId1, argId2); ASSERT(argId1 != argId2); mMutExList.push_back(mutEx); } /////////////////////////////////////////////////////////////////////////////// // AddDependency /////////////////////////////////////////////////////////////////////////////// void cCmdLineParser::AddDependency(int argId1, int argId2, bool mutual ) { // again, no checking for duplicates... would a set // prove to be a better container for this operation? std::pair< int, int > Args( argId1, argId2 ); std::pair< std::pair< int, int >, bool > Dep( Args, mutual ); ASSERT(argId1 != argId2); mDependencyList.push_back( Dep); } /////////////////////////////////////////////////////////////////////////////// // TraceContents /////////////////////////////////////////////////////////////////////////////// #ifdef _DEBUG void cCmdLineParser::TraceContents(int dl) { cDebug d("cCmdLineParser::TraceContents"); if(dl == -1) dl = cDebug::D_DEBUG; std::list::const_iterator i; for(i = mArgData.begin(); i != mArgData.end(); i++) { d.Trace(dl, "* Item id:%d\n", i->mId); for(std::vector::const_iterator vi = i->mParams.begin(); vi != i->mParams.end(); vi++) { d.Trace(dl, "\t%s\n", vi->c_str()); } } d.Trace(dl, "--- Switch id table ---\n"); cHashTableIter iter(mArgTable); for(iter.SeekBegin(); ! iter.Done(); iter.Next()) { d.Trace(dl, "[%d] %s\n", iter.Val().mId, iter.Key().c_str()); } d.Trace(dl, "[%d] Final Param List\n", mLastArgInfo.mId); } #endif /////////////////////////////////////////////////////////////////////////////// // LookupArgInfo /////////////////////////////////////////////////////////////////////////////// bool cCmdLineParser::LookupArgInfo(int argId, TSTRING& arg, TSTRING& alias) const { arg = _T(""); alias = _T(""); cHashTableIter iter(mArgTable); for(iter.SeekBegin(); ! iter.Done(); iter.Next()) { if(iter.Val().mId == argId) { TSTRING str = iter.Key(); if((str.length() > 0) && (str[0] == _T('-'))) { // this is the alias! alias = (str.c_str() + 1); } else { // this is the arg... arg = str; } } } return ((! arg.empty()) || (! alias.empty())); } /////////////////////////////////////////////////////////////////////////////// // ArgInList /////////////////////////////////////////////////////////////////////////////// bool cCmdLineParser::ArgInList(int argId) { std::list::iterator i; for( i = mArgData.begin(); i != mArgData.end(); i++ ) { if( i->mId == argId ) return true; } return false; } //############################################################################# // cCmdLineIter //############################################################################# /////////////////////////////////////////////////////////////////////////////// // SeekToArg /////////////////////////////////////////////////////////////////////////////// bool cCmdLineIter::SeekToArg(int argId) const { for(SeekBegin(); ! Done(); Next()) { if(ArgId() == argId) return true; } return false; }