|
I have been using this class with modifications in my project, so thought I'd contribute something back.
Added support for parameters with two dashes (like --foobar) or actually any number of delimiter combinations (-/-foobar):
LPCTSTR pszArg = _tcspbrk(pszCurrent, m_pszDelimeters);
if (! pszArg)
{
break;
}
LPCTSTR pszInArg;
do
{
pszArg = _tcsinc(pszArg);
pszInArg = _tcspbrk(pszArg, m_pszDelimeters);
} while (pszInArg == pszArg);
...
I also derived from CMapStringToString as many have suggested already and removed all the find and iterator functions to use those of the base class. Made code a lot simpler.
In addition, for those that use CppUnit for unit testing, here's a unit test:
typedef struct
{
LPCTSTR pszName;
LPCTSTR pszValue;
} CCmdLineTestDataPair;
typedef struct
{
LPCTSTR pszCmdLine;
BOOL fCaseSensitive;
ULONG ulParameters;
CCmdLineTestDataPair testDataPairs[3];
} CCmdLineTestData;
void CCmdLineUnitTest::testParseCmdLine(void)
{
CCmdLineTestData testdata[] =
{
{ _T(""), FALSE, 0, { { NULL, NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("--test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/-/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("----test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/p1 /p2"), TRUE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/p1=p1value /p2=p2value /p3=p3value"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } },
{ _T("/p1=\"p1value\" /p2=\'p2value\' /p3:`p3value`"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } }
};
for (int i = 0; i < sizeof(testdata) / sizeof(CCmdLineTestData); i++)
{
CCmdLine c(testdata[i].pszCmdLine, testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid command line parsed.", c.GetCmdLine().Compare(testdata[i].pszCmdLine) == 0);
CPPUNIT_ASSERT_MESSAGE("Invalid case sensitive parameter.", c.GetCaseSensitive() == testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid parameter count.", c.GetCount() == testdata[i].ulParameters);
for (int j = 0; j < sizeof(testdata[i].testDataPairs) / sizeof(CCmdLineTestDataPair); j++)
{
if (testdata[i].testDataPairs[j].pszName == NULL && testdata[i].testDataPairs[j].pszValue == NULL)
break;
CString strValue;
CPPUNIT_ASSERT_MESSAGE("Expected parameter not found.", c.Lookup(testdata[i].testDataPairs[j].pszName, strValue));
CPPUNIT_ASSERT_MESSAGE("Parameter value does not match.",
((strValue.GetLength() == 0) && (testdata[i].testDataPairs[j].pszValue == NULL))
|| strValue.Compare(testdata[i].testDataPairs[j].pszValue) == 0);
}
}
}
cheers
dB.
|
|
|
|
|
Someone asked me for full code.
CmdLine.h:
#pragma once
class CCmdLine
: public CMapStringToString
{
DECLARE_DYNAMIC(CCmdLine)
public:
CCmdLine(LPCTSTR sCmdLine = NULL, BOOL bCaseSensitive = FALSE);
virtual ~CCmdLine(void);
void Parse(LPCTSTR pszCmdLine);
inline const CString& GetCmdLine(void) const { return m_strCmdLine; }
inline void SetCaseSensitive(BOOL bSensitive) { m_bCaseSensitive = bSensitive; }
inline BOOL GetCaseSensitive(void) const { return m_bCaseSensitive; }
inline BOOL HasKey(LPCTSTR pszKey) const { CString strValue; return CMapStringToString::Lookup(pszKey, strValue); }
private:
CString m_strCmdLine;
BOOL m_bCaseSensitive;
static const TCHAR m_pszDelimeters[];
static const TCHAR m_pszValueSep[];
static const TCHAR m_pszQuotes[];
};
#ifdef CPPUNIT_API
class CCmdLineUnitTest : public CppUnit::TestCase
{
public:
void testParseCmdLine(void);
public:
CPPUNIT_TEST_SUITE(CCmdLineUnitTest);
CPPUNIT_TEST(testParseCmdLine);
CPPUNIT_TEST_SUITE_END();
};
CPPUNIT_TEST_SUITE_REGISTRATION(CCmdLineUnitTest);
#endif
CmdLine.cpp:
#include "stdafx.h"
IMPLEMENT_DYNAMIC(CCmdLine, CMapStringToString)
const TCHAR CCmdLine::m_pszDelimeters[] = _T("-/");
const TCHAR CCmdLine::m_pszQuotes[] = _T("\"\'`");
const TCHAR CCmdLine::m_pszValueSep[] = _T(" :=");
CCmdLine::CCmdLine(LPCTSTR pszCmdLine, BOOL bCaseSensitive)
: m_bCaseSensitive(bCaseSensitive)
{
if (pszCmdLine != NULL)
{
Parse(pszCmdLine);
}
}
CCmdLine::~CCmdLine()
{
}
void CCmdLine::Parse(LPCTSTR pszCmdLine)
{
if (! pszCmdLine)
return;
m_strCmdLine = pszCmdLine;
RemoveAll();
int nArgs = 0;
LPCTSTR pszCurrent = pszCmdLine;
while(TRUE)
{
if (_tcslen(pszCurrent) == 0)
{
break;
}
LPCTSTR pszArg = _tcspbrk(pszCurrent, m_pszDelimeters);
if (! pszArg)
{
break;
}
LPCTSTR pszInArg;
do
{
pszArg = _tcsinc(pszArg);
pszInArg = _tcspbrk(pszArg, m_pszDelimeters);
} while (pszInArg == pszArg);
if (_tcslen(pszArg) == 0)
{
break;
}
LPCTSTR pszVal = _tcspbrk(pszArg, m_pszValueSep);
if (pszVal == NULL)
{
CString strKey(pszArg);
if(! m_bCaseSensitive)
{
strKey.MakeLower();
}
SetAt(strKey, _T(""));
break;
}
else if (pszVal[0] == _T(' ') || _tcslen(pszVal) == 1 )
{
CString strKey(pszArg, (int) (pszVal - pszArg));
if(! strKey.IsEmpty())
{
if(!m_bCaseSensitive)
{
strKey.MakeLower();
}
SetAt(strKey, _T(""));
}
pszCurrent = _tcsinc(pszVal);
continue;
}
else
{
CString strKey(pszArg, (int) (pszVal - pszArg));
if (! m_bCaseSensitive)
{
strKey.MakeLower();
}
pszVal = _tcsinc(pszVal);
LPCTSTR pszQuote = _tcspbrk(pszVal, m_pszQuotes), pszEndQuote(NULL);
if (pszQuote == pszVal)
{
pszQuote = _tcsinc(pszVal);
pszEndQuote = _tcspbrk(pszQuote, m_pszQuotes);
}
else
{
pszQuote = pszVal;
pszEndQuote = _tcschr(pszQuote, _T(' '));
}
if (pszEndQuote == NULL)
{
CString strValue(pszQuote);
if(! strKey.IsEmpty())
{
SetAt(strKey, strValue);
}
break;
}
else
{
if(! strKey.IsEmpty())
{
CString strValue(pszQuote, (int) (pszEndQuote - pszQuote));
SetAt(strKey, strValue);
}
pszCurrent = _tcsinc(pszEndQuote);
continue;
}
}
}
}
#ifdef CPPUNIT_API
typedef struct
{
LPCTSTR pszName;
LPCTSTR pszValue;
} CCmdLineTestDataPair;
typedef struct
{
LPCTSTR pszCmdLine;
BOOL fCaseSensitive;
ULONG ulParameters;
CCmdLineTestDataPair testDataPairs[3];
} CCmdLineTestData;
void CCmdLineUnitTest::testParseCmdLine(void)
{
CCmdLineTestData testdata[] =
{
{ _T(""), FALSE, 0, { { NULL, NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("--test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/-/test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("----test"), FALSE, 1, { { _T("test"), NULL }, { NULL, NULL }, { NULL, NULL } } },
{ _T("/p1 /p2"), TRUE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/P1 /p2 /p2"), FALSE, 2, { { _T("p1"), NULL }, { _T("p2"), NULL }, { NULL, NULL } } },
{ _T("/p1=p1value /p2=p2value /p3=p3value"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } },
{ _T("/p1=\"p1value\" /p2=\'p2value\' /p3:`p3value`"), FALSE, 3, { { _T("p1"), _T("p1value") }, { _T("p2"), _T("p2value") }, { _T("p3"), _T("p3value") } } }
};
for (int i = 0; i < sizeof(testdata) / sizeof(CCmdLineTestData); i++)
{
CCmdLine c(testdata[i].pszCmdLine, testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid command line parsed.", c.GetCmdLine().Compare(testdata[i].pszCmdLine) == 0);
CPPUNIT_ASSERT_MESSAGE("Invalid case sensitive parameter.", c.GetCaseSensitive() == testdata[i].fCaseSensitive);
CPPUNIT_ASSERT_MESSAGE("Invalid parameter count.", c.GetCount() == testdata[i].ulParameters);
for (int j = 0; j < sizeof(testdata[i].testDataPairs) / sizeof(CCmdLineTestDataPair); j++)
{
if (testdata[i].testDataPairs[j].pszName == NULL && testdata[i].testDataPairs[j].pszValue == NULL)
break;
CString strValue;
CPPUNIT_ASSERT_MESSAGE("Expected parameter not found.", c.Lookup(testdata[i].testDataPairs[j].pszName, strValue));
CPPUNIT_ASSERT_MESSAGE("Parameter value does not match.",
((strValue.GetLength() == 0) && (testdata[i].testDataPairs[j].pszValue == NULL))
|| strValue.Compare(testdata[i].testDataPairs[j].pszValue) == 0);
}
}
}
#endif
|
|
|
|
|