1084 lines
20 KiB
C
1084 lines
20 KiB
C
//=======================================================================
|
|
// Copyright XashXT Group 2007 ©
|
|
// parselib.c - script files parser
|
|
//=======================================================================
|
|
|
|
#include "launch.h"
|
|
|
|
typedef struct
|
|
{
|
|
char filename[1024];
|
|
byte *buffer;
|
|
byte *script_p;
|
|
byte *end_p;
|
|
int line;
|
|
} script_t;
|
|
|
|
// max included scripts
|
|
#define MAX_INCLUDES 32
|
|
#define SC_EndOfScript( x ) SC_EndOfScript_( x, __LINE__ )
|
|
script_t scriptstack[ MAX_INCLUDES ];
|
|
script_t *script;
|
|
|
|
char token[MAX_MSGLEN]; // contains token info
|
|
static const char *saved_token;
|
|
static int saved_scriptline;
|
|
|
|
bool endofscript;
|
|
bool tokenready; // only true if UnGetToken was just called
|
|
bool SC_EndOfScript_ (bool newline, const int line );
|
|
|
|
/*
|
|
==============
|
|
SC_AddScriptToStack
|
|
==============
|
|
*/
|
|
bool SC_AddScriptToStack( const char *name, byte *buffer, int size )
|
|
{
|
|
if (script == &scriptstack[MAX_INCLUDES - 1])
|
|
{
|
|
MsgDev( D_ERROR, "AddScriptToStack: script file limit exceeded %d\n", MAX_INCLUDES );
|
|
return false;
|
|
}
|
|
if(!buffer || !size) return false;
|
|
|
|
script++;
|
|
com_strncpy(script->filename, name, sizeof(script->filename));
|
|
script->buffer = buffer;
|
|
script->line = com.com_scriptline = 1;
|
|
script->script_p = script->buffer;
|
|
script->end_p = script->buffer + size;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SC_LoadScript( const char *filename, char *buf, int size )
|
|
{
|
|
int result;
|
|
string scriptname;
|
|
|
|
if(!buf || size <= 0)
|
|
{
|
|
com_strncpy( scriptname, filename, MAX_STRING );
|
|
buf = FS_LoadFile( filename, &size );
|
|
}
|
|
else com_strncpy( scriptname, "*sc_buffer", MAX_STRING );
|
|
|
|
script = scriptstack;
|
|
result = SC_AddScriptToStack( scriptname, buf, size);
|
|
|
|
endofscript = false;
|
|
tokenready = false;
|
|
return result;
|
|
}
|
|
|
|
bool SC_AddScript( const char *filename, char *buf, int size )
|
|
{
|
|
string scriptname;
|
|
|
|
if(!buf || size <= 0)
|
|
{
|
|
com_strncpy( scriptname, filename, MAX_STRING );
|
|
buf = FS_LoadFile( filename, &size );
|
|
}
|
|
else com_strncpy( scriptname, "*sc_buffer", MAX_STRING );
|
|
return SC_AddScriptToStack( scriptname, buf, size );
|
|
}
|
|
|
|
void SC_ResetScript( void )
|
|
{
|
|
// can parsing again
|
|
script->line = com.com_scriptline = saved_scriptline = 1;
|
|
script->script_p = script->buffer;
|
|
saved_token = NULL;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SC_PushScript
|
|
|
|
FIXME: use scriptstack
|
|
=================
|
|
*/
|
|
void SC_PushScript( const char **data_p )
|
|
{
|
|
saved_token = *data_p;
|
|
saved_scriptline = com.com_scriptline;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SC_PopScript
|
|
|
|
FIXME: use scriptstack
|
|
=================
|
|
*/
|
|
void SC_PopScript( const char **data_p )
|
|
{
|
|
*data_p = saved_token;
|
|
com.com_scriptline = saved_scriptline;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_ReadToken
|
|
==============
|
|
*/
|
|
bool SC_ReadToken( bool newline )
|
|
{
|
|
char *token_p;
|
|
int c;
|
|
|
|
if( tokenready )
|
|
{
|
|
// is a token allready waiting?
|
|
tokenready = false;
|
|
return true;
|
|
}
|
|
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
|
|
|
|
skip_whitespace: // skip whitespace
|
|
while (*script->script_p <= 32)
|
|
{
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
|
|
if (*script->script_p++ == '\n')
|
|
{
|
|
if (!newline) goto line_incomplete;
|
|
com.com_scriptline = script->line++;
|
|
}
|
|
}
|
|
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
|
|
// ; // comments
|
|
if(*script->script_p == ';' || ( script->script_p[0] == '/' && script->script_p[1] == '/') )
|
|
{
|
|
if (!newline) goto line_incomplete;
|
|
|
|
// ets++
|
|
if (*script->script_p == '/') script->script_p++;
|
|
while (*script->script_p++ != '\n')
|
|
{
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
}
|
|
com.com_scriptline = script->line++;
|
|
goto skip_whitespace;
|
|
}
|
|
|
|
// /* */ comments
|
|
if (script->script_p[0] == '/' && script->script_p[1] == '*')
|
|
{
|
|
if (!newline) goto line_incomplete;
|
|
|
|
script->script_p += 2;
|
|
while (script->script_p[0] != '*' && script->script_p[1] != '/')
|
|
{
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
if (*script->script_p++ == '\n')
|
|
{
|
|
if (!newline) goto line_incomplete;
|
|
com.com_scriptline = script->line++;
|
|
}
|
|
}
|
|
script->script_p += 2;
|
|
goto skip_whitespace;
|
|
}
|
|
|
|
// copy token
|
|
token_p = token;
|
|
c = script->script_p[0];
|
|
|
|
// handle quoted strings specially
|
|
if (*script->script_p == '"')
|
|
{
|
|
// quoted token
|
|
script->script_p++;
|
|
while (*script->script_p != '"')
|
|
{
|
|
if (token_p == &token[MAX_SYSPATH - 1])
|
|
{
|
|
MsgDev(D_WARN, "GetToken: Token too large on line %i\n", com.com_scriptline);
|
|
break;
|
|
}
|
|
|
|
*token_p++ = *script->script_p++;
|
|
if (script->script_p == script->end_p)
|
|
break;
|
|
}
|
|
script->script_p++;
|
|
}
|
|
else if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':' || c == ',' || c == '[' || c == ']')
|
|
{
|
|
// parse single characters
|
|
*token_p++ = *script->script_p++;
|
|
}
|
|
else
|
|
{
|
|
// parse regular word
|
|
while ( *script->script_p > 32 )
|
|
{
|
|
if (token_p == &token[MAX_SYSPATH - 1])
|
|
{
|
|
MsgDev( D_WARN, "GetToken: Token too large on line %i\n",com.com_scriptline);
|
|
break;
|
|
}
|
|
*token_p = c;
|
|
*token_p++ = *script->script_p++;
|
|
c = *script->script_p;
|
|
|
|
if (script->script_p == script->end_p)
|
|
break;
|
|
if (c == '{' || c == '}'|| c == ')'|| c == '(' || c == '\'' || c == ':' || c == ',' || c == ';' || c == '[' || c == ']')
|
|
break;
|
|
}
|
|
}
|
|
*token_p = 0; // cutoff other symbols
|
|
|
|
// quake style include & default MSVC style
|
|
if (!com_strcmp(token, "$include") || !com_strcmp(token, "#include"))
|
|
{
|
|
SC_ReadToken( false );
|
|
SC_AddScript(token, NULL, 0 );
|
|
return SC_ReadToken (newline);
|
|
}
|
|
return true;
|
|
|
|
line_incomplete:
|
|
//invoke error
|
|
return SC_EndOfScript( newline );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_ReadTokenSimple
|
|
==============
|
|
*/
|
|
bool SC_ReadTokenSimple( bool newline )
|
|
{
|
|
char *token_p;
|
|
|
|
if( tokenready ) // is a token allready waiting?
|
|
{
|
|
tokenready = false;
|
|
return true;
|
|
}
|
|
|
|
if( script->script_p >= script->end_p )
|
|
return SC_EndOfScript( newline );
|
|
|
|
|
|
skip_whitespace: // skip whitespace
|
|
while (*script->script_p <= 32)
|
|
{
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
|
|
if (*script->script_p++ == '\n')
|
|
{
|
|
if( !newline ) return SC_EndOfScript( newline );
|
|
com.com_scriptline = script->line++;
|
|
}
|
|
}
|
|
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
|
|
// ; # // comments
|
|
if( *script->script_p == ';' || *script->script_p == '#' || ( script->script_p[0] == '/' && script->script_p[1] == '/'))
|
|
{
|
|
if( !newline ) return SC_EndOfScript( newline );
|
|
|
|
// ets+++
|
|
if(*script->script_p == '/') script->script_p++;
|
|
if( script->script_p[1] == 'T' && script->script_p[2] == 'X' )
|
|
{
|
|
GI.TXcommand = script->script_p[3];//TX#"-style comment
|
|
}
|
|
while (*script->script_p++ != '\n')
|
|
{
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
}
|
|
com.com_scriptline = script->line++;
|
|
goto skip_whitespace;
|
|
}
|
|
|
|
// /* */ comments
|
|
if (script->script_p[0] == '/' && script->script_p[1] == '*')
|
|
{
|
|
if (!newline) return SC_EndOfScript( newline );
|
|
|
|
script->script_p += 2;
|
|
while (script->script_p[0] != '*' && script->script_p[1] != '/')
|
|
{
|
|
if (script->script_p >= script->end_p)
|
|
return SC_EndOfScript (newline);
|
|
if (*script->script_p++ == '\n')
|
|
{
|
|
if( !newline ) return SC_EndOfScript( newline );
|
|
com.com_scriptline = script->line++;
|
|
}
|
|
}
|
|
script->script_p += 2;
|
|
goto skip_whitespace;
|
|
}
|
|
|
|
// copy token
|
|
token_p = token;
|
|
|
|
if (*script->script_p == '"')
|
|
{
|
|
// quoted token
|
|
script->script_p++;
|
|
while (*script->script_p != '"')
|
|
{
|
|
if (token_p == &token[MAX_SYSPATH - 1])
|
|
{
|
|
MsgDev(D_WARN, "SC_ReadToken: Token too large on line %i\n", com.com_scriptline);
|
|
break;
|
|
}
|
|
|
|
*token_p++ = *script->script_p++;
|
|
if (script->script_p == script->end_p)
|
|
break;
|
|
}
|
|
script->script_p++;
|
|
}
|
|
else // regular token
|
|
{
|
|
while ( *script->script_p > 32 && *script->script_p != ';')
|
|
{
|
|
if (token_p == &token[MAX_SYSPATH - 1])
|
|
{
|
|
MsgDev( D_WARN, "GetToken: Token too large on line %i\n",com.com_scriptline);
|
|
break;
|
|
}
|
|
|
|
*token_p++ = *script->script_p++;
|
|
if (script->script_p == script->end_p)
|
|
break;
|
|
}
|
|
}
|
|
|
|
*token_p = 0;
|
|
|
|
// quake style include & default MSVC style
|
|
if (!com_strcmp(token, "$include"))
|
|
{
|
|
SC_ReadTokenSimple(false);
|
|
SC_AddScript(token, NULL, 0 );
|
|
return SC_ReadTokenSimple(newline);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_EndOfScript
|
|
==============
|
|
*/
|
|
bool SC_EndOfScript_( bool newline, const int line )
|
|
{
|
|
if( !newline )
|
|
{
|
|
com.com_scriptline = script->line;
|
|
Sys_Break( "%s: line %i is incomplete (called at line %i)\n", script->filename, com.com_scriptline, line );
|
|
}
|
|
|
|
if(!com_strcmp(script->filename, "*sc_buffer"))
|
|
{
|
|
endofscript = true;
|
|
return false;
|
|
}
|
|
|
|
// FIXME: stupid xash bug
|
|
if(Mem_IsAllocated( script->buffer )) Mem_Free( script->buffer );
|
|
else MsgDev( D_WARN, "attempt to free already freed script buffer '%s'\n", script->filename );
|
|
|
|
// FIXME
|
|
// script->buffer = NULL;
|
|
|
|
if( script == scriptstack + 1 )
|
|
{
|
|
endofscript = true;
|
|
return false;
|
|
}
|
|
|
|
script--;
|
|
token[0] = 0; // clear last token
|
|
com.com_scriptline = script->line;
|
|
endofscript = true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_TokenAvailable
|
|
==============
|
|
*/
|
|
bool SC_TokenAvailable (void)
|
|
{
|
|
char *search_p;
|
|
|
|
search_p = script->script_p;
|
|
|
|
if(search_p >= script->end_p)
|
|
return false;
|
|
|
|
while ( *search_p <= 32)
|
|
{
|
|
if (*search_p == '\n')
|
|
return false;
|
|
search_p++;
|
|
if (search_p == script->end_p)
|
|
return false;
|
|
}
|
|
|
|
if (*search_p == ';')
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SC_SkipWhiteSpace
|
|
=================
|
|
*/
|
|
const char *SC_SkipWhiteSpace( const char *data_p, bool *newline )
|
|
{
|
|
int c;
|
|
|
|
while((c = *data_p) <= ' ')
|
|
{
|
|
if( !c ) return NULL;
|
|
if( c == '\n' )
|
|
{
|
|
com.com_scriptline++;
|
|
*newline = true;
|
|
}
|
|
data_p++;
|
|
}
|
|
return data_p;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SC_SkipBracedSection
|
|
|
|
Skips until a matching close brace is found.
|
|
Internal brace depths are properly skipped.
|
|
=================
|
|
*/
|
|
void SC_SkipBracedSection( char **data_p, int depth )
|
|
{
|
|
do
|
|
{
|
|
SC_ParseToken( data_p, true );
|
|
if( !token[1] )
|
|
{
|
|
if( token[0] == '{' ) depth++;
|
|
else if( token[0] == '}' ) depth--;
|
|
}
|
|
} while( depth && *data_p );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_ParseToken_Simple
|
|
|
|
Parse a token out of a string, behaving like the qwcl console
|
|
==============
|
|
*/
|
|
bool SC_ParseToken_Simple( const char **data_p )
|
|
{
|
|
int len = 0;
|
|
const char *data;
|
|
|
|
token[0] = 0;
|
|
data = *data_p;
|
|
|
|
if(!data)
|
|
{
|
|
endofscript = true;
|
|
*data_p = NULL;
|
|
return false;
|
|
}
|
|
|
|
// skip whitespace
|
|
skipwhite:
|
|
for (;*data <= ' ';data++)
|
|
{
|
|
if (*data == 0)
|
|
{
|
|
// end of file
|
|
endofscript = true;
|
|
*data_p = NULL;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (*data == '/' && data[1] == '/')
|
|
{
|
|
// comment
|
|
while (*data && *data != '\n' && *data != '\r')
|
|
data++;
|
|
goto skipwhite;
|
|
}
|
|
else if (*data == '\"')
|
|
{
|
|
// quoted string
|
|
for (data++;*data && *data != '\"';data++)
|
|
{
|
|
// allow escaped " and \ case
|
|
if (*data == '\\' && (data[1] == '\"' || data[1] == '\\'))
|
|
data++;
|
|
if (len < (int)sizeof(token) - 1)
|
|
token[len++] = *data;
|
|
}
|
|
token[len] = 0;
|
|
if (*data == '\"') data++;
|
|
*data_p = data;
|
|
}
|
|
else
|
|
{
|
|
// regular word
|
|
for (;*data > ' ';data++)
|
|
if (len < (int)sizeof(token) - 1)
|
|
token[len++] = *data;
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_ParseToken
|
|
|
|
Parse a token out of a string
|
|
==============
|
|
*/
|
|
char *SC_ParseToken( const char **data_p, bool allow_newline )
|
|
{
|
|
int c;
|
|
int len = 0;
|
|
const char *data;
|
|
bool newline = false;
|
|
|
|
token[0] = 0;
|
|
data = *data_p;
|
|
|
|
if( !data )
|
|
{
|
|
endofscript = true;
|
|
*data_p = NULL;
|
|
return token;
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
data = SC_SkipWhiteSpace( data, &newline );
|
|
if( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return token;
|
|
}
|
|
if( newline && !allow_newline )
|
|
{
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
|
|
c = *data;
|
|
|
|
if( c=='/' && data[1] == '/' )
|
|
{
|
|
// skip // comments
|
|
while( *data && *data != '\n' )
|
|
data++;
|
|
}
|
|
else if( c=='/' && data[1] == '*' )
|
|
{
|
|
// skip /* comments
|
|
while( data[1] && (data[0] != '*' || data[1] != '/'))
|
|
{
|
|
if( *data == '\n' ) com.com_scriptline++;
|
|
data++;
|
|
}
|
|
if( *data ) data += 2;
|
|
}
|
|
else break; // an actual token
|
|
}
|
|
|
|
// handle quoted strings specially
|
|
if (*data == '\"' || *data == '\'')
|
|
{
|
|
data++;
|
|
while( 1 )
|
|
{
|
|
c = *data++;
|
|
if( c == '\n' ) com.com_scriptline++;
|
|
if( c=='\"' || c=='\0' )
|
|
{
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
token[len++] = c;
|
|
}
|
|
}
|
|
|
|
// parse single characters
|
|
if( c == '{' || c == '}'|| c == ')' || c == '(' || c == '\'' || c == ':' || c == ',' )
|
|
{
|
|
token[len] = c;
|
|
data++;
|
|
len++;
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
|
|
// parse a regular word
|
|
do
|
|
{
|
|
token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if( c == '{' || c == '}'|| c == ')'|| c == '(' || c == '\'' || c == ':' || c == ',' )
|
|
break;
|
|
} while( c > 32 );
|
|
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_ParseWord
|
|
|
|
Parse a word out of a string
|
|
==============
|
|
*/
|
|
char *SC_ParseWord( const char **data_p, bool allow_newline )
|
|
{
|
|
int c;
|
|
const char *data;
|
|
int len = 0;
|
|
bool newline = false;
|
|
|
|
token[0] = 0;
|
|
data = *data_p;
|
|
|
|
if( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return NULL;
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
data = SC_SkipWhiteSpace( data, &newline );
|
|
if( !data )
|
|
{
|
|
*data_p = NULL;
|
|
return NULL;
|
|
}
|
|
if( newline && !allow_newline )
|
|
{
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
|
|
c = *data;
|
|
|
|
if( c=='/' && data[1] == '/' )
|
|
{
|
|
// skip // comments
|
|
while( *data && *data != '\n' )
|
|
data++;
|
|
}
|
|
else if( c=='/' && data[1] == '*' )
|
|
{
|
|
// skip /* comments
|
|
while( data[1] && (data[0] != '*' || data[1] != '/'))
|
|
{
|
|
if( *data == '\n' ) com.com_scriptline++;
|
|
data++;
|
|
}
|
|
if( *data ) data += 2;
|
|
}
|
|
else break; // an actual token
|
|
}
|
|
|
|
// handle quoted strings specially
|
|
if( c == '\"' )
|
|
{
|
|
data++;
|
|
do
|
|
{
|
|
c = *data++;
|
|
if (c=='\"' || c=='\0')
|
|
{
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
token[len] = c;
|
|
len++;
|
|
} while( 1 );
|
|
}
|
|
|
|
// parse numbers
|
|
if( c >= '0' && c <= '9' )
|
|
{
|
|
if( c == '0' && data[1] == 'x' )
|
|
{
|
|
// parse hex
|
|
token[0] = '0';
|
|
c='x';
|
|
len=1;
|
|
data++;
|
|
while( 1 )
|
|
{
|
|
// parse regular number
|
|
token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if ((c < '0'|| c > '9') && (c < 'a'||c > 'f') && (c < 'A'|| c > 'F') && c != '.')
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
while( 1 )
|
|
{
|
|
// parse regular number
|
|
token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
if ((c < '0'|| c > '9') && c != '.')
|
|
break;
|
|
}
|
|
}
|
|
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
|
|
// parse words
|
|
else if((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')
|
|
{
|
|
do
|
|
{
|
|
token[len] = c;
|
|
data++;
|
|
len++;
|
|
c = *data;
|
|
} while ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_');
|
|
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
else
|
|
{
|
|
token[len] = c;
|
|
len++;
|
|
token[len] = 0;
|
|
*data_p = data;
|
|
return token;
|
|
}
|
|
}
|
|
/*
|
|
=============================================================================
|
|
|
|
MAIN PUBLIC FUNCTIONS
|
|
|
|
=============================================================================
|
|
*/
|
|
|
|
/*
|
|
==============
|
|
Match Token With
|
|
|
|
check current token for match with user keyword
|
|
==============
|
|
*/
|
|
bool SC_MatchToken( const char *match )
|
|
{
|
|
if (!com_stricmp( token, match ))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_SkipToken
|
|
|
|
skip current token and jump into newline
|
|
==============
|
|
*/
|
|
void SC_SkipToken( void )
|
|
{
|
|
switch( Sys.app_name )
|
|
{
|
|
case HOST_BSPLIB:
|
|
case HOST_SPRITE:
|
|
case HOST_STUDIO:
|
|
case HOST_WADLIB:
|
|
// don't handle single characters
|
|
SC_ReadTokenSimple( false );
|
|
break;
|
|
default:
|
|
SC_ReadToken( false );
|
|
break;
|
|
}
|
|
tokenready = true;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_FreeToken
|
|
|
|
release current token to get
|
|
him again with SC_GetToken()
|
|
==============
|
|
*/
|
|
void SC_FreeToken( void )
|
|
{
|
|
tokenready = true;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_TryToken
|
|
|
|
check token for available on current line
|
|
==============
|
|
*/
|
|
bool SC_TryToken( void )
|
|
{
|
|
if(!SC_TokenAvailable())
|
|
return false;
|
|
|
|
switch( Sys.app_name )
|
|
{
|
|
case HOST_BSPLIB:
|
|
case HOST_SPRITE:
|
|
case HOST_STUDIO:
|
|
case HOST_WADLIB:
|
|
// don't handle single characters
|
|
SC_ReadTokenSimple( false );
|
|
break;
|
|
default:
|
|
SC_ReadToken( false );
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/*
|
|
==============
|
|
SC_GetToken
|
|
|
|
get token on current or newline
|
|
==============
|
|
*/
|
|
char *SC_GetToken( bool newline )
|
|
{
|
|
switch( Sys.app_name )
|
|
{
|
|
case HOST_BSPLIB:
|
|
case HOST_SPRITE:
|
|
case HOST_STUDIO:
|
|
case HOST_WADLIB:
|
|
// don't handle single characters
|
|
if(SC_ReadTokenSimple( newline ))
|
|
return token;
|
|
break;
|
|
default:
|
|
if(SC_ReadToken( newline ))
|
|
return token;
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
==============
|
|
SC_Token
|
|
|
|
return current token
|
|
==============
|
|
*/
|
|
char *SC_Token( void )
|
|
{
|
|
return token;
|
|
}
|
|
|
|
/*
|
|
============
|
|
SC_StringContains
|
|
============
|
|
*/
|
|
char *SC_StringContains(char *str1, char *str2, int casecmp)
|
|
{
|
|
int len, i, j;
|
|
|
|
len = com_strlen(str1) - com_strlen(str2);
|
|
|
|
for (i = 0; i <= len; i++, str1++)
|
|
{
|
|
for (j = 0; str2[j]; j++)
|
|
{
|
|
if (casecmp)
|
|
{
|
|
if (str1[j] != str2[j]) break;
|
|
}
|
|
else
|
|
{
|
|
if(com_toupper(str1[j]) != com_toupper(str2[j]))
|
|
break;
|
|
}
|
|
}
|
|
if (!str2[j]) return str1;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
============
|
|
SC_FilterToken
|
|
============
|
|
*/
|
|
bool SC_FilterToken(char *filter, char *name, int casecmp)
|
|
{
|
|
char buf[MAX_MSGLEN];
|
|
char *ptr;
|
|
int i, found;
|
|
|
|
while(*filter)
|
|
{
|
|
if(*filter == '*')
|
|
{
|
|
filter++;
|
|
for (i = 0; *filter; i++)
|
|
{
|
|
if (*filter == '*' || *filter == '?')
|
|
break;
|
|
buf[i] = *filter;
|
|
filter++;
|
|
}
|
|
buf[i] = '\0';
|
|
if (com_strlen(buf))
|
|
{
|
|
ptr = SC_StringContains(name, buf, casecmp);
|
|
if (!ptr) return false;
|
|
name = ptr + com_strlen(buf);
|
|
}
|
|
}
|
|
else if (*filter == '?')
|
|
{
|
|
filter++;
|
|
name++;
|
|
}
|
|
else if (*filter == '[' && *(filter+1) == '[')
|
|
{
|
|
filter++;
|
|
}
|
|
else if (*filter == '[')
|
|
{
|
|
filter++;
|
|
found = false;
|
|
while(*filter && !found)
|
|
{
|
|
if (*filter == ']' && *(filter+1) != ']') break;
|
|
if (*(filter+1) == '-' && *(filter+2) && (*(filter+2) != ']' || *(filter+3) == ']'))
|
|
{
|
|
if (casecmp)
|
|
{
|
|
if (*name >= *filter && *name <= *(filter+2)) found = true;
|
|
}
|
|
else
|
|
{
|
|
if (com_toupper(*name) >= com_toupper(*filter) && com_toupper(*name) <= com_toupper(*(filter+2)))
|
|
found = true;
|
|
}
|
|
filter += 3;
|
|
}
|
|
else
|
|
{
|
|
if (casecmp)
|
|
{
|
|
if (*filter == *name) found = true;
|
|
}
|
|
else
|
|
{
|
|
if (com_toupper(*filter) == com_toupper(*name)) found = true;
|
|
}
|
|
filter++;
|
|
}
|
|
}
|
|
if (!found) return false;
|
|
while(*filter)
|
|
{
|
|
if (*filter == ']' && *(filter+1) != ']')
|
|
break;
|
|
filter++;
|
|
}
|
|
filter++;
|
|
name++;
|
|
}
|
|
else
|
|
{
|
|
if (casecmp)
|
|
{
|
|
if (*filter != *name)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (com_toupper(*filter) != com_toupper(*name))
|
|
return false;
|
|
}
|
|
filter++;
|
|
name++;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
=================
|
|
SC_HashKey
|
|
|
|
returns hash key for string
|
|
=================
|
|
*/
|
|
uint SC_HashKey( const char *string, uint hashSize )
|
|
{
|
|
uint hashKey = 0;
|
|
int i;
|
|
|
|
for( i = 0; string[i]; i++ )
|
|
hashKey = (hashKey + i) * 37 + com_tolower(string[i]);
|
|
|
|
return (hashKey % hashSize);
|
|
} |