/* **++ ** ** 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)? 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 ); \ _vsnwprintf( buffer, MAX_TRACING_BUFFER - 1, 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, 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 "" 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; // Check if we have quota-enclosed parameters if (*str == L'"') { wszEnd = wcschr(str + 1, L'"'); if (wszEnd == NULL) ft.Throw( GXN_DBGINFO, E_INVALIDARG, L"Quota not enclosed at %s\n", (LPWSTR)str); value.CopyFrom( str + 1, wszEnd - str - 1 ); str += (wszEnd-str) + 1; return true; } else { // 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__