473 lines
16 KiB
C++
473 lines
16 KiB
C++
//
|
|
// The developer of the original code and/or files is Tripwire, Inc.
|
|
// Portions created by Tripwire, Inc. are copyright (C) 2000-2017 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<int>::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<std::pair<int,int> >::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<int, int> 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<cArgData>::const_iterator i;
|
|
for(i = mArgData.begin(); i != mArgData.end(); i++)
|
|
{
|
|
d.Trace(dl, "* Item id:%d\n", i->mId);
|
|
for(std::vector<TSTRING>::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<TSTRING, cArgInfo> 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<TSTRING, cArgInfo> 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<cArgData>::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;
|
|
|
|
}
|
|
|