Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1006 lines
32 KiB

// Copyright (c) 1996-1999 Microsoft Corporation
/* command.c - processing command keywords. */
#include "gpdparse.h"
// ---- functions defined in command.c ---- //
BOOL BprocessParam(
IN PABSARRAYREF paarValue,
IN PARRAYREF parStrValue,
IN OUT PGLOBL pglobl) ;
BOOL BparseCommandString(
IN PABSARRAYREF paarValue,
IN PARRAYREF parStrValue,
IN OUT PGLOBL pglobl ) ;
BOOL BconstructRPNtokenStream(
IN OUT PABSARRAYREF paarValue,
OUT PARRAYREF parRPNtokenStream,
IN OUT PGLOBL pglobl) ;
VOID VinitOperPrecedence(
IN OUT PGLOBL pglobl) ;
BOOL BparseArithmeticToken(
IN OUT PABSARRAYREF paarValue,
OUT PTOKENSTREAM ptstr,
PGLOBL pglobl
) ;
BOOL BparseDigits(
IN OUT PABSARRAYREF paarValue,
OUT PTOKENSTREAM ptstr ) ;
BOOL BparseParamKeyword(
IN OUT PABSARRAYREF paarValue,
OUT PTOKENSTREAM ptstr,
PGLOBL pglobl ) ;
BOOL BcmpAARtoStr(
PABSARRAYREF paarStr1,
PBYTE str2) ;
BOOL bDivByZeroCheck(PTOKENSTREAM ptstr) ;
// ---------------------------------------------------- //
BOOL BprocessParam(
IN PABSARRAYREF paarValue,
IN PARRAYREF parStrValue,
IN OUT PGLOBL pglobl)
/* notice this function will append the substring
"%dddd" (without the double quotes) onto the existing
parStrValue. To allow the control module to unambiguously
parse out this substring, The GPD spec requires any occurance
of the character '%' within an Invocation String must be
preceeded by another '%'. So the control module will find two
outcomes when encountering a % char in an invocation string:
it is followed by another % char in which case only one % should
be emitted. It is followed by exactly 4 decimal digits. This
specifies the parameter index and specifies the parameter should be
emitted at this point instead of the "%dddd". Additional digits
are part of the invocation string and should be emitted.
*/
{
PSTR pstrDelimiters = "dDcClmqgnvf" ;
PPARAMETER pparamRoot, pparam ;
DWORD dwParamIndex, dwDelIndex ;
ABSARRAYREF aarToken ;
if(! BeatDelimiter(paarValue, "%"))
return(FALSE) ; // BUG_BUG paranoid, inconsistency error.
if(!BdelimitToken(paarValue, pstrDelimiters, &aarToken, &dwDelIndex) )
{
ERR(("Command parameter: Missing format character.\n")) ;
return(FALSE) ;
}
pparamRoot = (PPARAMETER) gMasterTable[MTI_PARAMETER].pubStruct ;
if(!BallocElementFromMasterTable(MTI_PARAMETER, &dwParamIndex, pglobl) )
return(FALSE) ; // try again with more resources.
pparam = pparamRoot + dwParamIndex ;
pparam->dwFlags = 0 ;
pparam->dwFormat = (DWORD)(BYTE)pstrDelimiters[dwDelIndex] ;
(VOID) BeatLeadingWhiteSpaces(&aarToken) ;
if(aarToken.dw)
{
if(pstrDelimiters[dwDelIndex] == 'd' || pstrDelimiters[dwDelIndex] == 'D')
{
if(!BparseInteger(&aarToken, &pparam->dwDigits, VALUE_INTEGER, pglobl) )
{
ERR(("Command parameter: Syntax error in format width field.\n"));
return(FALSE) ;
}
if(pparam->dwDigits != WILDCARD_VALUE)
pparam->dwFlags |= PARAM_FLAG_FIELDWIDTH_USED ;
}
else
{
ERR(("Command parameter: Unexpected chars found preceeding format specifier: '%0.*s'.\n",
aarToken.dw, aarToken.pub));
return(FALSE) ;
}
}
// its ok to omit the fieldwidth specification
// now look for optional 'Limits' construct
pstrDelimiters = "[{" ;
if(!BdelimitToken(paarValue, pstrDelimiters, &aarToken, &dwDelIndex) )
{
ERR(("Command parameter: missing '{' in Value construct.\n"));
return(FALSE) ;
}
(VOID) BeatLeadingWhiteSpaces(&aarToken) ;
if(aarToken.dw)
{
ERR(("Command parameter: unexpected chars found preceeding Limits or Value construct: '%0.*s'.\n",
aarToken.dw, aarToken.pub));
return(FALSE) ;
}
if(pstrDelimiters[dwDelIndex] == '[')
{
if(!BdelimitToken(paarValue, ",", &aarToken, &dwDelIndex) )
{
ERR(("Command parameter: missing comma delimiter in Limits construct.\n"));
return(FALSE) ;
}
if(!BparseInteger(&aarToken, &pparam->lMin, VALUE_INTEGER, pglobl) )
{
ERR(("Command parameter: syntax error in Min Limit field.\n"));
return(FALSE) ;
}
if(pparam->lMin != WILDCARD_VALUE)
pparam->dwFlags |= PARAM_FLAG_MIN_USED ;
if(!BdelimitToken(paarValue, "]", &aarToken, &dwDelIndex) )
{
ERR(("Command parameter: missing closing bracket in Limits construct.\n"));
return(FALSE) ;
}
if(!BparseInteger(&aarToken, &pparam->lMax, VALUE_INTEGER, pglobl) )
{
ERR(("Command parameter: syntax error in Max Limit field.\n"));
return(FALSE) ;
}
if(pparam->lMax != WILDCARD_VALUE)
pparam->dwFlags |= PARAM_FLAG_MAX_USED ;
if(! BeatDelimiter(paarValue, "{"))
{
ERR(("Command parameter: missing required '{' in Value construct.\n"));
return(FALSE) ;
}
}
// now back to parsing Value construct.
if(!BconstructRPNtokenStream(paarValue, &pparam->arTokens, pglobl) )
return(FALSE) ;
// convert dwParamIndex to 4 digit ascii string of
// the form "%dddd"
{
BYTE aub[6] ; // temp array of unsigned bytes
DWORD dwI, dwNdigits = 4 ; // 4 is number of digits in string
ARRAYREF arTmpDest ; // write result here first.
// the most significant digit has the smaller byte address!
aub[0] = '%' ;
aub[dwNdigits + 1] = '\0' ; // null terminate, but not needed.
for(dwI = dwNdigits ; dwI ; dwI--)
{
aub[dwI] = (BYTE)('0' + dwParamIndex % 10) ;
dwParamIndex /= 10 ;
}
// write "%dddd" out to heap. note if parStrValue->dwCount
// is zero, we must initialize parStrValue from
// scratch instead of appending. Must use an Alignment of 1
// to avoid creating gaps in the command string.
if(!BwriteToHeap(&arTmpDest.loOffset, aub, dwNdigits + 1, 1, pglobl))
return(FALSE) ;
// append this run to existing string
if(!parStrValue->dwCount) // no prevs string exists
{
parStrValue->loOffset = arTmpDest.loOffset ;
}
else
{
// BUG_BUG paranoid: may check that string is contiguous
// parStrValue->loOffset + parStrValue->dwCount
// should equal arTmpDest.loOffset
ASSERT(parStrValue->loOffset + parStrValue->dwCount == arTmpDest.loOffset) ;
}
parStrValue->dwCount += dwNdigits + 1 ;
}
return(TRUE) ;
}
BOOL BparseCommandString(
IN PABSARRAYREF paarValue,
IN PARRAYREF parStrValue,
IN OUT PGLOBL pglobl
)
{
ABSARRAYREF aarToken ; // points to individual string segment.
DWORD dwDelIndex, dwNumParams = 0 ; // dummy
parStrValue->dwCount = 0 ; // tells functions to create new string
// in heap. Don't append to existing string.
(VOID) BeatLeadingWhiteSpaces(paarValue) ;
while(paarValue->dw)
{
if(paarValue->pub[0] == '%')
{
if(++dwNumParams > 14)
{
ERR(("CmdInvocation: Unidrv imposes a max limit of 14 parameters per command.\n"));
return(FALSE) ; // Black Death.
}
if(!BprocessParam(paarValue, parStrValue, pglobl) )
// deposits a little turd on the stringheap,
// modifies parStrValue accordingly and
// initializes one parameter array element.
{
return(FALSE) ; // Black Death.
}
}
else if(paarValue->pub[0] == '"')
{
paarValue->pub++ ;
paarValue->dw-- ; // skip the initial quote.
if(!BdelimitToken(paarValue, "\"", &aarToken, &dwDelIndex) )
{
ERR(("CmdInvocation: missing terminating '\"' in command string.\n"));
return(FALSE) ;
}
if(!BparseStrSegment(&aarToken, parStrValue, pglobl))
{
return(FALSE) ;
}
}
else
{
ERR(("CmdInvocation: command string segment must begin with '\"' or '%'.\n"));
return(FALSE) ;
}
(VOID) BeatLeadingWhiteSpaces(paarValue) ;
}
// We don't want null terminations to occur between parameter
// portion of a string. We just want to blindly add the NULL
// when parsing is really finished.
{
DWORD dwDummy ; // don't care about result
if(!BwriteToHeap(&dwDummy, "\0", 1, 1, pglobl) ) // add Null termination
return(FALSE) ;
}
return(TRUE) ;
}
BOOL BconstructRPNtokenStream(
IN OUT PABSARRAYREF paarValue,
OUT PARRAYREF parRPNtokenStream,
IN OUT PGLOBL pglobl)
/* the input stream is parsed into tokens,
each token is assigned an operator value see OPERATOR
enum.
The Control module interprets the tokenstream just as
Reverse Polish Notation.
In the Control Module, each token is processed as follows:
OP_INTEGER: take dwValue and copy to stack
OP_VARI_INDEX: dwValue is an index identifying the
Unidrv Standard Variable whose current value
should be placed on the stack.
OP_MIN :
OP_MAX :
pop the top 2 values from the stack and push the
smaller or larger value back onto the stack.
OP_ADD :
OP_SUB :
OP_MULT :
OP_DIV :
OP_MOD :
pop the top 2 values from the stack perform the
indicated arithmetic operation, and push the
result back onto the stack. The topmost value in the stack
should be used as the 2nd operand in the operation.
OP_NEG : pop the top value from the stack and push its
negative back on the stack.
OP_MAX_REPEAT : this should always be the last command
executed. It means take the top value on the stack and
if it exceeds the MaxValue specified in lMax, emit the
command invocation multiple times with this parameter no
larger than lMax so that the sum equals the value on
the stack.
OP_HALT: If the value on the stack has not yet been emitted
due to processing the OP_MAX_REPEAT operator,
emit this value or the closest of lMin or lMax if specified.
processing for this parameter is now complete.
In the parser, each token is processed as follows:
OP_INTEGER, OP_VARI_INDEX: these values are
placed directly into the RPNtokenStream in the order
they were encountered. They cause the valueToken count to
be incremented.
Then 2nd group of tokens from OP_MIN to OP_HALT
are first placed into an operator queue (or is it a stack?)
They are inserted into the queue in the order in which
they are encountered, and when they leave the queue, they
are inserted into the RPNtokenStream. There are the rules
governing when an operator token may leave the queue:
a) each token is assigned a precedence level.
here they are arranged from highest to lowest.
OP_NEG a unary operator
OP_MULT, OP_DIV, OP_MOD have the same level
OP_ADD, OP_SUB have the same level
OP_MIN, OP_MAX, OP_MAX_REPEAT have the same level
but are always preceeded by an OP_OPENPAR token.
OP_HALT has the lowest level. (it is generated when
the closing } is encountered.)
OP_CLOSEPAR and OP_OPENPAR have even lower
precedences for firewall purposes.
b) no token may leave the queue unless a token with equal
or lower precedence has just arrived. At that point
the token immediately adjacent to the new arrival
leaves. If the next token is also of higher or equal
precedence than the new arrival, it too leaves.
The tokens depart until this condition is false.
c) the OP_HALT token is not only able to allow all other
tokens to leave the queue, but after that, it too
leaves the queue and enters the tokenStream.
d) the OP_OPENPAR and OP_CLOSEPAR tokens are different.
They will never be inserted into the RPNtokenStream, but
only serve to modify the departure rules governing the
queue.
e) OP_OPENPAR acts to freeze all operators
already in the queue. Those operators cannot
exit the queue until the OP_OPENPAR has been removed
from the queue. All subsequent operators entering
the queue are unaffected by OP_OPENPAR already residing
within the queue.
f) the OP_CLOSEPAR acts to flush all operators in the
queue up to the first encountered OP_OPENPAR.
Upon meeting its counterpart, both vanish from the
queue never to be seen again.
The max_repeat operator if used, must be first token parsed.
Obviously it can appear only once in an expression.
Token counting , unary operator detection and syntax
error detection:
for every binary operator parsed the operator count is
incremented. Immediately after incrementing, the operator count
should always be equal to the valueToken count. An excess in
the operator count of one may indicate the operator is
a unary operator. Any deficit or excess greater than 2 indicates
a syntax error. Immediately after incrementing, the valueToken
count should always be one greater than the operator count.
For the purposes of the operator count, the following tokens
qualify:
OP_ADD, OP_SUB, OP_MULT,
OP_DIV, OP_MOD, OP_COMMA
Note: for function operators the function name and opening
parenthesis is parsed as one object.
*/
{
TOKENSTREAM tstrNode ;
BOOL bStatus ;
DWORD dwValueToken = 0 , // number of value tokens parsed.
dwOperatorToken = 0 , // number of binary operators parsed.
dwQueuePtr = 0 , // current position in the tmp operator queue.
dwCurToken, // index of curToken.
dwQueueSize ; // num elements in array
PDWORD pdwQueueRoot ; // Points to root of queue.
PTOKENSTREAM ptstr, ptstrRoot ;
pdwQueueRoot = (PDWORD) gMasterTable[MTI_OP_QUEUE].pubStruct ;
dwQueueSize = gMasterTable[MTI_OP_QUEUE].dwArraySize ;
ptstrRoot = (PTOKENSTREAM) gMasterTable[MTI_TOKENSTREAM].pubStruct ;
parRPNtokenStream->loOffset = gMasterTable[MTI_TOKENSTREAM].dwCurIndex ;
parRPNtokenStream->dwCount = 0 ;
while(bStatus = BparseArithmeticToken(paarValue, &tstrNode, pglobl))
{
switch(tstrNode.eType)
{
case OP_INTEGER :
case OP_VARI_INDEX :
{
if(dwOperatorToken != dwValueToken)
{
ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
bStatus = FALSE ;
}
dwValueToken++ ;
break ;
}
case OP_ADD :
case OP_SUB :
case OP_MULT :
case OP_DIV :
case OP_MOD :
case OP_COMMA :
{
dwOperatorToken++ ;
if(dwOperatorToken == dwValueToken)
break ;
else if(dwOperatorToken == dwValueToken + 1)
{
// maybe interpret operator as Unary.
if(tstrNode.eType == OP_SUB)
{
tstrNode.eType = OP_NEG ;
dwOperatorToken-- ;
break ;
}
else if(tstrNode.eType == OP_ADD)
{
tstrNode.eType = OP_NULL ;
dwOperatorToken-- ;
break ;
}
}
ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
bStatus = FALSE ;
break ;
}
case OP_MIN :
case OP_MAX :
case OP_OPENPAR :
{
if(dwValueToken != dwOperatorToken)
{
ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
bStatus = FALSE ;
}
break ;
}
case OP_CLOSEPAR :
case OP_HALT :
{
if(dwValueToken != dwOperatorToken + 1)
{
ERR(("Command parameter: arithmetic syntax error in value construct.\n"));
bStatus = FALSE ;
}
break ;
}
case OP_MAX_REPEAT :
{
if(dwValueToken || dwOperatorToken || dwQueuePtr)
{
ERR(("Command parameter: syntax error in value construct.\n"));
ERR((" OP_MAX_REPEAT must appear as the outermost operator only.\n"));
bStatus = FALSE ;
}
break ;
}
default:
{
break ;
}
}
if(!bStatus )
break ;
switch(tstrNode.eType)
{
case OP_INTEGER :
case OP_VARI_INDEX :
{
bStatus = BallocElementFromMasterTable(
MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
if(!bStatus )
break ;
parRPNtokenStream->dwCount++ ;
ptstr = ptstrRoot + dwCurToken ;
ptstr->eType = tstrNode.eType ;
ptstr->dwValue = tstrNode.dwValue ;
break ;
}
case OP_ADD :
case OP_SUB :
case OP_MULT :
case OP_DIV :
case OP_MOD :
case OP_NEG :
{
while (dwQueuePtr &&
(gdwOperPrecedence[tstrNode.eType] <=
gdwOperPrecedence[*(pdwQueueRoot + dwQueuePtr - 1)]) )
{
bStatus = BallocElementFromMasterTable(
MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
if(!bStatus )
break ;
parRPNtokenStream->dwCount++ ;
ptstr = ptstrRoot + dwCurToken ;
ptstr->eType = *(pdwQueueRoot + dwQueuePtr - 1) ;
ptstr->dwValue = 0 ; // undefined
dwQueuePtr-- ; // pop off the Queue.
bDivByZeroCheck(ptstr);
}
if(dwQueuePtr >= dwQueueSize)
bStatus = FALSE ; // Queue overflow
if(!bStatus )
break ;
//add current Oper to Queue
*(pdwQueueRoot + dwQueuePtr) = tstrNode.eType ;
dwQueuePtr++ ;
break ;
}
case OP_MIN :
case OP_MAX :
case OP_MAX_REPEAT :
case OP_OPENPAR :
{
if(dwQueuePtr + 1 >= dwQueueSize) // room for two?
bStatus = FALSE ; // Queue overflow
if(!bStatus )
break ;
//add OP_OPENPAR to Queue
*(pdwQueueRoot + dwQueuePtr) = OP_OPENPAR ;
dwQueuePtr++ ;
//add current Oper to Queue
if(tstrNode.eType != OP_OPENPAR)
{
*(pdwQueueRoot + dwQueuePtr) = tstrNode.eType ;
dwQueuePtr++ ;
}
break ;
}
case OP_CLOSEPAR :
case OP_COMMA :
case OP_HALT :
{
while (dwQueuePtr &&
(gdwOperPrecedence[tstrNode.eType] <=
gdwOperPrecedence[*(pdwQueueRoot + dwQueuePtr - 1)]) )
{
bStatus = BallocElementFromMasterTable(
MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
if(!bStatus )
break ;
parRPNtokenStream->dwCount++ ;
ptstr = ptstrRoot + dwCurToken ;
ptstr->eType = *(pdwQueueRoot + dwQueuePtr - 1) ;
ptstr->dwValue = 0 ; // undefined
dwQueuePtr-- ; // pop off the Queue.
bDivByZeroCheck(ptstr);
}
if(!bStatus )
break ;
if(tstrNode.eType == OP_COMMA)
{
// comma clears all operators in queue up to the
// function. This completes processing of the
// first argument. The comma effectively converts
// the syntax funct(A + B, C * D) into
// ((A + B) funct (C * D))
// check to see if function operator is now at
// top of queue.
if(dwQueuePtr)
{
OPERATOR eType ;
eType = *(pdwQueueRoot + dwQueuePtr - 1) ;
if(eType == OP_MIN || eType == OP_MAX)
break ;
}
ERR(("Command parameter: syntax error in value construct.\n"));
ERR((" comma used outside of function argument list.\n"));
bStatus = FALSE ;
}
else if(tstrNode.eType == OP_HALT)
{
if(dwQueuePtr)
{
ERR(("Command parameter: syntax error in value construct - unmatched OP_OPENPAR.\n"));
bStatus = FALSE ;
break ;
}
// now add halt operator to tokenStream
bStatus = BallocElementFromMasterTable(
MTI_TOKENSTREAM, &dwCurToken, pglobl) ;
if(!bStatus )
break ;
parRPNtokenStream->dwCount++ ;
ptstr = ptstrRoot + dwCurToken ;
ptstr->eType = OP_HALT ;
ptstr->dwValue = 0 ; // undefined
}
else if(dwQueuePtr &&
(*(pdwQueueRoot + dwQueuePtr - 1) == OP_OPENPAR))
{
dwQueuePtr-- ; // pop OP_OPENPAR off the Queue.
// conjugate pairs meet and annihilate
}
else
{
ERR(("Command parameter: syntax error in value construct - unmatched OP_CLOSEPAR.\n"));
bStatus = FALSE ;
}
break ;
}
default:
break ;
}
if(!bStatus || tstrNode.eType == OP_HALT)
break ;
}
if(!bStatus )
{
parRPNtokenStream->dwCount = 0 ;
parRPNtokenStream->loOffset = 0 ;
}
return(bStatus);
}
VOID VinitOperPrecedence(
IN OUT PGLOBL pglobl)
{
DWORD dwP ; // precedence level ;
dwP = 0 ; // lowest level. fire wall.
gdwOperPrecedence[OP_OPENPAR] = dwP ;
dwP++ ;
gdwOperPrecedence[OP_CLOSEPAR] = dwP ;
dwP++ ;
gdwOperPrecedence[OP_HALT] = dwP ;
dwP++ ;
gdwOperPrecedence[OP_MIN] = gdwOperPrecedence[OP_MAX] =
gdwOperPrecedence[OP_MAX_REPEAT] = dwP ;
dwP++ ; // comma must be next to function operators
gdwOperPrecedence[OP_COMMA] = dwP ; // in precedence level.
dwP++ ;
gdwOperPrecedence[OP_ADD] = gdwOperPrecedence[OP_SUB] = dwP ;
dwP++ ;
gdwOperPrecedence[OP_MULT] = gdwOperPrecedence[OP_DIV] =
gdwOperPrecedence[OP_MOD] = dwP ;
dwP++ ;
gdwOperPrecedence[OP_NEG] = dwP ;
}
BOOL BparseArithmeticToken(
IN OUT PABSARRAYREF paarValue,
OUT PTOKENSTREAM ptstr,
PGLOBL pglobl
)
/* objects to parse:
OP_INTEGER : string of digits delimited by non-Keyword char
OP_MIN : "min" and adjacent '('
OP_MAX : "max" and adjacent '('
OP_MAX_REPEAT : "max_repeat" and adjacent '('
OP_MOD : "MOD"
OP_VARI_INDEX : string of Keyword chars deliniated by non Keyword
chars, not starting with a digit. and not one of the recognized
operator keywords.
Note all of the above string objects must be delinated by
non-Keyword chars.
OP_ADD : '+'
OP_SUB : '-'
OP_MULT : '*'
OP_DIV : '/'
OP_HALT : '}'
OP_OPENPAR : '('
OP_CLOSEPAR : ')'
OP_COMMA : ','
*/
{
BYTE ubSrc ;
BOOL bStatus ;
if(!paarValue->dw)
return(FALSE); // nothing left!
(VOID) BeatLeadingWhiteSpaces(paarValue) ;
switch(ubSrc = *paarValue->pub)
{
case '+':
{
ptstr->eType = OP_ADD ;
break ;
}
case '-':
{
ptstr->eType = OP_SUB ;
break ;
}
case '*':
{
ptstr->eType = OP_MULT ;
break ;
}
case '/':
{
ptstr->eType = OP_DIV ;
break ;
}
case '}':
{
ptstr->eType = OP_HALT ;
break ;
}
case '(':
{
ptstr->eType = OP_OPENPAR ;
break ;
}
case ')':
{
ptstr->eType = OP_CLOSEPAR ;
break ;
}
case ',':
{
ptstr->eType = OP_COMMA ;
break ;
}
default:
{
bStatus = FALSE ; // till proven otherwise.
if(ubSrc >= '0' && ubSrc <= '9')
{
bStatus = BparseDigits(paarValue, ptstr) ;
}
else if( (ubSrc >= 'a' && ubSrc <= 'z') ||
(ubSrc >= 'A' && ubSrc <= 'Z') ||
ubSrc == '_' || ubSrc == '?')
{
bStatus = BparseParamKeyword(paarValue, ptstr, pglobl) ;
}
return(bStatus);
}
}
paarValue->pub++ ;
paarValue->dw-- ;
return(TRUE);
}
#define pubM (paarValue->pub)
#define dwM (paarValue->dw)
BOOL BparseDigits(
IN OUT PABSARRAYREF paarValue,
OUT PTOKENSTREAM ptstr )
/* paarValue left pointing after last digit upon exit.
there may in fact be nothing left in paarValue */
{
DWORD dwNumber = 0 ;
BOOL bStatus = FALSE ;
if(*pubM == '0') // leading zero indicates hexadecimal format
{
pubM++ ;
dwM-- ;
if(dwM && (*pubM == 'x' || *pubM == 'X'))
{
pubM++ ;
dwM-- ;
}
else
{
bStatus = TRUE ;
goto EndNumber ; // ignore leading zero and attempt to parse more digits.
}
if(!dwM)
{
ERR(("Command Param-BparseDigits: no digits found in Hex value.\n"));
return(FALSE);
}
for( ; dwM ; pubM++, dwM-- )
{
if(*pubM >= '0' && *pubM <= '9')
{
dwNumber *= 0x10 ;
dwNumber += (*pubM - '0') ;
}
else if(*pubM >= 'a' && *pubM <= 'f')
{
dwNumber *= 0x10 ;
dwNumber += (*pubM - 'a' + 0x0a) ;
}
else if(*pubM >= 'A' && *pubM <= 'F')
{
dwNumber *= 0x10 ;
dwNumber += (*pubM - 'A' + 0x0a) ;
}
else
break;
bStatus = TRUE ;
}
}
EndNumber:
for( ; dwM && *pubM >= '0' && *pubM <= '9' ; )
{
dwNumber *= 10 ;
dwNumber += (*pubM - '0') ;
pubM++ ;
dwM-- ;
bStatus = TRUE ;
}
if( dwM && ((*pubM >= 'a' && *pubM <= 'z') ||
(*pubM >= 'A' && *pubM <= 'Z') ||
*pubM == '_' || *pubM == '?'))
{
ERR(("Command parameter: syntax error in value construct.\n"));
ERR((" integer not clearly delimited using non Keyword characters.\n"));
return(FALSE);
}
ptstr->eType = OP_INTEGER ;
ptstr->dwValue = dwNumber ;
return(bStatus);
}
BOOL BparseParamKeyword(
IN OUT PABSARRAYREF paarValue,
OUT PTOKENSTREAM ptstr,
PGLOBL pglobl )
/* if the keyword happens to be a function operator,
find and eat the opening '('. If keyword is
a Standard Variable, determine its index value
and store in dwValue.
*/
{
BOOL bStatus = FALSE ;
ABSARRAYREF aarKey ;
aarKey.pub = pubM ; // start of Keyword
for(aarKey.dw = 0 ; dwM ; aarKey.dw++)
{
if( (*pubM >= 'a' && *pubM <= 'z') ||
(*pubM >= 'A' && *pubM <= 'Z') ||
(*pubM >= '0' && *pubM <= '9') ||
*pubM == '_' || *pubM == '?' )
{
pubM++ ;
dwM-- ;
bStatus = TRUE ;
}
else
break ;
}
// now identify this Keyword
if(!bStatus)
return(bStatus);
if(BcmpAARtoStr(&aarKey, "min"))
{
ptstr->eType = OP_MIN ;
bStatus = BeatDelimiter(paarValue, "(") ;
if(!bStatus)
{
ERR(("Command parameter: '(' must follow 'min' operator.\n"));
}
}
else if(BcmpAARtoStr(&aarKey, "max"))
{
ptstr->eType = OP_MAX ;
bStatus = BeatDelimiter(paarValue, "(") ;
if(!bStatus)
{
ERR(("Command parameter: '(' must follow 'max' operator.\n"));
}
}
else if(BcmpAARtoStr(&aarKey, "max_repeat"))
{
ptstr->eType = OP_MAX_REPEAT ;
bStatus = BeatDelimiter(paarValue, "(") ;
if(!bStatus)
{
ERR(("Command parameter: '(' must follow 'max_repeat' operator.\n"));
}
}
else if(BcmpAARtoStr(&aarKey, "MOD"))
{
ptstr->eType = OP_MOD ;
// don't eat any delimiters
}
else // must therefore be the name of a StandardValue
{
ptstr->eType = OP_VARI_INDEX ;
bStatus = BparseConstant(&aarKey, &ptstr->dwValue,
VALUE_CONSTANT_STANDARD_VARS, pglobl) ;
}
return(bStatus);
}
#undef pubM
#undef dwM
BOOL BcmpAARtoStr(
PABSARRAYREF paarStr1,
PBYTE str2)
// Compares two strings, one referenced by 'aar' the other
// referenced by 'pub or str'. Returns TRUE if they match, FALSE
// otherwise.
{
DWORD dwCnt ;
dwCnt = strlen(str2) ;
if(dwCnt != paarStr1->dw)
return(FALSE) ; // Lengths don't even match!
if(strncmp(paarStr1->pub, str2, dwCnt))
return(FALSE) ;
return(TRUE) ;
}
BOOL bDivByZeroCheck(PTOKENSTREAM ptstr)
{
// assumes ptstr is not pointing to start of tokenstream
// else (ptstr - 1) is undefined.
// this assumption is valid because the syntax checking in
// BconstructRPNtokenStream counts
// dwValueToken , // number of value tokens parsed.
// and dwOperatorToken and ensures that dwValueToken
// is non-zero when OP_DIV is parsed.
if (ptstr->eType == OP_DIV && (ptstr - 1)->eType == OP_INTEGER &&
(ptstr - 1)->dwValue == 0)
{
ERR(("Command parameter: Explicit divide by zero detected.\n"));
return(FALSE) ;
}
return(TRUE) ;
}