mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1279 lines
35 KiB
1279 lines
35 KiB
//+------------------------------------------------------------------
|
|
//
|
|
// File: cmdline.cxx
|
|
//
|
|
// Contents: implementation of CCmdline class
|
|
//
|
|
// Synopsis: CCmdline encapsulates the actual command line, and
|
|
// parses it looking for expected switches.
|
|
//
|
|
//
|
|
// Classes: CCmdline
|
|
//
|
|
// Functions:
|
|
//
|
|
// History: 12/23/91 Lizch Created
|
|
// 04/17/92 Lizch Converted to NLS_STR
|
|
// 09/09/92 Lizch Changed SUCCESS to NO_ERROR
|
|
// 09/18/92 Lizch Precompile headers
|
|
// 11/14/92 DwightKr Updates for new version of NLS_STR
|
|
//
|
|
//-------------------------------------------------------------------
|
|
#include <comtpch.hxx>
|
|
#pragma hdrstop
|
|
|
|
#include <cmdlinew.hxx> // public cmdlinew stuff
|
|
#include "_clw.hxx" // private cmdlinew stuff
|
|
#include <ctype.h> // is functions
|
|
|
|
|
|
static void DelimitWithNulls(LPNSTR pnszArgline,
|
|
LPNSTR pnszDest,
|
|
UINT *puiArgc);
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::CCmdline(int, char **, BOOL = FALSE)
|
|
//
|
|
// Synopsis: Copies argv and argc, and initialises defaults.
|
|
//
|
|
// Effects: Makes a copy of argv and argc. Argv (minus argv[0]) is
|
|
// copied into an array of CCmdlineArg objects.
|
|
// Argv[0] is copied into a program name data member.
|
|
//
|
|
// Arguments: [argc] - count of arguments.
|
|
// [argv] - arguments.
|
|
// [fInternalUsage] - Use internal Usage.
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Modifies: aeLastError
|
|
//
|
|
// History: 12/23/91 Lizch Created.
|
|
// 04/17/92 Lizch Converted to NLS_STR
|
|
// 07/28/92 Davey Intialize _pfExtraUsage, _usIndent,
|
|
// _usDisplayWidth.
|
|
// Modified call to SetProgName,
|
|
// Added fInternalUsage parameter.
|
|
// 10/11/94 XimingZ Initialize _pnszProgName.
|
|
// Call SetError before the first error return.
|
|
// Set _apArgs to NULL as it is deleted in
|
|
// case of memory allocatio failure.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
CCmdline::CCmdline(int argc, char *argv[], BOOL fInternalUsage) :
|
|
_apArgs(NULL),
|
|
_uiNumArgs(0),
|
|
_pfExtraUsage(NULL),
|
|
_usIndent(CMD_INDENT),
|
|
_usDisplayWidth(CMD_DISPLAY_WIDTH),
|
|
_usSwitchIndent(CMD_SWITCH_INDENT),
|
|
_pbcInternalUsage(NULL),
|
|
_pnszProgName(NULL)
|
|
{
|
|
INT iRC;
|
|
PNSTR pnszBuf;
|
|
|
|
SetError(CMDLINE_NO_ERROR);
|
|
|
|
if (fInternalUsage)
|
|
{
|
|
iRC = SetInternalUsage();
|
|
if (CMDLINE_NO_ERROR != iRC)
|
|
return;
|
|
}
|
|
|
|
pnszBuf = new NCHAR[strlen(argv[0])+1];
|
|
if (NULL == pnszBuf)
|
|
{
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
#ifdef CTUNICODE
|
|
mbstowcs(pnszBuf, argv[0], strlen(argv[0])+1);
|
|
#else
|
|
strcpy(pnszBuf, argv[0]);
|
|
#endif
|
|
|
|
}
|
|
|
|
iRC = SetProgName(pnszBuf);
|
|
delete pnszBuf;
|
|
if (iRC != CMDLINE_NO_ERROR)
|
|
{
|
|
SetError(iRC);
|
|
return;
|
|
}
|
|
|
|
|
|
// Don't include argv[0] in the argument count.
|
|
_uiNumArgs = argc - 1;
|
|
|
|
// Now set up an array of CCmdlineArg objects, each of which
|
|
// encapsulates one argv element
|
|
//
|
|
_apArgs = new (CCmdlineArg *[_uiNumArgs]);
|
|
if (_apArgs == NULL)
|
|
{
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
|
|
for (INT i=0; i< (INT)_uiNumArgs; i++)
|
|
{
|
|
// Convert argv[i] to unicode
|
|
pnszBuf = new NCHAR[strlen(argv[i+1])+1];
|
|
if (NULL == pnszBuf)
|
|
{
|
|
_apArgs[i] = NULL;
|
|
}
|
|
else
|
|
{
|
|
// We check for errors in the next block
|
|
#ifdef CTUNICODE
|
|
mbstowcs(pnszBuf, argv[i+1], strlen(argv[i+1])+1);
|
|
#else
|
|
strcpy(pnszBuf, argv[i+1]);
|
|
#endif
|
|
|
|
_apArgs[i] = new CCmdlineArg(pnszBuf);
|
|
delete pnszBuf;
|
|
}
|
|
|
|
// If an allocation failed, rewind through those we have
|
|
// allocated
|
|
//
|
|
if ((_apArgs[i] == NULL) || (_apArgs[i]->QueryError() != CMDLINE_NO_ERROR))
|
|
{
|
|
for (; i>=0; i--)
|
|
{
|
|
delete _apArgs[i];
|
|
}
|
|
|
|
delete [] _apArgs;
|
|
_apArgs = NULL;
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::CCmdline(BOOL fInternalUsage = FALSE)
|
|
//
|
|
// Synopsis: Takes arguments from GetCommandLine(). To be used
|
|
// with Windows programs whose startpoint is WinMain()
|
|
//
|
|
// Effects: Takes the arguments out of GetCommandLine(), and
|
|
// copies them into an array of CCmdlineArg objects.
|
|
//
|
|
// Arguments: [fInternalUsage] Use internal usage
|
|
//
|
|
// Returns: None
|
|
//
|
|
// Modifies: Not much really
|
|
//
|
|
// History: 2/6/1995 jesussp Created
|
|
//
|
|
// Note: For 16 bit, this will set the error to
|
|
// CMDLINE_ERROR_USAGE_FOUND, as the GetCommandLine is
|
|
// not support in WIN16.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
CCmdline::CCmdline(BOOL fInternalUsage) :
|
|
_apArgs(NULL),
|
|
_uiNumArgs(0),
|
|
_pfExtraUsage(NULL),
|
|
_usIndent(CMD_INDENT),
|
|
_usDisplayWidth(CMD_DISPLAY_WIDTH),
|
|
_usSwitchIndent(CMD_SWITCH_INDENT),
|
|
_pbcInternalUsage(NULL),
|
|
_pnszProgName(NULL)
|
|
{
|
|
|
|
#if !defined (_WIN32)
|
|
|
|
SetError(CMDLINE_ERROR_USAGE_FOUND);
|
|
return;
|
|
|
|
#else
|
|
|
|
INT iRC;
|
|
NCHAR *pnszArgs,
|
|
*pnszProg,
|
|
*pnszDst;
|
|
NCHAR *pncArg;
|
|
|
|
INT iError;
|
|
|
|
|
|
SetError(CMDLINE_NO_ERROR);
|
|
|
|
if (fInternalUsage)
|
|
{
|
|
iRC = SetInternalUsage();
|
|
if (CMDLINE_NO_ERROR != iRC)
|
|
return;
|
|
}
|
|
|
|
// Obtain a copy of the command line
|
|
|
|
#if defined(CTUNICODE)
|
|
pnszProg = new NCHAR[1+wcslen(GetCommandLineW())];
|
|
#else
|
|
pnszProg = new NCHAR[1+strlen(GetCommandLineA())];
|
|
#endif
|
|
|
|
if (NULL == pnszProg)
|
|
{
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
|
|
#if defined(CTUNICODE)
|
|
wcscpy(pnszProg, GetCommandLineW());
|
|
#else
|
|
strcpy(pnszProg, GetCommandLineA());
|
|
#endif
|
|
|
|
// Skip through the command line looking for arguments
|
|
|
|
pnszArgs = pnszProg;
|
|
while (nchClSpace != *pnszArgs && nchClNull != *pnszArgs)
|
|
{
|
|
++pnszArgs;
|
|
}
|
|
|
|
if (nchClSpace == *pnszArgs)
|
|
{
|
|
*pnszArgs++ = nchClNull;
|
|
}
|
|
|
|
// Now pnszProg points to a null-terminated string containing the
|
|
// program name, and pnszArgs points to a null-terminated string
|
|
// containing the program arguments...
|
|
|
|
SetProgName(pnszProg);
|
|
|
|
// Allocate memory for a buffer containing the different arguments
|
|
// separated by nulls
|
|
|
|
INT cBufSize = 1+_ncslen(pnszArgs);
|
|
|
|
//
|
|
// to accomodate for an extra null character
|
|
//
|
|
pnszDst = new NCHAR[1+cBufSize];
|
|
if (NULL == pnszDst)
|
|
{
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
delete pnszProg;
|
|
return;
|
|
}
|
|
|
|
// Parse the argument line and get a null-terminated string
|
|
|
|
DelimitWithNulls(pnszArgs, pnszDst, &_uiNumArgs);
|
|
|
|
|
|
// Set up an array of CCmdlineArg objects, each of which
|
|
// encapsulates one argument
|
|
//
|
|
_apArgs = new (CCmdlineArg *[_uiNumArgs]);
|
|
if (_apArgs == NULL)
|
|
{
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
return;
|
|
}
|
|
|
|
pncArg = pnszDst;
|
|
|
|
for (INT i=0; i< (INT)_uiNumArgs; i++)
|
|
{
|
|
|
|
// Copy argument string
|
|
|
|
NCHAR *pnszBuf = new NCHAR[_ncslen(pncArg)+1];
|
|
|
|
if (NULL == pnszBuf)
|
|
{
|
|
_apArgs[i] = NULL;
|
|
}
|
|
else
|
|
// We check for errors until the next block
|
|
{
|
|
_ncscpy(pnszBuf, pncArg);
|
|
_apArgs[i] = new CCmdlineArg(pnszBuf);
|
|
delete pnszBuf;
|
|
}
|
|
|
|
// If an allocation failed, rewind through those we have
|
|
// allocated
|
|
if ((_apArgs[i] == NULL) || (_apArgs[i]->QueryError() != CMDLINE_NO_ERROR))
|
|
{
|
|
for (; i>=0; i--)
|
|
{
|
|
delete _apArgs[i];
|
|
}
|
|
|
|
delete [] _apArgs;
|
|
_apArgs = NULL;
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
break;
|
|
}
|
|
|
|
// Skip to the next argument (past a null byte)
|
|
|
|
while (*pncArg++)
|
|
{
|
|
;
|
|
}
|
|
}
|
|
|
|
// Release allocated memory
|
|
|
|
delete pnszProg;
|
|
delete pnszDst;
|
|
|
|
#endif // if !defined (_WIN32)
|
|
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::~CCmdline()
|
|
//
|
|
// Synopsis: Destructor for CCmdline
|
|
//
|
|
// Effects: Deletes _apArgs
|
|
//
|
|
// History: 12/23/91 Lizch Created.
|
|
// 04/17/92 Lizch Converted to NLS_STR
|
|
// 08/10/92 Davey Added delete of _pnlsEquater/_pnlsSeparator
|
|
// 10/11/94 XimingZ Added checking _apArgs != NULL
|
|
//
|
|
//-------------------------------------------------------------------
|
|
CCmdline::~CCmdline()
|
|
{
|
|
if (_apArgs != NULL)
|
|
{
|
|
for (UINT i=0; i<_uiNumArgs; i++)
|
|
{
|
|
delete _apArgs[i];
|
|
}
|
|
delete [] _apArgs;
|
|
}
|
|
|
|
delete _pbcInternalUsage;
|
|
|
|
delete _pnszProgName;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::Parse, public
|
|
//
|
|
// Synoposis: Parses the stored command line, matching actual with
|
|
// expected command line switches.
|
|
//
|
|
// Effects: Calls FindSwitch which stores the command line values
|
|
// (eg. "lizch" in "/user:lizch") in the individual command
|
|
// line objects, via the polymorphic SetValue call.
|
|
//
|
|
// Arguments: [apExpectedArgs] - an array of all possible command line
|
|
// objects
|
|
// [iMaxArgs] - the number of elements in that array.
|
|
// [fExtras] - if TRUE, a warning is given if an
|
|
// unexpected command line argument is given.
|
|
//
|
|
// Returns: CMDLINE_NO_ERROR,
|
|
// CMDLINE_ERROR_INVALID_FLAG.
|
|
// CMDLINE_ERROR_ARGUMENT_MISSING
|
|
// CMDLINE_ERROR_UNRECOGNISED_ARG
|
|
// CMDLINE_ERROR_USAGE_FOUND
|
|
//
|
|
// History: 12/23/91 Lizch Created.
|
|
// 04/17/92 Lizch Converted to NLS_STR.
|
|
// 08/10/92 Davey Added internal usage check.
|
|
// 06/13/97 MariusB Reset found flag for each expected
|
|
// arg. Reset the value of the arg if it
|
|
// is not found, no default and not
|
|
// required.
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::Parse(
|
|
CBaseCmdlineObj *apExpectedArgs[],
|
|
UINT uiMaxArgs,
|
|
BOOL fCheckForExtras)
|
|
{
|
|
INT iRC = CMDLINE_NO_ERROR;
|
|
UINT i;
|
|
|
|
// Traverse the array of objects given, making sure none have errors
|
|
for (i=0; i<uiMaxArgs; i++)
|
|
{
|
|
iRC = apExpectedArgs[i]->QueryError();
|
|
if (CMDLINE_NO_ERROR != iRC)
|
|
{
|
|
return(iRC);
|
|
}
|
|
|
|
apExpectedArgs[i]->SetFoundFlag(FALSE);
|
|
}
|
|
|
|
// check for internal usage if defined.
|
|
if (_pbcInternalUsage != NULL)
|
|
{
|
|
iRC = FindSwitch(_pbcInternalUsage, fCheckForExtras);
|
|
if (iRC != CMDLINE_NO_ERROR)
|
|
{
|
|
DisplayUsage(apExpectedArgs, uiMaxArgs);
|
|
return(iRC);
|
|
}
|
|
|
|
// if found, print out usage and return.
|
|
if (_pbcInternalUsage->IsFound() == TRUE)
|
|
{
|
|
DisplayUsage(apExpectedArgs, uiMaxArgs);
|
|
return(CMDLINE_ERROR_USAGE_FOUND);
|
|
}
|
|
}
|
|
|
|
|
|
// look for each expected argument in turn.
|
|
for (i=0; i<uiMaxArgs; i++)
|
|
{
|
|
CBaseCmdlineObj *pArg = apExpectedArgs[i];
|
|
|
|
// Go along the actual command line, looking for this switch
|
|
// CMDLINE_NO_ERROR doesn't necessarily indicate we found it,
|
|
// just that nothing went wrong
|
|
//
|
|
iRC = FindSwitch(pArg, fCheckForExtras);
|
|
if (iRC != CMDLINE_NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
|
|
// We've gone through the entire command line. Check if any
|
|
// switch was omitted. For those that had defaults specified,
|
|
// set the default. For mandatory switches, generate an error.
|
|
// Note that you should never have a default value for a
|
|
// mandatory switch!
|
|
//
|
|
if ((pArg->IsFound()) == FALSE)
|
|
{
|
|
if (pArg->IsDefaultSpecified() == TRUE)
|
|
{
|
|
iRC = pArg->SetValueToDefault();
|
|
if (iRC != CMDLINE_NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((pArg->IsRequired()) == TRUE)
|
|
{
|
|
pArg->DisplayUsageDescr(
|
|
_usSwitchIndent,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
|
|
iRC = CMDLINE_ERROR_ARGUMENT_MISSING;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// If the object is not found, it has no default
|
|
// value and it is not required, reset it. This
|
|
// action will not harm for normal use, but it
|
|
// will allow you to reuse the same set of command
|
|
// line object within the same app.
|
|
|
|
pArg->ResetValue();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (iRC == CMDLINE_NO_ERROR)
|
|
{
|
|
// We've got all our expected switches. Now check to see if
|
|
// any switches are left on the command line unparsed,
|
|
// if that's what the user wanted.
|
|
//
|
|
if (fCheckForExtras == TRUE)
|
|
{
|
|
for (i=0; i<_uiNumArgs; i++)
|
|
{
|
|
if (_apArgs[i]->IsProcessed() != TRUE)
|
|
{
|
|
_sNprintf(_nszErrorBuf,
|
|
_TEXTN("Unrecognised switch %s\n"),
|
|
_apArgs[i]->QueryArg());
|
|
(*_pfnDisplay)(_nszErrorBuf);
|
|
iRC = CMDLINE_ERROR_UNRECOGNISED_ARG;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DisplayUsage(apExpectedArgs, uiMaxArgs);
|
|
}
|
|
|
|
return(iRC);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::FindSwitch, private
|
|
//
|
|
// Synoposis: Searches the command line for a given switch, checking
|
|
// for separator and equator characters.
|
|
//
|
|
// Effects: Stores the value of the given switch, if any
|
|
//
|
|
// Arguments: [pArg] - pointer to the switch object to search
|
|
// for
|
|
// [fCheckForExtras] - flag to see if should check for extra
|
|
// args.
|
|
//
|
|
// Returns: CMDLINE_NO_ERROR
|
|
// CMDLINE_ERROR_INVALID_VALUE
|
|
// CMDLINE_ERROR_UNRECOGNISED_ARG
|
|
//
|
|
// History: 12/23/91 Lizch Created.
|
|
// 04/17/92 Lizch Converted to NLS_STR
|
|
// 07/29/92 Davey Converted to use DisplayUsageDescr
|
|
// Also fixed error output, was using
|
|
// printf, changed to sprintf. Added
|
|
// Check for extras flag.
|
|
// 02/27/95 jesussp Corrected the case when the switch
|
|
// name is non-existent
|
|
// 06/25/97 mariusb Make /<switch>: and /<switch>
|
|
// equivalent when the switch takes a
|
|
// second argument (i.e. no equator
|
|
// means the switch does not take a
|
|
// second arg). Useful to specify NULL
|
|
// values.
|
|
// Notes: Terminology to help understand this:
|
|
// Example: /username:lizch
|
|
// The '/' is the separator
|
|
// The ':' is the equator
|
|
// 'username' is the switch
|
|
// 'lizch' is the value
|
|
//
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::FindSwitch(CBaseCmdlineObj * const pArg, BOOL fCheckForExtras)
|
|
{
|
|
INT iRC = CMDLINE_NO_ERROR;
|
|
LPCNSTR nszArg;
|
|
LPCNSTR nszSep;
|
|
LPCNSTR nszSwitch;
|
|
LPCNSTR nszThisSwitch;
|
|
LPCNSTR nszEquater;
|
|
USHORT cchSwitch;
|
|
USHORT usSecondArg = 1;
|
|
NCHAR nchSeparator = pArg->GetSeparator();
|
|
NCHAR nchEquater = pArg->GetEquater();
|
|
NCHAR nszSeparator[2];
|
|
|
|
// If this switch does NOT take a second argument, we don't advance
|
|
// past the equater when setting the value
|
|
//
|
|
if (!pArg->SecondArg())
|
|
{
|
|
usSecondArg = 0;
|
|
}
|
|
|
|
nszSeparator[0] = nchSeparator;
|
|
nszSeparator[1] = nchClNull;
|
|
|
|
for (UINT i=0; i<_uiNumArgs; i++)
|
|
{
|
|
nszArg = _apArgs[i]->QueryArg();
|
|
|
|
// Find the separator character - it should be the first
|
|
// character in the command line argument. If not, return
|
|
// an error if fCheckForExtras == TRUE.
|
|
//
|
|
nszSep = nszArg;
|
|
if (0 == _ncscspn(nszSep, nszSeparator))
|
|
{
|
|
// Get the switch value - first character after separator
|
|
// up to but not including the equater
|
|
//
|
|
nszSwitch = nszSep+1;
|
|
nszEquater = nszSwitch;
|
|
|
|
// Look for equater
|
|
while ((nchClNull != *nszEquater) && (nchEquater != *nszEquater))
|
|
{
|
|
nszEquater++;
|
|
}
|
|
cchSwitch = (USHORT) (nszEquater - nszSwitch);
|
|
|
|
nszThisSwitch = pArg->QuerySwitchString();
|
|
|
|
// See if this switch is for this argument
|
|
if (0 != cchSwitch &&
|
|
cchSwitch == _ncslen(nszThisSwitch) &&
|
|
0 == _ncsnicmp(nszSwitch, nszThisSwitch, cchSwitch))
|
|
{
|
|
// If we couldn't find the equater, decrease usSecondArg
|
|
// which means we accept a NULL value for the switch. Thus
|
|
// we make /<switch>: and /<switch> equivalent.
|
|
if (nchEquater != *nszEquater && 1 == usSecondArg)
|
|
{
|
|
--usSecondArg;
|
|
}
|
|
|
|
// It is - get this switch's value
|
|
_apArgs[i]->SetProcessedFlag(TRUE);
|
|
|
|
// Now set the value of the switch (if any). How this is
|
|
// done will vary from object to object: some won't take
|
|
// values, some take lists of values etc. The polymorphic
|
|
// SetValue call handles this. We don't want to pass
|
|
// in the equater so add one to get to the beginning
|
|
// of the value.
|
|
//
|
|
iRC = pArg->SetValue(nszSwitch+cchSwitch+usSecondArg);
|
|
if (iRC != CMDLINE_NO_ERROR)
|
|
{
|
|
pArg->DisplayUsageDescr(
|
|
_usSwitchIndent,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
}
|
|
|
|
pArg->SetFoundFlag(TRUE);
|
|
// We have found the value for the given switch.
|
|
|
|
break;
|
|
} //if
|
|
}
|
|
else
|
|
{
|
|
// Error out if didn't find separator, only if checking for
|
|
// extras
|
|
//
|
|
if (fCheckForExtras == TRUE)
|
|
{
|
|
_sNprintf(
|
|
_nszErrorBuf,
|
|
_TEXTN("The initial separator %c was not found on switch %s\n"),
|
|
nchSeparator,
|
|
nszArg);
|
|
|
|
(*_pfnDisplay)(_nszErrorBuf);
|
|
|
|
return(CMDLINE_ERROR_UNRECOGNISED_ARG);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(iRC);
|
|
}
|
|
|
|
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::SetProgName, public
|
|
//
|
|
// Synoposis: Sets the program name to the passed value
|
|
// This value can later be used in usage statements
|
|
//
|
|
// Arguments: [nszProgName] - the program name
|
|
//
|
|
// Returns: Error code
|
|
//
|
|
// History: Created 05/23/91 Lizch
|
|
//
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::SetProgName(PNSTR nszProgName)
|
|
{
|
|
|
|
|
|
INT nRet = CMDLINE_NO_ERROR;
|
|
NCHAR *pnchStart = nszProgName;
|
|
NCHAR *pnchDot;
|
|
|
|
// check for last slash.
|
|
if (NULL != (pnchStart = _ncsrchr(nszProgName, '\\')))
|
|
{
|
|
pnchStart++; // get past slash
|
|
}
|
|
else
|
|
{
|
|
// there was no slash so check for colon
|
|
if (NULL != (pnchStart = _ncschr(nszProgName, ':')))
|
|
{
|
|
pnchStart++; // get past colon
|
|
}
|
|
else
|
|
{
|
|
// there was no colon or slash so set to beginning of name
|
|
pnchStart = nszProgName;
|
|
}
|
|
}
|
|
|
|
if (NULL != (pnchDot = _ncschr(pnchStart, _TEXTN('.'))))
|
|
{
|
|
*pnchDot = nchClNull;
|
|
}
|
|
|
|
_pnszProgName = new NCHAR[_ncslen(pnchStart)+1];
|
|
if (NULL == _pnszProgName)
|
|
{
|
|
nRet = CMDLINE_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
else
|
|
{
|
|
_ncscpy(_pnszProgName, pnchStart);
|
|
}
|
|
|
|
return(nRet);
|
|
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::GetProgName, public
|
|
//
|
|
// Synoposis: Returns a pointer to the program name
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Returns: a const NCHAR pointer to the program name
|
|
//
|
|
// History: Created 05/23/91 Lizch
|
|
//
|
|
//-------------------------------------------------------------------
|
|
const NCHAR *CCmdline::GetProgName()
|
|
{
|
|
return(_pnszProgName);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::SetExtraUsage, public
|
|
//
|
|
// Synoposis: Sets the extra usage file pointer.
|
|
//
|
|
// Arguments: [pfUsage] - function pointer to usage function.
|
|
//
|
|
// History: 07/28/92 Davey Created.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
void CCmdline::SetExtraUsage(PFVOID pfUsage)
|
|
{
|
|
_pfExtraUsage = pfUsage;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::SetIndent, public
|
|
//
|
|
// Synoposis: Sets the indent parameter for usage display.
|
|
//
|
|
// Arguments: [usIndent] - how much to indent second lines.
|
|
//
|
|
// Returns: codes from CheckParamerterConsistency
|
|
//
|
|
// History: 07/28/92 Davey Created.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::SetIndent(USHORT usIndent)
|
|
{
|
|
_usIndent = usIndent;
|
|
return(CheckParameterConsistency());
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::SetSwitchIndent, public
|
|
//
|
|
// Synoposis: Sets the switch indent parameter for usage display.
|
|
//
|
|
// Arguments: [usSwitchIndent] - how much to indent switch.
|
|
//
|
|
// Returns: codes from CheckParamerterConsistency
|
|
//
|
|
// History: 07/28/92 Davey Created.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::SetSwitchIndent(USHORT usSwitchIndent)
|
|
{
|
|
_usSwitchIndent = usSwitchIndent;
|
|
return(CheckParameterConsistency());
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::SetDisplayWidth, public
|
|
//
|
|
// Synoposis: Sets the amount of line space available for the
|
|
// usage display.
|
|
//
|
|
// Arguments: [usDisplayWidth] - line space available
|
|
//
|
|
// Returns: codes from CheckParamerterConsistency
|
|
//
|
|
// History: 07/28/92 Davey Created.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::SetDisplayWidth(USHORT usDisplayWidth)
|
|
{
|
|
_usDisplayWidth = usDisplayWidth;
|
|
return(CheckParameterConsistency());
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::CheckParameterConsistency, private const
|
|
//
|
|
// Synoposis: Checks to make sure the display parameters are
|
|
// useable.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: CMDLINE_NO_ERROR
|
|
// CMDLINE_ERROR_DISPLAY_PARAMS
|
|
//
|
|
// History: 07/28/92 Davey Created.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::CheckParameterConsistency(void) const
|
|
{
|
|
if (_usSwitchIndent >= _usIndent)
|
|
{
|
|
return(CMDLINE_ERROR_DISPLAY_PARAMS);
|
|
}
|
|
if (_usIndent >= _usDisplayWidth)
|
|
{
|
|
return(CMDLINE_ERROR_DISPLAY_PARAMS);
|
|
}
|
|
|
|
return(CMDLINE_NO_ERROR);
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::QueryDisplayParameters, public
|
|
//
|
|
// Synoposis: Returns the three parameters which control the
|
|
// usage display output.
|
|
//
|
|
// Arguments: [pusDisplayWidth] - returns display width
|
|
// [pusSwitchIndent] - returns switch indent
|
|
// [pusIndent] - returns indent of second lines
|
|
//
|
|
// History: 07/28/92 Davey Created.
|
|
//
|
|
//-------------------------------------------------------------------
|
|
void CCmdline::QueryDisplayParameters(
|
|
USHORT *pusDisplayWidth,
|
|
USHORT *pusSwitchIndent,
|
|
USHORT *pusIndent) const
|
|
{
|
|
*pusDisplayWidth = _usDisplayWidth;
|
|
*pusSwitchIndent = _usSwitchIndent;
|
|
*pusIndent = _usIndent;
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::DisplayUsage, public
|
|
//
|
|
// Synoposis: Displays usage of switches,
|
|
//
|
|
// Arguments: apExpectedArgs: the array of command line objects
|
|
// uiMaxArgs: number of elements in the array
|
|
//
|
|
// Returns: Error code from QueryError
|
|
//
|
|
// History: 07/28/92 Davey Created.
|
|
//
|
|
// Notes: Looks like:
|
|
//
|
|
// Usage Instructions
|
|
// test2 /mc:<worker> [/ml:<log_server>] [/mt:<test>]
|
|
// [/mn:<tester-email>] [/mp:<path>] [/mo:<obj_name>]
|
|
// [/md:<dispatcher>] [/?]
|
|
//
|
|
// /mc Takes a list of strings specifying worker names.
|
|
// These workers should have full names.
|
|
// Don't you think so?
|
|
// The strings in the list are separated by one of the following
|
|
// character(s): ",; ".
|
|
// /ml Takes a string specifying log server name.
|
|
// /mt Takes a string specifying name of the test.
|
|
// /mn Takes a string specifying email name of the tester.
|
|
// /mp Takes a string specifying path name to log to.
|
|
// /mo Takes a string specifying name of the object.
|
|
// /md Takes a string specifying name of the dispatcher to use.
|
|
// /? Flag specifying command line usage. It defaults to FALSE.
|
|
//
|
|
// ***following is whatever is output by the function pfnExtraUsage****
|
|
//
|
|
//-------------------------------------------------------------------
|
|
INT CCmdline::DisplayUsage(
|
|
CBaseCmdlineObj * const apExpectedArgs[],
|
|
UINT uiMaxArgs)
|
|
{
|
|
USHORT usWidth = _usDisplayWidth;
|
|
USHORT StrLen;
|
|
INT iRC;
|
|
USHORT cchProgName;
|
|
PNSTR pnszLine;
|
|
USHORT i;
|
|
|
|
(*_pfnDisplay)(_TEXTN("\nUsage Instructions\n"));
|
|
|
|
// Start creating usage line.
|
|
//
|
|
|
|
// Determine length to make line buffer - if the size of the program
|
|
// name is less than the indentation, the buffer needs to be at
|
|
// least as big as the indentation, less 1 for an extra space that
|
|
// gets printed.
|
|
cchProgName = (USHORT) _ncslen(_pnszProgName);
|
|
|
|
if (cchProgName < _usIndent)
|
|
{
|
|
// Add 1 for null term
|
|
StrLen = (USHORT) (_usIndent + 1);
|
|
}
|
|
else
|
|
{
|
|
// Add 1 for null term, 1 for new line and indent in next line
|
|
StrLen = (USHORT) (cchProgName + _usIndent + 2);
|
|
}
|
|
|
|
// copy program name
|
|
//
|
|
pnszLine = new NCHAR[StrLen];
|
|
if (NULL == pnszLine)
|
|
{
|
|
return(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
|
|
_ncscpy(pnszLine, _pnszProgName);
|
|
|
|
// if the size of the program name is less than indentation then
|
|
// add in padding; else add in a new line and then padding.
|
|
//
|
|
if (cchProgName >= _usIndent)
|
|
{
|
|
_ncscat(pnszLine, nszClNewLine);
|
|
}
|
|
// fill in with spaces until get to indent position
|
|
for (i = (USHORT) _ncslen(pnszLine); i < StrLen - 1; i++)
|
|
{
|
|
_ncscat(pnszLine, nszClSpace);
|
|
}
|
|
|
|
// output program name
|
|
//
|
|
(*_pfnDisplay)(pnszLine);
|
|
|
|
// update remaining display width
|
|
//
|
|
usWidth = (USHORT) (usWidth - _usIndent);
|
|
|
|
// done with pnszLine - delete it now to avoid leaks in case of
|
|
// errors
|
|
//
|
|
delete pnszLine;
|
|
|
|
// display the command line usage statement, required ones first
|
|
//
|
|
for (i=0; i<uiMaxArgs; i++)
|
|
{
|
|
if (apExpectedArgs[i]->IsRequired() == TRUE)
|
|
{
|
|
iRC = apExpectedArgs[i]->DisplayUsageLine(
|
|
&usWidth,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
if (iRC)
|
|
{
|
|
SetError(iRC);
|
|
return(iRC);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0; i<uiMaxArgs; i++)
|
|
{
|
|
if (apExpectedArgs[i]->IsRequired() == FALSE)
|
|
{
|
|
iRC = apExpectedArgs[i]->DisplayUsageLine(
|
|
&usWidth,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
if (iRC)
|
|
{
|
|
SetError(iRC);
|
|
return(iRC);
|
|
}
|
|
}
|
|
}
|
|
|
|
// print out if using internal usage.
|
|
//
|
|
if (_pbcInternalUsage != NULL)
|
|
{
|
|
iRC = _pbcInternalUsage->DisplayUsageLine(
|
|
&usWidth,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
if (iRC)
|
|
{
|
|
SetError(iRC);
|
|
return(iRC);
|
|
}
|
|
}
|
|
|
|
|
|
// separate the line and descriptions
|
|
(*_pfnDisplay)(_TEXTN("\n\n"));
|
|
|
|
// display the switch descriptions, required ones first
|
|
for (i=0; i<uiMaxArgs; i++)
|
|
{
|
|
if (apExpectedArgs[i]->IsRequired() == TRUE)
|
|
{
|
|
iRC = apExpectedArgs[i]->DisplayUsageDescr(
|
|
_usSwitchIndent,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
if (iRC)
|
|
{
|
|
SetError(iRC);
|
|
return(iRC);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i=0; i<uiMaxArgs; i++)
|
|
{
|
|
if (apExpectedArgs[i]->IsRequired() == FALSE)
|
|
{
|
|
iRC = apExpectedArgs[i] -> DisplayUsageDescr(
|
|
_usSwitchIndent,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
if (iRC)
|
|
{
|
|
SetError(iRC);
|
|
return(iRC);
|
|
}
|
|
}
|
|
}
|
|
|
|
// print out if using internal usage.
|
|
//
|
|
if (_pbcInternalUsage != NULL)
|
|
{
|
|
iRC = _pbcInternalUsage->DisplayUsageDescr(
|
|
_usSwitchIndent,
|
|
_usDisplayWidth,
|
|
_usIndent);
|
|
if (iRC)
|
|
{
|
|
SetError(iRC);
|
|
return(iRC);
|
|
}
|
|
}
|
|
|
|
|
|
(*_pfnDisplay)(_TEXTN("\n\n"));
|
|
|
|
// invoke the special usage function if defined.
|
|
if (_pfExtraUsage != NULL)
|
|
{
|
|
_pfExtraUsage();
|
|
}
|
|
|
|
return(CMDLINE_NO_ERROR);
|
|
}
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Member: CCmdline::SetInternalUsage, private
|
|
//
|
|
// Synposis: Sets the internal usage flag
|
|
//
|
|
// Effects: Modifies _pcbInternalUsage, so that it displays
|
|
//
|
|
// Arguments: none
|
|
//
|
|
// Returns: CMDLINE_NO_ERROR
|
|
// CMDLINE_ERROR_OUT_OF_MEMORY
|
|
//
|
|
//
|
|
// History: 02/06/95 jesussp Created
|
|
//
|
|
// Notes:
|
|
//-------------------------------------------------------------------
|
|
|
|
INT CCmdline::SetInternalUsage(void)
|
|
{
|
|
INT iError; // Error to be returned
|
|
|
|
_pbcInternalUsage = new CBoolCmdlineObj(_TEXTN("?"), _TEXTN("command line usage."));
|
|
if (NULL == _pbcInternalUsage)
|
|
{
|
|
SetError(CMDLINE_ERROR_OUT_OF_MEMORY);
|
|
}
|
|
else
|
|
{
|
|
SetError(_pbcInternalUsage->QueryError());
|
|
}
|
|
iError = QueryError();
|
|
if (CMDLINE_NO_ERROR != iError)
|
|
{
|
|
// Since QueryError always sets CMDLINE_NO_ERROR, we need to
|
|
// call SetError to put the error back.
|
|
SetError(iError);
|
|
}
|
|
return iError;
|
|
}
|
|
|
|
//+------------------------------------------------------------------
|
|
//
|
|
// Function: DelimitWithNulls, private
|
|
//
|
|
// Synposis: Delimits an argument string, putting a null byte
|
|
// between parameters and an additional extra null byte
|
|
// at the end of the string. For use by CCmdline
|
|
// constructor.
|
|
//
|
|
// Effects: Copies pnszArgLine to pnszDest. Space must be
|
|
// allocated for both strings. Modifies uiArgc.
|
|
//
|
|
// Arguments: [pnszArgLine] The line with arguments
|
|
// [pnszDest] Null-delimited destination
|
|
// [puiArgc] Pointer to an argument count
|
|
//
|
|
// Returns: Nothing
|
|
//
|
|
//
|
|
// History: 02/10/95 jesussp Created
|
|
//
|
|
// Notes: Inspired on parse_cmdline(), crt32\startup\stdargv.c
|
|
// pnszArgLine and pnszDest must point to valid memory
|
|
// allocations. Also, the space allocation for
|
|
// pnszDest must be greater than that of pnszArgLine.
|
|
//-------------------------------------------------------------------
|
|
|
|
static void DelimitWithNulls(LPNSTR pnszArgLine,
|
|
LPNSTR pnszDest,
|
|
UINT *puiArgc)
|
|
{
|
|
LPNSTR pncSrc = pnszArgLine;
|
|
LPNSTR pncDst = pnszDest;
|
|
|
|
BOOL fInQuote = 0;
|
|
USHORT ucBackSlash = 0;
|
|
USHORT ucQuotes = 0;
|
|
|
|
|
|
*puiArgc = 0;
|
|
|
|
// Loop through the entire argument line
|
|
|
|
for (;;)
|
|
{
|
|
// skip blanks
|
|
while (*pncSrc && _isnspace(*pncSrc))
|
|
{
|
|
++pncSrc;
|
|
}
|
|
|
|
// Reached the end of the string?
|
|
if (nchClNull == *pncSrc)
|
|
{
|
|
// Put a null byte at the end only if we haven't had
|
|
// any argument...
|
|
|
|
if (0 == *puiArgc)
|
|
{
|
|
*pncDst++ = nchClNull;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// We now have one more argument...
|
|
++(*puiArgc);
|
|
|
|
// process one argument
|
|
|
|
for (;;)
|
|
{
|
|
|
|
ucBackSlash = 0;
|
|
|
|
while (nchClBackSlash == *pncSrc)
|
|
{
|
|
++pncSrc; ++ucBackSlash;
|
|
}
|
|
|
|
if (nchClQuote == *pncSrc)
|
|
{
|
|
if (ucBackSlash % 2 == 0) // Even number of backslashes?
|
|
{
|
|
fInQuote = !fInQuote;
|
|
++pncSrc; // Eat up quote
|
|
}
|
|
ucBackSlash /= 2;
|
|
}
|
|
|
|
while (ucBackSlash--)
|
|
{
|
|
*pncDst++ = nchClBackSlash;
|
|
}
|
|
|
|
if (nchClNull == *pncSrc || (!fInQuote && _isnspace(*pncSrc)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
*pncDst++ = *pncSrc++;
|
|
}
|
|
|
|
if (fInQuote)
|
|
{
|
|
*pncDst++ = nchClSpace;
|
|
}
|
|
else
|
|
{
|
|
*pncDst++ = nchClNull;
|
|
}
|
|
}
|
|
|
|
// Complete the string, adding an extra nul character
|
|
|
|
*pncDst = nchClNull;
|
|
|
|
}
|