This repository has been archived on 2022-06-27. You can view files and clone it, but cannot push or open issues or pull requests.
Xash3DArchive/launch/common/parselib.c

729 lines
13 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
script_t scriptstack[ MAX_INCLUDES ];
script_t *script;
int scriptline;
char token[ MAX_INPUTLINE ]; //contains token info
bool endofscript;
bool tokenready; // only true if UnGetToken was just called
bool SC_EndOfScript (bool newline);
/*
==============
SC_AddScriptToStack
==============
*/
bool SC_AddScriptToStack(const char *name, byte *buffer, int size)
{
if (script == &scriptstack[MAX_INCLUDES - 1])
{
MsgWarn("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 = 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;
if(!buf || size <= 0)
buf = FS_LoadFile (filename, &size );
script = scriptstack;
result = SC_AddScriptToStack( filename, buf, size);
endofscript = false;
tokenready = false;
return result;
}
bool SC_AddScript( const char *filename, char *buf, int size )
{
if(!buf || size <= 0)
buf = FS_LoadFile (filename, &size );
return SC_AddScriptToStack(filename, buf, size);
}
void SC_ResetScript( void )
{
// can parsing again
script->line = scriptline = 1;
script->script_p = script->buffer;
}
/*
==============
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;
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) goto line_incomplete;
// ets++
if (*script->script_p == '/') script->script_p++;
if (script->script_p[1] == 'T' && script->script_p[2] == 'X')
std.com_TXcommand = script->script_p[3];//TX#"-style comment (get rid of this)
while (*script->script_p++ != '\n')
{
if (script->script_p >= script->end_p)
return SC_EndOfScript (newline);
}
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;
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", 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 == ',')
{
// parse single characters
*token_p++ = *script->script_p++;
}
else
{
// parse regular word
while ( *script->script_p > 32 )
{
if (token_p == &token[MAX_SYSPATH - 1])
{
MsgWarn("GetToken: Token too large on line %i\n",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 == ';')
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_EndOfScript
==============
*/
bool SC_EndOfScript (bool newline)
{
if(!newline)
{
scriptline = script->line;
Sys_Error("%s: line %i is incomplete\n", script->filename, scriptline);
}
if(!com_strcmp (script->filename, "script buffer"))
{
endofscript = true;
return false;
}
if(script == scriptstack + 1)
{
endofscript = true;
return false;
}
script--;
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_ParseToken
Parse a token out of a string
==============
*/
char *SC_ParseToken(const char **data_p)
{
int c;
int len = 0;
const char *data;
token[0] = 0;
data = *data_p;
if (!data)
{
endofscript = true;
*data_p = NULL;
return NULL;
}
// skip whitespace
skipwhite:
while ( (c = *data) <= ' ')
{
if (c == 0)
{
endofscript = true;
*data_p = NULL;
return NULL; // end of file;
}
data++;
}
// skip // comments
if (c=='/' && data[1] == '/')
{
while (*data && *data != '\n')
data++;
goto skipwhite;
}
// skip /* comments
if (c=='/' && data[1] == '*')
{
while (data[1] && (data[0] != '*' || data[1] != '/'))
data++;
data += 2;
goto skipwhite;
}
// handle quoted strings specially
if (c == '\"')
{
data++;
while(1)
{
c = *data++;
if (c=='\"'||c=='\0')
{
token[len] = 0;
*data_p = data;
return token;
}
token[len] = c;
len++;
}
}
// 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 )
{
int c;
int len = 0;
const char *data;
token[0] = 0;
data = *data_p;
if (!data)
{
*data_p = NULL;
return NULL;
}
// skip whitespace
skipwhite:
while ( (c = *data) <= ' ')
{
if (c == 0)
{
*data_p = NULL;
endofscript = true;
return NULL; // end of file;
}
data++;
}
// skip // comments
if (c=='/' && data[1] == '/')
{
while (*data && *data != '\n')
data++;
goto skipwhite;
}
// 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 )
{
SC_ReadToken( true );
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;
SC_ReadToken( false );
return true;
}
/*
==============
SC_GetToken
get token on current or newline
==============
*/
char *SC_GetToken( bool newline )
{
if(SC_ReadToken( newline ))
return token;
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_INPUTLINE];
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;
}