windows-nt/Source/XPSP1/NT/sdktools/trace/tracewpp/tpl.cpp
2020-09-26 16:20:57 +08:00

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