mirror of
https://github.com/blendogames/thirtyflightsofloving.git
synced 2024-11-15 00:41:21 +00:00
3410 lines
92 KiB
C
3410 lines
92 KiB
C
/*
|
||
===========================================================================
|
||
|
||
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_precomp.c
|
||
*
|
||
* desc: pre compiler
|
||
*
|
||
*
|
||
*****************************************************************************/
|
||
|
||
//Notes: fix: PC_StringizeTokens
|
||
|
||
#ifdef SCREWUP
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <limits.h>
|
||
#include <string.h>
|
||
#include <stdarg.h>
|
||
#include <time.h>
|
||
#include <math.h>
|
||
#include "l_memory.h"
|
||
#include "l_script.h"
|
||
#include "l_precomp.h"
|
||
#include "l_log.h"
|
||
|
||
#ifdef _WIN32
|
||
#include <windows.h>
|
||
#endif
|
||
|
||
typedef enum {qfalse, qtrue} qboolean;
|
||
|
||
extern void Error( char *error, ... );
|
||
|
||
// Ridah, ripped from q_shared.c
|
||
/*
|
||
=============
|
||
Q_strncpyz
|
||
|
||
Safe strncpy that ensures a trailing zero
|
||
=============
|
||
*/
|
||
void Q_strncpyz (char *dest, size_t destSize, const char *src)
|
||
{
|
||
if (!dest) {
|
||
Error ("Q_strncatz: NULL dest");
|
||
}
|
||
if ( !src ) {
|
||
Error( "Q_strncpyz: NULL src" );
|
||
}
|
||
if ( destSize < 1 ) {
|
||
Error( "Q_strncpyz: destsize < 1" );
|
||
}
|
||
|
||
strncpy (dest, src, destSize - 1);
|
||
dest[destSize - 1] = 0;
|
||
}
|
||
|
||
/*
|
||
=================
|
||
Q_strncatz
|
||
|
||
Safe strncat that ensures a trailing zero
|
||
=================
|
||
*/
|
||
void Q_strncatz (char *dest, size_t destSize, const char *src)
|
||
{
|
||
char *d = dest;
|
||
const char *s = src;
|
||
size_t decSize = destSize;
|
||
|
||
if (!dest) {
|
||
Error ("Q_strncatz: NULL dest");
|
||
}
|
||
if (!src) {
|
||
Error ("Q_strncatz: NULL src");
|
||
}
|
||
if (destSize < 1) {
|
||
Error ("Q_strncatz: dstSize < 1");
|
||
}
|
||
|
||
while (--decSize && *d)
|
||
d++;
|
||
|
||
if (decSize > 0){
|
||
while (--decSize && *s)
|
||
*d++ = *s++;
|
||
|
||
*d = 0;
|
||
}
|
||
dest[destSize - 1] = 0;
|
||
}
|
||
|
||
/*
|
||
=================
|
||
Com_sprintf
|
||
=================
|
||
*/
|
||
void Com_sprintf (char *dest, size_t size, const char *fmt, ...)
|
||
{
|
||
size_t len;
|
||
va_list argptr;
|
||
char bigbuffer[32000]; // big, but small enough to fit in PPC stack
|
||
|
||
va_start( argptr,fmt );
|
||
// len = vsprintf( bigbuffer, fmt, argptr );
|
||
len = Q_vsnprintf (bigbuffer, sizeof(bigbuffer), fmt, argptr);
|
||
va_end( argptr );
|
||
if ( len >= sizeof( bigbuffer ) )
|
||
{
|
||
Error( "Com_sprintf: overflowed bigbuffer" );
|
||
}
|
||
if ( len >= size )
|
||
{
|
||
printf( "Com_sprintf: overflow of %i in %i\n", len, size );
|
||
}
|
||
Q_strncpyz (dest, size, bigbuffer);
|
||
}
|
||
|
||
int Q_stricmpn (const char *s1, const char *s2, int n)
|
||
{
|
||
int c1, c2;
|
||
|
||
do
|
||
{
|
||
c1 = *s1++;
|
||
c2 = *s2++;
|
||
|
||
if ( !n-- ) {
|
||
return 0; // strings are equal until end point
|
||
}
|
||
|
||
if ( c1 != c2 )
|
||
{
|
||
if ( c1 >= 'a' && c1 <= 'z' ) {
|
||
c1 -= ( 'a' - 'A' );
|
||
}
|
||
if ( c2 >= 'a' && c2 <= 'z' ) {
|
||
c2 -= ( 'a' - 'A' );
|
||
}
|
||
if ( c1 != c2 ) {
|
||
return c1 < c2 ? -1 : 1;
|
||
}
|
||
}
|
||
}
|
||
while ( c1 );
|
||
|
||
return 0; // strings are equal
|
||
}
|
||
|
||
int Q_strncmp (const char *s1, const char *s2, int n)
|
||
{
|
||
int c1, c2;
|
||
|
||
do
|
||
{
|
||
c1 = *s1++;
|
||
c2 = *s2++;
|
||
|
||
if ( !n-- ) {
|
||
return 0; // strings are equal until end point
|
||
}
|
||
|
||
if ( c1 != c2 ) {
|
||
return c1 < c2 ? -1 : 1;
|
||
}
|
||
}
|
||
while ( c1 );
|
||
|
||
return 0; // strings are equal
|
||
}
|
||
|
||
int Q_stricmp (const char *s1, const char *s2)
|
||
{
|
||
return Q_stricmpn( s1, s2, 99999 );
|
||
}
|
||
|
||
|
||
char *Q_strlwr (char *s1)
|
||
{
|
||
char *s;
|
||
|
||
s = s1;
|
||
while ( *s ) {
|
||
*s = tolower( *s );
|
||
s++;
|
||
}
|
||
return s1;
|
||
}
|
||
|
||
char *Q_strupr (char *s1)
|
||
{
|
||
char *s;
|
||
|
||
s = s1;
|
||
while ( *s ) {
|
||
*s = toupper( *s );
|
||
s++;
|
||
}
|
||
return s1;
|
||
}
|
||
|
||
#endif //SCREWUP
|
||
|
||
#ifdef BOTLIB
|
||
#include "../game/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
|
||
|
||
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 );
|
||
// 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", 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 );
|
||
// 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", 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
|
||
#ifdef SCREWUP
|
||
Error( "out of token space\n" );
|
||
#else
|
||
Com_Error( ERR_FATAL, "out of token space\n" );
|
||
#endif
|
||
#endif
|
||
return NULL;
|
||
} //end if
|
||
// freetokens = freetokens->next;
|
||
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
|
||
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 ) ) {
|
||
// Knightmare- debug output
|
||
// printf("PC_ReadDefineParms: PC_ReadSourceToken failed, define %s missing parms in file %s\n", define->name, source->filename);
|
||
SourceError( source, "define %s missing parms", define->name );
|
||
return qfalse;
|
||
} //end if
|
||
//
|
||
if ( define->numparms > maxparms ) {
|
||
// Knightmare- debug output
|
||
// printf("PC_ReadDefineParms: define %s with more than %d parameters in file %s\n", define->name, maxparms, source->filename);
|
||
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 );
|
||
// Knightmare- debug output
|
||
// printf("PC_ReadDefineParms: %s instead of leading \"(\" for define %s in file %s\n", token.string, define->name, source->filename);
|
||
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 ) {
|
||
// Knightmare- debug output
|
||
// printf("PC_ReadDefineParms: define %s with too many parms in file %s\n", define->name, source->filename);
|
||
SourceError( source, "define %s with too many parms", define->name );
|
||
return qfalse;
|
||
} //end if
|
||
if ( numparms >= define->numparms ) {
|
||
// Knightmare- debug output
|
||
// printf("PC_ReadDefineParms: define %s has too many parms in file %s\n", define->name, source->filename);
|
||
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 ) ) {
|
||
// Knightmare- debug output
|
||
// printf("PC_ReadDefineParms: define %s incomplete in file %s\n", define->name, source->filename);
|
||
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 )
|
||
{
|
||
token_t *t;
|
||
|
||
token->type = TT_STRING;
|
||
token->whitespace_p = NULL;
|
||
token->endwhitespace_p = NULL;
|
||
token->string[0] = '\0';
|
||
// strncat (token->string, "\"");
|
||
Q_strncatz (token->string, sizeof(token->string), "\"");
|
||
for ( t = tokens; t; t = t->next )
|
||
{
|
||
strncat( token->string, t->string, MAX_TOKEN - strlen( token->string ) );
|
||
} //end for
|
||
strncat( token->string, "\"", MAX_TOKEN - strlen( token->string ) );
|
||
return qtrue;
|
||
} //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 ) )
|
||
{
|
||
// strncat (t1->string, t2->string);
|
||
Q_strncatz (t1->string, sizeof(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
|
||
// strncat (t1->string, &t2->string[1]);
|
||
Q_strncatz (t1->string, sizeof(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 )
|
||
{
|
||
int register hash, i;
|
||
|
||
hash = 0;
|
||
for ( i = 0; name[i] != '\0'; i++ )
|
||
{
|
||
hash += name[i] * ( 119 + i );
|
||
//hash += (name[i] << 7) + i;
|
||
//hash += (name[i] << (i&15));
|
||
} //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 );
|
||
} //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) + (int)strlen(builtin[i].string) + 1);
|
||
memset( define, 0, sizeof(define_t) );
|
||
define->name = (char *)define + sizeof(define_t);
|
||
// strncpy (define->name, builtin[i].string);
|
||
Q_strncpyz (define->name, strlen(builtin[i].string) + 1, 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, define_t *define,
|
||
token_t **firsttoken, token_t **lasttoken )
|
||
{
|
||
token_t token;
|
||
time_t t; // unsigned long prevents LCC warning
|
||
char *curtime;
|
||
|
||
memcpy( &token, &source->token, sizeof( token_t ) );
|
||
switch ( define->builtin )
|
||
{
|
||
case BUILTIN_LINE:
|
||
{
|
||
Com_sprintf (token.string, sizeof(token.string), "%d", source->token.line);
|
||
#ifdef NUMBERVALUE
|
||
token.intvalue = source->token.line;
|
||
token.floatvalue = source->token.line;
|
||
#endif //NUMBERVALUE
|
||
token.type = TT_NUMBER;
|
||
token.subtype = TT_DECIMAL | TT_INTEGER;
|
||
*firsttoken = &token;
|
||
*lasttoken = &token;
|
||
break;
|
||
} //end case
|
||
case BUILTIN_FILE:
|
||
{
|
||
// strncpy (token.string, source->scriptstack->filename);
|
||
Q_strncpyz (token.string, sizeof(token.string), source->scriptstack->filename);
|
||
token.type = TT_NAME;
|
||
token.subtype = (int)strlen( token.string );
|
||
*firsttoken = &token;
|
||
*lasttoken = &token;
|
||
break;
|
||
} //end case
|
||
case BUILTIN_DATE:
|
||
{
|
||
t = time( NULL );
|
||
curtime = ctime( &t );
|
||
// strncpy (token.string, "\"");
|
||
Q_strncpyz (token.string, sizeof(token.string), "\"");
|
||
strncat (token.string, curtime + 4, 7);
|
||
strncat (token.string + 7, curtime + 20, 4);
|
||
// strncat (token.string, sizeof(token.string), "\"");
|
||
Q_strncatz (token.string, sizeof(token.string), "\"");
|
||
free( curtime );
|
||
token.type = TT_NAME;
|
||
token.subtype = (int)strlen( token.string );
|
||
*firsttoken = &token;
|
||
*lasttoken = &token;
|
||
break;
|
||
} //end case
|
||
case BUILTIN_TIME:
|
||
{
|
||
t = time( NULL );
|
||
curtime = ctime( &t );
|
||
// strncpy (token.string, "\"");
|
||
Q_strncpyz (token.string, sizeof(token.string), "\"");
|
||
strncat (token.string, curtime + 11, 8);
|
||
// strncat (token.string, "\"");
|
||
Q_strncatz (token.string, sizeof(token.string), "\"");
|
||
free( curtime );
|
||
token.type = TT_NAME;
|
||
token.subtype = (int)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, 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, 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, define_t *define )
|
||
{
|
||
token_t *firsttoken, *lasttoken;
|
||
|
||
if ( !PC_ExpandDefine( source, 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, size_t pathSize)
|
||
{
|
||
char *ptr;
|
||
|
||
// remove double path seperators
|
||
for ( ptr = path; *ptr; )
|
||
{
|
||
if ( ( *ptr == '\\' || *ptr == '/' ) &&
|
||
( *( ptr + 1 ) == '\\' || *( ptr + 1 ) == '/' ) ) {
|
||
// strncpy (ptr, ptr + 1);
|
||
Q_strncpyz (ptr, pathSize - (ptr - path), ptr + 1);
|
||
} // 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, sizeof(token.string));
|
||
PC_ConvertPath (token.string, sizeof(token.string));
|
||
script = LoadScriptFile( token.string );
|
||
if ( !script ) {
|
||
// strncpy (path, source->includepath);
|
||
// strncat (path, token.string);
|
||
Q_strncpyz (path, sizeof(path), source->includepath);
|
||
Q_strncatz (path, sizeof(path), token.string);
|
||
script = LoadScriptFile( path );
|
||
} //end if
|
||
} //end if
|
||
else if ( token.type == TT_PUNCTUATION && *token.string == '<' )
|
||
{
|
||
// strncpy (path, source->includepath);
|
||
Q_strncpyz (path, sizeof(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;
|
||
}
|
||
strncat( path, token.string, _MAX_PATH );
|
||
} //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, sizeof(path));
|
||
script = LoadScriptFile( path );
|
||
} //end if
|
||
else
|
||
{
|
||
SourceError( source, "#include without file name" );
|
||
return qfalse;
|
||
} //end else
|
||
#ifdef QUAKE
|
||
if ( !script ) {
|
||
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) + (int)strlen(token.string) + 1);
|
||
memset( define, 0, sizeof(define_t) );
|
||
define->name = (char *)define + sizeof(define_t);
|
||
// strncpy (define->name, token.string);
|
||
Q_strncpyz (define->name, strlen(token.string) + 1, 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, (int)strlen( string ), "*extern" );
|
||
//create a new source
|
||
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 succesfully
|
||
if ( res > 0 ) {
|
||
return def;
|
||
}
|
||
//free the define if 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) + (int)strlen(define->name) + 1);
|
||
// copy the define name
|
||
newdefine->name = (char *)newdefine + sizeof(define_t);
|
||
// strncpy (newdefine->name, define->name);
|
||
Q_strncpyz (newdefine->name, strlen(define->name) + 1, 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;
|
||
double floatvalue;
|
||
int parentheses;
|
||
struct value_s *prev, *next;
|
||
} value_t;
|
||
|
||
int PC_OperatorPriority( int op )
|
||
{
|
||
switch ( op )
|
||
{
|
||
case P_MUL: return 15;
|
||
case P_DIV: return 15;
|
||
case P_MOD: return 15;
|
||
case P_ADD: return 14;
|
||
case P_SUB: return 14;
|
||
|
||
case P_LOGIC_AND: return 7;
|
||
case P_LOGIC_OR: return 6;
|
||
case P_LOGIC_GEQ: return 12;
|
||
case P_LOGIC_LEQ: return 12;
|
||
case P_LOGIC_EQ: return 11;
|
||
case P_LOGIC_UNEQ: return 11;
|
||
|
||
case P_LOGIC_NOT: return 16;
|
||
case P_LOGIC_GREATER: return 12;
|
||
case P_LOGIC_LESS: return 12;
|
||
|
||
case P_RSHIFT: return 13;
|
||
case P_LSHIFT: return 13;
|
||
|
||
case P_BIN_AND: return 10;
|
||
case P_BIN_OR: return 8;
|
||
case P_BIN_XOR: return 9;
|
||
case P_BIN_NOT: return 16;
|
||
|
||
case P_COLON: return 5;
|
||
case P_QUESTIONMARK: return 5;
|
||
} //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 value space\n" ); \
|
||
error = 1; \
|
||
break; \
|
||
} \
|
||
else { \
|
||
val = &value_heap[numvalues++];}
|
||
#define FreeValue( val )
|
||
//
|
||
#define AllocOperator( op ) \
|
||
if ( numoperators >= MAX_OPERATORS ) { \
|
||
SourceError( source, "out 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,
|
||
double *floatvalue, int integer )
|
||
{
|
||
operator_t *o, *firstoperator, *lastoperator;
|
||
value_t *v, *firstvalue, *lastvalue, *v1, *v2;
|
||
token_t *t;
|
||
int brace = 0;
|
||
int parentheses = 0;
|
||
int error = 0;
|
||
int lastwasvalue = 0;
|
||
int negativevalue = 0;
|
||
int questmarkintvalue = 0;
|
||
double questmarkfloatvalue = 0;
|
||
int gotquestmarkvalue = qfalse;
|
||
int lastoperatortype = 0;
|
||
//
|
||
operator_t operator_heap[MAX_OPERATORS];
|
||
int numoperators = 0;
|
||
value_t value_heap[MAX_VALUES];
|
||
int numvalues = 0;
|
||
|
||
firstoperator = lastoperator = NULL;
|
||
firstvalue = lastvalue = NULL;
|
||
if ( intvalue ) {
|
||
*intvalue = 0;
|
||
}
|
||
if ( floatvalue ) {
|
||
*floatvalue = 0;
|
||
}
|
||
for ( t = tokens; t; t = t->next )
|
||
{
|
||
switch ( t->type )
|
||
{
|
||
case TT_NAME:
|
||
{
|
||
if ( lastwasvalue || negativevalue ) {
|
||
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_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: v1->intvalue /= v2->intvalue;
|
||
v1->floatvalue /= v2->floatvalue; break;
|
||
case P_MOD: 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,
|
||
double *floatvalue, int integer )
|
||
{
|
||
token_t token, *firsttoken, *lasttoken;
|
||
token_t *t, *nexttoken;
|
||
define_t *define;
|
||
int defined = qfalse;
|
||
|
||
if ( intvalue ) {
|
||
*intvalue = 0;
|
||
}
|
||
if ( floatvalue ) {
|
||
*floatvalue = 0;
|
||
}
|
||
//
|
||
if ( !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, 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,
|
||
double *floatvalue, int integer )
|
||
{
|
||
int indent, defined = qfalse;
|
||
token_t token, *firsttoken, *lasttoken;
|
||
token_t *t, *nexttoken;
|
||
define_t *define;
|
||
|
||
if ( intvalue ) {
|
||
*intvalue = 0;
|
||
}
|
||
if ( floatvalue ) {
|
||
*floatvalue = 0;
|
||
}
|
||
//
|
||
if ( !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, 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;
|
||
|
||
// strncpy (token.string, "");
|
||
Q_strncpyz (token.string, sizeof(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;
|
||
// strncpy (token.string, "-");
|
||
Q_strncpyz (token.string, sizeof(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;
|
||
Com_sprintf (token.string, sizeof(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 )
|
||
{
|
||
double 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;
|
||
Com_sprintf (token.string, sizeof(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;
|
||
Com_sprintf (token.string, sizeof(token.string), "%d", abs(value));
|
||
token.type = TT_NUMBER;
|
||
token.subtype = TT_INTEGER | TT_LONG | TT_DECIMAL;
|
||
#ifdef NUMBERVALUE
|
||
token.intvalue = value;
|
||
token.floatvalue = value;
|
||
#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 )
|
||
{
|
||
double 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;
|
||
Com_sprintf (token.string, sizeof(token.string), "%1.2f", fabs(value));
|
||
token.type = TT_NUMBER;
|
||
token.subtype = TT_FLOAT | TT_LONG | TT_DECIMAL;
|
||
#ifdef NUMBERVALUE
|
||
token.intvalue = (unsigned long) value;
|
||
token.floatvalue = value;
|
||
#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_ReadDollarDirective
|
||
|
||
#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 == '#' ) {
|
||
/* // Knightmare- re-enabled this
|
||
#ifdef SCREWUP // Ridah, skip all # directives
|
||
while ( PC_ReadLine( source, token ) ) ;
|
||
continue;
|
||
#endif // SCREWUP
|
||
*/
|
||
#ifdef QUAKEC
|
||
if ( !BuiltinFunction( source ) )
|
||
#endif //QUAKC
|
||
{
|
||
//read the precompiler directive
|
||
if ( !PC_ReadDirective( source ) ) {
|
||
// Knightmare- debug output
|
||
printf("PC_ReadToken: PC_ReadDirective failed on directive %s on line %i in file %s\n", token->string, token->line, source->filename);
|
||
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 ) ) {
|
||
// Knightmare- debug output
|
||
printf("PC_ReadToken: PC_ReadDollarDirective failed on directive %s on line %i in file %s\n", token->string, token->line, source->filename);
|
||
return qfalse;
|
||
}
|
||
continue;
|
||
} //end if
|
||
} //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 ) {
|
||
#ifdef SCREWUP
|
||
// Knightmare- skip defines w/ parameters
|
||
// They're not relevant to extracting functions & global types
|
||
// And are hard to parse.
|
||
if (!define->builtin && define->numparms) {
|
||
continue;
|
||
}
|
||
#endif
|
||
//expand the defined macro
|
||
if ( !PC_ExpandDefineIntoSource( source, define ) ) {
|
||
// Knightmare- debug output
|
||
printf("PC_ReadToken: PC_ExpandDefineIntoSource failed on define %s on line %i in file %s\n", define->name, token->line, source->filename);
|
||
return qfalse;
|
||
}
|
||
continue;
|
||
} //end if
|
||
} //end if
|
||
//copy token for unreading
|
||
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 )
|
||
{
|
||
// strncpy (str, "");
|
||
Q_strncpyz (str, sizeof(str), "");
|
||
if ( type == TT_STRING ) {
|
||
// strncpy (str, "string");
|
||
Q_strncpyz (str, sizeof(str), "string");
|
||
}
|
||
if ( type == TT_LITERAL ) {
|
||
// strncpy (str, "literal");
|
||
Q_strncpyz (str, sizeof(str), "literal");
|
||
}
|
||
if ( type == TT_NUMBER ) {
|
||
// strncpy (str, "number");
|
||
Q_strncpyz (str, sizeof(str), "number");
|
||
}
|
||
if ( type == TT_NAME ) {
|
||
// strncpy (str, "name");
|
||
Q_strncpyz (str, sizeof(str), "name");
|
||
}
|
||
if ( type == TT_PUNCTUATION ) {
|
||
// strncpy (str, "punctuation");
|
||
Q_strncpyz (str, sizeof(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 ) {
|
||
// strncpy ( str, "decimal");
|
||
Q_strncpyz ( str, sizeof(str), "decimal");
|
||
}
|
||
if ( subtype & TT_HEX ) {
|
||
// strncpy (str, "hex");
|
||
Q_strncpyz (str, sizeof(str), "hex");
|
||
}
|
||
if ( subtype & TT_OCTAL ) {
|
||
// strncpy (str, "octal");
|
||
Q_strncpyz (str, sizeof(str), "octal");
|
||
}
|
||
if ( subtype & TT_BINARY ) {
|
||
// strncpy (str, "binary");
|
||
Q_strncpyz (str, sizeof(str), "binary");
|
||
}
|
||
if ( subtype & TT_LONG ) {
|
||
// strncat (str, " long");
|
||
Q_strncatz (str, sizeof(str), " long");
|
||
}
|
||
if ( subtype & TT_UNSIGNED ) {
|
||
// strncat (str, " unsigned");
|
||
Q_strncatz (str, sizeof(str), " unsigned");
|
||
}
|
||
if ( subtype & TT_FLOAT ) {
|
||
// strncat (str, " float");
|
||
Q_strncatz (str, sizeof(str), " float");
|
||
}
|
||
if ( subtype & TT_INTEGER ) {
|
||
// strncat (str, " integer");
|
||
Q_strncatz (str, sizeof(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 ) {
|
||
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] != '/' ) {
|
||
// strncat (source->includepath, PATHSEPERATOR_STR);
|
||
Q_strncatz (source->includepath, sizeof(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( 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 ) );
|
||
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 ) );
|
||
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
|
||
|