477 lines
12 KiB
C++
477 lines
12 KiB
C++
// ==========================================================================
|
|
// Class Implementation : COXCommandOptions
|
|
// ==========================================================================
|
|
|
|
// Source file : OXCmdOpt.cpp
|
|
|
|
// This software along with its related components, documentation and files ("The Libraries")
|
|
// is © 1994-2007 The Code Project (1612916 Ontario Limited) and use of The Libraries is
|
|
// governed by a software license agreement ("Agreement"). Copies of the Agreement are
|
|
// available at The Code Project (www.codeproject.com), as part of the package you downloaded
|
|
// to obtain this file, or directly from our office. For a copy of the license governing
|
|
// this software, you may contact us at legalaffairs@codeproject.com, or by calling 416-849-8900.
|
|
|
|
// //////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "OXCmdOpt.h"
|
|
#include "OXCmdlex.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char BASED_CODE THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
IMPLEMENT_DYNAMIC(COXCommandOptions, CObject)
|
|
|
|
#define new DEBUG_NEW
|
|
|
|
COXCommandOptions::COXCommandOptions()
|
|
{
|
|
}
|
|
|
|
COXCommandOptions::~COXCommandOptions()
|
|
{
|
|
for (int i = m_CmdOptDescList.GetSize() - 1; i >= 0 ; i--)
|
|
{
|
|
delete m_CmdOptDescList[i];
|
|
}
|
|
|
|
ResetMaps();
|
|
}
|
|
|
|
void COXCommandOptions::ResetMaps()
|
|
{
|
|
POSITION pos = m_Values.GetStartPosition();
|
|
CString sKey;
|
|
CObject* pVal;
|
|
while (pos != NULL)
|
|
{
|
|
m_Values.GetNextAssoc(pos, sKey, pVal);
|
|
delete pVal;
|
|
}
|
|
|
|
m_Values.RemoveAll();
|
|
m_AbbrMap.RemoveAll();
|
|
}
|
|
|
|
void COXCommandOptions::Add(COXCommandOptionDesc* pOptionDesc)
|
|
{
|
|
BOOL bInserted = FALSE;
|
|
COXCommandOptionDesc* pNewOptDesc = new COXCommandOptionDesc(*pOptionDesc);
|
|
|
|
for (int i = 0; i < m_CmdOptDescList.GetSize(); i++)
|
|
{
|
|
CString sArrayElement = ((COXCommandOptionDesc*)(m_CmdOptDescList[i]))->GetOptName();
|
|
CString sNewElement = pNewOptDesc->GetOptName();
|
|
// Insert with longest GetOptName's first
|
|
// assume this makes a difference to the order of the AbbrMap ???
|
|
if (sNewElement.GetLength() >= sArrayElement.GetLength())
|
|
{
|
|
bInserted = TRUE;
|
|
m_CmdOptDescList.InsertAt(i, pNewOptDesc, 1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bInserted)
|
|
m_CmdOptDescList.Add(pNewOptDesc);
|
|
}
|
|
|
|
void COXCommandOptions::Add(LPCTSTR pszcOptionName, int iFlags,
|
|
LPCTSTR pszcDescription, LPCTSTR pszcAbbr /* = _T("") */)
|
|
{
|
|
COXCommandOptionDesc Cod(pszcOptionName, iFlags, pszcDescription, pszcAbbr);
|
|
Add(&Cod);
|
|
}
|
|
|
|
BOOL COXCommandOptions::Remove(LPCTSTR pszcOption)
|
|
{
|
|
CString sOption = pszcOption;
|
|
sOption.MakeUpper();
|
|
CObject* pTmp = NULL;
|
|
|
|
// what kind of option is it?
|
|
if (m_Values.Lookup(sOption, pTmp))
|
|
{
|
|
// delete the list of values associated with this option
|
|
delete (CMapStringToString*)pTmp;
|
|
}
|
|
|
|
// Remove the entry from the values list
|
|
m_Values.RemoveKey(sOption);
|
|
|
|
// Remove the entry from the abbreviations list
|
|
m_AbbrMap.RemoveKey(sOption);
|
|
|
|
BOOL bFound(FALSE);
|
|
for (int i = 0; i < m_CmdOptDescList.GetSize(); i++)
|
|
{
|
|
COXCommandOptionDesc* pOpt = (COXCommandOptionDesc*)m_CmdOptDescList[i];
|
|
if (pOpt->GetOptName() == sOption)
|
|
{
|
|
delete pOpt;
|
|
m_CmdOptDescList.RemoveAt(i);
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bFound;
|
|
}
|
|
|
|
// walk through CmdOptDescList, gathering .GetOptName() abbreviations
|
|
void COXCommandOptions::BuildAbbrTable()
|
|
{
|
|
CStringArray Abbray; // Abbreviation Array :-)
|
|
CUIntArray Indaray; // Index Array
|
|
CStringArray Clash; // Clash array
|
|
|
|
// Initialize Abbray with complete option names (and Indaray with pointers)
|
|
// This array is size sorted because CmdOptDescList is size sorted
|
|
for (int i = 0; i < m_CmdOptDescList.GetSize(); i++)
|
|
{
|
|
Abbray.Add(((COXCommandOptionDesc*)(m_CmdOptDescList[i]))->GetOptName());
|
|
Indaray.Add(i);
|
|
CString sReqAbbr = ((COXCommandOptionDesc*)(m_CmdOptDescList[i]))->GetOptAbbr();
|
|
if (sReqAbbr.GetLength())
|
|
{
|
|
Abbray.Add(sReqAbbr);
|
|
Indaray.Add(i);
|
|
}
|
|
}
|
|
|
|
// Now walk through AbbrMap extending it as we go with abbreviations
|
|
int iAbbr;
|
|
for (iAbbr = 0;iAbbr < Abbray.GetSize(); iAbbr++)
|
|
{
|
|
CString sAbbr = Abbray[iAbbr];
|
|
// Abbr is the name already in the map
|
|
int iNewAbbrLength = sAbbr.GetLength() - 1;
|
|
if (iNewAbbrLength)
|
|
{ // No zero length abbreviations
|
|
// NewAbbr is the name we are trying to add to map
|
|
CString sNewAbbr = sAbbr.GetBufferSetLength(iNewAbbrLength);
|
|
|
|
// See if sNewAbbr is unique (ignoring all the entries we've already processed)
|
|
BOOL bUnique = TRUE;
|
|
|
|
int iCmp;
|
|
for (iCmp = iAbbr+ 1; iCmp < Abbray.GetSize(); iCmp++)
|
|
{
|
|
// Is sNewAbbr an abbreviation of sCmp?
|
|
// Assumes that Left() returns all characters (without
|
|
// complaint) in the case that Left() is passed a number
|
|
// larger than the string length.
|
|
CString sCmp = Abbray.GetAt(iCmp).Left(sNewAbbr.GetLength());
|
|
|
|
if (sCmp == sNewAbbr)
|
|
{
|
|
// sNewAbbr is not unique ... abandon it
|
|
bUnique = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
// Did we previously find a clash that's a substring of sNewAbbr
|
|
if (bUnique)
|
|
{
|
|
for (int iClash = 0; iClash < Clash.GetSize(); iClash++)
|
|
{
|
|
CString sCmp = Clash.GetAt(iClash);
|
|
if (sCmp == sNewAbbr)
|
|
{
|
|
bUnique = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bUnique)
|
|
{
|
|
Abbray.Add(sNewAbbr);
|
|
Indaray.Add(Indaray[iAbbr]);
|
|
}
|
|
else
|
|
{
|
|
Clash.Add(sNewAbbr);
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(Abbray.GetSize() == Indaray.GetSize());
|
|
|
|
// Hmmm, this will freak if user calls this twice ... handle that case -tab
|
|
ASSERT(m_AbbrMap.GetCount() == 0);
|
|
|
|
// Now Build AbbrMap from Abbray and Indaray
|
|
for (int j = 0; j < Abbray.GetSize(); j++)
|
|
{
|
|
m_AbbrMap.SetAt(Abbray[j], m_CmdOptDescList[Indaray[j]]);
|
|
}
|
|
}
|
|
|
|
// Construct array of actual parameters based on command line
|
|
CString COXCommandOptions::ParseCmdLine(COXCommandLine& cl, CStringList* pRestList /* = NULL */)
|
|
{
|
|
CString sReturn;
|
|
|
|
// Clear all previous maps and rebuild afterwards
|
|
ResetMaps();
|
|
BuildAbbrTable();
|
|
|
|
int iNumArgs = cl.GetNumArgs();
|
|
for (int iArg = 0; iArg < iNumArgs; iArg++)
|
|
{
|
|
if ((cl[iArg][0] == _T('-')) || (cl[iArg][0] == _T('/')))
|
|
{
|
|
CString sToken = cl[iArg].Mid(1);
|
|
sToken.MakeUpper();
|
|
COXCommandOptionDesc* Opt = (COXCommandOptionDesc*)m_AbbrMap[sToken];
|
|
|
|
if (Opt != NULL)
|
|
{ // Valid switch or abbreviation
|
|
CObject* pTmp = NULL;
|
|
BOOL bIsAlreadyThere = m_Values.Lookup(Opt->GetOptName(), pTmp);
|
|
if (bIsAlreadyThere && (!Opt->IsRepeatable()))
|
|
{
|
|
TCHAR msg[80];
|
|
_stprintf(msg, _T("%s can only be specified once\r\n"), sToken);
|
|
TRACE(msg);
|
|
THROW(new COXCommandLineException(msg, this));
|
|
}
|
|
else if (bIsAlreadyThere && Opt->ReturnsFirstRep())
|
|
{
|
|
// It's already there ... do nothing
|
|
}
|
|
else
|
|
{
|
|
// Either it's not already there or it is already
|
|
// there and repetitions are allowed
|
|
|
|
// Assign new value to insert to sValue
|
|
if (!Opt->TakesArg())
|
|
{
|
|
m_Values[Opt->GetOptName()] = new CStringList;
|
|
}
|
|
else if (Opt->ReturnsAssociative())
|
|
{
|
|
if (iArg + 2 > iNumArgs - 1)
|
|
{
|
|
TCHAR msg[80];
|
|
_stprintf(msg, _T("%s requires 2 arguments\r\n"), Opt->GetOptName());
|
|
TRACE(msg);
|
|
THROW(new COXCommandLineException(msg, this));
|
|
}
|
|
|
|
CMapStringToString* assocmap;
|
|
if (!m_Values.Lookup(Opt->GetOptName(), pTmp))
|
|
{
|
|
assocmap = new CMapStringToString;
|
|
m_Values[Opt->GetOptName()] = assocmap;
|
|
}
|
|
else
|
|
{
|
|
assocmap = (CMapStringToString*)pTmp;
|
|
}
|
|
|
|
CString sKey = cl[++iArg];
|
|
(*assocmap)[sKey] = cl[++iArg];
|
|
}
|
|
else if (Opt->ReturnsArray())
|
|
{
|
|
if (iArg + 1 > iNumArgs - 1)
|
|
{
|
|
TCHAR msg[80];
|
|
_stprintf(msg, _T("%s requires an argument\r\n"), Opt->GetOptName());
|
|
TRACE(msg);
|
|
THROW(new COXCommandLineException(msg, this));
|
|
}
|
|
|
|
CStringList* pVals;
|
|
if (!m_Values.Lookup(Opt->GetOptName(), pTmp))
|
|
{
|
|
pVals = new CStringList;
|
|
m_Values[Opt->GetOptName()] = pVals;
|
|
}
|
|
else
|
|
{
|
|
pVals = (CStringList*)pTmp;
|
|
}
|
|
|
|
pVals->AddTail(cl[++iArg]);
|
|
}
|
|
else
|
|
{
|
|
// It's not already there and we expect an argument
|
|
|
|
if ((cl[iArg + 1] == _T("|")) || (cl[iArg + 1][0] == _T('-')) || (cl[iArg + 1][0] == _T('/')))
|
|
{
|
|
TCHAR msg[80];
|
|
_stprintf(msg, _T("%s requires an argument\r\n"), Opt->GetOptName());
|
|
TRACE(msg);
|
|
THROW(new COXCommandLineException(msg, this));
|
|
}
|
|
|
|
CObject* pTmp;
|
|
if (m_Values.Lookup(Opt->GetOptName(), pTmp))
|
|
{
|
|
delete pTmp;
|
|
}
|
|
|
|
CStringList* pVals = new CStringList;
|
|
pVals->AddTail(cl[++iArg]);
|
|
m_Values[Opt->GetOptName()] = pVals;
|
|
}
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
TCHAR msg[80];
|
|
_stprintf(msg, _T("%s is not a valid switch\r\n"), sToken);
|
|
TRACE(msg);
|
|
THROW(new COXCommandLineException(msg, this));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (pRestList != NULL)
|
|
{
|
|
ASSERT(pRestList->IsKindOf(RUNTIME_CLASS(CStringList)));
|
|
pRestList->AddTail(cl[iArg]);
|
|
}
|
|
|
|
sReturn += cl[iArg] + _T(" ");
|
|
}
|
|
}
|
|
|
|
// Glue the remaining strings back together
|
|
while (iArg < cl.GetNumArgs())
|
|
{
|
|
sReturn += cl[iArg++] + _T(" ");
|
|
}
|
|
|
|
return sReturn;
|
|
}
|
|
|
|
const COXCommandOptionDesc* COXCommandOptions::GetOptionObject(LPCTSTR psczOption) const
|
|
{
|
|
CString sOption(psczOption);
|
|
sOption.MakeUpper();
|
|
BOOL bFound(FALSE);
|
|
COXCommandOptionDesc* pOpt = NULL;
|
|
for (int i = 0; i < m_CmdOptDescList.GetSize(); i++)
|
|
{
|
|
pOpt = (COXCommandOptionDesc*)m_CmdOptDescList[i];
|
|
if (pOpt->GetOptName() == sOption)
|
|
{
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bFound ? pOpt : NULL;
|
|
}
|
|
|
|
BOOL COXCommandOptions::IsEnabled(LPCTSTR pszcOption) const
|
|
{
|
|
CString sOption = pszcOption;
|
|
sOption.MakeUpper();
|
|
CObject* pTmp;
|
|
return (m_Values.Lookup(sOption, pTmp));
|
|
}
|
|
|
|
CString COXCommandOptions::GetValue(LPCTSTR pszcOption) const
|
|
{
|
|
CString sOption = pszcOption;
|
|
sOption.MakeUpper();
|
|
COXCommandOptionDesc* pOpt = NULL;
|
|
CObject* pTmp = NULL;
|
|
|
|
m_AbbrMap.Lookup(sOption, pTmp);
|
|
pOpt = (COXCommandOptionDesc*)pTmp;
|
|
ASSERT(pOpt != NULL && !pOpt->ReturnsArray() && !pOpt->ReturnsAssociative());
|
|
CStringList* pList;
|
|
if (m_Values.Lookup(sOption, pTmp))
|
|
{
|
|
pList = (CStringList*)pTmp;
|
|
|
|
return pList->GetHead();
|
|
}
|
|
else
|
|
{
|
|
return _T("");
|
|
}
|
|
}
|
|
|
|
const CStringList* COXCommandOptions::GetValues(LPCTSTR pszcOption) const
|
|
{
|
|
CString sOption = pszcOption;
|
|
sOption.MakeUpper();
|
|
COXCommandOptionDesc* pOpt = NULL;
|
|
CObject* pTmp = NULL;
|
|
|
|
m_AbbrMap.Lookup(sOption, pTmp);
|
|
pOpt = (COXCommandOptionDesc*)pTmp;
|
|
ASSERT(pOpt != NULL && pOpt->ReturnsArray());
|
|
if (m_Values.Lookup(sOption, pTmp))
|
|
{
|
|
return (CStringList*)pTmp;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
const CMapStringToString* COXCommandOptions::GetAssocPairs(LPCTSTR pszcOption) const
|
|
{
|
|
CString sOption = pszcOption;
|
|
sOption.MakeUpper();
|
|
COXCommandOptionDesc* pOpt = NULL;
|
|
CObject* pTmp = NULL;
|
|
|
|
m_AbbrMap.Lookup(sOption, pTmp);
|
|
pOpt = (COXCommandOptionDesc*)pTmp;
|
|
ASSERT(pOpt != NULL && pOpt->ReturnsAssociative());
|
|
if (m_Values.Lookup(sOption, pTmp))
|
|
{
|
|
return (CMapStringToString*)pTmp;
|
|
}
|
|
else
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
CString COXCommandOptions::Usage(LPCTSTR pszcMsg) const
|
|
{
|
|
CString sMsg(pszcMsg);
|
|
|
|
sMsg += _T("\n");
|
|
|
|
CString sLine;
|
|
for (int i = 0; i < m_CmdOptDescList.GetSize(); i++)
|
|
{
|
|
COXCommandOptionDesc* pOpt = (COXCommandOptionDesc*)m_CmdOptDescList[i];
|
|
CString sAbbr = pOpt->GetOptAbbr();
|
|
if (sAbbr.GetLength() != 0)
|
|
{
|
|
CString sTmp(_T("(-"));
|
|
sTmp += sAbbr;
|
|
sTmp += _T(")");
|
|
sAbbr = sTmp;
|
|
}
|
|
|
|
_stprintf(sLine.GetBuffer(100), _T("-%s\t%s\t%s\n"), pOpt->GetOptName(), sAbbr, pOpt->GetDescription());
|
|
sLine.ReleaseBuffer();
|
|
sMsg += sLine;
|
|
}
|
|
|
|
return sMsg;
|
|
}
|
|
|
|
|
|
|
|
|