// Copyright (c) 1996-1999 Microsoft Corporation /* * state1.c - implements the state machine to track constructs */ #include "gpdparse.h" // ---- functions defined in state1.c ---- // BOOL BInterpretTokens( PTKMAP ptkmap, // pointer to tokenmap BOOL bFirstPass, // is this the first or 2nd time around? PGLOBL pglobl ) ; BOOL BprocessSpecialKeyword( PTKMAP ptkmap, // pointer to tokenmap BOOL bFirstPass, // is this the first or 2nd time around? PGLOBL pglobl ) ; BOOL BprocessSymbolKeyword( PTKMAP ptkmap, // pointer to current entry in tokenmap PGLOBL pglobl ) ; VOID VinitAllowedTransitions( PGLOBL pglobl ); BOOL BpushState( PTKMAP ptkmap, // pointer to current entry in tokenmap BOOL bFirstPass, PGLOBL pglobl ) ; BOOL BchangeState( PTKMAP ptkmap, // pointer to construct in tokenmap CONSTRUCT eConstruct, // this will induce a transition to NewState STATE stOldState, BOOL bSymbol, // should dwValue be saved as a SymbolID ? BOOL bFirstPass, PGLOBL pglobl ) ; DWORD DWregisterSymbol( PABSARRAYREF paarSymbol, // the symbol string to register CONSTRUCT eConstruct , // type of construct determines class of symbol. BOOL bCopy, // shall we copy paarSymbol to heap? May set DWORD dwFeatureID, // if you are registering an option symbol PGLOBL pglobl ) ; BOOL BaddAARtoHeap( PABSARRAYREF paarSrc, PARRAYREF parDest, DWORD dwAlign, PGLOBL pglobl) ; BOOL BwriteToHeap( OUT PDWORD pdwDestOff, // heap offset of dest string PBYTE pubSrc, // points to src string DWORD dwCnt, // num bytes to copy from src to dest. DWORD dwAlign, PGLOBL pglobl) ; DWORD DWsearchSymbolListForAAR( PABSARRAYREF paarSymbol, DWORD dwNodeIndex, PGLOBL pglobl) ; DWORD DWsearchSymbolListForID( DWORD dwSymbolID, // find node containing this ID. DWORD dwNodeIndex, PGLOBL pglobl ) ; BOOL BCmpAARtoAR( PABSARRAYREF paarStr1, PARRAYREF parStr2, PGLOBL pglobl ) ; BOOL BpopState( PGLOBL pglobl ) ; VOID VinitDictionaryIndex( PGLOBL pglobl ) ; VOID VcharSubstitution( PABSARRAYREF paarStr, BYTE ubTgt, BYTE ubReplcmnt, PGLOBL pglobl ) ; VOID VIgnoreBlock( PTKMAP ptkmap, BOOL bIgnoreBlock, PGLOBL pglobl ) ; // ---------------------------------------------------- // BOOL BInterpretTokens( PTKMAP ptkmap, // pointer to tokenmap BOOL bFirstPass, // is this the first or 2nd time around? PGLOBL pglobl ) { DWORD dwKeywordID ; KEYWORD_TYPE eType; WORD wEntry ; BOOL bStatus = FALSE ; if(bFirstPass) { // This bit of code creates a synthesized inputslot // which the UI code will interpret as the UseFormToTrayTable // BUG_BUG!!!: should be replaced by preprocessing // shortcuts etc. Or by an option in stdnames.gpd ABSARRAYREF aarSymbol ; DWORD dwFeaID ; aarSymbol.pub = "InputBin" ; // no way to keep this in ssync // with the global table. aarSymbol.dw = strlen(aarSymbol.pub) ; dwFeaID = DWregisterSymbol(&aarSymbol, CONSTRUCT_FEATURE, TRUE, INVALID_SYMBOLID, pglobl) ; if(dwFeaID != INVALID_SYMBOLID) { aarSymbol.pub = "FORMSOURCE" ; // no way to keep this in ssync // with the global table. aarSymbol.dw = strlen(aarSymbol.pub) ; dwFeaID = DWregisterSymbol(&aarSymbol, CONSTRUCT_OPTION, TRUE, dwFeaID, pglobl) ; ASSERT(dwFeaID == 0); // this option must be first. } } else { ARRAYREF arStrValue ; BOOL bPrevsExists ; if(!BwriteToHeap(&arStrValue.loOffset, "\0\0", 2, 4, pglobl) ) { bStatus = FALSE ; // heap overflow start over. } arStrValue.dwCount = 0 ; // create a NULL unicode string. BexchangeArbDataInFOATNode( 0 , // dwFea 0, // dwOption, offsetof(DFEATURE_OPTIONS, atrOptDisplayName ), sizeof(ARRAYREF), NULL, // previous contents of attribute node (PBYTE)&arStrValue, // new contents of attribute node. &bPrevsExists, // previous contents existed. FALSE, // access synthetic features pglobl ) ; } for(wEntry = 0 ; geErrorSev < ERRSEV_RESTART ; wEntry++) { // These ID's must be processed separately // because they do not index into the mainKeyword table. // The code for generic ID's will fail. dwKeywordID = ptkmap[wEntry].dwKeywordID ; if (dwKeywordID == gdwID_IgnoreBlock) { VIgnoreBlock(ptkmap + wEntry, TRUE, pglobl) ; continue ; } switch(dwKeywordID) { case (ID_EOF): { // BUG_BUG!!! Cleanup code here // integrity checking code: // check to see mdwCurStsPtr == 0 // and any other loose ends are tied. bStatus = (mdwCurStsPtr) ? (FALSE) : (TRUE); return(bStatus) ; } case (ID_NULLENTRY): { continue ; // does this work? // should drop to bottom of FOREVER for loop. } case (ID_SYMBOL): { bStatus = BprocessSymbolKeyword(ptkmap + wEntry, pglobl) ; continue ; // uses TKMF_SYMBOL_REGISTERED to track passes } case (ID_UNRECOGNIZED): { // if identified on first pass, won't pass this way again! if(bStatus = BidentifyAttributeKeyword(ptkmap + wEntry, pglobl) ) { dwKeywordID = ptkmap[wEntry].dwKeywordID ; break ; // fall out of switch statement // and into next switch. } if(bFirstPass) { vIdentifySource(ptkmap + wEntry, pglobl) ; ERR(("Warning, unrecognized keyword: %0.*s\n", \ ptkmap[wEntry].aarKeyword.dw, \ ptkmap[wEntry].aarKeyword.pub)); VIgnoreBlock(ptkmap + wEntry, FALSE, pglobl) ; // if this keyword is immediately // followed by open brace, ignore all // statements from there until the matching closing // brace. } continue ; } default : break ; } eType = mMainKeywordTable[dwKeywordID].eType ; switch (eType) { case (TY_CONSTRUCT): { if( CONSTRUCT_CLOSEBRACE == (CONSTRUCT)(mMainKeywordTable[dwKeywordID].dwSubType)) { bStatus = BpopState(pglobl) ; if(!bStatus) { vIdentifySource(ptkmap + wEntry, pglobl) ; ERR(("Unmatched closing brace!\n")); } } else { bStatus = BpushState(ptkmap + wEntry, bFirstPass, pglobl) ; if(!bStatus) { vIdentifySource(ptkmap + wEntry, pglobl) ; // ERR(("Fatal error parsing construct.\n")); // stack is now invalid. // in the future make parser smarter - // eject contents between braces. // geErrorType = ERRTY_SYNTAX ; // geErrorSev = ERRSEV_FATAL ; } } break ; } case (TY_ATTRIBUTE) : { if(!bFirstPass) // must wait till all attribute { // buffers are allocated. bStatus = BprocessAttribute(ptkmap + wEntry, pglobl) ; if(!bStatus) { vIdentifySource(ptkmap + wEntry, pglobl) ; } } break ; } case (TY_SPECIAL) : { bStatus = BprocessSpecialKeyword(ptkmap + wEntry, bFirstPass, pglobl) ; // don't really know if 2 passes { // are needed. if(!bStatus) { vIdentifySource(ptkmap + wEntry, pglobl) ; } } break ; } default: { vIdentifySource(ptkmap + wEntry, pglobl) ; ERR(("Internal Error: unrecognized keyword type! %0.*s.\n", ptkmap[wEntry].aarKeyword.dw, ptkmap[wEntry].aarKeyword.pub)); geErrorSev = ERRSEV_FATAL ; geErrorType = ERRTY_CODEBUG ; break ; } } } if(geErrorSev >= ERRSEV_RESTART) bStatus = FALSE ; return(bStatus) ; } BOOL BprocessSpecialKeyword( PTKMAP ptkmap, // pointer to tokenmap BOOL bFirstPass, // is this the first or 2nd time around? PGLOBL pglobl ) { DWORD dwKeywordID, dwOffset ; CONSTRUCT eSubType ; BOOL bStatus = FALSE ; STATE stState ; dwKeywordID = ptkmap->dwKeywordID ; eSubType = (SPECIAL)(mMainKeywordTable[dwKeywordID].dwSubType) ; dwOffset = mMainKeywordTable[dwKeywordID].dwOffset ; if(mdwCurStsPtr) stState = mpstsStateStack[mdwCurStsPtr - 1].stState ; else stState = STATE_ROOT ; switch(eSubType) { // note: for the record I despise special casing these // shortcuts. Ideally I could preprocess them // to convert // *TTFS: "font name" : // into // *TTFontSub: // { // *TTFontName: "font name" // *DevFontID: // } // the only glitch with this is if the same font is listed // multiple times in the GPD file, it will appear // in the TTFontSubTable multiple times. #if 0 case SPEC_FONTSUB : { ARRAYREF arFontname ; ABSARRAYREF aarFontname ; if(stState != STATE_ROOT) { vIdentifySource(ptkmap, pglobl) ; ERR(("The *TTFS keyword must reside at the root level.\n")); return(FALSE) ; } if(bFirstPass) { // parse string value and register as a symbol. if((ptkmap->dwFlags & TKMF_NOVALUE ) || !BparseString(&ptkmap->aarValue, &arFontname) ) { vIdentifySource(ptkmap, pglobl) ; ERR(("*TTFS fontname is not a valid string value.\n")); return(FALSE) ; } if(ptkmap->aarValue.pub[0] != ':') { ERR(("Colon delimiter expected after parsing fontname string for *TTFontSub.\n")) ; return(FALSE) ; } // a keyword with a composite value (VOID)BeatDelimiter(&ptkmap->aarValue, ":") ; // I know this will succeed! // paarValue should now contain the integer // fontID. Leave this for the 2nd pass. // convert arFontname to aar suitable for // Font registration. aarFontname.dw = arFontname.dwCount ; aarFontname.pub = arFontname.loOffset + mpubOffRef; // New version of DWregisterSymbol registers the entire // string - whitespaces and all. // // Note suppress copying symbol into Heap since // ParseString has already done that. ptkmap->dwValue = DWregisterSymbol(&aarFontname, CONSTRUCT_TTFONTSUBS, FALSE, pglobl ) ; if(ptkmap->dwValue != INVALID_SYMBOLID) { ptkmap->dwFlags |= TKMF_SYMBOL_REGISTERED ; } else { return(FALSE) ; } } else if(ptkmap->dwFlags & TKMF_SYMBOL_REGISTERED) // second pass, TTFONTSUBTABLE arrays allocated // for all successfully registered entrants. { PSYMBOLNODE psn ; DWORD dwDevFontID ; PTTFONTSUBTABLE pttft ; DWORD dwTTFontNameIndex ; psn = (PSYMBOLNODE) gMasterTable[MTI_SYMBOLTREE].pubStruct ; pttft = (PTTFONTSUBTABLE) gMasterTable[MTI_TTFONTSUBTABLE].pubStruct; pttft += ptkmap->dwValue ; // index correct element. dwTTFontNameIndex = DWsearchSymbolListForID(ptkmap->dwValue, mdwTTFontSymbols, pglobl) ; ASSERT(dwTTFontNameIndex != INVALID_INDEX) ; pttft->arTTFontName = psn[dwTTFontNameIndex].arSymbolName ; // if structure assignment is supported. bStatus = BparseInteger(&ptkmap->aarValue, &dwDevFontID, VALUE_INTEGER) ; if(bStatus) pttft->dwDevFontID = dwDevFontID ; else { // BUG_BUG! : Error parsing TTFontSub table entry // syntax error in devID. Dead codepath who cares? pttft->dwDevFontID = 0 ; // is this a good fallback? } } break; } #endif case SPEC_INVALID_COMBO: { if(bFirstPass) { bStatus = TRUE ; break; // do nothing on the FirstPass } bStatus = BparseInvalidCombination(&ptkmap->aarValue, dwOffset, pglobl) ; break; } case SPEC_INVALID_INS_COMBO: { if(bFirstPass) { bStatus = TRUE ; break; // do nothing on the FirstPass } bStatus = BparseInvalidInstallableCombination1(&ptkmap->aarValue, dwOffset, pglobl) ; break; } case SPEC_MEM_CONFIG_KB: // should already be replaced case SPEC_MEM_CONFIG_MB: // at parseKeyword default: { break ; } } return(bStatus) ; } BOOL BprocessSymbolKeyword( PTKMAP ptkmap, // pointer to current entry in tokenmap PGLOBL pglobl ) { // registering the TTFontNames as symbols allows // me to count the number of unique names and reserve // the proper amount of TTFONTSUBTABLE elements and // eliminates multiple instances of the same name. BOOL bStatus = FALSE ; STATE stState ; if(mdwCurStsPtr) stState = mpstsStateStack[mdwCurStsPtr - 1].stState ; else stState = STATE_ROOT ; switch (stState) { // note TTFontSubs now has its own keyword. // and is handled as a Special Keyword. default: { // assume its just VALUEMACRO state // or user-defined symbols from an undefined // keyword. // ignore these. bStatus = TRUE ; break ; } } return(bStatus); } VOID VinitAllowedTransitions( PGLOBL pglobl) { PSTATE pst ; PBOOL pb ; WORD wS, wC, wA ; // default initializer is STATE_INVALID for(wS = 0 ; wS < STATE_LAST ; wS++) { for(wC = 0 ; wC < CONSTRUCT_LAST ; wC++) gastAllowedTransitions[wS][wC] = STATE_INVALID ; } pst = gastAllowedTransitions[STATE_ROOT] ; pst[CONSTRUCT_UIGROUP] = STATE_UIGROUP; pst[CONSTRUCT_FEATURE] = STATE_FEATURE; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_ROOT; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_FONTCART] = STATE_FONTCART; pst[CONSTRUCT_TTFONTSUBS] = STATE_TTFONTSUBS; pst[CONSTRUCT_OEM] = STATE_OEM; pst = gastAllowedTransitions[STATE_UIGROUP] ; pst[CONSTRUCT_UIGROUP] = STATE_UIGROUP; pst[CONSTRUCT_FEATURE] = STATE_FEATURE; pst = gastAllowedTransitions[STATE_FEATURE] ; pst[CONSTRUCT_OPTION] = STATE_OPTIONS; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_FEATURE; pst = gastAllowedTransitions[STATE_OPTIONS] ; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_OPTION; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_OEM] = STATE_OEM; pst = gastAllowedTransitions[STATE_SWITCH_ROOT] ; pst[CONSTRUCT_CASE] = STATE_CASE_ROOT; pst[CONSTRUCT_DEFAULT] = STATE_DEFAULT_ROOT; pst = gastAllowedTransitions[STATE_SWITCH_FEATURE] ; pst[CONSTRUCT_CASE] = STATE_CASE_FEATURE; pst[CONSTRUCT_DEFAULT] = STATE_DEFAULT_FEATURE; pst = gastAllowedTransitions[STATE_SWITCH_OPTION] ; pst[CONSTRUCT_CASE] = STATE_CASE_OPTION; pst[CONSTRUCT_DEFAULT] = STATE_DEFAULT_OPTION; pst = gastAllowedTransitions[STATE_CASE_ROOT] ; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_ROOT; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_OEM] = STATE_OEM; pst = gastAllowedTransitions[STATE_DEFAULT_ROOT] ; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_ROOT; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_OEM] = STATE_OEM; pst = gastAllowedTransitions[STATE_CASE_FEATURE] ; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_FEATURE; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_OEM] = STATE_OEM; pst = gastAllowedTransitions[STATE_DEFAULT_FEATURE] ; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_FEATURE; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_OEM] = STATE_OEM; pst = gastAllowedTransitions[STATE_CASE_OPTION] ; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_OPTION; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_OEM] = STATE_OEM; pst = gastAllowedTransitions[STATE_DEFAULT_OPTION] ; pst[CONSTRUCT_SWITCH] = STATE_SWITCH_OPTION; pst[CONSTRUCT_COMMAND] = STATE_COMMAND; pst[CONSTRUCT_OEM] = STATE_OEM; // ------------------------------------------------------ // // now initialize allowed attributes table: // which attributes are allowed in each state. // default initializer is FALSE -- No attributes are allowed // in any state. for(wS = 0 ; wS < STATE_LAST ; wS++) { for(wA = 0 ; wA < ATT_LAST ; wA++) { gabAllowedAttributes[wS][wA] = FALSE ; } } pb = gabAllowedAttributes[STATE_ROOT] ; pb[ATT_GLOBAL_ONLY] = TRUE ; pb[ATT_GLOBAL_FREEFLOAT] = TRUE ; pb = gabAllowedAttributes[STATE_CASE_ROOT] ; pb[ATT_GLOBAL_FREEFLOAT] = TRUE ; pb = gabAllowedAttributes[STATE_DEFAULT_ROOT] ; pb[ATT_GLOBAL_FREEFLOAT] = TRUE ; pb = gabAllowedAttributes[STATE_OPTIONS] ; pb[ATT_GLOBAL_FREEFLOAT] = TRUE ; pb[ATT_LOCAL_FEATURE_FF] = TRUE ; pb[ATT_LOCAL_OPTION_ONLY] = TRUE ; pb[ATT_LOCAL_OPTION_FF] = TRUE ; pb = gabAllowedAttributes[STATE_CASE_OPTION] ; pb[ATT_GLOBAL_FREEFLOAT] = TRUE ; pb[ATT_LOCAL_FEATURE_FF] = TRUE ; pb[ATT_LOCAL_OPTION_FF] = TRUE ; pb = gabAllowedAttributes[STATE_DEFAULT_OPTION] ; pb[ATT_GLOBAL_FREEFLOAT] = TRUE ; pb[ATT_LOCAL_FEATURE_FF] = TRUE ; pb[ATT_LOCAL_OPTION_FF] = TRUE ; pb = gabAllowedAttributes[STATE_FEATURE] ; pb[ATT_LOCAL_FEATURE_ONLY] = TRUE ; pb[ATT_LOCAL_FEATURE_FF] = TRUE ; pb = gabAllowedAttributes[STATE_CASE_FEATURE] ; pb[ATT_LOCAL_FEATURE_FF] = TRUE ; pb = gabAllowedAttributes[STATE_DEFAULT_FEATURE] ; pb[ATT_LOCAL_FEATURE_FF] = TRUE ; pb = gabAllowedAttributes[STATE_COMMAND] ; pb[ATT_LOCAL_COMMAND_ONLY] = TRUE ; pb = gabAllowedAttributes[STATE_FONTCART] ; pb[ATT_LOCAL_FONTCART_ONLY] = TRUE ; pb = gabAllowedAttributes[STATE_TTFONTSUBS] ; pb[ATT_LOCAL_TTFONTSUBS_ONLY] = TRUE ; pb = gabAllowedAttributes[STATE_OEM] ; pb[ATT_LOCAL_OEM_ONLY] = TRUE ; } BOOL BpushState( PTKMAP ptkmap, // pointer to current entry in tokenmap BOOL bFirstPass, PGLOBL pglobl ) { // this function assumes (eType == TY_CONSTRUCT) DWORD dwKeywordID ; CONSTRUCT eSubType ; BOOL bStatus = FALSE ; STATE stOldState, stNewState ; if(mdwCurStsPtr >= mdwMaxStackDepth) { if(ERRSEV_RESTART > geErrorSev) { ERR(("Exceeded max state stack depth. Restarting\n")); geErrorSev = ERRSEV_RESTART ; geErrorType = ERRTY_MEMORY_ALLOCATION ; gdwMasterTabIndex = MTI_STSENTRY ; } return(FALSE); } dwKeywordID = ptkmap->dwKeywordID ; eSubType = (CONSTRUCT)(mMainKeywordTable[dwKeywordID].dwSubType) ; if(mdwCurStsPtr) stOldState = mpstsStateStack[mdwCurStsPtr - 1].stState ; else stOldState = STATE_ROOT ; switch (eSubType) { // note CONSTRUCT_CLOSEBRACE already processed // by PopState(). case (CONSTRUCT_OPENBRACE): { vIdentifySource(ptkmap, pglobl) ; ERR(("OpenBrace encountered without accompanying construct keyword.\n")); geErrorType = ERRTY_SYNTAX ; geErrorSev = ERRSEV_FATAL ; break ; } case (CONSTRUCT_FEATURE): case (CONSTRUCT_OPTION): case (CONSTRUCT_SWITCH): case (CONSTRUCT_COMMAND): // commandID's already registered. case (CONSTRUCT_CASE): case (CONSTRUCT_FONTCART): case (CONSTRUCT_TTFONTSUBS): { bStatus = BchangeState(ptkmap, eSubType, stOldState, TRUE, bFirstPass, pglobl) ; break ; } case (CONSTRUCT_UIGROUP): { // BUG_BUG!!!!! incomplete. no reqest for this. } case (CONSTRUCT_DEFAULT): case (CONSTRUCT_OEM): { bStatus = BchangeState(ptkmap, eSubType, stOldState, FALSE, bFirstPass, pglobl) ; break ; } default: { bStatus = TRUE ; // its ok to ignore some keywords. break ; } } return(bStatus) ; } /* dead code. VOID VsetbTTFontSubs( IN PABSARRAYREF paarValue) { // BUG_BUG!!!!!: // exactly what is supposed to happen ? register // synthesized symbol ? gbTTFontSubs = FALSE ; if( BeatSurroundingWhiteSpaces(paarValue) ) { if(paarValue->dw == 2 && ! strncmp(paarValue->pub, "ON", 2)) gbTTFontSubs = TRUE ; else if(paarValue->dw != 3 || strncmp(paarValue->pub, "OFF", 3)) { BUG_BUG!: value must be either "ON" or "OFF". } } } */ BOOL BchangeState( PTKMAP ptkmap, // pointer to construct in tokenmap CONSTRUCT eConstruct, // this will induce a transition to NewState STATE stOldState, BOOL bSymbol, // should dwValue be saved as a SymbolID ? BOOL bFirstPass, PGLOBL pglobl ) { BOOL bStatus = FALSE ; STATE stNewState ; // was checked in PushState, but never hurts to check // in the same function that consumes the resource. if(mdwCurStsPtr >= mdwMaxStackDepth) { if(ERRSEV_RESTART > geErrorSev) { geErrorSev = ERRSEV_RESTART ; geErrorType = ERRTY_MEMORY_ALLOCATION ; gdwMasterTabIndex = MTI_STSENTRY ; } return(FALSE); } stNewState = gastAllowedTransitions[stOldState][eConstruct] ; if(stNewState == STATE_INVALID) { vIdentifySource(ptkmap, pglobl) ; ERR(("the Construct %0.*s is not allowed within the state: %s\n", ptkmap->aarKeyword.dw, ptkmap->aarKeyword.pub, gpubStateNames[stOldState])); // (convert stOldState // and eConstruct to meaningful string) // This is a fatal error since parser cannot second // guess the problem. The parser's job is to report // as many legitemate problems as possible not to // create as useable binary in spite of all the syntax // errors. if(ERRSEV_FATAL > geErrorSev) { geErrorSev = ERRSEV_FATAL ; geErrorType = ERRTY_SYNTAX ; } return(FALSE); } else { if(bFirstPass) { // verify open brace follows construct and discard it. DWORD dwKeywordID ; PTKMAP ptkmapTmp = ptkmap + 1 ; dwKeywordID = ptkmapTmp->dwKeywordID ; while(dwKeywordID == ID_NULLENTRY) // skip nulls, comments etc. { dwKeywordID = (++ptkmapTmp)->dwKeywordID ; } if(dwKeywordID < ID_SPECIAL && mMainKeywordTable[dwKeywordID].eType == TY_CONSTRUCT && mMainKeywordTable[dwKeywordID].dwSubType == CONSTRUCT_OPENBRACE ) { ptkmapTmp->dwKeywordID = ID_NULLENTRY ; } else { vIdentifySource(ptkmap, pglobl) ; ERR(("open brace expected after construct: %0.*s but was not found\n", ptkmap->aarKeyword.dw , ptkmap->aarKeyword.pub )) ; geErrorType = ERRTY_SYNTAX ; geErrorSev = ERRSEV_FATAL ; return(FALSE); } } if(bSymbol) { // BUG_BUG: verify tokenmap.dwFlags set to SYMBOLID before // assuming dwValue is a symbol. An error here // is a parser bug. // dwValue is initialized when dwFlag is set. // further assert is pointless. // perform multiple passes. The first pass // registers symbols and counts number of arrays // to allocate, 2nd pass fills arrays. SymbolID // now serves as array index. if(!(ptkmap->dwFlags & TKMF_SYMBOL_REGISTERED)) { if(!bFirstPass) { vIdentifySource(ptkmap, pglobl) ; ERR(("symbol registration failed twice for: *%0.*s.\n", ptkmap->aarValue.dw, ptkmap->aarValue.pub)); return(FALSE) ; // retry } if((ptkmap->dwFlags & TKMF_NOVALUE ) || ! BeatSurroundingWhiteSpaces(&ptkmap->aarValue) ) { vIdentifySource(ptkmap, pglobl) ; ERR(("syntax error in symbol name.\n")); ptkmap->dwValue = INVALID_SYMBOLID ; return(FALSE) ; } ptkmap->dwValue = DWregisterSymbol(&ptkmap->aarValue, eConstruct, TRUE, INVALID_SYMBOLID, pglobl) ; if(ptkmap->dwValue != INVALID_SYMBOLID) { ptkmap->dwFlags |= TKMF_SYMBOL_REGISTERED ; } else { vIdentifySource(ptkmap, pglobl) ; ERR(("symbol registration failed: *%0.*s.\n", ptkmap->aarValue.dw, ptkmap->aarValue.pub)); return(FALSE) ; // retry } } else // second pass, DFEATURE_OPTION arrays allocated. { if(eConstruct == CONSTRUCT_SWITCH) { PDFEATURE_OPTIONS pfo ; pfo = (PDFEATURE_OPTIONS) gMasterTable[MTI_DFEATURE_OPTIONS].pubStruct ; pfo[ptkmap->dwValue].bReferenced = TRUE ; // this tells me this Feature is being referenced // by switch statement, hence the feature had better // be PICKONE. Sanity checks will later verify // this assumption. } if(eConstruct == CONSTRUCT_FEATURE || eConstruct == CONSTRUCT_SWITCH) { // BUG_BUG!!!!!: (DCR 454049) // Note, the same Feature symbol cannot appear // twice in the stack for any reason. // A sanity check is needed. // if duplicate symbol found in stack, // "a Nested Switch Construct refers to the // same feature as an enclosing switch or Feature // construct. This makes no sense." } } bStatus = TRUE ; mpstsStateStack[mdwCurStsPtr].dwSymbolID = ptkmap->dwValue ; } else bStatus = TRUE ; if(bStatus) { mpstsStateStack[mdwCurStsPtr].stState = stNewState ; mdwCurStsPtr++ ; } } return(bStatus) ; } DWORD DWregisterSymbol( PABSARRAYREF paarSymbol, // the symbol string to register CONSTRUCT eConstruct , // type of construct determines class of symbol. BOOL bCopy, // shall we copy paarSymbol to heap? May set // to FALSE only if paarSymbol already points // to a heap object! DWORD dwFeatureID, // if you are registering an option symbol // and you already know the feature , pass it in // here. Otherwise set to INVALID_SYMBOLID PGLOBL pglobl ) /* this function registers the entire string specified in paarSymbol. The caller must isolate the string. */ { // returns SymbolID, a zero indexed ordinal // for extra speed we may hash string PSYMBOLNODE psn ; DWORD dwCurNode, dwSymbolID = INVALID_SYMBOLID; // bCopy = TRUE; //check. Force BUDs to be the same. if(!paarSymbol->dw) { ERR(("DWregisterSymbol: No symbol value supplied.\n")); return(INVALID_SYMBOLID); // report failure. } psn = (PSYMBOLNODE) gMasterTable[MTI_SYMBOLTREE].pubStruct ; switch(eConstruct) { case CONSTRUCT_FEATURE : // since forward references are allowed case CONSTRUCT_SWITCH : // it cannot be assumed that references case CONSTRUCT_FONTCART: // will be to registered symbols . case CONSTRUCT_COMMAND: case CONSTRUCT_TTFONTSUBS: case CONSTRUCT_BLOCKMACRO: case CONSTRUCT_MACROS: case CONSTRUCT_PREPROCESSOR: { PDWORD pdwSymbolClass ; pdwSymbolClass = (PDWORD)gMasterTable[MTI_SYMBOLROOT].pubStruct ; if(eConstruct == CONSTRUCT_FONTCART) pdwSymbolClass += SCL_FONTCART ; else if(eConstruct == CONSTRUCT_TTFONTSUBS) pdwSymbolClass += SCL_TTFONTNAMES ; else if(eConstruct == CONSTRUCT_COMMAND) pdwSymbolClass += SCL_COMMANDNAMES ; else if(eConstruct == CONSTRUCT_BLOCKMACRO) pdwSymbolClass += SCL_BLOCKMACRO; else if(eConstruct == CONSTRUCT_MACROS) pdwSymbolClass += SCL_VALUEMACRO; else if(eConstruct == CONSTRUCT_PREPROCESSOR) pdwSymbolClass += SCL_PPDEFINES; else pdwSymbolClass += SCL_FEATURES ; if(*pdwSymbolClass == INVALID_INDEX) { // register this symbol now. if(!BallocElementFromMasterTable(MTI_SYMBOLTREE, &dwCurNode, pglobl)) { // we have run out of symbol nodes! return(INVALID_SYMBOLID); // report failure. } if(bCopy) { if(!BaddAARtoHeap(paarSymbol, &(psn[dwCurNode].arSymbolName), 1, pglobl)) return(INVALID_SYMBOLID); // report failure. } else { // derive one from the other. psn[dwCurNode].arSymbolName.dwCount = paarSymbol->dw ; psn[dwCurNode].arSymbolName.loOffset = (DWORD)(paarSymbol->pub - mpubOffRef); } dwSymbolID = psn[dwCurNode].dwSymbolID = 0 ; // first symbol // in list. psn[dwCurNode].dwNextSymbol = INVALID_INDEX ; // no previous // symbols exist. psn[dwCurNode].dwSubSpaceIndex = INVALID_INDEX ; // no // option symbols exist. *pdwSymbolClass = dwCurNode ; // now we have a registered // symbol } else { // search list for matching symbol. dwSymbolID = DWsearchSymbolListForAAR(paarSymbol, *pdwSymbolClass, pglobl) ; if(dwSymbolID != INVALID_SYMBOLID) // found ; // nothing else is needed, just return. else // not found, must register. { if(!BallocElementFromMasterTable(MTI_SYMBOLTREE, &dwCurNode, pglobl)) { return(INVALID_SYMBOLID); // report failure. } // tack new symbol onto head of list. if(bCopy) { if(!BaddAARtoHeap(paarSymbol, &(psn[dwCurNode].arSymbolName), 1, pglobl) ) return(INVALID_SYMBOLID); // report failure. } else { // derive one from the other. psn[dwCurNode].arSymbolName.dwCount = paarSymbol->dw ; psn[dwCurNode].arSymbolName.loOffset = (DWORD)(paarSymbol->pub - mpubOffRef); } dwSymbolID = psn[dwCurNode].dwSymbolID = psn[*pdwSymbolClass].dwSymbolID + 1; // increment last ID psn[dwCurNode].dwNextSymbol = *pdwSymbolClass ; // link to previous symbols. psn[dwCurNode].dwSubSpaceIndex = INVALID_INDEX ; // no // option symbols exist. *pdwSymbolClass = dwCurNode ; // points to most recent // symbol } } break; } case CONSTRUCT_OPTION : case CONSTRUCT_CASE : { DWORD dwFeatureIndex, // node containing this symbolID. dwRootOptions ; // root of option symbols. #if PARANOID if(mdwCurStsPtr) { // this safety check almost superfluous. stPrevsState = mpstsStateStack[mdwCurStsPtr - 1].State ; if(eConstruct == CONSTRUCT_OPTION && stPrevsState != STATE_FEATURE) { ERR(("DWregisterSymbol: option or case construct is not enclosed within feature or switch !\n")); return(INVALID_SYMBOLID); // report failure. } if(eConstruct == CONSTRUCT_CASE && (stPrevsState != STATE_SWITCH_ROOT || (stPrevsState != STATE_SWITCH_FEATURE || (stPrevsState != STATE_SWITCH_OPTION ) ) { ERR(("DWregisterSymbol: case construct is not enclosed within switch !\n")); return(INVALID_SYMBOLID); // report failure. } #endif // Boldly assume top of stack contains a featureID. // see paranoid code for all assumptions made. if(dwFeatureID == INVALID_SYMBOLID) dwFeatureID = mpstsStateStack[mdwCurStsPtr - 1].dwSymbolID ; dwFeatureIndex = DWsearchSymbolListForID(dwFeatureID, mdwFeatureSymbols, pglobl) ; // PARANOID BUG_BUG: coding error if symbolID isn't found! ASSERT(dwFeatureIndex != INVALID_INDEX) ; dwRootOptions = psn[dwFeatureIndex].dwSubSpaceIndex ; // found root of option symbols! if(dwRootOptions == INVALID_INDEX) { if(!BallocElementFromMasterTable(MTI_SYMBOLTREE, &dwCurNode, pglobl)) { return(INVALID_SYMBOLID); // report failure. } // register this symbol now. if(bCopy) { if(!BaddAARtoHeap(paarSymbol, &(psn[dwCurNode].arSymbolName), 1, pglobl) ) return(INVALID_SYMBOLID); // report failure. } else { // derive one from the other. psn[dwCurNode].arSymbolName.dwCount = paarSymbol->dw ; psn[dwCurNode].arSymbolName.loOffset = (DWORD)(paarSymbol->pub - mpubOffRef); } dwSymbolID = psn[dwCurNode].dwSymbolID = 0 ; // first symbol in list. psn[dwCurNode].dwNextSymbol = INVALID_INDEX ; // no previous symbols exist. psn[dwCurNode].dwSubSpaceIndex = INVALID_INDEX ; // option symbols have no subspace. psn[dwFeatureIndex].dwSubSpaceIndex = dwRootOptions = dwCurNode ; // now we have a registered symbol } else { // search list for matching symbol. dwSymbolID = DWsearchSymbolListForAAR(paarSymbol, dwRootOptions, pglobl) ; if(dwSymbolID != INVALID_SYMBOLID) // found ; // nothing else is needed, just return. else // not found, must register. { if(!BallocElementFromMasterTable(MTI_SYMBOLTREE, &dwCurNode, pglobl)) { return(INVALID_SYMBOLID); // report failure. } // tack new symbol onto head of list. if(bCopy) { if(!BaddAARtoHeap(paarSymbol, &(psn[dwCurNode].arSymbolName), 1, pglobl) ) return(INVALID_SYMBOLID); // report failure. } else { // derive one from the other. psn[dwCurNode].arSymbolName.dwCount = paarSymbol->dw ; psn[dwCurNode].arSymbolName.loOffset = (DWORD)(paarSymbol->pub - mpubOffRef); } dwSymbolID = psn[dwCurNode].dwSymbolID = psn[dwRootOptions].dwSymbolID + 1; // increment last ID psn[dwCurNode].dwNextSymbol = dwRootOptions ; // link to previous symbols. psn[dwCurNode].dwSubSpaceIndex = INVALID_INDEX ; // option symbols have no subspace. psn[dwFeatureIndex].dwSubSpaceIndex = dwRootOptions = dwCurNode ; // points to most recent symbol } } #if PARANOID } else { // BUG_BUG: ERR(("DWregisterSymbol: option or case construct is not enclosed within feature or switch !\n")); return(INVALID_SYMBOLID); // report failure. } #endif break; } default: { // PARANOID BUG_BUG: ERR(("DWregisterSymbol: construct has no symbol class.\n")); return(INVALID_SYMBOLID); // report failure. } } return(dwSymbolID) ; } BOOL BaddAARtoHeap( PABSARRAYREF paarSrc, PARRAYREF parDest, DWORD dwAlign, // write data to address that is a multiple of dwAlign PGLOBL pglobl) // this function copies a non NULL terminated string fragment // referenced by an 'aar' // into the communal STRINGHEAP and returns an 'ar' // which describes the location of the copy. { PBYTE pubSrc, pubDest ; DWORD dwCnt ; // num bytes to copy. // legal values for dwAlign are 1 and 4. mloCurHeap = (mloCurHeap + dwAlign - 1) / dwAlign ; mloCurHeap *= dwAlign ; pubSrc = paarSrc->pub ; dwCnt = paarSrc->dw ; pubDest = mpubOffRef + mloCurHeap ; // is there enough room in the heap ? // don't forget the NULL. if(mloCurHeap + dwCnt + 1 > mdwMaxHeap) { // log error to debug output. // register error so appropriate action is taken. if(ERRSEV_RESTART > geErrorSev) { geErrorSev = ERRSEV_RESTART ; geErrorType = ERRTY_MEMORY_ALLOCATION ; gdwMasterTabIndex = MTI_STRINGHEAP ; } return(FALSE); } parDest->dwCount = dwCnt ; parDest->loOffset = mloCurHeap; // offset only! memcpy(pubDest, pubSrc, dwCnt); // the copy may also fail for random reasons! pubDest[dwCnt] = '\0' ; // Add Null termination. mloCurHeap += (dwCnt + 1); // update heap ptr. return(TRUE) ; } BOOL BwriteToHeap( OUT PDWORD pdwDestOff, // heap offset of dest string PBYTE pubSrc, // points to src string DWORD dwCnt, // num bytes to copy from src to dest. DWORD dwAlign, // write data to address that is a multiple of dwAlign PGLOBL pglobl) // this function copies dwCnt bytes from pubSrc to // top of heap and writes the offset of the destination string // to pdwDestOff. Nothing is changed if FAILS. // Warning! No Null termination is added to string. { PBYTE pubDest ; // legal values for dwAlign are 1 and 4. mloCurHeap = (mloCurHeap + dwAlign - 1) / dwAlign ; mloCurHeap *= dwAlign ; pubDest = mpubOffRef + mloCurHeap ; // is there enough room in the heap ? if(mloCurHeap + dwCnt > mdwMaxHeap) { // log error to debug output. // register error so appropriate action is taken. ERR(("BwriteToHeap: out of heap - restarting.\n")); if(ERRSEV_RESTART > geErrorSev) { geErrorSev = ERRSEV_RESTART ; geErrorType = ERRTY_MEMORY_ALLOCATION ; gdwMasterTabIndex = MTI_STRINGHEAP ; } return(FALSE); } memcpy(pubDest, pubSrc, dwCnt); // the copy may also fail for random reasons! *pdwDestOff = mloCurHeap ; mloCurHeap += (dwCnt); // update heap ptr. return(TRUE) ; } DWORD DWsearchSymbolListForAAR( PABSARRAYREF paarSymbol, DWORD dwNodeIndex, PGLOBL pglobl) // given a 'aar' to a string representing a symbol, search // the SymbolList beginning at dwNodeIndex for this symbol. // Return its symbolID if found, else return the INVALID_SYMBOLID. { PSYMBOLNODE psn ; psn = (PSYMBOLNODE) gMasterTable[MTI_SYMBOLTREE].pubStruct ; for( ; dwNodeIndex != INVALID_INDEX ; dwNodeIndex = psn[dwNodeIndex].dwNextSymbol) { if(BCmpAARtoAR(paarSymbol, &(psn[dwNodeIndex].arSymbolName), pglobl) ) return(psn[dwNodeIndex].dwSymbolID); // string matches ! } return(INVALID_SYMBOLID); } DWORD DWsearchSymbolListForID( DWORD dwSymbolID, // find node containing this ID. DWORD dwNodeIndex, // start search here. PGLOBL pglobl) // given a symbolID, search the SymbolList beginning at dwNodeIndex // for this symbol. // If found return the node index which contains the requested symbolID, // else return INVALID_INDEX. { PSYMBOLNODE psn ; psn = (PSYMBOLNODE) gMasterTable[MTI_SYMBOLTREE].pubStruct ; for( ; dwNodeIndex != INVALID_INDEX ; dwNodeIndex = psn[dwNodeIndex].dwNextSymbol) { if(psn[dwNodeIndex].dwSymbolID == dwSymbolID) return(dwNodeIndex); // ID matches ! } return(INVALID_INDEX); } BOOL BCmpAARtoAR( PABSARRAYREF paarStr1, PARRAYREF parStr2, PGLOBL pglobl) // Compares two strings, one referenced by 'aar' the other // referenced by 'ar'. Returns TRUE if they match, FALSE // otherwise. { if(paarStr1->dw != parStr2->dwCount) return(FALSE) ; // Lengths don't even match! if(strncmp(paarStr1->pub, mpubOffRef + parStr2->loOffset , paarStr1->dw)) return(FALSE) ; return(TRUE) ; } BOOL BpopState( PGLOBL pglobl) { if(mdwCurStsPtr) { mdwCurStsPtr-- ; return(TRUE); } else { // ERR(("Unmatched closing brace!\n")); // message moved to caller. // in the future make parser smarter. geErrorType = ERRTY_SYNTAX ; geErrorSev = ERRSEV_FATAL ; return(FALSE); } } VOID VinitDictionaryIndex( PGLOBL pglobl) /* MainKeywordTable[] is assumed to be divided into a NonAttributes section and several Attributes sections with pstrKeyword = NULL dividing the sections. The end of the table is also terminated by a NULL entry. This function initializes the grngDictionary[] which serves as an index into the main keyword Table. */ { DWORD dwI, // keywordTable Index dwSect ; // RNGDICTIONARY Index PRANGE prng ; prng = (PRANGE)(gMasterTable[MTI_RNGDICTIONARY].pubStruct) ; for(dwI = dwSect = 0 ; dwSect < END_ATTR ; dwSect++, dwI++) { prng[dwSect].dwStart = dwI ; for( ; mMainKeywordTable[dwI].pstrKeyword ; dwI++ ) ; prng[dwSect].dwEnd = dwI ; // one past the last entry } } VOID VcharSubstitution( PABSARRAYREF paarStr, BYTE ubTgt, BYTE ubReplcmnt, PGLOBL pglobl) { DWORD dwI ; for(dwI = 0 ; dwI < paarStr->dw ; dwI++) { if(paarStr->pub[dwI] == ubTgt) paarStr->pub[dwI] = ubReplcmnt ; } } VOID VIgnoreBlock( PTKMAP ptkmap, BOOL bIgnoreBlock, PGLOBL pglobl) // This boolean determines the message that will be issued. { /* Should we ignore? check that first non-NULL entry after wCurEntry is open brace if so ignore all entries up to EOF or matching closing brace. */ DWORD dwKeywordID, dwDepth ; // depth relative to *IgnoreBlock ptkmap->dwKeywordID = ID_NULLENTRY ; // neutralize keyword regardless. ptkmap++ ; dwKeywordID = ptkmap->dwKeywordID ; while(dwKeywordID == ID_NULLENTRY) // skip nulls, comments etc. { dwKeywordID = (++ptkmap)->dwKeywordID ; } if(dwKeywordID < ID_SPECIAL && mMainKeywordTable[dwKeywordID].eType == TY_CONSTRUCT && mMainKeywordTable[dwKeywordID].dwSubType == CONSTRUCT_OPENBRACE ) { ptkmap->dwKeywordID = ID_NULLENTRY ; dwDepth = 1 ; ptkmap++ ; if(bIgnoreBlock) { if(gdwVerbosity >= 4) ERR(("Note: Ignoring block following *IgnoreBlock.\n")); } else ERR(("Ignoring block following unrecognized keyword.\n")); } else { if(bIgnoreBlock && gdwVerbosity >= 2) ERR(("Note: Brace delimited block not found after *IgnoreBlock.\n")); return ; // do nothing. } while(dwDepth) { dwKeywordID = ptkmap->dwKeywordID ; if(dwKeywordID == ID_EOF) { ERR(("Ignoring Block: EOF encountered before closing brace.\n")); return ; // stop regardless! } if(dwKeywordID < ID_SPECIAL) { KEYWORD_TYPE eType; CONSTRUCT eSubType ; eType = mMainKeywordTable[dwKeywordID].eType ; if(eType == TY_CONSTRUCT) { eSubType = (CONSTRUCT)(mMainKeywordTable[dwKeywordID].dwSubType) ; if(eSubType == CONSTRUCT_OPENBRACE) dwDepth++ ; else if( eSubType == CONSTRUCT_CLOSEBRACE) dwDepth-- ; } } ptkmap->dwKeywordID = ID_NULLENTRY ; ptkmap++ ; } return ; }