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
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) ;
|
|
}
|
|
|