X-Git-Url: http://git.draconx.ca/gitweb/upkg.git/blobdiff_plain/aa3d8014ba02b49656689d1b4951ea79de76a414..b228d2b354248b62d70a582a37693cff829fe0c6:/src/uobject/vfs.c diff --git a/src/uobject/vfs.c b/src/uobject/vfs.c index fcbb3e5..8afb49b 100644 --- a/src/uobject/vfs.c +++ b/src/uobject/vfs.c @@ -35,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) { @@ -116,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; @@ -124,9 +183,52 @@ void u_pkg_vfs_del_local(const char *name) free(item); } +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 (!initialized) return NULL; @@ -134,7 +236,17 @@ struct upkg *u_pkg_vfs_open_by_name(const char *name) item = avl_find(local_tree, &spec); if (item) return upkg_fopen(item->file); - return NULL; + + st = malloc(sizeof *st + 256); + if (!st) + return NULL; + *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)