285 lines
5.8 KiB
C
285 lines
5.8 KiB
C
/*
|
|
* This software is licensed under the terms of the MIT License.
|
|
* See COPYING for further information.
|
|
* ---
|
|
* Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
|
|
* Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
|
|
*/
|
|
|
|
#include "taisei.h"
|
|
|
|
#include "private.h"
|
|
|
|
typedef struct VFSDir {
|
|
VFSNode *node;
|
|
void *opaque;
|
|
} VFSDir;
|
|
|
|
bool vfs_mount_alias(const char *dst, const char *src) {
|
|
char dstbuf[strlen(dst)+1];
|
|
char srcbuf[strlen(src)+1];
|
|
|
|
dst = vfs_path_normalize(dst, dstbuf);
|
|
src = vfs_path_normalize(src, srcbuf);
|
|
|
|
VFSNode *srcnode = vfs_locate(vfs_root, src);
|
|
|
|
if(!srcnode) {
|
|
vfs_set_error("Node '%s' does not exist", src);
|
|
return false;
|
|
}
|
|
|
|
return vfs_mount_or_decref(vfs_root, dst, srcnode);
|
|
}
|
|
|
|
bool vfs_unmount(const char *path) {
|
|
char p[strlen(path)+1], *parent, *subdir;
|
|
path = vfs_path_normalize(path, p);
|
|
vfs_path_split_right(p, &parent, &subdir);
|
|
VFSNode *node = vfs_locate(vfs_root, parent);
|
|
|
|
if(node) {
|
|
bool result = vfs_node_unmount(node, subdir);
|
|
vfs_decref(node);
|
|
return result;
|
|
}
|
|
|
|
vfs_set_error("Mountpoint root '%s' doesn't exist", parent);
|
|
return false;
|
|
}
|
|
|
|
SDL_RWops* vfs_open(const char *path, VFSOpenMode mode) {
|
|
SDL_RWops *rwops = NULL;
|
|
char p[strlen(path)+1];
|
|
path = vfs_path_normalize(path, p);
|
|
VFSNode *node = vfs_locate(vfs_root, path);
|
|
|
|
if(node) {
|
|
assert(node->funcs != NULL);
|
|
|
|
if(!(rwops = vfs_node_open(node, mode))) {
|
|
vfs_set_error("Can't open '%s': %s", path, vfs_get_error());
|
|
}
|
|
|
|
vfs_decref(node);
|
|
} else {
|
|
vfs_set_error("Node '%s' does not exist", path);
|
|
}
|
|
|
|
return rwops;
|
|
}
|
|
|
|
VFSInfo vfs_query(const char *path) {
|
|
char p[strlen(path)+1];
|
|
path = vfs_path_normalize(path, p);
|
|
VFSNode *node = vfs_locate(vfs_root, path);
|
|
|
|
if(node) {
|
|
// expected to set error on failure
|
|
// note that e.g. a file not existing on a real filesystem
|
|
// is not an error condition. If we can't tell whether it
|
|
// exists or not, that is an error.
|
|
|
|
VFSInfo i = vfs_node_query(node);
|
|
vfs_decref(node);
|
|
return i;
|
|
}
|
|
|
|
vfs_set_error("Node '%s' does not exist", path);
|
|
return VFSINFO_ERROR;
|
|
}
|
|
|
|
bool vfs_mkdir(const char *path) {
|
|
char p[strlen(path)+1];
|
|
path = vfs_path_normalize(path, p);
|
|
VFSNode *node = vfs_locate(vfs_root, path);
|
|
bool ok = false;
|
|
|
|
if(node) {
|
|
ok = vfs_node_mkdir(node, NULL);
|
|
vfs_decref(node);
|
|
|
|
if(ok) {
|
|
return ok;
|
|
}
|
|
}
|
|
|
|
char *parent, *subdir;
|
|
vfs_path_split_right(p, &parent, &subdir);
|
|
node = vfs_locate(vfs_root, parent);
|
|
|
|
if(node) {
|
|
ok = vfs_node_mkdir(node, subdir);
|
|
vfs_decref(node);
|
|
return ok;
|
|
} else {
|
|
vfs_set_error("Node '%s' does not exist", parent);
|
|
vfs_decref(node);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void vfs_mkdir_required(const char *path) {
|
|
if(!vfs_mkdir(path)) {
|
|
log_fatal("%s", vfs_get_error());
|
|
}
|
|
}
|
|
|
|
char* vfs_repr(const char *path, bool try_syspath) {
|
|
char buf[strlen(path)+1];
|
|
path = vfs_path_normalize(path, buf);
|
|
VFSNode *node = vfs_locate(vfs_root, path);
|
|
|
|
if(node) {
|
|
char *p = vfs_node_repr(node, try_syspath);
|
|
vfs_decref(node);
|
|
return p;
|
|
}
|
|
|
|
vfs_set_error("Node '%s' does not exist", path);
|
|
return NULL;
|
|
}
|
|
|
|
bool vfs_print_tree(SDL_RWops *dest, const char *path) {
|
|
char p[strlen(path)+3], *trail;
|
|
vfs_path_normalize(path, p);
|
|
|
|
while(*p && *(trail = strchr(p, 0) - 1) == '/') {
|
|
*trail = 0;
|
|
}
|
|
|
|
VFSNode *node = vfs_locate(vfs_root, p);
|
|
|
|
if(!node) {
|
|
vfs_set_error("Node '%s' does not exist", path);
|
|
return false;
|
|
}
|
|
|
|
if(*p) {
|
|
vfs_path_root_prefix(p);
|
|
}
|
|
|
|
vfs_print_tree_recurse(dest, node, p, "");
|
|
vfs_decref(node);
|
|
return true;
|
|
}
|
|
|
|
VFSDir* vfs_dir_open(const char *path) {
|
|
char p[strlen(path)+1];
|
|
path = vfs_path_normalize(path, p);
|
|
VFSNode *node = vfs_locate(vfs_root, path);
|
|
|
|
if(node) {
|
|
if(node->funcs->iter && vfs_node_query(node).is_dir) {
|
|
VFSDir *d = calloc(1, sizeof(VFSDir));
|
|
d->node = node;
|
|
return d;
|
|
}
|
|
|
|
vfs_set_error("Node '%s' is not a directory", path);
|
|
vfs_decref(node);
|
|
} else {
|
|
vfs_set_error("Node '%s' does not exist", path);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void vfs_dir_close(VFSDir *dir) {
|
|
if(dir) {
|
|
vfs_node_iter_stop(dir->node, &dir->opaque);
|
|
vfs_decref(dir->node);
|
|
free(dir);
|
|
}
|
|
}
|
|
|
|
const char* vfs_dir_read(VFSDir *dir) {
|
|
return vfs_node_iter(dir->node, &dir->opaque);
|
|
}
|
|
|
|
char** vfs_dir_list_sorted(const char *path, size_t *out_size, int (*compare)(const void*, const void*), bool (*filter)(const char*)) {
|
|
char **results = NULL;
|
|
VFSDir *dir = vfs_dir_open(path);
|
|
|
|
if(!dir) {
|
|
return results;
|
|
}
|
|
|
|
size_t real_size = 8;
|
|
results = malloc(sizeof(char*) * real_size);
|
|
*out_size = 0;
|
|
|
|
for(const char *e; (e = vfs_dir_read(dir));) {
|
|
if(filter && !filter(e)) {
|
|
continue;
|
|
}
|
|
|
|
results[(*out_size)++] = strdup(e);
|
|
|
|
if(*out_size >= real_size) {
|
|
real_size *= 2;
|
|
results = realloc(results, sizeof(char*) * real_size);
|
|
}
|
|
}
|
|
|
|
vfs_dir_close(dir);
|
|
|
|
if(*out_size) {
|
|
qsort(results, *out_size, sizeof(char*), compare);
|
|
}
|
|
|
|
return results;
|
|
}
|
|
|
|
void vfs_dir_list_free(char **list, size_t size) {
|
|
if(!list) {
|
|
return;
|
|
}
|
|
|
|
for(size_t i = 0; i < size; ++i) {
|
|
free(list[i]);
|
|
}
|
|
|
|
free(list);
|
|
}
|
|
|
|
int vfs_dir_list_order_ascending(const void *a, const void *b) {
|
|
return strcmp(*(char**)a, *(char**)b);
|
|
}
|
|
|
|
int vfs_dir_list_order_descending(const void *a, const void *b) {
|
|
return strcmp(*(char**)b, *(char**)a);
|
|
}
|
|
|
|
void* vfs_dir_walk(const char *path, void* (*visit)(const char *path, void *arg), void *arg) {
|
|
char npath[strlen(path) + 1];
|
|
vfs_path_normalize(path, npath);
|
|
strip_trailing_slashes(npath);
|
|
|
|
VFSDir *dir = vfs_dir_open(npath);
|
|
void *result = NULL;
|
|
|
|
if(!dir) {
|
|
return result;
|
|
}
|
|
|
|
for(const char *e; (e = vfs_dir_read(dir));) {
|
|
char fullpath[strlen(npath) + strlen(e) + 2];
|
|
snprintf(fullpath, sizeof(fullpath), "%s%c%s", npath, VFS_PATH_SEPARATOR, e);
|
|
|
|
if((result = visit(fullpath, arg))) {
|
|
return result;
|
|
}
|
|
|
|
if(!vfs_query(fullpath).is_dir) {
|
|
continue;
|
|
}
|
|
|
|
if((result = vfs_dir_walk(fullpath, visit, arg))) {
|
|
return result;
|
|
}
|
|
}
|
|
|
|
vfs_dir_close(dir);
|
|
return result;
|
|
}
|