forked from 0ad/0ad
#further VFS refactor (dirent enumeration interface unified, more code sharing)
- file: make DirIterator opaque struct with type field, as with File/FileIo in recent commit. unifies all dir enum interfaces. - add dir open/nextent/close functions to FileProvider_VTbl - allocators: change signature of SingleAllocator to that of the templated type (avoids need for cast) (affects archive, compression, zip) - file_util: move dirent filter logic here so it can be used by file.cpp also, not only vfs. This was SVN commit r3822.
This commit is contained in:
parent
2e1374c3ed
commit
641e55fefd
@ -293,12 +293,12 @@ public:
|
||||
is_in_use = 0;
|
||||
}
|
||||
|
||||
void* alloc()
|
||||
T* alloc()
|
||||
{
|
||||
return single_calloc(&storage, &is_in_use, sizeof(storage));
|
||||
return (T*)single_calloc(&storage, &is_in_use, sizeof(storage));
|
||||
}
|
||||
|
||||
void release(void* p)
|
||||
void release(T* p)
|
||||
{
|
||||
single_free(&storage, &is_in_use, p);
|
||||
}
|
||||
|
@ -413,7 +413,7 @@ LibError afile_io_issue(File* f, off_t user_ofs, size_t max_output_size, void* u
|
||||
H_DEREF(af->ha, Archive, a);
|
||||
|
||||
ArchiveFileIo* aio = (ArchiveFileIo*)io->opaque;
|
||||
aio->io = (FileIo*)io_allocator.alloc();
|
||||
aio->io = io_allocator.alloc();
|
||||
if(!aio->io)
|
||||
WARN_RETURN(ERR_NO_MEM);
|
||||
|
||||
|
@ -421,11 +421,12 @@ public:
|
||||
|
||||
// allocator
|
||||
static const size_t MAX_COMPRESSOR_SIZE = sizeof(ZLibCompressor);
|
||||
typedef u8 CompressorMem[MAX_COMPRESSOR_SIZE];
|
||||
static SingleAllocator<u8[MAX_COMPRESSOR_SIZE]> compressor_allocator;
|
||||
|
||||
uintptr_t comp_alloc(ContextType type, CompressionMethod method)
|
||||
{
|
||||
void* c_mem = compressor_allocator.alloc();
|
||||
CompressorMem* c_mem = compressor_allocator.alloc();
|
||||
if(!c_mem)
|
||||
return 0;
|
||||
Compressor* c;
|
||||
@ -500,5 +501,5 @@ void comp_free(uintptr_t c_)
|
||||
c->release();
|
||||
|
||||
c->~Compressor();
|
||||
compressor_allocator.release(c);
|
||||
compressor_allocator.release((CompressorMem*)c);
|
||||
}
|
||||
|
@ -59,7 +59,7 @@
|
||||
// and additionally returns the file status (size and mtime).
|
||||
|
||||
// rationale: see DirIterator definition in header.
|
||||
struct DirIterator_
|
||||
struct PosixDirIterator
|
||||
{
|
||||
DIR* os_dir;
|
||||
|
||||
@ -68,18 +68,23 @@ struct DirIterator_
|
||||
// we latch dir_open's path and append entry name every dir_next_ent call.
|
||||
// this is also the storage to which DirEnt.name points!
|
||||
// PathPackage avoids repeated memory allocs and strlen() overhead.
|
||||
PathPackage pp;
|
||||
//
|
||||
// it can't be stored here directly because then the struct would
|
||||
// no longer fit in HDATA; we'll allocate it separately.
|
||||
PathPackage* pp;
|
||||
};
|
||||
|
||||
cassert(sizeof(DirIterator_) <= sizeof(DirIterator));
|
||||
cassert(sizeof(PosixDirIterator) <= DIR_ITERATOR_OPAQUE_SIZE);
|
||||
|
||||
static SingleAllocator<PathPackage> pp_allocator;
|
||||
|
||||
|
||||
// prepare to iterate (once) over entries in the given directory.
|
||||
// if ERR_OK is returned, <d> is ready for subsequent dir_next_ent calls and
|
||||
// must be freed via dir_close.
|
||||
LibError dir_open(const char* P_path, DirIterator* d_)
|
||||
LibError dir_open(const char* P_path, DirIterator* di)
|
||||
{
|
||||
DirIterator_* d = (DirIterator_*)d_;
|
||||
PosixDirIterator* pdi = (PosixDirIterator*)di->opaque;
|
||||
|
||||
char n_path[PATH_MAX];
|
||||
// HACK: allow calling with a full (absolute) native path.
|
||||
@ -95,11 +100,16 @@ LibError dir_open(const char* P_path, DirIterator* d_)
|
||||
RETURN_ERR(file_make_full_native_path(P_path, n_path));
|
||||
}
|
||||
|
||||
d->os_dir = opendir(n_path);
|
||||
if(!d->os_dir)
|
||||
pdi->pp = pp_allocator.alloc();
|
||||
if(!pdi->pp)
|
||||
WARN_RETURN(ERR_NO_MEM);
|
||||
|
||||
errno = 0;
|
||||
pdi->os_dir = opendir(n_path);
|
||||
if(!pdi->os_dir)
|
||||
return LibError_from_errno();
|
||||
|
||||
RETURN_ERR(path_package_set_dir(&d->pp, n_path));
|
||||
(void)path_package_set_dir(pdi->pp, n_path);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
@ -107,13 +117,13 @@ LibError dir_open(const char* P_path, DirIterator* d_)
|
||||
// return ERR_DIR_END if all entries have already been returned once,
|
||||
// another negative error code, or ERR_OK on success, in which case <ent>
|
||||
// describes the next (order is unspecified) directory entry.
|
||||
LibError dir_next_ent(DirIterator* d_, DirEnt* ent)
|
||||
LibError dir_next_ent(DirIterator* di, DirEnt* ent)
|
||||
{
|
||||
DirIterator_* d = (DirIterator_*)d_;
|
||||
PosixDirIterator* pdi = (PosixDirIterator*)di->opaque;
|
||||
|
||||
get_another_entry:
|
||||
errno = 0;
|
||||
struct dirent* os_ent = readdir(d->os_dir);
|
||||
struct dirent* os_ent = readdir(pdi->os_dir);
|
||||
if(!os_ent)
|
||||
{
|
||||
// no error, just no more entries to return
|
||||
@ -124,21 +134,21 @@ get_another_entry:
|
||||
|
||||
// copy os_ent.name[]; we need it for stat() #if !OS_WIN and
|
||||
// return it as ent.name (since os_ent.name[] is volatile).
|
||||
path_package_append_file(&d->pp, os_ent->d_name);
|
||||
const char* name = d->pp.end;
|
||||
path_package_append_file(pdi->pp, os_ent->d_name);
|
||||
const char* name = pdi->pp->end;
|
||||
|
||||
// get file information (mode, size, mtime)
|
||||
struct stat s;
|
||||
#if OS_WIN
|
||||
// .. wposix readdir has enough information to return dirent
|
||||
// status directly (much faster than calling stat).
|
||||
CHECK_ERR(readdir_stat_np(d->os_dir, &s));
|
||||
CHECK_ERR(readdir_stat_np(pdi->os_dir, &s));
|
||||
#else
|
||||
// .. call regular stat().
|
||||
// we need the full pathname for this. don't use path_append because
|
||||
// it would unnecessarily call strlen.
|
||||
|
||||
CHECK_ERR(stat(d->pp.path, &s));
|
||||
CHECK_ERR(stat(pdi->pp.path, &s));
|
||||
#endif
|
||||
|
||||
// skip "undesirable" entries that POSIX readdir returns:
|
||||
@ -163,10 +173,14 @@ get_another_entry:
|
||||
|
||||
// indicate the directory iterator is no longer needed; all resources it
|
||||
// held are freed.
|
||||
LibError dir_close(DirIterator* d_)
|
||||
LibError dir_close(DirIterator* di)
|
||||
{
|
||||
DirIterator_* d = (DirIterator_*)d_;
|
||||
WARN_ERR(closedir(d->os_dir));
|
||||
PosixDirIterator* pdi = (PosixDirIterator*)di->opaque;
|
||||
pp_allocator.release(pdi->pp);
|
||||
|
||||
errno = 0;
|
||||
if(closedir(pdi->os_dir) < 0)
|
||||
return LibError_from_errno();
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
@ -28,6 +28,9 @@
|
||||
|
||||
extern LibError file_init();
|
||||
|
||||
// used by vfs_redirector to call various file objects' methods.
|
||||
struct FileProvider_VTbl;
|
||||
|
||||
|
||||
//
|
||||
// path conversion functions (native <--> portable),
|
||||
@ -78,6 +81,8 @@ extern const char* file_get_random_name();
|
||||
// dir_next_ent
|
||||
//
|
||||
|
||||
const size_t DIR_ITERATOR_OPAQUE_SIZE = 40;
|
||||
|
||||
// layer on top of POSIX opendir/readdir/closedir that handles
|
||||
// portable -> native path conversion, ignores non-file/directory entries,
|
||||
// and additionally returns the file status (size and mtime).
|
||||
@ -94,7 +99,15 @@ extern const char* file_get_random_name();
|
||||
// instantiate this.
|
||||
struct DirIterator
|
||||
{
|
||||
char opaque[PATH_MAX+32];
|
||||
// safety check - used to verify correct calling of dir_filtered_next_ent
|
||||
const char* filter;
|
||||
// .. has filter been assigned? this flag is necessary because
|
||||
// there are no "invalid" filter values we can use.
|
||||
uint filter_latched : 1;
|
||||
|
||||
const FileProvider_VTbl* type;
|
||||
|
||||
char opaque[DIR_ITERATOR_OPAQUE_SIZE];
|
||||
};
|
||||
|
||||
class TFile;
|
||||
@ -165,8 +178,6 @@ typedef LibError (*FileCB)(const char* name, const struct stat* s, uintptr_t mem
|
||||
// return first error encountered while listing files, or 0 on success.
|
||||
extern LibError file_enum(const char* dir, FileCB cb, uintptr_t user);
|
||||
|
||||
struct FileProvider_VTbl;
|
||||
|
||||
// chosen for semi-nice 48 byte total struct File size.
|
||||
// each implementation checks if this is enough.
|
||||
const size_t FILE_OPAQUE_SIZE = 52;
|
||||
|
@ -63,6 +63,27 @@ const size_t FILE_BLOCK_SIZE = 32*KiB;
|
||||
extern LibError file_io_call_back(const void* block, size_t size,
|
||||
FileIOCB cb, uintptr_t ctx, size_t& bytes_processed);
|
||||
|
||||
|
||||
// retrieve the next (order is unspecified) dir entry matching <filter>.
|
||||
// return 0 on success, ERR_DIR_END if no matching entry was found,
|
||||
// or a negative error code on failure.
|
||||
// filter values:
|
||||
// - 0: anything;
|
||||
// - "/": any subdirectory;
|
||||
// - "/|<pattern>": any subdirectory, or as below with <pattern>;
|
||||
// - <pattern>: any file whose name matches; ? and * wildcards are allowed.
|
||||
//
|
||||
// note that the directory entries are only scanned once; after the
|
||||
// end is reached (-> ERR_DIR_END returned), no further entries can
|
||||
// be retrieved, even if filter changes (which shouldn't happen - see impl).
|
||||
//
|
||||
// rationale: we do not sort directory entries alphabetically here.
|
||||
// most callers don't need it and the overhead is considerable
|
||||
// (we'd have to store all entries in a vector). it is left up to
|
||||
// higher-level code such as VfsUtil.
|
||||
extern LibError dir_filtered_next_ent(DirIterator* di, DirEnt* ent, const char* filter);
|
||||
|
||||
|
||||
// used by file.cpp and file_io.cpp
|
||||
struct PosixFile
|
||||
{
|
||||
|
@ -88,6 +88,154 @@ LibError file_enum(const char* P_path, const FileCB cb, const uintptr_t user)
|
||||
|
||||
|
||||
|
||||
// retrieve the next (order is unspecified) dir entry matching <filter>.
|
||||
// return 0 on success, ERR_DIR_END if no matching entry was found,
|
||||
// or a negative error code on failure.
|
||||
// filter values:
|
||||
// - 0: anything;
|
||||
// - "/": any subdirectory;
|
||||
// - "/|<pattern>": any subdirectory, or as below with <pattern>;
|
||||
// - <pattern>: any file whose name matches; ? and * wildcards are allowed.
|
||||
//
|
||||
// note that the directory entries are only scanned once; after the
|
||||
// end is reached (-> ERR_DIR_END returned), no further entries can
|
||||
// be retrieved, even if filter changes (which shouldn't happen - see impl).
|
||||
//
|
||||
// rationale: we do not sort directory entries alphabetically here.
|
||||
// most callers don't need it and the overhead is considerable
|
||||
// (we'd have to store all entries in a vector). it is left up to
|
||||
// higher-level code such as VfsUtil.
|
||||
LibError dir_filtered_next_ent(DirIterator* di, DirEnt* ent, const char* filter)
|
||||
{
|
||||
// warn if scanning the directory twice with different filters
|
||||
// (this used to work with dir/file because they were stored separately).
|
||||
// it is imaginable that someone will want to change it, but until
|
||||
// there's a good reason, leave this check in. note: only comparing
|
||||
// pointers isn't 100% certain, but it's safe enough and easy.
|
||||
if(!di->filter_latched)
|
||||
{
|
||||
di->filter = filter;
|
||||
di->filter_latched = 1;
|
||||
}
|
||||
if(di->filter != filter)
|
||||
debug_warn("filter has changed for this directory. are you scanning it twice?");
|
||||
|
||||
bool want_dir = true;
|
||||
if(filter)
|
||||
{
|
||||
// directory
|
||||
if(filter[0] == '/')
|
||||
{
|
||||
// .. and also files
|
||||
if(filter[1] == '|')
|
||||
filter += 2;
|
||||
}
|
||||
// file only
|
||||
else
|
||||
want_dir = false;
|
||||
}
|
||||
|
||||
// loop until ent matches what is requested, or end of directory.
|
||||
for(;;)
|
||||
{
|
||||
RETURN_ERR(xdir_next_ent(di, ent));
|
||||
|
||||
if(DIRENT_IS_DIR(ent))
|
||||
{
|
||||
if(want_dir)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// (note: filter = 0 matches anything)
|
||||
if(match_wildcard(ent->name, filter))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// call <cb> for each entry matching <user_filter> (see vfs_next_dirent) in
|
||||
// directory <path>; if flags & VFS_DIR_RECURSIVE, entries in
|
||||
// subdirectories are also returned.
|
||||
//
|
||||
// note: EnumDirEntsCB path and ent are only valid during the callback.
|
||||
LibError vfs_dir_enum(const char* start_path, uint flags, const char* user_filter,
|
||||
DirEnumCB cb, void* context)
|
||||
{
|
||||
debug_assert((flags & ~(VFS_DIR_RECURSIVE)) == 0);
|
||||
const bool recursive = (flags & VFS_DIR_RECURSIVE) != 0;
|
||||
|
||||
char filter_buf[PATH_MAX];
|
||||
const char* filter = user_filter;
|
||||
bool user_filter_wants_dirs = true;
|
||||
if(user_filter)
|
||||
{
|
||||
if(user_filter[0] != '/')
|
||||
user_filter_wants_dirs = false;
|
||||
|
||||
// we need subdirectories and the caller hasn't already requested them
|
||||
if(recursive && !user_filter_wants_dirs)
|
||||
{
|
||||
snprintf(filter_buf, sizeof(filter_buf), "/|%s", user_filter);
|
||||
filter = filter_buf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// note: FIFO queue instead of recursion is much more efficient
|
||||
// (less stack usage; avoids seeks by reading all entries in a
|
||||
// directory consecutively)
|
||||
|
||||
std::queue<const char*> dir_queue;
|
||||
dir_queue.push(file_make_unique_fn_copy(start_path));
|
||||
|
||||
// for each directory:
|
||||
do
|
||||
{
|
||||
// get current directory path from queue
|
||||
// note: can't refer to the queue contents - those are invalidated
|
||||
// as soon as a directory is pushed onto it.
|
||||
PathPackage pp;
|
||||
(void)path_package_set_dir(&pp, dir_queue.front());
|
||||
dir_queue.pop();
|
||||
|
||||
Handle hdir = vfs_dir_open(pp.path);
|
||||
if(hdir <= 0)
|
||||
{
|
||||
debug_warn("vfs_open_dir failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
// for each entry (file, subdir) in directory:
|
||||
DirEnt ent;
|
||||
while(vfs_dir_next_ent(hdir, &ent, filter) == 0)
|
||||
{
|
||||
// build complete path (DirEnt only stores entry name)
|
||||
(void)path_package_append_file(&pp, ent.name);
|
||||
const char* atom_path = file_make_unique_fn_copy(pp.path);
|
||||
|
||||
if(DIRENT_IS_DIR(&ent))
|
||||
{
|
||||
if(recursive)
|
||||
dir_queue.push(atom_path);
|
||||
|
||||
if(user_filter_wants_dirs)
|
||||
cb(atom_path, &ent, context);
|
||||
}
|
||||
else
|
||||
cb(atom_path, &ent, context);
|
||||
}
|
||||
|
||||
vfs_dir_close(hdir);
|
||||
}
|
||||
while(!dir_queue.empty());
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
// fill V_next_fn (which must be big enough for PATH_MAX chars) with
|
||||
// the next numbered filename according to the pattern defined by V_fn_fmt.
|
||||
|
@ -92,14 +92,8 @@
|
||||
|
||||
struct VDir
|
||||
{
|
||||
TreeDirIterator it;
|
||||
uint it_valid : 1; // <it> will be closed iff == 1
|
||||
|
||||
// safety check
|
||||
const char* filter;
|
||||
// has filter been assigned? this flag is necessary because there are no
|
||||
// "invalid" filter values we can use.
|
||||
uint filter_latched : 1;
|
||||
DirIterator di;
|
||||
uint di_valid : 1; // <di> will be closed iff == 1
|
||||
};
|
||||
|
||||
H_TYPE_DEFINE(VDir);
|
||||
@ -110,12 +104,12 @@ static void VDir_init(VDir* UNUSED(vd), va_list UNUSED(args))
|
||||
|
||||
static void VDir_dtor(VDir* vd)
|
||||
{
|
||||
// note: TreeDirIterator has no way of checking if it's valid;
|
||||
// note: DirIterator has no way of checking if it's valid;
|
||||
// we must therefore only free it if reload() succeeded.
|
||||
if(vd->it_valid)
|
||||
if(vd->di_valid)
|
||||
{
|
||||
tree_dir_close(&vd->it);
|
||||
vd->it_valid = 0;
|
||||
xdir_close(&vd->di);
|
||||
vd->di_valid = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,23 +120,23 @@ static LibError VDir_reload(VDir* vd, const char* path, Handle UNUSED(hvd))
|
||||
char V_path_slash[PATH_MAX];
|
||||
RETURN_ERR(path_append(V_path_slash, path, ""));
|
||||
|
||||
RETURN_ERR(tree_dir_open(V_path_slash, &vd->it));
|
||||
vd->it_valid = 1;
|
||||
RETURN_ERR(xdir_open(V_path_slash, &vd->di));
|
||||
vd->di_valid = 1;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static LibError VDir_validate(const VDir* vd)
|
||||
{
|
||||
// note: <it> is opaque and cannot be validated.
|
||||
if(vd->filter && !isprint(vd->filter[0]))
|
||||
// note: <di> is mostly opaque and cannot be validated.
|
||||
if(vd->di.filter && !isprint(vd->di.filter[0]))
|
||||
WARN_RETURN(ERR_1);
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
static LibError VDir_to_string(const VDir* d, char* buf)
|
||||
static LibError VDir_to_string(const VDir* vd, char* buf)
|
||||
{
|
||||
const char* filter = d->filter;
|
||||
if(!d->filter_latched)
|
||||
const char* filter = vd->di.filter;
|
||||
if(!vd->di.filter_latched)
|
||||
filter = "?";
|
||||
if(!filter)
|
||||
filter = "*";
|
||||
@ -153,11 +147,11 @@ static LibError VDir_to_string(const VDir* d, char* buf)
|
||||
|
||||
// open a directory for reading its entries via vfs_next_dirent.
|
||||
// <v_dir> need not end in '/'; we add it if not present.
|
||||
Handle vfs_dir_open(const char* v_dir)
|
||||
Handle vfs_dir_open(const char* V_dir)
|
||||
{
|
||||
// must disallow handle caching because this object is not
|
||||
// copy-equivalent (since the iterator is advanced by each user).
|
||||
return h_alloc(H_VDir, v_dir, RES_NO_CACHE);
|
||||
return h_alloc(H_VDir, V_dir, RES_NO_CACHE);
|
||||
}
|
||||
|
||||
|
||||
@ -188,136 +182,10 @@ LibError vfs_dir_close(Handle& hd)
|
||||
LibError vfs_dir_next_ent(const Handle hd, DirEnt* ent, const char* filter)
|
||||
{
|
||||
H_DEREF(hd, VDir, vd);
|
||||
|
||||
// warn if scanning the directory twice with different filters
|
||||
// (this used to work with dir/file because they were stored separately).
|
||||
// it is imaginable that someone will want to change it, but until
|
||||
// there's a good reason, leave this check in. note: only comparing
|
||||
// pointers isn't 100% certain, but it's safe enough and easy.
|
||||
if(!vd->filter_latched)
|
||||
{
|
||||
vd->filter = filter;
|
||||
vd->filter_latched = 1;
|
||||
}
|
||||
if(vd->filter != filter)
|
||||
debug_warn("filter has changed for this directory. are you scanning it twice?");
|
||||
|
||||
bool want_dir = true;
|
||||
if(filter)
|
||||
{
|
||||
// directory
|
||||
if(filter[0] == '/')
|
||||
{
|
||||
// .. and also files
|
||||
if(filter[1] == '|')
|
||||
filter += 2;
|
||||
}
|
||||
// file only
|
||||
else
|
||||
want_dir = false;
|
||||
}
|
||||
|
||||
// loop until ent matches what is requested, or end of directory.
|
||||
for(;;)
|
||||
{
|
||||
RETURN_ERR(tree_dir_next_ent(&vd->it, ent));
|
||||
|
||||
if(DIRENT_IS_DIR(ent))
|
||||
{
|
||||
if(want_dir)
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// (note: filter = 0 matches anything)
|
||||
if(match_wildcard(ent->name, filter))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ERR_OK;
|
||||
return dir_filtered_next_ent(&vd->di, ent, filter);
|
||||
}
|
||||
|
||||
|
||||
// call <cb> for each entry matching <user_filter> (see vfs_next_dirent) in
|
||||
// directory <path>; if flags & VFS_DIR_RECURSIVE, entries in
|
||||
// subdirectories are also returned.
|
||||
//
|
||||
// note: EnumDirEntsCB path and ent are only valid during the callback.
|
||||
LibError vfs_dir_enum(const char* start_path, uint flags, const char* user_filter,
|
||||
DirEnumCB cb, void* context)
|
||||
{
|
||||
debug_assert((flags & ~(VFS_DIR_RECURSIVE)) == 0);
|
||||
const bool recursive = (flags & VFS_DIR_RECURSIVE) != 0;
|
||||
|
||||
char filter_buf[PATH_MAX];
|
||||
const char* filter = user_filter;
|
||||
bool user_filter_wants_dirs = true;
|
||||
if(user_filter)
|
||||
{
|
||||
if(user_filter[0] != '/')
|
||||
user_filter_wants_dirs = false;
|
||||
|
||||
// we need subdirectories and the caller hasn't already requested them
|
||||
if(recursive && !user_filter_wants_dirs)
|
||||
{
|
||||
snprintf(filter_buf, sizeof(filter_buf), "/|%s", user_filter);
|
||||
filter = filter_buf;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// note: FIFO queue instead of recursion is much more efficient
|
||||
// (less stack usage; avoids seeks by reading all entries in a
|
||||
// directory consecutively)
|
||||
|
||||
std::queue<const char*> dir_queue;
|
||||
dir_queue.push(file_make_unique_fn_copy(start_path));
|
||||
|
||||
// for each directory:
|
||||
do
|
||||
{
|
||||
// get current directory path from queue
|
||||
// note: can't refer to the queue contents - those are invalidated
|
||||
// as soon as a directory is pushed onto it.
|
||||
PathPackage pp;
|
||||
(void)path_package_set_dir(&pp, dir_queue.front());
|
||||
dir_queue.pop();
|
||||
|
||||
Handle hdir = vfs_dir_open(pp.path);
|
||||
if(hdir <= 0)
|
||||
{
|
||||
debug_warn("vfs_open_dir failed");
|
||||
continue;
|
||||
}
|
||||
|
||||
// for each entry (file, subdir) in directory:
|
||||
DirEnt ent;
|
||||
while(vfs_dir_next_ent(hdir, &ent, filter) == 0)
|
||||
{
|
||||
// build complete path (DirEnt only stores entry name)
|
||||
(void)path_package_append_file(&pp, ent.name);
|
||||
const char* atom_path = file_make_unique_fn_copy(pp.path);
|
||||
|
||||
if(DIRENT_IS_DIR(&ent))
|
||||
{
|
||||
if(recursive)
|
||||
dir_queue.push(atom_path);
|
||||
|
||||
if(user_filter_wants_dirs)
|
||||
cb(atom_path, &ent, context);
|
||||
}
|
||||
else
|
||||
cb(atom_path, &ent, context);
|
||||
}
|
||||
|
||||
vfs_dir_close(hdir);
|
||||
}
|
||||
while(!dir_queue.empty());
|
||||
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@ -643,7 +511,7 @@ static LibError VIo_reload(VIo* vio, const char* UNUSED(fn), Handle UNUSED(h))
|
||||
off_t ofs = vf->ofs;
|
||||
vf->ofs += (off_t)size;
|
||||
|
||||
return xfile_io_validate(&vf->f, ofs, size, buf, &vio->io);
|
||||
return xfile_io_issue(&vf->f, ofs, size, buf, &vio->io);
|
||||
}
|
||||
|
||||
static LibError VIo_validate(const VIo* vio)
|
||||
|
@ -56,6 +56,7 @@ LibError file_open_vfs(const char* V_path, uint flags, TFile* tf,
|
||||
static const FileProvider_VTbl archive_vtbl =
|
||||
{
|
||||
vtbl_magic,
|
||||
0,0,0, // not supported for archives ATM
|
||||
afile_open_vfs, afile_close, afile_validate,
|
||||
afile_io_issue, afile_io_has_completed, afile_io_wait, afile_io_discard, afile_io_validate,
|
||||
afile_read,
|
||||
@ -65,12 +66,24 @@ static const FileProvider_VTbl archive_vtbl =
|
||||
static const FileProvider_VTbl file_vtbl =
|
||||
{
|
||||
vtbl_magic,
|
||||
dir_open, dir_next_ent, dir_close,
|
||||
file_open_vfs, file_close, file_validate,
|
||||
file_io_issue, file_io_has_completed, file_io_wait, file_io_discard, file_io_validate,
|
||||
file_io,
|
||||
file_map, file_unmap
|
||||
};
|
||||
|
||||
// see FileProvider_VTbl decl for details on why this is so empty.
|
||||
static const FileProvider_VTbl tree_vtbl =
|
||||
{
|
||||
vtbl_magic,
|
||||
tree_dir_open, tree_dir_next_ent, tree_dir_close,
|
||||
0, 0, 0,
|
||||
0, 0, 0, 0, 0,
|
||||
0,
|
||||
0, 0
|
||||
};
|
||||
|
||||
|
||||
|
||||
// rationale for not using virtual functions for file_open vs afile_open:
|
||||
@ -91,6 +104,36 @@ static LibError vtbl_validate(const FileProvider_VTbl* vtbl)
|
||||
#define CHECK_VTBL(type) RETURN_ERR(vtbl_validate(type))
|
||||
|
||||
|
||||
//
|
||||
// directory entry enumeration
|
||||
//
|
||||
|
||||
LibError xdir_open(const char* dir, DirIterator* di)
|
||||
{
|
||||
// HACK: it is unclear ATM how to set this properly. assume tree_dir_* is
|
||||
// the only user ATM.
|
||||
di->type = &tree_vtbl;
|
||||
CHECK_VTBL(di->type);
|
||||
return di->type->dir_open(dir, di);
|
||||
}
|
||||
|
||||
LibError xdir_next_ent(DirIterator* di, DirEnt* ent)
|
||||
{
|
||||
CHECK_VTBL(di->type);
|
||||
return di->type->dir_next_ent(di, ent);
|
||||
}
|
||||
|
||||
LibError xdir_close(DirIterator* di)
|
||||
{
|
||||
CHECK_VTBL(di->type);
|
||||
return di->type->dir_close(di);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// file object
|
||||
//
|
||||
|
||||
bool xfile_is_open(const File* f)
|
||||
{
|
||||
// not currently in use
|
||||
@ -101,11 +144,6 @@ bool xfile_is_open(const File* f)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// file object
|
||||
//
|
||||
|
||||
LibError xfile_open(const char* V_path, uint flags, TFile* tf, File* f)
|
||||
{
|
||||
// find out who is providing this file
|
||||
@ -116,7 +154,7 @@ char c = mount_get_type(m);
|
||||
const FileProvider_VTbl* vtbl = (c == 'F')? &file_vtbl : &archive_vtbl;
|
||||
|
||||
CHECK_VTBL(vtbl);
|
||||
RETURN_ERR(vtbl->open(V_path, flags, tf, f));
|
||||
RETURN_ERR(vtbl->file_open(V_path, flags, tf, f));
|
||||
|
||||
// success
|
||||
// note: don't assign these unless we succeed to avoid the
|
||||
@ -133,7 +171,7 @@ LibError xfile_close(File* f)
|
||||
// note: this takes care of checking the vtbl.
|
||||
if(!xfile_is_open(f))
|
||||
return ERR_OK;
|
||||
LibError ret = f->type->close(f);
|
||||
LibError ret = f->type->file_close(f);
|
||||
f->type = 0;
|
||||
return ret;
|
||||
}
|
||||
@ -141,15 +179,15 @@ LibError xfile_close(File* f)
|
||||
LibError xfile_validate(const File* f)
|
||||
{
|
||||
CHECK_VTBL(f->type);
|
||||
return f->type->validate(f);
|
||||
return f->type->file_validate(f);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// async IO
|
||||
// IO
|
||||
//
|
||||
|
||||
LibError xfile_io_validate(File* f, off_t ofs, size_t size, void* buf, FileIo* io)
|
||||
LibError xfile_io_issue(File* f, off_t ofs, size_t size, void* buf, FileIo* io)
|
||||
{
|
||||
io->type = f->type;
|
||||
CHECK_VTBL(io->type);
|
||||
@ -180,11 +218,6 @@ LibError xfile_io_validate(const FileIo* io)
|
||||
return io->type->io_validate(io);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// sync IO
|
||||
//
|
||||
|
||||
ssize_t xfile_io(File* f, off_t ofs, size_t size, FileIOBuf* pbuf, FileIOCB cb, uintptr_t ctx)
|
||||
{
|
||||
CHECK_VTBL(f->type);
|
||||
|
@ -30,20 +30,28 @@ struct FileProvider_VTbl
|
||||
// note: no need to store name of this provider for debugging purposes;
|
||||
// that can be deduced from the function pointers below.
|
||||
|
||||
// directory entry enumeration
|
||||
// note: these don't really fit in with the other methods.
|
||||
// they make sense for both the VFS tree as well as the concrete
|
||||
// file providers underlying it. due to this overlap and to allow
|
||||
// file.cpp's next_ent function to access dir_filtered_next_ent,
|
||||
// it is included anyway.
|
||||
LibError (*dir_open)(const char* dir, DirIterator* di);
|
||||
LibError (*dir_next_ent)(DirIterator* di, DirEnt* ent);
|
||||
LibError (*dir_close)(DirIterator* di);
|
||||
|
||||
// file objects
|
||||
LibError (*open)(const char* V_path, uint flags, TFile* tf, File* f);
|
||||
LibError (*close)(File* f);
|
||||
LibError (*validate)(const File* f);
|
||||
LibError (*file_open)(const char* V_path, uint flags, TFile* tf, File* f);
|
||||
LibError (*file_close)(File* f);
|
||||
LibError (*file_validate)(const File* f);
|
||||
|
||||
// async IO
|
||||
LibError (*io_issue)(File* f, off_t ofs, size_t size, void* buf, FileIo* xio);
|
||||
int (*io_has_completed)(FileIo* xio);
|
||||
LibError (*io_wait)(FileIo* xio, void*& p, size_t& size);
|
||||
LibError (*io_discard)(FileIo* xio);
|
||||
LibError (*io_validate)(const FileIo* xio);
|
||||
|
||||
// sync IO
|
||||
ssize_t (*io)(File* f, off_t ofs, size_t size, FileIOBuf* pbuf, FileIOCB cb, uintptr_t ctx);
|
||||
// IO
|
||||
LibError (*io_issue)(File* f, off_t ofs, size_t size, void* buf, FileIo* io);
|
||||
int (*io_has_completed)(FileIo* io);
|
||||
LibError (*io_wait)(FileIo* io, void*& p, size_t& size);
|
||||
LibError (*io_discard)(FileIo* io);
|
||||
LibError (*io_validate)(const FileIo* io);
|
||||
ssize_t (*io)(File* f, off_t ofs, size_t size, FileIOBuf* pbuf, FileIOCB cb, uintptr_t ctx);
|
||||
|
||||
// file mapping
|
||||
LibError (*map)(File* f, void*& p, size_t& size);
|
||||
@ -51,19 +59,21 @@ struct FileProvider_VTbl
|
||||
};
|
||||
|
||||
|
||||
extern bool xfile_is_open(const File* f);
|
||||
extern LibError xdir_open(const char* dir, DirIterator* di);
|
||||
extern LibError xdir_next_ent(DirIterator* di, DirEnt* ent);
|
||||
extern LibError xdir_close(DirIterator* di);
|
||||
|
||||
extern bool xfile_is_open(const File* f);
|
||||
extern LibError xfile_open(const char* V_path, uint flags, TFile* tf, File* f);
|
||||
extern LibError xfile_close(File* f);
|
||||
extern LibError xfile_validate(const File* f);
|
||||
|
||||
extern LibError xfile_io_validate(File* f, off_t ofs, size_t size, void* buf, FileIo* xio);
|
||||
extern int xfile_io_has_completed(FileIo* xio);
|
||||
extern LibError xfile_io_wait(FileIo* xio, void*& p, size_t& size);
|
||||
extern LibError xfile_io_discard(FileIo* xio);
|
||||
extern LibError xfile_io_validate(const FileIo* xio);
|
||||
|
||||
extern ssize_t xfile_io(File* f, off_t ofs, size_t size, FileIOBuf* pbuf, FileIOCB cb, uintptr_t ctx);
|
||||
extern LibError xfile_io_issue(File* f, off_t ofs, size_t size, void* buf, FileIo* io);
|
||||
extern int xfile_io_has_completed(FileIo* io);
|
||||
extern LibError xfile_io_wait(FileIo* io, void*& p, size_t& size);
|
||||
extern LibError xfile_io_discard(FileIo* io);
|
||||
extern LibError xfile_io_validate(const FileIo* io);
|
||||
extern ssize_t xfile_io(File* f, off_t ofs, size_t size, FileIOBuf* pbuf, FileIOCB cb, uintptr_t ctx);
|
||||
|
||||
extern LibError xfile_map(File* f, void*& p, size_t& size);
|
||||
extern LibError xfile_unmap(File* f);
|
||||
|
@ -600,7 +600,7 @@ LibError tree_lookup(const char* path, TFile** pfile, uint flags)
|
||||
|
||||
|
||||
// rationale: see DirIterator definition in file.h.
|
||||
struct TreeDirIterator_
|
||||
struct TreeDirIterator
|
||||
{
|
||||
TChildren::iterator it;
|
||||
|
||||
@ -612,12 +612,12 @@ struct TreeDirIterator_
|
||||
TDir* td;
|
||||
};
|
||||
|
||||
cassert(sizeof(TreeDirIterator_) <= sizeof(TreeDirIterator));
|
||||
cassert(sizeof(TreeDirIterator) <= DIR_ITERATOR_OPAQUE_SIZE);
|
||||
|
||||
|
||||
LibError tree_dir_open(const char* path_slash, TreeDirIterator* d_)
|
||||
LibError tree_dir_open(const char* path_slash, DirIterator* di)
|
||||
{
|
||||
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
||||
TreeDirIterator* tdi = (TreeDirIterator*)di->opaque;
|
||||
|
||||
TDir* td;
|
||||
CHECK_ERR(tree_lookup_dir(path_slash, &td));
|
||||
@ -631,21 +631,21 @@ LibError tree_dir_open(const char* path_slash, TreeDirIterator* d_)
|
||||
// more overhead (we have hundreds of directories) and is unnecessary.
|
||||
tree_lock();
|
||||
|
||||
d->it = td->begin();
|
||||
d->end = td->end();
|
||||
d->td = td;
|
||||
tdi->it = td->begin();
|
||||
tdi->end = td->end();
|
||||
tdi->td = td;
|
||||
return ERR_OK;
|
||||
}
|
||||
|
||||
|
||||
LibError tree_dir_next_ent(TreeDirIterator* d_, DirEnt* ent)
|
||||
LibError tree_dir_next_ent(DirIterator* di, DirEnt* ent)
|
||||
{
|
||||
TreeDirIterator_* d = (TreeDirIterator_*)d_;
|
||||
TreeDirIterator* tdi = (TreeDirIterator*)di->opaque;
|
||||
|
||||
if(d->it == d->end)
|
||||
if(tdi->it == tdi->end)
|
||||
return ERR_DIR_END; // NOWARN
|
||||
|
||||
const TNode* node = *(d->it++);
|
||||
const TNode* node = *(tdi->it++);
|
||||
ent->name = node->name;
|
||||
|
||||
// set size and mtime fields depending on node type:
|
||||
@ -672,7 +672,7 @@ LibError tree_dir_next_ent(TreeDirIterator* d_, DirEnt* ent)
|
||||
}
|
||||
|
||||
|
||||
LibError tree_dir_close(TreeDirIterator* UNUSED(d))
|
||||
LibError tree_dir_close(DirIterator* UNUSED(d))
|
||||
{
|
||||
tree_unlock();
|
||||
|
||||
|
@ -91,15 +91,9 @@ extern LibError tree_lookup(const char* path, TFile** ptf, uint flags = 0);
|
||||
extern LibError tree_lookup_dir(const char* path, TDir** ptd, uint flags = 0);
|
||||
|
||||
|
||||
// documentation and rationale: see file.h's dir_next_ent interface
|
||||
struct TreeDirIterator
|
||||
{
|
||||
char opaque[32];
|
||||
};
|
||||
|
||||
extern LibError tree_dir_open(const char* path_slash, TreeDirIterator* d);
|
||||
extern LibError tree_dir_next_ent(TreeDirIterator* d, DirEnt* ent);
|
||||
extern LibError tree_dir_close(TreeDirIterator* d);
|
||||
extern LibError tree_dir_open(const char* path_slash, DirIterator* di);
|
||||
extern LibError tree_dir_next_ent(DirIterator* di, DirEnt* ent);
|
||||
extern LibError tree_dir_close(DirIterator* di);
|
||||
|
||||
|
||||
// given a file that is stored on disk and its VFS path,
|
||||
|
@ -600,7 +600,7 @@ LibError zip_archive_create(const char* zip_filename, ZipArchive** pza)
|
||||
RETURN_ERR(file_open(zip_filename, FILE_WRITE|FILE_NO_AIO, &za_copy.f));
|
||||
RETURN_ERR(pool_create(&za_copy.cdfhs, 10*MiB, 0));
|
||||
|
||||
ZipArchive* za = (ZipArchive*)za_mgr.alloc();
|
||||
ZipArchive* za = za_mgr.alloc();
|
||||
if(!za)
|
||||
WARN_RETURN(ERR_NO_MEM);
|
||||
*za = za_copy;
|
||||
|
Loading…
Reference in New Issue
Block a user