/*++ Copyright (c) 1997-2000 Microsoft Corporation Module Name: tpl.cpp Abstract: template file interpreter for tracewpp.exe Author: Gor Nishanov (gorn) 03-Apr-1999 Revision History: Gor Nishanov (gorn) 03-Apr-1999 -- hacked together to prove that this can work GorN: 29-Sep-2000 - fix WHERE clause handling ToDo: Clean it up --*/ #define UNICODE #include #include #pragma warning(disable: 4786) #pragma warning(disable: 4503) // decorated length #pragma warning(disable: 4512) // cannot generate assignment #pragma warning(disable: 4100) // '_P' : unreferenced formal parameter #pragma warning(disable: 4018) // signed/unsigned mismatch #pragma warning(disable: 4267) // 'return' : conversion from 'size_t' to 'int' #include #include #include #include #pragma warning(disable: 4663 4018) #include //#pragma warning(default: 4018 4663) // signed/unsigned mismatch #pragma warning(default: 4100) #include "ezparse.h" #include "fieldtable.h" #include "tpl.h" LPCSTR FieldNames[] = { #define FIELD_NAME(f) #f, INSERT_FIELD_NAMES #undef FIELD_NAME }; OBJECT_MAP ObjectMap; typedef std::map FIELD_MAP; FIELD_MAP FieldMap; void PopulateFieldMap() { #define FIELD_NAME(_name_) FieldMap[#_name_] = fid_ ## _name_; INSERT_FIELD_NAMES #undef FIELD_NAME FIELD_MAP::iterator i; } //////////////////////////////////////////////////////////////////////////////////////////// struct LoopVar : FieldHolder { Enumerator * Enum; std::string Name; LoopVar() {} DWORD PrintField(int fieldId, FILE* f, const Enumerator** pEnum) const { return Enum->GetData()->PrintField(fieldId, f, pEnum); } }; /////////////////////////////////////////////////////////////////////////////////////////// char Delimiter = '`'; std::string COMMENT("*"); std::string FORALL("FORALL"); std::string ENDFOR("ENDFOR"); std::string IF("IF"); std::string ENDIF("ENDIF"); std::string DELIMITER("DELIMITER"); std::string INCLUDE("INCLUDE"); std::string ENV("ENV"); typedef enum Action { actText, actVar, actLoop, actIf, actInclude, actLiteralString, } Action; #pragma warning(disable: 4201) // nonstandard extension used : nameless struct/union struct Chunk { struct { Action action : 8; UCHAR level : 8; SHORT loopEnd:16; }; union { struct { LPCSTR textBeg; LPCSTR textEnd; }; struct { FieldHolder * p; FieldId FieldNo; std::string Filter; }; }; Enumerator* getEnum() { const Enumerator* Enum; p->PrintField(FieldNo, 0, &Enum); return (Enumerator*)Enum; } void printField(FILE* out) const { p->PrintField(FieldNo, out, 0); } Chunk(){} // to make vector happy Chunk (Action Act, FieldHolder* fh, FieldId fid, int lvl, const std::string& filter): action(Act),FieldNo(fid),p(fh),level((UCHAR)lvl),Filter(filter) {} Chunk (FieldHolder* fh, FieldId fid):action(actVar),FieldNo(fid),p(fh) {} Chunk (LPCSTR b, LPCSTR e):action(actText),textBeg(b),textEnd(e) {} Chunk (Action act, LPCSTR b, LPCSTR e):action(act),textBeg(b),textEnd(e) {} explicit Chunk(std::string const& Text):action(actLiteralString),Filter(Text) {} }; #define MAX_LOOP_LEVEL 127 struct TemplateProcessor { LoopVar Loop[MAX_LOOP_LEVEL]; std::vector Chunks; void RunIt(int beg, int end, FILE* out) { for(int i = beg; i < end; ) { switch(Chunks[i].action) { case actLiteralString: { fwrite(Chunks[i].Filter.c_str(), Chunks[i].Filter.length(), 1, out ); ++i; break; } case actText: { for(LPCSTR p = Chunks[i].textBeg; p < Chunks[i].textEnd; ++p) { if (*p != '\r') putc(*p, out); } ++i; break; } case actVar: { Chunks[i].printField(out); ++i; break; } case actIf: { if (!Chunks[i].p->Hidden(Chunks[i].Filter)) { RunIt(i+1, Chunks[i].loopEnd, out); } i = Chunks[i].loopEnd; break; } case actLoop: { Enumerator * Enum = Chunks[i].getEnum(); Loop[Chunks[i].level].Enum = Enum; for(Enum->Reset(Chunks[i].Filter); Enum->Valid(); Enum->Next(Chunks[i].Filter) ) { RunIt(i+1, Chunks[i].loopEnd, out); } delete Enum; i = Chunks[i].loopEnd; break; } case actInclude: { ProcessTemplate(Chunks[i].textBeg, Chunks[i].textEnd, out); ++i; break; } } } } void DoId(LPCSTR q, LPCSTR p, FieldId& fid, FieldHolder*& fh) { LPCSTR dot = q; while (q < p && isspace(*q)) ++q; while (q < p && isspace(p[-1])) --p; while (dot < p && *dot != '.') ++dot; std::string ObjectName(q, dot); OBJECT_MAP::iterator it = ObjectMap.find( ObjectName ); if (it == ObjectMap.end()) { ReportError("Var not found: %s\n", ObjectName.c_str() ); exit(1); } else { std::string FieldName; if (dot == p) { fid = (FieldId)fid___default__; FieldName.assign("__default__"); } else { ++dot; while (p < dot && isspace(*dot)) ++dot; FieldName.assign(dot,p); FIELD_MAP::iterator fit = FieldMap.find( FieldName.c_str() ); if (fit == FieldMap.end()) { ReportError("FieldNotFound: %s.%s\n", ObjectName.c_str(), FieldName.c_str() ); exit(1); } else { fid = fit->second; } } } fh = it->second; } void DoVar(LPCSTR q, LPCSTR p) { FieldHolder* fh; FieldId fid; DoId(q,p, fid, fh); Chunks.push_back( Chunk(fh, fid) ); } void DoLoop(int loopLevel, LPCSTR beg, LPCSTR end) { FieldHolder* fh; FieldId fid; std::string LoopVar; std::string LoopSet; std::string Filter; LPCSTR p,q; p = beg+6; while (p < end && isspace(*p)) ++p; q = p; while(p < end && !isspace(*p)) ++p; LoopVar.assign(q,p); Loop[loopLevel].Name = LoopVar; p += 4; while (p < end && isspace(*p)) ++p; q = p; while(p < end && !isspace(*p)) ++p; DoId(q,p, fid, fh); LoopSet.assign(q, p); p += 7; while (p < end && isspace(*p)) ++p; q = p; if (q < end) { p = end; while(p > q && isspace(*--p)); if (p < end && !isspace(*p)) ++p; } Filter.assign(q,p); Flood("FORALL %s IN %s WHERE %s\n", LoopVar.c_str(), LoopSet.c_str(), Filter.c_str()); ObjectMap[LoopVar] = &Loop[loopLevel]; Chunks.push_back( Chunk(actLoop, fh, fid, loopLevel, Filter) ); } void DoIf(int loopLevel, LPCSTR beg, LPCSTR end) { FieldHolder* fh; FieldId fid; std::string Object; std::string Filter; LPCSTR p,q; p = beg+3; while (p < end && isspace(*p)) ++p; q = p; while(p < end && !isspace(*p)) ++p; DoId(q,p, fid, fh); // Split id // Object.assign(q, p); while (p < end && isspace(*p)) ++p; while (p < end && isspace(end[-1]) ) --end; Filter.assign(p,end); Flood("IF %s %s\n", Object.c_str(), Filter.c_str() ); Chunks.push_back( Chunk(actIf, fh, fid, loopLevel, Filter) ); } DWORD CompileAndRun( IN LPCSTR begin, IN LPCSTR end, IN FILE* out ) { LPCSTR p = begin, PlainText = begin; int loop = -1; int loopLevel = -1; bool comment; Chunks.erase(Chunks.begin(), Chunks.end()); Chunks.reserve(128); for(;;) { LPCSTR q; for(;;) { if (p == end) { Chunks.push_back( Chunk(PlainText, p) ); goto done; } if (*p == Delimiter) break; ++p; } q = ++p; comment = (p < end && *p == '*'); for(;;) { if (p == end) { ReportError("Unmatched delimiters\n"); exit(1); } if (*p == Delimiter) { if (comment) { if (p[-1] == '*') break; } else { break; } } ++p; } if (q-1 > PlainText) { Chunks.push_back( Chunk(PlainText, q-1) ); } if (p == q) { // PERFPERF If the previous chunk was a text, we can extend it Chunks.push_back( Chunk(q-1, p) ); } else { std::string x(q,p); if (x.compare(0, IF.size(), IF) == 0) { int previous = loop; // KLUDGE merge with FORALL if (loopLevel == MAX_LOOP_LEVEL) { ReportError("Too many nested blocks!\n"); exit(1); } ++loopLevel; loop = static_cast( Chunks.size() ); DoIf(loopLevel, q,p); if (previous >= 32765) { ReportError("Too many chunks. Make loopEnd a UINT, %d\n", previous); exit(1); } Chunks.back().loopEnd = (SHORT)previous; while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else if (x.compare(0, FORALL.size(), FORALL) == 0) { int previous = loop; if (loopLevel == MAX_LOOP_LEVEL) { ReportError("Too many nested loops!\n"); exit(1); } ++loopLevel; loop = static_cast( Chunks.size() ); DoLoop(loopLevel, q,p); if (previous >= 32765) { ReportError("Too many chunks. Make loopEnd a UINT, %d\n", previous); exit(1); } Chunks.back().loopEnd = (SHORT)previous; while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else if (x.compare(0, DELIMITER.size(), DELIMITER) == 0 && x.size() > 10) { Delimiter = x[10]; } else if (x.compare(0, ENV.size(), ENV) == 0) { // we need to replace this field with // the value of the specified env variable LPCSTR val = getenv( std::string(q+4,p).c_str() ); if (val != NULL) { Chunks.push_back( Chunk(std::string(val)) ); } } else if (x.compare(0, COMMENT.size(), COMMENT) == 0) { // eat away the whitespace while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else if (x.compare(0, INCLUDE.size(), INCLUDE) == 0) { // Doesn't work Chunks.push_back( Chunk(actInclude, q + INCLUDE.size() + 1, p) ); } else if ((x.compare(0, ENDIF.size(), ENDIF) == 0) || (x.compare(0, ENDFOR.size(), ENDFOR) == 0)) { // KLUDGE make them separate or rename both to simply END // End will be set in ENDFOR // if (loop == -1) { ReportError("ENDFOR without FORALL\n"); exit(1); } ObjectMap.erase( Loop[loopLevel].Name ); int previous = Chunks[loop].loopEnd; // BUGBUG have a check that confirms that we didn't run out of space Chunks[loop].loopEnd = (SHORT)Chunks.size(); loop = previous; --loopLevel; while (p+1 < end && (p[1] == '\n' || p[1] == '\r') ) ++p; } else { DoVar(q, p); } } PlainText = ++p; } done:; if (loop != -1) { ReportError("No ENDFOR for loop, %d\n", loop); exit(1); } RunIt(0, static_cast( Chunks.size() ), out); return 0; } }; DWORD processTemplate( IN LPCSTR begin, IN LPCSTR end, IN EZPARSE_CALLBACK, IN PVOID Context, IN PEZPARSE_CONTEXT ) { FILE *out = (FILE*)Context; TemplateProcessor tpl; return tpl.CompileAndRun(begin,end,out); }