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

Return to Castle Wolfenstein single player GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).  

RTCW SP 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.

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

In addition, the RTCW SP 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 RTCW SP
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.

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


/*****************************************************************************
 * name:		l_script.c
 *
 * desc:		lexicographical parser
 *
 *
 *****************************************************************************/

//#define SCREWUP
//#define BOTLIB
//#define MEQCC
//#define BSPC

#ifdef SCREWUP
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include "l_memory.h"
#include "l_script.h"

typedef enum {qfalse, qtrue}    qboolean;

#endif //SCREWUP

#ifdef BOTLIB
//include files for usage in the bot library
#include "../game/q_shared.h"
#include "botlib.h"
#include "be_interface.h"
#include "l_script.h"
#include "l_memory.h"
#include "l_log.h"
#include "l_libvar.h"
#endif //BOTLIB

#ifdef MEQCC
//include files for usage in MrElusive's QuakeC Compiler
#include "qcc.h"
#include "l_script.h"
#include "l_memory.h"
#include "l_log.h"

#define qtrue   true
#define qfalse  false
#endif //MEQCC

#ifdef BSPC
//include files for usage in the BSP Converter
#include "../bspc/qbsp.h"
#include "../bspc/l_log.h"
#include "../bspc/l_mem.h"

#define qtrue   true
#define qfalse  false
#endif //BSPC


#define PUNCTABLE

//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},
#ifdef DOLLAR
	{"$",P_DOLLAR, NULL},
#endif //DOLLAR
	{NULL, 0}
};

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void PS_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 **)
								   GetMemory( 256 * sizeof( punctuation_t * ) );
	}
	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;
			} //end if
			lastp = p;
		} //end for
		if ( !p ) {
			newp->next = NULL;
			if ( lastp ) {
				lastp->next = newp;
			} else { script->punctuationtable[(unsigned int) newp->p[0]] = newp;}
		} //end if
	} //end for
} //end of the function PS_CreatePunctuationTable
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
char *PunctuationFromNum( script_t *script, int num ) {
	int i;

	for ( i = 0; script->punctuations[i].p; i++ )
	{
		if ( script->punctuations[i].n == num ) {
			return script->punctuations[i].p;
		}
	} //end for
	return "unkown punctuation";
} //end of the function PunctuationFromNum
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void QDECL 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 );
	Q_vsnprintf (text, sizeof(text), str, ap);
	va_end( ap );
#ifdef BOTLIB
	botimport.Print( PRT_ERROR, "file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BOTLIB
#ifdef MEQCC
	printf( "error: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //MEQCC
#ifdef BSPC
	Log_Print( "error: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BSPC
} //end of the function ScriptError
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void QDECL 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 );
	Q_vsnprintf (text, sizeof(text), str, ap);
	va_end( ap );
#ifdef BOTLIB
	botimport.Print( PRT_WARNING, "file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BOTLIB
#ifdef MEQCC
	printf( "warning: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //MEQCC
#ifdef BSPC
	Log_Print( "warning: file %s, line %d: %s\n", script->filename, script->line, text );
#endif //BSPC
} //end of the function ScriptWarning
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void SetScriptPunctuations( script_t *script, punctuation_t *p ) {
#ifdef PUNCTABLE
	if ( p ) {
		PS_CreatePunctuationTable( script, p );
	} else { PS_CreatePunctuationTable( script, default_punctuations );}
#endif //PUNCTABLE
	if ( p ) {
		script->punctuations = p;
	} else { script->punctuations = default_punctuations;}
} //end of the function SetScriptPunctuations
//============================================================================
// Reads spaces, tabs, C-like comments etc.
// When a newline character is found the scripts line counter is increased.
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_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++;
		} //end while
		  //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;
					}
				} //end do
				while ( *script->script_p != '\n' );
				script->line++;
				script->script_p++;
				if ( !*script->script_p ) {
					return 0;
				}
				continue;
			} //end if
			  //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++;
					}
				} //end do
				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;
			} //end if
		} //end if
		break;
	} //end while
	return 1;
} //end of the function PS_ReadWhiteSpace
//============================================================================
// Reads an escape character.
//
// Parameter:				script		: script to read from
//								ch				: place to store the read escape character
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_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;
		}     //end for
		script->script_p--;
		if ( val > 0xFF ) {
			ScriptWarning( script, "too large value in escape character" );
			val = 0xFF;
		}     //end if
		c = val;
		break;
	}     //end case
	default:     //NOTE: decimal ASCII code, NOT octal
	{
		if ( *script->script_p < '0' || *script->script_p > '9' ) {
			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;
		}     //end for
		script->script_p--;
		if ( val > 0xFF ) {
			ScriptWarning( script, "too large value in escape character" );
			val = 0xFF;
		}     //end if
		c = val;
		break;
	}     //end default
	} //end switch
	  //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;
} //end of the function PS_ReadEscapeCharacter
//============================================================================
// 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.
//
// Parameter:				script		: script to read from
//								token			: buffer to store the string
// Returns:					qtrue when a string was read succesfully
// Changes Globals:		-
//============================================================================
int PS_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 - 2 ) {
			ScriptError( script, "string longer than MAX_TOKEN = %d", MAX_TOKEN );
			return 0;
		} //end if
		  //if there is an escape character and
		  //if escape characters inside a string are allowed
		if ( *script->script_p == '\\' && !( script->flags & SCFL_NOSTRINGESCAPECHARS ) ) {
			if ( !PS_ReadEscapeCharacter( script, &token->string[len] ) ) {
				token->string[len] = 0;
				return 0;
			} //end if
			len++;
		} //end if
		  //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 ( !PS_ReadWhiteSpace( script ) ) {
				script->script_p = tmpscript_p;
				script->line = tmpline;
				break;
			} //end if
			  //if there's no leading double qoute
			if ( *script->script_p != quote ) {
				script->script_p = tmpscript_p;
				script->line = tmpline;
				break;
			} //end if
			  //step over the new leading double quote
			script->script_p++;
		} //end if
		else
		{
			if ( *script->script_p == '\0' ) {
				token->string[len] = 0;
				ScriptError( script, "missing trailing quote" );
				return 0;
			} //end if
			if ( *script->script_p == '\n' ) {
				token->string[len] = 0;
				ScriptError( script, "newline inside string %s", token->string );
				return 0;
			} //end if
			token->string[len++] = *script->script_p++;
		} //end else
	} //end while
	  //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;
} //end of the function PS_ReadString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_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 ) {
			ScriptError( script, "name longer than MAX_TOKEN = %d", MAX_TOKEN );
			return 0;
		} //end if
		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;
} //end of the function PS_ReadName
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void NumberValue( char *string, int subtype, unsigned long int *intvalue,
				  long 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++;
			} //end if
			if ( dotfound ) {
				*floatvalue = *floatvalue + ( long double )( *string - '0' ) /
							  (long double) dotfound;
				dotfound *= 10;
			} //end if
			else
			{
				*floatvalue = *floatvalue * 10.0 + ( long double )( *string - '0' );
			} //end else
			string++;
		} //end while
		*intvalue = (unsigned long) *floatvalue;
	} //end if
	else if ( subtype & TT_DECIMAL ) {
		while ( *string ) *intvalue = *intvalue * 10 + ( *string++ - '0' );
		*floatvalue = *intvalue;
	} //end else if
	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++;
		} //end while
		*floatvalue = *intvalue;
	} //end else if
	else if ( subtype & TT_OCTAL ) {
		//step over the first zero
		string += 1;
		while ( *string ) *intvalue = ( *intvalue << 3 ) + ( *string++ - '0' );
		*floatvalue = *intvalue;
	} //end else if
	else if ( subtype & TT_BINARY ) {
		//step over the leading 0b or 0B
		string += 2;
		while ( *string ) *intvalue = ( *intvalue << 1 ) + ( *string++ - '0' );
		*floatvalue = *intvalue;
	} //end else if
} //end of the function NumberValue
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadNumber( script_t *script, token_t *token ) {
	int len = 0, i;
	int octal, dot;
	char c;
//	unsigned long int intvalue = 0;
//	long 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 ) {
				ScriptError( script, "hexadecimal number longer than MAX_TOKEN = %d", MAX_TOKEN );
				return 0;
			} //end if
			c = *script->script_p;
		} //end while
		token->subtype |= TT_HEX;
	} //end if
#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;
		//hexadecimal
		while ( c == '0' || c == '1' )
		{
			token->string[len++] = *script->script_p++;
			if ( len >= MAX_TOKEN ) {
				ScriptError( script, "binary number longer than MAX_TOKEN = %d", MAX_TOKEN );
				return 0;
			} //end if
			c = *script->script_p;
		} //end while
		token->subtype |= TT_BINARY;
	} //end if
#endif //BINARYNUMBERS
	else //decimal or octal integer or floating point number
	{
		octal = qfalse;
		dot = qfalse;
		if ( *script->script_p == '0' ) {
			octal = qtrue;
		}
		while ( 1 )
		{
			token->string[len++] = *script->script_p++;
			if ( len >= MAX_TOKEN ) {
				ScriptError( script, "number longer than MAX_TOKEN = %d", MAX_TOKEN );
				return 0;
			} //end if
			c = *script->script_p;
			if ( c == '.' ) {
				dot = qtrue;
			} else if ( c == '8' || c == '9' ) {
				octal = qfalse;
			} else if ( c < '0' || c > '9' )                                              {
				break;
			}
		} //end while
		if ( octal ) {
			token->subtype |= TT_OCTAL;
		} else { token->subtype |= TT_DECIMAL;}
		if ( dot ) {
			token->subtype |= TT_FLOAT;
		}
	} //end else
	for ( i = 0; i < 2; i++ )
	{
		c = *script->script_p;
		//check for a LONG number
		if ( c == 'l' || c == 'L' &&
			 !( token->subtype & TT_LONG ) ) {
			script->script_p++;
			token->subtype |= TT_LONG;
		} //end if
		  //check for an UNSIGNED number
		else if ( c == 'u' || c == 'U' &&
				  !( token->subtype & ( TT_UNSIGNED | TT_FLOAT ) ) ) {
			script->script_p++;
			token->subtype |= TT_UNSIGNED;
		} //end if
	} //end for
	token->string[len] = '\0';
#ifdef NUMBERVALUE
	NumberValue( token->string, token->subtype, &token->intvalue, &token->floatvalue );
#endif //NUMBERVALUE
	if ( !( token->subtype & TT_FLOAT ) ) {
		token->subtype |= TT_INTEGER;
	}
	return 1;
} //end of the function PS_ReadNumber
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadLiteral( script_t *script, token_t *token ) {
	token->type = TT_LITERAL;
	//first quote
	token->string[0] = *script->script_p++;
	//check for end of file
	if ( !*script->script_p ) {
		ScriptError( script, "end of file before trailing \'" );
		return 0;
	} //end if
	  //if it is an escape character
	if ( *script->script_p == '\\' ) {
		if ( !PS_ReadEscapeCharacter( script, &token->string[1] ) ) {
			return 0;
		}
	} //end if
	else
	{
		token->string[1] = *script->script_p++;
	} //end else
	  //check for trailing quote
	if ( *script->script_p != '\'' ) {
		ScriptWarning( script, "too many characters in literal, ignored" );
		while ( *script->script_p &&
				*script->script_p != '\'' &&
				*script->script_p != '\n' )
		{
			script->script_p++;
		} //end while
		if ( *script->script_p == '\'' ) {
			script->script_p++;
		}
	} //end if
	  //store the trailing quote
	token->string[2] = *script->script_p++;
	//store trailing zero to end the string
	token->string[3] = '\0';
	//the sub type is the integer literal value
	token->subtype = token->string[1];
	//
	return 1;
} //end of the function PS_ReadLiteral
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadPunctuation( script_t *script, token_t *token ) {
	int len;
	char *p;
	punctuation_t *punc;

#ifdef PUNCTABLE
	for ( punc = script->punctuationtable[(unsigned int)*script->script_p]; punc; punc = punc->next )
	{
#else
	int i;

	for ( i = 0; script->punctuations[i].p; i++ )
	{
		punc = &script->punctuations[i];
#endif //PUNCTABLE
		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 );
				script->script_p += len;
				token->type = TT_PUNCTUATION;
				//sub type is the number of the punctuation
				token->subtype = punc->n;
				return 1;
			} //end if
		} //end if
	} //end for
	return 0;
} //end of the function PS_ReadPunctuation
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadPrimitive( script_t *script, token_t *token ) {
	int len;

	len = 0;
	while ( *script->script_p > ' ' && *script->script_p != ';' )
	{
		if ( len >= MAX_TOKEN ) {
			ScriptError( script, "primitive token longer than MAX_TOKEN = %d", MAX_TOKEN );
			return 0;
		} //end if
		token->string[len++] = *script->script_p++;
	} //end while
	token->string[len] = 0;
	//copy the token into the script structure
	memcpy( &script->token, token, sizeof( token_t ) );
	//primitive reading successfull
	return 1;
} //end of the function PS_ReadPrimitive
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ReadToken( script_t *script, token_t *token ) {
	//if there is a token available (from UnreadToken)
	if ( script->tokenavailable ) {
		script->tokenavailable = 0;
		memcpy( token, &script->token, sizeof( token_t ) );
		return 1;
	} //end if
	  //save script pointer
	script->lastscript_p = script->script_p;
	//save line counter
	script->lastline = script->line;
	//clear the token stuff
	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 ( !PS_ReadWhiteSpace( script ) ) {
		return 0;
	}
	//end of the white space
	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 ( !PS_ReadString( script, token, '\"' ) ) {
			return 0;
		}
	} //end if
	  //if an literal
	else if ( *script->script_p == '\'' ) {
		//if (!PS_ReadLiteral(script, token)) return 0;
		if ( !PS_ReadString( script, token, '\'' ) ) {
			return 0;
		}
	} //end if
	  //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 ( !PS_ReadNumber( script, token ) ) {
			return 0;
		}
	} //end if
	  //if this is a primitive script
	else if ( script->flags & SCFL_PRIMITIVE ) {
		return PS_ReadPrimitive( script, token );
	} //end else if
	  //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 ( !PS_ReadName( script, token ) ) {
			return 0;
		}
	} //end if
	  //check for punctuations
	else if ( !PS_ReadPunctuation( script, token ) ) {
		ScriptError( script, "can't read token" );
		return 0;
	} //end if
	  //copy the token into the script structure
	memcpy( &script->token, token, sizeof( token_t ) );
	//succesfully read a token
	return 1;
} //end of the function PS_ReadToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectTokenString( script_t *script, char *string ) {
	token_t token;

	if ( !PS_ReadToken( script, &token ) ) {
		ScriptError( script, "couldn't find expected %s", string );
		return 0;
	} //end if

	if ( strcmp( token.string, string ) ) {
		ScriptError( script, "expected %s, found %s", string, token.string );
		return 0;
	} //end if
	return 1;
} //end of the function PS_ExpectToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectTokenType( script_t *script, int type, int subtype, token_t *token ) {
	char str[MAX_TOKEN];

	if ( !PS_ReadToken( script, token ) ) {
		ScriptError( script, "couldn't read expected token" );
		return 0;
	} //end if

	if ( token->type != type ) {
		if ( type == TT_STRING ) {
			strcpy( str, "string" );
		}
		if ( type == TT_LITERAL ) {
			strcpy( str, "literal" );
		}
		if ( type == TT_NUMBER ) {
			strcpy( str, "number" );
		}
		if ( type == TT_NAME ) {
			strcpy( str, "name" );
		}
		if ( type == TT_PUNCTUATION ) {
			strcpy( str, "punctuation" );
		}
		ScriptError( script, "expected a %s, found %s", str, token->string );
		return 0;
	} //end if
	if ( token->type == TT_NUMBER ) {
		if ( ( token->subtype & subtype ) != subtype ) {
			if ( subtype & TT_DECIMAL ) {
				strcpy( str, "decimal" );
			}
			if ( subtype & TT_HEX ) {
				strcpy( str, "hex" );
			}
			if ( subtype & TT_OCTAL ) {
				strcpy( str, "octal" );
			}
			if ( subtype & TT_BINARY ) {
				strcpy( str, "binary" );
			}
			if ( subtype & TT_LONG ) {
				strcat( str, " long" );
			}
			if ( subtype & TT_UNSIGNED ) {
				strcat( str, " unsigned" );
			}
			if ( subtype & TT_FLOAT ) {
				strcat( str, " float" );
			}
			if ( subtype & TT_INTEGER ) {
				strcat( str, " integer" );
			}
			ScriptError( script, "expected %s, found %s", str, token->string );
			return 0;
		} //end if
	} //end if
	else if ( token->type == TT_PUNCTUATION ) {
		if ( subtype < 0 ) {
			ScriptError( script, "BUG: wrong punctuation subtype" );
			return 0;
		} //end if
		if ( token->subtype != subtype ) {
			ScriptError( script, "expected %s, found %s",
						 script->punctuations[subtype], token->string );
			return 0;
		} //end if
	} //end else if
	return 1;
} //end of the function PS_ExpectTokenType
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_ExpectAnyToken( script_t *script, token_t *token ) {
	if ( !PS_ReadToken( script, token ) ) {
		ScriptError( script, "couldn't read expected token" );
		return 0;
	} //end if
	else
	{
		return 1;
	} //end else
} //end of the function PS_ExpectAnyToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_CheckTokenString( script_t *script, char *string ) {
	token_t tok;

	if ( !PS_ReadToken( script, &tok ) ) {
		return 0;
	}
	//if the token is available
	if ( !strcmp( tok.string, string ) ) {
		return 1;
	}
	//token not available
	script->script_p = script->lastscript_p;
	return 0;
} //end of the function PS_CheckTokenString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_CheckTokenType( script_t *script, int type, int subtype, token_t *token ) {
	token_t tok;

	if ( !PS_ReadToken( script, &tok ) ) {
		return 0;
	}
	//if the type matches
	if ( tok.type == type &&
		 ( tok.subtype & subtype ) == subtype ) {
		memcpy( token, &tok, sizeof( token_t ) );
		return 1;
	} //end if
	  //token is not available
	script->script_p = script->lastscript_p;
	return 0;
} //end of the function PS_CheckTokenType
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int PS_SkipUntilString( script_t *script, char *string ) {
	token_t token;

	while ( PS_ReadToken( script, &token ) )
	{
		if ( !strcmp( token.string, string ) ) {
			return 1;
		}
	} //end while
	return 0;
} //end of the function PS_SkipUntilString
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void PS_UnreadLastToken( script_t *script ) {
	script->tokenavailable = 1;
} //end of the function UnreadLastToken
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void PS_UnreadToken( script_t *script, token_t *token ) {
	memcpy( &script->token, token, sizeof( token_t ) );
	script->tokenavailable = 1;
} //end of the function UnreadToken
//============================================================================
// returns the next character of the read white space, returns NULL if none
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
char PS_NextWhiteSpaceChar( script_t *script ) {
	if ( script->whitespace_p != script->endwhitespace_p ) {
		return *script->whitespace_p++;
	} //end if
	else
	{
		return 0;
	} //end else
} //end of the function PS_NextWhiteSpaceChar
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void StripDoubleQuotes( char *string ) {
	if ( *string == '\"' ) {
		strcpy( string, string + 1 );
	} //end if
	if ( string[strlen( string ) - 1] == '\"' ) {
		string[strlen( string ) - 1] = '\0';
	} //end if
} //end of the function StripDoubleQuotes
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void StripSingleQuotes( char *string ) {
	if ( *string == '\'' ) {
		strcpy( string, string + 1 );
	} //end if
	if ( string[strlen( string ) - 1] == '\'' ) {
		string[strlen( string ) - 1] = '\0';
	} //end if
} //end of the function StripSingleQuotes
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
long double ReadSignedFloat( script_t *script ) {
	token_t token;
	long double sign = 1;

	PS_ExpectAnyToken( script, &token );
	if ( !strcmp( token.string, "-" ) ) {
		sign = -1;
		PS_ExpectTokenType( script, TT_NUMBER, 0, &token );
	} //end if
	else if ( token.type != TT_NUMBER ) {
		ScriptError( script, "expected float value, found %s\n", token.string );
	} //end else if
	return sign * token.floatvalue;
} //end of the function ReadSignedFloat
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
signed long int ReadSignedInt( script_t *script ) {
	token_t token;
	signed long int sign = 1;

	PS_ExpectAnyToken( script, &token );
	if ( !strcmp( token.string, "-" ) ) {
		sign = -1;
		PS_ExpectTokenType( script, TT_NUMBER, TT_INTEGER, &token );
	} //end if
	else if ( token.type != TT_NUMBER || token.subtype == TT_FLOAT ) {
		ScriptError( script, "expected integer value, found %s\n", token.string );
	} //end else if
	return sign * token.intvalue;
} //end of the function ReadSignedInt
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void SetScriptFlags( script_t *script, int flags ) {
	script->flags = flags;
} //end of the function SetScriptFlags
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int GetScriptFlags( script_t *script ) {
	return script->flags;
} //end of the function GetScriptFlags
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void ResetScript( script_t *script ) {
	//pointer in script buffer
	script->script_p = script->buffer;
	//pointer in script buffer before reading token
	script->lastscript_p = script->buffer;
	//begin of white space
	script->whitespace_p = NULL;
	//end of white space
	script->endwhitespace_p = NULL;
	//set if there's a token available in script->token
	script->tokenavailable = 0;
	//
	script->line = 1;
	script->lastline = 1;
	//clear the saved token
	memset( &script->token, 0, sizeof( token_t ) );
} //end of the function ResetScript
//============================================================================
// returns true if at the end of the script
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int EndOfScript( script_t *script ) {
	return script->script_p >= script->end_p;
} //end of the function EndOfScript
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int NumLinesCrossed( script_t *script ) {
	return script->line - script->lastline;
} //end of the function NumLinesCrossed
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int ScriptSkipTo( script_t *script, char *value ) {
	int len;
	char firstchar;

	firstchar = *value;
	len = strlen( value );
	do
	{
		if ( !PS_ReadWhiteSpace( script ) ) {
			return 0;
		}
		if ( *script->script_p == firstchar ) {
			if ( !strncmp( script->script_p, value, len ) ) {
				return 1;
			} //end if
		} //end if
		script->script_p++;
	} while ( 1 );
} //end of the function ScriptSkipTo
#ifndef BOTLIB
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
int FileLength( FILE *fp ) {
	int pos;
	int end;

	pos = ftell( fp );
	fseek( fp, 0, SEEK_END );
	end = ftell( fp );
	fseek( fp, pos, SEEK_SET );

	return end;
} //end of the function FileLength
#endif
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
script_t *LoadScriptFile( char *filename ) {
#ifdef BOTLIB
	fileHandle_t fp;
	char pathname[MAX_QPATH];
#else
	FILE *fp;
#endif
	int length;
	void *buffer;
	script_t *script;

#ifdef BOTLIB
	Com_sprintf( pathname, MAX_QPATH, "botfiles/%s", filename );
	length = botimport.FS_FOpenFile( pathname, &fp, FS_READ );
	if ( !fp ) {
		return NULL;
	}
#else
	fp = fopen( filename, "rb" );
	if ( !fp ) {
		return NULL;
	}

	length = FileLength( fp );
#endif

	buffer = GetClearedMemory( sizeof( script_t ) + length + 1 );
	script = (script_t *) buffer;
	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;
	//
	SetScriptPunctuations( script, NULL );
	//
#ifdef BOTLIB
	botimport.FS_Read( script->buffer, length, fp );
	botimport.FS_FCloseFile( fp );
#else
	if ( fread( script->buffer, length, 1, fp ) != 1 ) {
		FreeMemory( buffer );
		script = NULL;
	} //end if
	fclose( fp );
#endif
	//
	return script;
} //end of the function LoadScriptFile
//============================================================================
//load a script from the given memory with the given length
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
script_t *LoadScriptMemory( char *ptr, int length, char *name ) {
	void *buffer;
	script_t *script;

	buffer = GetClearedMemory( sizeof( script_t ) + length + 1 );
	script = (script_t *) buffer;
	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;
	//
	SetScriptPunctuations( script, NULL );
	//
	memcpy( script->buffer, ptr, length );
	//
	return script;
} //end of the function LoadScriptMemory
//============================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//============================================================================
void FreeScript( script_t *script ) {
#ifdef PUNCTABLE
	if ( script->punctuationtable ) {
		FreeMemory( script->punctuationtable );
	}
#endif //PUNCTABLE
	FreeMemory( script );
} //end of the function FreeScript
