/*** ntexpr.c - expression evaluator for NT debugger * Copyright 1990, Microsoft Corporation * Purpose: * With the current command line at *pchCommand, parse and evaluate the next expression. * Revision History: * [-] 18-Apr-1990 Richk Created - split from ntcmd.c. */ #include "precomp.h" #pragma hdrstop #include "ntexpr.h" #define INVALID_CLASS ((ULONG)-1) ULONG PeekToken(PLONG64); ULONG GetTokenSym(PLONG64); ULONG NextToken(PLONG64); void AcceptToken(void); UCHAR PeekChar(void); ULONG64 GetCommonExpression(VOID); void GetLowerString(PUCHAR, ULONG); LONG64 GetExpr(void); LONG64 GetLRterm(void); LONG64 GetLterm(void); LONG64 GetAterm(void); LONG64 GetMterm(void); LONG64 GetTerm(void); USHORT addrExpression; NTSDADDR tempAddr; static PUCHAR pchCommand; static ULONG savedClass; static LONG64 savedValue; static UCHAR *savedpchCmd; static ULONG64 EXPRLastExpression = 0; static int BaseDefault = 16; static MPT MptProcessorType; static struct Res { UCHAR chRes[3]; ULONG classRes; ULONG valueRes; } Reserved[] = { { 'o', 'r', '\0', LOGOP_CLASS, LOGOP_OR }, { 'b', 'y', '\0', UNOP_CLASS, UNOP_BY }, { 'w', 'o', '\0', UNOP_CLASS, UNOP_WO }, { 'd', 'w', '\0', UNOP_CLASS, UNOP_DW }, { 'h', 'i', '\0', UNOP_CLASS, UNOP_HI }, { 'm', 'o', 'd', MULOP_CLASS, MULOP_MOD }, { 'x', 'o', 'r', LOGOP_CLASS, LOGOP_XOR }, { 'a', 'n', 'd', LOGOP_CLASS, LOGOP_AND }, { 'p', 'o', 'i', UNOP_CLASS, UNOP_POI }, { 'n', 'o', 't', UNOP_CLASS, UNOP_NOT }, { 'l', 'o', 'w', UNOP_CLASS, UNOP_LOW }, #if 0 { 'e', 'a', 'x', REG_CLASS, REGEAX }, { 'e', 'b', 'x', REG_CLASS, REGEBX }, { 'e', 'c', 'x', REG_CLASS, REGECX }, { 'e', 'd', 'x', REG_CLASS, REGEDX }, { 'e', 'b', 'p', REG_CLASS, REGEBP }, { 'e', 's', 'p', REG_CLASS, REGESP }, { 'e', 'i', 'p', REG_CLASS, REGEIP }, { 'e', 's', 'i', REG_CLASS, REGESI }, { 'e', 'd', 'i', REG_CLASS, REGEDI }, { 'e', 'f', 'l', REG_CLASS, REGEFL } #endif }; #define RESERVESIZE (sizeof(Reserved) / sizeof(struct Res)) static char * SegRegs[] = { "cs", "ds", "es", "fs", "gs", "ss" }; #define SEGREGSIZE (sizeof(SegRegs)/sizeof(char *)) #ifdef KERNEL extern USHORT DefaultProcessor; #endif void error(ULONG_PTR errorval) { RaiseException(NTEXPR_EXCEPTION, 0, 1, &errorval); } ULONG GetExprFilter(LPEXCEPTION_POINTERS ep, PULONG64 status) { if (ep->ExceptionRecord->ExceptionCode != NTEXPR_EXCEPTION) { return EXCEPTION_CONTINUE_SEARCH; } else { *status = ep->ExceptionRecord->ExceptionInformation[0]; return EXCEPTION_EXECUTE_HANDLER; } } ULONG GetExpression(PUCHAR szExpr, PULONG64 pResult, int radix, MPT mptProcessorType, PUCHAR *lpNext) /*++ Routine Description: read and evaluate the next possible expression from szExpr and return its value. The expression is parsed and evaluated using a recursive descent method. Arguments: szExpr - Supplies the expression string pResult - Returns the value of the expression radix - Supplies the default numeric radix mptProcessorType - Supplies the type of CPU for register recognition Return Value: returns 0 if successful. Non-zero values are error codes. --*/ { ULONG64 value; ULONG64 result; _try{ if (szExpr) { pchCommand = szExpr; } BaseDefault = radix; MptProcessorType = mptProcessorType; value = GetCommonExpression(); *lpNext = pchCommand; EXPRLastExpression = value; *pResult = value; result = 0; } _except(GetExprFilter(GetExceptionInformation(), &result)) { ; } return (ULONG)result; } ULONG GetAddrExpression( PUCHAR szExpr, PNTSDADDR Address, int radix, MPT mptProcessorType, ULONG defaultSeg, PUCHAR * lpNext) /*** GetAddrExpression - read and evaluate address expression * Purpose: * Used to get an address expression. * Returns: * Pointer to address packet * Exceptions: * error exit: SYNTAX - empty expression or premature end-of-line */ { ULONG64 value; ULONG status; NotFlat(*Address); // Do a normal GetExpression call status = GetExpression(szExpr, &value, radix, mptProcessorType, lpNext); if (status) { return status; } *Address = tempAddr; // If it wasn't an explicit address expression // force it to be an address if (!(addrExpression & ~INSTR_POINTER)) { if (!Is64PtrSE(value)) { value = SE32To64(value); } Off(*Address) = value; #if 0 if (mptProcessorType == mptix86) { addrExpression = Address->type = fVm86 ? ADDR_V86 : (f16pm ? ADDR_16 : ADDR_FLAT); if ((addrExpression != ADDR_FLAT) && defaultSeg != -1L) { Address->seg = (USHORT)GetRegFlagValue(defaultSeg); } } else { addrExpression = Address->type = ADDR_FLAT; } #else addrExpression = Address->type = ADDR_FLAT; #endif ComputeFlatAddress(Address, NULL); } else if fnotFlat(*Address) { // This case (i.e., addrExpression && !flat) results from // an override (i.e., %,,&, or #) being used but no segment // being specified to force a flat address computation. Type(*Address) = addrExpression; Off(*Address) = value; if (mptProcessorType == mptix86) { if (defaultSeg == -1L) { Address->seg = 0; } else { // test flag for IP or EIP as register argument // if so, use CS as default register if (fInstrPtr(*Address)) { defaultSeg = GetRegString((PUCHAR) "cs", NULL); } Address->seg = (USHORT)GetRegFlagValue(defaultSeg); } } ComputeFlatAddress(Address, NULL); } return 0; } /*** GetCommonExpression - read and evaluate expression * Purpose: * From the current command line position at pchCommand, * read and evaluate the next possible expression. * return either its value if a memory expression, or * its symbol file and line number structures if a source * line expression. * Input: * pchCommand - command line position * Outputs: * *ppSymfile - nonNULL for symbol file structure * NULL for returning memory offset * *ppLineno - lineno structure * fSourceDefault - treat '.' as line number if TRUE * else as program counter value in GetExpr. * Returns: * if (*ppSymfile) - line number (USHORT) * if (!*ppSymfile) - ULONG value of expression. * Exceptions: * error exit: SYNTAX - empty expression or premature end-of-line * Notes: * parsing for source line expressions is done here. the syntax is: * [! [[.]]] * if no and/or is/are specified, the current * program counter is used to determine the defaults. * if '.' is used, evaluates to a line number, else * a memory address is calculated. NO WHITE SPACE BETWEEN * '.' AND . */ ULONG64 GetCommonExpression(VOID) { PUCHAR pchCommandSaved; UCHAR chModule[40]; UCHAR chFilename[40]; UCHAR ch; ULONG64 value; PUCHAR pchFilename; savedClass = INVALID_CLASS; pchCommandSaved = pchCommand; if (PeekChar() == '!') { pchCommand++; } GetLowerString(chModule, sizeof(chModule)); ch = PeekChar(); // if '!' is next, chModule has , chFilename has // else copy chFilename to chModule and set chModule to NULL if (ch == '!') { pchCommand++; GetLowerString(chFilename, sizeof(chFilename)); ch = PeekChar(); } else { strcpy((PSTR)chFilename, (PSTR)chModule); chModule[0] = '\0'; } pchCommand = pchCommandSaved; ch = PeekChar(); switch (ch) { case '&': pchCommand++; addrExpression = ADDR_V86; break; case '#': pchCommand++; addrExpression = ADDR_16; break; case '%': pchCommand++; addrExpression = ADDR_FLAT; break; default: addrExpression = FALSE; break; } ch = PeekChar(); value = (ULONG64)GetExpr(); return value; } void GetLowerString(PUCHAR pchBuffer, ULONG cbBuffer) { UCHAR ch; ch = PeekChar(); ch = (UCHAR)tolower(ch); while ((ch == '_' || (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') ) && --cbBuffer) { *pchBuffer++ = ch; ch = *++pchCommand; } *pchBuffer = '\0'; } /*** GetExpr - Get expression * Purpose: * Parse logical-terms separated by logical operators into * expression value. * Input: * pchCommand - present command line position * Returns: * long value of logical result. * Exceptions: * error exit: SYNTAX - bad expression or premature end-of-line * Notes: * may be called recursively. * = [ ]* * = AND (&), OR (|), XOR (^) */ LONG64 GetExpr(void) { LONG64 value1; LONG64 value2; ULONG opclass; LONG64 opvalue; value1 = GetLRterm(); while ((opclass = PeekToken(&opvalue)) == LOGOP_CLASS) { AcceptToken(); value2 = GetLRterm(); switch (opvalue) { case LOGOP_AND: value1 &= value2; break; case LOGOP_OR: value1 |= value2; break; case LOGOP_XOR: value1 ^= value2; break; default: error(ERR_SYNTAX); } } return value1; } /*** GetLRterm - get logical relational term * Purpose: * Parse logical-terms separated by logical relational * operators into the expression value. * Input: * pchCommand - present command line position * Returns: * long value of logical result. * Exceptions: * error exit: SYNTAX - bad expression or premature end-of-line * Notes: * may be called recursively. * = [ ]* * = '==' or '=', '!=', '>', '<' */ LONG64 GetLRterm() { LONG64 value1; LONG64 value2; ULONG opclass; LONG64 opvalue; //dprintf("LONG GetLRterm ()\n"); value1 = GetLterm(); while ((opclass = PeekToken(&opvalue)) == LRELOP_CLASS) { AcceptToken(); value2 = GetLterm(); switch (opvalue) { case LRELOP_EQ: value1 = (value1 == value2); break; case LRELOP_NE: value1 = (value1 != value2); break; case LRELOP_LT: value1 = (value1 < value2); break; case LRELOP_GT: value1 = (value1 > value2); break; default: error(ERR_SYNTAX); } } return value1; } /*** GetLterm - get logical term * Purpose: * Parse additive-terms separated by additive operators into * logical term value. * Input: * pchCommand - present command line position * Returns: * long value of sum. * Exceptions: * error exit: SYNTAX - bad logical term or premature end-of-line * Notes: * may be called recursively. * = [ ]* * = +, - */ LONG64 GetLterm(void) { LONG64 value1 = GetAterm(); LONG64 value2; ULONG opclass; LONG64 opvalue; BOOL faddr = (BOOL)addrExpression; //dprintf("LONG GetLterm ()\n"); while ((opclass = PeekToken(&opvalue)) == ADDOP_CLASS) { AcceptToken(); value2 = GetAterm(); if (!faddr && addrExpression) { LONG64 tmp = value1; value1 = value2; value2 = tmp; } if ((MptProcessorType != mptix86 && addrExpression) || (MptProcessorType == mptix86 && (addrExpression & ~INSTR_POINTER))) { switch (opvalue) { case ADDOP_PLUS: AddrAdd(&tempAddr, value2); value1 += value2; break; case ADDOP_MINUS: AddrSub(&tempAddr, value2); value1 -= value2; break; default: error(ERR_SYNTAX); } } else { switch (opvalue) { case ADDOP_PLUS: value1 += value2; break; case ADDOP_MINUS: value1 -= value2; break; default: error(ERR_SYNTAX); } } } return value1; } /*** GetAterm - get additive term * Purpose: * Parse multiplicative-terms separated by multipicative operators * into additive term value. * Input: * pchCommand - present command line position * Returns: * long value of product. * Exceptions: * error exit: SYNTAX - bad additive term or premature end-of-line * Notes: * may be called recursively. * = [ ]* * = *, /, MOD (%) */ LONG64 GetAterm(void) { LONG64 value1; LONG64 value2; ULONG opclass; LONG64 opvalue; //dprintf("LONG GetAterm ()\n"); value1 = GetMterm(); while ((opclass = PeekToken(&opvalue)) == MULOP_CLASS) { AcceptToken(); value2 = GetAterm(); switch (opvalue) { case MULOP_MULT: value1 *= value2; break; case MULOP_DIVIDE: value1 /= value2; break; case MULOP_MOD: value1 %= value2; break; case MULOP_SEG: { PXDESCRIPTOR_TABLE_ENTRY pdesc = NULL; XDESCRIPTOR_TABLE_ENTRY desc; if (addrExpression) { Type(tempAddr) = addrExpression; } else { // We don't know what kind of address this is // Let's try to figure it out. if (MptProcessorType != mptix86) { addrExpression = Type(tempAddr) = ADDR_FLAT; #if 0 } else if (fVm86) { addrExpression = Type(tempAddr) = ADDR_V86; #endif } else { desc.Selector = (DWORD)value1; if (!LookupSelector(LptdCur, &desc)) { error(ERR_BADSEG); } addrExpression = Type(tempAddr) = (UCHAR)desc.Descriptor.HighWord. Bits.Default_Big ? ADDR_1632 : ADDR_16; pdesc = &desc; } } tempAddr.seg = (USHORT)value1; tempAddr.off = value2; ComputeFlatAddress(&tempAddr, pdesc); value1 = value2; } break; // 64-bit address specifier. // Treat identically to 32-bit address. case MULOP_64: addrExpression = ADDR_FLAT; tempAddr.type = ADDR_FLAT; tempAddr.seg = 0; tempAddr.off = value2; ComputeFlatAddress(&tempAddr, NULL); value1 = value2; break; default: error(ERR_SYNTAX); } } return value1; } /*** GetMterm - get multiplicative term * Purpose: * Parse basic-terms optionally prefaced by one or more * unary operators into a multiplicative term. * Input: * pchCommand - present command line position * Returns: * long value of multiplicative term. * Exceptions: * error exit: SYNTAX - bad multiplicative term or premature end-of-line * Notes: * may be called recursively. * = [] | * = , ~ (NOT), BY, WO, DW, HI, LOW */ LONG64 GetMterm(void) { LONG64 value; USHORT wvalue; UCHAR bvalue; ULONG opclass; LONG64 opvalue; //dprintf("LONG GetMterm ()\n"); if ((opclass = PeekToken(&opvalue)) != UNOP_CLASS && opclass != ADDOP_CLASS) { value = GetTerm(); } else { AcceptToken(); value = GetMterm(); switch (opvalue) { case UNOP_NOT: value = !value; break; case UNOP_BY: case UNOP_WO: case UNOP_DW: case UNOP_POI: NotFlat(tempAddr); Off(tempAddr) = value; Type(tempAddr) = ADDR_FLAT; ComputeFlatAddress(&tempAddr, NULL); switch (opvalue) { case UNOP_BY: if (!GetMemByte(&tempAddr, &bvalue)) { error(ERR_MEMORY); } value = (LONG)bvalue; break; case UNOP_WO: if (!GetMemWord(&tempAddr, &wvalue)) { error(ERR_MEMORY); } value = (LONG)wvalue; break; case UNOP_DW: if (!GetMemDword(&tempAddr, (PULONG)&value)) { error(ERR_MEMORY); } break; case UNOP_POI: // There should be some special processing for // 16:16 or 16:32 addresses (i.e. take the DWORD) // and make it back into a value with a possible // segment, but I've left this for others who might // know more of what they want. if (!GetMemDword(&tempAddr, (PULONG)&value)) { error(ERR_MEMORY); } break; } break; case UNOP_LOW: value &= 0xffff; break; case UNOP_HI: value = ((ULONG)value >> 16); break; case ADDOP_PLUS: break; case ADDOP_MINUS: value = -value; break; default: error(ERR_SYNTAX); } } return value; } /*** GetTerm - get basic term * Purpose: * Parse numeric, variable, or register name into a basic * term value. * Input: * pchCommand - present command line position * Returns: * long value of basic term. * Exceptions: * error exit: SYNTAX - empty basic term or premature end-of-line * Notes: * may be called recursively. * = ( ) | | | * = @ */ LONG64 GetTerm(void) { LONG64 value = 0; ULONG opclass; LONG64 opvalue; //dprintf("LONG GetTerm ()\n"); opclass = GetTokenSym(&opvalue); if (opclass == LPAREN_CLASS) { value = GetExpr(); if (GetTokenSym(&opvalue) != RPAREN_CLASS) { error(ERR_SYNTAX); } } else if (opclass == REG_CLASS) { value = GetRegFlagValue((ULONG)opvalue); } else if (opclass == REG_PC_CLASS) { addrExpression |= INSTR_POINTER; value = GetRegFlagValue((ULONG)opvalue); } else if (opclass == NUMBER_CLASS || opclass == SYMBOL_CLASS) { value = opvalue; } else { error(ERR_SYNTAX); } return value; } /*** GetRange - parse address range specification * Purpose: * With the current command line position, parse an * address range specification. Forms accepted are: * - starting address with default length * - inclusive address range * l - starting address with item count * Input: * pchCommand - present command line location * size - nonzero - (for data) size in bytes of items to list * specification will be "length" type with * *fLength forced to TRUE. * zero - (for instructions) specification either "length" * or "range" type, no size assumption made. * Output: * *addr - starting address of range * *value - if *fLength = TRUE, count of items (forced if size != 0) * FALSE, ending address of range * (*addr and *value unchanged if no second argument in command) * lpbSecondParamIsALength - Indicates whether second address is of the form . * Returns: * A value of TRUE is returned if no length is specified, or a length * or an ending address is specified and size is not zero. Otherwise, * a value of FALSE is returned. * Exceptions: * error exit: * SYNTAX - expression error * BADRANGE - if ending address before starting address */ BOOL GetRange( PUCHAR szExpr, PNTSDADDR addr, PULONG64 value, ULONG size, BOOL *fLength, int radix, MPT mptProcessorType, ULONG defaultSeg, PUCHAR * lpNext, LPBOOL lpbSecondParamIsALength ) /*++ Routine Description: With the current command line position, parse an address range specification. Forms accepted are: - starting address with default length - inclusive address range l - starting address with item count Arguments: szExpr - Supplies the command string addr - Returns the starting address value - size - fLength - radix - mptProcessorType - defaultSeg - lpNext - Returns the location of the first character not consumed from the input string. lpbSecondParamIsALength - (out) Optional, can be null. If not null, will return TRUE or FALSE. TRUE indicates that the second address is of the form . FALSE indicates that the second address is of the form
. If an error occurs, this value is undefined. Return Value: 0 for success, ntsd error status otherwise. --*/ { UCHAR ch; PUCHAR psz; static NTSDADDR EndRange; BOOL fL = FALSE; BOOL fSpace = FALSE; ULONG status; pchCommand = szExpr; if (mptProcessorType != mptix86) { defaultSeg = 0; } PeekChar(); // skip leading whitespace first // Pre-parse the line, look for a " L" for (psz = pchCommand; *psz; psz++) { if ((*psz == 'L' || *psz == 'l') && fSpace) { fL = TRUE; *psz = '\0'; break; } fSpace = (*psz == ' '); } *fLength = TRUE; if ((ch = PeekChar()) != '\0' && ch != ';') { status = GetAddrExpression(NULL, addr, radix, mptProcessorType, defaultSeg, lpNext); if (status) { return status; } if (((ch = PeekChar()) != '\0' && ch != ';') || fL) { if (!fL) { // An address was specified if (lpbSecondParamIsALength) { *lpbSecondParamIsALength = FALSE; } status = GetAddrExpression(NULL, &EndRange, radix, mptProcessorType, defaultSeg, lpNext); if (status) { return status; } if (AddrGt(*addr, EndRange)) { return ERR_BADRANGE; } if (size) { *value = AddrDiff(EndRange, *addr) / size + 1; } else { *value = (ULONG_PTR)&EndRange; *fLength = FALSE; } } else { // A length was specified. if (lpbSecondParamIsALength) { *lpbSecondParamIsALength = TRUE; } pchCommand = psz + 1; status = GetExpression(NULL, value, radix, mptProcessorType, lpNext); *psz = 'l'; if (status) { return status; } } } } return 0; } /*** PeekChar - peek the next non-white-space character * Purpose: * Return the next non-white-space character and update * pchCommand to point to it. * Input: * pchCommand - present command line position. * Returns: * next non-white-space character */ UCHAR PeekChar(void) { UCHAR ch; //dprintf("UCHAR PeekChar (void)\n"); do { ch = *pchCommand++; } while (ch == ' ' || ch == '\t'); pchCommand--; return ch; } /*** PeekToken - peek the next command line token * Purpose: * Return the next command line token, but do not advance * the pchCommand pointer. * Input: * pchCommand - present command line position. * Output: * *pvalue - optional value of token * Returns: * class of token * Notes: * savedClass, savedValue, and savedpchCmd saves the token getting * state for future peeks. To get the next token, a GetToken or * AcceptToken call must first be made. */ ULONG PeekToken(PLONG64 pvalue) { UCHAR *pchTemp; //dprintf("ULONG PeekToken (PLONG pvalue)\n"); // Get next class and value, but do not // move pchCommand, but save it in savedpchCmd. // Do not report any error condition. if (savedClass == INVALID_CLASS) { pchTemp = pchCommand; savedClass = NextToken(&savedValue); savedpchCmd = pchCommand; pchCommand = pchTemp; } *pvalue = savedValue; return savedClass; } /*** AcceptToken - accept any peeked token * Purpose: * To reset the PeekToken saved variables so the next PeekToken * will get the next token in the command line. * Input: * None. * Output: * None. */ void AcceptToken(void) { //dprintf("void AcceptToken (void)\n"); savedClass = INVALID_CLASS; pchCommand = savedpchCmd; } /*** GetToken - peek and accept the next token * Purpose: * Combines the functionality of PeekToken and AcceptToken * to return the class and optional value of the next token * as well as updating the command pointer pchCommand. * Input: * pchCommand - present command string pointer * Output: * *pvalue - pointer to the token value optionally set. * Returns: * class of the token read. * Notes: * An illegal token returns the value of ERROR_CLASS with *pvalue * being the error number, but produces no actual error. */ ULONG GetTokenSym(PLONG64 pvalue) { ULONG opclass; //dprintf("ULONG GetTokenSym (PLONG pvalue)\n"); if (savedClass != INVALID_CLASS) { opclass = savedClass; savedClass = INVALID_CLASS; *pvalue = savedValue; pchCommand = savedpchCmd; } else { opclass = NextToken(pvalue); } if (opclass == ERROR_CLASS) { error((ULONG_PTR)*pvalue); } return opclass; } /*** NextToken - process the next token * Purpose: * Parse the next token from the present command string. * After skipping any leading white space, first check for * any single character tokens or register variables. If * no match, then parse for a number or variable. If a * possible variable, check the reserved word list for operators. * Input: * pchCommand - pointer to present command string * Output: * *pvalue - optional value of token returned * pchCommand - updated to point past processed token * Returns: * class of token returned * Notes: * An illegal token returns the value of ERROR_CLASS with *pvalue * being the error number, but produces no actual error. */ ULONG NextToken(PLONG64 pvalue) { ULONG base; UCHAR chSymbol[SYMBOLSIZE]; UCHAR chSymbolString[SYMBOLSIZE]; UCHAR chPreSym[9] = {0}; ULONG cbSymbol = 0; BOOL fNumber = TRUE; BOOL fSymbol = TRUE; BOOL fForceReg = FALSE; BOOL fForceSym = FALSE; ULONG errNumber = 0; UCHAR ch; UCHAR chlow; UCHAR chtemp; UCHAR limit1 = '9'; UCHAR limit2 = '9'; BOOL fDigit = FALSE; ULONG64 value = 0; ULONG64 tmpvalue; ULONG index; PUCHAR pchCmdSave; BOOL UseDeferred; RD rd; ADDR addr; base = BaseDefault; // skip leading white space. do { ch = *pchCommand++; } while (ch == ' ' || ch == '\t'); chlow = (UCHAR)tolower(ch); // test for special character operators and register variable switch (chlow) { case '\0': case ';': pchCommand--; return EOL_CLASS; case '+': *pvalue = ADDOP_PLUS; return ADDOP_CLASS; case '-': *pvalue = ADDOP_MINUS; return ADDOP_CLASS; case '*': *pvalue = MULOP_MULT; return MULOP_CLASS; case '/': *pvalue = MULOP_DIVIDE; return MULOP_CLASS; case '%': *pvalue = MULOP_MOD; return MULOP_CLASS; case '&': *pvalue = LOGOP_AND; return LOGOP_CLASS; case '|': *pvalue = LOGOP_OR; return LOGOP_CLASS; case '^': *pvalue = LOGOP_XOR; return LOGOP_CLASS; case '=': if (*pchCommand == '=') { pchCommand++; } *pvalue = LRELOP_EQ; return LRELOP_CLASS; case '>': *pvalue = LRELOP_GT; return LRELOP_CLASS; case '<': *pvalue = LRELOP_LT; return LRELOP_CLASS; case '!': if (*pchCommand != '=') { break; } pchCommand++; *pvalue = LRELOP_NE; return LRELOP_CLASS; case '~': *pvalue = UNOP_NOT; return UNOP_CLASS; case '(': return LPAREN_CLASS; case ')': return RPAREN_CLASS; case '[': return LBRACK_CLASS; case ']': return RBRACK_CLASS; case '.': #ifdef _PPC_ // Modified to test for .# to all .symbol (added if{}break; IBMCDB if (*(pchCommand + 1) >= '0' && *(pchCommand + 1) <= '9') { #endif GetRegPCValue(&tempAddr); *pvalue = Flat(tempAddr); addrExpression = Type(tempAddr); return NUMBER_CLASS; #ifdef _PPC_ } else { fForceSym = TRUE; fForceReg = FALSE; fNumber = FALSE; break; } #endif case ':': *pvalue = MULOP_SEG; return MULOP_CLASS; case '`': *pvalue = MULOP_64; return MULOP_CLASS; } // special prefixes - '@' for register - '!' for symbol if (chlow == '@' || chlow == '!' || (chlow == '.' && *(pchCommand + 1) == '.')) { // CDB fForceReg = (BOOL)(chlow == '@'); fForceSym = (BOOL)!fForceReg; fNumber = FALSE; ch = *pchCommand++; chlow = (UCHAR)tolower(ch); } // if string is followed by '!', but not '!=', // then it is a module name and treat as text pchCmdSave = pchCommand; while ((chlow >= 'a' && chlow <= 'z') || (chlow >= '0' && chlow <= '9') || (chlow == '_') || (chlow == '$') || (chlow == '.')) { chlow = (UCHAR)tolower(*pchCommand); pchCommand++; } // treat as symbol if a nonnull string is followed by '!', // but not '!=' if (chlow == '!' && *pchCommand != '=' && pchCmdSave != pchCommand) { fNumber = FALSE; } pchCommand = pchCmdSave; chlow = (UCHAR)tolower(ch); // ch was NOT modified if (fNumber) { if (chlow == '\'') { *pvalue = 0; while (TRUE) { ch = *pchCommand++; if (!ch) { *pvalue = ERR_SYNTAX; return ERROR_CLASS; } if (ch == '\'') { if (*pchCommand != '\'') { break; } ch = *pchCommand++; } else if (ch == '\\') { ch = *pchCommand++; } *pvalue = (*pvalue << 8) | ch; } return NUMBER_CLASS; } // if first character is a decimal digit, it cannot // be a symbol. leading '0' implies octal, except // a leading '0x' implies hexadecimal. if (chlow >= '0' && chlow <= '9') { if (fForceReg) { *pvalue = ERR_SYNTAX; return ERROR_CLASS; } fSymbol = FALSE; if (chlow == '0') { ch = *pchCommand++; chlow = (UCHAR)tolower(ch); if (chlow == 'x') { base = 16; ch = *pchCommand++; chlow = (UCHAR)tolower(ch); fDigit = TRUE; } else if (chlow == 'n' || chlow == 't') { base = 10; ch = *pchCommand++; chlow = (UCHAR)tolower(ch); } else { base = 8; fDigit = TRUE; } } } // a number can start with a letter only if base is // hexadecimal and it is a hexadecimal digit 'a'-'f'. else if ((chlow < 'a' || chlow > 'f') || base != 16) { fNumber = FALSE; } // set limit characters for the appropriate base. if (base == 8) { limit1 = '7'; } if (base == 16) { limit2 = 'f'; } } // perform processing while character is a letter, // digit, underscore, or dollar-sign. while ((chlow >= 'a' && chlow <= 'z') || (chlow >= '0' && chlow <= '9') || (chlow == '_') || (chlow == '$') || (chlow == '.')) { // if possible number, test if within proper range, // and if so, accumulate sum. if (fNumber) { if ((chlow >= '0' && chlow <= limit1) || (chlow >= 'a' && chlow <= limit2)) { fDigit = TRUE; tmpvalue = value * base; if (tmpvalue < value) { errNumber = ERR_OVERFLOW; } chtemp = (UCHAR)(chlow - '0'); if (chtemp > 9) { chtemp -= 'a' - '0' - 10; } value = tmpvalue + (ULONG)chtemp; if (value < tmpvalue) { errNumber = ERR_OVERFLOW; } } else { fNumber = FALSE; errNumber = ERR_SYNTAX; } } if (fSymbol) { if (cbSymbol < 9) { chPreSym[cbSymbol] = chlow; } if (cbSymbol < SYMBOLSIZE - 1) { chSymbol[cbSymbol++] = ch; } } ch = *pchCommand++; chlow = (UCHAR)tolower(ch); } // back up pointer to first character after token. pchCommand--; if (cbSymbol < 9) { chPreSym[cbSymbol] = '\0'; } if (MptProcessorType == mptix86) { // catch segment overrides here if (!fForceReg && ch == ':') { for (index = 0; index < SEGREGSIZE; index++) { if (!strncmp((PSTR)chPreSym, SegRegs[index], 2)) { fForceReg = TRUE; fSymbol = FALSE; break; } } } } // if fForceReg, check for register name and return // success or failure if (fForceReg) { if ((*pvalue = GetRegString(chPreSym, &rd)) != -1) { return (rd.rt & rtPC) ? REG_PC_CLASS : REG_CLASS; } else { *pvalue = ERR_BADREG; return ERROR_CLASS; } } // test if number if (fNumber && !errNumber && fDigit) { *pvalue = value; return NUMBER_CLASS; } // next test for reserved word and symbol string if (fSymbol && !fForceReg) { // check lowercase string in chPreSym for text operator // or register name. // otherwise, return symbol value from name in chSymbol. if (!fForceSym && (cbSymbol == 2 || cbSymbol == 3)) { for (index = 0; index < RESERVESIZE; index++) { if (!strncmp((PSTR)chPreSym, (PSTR)Reserved[index].chRes, 3)) { *pvalue = Reserved[index].valueRes; return Reserved[index].classRes; } } } // start processing string as symbol chSymbol[cbSymbol] = '\0'; // test if symbol is a module name (followed by '!') // if so, get next token and treat as symbol if (PeekChar() == '!') { // chSymbolString holds the name of the symbol to be searched. // chSymbol holds the symbol image file name. pchCommand++; ch = PeekChar(); pchCommand++; cbSymbol = 0; while ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9') || (ch == '_') || (ch == '$') || (ch == '.')) { // IBMCDB chSymbolString[cbSymbol++] = ch; ch = *pchCommand++; } chSymbolString[cbSymbol] = '\0'; pchCommand--; if (cbSymbol == 0) { *pvalue = ERR_SYNTAX; return(ERROR_CLASS); } strcat((PSTR)chSymbol, "!"); strcat((PSTR)chSymbol, (PSTR)chSymbolString); if (GetOffsetFromSym((PSTR)chSymbol, &addr)) { *pvalue = GetAddrOff(addr); Type(tempAddr) = ADDR_FLAT | FLAT_COMPUTED; Flat(tempAddr) = Off(tempAddr) = *pvalue; addrExpression = Type(tempAddr); return SYMBOL_CLASS; } } else { if (cbSymbol == 0) { *pvalue = ERR_SYNTAX; return(ERROR_CLASS); } if (GetOffsetFromSym((PSTR)chSymbol, &addr)) { *pvalue = GetAddrOff(addr); Type(tempAddr) = ADDR_FLAT | FLAT_COMPUTED; Flat(tempAddr) = Off(tempAddr) = *pvalue; addrExpression = Type(tempAddr); return SYMBOL_CLASS; } // Quick test for register names too if (!fForceSym && (*pvalue = GetRegString(chPreSym, &rd)) != -1) { return (rd.rt & rtPC) ? REG_PC_CLASS : REG_CLASS; } } // symbol is undefined. // if a possible hex number, do not set the error type if (!fNumber) { errNumber = ERR_VARDEF; } } // last chance, undefined symbol and illegal number, // so test for register, will handle old format if (!fForceSym && (*pvalue = GetRegString(chPreSym, &rd)) != -1) { return (rd.rt & rtPC) ? REG_PC_CLASS : REG_CLASS; } // no success, so set error message and return *pvalue = (ULONG)errNumber; return ERROR_CLASS; }