//========= 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 #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 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 #include #ifdef WIN32 #include #include #include #endif #include #include #include #include #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 ); 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; FindFiles( tempPath, false, fileList ); for ( int i=0; i &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 &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