summaryrefslogtreecommitdiff
path: root/src/qcommon/parse.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/qcommon/parse.c')
-rw-r--r--src/qcommon/parse.c3585
1 files changed, 3585 insertions, 0 deletions
diff --git a/src/qcommon/parse.c b/src/qcommon/parse.c
new file mode 100644
index 0000000..136ba0b
--- /dev/null
+++ b/src/qcommon/parse.c
@@ -0,0 +1,3585 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+Copyright (C) 2000-2006 Tim Angus
+
+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);
+
+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}
+};
+
+char basefolder[MAX_QPATH];
+
+/*
+===============
+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') // bk001204 - brackets
+ && !(token->subtype & TT_LONG))
+ {
+ script->script_p++;
+ token->subtype |= TT_LONG;
+ }
+ //check for an UNSIGNED number
+ else if ( (c == 'u' || c == 'U') // bk001204 - brackets
+ && !(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_ReadSciptToken
+===============
+*/
+static int Parse_ReadSciptToken(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 == '\"')
+ {
+ strcpy(string, 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;
+ char pathname[MAX_QPATH];
+ int length;
+ void *buffer;
+ script_t *script;
+
+ if (strlen(basefolder))
+ Com_sprintf(pathname, sizeof(pathname), "%s/%s", basefolder, filename);
+ else
+ Com_sprintf(pathname, sizeof(pathname), "%s", filename);
+ length = FS_FOpenFileRead( pathname, &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);
+ //
+ script->length = COM_Compress(script->buffer);
+
+ 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_SetBaseFolder
+===============
+*/
+static void Parse_SetBaseFolder(char *path)
+{
+ Com_sprintf(basefolder, sizeof(basefolder), path);
+}
+
+/*
+===============
+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;
+
+ //if there's no token already available
+ while(!source->tokens)
+ {
+ //if there's a token to read from the script
+ if (Parse_ReadSciptToken(source->scriptstack, token)) return qtrue;
+ //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;
+#ifdef _WIN32
+ unsigned long t; // time_t t; //to prevent LCC warning
+#else
+ time_t t;
+#endif
+
+ 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) == '/'))
+ {
+ strcpy(ptr, ptr+1);
+ }
+ 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);
+ }
+ 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_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;
+ }
+ }
+ // 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_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;
+ Parse_SetBaseFolder("");
+ 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;
+}