fteqw/engine/botlib/l_precomp.c
Spoike cd97d1fff3 cmake now builds botlib (as a shared object)
fix q3 looping sounds
fix q3sv bug that was kicking clients on map changes
attempt to resize q3ui if the window changes size
added some more disconnect reasons (for menuqc/q3ui to report).
reworked particle count/step arguments for better compat with DP. particles that used count for trails were already broken...
drawtextfield builtin will report line numbers shown, so qc can finally tell how much text there actually was
added some more items to 'fps_preset dp', stuff needed for the 'quake15' mod.
added dpcompat_noretouchground cvar for people wanting to mimic dp's bugs.
added 'r_netgraph 2' setting, which will show packet sizes per svc, in part to highlight wasteful mods.
added cvar to disable the q1mdl floodfill, which caused problems for yet another person.
internal editor now attempts to support debugging asm, if no source is available.
fix 64bit icon bug in x11.
FINALLY fix high's te_teleport effect.
load with no arguments will scan for the most recent a#/s#/quick savedgame instead of using just quick.
load command will chose between fte and vanilla savedgame formats based on modification time, if both exist in the same package/dir.



git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5394 fc73d0e0-1445-4013-8a0c-d673dee63da5
2019-01-29 07:18:07 +00:00

3253 lines
87 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena 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 2 of the License,
or (at your option) any later version.
Quake III Arena 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 Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
/*****************************************************************************
* name: l_precomp.c
*
* desc: pre compiler
*
* $Archive: /MissionPack/code/botlib/l_precomp.c $
*
*****************************************************************************/
//Notes: fix: PC_StringizeTokens
//#define SCREWUP
//#define BOTLIB
//#define QUAKE
//#define QUAKEC
//#define MEQCC
#ifdef SCREWUP
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include <time.h>
#include "l_memory.h"
#include "l_script.h"
#include "l_precomp.h"
typedef enum {qfalse, qtrue} qboolean;
#endif //SCREWUP
#ifdef BOTLIB
#include "q_shared.h"
#include "botlib.h"
#include "be_interface.h"
#include "l_memory.h"
#include "l_script.h"
#include "l_precomp.h"
#include "l_log.h"
#endif //BOTLIB
#ifdef MEQCC
#include "qcc.h"
#include "time.h" //time & ctime
#include "math.h" //fabs
#include "l_memory.h"
#include "l_script.h"
#include "l_precomp.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"
#include "l_precomp.h"
#define qtrue true
#define qfalse false
#define Q_stricmp stricmp
#endif //BSPC
#if defined(QUAKE) && !defined(BSPC)
#include "l_utils.h"
#endif //QUAKE
//#define DEBUG_EVAL
#define MAX_DEFINEPARMS 128
#define DEFINEHASHING 1
//directive name with parse function
typedef struct directive_s
{
char *name;
int (*func)(source_t *source);
} directive_t;
#define DEFINEHASHSIZE 1024
#define TOKEN_HEAP_SIZE 4096
#ifdef _WIN64
#ifdef FTE_SDL
#define vsnprintf linuxlike_vsnprintf
#endif
#endif
int numtokens;
/*
int tokenheapinitialized; //true when the token heap is initialized
token_t token_heap[TOKEN_HEAP_SIZE]; //heap with tokens
token_t *freetokens; //free tokens from the heap
*/
//list with global defines added to every source loaded
define_t *globaldefines;
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void QDECL SourceError(source_t *source, char *str, ...)
{
char text[1024];
va_list ap;
va_start(ap, str);
vsnprintf(text, sizeof(text), str, ap);
va_end(ap);
#ifdef BOTLIB
botimport.Print(PRT_ERROR, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
#endif //BOTLIB
#ifdef MEQCC
printf("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
#endif //MEQCC
#ifdef BSPC
Log_Print("error: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
#endif //BSPC
} //end of the function SourceError
//===========================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//===========================================================================
void QDECL SourceWarning(source_t *source, char *str, ...)
{
char text[1024];
va_list ap;
va_start(ap, str);
vsnprintf(text, sizeof(text), str, ap);
va_end(ap);
#ifdef BOTLIB
botimport.Print(PRT_WARNING, "file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
#endif //BOTLIB
#ifdef MEQCC
printf("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
#endif //MEQCC
#ifdef BSPC
Log_Print("warning: file %s, line %d: %s\n", source->scriptstack->filename, source->scriptstack->line, text);
#endif //BSPC
} //end of the function ScriptWarning
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_PushIndent(source_t *source, int type, int skip)
{
indent_t *indent;
indent = (indent_t *) GetMemory(sizeof(indent_t));
indent->type = type;
indent->script = source->scriptstack;
indent->skip = (skip != 0);
source->skip += indent->skip;
indent->next = source->indentstack;
source->indentstack = indent;
} //end of the function PC_PushIndent
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_PopIndent(source_t *source, int *type, int *skip)
{
indent_t *indent;
*type = 0;
*skip = 0;
indent = source->indentstack;
if (!indent) return;
//must be an indent from the current script
if (source->indentstack->script != source->scriptstack) return;
*type = indent->type;
*skip = indent->skip;
source->indentstack = source->indentstack->next;
source->skip -= indent->skip;
FreeMemory(indent);
} //end of the function PC_PopIndent
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_PushScript(source_t *source, script_t *script)
{
script_t *s;
for (s = source->scriptstack; s; s = s->next)
{
if (!Q_stricmp(s->filename, script->filename))
{
SourceError(source, "%s recursively included", script->filename);
return;
} //end if
} //end for
//push the script on the script stack
script->next = source->scriptstack;
source->scriptstack = script;
} //end of the function PC_PushScript
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_InitTokenHeap(void)
{
/*
int i;
if (tokenheapinitialized) return;
freetokens = NULL;
for (i = 0; i < TOKEN_HEAP_SIZE; i++)
{
token_heap[i].next = freetokens;
freetokens = &token_heap[i];
} //end for
tokenheapinitialized = qtrue;
*/
} //end of the function PC_InitTokenHeap
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
token_t *PC_CopyToken(token_t *token)
{
token_t *t;
// t = (token_t *) malloc(sizeof(token_t));
t = (token_t *) GetMemory(sizeof(token_t));
// t = freetokens;
if (!t)
{
#ifdef BSPC
Error("out of token space\n");
#else
botimport.Error("out of token space");
#endif
return NULL;
} //end if
// freetokens = freetokens->next;
Com_Memcpy(t, token, sizeof(token_t));
t->next = NULL;
numtokens++;
return t;
} //end of the function PC_CopyToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_FreeToken(token_t *token)
{
//free(token);
FreeMemory(token);
// token->next = freetokens;
// freetokens = token;
numtokens--;
} //end of the function PC_FreeToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ReadSourceToken(source_t *source, token_t *token)
{
token_t *t;
script_t *script;
int type, skip;
//if there's no token already available
while(!source->tokens)
{
//if there's a token to read from the script
if (PS_ReadToken(source->scriptstack, token)) return qtrue;
//if at the end of the script
if (EndOfScript(source->scriptstack))
{
//remove all indents of the script
while(source->indentstack &&
source->indentstack->script == source->scriptstack)
{
SourceWarning(source, "missing #endif");
PC_PopIndent(source, &type, &skip);
} //end if
} //end if
//if this was the initial script
if (!source->scriptstack->next) return qfalse;
//remove the script and return to the last one
script = source->scriptstack;
source->scriptstack = source->scriptstack->next;
FreeScript(script);
} //end while
//copy the already available token
Com_Memcpy(token, source->tokens, sizeof(token_t));
//free the read token
t = source->tokens;
source->tokens = source->tokens->next;
PC_FreeToken(t);
return qtrue;
} //end of the function PC_ReadSourceToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_UnreadSourceToken(source_t *source, token_t *token)
{
token_t *t;
t = PC_CopyToken(token);
t->next = source->tokens;
source->tokens = t;
return qtrue;
} //end of the function PC_UnreadSourceToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ReadDefineParms(source_t *source, define_t *define, token_t **parms, int maxparms)
{
token_t token, *t, *last;
int i, done, lastcomma, numparms, indent;
if (!PC_ReadSourceToken(source, &token))
{
SourceError(source, "define %s missing parms", define->name);
return qfalse;
} //end if
//
if (define->numparms > maxparms)
{
SourceError(source, "define with more than %d parameters", maxparms);
return qfalse;
} //end if
//
for (i = 0; i < define->numparms; i++) parms[i] = NULL;
//if no leading "("
if (strcmp(token.string, "("))
{
PC_UnreadSourceToken(source, &token);
SourceError(source, "define %s missing parms", define->name);
return qfalse;
} //end if
//read the define parameters
for (done = 0, numparms = 0, indent = 0; !done;)
{
if (numparms >= maxparms)
{
SourceError(source, "define %s with too many parms", define->name);
return qfalse;
} //end if
if (numparms >= define->numparms)
{
SourceWarning(source, "define %s has too many parms", define->name);
return qfalse;
} //end if
parms[numparms] = NULL;
lastcomma = 1;
last = NULL;
while(!done)
{
//
if (!PC_ReadSourceToken(source, &token))
{
SourceError(source, "define %s incomplete", define->name);
return qfalse;
} //end if
//
if (!strcmp(token.string, ","))
{
if (indent <= 0)
{
if (lastcomma) SourceWarning(source, "too many comma's");
lastcomma = 1;
break;
} //end if
} //end if
lastcomma = 0;
//
if (!strcmp(token.string, "("))
{
indent++;
continue;
} //end if
else if (!strcmp(token.string, ")"))
{
if (--indent <= 0)
{
if (!parms[define->numparms-1])
{
SourceWarning(source, "too few define parms");
} //end if
done = 1;
break;
} //end if
} //end if
//
if (numparms < define->numparms)
{
//
t = PC_CopyToken(&token);
t->next = NULL;
if (last) last->next = t;
else parms[numparms] = t;
last = t;
} //end if
} //end while
numparms++;
} //end for
return qtrue;
} //end of the function PC_ReadDefineParms
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_StringizeTokens(token_t *tokens, token_t *token)
{
int ret = qtrue;
token_t *t;
token->type = TT_STRING;
token->whitespace_p = NULL;
token->endwhitespace_p = NULL;
token->string[0] = '\0';
strcat(token->string, "\"");
for (t = tokens; t; t = t->next)
{
if (strlen(token->string)+strlen(t->string) >= MAX_TOKEN-1)
{
ret = qfalse;
break;
}
strcat(token->string, t->string);
} //end for
strncat(token->string, "\"", MAX_TOKEN - strlen(token->string) - 1);
return ret;
} //end of the function PC_StringizeTokens
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_MergeTokens(token_t *t1, token_t *t2)
{
//merging of a name with a name or number
if (t1->type == TT_NAME && (t2->type == TT_NAME || t2->type == TT_NUMBER))
{
strcat(t1->string, t2->string);
return qtrue;
} //end if
//merging of two strings
if (t1->type == TT_STRING && t2->type == TT_STRING)
{
//remove trailing double quote
t1->string[strlen(t1->string)-1] = '\0';
//concat without leading double quote
strcat(t1->string, &t2->string[1]);
return qtrue;
} //end if
//FIXME: merging of two number of the same sub type
return qfalse;
} //end of the function PC_MergeTokens
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
/*
void PC_PrintDefine(define_t *define)
{
printf("define->name = %s\n", define->name);
printf("define->flags = %d\n", define->flags);
printf("define->builtin = %d\n", define->builtin);
printf("define->numparms = %d\n", define->numparms);
// token_t *parms; //define parameters
// token_t *tokens; //macro tokens (possibly containing parm tokens)
// struct define_s *next; //next defined macro in a list
} //end of the function PC_PrintDefine*/
#if DEFINEHASHING
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
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);
} //end for
Log_Write("\n");
} //end for
} //end of the function PC_PrintDefineHashTable
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
//char primes[16] = {1, 3, 5, 7, 11, 13, 17, 19, 23, 27, 29, 31, 37, 41, 43, 47};
int PC_NameHash(char *name)
{
register int hash, i;
hash = 0;
for (i = 0; name[i] != '\0'; i++)
{
hash += name[i] * (119 + i);
//hash += (name[i] << 7) + i;
//hash += (name[i] << (i&15));
} //end while
hash = (hash ^ (hash >> 10) ^ (hash >> 20)) & (DEFINEHASHSIZE-1);
return hash;
} //end of the function PC_NameHash
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_AddDefineToHash(define_t *define, define_t **definehash)
{
int hash;
hash = PC_NameHash(define->name);
define->hashnext = definehash[hash];
definehash[hash] = define;
} //end of the function PC_AddDefineToHash
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
define_t *PC_FindHashedDefine(define_t **definehash, 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;
} //end for
return NULL;
} //end of the function PC_FindHashedDefine
#endif //DEFINEHASHING
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
define_t *PC_FindDefine(define_t *defines, char *name)
{
define_t *d;
for (d = defines; d; d = d->next)
{
if (!strcmp(d->name, name)) return d;
} //end for
return NULL;
} //end of the function PC_FindDefine
//============================================================================
//
// Parameter: -
// Returns: number of the parm
// if no parm found with the given name -1 is returned
// Changes Globals: -
//============================================================================
int PC_FindDefineParm(define_t *define, char *name)
{
token_t *p;
int i;
i = 0;
for (p = define->parms; p; p = p->next)
{
if (!strcmp(p->string, name)) return i;
i++;
} //end for
return -1;
} //end of the function PC_FindDefineParm
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_FreeDefine(define_t *define)
{
token_t *t, *next;
//free the define parameters
for (t = define->parms; t; t = next)
{
next = t->next;
PC_FreeToken(t);
} //end for
//free the define tokens
for (t = define->tokens; t; t = next)
{
next = t->next;
PC_FreeToken(t);
} //end for
//free the define
FreeMemory(define->name);
FreeMemory(define);
} //end of the function PC_FreeDefine
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_AddBuiltinDefines(source_t *source)
{
int i;
define_t *define;
struct builtin
{
char *string;
int builtin;
} 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 *) GetMemory(sizeof(define_t));
Com_Memset(define, 0, sizeof(define_t));
define->name = (char *) GetMemory(strlen(builtin[i].string) + 1);
strcpy(define->name, builtin[i].string);
define->flags |= DEFINE_FIXED;
define->builtin = builtin[i].builtin;
//add the define to the source
#if DEFINEHASHING
PC_AddDefineToHash(define, source->definehash);
#else
define->next = source->defines;
source->defines = define;
#endif //DEFINEHASHING
} //end for
} //end of the function PC_AddBuiltinDefines
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ExpandBuiltinDefine(source_t *source, token_t *deftoken, define_t *define,
token_t **firsttoken, token_t **lasttoken)
{
token_t *token;
time_t t;
char *curtime;
token = PC_CopyToken(deftoken);
switch(define->builtin)
{
case BUILTIN_LINE:
{
sprintf(token->string, "%d", deftoken->line);
#ifdef NUMBERVALUE
token->intvalue = deftoken->line;
token->floatvalue = deftoken->line;
#endif //NUMBERVALUE
token->type = TT_NUMBER;
token->subtype = TT_DECIMAL | TT_INTEGER;
*firsttoken = token;
*lasttoken = token;
break;
} //end case
case BUILTIN_FILE:
{
strcpy(token->string, source->scriptstack->filename);
token->type = TT_NAME;
token->subtype = strlen(token->string);
*firsttoken = token;
*lasttoken = token;
break;
} //end case
case BUILTIN_DATE:
{
t = time(NULL);
curtime = ctime(&t);
strcpy(token->string, "\"");
strncat(token->string, curtime+4, 7);
strncat(token->string+7, curtime+20, 4);
strcat(token->string, "\"");
free(curtime);
token->type = TT_NAME;
token->subtype = strlen(token->string);
*firsttoken = token;
*lasttoken = token;
break;
} //end case
case BUILTIN_TIME:
{
t = time(NULL);
curtime = ctime(&t);
strcpy(token->string, "\"");
strncat(token->string, curtime+11, 8);
strcat(token->string, "\"");
free(curtime);
token->type = TT_NAME;
token->subtype = strlen(token->string);
*firsttoken = token;
*lasttoken = token;
break;
} //end case
case BUILTIN_STDC:
default:
{
*firsttoken = NULL;
*lasttoken = NULL;
break;
} //end case
} //end switch
return qtrue;
} //end of the function PC_ExpandBuiltinDefine
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ExpandDefine(source_t *source, token_t *deftoken, define_t *define,
token_t **firsttoken, token_t **lasttoken)
{
token_t *parms[MAX_DEFINEPARMS], *dt, *pt, *t;
token_t *t1, *t2, *first, *last, *nextpt, token;
int parmnum, i;
//if it is a builtin define
if (define->builtin)
{
return PC_ExpandBuiltinDefine(source, deftoken, define, firsttoken, lasttoken);
} //end if
//if the define has parameters
if (define->numparms)
{
if (!PC_ReadDefineParms(source, define, parms, MAX_DEFINEPARMS)) return qfalse;
#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->string);
} //end for
} //end for
#endif //DEBUG_EVAL
} //end if
//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 = PC_FindDefineParm(define, dt->string);
} //end if
//if it is a define parameter
if (parmnum >= 0)
{
for (pt = parms[parmnum]; pt; pt = pt->next)
{
t = PC_CopyToken(pt);
//add the token to the list
t->next = NULL;
if (last) last->next = t;
else first = t;
last = t;
} //end for
} //end if
else
{
//if stringizing operator
if (dt->string[0] == '#' && dt->string[1] == '\0')
{
//the stringizing operator must be followed by a define parameter
if (dt->next) parmnum = PC_FindDefineParm(define, dt->next->string);
else parmnum = -1;
//
if (parmnum >= 0)
{
//step over the stringizing operator
dt = dt->next;
//stringize the define parameter tokens
if (!PC_StringizeTokens(parms[parmnum], &token))
{
SourceError(source, "can't stringize tokens");
return qfalse;
} //end if
t = PC_CopyToken(&token);
} //end if
else
{
SourceWarning(source, "stringizing operator without define parameter");
continue;
} //end if
} //end if
else
{
t = PC_CopyToken(dt);
} //end else
//add the token to the list
t->next = NULL;
if (last) last->next = t;
else first = t;
last = t;
} //end else
} //end for
//check for the merging operator
for (t = first; t; )
{
if (t->next)
{
//if the merging operator
if (t->next->string[0] == '#' && t->next->string[1] == '#')
{
t1 = t;
t2 = t->next->next;
if (t2)
{
if (!PC_MergeTokens(t1, t2))
{
SourceError(source, "can't merge %s with %s", t1->string, t2->string);
return qfalse;
} //end if
PC_FreeToken(t1->next);
t1->next = t2->next;
if (t2 == last) last = t1;
PC_FreeToken(t2);
continue;
} //end if
} //end if
} //end if
t = t->next;
} //end for
//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;
PC_FreeToken(pt);
} //end for
} //end for
//
return qtrue;
} //end of the function PC_ExpandDefine
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ExpandDefineIntoSource(source_t *source, token_t *deftoken, define_t *define)
{
token_t *firsttoken, *lasttoken;
if (!PC_ExpandDefine(source, deftoken, define, &firsttoken, &lasttoken)) return qfalse;
if (firsttoken && lasttoken)
{
lasttoken->next = source->tokens;
source->tokens = firsttoken;
return qtrue;
} //end if
return qfalse;
} //end of the function PC_ExpandDefineIntoSource
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_ConvertPath(char *path)
{
char *ptr;
//remove double path seperators
for (ptr = path; *ptr;)
{
if ((*ptr == '\\' || *ptr == '/') &&
(*(ptr+1) == '\\' || *(ptr+1) == '/'))
{
memmove(ptr, ptr+1, strlen(ptr));
} //end if
else
{
ptr++;
} //end else
} //end while
//set OS dependent path seperators
for (ptr = path; *ptr;)
{
if (*ptr == '/' || *ptr == '\\') *ptr = PATHSEPERATOR_CHAR;
ptr++;
} //end while
} //end of the function PC_ConvertPath
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_include(source_t *source)
{
script_t *script;
token_t token;
char path[MAX_PATH];
#ifdef QUAKE
foundfile_t file;
#endif //QUAKE
if (source->skip > 0) return qtrue;
//
if (!PC_ReadSourceToken(source, &token))
{
SourceError(source, "#include without file name");
return qfalse;
} //end if
if (token.linescrossed > 0)
{
SourceError(source, "#include without file name");
return qfalse;
} //end if
if (token.type == TT_STRING)
{
StripDoubleQuotes(token.string);
PC_ConvertPath(token.string);
script = LoadScriptFile(token.string);
if (!script)
{
strcpy(path, source->includepath);
strcat(path, token.string);
script = LoadScriptFile(path);
} //end if
} //end if
else if (token.type == TT_PUNCTUATION && *token.string == '<')
{
strcpy(path, source->includepath);
while(PC_ReadSourceToken(source, &token))
{
if (token.linescrossed > 0)
{
PC_UnreadSourceToken(source, &token);
break;
} //end if
if (token.type == TT_PUNCTUATION && *token.string == '>') break;
if (strlen(path) + strlen(token.string) >= MAX_PATH)
SourceWarning(source, "#include truncation");
else
strcat(path, token.string);
} //end while
if (*token.string != '>')
{
SourceWarning(source, "#include missing trailing >");
} //end if
if (!strlen(path))
{
SourceError(source, "#include without file name between < >");
return qfalse;
} //end if
PC_ConvertPath(path);
script = LoadScriptFile(path);
} //end if
else
{
SourceError(source, "#include without file name");
return qfalse;
} //end else
#ifdef QUAKE
if (!script)
{
Com_Memset(&file, 0, sizeof(foundfile_t));
script = LoadScriptFile(path);
if (script) strncpy(script->filename, path, MAX_PATH);
} //end if
#endif //QUAKE
if (!script)
{
#ifdef SCREWUP
SourceWarning(source, "file %s not found", path);
return qtrue;
#else
SourceError(source, "file %s not found", path);
return qfalse;
#endif //SCREWUP
} //end if
PC_PushScript(source, script);
return qtrue;
} //end of the function PC_Directive_include
//============================================================================
// reads a token from the current line, continues reading on the next
// line only if a backslash '\' is encountered.
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ReadLine(source_t *source, token_t *token)
{
int crossline;
crossline = 0;
do
{
if (!PC_ReadSourceToken(source, token)) return qfalse;
if (token->linescrossed > crossline)
{
PC_UnreadSourceToken(source, token);
return qfalse;
} //end if
crossline = 1;
} while(!strcmp(token->string, "\\"));
return qtrue;
} //end of the function PC_ReadLine
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_WhiteSpaceBeforeToken(token_t *token)
{
return token->endwhitespace_p - token->whitespace_p > 0;
} //end of the function PC_WhiteSpaceBeforeToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_ClearTokenWhiteSpace(token_t *token)
{
token->whitespace_p = NULL;
token->endwhitespace_p = NULL;
token->linescrossed = 0;
} //end of the function PC_ClearTokenWhiteSpace
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_undef(source_t *source)
{
token_t token;
define_t *define, *lastdefine;
int hash;
if (source->skip > 0) return qtrue;
//
if (!PC_ReadLine(source, &token))
{
SourceError(source, "undef without name");
return qfalse;
} //end if
if (token.type != TT_NAME)
{
PC_UnreadSourceToken(source, &token);
SourceError(source, "expected name, found %s", token.string);
return qfalse;
} //end if
#if DEFINEHASHING
hash = PC_NameHash(token.string);
for (lastdefine = NULL, define = source->definehash[hash]; define; define = define->hashnext)
{
if (!strcmp(define->name, token.string))
{
if (define->flags & DEFINE_FIXED)
{
SourceWarning(source, "can't undef %s", token.string);
} //end if
else
{
if (lastdefine) lastdefine->hashnext = define->hashnext;
else source->definehash[hash] = define->hashnext;
PC_FreeDefine(define);
} //end else
break;
} //end if
lastdefine = define;
} //end for
#else //DEFINEHASHING
for (lastdefine = NULL, define = source->defines; define; define = define->next)
{
if (!strcmp(define->name, token.string))
{
if (define->flags & DEFINE_FIXED)
{
SourceWarning(source, "can't undef %s", token.string);
} //end if
else
{
if (lastdefine) lastdefine->next = define->next;
else source->defines = define->next;
PC_FreeDefine(define);
} //end else
break;
} //end if
lastdefine = define;
} //end for
#endif //DEFINEHASHING
return qtrue;
} //end of the function PC_Directive_undef
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_define(source_t *source)
{
token_t token, *t, *last;
define_t *define;
if (source->skip > 0) return qtrue;
//
if (!PC_ReadLine(source, &token))
{
SourceError(source, "#define without name");
return qfalse;
} //end if
if (token.type != TT_NAME)
{
PC_UnreadSourceToken(source, &token);
SourceError(source, "expected name after #define, found %s", token.string);
return qfalse;
} //end if
//check if the define already exists
#if DEFINEHASHING
define = PC_FindHashedDefine(source->definehash, token.string);
#else
define = PC_FindDefine(source->defines, token.string);
#endif //DEFINEHASHING
if (define)
{
if (define->flags & DEFINE_FIXED)
{
SourceError(source, "can't redefine %s", token.string);
return qfalse;
} //end if
SourceWarning(source, "redefinition of %s", token.string);
//unread the define name before executing the #undef directive
PC_UnreadSourceToken(source, &token);
if (!PC_Directive_undef(source)) return qfalse;
//if the define was not removed (define->flags & DEFINE_FIXED)
#if DEFINEHASHING
define = PC_FindHashedDefine(source->definehash, token.string);
#else
define = PC_FindDefine(source->defines, token.string);
#endif //DEFINEHASHING
} //end if
//allocate define
define = (define_t *) GetMemory(sizeof(define_t));
Com_Memset(define, 0, sizeof(define_t));
define->name = (char *) GetMemory(strlen(token.string) + 1);
strcpy(define->name, token.string);
//add the define to the source
#if DEFINEHASHING
PC_AddDefineToHash(define, source->definehash);
#else //DEFINEHASHING
define->next = source->defines;
source->defines = define;
#endif //DEFINEHASHING
//if nothing is defined, just return
if (!PC_ReadLine(source, &token)) return qtrue;
//if it is a define with parameters
if (!PC_WhiteSpaceBeforeToken(&token) && !strcmp(token.string, "("))
{
//read the define parameters
last = NULL;
if (!PC_CheckTokenString(source, ")"))
{
while(1)
{
if (!PC_ReadLine(source, &token))
{
SourceError(source, "expected define parameter");
return qfalse;
} //end if
//if it isn't a name
if (token.type != TT_NAME)
{
SourceError(source, "invalid define parameter");
return qfalse;
} //end if
//
if (PC_FindDefineParm(define, token.string) >= 0)
{
SourceError(source, "two the same define parameters");
return qfalse;
} //end if
//add the define parm
t = PC_CopyToken(&token);
PC_ClearTokenWhiteSpace(t);
t->next = NULL;
if (last) last->next = t;
else define->parms = t;
last = t;
define->numparms++;
//read next token
if (!PC_ReadLine(source, &token))
{
SourceError(source, "define parameters not terminated");
return qfalse;
} //end if
//
if (!strcmp(token.string, ")")) break;
//then it must be a comma
if (strcmp(token.string, ","))
{
SourceError(source, "define not terminated");
return qfalse;
} //end if
} //end while
} //end if
if (!PC_ReadLine(source, &token)) return qtrue;
} //end if
//read the defined stuff
last = NULL;
do
{
t = PC_CopyToken(&token);
if (t->type == TT_NAME && !strcmp(t->string, define->name))
{
SourceError(source, "recursive define (removed recursion)");
continue;
} //end if
PC_ClearTokenWhiteSpace(t);
t->next = NULL;
if (last) last->next = t;
else define->tokens = t;
last = t;
} while(PC_ReadLine(source, &token));
//
if (last)
{
//check for merge operators at the beginning or end
if (!strcmp(define->tokens->string, "##") ||
!strcmp(last->string, "##"))
{
SourceError(source, "define with misplaced ##");
return qfalse;
} //end if
} //end if
return qtrue;
} //end of the function PC_Directive_define
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
define_t *PC_DefineFromString(char *string)
{
script_t *script;
source_t src;
token_t *t;
int res, i;
define_t *def;
PC_InitTokenHeap();
script = LoadScriptMemory(string, strlen(string), "*extern");
//create a new source
Com_Memset(&src, 0, sizeof(source_t));
strncpy(src.filename, "*extern", MAX_PATH);
src.scriptstack = script;
#if DEFINEHASHING
src.definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));
#endif //DEFINEHASHING
//create a define from the source
res = PC_Directive_define(&src);
//free any tokens if left
for (t = src.tokens; t; t = src.tokens)
{
src.tokens = src.tokens->next;
PC_FreeToken(t);
} //end for
#ifdef DEFINEHASHING
def = NULL;
for (i = 0; i < DEFINEHASHSIZE; i++)
{
if (src.definehash[i])
{
def = src.definehash[i];
break;
} //end if
} //end for
#else
def = src.defines;
#endif //DEFINEHASHING
//
#if DEFINEHASHING
FreeMemory(src.definehash);
#endif //DEFINEHASHING
//
FreeScript(script);
//if the define was created successfully
if (res > 0) return def;
//free the define is created
if (src.defines) PC_FreeDefine(def);
//
return NULL;
} //end of the function PC_DefineFromString
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_AddDefine(source_t *source, char *string)
{
define_t *define;
define = PC_DefineFromString(string);
if (!define) return qfalse;
#if DEFINEHASHING
PC_AddDefineToHash(define, source->definehash);
#else //DEFINEHASHING
define->next = source->defines;
source->defines = define;
#endif //DEFINEHASHING
return qtrue;
} //end of the function PC_AddDefine
//============================================================================
// add a globals define that will be added to all opened sources
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_AddGlobalDefine(char *string)
{
define_t *define;
define = PC_DefineFromString(string);
if (!define) return qfalse;
define->next = globaldefines;
globaldefines = define;
return qtrue;
} //end of the function PC_AddGlobalDefine
//============================================================================
// remove the given global define
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_RemoveGlobalDefine(char *name)
{
define_t *define;
define = PC_FindDefine(globaldefines, name);
if (define)
{
PC_FreeDefine(define);
return qtrue;
} //end if
return qfalse;
} //end of the function PC_RemoveGlobalDefine
//============================================================================
// remove all globals defines
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_RemoveAllGlobalDefines(void)
{
define_t *define;
for (define = globaldefines; define; define = globaldefines)
{
globaldefines = globaldefines->next;
PC_FreeDefine(define);
} //end for
} //end of the function PC_RemoveAllGlobalDefines
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
define_t *PC_CopyDefine(source_t *source, define_t *define)
{
define_t *newdefine;
token_t *token, *newtoken, *lasttoken;
newdefine = (define_t *) GetMemory(sizeof(define_t));
//copy the define name
newdefine->name = (char *) GetMemory(strlen(define->name) + 1);
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 = PC_CopyToken(token);
newtoken->next = NULL;
if (lasttoken) lasttoken->next = newtoken;
else newdefine->tokens = newtoken;
lasttoken = newtoken;
} //end for
//copy the define parameters
newdefine->parms = NULL;
for (lasttoken = NULL, token = define->parms; token; token = token->next)
{
newtoken = PC_CopyToken(token);
newtoken->next = NULL;
if (lasttoken) lasttoken->next = newtoken;
else newdefine->parms = newtoken;
lasttoken = newtoken;
} //end for
return newdefine;
} //end of the function PC_CopyDefine
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_AddGlobalDefinesToSource(source_t *source)
{
define_t *define, *newdefine;
for (define = globaldefines; define; define = define->next)
{
newdefine = PC_CopyDefine(source, define);
#if DEFINEHASHING
PC_AddDefineToHash(newdefine, source->definehash);
#else //DEFINEHASHING
newdefine->next = source->defines;
source->defines = newdefine;
#endif //DEFINEHASHING
} //end for
} //end of the function PC_AddGlobalDefinesToSource
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_if_def(source_t *source, int type)
{
token_t token;
define_t *d;
int skip;
if (!PC_ReadLine(source, &token))
{
SourceError(source, "#ifdef without name");
return qfalse;
} //end if
if (token.type != TT_NAME)
{
PC_UnreadSourceToken(source, &token);
SourceError(source, "expected name after #ifdef, found %s", token.string);
return qfalse;
} //end if
#if DEFINEHASHING
d = PC_FindHashedDefine(source->definehash, token.string);
#else
d = PC_FindDefine(source->defines, token.string);
#endif //DEFINEHASHING
skip = (type == INDENT_IFDEF) == (d == NULL);
PC_PushIndent(source, type, skip);
return qtrue;
} //end of the function PC_Directiveif_def
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_ifdef(source_t *source)
{
return PC_Directive_if_def(source, INDENT_IFDEF);
} //end of the function PC_Directive_ifdef
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_ifndef(source_t *source)
{
return PC_Directive_if_def(source, INDENT_IFNDEF);
} //end of the function PC_Directive_ifndef
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_else(source_t *source)
{
int type, skip;
PC_PopIndent(source, &type, &skip);
if (!type)
{
SourceError(source, "misplaced #else");
return qfalse;
} //end if
if (type == INDENT_ELSE)
{
SourceError(source, "#else after #else");
return qfalse;
} //end if
PC_PushIndent(source, INDENT_ELSE, !skip);
return qtrue;
} //end of the function PC_Directive_else
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_endif(source_t *source)
{
int type, skip;
PC_PopIndent(source, &type, &skip);
if (!type)
{
SourceError(source, "misplaced #endif");
return qfalse;
} //end if
return qtrue;
} //end of the function PC_Directive_endif
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
typedef struct operator_s
{
int operator;
int priority;
int parentheses;
struct operator_s *prev, *next;
} operator_t;
typedef struct value_s
{
signed long int intvalue;
float 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;
} //end switch
return qfalse;
} //end of the function PC_OperatorPriority
//#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) { \
SourceError(source, "out of value space\n"); \
error = 1; \
break; \
} \
else \
val = &value_heap[numvalues++];
#define FreeValue(val)
//
#define AllocOperator(op) \
if (numoperators >= MAX_OPERATORS) { \
SourceError(source, "out of operator space\n"); \
error = 1; \
break; \
} \
else \
op = &operator_heap[numoperators++];
#define FreeOperator(op)
int PC_EvaluateTokens(source_t *source, token_t *tokens, signed long int *intvalue,
float *floatvalue, int integer)
{
operator_t *o, *firstoperator, *lastoperator;
value_t *v, *firstvalue, *lastvalue, *v1, *v2;
token_t *t;
int brace = 0;
int parentheses = 0;
int error = 0;
int lastwasvalue = 0;
int negativevalue = 0;
int questmarkintvalue = 0;
float questmarkfloatvalue = 0;
int gotquestmarkvalue = qfalse;
//int lastoperatortype = 0;
//
operator_t operator_heap[MAX_OPERATORS];
int numoperators = 0;
value_t value_heap[MAX_VALUES];
int numvalues = 0;
firstoperator = lastoperator = NULL;
firstvalue = lastvalue = NULL;
if (intvalue) *intvalue = 0;
if (floatvalue) *floatvalue = 0;
for (t = tokens; t; t = t->next)
{
switch(t->type)
{
case TT_NAME:
{
if (lastwasvalue || negativevalue)
{
SourceError(source, "syntax error in #if/#elif");
error = 1;
break;
} //end if
if (strcmp(t->string, "defined"))
{
SourceError(source, "undefined name %s in #if/#elif", t->string);
error = 1;
break;
} //end if
t = t->next;
if (!strcmp(t->string, "("))
{
brace = qtrue;
t = t->next;
} //end if
if (!t || t->type != TT_NAME)
{
SourceError(source, "defined without name in #if/#elif");
error = 1;
break;
} //end if
//v = (value_t *) GetClearedMemory(sizeof(value_t));
AllocValue(v);
#if DEFINEHASHING
if (PC_FindHashedDefine(source->definehash, t->string))
#else
if (PC_FindDefine(source->defines, t->string))
#endif //DEFINEHASHING
{
v->intvalue = 1;
v->floatvalue = 1;
} //end if
else
{
v->intvalue = 0;
v->floatvalue = 0;
} //end else
v->parentheses = parentheses;
v->next = NULL;
v->prev = lastvalue;
if (lastvalue) lastvalue->next = v;
else firstvalue = v;
lastvalue = v;
if (brace)
{
t = t->next;
if (!t || strcmp(t->string, ")"))
{
SourceError(source, "defined without ) in #if/#elif");
error = 1;
break;
} //end if
} //end if
brace = qfalse;
// defined() creates a value
lastwasvalue = 1;
break;
} //end case
case TT_NUMBER:
{
if (lastwasvalue)
{
SourceError(source, "syntax error in #if/#elif");
error = 1;
break;
} //end if
//v = (value_t *) GetClearedMemory(sizeof(value_t));
AllocValue(v);
if (negativevalue)
{
v->intvalue = - (signed int) t->intvalue;
v->floatvalue = - t->floatvalue;
} //end if
else
{
v->intvalue = t->intvalue;
v->floatvalue = t->floatvalue;
} //end else
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;
} //end case
case TT_PUNCTUATION:
{
if (negativevalue)
{
SourceError(source, "misplaced minus sign in #if/#elif");
error = 1;
break;
} //end if
if (t->subtype == P_PARENTHESESOPEN)
{
parentheses++;
break;
} //end if
else if (t->subtype == P_PARENTHESESCLOSE)
{
parentheses--;
if (parentheses < 0)
{
SourceError(source, "too many ) in #if/#elsif");
error = 1;
} //end if
break;
} //end else if
//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)
{
SourceError(source, "illigal operator %s on floating point operands\n", t->string);
error = 1;
break;
} //end if
} //end if
switch(t->subtype)
{
case P_LOGIC_NOT:
case P_BIN_NOT:
{
if (lastwasvalue)
{
SourceError(source, "! or ~ after value in #if/#elif");
error = 1;
break;
} //end if
break;
} //end case
case P_INC:
case P_DEC:
{
SourceError(source, "++ or -- used in #if/#elif");
break;
} //end case
case P_SUB:
{
if (!lastwasvalue)
{
negativevalue = 1;
break;
} //end if
} //end case
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)
{
SourceError(source, "operator %s after operator in #if/#elif", t->string);
error = 1;
break;
} //end if
break;
} //end case
default:
{
SourceError(source, "invalid operator %s in #if/#elif", t->string);
error = 1;
break;
} //end default
} //end switch
if (!error && !negativevalue)
{
//o = (operator_t *) GetClearedMemory(sizeof(operator_t));
AllocOperator(o);
o->operator = 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;
} //end if
break;
} //end case
default:
{
SourceError(source, "unknown %s in #if/#elif", t->string);
error = 1;
break;
} //end default
} //end switch
if (error) break;
} //end for
if (!error)
{
if (!lastwasvalue)
{
SourceError(source, "trailing operator in #if/#elif");
error = 1;
} //end if
else if (parentheses)
{
SourceError(source, "too many ( in #if/#elif");
error = 1;
} //end else if
} //end if
//
gotquestmarkvalue = qfalse;
questmarkintvalue = 0;
questmarkfloatvalue = 0;
//while there are operators
while(!error && firstoperator)
{
v = firstvalue;
for (o = firstoperator; o->next; o = o->next)
{
//if the current operator is nested deeper in parentheses
//than the next operator
if (o->parentheses > o->next->parentheses) break;
//if the current and next operator are nested equally deep in parentheses
if (o->parentheses == o->next->parentheses)
{
//if the priority of the current operator is equal or higher
//than the priority of the next operator
if (o->priority >= o->next->priority) break;
} //end if
//if the arity of the operator isn't equal to 1
if (o->operator != P_LOGIC_NOT
&& o->operator != P_BIN_NOT) v = v->next;
//if there's no value or no next value
if (!v)
{
SourceError(source, "mising values in #if/#elif");
error = 1;
break;
} //end if
} //end for
if (error) break;
v1 = v;
v2 = v->next;
#ifdef DEBUG_EVAL
if (integer)
{
Log_Write("operator %s, value1 = %d", PunctuationFromNum(source->scriptstack, o->operator), v1->intvalue);
if (v2) Log_Write("value2 = %d", v2->intvalue);
} //end if
else
{
Log_Write("operator %s, value1 = %f", PunctuationFromNum(source->scriptstack, o->operator), v1->floatvalue);
if (v2) Log_Write("value2 = %f", v2->floatvalue);
} //end else
#endif //DEBUG_EVAL
switch(o->operator)
{
case P_LOGIC_NOT: v1->intvalue = !v1->intvalue;
v1->floatvalue = !v1->floatvalue; break;
case P_BIN_NOT: v1->intvalue = ~v1->intvalue;
break;
case P_MUL: v1->intvalue *= v2->intvalue;
v1->floatvalue *= v2->floatvalue; break;
case P_DIV: if (!v2->intvalue || !v2->floatvalue)
{
SourceError(source, "divide by zero in #if/#elif\n");
error = 1;
break;
}
v1->intvalue /= v2->intvalue;
v1->floatvalue /= v2->floatvalue; break;
case P_MOD: if (!v2->intvalue)
{
SourceError(source, "divide by zero in #if/#elif\n");
error = 1;
break;
}
v1->intvalue %= v2->intvalue; break;
case P_ADD: v1->intvalue += v2->intvalue;
v1->floatvalue += v2->floatvalue; break;
case P_SUB: v1->intvalue -= v2->intvalue;
v1->floatvalue -= v2->floatvalue; break;
case P_LOGIC_AND: v1->intvalue = v1->intvalue && v2->intvalue;
v1->floatvalue = v1->floatvalue && v2->floatvalue; break;
case P_LOGIC_OR: v1->intvalue = v1->intvalue || v2->intvalue;
v1->floatvalue = v1->floatvalue || v2->floatvalue; break;
case P_LOGIC_GEQ: v1->intvalue = v1->intvalue >= v2->intvalue;
v1->floatvalue = v1->floatvalue >= v2->floatvalue; break;
case P_LOGIC_LEQ: v1->intvalue = v1->intvalue <= v2->intvalue;
v1->floatvalue = v1->floatvalue <= v2->floatvalue; break;
case P_LOGIC_EQ: v1->intvalue = v1->intvalue == v2->intvalue;
v1->floatvalue = v1->floatvalue == v2->floatvalue; break;
case P_LOGIC_UNEQ: v1->intvalue = v1->intvalue != v2->intvalue;
v1->floatvalue = v1->floatvalue != v2->floatvalue; break;
case P_LOGIC_GREATER: v1->intvalue = v1->intvalue > v2->intvalue;
v1->floatvalue = v1->floatvalue > v2->floatvalue; break;
case P_LOGIC_LESS: v1->intvalue = v1->intvalue < v2->intvalue;
v1->floatvalue = v1->floatvalue < v2->floatvalue; break;
case P_RSHIFT: v1->intvalue >>= v2->intvalue;
break;
case P_LSHIFT: v1->intvalue <<= v2->intvalue;
break;
case P_BIN_AND: v1->intvalue &= v2->intvalue;
break;
case P_BIN_OR: v1->intvalue |= v2->intvalue;
break;
case P_BIN_XOR: v1->intvalue ^= v2->intvalue;
break;
case P_COLON:
{
if (!gotquestmarkvalue)
{
SourceError(source, ": without ? in #if/#elif");
error = 1;
break;
} //end if
if (integer)
{
if (!questmarkintvalue) v1->intvalue = v2->intvalue;
} //end if
else
{
if (!questmarkfloatvalue) v1->floatvalue = v2->floatvalue;
} //end else
gotquestmarkvalue = qfalse;
break;
} //end case
case P_QUESTIONMARK:
{
if (gotquestmarkvalue)
{
SourceError(source, "? after ? in #if/#elif");
error = 1;
break;
} //end if
questmarkintvalue = v1->intvalue;
questmarkfloatvalue = v1->floatvalue;
gotquestmarkvalue = qtrue;
break;
} //end if
} //end switch
#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;
//lastoperatortype = o->operator;
//if not an operator with arity 1
if (o->operator != P_LOGIC_NOT
&& o->operator != P_BIN_NOT)
{
//remove the second value if not question mark operator
if (o->operator != P_QUESTIONMARK) v = v->next;
//
if (v->prev) v->prev->next = v->next;
else firstvalue = v->next;
if (v->next) v->next->prev = v->prev;
else lastvalue = v->prev;
//FreeMemory(v);
FreeValue(v);
} //end if
//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);
} //end while
if (firstvalue)
{
if (intvalue) *intvalue = firstvalue->intvalue;
if (floatvalue) *floatvalue = firstvalue->floatvalue;
} //end if
for (o = firstoperator; o; o = lastoperator)
{
lastoperator = o->next;
//FreeMemory(o);
FreeOperator(o);
} //end for
for (v = firstvalue; v; v = lastvalue)
{
lastvalue = v->next;
//FreeMemory(v);
FreeValue(v);
} //end for
if (!error) return qtrue;
if (intvalue) *intvalue = 0;
if (floatvalue) *floatvalue = 0;
return qfalse;
} //end of the function PC_EvaluateTokens
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Evaluate(source_t *source, signed long int *intvalue,
float *floatvalue, int integer)
{
token_t token, *firsttoken, *lasttoken;
token_t *t, *nexttoken;
define_t *define;
int defined = qfalse;
if (intvalue) *intvalue = 0;
if (floatvalue) *floatvalue = 0;
//
if (!PC_ReadLine(source, &token))
{
SourceError(source, "no value after #if/#elif");
return qfalse;
} //end if
firsttoken = NULL;
lasttoken = NULL;
do
{
//if the token is a name
if (token.type == TT_NAME)
{
if (defined)
{
defined = qfalse;
t = PC_CopyToken(&token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
} //end if
else if (!strcmp(token.string, "defined"))
{
defined = qtrue;
t = PC_CopyToken(&token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
} //end if
else
{
//then it must be a define
#if DEFINEHASHING
define = PC_FindHashedDefine(source->definehash, token.string);
#else
define = PC_FindDefine(source->defines, token.string);
#endif //DEFINEHASHING
if (!define)
{
SourceError(source, "can't evaluate %s, not defined", token.string);
return qfalse;
} //end if
if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse;
} //end else
} //end if
//if the token is a number or a punctuation
else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION)
{
t = PC_CopyToken(&token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
} //end else
else //can't evaluate the token
{
SourceError(source, "can't evaluate %s", token.string);
return qfalse;
} //end else
} while(PC_ReadLine(source, &token));
//
if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse;
//
#ifdef DEBUG_EVAL
Log_Write("eval:");
#endif //DEBUG_EVAL
for (t = firsttoken; t; t = nexttoken)
{
#ifdef DEBUG_EVAL
Log_Write(" %s", t->string);
#endif //DEBUG_EVAL
nexttoken = t->next;
PC_FreeToken(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 qtrue;
} //end of the function PC_Evaluate
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_DollarEvaluate(source_t *source, signed long int *intvalue,
float *floatvalue, int integer)
{
int indent, defined = qfalse;
token_t token, *firsttoken, *lasttoken;
token_t *t, *nexttoken;
define_t *define;
if (intvalue) *intvalue = 0;
if (floatvalue) *floatvalue = 0;
//
if (!PC_ReadSourceToken(source, &token))
{
SourceError(source, "no leading ( after $evalint/$evalfloat");
return qfalse;
} //end if
if (!PC_ReadSourceToken(source, &token))
{
SourceError(source, "nothing to evaluate");
return qfalse;
} //end if
indent = 1;
firsttoken = NULL;
lasttoken = NULL;
do
{
//if the token is a name
if (token.type == TT_NAME)
{
if (defined)
{
defined = qfalse;
t = PC_CopyToken(&token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
} //end if
else if (!strcmp(token.string, "defined"))
{
defined = qtrue;
t = PC_CopyToken(&token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
} //end if
else
{
//then it must be a define
#if DEFINEHASHING
define = PC_FindHashedDefine(source->definehash, token.string);
#else
define = PC_FindDefine(source->defines, token.string);
#endif //DEFINEHASHING
if (!define)
{
SourceError(source, "can't evaluate %s, not defined", token.string);
return qfalse;
} //end if
if (!PC_ExpandDefineIntoSource(source, &token, define)) return qfalse;
} //end else
} //end if
//if the token is a number or a punctuation
else if (token.type == TT_NUMBER || token.type == TT_PUNCTUATION)
{
if (*token.string == '(') indent++;
else if (*token.string == ')') indent--;
if (indent <= 0) break;
t = PC_CopyToken(&token);
t->next = NULL;
if (lasttoken) lasttoken->next = t;
else firsttoken = t;
lasttoken = t;
} //end else
else //can't evaluate the token
{
SourceError(source, "can't evaluate %s", token.string);
return qfalse;
} //end else
} while(PC_ReadSourceToken(source, &token));
//
if (!PC_EvaluateTokens(source, firsttoken, intvalue, floatvalue, integer)) return qfalse;
//
#ifdef DEBUG_EVAL
Log_Write("$eval:");
#endif //DEBUG_EVAL
for (t = firsttoken; t; t = nexttoken)
{
#ifdef DEBUG_EVAL
Log_Write(" %s", t->string);
#endif //DEBUG_EVAL
nexttoken = t->next;
PC_FreeToken(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 qtrue;
} //end of the function PC_DollarEvaluate
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_elif(source_t *source)
{
signed long int value;
int type, skip;
PC_PopIndent(source, &type, &skip);
if (!type || type == INDENT_ELSE)
{
SourceError(source, "misplaced #elif");
return qfalse;
} //end if
if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;
skip = (value == 0);
PC_PushIndent(source, INDENT_ELIF, skip);
return qtrue;
} //end of the function PC_Directive_elif
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_if(source_t *source)
{
signed long int value;
int skip;
if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;
skip = (value == 0);
PC_PushIndent(source, INDENT_IF, skip);
return qtrue;
} //end of the function PC_Directive
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_line(source_t *source)
{
SourceError(source, "#line directive not supported");
return qfalse;
} //end of the function PC_Directive_line
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_error(source_t *source)
{
token_t token;
strcpy(token.string, "");
PC_ReadSourceToken(source, &token);
SourceError(source, "#error directive: %s", token.string);
return qfalse;
} //end of the function PC_Directive_error
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_pragma(source_t *source)
{
token_t token;
SourceWarning(source, "#pragma directive not supported");
while(PC_ReadLine(source, &token)) ;
return qtrue;
} //end of the function PC_Directive_pragma
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void UnreadSignToken(source_t *source)
{
token_t token;
token.line = source->scriptstack->line;
token.whitespace_p = source->scriptstack->script_p;
token.endwhitespace_p = source->scriptstack->script_p;
token.linescrossed = 0;
strcpy(token.string, "-");
token.type = TT_PUNCTUATION;
token.subtype = P_SUB;
PC_UnreadSourceToken(source, &token);
} //end of the function UnreadSignToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_eval(source_t *source)
{
signed long int value;
token_t token;
if (!PC_Evaluate(source, &value, NULL, qtrue)) return qfalse;
//
token.line = source->scriptstack->line;
token.whitespace_p = source->scriptstack->script_p;
token.endwhitespace_p = source->scriptstack->script_p;
token.linescrossed = 0;
sprintf(token.string, "%d", abs(value));
token.type = TT_NUMBER;
token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;
PC_UnreadSourceToken(source, &token);
if (value < 0) UnreadSignToken(source);
return qtrue;
} //end of the function PC_Directive_eval
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_Directive_evalfloat(source_t *source)
{
float value;
token_t token;
if (!PC_Evaluate(source, NULL, &value, qfalse)) return qfalse;
token.line = source->scriptstack->line;
token.whitespace_p = source->scriptstack->script_p;
token.endwhitespace_p = source->scriptstack->script_p;
token.linescrossed = 0;
sprintf(token.string, "%1.2f", fabs(value));
token.type = TT_NUMBER;
token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;
PC_UnreadSourceToken(source, &token);
if (value < 0) UnreadSignToken(source);
return qtrue;
} //end of the function PC_Directive_evalfloat
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
directive_t directives[20] =
{
{"if", PC_Directive_if},
{"ifdef", PC_Directive_ifdef},
{"ifndef", PC_Directive_ifndef},
{"elif", PC_Directive_elif},
{"else", PC_Directive_else},
{"endif", PC_Directive_endif},
{"include", PC_Directive_include},
{"define", PC_Directive_define},
{"undef", PC_Directive_undef},
{"line", PC_Directive_line},
{"error", PC_Directive_error},
{"pragma", PC_Directive_pragma},
{"eval", PC_Directive_eval},
{"evalfloat", PC_Directive_evalfloat},
{NULL, NULL}
};
int PC_ReadDirective(source_t *source)
{
token_t token;
int i;
//read the directive name
if (!PC_ReadSourceToken(source, &token))
{
SourceError(source, "found # without name");
return qfalse;
} //end if
//directive name must be on the same line
if (token.linescrossed > 0)
{
PC_UnreadSourceToken(source, &token);
SourceError(source, "found # at end of line");
return qfalse;
} //end if
//if if is a name
if (token.type == TT_NAME)
{
//find the precompiler directive
for (i = 0; directives[i].name; i++)
{
if (!strcmp(directives[i].name, token.string))
{
return directives[i].func(source);
} //end if
} //end for
} //end if
SourceError(source, "unknown precompiler directive %s", token.string);
return qfalse;
} //end of the function PC_ReadDirective
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_DollarDirective_evalint(source_t *source)
{
signed long int value;
token_t token;
if (!PC_DollarEvaluate(source, &value, NULL, qtrue)) return qfalse;
//
token.line = source->scriptstack->line;
token.whitespace_p = source->scriptstack->script_p;
token.endwhitespace_p = source->scriptstack->script_p;
token.linescrossed = 0;
sprintf(token.string, "%d", abs(value));
token.type = TT_NUMBER;
token.subtype = TT_INTEGER|TT_LONG|TT_DECIMAL;
#ifdef NUMBERVALUE
token.intvalue = abs(value);
token.floatvalue = token.intvalue;
#endif //NUMBERVALUE
PC_UnreadSourceToken(source, &token);
if (value < 0)
UnreadSignToken(source);
return qtrue;
} //end of the function PC_DollarDirective_evalint
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_DollarDirective_evalfloat(source_t *source)
{
float value;
token_t token;
if (!PC_DollarEvaluate(source, NULL, &value, qfalse)) return qfalse;
token.line = source->scriptstack->line;
token.whitespace_p = source->scriptstack->script_p;
token.endwhitespace_p = source->scriptstack->script_p;
token.linescrossed = 0;
sprintf(token.string, "%1.2f", fabs(value));
token.type = TT_NUMBER;
token.subtype = TT_FLOAT|TT_LONG|TT_DECIMAL;
#ifdef NUMBERVALUE
token.floatvalue = fabs(value);
token.intvalue = (unsigned long) token.floatvalue;
#endif //NUMBERVALUE
PC_UnreadSourceToken(source, &token);
if (value < 0)
UnreadSignToken(source);
return qtrue;
} //end of the function PC_DollarDirective_evalfloat
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
directive_t dollardirectives[20] =
{
{"evalint", PC_DollarDirective_evalint},
{"evalfloat", PC_DollarDirective_evalfloat},
{NULL, NULL}
};
int PC_ReadDollarDirective(source_t *source)
{
token_t token;
int i;
//read the directive name
if (!PC_ReadSourceToken(source, &token))
{
SourceError(source, "found $ without name");
return qfalse;
} //end if
//directive name must be on the same line
if (token.linescrossed > 0)
{
PC_UnreadSourceToken(source, &token);
SourceError(source, "found $ at end of line");
return qfalse;
} //end if
//if if is a name
if (token.type == TT_NAME)
{
//find the precompiler directive
for (i = 0; dollardirectives[i].name; i++)
{
if (!strcmp(dollardirectives[i].name, token.string))
{
return dollardirectives[i].func(source);
} //end if
} //end for
} //end if
PC_UnreadSourceToken(source, &token);
SourceError(source, "unknown precompiler directive %s", token.string);
return qfalse;
} //end of the function PC_ReadDirective
#ifdef QUAKEC
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int BuiltinFunction(source_t *source)
{
token_t token;
if (!PC_ReadSourceToken(source, &token)) return qfalse;
if (token.type == TT_NUMBER)
{
PC_UnreadSourceToken(source, &token);
return qtrue;
} //end if
else
{
PC_UnreadSourceToken(source, &token);
return qfalse;
} //end else
} //end of the function BuiltinFunction
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int QuakeCMacro(source_t *source)
{
int i;
token_t token;
if (!PC_ReadSourceToken(source, &token)) return qtrue;
if (token.type != TT_NAME)
{
PC_UnreadSourceToken(source, &token);
return qtrue;
} //end if
//find the precompiler directive
for (i = 0; dollardirectives[i].name; i++)
{
if (!strcmp(dollardirectives[i].name, token.string))
{
PC_UnreadSourceToken(source, &token);
return qfalse;
} //end if
} //end for
PC_UnreadSourceToken(source, &token);
return qtrue;
} //end of the function QuakeCMacro
#endif //QUAKEC
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ReadToken(source_t *source, token_t *token)
{
define_t *define;
while(1)
{
if (!PC_ReadSourceToken(source, token)) return qfalse;
//check for precompiler directives
if (token->type == TT_PUNCTUATION && *token->string == '#')
{
#ifdef QUAKEC
if (!BuiltinFunction(source))
#endif //QUAKC
{
//read the precompiler directive
if (!PC_ReadDirective(source)) return qfalse;
continue;
} //end if
} //end if
if (token->type == TT_PUNCTUATION && *token->string == '$')
{
#ifdef QUAKEC
if (!QuakeCMacro(source))
#endif //QUAKEC
{
//read the precompiler directive
if (!PC_ReadDollarDirective(source)) return qfalse;
continue;
} //end if
} //end if
// recursively concatenate strings that are behind each other still resolving defines
if (token->type == TT_STRING)
{
token_t newtoken;
if (PC_ReadToken(source, &newtoken))
{
if (newtoken.type == TT_STRING)
{
token->string[strlen(token->string)-1] = '\0';
if (strlen(token->string) + strlen(newtoken.string+1) + 1 >= MAX_TOKEN)
{
SourceError(source, "string longer than MAX_TOKEN %d\n", MAX_TOKEN);
return qfalse;
}
strcat(token->string, newtoken.string+1);
}
else
{
PC_UnreadToken(source, &newtoken);
}
}
} //end if
//if skipping source because of conditional compilation
if (source->skip) continue;
//if the token is a name
if (token->type == TT_NAME)
{
//check if the name is a define macro
#if DEFINEHASHING
define = PC_FindHashedDefine(source->definehash, token->string);
#else
define = PC_FindDefine(source->defines, token->string);
#endif //DEFINEHASHING
//if it is a define macro
if (define)
{
//expand the defined macro
if (!PC_ExpandDefineIntoSource(source, token, define)) return qfalse;
continue;
} //end if
} //end if
//copy token for unreading
Com_Memcpy(&source->token, token, sizeof(token_t));
//found a token
return qtrue;
} //end while
} //end of the function PC_ReadToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ExpectTokenString(source_t *source, char *string)
{
token_t token;
if (!PC_ReadToken(source, &token))
{
SourceError(source, "couldn't find expected %s", string);
return qfalse;
} //end if
if (strcmp(token.string, string))
{
SourceError(source, "expected %s, found %s", string, token.string);
return qfalse;
} //end if
return qtrue;
} //end of the function PC_ExpectTokenString
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ExpectTokenType(source_t *source, int type, int subtype, token_t *token)
{
char str[MAX_TOKEN];
if (!PC_ReadToken(source, token))
{
SourceError(source, "couldn't read expected token");
return qfalse;
} //end if
if (token->type != type)
{
strcpy(str, "");
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");
SourceError(source, "expected a %s, found %s", str, token->string);
return qfalse;
} //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");
SourceError(source, "expected %s, found %s", str, token->string);
return qfalse;
} //end if
} //end if
else if (token->type == TT_PUNCTUATION)
{
if (token->subtype != subtype)
{
SourceError(source, "found %s", token->string);
return qfalse;
} //end if
} //end else if
return qtrue;
} //end of the function PC_ExpectTokenType
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ExpectAnyToken(source_t *source, token_t *token)
{
if (!PC_ReadToken(source, token))
{
SourceError(source, "couldn't read expected token");
return qfalse;
} //end if
else
{
return qtrue;
} //end else
} //end of the function PC_ExpectAnyToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_CheckTokenString(source_t *source, char *string)
{
token_t tok;
if (!PC_ReadToken(source, &tok)) return qfalse;
//if the token is available
if (!strcmp(tok.string, string)) return qtrue;
//
PC_UnreadSourceToken(source, &tok);
return qfalse;
} //end of the function PC_CheckTokenString
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_CheckTokenType(source_t *source, int type, int subtype, token_t *token)
{
token_t tok;
if (!PC_ReadToken(source, &tok)) return qfalse;
//if the type matches
if (tok.type == type &&
(tok.subtype & subtype) == subtype)
{
Com_Memcpy(token, &tok, sizeof(token_t));
return qtrue;
} //end if
//
PC_UnreadSourceToken(source, &tok);
return qfalse;
} //end of the function PC_CheckTokenType
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_SkipUntilString(source_t *source, char *string)
{
token_t token;
while(PC_ReadToken(source, &token))
{
if (!strcmp(token.string, string)) return qtrue;
} //end while
return qfalse;
} //end of the function PC_SkipUntilString
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_UnreadLastToken(source_t *source)
{
PC_UnreadSourceToken(source, &source->token);
} //end of the function PC_UnreadLastToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_UnreadToken(source_t *source, token_t *token)
{
PC_UnreadSourceToken(source, token);
} //end of the function PC_UnreadToken
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_SetIncludePath(source_t *source, char *path)
{
strncpy(source->includepath, path, MAX_PATH);
//add trailing path seperator
if (source->includepath[strlen(source->includepath)-1] != '\\' &&
source->includepath[strlen(source->includepath)-1] != '/')
{
strcat(source->includepath, PATHSEPERATOR_STR);
} //end if
} //end of the function PC_SetIncludePath
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_SetPunctuations(source_t *source, punctuation_t *p)
{
source->punctuations = p;
} //end of the function PC_SetPunctuations
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
source_t *LoadSourceFile(const char *filename)
{
source_t *source;
script_t *script;
PC_InitTokenHeap();
script = LoadScriptFile(filename);
if (!script) return NULL;
script->next = NULL;
source = (source_t *) GetMemory(sizeof(source_t));
Com_Memset(source, 0, sizeof(source_t));
strncpy(source->filename, filename, MAX_PATH);
source->scriptstack = script;
source->tokens = NULL;
source->defines = NULL;
source->indentstack = NULL;
source->skip = 0;
#if DEFINEHASHING
source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));
#endif //DEFINEHASHING
PC_AddGlobalDefinesToSource(source);
return source;
} //end of the function LoadSourceFile
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
source_t *LoadSourceMemory(char *ptr, int length, char *name)
{
source_t *source;
script_t *script;
PC_InitTokenHeap();
script = LoadScriptMemory(ptr, length, name);
if (!script) return NULL;
script->next = NULL;
source = (source_t *) GetMemory(sizeof(source_t));
Com_Memset(source, 0, sizeof(source_t));
strncpy(source->filename, name, MAX_PATH);
source->scriptstack = script;
source->tokens = NULL;
source->defines = NULL;
source->indentstack = NULL;
source->skip = 0;
#if DEFINEHASHING
source->definehash = GetClearedMemory(DEFINEHASHSIZE * sizeof(define_t *));
#endif //DEFINEHASHING
PC_AddGlobalDefinesToSource(source);
return source;
} //end of the function LoadSourceMemory
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void FreeSource(source_t *source)
{
script_t *script;
token_t *token;
define_t *define;
indent_t *indent;
int i;
//PC_PrintDefineHashTable(source->definehash);
//free all the scripts
while(source->scriptstack)
{
script = source->scriptstack;
source->scriptstack = source->scriptstack->next;
FreeScript(script);
} //end for
//free all the tokens
while(source->tokens)
{
token = source->tokens;
source->tokens = source->tokens->next;
PC_FreeToken(token);
} //end for
#if DEFINEHASHING
for (i = 0; i < DEFINEHASHSIZE; i++)
{
while(source->definehash[i])
{
define = source->definehash[i];
source->definehash[i] = source->definehash[i]->hashnext;
PC_FreeDefine(define);
} //end while
} //end for
#else //DEFINEHASHING
//free all defines
while(source->defines)
{
define = source->defines;
source->defines = source->defines->next;
PC_FreeDefine(define);
} //end for
#endif //DEFINEHASHING
//free all indents
while(source->indentstack)
{
indent = source->indentstack;
source->indentstack = source->indentstack->next;
FreeMemory(indent);
} //end for
#if DEFINEHASHING
//
if (source->definehash) FreeMemory(source->definehash);
#endif //DEFINEHASHING
//free the source itself
FreeMemory(source);
} //end of the function FreeSource
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
#define MAX_SOURCEFILES 64
source_t *sourceFiles[MAX_SOURCEFILES];
int PC_LoadSourceHandle(const char *filename)
{
source_t *source;
int i;
for (i = 1; i < MAX_SOURCEFILES; i++)
{
if (!sourceFiles[i])
break;
} //end for
if (i >= MAX_SOURCEFILES)
return 0;
PS_SetBaseFolder("");
source = LoadSourceFile(filename);
if (!source)
return 0;
sourceFiles[i] = source;
return i;
} //end of the function PC_LoadSourceHandle
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_FreeSourceHandle(int handle)
{
if (handle < 1 || handle >= MAX_SOURCEFILES)
return qfalse;
if (!sourceFiles[handle])
return qfalse;
FreeSource(sourceFiles[handle]);
sourceFiles[handle] = NULL;
return qtrue;
} //end of the function PC_FreeSourceHandle
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_ReadTokenHandle(int handle, pc_token_t *pc_token)
{
token_t token;
int ret;
if (handle < 1 || handle >= MAX_SOURCEFILES)
return 0;
if (!sourceFiles[handle])
return 0;
ret = PC_ReadToken(sourceFiles[handle], &token);
strcpy(pc_token->string, token.string);
pc_token->type = token.type;
pc_token->subtype = token.subtype;
pc_token->intvalue = token.intvalue;
pc_token->floatvalue = token.floatvalue;
if (pc_token->type == TT_STRING)
StripDoubleQuotes(pc_token->string);
return ret;
} //end of the function PC_ReadTokenHandle
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
int PC_SourceFileAndLine(int handle, char *filename, int *line)
{
if (handle < 1 || handle >= MAX_SOURCEFILES)
return qfalse;
if (!sourceFiles[handle])
return qfalse;
strcpy(filename, sourceFiles[handle]->filename);
if (sourceFiles[handle]->scriptstack)
*line = sourceFiles[handle]->scriptstack->line;
else
*line = 0;
return qtrue;
} //end of the function PC_SourceFileAndLine
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_SetBaseFolder(char *path)
{
PS_SetBaseFolder(path);
} //end of the function PC_SetBaseFolder
//============================================================================
//
// Parameter: -
// Returns: -
// Changes Globals: -
//============================================================================
void PC_CheckOpenSourceHandles(void)
{
int i;
for (i = 1; i < MAX_SOURCEFILES; i++)
{
if (sourceFiles[i])
{
#ifdef BOTLIB
botimport.Print(PRT_ERROR, "file %s still open in precompiler\n", sourceFiles[i]->scriptstack->filename);
#endif //BOTLIB
} //end if
} //end for
} //end of the function PC_CheckOpenSourceHandles