|
|
/*
**++ ** ** Copyright (c) 2000-2002 Microsoft Corporation ** ** ** Module Name: ** ** cmdparse.h ** ** ** Abstract: ** ** Command line parser ** ** Author: ** ** Adi Oltean [aoltean] 02/26/2002 ** ** Revision History: ** **-- */
#ifndef __CMD_PARSE_HEADER_H__
#define __CMD_PARSE_HEADER_H__
#if _MSC_VER > 1000
#pragma once
#endif
//////////////////////////////////////////////////////////////////
//
// Generic string class
//
class CGxnString { private: CGxnString(const CGxnString&); public:
CGxnString(): m_pwszString(NULL), m_pwszCurrent(NULL) {}; CGxnString(int nAllocatedChars): m_pwszString(NULL), m_pwszCurrent(NULL) { Allocate(nAllocatedChars); }; CGxnString( const WCHAR* wszString, int nAllocatedChars = -1 ): m_pwszString(NULL), m_pwszCurrent(NULL) { CopyFrom(wszString, nAllocatedChars); }; ~CGxnString(){ Clear(); }; operator WCHAR* () { return m_pwszCurrent; };
void operator ++ (int) { m_pwszCurrent++; }; void operator += (int nChars) { m_pwszCurrent += nChars; }; void CopyFrom(const WCHAR * wszString, int nAllocatedChars = -1) { int nLen = (nAllocatedChars == -1)? (int)wcslen(wszString): nAllocatedChars; Allocate(nLen); ::wcsncpy(m_pwszString, wszString, nLen); };
void Allocate(int nAllocatedChars) { delete[] m_pwszString; m_pwszString = new WCHAR[nAllocatedChars + 1]; if (NULL == m_pwszString) throw(E_OUTOFMEMORY); m_pwszString[nAllocatedChars] = L'\0'; m_pwszCurrent = m_pwszString; };
void Clear() { delete[] m_pwszString; m_pwszString = m_pwszCurrent = NULL; };
private: WCHAR * m_pwszString; WCHAR * m_pwszCurrent; };
//////////////////////////////////////////////////////////////////
//
// Generic tracing mechanism (can be replaced with a better one)
//
// Useful macros for tracing
#define GXN_DBGINFO __LINE__, __FILE__
// Tracing buffer - max value
#define MAX_TRACING_BUFFER 400
// Macro used for commoditized vsprintf
#define GXN_VARARG( LastParam ) \
CGxnString buffer(MAX_TRACING_BUFFER); \ va_list marker; \ va_start( marker, LastParam ); \ StringCchVPrintfW( buffer, MAX_TRACING_BUFFER, LastParam, marker ); \ va_end( marker );
// The tracing class (a very simple implementation)
struct CGxnTracer { enum{ TraceFlag = 1, OutputFlag = 2, ErrorFlag = 4, AllFlags = 7, };
CGxnTracer(int nLine = 0, char* szFile = NULL, WCHAR* wszFunction = NULL): m_nTraceFlags(OutputFlag | ErrorFlag), m_szFile(szFile), m_nLine(nLine), m_wszFunction(wszFunction) {};
CGxnTracer(int nTraceFlags, int nLine = 0, char* szFile = NULL, WCHAR* wszFunction = NULL): m_nTraceFlags(nTraceFlags), m_szFile(szFile), m_nLine(nLine), m_wszFunction(wszFunction) { if (m_wszFunction) Trace(m_nLine, m_szFile, L"* Enter %s\n", m_wszFunction); };
~CGxnTracer() { if (m_wszFunction) Trace(m_nLine, m_szFile, L"* Exit %s\n", m_wszFunction); };
int SetOptions( int nTraceFlags ) { int nPrevTraceFlags = m_nTraceFlags; m_nTraceFlags = nTraceFlags; return nPrevTraceFlags; }
void Out( WCHAR* pwszMsgFormat, ... ) { if (m_nTraceFlags & OutputFlag) { GXN_VARARG( pwszMsgFormat ); wprintf( L"%s", (LPWSTR)buffer); } }
void Trace( int nLine, char* szFile, const WCHAR* pwszMsgFormat, ... ) { if (m_nTraceFlags & TraceFlag) { GXN_VARARG( pwszMsgFormat ); wprintf( L"%s - %hs(%d)\n", (LPWSTR)buffer, szFile, nLine); } }
void Err( WCHAR* pwszMsgFormat, ... ) { if (m_nTraceFlags & ErrorFlag) { GXN_VARARG( pwszMsgFormat ); wprintf( L"%s", (LPWSTR)buffer); } }
__declspec(noreturn) void Throw( INT nLine, char* szFile, HRESULT hr, const WCHAR* pwszMsgFormat, ... ) { if (m_nTraceFlags & ErrorFlag) { GXN_VARARG( pwszMsgFormat ); wprintf( L"%s [ERROR: 0x%08lx] - %hs(%d)\n", (LPWSTR)buffer, hr, szFile, nLine); } throw (hr); }
private: int m_nTraceFlags; char* m_szFile; int m_nLine; WCHAR* m_wszFunction; };
//////////////////////////////////////////////////////////////////////////
//
// Command line parser
//
#define BEGIN_CMD_PARSER( AppName ) \
virtual CHAR* GetAppName() { return #AppName; }; \ virtual CmdTemplateEntry & GetCmdTemplate(INT nIndex) { \ static CmdTemplateEntry arrEntries[] = { \
#define CMD_ENTRY(pRoutine, wszTemplate, wszComment) \
{ pRoutine, wszTemplate, wszComment}, \
#define END_CMD_PARSER \
{ NULL, NULL, NULL }, \ }; \ return arrEntries[nIndex]; \ }
// Command line parser class
template < class CRoutineSupport, class CTracer = CGxnTracer, int MAX_PARAMS = 40 > class CGxnCmdLineParser { // Types
public: typedef void (CRoutineSupport::*PRoutineNonaligned)(); typedef __declspec(align(16)) PRoutineNonaligned PRoutine; typedef struct { PRoutine pRoutine; LPWSTR wszCmdLine; LPWSTR wszComment; } CmdTemplateEntry;
// Constructors/destructors
private: CGxnCmdLineParser(const CGxnCmdLineParser&);
public: CGxnCmdLineParser(): m_nParamCount(0), m_nSelectedTemplate(0) {};
public: virtual CHAR* GetAppName() = 0; virtual CmdTemplateEntry & GetCmdTemplate(INT nIndex) = 0;
// Operations
public:
bool ParseCmdLine(WCHAR* pwszCommandLine) { // CTracer ft( GXN_DBGINFO, L"CGxnCmdLineParser::ParseCmdLine");
for (INT nIndex = 0;; nIndex++) { CmdTemplateEntry & entry = GetCmdTemplate(nIndex); // If this is the last entry print usage
if (entry.pRoutine == NULL) return PrintUsage();
// Clean parameter associations from previous iteration (if any)
CleanParams();
CGxnString strCommandLine(pwszCommandLine); CGxnString strCommandTemplate(entry.wszCmdLine); while (true) { // Skip spaces
for(;iswspace(*strCommandLine);strCommandLine++); for(;iswspace(*strCommandTemplate);strCommandTemplate++);
// Extract a name/value pair if possible
CGxnString name, value; if (ExtractVariable(strCommandTemplate, name)) { // No match, try with the next template
if (!ExtractValue(strCommandLine, value)) break; AddParam(name, value); continue; } // No match, try with the next template
if (*strCommandTemplate != *strCommandLine) break;
// Eliminate the current matching tokens
while(*strCommandTemplate == *strCommandLine) { // If we reach an end, we just finished
if ((*strCommandTemplate == L'\0') && (*strCommandLine == L'\0')) { m_nSelectedTemplate = nIndex; return true; } strCommandTemplate++; strCommandLine++; } } } return false; }
LPWSTR GetStringParam(const WCHAR* wszName) { if ((wszName[0] != L'<') && (wszName[wcslen(wszName)-1] != L'>') ) ft.Throw( GXN_DBGINFO, E_UNEXPECTED, L"Invalid name %s\n", wszName);
// Extract the '<' and '>' suffixes and search into the array
CGxnString name(wszName + 1, (int)wcslen(wszName) - 2); for (INT nIndex = 0; nIndex < m_nParamCount; nIndex++) if (wcscmp(name, m_arrNames[nIndex]) == 0) return m_arrValues[nIndex]; ft.Throw( GXN_DBGINFO, E_UNEXPECTED, L"Invalid string param %s\n", wszName); }
// Get a integer value
INT GetIntParam(const WCHAR* wszName) { return _wtoi(GetStringParam( wszName )); }
// Get a int64 value
LONGLONG GetInt64Param(const WCHAR* wszName) { return _wtoi64(GetStringParam( wszName )); }
// Get a int64 value
LARGE_INTEGER GetLargeIntParam(const WCHAR* wszName) { LARGE_INTEGER li; li.QuadPart = _wtoi64(GetStringParam( wszName )); return li; }
// Get a GUID value
GUID GetGuidParam(const WCHAR* wszName) { GUID guid; LPWSTR wszString = GetValue(wszName); if (FAILED(CLSIDFromString(wszString, &guidValue))) ft.Throw( GXN_DBGINFO, E_INVALIDARG, L"Invalid GUID %s for param %s\n", wszString, wszName); return guid; }
bool IsOptionPresent(const WCHAR* /*wszName*/) { return false; }
PRoutine GetCurrentRoutine() { return GetCmdTemplate(m_nSelectedTemplate).pRoutine; };
LPWSTR GetCurrentComment() { return GetCmdTemplate(m_nSelectedTemplate).wszComment; };
LPWSTR GetCurrentTemplate() { return GetCmdTemplate(m_nSelectedTemplate).wszCmdLine; };
void PrintArguments() { ft.Out(L"\n\nMatching parameters for template '%s':\n", GetCurrentTemplate()); for(INT nIndex = 0; nIndex < m_nParamCount; nIndex++) ft.Out( L"* <%s> = '%s'\n", (LPWSTR)m_arrNames[nIndex], (LPWSTR)m_arrValues[nIndex] ); if (m_nParamCount == 0) ft.Out( L"* (None)\n"); ft.Out(L"\n"); }
bool PrintUsage() { ft.Out(L"\n\nUsage:\n"); for (INT nIndex = 0;; nIndex++) { CmdTemplateEntry & entry = GetCmdTemplate(nIndex); if (entry.pRoutine == NULL) break; ft.Out(L" * %s:\t%hs %s\n", entry.wszComment, GetAppName(), entry.wszCmdLine); } ft.Out(L"\n"); return false; }
// Utility methods
private:
// Extract a variable of the "<name>" format
bool ExtractVariable(CGxnString & str, CGxnString & name) { if ( *str != L'<') return false; str++; WCHAR* wszEnd = wcschr(str, L'>'); if (!wszEnd || (str == wszEnd)) ft.Throw( GXN_DBGINFO, E_INVALIDARG, L"Invalid variable name %s\n", (LPWSTR)str); name.CopyFrom( str, wszEnd - str ); str += (wszEnd - str) + 1; // Skip the L'>' character also
return true; }
// Extract a value from the current string until we reach a space.
bool ExtractValue(CGxnString & str, CGxnString & value) { LPWSTR wszEnd = str; // Get the first space or zero terminator
for(; (*wszEnd) && !iswspace(*wszEnd); wszEnd++); if (str == wszEnd) return false;
value.CopyFrom( str, wszEnd - str ); str += (wszEnd - str); return true; }
void CleanParams() { for (INT nIndex = 0; nIndex < m_nParamCount; nIndex++) { m_arrNames[nIndex].Clear(); m_arrValues[nIndex].Clear(); } m_nParamCount = 0; }
void AddParam(CGxnString & name, CGxnString & value) { if (m_nParamCount == MAX_PARAMS) ft.Throw( GXN_DBGINFO, E_INVALIDARG, L"Too many parameters [%d]\n", m_nParamCount);
m_arrNames[m_nParamCount].CopyFrom(name); m_arrValues[m_nParamCount].CopyFrom(value); m_nParamCount++; }
// Internal data members
private: INT m_nParamCount; CGxnString m_arrNames[MAX_PARAMS]; CGxnString m_arrValues[MAX_PARAMS]; INT m_nSelectedTemplate;
protected: CTracer ft; };
#endif // __CMD_PARSE_HEADER_H__
|