| //========= Copyright Valve Corporation ============// |
| #include <vrcore/dirtools_public.h> |
| #include <vrcore/strtools_public.h> |
| #include <vrcore/pathtools_public.h> |
| |
| #include <errno.h> |
| #include <string.h> |
| |
| #ifdef _WIN32 |
| #include "windows.h" |
| #else |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #endif |
| |
| #if defined( OSX ) |
| #include <sys/syslimits.h> |
| #endif |
| |
| #define HMD_INVALID_HANDLE_VALUE ( ( HMDHANDLE )( LONG_PTR )-1 ) |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: utility function to create dirs & subdirs |
| //----------------------------------------------------------------------------- |
| bool BCreateDirectoryRecursive( const char *pchPath ) |
| { |
| // Does it already exist? |
| if ( Path_IsDirectory( pchPath ) ) |
| return true; |
| |
| // copy the path into something we can munge |
| int len = (int)strlen( pchPath ); |
| char *path = (char *)malloc( len + 1 ); |
| strcpy( path, pchPath ); |
| |
| // Walk backwards to first non-existing dir that we find |
| char *s = path + len - 1; |
| |
| const char slash = Path_GetSlash(); |
| while ( s > path ) |
| { |
| if ( *s == slash ) |
| { |
| *s = '\0'; |
| bool bExists = Path_IsDirectory( path ); |
| *s = slash; |
| |
| if ( bExists ) |
| { |
| ++s; |
| break; |
| } |
| } |
| --s; |
| } |
| |
| // and then move forwards from there |
| |
| while ( *s ) |
| { |
| if ( *s == slash ) |
| { |
| *s = '\0'; |
| BCreateDirectory( path ); |
| *s = slash; |
| } |
| s++; |
| } |
| |
| bool bRetVal = BCreateDirectory( path ); |
| free( path ); |
| return bRetVal; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Creates the directory, returning true if it is created, or if it already existed |
| //----------------------------------------------------------------------------- |
| bool BCreateDirectory( const char *pchPath ) |
| { |
| #ifdef WIN32 |
| std::wstring wPath = UTF8to16( pchPath ); |
| if ( ::CreateDirectoryW( wPath.c_str(), NULL ) ) |
| return true; |
| |
| if ( ::GetLastError() == ERROR_ALREADY_EXISTS ) |
| return true; |
| |
| return false; |
| #else |
| int i = mkdir( pchPath, S_IRWXU | S_IRWXG | S_IRWXO ); |
| if ( i == 0 ) |
| return true; |
| if ( errno == EEXIST ) |
| return true; |
| |
| return false; |
| #endif |
| } |
| |
| #if !defined( VRCORE_NO_PLATFORM ) |
| |
| #include <vrcore/log.h> |
| |
| //----------------------------------------------------------------------------- |
| CDirIterator::CDirIterator( const char *pchPath, const char *pchPattern ) |
| { |
| m_pFindData = NULL; |
| |
| // put in the path |
| if ( pchPath ) |
| { |
| std::string sPathAndPattern = Path_Join( pchPath, pchPattern ); |
| |
| Init( sPathAndPattern.c_str() ); |
| } |
| else |
| { |
| m_bNoFiles = true; |
| m_bUsedFirstFile = true; |
| |
| #if defined( _WIN32 ) |
| m_hFind = HMD_INVALID_HANDLE_VALUE; |
| m_pFindData = new WIN32_FIND_DATAW; |
| #else |
| m_hFind = -1; |
| m_pFindData = new _finddata_t; |
| #endif |
| memset( m_pFindData, 0, sizeof( *m_pFindData ) ); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Initialize iteration structure |
| //----------------------------------------------------------------------------- |
| void CDirIterator::Init( const std::string &sPathAndPattern ) |
| { |
| #if defined( _WIN32 ) |
| m_pFindData = new WIN32_FIND_DATAW; |
| memset( m_pFindData, 0, sizeof( *m_pFindData ) ); |
| |
| std::wstring sWPathAndPattern = UTF8to16( sPathAndPattern.c_str() ); |
| |
| m_hFind = FindFirstFileW( sWPathAndPattern.c_str(), m_pFindData ); |
| bool bSuccess = ( m_hFind != HMD_INVALID_HANDLE_VALUE ); |
| // Conversion should never fail with valid filenames... |
| if ( bSuccess ) |
| { |
| m_sFilename = UTF16to8( m_pFindData->cFileName ); |
| } |
| #else |
| m_pFindData = new _finddata_t; |
| memset( m_pFindData, 0, sizeof( *m_pFindData ) ); |
| |
| m_hFind = _findfirst( sPathAndPattern.c_str(), m_pFindData ); |
| bool bSuccess = ( m_hFind != -1 ); |
| #endif |
| |
| if ( !bSuccess ) |
| { |
| m_bNoFiles = true; |
| m_bUsedFirstFile = true; |
| } |
| else |
| { |
| m_bNoFiles = false; |
| // if we're pointing at . or .., set it as used |
| // so we'll look for the next item when BNextFile() is called |
| m_bUsedFirstFile = !BValidFilename(); |
| } |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Destructor |
| //----------------------------------------------------------------------------- |
| CDirIterator::~CDirIterator() |
| { |
| #if defined( _WIN32 ) |
| if ( m_hFind != HMD_INVALID_HANDLE_VALUE ) |
| { |
| FindClose( m_hFind ); |
| } |
| delete m_pFindData; |
| #else |
| if ( m_hFind != -1 ) |
| { |
| _findclose( m_hFind ); |
| } |
| if ( m_pFindData ) |
| { |
| for ( int i = 0; i < m_pFindData->numNames; i++ ) |
| { |
| // scandir allocates with malloc, so free with free |
| free( m_pFindData->namelist[ i ] ); |
| } |
| free( m_pFindData->namelist ); |
| delete m_pFindData; |
| } |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Check for successful construction |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::IsValid() const |
| { |
| #if defined( _WIN32 ) |
| return m_hFind != HMD_INVALID_HANDLE_VALUE; |
| #else |
| return m_hFind != -1; |
| #endif |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Filter out . and .. |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::BValidFilename() |
| { |
| #if defined( _WIN32 ) |
| const char *pch = m_sFilename.c_str(); |
| #else |
| const char *pch = m_pFindData->name; |
| #endif |
| |
| if ( ( pch[ 0 ] == '.' && pch[ 1 ] == 0 ) || ( pch[ 0 ] == '.' && pch[ 1 ] == '.' && pch[ 2 ] == 0 ) ) |
| return false; |
| |
| return true; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns true if there is a file to read |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::BNextFile() |
| { |
| if ( m_bNoFiles ) |
| return false; |
| |
| // use the first result |
| if ( !m_bUsedFirstFile ) |
| { |
| m_bUsedFirstFile = true; |
| return true; |
| } |
| |
| // find the next item |
| for ( ;; ) |
| { |
| #if defined( _WIN32 ) |
| bool bFound = ( FindNextFileW( m_hFind, m_pFindData ) != FALSE ); |
| // Conversion should never fail with valid filenames... |
| if ( bFound ) |
| { |
| m_sFilename = UTF16to8( m_pFindData->cFileName ); |
| } |
| #else |
| bool bFound = ( _findnext( m_hFind, m_pFindData ) == 0 ); |
| #endif |
| |
| if ( !bFound ) |
| { |
| // done |
| m_bNoFiles = true; |
| return false; |
| } |
| |
| // skip over the '.' and '..' paths |
| if ( !BValidFilename() ) |
| continue; |
| |
| break; |
| } |
| |
| // have one more file |
| return true; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns name (filename portion only) of the current file. |
| // Name is emitted in UTF-8 encoding. |
| // NOTE: This method returns a pointer into a static buffer, either a member |
| // or the buffer inside the _finddata_t. |
| //----------------------------------------------------------------------------- |
| std::string CDirIterator::CurrentFileName() |
| { |
| #if defined( _WIN32 ) |
| return m_sFilename; |
| #else |
| return m_pFindData->name; |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns size of the file |
| //----------------------------------------------------------------------------- |
| int64_t CDirIterator::CurrentFileLength() const |
| { |
| #if defined( _WIN32 ) |
| LARGE_INTEGER li = { { m_pFindData->nFileSizeLow, ( LONG )m_pFindData->nFileSizeHigh } }; |
| return li.QuadPart; |
| #else |
| return ( int64_t )m_pFindData->size; |
| #endif |
| } |
| |
| #if defined( _WIN32 ) |
| //----------------------------------------------------------------------------- |
| // Purpose: utility for converting a regular time to a system filetime |
| //----------------------------------------------------------------------------- |
| FILETIME UnixTimeToFileTime( int64_t t ) |
| { |
| FILETIME retf; |
| long long int ll; |
| ll = ( ( long long )t * 10000000LL ); |
| ll += 116444736000000000LL; |
| retf.dwHighDateTime = ll >> 32; |
| retf.dwLowDateTime = ll & 0x00000000FFFFFFFF; |
| return retf; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: utility for converting a system filetime to a regular time |
| //----------------------------------------------------------------------------- |
| int64_t FileTimeToUnixTime( FILETIME filetime ) |
| { |
| long long int t = filetime.dwHighDateTime; |
| t <<= 32; |
| t += ( unsigned long )filetime.dwLowDateTime; |
| t -= 116444736000000000LL; |
| return t / 10000000; |
| } |
| #endif // _WIN32 |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns last write time of the file |
| //----------------------------------------------------------------------------- |
| int64_t CDirIterator::CurrentFileWriteTime() const |
| { |
| #if defined( _WIN32 ) |
| return FileTimeToUnixTime( m_pFindData->ftLastWriteTime ); |
| #else |
| return m_pFindData->time_write; |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns the creation time of the file |
| //----------------------------------------------------------------------------- |
| int64_t CDirIterator::CurrentFileCreateTime() const |
| { |
| #if defined( _WIN32 ) |
| return FileTimeToUnixTime( m_pFindData->ftCreationTime ); |
| #else |
| return m_pFindData->time_create; |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns whether current item under examination is a directory |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::BCurrentIsDir() const |
| { |
| #if defined( _WIN32 ) |
| return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0; |
| #else |
| return ( m_pFindData->attrib & _A_SUBDIR ? true : false ); |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns whether current item under examination is a hidden file |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::BCurrentIsHidden() const |
| { |
| #if defined( _WIN32 ) |
| return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_HIDDEN ) != 0; |
| #else |
| return ( m_pFindData->attrib & _A_HIDDEN ? true : false ); |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns whether current item under examination is read-only |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::BCurrentIsReadOnly() const |
| { |
| #if defined( _WIN32 ) |
| return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_READONLY ) != 0; |
| #else |
| return ( m_pFindData->attrib & _A_RDONLY ? true : false ); |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns whether current item under examination is marked as a system file |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::BCurrentIsSystem() const |
| { |
| #if defined( _WIN32 ) |
| return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_SYSTEM ) != 0; |
| #else |
| return ( m_pFindData->attrib & _A_SYSTEM ? true : false ); |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: returns whether current item under examination is marked for archiving |
| //----------------------------------------------------------------------------- |
| bool CDirIterator::BCurrentIsMarkedForArchive() const |
| { |
| #if defined( _WIN32 ) |
| return ( m_pFindData->dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE ) != 0; |
| #else |
| return ( m_pFindData->attrib & _A_ARCH ? true : false ); |
| #endif |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // |
| // A huge pile of stuff from fileio.cpp in steam/tier1 |
| // |
| //----------------------------------------------------------------------------- |
| |
| #if !defined( _WIN32 ) |
| // findfirst/findnext implementation from filesystem/linux_support.[h|cpp] |
| |
| static char selectBuf[ PATH_MAX ]; |
| |
| #if defined( OSX ) && !defined( __MAC_10_8 ) |
| static int FileSelect( direntBig_t *ent ) |
| #elif defined( LINUX ) || defined( OSX ) |
| static int FileSelect( const direntBig_t *ent ) |
| #else |
| #error |
| #endif |
| { |
| const char *mask = selectBuf; |
| const char *name = ent->d_name; |
| |
| return FileSelect( name, mask ); |
| } |
| |
| static int FileSelect( const char *name, const char *mask ) |
| { |
| // printf("Test:%s %s\n",mask,name); |
| |
| if ( !strcmp( name, "." ) || !strcmp( name, ".." ) ) |
| return 0; |
| |
| if ( !strcmp( mask, "*.*" ) || !strcmp( mask, "*" ) ) |
| return 1; |
| |
| while ( *mask && *name ) |
| { |
| if ( *mask == '*' ) |
| { |
| mask++; // move to the next char in the mask |
| if ( !*mask ) // if this is the end of the mask its a match |
| { |
| return 1; |
| } |
| while ( *name && toupper( *name ) != toupper( *mask ) ) |
| { // while the two don't meet up again |
| name++; |
| } |
| if ( !*name ) |
| { // end of the name |
| break; |
| } |
| } |
| else if ( *mask != '?' ) |
| { |
| if ( toupper( *mask ) != toupper( *name ) ) |
| { // mismatched! |
| return 0; |
| } |
| else |
| { |
| mask++; |
| name++; |
| if ( !*mask && !*name ) |
| { // if its at the end of the buffer |
| return 1; |
| } |
| } |
| } |
| else /* mask is "?", we don't care*/ |
| { |
| mask++; |
| name++; |
| } |
| } |
| |
| return ( !*mask && !*name ); // both of the strings are at the end |
| } |
| |
| int FillDataStruct( _finddata_t *dat ) |
| { |
| statBig_t fileStat; |
| |
| if ( dat->curName >= dat->numNames ) |
| return -1; |
| |
| strncpy( dat->name, dat->namelist[ dat->curName ]->d_name, sizeof( dat->name ) ); |
| char szFullPath[ MAX_PATH ]; |
| int nWriteSize = snprintf( szFullPath, sizeof( szFullPath ), "%s%c%s", dat->dirBase, Path_GetSlash(), dat->name ); |
| if ( nWriteSize >= sizeof( szFullPath ) ) |
| { |
| Log( LogError, "File path truncated\n" ); |
| } |
| if ( statBig( szFullPath, &fileStat ) == 0 ) |
| { |
| dat->attrib = fileStat.st_mode; |
| dat->size = fileStat.st_size; |
| dat->time_write = fileStat.st_mtime; |
| dat->time_create = fileStat.st_ctime; |
| } |
| else |
| { |
| dat->attrib = 0; |
| dat->size = 0; |
| dat->time_write = 0; |
| dat->time_create = 0; |
| } |
| free( dat->namelist[ dat->curName ] ); |
| dat->namelist[ dat->curName ] = NULL; |
| dat->curName++; |
| return 1; |
| } |
| |
| int _findfirst( const char *fileName, _finddata_t *dat ) |
| { |
| char nameStore[ MAX_PATH ]; |
| char *dir = NULL; |
| int n, iret = -1; |
| |
| strncpy( nameStore, fileName, sizeof( nameStore ) ); |
| |
| if ( strrchr( nameStore, '/' ) ) |
| { |
| dir = nameStore; |
| while ( strrchr( dir, '/' ) ) |
| { |
| statBig_t dirChk; |
| |
| // zero this with the dir name |
| dir = strrchr( nameStore, '/' ); |
| *dir = '\0'; |
| if ( dir == nameStore ) |
| { |
| strcpy( nameStore, "/" ); // C++11 would prefer no `dir = "/";` |
| } |
| else |
| { |
| dir = nameStore; |
| } |
| |
| if ( statBig( dir, &dirChk ) == 0 && S_ISDIR( dirChk.st_mode ) ) |
| { |
| break; |
| } |
| } |
| } |
| else |
| { |
| // couldn't find a dir separator... |
| return -1; |
| } |
| |
| if ( strlen( dir ) > 0 ) |
| { |
| if ( strlen( dir ) == 1 ) |
| strncpy( selectBuf, fileName + 1, sizeof( selectBuf ) ); |
| else |
| strncpy( selectBuf, fileName + strlen( dir ) + 1, sizeof( selectBuf ) ); |
| |
| n = scandirBig( dir, &dat->namelist, FileSelect, alphasortBig ); |
| if ( n < 0 ) |
| { |
| // silently return, nothing interesting |
| } |
| else |
| { |
| dat->curName = 0; |
| dat->numNames = n; // n is the number of matches |
| strncpy( dat->dirBase, dir, sizeof( dat->dirBase ) ); |
| iret = FillDataStruct( dat ); |
| if ( iret < 0 ) |
| { |
| free( dat->namelist ); |
| dat->namelist = NULL; |
| dat->curName = 0; |
| dat->numNames = 0; |
| } |
| } |
| } |
| |
| // printf("Returning: %i \n",iret); |
| return iret; |
| } |
| |
| int _findnext( int64_t handle, _finddata_t *dat ) |
| { |
| if ( dat->curName >= dat->numNames ) |
| { |
| free( dat->namelist ); |
| dat->namelist = NULL; |
| dat->curName = 0; |
| dat->numNames = 0; |
| return -1; // no matches left |
| } |
| |
| FillDataStruct( dat ); |
| return 0; |
| } |
| |
| bool _findclose( int64_t handle ) |
| { |
| return true; |
| } |
| |
| #endif // !defined( _WIN32 ) |
| |
| #endif // VRCORE_NO_PLATFORM |