/* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. Copyright (C) 2000-2009 Darklegion Development This file is part of Tremulous. Tremulous is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Tremulous is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Tremulous; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA =========================================================================== */ #include "q_shared.h" #include "qcommon.h" //script flags #define SCFL_NOERRORS 0x0001 #define SCFL_NOWARNINGS 0x0002 #define SCFL_NOSTRINGWHITESPACES 0x0004 #define SCFL_NOSTRINGESCAPECHARS 0x0008 #define SCFL_PRIMITIVE 0x0010 #define SCFL_NOBINARYNUMBERS 0x0020 #define SCFL_NONUMBERVALUES 0x0040 //token types #define TT_STRING 1 // string #define TT_LITERAL 2 // literal #define TT_NUMBER 3 // number #define TT_NAME 4 // name #define TT_PUNCTUATION 5 // punctuation //string sub type //--------------- // the length of the string //literal sub type //---------------- // the ASCII code of the literal //number sub type //--------------- #define TT_DECIMAL 0x0008 // decimal number #define TT_HEX 0x0100 // hexadecimal number #define TT_OCTAL 0x0200 // octal number #define TT_BINARY 0x0400 // binary number #define TT_FLOAT 0x0800 // floating point number #define TT_INTEGER 0x1000 // integer number #define TT_LONG 0x2000 // long number #define TT_UNSIGNED 0x4000 // unsigned number //punctuation sub type //-------------------- #define P_RSHIFT_ASSIGN 1 #define P_LSHIFT_ASSIGN 2 #define P_PARMS 3 #define P_PRECOMPMERGE 4 #define P_LOGIC_AND 5 #define P_LOGIC_OR 6 #define P_LOGIC_GEQ 7 #define P_LOGIC_LEQ 8 #define P_LOGIC_EQ 9 #define P_LOGIC_UNEQ 10 #define P_MUL_ASSIGN 11 #define P_DIV_ASSIGN 12 #define P_MOD_ASSIGN 13 #define P_ADD_ASSIGN 14 #define P_SUB_ASSIGN 15 #define P_INC 16 #define P_DEC 17 #define P_BIN_AND_ASSIGN 18 #define P_BIN_OR_ASSIGN 19 #define P_BIN_XOR_ASSIGN 20 #define P_RSHIFT 21 #define P_LSHIFT 22 #define P_POINTERREF 23 #define P_CPP1 24 #define P_CPP2 25 #define P_MUL 26 #define P_DIV 27 #define P_MOD 28 #define P_ADD 29 #define P_SUB 30 #define P_ASSIGN 31 #define P_BIN_AND 32 #define P_BIN_OR 33 #define P_BIN_XOR 34 #define P_BIN_NOT 35 #define P_LOGIC_NOT 36 #define P_LOGIC_GREATER 37 #define P_LOGIC_LESS 38 #define P_REF 39 #define P_COMMA 40 #define P_SEMICOLON 41 #define P_COLON 42 #define P_QUESTIONMARK 43 #define P_PARENTHESESOPEN 44 #define P_PARENTHESESCLOSE 45 #define P_BRACEOPEN 46 #define P_BRACECLOSE 47 #define P_SQBRACKETOPEN 48 #define P_SQBRACKETCLOSE 49 #define P_BACKSLASH 50 #define P_PRECOMP 51 #define P_DOLLAR 52 //name sub type //------------- // the length of the name //punctuation typedef struct punctuation_s { char *p; //punctuation character(s) int n; //punctuation indication struct punctuation_s *next; //next punctuation } punctuation_t; //token typedef struct token_s { char string[MAX_TOKEN_CHARS]; //available token int type; //last read token type int subtype; //last read token sub type unsigned long int intvalue; //integer value double floatvalue; //floating point value char *whitespace_p; //start of white space before token char *endwhitespace_p; //start of white space before token int line; //line the token was on int linescrossed; //lines crossed in white space struct token_s *next; //next token in chain } token_t; //script file typedef struct script_s { char filename[1024]; //file name of the script char *buffer; //buffer containing the script char *script_p; //current pointer in the script char *end_p; //pointer to the end of the script char *lastscript_p; //script pointer before reading token char *whitespace_p; //begin of the white space char *endwhitespace_p; int length; //length of the script in bytes int line; //current line in script int lastline; //line before reading token int tokenavailable; //set by UnreadLastToken int flags; //several script flags punctuation_t *punctuations; //the punctuations used in the script punctuation_t **punctuationtable; token_t token; //available token struct script_s *next; //next script in a chain } script_t; #define DEFINE_FIXED 0x0001 #define BUILTIN_LINE 1 #define BUILTIN_FILE 2 #define BUILTIN_DATE 3 #define BUILTIN_TIME 4 #define BUILTIN_STDC 5 #define INDENT_IF 0x0001 #define INDENT_ELSE 0x0002 #define INDENT_ELIF 0x0004 #define INDENT_IFDEF 0x0008 #define INDENT_IFNDEF 0x0010 //macro definitions typedef struct define_s { char *name; //define name int flags; //define flags int builtin; // > 0 if builtin define int numparms; //number of define parameters token_t *parms; //define parameters token_t *tokens; //macro tokens (possibly containing parm tokens) struct define_s *next; //next defined macro in a list struct define_s *hashnext; //next define in the hash chain } define_t; //indents //used for conditional compilation directives: //#if, #else, #elif, #ifdef, #ifndef typedef struct indent_s { int type; //indent type int skip; //true if skipping current indent script_t *script; //script the indent was in struct indent_s *next; //next indent on the indent stack } indent_t; //source file typedef struct source_s { char filename[MAX_QPATH]; //file name of the script char includepath[MAX_QPATH]; //path to include files punctuation_t *punctuations; //punctuations to use script_t *scriptstack; //stack with scripts of the source token_t *tokens; //tokens to read first define_t *defines; //list with macro definitions define_t **definehash; //hash chain with defines indent_t *indentstack; //stack with indents int skip; // > 0 if skipping conditional code token_t token; //last read token } source_t; #define MAX_DEFINEPARMS 128 //directive name with parse function typedef struct directive_s { char *name; int (*func)(source_t *source); } directive_t; #define DEFINEHASHSIZE 1024 static int Parse_ReadToken(source_t *source, token_t *token); static qboolean Parse_AddDefineToSourceFromString( source_t *source, char *string ); int numtokens; //list with global defines added to every source loaded define_t *globaldefines; //longer punctuations first punctuation_t default_punctuations[] = { //binary operators {">>=",P_RSHIFT_ASSIGN, NULL}, {"<<=",P_LSHIFT_ASSIGN, NULL}, // {"...",P_PARMS, NULL}, //define merge operator {"##",P_PRECOMPMERGE, NULL}, //logic operators {"&&",P_LOGIC_AND, NULL}, {"||",P_LOGIC_OR, NULL}, {">=",P_LOGIC_GEQ, NULL}, {"<=",P_LOGIC_LEQ, NULL}, {"==",P_LOGIC_EQ, NULL}, {"!=",P_LOGIC_UNEQ, NULL}, //arithmatic operators {"*=",P_MUL_ASSIGN, NULL}, {"/=",P_DIV_ASSIGN, NULL}, {"%=",P_MOD_ASSIGN, NULL}, {"+=",P_ADD_ASSIGN, NULL}, {"-=",P_SUB_ASSIGN, NULL}, {"++",P_INC, NULL}, {"--",P_DEC, NULL}, //binary operators {"&=",P_BIN_AND_ASSIGN, NULL}, {"|=",P_BIN_OR_ASSIGN, NULL}, {"^=",P_BIN_XOR_ASSIGN, NULL}, {">>",P_RSHIFT, NULL}, {"<<",P_LSHIFT, NULL}, //reference operators {"->",P_POINTERREF, NULL}, //C++ {"::",P_CPP1, NULL}, {".*",P_CPP2, NULL}, //arithmatic operators {"*",P_MUL, NULL}, {"/",P_DIV, NULL}, {"%",P_MOD, NULL}, {"+",P_ADD, NULL}, {"-",P_SUB, NULL}, {"=",P_ASSIGN, NULL}, //binary operators {"&",P_BIN_AND, NULL}, {"|",P_BIN_OR, NULL}, {"^",P_BIN_XOR, NULL}, {"~",P_BIN_NOT, NULL}, //logic operators {"!",P_LOGIC_NOT, NULL}, {">",P_LOGIC_GREATER, NULL}, {"<",P_LOGIC_LESS, NULL}, //reference operator {".",P_REF, NULL}, //seperators {",",P_COMMA, NULL}, {";",P_SEMICOLON, NULL}, //label indication {":",P_COLON, NULL}, //if statement {"?",P_QUESTIONMARK, NULL}, //embracements {"(",P_PARENTHESESOPEN, NULL}, {")",P_PARENTHESESCLOSE, NULL}, {"{",P_BRACEOPEN, NULL}, {"}",P_BRACECLOSE, NULL}, {"[",P_SQBRACKETOPEN, NULL}, {"]",P_SQBRACKETCLOSE, NULL}, // {"\\",P_BACKSLASH, NULL}, //precompiler operator {"#",P_PRECOMP, NULL}, {"$",P_DOLLAR, NULL}, {NULL, 0} }; /* =============== Parse_CreatePunctuationTable =============== */ static void Parse_CreatePunctuationTable(script_t *script, punctuation_t *punctuations) { int i; punctuation_t *p, *lastp, *newp; //get memory for the table if (!script->punctuationtable) script->punctuationtable = (punctuation_t **) Z_Malloc(256 * sizeof(punctuation_t *)); Com_Memset(script->punctuationtable, 0, 256 * sizeof(punctuation_t *)); //add the punctuations in the list to the punctuation table for (i = 0; punctuations[i].p; i++) { newp = &punctuations[i]; lastp = NULL; //sort the punctuations in this table entry on length (longer punctuations first) for (p = script->punctuationtable[(unsigned int) newp->p[0]]; p; p = p->next) { if (strlen(p->p) < strlen(newp->p)) { newp->next = p; if (lastp) lastp->next = newp; else script->punctuationtable[(unsigned int) newp->p[0]] = newp; break; } lastp = p; } if (!p) { newp->next = NULL; if (lastp) lastp->next = newp; else script->punctuationtable[(unsigned int) newp->p[0]] = newp; } } } /* =============== Parse_ScriptError =============== */ static void QDECL Parse_ScriptError(script_t *script, char *str, ...) { char text[1024]; va_list ap; if (script->flags & SCFL_NOERRORS) return; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); Com_Printf( "file %s, line %d: %s\n", script->filename, script->line, text); } /* =============== Parse_ScriptWarning =============== */ static void QDECL Parse_ScriptWarning(script_t *script, char *str, ...) { char text[1024]; va_list ap; if (script->flags & SCFL_NOWARNINGS) return; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); Com_Printf( "file %s, line %d: %s\n", script->filename, script->line, text); } /* =============== Parse_SetScriptPunctuations =============== */ static void Parse_SetScriptPunctuations(script_t *script, punctuation_t *p) { if (p) Parse_CreatePunctuationTable(script, p); else Parse_CreatePunctuationTable(script, default_punctuations); if (p) script->punctuations = p; else script->punctuations = default_punctuations; } /* =============== Parse_ReadWhiteSpace =============== */ static int Parse_ReadWhiteSpace(script_t *script) { while(1) { //skip white space while(*script->script_p <= ' ') { if (!*script->script_p) return 0; if (*script->script_p == '\n') script->line++; script->script_p++; } //skip comments if (*script->script_p == '/') { //comments // if (*(script->script_p+1) == '/') { script->script_p++; do { script->script_p++; if (!*script->script_p) return 0; } while(*script->script_p != '\n'); script->line++; script->script_p++; if (!*script->script_p) return 0; continue; } //comments /* */ else if (*(script->script_p+1) == '*') { script->script_p++; do { script->script_p++; if (!*script->script_p) return 0; if (*script->script_p == '\n') script->line++; } while(!(*script->script_p == '*' && *(script->script_p+1) == '/')); script->script_p++; if (!*script->script_p) return 0; script->script_p++; if (!*script->script_p) return 0; continue; } } break; } return 1; } /* =============== Parse_ReadEscapeCharacter =============== */ static int Parse_ReadEscapeCharacter(script_t *script, char *ch) { int c, val, i; //step over the leading '\\' script->script_p++; //determine the escape character switch(*script->script_p) { case '\\': c = '\\'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'a': c = '\a'; break; case '\'': c = '\''; break; case '\"': c = '\"'; break; case '\?': c = '\?'; break; case 'x': { script->script_p++; for (i = 0, val = 0; ; i++, script->script_p++) { c = *script->script_p; if (c >= '0' && c <= '9') c = c - '0'; else if (c >= 'A' && c <= 'Z') c = c - 'A' + 10; else if (c >= 'a' && c <= 'z') c = c - 'a' + 10; else break; val = (val << 4) + c; } script->script_p--; if (val > 0xFF) { Parse_ScriptWarning(script, "too large value in escape character"); val = 0xFF; } c = val; break; } default: //NOTE: decimal ASCII code, NOT octal { if (*script->script_p < '0' || *script->script_p > '9') Parse_ScriptError(script, "unknown escape char"); for (i = 0, val = 0; ; i++, script->script_p++) { c = *script->script_p; if (c >= '0' && c <= '9') c = c - '0'; else break; val = val * 10 + c; } script->script_p--; if (val > 0xFF) { Parse_ScriptWarning(script, "too large value in escape character"); val = 0xFF; } c = val; break; } } //step over the escape character or the last digit of the number script->script_p++; //store the escape character *ch = c; //succesfully read escape character return 1; } /* =============== Parse_ReadString Reads C-like string. Escape characters are interpretted. Quotes are included with the string. Reads two strings with a white space between them as one string. =============== */ static int Parse_ReadString(script_t *script, token_t *token, int quote) { int len, tmpline; char *tmpscript_p; if (quote == '\"') token->type = TT_STRING; else token->type = TT_LITERAL; len = 0; //leading quote token->string[len++] = *script->script_p++; // while(1) { //minus 2 because trailing double quote and zero have to be appended if (len >= MAX_TOKEN_CHARS - 2) { Parse_ScriptError(script, "string longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS); return 0; } //if there is an escape character and //if escape characters inside a string are allowed if (*script->script_p == '\\' && !(script->flags & SCFL_NOSTRINGESCAPECHARS)) { if (!Parse_ReadEscapeCharacter(script, &token->string[len])) { token->string[len] = 0; return 0; } len++; } //if a trailing quote else if (*script->script_p == quote) { //step over the double quote script->script_p++; //if white spaces in a string are not allowed if (script->flags & SCFL_NOSTRINGWHITESPACES) break; // tmpscript_p = script->script_p; tmpline = script->line; //read unusefull stuff between possible two following strings if (!Parse_ReadWhiteSpace(script)) { script->script_p = tmpscript_p; script->line = tmpline; break; } //if there's no leading double qoute if (*script->script_p != quote) { script->script_p = tmpscript_p; script->line = tmpline; break; } //step over the new leading double quote script->script_p++; } else { if (*script->script_p == '\0') { token->string[len] = 0; Parse_ScriptError(script, "missing trailing quote"); return 0; } if (*script->script_p == '\n') { token->string[len] = 0; Parse_ScriptError(script, "newline inside string %s", token->string); return 0; } token->string[len++] = *script->script_p++; } } //trailing quote token->string[len++] = quote; //end string with a zero token->string[len] = '\0'; //the sub type is the length of the string token->subtype = len; return 1; } /* =============== Parse_ReadName =============== */ static int Parse_ReadName(script_t *script, token_t *token) { int len = 0; char c; token->type = TT_NAME; do { token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN_CHARS) { Parse_ScriptError(script, "name longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS); return 0; } c = *script->script_p; } while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'); token->string[len] = '\0'; //the sub type is the length of the name token->subtype = len; return 1; } /* =============== Parse_NumberValue =============== */ static void Parse_NumberValue(char *string, int subtype, unsigned long int *intvalue, double *floatvalue) { unsigned long int dotfound = 0; *intvalue = 0; *floatvalue = 0; //floating point number if (subtype & TT_FLOAT) { while(*string) { if (*string == '.') { if (dotfound) return; dotfound = 10; string++; } if (dotfound) { *floatvalue = *floatvalue + (double) (*string - '0') / (double) dotfound; dotfound *= 10; } else { *floatvalue = *floatvalue * 10.0 + (double) (*string - '0'); } string++; } *intvalue = (unsigned long) *floatvalue; } else if (subtype & TT_DECIMAL) { while(*string) *intvalue = *intvalue * 10 + (*string++ - '0'); *floatvalue = *intvalue; } else if (subtype & TT_HEX) { //step over the leading 0x or 0X string += 2; while(*string) { *intvalue <<= 4; if (*string >= 'a' && *string <= 'f') *intvalue += *string - 'a' + 10; else if (*string >= 'A' && *string <= 'F') *intvalue += *string - 'A' + 10; else *intvalue += *string - '0'; string++; } *floatvalue = *intvalue; } else if (subtype & TT_OCTAL) { //step over the first zero string += 1; while(*string) *intvalue = (*intvalue << 3) + (*string++ - '0'); *floatvalue = *intvalue; } else if (subtype & TT_BINARY) { //step over the leading 0b or 0B string += 2; while(*string) *intvalue = (*intvalue << 1) + (*string++ - '0'); *floatvalue = *intvalue; } } /* =============== Parse_ReadNumber =============== */ static int Parse_ReadNumber(script_t *script, token_t *token) { int len = 0, i; int octal, dot; char c; // unsigned long int intvalue = 0; // double floatvalue = 0; token->type = TT_NUMBER; //check for a hexadecimal number if (*script->script_p == '0' && (*(script->script_p + 1) == 'x' || *(script->script_p + 1) == 'X')) { token->string[len++] = *script->script_p++; token->string[len++] = *script->script_p++; c = *script->script_p; //hexadecimal while((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'A')) { token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN_CHARS) { Parse_ScriptError(script, "hexadecimal number longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS); return 0; } c = *script->script_p; } token->subtype |= TT_HEX; } #ifdef BINARYNUMBERS //check for a binary number else if (*script->script_p == '0' && (*(script->script_p + 1) == 'b' || *(script->script_p + 1) == 'B')) { token->string[len++] = *script->script_p++; token->string[len++] = *script->script_p++; c = *script->script_p; //binary while(c == '0' || c == '1') { token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN_CHARS) { Parse_ScriptError(script, "binary number longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS); return 0; } c = *script->script_p; } token->subtype |= TT_BINARY; } #endif //BINARYNUMBERS else //decimal or octal integer or floating point number { octal = qfalse; dot = qfalse; if (*script->script_p == '0') octal = qtrue; while(1) { c = *script->script_p; if (c == '.') dot = qtrue; else if (c == '8' || c == '9') octal = qfalse; else if (c < '0' || c > '9') break; token->string[len++] = *script->script_p++; if (len >= MAX_TOKEN_CHARS - 1) { Parse_ScriptError(script, "number longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS); return 0; } } if (octal) token->subtype |= TT_OCTAL; else token->subtype |= TT_DECIMAL; if (dot) token->subtype |= TT_FLOAT; } for (i = 0; i < 2; i++) { c = *script->script_p; //check for a LONG number if ( (c == 'l' || c == 'L') && !(token->subtype & TT_LONG)) { script->script_p++; token->subtype |= TT_LONG; } //check for an UNSIGNED number else if ( (c == 'u' || c == 'U') && !(token->subtype & (TT_UNSIGNED | TT_FLOAT))) { script->script_p++; token->subtype |= TT_UNSIGNED; } } token->string[len] = '\0'; Parse_NumberValue(token->string, token->subtype, &token->intvalue, &token->floatvalue); if (!(token->subtype & TT_FLOAT)) token->subtype |= TT_INTEGER; return 1; } /* =============== Parse_ReadPunctuation =============== */ static int Parse_ReadPunctuation(script_t *script, token_t *token) { int len; char *p; punctuation_t *punc; for (punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next) { p = punc->p; len = strlen(p); //if the script contains at least as much characters as the punctuation if (script->script_p + len <= script->end_p) { //if the script contains the punctuation if (!strncmp(script->script_p, p, len)) { strncpy(token->string, p, MAX_TOKEN_CHARS); script->script_p += len; token->type = TT_PUNCTUATION; //sub type is the number of the punctuation token->subtype = punc->n; return 1; } } } return 0; } /* =============== Parse_ReadPrimitive =============== */ static int Parse_ReadPrimitive(script_t *script, token_t *token) { int len; len = 0; while(*script->script_p > ' ' && *script->script_p != ';') { if (len >= MAX_TOKEN_CHARS) { Parse_ScriptError(script, "primitive token longer than MAX_TOKEN_CHARS = %d", MAX_TOKEN_CHARS); return 0; } token->string[len++] = *script->script_p++; } token->string[len] = 0; //copy the token into the script structure Com_Memcpy(&script->token, token, sizeof(token_t)); //primitive reading successfull return 1; } /* =============== Parse_ReadScriptToken =============== */ static int Parse_ReadScriptToken(script_t *script, token_t *token) { //if there is a token available (from UnreadToken) if (script->tokenavailable) { script->tokenavailable = 0; Com_Memcpy(token, &script->token, sizeof(token_t)); return 1; } //save script pointer script->lastscript_p = script->script_p; //save line counter script->lastline = script->line; //clear the token stuff Com_Memset(token, 0, sizeof(token_t)); //start of the white space script->whitespace_p = script->script_p; token->whitespace_p = script->script_p; //read unusefull stuff if (!Parse_ReadWhiteSpace(script)) return 0; script->endwhitespace_p = script->script_p; token->endwhitespace_p = script->script_p; //line the token is on token->line = script->line; //number of lines crossed before token token->linescrossed = script->line - script->lastline; //if there is a leading double quote if (*script->script_p == '\"') { if (!Parse_ReadString(script, token, '\"')) return 0; } //if an literal else if (*script->script_p == '\'') { //if (!Parse_ReadLiteral(script, token)) return 0; if (!Parse_ReadString(script, token, '\'')) return 0; } //if there is a number else if ((*script->script_p >= '0' && *script->script_p <= '9') || (*script->script_p == '.' && (*(script->script_p + 1) >= '0' && *(script->script_p + 1) <= '9'))) { if (!Parse_ReadNumber(script, token)) return 0; } //if this is a primitive script else if (script->flags & SCFL_PRIMITIVE) { return Parse_ReadPrimitive(script, token); } //if there is a name else if ((*script->script_p >= 'a' && *script->script_p <= 'z') || (*script->script_p >= 'A' && *script->script_p <= 'Z') || *script->script_p == '_') { if (!Parse_ReadName(script, token)) return 0; } //check for punctuations else if (!Parse_ReadPunctuation(script, token)) { Parse_ScriptError(script, "can't read token"); return 0; } //copy the token into the script structure Com_Memcpy(&script->token, token, sizeof(token_t)); //succesfully read a token return 1; } /* =============== Parse_StripDoubleQuotes =============== */ static void Parse_StripDoubleQuotes(char *string) { if (*string == '\"') { memmove( string, string + 1, strlen( string ) + 1 ); } if (string[strlen(string)-1] == '\"') { string[strlen(string)-1] = '\0'; } } /* =============== Parse_EndOfScript =============== */ static int Parse_EndOfScript(script_t *script) { return script->script_p >= script->end_p; } /* =============== Parse_LoadScriptFile =============== */ static script_t *Parse_LoadScriptFile(const char *filename) { fileHandle_t fp; int length; void *buffer; script_t *script; length = FS_FOpenFileRead( filename, &fp, qfalse ); if (!fp) return NULL; buffer = Z_Malloc(sizeof(script_t) + length + 1); Com_Memset( buffer, 0, sizeof(script_t) + length + 1 ); script = (script_t *) buffer; Com_Memset(script, 0, sizeof(script_t)); strcpy(script->filename, filename); script->buffer = (char *) buffer + sizeof(script_t); script->buffer[length] = 0; script->length = length; //pointer in script buffer script->script_p = script->buffer; //pointer in script buffer before reading token script->lastscript_p = script->buffer; //pointer to end of script buffer script->end_p = &script->buffer[length]; //set if there's a token available in script->token script->tokenavailable = 0; // script->line = 1; script->lastline = 1; // Parse_SetScriptPunctuations(script, NULL); // FS_Read(script->buffer, length, fp); FS_FCloseFile(fp); // return script; } /* =============== Parse_LoadScriptMemory =============== */ static script_t *Parse_LoadScriptMemory(char *ptr, int length, char *name) { void *buffer; script_t *script; buffer = Z_Malloc(sizeof(script_t) + length + 1); Com_Memset( buffer, 0, sizeof(script_t) + length + 1 ); script = (script_t *) buffer; Com_Memset(script, 0, sizeof(script_t)); strcpy(script->filename, name); script->buffer = (char *) buffer + sizeof(script_t); script->buffer[length] = 0; script->length = length; //pointer in script buffer script->script_p = script->buffer; //pointer in script buffer before reading token script->lastscript_p = script->buffer; //pointer to end of script buffer script->end_p = &script->buffer[length]; //set if there's a token available in script->token script->tokenavailable = 0; // script->line = 1; script->lastline = 1; // Parse_SetScriptPunctuations(script, NULL); // Com_Memcpy(script->buffer, ptr, length); // return script; } /* =============== Parse_FreeScript =============== */ static void Parse_FreeScript(script_t *script) { if (script->punctuationtable) Z_Free(script->punctuationtable); Z_Free(script); } /* =============== Parse_SourceError =============== */ static void QDECL Parse_SourceError(source_t *source, char *str, ...) { char text[1024]; va_list ap; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); Com_Printf( "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); } /* =============== Parse_SourceWarning =============== */ static void QDECL Parse_SourceWarning(source_t *source, char *str, ...) { char text[1024]; va_list ap; va_start(ap, str); vsprintf(text, str, ap); va_end(ap); Com_Printf( "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text); } /* =============== Parse_PushIndent =============== */ static void Parse_PushIndent(source_t *source, int type, int skip) { indent_t *indent; indent = (indent_t *) Z_Malloc(sizeof(indent_t)); indent->type = type; indent->script = source->scriptstack; indent->skip = (skip != 0); source->skip += indent->skip; indent->next = source->indentstack; source->indentstack = indent; } /* =============== Parse_PopIndent =============== */ static void Parse_PopIndent(source_t *source, int *type, int *skip) { indent_t *indent; *type = 0; *skip = 0; indent = source->indentstack; if (!indent) return; //must be an indent from the current script if (source->indentstack->script != source->scriptstack) return; *type = indent->type; *skip = indent->skip; source->indentstack = source->indentstack->next; source->skip -= indent->skip; Z_Free(indent); } /* =============== Parse_PushScript =============== */ static void Parse_PushScript(source_t *source, script_t *script) { script_t *s; for (s = source->scriptstack; s; s = s->next) { if (!Q_stricmp(s->filename, script->filename)) { Parse_SourceError(source, "%s recursively included", script->filename); return; } } //push the script on the script stack script->next = source->scriptstack; source->scriptstack = script; } /* =============== Parse_CopyToken =============== */ static token_t *Parse_CopyToken(token_t *token) { token_t *t; // t = (token_t *) malloc(sizeof(token_t)); t = (token_t *) Z_Malloc(sizeof(token_t)); // t = freetokens; if (!t) { Com_Error(ERR_FATAL, "out of token space\n"); return NULL; } // freetokens = freetokens->next; Com_Memcpy(t, token, sizeof(token_t)); t->next = NULL; numtokens++; return t; } /* =============== Parse_FreeToken =============== */ static void Parse_FreeToken(token_t *token) { //free(token); Z_Free(token); // token->next = freetokens; // freetokens = token; numtokens--; } /* =============== Parse_ReadSourceToken =============== */ static int Parse_ReadSourceToken(source_t *source, token_t *token) { token_t *t; script_t *script; int type, skip, lines; lines = 0; //if there's no token already available while(!source->tokens) { //if there's a token to read from the script if( Parse_ReadScriptToken( source->scriptstack, token ) ) { token->linescrossed += lines; return qtrue; } // if lines were crossed before the end of the script, count them lines += source->scriptstack->line - source->scriptstack->lastline; //if at the end of the script if (Parse_EndOfScript(source->scriptstack)) { //remove all indents of the script while(source->indentstack && source->indentstack->script == source->scriptstack) { Parse_SourceWarning(source, "missing #endif"); Parse_PopIndent(source, &type, &skip); } } //if this was the initial script if (!source->scriptstack->next) return qfalse; //remove the script and return to the last one script = source->scriptstack; source->scriptstack = source->scriptstack->next; Parse_FreeScript(script); } //copy the already available token Com_Memcpy(token, source->tokens, sizeof(token_t)); //free the read token t = source->tokens; source->tokens = source->tokens->next; Parse_FreeToken(t); return qtrue; } /* =============== Parse_UnreadSourceToken =============== */ static int Parse_UnreadSourceToken(source_t *source, token_t *token) { token_t *t; t = Parse_CopyToken(token); t->next = source->tokens; source->tokens = t; return qtrue; } /* =============== Parse_ReadDefineParms =============== */ static int Parse_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms) { token_t token, *t, *last; int i, done, lastcomma, numparms, indent; if (!Parse_ReadSourceToken(source, &token)) { Parse_SourceError(source, "define %s missing parms", define->name); return qfalse; } // if (define->numparms > maxparms) { Parse_SourceError(source, "define with more than %d parameters", maxparms); return qfalse; } // for (i = 0; i < define->numparms; i++) parms[i] = NULL; //if no leading "(" if (strcmp(token.string, "(")) { Parse_UnreadSourceToken(source, &token); Parse_SourceError(source, "define %s missing parms", define->name); return qfalse; } //read the define parameters for (done = 0, numparms = 0, indent = 0; !done;) { if (numparms >= maxparms) { Parse_SourceError(source, "define %s with too many parms", define->name); return qfalse; } if (numparms >= define->numparms) { Parse_SourceWarning(source, "define %s has too many parms", define->name); return qfalse; } parms[numparms] = NULL; lastcomma = 1; last = NULL; while(!done) { // if (!Parse_ReadSourceToken(source, &token)) { Parse_SourceError(source, "define %s incomplete", define->name); return qfalse; } // if (!strcmp(token.string, ",")) { if (indent <= 0) { if (lastcomma) Parse_SourceWarning(source, "too many comma's"); lastcomma = 1; break; } } lastcomma = 0; // if (!strcmp(token.string, "(")) { indent++; continue; } else if (!strcmp(token.string, ")")) { if (--indent <= 0) { if (!parms[define->numparms-1]) { Parse_SourceWarning(source, "too few define parms"); } done = 1; break; } } // if (numparms < define->numparms) { // t = Parse_CopyToken(&token); t->next = NULL; if (last) last->next = t; else parms[numparms] = t; last = t; } } numparms++; } return qtrue; } /* =============== Parse_StringizeTokens =============== */ static int Parse_StringizeTokens(token_t *tokens, token_t *token) { token_t *t; token->type = TT_STRING; token->whitespace_p = NULL; token->endwhitespace_p = NULL; token->string[0] = '\0'; strcat(token->string, "\""); for (t = tokens; t; t = t->next) { strncat(token->string, t->string, MAX_TOKEN_CHARS - strlen(token->string)); } strncat(token->string, "\"", MAX_TOKEN_CHARS - strlen(token->string)); return qtrue; } /* =============== Parse_MergeTokens =============== */ static int Parse_MergeTokens(token_t *t1, token_t *t2) { //merging of a name with a name or number if (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER)) { strcat(t1->string, t2->string); return qtrue; } //merging of two strings if (t1->type == TT_STRING && t2->type == TT_STRING) { //remove trailing double quote t1->string[strlen(t1->string)-1] = '\0'; //concat without leading double quote strcat(t1->string, &t2->string[1]); return qtrue; } //FIXME: merging of two number of the same sub type return qfalse; } /* =============== Parse_NameHash =============== */ //char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47}; static int Parse_NameHash(char *name) { int register hash, i; hash = 0; for (i = 0; name[i] != '\0'; i++) { hash += name[i] * (119 + i); //hash += (name[i] << 7) + i; //hash += (name[i] << (i&15)); } hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1); return hash; } /* =============== Parse_AddDefineToHash =============== */ static void Parse_AddDefineToHash(define_t *define, define_t **definehash) { int hash; hash = Parse_NameHash(define->name); define->hashnext = definehash[hash]; definehash[hash] = define; } /* =============== Parse_FindHashedDefine =============== */ static define_t *Parse_FindHashedDefine(define_t **definehash, char *name) { define_t *d; int hash; hash = Parse_NameHash(name); for (d = definehash[hash]; d; d = d->hashnext) { if (!strcmp(d->name, name)) return d; } return NULL; } /* =============== Parse_FindDefineParm =============== */ static int Parse_FindDefineParm(define_t *define, char *name) { token_t *p; int i; i = 0; for (p = define->parms; p; p = p->next) { if (!strcmp(p->string, name)) return i; i++; } return -1; } /* =============== Parse_FreeDefine =============== */ static void Parse_FreeDefine(define_t *define) { token_t *t, *next; //free the define parameters for (t = define->parms; t; t = next) { next = t->next; Parse_FreeToken(t); } //free the define tokens for (t = define->tokens; t; t = next) { next = t->next; Parse_FreeToken(t); } //free the define Z_Free(define); } /* =============== Parse_ExpandBuiltinDefine =============== */ static int Parse_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define, token_t **firsttoken, token_t **lasttoken) { token_t *token; time_t t; char *curtime; token = Parse_CopyToken(deftoken); switch(define->builtin) { case BUILTIN_LINE: { sprintf(token->string, "%d", deftoken->line); token->intvalue = deftoken->line; token->floatvalue = deftoken->line; token->type = TT_NUMBER; token->subtype = TT_DECIMAL | TT_INTEGER; *firsttoken = token; *lasttoken = token; break; } case BUILTIN_FILE: { strcpy(token->string, source->scriptstack->filename); token->type = TT_NAME; token->subtype = strlen(token->string); *firsttoken = token; *lasttoken = token; break; } case BUILTIN_DATE: { t = time(NULL); curtime = ctime(&t); strcpy(token->string, "\""); strncat(token->string, curtime+4, 7); strncat(token->string+7, curtime+20, 4); strcat(token->string, "\""); free(curtime); token->type = TT_NAME; token->subtype = strlen(token->string); *firsttoken = token; *lasttoken = token; break; } case BUILTIN_TIME: { t = time(NULL); curtime = ctime(&t); strcpy(token->string, "\""); strncat(token->string, curtime+11, 8); strcat(token->string, "\""); free(curtime); token->type = TT_NAME; token->subtype = strlen(token->string); *firsttoken = token; *lasttoken = token; break; } case BUILTIN_STDC: default: { *firsttoken = NULL; *lasttoken = NULL; break; } } return qtrue; } /* =============== Parse_ExpandDefine =============== */ static int Parse_ExpandDefine(source_t *source, token_t *deftoken, define_t *define, token_t **firsttoken, token_t **lasttoken) { token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t; token_t *t1, *t2, *first, *last, *nextpt, token; int parmnum, i; //if it is a builtin define if (define->builtin) { return Parse_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken); } //if the define has parameters if (define->numparms) { if (!Parse_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse; } //empty list at first first = NULL; last = NULL; //create a list with tokens of the expanded define for (dt = define->tokens; dt; dt = dt->next) { parmnum = -1; //if the token is a name, it could be a define parameter if (dt->type == TT_NAME) { parmnum = Parse_FindDefineParm(define, dt->string); } //if it is a define parameter if (parmnum >= 0) { for (pt = parms[parmnum]; pt; pt = pt->next) { t = Parse_CopyToken(pt); //add the token to the list t->next = NULL; if (last) last->next = t; else first = t; last = t; } } else { //if stringizing operator if (dt->string[0] == '#' && dt->string[1] == '\0') { //the stringizing operator must be followed by a define parameter if (dt->next) parmnum = Parse_FindDefineParm(define, dt->next->string); else parmnum = -1; // if (parmnum >= 0) { //step over the stringizing operator dt = dt->next; //stringize the define parameter tokens if (!Parse_StringizeTokens(parms[parmnum], &token)) { Parse_SourceError(source, "can't stringize tokens"); return qfalse; } t = Parse_CopyToken(&token); } else { Parse_SourceWarning(source, "stringizing operator without define parameter"); continue; } } else { t = Parse_CopyToken(dt); } //add the token to the list t->next = NULL; if (last) last->next = t; else first = t; last = t; } } //check for the merging operator for (t = first; t; ) { if (t->next) { //if the merging operator if (t->next->string[0] == '#' && t->next->string[1] == '#') { t1 = t; t2 = t->next->next; if (t2) { if (!Parse_MergeTokens(t1, t2)) { Parse_SourceError(source, "can't merge %s with %s", t1->string, t2->string); return qfalse; } Parse_FreeToken(t1->next); t1->next = t2->next; if (t2 == last) last = t1; Parse_FreeToken(t2); continue; } } } t = t->next; } //store the first and last token of the list *firsttoken = first; *lasttoken = last; //free all the parameter tokens for (i = 0; i < define->numparms; i++) { for (pt = parms[i]; pt; pt = nextpt) { nextpt = pt->next; Parse_FreeToken(pt); } } // return qtrue; } /* =============== Parse_ExpandDefineIntoSource =============== */ static int Parse_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define) { token_t *firsttoken, *lasttoken; if (!Parse_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse; if (firsttoken && lasttoken) { lasttoken->next = source->tokens; source->tokens = firsttoken; return qtrue; } return qfalse; } /* =============== Parse_ConvertPath =============== */ static void Parse_ConvertPath(char *path) { char *ptr; //remove double path seperators for (ptr = path; *ptr;) { if ((*ptr == '\\' || *ptr == '/') && (*(ptr+1) == '\\' || *(ptr+1) == '/')) { memmove(ptr, ptr+1, strlen(ptr)); } else { ptr++; } } //set OS dependent path seperators for (ptr = path; *ptr;) { if (*ptr == '/' || *ptr == '\\') *ptr = PATH_SEP; ptr++; } } /* =============== Parse_ReadLine reads a token from the current line, continues reading on the next line only if a backslash '\' is encountered. =============== */ static int Parse_ReadLine(source_t *source, token_t *token) { int crossline; crossline = 0; do { if (!Parse_ReadSourceToken(source, token)) return qfalse; if (token->linescrossed > crossline) { Parse_UnreadSourceToken(source, token); return qfalse; } crossline = 1; } while(!strcmp(token->string, "\\")); return qtrue; } /* =============== Parse_OperatorPriority =============== */ typedef struct operator_s { int operator; int priority; int parentheses; struct operator_s *prev, *next; } operator_t; typedef struct value_s { signed long int intvalue; double floatvalue; int parentheses; struct value_s *prev, *next; } value_t; static int Parse_OperatorPriority(int op) { switch(op) { case P_MUL: return 15; case P_DIV: return 15; case P_MOD: return 15; case P_ADD: return 14; case P_SUB: return 14; case P_LOGIC_AND: return 7; case P_LOGIC_OR: return 6; case P_LOGIC_GEQ: return 12; case P_LOGIC_LEQ: return 12; case P_LOGIC_EQ: return 11; case P_LOGIC_UNEQ: return 11; case P_LOGIC_NOT: return 16; case P_LOGIC_GREATER: return 12; case P_LOGIC_LESS: return 12; case P_RSHIFT: return 13; case P_LSHIFT: return 13; case P_BIN_AND: return 10; case P_BIN_OR: return 8; case P_BIN_XOR: return 9; case P_BIN_NOT: return 16; case P_COLON: return 5; case P_QUESTIONMARK: return 5; } return qfalse; } #define MAX_VALUES 64 #define MAX_OPERATORS 64 #define AllocValue(val) \ if (numvalues >= MAX_VALUES) { \ Parse_SourceError(source, "out of value space\n"); \ error = 1; \ break; \ } \ else \ val = &value_heap[numvalues++]; #define FreeValue(val) // #define AllocOperator(op) \ if (numoperators >= MAX_OPERATORS) { \ Parse_SourceError(source, "out of operator space\n"); \ error = 1; \ break; \ } \ else \ op = &operator_heap[numoperators++]; #define FreeOperator(op) /* =============== Parse_EvaluateTokens =============== */ static int Parse_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue, double *floatvalue, int integer) { operator_t *o, *firstoperator, *lastoperator; value_t *v, *firstvalue, *lastvalue, *v1, *v2; token_t *t; int brace = 0; int parentheses = 0; int error = 0; int lastwasvalue = 0; int negativevalue = 0; int questmarkintvalue = 0; double questmarkfloatvalue = 0; int gotquestmarkvalue = qfalse; int lastoperatortype = 0; // operator_t operator_heap[MAX_OPERATORS]; int numoperators = 0; value_t value_heap[MAX_VALUES]; int numvalues = 0; firstoperator = lastoperator = NULL; firstvalue = lastvalue = NULL; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; for (t = tokens; t; t = t->next) { switch(t->type) { case TT_NAME: { if (lastwasvalue || negativevalue) { Parse_SourceError(source, "syntax error in #if/#elif"); error = 1; break; } if (strcmp(t->string, "defined")) { Parse_SourceError(source, "undefined name %s in #if/#elif", t->string); error = 1; break; } t = t->next; if (!strcmp(t->string, "(")) { brace = qtrue; t = t->next; } if (!t || t->type != TT_NAME) { Parse_SourceError(source, "defined without name in #if/#elif"); error = 1; break; } //v = (value_t *) Z_Malloc(sizeof(value_t)); AllocValue(v); if (Parse_FindHashedDefine(source->definehash, t->string)) { v->intvalue = 1; v->floatvalue = 1; } else { v->intvalue = 0; v->floatvalue = 0; } v->parentheses = parentheses; v->next = NULL; v->prev = lastvalue; if (lastvalue) lastvalue->next = v; else firstvalue = v; lastvalue = v; if (brace) { t = t->next; if (!t || strcmp(t->string, ")")) { Parse_SourceError(source, "defined without ) in #if/#elif"); error = 1; break; } } brace = qfalse; // defined() creates a value lastwasvalue = 1; break; } case TT_NUMBER: { if (lastwasvalue) { Parse_SourceError(source, "syntax error in #if/#elif"); error = 1; break; } //v = (value_t *) Z_Malloc(sizeof(value_t)); AllocValue(v); if (negativevalue) { v->intvalue = - (signed int) t->intvalue; v->floatvalue = - t->floatvalue; } else { v->intvalue = t->intvalue; v->floatvalue = t->floatvalue; } v->parentheses = parentheses; v->next = NULL; v->prev = lastvalue; if (lastvalue) lastvalue->next = v; else firstvalue = v; lastvalue = v; //last token was a value lastwasvalue = 1; // negativevalue = 0; break; } case TT_PUNCTUATION: { if (negativevalue) { Parse_SourceError(source, "misplaced minus sign in #if/#elif"); error = 1; break; } if (t->subtype == P_PARENTHESESOPEN) { parentheses++; break; } else if (t->subtype == P_PARENTHESESCLOSE) { parentheses--; if (parentheses < 0) { Parse_SourceError(source, "too many ) in #if/#elsif"); error = 1; } break; } //check for invalid operators on floating point values if (!integer) { if (t->subtype == P_BIN_NOT || t->subtype == P_MOD || t->subtype == P_RSHIFT || t->subtype == P_LSHIFT || t->subtype == P_BIN_AND || t->subtype == P_BIN_OR || t->subtype == P_BIN_XOR) { Parse_SourceError(source, "illigal operator %s on floating point operands\n", t->string); error = 1; break; } } switch(t->subtype) { case P_LOGIC_NOT: case P_BIN_NOT: { if (lastwasvalue) { Parse_SourceError(source, "! or ~ after value in #if/#elif"); error = 1; break; } break; } case P_INC: case P_DEC: { Parse_SourceError(source, "++ or -- used in #if/#elif"); break; } case P_SUB: { if (!lastwasvalue) { negativevalue = 1; break; } } case P_MUL: case P_DIV: case P_MOD: case P_ADD: case P_LOGIC_AND: case P_LOGIC_OR: case P_LOGIC_GEQ: case P_LOGIC_LEQ: case P_LOGIC_EQ: case P_LOGIC_UNEQ: case P_LOGIC_GREATER: case P_LOGIC_LESS: case P_RSHIFT: case P_LSHIFT: case P_BIN_AND: case P_BIN_OR: case P_BIN_XOR: case P_COLON: case P_QUESTIONMARK: { if (!lastwasvalue) { Parse_SourceError(source, "operator %s after operator in #if/#elif", t->string); error = 1; break; } break; } default: { Parse_SourceError(source, "invalid operator %s in #if/#elif", t->string); error = 1; break; } } if (!error && !negativevalue) { //o = (operator_t *) Z_Malloc(sizeof(operator_t)); AllocOperator(o); o->operator = t->subtype; o->priority = Parse_OperatorPriority(t->subtype); o->parentheses = parentheses; o->next = NULL; o->prev = lastoperator; if (lastoperator) lastoperator->next = o; else firstoperator = o; lastoperator = o; lastwasvalue = 0; } break; } default: { Parse_SourceError(source, "unknown %s in #if/#elif", t->string); error = 1; break; } } if (error) break; } if (!error) { if (!lastwasvalue) { Parse_SourceError(source, "trailing operator in #if/#elif"); error = 1; } else if (parentheses) { Parse_SourceError(source, "too many ( in #if/#elif"); error = 1; } } // gotquestmarkvalue = qfalse; questmarkintvalue = 0; questmarkfloatvalue = 0; //while there are operators while(!error && firstoperator) { v = firstvalue; for (o = firstoperator; o->next; o = o->next) { //if the current operator is nested deeper in parentheses //than the next operator if (o->parentheses > o->next->parentheses) break; //if the current and next operator are nested equally deep in parentheses if (o->parentheses == o->next->parentheses) { //if the priority of the current operator is equal or higher //than the priority of the next operator if (o->priority >= o->next->priority) break; } //if the arity of the operator isn't equal to 1 if (o->operator != P_LOGIC_NOT && o->operator != P_BIN_NOT) v = v->next; //if there's no value or no next value if (!v) { Parse_SourceError(source, "mising values in #if/#elif"); error = 1; break; } } if (error) break; v1 = v; v2 = v->next; switch(o->operator) { case P_LOGIC_NOT: v1->intvalue = !v1->intvalue; v1->floatvalue = !v1->floatvalue; break; case P_BIN_NOT: v1->intvalue = ~v1->intvalue; break; case P_MUL: v1->intvalue *= v2->intvalue; v1->floatvalue *= v2->floatvalue; break; case P_DIV: if (!v2->intvalue || !v2->floatvalue) { Parse_SourceError(source, "divide by zero in #if/#elif\n"); error = 1; break; } v1->intvalue /= v2->intvalue; v1->floatvalue /= v2->floatvalue; break; case P_MOD: if (!v2->intvalue) { Parse_SourceError(source, "divide by zero in #if/#elif\n"); error = 1; break; } v1->intvalue %= v2->intvalue; break; case P_ADD: v1->intvalue += v2->intvalue; v1->floatvalue += v2->floatvalue; break; case P_SUB: v1->intvalue -= v2->intvalue; v1->floatvalue -= v2->floatvalue; break; case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue; v1->floatvalue = v1->floatvalue && v2->floatvalue; break; case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue; v1->floatvalue = v1->floatvalue || v2->floatvalue; break; case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue; v1->floatvalue = v1->floatvalue >= v2->floatvalue; break; case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue; v1->floatvalue = v1->floatvalue <= v2->floatvalue; break; case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue; v1->floatvalue = v1->floatvalue == v2->floatvalue; break; case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue; v1->floatvalue = v1->floatvalue != v2->floatvalue; break; case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue; v1->floatvalue = v1->floatvalue > v2->floatvalue; break; case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue; v1->floatvalue = v1->floatvalue < v2->floatvalue; break; case P_RSHIFT: v1->intvalue >>= v2->intvalue; break; case P_LSHIFT: v1->intvalue <<= v2->intvalue; break; case P_BIN_AND: v1->intvalue &= v2->intvalue; break; case P_BIN_OR: v1->intvalue |= v2->intvalue; break; case P_BIN_XOR: v1->intvalue ^= v2->intvalue; break; case P_COLON: { if (!gotquestmarkvalue) { Parse_SourceError(source, ": without ? in #if/#elif"); error = 1; break; } if (integer) { if (!questmarkintvalue) v1->intvalue = v2->intvalue; } else { if (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue; } gotquestmarkvalue = qfalse; break; } case P_QUESTIONMARK: { if (gotquestmarkvalue) { Parse_SourceError(source, "? after ? in #if/#elif"); error = 1; break; } questmarkintvalue = v1->intvalue; questmarkfloatvalue = v1->floatvalue; gotquestmarkvalue = qtrue; break; } } if (error) break; lastoperatortype = o->operator; //if not an operator with arity 1 if (o->operator != P_LOGIC_NOT && o->operator != P_BIN_NOT) { //remove the second value if not question mark operator if (o->operator != P_QUESTIONMARK) v = v->next; // if (v->prev) v->prev->next = v->next; else firstvalue = v->next; if (v->next) v->next->prev = v->prev; else lastvalue = v->prev; //Z_Free(v); FreeValue(v); } //remove the operator if (o->prev) o->prev->next = o->next; else firstoperator = o->next; if (o->next) o->next->prev = o->prev; else lastoperator = o->prev; //Z_Free(o); FreeOperator(o); } if (firstvalue) { if (intvalue) *intvalue = firstvalue->intvalue; if (floatvalue) *floatvalue = firstvalue->floatvalue; } for (o = firstoperator; o; o = lastoperator) { lastoperator = o->next; //Z_Free(o); FreeOperator(o); } for (v = firstvalue; v; v = lastvalue) { lastvalue = v->next; //Z_Free(v); FreeValue(v); } if (!error) return qtrue; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; return qfalse; } /* =============== Parse_Evaluate =============== */ static int Parse_Evaluate(source_t *source, signed long int *intvalue, double *floatvalue, int integer) { token_t token, *firsttoken, *lasttoken; token_t *t, *nexttoken; define_t *define; int defined = qfalse; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; // if (!Parse_ReadLine(source, &token)) { Parse_SourceError(source, "no value after #if/#elif"); return qfalse; } firsttoken = NULL; lasttoken = NULL; do { //if the token is a name if (token.type == TT_NAME) { if (defined) { defined = qfalse; t = Parse_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } else if (!strcmp(token.string, "defined")) { defined = qtrue; t = Parse_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } else { //then it must be a define define = Parse_FindHashedDefine(source->definehash, token.string); if (!define) { Parse_SourceError(source, "can't evaluate %s, not defined", token.string); return qfalse; } if (!Parse_ExpandDefineIntoSource(source, &token, define)) return qfalse; } } //if the token is a number or a punctuation else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) { t = Parse_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } else //can't evaluate the token { Parse_SourceError(source, "can't evaluate %s", token.string); return qfalse; } } while(Parse_ReadLine(source, &token)); // if (!Parse_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; // for (t = firsttoken; t; t = nexttoken) { nexttoken = t->next; Parse_FreeToken(t); } // return qtrue; } /* =============== Parse_DollarEvaluate =============== */ static int Parse_DollarEvaluate(source_t *source, signed long int *intvalue, double *floatvalue, int integer) { int indent, defined = qfalse; token_t token, *firsttoken, *lasttoken; token_t *t, *nexttoken; define_t *define; if (intvalue) *intvalue = 0; if (floatvalue) *floatvalue = 0; // if (!Parse_ReadSourceToken(source, &token)) { Parse_SourceError(source, "no leading ( after $evalint/$evalfloat"); return qfalse; } if (!Parse_ReadSourceToken(source, &token)) { Parse_SourceError(source, "nothing to evaluate"); return qfalse; } indent = 1; firsttoken = NULL; lasttoken = NULL; do { //if the token is a name if (token.type == TT_NAME) { if (defined) { defined = qfalse; t = Parse_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } else if (!strcmp(token.string, "defined")) { defined = qtrue; t = Parse_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } else { //then it must be a define define = Parse_FindHashedDefine(source->definehash, token.string); if (!define) { Parse_SourceError(source, "can't evaluate %s, not defined", token.string); return qfalse; } if (!Parse_ExpandDefineIntoSource(source, &token, define)) return qfalse; } } //if the token is a number or a punctuation else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) { if (*token.string == '(') indent++; else if (*token.string == ')') indent--; if (indent <= 0) break; t = Parse_CopyToken(&token); t->next = NULL; if (lasttoken) lasttoken->next = t; else firsttoken = t; lasttoken = t; } else //can't evaluate the token { Parse_SourceError(source, "can't evaluate %s", token.string); return qfalse; } } while(Parse_ReadSourceToken(source, &token)); // if (!Parse_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse; // for (t = firsttoken; t; t = nexttoken) { nexttoken = t->next; Parse_FreeToken(t); } // return qtrue; } /* =============== Parse_Directive_include =============== */ static int Parse_Directive_include(source_t *source) { script_t *script; token_t token; char path[MAX_QPATH]; if (source->skip > 0) return qtrue; // if (!Parse_ReadSourceToken(source, &token)) { Parse_SourceError(source, "#include without file name"); return qfalse; } if (token.linescrossed > 0) { Parse_SourceError(source, "#include without file name"); return qfalse; } if (token.type == TT_STRING) { Parse_StripDoubleQuotes(token.string); Parse_ConvertPath(token.string); script = Parse_LoadScriptFile(token.string); if (!script) { strcpy(path, source->includepath); strcat(path, token.string); script = Parse_LoadScriptFile(path); } } else if (token.type == TT_PUNCTUATION && *token.string == '<') { strcpy(path, source->includepath); while(Parse_ReadSourceToken(source, &token)) { if (token.linescrossed > 0) { Parse_UnreadSourceToken(source, &token); break; } if (token.type == TT_PUNCTUATION && *token.string == '>') break; strncat(path, token.string, MAX_QPATH - 1); } if (*token.string != '>') { Parse_SourceWarning(source, "#include missing trailing >"); } if (!strlen(path)) { Parse_SourceError(source, "#include without file name between < >"); return qfalse; } Parse_ConvertPath(path); script = Parse_LoadScriptFile(path); } else { Parse_SourceError(source, "#include without file name"); return qfalse; } if (!script) { Parse_SourceError(source, "file %s not found", path); return qfalse; } Parse_PushScript(source, script); return qtrue; } /* =============== Parse_WhiteSpaceBeforeToken =============== */ static int Parse_WhiteSpaceBeforeToken(token_t *token) { return token->endwhitespace_p - token->whitespace_p > 0; } /* =============== Parse_ClearTokenWhiteSpace =============== */ static void Parse_ClearTokenWhiteSpace(token_t *token) { token->whitespace_p = NULL; token->endwhitespace_p = NULL; token->linescrossed = 0; } /* =============== Parse_Directive_undef =============== */ static int Parse_Directive_undef(source_t *source) { token_t token; define_t *define, *lastdefine; int hash; if (source->skip > 0) return qtrue; // if (!Parse_ReadLine(source, &token)) { Parse_SourceError(source, "undef without name"); return qfalse; } if (token.type != TT_NAME) { Parse_UnreadSourceToken(source, &token); Parse_SourceError(source, "expected name, found %s", token.string); return qfalse; } hash = Parse_NameHash(token.string); for (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext) { if (!strcmp(define->name, token.string)) { if (define->flags & DEFINE_FIXED) { Parse_SourceWarning(source, "can't undef %s", token.string); } else { if (lastdefine) lastdefine->hashnext = define->hashnext; else source->definehash[hash] = define->hashnext; Parse_FreeDefine(define); } break; } lastdefine = define; } return qtrue; } /* =============== Parse_Directive_elif =============== */ static int Parse_Directive_elif(source_t *source) { signed long int value; int type, skip; Parse_PopIndent(source, &type, &skip); if (!type || type == INDENT_ELSE) { Parse_SourceError(source, "misplaced #elif"); return qfalse; } if (!Parse_Evaluate(source, &value, NULL, qtrue)) return qfalse; skip = (value == 0); Parse_PushIndent(source, INDENT_ELIF, skip); return qtrue; } /* =============== Parse_Directive_if =============== */ static int Parse_Directive_if(source_t *source) { signed long int value; int skip; if (!Parse_Evaluate(source, &value, NULL, qtrue)) return qfalse; skip = (value == 0); Parse_PushIndent(source, INDENT_IF, skip); return qtrue; } /* =============== Parse_Directive_line =============== */ static int Parse_Directive_line(source_t *source) { Parse_SourceError(source, "#line directive not supported"); return qfalse; } /* =============== Parse_Directive_error =============== */ static int Parse_Directive_error(source_t *source) { token_t token; strcpy(token.string, ""); Parse_ReadSourceToken(source, &token); Parse_SourceError(source, "#error directive: %s", token.string); return qfalse; } /* =============== Parse_Directive_pragma =============== */ static int Parse_Directive_pragma(source_t *source) { token_t token; Parse_SourceWarning(source, "#pragma directive not supported"); while(Parse_ReadLine(source, &token)) ; return qtrue; } /* =============== Parse_UnreadSignToken =============== */ static void Parse_UnreadSignToken(source_t *source) { token_t token; token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; strcpy(token.string, "-"); token.type = TT_PUNCTUATION; token.subtype = P_SUB; Parse_UnreadSourceToken(source, &token); } /* =============== Parse_Directive_eval =============== */ static int Parse_Directive_eval(source_t *source) { signed long int value; token_t token; if (!Parse_Evaluate(source, &value, NULL, qtrue)) return qfalse; // token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%d", abs(value)); token.type = TT_NUMBER; token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; Parse_UnreadSourceToken(source, &token); if (value < 0) Parse_UnreadSignToken(source); return qtrue; } /* =============== Parse_Directive_evalfloat =============== */ static int Parse_Directive_evalfloat(source_t *source) { double value; token_t token; if (!Parse_Evaluate(source, NULL, &value, qfalse)) return qfalse; token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%1.2f", fabs(value)); token.type = TT_NUMBER; token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; Parse_UnreadSourceToken(source, &token); if (value < 0) Parse_UnreadSignToken(source); return qtrue; } /* =============== Parse_DollarDirective_evalint =============== */ static int Parse_DollarDirective_evalint(source_t *source) { signed long int value; token_t token; if (!Parse_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse; // token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%d", abs(value)); token.type = TT_NUMBER; token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL; token.intvalue = value; token.floatvalue = value; Parse_UnreadSourceToken(source, &token); if (value < 0) Parse_UnreadSignToken(source); return qtrue; } /* =============== Parse_DollarDirective_evalfloat =============== */ static int Parse_DollarDirective_evalfloat(source_t *source) { double value; token_t token; if (!Parse_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse; token.line = source->scriptstack->line; token.whitespace_p = source->scriptstack->script_p; token.endwhitespace_p = source->scriptstack->script_p; token.linescrossed = 0; sprintf(token.string, "%1.2f", fabs(value)); token.type = TT_NUMBER; token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL; token.intvalue = (unsigned long) value; token.floatvalue = value; Parse_UnreadSourceToken(source, &token); if (value < 0) Parse_UnreadSignToken(source); return qtrue; } /* =============== Parse_ReadDollarDirective =============== */ directive_t dollardirectives[20] = { {"evalint", Parse_DollarDirective_evalint}, {"evalfloat", Parse_DollarDirective_evalfloat}, {NULL, NULL} }; static int Parse_ReadDollarDirective(source_t *source) { token_t token; int i; //read the directive name if (!Parse_ReadSourceToken(source, &token)) { Parse_SourceError(source, "found $ without name"); return qfalse; } //directive name must be on the same line if (token.linescrossed > 0) { Parse_UnreadSourceToken(source, &token); Parse_SourceError(source, "found $ at end of line"); return qfalse; } //if if is a name if (token.type == TT_NAME) { //find the precompiler directive for (i = 0; dollardirectives[i].name; i++) { if (!strcmp(dollardirectives[i].name, token.string)) { return dollardirectives[i].func(source); } } } Parse_UnreadSourceToken(source, &token); Parse_SourceError(source, "unknown precompiler directive %s", token.string); return qfalse; } /* =============== Parse_Directive_if_def =============== */ static int Parse_Directive_if_def(source_t *source, int type) { token_t token; define_t *d; int skip; if (!Parse_ReadLine(source, &token)) { Parse_SourceError(source, "#ifdef without name"); return qfalse; } if (token.type != TT_NAME) { Parse_UnreadSourceToken(source, &token); Parse_SourceError(source, "expected name after #ifdef, found %s", token.string); return qfalse; } d = Parse_FindHashedDefine(source->definehash, token.string); skip = (type == INDENT_IFDEF) == (d == NULL); Parse_PushIndent(source, type, skip); return qtrue; } /* =============== Parse_Directive_ifdef =============== */ static int Parse_Directive_ifdef(source_t *source) { return Parse_Directive_if_def(source, INDENT_IFDEF); } /* =============== Parse_Directive_ifndef =============== */ static int Parse_Directive_ifndef(source_t *source) { return Parse_Directive_if_def(source, INDENT_IFNDEF); } /* =============== Parse_Directive_else =============== */ static int Parse_Directive_else(source_t *source) { int type, skip; Parse_PopIndent(source, &type, &skip); if (!type) { Parse_SourceError(source, "misplaced #else"); return qfalse; } if (type == INDENT_ELSE) { Parse_SourceError(source, "#else after #else"); return qfalse; } Parse_PushIndent(source, INDENT_ELSE, !skip); return qtrue; } /* =============== Parse_Directive_endif =============== */ static int Parse_Directive_endif(source_t *source) { int type, skip; Parse_PopIndent(source, &type, &skip); if (!type) { Parse_SourceError(source, "misplaced #endif"); return qfalse; } return qtrue; } /* =============== Parse_CheckTokenString =============== */ static int Parse_CheckTokenString(source_t *source, char *string) { token_t tok; if (!Parse_ReadToken(source, &tok)) return qfalse; //if the token is available if (!strcmp(tok.string, string)) return qtrue; // Parse_UnreadSourceToken(source, &tok); return qfalse; } /* =============== Parse_Directive_define =============== */ static int Parse_Directive_define(source_t *source) { token_t token, *t, *last; define_t *define; if (source->skip > 0) return qtrue; // if (!Parse_ReadLine(source, &token)) { Parse_SourceError(source, "#define without name"); return qfalse; } if (token.type != TT_NAME) { Parse_UnreadSourceToken(source, &token); Parse_SourceError(source, "expected name after #define, found %s", token.string); return qfalse; } //check if the define already exists define = Parse_FindHashedDefine(source->definehash, token.string); if (define) { if (define->flags & DEFINE_FIXED) { Parse_SourceError(source, "can't redefine %s", token.string); return qfalse; } Parse_SourceWarning(source, "redefinition of %s", token.string); //unread the define name before executing the #undef directive Parse_UnreadSourceToken(source, &token); if (!Parse_Directive_undef(source)) return qfalse; //if the define was not removed (define->flags & DEFINE_FIXED) define = Parse_FindHashedDefine(source->definehash, token.string); } //allocate define define = (define_t *) Z_Malloc(sizeof(define_t) + strlen(token.string) + 1); Com_Memset(define, 0, sizeof(define_t)); define->name = (char *) define + sizeof(define_t); strcpy(define->name, token.string); //add the define to the source Parse_AddDefineToHash(define, source->definehash); //if nothing is defined, just return if (!Parse_ReadLine(source, &token)) return qtrue; //if it is a define with parameters if (!Parse_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "(")) { //read the define parameters last = NULL; if (!Parse_CheckTokenString(source, ")")) { while(1) { if (!Parse_ReadLine(source, &token)) { Parse_SourceError(source, "expected define parameter"); return qfalse; } //if it isn't a name if (token.type != TT_NAME) { Parse_SourceError(source, "invalid define parameter"); return qfalse; } // if (Parse_FindDefineParm(define, token.string) >= 0) { Parse_SourceError(source, "two the same define parameters"); return qfalse; } //add the define parm t = Parse_CopyToken(&token); Parse_ClearTokenWhiteSpace(t); t->next = NULL; if (last) last->next = t; else define->parms = t; last = t; define->numparms++; //read next token if (!Parse_ReadLine(source, &token)) { Parse_SourceError(source, "define parameters not terminated"); return qfalse; } // if (!strcmp(token.string, ")")) break; //then it must be a comma if (strcmp(token.string, ",")) { Parse_SourceError(source, "define not terminated"); return qfalse; } } } if (!Parse_ReadLine(source, &token)) return qtrue; } //read the defined stuff last = NULL; do { t = Parse_CopyToken(&token); if (t->type == TT_NAME && !strcmp(t->string, define->name)) { Parse_SourceError(source, "recursive define (removed recursion)"); continue; } Parse_ClearTokenWhiteSpace(t); t->next = NULL; if (last) last->next = t; else define->tokens = t; last = t; } while(Parse_ReadLine(source, &token)); // if (last) { //check for merge operators at the beginning or end if (!strcmp(define->tokens->string, "##") || !strcmp(last->string, "##")) { Parse_SourceError(source, "define with misplaced ##"); return qfalse; } } return qtrue; } /* =============== Parse_ReadDirective =============== */ directive_t directives[20] = { {"if", Parse_Directive_if}, {"ifdef", Parse_Directive_ifdef}, {"ifndef", Parse_Directive_ifndef}, {"elif", Parse_Directive_elif}, {"else", Parse_Directive_else}, {"endif", Parse_Directive_endif}, {"include", Parse_Directive_include}, {"define", Parse_Directive_define}, {"undef", Parse_Directive_undef}, {"line", Parse_Directive_line}, {"error", Parse_Directive_error}, {"pragma", Parse_Directive_pragma}, {"eval", Parse_Directive_eval}, {"evalfloat", Parse_Directive_evalfloat}, {NULL, NULL} }; static int Parse_ReadDirective(source_t *source) { token_t token; int i; //read the directive name if (!Parse_ReadSourceToken(source, &token)) { Parse_SourceError(source, "found # without name"); return qfalse; } //directive name must be on the same line if (token.linescrossed > 0) { Parse_UnreadSourceToken(source, &token); Parse_SourceError(source, "found # at end of line"); return qfalse; } //if if is a name if (token.type == TT_NAME) { //find the precompiler directive for (i = 0; directives[i].name; i++) { if (!strcmp(directives[i].name, token.string)) { return directives[i].func(source); } } } Parse_SourceError(source, "unknown precompiler directive %s", token.string); return qfalse; } /* =============== Parse_UnreadToken =============== */ static void Parse_UnreadToken(source_t *source, token_t *token) { Parse_UnreadSourceToken(source, token); } /* =============== Parse_ReadEnumeration It is assumed that the 'enum' token has already been consumed This is fairly basic: it doesn't catch some fairly obvious errors like nested enums, and enumerated names conflict with #define parameters =============== */ static qboolean Parse_ReadEnumeration( source_t *source ) { token_t newtoken; int value; if( !Parse_ReadToken( source, &newtoken ) ) return qfalse; if( newtoken.type != TT_PUNCTUATION || newtoken.subtype != P_BRACEOPEN ) { Parse_SourceError( source, "Found %s when expecting {\n", newtoken.string ); return qfalse; } for( value = 0;; value++ ) { token_t name; // read the name if( !Parse_ReadToken( source, &name ) ) break; // it's ok for the enum to end immediately if( name.type == TT_PUNCTUATION && name.subtype == P_BRACECLOSE ) { if( !Parse_ReadToken( source, &name ) ) break; // ignore trailing semicolon if( name.type != TT_PUNCTUATION || name.subtype != P_SEMICOLON ) Parse_UnreadToken( source, &name ); return qtrue; } // ... but not for it to do anything else if( name.type != TT_NAME ) { Parse_SourceError( source, "Found %s when expecting identifier\n", name.string ); return qfalse; } if( !Parse_ReadToken( source, &newtoken ) ) break; if( newtoken.type != TT_PUNCTUATION ) { Parse_SourceError( source, "Found %s when expecting , or = or }\n", newtoken.string ); return qfalse; } if( newtoken.subtype == P_ASSIGN ) { int neg = 1; if( !Parse_ReadToken( source, &newtoken ) ) break; // Parse_ReadToken doesn't seem to read negative numbers, so we do it // ourselves if( newtoken.type == TT_PUNCTUATION && newtoken.subtype == P_SUB ) { neg = -1; // the next token should be the number if( !Parse_ReadToken( source, &newtoken ) ) break; } if( newtoken.type != TT_NUMBER || !( newtoken.subtype & TT_INTEGER ) ) { Parse_SourceError( source, "Found %s when expecting integer\n", newtoken.string ); return qfalse; } // this is somewhat silly, but cheap to check if( neg == -1 && ( newtoken.subtype & TT_UNSIGNED ) ) { Parse_SourceWarning( source, "Value in enumeration is negative and " "unsigned\n" ); } // set the new define value value = newtoken.intvalue * neg; if( !Parse_ReadToken( source, &newtoken ) ) break; } if( newtoken.type != TT_PUNCTUATION || ( newtoken.subtype != P_COMMA && newtoken.subtype != P_BRACECLOSE ) ) { Parse_SourceError( source, "Found %s when expecting , or }\n", newtoken.string ); return qfalse; } if( !Parse_AddDefineToSourceFromString( source, va( "%s %d\n", name.string, value ) ) ) { Parse_SourceWarning( source, "Couldn't add define to source: %s = %d\n", name.string, value ); return qfalse; } if( newtoken.subtype == P_BRACECLOSE ) { if( !Parse_ReadToken( source, &name ) ) break; // ignore trailing semicolon if( name.type != TT_PUNCTUATION || name.subtype != P_SEMICOLON ) Parse_UnreadToken( source, &name ); return qtrue; } } // got here if a ReadToken returned false return qfalse; } /* =============== Parse_ReadToken =============== */ static int Parse_ReadToken(source_t *source, token_t *token) { define_t *define; while(1) { if (!Parse_ReadSourceToken(source, token)) return qfalse; //check for precompiler directives if (token->type == TT_PUNCTUATION && *token->string == '#') { { //read the precompiler directive if (!Parse_ReadDirective(source)) return qfalse; continue; } } if (token->type == TT_PUNCTUATION && *token->string == '$') { { //read the precompiler directive if (!Parse_ReadDollarDirective(source)) return qfalse; continue; } } if( token->type == TT_NAME && !Q_stricmp( token->string, "enum" ) ) { if( !Parse_ReadEnumeration( source ) ) return qfalse; continue; } // recursively concatenate strings that are behind each other still resolving defines if (token->type == TT_STRING) { token_t newtoken; if (Parse_ReadToken(source, &newtoken)) { if (newtoken.type == TT_STRING) { token->string[strlen(token->string)-1] = '\0'; if (strlen(token->string) + strlen(newtoken.string+1) + 1 >= MAX_TOKEN_CHARS) { Parse_SourceError(source, "string longer than MAX_TOKEN_CHARS %d\n", MAX_TOKEN_CHARS); return qfalse; } strcat(token->string, newtoken.string+1); } else { Parse_UnreadToken(source, &newtoken); } } } //if skipping source because of conditional compilation if (source->skip) continue; //if the token is a name if (token->type == TT_NAME) { //check if the name is a define macro define = Parse_FindHashedDefine(source->definehash, token->string); //if it is a define macro if (define) { //expand the defined macro if (!Parse_ExpandDefineIntoSource(source, token, define)) return qfalse; continue; } } //copy token for unreading Com_Memcpy(&source->token, token, sizeof(token_t)); //found a token return qtrue; } } /* =============== Parse_DefineFromString =============== */ static define_t *Parse_DefineFromString(char *string) { script_t *script; source_t src; token_t *t; int res, i; define_t *def; script = Parse_LoadScriptMemory(string, strlen(string), "*extern"); //create a new source Com_Memset(&src, 0, sizeof(source_t)); strncpy(src.filename, "*extern", MAX_QPATH); src.scriptstack = script; src.definehash = Z_Malloc(DEFINEHASHSIZE * sizeof(define_t *)); Com_Memset( src.definehash, 0, DEFINEHASHSIZE * sizeof(define_t *)); //create a define from the source res = Parse_Directive_define(&src); //free any tokens if left for (t = src.tokens; t; t = src.tokens) { src.tokens = src.tokens->next; Parse_FreeToken(t); } def = NULL; for (i = 0; i < DEFINEHASHSIZE; i++) { if (src.definehash[i]) { def = src.definehash[i]; break; } } // Z_Free(src.definehash); // Parse_FreeScript(script); //if the define was created succesfully if (res > 0) return def; //free the define is created if (src.defines) Parse_FreeDefine(def); // return NULL; } /* =============== Parse_AddDefineToSourceFromString =============== */ static qboolean Parse_AddDefineToSourceFromString( source_t *source, char *string ) { Parse_PushScript( source, Parse_LoadScriptMemory( string, strlen( string ), "*extern" ) ); return Parse_Directive_define( source ); } /* =============== Parse_AddGlobalDefine add a globals define that will be added to all opened sources =============== */ int Parse_AddGlobalDefine(char *string) { define_t *define; define = Parse_DefineFromString(string); if (!define) return qfalse; define->next = globaldefines; globaldefines = define; return qtrue; } /* =============== Parse_CopyDefine =============== */ static define_t *Parse_CopyDefine(source_t *source, define_t *define) { define_t *newdefine; token_t *token, *newtoken, *lasttoken; newdefine = (define_t *) Z_Malloc(sizeof(define_t) + strlen(define->name) + 1); //copy the define name newdefine->name = (char *) newdefine + sizeof(define_t); strcpy(newdefine->name, define->name); newdefine->flags = define->flags; newdefine->builtin = define->builtin; newdefine->numparms = define->numparms; //the define is not linked newdefine->next = NULL; newdefine->hashnext = NULL; //copy the define tokens newdefine->tokens = NULL; for (lasttoken = NULL, token = define->tokens; token; token = token->next) { newtoken = Parse_CopyToken(token); newtoken->next = NULL; if (lasttoken) lasttoken->next = newtoken; else newdefine->tokens = newtoken; lasttoken = newtoken; } //copy the define parameters newdefine->parms = NULL; for (lasttoken = NULL, token = define->parms; token; token = token->next) { newtoken = Parse_CopyToken(token); newtoken->next = NULL; if (lasttoken) lasttoken->next = newtoken; else newdefine->parms = newtoken; lasttoken = newtoken; } return newdefine; } /* =============== Parse_AddGlobalDefinesToSource =============== */ static void Parse_AddGlobalDefinesToSource(source_t *source) { define_t *define, *newdefine; for (define = globaldefines; define; define = define->next) { newdefine = Parse_CopyDefine(source, define); Parse_AddDefineToHash(newdefine, source->definehash); } } /* =============== Parse_LoadSourceFile =============== */ static source_t *Parse_LoadSourceFile(const char *filename) { source_t *source; script_t *script; script = Parse_LoadScriptFile(filename); if (!script) return NULL; script->next = NULL; source = (source_t *) Z_Malloc(sizeof(source_t)); Com_Memset(source, 0, sizeof(source_t)); strncpy(source->filename, filename, MAX_QPATH); source->scriptstack = script; source->tokens = NULL; source->defines = NULL; source->indentstack = NULL; source->skip = 0; source->definehash = Z_Malloc(DEFINEHASHSIZE * sizeof(define_t *)); Com_Memset( source->definehash, 0, DEFINEHASHSIZE * sizeof(define_t *)); Parse_AddGlobalDefinesToSource(source); return source; } /* =============== Parse_FreeSource =============== */ static void Parse_FreeSource(source_t *source) { script_t *script; token_t *token; define_t *define; indent_t *indent; int i; //Parse_PrintDefineHashTable(source->definehash); //free all the scripts while(source->scriptstack) { script = source->scriptstack; source->scriptstack = source->scriptstack->next; Parse_FreeScript(script); } //free all the tokens while(source->tokens) { token = source->tokens; source->tokens = source->tokens->next; Parse_FreeToken(token); } for (i = 0; i < DEFINEHASHSIZE; i++) { while(source->definehash[i]) { define = source->definehash[i]; source->definehash[i] = source->definehash[i]->hashnext; Parse_FreeDefine(define); } } //free all indents while(source->indentstack) { indent = source->indentstack; source->indentstack = source->indentstack->next; Z_Free(indent); } // if (source->definehash) Z_Free(source->definehash); //free the source itself Z_Free(source); } #define MAX_SOURCEFILES 64 source_t *sourceFiles[MAX_SOURCEFILES]; /* =============== Parse_LoadSourceHandle =============== */ int Parse_LoadSourceHandle(const char *filename) { source_t *source; int i; for (i = 1; i < MAX_SOURCEFILES; i++) { if (!sourceFiles[i]) break; } if (i >= MAX_SOURCEFILES) return 0; source = Parse_LoadSourceFile(filename); if (!source) return 0; sourceFiles[i] = source; return i; } /* =============== Parse_FreeSourceHandle =============== */ int Parse_FreeSourceHandle(int handle) { if (handle < 1 || handle >= MAX_SOURCEFILES) return qfalse; if (!sourceFiles[handle]) return qfalse; Parse_FreeSource(sourceFiles[handle]); sourceFiles[handle] = NULL; return qtrue; } /* =============== Parse_ReadTokenHandle =============== */ int Parse_ReadTokenHandle(int handle, pc_token_t *pc_token) { token_t token; int ret; if (handle < 1 || handle >= MAX_SOURCEFILES) return 0; if (!sourceFiles[handle]) return 0; ret = Parse_ReadToken(sourceFiles[handle], &token); strcpy(pc_token->string, token.string); pc_token->type = token.type; pc_token->subtype = token.subtype; pc_token->intvalue = token.intvalue; pc_token->floatvalue = token.floatvalue; if (pc_token->type == TT_STRING) Parse_StripDoubleQuotes(pc_token->string); return ret; } /* =============== Parse_SourceFileAndLine =============== */ int Parse_SourceFileAndLine(int handle, char *filename, int *line) { if (handle < 1 || handle >= MAX_SOURCEFILES) return qfalse; if (!sourceFiles[handle]) return qfalse; strcpy(filename, sourceFiles[handle]->filename); if (sourceFiles[handle]->scriptstack) *line = sourceFiles[handle]->scriptstack->line; else *line = 0; return qtrue; }