2025-11-27 16:46:48 +09:00

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;
}