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.
2457 lines
78 KiB
2457 lines
78 KiB
// ****************************************************************************
|
|
//
|
|
// Copyright (c) Microsoft Corporation
|
|
//
|
|
// Module Name:
|
|
//
|
|
// CmdLineParser.c
|
|
//
|
|
// Abstract:
|
|
//
|
|
// This modules implements parsing of command line arguments for the
|
|
// specified options
|
|
//
|
|
// Author:
|
|
//
|
|
// Sunil G.V.N. Murali ([email protected]) 1-Sep-2000
|
|
//
|
|
// Revision History:
|
|
//
|
|
// Sunil G.V.N. Murali ([email protected]) 1-Sep-2000 : Created It.
|
|
//
|
|
// ****************************************************************************
|
|
#include "pch.h"
|
|
#include "cmdline.h"
|
|
#include "CmdLineRes.h"
|
|
|
|
// permanent indexes to the temporary buffers
|
|
#define INDEX_TEMP_NONE 0
|
|
#define INDEX_TEMP_SPLITOPTION 1
|
|
#define INDEX_TEMP_SPLITVALUE 2
|
|
#define INDEX_TEMP_SAVEDATA 3
|
|
#define INDEX_TEMP_USAGEHELPER 4
|
|
#define INDEX_TEMP_MAINOPTION 5
|
|
|
|
//
|
|
// defines / constants / enumerations
|
|
//
|
|
|
|
// constants
|
|
const WCHAR cwszOptionChars[ 3 ] = L"-/";
|
|
const CHAR cszParserSignature[ 8 ] = "PARSER2";
|
|
|
|
// version resource specific structures
|
|
typedef struct __tagLanguageAndCodePage {
|
|
WORD wLanguage;
|
|
WORD wCodePage;
|
|
} TTRANSLATE, *PTTRANSLATE;
|
|
|
|
// error messages
|
|
#define ERROR_USAGEHELPER GetResString( IDS_ERROR_CMDPARSER_USAGEHELPER )
|
|
#define ERROR_NULLVALUE GetResString( IDS_ERROR_CMDPARSER_NULLVALUE )
|
|
#define ERROR_DEFAULT_NULLVALUE GetResString( IDS_ERROR_CMDPARSER_DEFAULT_NULLVALUE )
|
|
#define ERROR_VALUE_EXPECTED GetResString( IDS_ERROR_CMDPARSER_VALUE_EXPECTED )
|
|
#define ERROR_NOTINLIST GetResString( IDS_ERROR_CMDPARSER_NOTINLIST )
|
|
#define ERROR_DEFAULT_NOTINLIST GetResString( IDS_ERROR_CMDPARSER_DEFAULT_NOTINLIST )
|
|
#define ERROR_INVALID_NUMERIC GetResString( IDS_ERROR_CMDPARSER_INVALID_NUMERIC )
|
|
#define ERROR_DEFAULT_INVALID_NUMERIC GetResString( IDS_ERROR_CMDPARSER_DEFAULT_INVALID_NUMERIC )
|
|
#define ERROR_INVALID_FLOAT GetResString( IDS_ERROR_CMDPARSER_INVALID_FLOAT )
|
|
#define ERROR_DEFAULT_INVALID_FLOAT GetResString( IDS_ERROR_CMDPARSER_DEFAULT_INVALID_FLOAT )
|
|
#define ERROR_LENGTH_EXCEEDED GetResString( IDS_ERROR_CMDPARSER_LENGTH_EXCEEDED_EX )
|
|
#define ERROR_DEFAULT_LENGTH_EXCEEDED GetResString( IDS_ERROR_CMDPARSER_DEFAULT_LENGTH_EXCEEDED_EX )
|
|
#define ERROR_INVALID_OPTION GetResString( IDS_ERROR_CMDPARSER_INVALID_OPTION )
|
|
#define ERROR_OPTION_REPEATED GetResString( IDS_ERROR_CMDPARSER_OPTION_REPEATED )
|
|
#define ERROR_DEFAULT_OPTION_REPEATED GetResString( IDS_ERROR_CMDPARSER_DEFAULT_OPTION_REPEATED )
|
|
#define ERROR_MANDATORY_OPTION_MISSING GetResString( IDS_ERROR_CMDPARSER_MANDATORY_OPTION_MISSING )
|
|
#define ERROR_DEFAULT_OPTION_MISSING GetResString( IDS_ERROR_CMDPARSER_DEFAULT_OPTION_MISSING )
|
|
#define ERROR_VALUENOTALLOWED GetResString( IDS_ERROR_CMDPARSER_VALUENOTALLOWED )
|
|
|
|
//
|
|
// custom macros
|
|
#define REASON_VALUE_NOTINLIST( value, option, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_DEFAULT_NOTINLIST, \
|
|
_X( value ), _X2( helptext ) ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 3, ERROR_NOTINLIST, \
|
|
_X( value ), _X2( option ), _X3( helptext ) ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_NULLVALUE( option, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
SetReason2( 1, ERROR_DEFAULT_NULLVALUE, _X( helptext ) ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_NULLVALUE, _X( option ), _X2( helptext ) ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_VALUE_EXPECTED( option, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
UNEXPECTED_ERROR(); \
|
|
SaveLastError(); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_VALUE_EXPECTED, _X( option ), _X2( helptext ) ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_INVALID_NUMERIC( option, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
SetReason2( 1, \
|
|
ERROR_DEFAULT_INVALID_NUMERIC, _X( helptext ) ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_INVALID_NUMERIC, _X( option ), _X2( helptext ) ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_INVALID_FLOAT( option, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
SetReason2( 1, \
|
|
ERROR_DEFAULT_INVALID_FLOAT, _X( helptext ) ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_INVALID_FLOAT, _X( option ), _X2( helptext ) ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_LENGTH_EXCEEDED( option, length ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
SetReason2( 1, ERROR_DEFAULT_LENGTH_EXCEEDED, length ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_LENGTH_EXCEEDED, _X( option ), length ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_OPTION_REPEATED( option, count, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_DEFAULT_OPTION_REPEATED, count, helptext ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 3, \
|
|
ERROR_OPTION_REPEATED, _X( option ), count, helptext ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_MANDATORY_OPTION_MISSING( option, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
SetReason2( 1, \
|
|
ERROR_DEFAULT_OPTION_MISSING, helptext ); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_MANDATORY_OPTION_MISSING, \
|
|
_X( option ), helptext ); \
|
|
} \
|
|
1
|
|
|
|
#define REASON_VALUENOTALLOWED( option, helptext ) \
|
|
if ( option == NULL || lstrlen( option ) == 0 ) \
|
|
{ \
|
|
UNEXPECTED_ERROR(); \
|
|
SaveLastError(); \
|
|
} \
|
|
else \
|
|
{ \
|
|
SetReason2( 2, \
|
|
ERROR_VALUENOTALLOWED, _X( option ), _X2( helptext ) ); \
|
|
} \
|
|
1
|
|
|
|
//
|
|
// internal structures
|
|
//
|
|
typedef struct __tagMatchOptionInfo
|
|
{
|
|
LPWSTR pwszOption;
|
|
LPWSTR pwszValue;
|
|
} TMATCHOPTION_INFO;
|
|
|
|
typedef struct __tagParserSaveData
|
|
{
|
|
DWORD dwIncrement;
|
|
LONG lDefaultIndex;
|
|
LPCWSTR pwszUsageHelper;
|
|
PTCMDPARSER2 pcmdparser;
|
|
} TPARSERSAVE_DATA;
|
|
|
|
//
|
|
// private functions ... used only within this file
|
|
//
|
|
BOOL IsOption( LPCWSTR pwszOption );
|
|
BOOL IsValueNeeded( DWORD dwType );
|
|
LPCWSTR PrepareUsageHelperText( LPCWSTR pwszOption );
|
|
LPCWSTR ExtractMainOption( LPCWSTR pwszOptions, DWORD dwReserved );
|
|
BOOL VerifyParserOptions( LONG* plDefaultIndex,
|
|
DWORD dwCount, PTCMDPARSER2 pcmdOptions );
|
|
BOOL ParseAndSaveOptionValue( LPCWSTR pwszOption,
|
|
LPCWSTR pwszValue, TPARSERSAVE_DATA* pSaveData );
|
|
LONG MatchOption( DWORD dwOptions,
|
|
PTCMDPARSER2 pcmdOptions, LPCWSTR pwszOption );
|
|
LONG MatchOptionEx( DWORD dwOptions, PTCMDPARSER2 pcmdOptions,
|
|
LPCWSTR pwszOption, TMATCHOPTION_INFO* pMatchInfo );
|
|
BOOL Parser1FromParser2Stub( LPCWSTR pwszOption,
|
|
LPCWSTR pwszValue,
|
|
LPVOID pData, DWORD* pdwIncrement );
|
|
BOOL ReleaseAllocatedMemory( DWORD dwOptionsCount, PTCMDPARSER2 pcmdOptions );
|
|
|
|
//
|
|
// implementation
|
|
//
|
|
|
|
__inline
|
|
LPWSTR
|
|
GetParserTempBuffer( IN DWORD dwIndexNumber,
|
|
IN LPCWSTR pwszText,
|
|
IN DWORD dwLength,
|
|
IN BOOL bNullify )
|
|
/*++
|
|
Routine Description:
|
|
|
|
since every file will need the temporary buffers -- in order to see
|
|
that their buffers wont be override with other functions, we are
|
|
creating seperate buffer space a for each file
|
|
this function will provide an access to those internal buffers and also
|
|
safe guards the file buffer boundaries
|
|
|
|
Arguments:
|
|
|
|
[ in ] dwIndexNumber - file specific index number
|
|
|
|
[ in ] pwszText - default text that needs to be copied into
|
|
temporary buffer
|
|
|
|
[ in ] dwLength - Length of the temporary buffer that is required
|
|
Ignored when pwszText is specified
|
|
|
|
[ in ] bNullify - Informs whether to clear the buffer or not
|
|
before giving the temporary buffer
|
|
|
|
Return Value:
|
|
|
|
NULL - when any failure occurs
|
|
NOTE: do not rely on GetLastError to know the reason
|
|
for the failure.
|
|
|
|
success - return memory address of the requested size
|
|
|
|
NOTE:
|
|
----
|
|
if pwszText and dwLength both are NULL, then we treat that the caller
|
|
is asking for the reference of the buffer and we return the buffer address.
|
|
In this call, there wont be any memory allocations -- if the requested index
|
|
doesn't exist, we return as failure
|
|
|
|
Also, the buffer returned by this function need not released by the caller.
|
|
While exiting from the tool, all the memory will be freed automatically by
|
|
the ReleaseGlobals functions.
|
|
|
|
--*/
|
|
{
|
|
if ( dwIndexNumber >= TEMP_CMDLINEPARSER_C_COUNT )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// check if caller is requesting existing buffer contents
|
|
if ( pwszText == NULL && dwLength == 0 && bNullify == FALSE )
|
|
{
|
|
// yes -- we need to pass the existing buffer contents
|
|
return GetInternalTemporaryBufferRef(
|
|
dwIndexNumber + INDEX_TEMP_CMDLINEPARSER_C );
|
|
}
|
|
|
|
// ...
|
|
return GetInternalTemporaryBuffer(
|
|
dwIndexNumber + INDEX_TEMP_CMDLINEPARSER_C, pwszText, dwLength, bNullify );
|
|
}
|
|
|
|
|
|
BOOL IsOption( IN LPCWSTR pwszOption )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Checks whether the passed argument starts with the option character
|
|
or not -- currently the we treat the string as option if they start with
|
|
"-" and "/" .
|
|
|
|
Arguments:
|
|
|
|
[ in ] pwszOption - string value
|
|
|
|
Return Value:
|
|
|
|
FALSE - 1. if the parameter is invalid
|
|
2. if the string doesn't start with option character
|
|
To differentiate between the case 1 and case 2 call
|
|
GetLastError() and check for ERROR_INVALID_PARAMETER.
|
|
|
|
TRUE - if the string starts with option character
|
|
|
|
--*/
|
|
{
|
|
// clear error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
// check the input value
|
|
if ( pwszOption == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// check whether the string starts with '-' or '/' character
|
|
if ( lstrlen( pwszOption ) > 1 &&
|
|
FindChar2( cwszOptionChars, pwszOption[ 0 ], TRUE, 0 ) != -1 )
|
|
{
|
|
return TRUE; // string value is an option
|
|
}
|
|
|
|
// this is not an option
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL IsValueNeeded( DWORD dwType )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Checks whether the supported data type requires argument for the
|
|
option or not.
|
|
|
|
Arguments:
|
|
|
|
[ in ] dwType - specifies one of the CP_TYPE_xxxx values
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the supported data type requires argument for the option
|
|
|
|
FALSE - if the data type passed is not supported (or) if the
|
|
option of the requested type doesn't require argument.
|
|
NOTE: Do not rely on GetLastError() for detecting the reason
|
|
for the failure.
|
|
--*/
|
|
{
|
|
switch( dwType )
|
|
{
|
|
case CP_TYPE_TEXT:
|
|
case CP_TYPE_NUMERIC:
|
|
case CP_TYPE_UNUMERIC:
|
|
case CP_TYPE_FLOAT:
|
|
case CP_TYPE_DOUBLE:
|
|
return TRUE;
|
|
|
|
case CP_TYPE_DATE:
|
|
case CP_TYPE_TIME:
|
|
case CP_TYPE_DATETIME:
|
|
return FALSE;
|
|
|
|
case CP_TYPE_BOOLEAN:
|
|
return FALSE;
|
|
|
|
case CP_TYPE_CUSTOM:
|
|
// actually -- we dont know -- but for now, simply say yes
|
|
return TRUE;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
LPCWSTR PrepareUsageHelperText( LPCWSTR pwszOption )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Extracts tool name from the executable module's version resource
|
|
and prepares usage help text -- for ex. if the tool name is
|
|
eventcreate.exe, this function will generate the text as
|
|
|
|
Type "EVENTCREATE /?" for usage.
|
|
|
|
Since some tools requires option to be displayed along with the
|
|
tool name, this function accepts a parameter which specifies
|
|
that extra option -- if that extra option is present the message looks
|
|
like:
|
|
|
|
Type "SCHTASKS /CREATE /?" for usage.
|
|
|
|
If the message need not have the extra option information,
|
|
the caller just needs to pass NULL as parameter to this function.
|
|
|
|
Arguments:
|
|
|
|
[ in ] pwszOption - option that needs to be shown along with
|
|
the error text. If option need not be shown,
|
|
pass NULL for this argument.
|
|
|
|
Return Value:
|
|
|
|
NULL - this will be returned when anything goes wrong. Use
|
|
GetLastError() to know what went wrong
|
|
|
|
on success - formatted usage error text will be returned
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
DWORD dw = 0;
|
|
UINT dwSize = 0;
|
|
UINT dwTranslateSize = 0;
|
|
LPWSTR pwszTemp = NULL;
|
|
LPWSTR pwszBuffer = NULL;
|
|
LPWSTR pwszUtilityName = NULL;
|
|
LPVOID pVersionInfo = NULL;
|
|
LPWSTR pwszExeName = NULL;
|
|
PTTRANSLATE pTranslate = NULL;
|
|
|
|
// clear last error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
//
|
|
// try to get the current running module name
|
|
//
|
|
// we dont know whether GetModuleFileName will terminate
|
|
// the module name or not -- also, if the length of the buffer is not
|
|
// sufficient, GetModuleFileName will truncate the file name -- keeping
|
|
// all these scenarios in mind, we will loop in the GetModuleFileName
|
|
// until we make sure that we have the complete the executable name
|
|
// which is also null terminated
|
|
|
|
// init
|
|
dw = 0;
|
|
dwSize = _MAX_PATH;
|
|
|
|
// ...
|
|
|
|
do
|
|
{
|
|
// get the buffer
|
|
dwSize += (dw == 0) ? 0 : _MAX_PATH;
|
|
pwszExeName = GetParserTempBuffer( 0, NULL, dwSize, TRUE );
|
|
if ( pwszExeName == NULL )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
return NULL;
|
|
}
|
|
|
|
// get the module name
|
|
dw = GetModuleFileName( NULL, pwszExeName, dwSize );
|
|
if ( dw == 0 )
|
|
{
|
|
return NULL;
|
|
}
|
|
} while (dw >= dwSize - 1);
|
|
|
|
// get the version information size
|
|
dwSize = GetFileVersionInfoSize( pwszExeName, 0 );
|
|
if ( dwSize == 0 )
|
|
{
|
|
// tool might have encountered error (or)
|
|
// tool doesn't have version information
|
|
// but version information is mandatory for us
|
|
// so, just exit
|
|
if ( GetLastError() == NO_ERROR )
|
|
{
|
|
INVALID_PARAMETER();
|
|
}
|
|
|
|
// ...
|
|
return NULL;
|
|
}
|
|
|
|
// allocate memory for the version resource
|
|
// take some 10 bytes extra -- for safety purposes
|
|
dwSize += 10;
|
|
pVersionInfo = AllocateMemory( dwSize );
|
|
if ( pVersionInfo == NULL )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// now get the version information
|
|
if ( GetFileVersionInfo( pwszExeName, 0,
|
|
dwSize, pVersionInfo ) == FALSE )
|
|
{
|
|
FreeMemory( &pVersionInfo );
|
|
return NULL;
|
|
}
|
|
|
|
// get the translation info
|
|
if ( VerQueryValue( pVersionInfo,
|
|
L"\\VarFileInfo\\Translation",
|
|
(LPVOID*) &pTranslate, &dwTranslateSize ) == FALSE )
|
|
{
|
|
FreeMemory( &pVersionInfo );
|
|
return NULL;
|
|
}
|
|
|
|
// get the buffer to store the translation array format string
|
|
pwszBuffer = GetParserTempBuffer( 0, NULL, 64, TRUE );
|
|
|
|
// try to get the internal name of the tool for each language and code page.
|
|
pwszUtilityName = NULL;
|
|
for( dw = 0; dw < ( dwTranslateSize / sizeof( TTRANSLATE ) ); dw++ )
|
|
{
|
|
// prepare the format string to get the localized the version info
|
|
StringCchPrintfW( pwszBuffer, 64,
|
|
L"\\StringFileInfo\\%04x%04x\\InternalName",
|
|
pTranslate[ dw ].wLanguage, pTranslate[ dw ].wCodePage );
|
|
|
|
// retrieve file description for language and code page "i".
|
|
if ( VerQueryValue( pVersionInfo, pwszBuffer,
|
|
(LPVOID*) &pwszUtilityName, &dwSize ) == FALSE )
|
|
{
|
|
// we cannot decide the failure based on the result of this
|
|
// function failure -- we will decide about this
|
|
// after terminating from the 'for' loop
|
|
// for now, make the pwszExeName to NULL -- this will
|
|
// enable us to decide the result
|
|
pwszUtilityName = NULL;
|
|
}
|
|
else
|
|
{
|
|
// successfully retrieved the internal name
|
|
break;
|
|
}
|
|
}
|
|
|
|
// check whether we got the executable name or not -- if not, error
|
|
if ( pwszUtilityName == NULL )
|
|
{
|
|
FreeMemory( &pVersionInfo );
|
|
return NULL;
|
|
}
|
|
|
|
// check whether filename has .EXE as extension or not
|
|
// also the file name should be more than 4 characters (including extension)
|
|
if ( StringLength( pwszUtilityName, 0 ) <= 4 )
|
|
{
|
|
// some thing wrong -- version resource should include the internal name
|
|
FreeMemory( &pVersionInfo );
|
|
UNEXPECTED_ERROR();
|
|
return NULL;
|
|
}
|
|
else if ( FindString2( pwszUtilityName, L".EXE", TRUE, 0 ) != -1 )
|
|
{
|
|
// now put null character -- this is to trim the extension
|
|
pwszUtilityName[ lstrlen( pwszUtilityName ) - lstrlen( L".EXE" ) ] = cwchNullChar;
|
|
}
|
|
|
|
// determine the size we need for
|
|
if ( pwszOption != NULL )
|
|
{
|
|
// "length of utility name + 1 (space) + length of option" + 10 buffer (for safety)
|
|
dwSize = lstrlen( pwszUtilityName ) + lstrlen( pwszOption ) + 11;
|
|
|
|
// get the temporary buffer for that
|
|
if ( (pwszTemp = GetParserTempBuffer( 0, NULL, dwSize, TRUE )) == NULL )
|
|
{
|
|
FreeMemory( &pVersionInfo );
|
|
OUT_OF_MEMORY();
|
|
return NULL;
|
|
}
|
|
|
|
// ...
|
|
StringCchPrintfW( pwszTemp, dwSize, L"%s %s", pwszUtilityName, pwszOption );
|
|
|
|
// now remap the utility name pointer to the temp pointer
|
|
pwszUtilityName = pwszTemp;
|
|
}
|
|
else
|
|
{
|
|
// get the temporary buffer with this utilty name
|
|
if ( (pwszTemp = GetParserTempBuffer( 0, pwszUtilityName, 0, FALSE )) == NULL )
|
|
{
|
|
FreeMemory( &pVersionInfo );
|
|
OUT_OF_MEMORY();
|
|
return NULL;
|
|
}
|
|
|
|
// now remap the utility name pointer to the temp pointer
|
|
pwszUtilityName = pwszTemp;
|
|
}
|
|
|
|
// convert the utility name into uppercase
|
|
CharUpper( pwszUtilityName );
|
|
|
|
// get the temporary buffer
|
|
// NOTE: we will restrict this to 80 characters only -- this itself is
|
|
// too high memory for this simple text string
|
|
pwszBuffer = GetParserTempBuffer( INDEX_TEMP_USAGEHELPER, NULL, 80, TRUE );
|
|
if ( pwszBuffer == NULL )
|
|
{
|
|
FreeMemory( &pVersionInfo );
|
|
OUT_OF_MEMORY();
|
|
return FALSE;
|
|
}
|
|
|
|
// prepare the text now
|
|
// NOTE: look -- we are passing 79 only in _snwprintf
|
|
StringCchPrintfW( pwszBuffer, 80, ERROR_USAGEHELPER, pwszUtilityName );
|
|
|
|
// releast the memory allocated for version information
|
|
FreeMemory( &pVersionInfo );
|
|
|
|
// return the text
|
|
return pwszBuffer;
|
|
}
|
|
|
|
|
|
LPCWSTR ExtractMainOption( LPCWSTR pwszOptions, DWORD dwReserved )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Our command line parser can handle multiple names for the single option.
|
|
But while displaying error messages, it will be weird if we display all
|
|
those options when some error occured. To eliminate that, this function
|
|
will identify how many options are present in the given options list and
|
|
if it finds multiple options, it will extract the first option in the list
|
|
and returns to the caller otherwise if it finds only one argument, this
|
|
function will just return the option as it is.
|
|
|
|
Arguments:
|
|
|
|
[ in ] pwszOptions - List of options seperated by "|" character
|
|
|
|
[ in ] dwReserved - reserved for future use
|
|
|
|
Return Value:
|
|
|
|
NULL - on failure. Call GetLastError() function to know the
|
|
cause for the failure.
|
|
|
|
on success - the first option the list of supplied options will be
|
|
returned. If there is only one option, then the same will
|
|
be returned.
|
|
--*/
|
|
{
|
|
// local variables
|
|
LONG lIndex = 0;
|
|
LPWSTR pwszBuffer = NULL;
|
|
|
|
// clear last error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
// check the input
|
|
if ( pwszOptions == NULL || dwReserved != 0 )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return NULL;
|
|
}
|
|
|
|
// search for the option seperator
|
|
lIndex = FindChar2( pwszOptions, L'|', TRUE, 0 );
|
|
if ( lIndex == -1 )
|
|
{
|
|
// there are no multiple options
|
|
CLEAR_LAST_ERROR();
|
|
lIndex = StringLength( pwszOptions, 0 );
|
|
}
|
|
|
|
// get the temporary buffer
|
|
// NOTE: get the buffer with more characters to fit in
|
|
pwszBuffer = GetParserTempBuffer( INDEX_TEMP_MAINOPTION, NULL, lIndex + 5, TRUE );
|
|
if ( pwszBuffer == NULL )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
return NULL;
|
|
}
|
|
|
|
// now extract the main option
|
|
// NOTE: observe the (lIndex + 2) in the StringConcat function call
|
|
// that plays the trick of extracting the main option
|
|
StringCopy( pwszBuffer, L"/", lIndex + 1 );
|
|
StringConcat( pwszBuffer, pwszOptions, lIndex + 2 );
|
|
|
|
// return
|
|
return pwszBuffer;
|
|
}
|
|
|
|
|
|
BOOL VerifyParserOptions( LONG* plDefaultIndex,
|
|
DWORD dwCount,
|
|
PTCMDPARSER2 pcmdOptions )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Checks the validity of the parsing instructions passed by the caller.
|
|
|
|
Arguments:
|
|
|
|
[ out ] plDefaultIndex - Updates the variable with default option
|
|
index.
|
|
|
|
[ in ] dwCount - Specifies the count of parser structures
|
|
passed to this function.
|
|
|
|
[ in ] pcmdoptions - array of parser structures
|
|
|
|
Return Value:
|
|
|
|
TRUE - if all the data passed to this function is valid
|
|
|
|
FALSE - if any of the data is not correct. This also sets the last
|
|
error ERROR_INVALID_PARAMETER.
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
DWORD dw = 0;
|
|
DWORD64 dwFlags = 0;
|
|
BOOL bUsage = FALSE;
|
|
PTCMDPARSER2 pcmdparser = NULL;
|
|
|
|
// clear last error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
// check the input
|
|
if ( dwCount != 0 && pcmdOptions == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// ...
|
|
if ( plDefaultIndex == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
*plDefaultIndex = -1;
|
|
}
|
|
|
|
// loop thru each option data and verify
|
|
for( dw = 0; dw < dwCount; dw++ )
|
|
{
|
|
pcmdparser = pcmdOptions + dw;
|
|
|
|
// safety check
|
|
if ( pcmdparser == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
return FALSE;
|
|
}
|
|
|
|
// verify the signature
|
|
if ( StringCompareA( pcmdparser->szSignature,
|
|
cszParserSignature, TRUE, 0 ) != 0 )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// ...
|
|
dwFlags = pcmdparser->dwFlags;
|
|
|
|
if ( pcmdparser->dwReserved != 0 ||
|
|
pcmdparser->pReserved1 != NULL ||
|
|
pcmdparser->pReserved2 != NULL ||
|
|
pcmdparser->pReserved3 != NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// check the contents of pwszOptions
|
|
// this can be NULL (or) empty only when dwFlags has CP2_DEFAULT
|
|
if ( ((dwFlags & CP2_DEFAULT) == 0) &&
|
|
(pcmdparser->pwszOptions == NULL ||
|
|
lstrlen( pcmdparser->pwszOptions ) == 0) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// usage flag can be specified only for boolean types
|
|
if ( (dwFlags & CP2_USAGE) && pcmdparser->dwType != CP_TYPE_BOOLEAN )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// CP2_USAGE can be specified only once
|
|
if ( dwFlags & CP2_USAGE )
|
|
{
|
|
if ( bUsage == TRUE )
|
|
{
|
|
// help switch can be specified only once
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
bUsage = TRUE;
|
|
}
|
|
}
|
|
|
|
// CP2_DEFAULT can be specified only once
|
|
if ( dwFlags & CP2_DEFAULT )
|
|
{
|
|
if ( *plDefaultIndex != -1 )
|
|
{
|
|
// default switch can be specified only once
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
*plDefaultIndex = (LONG) dw;
|
|
}
|
|
}
|
|
|
|
// CP2_VALUE_OPTIONAL is not allowed along with
|
|
// CP2_MODE_VALUES
|
|
// if ( (dwFlags & CP2_VALUE_OPTIONAL) && (dwFlags & CP2_MODE_VALUES) )
|
|
// {
|
|
// INVALID_PARAMETER();
|
|
// return FALSE;
|
|
// }
|
|
|
|
// CP2_USAGE and CP2_DEFAULT cannot be specified on the same index
|
|
if ( (dwFlags & CP2_USAGE) && (dwFlags & CP2_DEFAULT) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// check the data type
|
|
switch( pcmdparser->dwType )
|
|
{
|
|
case CP_TYPE_TEXT:
|
|
{
|
|
if ( dwFlags & CP2_ALLOCMEMORY )
|
|
{
|
|
// mode should not be any array
|
|
if ( (dwFlags & CP2_MODE_ARRAY) || pcmdparser->pValue != NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// check the length attribute
|
|
if ( pcmdparser->dwLength != 0 && pcmdparser->dwLength < 2 )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( pcmdparser->pValue == NULL )
|
|
{
|
|
// invalid memory reference
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
if ( dwFlags & CP2_MODE_ARRAY )
|
|
{
|
|
if ( IsValidArray( *((PTARRAY) pcmdparser->pValue) )== FALSE )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( (dwFlags & CP2_MODE_VALUES) &&
|
|
(pcmdparser->pwszValues == NULL) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (dwFlags & CP2_MODE_ARRAY) == 0 )
|
|
{
|
|
if ( pcmdparser->dwCount != 1 ||
|
|
(dwFlags & CP2_VALUE_NODUPLICATES) ||
|
|
( ((dwFlags & CP2_ALLOCMEMORY) == 0) &&
|
|
pcmdparser->dwLength < 2 ) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// ...
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_NUMERIC:
|
|
case CP_TYPE_UNUMERIC:
|
|
case CP_TYPE_FLOAT:
|
|
case CP_TYPE_DOUBLE:
|
|
{
|
|
// currently not implemented
|
|
if ( dwFlags & CP2_MODE_VALUES )
|
|
{
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (dwFlags & CP2_ALLOCMEMORY) ||
|
|
(dwFlags & CP2_VALUE_TRIMINPUT) ||
|
|
(dwFlags & CP2_VALUE_NONULL) )
|
|
{
|
|
// memory allocation will not be accepted for
|
|
// these data types
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// check the pointer
|
|
if ( pcmdparser->pValue == NULL )
|
|
{
|
|
// invalid memory reference
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// if the value acceptance mode is array, check that
|
|
if ( dwFlags & CP2_MODE_ARRAY )
|
|
{
|
|
if ( IsValidArray( *((PTARRAY) pcmdparser->pValue) ) == FALSE )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if ( (pcmdparser->dwCount > 1) ||
|
|
(dwFlags & CP2_VALUE_NODUPLICATES) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (dwFlags & CP2_MODE_VALUES) &&
|
|
pcmdparser->pwszValues == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// ...
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_CUSTOM:
|
|
{
|
|
if ( pcmdparser->pFunction == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// if the custom function data is NULL, assign the current
|
|
// object itself to it
|
|
if ( pcmdparser->pFunctionData == NULL )
|
|
{
|
|
pcmdparser->pFunctionData = pcmdparser;
|
|
}
|
|
|
|
// ...
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_DATE:
|
|
case CP_TYPE_TIME:
|
|
case CP_TYPE_DATETIME:
|
|
// currently not supported
|
|
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
|
|
return FALSE;
|
|
|
|
case CP_TYPE_BOOLEAN:
|
|
{
|
|
// no flags are allowed for this type
|
|
if ( (dwFlags & CP2_MODE_MASK) || (dwFlags & CP2_VALUE_MASK) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// CP2_USAGE and CP2_CASESENSITIVE
|
|
// are the only two flags that can be associated with this
|
|
// type of options
|
|
if ( dwFlags & ( ~(CP2_USAGE | CP2_CASESENSITIVE) ) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// ...
|
|
break;
|
|
}
|
|
|
|
default:
|
|
INVALID_PARAMETER();
|
|
return FALSE;
|
|
}
|
|
|
|
// init the actuals to 0
|
|
pcmdparser->dwActuals = 0;
|
|
}
|
|
|
|
// everything went fine -- success
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL ParseAndSaveOptionValue( LPCWSTR pwszOption,
|
|
LPCWSTR pwszValue,
|
|
TPARSERSAVE_DATA* pSaveData )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Processes the value and saves the data back into the memory location
|
|
passed by the caller via PARSER structure.
|
|
|
|
Arguments:
|
|
|
|
[ in ] pwszOption - option specified at the command prompt
|
|
|
|
[ in ] pwszValue - value that needs to be assciated with option.
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
LONG lIndex = 0, lValue = 0;
|
|
DWORD dwLength = 0, dwValue = 0;
|
|
float fValue = 0.0f;
|
|
double dblValue = 0.0f;
|
|
BOOL bSigned = FALSE;
|
|
DWORD64 dwFlags = 0;
|
|
LPVOID pvData = NULL;
|
|
LPWSTR pwszBuffer = NULL;
|
|
LPCWSTR pwszOptionValues = NULL;
|
|
LPCWSTR pwszUsageHelper = NULL;
|
|
PTCMDPARSER2 pcmdparser = NULL;
|
|
|
|
// clear last error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
// check the input
|
|
if ( pSaveData == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// extract the structure data into local variables
|
|
pcmdparser = pSaveData->pcmdparser;
|
|
pwszUsageHelper = pSaveData->pwszUsageHelper;
|
|
|
|
if ( pcmdparser == NULL || pwszUsageHelper == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// ...
|
|
pvData = pcmdparser->pValue;
|
|
dwFlags = pcmdparser->dwFlags;
|
|
dwLength = pcmdparser->dwLength;
|
|
pwszOptionValues = pcmdparser->pwszValues;
|
|
|
|
// except for the boolean types, for all the other types,
|
|
// the value for an option is mandatory except when optional flag is
|
|
// explicitly specified
|
|
if ( pcmdparser->dwType != CP_TYPE_BOOLEAN )
|
|
{
|
|
if ( pwszValue == NULL &&
|
|
(dwFlags & CP2_VALUE_OPTIONAL) == 0 )
|
|
{
|
|
REASON_VALUE_EXPECTED( pwszOption, pwszUsageHelper );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// pwszOption can be NULL only if dwFlags contains CP2_DEFAULT
|
|
if ( pwszOption == NULL && ((dwFlags & CP2_DEFAULT) == 0) )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// determine whether we can make use of the friendly name
|
|
if ( pwszOption == NULL ||
|
|
(pcmdparser->pwszFriendlyName != NULL &&
|
|
pcmdparser->dwType != CP_TYPE_CUSTOM) )
|
|
{
|
|
pwszOption = pcmdparser->pwszFriendlyName;
|
|
}
|
|
|
|
switch( pcmdparser->dwType )
|
|
{
|
|
case CP_TYPE_TEXT:
|
|
{
|
|
// check whether we need to trim the string
|
|
if ( pwszValue != NULL &&
|
|
(dwFlags & (CP2_MODE_VALUES | CP2_VALUE_TRIMINPUT)) )
|
|
{
|
|
if ( (pwszBuffer = GetParserTempBuffer(
|
|
INDEX_TEMP_SAVEDATA,
|
|
pwszValue, 0, FALSE )) == NULL )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// trim the contents
|
|
pwszValue = TrimString2( pwszBuffer, L" \t", TRIM_ALL );
|
|
if ( GetLastError() != NO_ERROR )
|
|
{
|
|
// unexpected error occured
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// check whether the value is in the allowed list -- if needed
|
|
if ( dwFlags & CP2_MODE_VALUES )
|
|
{
|
|
// check the value for NULL
|
|
if ( pwszValue == NULL )
|
|
{
|
|
// CP2_MODE_VALUES takes the precedence over CP2_VALUE_OPTIONAL
|
|
// INVALID_SYNTAX();
|
|
// SaveLastError();
|
|
return TRUE;
|
|
}
|
|
|
|
if ( InString( pwszValue, pwszOptionValues, TRUE ) == FALSE )
|
|
{
|
|
REASON_VALUE_NOTINLIST( pwszValue, pwszOption, pwszUsageHelper );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// check the pwszValue argument -- if it is null,
|
|
// just return as success -- this is 'coz the current argument
|
|
// has value optional flag
|
|
if ( pwszValue == NULL )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
// check for non-null (if requested)
|
|
if ( (dwFlags & CP2_VALUE_NONULL) && lstrlen( pwszValue ) == 0 )
|
|
{
|
|
REASON_NULLVALUE( pwszOption, pwszUsageHelper );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
|
|
// check the mode of the input
|
|
if ( dwFlags & CP2_MODE_ARRAY )
|
|
{
|
|
// if the mode is array, add to the array
|
|
// but before adding check whether duplicates
|
|
// has to be eliminated or not
|
|
lIndex = -1;
|
|
if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
|
|
{
|
|
// check whether current value already exists in the list or not
|
|
lIndex =
|
|
DynArrayFindString(
|
|
*((PTARRAY) pvData), pwszValue, TRUE, 0 );
|
|
}
|
|
|
|
// now add the value to array only if the item doesn't exist in list
|
|
if ( lIndex == -1 )
|
|
{
|
|
if ( DynArrayAppendString( *((PTARRAY) pvData),
|
|
pwszValue, 0 ) == -1 )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// do the length check
|
|
// NOTE: user should specify the value which is one character
|
|
// less than the length allowed
|
|
if ( dwLength != 0 && lstrlen( pwszValue ) >= (LONG) dwLength )
|
|
{
|
|
REASON_LENGTH_EXCEEDED( pwszOption, dwLength - 1 );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
|
|
// allocate memory if requested
|
|
if ( dwFlags & CP2_ALLOCMEMORY )
|
|
{
|
|
dwLength = lstrlen( pwszValue ) + 1;
|
|
pvData = AllocateMemory( dwLength * sizeof( WCHAR ) );
|
|
if ( pvData == NULL )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// ...
|
|
pcmdparser->pValue = pvData;
|
|
}
|
|
|
|
// else just do copy
|
|
StringCopy( ( LPWSTR ) pvData, pwszValue, dwLength );
|
|
}
|
|
|
|
// break from the switch ... case
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_NUMERIC:
|
|
case CP_TYPE_UNUMERIC:
|
|
{
|
|
// ...
|
|
bSigned = (pcmdparser->dwType == CP_TYPE_NUMERIC);
|
|
|
|
// check the pwszValue argument -- if it is null,
|
|
// just return as success -- this is 'coz the current argument
|
|
// has value optional flag
|
|
if ( pwszValue == NULL )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// check whether the value is numeric or not
|
|
if ( StringLength(pwszValue,0) == 0 || IsNumeric( pwszValue, 10, bSigned ) == FALSE )
|
|
{
|
|
//
|
|
// error ... non numeric value
|
|
// but, this option might have an optional value
|
|
// check that flag
|
|
if ( dwFlags & CP2_VALUE_OPTIONAL )
|
|
{
|
|
// yes -- this option takes an optional value
|
|
// so, the next one could be possibly a default
|
|
// option -- we need to confirm this -- 'coz this is
|
|
// very very rare occassion -- but we still need to handle it
|
|
if ( pSaveData->lDefaultIndex != -1 )
|
|
{
|
|
// yes -- the value might be a default argument
|
|
// update the increment accordingly
|
|
pSaveData->dwIncrement = 1;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// all the tests failed -- so
|
|
// set the reason for the failure and return
|
|
REASON_INVALID_NUMERIC( pwszOption, pwszUsageHelper );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
|
|
// convert the values
|
|
if ( bSigned == TRUE )
|
|
{
|
|
lValue = AsLong( pwszValue, 10 );
|
|
}
|
|
else
|
|
{
|
|
dwValue = AsLong( pwszValue, 10 );
|
|
}
|
|
|
|
// ***************************************************
|
|
// *** NEED TO ADD THE RANGE CHECKING LOGIC HERE ***
|
|
// ***************************************************
|
|
|
|
// check the mode of the input
|
|
if ( dwFlags & CP2_MODE_ARRAY )
|
|
{
|
|
// if the mode is array, add to the array
|
|
// but before adding check whether duplicates
|
|
// has to be eliminated or not
|
|
lIndex = -1;
|
|
if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
|
|
{
|
|
// check whether current value already exists in the list or not
|
|
if ( bSigned == TRUE )
|
|
{
|
|
lIndex = DynArrayFindLong( *((PTARRAY) pvData), lValue );
|
|
}
|
|
else
|
|
{
|
|
lIndex = DynArrayFindDWORD( *((PTARRAY) pvData), dwValue );
|
|
}
|
|
}
|
|
|
|
// now add the value to array only if the item doesn't exist in list
|
|
if ( lIndex == -1 )
|
|
{
|
|
if ( bSigned == TRUE )
|
|
{
|
|
lIndex = DynArrayAppendLong( *((PTARRAY) pvData), lValue );
|
|
}
|
|
else
|
|
{
|
|
lIndex = DynArrayAppendDWORD( *((PTARRAY) pvData), dwValue );
|
|
}
|
|
|
|
if ( lIndex == -1 )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// else just assign
|
|
if ( bSigned == TRUE )
|
|
{
|
|
*( ( LONG* ) pvData ) = lValue;
|
|
}
|
|
else
|
|
{
|
|
*( ( DWORD* ) pvData ) = dwValue;
|
|
}
|
|
}
|
|
|
|
// break from the switch ... case
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_FLOAT:
|
|
case CP_TYPE_DOUBLE:
|
|
{
|
|
// check the pwszValue argument -- if it is null,
|
|
// just return as success -- this is 'coz the current argument
|
|
// has value optional flag
|
|
if ( pwszValue == NULL )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// check whether the value is numeric or not
|
|
if ( IsFloatingPoint( pwszValue ) == FALSE )
|
|
{
|
|
//
|
|
// error ... non floating point value
|
|
// but, this option might have an optional value
|
|
// check that flag
|
|
if ( dwFlags & CP2_VALUE_OPTIONAL )
|
|
{
|
|
// yes -- this option takes an optional value
|
|
// so, the next one could be possibly a default
|
|
// option -- we need to confirm this -- 'coz this is
|
|
// very very rare occassion -- but we still need to handle it
|
|
if ( pSaveData->lDefaultIndex != -1 )
|
|
{
|
|
// yes -- the value might be a default argument
|
|
// update the increment accordingly
|
|
pSaveData->dwIncrement = 1;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
// all the tests failed -- so
|
|
// set the reason for the failure and return
|
|
REASON_INVALID_FLOAT( pwszOption, pwszUsageHelper );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
|
|
// convert the values
|
|
if ( pcmdparser->dwType == CP_TYPE_FLOAT )
|
|
{
|
|
fValue = (float) AsFloat( pwszValue );
|
|
}
|
|
else
|
|
{
|
|
dblValue = AsFloat( pwszValue );
|
|
}
|
|
|
|
// ***************************************************
|
|
// *** NEED TO ADD THE RANGE CHECKING LOGIC HERE ***
|
|
// ***************************************************
|
|
|
|
// check the mode of the input
|
|
if ( dwFlags & CP2_MODE_ARRAY )
|
|
{
|
|
// if the mode is array, add to the array
|
|
// but before adding check whether duplicates
|
|
// has to be eliminated or not
|
|
lIndex = -1;
|
|
if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
|
|
{
|
|
// check whether current value already exists in the list or not
|
|
if ( pcmdparser->dwType == CP_TYPE_FLOAT )
|
|
{
|
|
lIndex = DynArrayFindFloat( *((PTARRAY) pvData), fValue );
|
|
}
|
|
else
|
|
{
|
|
lIndex = DynArrayFindDouble( *((PTARRAY) pvData), dblValue );
|
|
}
|
|
}
|
|
|
|
// now add the value to array only if the item doesn't exist in list
|
|
if ( lIndex == -1 )
|
|
{
|
|
if ( pcmdparser->dwType == CP_TYPE_FLOAT )
|
|
{
|
|
lIndex = DynArrayAppendFloat( *((PTARRAY) pvData), fValue );
|
|
}
|
|
else
|
|
{
|
|
lIndex = DynArrayAppendDouble( *((PTARRAY) pvData), dblValue );
|
|
}
|
|
|
|
if ( lIndex == -1 )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// else just assign
|
|
if ( pcmdparser->dwType == CP_TYPE_FLOAT )
|
|
{
|
|
*( ( float* ) pvData ) = fValue;
|
|
}
|
|
else
|
|
{
|
|
*( ( double* ) pvData ) = dblValue;
|
|
}
|
|
}
|
|
|
|
// break from the switch ... case
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_CUSTOM:
|
|
{
|
|
// call the custom function
|
|
// and result itself is return value of this function
|
|
return ( *pcmdparser->pFunction)( pwszOption,
|
|
pwszValue, pcmdparser->pFunctionData, &pSaveData->dwIncrement );
|
|
|
|
// ...
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_DATE:
|
|
case CP_TYPE_TIME:
|
|
case CP_TYPE_DATETIME:
|
|
{
|
|
// break from the switch ... case
|
|
break;
|
|
}
|
|
|
|
case CP_TYPE_BOOLEAN:
|
|
{
|
|
// it is compulsory that the pwszValue should point to NULL
|
|
if ( pwszValue != NULL )
|
|
{
|
|
REASON_VALUENOTALLOWED( pwszOption, pwszUsageHelper );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
|
|
*( ( BOOL* ) pvData ) = TRUE;
|
|
|
|
// break from the switch ... case
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// nothing -- but should be failure
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// everything went fine -- success
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
LONG MatchOption( DWORD dwOptions,
|
|
PTCMDPARSER2 pcmdOptions,
|
|
LPCWSTR pwszOption )
|
|
/*++
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
DWORD dw = 0;
|
|
BOOL bIgnoreCase = FALSE;
|
|
PTCMDPARSER2 pcmdparser = NULL;
|
|
|
|
// clear last error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
// check the input value
|
|
if ( dwOptions == 0 || pcmdOptions == NULL || pwszOption == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return -1;
|
|
}
|
|
|
|
// check whether the passed argument is an option or not.
|
|
// option : starts with '-' or '/'
|
|
if ( IsOption( pwszOption ) == FALSE )
|
|
{
|
|
SetLastError( ERROR_NOT_FOUND );
|
|
return -1;
|
|
}
|
|
|
|
// parse thru the list of options and return the appropriate option id
|
|
for( dw = 0; dw < dwOptions; dw++ )
|
|
{
|
|
pcmdparser = pcmdOptions + dw;
|
|
|
|
// safety check
|
|
if ( pcmdparser == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// determine the case-sensitivity choice
|
|
bIgnoreCase = (pcmdparser->dwFlags & CP2_CASESENSITIVE) ? FALSE : TRUE;
|
|
|
|
// ...
|
|
if ( pcmdparser->pwszOptions != NULL &&
|
|
lstrlen( pcmdparser->pwszOptions ) > 0 )
|
|
{
|
|
// find the appropriate option entry in parser list
|
|
if ( InString( pwszOption + 1,
|
|
pcmdparser->pwszOptions, bIgnoreCase) == TRUE )
|
|
{
|
|
return dw; // option matched
|
|
}
|
|
}
|
|
}
|
|
|
|
// here we know that option is not found
|
|
SetLastError( ERROR_NOT_FOUND );
|
|
return -1;
|
|
}
|
|
|
|
|
|
LONG MatchOptionEx( DWORD dwOptions, PTCMDPARSER2 pcmdOptions,
|
|
LPCWSTR pwszOption, TMATCHOPTION_INFO* pMatchInfo )
|
|
/*++
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
LONG lValueLength = 0;
|
|
LONG lOptionLength = 0;
|
|
|
|
// clear the last error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
// check input
|
|
if ( dwOptions == 0 || pcmdOptions == NULL ||
|
|
pwszOption == NULL || pMatchInfo == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
return -1;
|
|
}
|
|
|
|
// init
|
|
pMatchInfo->pwszOption = NULL;
|
|
pMatchInfo->pwszValue = NULL;
|
|
|
|
// search for ':' seperator
|
|
lOptionLength = FindChar2( pwszOption, L':', TRUE, 0 );
|
|
if ( lOptionLength == -1 )
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// determine the length of value argument
|
|
lValueLength = lstrlen( pwszOption ) - lOptionLength - 1;
|
|
|
|
//
|
|
// get the buffers for option and value
|
|
// ( while taking memory, add some buffer to the required length )
|
|
|
|
// option
|
|
pMatchInfo->pwszOption = GetParserTempBuffer(
|
|
INDEX_TEMP_SPLITOPTION, NULL, lOptionLength + 5, TRUE );
|
|
if ( pMatchInfo->pwszOption == NULL )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
return -1;
|
|
}
|
|
|
|
// value
|
|
pMatchInfo->pwszValue = GetParserTempBuffer(
|
|
INDEX_TEMP_SPLITVALUE, NULL, lValueLength + 5, TRUE );
|
|
if ( pMatchInfo->pwszValue == NULL )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
return -1;
|
|
}
|
|
|
|
// copy the values into appropriate buffers (+1 for null character)
|
|
StringCopy( pMatchInfo->pwszOption, pwszOption, lOptionLength + 1 );
|
|
|
|
if ( lValueLength != 0 )
|
|
{
|
|
StringCopy( pMatchInfo->pwszValue,
|
|
pwszOption + lOptionLength + 1, lValueLength + 1 );
|
|
}
|
|
|
|
// search for the match and return the same result
|
|
return MatchOption( dwOptions, pcmdOptions, pMatchInfo->pwszOption );
|
|
}
|
|
|
|
|
|
BOOL Parser1FromParser2Stub( LPCWSTR pwszOption,
|
|
LPCWSTR pwszValue,
|
|
LPVOID pData, DWORD* pdwIncrement )
|
|
/*++
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
LPCWSTR pwszUsageText = NULL;
|
|
PTCMDPARSER pcmdparser = NULL;
|
|
|
|
// check the input
|
|
// NOTE: we dont care about the pwszOption and pwszValue
|
|
if ( pData == NULL || pdwIncrement == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// both pwszOption and pwszValue cannot be NULL at the same time
|
|
if ( pwszOption == NULL && pwszValue == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// extract the "version 1" structure
|
|
pcmdparser = (PTCMDPARSER) pData;
|
|
|
|
// in "version 1" of command line parsing, pwszValue and pwszOption
|
|
// should not be NULL -- that means, those both should point to some data
|
|
// but "version 2", pwszOption and pwszValue can be NULL -- but whether
|
|
// they can be NULL or not depends on the data type and the requirement
|
|
// so, in order to port the old code successfully, we need to do the
|
|
// necessary substitutions
|
|
|
|
// check the option
|
|
if ( pwszOption == NULL )
|
|
{
|
|
// this means the value is of CP_DEFAULT -- check that
|
|
if ( (pcmdparser->dwFlags & CP_DEFAULT) == 0 )
|
|
{
|
|
// this case -- since the option is not marked as default
|
|
// the option should not be NULL
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// let value and option point to the same contents(address)
|
|
// this is how the "version 1" used to treat
|
|
pwszOption = pwszValue;
|
|
}
|
|
|
|
// now check the value
|
|
else if ( pwszValue == NULL )
|
|
{
|
|
// in "version 1" the value field should never to NULL
|
|
// especially when dealing with CUSTOM types
|
|
// but to be on safe side, check whether user informed that
|
|
// the value is optional
|
|
if ( pcmdparser->dwFlags & CP_VALUE_MANDATORY )
|
|
{
|
|
// get the usage help text
|
|
pwszUsageText = GetParserTempBuffer(
|
|
INDEX_TEMP_USAGEHELPER, NULL, 0, FALSE );
|
|
if ( pwszUsageText == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// set the error
|
|
REASON_VALUE_EXPECTED( pwszOption, pwszUsageText );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// update the incrementer as 2 --
|
|
// this is default for custom function in "version 1"
|
|
*pdwIncrement = 2;
|
|
|
|
// call the custom function and return the value
|
|
return ( *pcmdparser->pFunction)( pwszOption, pwszValue, pcmdparser->pFunctionData );
|
|
}
|
|
|
|
|
|
BOOL ReleaseAllocatedMemory( DWORD dwOptionsCount,
|
|
PTCMDPARSER2 pcmdOptions )
|
|
/*++
|
|
Routine Description:
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
DWORD dw = 0;
|
|
DWORD dwLastError = 0;
|
|
LPCWSTR pwszReason = NULL;
|
|
PTCMDPARSER2 pcmdparser = NULL;
|
|
|
|
// check the input
|
|
if ( dwOptionsCount == 0 || pcmdOptions == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// save the last error and error text
|
|
dwLastError = GetLastError();
|
|
pwszReason = GetParserTempBuffer( 0, GetReason(), 0, FALSE );
|
|
if ( pwszReason == NULL )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// free the memory allocated by parser for CP2_ALLOCMEMORY
|
|
for( dw = 0; dw < dwOptionsCount; dw++ )
|
|
{
|
|
pcmdparser = pcmdOptions + dw;
|
|
if ( pcmdparser->pValue != NULL &&
|
|
(pcmdparser->dwFlags & CP2_ALLOCMEMORY) )
|
|
{
|
|
FreeMemory( &pcmdparser->pValue );
|
|
}
|
|
}
|
|
|
|
// ...
|
|
SetReason( pwszReason );
|
|
SetLastError( dwLastError );
|
|
|
|
// return success
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//
|
|
// public functions ... exposed to external world
|
|
//
|
|
|
|
|
|
BOOL DoParseParam2( DWORD dwCount,
|
|
LPCWSTR argv[],
|
|
LONG lSubOptionIndex,
|
|
DWORD dwOptionsCount,
|
|
PTCMDPARSER2 pcmdOptions,
|
|
DWORD dwReserved )
|
|
/*++
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
DWORD dw = 0;
|
|
BOOL bUsage = FALSE;
|
|
BOOL bResult = FALSE;
|
|
DWORD dwIncrement = 0;
|
|
LONG lIndex = 0;
|
|
LONG lDefaultIndex = 0;
|
|
LPCWSTR pwszValue = NULL;
|
|
LPCWSTR pwszOption = NULL;
|
|
LPCWSTR pwszUsageText = NULL;
|
|
PTCMDPARSER2 pcmdparser = NULL;
|
|
TMATCHOPTION_INFO matchoptioninfo;
|
|
TPARSERSAVE_DATA parsersavedata;
|
|
|
|
// clear the last error
|
|
CLEAR_LAST_ERROR();
|
|
|
|
// check the input value
|
|
if ( dwCount == 0 || argv == NULL ||
|
|
dwOptionsCount == 0 || pcmdOptions == NULL )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// dwReserved should be 0 ( ZERO )
|
|
if ( dwReserved != 0 )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// check for version compatibility
|
|
if ( IsWin2KOrLater() == FALSE )
|
|
{
|
|
SetReason( ERROR_OS_INCOMPATIBLE );
|
|
SetLastError( ERROR_OLD_WIN_VERSION );
|
|
return FALSE;
|
|
}
|
|
|
|
// initialize the global data structure
|
|
if ( InitGlobals() == FALSE )
|
|
{
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// utility name retrieval block -- also, prepares the usage helper text
|
|
// TYPE "<utility name> <option> -?" for usage."
|
|
if ( lSubOptionIndex != -1 )
|
|
{
|
|
// validate the sub-option index value
|
|
if ( lSubOptionIndex >= (LONG) dwOptionsCount )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// extract the main option
|
|
pwszOption = ExtractMainOption( pcmdOptions[ lSubOptionIndex ].pwszOptions, 0 );
|
|
if ( pwszOption == NULL )
|
|
{
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
// prepare the usage helper text
|
|
pwszUsageText = PrepareUsageHelperText( pwszOption );
|
|
if ( pwszUsageText == NULL )
|
|
{
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// verify the options information
|
|
if ( VerifyParserOptions( &lDefaultIndex, dwOptionsCount, pcmdOptions ) == FALSE )
|
|
{
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// Note: though the array starts at index 0 in C, the value at the array
|
|
// index 0 in a command line is the executable name ... so leave
|
|
// and parse the command line from the second parameter
|
|
// i.e; array index 1
|
|
bUsage = FALSE;
|
|
for( dw = 1; dw < dwCount; dw += dwIncrement )
|
|
{
|
|
// init
|
|
dwIncrement = 2;
|
|
pwszOption = argv[ dw ];
|
|
pwszValue = ( (dw + 1) < dwCount ) ? argv[ dw + 1 ] : NULL;
|
|
|
|
// find the appropriate the option match
|
|
lIndex = MatchOption( dwOptionsCount, pcmdOptions, pwszOption );
|
|
|
|
// check the result
|
|
if ( lIndex == -1 )
|
|
{
|
|
// value might have specified along with the option
|
|
// with ':' as seperator -- check that out
|
|
lIndex = MatchOptionEx( dwOptionsCount,
|
|
pcmdOptions, pwszOption, &matchoptioninfo );
|
|
|
|
// check the result
|
|
if ( lIndex == -1 )
|
|
{
|
|
// option is not found - now check atleast
|
|
// default option is present or not -- if it is not found, then error
|
|
if ( lDefaultIndex == -1 )
|
|
{
|
|
SetReason2( 2, ERROR_INVALID_OPTION, pwszOption, pwszUsageText );
|
|
ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
// this should be default argument
|
|
lIndex = lDefaultIndex;
|
|
|
|
// since we know that the current argument
|
|
// is a default argumen
|
|
// treat the option as value and option as NULL
|
|
pwszValue = pwszOption;
|
|
pwszOption = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// option is matched -- it is seperated with ':'
|
|
// so, extract the information from the structure
|
|
pwszOption = matchoptioninfo.pwszOption;
|
|
pwszValue = matchoptioninfo.pwszValue;
|
|
}
|
|
|
|
// since the value is in-directly specified along with the option
|
|
// (or as default) we need to process the next argument -- so, update the
|
|
// incrementer accordingly
|
|
dwIncrement = 1;
|
|
}
|
|
|
|
// ...
|
|
pcmdparser = pcmdOptions + lIndex;
|
|
|
|
// safety check
|
|
if ( pcmdparser == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// scoop into the next argument which we are assuming as
|
|
// as the value for the current -- but do this only if the
|
|
// current option type needs that
|
|
if ( pwszValue != NULL && dwIncrement == 2 )
|
|
{
|
|
if ( IsValueNeeded( pcmdparser->dwType ) == TRUE )
|
|
{
|
|
lIndex = MatchOption( dwOptionsCount, pcmdOptions, pwszValue );
|
|
if ( lIndex == -1 )
|
|
{
|
|
lIndex = MatchOptionEx( dwOptionsCount,
|
|
pcmdOptions, pwszValue, &matchoptioninfo );
|
|
}
|
|
|
|
// check the result
|
|
if ( lIndex != -1 )
|
|
{
|
|
// so, the next argument cannot be the value for this
|
|
// option -- so ...
|
|
pwszValue = NULL;
|
|
dwIncrement = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pwszValue = NULL;
|
|
dwIncrement = 1;
|
|
}
|
|
}
|
|
|
|
// update the parser data structure
|
|
parsersavedata.pcmdparser = pcmdparser;
|
|
parsersavedata.dwIncrement = dwIncrement;
|
|
parsersavedata.lDefaultIndex = lDefaultIndex;
|
|
parsersavedata.pwszUsageHelper = pwszUsageText;
|
|
|
|
// try to save the data
|
|
bResult = ParseAndSaveOptionValue( pwszOption, pwszValue, &parsersavedata );
|
|
|
|
// get the increment value -- it might have changed by the data parser
|
|
dwIncrement = parsersavedata.dwIncrement;
|
|
|
|
// now check the result
|
|
if ( bResult == FALSE )
|
|
{
|
|
// return
|
|
ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
|
|
return FALSE;
|
|
}
|
|
|
|
// check the current option repetition trigger
|
|
if ( pcmdparser->dwCount != 0 &&
|
|
pcmdparser->dwCount == pcmdparser->dwActuals )
|
|
{
|
|
REASON_OPTION_REPEATED( pwszOption,
|
|
pcmdparser->dwCount, pwszUsageText );
|
|
ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
|
|
// update the option repetition count
|
|
pcmdparser->dwActuals++;
|
|
|
|
// check if the usage option is choosed
|
|
if ( pcmdparser->dwFlags & CP2_USAGE )
|
|
{
|
|
bUsage = TRUE;
|
|
}
|
|
}
|
|
|
|
// atlast check whether the mandatory options has come or not
|
|
// NOTE: checking of mandatory options will be done only if help is not requested
|
|
if ( bUsage == FALSE )
|
|
{
|
|
for( dw = 0; dw < dwOptionsCount; dw++ )
|
|
{
|
|
// check whether the option has come or not if it is mandatory
|
|
pcmdparser = pcmdOptions + dw;
|
|
|
|
// safety check
|
|
if ( pcmdparser == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
|
|
return FALSE;
|
|
}
|
|
|
|
if ( (pcmdparser->dwFlags & CP2_MANDATORY) && pcmdparser->dwActuals == 0 )
|
|
{
|
|
//
|
|
// mandatory option not exist ... fail
|
|
|
|
// ...
|
|
pwszOption = pcmdparser->pwszOptions;
|
|
if ( pwszOption == NULL )
|
|
{
|
|
if ( (pcmdparser->dwFlags & CP2_DEFAULT) == 0 )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
pwszOption = pcmdparser->pwszFriendlyName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// check if user specified any friendly name for this option
|
|
if ( pcmdparser->pwszFriendlyName == NULL )
|
|
{
|
|
pwszOption = ExtractMainOption( pwszOption, 0 );
|
|
if ( pwszOption == NULL )
|
|
{
|
|
SaveLastError();
|
|
ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pwszOption = pcmdparser->pwszFriendlyName;
|
|
}
|
|
}
|
|
|
|
// set the reason for the failure and return
|
|
REASON_MANDATORY_OPTION_MISSING( pwszOption, pwszUsageText );
|
|
ReleaseAllocatedMemory( dwOptionsCount, pcmdOptions );
|
|
INVALID_SYNTAX();
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// parsing complete -- success
|
|
CLEAR_LAST_ERROR();
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL DoParseParam( DWORD dwCount,
|
|
LPCTSTR argv[],
|
|
DWORD dwOptionsCount,
|
|
PTCMDPARSER pcmdOptions )
|
|
/*++
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
DWORD dw = 0;
|
|
BOOL bResult = FALSE;
|
|
LONG lMainOption = -1;
|
|
DWORD dwLastError = 0;
|
|
LPCWSTR pwszReason = NULL;
|
|
PTCMDPARSER pcmdparser = NULL;
|
|
PTCMDPARSER2 pcmdparser2 = NULL;
|
|
PTCMDPARSER2 pcmdOptions2 = NULL;
|
|
|
|
// check the input
|
|
if ( dwCount == 0 || argv == NULL ||
|
|
dwOptionsCount == 0 || pcmdOptions == NULL )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// allocate new structure
|
|
pcmdOptions2 = (PTCMDPARSER2) AllocateMemory( dwOptionsCount * sizeof( TCMDPARSER2 ) );
|
|
if ( pcmdOptions2 == NULL )
|
|
{
|
|
OUT_OF_MEMORY();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// update the new structure with the data we have
|
|
for( dw = 0; dw < dwOptionsCount; dw++ )
|
|
{
|
|
// version 1
|
|
pcmdparser = pcmdOptions + dw;
|
|
if ( pcmdparser == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// version 2
|
|
pcmdparser2 = pcmdOptions2 + dw;
|
|
if ( pcmdparser2 == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// copy the signature
|
|
StringCopyA( pcmdparser2->szSignature,
|
|
cszParserSignature, SIZE_OF_ARRAY( pcmdparser2->szSignature ) );
|
|
|
|
// first init the version 2 structure with defaults (except signature)
|
|
pcmdparser2->dwType = 0;
|
|
pcmdparser2->dwFlags = 0;
|
|
pcmdparser2->dwCount = 0;
|
|
pcmdparser2->dwActuals = 0;
|
|
pcmdparser2->pwszOptions = NULL;
|
|
pcmdparser2->pwszFriendlyName = NULL;
|
|
pcmdparser2->pwszValues = NULL;
|
|
pcmdparser2->pValue = NULL;
|
|
pcmdparser2->pFunction = NULL;
|
|
pcmdparser2->pFunctionData = NULL;
|
|
pcmdparser2->dwReserved = 0;
|
|
pcmdparser2->pReserved1 = NULL;
|
|
pcmdparser2->pReserved2 = NULL;
|
|
pcmdparser2->pReserved3 = NULL;
|
|
|
|
//
|
|
// option information
|
|
pcmdparser2->pwszOptions = pcmdparser->szOption;
|
|
|
|
//
|
|
// value information
|
|
pcmdparser2->pValue = pcmdparser->pValue;
|
|
|
|
//
|
|
// type information
|
|
pcmdparser2->dwType = (pcmdparser->dwFlags & CP_TYPE_MASK);
|
|
if ( pcmdparser2->dwType == 0 )
|
|
{
|
|
// NONE
|
|
// this is how BOOLEAN type is specified in version 1
|
|
pcmdparser2->dwType = CP_TYPE_BOOLEAN;
|
|
}
|
|
|
|
//
|
|
// length information
|
|
if ( pcmdparser2->dwType == CP_TYPE_TEXT )
|
|
{
|
|
// MAX_STRING_LENGTH is what users assumed
|
|
// as maximum length allowed
|
|
pcmdparser2->dwLength = MAX_STRING_LENGTH;
|
|
}
|
|
|
|
//
|
|
// option values
|
|
if ( pcmdparser->dwFlags & CP_MODE_VALUES )
|
|
{
|
|
pcmdparser2->pwszValues = pcmdparser->szValues;
|
|
}
|
|
|
|
//
|
|
// count
|
|
pcmdparser2->dwCount = pcmdparser->dwCount;
|
|
|
|
//
|
|
// function
|
|
if ( pcmdparser2->dwType == CP_TYPE_CUSTOM )
|
|
{
|
|
//
|
|
// we play bit trick here
|
|
// since the prototype of call back function for version 2 is
|
|
// changed, we can't pass the version 1 prototype directly to
|
|
// the version 2 -- to handle this special case, we will write
|
|
// intermediate function as part of this migration which will
|
|
// act a stub and pass the version 1 structure as data parameter to
|
|
// the version 2 structure
|
|
pcmdparser2->pFunction = Parser1FromParser2Stub;
|
|
pcmdparser2->pFunctionData = pcmdparser;
|
|
|
|
// if the "version 1" structure has its data as NULL
|
|
// assign it as its own -- that is how "version 1" used to do
|
|
if ( pcmdparser->pFunctionData == NULL )
|
|
{
|
|
pcmdparser->pFunctionData = pcmdparser;
|
|
}
|
|
}
|
|
|
|
//
|
|
// flags
|
|
// CP_VALUE_MANDATORY, CP_IGNOREVALUE flags are discarded
|
|
if ( pcmdparser->dwFlags & CP_MODE_ARRAY )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_MODE_ARRAY;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_MODE_VALUES )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_MODE_VALUES;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_VALUE_OPTIONAL )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_VALUE_OPTIONAL;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_VALUE_NODUPLICATES )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_VALUE_NODUPLICATES;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_VALUE_NOLENGTHCHECK )
|
|
{
|
|
// actually, in "version 2" this is flag is discarded
|
|
// but make sure that the type for this flag is specified
|
|
// with data type as custom (or) mode array
|
|
if ( ( pcmdparser2->dwType != CP_TYPE_CUSTOM ) &&
|
|
( pcmdparser2->dwFlags & CP2_MODE_ARRAY ) == 0 )
|
|
{
|
|
INVALID_PARAMETER();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_MAIN_OPTION )
|
|
{
|
|
// the functionality of this flag is handled differently
|
|
// in "version 2" -- so memorize the index of the current option
|
|
lMainOption = dw;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_USAGE )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_USAGE;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_DEFAULT )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_DEFAULT;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_MANDATORY )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_MANDATORY;
|
|
}
|
|
|
|
if ( pcmdparser->dwFlags & CP_CASESENSITIVE )
|
|
{
|
|
pcmdparser2->dwFlags |= CP2_CASESENSITIVE;
|
|
}
|
|
}
|
|
|
|
// "version 2" structure is ready to use --
|
|
// call the "version 2" of parser
|
|
bResult = DoParseParam2( dwCount, argv, lMainOption,
|
|
dwOptionsCount, pcmdOptions2, 0 );
|
|
|
|
// update the "version 1" structure with "version 2" structure data
|
|
for( dw = 0; dw < dwOptionsCount; dw++ )
|
|
{
|
|
// version 1
|
|
pcmdparser = pcmdOptions + dw;
|
|
if ( pcmdparser == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// version 2
|
|
pcmdparser2 = pcmdOptions2 + dw;
|
|
if ( pcmdparser2 == NULL )
|
|
{
|
|
UNEXPECTED_ERROR();
|
|
SaveLastError();
|
|
return FALSE;
|
|
}
|
|
|
|
// update the actuals
|
|
pcmdparser->dwActuals = pcmdparser2->dwActuals;
|
|
}
|
|
|
|
// release the memory that is allocated for "version 2" structure
|
|
// but since the "FreeMemory" will clear the last error
|
|
// we need to save that information before releasing the memory
|
|
dwLastError = GetLastError();
|
|
pwszReason = GetParserTempBuffer( 0, GetReason(), 0, FALSE );
|
|
|
|
// now free the memory
|
|
FreeMemory( &pcmdOptions2 );
|
|
|
|
// now, check whether we successfully saved the last error or not
|
|
// if not, return out of memory
|
|
if ( pwszReason != NULL )
|
|
{
|
|
SetReason( pwszReason );
|
|
SetLastError( dwLastError );
|
|
}
|
|
else
|
|
{
|
|
bResult = FALSE;
|
|
OUT_OF_MEMORY();
|
|
SaveLastError();
|
|
}
|
|
|
|
// return
|
|
return bResult;
|
|
}
|
|
|
|
|
|
LONG GetOptionCount( LPCWSTR pwszOption,
|
|
DWORD dwCount,
|
|
PTCMDPARSER pcmdOptions )
|
|
/*++
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
// local variables
|
|
DWORD dw;
|
|
PTCMDPARSER pcp = NULL;
|
|
|
|
// check the input value
|
|
if ( pwszOption == NULL || pcmdOptions == NULL )
|
|
{
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
SaveLastError();
|
|
return -1;
|
|
}
|
|
|
|
// traverse thru the loop and find out how many times, the option has repeated at cmd prompt
|
|
for( dw = 0; dw < dwCount; dw++ )
|
|
{
|
|
// get the option information and check whether we are looking for this option or not
|
|
// if the option is matched, return the no. of times the option is repeated at the command prompt
|
|
pcp = pcmdOptions + dw;
|
|
if ( StringCompare( pcp->szOption, pwszOption, TRUE, 0 ) == 0 )
|
|
return pcp->dwActuals;
|
|
}
|
|
|
|
// this will / shouldn't occur
|
|
return -1;
|
|
}
|