267 lines
6.9 KiB
C++
267 lines
6.9 KiB
C++
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved
|
|
//
|
|
// This code is not part of SDK sample itself.
|
|
//
|
|
// The code contained in CmdLineBase.h/.cpp provides the boiler-plate code to create a simple
|
|
// command-line applications with built-in support for option processing, automatic generation
|
|
// of usage text, and grouping and subclassing of related commands.
|
|
|
|
#include "CmdLineBase.h"
|
|
|
|
|
|
CCmdBase::CCmdBase(PCWSTR pszName, PCWSTR pszDescription, PCWSTR pszArgTemplate) :
|
|
_fPrintUsageAndExit(FALSE),
|
|
_fError(FALSE)
|
|
{
|
|
StringCchCopy(_szName, ARRAYSIZE(_szName), pszName);
|
|
StringCchCopy(_szPrefixedName, ARRAYSIZE(_szPrefixedName), pszName);
|
|
StringCchCopy(_szDescription, ARRAYSIZE(_szDescription), pszDescription);
|
|
StringCchCopy(_szArgTemplate, ARRAYSIZE(_szArgTemplate), pszArgTemplate);
|
|
}
|
|
|
|
HRESULT CCmdBase::Execute(PCWSTR* ppszArgs, int cArgs)
|
|
{
|
|
HRESULT hr;
|
|
PCWSTR pszFirstArg = cArgs ? ppszArgs[0] : NULL;
|
|
if (pszFirstArg &&
|
|
(pszFirstArg[0] == L'-' || pszFirstArg[0] == L'/') &&
|
|
(!StrCmpICW(++pszFirstArg, L"help") ||
|
|
!StrCmpICW(pszFirstArg, L"h") ||
|
|
!StrCmpICW(pszFirstArg, L"?")))
|
|
{
|
|
hr = S_FALSE;
|
|
PrintUsage();
|
|
}
|
|
else
|
|
{
|
|
hr = _ProcessOptions(&ppszArgs, &cArgs);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = v_ProcessArguments(ppszArgs, cArgs);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = v_ExecuteCommand();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCmdBase::_ProcessOptions(PCWSTR** pppszArgs, int* pcArgs)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
|
|
if (pppszArgs && *pppszArgs && pcArgs)
|
|
{
|
|
PCWSTR* ppszArgs = *pppszArgs;
|
|
int cArgs = *pcArgs;
|
|
|
|
// process prefixed options using registered handlers
|
|
hr = S_OK;
|
|
int iArg = 0;
|
|
while (SUCCEEDED(hr) &&
|
|
(iArg < cArgs) &&
|
|
(ppszArgs[iArg][0] == L'-' ||
|
|
ppszArgs[iArg][0] == L'/'))
|
|
{
|
|
PCWSTR pszOptionName = &(ppszArgs[iArg][1]);
|
|
PWSTR pszOptionArgs = const_cast<PWSTR>(wcschr(pszOptionName, L':'));
|
|
if (pszOptionArgs)
|
|
{
|
|
*pszOptionArgs = L'\0';
|
|
pszOptionArgs++;
|
|
}
|
|
else
|
|
{
|
|
pszOptionArgs = L"";
|
|
}
|
|
|
|
COptionHandler *pHandler;
|
|
hr = _mapOptionHandlers.Find(pszOptionName, &pHandler);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pHandler->v_SetOption(this, pszOptionArgs);
|
|
}
|
|
else
|
|
{
|
|
ParseError(L"Unrecognized option: %s%s%s\n",
|
|
ppszArgs[iArg],
|
|
(wcslen(pszOptionArgs) ? L":" : L""),
|
|
pszOptionArgs);
|
|
}
|
|
|
|
iArg++;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// leave remaining arguments for the derived class
|
|
*pppszArgs = ppszArgs + iArg;
|
|
*pcArgs = cArgs - iArg;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CCmdBase::_PrintUsageForOption(COptionHandler *pOption) const
|
|
{
|
|
pOption->v_PrintUsage(this);
|
|
}
|
|
|
|
void CCmdBase::PrintUsage() const
|
|
{
|
|
bool fHaveOptions = !_mapOptionHandlers.IsEmpty();
|
|
Output(L"Usage: %s %s%s\n\n", GetName(true), (fHaveOptions ? L"[OPTIONS] " : L""), GetArgTemplate());
|
|
if (_szDescription[0])
|
|
{
|
|
Output(L"%s\n\n", GetDescription());
|
|
}
|
|
if (fHaveOptions)
|
|
{
|
|
Output(L"Options:\n");
|
|
_mapOptionHandlers.ForEachValue(&CCmdBase::_PrintUsageForOption, this);
|
|
Output(L"\n");
|
|
}
|
|
v_PrintInstructions();
|
|
}
|
|
|
|
PCWSTR CCmdBase::GetName(bool fIncludePrefix) const
|
|
{
|
|
return fIncludePrefix ? _szPrefixedName : _szName;
|
|
}
|
|
|
|
PCWSTR CCmdBase::GetDescription() const
|
|
{
|
|
return _szDescription;
|
|
}
|
|
|
|
PCWSTR CCmdBase::GetArgTemplate() const
|
|
{
|
|
return _szArgTemplate;
|
|
}
|
|
|
|
void CCmdBase::AddPrefix(PCWSTR pszPrefix)
|
|
{
|
|
WCHAR szNewPrefix[MAX_PATH];
|
|
StringCchPrintf(szNewPrefix, ARRAYSIZE(szNewPrefix), L"%s %s", _szPrefixedName, pszPrefix);
|
|
StringCchCopy(_szPrefixedName, ARRAYSIZE(_szPrefixedName), szNewPrefix);
|
|
}
|
|
|
|
void CCmdBase::Output(PCWSTR pszFormat, ...) const
|
|
{
|
|
va_list args;
|
|
va_start(args, pszFormat);
|
|
|
|
vwprintf(pszFormat, args);
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
void CCmdBase::ParseError(PCWSTR pszFormat, ...)
|
|
{
|
|
if (!_fError)
|
|
{
|
|
_fError = TRUE;
|
|
va_list args;
|
|
va_start(args, pszFormat);
|
|
|
|
wprintf(L"ERROR - ");
|
|
vwprintf(pszFormat, args);
|
|
wprintf(L"\n");
|
|
PrintUsage();
|
|
|
|
va_end(args);
|
|
}
|
|
}
|
|
|
|
void CCmdBase::RuntimeError(PCWSTR pszFormat, ...)
|
|
{
|
|
_fError = TRUE;
|
|
va_list args;
|
|
va_start(args, pszFormat);
|
|
|
|
wprintf(L"ERROR - ");
|
|
vwprintf(pszFormat, args);
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
CMetaCommand::CMetaCommand(PCWSTR pszName, PCWSTR pszDescription, CMetaCommand::PFNCREATECOMMAND* pCmds, int cCmds) :
|
|
CCmdBase(pszName, pszDescription, L"SUBCOMMAND"),
|
|
_pSpecifiedCmd(NULL),
|
|
_ppszArgs(NULL),
|
|
_cArgs(0)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
for (int iCmd = 0; SUCCEEDED(hr) && (iCmd < cCmds); iCmd++)
|
|
{
|
|
hr = AddCommand(pCmds[iCmd]);
|
|
}
|
|
}
|
|
|
|
HRESULT CMetaCommand::AddCommand(CMetaCommand::PFNCREATECOMMAND pfnCreate)
|
|
{
|
|
CCmdBase *pCmd;
|
|
HRESULT hr = pfnCreate(&pCmd, GetName(true));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _mapCmds.Add(pCmd->GetName(), &pCmd);
|
|
delete pCmd;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CMetaCommand::v_ProcessArguments(PCWSTR* ppszArgs, int cArgs)
|
|
{
|
|
HRESULT hr = E_UNEXPECTED;
|
|
|
|
if (!_pSpecifiedCmd)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
if (cArgs >= 1)
|
|
{
|
|
// look up the command name
|
|
hr = _mapCmds.Find(ppszArgs[0], &_pSpecifiedCmd);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// save the remaining arguments for the specified command
|
|
// this assumes that the lifetime of ppszArgs and cArgs is tied to the calling function, CCmdBase::Execute
|
|
_ppszArgs = ppszArgs + 1;
|
|
_cArgs = cArgs - 1;
|
|
}
|
|
else
|
|
{
|
|
ParseError(L"Command not recognized: %s\n", ppszArgs[0]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ParseError(L"No command specified.\n");
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CMetaCommand::v_ExecuteCommand()
|
|
{
|
|
return _pSpecifiedCmd ? _pSpecifiedCmd->Execute(_ppszArgs, _cArgs) : E_UNEXPECTED;
|
|
}
|
|
|
|
void CMetaCommand::_PrintDescriptionForCommand(CCmdBase *pCmd) const
|
|
{
|
|
Output(L" %-12s%s\n", pCmd->GetName(), pCmd->GetDescription());
|
|
}
|
|
|
|
void CMetaCommand::v_PrintInstructions() const
|
|
{
|
|
Output(L"Supported commands:\n");
|
|
_mapCmds.ForEachValue(&CMetaCommand::_PrintDescriptionForCommand, this);
|
|
}
|