2003-07-03 02:41:27 +00:00
|
|
|
#include "global.h"
|
|
|
|
|
|
|
|
|
|
#include "RageUtil_FileDB.h"
|
|
|
|
|
#include "RageUtil.h"
|
|
|
|
|
#include "RageTimer.h"
|
|
|
|
|
#include "RageLog.h"
|
|
|
|
|
#include <map>
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <sys/types.h>
|
2003-07-04 00:59:54 +00:00
|
|
|
#include <errno.h>
|
2003-11-30 19:01:19 +00:00
|
|
|
#include "RageFile.h"
|
2003-07-22 07:47:27 +00:00
|
|
|
#include "arch/arch.h"
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
#if !defined(WIN32)
|
|
|
|
|
#include <dirent.h>
|
2003-09-16 02:45:49 +00:00
|
|
|
#include <fcntl.h>
|
2003-11-14 17:17:36 +00:00
|
|
|
#else
|
|
|
|
|
#include "windows.h"
|
2003-07-03 02:41:27 +00:00
|
|
|
#endif
|
|
|
|
|
|
2003-12-06 06:17:21 +00:00
|
|
|
enum FileType { TTYPE_FILE, TTYPE_DIR, TTYPE_NONE };
|
2003-07-03 02:41:27 +00:00
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
struct File
|
|
|
|
|
{
|
|
|
|
|
CString name;
|
|
|
|
|
CString lname;
|
|
|
|
|
|
|
|
|
|
void SetName( const CString &fn )
|
|
|
|
|
{
|
|
|
|
|
name = fn;
|
|
|
|
|
lname = name;
|
|
|
|
|
lname.MakeLower();
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-03 02:41:27 +00:00
|
|
|
bool dir;
|
2003-09-22 05:14:00 +00:00
|
|
|
int size;
|
2003-09-23 00:09:10 +00:00
|
|
|
/* Modification time of the file. The contents of this is undefined, except that
|
|
|
|
|
* when the file has been modified, this value will change. */
|
|
|
|
|
int mtime;
|
2003-09-22 05:14:00 +00:00
|
|
|
|
2003-09-23 00:09:10 +00:00
|
|
|
File() { dir=false; size=-1; mtime=-1; }
|
2003-12-06 05:50:02 +00:00
|
|
|
File( const CString &fn )
|
|
|
|
|
{
|
|
|
|
|
SetName( fn );
|
|
|
|
|
dir=false; size=-1; mtime=-1;
|
|
|
|
|
}
|
2003-07-03 02:41:27 +00:00
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
bool operator== (const File &rhs) const { return lname==rhs.lname; }
|
|
|
|
|
bool operator< (const File &rhs) const { return lname<rhs.lname; }
|
2003-07-03 02:41:27 +00:00
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
bool equal(const File &rhs) const { return lname == rhs.lname; }
|
|
|
|
|
bool equal(const CString &rhs) const
|
|
|
|
|
{
|
|
|
|
|
CString l = rhs;
|
|
|
|
|
l.MakeLower();
|
|
|
|
|
return lname == l;
|
2003-07-03 02:41:27 +00:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* This represents a directory. */
|
|
|
|
|
struct FileSet
|
|
|
|
|
{
|
|
|
|
|
set<File> files;
|
|
|
|
|
RageTimer age;
|
|
|
|
|
void LoadFromDir(const CString &dir);
|
|
|
|
|
void GetFilesMatching(
|
|
|
|
|
const CString &beginning, const CString &containing, const CString &ending,
|
|
|
|
|
vector<CString> &out, bool bOnlyDirs) const;
|
|
|
|
|
void GetFilesEqualTo(const CString &pat, vector<CString> &out, bool bOnlyDirs) const;
|
2003-12-06 06:17:21 +00:00
|
|
|
|
|
|
|
|
FileType GetFileType( const CString &path ) const;
|
2003-09-22 05:14:00 +00:00
|
|
|
int GetFileSize(const CString &path) const;
|
2003-09-23 00:09:10 +00:00
|
|
|
int GetFileModTime(const CString &path) const;
|
2003-07-03 02:41:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
void FileSet::LoadFromDir(const CString &dir)
|
|
|
|
|
{
|
|
|
|
|
age.GetDeltaTime(); /* reset */
|
|
|
|
|
files.clear();
|
|
|
|
|
|
|
|
|
|
#if defined(WIN32)
|
|
|
|
|
WIN32_FIND_DATA fd;
|
2003-11-13 00:39:36 +00:00
|
|
|
CString dirHolder = dir ;
|
|
|
|
|
|
|
|
|
|
if ( dirHolder.size() > 0 && dirHolder.Right(1) == SLASH )
|
|
|
|
|
{
|
|
|
|
|
dirHolder.erase( dirHolder.size() - 1 ) ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HANDLE hFind = FindFirstFile( dirHolder+SLASH "*", &fd );
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
if( hFind == INVALID_HANDLE_VALUE )
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
if(!strcmp(fd.cFileName, ".") || !strcmp(fd.cFileName, ".."))
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
File f;
|
2003-12-06 05:50:02 +00:00
|
|
|
f.SetName( fd.cFileName );
|
2003-07-03 02:41:27 +00:00
|
|
|
f.dir = !!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
|
2003-09-22 05:14:00 +00:00
|
|
|
f.size = fd.nFileSizeLow;
|
2003-09-23 00:09:10 +00:00
|
|
|
f.mtime = fd.ftLastWriteTime.dwLowDateTime;
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
files.insert(f);
|
|
|
|
|
} while( FindNextFile( hFind, &fd ) );
|
|
|
|
|
FindClose(hFind);
|
|
|
|
|
#else
|
2003-09-16 02:45:49 +00:00
|
|
|
int OldDir = open(".", O_RDONLY);
|
|
|
|
|
if( OldDir == -1 )
|
|
|
|
|
RageException::Throw( "Couldn't open(.): %s", strerror(errno) );
|
2003-09-05 06:21:47 +00:00
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
if( chdir(dir) == -1 )
|
2003-07-27 03:06:50 +00:00
|
|
|
{
|
|
|
|
|
/* Only log once per dir. */
|
2003-09-05 20:10:40 +00:00
|
|
|
if( LOG )
|
|
|
|
|
LOG->MapLog("chdir " + dir, "Couldn't chdir(%s): %s", dir.c_str(), strerror(errno) );
|
2003-09-16 02:45:49 +00:00
|
|
|
close( OldDir );
|
2003-07-27 03:06:50 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2003-07-22 14:35:51 +00:00
|
|
|
DIR *d = opendir(".");
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
while(struct dirent *ent = readdir(d))
|
|
|
|
|
{
|
|
|
|
|
if(!strcmp(ent->d_name, ".")) continue;
|
|
|
|
|
if(!strcmp(ent->d_name, "..")) continue;
|
|
|
|
|
|
|
|
|
|
File f;
|
2003-12-06 05:50:02 +00:00
|
|
|
f.SetName( ent->d_name );
|
2003-07-04 00:59:54 +00:00
|
|
|
|
2003-09-22 05:14:00 +00:00
|
|
|
struct stat st;
|
|
|
|
|
if( stat(ent->d_name, &st) == -1 )
|
2003-07-04 00:59:54 +00:00
|
|
|
{
|
2003-11-04 04:32:11 +00:00
|
|
|
/* If it's a broken symlink, ignore it. Otherwise, warn. */
|
|
|
|
|
if( lstat(ent->d_name, &st) == 0 )
|
|
|
|
|
continue;
|
|
|
|
|
|
2003-09-22 05:14:00 +00:00
|
|
|
/* Huh? */
|
|
|
|
|
if(LOG)
|
|
|
|
|
LOG->Warn("Got file '%s' in '%s' from list, but can't stat? (%s)",
|
|
|
|
|
ent->d_name, dir.c_str(), strerror(errno));
|
2003-11-04 04:32:11 +00:00
|
|
|
continue;
|
2003-09-22 05:14:00 +00:00
|
|
|
} else {
|
|
|
|
|
f.dir = (st.st_mode & S_IFDIR);
|
|
|
|
|
f.size = st.st_size;
|
2003-09-23 00:09:10 +00:00
|
|
|
f.mtime = st.st_mtime;
|
2003-09-22 05:14:00 +00:00
|
|
|
}
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
files.insert(f);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
closedir(d);
|
2003-09-16 02:45:49 +00:00
|
|
|
if( fchdir( OldDir ) == -1 )
|
|
|
|
|
RageException::Throw( "Couldn't fchdir(): %s", strerror(errno) );
|
|
|
|
|
close( OldDir );
|
2003-07-03 02:41:27 +00:00
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Search for "beginning*containing*ending". */
|
|
|
|
|
void FileSet::GetFilesMatching(const CString &beginning, const CString &containing, const CString &ending, vector<CString> &out, bool bOnlyDirs) const
|
|
|
|
|
{
|
2003-10-19 22:20:56 +00:00
|
|
|
/* "files" is a case-insensitive mapping, by filename. Use lower_bound to figure
|
|
|
|
|
* out where to start. */
|
2003-12-06 05:50:02 +00:00
|
|
|
set<File>::const_iterator i = files.lower_bound( File(beginning) );
|
2003-07-03 02:41:27 +00:00
|
|
|
for( ; i != files.end(); ++i)
|
|
|
|
|
{
|
|
|
|
|
if(bOnlyDirs && !i->dir) continue;
|
|
|
|
|
|
2003-10-19 22:20:56 +00:00
|
|
|
/* Check beginning. Once we hit a filename that no longer matches beginning,
|
|
|
|
|
* we're past all possible matches in the sort, so stop. */
|
|
|
|
|
if(beginning.size() > i->name.size()) break; /* can't start with it */
|
2003-12-06 05:50:02 +00:00
|
|
|
if(strnicmp(i->name, beginning, beginning.size())) break; /* doesn't start with it */
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
/* Position the end starts on: */
|
|
|
|
|
int end_pos = int(i->name.size())-int(ending.size());
|
|
|
|
|
|
|
|
|
|
/* Check end. */
|
|
|
|
|
if(end_pos < 0) continue; /* can't end with it */
|
2003-12-06 05:50:02 +00:00
|
|
|
if( stricmp(i->name.c_str()+end_pos, ending) ) continue; /* doesn't end with it */
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
/* Check containing. Do this last, since it's the slowest (substring
|
|
|
|
|
* search instead of string match). */
|
|
|
|
|
if(containing.size())
|
|
|
|
|
{
|
|
|
|
|
unsigned pos = i->name.find(containing, beginning.size());
|
|
|
|
|
if(pos == i->name.npos) continue; /* doesn't contain it */
|
|
|
|
|
if(pos + containing.size() > unsigned(end_pos)) continue; /* found it but it overlaps with the end */
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
out.push_back( i->name );
|
2003-07-03 02:41:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FileSet::GetFilesEqualTo(const CString &str, vector<CString> &out, bool bOnlyDirs) const
|
|
|
|
|
{
|
2003-12-06 05:50:02 +00:00
|
|
|
set<File>::const_iterator i = files.find( File(str) );
|
2003-07-03 02:41:27 +00:00
|
|
|
if(i == files.end())
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if(bOnlyDirs && !i->dir)
|
|
|
|
|
return;
|
|
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
out.push_back( i->name );
|
2003-07-03 02:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
2003-12-06 06:17:21 +00:00
|
|
|
FileType FileSet::GetFileType(const CString &path ) const
|
2003-07-03 02:41:27 +00:00
|
|
|
{
|
2003-12-06 05:50:02 +00:00
|
|
|
set<File>::const_iterator i = files.find( File(path) );
|
2003-07-03 02:41:27 +00:00
|
|
|
if(i == files.end())
|
2003-12-06 06:17:21 +00:00
|
|
|
return TTYPE_NONE;
|
2003-07-03 02:41:27 +00:00
|
|
|
|
2003-12-06 06:17:21 +00:00
|
|
|
return i->dir? TTYPE_DIR:TTYPE_FILE;
|
2003-07-03 02:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
2003-09-22 05:14:00 +00:00
|
|
|
int FileSet::GetFileSize(const CString &path) const
|
|
|
|
|
{
|
2003-12-06 05:50:02 +00:00
|
|
|
set<File>::const_iterator i = files.find( File(path) );
|
2003-09-22 05:14:00 +00:00
|
|
|
if(i == files.end())
|
|
|
|
|
return -1;
|
|
|
|
|
return i->size;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-23 00:09:10 +00:00
|
|
|
int FileSet::GetFileModTime(const CString &path) const
|
|
|
|
|
{
|
2003-12-06 05:50:02 +00:00
|
|
|
set<File>::const_iterator i = files.find( File(path) );
|
2003-09-23 00:09:10 +00:00
|
|
|
if(i == files.end())
|
|
|
|
|
return -1;
|
|
|
|
|
return i->mtime;
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-03 02:41:27 +00:00
|
|
|
/* Given "foo/bar/baz/" or "foo/bar/baz", return "foo/bar/" and "baz". */
|
|
|
|
|
static void SplitPath( CString Path, CString &Dir, CString &Name )
|
|
|
|
|
{
|
2003-09-06 22:55:20 +00:00
|
|
|
/* Strip off any trailing slashes. */
|
|
|
|
|
if( Path.size() > 0 && Path.Right(1) == SLASH )
|
|
|
|
|
Path.erase( Path.size()-1 );
|
|
|
|
|
|
2003-11-13 00:39:36 +00:00
|
|
|
#ifdef _XBOX
|
|
|
|
|
static Regex split("(.*\\\\)([^\\\\]+)");
|
|
|
|
|
#else
|
2003-07-03 02:41:27 +00:00
|
|
|
static Regex split("(.*/)([^/]+)");
|
2003-11-13 00:39:36 +00:00
|
|
|
#endif
|
|
|
|
|
|
2003-07-03 02:41:27 +00:00
|
|
|
CStringArray match;
|
|
|
|
|
if(split.Compare(Path, match)) {
|
|
|
|
|
/* At least one slash. */
|
|
|
|
|
Dir = match[0];
|
|
|
|
|
Name = match[1];
|
|
|
|
|
} else {
|
|
|
|
|
/* No slash. */
|
2003-11-13 00:39:36 +00:00
|
|
|
#ifdef _XBOX
|
|
|
|
|
Dir = "D:\\" ;
|
|
|
|
|
Name = "" ;
|
|
|
|
|
#else
|
2003-07-22 07:47:27 +00:00
|
|
|
Dir = "." SLASH;
|
2003-07-03 02:41:27 +00:00
|
|
|
Name = Path;
|
2003-11-13 00:39:36 +00:00
|
|
|
#endif
|
2003-07-03 02:41:27 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class FilenameDB
|
|
|
|
|
{
|
2003-12-06 05:50:02 +00:00
|
|
|
FileSet &GetFileSet( CString dir );
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
/* Directories we have cached: */
|
2003-12-06 05:50:02 +00:00
|
|
|
map<CString, FileSet *> dirs;
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
void GetFilesEqualTo(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs);
|
|
|
|
|
void GetFilesMatching(const CString &dir,
|
|
|
|
|
const CString &beginning, const CString &containing, const CString &ending,
|
|
|
|
|
vector<CString> &out, bool bOnlyDirs);
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
/* This handles at most one * wildcard. If we need anything more complicated,
|
|
|
|
|
* we'll need to use fnmatch or regex. */
|
|
|
|
|
void GetFilesSimpleMatch(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs);
|
|
|
|
|
|
|
|
|
|
/* Search for "path" case-insensitively and replace it with the correct
|
|
|
|
|
* case. If "path" doesn't exist at all, return false and don't change it. */
|
|
|
|
|
bool ResolvePath(CString &path);
|
|
|
|
|
|
2003-12-06 06:17:21 +00:00
|
|
|
FileType GetFileType( const CString &path );
|
2003-09-22 05:14:00 +00:00
|
|
|
int GetFileSize(const CString &path);
|
2003-09-23 00:09:10 +00:00
|
|
|
int GetFileModTime( const CString &sFilePath );
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
void FlushDirCache();
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2003-12-06 06:17:21 +00:00
|
|
|
FileType FilenameDB::GetFileType( const CString &sPath )
|
2003-07-03 02:41:27 +00:00
|
|
|
{
|
|
|
|
|
CString Dir, Name;
|
2003-12-06 06:17:21 +00:00
|
|
|
SplitPath( sPath, Dir, Name );
|
2003-12-06 05:50:02 +00:00
|
|
|
FileSet &fs = GetFileSet( Dir );
|
2003-12-06 06:17:21 +00:00
|
|
|
return fs.GetFileType( Name );
|
2003-07-03 02:41:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2003-09-22 05:14:00 +00:00
|
|
|
int FilenameDB::GetFileSize( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
CString Dir, Name;
|
|
|
|
|
SplitPath(sPath, Dir, Name);
|
2003-12-06 05:50:02 +00:00
|
|
|
FileSet &fs = GetFileSet( Dir );
|
2003-09-22 05:14:00 +00:00
|
|
|
return fs.GetFileSize(Name);
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-23 00:09:10 +00:00
|
|
|
int FilenameDB::GetFileModTime( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
CString Dir, Name;
|
|
|
|
|
SplitPath(sPath, Dir, Name);
|
2003-12-06 05:50:02 +00:00
|
|
|
FileSet &fs = GetFileSet( Dir );
|
2003-09-23 00:09:10 +00:00
|
|
|
return fs.GetFileModTime(Name);
|
|
|
|
|
}
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
/* XXX: this won't work right for URIs, eg \\foo\bar */
|
|
|
|
|
bool FilenameDB::ResolvePath(CString &path)
|
|
|
|
|
{
|
|
|
|
|
if(path == ".") return true;
|
|
|
|
|
if(path == "") return true;
|
|
|
|
|
|
|
|
|
|
/* Split path into components. */
|
|
|
|
|
vector<CString> p;
|
2003-09-05 18:54:08 +00:00
|
|
|
split(path, SLASH, p, true);
|
|
|
|
|
|
|
|
|
|
/* If we have "/foo", then add a blank entry to the beginning to line things up. */
|
2003-09-05 20:07:36 +00:00
|
|
|
if( path.Left( strlen(SLASH) ) == SLASH )
|
2003-09-05 18:54:08 +00:00
|
|
|
p.insert( p.begin(), "" );
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
/* Resolve each component. Assume the first component is correct. XXX
|
|
|
|
|
* don't do that! "Songs/" vs "songs/" */
|
|
|
|
|
CString ret = p[0];
|
|
|
|
|
for(unsigned i = 1; i < p.size(); ++i)
|
|
|
|
|
{
|
2003-07-22 07:47:27 +00:00
|
|
|
ret += SLASH;
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
vector<CString> lst;
|
2003-09-05 20:07:36 +00:00
|
|
|
FileSet &fs = GetFileSet( ret );
|
2003-07-03 02:41:27 +00:00
|
|
|
fs.GetFilesEqualTo(p[i], lst, false);
|
|
|
|
|
|
|
|
|
|
/* If there were no matches, the path isn't found. */
|
|
|
|
|
if(lst.empty()) return false;
|
|
|
|
|
|
2003-09-05 20:10:40 +00:00
|
|
|
if( lst.size() > 1 && LOG )
|
2003-07-22 07:47:27 +00:00
|
|
|
LOG->Warn("Ambiguous filenames '%s' and '%s'",
|
2003-07-03 02:41:27 +00:00
|
|
|
lst[0].c_str(), lst[1].c_str());
|
|
|
|
|
|
|
|
|
|
ret += lst[0];
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-22 07:47:27 +00:00
|
|
|
if(path.Right(1) == SLASH)
|
|
|
|
|
path = ret + SLASH;
|
2003-07-03 02:41:27 +00:00
|
|
|
else
|
|
|
|
|
path = ret;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FilenameDB::GetFilesMatching(const CString &dir, const CString &beginning, const CString &containing, const CString &ending, vector<CString> &out, bool bOnlyDirs)
|
|
|
|
|
{
|
2003-12-06 05:50:02 +00:00
|
|
|
FileSet &fs = GetFileSet( dir );
|
2003-07-03 02:41:27 +00:00
|
|
|
fs.GetFilesMatching(beginning, containing, ending, out, bOnlyDirs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FilenameDB::GetFilesEqualTo(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs)
|
|
|
|
|
{
|
2003-12-06 05:50:02 +00:00
|
|
|
FileSet &fs = GetFileSet( dir );
|
2003-07-03 02:41:27 +00:00
|
|
|
fs.GetFilesEqualTo(fn, out, bOnlyDirs);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void FilenameDB::GetFilesSimpleMatch(const CString &dir, const CString &fn, vector<CString> &out, bool bOnlyDirs)
|
|
|
|
|
{
|
|
|
|
|
/* Does this contain a wildcard? */
|
|
|
|
|
unsigned first_pos = fn.find_first_of('*');
|
|
|
|
|
if(first_pos == fn.npos)
|
|
|
|
|
{
|
|
|
|
|
/* No; just do a regular search. */
|
|
|
|
|
GetFilesEqualTo(dir, fn, out, bOnlyDirs);
|
|
|
|
|
} else {
|
|
|
|
|
unsigned second_pos = fn.find_first_of('*', first_pos+1);
|
|
|
|
|
if(second_pos == fn.npos)
|
|
|
|
|
{
|
|
|
|
|
/* Only one *: "A*B". */
|
2003-09-03 04:30:29 +00:00
|
|
|
/* XXX: "_blank.png*.png" shouldn't match the file "_blank.png". */
|
2003-07-03 02:41:27 +00:00
|
|
|
GetFilesMatching(dir, fn.substr(0, first_pos), "", fn.substr(first_pos+1), out, bOnlyDirs);
|
|
|
|
|
} else {
|
|
|
|
|
/* Two *s: "A*B*C". */
|
|
|
|
|
GetFilesMatching(dir,
|
|
|
|
|
fn.substr(0, first_pos),
|
|
|
|
|
fn.substr(first_pos+1, second_pos-first_pos-1),
|
|
|
|
|
fn.substr(second_pos+1), out, bOnlyDirs);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
FileSet &FilenameDB::GetFileSet( CString dir )
|
2003-07-03 02:41:27 +00:00
|
|
|
{
|
|
|
|
|
/* Normalize the path. */
|
2003-07-22 07:47:27 +00:00
|
|
|
dir.Replace("\\", SLASH); /* foo\bar -> foo/bar */
|
|
|
|
|
dir.Replace("/", SLASH); /* foo//bar -> foo/bar */
|
|
|
|
|
dir.Replace("//", SLASH); /* foo//bar -> foo/bar */
|
2003-12-06 05:50:02 +00:00
|
|
|
CString lower = dir;
|
|
|
|
|
lower.MakeLower();
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
FileSet *ret;
|
2003-12-06 08:22:03 +00:00
|
|
|
map<CString, FileSet *>::iterator i = dirs.find( lower );
|
2003-07-03 02:41:27 +00:00
|
|
|
bool reload = false;
|
|
|
|
|
if(i == dirs.end())
|
|
|
|
|
{
|
|
|
|
|
ret = new FileSet;
|
2003-12-06 05:50:02 +00:00
|
|
|
dirs[lower] = ret;
|
2003-07-03 02:41:27 +00:00
|
|
|
reload = true;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ret = i->second;
|
|
|
|
|
if(ret->age.PeekDeltaTime() > 30)
|
|
|
|
|
reload = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(reload)
|
|
|
|
|
{
|
|
|
|
|
CString RealDir = dir;
|
|
|
|
|
|
2003-12-06 05:50:02 +00:00
|
|
|
/* Resolve path cases (path/Path -> PATH/path). */
|
|
|
|
|
ResolvePath( RealDir );
|
2003-07-03 02:41:27 +00:00
|
|
|
|
|
|
|
|
ret->LoadFromDir(RealDir);
|
|
|
|
|
}
|
|
|
|
|
return *ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void FilenameDB::FlushDirCache()
|
|
|
|
|
{
|
2003-11-12 04:54:59 +00:00
|
|
|
set<FileSet *> freed;
|
2003-12-06 05:50:02 +00:00
|
|
|
for( map<CString, FileSet *>::iterator i = dirs.begin(); i != dirs.end(); ++i )
|
2003-11-12 04:54:59 +00:00
|
|
|
{
|
|
|
|
|
if( freed.find(i->second) != freed.end() )
|
|
|
|
|
continue;
|
|
|
|
|
|
2003-11-11 17:51:06 +00:00
|
|
|
delete i->second;
|
2003-11-12 04:54:59 +00:00
|
|
|
freed.insert( i->second );
|
|
|
|
|
}
|
2003-07-03 02:41:27 +00:00
|
|
|
dirs.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
FilenameDB FDB;
|
|
|
|
|
|
2003-12-05 02:26:45 +00:00
|
|
|
void FDB_GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo )
|
2003-07-03 02:41:27 +00:00
|
|
|
{
|
2003-07-22 07:47:27 +00:00
|
|
|
// LOG->Trace( "GetDirListing( %s )", sPath.c_str() );
|
|
|
|
|
|
2003-07-03 02:41:27 +00:00
|
|
|
/* If you want the CWD, use ".". */
|
|
|
|
|
ASSERT(!sPath.empty());
|
|
|
|
|
|
|
|
|
|
/* XXX: for case-insensitive resolving, we assume the first element is
|
|
|
|
|
* correct (we need a place to start from); so if sPath is relative,
|
|
|
|
|
* prepend "./" */
|
|
|
|
|
|
|
|
|
|
/* Strip off the last path element and use it as a mask. */
|
2003-07-22 07:47:27 +00:00
|
|
|
unsigned pos = sPath.find_last_of( SLASH );
|
2003-07-03 02:41:27 +00:00
|
|
|
CString fn;
|
|
|
|
|
if(pos != sPath.npos)
|
|
|
|
|
{
|
|
|
|
|
fn = sPath.substr(pos+1);
|
|
|
|
|
sPath = sPath.substr(0, pos+1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* If there was only one path element, or if the last element was empty,
|
|
|
|
|
* use "*". */
|
|
|
|
|
if(fn.size() == 0)
|
|
|
|
|
fn = "*";
|
|
|
|
|
|
|
|
|
|
unsigned start = AddTo.size();
|
|
|
|
|
FDB.GetFilesSimpleMatch(sPath, fn, AddTo, bOnlyDirs);
|
|
|
|
|
|
|
|
|
|
if(bReturnPathToo && start < AddTo.size())
|
|
|
|
|
{
|
|
|
|
|
FDB.ResolvePath(sPath);
|
|
|
|
|
while(start < AddTo.size())
|
|
|
|
|
{
|
|
|
|
|
AddTo[start] = sPath + AddTo[start];
|
|
|
|
|
start++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-10 11:47:28 +00:00
|
|
|
bool ResolvePath(CString &path) { return FDB.ResolvePath(path); }
|
|
|
|
|
|
2003-09-22 05:14:00 +00:00
|
|
|
void FlushDirCache()
|
|
|
|
|
{
|
|
|
|
|
FDB.FlushDirCache();
|
|
|
|
|
}
|
|
|
|
|
|
2003-07-03 02:41:27 +00:00
|
|
|
|
2003-12-05 02:26:45 +00:00
|
|
|
#if 0
|
2003-12-06 06:17:21 +00:00
|
|
|
bool DoesFileExist( const CString &sPath ) { return FDB.GetFileType( sPath ) != TTYPE_NONE; }
|
|
|
|
|
bool IsAFile( const CString &sPath ) { return FDB.GetFileType( sPath ) == TTYPE_FILE; }
|
|
|
|
|
bool IsADirectory( const CString &sPath ) { return FDB.GetFileType( sPath ) == TTYPE_DIR; }
|
2003-09-23 00:09:10 +00:00
|
|
|
int GetFileModTime( const CString &sPath ) { return FDB.GetFileModTime(sPath); }
|
2003-09-22 05:14:00 +00:00
|
|
|
unsigned GetFileSizeInBytes( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
int ret = FDB.GetFileSize(sPath);
|
2003-09-23 00:09:10 +00:00
|
|
|
// LOG->Trace("file '%s' size %i", sPath.c_str(), ret);
|
2003-09-22 05:14:00 +00:00
|
|
|
if( ret == -1 )
|
|
|
|
|
return 0;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2003-12-05 02:26:45 +00:00
|
|
|
void GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo )
|
|
|
|
|
{
|
|
|
|
|
FDB_GetDirListing( sPath, AddTo, bOnlyDirs, bReturnPathToo );
|
|
|
|
|
}
|
|
|
|
|
#elif 0
|
2003-09-23 00:09:10 +00:00
|
|
|
static bool DoStat(CString sPath, struct stat *st)
|
|
|
|
|
{
|
|
|
|
|
TrimRight(sPath, "/\\");
|
2003-12-06 05:50:02 +00:00
|
|
|
return stat(sPath, st) != -1;
|
2003-09-23 00:09:10 +00:00
|
|
|
}
|
2003-07-03 02:41:27 +00:00
|
|
|
bool DoesFileExist( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
if(sPath.empty()) return false;
|
|
|
|
|
struct stat st;
|
|
|
|
|
return DoStat(sPath, &st);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsAFile( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
return DoesFileExist(sPath) && !IsADirectory(sPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsADirectory( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
if(sPath.empty()) return false;
|
|
|
|
|
struct stat st;
|
|
|
|
|
if (!DoStat(sPath, &st))
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
return !!(st.st_mode & S_IFDIR);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned GetFileSizeInBytes( const CString &sFilePath )
|
|
|
|
|
{
|
|
|
|
|
struct stat st;
|
|
|
|
|
if(!DoStat(sFilePath, &st))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
return st.st_size;
|
|
|
|
|
}
|
|
|
|
|
|
2003-09-23 00:09:10 +00:00
|
|
|
int GetFileModTime( const CString &sPath )
|
2003-07-03 02:41:27 +00:00
|
|
|
{
|
2003-09-23 00:09:10 +00:00
|
|
|
struct stat st;
|
|
|
|
|
if(!DoStat(sFilePath, &st))
|
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
|
|
return st.st_mtime;
|
2003-07-03 02:41:27 +00:00
|
|
|
}
|
2003-12-05 02:26:45 +00:00
|
|
|
#else
|
|
|
|
|
#include "RageFileManager.h"
|
|
|
|
|
|
|
|
|
|
bool DoesFileExist( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
return FILEMAN->DoesFileExist( sPath );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsAFile( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
return FILEMAN->IsAFile( sPath );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool IsADirectory( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
return FILEMAN->IsADirectory( sPath );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned GetFileSizeInBytes( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
return FILEMAN->GetFileSizeInBytes( sPath );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int GetFileModTime( const CString &sPath )
|
|
|
|
|
{
|
|
|
|
|
return FILEMAN->GetFileModTime( sPath );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void GetDirListing( CString sPath, CStringArray &AddTo, bool bOnlyDirs, bool bReturnPathToo )
|
|
|
|
|
{
|
|
|
|
|
FILEMAN->GetDirListing( sPath, AddTo, bOnlyDirs, bReturnPathToo );
|
|
|
|
|
}
|
2003-09-23 00:09:10 +00:00
|
|
|
#endif
|