/*
===========================================================================

Doom 3 GPL Source Code
Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.

This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").

Doom 3 Source Code 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 3 of the License, or
(at your option) any later version.

Doom 3 Source Code 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 Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

#include "sys/platform.h"
#include "idlib/Lib.h"
#include "framework/Common.h"

#include "idlib/Parser.h"

//#define DEBUG_EVAL
#define MAX_DEFINEPARMS				128
#define DEFINEHASHSIZE				2048

#define TOKEN_FL_RECURSIVE_DEFINE	1

define_t * idParser::globaldefines;

/*
================
idParser::SetBaseFolder
================
*/
void idParser::SetBaseFolder( const char *path) {
	idLexer::SetBaseFolder(path);
}

/*
================
idParser::AddGlobalDefine
================
*/
int idParser::AddGlobalDefine( const char *string ) {
	define_t *define;

	define = idParser::DefineFromString(string);
	if (!define) {
		return false;
	}
	define->next = globaldefines;
	globaldefines = define;
	return true;
}

/*
================
idParser::RemoveGlobalDefine
================
*/
int idParser::RemoveGlobalDefine( const char *name ) {
	define_t *d, *prev;

	for ( prev = NULL, d = idParser::globaldefines; d; prev = d, d = d->next ) {
		if ( !strcmp( d->name, name ) ) {
			break;
		}
	}
	if ( d ) {
		if ( prev ) {
			prev->next = d->next;
		}
		else {
			idParser::globaldefines = d->next;
		}
		idParser::FreeDefine( d );
		return true;
	}
	return false;
}

/*
================
idParser::RemoveAllGlobalDefines
================
*/
void idParser::RemoveAllGlobalDefines( void ) {
	define_t *define;

	for ( define = globaldefines; define; define = globaldefines ) {
		globaldefines = globaldefines->next;
		idParser::FreeDefine(define);
	}
}


/*
===============================================================================

idParser

===============================================================================
*/

/*
================
idParser::PrintDefine
================
*/
void idParser::PrintDefine( define_t *define ) {
	idLib::common->Printf("define->name = %s\n", define->name);
	idLib::common->Printf("define->flags = %d\n", define->flags);
	idLib::common->Printf("define->builtin = %d\n", define->builtin);
	idLib::common->Printf("define->numparms = %d\n", define->numparms);
}

/*
================
PC_PrintDefineHashTable
================
* /
static void PC_PrintDefineHashTable(define_t **definehash) {
	int i;
	define_t *d;

	for (i = 0; i < DEFINEHASHSIZE; i++) {
		Log_Write("%4d:", i);
		for (d = definehash[i]; d; d = d->hashnext) {
			Log_Write(" %s", d->name);
		}
		Log_Write("\n");
	}
}
*/

/*
================
PC_NameHash
================
*/
ID_INLINE int PC_NameHash( const char *name ) {
	int hash, i;

	hash = 0;
	for ( i = 0; name[i] != '\0'; i++ ) {
		hash += name[i] * (119 + i);
	}
	hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1);
	return hash;
}

/*
================
idParser::AddDefineToHash
================
*/
void idParser::AddDefineToHash( define_t *define, define_t **definehash ) {
	int hash;

	hash = PC_NameHash(define->name);
	define->hashnext = definehash[hash];
	definehash[hash] = define;
}

/*
================
FindHashedDefine
================
*/
define_t *idParser::FindHashedDefine( define_t **definehash, const char *name ) {
	define_t *d;
	int hash;

	hash = PC_NameHash(name);
	for ( d = definehash[hash]; d; d = d->hashnext ) {
		if ( !strcmp(d->name, name) ) {
			return d;
		}
	}
	return NULL;
}

/*
================
idParser::FindDefine
================
*/
define_t *idParser::FindDefine( define_t *defines, const char *name ) {
	define_t *d;

	for ( d = defines; d; d = d->next ) {
		if ( !strcmp(d->name, name) ) {
			return d;
		}
	}
	return NULL;
}

/*
================
idParser::FindDefineParm
================
*/
int idParser::FindDefineParm( define_t *define, const char *name ) {
	idToken *p;
	int i;

	i = 0;
	for ( p = define->parms; p; p = p->next ) {
		if ( (*p) == name ) {
			return i;
		}
		i++;
	}
	return -1;
}

/*
================
idParser::CopyDefine
================
*/
define_t *idParser::CopyDefine( define_t *define ) {
	define_t *newdefine;
	idToken *token, *newtoken, *lasttoken;

	newdefine = (define_t *) Mem_Alloc(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 = new idToken(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 = new idToken(token);
		newtoken->next = NULL;
		if (lasttoken) lasttoken->next = newtoken;
		else newdefine->parms = newtoken;
		lasttoken = newtoken;
	}
	return newdefine;
}

/*
================
idParser::FreeDefine
================
*/
void idParser::FreeDefine( define_t *define ) {
	idToken *t, *next;

	//free the define parameters
	for (t = define->parms; t; t = next) {
		next = t->next;
		delete t;
	}
	//free the define tokens
	for (t = define->tokens; t; t = next) {
		next = t->next;
		delete t;
	}
	//free the define
	Mem_Free( define );
}

/*
================
idParser::DefineFromString
================
*/
define_t *idParser::DefineFromString( const char *string ) {
	idParser src;
	define_t *def;

	if ( !src.LoadMemory(string, strlen(string), "*defineString") ) {
		return NULL;
	}
	// create a define from the source
	if ( !src.Directive_define() ) {
		src.FreeSource();
		return NULL;
	}
	def = src.CopyFirstDefine();
	src.FreeSource();
	//if the define was created succesfully
	return def;
}

/*
================
idParser::Error
================
*/
void idParser::Error( const char *str, ... ) const {
	char text[MAX_STRING_CHARS];
	va_list ap;

	va_start(ap, str);
	vsprintf(text, str, ap);
	va_end(ap);
	if ( idParser::scriptstack ) {
		idParser::scriptstack->Error( text );
	}
}

/*
================
idParser::Warning
================
*/
void idParser::Warning( const char *str, ... ) const {
	char text[MAX_STRING_CHARS];
	va_list ap;

	va_start(ap, str);
	vsprintf(text, str, ap);
	va_end(ap);
	if ( idParser::scriptstack ) {
		idParser::scriptstack->Warning( text );
	}
}

/*
================
idParser::PushIndent
================
*/
void idParser::PushIndent( int type, int skip ) {
	indent_t *indent;

	indent = (indent_t *) Mem_Alloc(sizeof(indent_t));
	indent->type = type;
	indent->script = idParser::scriptstack;
	indent->skip = (skip != 0);
	idParser::skip += indent->skip;
	indent->next = idParser::indentstack;
	idParser::indentstack = indent;
}

/*
================
idParser::PopIndent
================
*/
void idParser::PopIndent( int *type, int *skip ) {
	indent_t *indent;

	*type = 0;
	*skip = 0;

	indent = idParser::indentstack;
	if (!indent) return;

	// must be an indent from the current script
	if (idParser::indentstack->script != idParser::scriptstack) {
		return;
	}

	*type = indent->type;
	*skip = indent->skip;
	idParser::indentstack = idParser::indentstack->next;
	idParser::skip -= indent->skip;
	Mem_Free( indent );
}

/*
================
idParser::PushScript
================
*/
void idParser::PushScript( idLexer *script ) {
	idLexer *s;

	for ( s = idParser::scriptstack; s; s = s->next ) {
		if ( !idStr::Icmp(s->GetFileName(), script->GetFileName()) ) {
			idParser::Warning( "'%s' recursively included", script->GetFileName() );
			return;
		}
	}
	//push the script on the script stack
	script->next = idParser::scriptstack;
	idParser::scriptstack = script;
}

/*
================
idParser::ReadSourceToken
================
*/
int idParser::ReadSourceToken( idToken *token ) {
	idToken *t;
	idLexer *script;
	int type, skip, changedScript;

	if ( !idParser::scriptstack ) {
		idLib::common->FatalError( "idParser::ReadSourceToken: not loaded" );
		return false;
	}
	changedScript = 0;
	// if there's no token already available
	while( !idParser::tokens ) {
		// if there's a token to read from the script
		if ( idParser::scriptstack->ReadToken( token ) ) {
			token->linesCrossed += changedScript;

			// set the marker based on the start of the token read in
			if ( !marker_p ) {
				marker_p = token->whiteSpaceEnd_p;
			}
			return true;
		}
		// if at the end of the script
		if ( idParser::scriptstack->EndOfFile() ) {
			// remove all indents of the script
			while( idParser::indentstack && idParser::indentstack->script == idParser::scriptstack ) {
				idParser::Warning( "missing #endif" );
				idParser::PopIndent( &type, &skip );
			}
			changedScript = 1;
		}
		// if this was the initial script
		if ( !idParser::scriptstack->next ) {
			return false;
		}
		// remove the script and return to the previous one
		script = idParser::scriptstack;
		idParser::scriptstack = idParser::scriptstack->next;
		delete script;
	}
	// copy the already available token
	*token = idParser::tokens;
	// remove the token from the source
	t = idParser::tokens;
	idParser::tokens = idParser::tokens->next;
	delete t;
	return true;
}

/*
================
idParser::UnreadSourceToken
================
*/
int idParser::UnreadSourceToken( idToken *token ) {
	idToken *t;

	t = new idToken(token);
	t->next = idParser::tokens;
	idParser::tokens = t;
	return true;
}

/*
================
idParser::ReadDefineParms
================
*/
int idParser::ReadDefineParms( define_t *define, idToken **parms, int maxparms ) {
	define_t *newdefine;
	idToken token, *t, *last;
	int i, done, lastcomma, numparms, indent;

	if ( !idParser::ReadSourceToken( &token ) ) {
		idParser::Error( "define '%s' missing parameters", define->name );
		return false;
	}

	if ( define->numparms > maxparms ) {
		idParser::Error( "define with more than %d parameters", maxparms );
		return false;
	}

	for ( i = 0; i < define->numparms; i++ ) {
		parms[i] = NULL;
	}
	// if no leading "("
	if ( token != "(" ) {
		idParser::UnreadSourceToken( &token );
		idParser::Error( "define '%s' missing parameters", define->name );
		return false;
	}
	// read the define parameters
	for ( done = 0, numparms = 0, indent = 1; !done; ) {
		if ( numparms >= maxparms ) {
			idParser::Error( "define '%s' with too many parameters", define->name );
			return false;
		}
		parms[numparms] = NULL;
		lastcomma = 1;
		last = NULL;
		while( !done ) {

			if ( !idParser::ReadSourceToken( &token ) ) {
				idParser::Error( "define '%s' incomplete", define->name );
				return false;
			}

			if ( token == "," ) {
				if ( indent <= 1 ) {
					if ( lastcomma ) {
						idParser::Warning( "too many comma's" );
					}
					if ( numparms >= define->numparms ) {
						idParser::Warning( "too many define parameters" );
					}
					lastcomma = 1;
					break;
				}
			}
			else if ( token == "(" ) {
				indent++;
			}
			else if ( token == ")" ) {
				indent--;
				if ( indent <= 0 ) {
					if ( !parms[define->numparms-1] ) {
						idParser::Warning( "too few define parameters" );
					}
					done = 1;
					break;
				}
			}
			else if ( token.type == TT_NAME ) {
				newdefine = FindHashedDefine( idParser::definehash, token.c_str() );
				if ( newdefine ) {
					if ( !idParser::ExpandDefineIntoSource( &token, newdefine ) ) {
						return false;
					}
					continue;
				}
			}

			lastcomma = 0;

			if ( numparms < define->numparms ) {

				t = new idToken( token );
				t->next = NULL;
				if (last) last->next = t;
				else parms[numparms] = t;
				last = t;
			}
		}
		numparms++;
	}
	return true;
}

/*
================
idParser::StringizeTokens
================
*/
int idParser::StringizeTokens( idToken *tokens, idToken *token ) {
	idToken *t;

	token->type = TT_STRING;
	token->whiteSpaceStart_p = NULL;
	token->whiteSpaceEnd_p = NULL;
	(*token) = "";
	for ( t = tokens; t; t = t->next ) {
		token->Append( t->c_str() );
	}
	return true;
}

/*
================
idParser::MergeTokens
================
*/
int idParser::MergeTokens( idToken *t1, idToken *t2 ) {
	// merging of a name with a name or number
	if ( t1->type == TT_NAME && (t2->type == TT_NAME || (t2->type == TT_NUMBER && !(t2->subtype & TT_FLOAT))) ) {
		t1->Append( t2->c_str() );
		return true;
	}
	// merging of two strings
	if (t1->type == TT_STRING && t2->type == TT_STRING) {
		t1->Append( t2->c_str() );
		return true;
	}
	// merging of two numbers
	if ( t1->type == TT_NUMBER && t2->type == TT_NUMBER &&
			!(t1->subtype & (TT_HEX|TT_BINARY)) && !(t2->subtype & (TT_HEX|TT_BINARY)) &&
			(!(t1->subtype & TT_FLOAT) || !(t2->subtype & TT_FLOAT)) ) {
		t1->Append( t2->c_str() );
		return true;
	}

	return false;
}

/*
================
idParser::AddBuiltinDefines
================
*/
void idParser::AddBuiltinDefines( void ) {
	int i;
	define_t *define;
	struct builtin
	{
		const char *string;
		int id;
	} builtin[] = {
		{ "__LINE__",	BUILTIN_LINE },
		{ "__FILE__",	BUILTIN_FILE },
		{ "__DATE__",	BUILTIN_DATE },
		{ "__TIME__",	BUILTIN_TIME },
		{ "__STDC__", BUILTIN_STDC },
		{ NULL, 0 }
	};

	for (i = 0; builtin[i].string; i++) {
		define = (define_t *) Mem_Alloc(sizeof(define_t) + strlen(builtin[i].string) + 1);
		define->name = (char *) define + sizeof(define_t);
		strcpy(define->name, builtin[i].string);
		define->flags = DEFINE_FIXED;
		define->builtin = builtin[i].id;
		define->numparms = 0;
		define->parms = NULL;
		define->tokens = NULL;
		// add the define to the source
		AddDefineToHash(define, idParser::definehash);
	}
}

/*
================
idParser::CopyFirstDefine
================
*/
define_t *idParser::CopyFirstDefine( void ) {
	int i;

	for ( i = 0; i < DEFINEHASHSIZE; i++ ) {
		if ( idParser::definehash[i] ) {
			return CopyDefine(idParser::definehash[i]);
		}
	}
	return NULL;
}

/*
================
idParser::ExpandBuiltinDefine
================
*/
int idParser::ExpandBuiltinDefine( idToken *deftoken, define_t *define, idToken **firsttoken, idToken **lasttoken ) {
	idToken *token;
	ID_TIME_T t;
	char *curtime;
	char buf[MAX_STRING_CHARS];

	token = new idToken(deftoken);
	switch( define->builtin ) {
		case BUILTIN_LINE: {
			sprintf( buf, "%d", deftoken->line );
			(*token) = buf;
			token->intvalue = deftoken->line;
			token->floatvalue = deftoken->line;
			token->type = TT_NUMBER;
			token->subtype = TT_DECIMAL | TT_INTEGER | TT_VALUESVALID;
			token->line = deftoken->line;
			token->linesCrossed = deftoken->linesCrossed;
			token->flags = 0;
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_FILE: {
			(*token) = idParser::scriptstack->GetFileName();
			token->type = TT_NAME;
			token->subtype = token->Length();
			token->line = deftoken->line;
			token->linesCrossed = deftoken->linesCrossed;
			token->flags = 0;
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_DATE: {
			t = time(NULL);
			curtime = ctime(&t);
			(*token) = "\"";
			token->Append( curtime+4 );
			token[7] = NULL;
			token->Append( curtime+20 );
			token[10] = NULL;
			token->Append( "\"" );
			free(curtime);
			token->type = TT_STRING;
			token->subtype = token->Length();
			token->line = deftoken->line;
			token->linesCrossed = deftoken->linesCrossed;
			token->flags = 0;
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_TIME: {
			t = time(NULL);
			curtime = ctime(&t);
			(*token) = "\"";
			token->Append( curtime+11 );
			token[8] = NULL;
			token->Append( "\"" );
			free(curtime);
			token->type = TT_STRING;
			token->subtype = token->Length();
			token->line = deftoken->line;
			token->linesCrossed = deftoken->linesCrossed;
			token->flags = 0;
			*firsttoken = token;
			*lasttoken = token;
			break;
		}
		case BUILTIN_STDC: {
			idParser::Warning( "__STDC__ not supported\n" );
			*firsttoken = NULL;
			*lasttoken = NULL;
			break;
		}
		default: {
			*firsttoken = NULL;
			*lasttoken = NULL;
			break;
		}
	}
	return true;
}

/*
================
idParser::ExpandDefine
================
*/
int idParser::ExpandDefine( idToken *deftoken, define_t *define, idToken **firsttoken, idToken **lasttoken ) {
	idToken *parms[MAX_DEFINEPARMS], *dt, *pt, *t;
	idToken *t1, *t2, *first, *last, *nextpt, token;
	int parmnum, i;

	// if it is a builtin define
	if ( define->builtin ) {
		return idParser::ExpandBuiltinDefine( deftoken, define, firsttoken, lasttoken );
	}
	// if the define has parameters
	if ( define->numparms ) {
		if ( !idParser::ReadDefineParms( define, parms, MAX_DEFINEPARMS ) ) {
			return false;
		}
#ifdef DEBUG_EVAL
		for ( i = 0; i < define->numparms; i++ ) {
			Log_Write("define parms %d:", i);
			for ( pt = parms[i]; pt; pt = pt->next ) {
				Log_Write( "%s", pt->c_str() );
			}
		}
#endif //DEBUG_EVAL
	}
	// 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 = FindDefineParm( define, dt->c_str() );
		}
		// if it is a define parameter
		if ( parmnum >= 0 ) {
			for ( pt = parms[parmnum]; pt; pt = pt->next ) {
				t = new idToken(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) == "#" ) {
				// the stringizing operator must be followed by a define parameter
				if ( dt->next ) {
					parmnum = FindDefineParm( define, dt->next->c_str() );
				}
				else {
					parmnum = -1;
				}

				if ( parmnum >= 0 ) {
					// step over the stringizing operator
					dt = dt->next;
					// stringize the define parameter tokens
					if ( !idParser::StringizeTokens( parms[parmnum], &token ) ) {
						idParser::Error( "can't stringize tokens" );
						return false;
					}
					t = new idToken(token);
					t->line = deftoken->line;
				}
				else {
					idParser::Warning( "stringizing operator without define parameter" );
					continue;
				}
			}
			else {
				t = new idToken(dt);
				t->line = deftoken->line;
			}
			// add the token to the list
			t->next = NULL;
// the token being read from the define list should use the line number of
// the original file, not the header file
			t->line = deftoken->line;

			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) == "##" ) {
				t1 = t;
				t2 = t->next->next;
				if ( t2 ) {
					if ( !idParser::MergeTokens( t1, t2 ) ) {
						idParser::Error( "can't merge '%s' with '%s'", t1->c_str(), t2->c_str() );
						return false;
					}
					delete t1->next;
					t1->next = t2->next;
					if ( t2 == last ) last = t1;
					delete 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;
			delete pt;
		}
	}

	return true;
}

/*
================
idParser::ExpandDefineIntoSource
================
*/
int idParser::ExpandDefineIntoSource( idToken *deftoken, define_t *define ) {
	idToken *firsttoken, *lasttoken;

	if ( !idParser::ExpandDefine( deftoken, define, &firsttoken, &lasttoken ) ) {
		return false;
	}
	// if the define is not empty
	if ( firsttoken && lasttoken ) {
		firsttoken->linesCrossed += deftoken->linesCrossed;
		lasttoken->next = idParser::tokens;
		idParser::tokens = firsttoken;
	}
	return true;
}

/*
================
idParser::ReadLine

reads a token from the current line, continues reading on the next
line only if a backslash '\' is found
================
*/
int idParser::ReadLine( idToken *token ) {
	int crossline;

	crossline = 0;
	do {
		if (!idParser::ReadSourceToken( token )) {
			return false;
		}

		if (token->linesCrossed > crossline) {
			idParser::UnreadSourceToken( token );
			return false;
		}
		crossline = 1;
	} while( (*token) == "\\" );
	return true;
}

/*
================
idParser::Directive_include
================
*/
int idParser::Directive_include( void ) {
	idLexer *script;
	idToken token;
	idStr path;

	if ( !idParser::ReadSourceToken( &token ) ) {
		idParser::Error( "#include without file name" );
		return false;
	}
	if ( token.linesCrossed > 0 ) {
		idParser::Error( "#include without file name" );
		return false;
	}
	if ( token.type == TT_STRING ) {
		script = new idLexer;
		// try relative to the current file
		path = scriptstack->GetFileName();
		path.StripFilename();
		path += "/";
		path += token;
		if ( !script->LoadFile( path, OSPath ) ) {
			// try absolute path
			path = token;
			if ( !script->LoadFile( path, OSPath ) ) {
				// try from the include path
				path = includepath + token;
				if ( !script->LoadFile( path, OSPath ) ) {
					delete script;
					script = NULL;
				}
			}
		}
	}
	else if ( token.type == TT_PUNCTUATION && token == "<" ) {
		path = idParser::includepath;
		while( idParser::ReadSourceToken( &token ) ) {
			if ( token.linesCrossed > 0 ) {
				idParser::UnreadSourceToken( &token );
				break;
			}
			if ( token.type == TT_PUNCTUATION && token == ">" ) {
				break;
			}
			path += token;
		}
		if ( token != ">" ) {
			idParser::Warning( "#include missing trailing >" );
		}
		if ( !path.Length() ) {
			idParser::Error( "#include without file name between < >" );
			return false;
		}
		if ( idParser::flags & LEXFL_NOBASEINCLUDES ) {
			return true;
		}
		script = new idLexer;
		if ( !script->LoadFile( includepath + path, OSPath ) ) {
			delete script;
			script = NULL;
		}
	}
	else {
		idParser::Error( "#include without file name" );
		return false;
	}
	if (!script) {
		idParser::Error( "file '%s' not found", path.c_str() );
		return false;
	}
	script->SetFlags( idParser::flags );
	script->SetPunctuations( idParser::punctuations );
	idParser::PushScript( script );
	return true;
}

/*
================
idParser::Directive_undef
================
*/
int idParser::Directive_undef( void ) {
	idToken token;
	define_t *define, *lastdefine;
	int hash;

	//
	if (!idParser::ReadLine( &token )) {
		idParser::Error( "undef without name" );
		return false;
	}
	if (token.type != TT_NAME) {
		idParser::UnreadSourceToken( &token );
		idParser::Error( "expected name but found '%s'", token.c_str() );
		return false;
	}

	hash = PC_NameHash( token.c_str() );
	for (lastdefine = NULL, define = idParser::definehash[hash]; define; define = define->hashnext) {
		if (!strcmp(define->name, token.c_str()))
		{
			if (define->flags & DEFINE_FIXED) {
				idParser::Warning( "can't undef '%s'", token.c_str() );
			}
			else {
				if (lastdefine) {
					lastdefine->hashnext = define->hashnext;
				}
				else {
					idParser::definehash[hash] = define->hashnext;
				}
				FreeDefine(define);
			}
			break;
		}
		lastdefine = define;
	}
	return true;
}

/*
================
idParser::Directive_define
================
*/
int idParser::Directive_define( void ) {
	idToken token, *t, *last;
	define_t *define;

	if (!idParser::ReadLine( &token )) {
		idParser::Error( "#define without name" );
		return false;
	}
	if (token.type != TT_NAME) {
		idParser::UnreadSourceToken( &token );
		idParser::Error( "expected name after #define, found '%s'", token.c_str() );
		return false;
	}
	// check if the define already exists
	define = FindHashedDefine(idParser::definehash, token.c_str());
	if (define) {
		if (define->flags & DEFINE_FIXED) {
			idParser::Error( "can't redefine '%s'", token.c_str() );
			return false;
		}
		idParser::Warning( "redefinition of '%s'", token.c_str() );
		// unread the define name before executing the #undef directive
		idParser::UnreadSourceToken( &token );
		if (!idParser::Directive_undef())
			return false;
		// if the define was not removed (define->flags & DEFINE_FIXED)
		define = FindHashedDefine(idParser::definehash, token.c_str());
	}
	// allocate define
	define = (define_t *) Mem_ClearedAlloc(sizeof(define_t) + token.Length() + 1);
	define->name = (char *) define + sizeof(define_t);
	strcpy(define->name, token.c_str());
	// add the define to the source
	AddDefineToHash(define, idParser::definehash);
	// if nothing is defined, just return
	if ( !idParser::ReadLine( &token ) ) {
		return true;
	}
	// if it is a define with parameters
	if ( token.WhiteSpaceBeforeToken() == 0 && token == "(" ) {
		// read the define parameters
		last = NULL;
		if ( !idParser::CheckTokenString(")") ) {
			while(1) {
				if ( !idParser::ReadLine( &token ) ) {
					idParser::Error( "expected define parameter" );
					return false;
				}
				// if it isn't a name
				if (token.type != TT_NAME) {
					idParser::Error( "invalid define parameter" );
					return false;
				}

				if (FindDefineParm(define, token.c_str()) >= 0) {
					idParser::Error( "two the same define parameters" );
					return false;
				}
				// add the define parm
				t = new idToken(token);
				t->ClearTokenWhiteSpace();
				t->next = NULL;
				if (last) last->next = t;
				else define->parms = t;
				last = t;
				define->numparms++;
				// read next token
				if (!idParser::ReadLine( &token )) {
					idParser::Error( "define parameters not terminated" );
					return false;
				}

				if ( token == ")" ) {
					break;
				}
				// then it must be a comma
				if ( token != "," ) {
					idParser::Error( "define not terminated" );
					return false;
				}
			}
		}
		if ( !idParser::ReadLine( &token ) ) {
			return true;
		}
	}
	// read the defined stuff
	last = NULL;
	do
	{
		t = new idToken(token);
		if ( t->type == TT_NAME && !strcmp( t->c_str(), define->name ) ) {
			t->flags |= TOKEN_FL_RECURSIVE_DEFINE;
			idParser::Warning( "recursive define (removed recursion)" );
		}
		t->ClearTokenWhiteSpace();
		t->next = NULL;
		if ( last ) last->next = t;
		else define->tokens = t;
		last = t;
	} while( idParser::ReadLine( &token ) );

	if ( last ) {
		// check for merge operators at the beginning or end
		if ( (*define->tokens) == "##" || (*last) == "##" ) {
			idParser::Error( "define with misplaced ##" );
			return false;
		}
	}
	return true;
}

/*
================
idParser::AddDefine
================
*/
int idParser::AddDefine( const char *string ) {
	define_t *define;

	define = DefineFromString( string );
	if (!define) {
		return false;
	}
	AddDefineToHash(define, idParser::definehash);
	return true;
}

/*
================
idParser::AddGlobalDefinesToSource
================
*/
void idParser::AddGlobalDefinesToSource( void ) {
	define_t *define, *newdefine;

	for (define = globaldefines; define; define = define->next) {
		newdefine = CopyDefine( define );
		AddDefineToHash(newdefine, idParser::definehash);
	}
}

/*
================
idParser::Directive_if_def
================
*/
int idParser::Directive_if_def( int type ) {
	idToken token;
	define_t *d;
	int skip;

	if ( !idParser::ReadLine( &token ) ) {
		idParser::Error( "#ifdef without name" );
		return false;
	}
	if (token.type != TT_NAME) {
		idParser::UnreadSourceToken( &token );
		idParser::Error( "expected name after #ifdef, found '%s'", token.c_str() );
		return false;
	}
	d = FindHashedDefine(idParser::definehash, token.c_str());
	skip = (type == INDENT_IFDEF) == (d == NULL);
	idParser::PushIndent( type, skip );
	return true;
}

/*
================
idParser::Directive_ifdef
================
*/
int idParser::Directive_ifdef( void ) {
	return idParser::Directive_if_def( INDENT_IFDEF );
}

/*
================
idParser::Directive_ifndef
================
*/
int idParser::Directive_ifndef( void ) {
	return idParser::Directive_if_def( INDENT_IFNDEF );
}

/*
================
idParser::Directive_else
================
*/
int idParser::Directive_else( void ) {
	int type, skip;

	idParser::PopIndent( &type, &skip );
	if (!type) {
		idParser::Error( "misplaced #else" );
		return false;
	}
	if (type == INDENT_ELSE) {
		idParser::Error( "#else after #else" );
		return false;
	}
	idParser::PushIndent( INDENT_ELSE, !skip );
	return true;
}

/*
================
idParser::Directive_endif
================
*/
int idParser::Directive_endif( void ) {
	int type, skip;

	idParser::PopIndent( &type, &skip );
	if (!type) {
		idParser::Error( "misplaced #endif" );
		return false;
	}
	return true;
}

/*
================
idParser::EvaluateTokens
================
*/
typedef struct operator_s
{
	int op;
	int priority;
	int parentheses;
	struct operator_s *prev, *next;
} operator_t;

typedef struct value_s
{
	int intvalue;
	double floatvalue;
	int parentheses;
	struct value_s *prev, *next;
} value_t;

int PC_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 false;
}

//#define AllocValue()			GetClearedMemory(sizeof(value_t));
//#define FreeValue(val)		FreeMemory(val)
//#define AllocOperator(op)		op = (operator_t *) GetClearedMemory(sizeof(operator_t));
//#define FreeOperator(op)		FreeMemory(op);

#define MAX_VALUES		64
#define MAX_OPERATORS	64

#define AllocValue(val)									\
	if ( numvalues >= MAX_VALUES ) {					\
		idParser::Error( "out of value space\n" );		\
		error = 1;										\
		break;											\
	}													\
	else {												\
		val = &value_heap[numvalues++];					\
	}

#define FreeValue(val)

#define AllocOperator(op)								\
	if ( numoperators >= MAX_OPERATORS ) {				\
		idParser::Error( "out of operator space\n" );	\
		error = 1;										\
		break;											\
	}													\
	else {												\
		op = &operator_heap[numoperators++];			\
	}

#define FreeOperator(op)

int idParser::EvaluateTokens( idToken *tokens, int *intvalue, double *floatvalue, int integer ) {
	operator_t *o, *firstoperator, *lastoperator;
	value_t *v, *firstvalue, *lastvalue, *v1, *v2;
	idToken *t;
	int brace = 0;
	int parentheses = 0;
	int error = 0;
	int lastwasvalue = 0;
	int negativevalue = 0;
	int questmarkintvalue = 0;
	double questmarkfloatvalue = 0;
	int gotquestmarkvalue = false;
	//
	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 ) {
					idParser::Error( "syntax error in #if/#elif" );
					error = 1;
					break;
				}
				if ( (*t) != "defined" ) {
					idParser::Error( "undefined name '%s' in #if/#elif", t->c_str() );
					error = 1;
					break;
				}
				t = t->next;
				if ( (*t) == "(" ) {
					brace = true;
					t = t->next;
				}
				if (!t || t->type != TT_NAME) {
					idParser::Error( "defined() without name in #if/#elif" );
					error = 1;
					break;
				}
				//v = (value_t *) GetClearedMemory(sizeof(value_t));
				AllocValue(v);
				if (FindHashedDefine(idParser::definehash, t->c_str())) {
					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 || (*t) != ")" ) {
						idParser::Error( "defined missing ) in #if/#elif" );
						error = 1;
						break;
					}
				}
				brace = false;
				// defined() creates a value
				lastwasvalue = 1;
				break;
			}
			case TT_NUMBER:
			{
				if (lastwasvalue) {
					idParser::Error( "syntax error in #if/#elif" );
					error = 1;
					break;
				}
				//v = (value_t *) GetClearedMemory(sizeof(value_t));
				AllocValue(v);
				if (negativevalue) {
					v->intvalue = - t->GetIntValue();
					v->floatvalue = - t->GetFloatValue();
				}
				else {
					v->intvalue = t->GetIntValue();
					v->floatvalue = t->GetFloatValue();
				}
				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) {
					idParser::Error( "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) {
						idParser::Error( "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) {
						idParser::Error( "illigal operator '%s' on floating point operands\n", t->c_str() );
						error = 1;
						break;
					}
				}
				switch( t->subtype ) {
					case P_LOGIC_NOT:
					case P_BIN_NOT:
					{
						if (lastwasvalue) {
							idParser::Error( "! or ~ after value in #if/#elif" );
							error = 1;
							break;
						}
						break;
					}
					case P_INC:
					case P_DEC:
					{
						idParser::Error( "++ 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) {
							idParser::Error( "operator '%s' after operator in #if/#elif", t->c_str() );
							error = 1;
							break;
						}
						break;
					}
					default:
					{
						idParser::Error( "invalid operator '%s' in #if/#elif", t->c_str() );
						error = 1;
						break;
					}
				}
				if (!error && !negativevalue) {
					//o = (operator_t *) GetClearedMemory(sizeof(operator_t));
					AllocOperator(o);
					o->op = t->subtype;
					o->priority = PC_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:
			{
				idParser::Error( "unknown '%s' in #if/#elif", t->c_str() );
				error = 1;
				break;
			}
		}
		if (error) {
			break;
		}
	}
	if (!error) {
		if (!lastwasvalue) {
			idParser::Error( "trailing operator in #if/#elif" );
			error = 1;
		}
		else if (parentheses) {
			idParser::Error( "too many ( in #if/#elif" );
			error = 1;
		}
	}
	//
	gotquestmarkvalue = false;
	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->op != P_LOGIC_NOT && o->op != P_BIN_NOT) {
				v = v->next;
			}
			//if there's no value or no next value
			if (!v) {
				idParser::Error( "mising values in #if/#elif" );
				error = 1;
				break;
			}
		}
		if (error) {
			break;
		}
		v1 = v;
		v2 = v->next;
#ifdef DEBUG_EVAL
		if (integer) {
			Log_Write("operator %s, value1 = %d", idParser::scriptstack->getPunctuationFromId(o->op), v1->intvalue);
			if (v2) Log_Write("value2 = %d", v2->intvalue);
		}
		else {
			Log_Write("operator %s, value1 = %f", idParser::scriptstack->getPunctuationFromId(o->op), v1->floatvalue);
			if (v2) Log_Write("value2 = %f", v2->floatvalue);
		}
#endif //DEBUG_EVAL
		switch(o->op) {
			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)
									{
										idParser::Error( "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)
									{
										idParser::Error( "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) {
					idParser::Error( ": without ? in #if/#elif" );
					error = 1;
					break;
				}
				if (integer) {
					if (!questmarkintvalue)
						v1->intvalue = v2->intvalue;
				}
				else {
					if (!questmarkfloatvalue)
						v1->floatvalue = v2->floatvalue;
				}
				gotquestmarkvalue = false;
				break;
			}
			case P_QUESTIONMARK:
			{
				if (gotquestmarkvalue) {
					idParser::Error( "? after ? in #if/#elif" );
					error = 1;
					break;
				}
				questmarkintvalue = v1->intvalue;
				questmarkfloatvalue = v1->floatvalue;
				gotquestmarkvalue = true;
				break;
			}
		}
#ifdef DEBUG_EVAL
		if (integer) Log_Write("result value = %d", v1->intvalue);
		else Log_Write("result value = %f", v1->floatvalue);
#endif //DEBUG_EVAL
		if (error)
			break;
		//if not an operator with arity 1
		if (o->op != P_LOGIC_NOT && o->op != P_BIN_NOT) {
			//remove the second value if not question mark operator
			if (o->op != 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;
			//FreeMemory(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;
		//FreeMemory(o);
		FreeOperator(o);
	}
	if (firstvalue) {
		if (intvalue) *intvalue = firstvalue->intvalue;
		if (floatvalue) *floatvalue = firstvalue->floatvalue;
	}
	for (o = firstoperator; o; o = lastoperator) {
		lastoperator = o->next;
		//FreeMemory(o);
		FreeOperator(o);
	}
	for (v = firstvalue; v; v = lastvalue) {
		lastvalue = v->next;
		//FreeMemory(v);
		FreeValue(v);
	}
	if (!error) {
		return true;
	}
	if (intvalue) {
		*intvalue = 0;
	}
	if (floatvalue) {
		*floatvalue = 0;
	}
	return false;
}

/*
================
idParser::Evaluate
================
*/
int idParser::Evaluate( int *intvalue, double *floatvalue, int integer ) {
	idToken token, *firsttoken, *lasttoken;
	idToken *t, *nexttoken;
	define_t *define;
	int defined = false;

	if (intvalue) {
		*intvalue = 0;
	}
	if (floatvalue) {
		*floatvalue = 0;
	}
	//
	if ( !idParser::ReadLine( &token ) ) {
		idParser::Error( "no value after #if/#elif" );
		return false;
	}
	firsttoken = NULL;
	lasttoken = NULL;
	do {
		//if the token is a name
		if (token.type == TT_NAME) {
			if (defined) {
				defined = false;
				t = new idToken(token);
				t->next = NULL;
				if (lasttoken) lasttoken->next = t;
				else firsttoken = t;
				lasttoken = t;
			}
			else if ( token == "defined" ) {
				defined = true;
				t = new idToken(token);
				t->next = NULL;
				if (lasttoken) lasttoken->next = t;
				else firsttoken = t;
				lasttoken = t;
			}
			else {
				//then it must be a define
				define = FindHashedDefine(idParser::definehash, token.c_str());
				if (!define) {
					idParser::Error( "can't Evaluate '%s', not defined", token.c_str() );
					return false;
				}
				if ( !idParser::ExpandDefineIntoSource( &token, define ) ) {
					return false;
				}
			}
		}
		//if the token is a number or a punctuation
		else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) {
			t = new idToken(token);
			t->next = NULL;
			if (lasttoken) lasttoken->next = t;
			else firsttoken = t;
			lasttoken = t;
		}
		else {
			idParser::Error( "can't Evaluate '%s'", token.c_str() );
			return false;
		}
	} while(idParser::ReadLine( &token ));
	//
	if ( !idParser::EvaluateTokens( firsttoken, intvalue, floatvalue, integer ) ) {
		return false;
	}
	//
#ifdef DEBUG_EVAL
	Log_Write("eval:");
#endif //DEBUG_EVAL
	for (t = firsttoken; t; t = nexttoken) {
#ifdef DEBUG_EVAL
		Log_Write(" %s", t->c_str());
#endif //DEBUG_EVAL
		nexttoken = t->next;
		delete t;
	} //end for
#ifdef DEBUG_EVAL
	if (integer) Log_Write("eval result: %d", *intvalue);
	else Log_Write("eval result: %f", *floatvalue);
#endif //DEBUG_EVAL
	//
	return true;
}

/*
================
idParser::DollarEvaluate
================
*/
int idParser::DollarEvaluate( int *intvalue, double *floatvalue, int integer) {
	int indent, defined = false;
	idToken token, *firsttoken, *lasttoken;
	idToken *t, *nexttoken;
	define_t *define;

	if (intvalue) {
		*intvalue = 0;
	}
	if (floatvalue) {
		*floatvalue = 0;
	}
	//
	if ( !idParser::ReadSourceToken( &token ) ) {
		idParser::Error( "no leading ( after $evalint/$evalfloat" );
		return false;
	}
	if ( !idParser::ReadSourceToken( &token ) ) {
		idParser::Error( "nothing to Evaluate" );
		return false;
	}
	indent = 1;
	firsttoken = NULL;
	lasttoken = NULL;
	do {
		//if the token is a name
		if (token.type == TT_NAME) {
			if (defined) {
				defined = false;
				t = new idToken(token);
				t->next = NULL;
				if (lasttoken) lasttoken->next = t;
				else firsttoken = t;
				lasttoken = t;
			}
			else if ( token == "defined" ) {
				defined = true;
				t = new idToken(token);
				t->next = NULL;
				if (lasttoken) lasttoken->next = t;
				else firsttoken = t;
				lasttoken = t;
			}
			else {
				//then it must be a define
				define = FindHashedDefine(idParser::definehash, token.c_str());
				if (!define) {
					idParser::Warning( "can't Evaluate '%s', not defined", token.c_str() );
					return false;
				}
				if ( !idParser::ExpandDefineIntoSource( &token, define ) ) {
					return false;
				}
			}
		}
		//if the token is a number or a punctuation
		else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION) {
			if ( token[0] == '(' ) indent++;
			else if ( token[0] == ')' ) indent--;
			if (indent <= 0) {
				break;
			}
			t = new idToken(token);
			t->next = NULL;
			if (lasttoken) lasttoken->next = t;
			else firsttoken = t;
			lasttoken = t;
		}
		else {
			idParser::Error( "can't Evaluate '%s'", token.c_str() );
			return false;
		}
	} while(idParser::ReadSourceToken( &token ));
	//
	if (!idParser::EvaluateTokens( firsttoken, intvalue, floatvalue, integer)) {
		return false;
	}
	//
#ifdef DEBUG_EVAL
	Log_Write("$eval:");
#endif //DEBUG_EVAL
	for (t = firsttoken; t; t = nexttoken) {
#ifdef DEBUG_EVAL
		Log_Write(" %s", t->c_str());
#endif //DEBUG_EVAL
		nexttoken = t->next;
		delete t;
	} //end for
#ifdef DEBUG_EVAL
	if (integer) Log_Write("$eval result: %d", *intvalue);
	else Log_Write("$eval result: %f", *floatvalue);
#endif //DEBUG_EVAL
	//
	return true;
}

/*
================
idParser::Directive_elif
================
*/
int idParser::Directive_elif( void ) {
	int value;
	int type, skip;

	idParser::PopIndent( &type, &skip );
	if (!type || type == INDENT_ELSE) {
		idParser::Error( "misplaced #elif" );
		return false;
	}
	if ( !idParser::Evaluate( &value, NULL, true ) ) {
		return false;
	}
	skip = (value == 0);
	idParser::PushIndent( INDENT_ELIF, skip );
	return true;
}

/*
================
idParser::Directive_if
================
*/
int idParser::Directive_if( void ) {
	int value;
	int skip;

	if ( !idParser::Evaluate( &value, NULL, true ) ) {
		return false;
	}
	skip = (value == 0);
	idParser::PushIndent( INDENT_IF, skip );
	return true;
}

/*
================
idParser::Directive_line
================
*/
int idParser::Directive_line( void ) {
	idToken token;

	idParser::Error( "#line directive not supported" );
	while( idParser::ReadLine( &token ) ) {
	}
	return true;
}

/*
================
idParser::Directive_error
================
*/
int idParser::Directive_error( void ) {
	idToken token;

	if ( !idParser::ReadLine( &token) || token.type != TT_STRING ) {
		idParser::Error( "#error without string" );
		return false;
	}
	idParser::Error( "#error: %s", token.c_str() );
	return true;
}

/*
================
idParser::Directive_warning
================
*/
int idParser::Directive_warning( void ) {
	idToken token;

	if ( !idParser::ReadLine( &token) || token.type != TT_STRING ) {
		idParser::Warning( "#warning without string" );
		return false;
	}
	idParser::Warning( "#warning: %s", token.c_str() );
	return true;
}

/*
================
idParser::Directive_pragma
================
*/
int idParser::Directive_pragma( void ) {
	idToken token;

	idParser::Warning( "#pragma directive not supported" );
	while( idParser::ReadLine( &token ) ) {
	}
	return true;
}

/*
================
idParser::UnreadSignToken
================
*/
void idParser::UnreadSignToken( void ) {
	idToken token;

	token.line = idParser::scriptstack->GetLineNum();
	token.whiteSpaceStart_p = NULL;
	token.whiteSpaceEnd_p = NULL;
	token.linesCrossed = 0;
	token.flags = 0;
	token = "-";
	token.type = TT_PUNCTUATION;
	token.subtype = P_SUB;
	idParser::UnreadSourceToken( &token );
}

/*
================
idParser::Directive_eval
================
*/
int idParser::Directive_eval( void ) {
	int value;
	idToken token;
	char buf[128];

	if ( !idParser::Evaluate( &value, NULL, true ) ) {
		return false;
	}

	token.line = idParser::scriptstack->GetLineNum();
	token.whiteSpaceStart_p = NULL;
	token.whiteSpaceEnd_p = NULL;
	token.linesCrossed = 0;
	token.flags = 0;
	sprintf(buf, "%d", abs(value));
	token = buf;
	token.type = TT_NUMBER;
	token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;
	idParser::UnreadSourceToken( &token );
	if ( value < 0 ) {
		idParser::UnreadSignToken();
	}
	return true;
}

/*
================
idParser::Directive_evalfloat
================
*/
int idParser::Directive_evalfloat( void ) {
	double value;
	idToken token;
	char buf[128];

	if ( !idParser::Evaluate( NULL, &value, false ) ) {
		return false;
	}

	token.line = idParser::scriptstack->GetLineNum();
	token.whiteSpaceStart_p = NULL;
	token.whiteSpaceEnd_p = NULL;
	token.linesCrossed = 0;
	token.flags = 0;
	sprintf(buf, "%1.2f", idMath::Fabs(value));
	token = buf;
	token.type = TT_NUMBER;
	token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;
	idParser::UnreadSourceToken( &token );
	if (value < 0) {
		idParser::UnreadSignToken();
	}
	return true;
}

/*
================
idParser::ReadDirective
================
*/
int idParser::ReadDirective( void ) {
	idToken token;

	//read the directive name
	if ( !idParser::ReadSourceToken( &token ) ) {
		idParser::Error( "found '#' without name" );
		return false;
	}
	//directive name must be on the same line
	if (token.linesCrossed > 0) {
		idParser::UnreadSourceToken( &token );
		idParser::Error( "found '#' at end of line" );
		return false;
	}
	//if if is a name
	if (token.type == TT_NAME) {
		if ( token == "if" ) {
			return idParser::Directive_if();
		}
		else if ( token == "ifdef" ) {
			return idParser::Directive_ifdef();
		}
		else if ( token == "ifndef" ) {
			return idParser::Directive_ifndef();
		}
		else if ( token == "elif" ) {
			return idParser::Directive_elif();
		}
		else if ( token == "else" ) {
			return idParser::Directive_else();
		}
		else if ( token == "endif" ) {
			return idParser::Directive_endif();
		}
		else if (idParser::skip > 0) {
			// skip the rest of the line
			while( idParser::ReadLine( &token ) ) {
			}
			return true;
		}
		else {
			if ( token == "include" ) {
				return idParser::Directive_include();
			}
			else if ( token == "define" ) {
				return idParser::Directive_define();
			}
			else if ( token == "undef" ) {
				return idParser::Directive_undef();
			}
			else if ( token == "line" ) {
				return idParser::Directive_line();
			}
			else if ( token == "error" ) {
				return idParser::Directive_error();
			}
			else if ( token == "warning" ) {
				return idParser::Directive_warning();
			}
			else if ( token == "pragma" ) {
				return idParser::Directive_pragma();
			}
			else if ( token == "eval" ) {
				return idParser::Directive_eval();
			}
			else if ( token == "evalfloat" ) {
				return idParser::Directive_evalfloat();
			}
		}
	}
	idParser::Error( "unknown precompiler directive '%s'", token.c_str() );
	return false;
}

/*
================
idParser::DollarDirective_evalint
================
*/
int idParser::DollarDirective_evalint( void ) {
	int value;
	idToken token;
	char buf[128];

	if ( !idParser::DollarEvaluate( &value, NULL, true ) ) {
		return false;
	}

	token.line = idParser::scriptstack->GetLineNum();
	token.whiteSpaceStart_p = NULL;
	token.whiteSpaceEnd_p = NULL;
	token.linesCrossed = 0;
	token.flags = 0;
	sprintf( buf, "%d", abs( value ) );
	token = buf;
	token.type = TT_NUMBER;
	token.subtype = TT_INTEGER | TT_LONG | TT_DECIMAL | TT_VALUESVALID;
	token.intvalue = abs( value );
	token.floatvalue = abs( value );
	idParser::UnreadSourceToken( &token );
	if ( value < 0 ) {
		idParser::UnreadSignToken();
	}
	return true;
}

/*
================
idParser::DollarDirective_evalfloat
================
*/
int idParser::DollarDirective_evalfloat( void ) {
	double value;
	idToken token;
	char buf[128];

	if ( !idParser::DollarEvaluate( NULL, &value, false ) ) {
		return false;
	}

	token.line = idParser::scriptstack->GetLineNum();
	token.whiteSpaceStart_p = NULL;
	token.whiteSpaceEnd_p = NULL;
	token.linesCrossed = 0;
	token.flags = 0;
	sprintf( buf, "%1.2f", fabs( value ) );
	token = buf;
	token.type = TT_NUMBER;
	token.subtype = TT_FLOAT | TT_LONG | TT_DECIMAL | TT_VALUESVALID;
	token.intvalue = (unsigned int) fabs( value );
	token.floatvalue = fabs( value );
	idParser::UnreadSourceToken( &token );
	if ( value < 0 ) {
		idParser::UnreadSignToken();
	}
	return true;
}

/*
================
idParser::ReadDollarDirective
================
*/
int idParser::ReadDollarDirective( void ) {
	idToken token;

	// read the directive name
	if ( !idParser::ReadSourceToken( &token ) ) {
		idParser::Error( "found '$' without name" );
		return false;
	}
	// directive name must be on the same line
	if ( token.linesCrossed > 0 ) {
		idParser::UnreadSourceToken( &token );
		idParser::Error( "found '$' at end of line" );
		return false;
	}
	// if if is a name
	if (token.type == TT_NAME) {
		if ( token == "evalint" ) {
			return idParser::DollarDirective_evalint();
		}
		else if ( token == "evalfloat" ) {
			return idParser::DollarDirective_evalfloat();
		}
	}
	idParser::UnreadSourceToken( &token );
	return false;
}

/*
================
idParser::ReadToken
================
*/
int idParser::ReadToken( idToken *token ) {
	define_t *define;

	while(1) {
		if ( !idParser::ReadSourceToken( token ) ) {
			return false;
		}
		// check for precompiler directives
		if ( token->type == TT_PUNCTUATION && (*token)[0] == '#' && (*token)[1] == '\0' ) {
			// read the precompiler directive
			if ( !idParser::ReadDirective() ) {
				return false;
			}
			continue;
		}
		// if skipping source because of conditional compilation
		if ( idParser::skip ) {
			continue;
		}
		// recursively concatenate strings that are behind each other still resolving defines
		if ( token->type == TT_STRING && !(idParser::scriptstack->GetFlags() & LEXFL_NOSTRINGCONCAT) ) {
			idToken newtoken;
			if ( idParser::ReadToken( &newtoken ) ) {
				if ( newtoken.type == TT_STRING ) {
					token->Append( newtoken.c_str() );
				}
				else {
					idParser::UnreadSourceToken( &newtoken );
				}
			}
		}
		//
		if ( !(idParser::scriptstack->GetFlags() & LEXFL_NODOLLARPRECOMPILE) ) {
			// check for special precompiler directives
			if ( token->type == TT_PUNCTUATION && (*token)[0] == '$' && (*token)[1] == '\0' ) {
				// read the precompiler directive
				if ( idParser::ReadDollarDirective() ) {
					continue;
				}
			}
		}
		// if the token is a name
		if ( token->type == TT_NAME && !( token->flags & TOKEN_FL_RECURSIVE_DEFINE ) ) {
			// check if the name is a define macro
			define = FindHashedDefine( idParser::definehash, token->c_str() );
			// if it is a define macro
			if ( define ) {
				// expand the defined macro
				if ( !idParser::ExpandDefineIntoSource( token, define ) ) {
					return false;
				}
				continue;
			}
		}
		// found a token
		return true;
	}
}

/*
================
idParser::ExpectTokenString
================
*/
int idParser::ExpectTokenString( const char *string ) {
	idToken token;

	if ( !idParser::ReadToken( &token ) ) {
		idParser::Error( "couldn't find expected '%s'", string );
		return false;
	}

	if ( token != string ) {
		idParser::Error( "expected '%s' but found '%s'", string, token.c_str() );
		return false;
	}
	return true;
}

/*
================
idParser::ExpectTokenType
================
*/
int idParser::ExpectTokenType( int type, int subtype, idToken *token ) {
	idStr str;

	if ( !idParser::ReadToken( token ) ) {
		idParser::Error( "couldn't read expected token" );
		return 0;
	}

	if ( token->type != type ) {
		switch( type ) {
			case TT_STRING: str = "string"; break;
			case TT_LITERAL: str = "literal"; break;
			case TT_NUMBER: str = "number"; break;
			case TT_NAME: str = "name"; break;
			case TT_PUNCTUATION: str = "punctuation"; break;
			default: str = "unknown type"; break;
		}
		idParser::Error( "expected a %s but found '%s'", str.c_str(), token->c_str() );
		return 0;
	}
	if ( token->type == TT_NUMBER ) {
		if ( (token->subtype & subtype) != subtype ) {
			str.Clear();
			if ( subtype & TT_DECIMAL ) str = "decimal ";
			if ( subtype & TT_HEX ) str = "hex ";
			if ( subtype & TT_OCTAL ) str = "octal ";
			if ( subtype & TT_BINARY ) str = "binary ";
			if ( subtype & TT_UNSIGNED ) str += "unsigned ";
			if ( subtype & TT_LONG ) str += "long ";
			if ( subtype & TT_FLOAT ) str += "float ";
			if ( subtype & TT_INTEGER ) str += "integer ";
			str.StripTrailing( ' ' );
			idParser::Error( "expected %s but found '%s'", str.c_str(), token->c_str() );
			return 0;
		}
	}
	else if ( token->type == TT_PUNCTUATION ) {
		if ( subtype < 0 ) {
			idParser::Error( "BUG: wrong punctuation subtype" );
			return 0;
		}
		if ( token->subtype != subtype ) {
			idParser::Error( "expected '%s' but found '%s'", scriptstack->GetPunctuationFromId( subtype ), token->c_str() );
			return 0;
		}
	}
	return 1;
}

/*
================
idParser::ExpectAnyToken
================
*/
int idParser::ExpectAnyToken( idToken *token ) {
	if (!idParser::ReadToken( token )) {
		idParser::Error( "couldn't read expected token" );
		return false;
	}
	else {
		return true;
	}
}

/*
================
idParser::CheckTokenString
================
*/
int idParser::CheckTokenString( const char *string ) {
	idToken tok;

	if ( !ReadToken( &tok ) ) {
		return false;
	}
	//if the token is available
	if ( tok == string ) {
		return true;
	}

	UnreadSourceToken( &tok );
	return false;
}

/*
================
idParser::CheckTokenType
================
*/
int idParser::CheckTokenType( int type, int subtype, idToken *token ) {
	idToken tok;

	if ( !ReadToken( &tok ) ) {
		return false;
	}
	//if the type matches
	if (tok.type == type && (tok.subtype & subtype) == subtype) {
		*token = tok;
		return true;
	}

	UnreadSourceToken( &tok );
	return false;
}

/*
================
idParser::PeekTokenString
================
*/
int idParser::PeekTokenString( const char *string ) {
	idToken tok;

	if ( !ReadToken( &tok ) ) {
		return false;
	}

	UnreadSourceToken( &tok );

	// if the token is available
	if ( tok == string ) {
		return true;
	}
	return false;
}

/*
================
idParser::PeekTokenType
================
*/
int idParser::PeekTokenType( int type, int subtype, idToken *token ) {
	idToken tok;

	if ( !ReadToken( &tok ) ) {
		return false;
	}

	UnreadSourceToken( &tok );

	// if the type matches
	if ( tok.type == type && ( tok.subtype & subtype ) == subtype ) {
		*token = tok;
		return true;
	}
	return false;
}

/*
================
idParser::SkipUntilString
================
*/
int idParser::SkipUntilString( const char *string ) {
	idToken token;

	while(idParser::ReadToken( &token )) {
		if ( token == string ) {
			return true;
		}
	}
	return false;
}

/*
================
idParser::SkipRestOfLine
================
*/
int idParser::SkipRestOfLine( void ) {
	idToken token;

	while(idParser::ReadToken( &token )) {
		if ( token.linesCrossed ) {
			idParser::UnreadSourceToken( &token );
			return true;
		}
	}
	return false;
}

/*
=================
idParser::SkipBracedSection

Skips until a matching close brace is found.
Internal brace depths are properly skipped.
=================
*/
int idParser::SkipBracedSection( bool parseFirstBrace ) {
	idToken token;
	int depth;

	depth = parseFirstBrace ? 0 : 1;
	do {
		if ( !ReadToken( &token ) ) {
			return false;
		}
		if( token.type == TT_PUNCTUATION ) {
			if( token == "{" ) {
				depth++;
			} else if ( token == "}" ) {
				depth--;
			}
		}
	} while( depth );
	return true;
}

/*
=================
idParser::ParseBracedSectionExact

The next token should be an open brace.
Parses until a matching close brace is found.
Maintains the exact formating of the braced section

  FIXME: what about precompilation ?
=================
*/
const char *idParser::ParseBracedSectionExact( idStr &out, int tabs ) {
	return scriptstack->ParseBracedSectionExact( out, tabs );
}

/*
=================
idParser::ParseBracedSection

The next token should be an open brace.
Parses until a matching close brace is found.
Internal brace depths are properly skipped.
=================
*/
const char *idParser::ParseBracedSection( idStr &out, int tabs ) {
	idToken token;
	int i, depth;
	bool doTabs = false;
	if (tabs >= 0) {
		doTabs = true;
	}

	out.Empty();
	if ( !idParser::ExpectTokenString( "{" ) ) {
		return out.c_str();
	}
	out = "{";
	depth = 1;
	do {
		if ( !idParser::ReadToken( &token ) ) {
			Error( "missing closing brace" );
			return out.c_str();
		}

		// if the token is on a new line
		for ( i = 0; i < token.linesCrossed; i++ ) {
			out += "\r\n";
		}

		if (doTabs && token.linesCrossed) {
			i = tabs;
			if (token[0] == '}' && i > 0) {
				i--;
			}
			while (i-- > 0) {
				out += "\t";
			}
		}
		if ( token.type == TT_PUNCTUATION ) {
			if ( token[0] == '{' ) {
				depth++;
				if (doTabs) {
					tabs++;
				}
			}
			else if ( token[0] == '}' ) {
				depth--;
				if (doTabs) {
					tabs--;
				}
			}
		}

		if ( token.type == TT_STRING ) {
			out += "\"" + token + "\"";
		}
		else {
			out += token;
		}
		out += " ";
	} while( depth );

	return out.c_str();
}

/*
=================
idParser::ParseRestOfLine

  parse the rest of the line
=================
*/
const char *idParser::ParseRestOfLine( idStr &out ) {
	idToken token;

	out.Empty();
	while(idParser::ReadToken( &token )) {
		if ( token.linesCrossed ) {
			idParser::UnreadSourceToken( &token );
			break;
		}
		if ( out.Length() ) {
			out += " ";
		}
		out += token;
	}
	return out.c_str();
}

/*
================
idParser::UnreadToken
================
*/
void idParser::UnreadToken( idToken *token ) {
	idParser::UnreadSourceToken( token );
}

/*
================
idParser::ReadTokenOnLine
================
*/
int idParser::ReadTokenOnLine( idToken *token ) {
	idToken tok;

	if (!idParser::ReadToken( &tok )) {
		return false;
	}
	// if no lines were crossed before this token
	if ( !tok.linesCrossed ) {
		*token = tok;
		return true;
	}
	//
	idParser::UnreadSourceToken( &tok );
	return false;
}

/*
================
idParser::ParseInt
================
*/
int idParser::ParseInt( void ) {
	idToken token;

	if ( !idParser::ReadToken( &token ) ) {
		idParser::Error( "couldn't read expected integer" );
		return 0;
	}
	if ( token.type == TT_PUNCTUATION && token == "-" ) {
		idParser::ExpectTokenType( TT_NUMBER, TT_INTEGER, &token );
		return -((signed int) token.GetIntValue());
	}
	else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) {
		idParser::Error( "expected integer value, found '%s'", token.c_str() );
	}
	return token.GetIntValue();
}

/*
================
idParser::ParseBool
================
*/
bool idParser::ParseBool( void ) {
	idToken token;

	if ( !idParser::ExpectTokenType( TT_NUMBER, 0, &token ) ) {
		idParser::Error( "couldn't read expected boolean" );
		return false;
	}
	return ( token.GetIntValue() != 0 );
}

/*
================
idParser::ParseFloat
================
*/
float idParser::ParseFloat( void ) {
	idToken token;

	if ( !idParser::ReadToken( &token ) ) {
		idParser::Error( "couldn't read expected floating point number" );
		return 0.0f;
	}
	if ( token.type == TT_PUNCTUATION && token == "-" ) {
		idParser::ExpectTokenType( TT_NUMBER, 0, &token );
		return -token.GetFloatValue();
	}
	else if ( token.type != TT_NUMBER ) {
		idParser::Error( "expected float value, found '%s'", token.c_str() );
	}
	return token.GetFloatValue();
}

/*
================
idParser::Parse1DMatrix
================
*/
int idParser::Parse1DMatrix( int x, float *m ) {
	int i;

	if ( !idParser::ExpectTokenString( "(" ) ) {
		return false;
	}

	for ( i = 0; i < x; i++ ) {
		m[i] = idParser::ParseFloat();
	}

	if ( !idParser::ExpectTokenString( ")" ) ) {
		return false;
	}
	return true;
}

/*
================
idParser::Parse2DMatrix
================
*/
int idParser::Parse2DMatrix( int y, int x, float *m ) {
	int i;

	if ( !idParser::ExpectTokenString( "(" ) ) {
		return false;
	}

	for ( i = 0; i < y; i++ ) {
		if ( !idParser::Parse1DMatrix( x, m + i * x ) ) {
			return false;
		}
	}

	if ( !idParser::ExpectTokenString( ")" ) ) {
		return false;
	}
	return true;
}

/*
================
idParser::Parse3DMatrix
================
*/
int idParser::Parse3DMatrix( int z, int y, int x, float *m ) {
	int i;

	if ( !idParser::ExpectTokenString( "(" ) ) {
		return false;
	}

	for ( i = 0 ; i < z; i++ ) {
		if ( !idParser::Parse2DMatrix( y, x, m + i * x*y ) ) {
			return false;
		}
	}

	if ( !idParser::ExpectTokenString( ")" ) ) {
		return false;
	}
	return true;
}

/*
================
idParser::GetLastWhiteSpace
================
*/
int idParser::GetLastWhiteSpace( idStr &whiteSpace ) const {
	if ( scriptstack ) {
		scriptstack->GetLastWhiteSpace( whiteSpace );
	} else {
		whiteSpace.Clear();
	}
	return whiteSpace.Length();
}

/*
================
idParser::SetMarker
================
*/
void idParser::SetMarker( void ) {
	marker_p = NULL;
}

/*
================
idParser::GetStringFromMarker

  FIXME: this is very bad code, the script isn't even garrenteed to still be around
================
*/
void idParser::GetStringFromMarker( idStr& out, bool clean ) {
	char*	p;
	char	save;

	if ( marker_p == NULL ) {
		marker_p = scriptstack->buffer;
	}

	if ( tokens ) {
		p = (char*)tokens->whiteSpaceStart_p;
	} else {
		p = (char*)scriptstack->script_p;
	}

	// Set the end character to NULL to give us a complete string
	save = *p;
	*p = 0;

	// If cleaning then reparse
	if ( clean ) {
		idParser temp( marker_p, strlen( marker_p ), "temp", flags );
		idToken token;
		while ( temp.ReadToken ( &token ) ) {
			out += token;
		}
	} else {
		out = marker_p;
	}

	// restore the character we set to NULL
	*p = save;
}

/*
================
idParser::SetIncludePath
================
*/
void idParser::SetIncludePath( const char *path ) {
	idParser::includepath = path;
	// add trailing path seperator
	if (idParser::includepath[idParser::includepath.Length()-1] != '\\' &&
		idParser::includepath[idParser::includepath.Length()-1] != '/') {
		idParser::includepath += PATHSEPERATOR_STR;
	}
}

/*
================
idParser::SetPunctuations
================
*/
void idParser::SetPunctuations( const punctuation_t *p ) {
	idParser::punctuations = p;
}

/*
================
idParser::SetFlags
================
*/
void idParser::SetFlags( int flags ) {
	idLexer *s;

	idParser::flags = flags;
	for ( s = idParser::scriptstack; s; s = s->next ) {
		s->SetFlags( flags );
	}
}

/*
================
idParser::GetFlags
================
*/
int idParser::GetFlags( void ) const {
	return idParser::flags;
}

/*
================
idParser::LoadFile
================
*/
int idParser::LoadFile( const char *filename, bool OSPath ) {
	idLexer *script;

	if ( idParser::loaded ) {
		idLib::common->FatalError("idParser::loadFile: another source already loaded");
		return false;
	}
	script = new idLexer( filename, 0, OSPath );
	if ( !script->IsLoaded() ) {
		delete script;
		return false;
	}
	script->SetFlags( idParser::flags );
	script->SetPunctuations( idParser::punctuations );
	script->next = NULL;
	idParser::OSPath = OSPath;
	idParser::filename = filename;
	idParser::scriptstack = script;
	idParser::tokens = NULL;
	idParser::indentstack = NULL;
	idParser::skip = 0;
	idParser::loaded = true;

	if ( !idParser::definehash ) {
		idParser::defines = NULL;
		idParser::definehash = (define_t **) Mem_ClearedAlloc( DEFINEHASHSIZE * sizeof(define_t *) );
		idParser::AddGlobalDefinesToSource();
	}
	return true;
}

/*
================
idParser::LoadMemory
================
*/
int idParser::LoadMemory(const char *ptr, int length, const char *name ) {
	idLexer *script;

	if ( idParser::loaded ) {
		idLib::common->FatalError("idParser::loadMemory: another source already loaded");
		return false;
	}
	script = new idLexer( ptr, length, name );
	if ( !script->IsLoaded() ) {
		delete script;
		return false;
	}
	script->SetFlags( idParser::flags );
	script->SetPunctuations( idParser::punctuations );
	script->next = NULL;
	idParser::filename = name;
	idParser::scriptstack = script;
	idParser::tokens = NULL;
	idParser::indentstack = NULL;
	idParser::skip = 0;
	idParser::loaded = true;

	if ( !idParser::definehash ) {
		idParser::defines = NULL;
		idParser::definehash = (define_t **) Mem_ClearedAlloc( DEFINEHASHSIZE * sizeof(define_t *) );
		idParser::AddGlobalDefinesToSource();
	}
	return true;
}

/*
================
idParser::FreeSource
================
*/
void idParser::FreeSource( bool keepDefines ) {
	idLexer *script;
	idToken *token;
	define_t *define;
	indent_t *indent;
	int i;

	// free all the scripts
	while( scriptstack ) {
		script = scriptstack;
		scriptstack = scriptstack->next;
		delete script;
	}
	// free all the tokens
	while( tokens ) {
		token = tokens;
		tokens = tokens->next;
		delete token;
	}
	// free all indents
	while( indentstack ) {
		indent = indentstack;
		indentstack = indentstack->next;
		Mem_Free( indent );
	}
	if ( !keepDefines ) {
		// free hash table
		if ( definehash ) {
			// free defines
			for ( i = 0; i < DEFINEHASHSIZE; i++ ) {
				while( definehash[i] ) {
					define = definehash[i];
					definehash[i] = definehash[i]->hashnext;
					FreeDefine(define);
				}
			}
			defines = NULL;
			Mem_Free( idParser::definehash );
			definehash = NULL;
		}
	}
	loaded = false;
}

/*
================
idParser::GetPunctuationFromId
================
*/
const char *idParser::GetPunctuationFromId( int id ) {
	int i;

	if ( !idParser::punctuations ) {
		idLexer lex;
		return lex.GetPunctuationFromId( id );
	}

	for (i = 0; idParser::punctuations[i].p; i++) {
		if ( idParser::punctuations[i].n == id ) {
			return idParser::punctuations[i].p;
		}
	}
	return "unknown punctuation";
}

/*
================
idParser::GetPunctuationId
================
*/
int idParser::GetPunctuationId( const char *p ) {
	int i;

	if ( !idParser::punctuations ) {
		idLexer lex;
		return lex.GetPunctuationId( p );
	}

	for (i = 0; idParser::punctuations[i].p; i++) {
		if ( !strcmp(idParser::punctuations[i].p, p) ) {
			return idParser::punctuations[i].n;
		}
	}
	return 0;
}

/*
================
idParser::idParser
================
*/
idParser::idParser() {
	this->loaded = false;
	this->OSPath = false;
	this->punctuations = 0;
	this->flags = 0;
	this->scriptstack = NULL;
	this->indentstack = NULL;
	this->definehash = NULL;
	this->defines = NULL;
	this->tokens = NULL;
	this->marker_p = NULL;
}

/*
================
idParser::idParser
================
*/
idParser::idParser( int flags ) {
	this->loaded = false;
	this->OSPath = false;
	this->punctuations = 0;
	this->flags = flags;
	this->scriptstack = NULL;
	this->indentstack = NULL;
	this->definehash = NULL;
	this->defines = NULL;
	this->tokens = NULL;
	this->marker_p = NULL;
}

/*
================
idParser::idParser
================
*/
idParser::idParser( const char *filename, int flags, bool OSPath ) {
	this->loaded = false;
	this->OSPath = true;
	this->punctuations = 0;
	this->flags = flags;
	this->scriptstack = NULL;
	this->indentstack = NULL;
	this->definehash = NULL;
	this->defines = NULL;
	this->tokens = NULL;
	this->marker_p = NULL;
	LoadFile( filename, OSPath );
}

/*
================
idParser::idParser
================
*/
idParser::idParser( const char *ptr, int length, const char *name, int flags ) {
	this->loaded = false;
	this->OSPath = false;
	this->punctuations = 0;
	this->flags = flags;
	this->scriptstack = NULL;
	this->indentstack = NULL;
	this->definehash = NULL;
	this->defines = NULL;
	this->tokens = NULL;
	this->marker_p = NULL;
	LoadMemory( ptr, length, name );
}

/*
================
idParser::~idParser
================
*/
idParser::~idParser( void ) {
	idParser::FreeSource( false );
}
