You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1349 lines
29 KiB
1349 lines
29 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//===========================================================================// |
|
|
|
// scriplib.c |
|
|
|
#include "tier1/strtools.h" |
|
#include "tier2/tier2.h" |
|
#include "cmdlib.h" |
|
#include "scriplib.h" |
|
#if defined( _X360 ) |
|
#include "xbox\xbox_win32stubs.h" |
|
#endif |
|
#if defined(POSIX) |
|
#include "../../filesystem/linux_support.h" |
|
#include <sys/stat.h> |
|
#endif |
|
/* |
|
============================================================================= |
|
|
|
PARSING STUFF |
|
|
|
============================================================================= |
|
*/ |
|
|
|
typedef struct |
|
{ |
|
char filename[1024]; |
|
char *buffer,*script_p,*end_p; |
|
int line; |
|
|
|
char macrobuffer[4096]; |
|
char *macroparam[64]; |
|
char *macrovalue[64]; |
|
int nummacroparams; |
|
|
|
} script_t; |
|
|
|
#define MAX_INCLUDES 64 |
|
script_t scriptstack[MAX_INCLUDES]; |
|
script_t *script = NULL; |
|
int scriptline; |
|
|
|
char token[MAXTOKEN]; |
|
qboolean endofscript; |
|
qboolean tokenready; // only true if UnGetToken was just called |
|
|
|
typedef struct |
|
{ |
|
char *param; |
|
char *value; |
|
} variable_t; |
|
|
|
CUtlVector<variable_t> g_definevariable; |
|
|
|
/* |
|
Callback stuff |
|
*/ |
|
|
|
void DefaultScriptLoadedCallback( char const *pFilenameLoaded, char const *pIncludedFromFileName, int nIncludeLineNumber ) |
|
{ |
|
NULL; |
|
} |
|
|
|
SCRIPT_LOADED_CALLBACK g_pfnCallback = DefaultScriptLoadedCallback; |
|
|
|
SCRIPT_LOADED_CALLBACK SetScriptLoadedCallback( SCRIPT_LOADED_CALLBACK pfnNewScriptLoadedCallback ) |
|
{ |
|
SCRIPT_LOADED_CALLBACK pfnCallback = g_pfnCallback; |
|
g_pfnCallback = pfnNewScriptLoadedCallback; |
|
return pfnCallback; |
|
} |
|
|
|
/* |
|
============== |
|
AddScriptToStack |
|
============== |
|
*/ |
|
void AddScriptToStack (char *filename, ScriptPathMode_t pathMode = SCRIPT_USE_ABSOLUTE_PATH) |
|
{ |
|
int size; |
|
|
|
script++; |
|
if (script == &scriptstack[MAX_INCLUDES]) |
|
Error ("script file exceeded MAX_INCLUDES"); |
|
|
|
if ( pathMode == SCRIPT_USE_RELATIVE_PATH ) |
|
Q_strncpy( script->filename, filename, sizeof( script->filename ) ); |
|
else |
|
Q_strncpy (script->filename, ExpandPath (filename), sizeof( script->filename ) ); |
|
|
|
size = LoadFile (script->filename, (void **)&script->buffer); |
|
|
|
// printf ("entering %s\n", script->filename); |
|
if ( g_pfnCallback ) |
|
{ |
|
if ( script == scriptstack + 1 ) |
|
g_pfnCallback( script->filename, NULL, 0 ); |
|
else |
|
g_pfnCallback( script->filename, script[-1].filename, script[-1].line ); |
|
} |
|
|
|
script->line = 1; |
|
|
|
script->script_p = script->buffer; |
|
script->end_p = script->buffer + size; |
|
} |
|
|
|
|
|
/* |
|
============== |
|
LoadScriptFile |
|
============== |
|
*/ |
|
void LoadScriptFile (char *filename, ScriptPathMode_t pathMode) |
|
{ |
|
script = scriptstack; |
|
AddScriptToStack (filename, pathMode); |
|
|
|
endofscript = false; |
|
tokenready = false; |
|
} |
|
|
|
|
|
/* |
|
============== |
|
============== |
|
*/ |
|
|
|
script_t *macrolist[256]; |
|
int nummacros; |
|
|
|
void DefineMacro( char *macroname ) |
|
{ |
|
script_t *pmacro = (script_t *)malloc( sizeof( script_t ) ); |
|
|
|
strcpy( pmacro->filename, macroname ); |
|
pmacro->line = script->line; |
|
pmacro->nummacroparams = 0; |
|
|
|
char *mp = pmacro->macrobuffer; |
|
char *cp = script->script_p; |
|
|
|
while (TokenAvailable( )) |
|
{ |
|
GetToken( false ); |
|
|
|
if (token[0] == '\\' && token[1] == '\\') |
|
{ |
|
break; |
|
} |
|
cp = script->script_p; |
|
|
|
pmacro->macroparam[pmacro->nummacroparams++] = mp; |
|
|
|
strcpy( mp, token ); |
|
mp += strlen( token ) + 1; |
|
|
|
if (mp >= pmacro->macrobuffer + sizeof( pmacro->macrobuffer )) |
|
Error("Macro buffer overflow\n"); |
|
} |
|
// roll back script_p to previous valid location |
|
script->script_p = cp; |
|
|
|
// find end of macro def |
|
while (*cp && *cp != '\n') |
|
{ |
|
//Msg("%d ", *cp ); |
|
if (*cp == '\\' && *(cp+1) == '\\') |
|
{ |
|
// skip till end of line |
|
while (*cp && *cp != '\n') |
|
{ |
|
*cp = ' '; // replace with spaces |
|
cp++; |
|
} |
|
|
|
if (*cp) |
|
{ |
|
cp++; |
|
} |
|
} |
|
else |
|
{ |
|
cp++; |
|
} |
|
} |
|
|
|
int size = (cp - script->script_p); |
|
|
|
pmacro->buffer = (char *)malloc( size + 1); |
|
memcpy( pmacro->buffer, script->script_p, size ); |
|
pmacro->buffer[size] = '\0'; |
|
pmacro->end_p = &pmacro->buffer[size]; |
|
|
|
macrolist[nummacros++] = pmacro; |
|
|
|
script->script_p = cp; |
|
} |
|
|
|
|
|
void DefineVariable( char *variablename ) |
|
{ |
|
variable_t v; |
|
|
|
v.param = strdup( variablename ); |
|
|
|
GetToken( false ); |
|
|
|
v.value = strdup( token ); |
|
|
|
g_definevariable.AddToTail( v ); |
|
} |
|
|
|
|
|
|
|
/* |
|
============== |
|
============== |
|
*/ |
|
bool AddMacroToStack( char *macroname ) |
|
{ |
|
// lookup macro |
|
if (macroname[0] != '$') |
|
return false; |
|
|
|
int i; |
|
for (i = 0; i < nummacros; i++) |
|
{ |
|
if (strcmpi( macrolist[i]->filename, ¯oname[1] ) == 0) |
|
{ |
|
break; |
|
} |
|
} |
|
if (i == nummacros) |
|
return false; |
|
|
|
script_t *pmacro = macrolist[i]; |
|
|
|
// get tokens |
|
script_t *pnext = script + 1; |
|
|
|
pnext++; |
|
if (pnext == &scriptstack[MAX_INCLUDES]) |
|
Error ("script file exceeded MAX_INCLUDES"); |
|
|
|
// get tokens |
|
char *cp = pnext->macrobuffer; |
|
|
|
pnext->nummacroparams = pmacro->nummacroparams; |
|
|
|
for (i = 0; i < pnext->nummacroparams; i++) |
|
{ |
|
GetToken(false); |
|
|
|
strcpy( cp, token ); |
|
pnext->macroparam[i] = pmacro->macroparam[i]; |
|
pnext->macrovalue[i] = cp; |
|
|
|
cp += strlen( token ) + 1; |
|
|
|
if (cp >= pnext->macrobuffer + sizeof( pnext->macrobuffer )) |
|
Error("Macro buffer overflow\n"); |
|
} |
|
|
|
script = pnext; |
|
strcpy( script->filename, pmacro->filename ); |
|
|
|
int size = pmacro->end_p - pmacro->buffer; |
|
script->buffer = (char *)malloc( size + 1 ); |
|
memcpy( script->buffer, pmacro->buffer, size ); |
|
pmacro->buffer[size] = '\0'; |
|
script->script_p = script->buffer; |
|
script->end_p = script->buffer + size; |
|
script->line = pmacro->line; |
|
|
|
return true; |
|
} |
|
|
|
|
|
|
|
bool ExpandMacroToken( char *&token_p ) |
|
{ |
|
if ( script->nummacroparams && *script->script_p == '$' ) |
|
{ |
|
char *cp = script->script_p + 1; |
|
|
|
while ( *cp > 32 && *cp != '$' ) |
|
{ |
|
cp++; |
|
} |
|
|
|
// found a word with $'s on either end? |
|
if (*cp != '$') |
|
return false; |
|
|
|
// get token pointer |
|
char *tp = script->script_p + 1; |
|
int len = (cp - tp); |
|
*(tp + len) = '\0'; |
|
|
|
// lookup macro parameter |
|
int index = 0; |
|
for (index = 0; index < script->nummacroparams; index++) |
|
{ |
|
if (stricmp( script->macroparam[index], tp ) == 0) |
|
break; |
|
} |
|
if (index >= script->nummacroparams) |
|
{ |
|
Error("unknown macro token \"%s\" in %s\n", tp, script->filename ); |
|
} |
|
|
|
// paste token into |
|
len = strlen( script->macrovalue[index] ); |
|
strcpy( token_p, script->macrovalue[index] ); |
|
token_p += len; |
|
|
|
script->script_p = cp + 1; |
|
|
|
if (script->script_p >= script->end_p) |
|
Error ("Macro expand overflow\n"); |
|
|
|
if (token_p >= &token[MAXTOKEN]) |
|
Error ("Token too large on line %i\n",scriptline); |
|
|
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
/* |
|
============== |
|
============== |
|
*/ |
|
// FIXME: this should create a new script context so the individual tokens in the variable can be parsed |
|
bool ExpandVariableToken( char *&token_p ) |
|
{ |
|
if ( *script->script_p == '$' ) |
|
{ |
|
char *cp = script->script_p + 1; |
|
|
|
while ( *cp > 32 && *cp != '$' ) |
|
{ |
|
cp++; |
|
} |
|
|
|
// found a word with $'s on either end? |
|
if (*cp != '$') |
|
return false; |
|
|
|
// get token pointer |
|
char *tp = script->script_p + 1; |
|
int len = (cp - tp); |
|
*(tp + len) = '\0'; |
|
|
|
// lookup macro parameter |
|
|
|
int index; |
|
for (index = 0; index < g_definevariable.Count(); index++) |
|
{ |
|
if (Q_strnicmp( g_definevariable[index].param, tp, len ) == 0) |
|
break; |
|
} |
|
|
|
if (index >= g_definevariable.Count() ) |
|
{ |
|
Error("unknown variable token \"%s\" in %s\n", tp, script->filename ); |
|
} |
|
|
|
// paste token into |
|
len = strlen( g_definevariable[index].value ); |
|
strcpy( token_p, g_definevariable[index].value ); |
|
token_p += len; |
|
|
|
script->script_p = cp + 1; |
|
|
|
if (script->script_p >= script->end_p) |
|
Error ("Macro expand overflow\n"); |
|
|
|
if (token_p >= &token[MAXTOKEN]) |
|
Error ("Token too large on line %i\n",scriptline); |
|
|
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
|
|
|
|
/* |
|
============== |
|
ParseFromMemory |
|
============== |
|
*/ |
|
void ParseFromMemory (char *buffer, int size) |
|
{ |
|
script = scriptstack; |
|
script++; |
|
if (script == &scriptstack[MAX_INCLUDES]) |
|
Error ("script file exceeded MAX_INCLUDES"); |
|
strcpy (script->filename, "memory buffer" ); |
|
|
|
script->buffer = buffer; |
|
script->line = 1; |
|
script->script_p = script->buffer; |
|
script->end_p = script->buffer + size; |
|
|
|
endofscript = false; |
|
tokenready = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used instead of ParseFromMemory to temporarily add a memory buffer |
|
// to the script stack. ParseFromMemory just blows away the stack. |
|
//----------------------------------------------------------------------------- |
|
void PushMemoryScript( char *pszBuffer, const int nSize ) |
|
{ |
|
if ( script == NULL ) |
|
{ |
|
script = scriptstack; |
|
} |
|
script++; |
|
if ( script == &scriptstack[MAX_INCLUDES] ) |
|
{ |
|
Error ( "script file exceeded MAX_INCLUDES" ); |
|
} |
|
strcpy (script->filename, "memory buffer" ); |
|
|
|
script->buffer = pszBuffer; |
|
script->line = 1; |
|
script->script_p = script->buffer; |
|
script->end_p = script->buffer + nSize; |
|
|
|
endofscript = false; |
|
tokenready = false; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Used after calling PushMemoryScript to clean up the memory buffer |
|
// added to the script stack. The normal end of script terminates |
|
// all parsing at the end of a memory buffer even if there are more scripts |
|
// remaining on the script stack |
|
//----------------------------------------------------------------------------- |
|
bool PopMemoryScript() |
|
{ |
|
if ( V_stricmp( script->filename, "memory buffer" ) ) |
|
return false; |
|
|
|
if ( script == scriptstack ) |
|
{ |
|
endofscript = true; |
|
return false; |
|
} |
|
script--; |
|
scriptline = script->line; |
|
|
|
endofscript = false; |
|
|
|
return true; |
|
} |
|
|
|
|
|
/* |
|
============== |
|
UnGetToken |
|
|
|
Signals that the current token was not used, and should be reported |
|
for the next GetToken. Note that |
|
|
|
GetToken (true); |
|
UnGetToken (); |
|
GetToken (false); |
|
|
|
could cross a line boundary. |
|
============== |
|
*/ |
|
void UnGetToken (void) |
|
{ |
|
tokenready = true; |
|
} |
|
|
|
|
|
qboolean EndOfScript (qboolean crossline) |
|
{ |
|
if (!crossline) |
|
Error ("Line %i is incomplete\n",scriptline); |
|
|
|
if (!strcmp (script->filename, "memory buffer")) |
|
{ |
|
endofscript = true; |
|
return false; |
|
} |
|
|
|
free (script->buffer); |
|
script->buffer = NULL; |
|
if (script == scriptstack+1) |
|
{ |
|
endofscript = true; |
|
return false; |
|
} |
|
script--; |
|
scriptline = script->line; |
|
// printf ("returning to %s\n", script->filename); |
|
return GetToken (crossline); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Given an absolute path, do a find first find next on it and build |
|
// a list of files. Physical file system only |
|
//----------------------------------------------------------------------------- |
|
static void FindFileAbsoluteList( CUtlVector< CUtlString > &outAbsolutePathNames, const char *pszFindName ) |
|
{ |
|
char szPath[MAX_PATH]; |
|
V_strncpy( szPath, pszFindName, sizeof( szPath ) ); |
|
V_StripFilename( szPath ); |
|
|
|
char szResult[MAX_PATH]; |
|
FileFindHandle_t hFile = FILESYSTEM_INVALID_FIND_HANDLE; |
|
|
|
for ( const char *pszFoundFile = g_pFullFileSystem->FindFirst( pszFindName, &hFile ); pszFoundFile && hFile != FILESYSTEM_INVALID_FIND_HANDLE; pszFoundFile = g_pFullFileSystem->FindNext( hFile ) ) |
|
{ |
|
V_ComposeFileName( szPath, pszFoundFile, szResult, sizeof( szResult ) ); |
|
outAbsolutePathNames.AddToTail( szResult ); |
|
} |
|
|
|
g_pFullFileSystem->FindClose( hFile ); |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Data for checking for single character tokens while parsing |
|
//----------------------------------------------------------------------------- |
|
bool g_bCheckSingleCharTokens = false; |
|
CUtlString g_sSingleCharTokens; |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets whether the scriplib parser will do a special check for single |
|
// character tokens. Returns previous state of whether single character |
|
// tokens will be checked. |
|
//----------------------------------------------------------------------------- |
|
bool SetCheckSingleCharTokens( bool bCheck ) |
|
{ |
|
const bool bRetVal = g_bCheckSingleCharTokens; |
|
|
|
g_bCheckSingleCharTokens = bCheck; |
|
|
|
return bRetVal; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Sets the list of single character tokens to check if SetCheckSingleCharTokens |
|
// is turned on. |
|
//----------------------------------------------------------------------------- |
|
CUtlString SetSingleCharTokenList( const char *pszSingleCharTokenList ) |
|
{ |
|
const CUtlString sRetVal = g_sSingleCharTokens; |
|
|
|
if ( pszSingleCharTokenList ) |
|
{ |
|
g_sSingleCharTokens = pszSingleCharTokenList; |
|
} |
|
|
|
return sRetVal; |
|
} |
|
|
|
|
|
/* |
|
============== |
|
GetToken |
|
============== |
|
*/ |
|
qboolean GetToken (qboolean crossline) |
|
{ |
|
char *token_p; |
|
|
|
if (tokenready) // is a token allready waiting? |
|
{ |
|
tokenready = false; |
|
return true; |
|
} |
|
|
|
// printf("script_p %x (%x)\n", script->script_p, script->end_p ); fflush( stdout ); |
|
|
|
if (script->script_p >= script->end_p) |
|
{ |
|
return EndOfScript (crossline); |
|
} |
|
|
|
tokenready = false; |
|
|
|
// skip space, ctrl chars |
|
skipspace: |
|
while (*script->script_p <= 32) |
|
{ |
|
if (script->script_p >= script->end_p) |
|
{ |
|
return EndOfScript (crossline); |
|
} |
|
if (*(script->script_p++) == '\n') |
|
{ |
|
if (!crossline) |
|
{ |
|
Error ("Line %i is incomplete\n",scriptline); |
|
} |
|
scriptline = ++script->line; |
|
} |
|
} |
|
|
|
if (script->script_p >= script->end_p) |
|
{ |
|
return EndOfScript (crossline); |
|
} |
|
|
|
// strip single line comments |
|
if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field |
|
(*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field |
|
{ |
|
if (!crossline) |
|
Error ("Line %i is incomplete\n",scriptline); |
|
while (*script->script_p++ != '\n') |
|
{ |
|
if (script->script_p >= script->end_p) |
|
{ |
|
return EndOfScript (crossline); |
|
} |
|
} |
|
scriptline = ++script->line; |
|
goto skipspace; |
|
} |
|
|
|
// strip out matching /* */ comments |
|
if (*script->script_p == '/' && *((script->script_p)+1) == '*') |
|
{ |
|
script->script_p += 2; |
|
while (*script->script_p != '*' || *((script->script_p)+1) != '/') |
|
{ |
|
if (*script->script_p++ != '\n') |
|
{ |
|
if (script->script_p >= script->end_p) |
|
{ |
|
return EndOfScript (crossline); |
|
} |
|
|
|
scriptline = ++script->line; |
|
} |
|
} |
|
script->script_p += 2; |
|
goto skipspace; |
|
} |
|
|
|
// copy token to buffer |
|
token_p = token; |
|
|
|
if (*script->script_p == '"') |
|
{ |
|
// quoted token |
|
script->script_p++; |
|
while (*script->script_p != '"') |
|
{ |
|
*token_p++ = *script->script_p++; |
|
if (script->script_p == script->end_p) |
|
break; |
|
if (token_p == &token[MAXTOKEN]) |
|
Error ("Token too large on line %i\n",scriptline); |
|
} |
|
script->script_p++; |
|
} |
|
else if ( g_bCheckSingleCharTokens && !g_sSingleCharTokens.IsEmpty() && strchr( g_sSingleCharTokens.String(), *script->script_p ) != NULL ) |
|
{ |
|
*token_p++ = *script->script_p++; |
|
} |
|
else // regular token |
|
while ( *script->script_p > 32 && *script->script_p != ';') |
|
{ |
|
if ( !ExpandMacroToken( token_p ) ) |
|
{ |
|
if ( !ExpandVariableToken( token_p ) ) |
|
{ |
|
*token_p++ = *script->script_p++; |
|
if (script->script_p == script->end_p) |
|
break; |
|
if (token_p == &token[MAXTOKEN]) |
|
Error ("Token too large on line %i\n",scriptline); |
|
|
|
} |
|
} |
|
} |
|
|
|
// add null to end of token |
|
*token_p = 0; |
|
|
|
// check for other commands |
|
if ( !stricmp( token, "$include" ) ) |
|
{ |
|
GetToken( false ); |
|
|
|
bool bFallbackToToken = true; |
|
|
|
CUtlVector< CUtlString > expandedPathList; |
|
|
|
if ( CmdLib_ExpandWithBasePaths( expandedPathList, token ) > 0 ) |
|
{ |
|
for ( int i = 0; i < expandedPathList.Count(); ++i ) |
|
{ |
|
CUtlVector< CUtlString > findFileList; |
|
FindFileAbsoluteList( findFileList, expandedPathList[i].String() ); |
|
|
|
if ( findFileList.Count() > 0 ) |
|
{ |
|
bFallbackToToken = false; |
|
|
|
// Only add the first set of glob matches from the first base path |
|
for ( int j = 0; j < findFileList.Count(); ++j ) |
|
{ |
|
AddScriptToStack( const_cast< char * >( findFileList[j].String() ) ); |
|
} |
|
|
|
break; |
|
} |
|
} |
|
} |
|
|
|
if ( bFallbackToToken ) |
|
{ |
|
AddScriptToStack( token ); |
|
} |
|
|
|
return GetToken( crossline ); |
|
} |
|
else if (!stricmp (token, "$definemacro")) |
|
{ |
|
GetToken (false); |
|
DefineMacro(token); |
|
return GetToken (crossline); |
|
} |
|
else if (!stricmp (token, "$definevariable")) |
|
{ |
|
GetToken (false); |
|
DefineVariable(token); |
|
return GetToken (crossline); |
|
} |
|
else if (AddMacroToStack( token )) |
|
{ |
|
return GetToken (crossline); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
/* |
|
============== |
|
GetExprToken - use C mathematical operator parsing rules to split tokens instead of whitespace |
|
============== |
|
*/ |
|
qboolean GetExprToken (qboolean crossline) |
|
{ |
|
char *token_p; |
|
|
|
if (tokenready) // is a token allready waiting? |
|
{ |
|
tokenready = false; |
|
return true; |
|
} |
|
|
|
if (script->script_p >= script->end_p) |
|
return EndOfScript (crossline); |
|
|
|
tokenready = false; |
|
|
|
// |
|
// skip space |
|
// |
|
skipspace: |
|
while (*script->script_p <= 32) |
|
{ |
|
if (script->script_p >= script->end_p) |
|
return EndOfScript (crossline); |
|
if (*script->script_p++ == '\n') |
|
{ |
|
if (!crossline) |
|
Error ("Line %i is incomplete\n",scriptline); |
|
scriptline = ++script->line; |
|
} |
|
} |
|
|
|
if (script->script_p >= script->end_p) |
|
return EndOfScript (crossline); |
|
|
|
if (*script->script_p == ';' || *script->script_p == '#' || // semicolon and # is comment field |
|
(*script->script_p == '/' && *((script->script_p)+1) == '/')) // also make // a comment field |
|
{ |
|
if (!crossline) |
|
Error ("Line %i is incomplete\n",scriptline); |
|
while (*script->script_p++ != '\n') |
|
if (script->script_p >= script->end_p) |
|
return EndOfScript (crossline); |
|
goto skipspace; |
|
} |
|
|
|
// |
|
// copy token |
|
// |
|
token_p = token; |
|
|
|
if (*script->script_p == '"') |
|
{ |
|
// quoted token |
|
script->script_p++; |
|
while (*script->script_p != '"') |
|
{ |
|
*token_p++ = *script->script_p++; |
|
if (script->script_p == script->end_p) |
|
break; |
|
if (token_p == &token[MAXTOKEN]) |
|
Error ("Token too large on line %i\n",scriptline); |
|
} |
|
script->script_p++; |
|
} |
|
else |
|
{ |
|
if ( V_isalpha( *script->script_p ) || *script->script_p == '_' ) |
|
{ |
|
// regular token |
|
while ( V_isalnum( *script->script_p ) || *script->script_p == '_' ) |
|
{ |
|
*token_p++ = *script->script_p++; |
|
if (script->script_p == script->end_p) |
|
break; |
|
if (token_p == &token[MAXTOKEN]) |
|
Error ("Token too large on line %i\n",scriptline); |
|
} |
|
} |
|
else if ( V_isdigit( *script->script_p ) || *script->script_p == '.' ) |
|
{ |
|
// regular token |
|
while ( V_isdigit( *script->script_p ) || *script->script_p == '.' ) |
|
{ |
|
*token_p++ = *script->script_p++; |
|
if (script->script_p == script->end_p) |
|
break; |
|
if (token_p == &token[MAXTOKEN]) |
|
Error ("Token too large on line %i\n",scriptline); |
|
} |
|
} |
|
else |
|
{ |
|
// single char |
|
*token_p++ = *script->script_p++; |
|
} |
|
} |
|
|
|
*token_p = 0; |
|
|
|
if (!stricmp (token, "$include")) |
|
{ |
|
GetToken (false); |
|
AddScriptToStack (token); |
|
return GetToken (crossline); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
|
|
/* |
|
============== |
|
TokenAvailable |
|
|
|
Returns true if there is another token on the line |
|
============== |
|
*/ |
|
qboolean TokenAvailable (void) |
|
{ |
|
char *search_p; |
|
|
|
if (tokenready) // is a token allready waiting? |
|
{ |
|
return true; |
|
} |
|
|
|
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 == ';' || *search_p == '#' || // semicolon and # is comment field |
|
(*search_p == '/' && *((search_p)+1) == '/')) // also make // a comment field |
|
return false; |
|
|
|
return true; |
|
} |
|
|
|
qboolean GetTokenizerStatus( char **pFilename, int *pLine ) |
|
{ |
|
// is this the default state? |
|
if (!script) |
|
return false; |
|
|
|
if (script->script_p >= script->end_p) |
|
return false; |
|
|
|
if (pFilename) |
|
{ |
|
*pFilename = script->filename; |
|
} |
|
if (pLine) |
|
{ |
|
*pLine = script->line; |
|
} |
|
return true; |
|
} |
|
|
|
|
|
#include <stdio.h> |
|
#include <stdlib.h> |
|
#ifdef WIN32 |
|
#include <direct.h> |
|
#include <io.h> |
|
#include <sys/utime.h> |
|
#endif |
|
#include <time.h> |
|
#include <fcntl.h> |
|
#include <sys/stat.h> |
|
#include <sys/types.h> |
|
#include "tier1/utlbuffer.h" |
|
|
|
class CScriptLib : public IScriptLib |
|
{ |
|
public: |
|
virtual bool ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText = false, bool bNoOpenFailureWarning = false ); |
|
virtual bool WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ); |
|
virtual int FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList ); |
|
virtual char *MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ); |
|
virtual void DeleteTemporaryFiles( const char *pFileMask ); |
|
virtual int CompareFileTime( const char *pFilenameA, const char *pFilenameB ); |
|
virtual bool DoesFileExist( const char *pFilename ); |
|
|
|
private: |
|
|
|
int GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList ); |
|
void RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList ); |
|
}; |
|
|
|
static CScriptLib g_ScriptLib; |
|
IScriptLib *scriptlib = &g_ScriptLib; |
|
IScriptLib *g_pScriptLib = &g_ScriptLib; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Existence check |
|
//----------------------------------------------------------------------------- |
|
bool CScriptLib::DoesFileExist( const char *pFilename ) |
|
{ |
|
return g_pFullFileSystem->FileExists( pFilename ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Helper utility, read file into buffer |
|
//----------------------------------------------------------------------------- |
|
bool CScriptLib::ReadFileToBuffer( const char *pSourceName, CUtlBuffer &buffer, bool bText, bool bNoOpenFailureWarning ) |
|
{ |
|
bool bSuccess = true; |
|
|
|
if ( !g_pFullFileSystem->ReadFile( pSourceName, NULL, buffer ) ) |
|
{ |
|
if ( !bNoOpenFailureWarning ) |
|
{ |
|
Msg( "ReadFileToBuffer(): Error opening %s: %s\n", pSourceName, strerror( errno ) ); |
|
} |
|
return false; |
|
} |
|
|
|
if ( bText ) |
|
{ |
|
// force it into text mode |
|
buffer.SetBufferType( true, true ); |
|
} |
|
else |
|
{ |
|
buffer.SetBufferType( false, false ); |
|
} |
|
|
|
return bSuccess; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Helper utility, Write buffer to file |
|
//----------------------------------------------------------------------------- |
|
bool CScriptLib::WriteBufferToFile( const char *pTargetName, CUtlBuffer &buffer, DiskWriteMode_t writeMode ) |
|
{ |
|
char* ptr; |
|
char dirPath[MAX_PATH]; |
|
|
|
bool bSuccess = true; |
|
|
|
// create path |
|
// prime and skip to first seperator |
|
strcpy( dirPath, pTargetName ); |
|
ptr = strchr( dirPath, '\\' ); |
|
while ( ptr ) |
|
{ |
|
ptr = strchr( ptr+1, '\\' ); |
|
if ( ptr ) |
|
{ |
|
*ptr = '\0'; |
|
_mkdir( dirPath ); |
|
*ptr = '\\'; |
|
} |
|
} |
|
|
|
bool bDoWrite = false; |
|
if ( writeMode == WRITE_TO_DISK_ALWAYS ) |
|
{ |
|
bDoWrite = true; |
|
} |
|
else if ( writeMode == WRITE_TO_DISK_UPDATE ) |
|
{ |
|
if ( DoesFileExist( pTargetName ) ) |
|
{ |
|
bDoWrite = true; |
|
} |
|
} |
|
|
|
if ( bDoWrite ) |
|
{ |
|
bSuccess = g_pFullFileSystem->WriteFile( pTargetName, NULL, buffer ); |
|
} |
|
|
|
return bSuccess; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Returns -1, 0, or 1. |
|
//----------------------------------------------------------------------------- |
|
int CScriptLib::CompareFileTime( const char *pFilenameA, const char *pFilenameB ) |
|
{ |
|
int timeA = g_pFullFileSystem->GetFileTime( (char *)pFilenameA ); |
|
int timeB = g_pFullFileSystem->GetFileTime( (char *)pFilenameB ); |
|
|
|
if ( timeA == -1) |
|
{ |
|
// file a not exist |
|
timeA = 0; |
|
} |
|
if ( timeB == -1 ) |
|
{ |
|
// file b not exist |
|
timeB = 0; |
|
} |
|
|
|
if ( (unsigned int)timeA < (unsigned int)timeB ) |
|
{ |
|
return -1; |
|
} |
|
else if ( (unsigned int)timeA > (unsigned int)timeB ) |
|
{ |
|
return 1; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Make a temporary filename |
|
//----------------------------------------------------------------------------- |
|
char *CScriptLib::MakeTemporaryFilename( char const *pchModPath, char *pPath, int pathSize ) |
|
{ |
|
char *pBuffer = _tempnam( pchModPath, "mgd_" ); |
|
if ( pBuffer[0] == '\\' ) |
|
{ |
|
pBuffer++; |
|
} |
|
if ( pBuffer[strlen( pBuffer )-1] == '.' ) |
|
{ |
|
pBuffer[strlen( pBuffer )-1] = '\0'; |
|
} |
|
V_snprintf( pPath, pathSize, "%s.tmp", pBuffer ); |
|
|
|
free( pBuffer ); |
|
|
|
return pPath; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Delete temporary files |
|
//----------------------------------------------------------------------------- |
|
void CScriptLib::DeleteTemporaryFiles( const char *pFileMask ) |
|
{ |
|
#if !defined( _X360 ) |
|
const char *pEnv = getenv( "temp" ); |
|
if ( !pEnv ) |
|
{ |
|
pEnv = getenv( "tmp" ); |
|
} |
|
|
|
if ( pEnv ) |
|
{ |
|
char tempPath[MAX_PATH]; |
|
strcpy( tempPath, pEnv ); |
|
V_AppendSlash( tempPath, sizeof( tempPath ) ); |
|
strcat( tempPath, pFileMask ); |
|
|
|
CUtlVector<fileList_t> fileList; |
|
FindFiles( tempPath, false, fileList ); |
|
for ( int i=0; i<fileList.Count(); i++ ) |
|
{ |
|
_unlink( fileList[i].fileName.String() ); |
|
} |
|
} |
|
#else |
|
AssertOnce( !"CScriptLib::DeleteTemporaryFiles: Not avail on 360\n" ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Get list of files from current path that match pattern |
|
//----------------------------------------------------------------------------- |
|
int CScriptLib::GetFileList( const char* pDirPath, const char* pPattern, CUtlVector< fileList_t > &fileList ) |
|
{ |
|
char sourcePath[MAX_PATH]; |
|
char fullPath[MAX_PATH]; |
|
bool bFindDirs; |
|
|
|
fileList.Purge(); |
|
|
|
strcpy( sourcePath, pDirPath ); |
|
int len = (int)strlen( sourcePath ); |
|
if ( !len ) |
|
{ |
|
strcpy( sourcePath, ".\\" ); |
|
} |
|
else if ( sourcePath[len-1] != '\\' ) |
|
{ |
|
sourcePath[len] = '\\'; |
|
sourcePath[len+1] = '\0'; |
|
} |
|
|
|
strcpy( fullPath, sourcePath ); |
|
if ( pPattern[0] == '\\' && pPattern[1] == '\0' ) |
|
{ |
|
// find directories only |
|
bFindDirs = true; |
|
strcat( fullPath, "*" ); |
|
} |
|
else |
|
{ |
|
// find files, use provided pattern |
|
bFindDirs = false; |
|
strcat( fullPath, pPattern ); |
|
} |
|
|
|
#ifdef WIN32 |
|
struct _finddata_t findData; |
|
intptr_t h = _findfirst( fullPath, &findData ); |
|
if ( h == -1 ) |
|
{ |
|
return 0; |
|
} |
|
|
|
do |
|
{ |
|
// dos attribute complexities i.e. _A_NORMAL is 0 |
|
if ( bFindDirs ) |
|
{ |
|
// skip non dirs |
|
if ( !( findData.attrib & _A_SUBDIR ) ) |
|
continue; |
|
} |
|
else |
|
{ |
|
// skip dirs |
|
if ( findData.attrib & _A_SUBDIR ) |
|
continue; |
|
} |
|
|
|
if ( !stricmp( findData.name, "." ) ) |
|
continue; |
|
|
|
if ( !stricmp( findData.name, ".." ) ) |
|
continue; |
|
|
|
char fileName[MAX_PATH]; |
|
strcpy( fileName, sourcePath ); |
|
strcat( fileName, findData.name ); |
|
|
|
int j = fileList.AddToTail(); |
|
fileList[j].fileName.Set( fileName ); |
|
fileList[j].timeWrite = findData.time_write; |
|
} |
|
while ( !_findnext( h, &findData ) ); |
|
|
|
_findclose( h ); |
|
#elif defined(POSIX) |
|
FIND_DATA findData; |
|
Q_FixSlashes( fullPath ); |
|
void *h = FindFirstFile( fullPath, &findData ); |
|
if ( (int)h == -1 ) |
|
{ |
|
return 0; |
|
} |
|
|
|
do |
|
{ |
|
// dos attribute complexities i.e. _A_NORMAL is 0 |
|
if ( bFindDirs ) |
|
{ |
|
// skip non dirs |
|
if ( !( findData.dwFileAttributes & S_IFDIR ) ) |
|
continue; |
|
} |
|
else |
|
{ |
|
// skip dirs |
|
if ( findData.dwFileAttributes & S_IFDIR ) |
|
continue; |
|
} |
|
|
|
if ( !stricmp( findData.cFileName, "." ) ) |
|
continue; |
|
|
|
if ( !stricmp( findData.cFileName, ".." ) ) |
|
continue; |
|
|
|
char fileName[MAX_PATH]; |
|
strcpy( fileName, sourcePath ); |
|
strcat( fileName, findData.cFileName ); |
|
|
|
int j = fileList.AddToTail(); |
|
fileList[j].fileName.Set( fileName ); |
|
struct stat statbuf; |
|
if ( stat( fileName, &statbuf ) ) |
|
#ifdef OSX |
|
fileList[j].timeWrite = statbuf.st_mtimespec.tv_sec; |
|
#else |
|
fileList[j].timeWrite = statbuf.st_mtime; |
|
#endif |
|
else |
|
fileList[j].timeWrite = 0; |
|
} |
|
while ( !FindNextFile( h, &findData ) ); |
|
|
|
FindClose( h ); |
|
|
|
#else |
|
#error |
|
#endif |
|
|
|
|
|
return fileList.Count(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Recursively determine directory tree |
|
//----------------------------------------------------------------------------- |
|
void CScriptLib::RecurseFileTree_r( const char* pDirPath, int depth, CUtlVector< CUtlString > &dirList ) |
|
{ |
|
// recurse from source directory, get directories only |
|
CUtlVector< fileList_t > fileList; |
|
int dirCount = GetFileList( pDirPath, "\\", fileList ); |
|
if ( !dirCount ) |
|
{ |
|
// add directory name to search tree |
|
int j = dirList.AddToTail(); |
|
dirList[j].Set( pDirPath ); |
|
return; |
|
} |
|
|
|
for ( int i=0; i<dirCount; i++ ) |
|
{ |
|
// form new path name, recurse into |
|
RecurseFileTree_r( fileList[i].fileName.String(), depth+1, dirList ); |
|
} |
|
|
|
int j = dirList.AddToTail(); |
|
dirList[j].Set( pDirPath ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Generate a list of file matching mask |
|
//----------------------------------------------------------------------------- |
|
int CScriptLib::FindFiles( char* pFileMask, bool bRecurse, CUtlVector<fileList_t> &fileList ) |
|
{ |
|
char dirPath[MAX_PATH]; |
|
char pattern[MAX_PATH]; |
|
char extension[MAX_PATH]; |
|
|
|
// get path only |
|
strcpy( dirPath, pFileMask ); |
|
V_StripFilename( dirPath ); |
|
|
|
// get pattern only |
|
V_FileBase( pFileMask, pattern, sizeof( pattern ) ); |
|
V_ExtractFileExtension( pFileMask, extension, sizeof( extension ) ); |
|
if ( extension[0] ) |
|
{ |
|
strcat( pattern, "." ); |
|
strcat( pattern, extension ); |
|
} |
|
|
|
if ( !bRecurse ) |
|
{ |
|
GetFileList( dirPath, pattern, fileList ); |
|
} |
|
else |
|
{ |
|
// recurse and get the tree |
|
CUtlVector< fileList_t > tempList; |
|
CUtlVector< CUtlString > dirList; |
|
RecurseFileTree_r( dirPath, 0, dirList ); |
|
for ( int i=0; i<dirList.Count(); i++ ) |
|
{ |
|
// iterate each directory found |
|
tempList.Purge(); |
|
tempList.EnsureCapacity( dirList.Count() ); |
|
|
|
GetFileList( dirList[i].String(), pattern, tempList ); |
|
|
|
int start = fileList.AddMultipleToTail( tempList.Count() ); |
|
for ( int j=0; j<tempList.Count(); j++ ) |
|
{ |
|
fileList[start+j] = tempList[j]; |
|
} |
|
} |
|
} |
|
|
|
return fileList.Count(); |
|
}
|
|
|