ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/slghscan.l

672 lines
22 KiB
Text

/* ###
* IP: GHIDRA
* NOTE: flex skeletons are NOT bound by flex's BSD license
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
%{
#include "slgh_compile.hh"
#include "slghparse.tab.hh"
#define yywrap() 1
#define YY_SKIP_YYWRAP
/* If we are building don't include unistd.h */
/* flex provides us with this macro for turning it off */
#ifdef _WIN32
#define YY_NO_UNISTD_H
static int isatty (int fildes) { return 0; }
#endif
struct FileStreamState {
YY_BUFFER_STATE lastbuffer; // Last lex buffer corresponding to the stream
FILE *file; // The NEW file stream
};
extern SleighCompile *slgh;
int4 last_preproc; // lex state before last preprocessing erasure
int4 actionon; // whether '&' '|' and '^' are treated as actionon in pattern section
int4 withsection = 0; // whether we are between the 'with' keyword and its open brace '{'
vector<FileStreamState> filebuffers;
vector<int4> ifstack;
int4 negative_if = -1;
void preproc_error(const string &err)
{
slgh->reportError((const Location *)0, err);
cerr << "Terminating due to error in preprocessing" << endl;
exit(1);
}
void check_to_endofline(istream &s)
{ // Make sure there is nothing to the end of the line
s >> ws;
if (!s.eof())
if (s.peek() != '#')
preproc_error("Extra characters in preprocessor directive");
}
string read_identifier(istream &s)
{ // Read a proper identifier from the stream
s >> ws; // Skip any whitespace
string res;
while(!s.eof()) {
char tok = s.peek();
if (isalnum(tok) || (tok == '_')) {
s >> tok;
res += tok;
}
else
break;
}
return res;
}
void preprocess_string(istream &s,string &res)
{ // Grab string surrounded by double quotes from stream or call preprocess_error
int4 val;
s >> ws; // Skip any whitespace
val = s.get();
if (val != '\"')
preproc_error("Expecting double quoted string");
val = s.get();
while((val != '\"')&&(val>=0)) {
res += (char)val;
val = s.get();
}
if (val != '\"')
preproc_error("Missing terminating double quote");
}
extern int4 preprocess_if(istream &s); // Forward declaration for recursion
int4 read_defined_operator(istream &s)
{ // We have seen a -defined- keyword in an if or elif
// Read macro name used as input, return 1 if it is defined
char tok = ' ';
string macroname;
s >> ws >> tok;
if (tok != '(')
preproc_error("Badly formed \"defined\" operator");
macroname = read_identifier(s);
int4 res = slgh->getPreprocValue(macroname,macroname) ? 1 : 0;
s >> ws >> tok;
if (tok != ')')
preproc_error("Badly formed \"defined\" operator");
return res;
}
int4 read_boolean_clause(istream &s)
{ // We have seen an if or elif
// return 1 if condition is true or else 0
s >> ws;
if (s.peek()=='(') { // Parenthetical expression spawns recursion
int4 val = s.get();
int4 res = preprocess_if(s);
s >> ws;
val = s.get();
if (val != ')')
preproc_error("Unbalanced parentheses");
return res;
}
// Otherwise we must have a normal comparison operator
string lhs,rhs,comp;
if (s.peek()=='\"') // Read left-hand side string
preprocess_string(s,lhs);
else {
lhs = read_identifier(s);
if (lhs == "defined")
return read_defined_operator(s);
if (!slgh->getPreprocValue(lhs,lhs))
preproc_error("Could not find preprocessor macro "+lhs);
}
char tok;
s >> tok; // Read comparison symbol
comp += tok;
s >> tok;
comp += tok;
s >> ws;
if (s.peek()=='\"') // Read right-hand side string
preprocess_string(s,rhs);
else {
rhs = read_identifier(s);
if (!slgh->getPreprocValue(rhs,rhs))
preproc_error("Could not find preprocessor macro "+rhs);
}
if (comp == "==")
return (lhs == rhs) ? 1 : 0;
else if (comp=="!=")
return (lhs != rhs) ? 1 : 0;
else
preproc_error("Syntax error in condition");
return 0;
}
int4 preprocess_if(istream &s)
{
int4 res = read_boolean_clause(s);
s >> ws;
while((!s.eof())&&(s.peek()!=')')) {
string boolop;
char tok;
s >> tok;
boolop += tok;
s >> tok;
boolop += tok;
int4 res2 = read_boolean_clause(s);
if (boolop == "&&")
res = res & res2;
else if (boolop == "||")
res = res | res2;
else if (boolop == "^^")
res = res ^ res2;
else
preproc_error("Syntax error in expression");
s >> ws;
}
return res;
}
void expand_preprocmacros(string &str)
{
string::size_type pos;
string::size_type lastpos = 0;
pos = str.find("$(",lastpos);
if (pos == string::npos)
return;
string res;
for(;;) {
if (pos == string::npos) {
res += str.substr(lastpos);
str = res;
return;
}
else {
res += str.substr(lastpos,(pos-lastpos));
string::size_type endpos = str.find(')',pos+2);
if (endpos == string::npos) {
preproc_error("Unterminated macro in string");
break;
}
string macro = str.substr(pos+2, endpos - (pos+2));
string value;
if (!slgh->getPreprocValue(macro,value)) {
preproc_error("Unknown preprocessing macro "+macro);
break;
}
res += value;
lastpos = endpos + 1;
}
pos = str.find("$(",lastpos);
}
}
int4 preprocess(int4 cur_state,int4 blank_state)
{
string str(yytext);
string::size_type pos = str.find('#');
if (pos != string::npos)
str.erase(pos);
istringstream s(str);
string type;
if (cur_state != blank_state)
last_preproc = cur_state;
s.get(); // Skip the preprocessor marker
s >> type;
if (type == "include") {
if (negative_if == -1) { // Not in the middle of a false if clause
filebuffers.push_back(FileStreamState()); // Save state of current file
filebuffers.back().lastbuffer = YY_CURRENT_BUFFER;
filebuffers.back().file = (FILE *)0;
s >> ws;
string fname;
preprocess_string(s,fname);
expand_preprocmacros(fname);
slgh->parseFromNewFile(fname);
fname = slgh->grabCurrentFilePath();
yyin = fopen(fname.c_str(),"r");
if (yyin == (FILE *)0)
preproc_error("Could not open included file "+fname);
filebuffers.back().file = yyin;
yy_switch_to_buffer( yy_create_buffer(yyin, YY_BUF_SIZE) );
check_to_endofline(s);
}
}
else if (type == "define") {
if (negative_if == -1) {
string varname;
string value;
varname = read_identifier(s); // Get name of variable being defined
s >> ws;
if (s.peek() == '\"')
preprocess_string(s,value);
else
value = read_identifier(s);
if (varname.size()==0)
preproc_error("Error in preprocessor definition");
slgh->setPreprocValue(varname,value);
check_to_endofline(s);
}
}
else if (type == "undef") {
if (negative_if == -1) {
string varname;
varname = read_identifier(s); // Name of variable to undefine
if (varname.size()==0)
preproc_error("Error in preprocessor undef");
slgh->undefinePreprocValue(varname);
check_to_endofline(s);
}
}
else if (type=="ifdef") {
string varname;
varname = read_identifier(s);
if (varname.size()==0)
preproc_error("Error in preprocessor ifdef");
string value;
int4 truth = (slgh->getPreprocValue(varname,value)) ? 1 : 0;
ifstack.push_back(truth);
check_to_endofline(s);
}
else if (type=="ifndef") {
string varname;
varname = read_identifier(s);
if (varname.size()==0)
preproc_error("Error in preprocessor ifndef");
string value;
int4 truth = (slgh->getPreprocValue(varname,value)) ? 0 : 1; // flipped from ifdef
ifstack.push_back(truth);
check_to_endofline(s);
}
else if (type=="if") {
int4 truth = preprocess_if(s);
if (!s.eof())
preproc_error("Unbalanced parentheses");
ifstack.push_back(truth);
}
else if (type=="elif") {
if (ifstack.empty())
preproc_error("elif without preceding if");
if ((ifstack.back()&2)!=0) // We have already seen an else clause
preproc_error("elif follows else");
if ((ifstack.back()&4)!=0) // We have already seen a true elif clause
ifstack.back() = 4; // don't include any other elif clause
else if ((ifstack.back()&1)!=0) // Last clause was a true if
ifstack.back() = 4; // don't include this elif
else {
int4 truth = preprocess_if(s);
if (!s.eof())
preproc_error("Unbalanced parentheses");
if (truth==0)
ifstack.back() = 0;
else
ifstack.back() = 5;
}
}
else if (type=="endif") {
if (ifstack.empty())
preproc_error("preprocessing endif without matching if");
ifstack.pop_back();
check_to_endofline(s);
}
else if (type=="else") {
if (ifstack.empty())
preproc_error("preprocessing else without matching if");
if ((ifstack.back()&2)!=0)
preproc_error("second else for one if");
if ((ifstack.back()&4)!=0) // Seen a true elif clause before
ifstack.back() = 6;
else if (ifstack.back()==0)
ifstack.back() = 3;
else
ifstack.back() = 2;
check_to_endofline(s);
}
else
preproc_error("Unknown preprocessing directive: "+type);
if (negative_if >= 0) { // We were in a false state
if (negative_if+1 < ifstack.size())
return blank_state; // false state is still deep in stack
else // false state is popped off or is current and changed
negative_if = -1;
}
if (ifstack.empty()) return last_preproc;
if ((ifstack.back()&1)==0) {
negative_if = ifstack.size()-1;
return blank_state;
}
return last_preproc;
}
void preproc_macroexpand(void)
{
filebuffers.push_back(FileStreamState());
filebuffers.back().lastbuffer = YY_CURRENT_BUFFER;
filebuffers.back().file = (FILE *)0;
string macro(yytext);
macro.erase(0,2);
macro.erase(macro.size()-1,1);
string value;
if (!slgh->getPreprocValue(macro,value))
preproc_error("Unknown preprocessing macro "+macro);
yy_switch_to_buffer( yy_scan_string( value.c_str() ) );
slgh->parsePreprocMacro();
}
int4 find_symbol(void) {
string * newstring = new string(yytext);
SleighSymbol *sym = slgh->findSymbol(*newstring);
if (sym == (SleighSymbol *)0) {
yylval.str = newstring;
return STRING;
}
delete newstring;
switch(sym->getType()) {
case SleighSymbol::section_symbol:
yylval.sectionsym = (SectionSymbol *)sym;
return SECTIONSYM;
case SleighSymbol::space_symbol:
yylval.spacesym = (SpaceSymbol *)sym;
return SPACESYM;
case SleighSymbol::token_symbol:
yylval.tokensym = (TokenSymbol *)sym;
return TOKENSYM;
case SleighSymbol::userop_symbol:
yylval.useropsym = (UserOpSymbol *)sym;
return USEROPSYM;
case SleighSymbol::value_symbol:
yylval.valuesym = (ValueSymbol *)sym;
return VALUESYM;
case SleighSymbol::valuemap_symbol:
yylval.valuemapsym = (ValueMapSymbol *)sym;
return VALUEMAPSYM;
case SleighSymbol::name_symbol:
yylval.namesym = (NameSymbol *)sym;
return NAMESYM;
case SleighSymbol::varnode_symbol:
yylval.varsym = (VarnodeSymbol *)sym;
return VARSYM;
case SleighSymbol::bitrange_symbol:
yylval.bitsym = (BitrangeSymbol *)sym;
return BITSYM;
case SleighSymbol::varnodelist_symbol:
yylval.varlistsym = (VarnodeListSymbol *)sym;
return VARLISTSYM;
case SleighSymbol::operand_symbol:
yylval.operandsym = (OperandSymbol *)sym;
return OPERANDSYM;
case SleighSymbol::start_symbol:
yylval.startsym = (StartSymbol *)sym;
return STARTSYM;
case SleighSymbol::end_symbol:
yylval.endsym = (EndSymbol *)sym;
return ENDSYM;
case SleighSymbol::subtable_symbol:
yylval.subtablesym = (SubtableSymbol *)sym;
return SUBTABLESYM;
case SleighSymbol::macro_symbol:
yylval.macrosym = (MacroSymbol *)sym;
return MACROSYM;
case SleighSymbol::label_symbol:
yylval.labelsym = (LabelSymbol *)sym;
return LABELSYM;
case SleighSymbol::epsilon_symbol:
yylval.specsym = (SpecificSymbol *)sym;
return SPECSYM;
case SleighSymbol::context_symbol:
yylval.contextsym = (ContextSymbol *)sym;
return CONTEXTSYM;
case SleighSymbol::dummy_symbol:
break;
}
return -1; // Should never reach here
}
int4 scan_number(char *numtext,YYSTYPE *lval,bool signednum)
{
uintb val;
if (numtext[0] == '0' && numtext[1] == 'b') {
val = 0;
numtext += 2;
while ((*numtext) != 0) {
val <<= 1;
if (*numtext == '1') {
val |= 1;
}
++numtext;
}
} else {
istringstream s(numtext);
s.unsetf(ios::dec | ios::hex | ios::oct);
s >> val;
if (!s)
return BADINTEGER;
}
if (signednum) {
lval->big = new intb(val);
return INTB;
}
lval->i = new uintb(val);
return INTEGER;
}
%}
%x defblock
%x macroblock
%x print
%x pattern
%x sem
%x preproc
%%
^@[^\n]*\n? { slgh->nextLine(); BEGIN( preprocess(INITIAL,preproc) ); }
\$\([a-zA-Z0-9_.][a-zA-Z0-9_.]*\) { preproc_macroexpand(); }
[(),\-] { yylval.ch = yytext[0]; return yytext[0]; }
\: { BEGIN(print); slgh->calcContextLayout(); yylval.ch = yytext[0]; return yytext[0]; }
\{ { BEGIN(sem); yylval.ch = yytext[0]; return yytext[0]; }
#.*$
[\r\ \t\v]+
\n { slgh->nextLine(); }
macro { BEGIN(macroblock); return MACRO_KEY; }
define { BEGIN(defblock); return DEFINE_KEY; }
attach { BEGIN(defblock); slgh->calcContextLayout(); return ATTACH_KEY; }
with { BEGIN(pattern); withsection = 1; slgh->calcContextLayout(); return WITH_KEY; }
[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); }
. { return yytext[0]; }
<macroblock>^@[^\n]*\n? { slgh->nextLine(); BEGIN( preprocess(macroblock,preproc) ); }
<macroblock>\$\([a-zA-Z0-9_.][a-zA-Z0-9_.]*\) { preproc_macroexpand(); }
<macroblock>[(),] { yylval.ch = yytext[0]; return yytext[0]; }
<macroblock>\{ { BEGIN(sem); return yytext[0]; }
<macroblock>[a-zA-Z_.][a-zA-Z0-9_.]* { yylval.str = new string(yytext); return STRING; }
<macroblock>[\r\ \t\v]+
<macroblock>\n { slgh->nextLine(); }
<macroblock>. { return yytext[0]; }
<defblock>^@[^\n]*\n? { slgh->nextLine(); BEGIN( preprocess(defblock,preproc) ); }
<defblock>\$\([a-zA-Z0-9_.][a-zA-Z0-9_.]*\) { preproc_macroexpand(); }
<defblock>[(),=:\[\]] { yylval.ch = yytext[0]; return yytext[0]; }
<defblock>\; { BEGIN(INITIAL); yylval.ch = yytext[0]; return yytext[0]; }
<defblock>space { return SPACE_KEY; }
<defblock>type { return TYPE_KEY; }
<defblock>ram_space { return RAM_KEY; }
<defblock>default { return DEFAULT_KEY; }
<defblock>register_space { return REGISTER_KEY; }
<defblock>token { return TOKEN_KEY; }
<defblock>context { return CONTEXT_KEY; }
<defblock>bitrange { return BITRANGE_KEY; }
<defblock>signed { return SIGNED_KEY; }
<defblock>noflow { return NOFLOW_KEY; }
<defblock>hex { return HEX_KEY; }
<defblock>dec { return DEC_KEY; }
<defblock>endian { return ENDIAN_KEY; }
<defblock>alignment { return ALIGN_KEY; }
<defblock>big { return BIG_KEY; }
<defblock>little { return LITTLE_KEY; }
<defblock>size { return SIZE_KEY; }
<defblock>wordsize { return WORDSIZE_KEY; }
<defblock>offset { return OFFSET_KEY; }
<defblock>names { return NAMES_KEY; }
<defblock>values { return VALUES_KEY; }
<defblock>variables { return VARIABLES_KEY; }
<defblock>pcodeop { return PCODEOP_KEY; }
<defblock>#.*$
<defblock>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); }
<defblock>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,false); }
<defblock>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,false); }
<defblock>0b[01]+ { return scan_number(yytext,&yylval,false); }
<defblock>\"([^\"]|\"\")*\" { yylval.str = new string(yytext+1,strlen(yytext)-2); return STRING; }
<defblock>[\r\ \t\v]+
<defblock>\n { slgh->nextLine(); }
<defblock>. { return yytext[0]; }
<print>^@[^\n]*\n? { slgh->nextLine(); BEGIN( preprocess(print,preproc) ); }
<print>\$\([a-zA-Z0-9_.][a-zA-Z0-9_.]*\) { preproc_macroexpand(); }
<print>[~!@#$%&*()\-=+\[\]{}|;:<>?,/0-9] { yylval.ch = yytext[0]; return CHAR; }
<print>\^ { yylval.ch = '^'; return '^'; }
<print>is { BEGIN(pattern); actionon=0; return IS_KEY; }
<print>[a-zA-Z_.][a-zA-Z0-9_.]* { yylval.str = new string(yytext); return SYMBOLSTRING; }
<print>\"([^\"]|\"\")*\" { yylval.str = new string(yytext+1,strlen(yytext)-2); return STRING; }
<print>[\r\ \t\v]+ { yylval.ch = ' '; return ' '; }
<print>\n { slgh->nextLine(); return ' '; }
<print>. { return yytext[0]; }
<pattern>^@[^\n]*\n? { slgh->nextLine(); BEGIN( preprocess(pattern,preproc) ); }
<pattern>\$\([a-zA-Z0-9_.][a-zA-Z0-9_.]*\) { preproc_macroexpand(); }
<pattern>\{ { BEGIN((withsection==1) ? INITIAL:sem); withsection=0; yylval.ch = yytext[0]; return yytext[0]; }
<pattern>unimpl { BEGIN(INITIAL); return OP_UNIMPL; }
<pattern>globalset { return GLOBALSET_KEY; }
<pattern>\>\> { return OP_RIGHT; }
<pattern>\<\< { return OP_LEFT; }
<pattern>\!\= { return OP_NOTEQUAL; }
<pattern>\<\= { return OP_LESSEQUAL; }
<pattern>\>\= { return OP_GREATEQUAL; }
<pattern>\$and { return OP_AND; }
<pattern>\$or { return OP_OR; }
<pattern>\$xor { return OP_XOR; }
<pattern>\.\.\. { return ELLIPSIS_KEY; }
<pattern>\[ { actionon = 1; yylval.ch = yytext[0]; return yytext[0]; }
<pattern>\] { actionon = 0; yylval.ch = yytext[0]; return yytext[0]; }
<pattern>\& { yylval.ch = yytext[0]; return (actionon==0) ? yytext[0] : OP_AND; }
<pattern>\| { yylval.ch = yytext[0]; return (actionon==0) ? yytext[0] : OP_OR; }
<pattern>\^ { return OP_XOR; }
<pattern>[=(),:;+\-*/~<>] { yylval.ch = yytext[0]; return yytext[0]; }
<pattern>#.*$
<pattern>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); }
<pattern>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,true); }
<pattern>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,true); }
<pattern>0b[01]+ { return scan_number(yytext,&yylval,true); }
<pattern>[\r\ \t\v]+
<pattern>\n { slgh->nextLine(); }
<pattern>. { return yytext[0]; }
<sem>^@[^\n]*\n? { slgh->nextLine(); BEGIN( preprocess(sem,preproc) ); }
<sem>\$\([a-zA-Z0-9_.][a-zA-Z0-9_.]*\) { preproc_macroexpand(); }
<sem>\} { BEGIN(INITIAL); yylval.ch = yytext[0]; return yytext[0]; }
<sem>\|\| { return OP_BOOL_OR; }
<sem>\&\& { return OP_BOOL_AND; }
<sem>\^\^ { return OP_BOOL_XOR; }
<sem>\>\> { return OP_RIGHT; }
<sem>\<\< { return OP_LEFT; }
<sem>\=\= { return OP_EQUAL; }
<sem>\!\= { return OP_NOTEQUAL; }
<sem>\<\= { return OP_LESSEQUAL; }
<sem>\>\= { return OP_GREATEQUAL; }
<sem>s\/ { return OP_SDIV; }
<sem>s\% { return OP_SREM; }
<sem>s\>\> { return OP_SRIGHT; }
<sem>s\< { return OP_SLESS; }
<sem>s\> { return OP_SGREAT; }
<sem>s\<\= { return OP_SLESSEQUAL; }
<sem>s\>\= { return OP_SGREATEQUAL; }
<sem>f\+ { return OP_FADD; }
<sem>f\- { return OP_FSUB; }
<sem>f\* { return OP_FMULT; }
<sem>f\/ { return OP_FDIV; }
<sem>f\=\= { return OP_FEQUAL; }
<sem>f\!\= { return OP_FNOTEQUAL; }
<sem>f\< { return OP_FLESS; }
<sem>f\> { return OP_FGREAT; }
<sem>f\<\= { return OP_FLESSEQUAL; }
<sem>f\>\= { return OP_FGREATEQUAL; }
<sem>zext { return OP_ZEXT; }
<sem>carry { return OP_CARRY; }
<sem>borrow { return OP_BORROW; }
<sem>sext { return OP_SEXT; }
<sem>scarry { return OP_SCARRY; }
<sem>sborrow { return OP_SBORROW; }
<sem>nan { return OP_NAN; }
<sem>abs { return OP_ABS; }
<sem>sqrt { return OP_SQRT; }
<sem>ceil { return OP_CEIL; }
<sem>floor { return OP_FLOOR; }
<sem>round { return OP_ROUND; }
<sem>int2float { return OP_INT2FLOAT; }
<sem>float2float { return OP_FLOAT2FLOAT; }
<sem>trunc { return OP_TRUNC; }
<sem>cpool { return OP_CPOOLREF; }
<sem>newobject { return OP_NEW; }
<sem>popcount { return OP_POPCOUNT; }
<sem>if { return IF_KEY; }
<sem>goto { return GOTO_KEY; }
<sem>call { return CALL_KEY; }
<sem>return { return RETURN_KEY; }
<sem>delayslot { return DELAYSLOT_KEY; }
<sem>crossbuild { return CROSSBUILD_KEY; }
<sem>export { return EXPORT_KEY; }
<sem>build { return BUILD_KEY; }
<sem>local { return LOCAL_KEY; }
<sem>[=(),:\[\];!&|^+\-*/%~<>] { yylval.ch = yytext[0]; return yytext[0]; }
<sem>#.*$
<sem>[a-zA-Z_.][a-zA-Z0-9_.]* { return find_symbol(); }
<sem>[0-9]|[1-9][0-9]+ { return scan_number(yytext,&yylval,false); }
<sem>0x[0-9a-fA-F]+ { return scan_number(yytext,&yylval,false); }
<sem>0b[01]+ { return scan_number(yytext,&yylval,false); }
<sem>[\r\ \t\v]+
<sem>\n { slgh->nextLine(); }
<sem>. { return yytext[0]; }
<preproc>^@.*\n? { slgh->nextLine(); BEGIN( preprocess(preproc,preproc) ); }
<preproc>^.*\n { slgh->nextLine(); }
<<EOF>> { yy_delete_buffer( YY_CURRENT_BUFFER );
if (filebuffers.empty())
yyterminate();
yy_switch_to_buffer( filebuffers.back().lastbuffer );
FILE *curfile = filebuffers.back().file;
if (curfile != (FILE *)0)
fclose(curfile);
filebuffers.pop_back();
slgh->parseFileFinished();
}