1350 lines
29 KiB
C++
1350 lines
29 KiB
C++
//========= 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();
|
|
}
|