450 lines
16 KiB
C++
450 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-2018 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;
|
|
}
|