// // 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::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, 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 Args(argId1, argId2); std::pair, 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; }