X-Git-Url: https://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/0a55ae84545c5e5a45e2240c06bd77a1917f7d3e..b228d2b354248b62d70a582a37693cff829fe0c6:/src/uobject/vfs.c diff --git a/src/uobject/vfs.c b/src/uobject/vfs.c index 5465144..8afb49b 100644 --- a/src/uobject/vfs.c +++ b/src/uobject/vfs.c @@ -23,6 +23,7 @@ #include #include +#include #include #include "avl.h" @@ -34,12 +35,21 @@ # define IS_DIRSEP(x) ((x) == '/') #endif +/* Number of times the library has been initialized. */ +static int initialized; + +/* Local package definitions. */ struct local_pkg { char *file, *name; }; - static struct avl_table *local_tree; -static int initialized; + +/* Global package search path. */ +static char *search_path; +static size_t search_path_sz; + +/* Package file extensions, in decreasing order of precedence. */ +static const char u_pkg_exts[][5] = { ".u", ".utx", ".uax", ".umx", ".unr" }; static int localcmp(const void *_a, const void *_b, void *_data) { @@ -115,6 +125,56 @@ const char *u_pkg_vfs_add_local(const char *name, const char *file) return spec->name; } +/* Enlarge the search path buffer so that it can store at least need bytes. */ +static int expand_search_path(size_t need) +{ + size_t want = search_path_sz; + if (want == 0) want = 1; + + while (want < need) + want *= 2; + + if (want > search_path_sz) { + char *new = realloc(search_path, want); + if (!new) { + return -1; + } + + search_path = new; + search_path_sz = want; + } + + return 0; +} + +const char *u_pkg_vfs_get_search_path(void) +{ + return search_path ? search_path : ""; +} + +int u_pkg_vfs_set_search_path(const char *path) +{ + if (expand_search_path(strlen(path)+1) != 0) + return -1; + strcpy(search_path, path); + return 0; +} + +int u_pkg_vfs_add_search_dir(const char *path) +{ + size_t end = search_path ? strlen(search_path) : 0; + + if (end == 0) { + return u_pkg_vfs_set_search_path(path); + } + + if (expand_search_path(end + strlen(path) + 2) != 0) + return -1; + search_path[end] = LT_PATHSEP_CHAR; + strcpy(search_path+end+1, path); + return 0; +} + void u_pkg_vfs_del_local(const char *name) { struct local_pkg spec = { .name = (char *)name }, *item; @@ -123,17 +183,70 @@ void u_pkg_vfs_del_local(const char *name) free(item); } -const char *u_pkg_vfs_lookup(const char *name) +struct foreach_state { + const char *name; + struct upkg *f; + size_t sz; + char buf[]; +}; + +static int foreachfile(const char *filename, void *_st) +{ + struct foreach_state **st = _st, *tmp; + size_t need, len; + const char *base; + + /* Check if the filename matches the package name. */ + base = pkgname_base(filename); + if (!base || strcasecmp(base, (*st)->name) != 0) + return 0; + + /* Enlarge the state buffer, if necessary. */ + need = strlen(filename) + sizeof **u_pkg_exts; + if ((*st)->sz < need) { + tmp = realloc(*st, sizeof **st + need); + if (!tmp) + return -1; + *st = tmp; + (*st)->sz = need; + } + + /* Try each file extension, in order. */ + len = sprintf((*st)->buf, "%s", filename); + for (unsigned i = 0; i < sizeof u_pkg_exts / sizeof *u_pkg_exts; i++) { + strcpy((*st)->buf+len, u_pkg_exts[i]); + + (*st)->f = upkg_fopen((*st)->buf); + if ((*st)->f != NULL) + return 1; + } + + return 0; +} + +struct upkg *u_pkg_vfs_open_by_name(const char *name) { struct local_pkg spec = { .name = (char *)name }, *item; + struct foreach_state *st; + struct upkg *f = NULL; - if (!local_tree) + if (!initialized) return NULL; item = avl_find(local_tree, &spec); - if (!item) + if (item) + return upkg_fopen(item->file); + + st = malloc(sizeof *st + 256); + if (!st) return NULL; - return item->file; + *st = (struct foreach_state) { .sz = 256, .name = name }; + + if (lt_dlforeachfile(u_pkg_vfs_get_search_path(), foreachfile, &st) > 0) + f = st->f; + + free(st); + return f; } int u_pkg_vfs_init(void)