478 lines
12 KiB
C++
478 lines
12 KiB
C++
/*++
|
|
|
|
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 <stdio.h>
|
|
#include <windows.h>
|
|
|
|
#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 <xmemory>
|
|
#include <xstring>
|
|
#include <set>
|
|
#include <map>
|
|
#pragma warning(disable: 4663 4018)
|
|
#include <vector>
|
|
//#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<std::string, FieldId, strless> 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<Chunk> 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<int>( 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<int>( 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<int>( 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);
|
|
}
|
|
|