mirror of
https://github.com/tribes2/engine.git
synced 2026-01-20 03:34:48 +00:00
854 lines
17 KiB
C++
854 lines
17 KiB
C++
|
|
//-----------------------------------------------------------------------------
|
||
|
|
// V12 Engine
|
||
|
|
//
|
||
|
|
// Copyright (c) 2001 GarageGames.Com
|
||
|
|
// Portions Copyright (c) 2001 by Sierra Online, Inc.
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
|
||
|
|
#include <errno.h>
|
||
|
|
#include <unistd.h>
|
||
|
|
#include <sys/stat.h>
|
||
|
|
#include <fcntl.h>
|
||
|
|
#include <dirent.h>
|
||
|
|
#include <regex.h>
|
||
|
|
#include <string.h>
|
||
|
|
#include <stdlib.h>
|
||
|
|
#include <assert.h>
|
||
|
|
|
||
|
|
#include "loki_utils.h"
|
||
|
|
|
||
|
|
#include "engine/platformLinux/platformLinux.h"
|
||
|
|
#include "engine/core/fileio.h"
|
||
|
|
#include "engine/core/tVector.h"
|
||
|
|
#include "engine/core/stringTable.h"
|
||
|
|
#include "engine/console/console.h"
|
||
|
|
|
||
|
|
static void build_canonical_name( const char* path, char* buffer, int size )
|
||
|
|
{
|
||
|
|
struct stat sb;
|
||
|
|
|
||
|
|
/* default value is identity */
|
||
|
|
strncpy( buffer, path, size );
|
||
|
|
buffer[size-1] = '\0';
|
||
|
|
|
||
|
|
/* Shortcut, most of the time the file name is fine */
|
||
|
|
if ( stat(buffer, &sb) != 0 ) {
|
||
|
|
char* r;
|
||
|
|
char *pathname;
|
||
|
|
char *filename;
|
||
|
|
DIR* dir;
|
||
|
|
struct dirent* de;
|
||
|
|
|
||
|
|
/* find the last slash */
|
||
|
|
pathname = buffer;
|
||
|
|
r = strrchr( pathname, '/' );
|
||
|
|
if( r ) {
|
||
|
|
*r = '\0';
|
||
|
|
filename = r+1;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
* Look for an existing name.
|
||
|
|
*/
|
||
|
|
dir = opendir( pathname );
|
||
|
|
if( dir ) {
|
||
|
|
while( ( de = readdir( dir ) ) != NULL ) {
|
||
|
|
|
||
|
|
if( strcasecmp( de->d_name, filename ) == 0 ) {
|
||
|
|
strncpy(filename, de->d_name, size-(strlen(pathname)+1));
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
closedir( dir );
|
||
|
|
}
|
||
|
|
if ( r ) {
|
||
|
|
*r = '/';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int iopen( const char* path, int flags )
|
||
|
|
{
|
||
|
|
char buffer[PATH_MAX];
|
||
|
|
int fd = -1;
|
||
|
|
|
||
|
|
build_canonical_name( path, buffer, PATH_MAX );
|
||
|
|
fd = open( buffer, flags );
|
||
|
|
return fd;
|
||
|
|
}
|
||
|
|
|
||
|
|
FILE* ifopen( const char* path, const char* mode )
|
||
|
|
{
|
||
|
|
char buffer[PATH_MAX];
|
||
|
|
FILE* f = NULL;
|
||
|
|
|
||
|
|
build_canonical_name( path, buffer, PATH_MAX );
|
||
|
|
f = fopen( buffer, mode );
|
||
|
|
return f;
|
||
|
|
}
|
||
|
|
|
||
|
|
static void forwardSlash( char* s )
|
||
|
|
{
|
||
|
|
while( *s ) {
|
||
|
|
if( *s == '\\' ) {
|
||
|
|
*s = '/';
|
||
|
|
}
|
||
|
|
s++;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool copyFile(const char *src, const char *dst)
|
||
|
|
{
|
||
|
|
int status;
|
||
|
|
FILE *i, *o;
|
||
|
|
int len;
|
||
|
|
char data[BUFSIZ];
|
||
|
|
|
||
|
|
i = fopen(src, "rb");
|
||
|
|
o = fopen(dst, "wb");
|
||
|
|
if ( !i || !o ) {
|
||
|
|
if ( i ) {
|
||
|
|
fclose(i);
|
||
|
|
}
|
||
|
|
if ( o ) {
|
||
|
|
fclose(i);
|
||
|
|
}
|
||
|
|
return(0);
|
||
|
|
}
|
||
|
|
status = 0;
|
||
|
|
while ( (len = fread(data, 1, sizeof(data), i)) > 0 ) {
|
||
|
|
if ( fwrite(data, 1, len, o) != len ) {
|
||
|
|
status = -1;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
fclose(i);
|
||
|
|
if ( fclose(o) != 0 ) {
|
||
|
|
status = -1;
|
||
|
|
}
|
||
|
|
if ( status < 0 ) {
|
||
|
|
unlink(dst);
|
||
|
|
}
|
||
|
|
return(status == 0);
|
||
|
|
}
|
||
|
|
|
||
|
|
//-----------------------------------------------------------------------------
|
||
|
|
bool dFileDelete(const char * name)
|
||
|
|
{
|
||
|
|
bool status;
|
||
|
|
|
||
|
|
status = false;
|
||
|
|
if (name && !dStrstr(name, "../")) {
|
||
|
|
char canonical[PATH_MAX];
|
||
|
|
char path[PATH_MAX];
|
||
|
|
|
||
|
|
// We can only write and delete files in the prefs directory
|
||
|
|
dSprintf(canonical, sizeof(canonical), "%s/%s", loki_getprefpath(), name);
|
||
|
|
build_canonical_name( canonical, path, sizeof(path) );
|
||
|
|
if ( unlink(path) == 0 ) {
|
||
|
|
status = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
// change the modified time to the current time
|
||
|
|
bool dFileTouch(const char * name)
|
||
|
|
{
|
||
|
|
bool status;
|
||
|
|
|
||
|
|
status = false;
|
||
|
|
if (name && !dStrstr(name, "../")) {
|
||
|
|
char canonical[PATH_MAX];
|
||
|
|
char path[PATH_MAX];
|
||
|
|
|
||
|
|
// We can only modify files in the prefs directory
|
||
|
|
dSprintf(canonical, sizeof(canonical), "%s/%s", loki_getprefpath(), name);
|
||
|
|
build_canonical_name( canonical, path, sizeof(path) );
|
||
|
|
if ( utime(path, NULL) == 0 ) {
|
||
|
|
status = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
File::File( void ) : currentStatus( Closed ), capability( 0 )
|
||
|
|
{
|
||
|
|
handle = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
File::~File( void )
|
||
|
|
{
|
||
|
|
close( );
|
||
|
|
handle = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::open( const char* filename, const AccessMode mode )
|
||
|
|
{
|
||
|
|
char modded[PATH_MAX];
|
||
|
|
char canonical[PATH_MAX];
|
||
|
|
|
||
|
|
assert( filename );
|
||
|
|
|
||
|
|
dStrncpy( modded, filename, PATH_MAX );
|
||
|
|
forwardSlash( modded );
|
||
|
|
filename = modded;
|
||
|
|
|
||
|
|
// if it's not absolute already, put it in the user dir.
|
||
|
|
if( filename[0] != '/' ) {
|
||
|
|
dSprintf( canonical, PATH_MAX, "%s/%s", loki_getprefpath( ), filename );
|
||
|
|
} else {
|
||
|
|
dStrncpy( canonical, filename, PATH_MAX );
|
||
|
|
}
|
||
|
|
|
||
|
|
if( currentStatus != Closed ) {
|
||
|
|
close( );
|
||
|
|
}
|
||
|
|
|
||
|
|
switch( mode ) {
|
||
|
|
case Read:
|
||
|
|
#if 1
|
||
|
|
#warning FIXME for performance increase!
|
||
|
|
// FIXME:
|
||
|
|
// Cache the contents of the preferences directory
|
||
|
|
// so we don't have to keep searching for each file
|
||
|
|
// Can we use the output of dumpPath() in the game?
|
||
|
|
#endif
|
||
|
|
handle = ifopen( canonical, "r" );
|
||
|
|
|
||
|
|
if( !handle ) {
|
||
|
|
// if read-only we can try to open in the game dir itself
|
||
|
|
// because of the "if absolute" check above, it's possible
|
||
|
|
// that canonical == filename.
|
||
|
|
handle = ifopen( filename, "r" );
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case Write:
|
||
|
|
handle = ifopen( canonical, "w" );
|
||
|
|
break;
|
||
|
|
case ReadWrite:
|
||
|
|
handle = ifopen( canonical, "a+" );
|
||
|
|
|
||
|
|
// mimic win32 create on missing by seeking to begin
|
||
|
|
if( handle ) {
|
||
|
|
fseek( (FILE*) handle, 0, SEEK_SET );
|
||
|
|
}
|
||
|
|
|
||
|
|
break;
|
||
|
|
case WriteAppend:
|
||
|
|
handle = ifopen( canonical, "r" );
|
||
|
|
if( handle ) {
|
||
|
|
fclose(handle);
|
||
|
|
} else {
|
||
|
|
// We have to copy the original file to append
|
||
|
|
if ( ! copyFile(filename, canonical) ) {
|
||
|
|
return setStatus( );
|
||
|
|
}
|
||
|
|
}
|
||
|
|
handle = ifopen( canonical, "a" );
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( !handle ) {
|
||
|
|
#ifdef DEBUG
|
||
|
|
fprintf(stderr, "Unable to open %s\n", filename);
|
||
|
|
#endif
|
||
|
|
return setStatus( );
|
||
|
|
}
|
||
|
|
|
||
|
|
switch( mode ) {
|
||
|
|
case Read:
|
||
|
|
capability = static_cast<U32>( FileRead );
|
||
|
|
break;
|
||
|
|
case Write:
|
||
|
|
case WriteAppend:
|
||
|
|
capability = static_cast<U32>( FileWrite );
|
||
|
|
break;
|
||
|
|
case ReadWrite:
|
||
|
|
capability = static_cast<U32>( FileRead ) |
|
||
|
|
static_cast<U32>( FileWrite );
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
return ( currentStatus = Ok );
|
||
|
|
}
|
||
|
|
|
||
|
|
U32 File::getPosition( void ) const
|
||
|
|
{
|
||
|
|
return ftell( (FILE*) handle );
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::setPosition( S32 position, bool absolute )
|
||
|
|
{
|
||
|
|
FILE* file = (FILE*) handle;
|
||
|
|
|
||
|
|
if( currentStatus != Ok && currentStatus != EOS ) {
|
||
|
|
return currentStatus;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( absolute ) {
|
||
|
|
fseek( file, position, SEEK_SET );
|
||
|
|
} else {
|
||
|
|
fseek( file, position, SEEK_CUR );
|
||
|
|
}
|
||
|
|
|
||
|
|
long current = ftell( file );
|
||
|
|
|
||
|
|
if( current == -1 ) {
|
||
|
|
return setStatus( );
|
||
|
|
} else if( current >= getSize( ) ) {
|
||
|
|
return ( currentStatus = EOS );
|
||
|
|
} else {
|
||
|
|
return ( currentStatus = Ok );
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
U32 File::getSize( void ) const
|
||
|
|
{
|
||
|
|
|
||
|
|
if( currentStatus == Ok || currentStatus == EOS ) {
|
||
|
|
struct stat buf;
|
||
|
|
|
||
|
|
int fd = fileno( (FILE*) handle );
|
||
|
|
|
||
|
|
if( fstat( fd, &buf ) ) {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
return buf.st_size;
|
||
|
|
}
|
||
|
|
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::flush( void )
|
||
|
|
{
|
||
|
|
|
||
|
|
if( fflush( (FILE*) handle ) ) {
|
||
|
|
return setStatus( );
|
||
|
|
}
|
||
|
|
|
||
|
|
return ( currentStatus = Ok );
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::close( void )
|
||
|
|
{
|
||
|
|
|
||
|
|
if( currentStatus == Closed ) {
|
||
|
|
return currentStatus;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( handle ) {
|
||
|
|
|
||
|
|
if( fclose( (FILE*) handle ) ) {
|
||
|
|
return setStatus( );
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
handle = NULL;
|
||
|
|
return ( currentStatus = Closed );
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::getStatus( void ) const
|
||
|
|
{
|
||
|
|
return currentStatus;
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::setStatus( void )
|
||
|
|
{
|
||
|
|
|
||
|
|
switch( errno ) {
|
||
|
|
case EINVAL:
|
||
|
|
case EACCES:
|
||
|
|
case EMFILE:
|
||
|
|
case ENFILE:
|
||
|
|
case ENOSPC:
|
||
|
|
return ( currentStatus = IOError );
|
||
|
|
|
||
|
|
default:
|
||
|
|
return ( currentStatus = UnknownError );
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::setStatus( File::Status status )
|
||
|
|
{
|
||
|
|
return ( currentStatus = status );
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::read( U32 size, char* dst, U32* bytes )
|
||
|
|
{
|
||
|
|
|
||
|
|
if( currentStatus != Ok || size == 0 ) {
|
||
|
|
return currentStatus;
|
||
|
|
}
|
||
|
|
|
||
|
|
U32 dummyBytes = 0;
|
||
|
|
U32* bytesWritten = ( bytes == 0 ) ? &dummyBytes : bytes;
|
||
|
|
|
||
|
|
if( ( *bytesWritten = fread( dst, 1, size, (FILE*) handle ) ) != size ) {
|
||
|
|
|
||
|
|
if( ferror( (FILE*) handle ) ) {
|
||
|
|
return setStatus( );
|
||
|
|
} else {
|
||
|
|
return ( currentStatus = EOS );
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return ( currentStatus = Ok );
|
||
|
|
}
|
||
|
|
|
||
|
|
File::Status File::write( U32 size, const char* src, U32* bytes )
|
||
|
|
{
|
||
|
|
|
||
|
|
if( ( currentStatus != Ok && currentStatus != EOS ) || 0 == size ) {
|
||
|
|
return currentStatus;
|
||
|
|
}
|
||
|
|
|
||
|
|
U32 dummyBytes = 0;
|
||
|
|
U32* bytesWritten = ( bytes == 0 ) ? &dummyBytes : bytes;
|
||
|
|
|
||
|
|
if( ( *bytesWritten = fwrite( src, 1, size, (FILE*) handle ) ) != size ) {
|
||
|
|
|
||
|
|
if( ferror( (FILE*) handle ) ) {
|
||
|
|
return setStatus( );
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return ( currentStatus = Ok );
|
||
|
|
}
|
||
|
|
|
||
|
|
bool File::hasCapability( Capability cap ) const
|
||
|
|
{
|
||
|
|
return ( ( static_cast<U32>( cap ) & capability ) != 0 );
|
||
|
|
}
|
||
|
|
|
||
|
|
S32 Platform::compareFileTimes( const FileTime& a, const FileTime& b )
|
||
|
|
{
|
||
|
|
|
||
|
|
if( a < b ) {
|
||
|
|
return -1;
|
||
|
|
} else if( a > b ) {
|
||
|
|
return 1;
|
||
|
|
} else {
|
||
|
|
return 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
typedef void* HANDLE;
|
||
|
|
typedef unsigned char BOOL;
|
||
|
|
typedef unsigned int DWORD;
|
||
|
|
typedef char TCHAR;
|
||
|
|
typedef const char* LPCTSTR;
|
||
|
|
|
||
|
|
#define INVALID_HANDLE_VALUE NULL
|
||
|
|
#define FILE_ATTRIBUTE_NORMAL 0x0000
|
||
|
|
#define FILE_ATTRIBUTE_DIRECTORY 0x0040
|
||
|
|
#define MAX_PATH PATH_MAX
|
||
|
|
#define TRUE 1
|
||
|
|
#define FALSE 0
|
||
|
|
|
||
|
|
typedef struct _FILETIME {
|
||
|
|
DWORD dwLowDateTime;
|
||
|
|
DWORD dwHighDateTime;
|
||
|
|
} FILETIME;
|
||
|
|
|
||
|
|
typedef struct _WIN32_FIND_DATA {
|
||
|
|
DWORD dwFileAttributes;
|
||
|
|
FILETIME ftCreationTime;
|
||
|
|
FILETIME ftLastAccessTime;
|
||
|
|
FILETIME ftLastWriteTime;
|
||
|
|
DWORD nFileSizeHigh;
|
||
|
|
DWORD nFileSizeLow;
|
||
|
|
DWORD dwReserved0;
|
||
|
|
DWORD dwReserved1;
|
||
|
|
TCHAR cFileName[ MAX_PATH ];
|
||
|
|
TCHAR cAlternateFileName[ 14 ];
|
||
|
|
} WIN32_FIND_DATA, *LPWIN32_FIND_DATA;
|
||
|
|
|
||
|
|
HANDLE FindFirstFile( LPCTSTR, LPWIN32_FIND_DATA );
|
||
|
|
BOOL FindNextFile( HANDLE, LPWIN32_FIND_DATA );
|
||
|
|
BOOL FindClose( HANDLE );
|
||
|
|
|
||
|
|
/* Opaque handle for Find* functions. */
|
||
|
|
typedef struct tagFINDSTRUCT {
|
||
|
|
char dirpath[PATH_MAX];
|
||
|
|
DIR* dp;
|
||
|
|
regex_t preg;
|
||
|
|
} FINDSTRUCT;
|
||
|
|
|
||
|
|
ssize_t PreprocessFilename( const char *src, char *dst, ssize_t maxlen )
|
||
|
|
{
|
||
|
|
int len;
|
||
|
|
const char *tmp;
|
||
|
|
|
||
|
|
/* Check the size of the destination buffer */
|
||
|
|
if( dst ) {
|
||
|
|
|
||
|
|
if( PreprocessFilename( src, NULL, 0 ) > maxlen ) {
|
||
|
|
/* Not enough space in destination */
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/* "*.*" is a special match anything case */
|
||
|
|
if( strcmp( src, "*.*") == 0 ) {
|
||
|
|
const char *match_all = "^.*$";
|
||
|
|
|
||
|
|
len = strlen( match_all ) + 1;
|
||
|
|
|
||
|
|
if( dst ) {
|
||
|
|
strcpy( dst, match_all );
|
||
|
|
}
|
||
|
|
|
||
|
|
} else
|
||
|
|
|
||
|
|
/* "*." is also a special case -- match anything with no suffix
|
||
|
|
Warning: "?*." also matches this case, but hope we don't hit it
|
||
|
|
*/
|
||
|
|
if( strcmp( src, "*." ) == 0 ) {
|
||
|
|
const char *match_all_nodot = "^[^\\.]*$";
|
||
|
|
|
||
|
|
len = strlen( match_all_nodot ) + 1;
|
||
|
|
|
||
|
|
if ( dst ) {
|
||
|
|
strcpy( dst, match_all_nodot );
|
||
|
|
}
|
||
|
|
|
||
|
|
} else {
|
||
|
|
len = 1;
|
||
|
|
|
||
|
|
for( tmp = src; *tmp; ++tmp ) {
|
||
|
|
|
||
|
|
switch( *tmp ) {
|
||
|
|
case '*':
|
||
|
|
case '.':
|
||
|
|
++len;
|
||
|
|
default:
|
||
|
|
++len;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
len += 2;
|
||
|
|
|
||
|
|
if( dst ) {
|
||
|
|
*dst++ = '^';
|
||
|
|
|
||
|
|
for( tmp = src; *tmp; ++tmp ) {
|
||
|
|
|
||
|
|
switch( *tmp ) {
|
||
|
|
case '*':
|
||
|
|
*dst++ = '.';
|
||
|
|
*dst++ = '*';
|
||
|
|
break;
|
||
|
|
case '?':
|
||
|
|
*dst++ = '.';
|
||
|
|
break;
|
||
|
|
case '.':
|
||
|
|
*dst++ = '\\';
|
||
|
|
*dst++ = '.';
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
*dst++ = *tmp;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
*dst++ = '$';
|
||
|
|
*dst++ = '\0';
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return len;
|
||
|
|
}
|
||
|
|
|
||
|
|
HANDLE FindFirstFile( LPCTSTR lpszSearchFile, LPWIN32_FIND_DATA lpffd )
|
||
|
|
{
|
||
|
|
FINDSTRUCT * findhandle;
|
||
|
|
int name_pos;
|
||
|
|
char SearchFilename[256];
|
||
|
|
|
||
|
|
assert( lpffd != NULL );
|
||
|
|
assert( lpszSearchFile != NULL );
|
||
|
|
assert( strstr( (char*)lpszSearchFile, ":" ) == NULL );
|
||
|
|
assert( strstr( (char*)lpszSearchFile, "\\" ) == NULL );
|
||
|
|
|
||
|
|
/* Allocate memory for the file handle we return */
|
||
|
|
findhandle = (FINDSTRUCT*) malloc( sizeof( *findhandle ) );
|
||
|
|
|
||
|
|
if( findhandle == NULL ) {
|
||
|
|
return INVALID_HANDLE_VALUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
memset( findhandle, 0, sizeof( *findhandle ) );
|
||
|
|
|
||
|
|
/* Detect and separate a path from a filename in the event there is one */
|
||
|
|
strncpy( findhandle->dirpath, lpszSearchFile, 256 - 1 );
|
||
|
|
findhandle->dirpath[256-1] = '\0';
|
||
|
|
|
||
|
|
for( name_pos = strlen( findhandle->dirpath ) - 1; name_pos >= 0; name_pos-- ) {
|
||
|
|
|
||
|
|
if( findhandle->dirpath[name_pos] == '/' ) {
|
||
|
|
findhandle->dirpath[name_pos] = '\0';
|
||
|
|
++name_pos;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Get the filename as a regular expression pattern */
|
||
|
|
PreprocessFilename( &lpszSearchFile[name_pos], SearchFilename, 256 );
|
||
|
|
|
||
|
|
if( regcomp( &(findhandle->preg), SearchFilename, REG_ICASE ) != 0 ) {
|
||
|
|
free( findhandle );
|
||
|
|
return INVALID_HANDLE_VALUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Make sure we still have a directory after removing filename */
|
||
|
|
if ( ! findhandle->dirpath[0] )
|
||
|
|
strcpy( findhandle->dirpath, "." );
|
||
|
|
|
||
|
|
/* Open the directory for reading */
|
||
|
|
findhandle->dp = opendir( findhandle->dirpath );
|
||
|
|
|
||
|
|
if( findhandle->dp == NULL ){
|
||
|
|
FindClose( findhandle );
|
||
|
|
return INVALID_HANDLE_VALUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Perform search */
|
||
|
|
if( ! FindNextFile( findhandle, lpffd ) ) {
|
||
|
|
FindClose( findhandle );
|
||
|
|
return INVALID_HANDLE_VALUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
return findhandle;
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL FindNextFile( HANDLE handle, LPWIN32_FIND_DATA lpffd )
|
||
|
|
{
|
||
|
|
FINDSTRUCT* findhandle = (FINDSTRUCT*) handle;
|
||
|
|
char filepath[256];
|
||
|
|
struct stat statbuf;
|
||
|
|
struct dirent *entry;
|
||
|
|
|
||
|
|
assert( lpffd != NULL );
|
||
|
|
assert( findhandle != NULL );
|
||
|
|
|
||
|
|
while( ( entry = readdir( ( (FINDSTRUCT*)findhandle )->dp ) ) != NULL ) {
|
||
|
|
|
||
|
|
if( (entry->d_name[0] != '.') && regexec( &( ( (FINDSTRUCT*) findhandle )->preg ),
|
||
|
|
entry->d_name,
|
||
|
|
0,
|
||
|
|
0,
|
||
|
|
0 ) == 0 ) {
|
||
|
|
sprintf( filepath, "%s/%s", findhandle->dirpath, entry->d_name );
|
||
|
|
|
||
|
|
if( stat( filepath, &statbuf ) < 0 ) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
|
||
|
|
lpffd->ftLastAccessTime.dwLowDateTime = statbuf.st_atime;
|
||
|
|
lpffd->ftLastWriteTime.dwLowDateTime = statbuf.st_ctime;
|
||
|
|
|
||
|
|
//Is path also included?
|
||
|
|
strncpy( lpffd->cFileName, entry->d_name, 256 - 1 );
|
||
|
|
lpffd->cFileName[256 - 1] = '\0';
|
||
|
|
|
||
|
|
if( S_ISDIR( statbuf.st_mode ) ) {
|
||
|
|
lpffd->dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
|
||
|
|
} else if( S_ISREG( statbuf.st_mode ) ) {
|
||
|
|
lpffd->dwFileAttributes = FILE_ATTRIBUTE_NORMAL;
|
||
|
|
} else {
|
||
|
|
lpffd->dwFileAttributes = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
lpffd->nFileSizeLow = statbuf.st_size;
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
BOOL FindClose( HANDLE hFindFile )
|
||
|
|
{
|
||
|
|
FINDSTRUCT* findhandle;
|
||
|
|
|
||
|
|
findhandle = (FINDSTRUCT*) hFindFile;
|
||
|
|
|
||
|
|
if( findhandle == NULL ) {
|
||
|
|
return FALSE;
|
||
|
|
}
|
||
|
|
|
||
|
|
regfree( &findhandle->preg );
|
||
|
|
|
||
|
|
if( findhandle->dp ) {
|
||
|
|
closedir( findhandle->dp );
|
||
|
|
}
|
||
|
|
|
||
|
|
free( findhandle );
|
||
|
|
|
||
|
|
return TRUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
static bool recurseDumpPath( const char* basep,
|
||
|
|
const char* currentp,
|
||
|
|
Vector<Platform::FileInfo>& out )
|
||
|
|
{
|
||
|
|
char current[PATH_MAX];
|
||
|
|
char base[PATH_MAX];
|
||
|
|
char buffer[PATH_MAX];
|
||
|
|
char scratch[PATH_MAX];
|
||
|
|
|
||
|
|
if( currentp ) {
|
||
|
|
dStrncpy( current, currentp, PATH_MAX );
|
||
|
|
} else {
|
||
|
|
current[0] = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
dStrncpy( base, basep, PATH_MAX );
|
||
|
|
basep = base;
|
||
|
|
|
||
|
|
if( current[0] ) {
|
||
|
|
dSprintf( buffer, sizeof( buffer ), "%s/%s/*", base, current );
|
||
|
|
} else {
|
||
|
|
dSprintf( buffer, sizeof( buffer ), "%s/*", base );
|
||
|
|
}
|
||
|
|
|
||
|
|
WIN32_FIND_DATA findData;
|
||
|
|
HANDLE hFind = FindFirstFile( buffer, &findData );
|
||
|
|
|
||
|
|
if( hFind == INVALID_HANDLE_VALUE ) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
while( hFind != INVALID_HANDLE_VALUE ) {
|
||
|
|
|
||
|
|
if( ( findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 ) {
|
||
|
|
|
||
|
|
if( findData.cFileName[0] != '.' ) {
|
||
|
|
scratch[0] = 0;
|
||
|
|
int length = PATH_MAX;
|
||
|
|
|
||
|
|
if( current[0] ) {
|
||
|
|
dStrncpy( scratch, current, length );
|
||
|
|
length -= dStrlen( current );
|
||
|
|
dStrncat( scratch, "/", length );
|
||
|
|
length--;
|
||
|
|
}
|
||
|
|
|
||
|
|
dStrncat( scratch, findData.cFileName, length );
|
||
|
|
recurseDumpPath( base, scratch, out );
|
||
|
|
}
|
||
|
|
|
||
|
|
} else {
|
||
|
|
out.increment( );
|
||
|
|
Platform::FileInfo& info = out.last( );
|
||
|
|
|
||
|
|
if( current[0] ) {
|
||
|
|
dSprintf( scratch, sizeof( scratch ), "%s/%s", base, current );
|
||
|
|
info.pFullPath = StringTable->insert( scratch );
|
||
|
|
info.pVirtPath = StringTable->insert( current );
|
||
|
|
} else {
|
||
|
|
info.pFullPath = StringTable->insert( base );
|
||
|
|
info.pVirtPath = NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
info.pFileName = StringTable->insert( findData.cFileName );
|
||
|
|
info.fileSize = findData.nFileSizeLow;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( FindNextFile( hFind, &findData ) == 0 ) {
|
||
|
|
FindClose( hFind );
|
||
|
|
hFind = INVALID_HANDLE_VALUE;
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Platform::getFileTimes( const char* path, FileTime* create, FileTime* modify )
|
||
|
|
{
|
||
|
|
WIN32_FIND_DATA findData;
|
||
|
|
HANDLE h = FindFirstFile( path, &findData );
|
||
|
|
|
||
|
|
if( h == INVALID_HANDLE_VALUE ) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( create ) {
|
||
|
|
// NOTE: always 0 under Linux, but shouldn't be
|
||
|
|
// an issue with the current usage in the codebase
|
||
|
|
*create = findData.ftCreationTime.dwLowDateTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
if( modify ) {
|
||
|
|
*modify = findData.ftLastWriteTime.dwLowDateTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
FindClose( h );
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Platform::createPath( const char* file )
|
||
|
|
{
|
||
|
|
char filename[PATH_MAX];
|
||
|
|
char pathbuf[PATH_MAX];
|
||
|
|
const char* dir = 0;
|
||
|
|
U32 length = 0;
|
||
|
|
|
||
|
|
pathbuf[0] = 0;
|
||
|
|
dSprintf( filename, PATH_MAX, "%s/%s", loki_getprefpath( ), file );
|
||
|
|
file = filename;
|
||
|
|
|
||
|
|
while( ( dir = dStrchr( file, '/' ) ) != 0 ) {
|
||
|
|
dStrncpy( pathbuf + length, file, dir - file );
|
||
|
|
pathbuf[ length + dir - file ] = 0;
|
||
|
|
mkdir( pathbuf, 0700 );
|
||
|
|
length += dir - file;
|
||
|
|
pathbuf[ length++ ] = '/';
|
||
|
|
file = dir + 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Platform::cdFileExists(const char *filePath, const char *volumeName, S32 serialNum)
|
||
|
|
{
|
||
|
|
if (!filePath || !filePath[0])
|
||
|
|
return true;
|
||
|
|
|
||
|
|
// FIXME: Add code to scan for CD drives
|
||
|
|
return true;
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool Platform::dumpPath( const char* spec, Vector<Platform::FileInfo>& out )
|
||
|
|
{
|
||
|
|
return recurseDumpPath( spec, 0, out );
|
||
|
|
}
|
||
|
|
|
||
|
|
void Platform::getCurrentDirectory( char* cwd, const U32 size )
|
||
|
|
{
|
||
|
|
getcwd( cwd, size );
|
||
|
|
}
|
||
|
|
|
||
|
|
// FIXME: Add to Platform::.
|
||
|
|
bool platformGetUserPath( const char* base, char* user, U32 size )
|
||
|
|
{
|
||
|
|
dSprintf( user, size, "%s/%s", loki_getprefpath( ), base );
|
||
|
|
return true;
|
||
|
|
}
|