From 0a55ae84545c5e5a45e2240c06bd77a1917f7d3e Mon Sep 17 00:00:00 2001 From: Nick Bowler Date: Mon, 7 Dec 2009 03:01:06 -0500 Subject: [PATCH] package: Get rid of separate mechanisms for opening packages. Remove u_pkg_new_by_name and u_pkg_new_by_file, replacing them with one function, u_pkg_open, that takes just a package name. Named files can now be part of the package search just like any other package. This should allow cool things like listing dependency packages on the upkg command line. --- m4/.gitignore | 2 + m4/gnulib-cache.m4 | 3 +- src/uobject/Makefile.inc | 5 +- src/uobject/module.c | 2 +- src/uobject/package.c | 90 ++++------------------ src/uobject/package.h | 3 +- src/uobject/vfs.c | 162 +++++++++++++++++++++++++++++++++++++++ src/uobject/vfs.h | 55 +++++++++++++ src/upkg.c | 28 +++++-- 9 files changed, 262 insertions(+), 88 deletions(-) create mode 100644 src/uobject/vfs.c create mode 100644 src/uobject/vfs.h diff --git a/m4/.gitignore b/m4/.gitignore index 222b4c6..e413b24 100644 --- a/m4/.gitignore +++ b/m4/.gitignore @@ -13,5 +13,7 @@ gnulib-comp.m4 gnulib-tool.m4 include_next.m4 stddef_h.m4 +strcase.m4 +strings_h.m4 unistd_h.m4 wchar_t.m4 diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4 index 0f97c57..7cb7a29 100644 --- a/m4/gnulib-cache.m4 +++ b/m4/gnulib-cache.m4 @@ -15,12 +15,13 @@ # Specification in the form of a command-line invocation: -# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --libtool --macro-prefix=gl --no-vc-files getopt-gnu +# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=. --libtool --macro-prefix=gl --no-vc-files getopt-gnu strcase # Specification in the form of a few gnulib-tool.m4 macro invocations: gl_LOCAL_DIR([]) gl_MODULES([ getopt-gnu + strcase ]) gl_AVOID([]) gl_SOURCE_BASE([lib]) diff --git a/src/uobject/Makefile.inc b/src/uobject/Makefile.inc index 7c42e97..2009d09 100644 --- a/src/uobject/Makefile.inc +++ b/src/uobject/Makefile.inc @@ -12,8 +12,9 @@ uobject_HEADERS = uobject/uobject.h uobject/exportable.h uobject/loadable.h \ noinst_HEADERS += uobject/avl.h libuobject_la_SOURCES = uobject/uobject.c uobject/module.c uobject/avl.c \ - uobject/package.c uobject/loadable.c uobject/exportable.c + uobject/package.c uobject/loadable.c uobject/exportable.c \ + uobject/vfs.c libuobject_la_CPPFLAGS = $(AM_CPPFLAGS) $(GLIB_CFLAGS) $(LTDLINCL) \ -DPKGLIBDIR=\"$(pkglibdir)\" -DPKGDATADIR=\"$(pkgdatadir)\" libuobject_la_LDFLAGS = $(AM_LDFLAGS) -export-symbols-regex '^u_' -libuobject_la_LIBADD = $(LIBLTDL) $(GLIB_LIBS) +libuobject_la_LIBADD = $(LIBLTDL) $(GLIB_LIBS) -lgnu diff --git a/src/uobject/module.c b/src/uobject/module.c index 1872a8d..2fa4402 100644 --- a/src/uobject/module.c +++ b/src/uobject/module.c @@ -84,7 +84,7 @@ GType u_object_module_get_class(const char *package, const char *class) if (!mod) { void **p; - mod = u_pkg_new_by_name(package); + mod = u_pkg_open(package); if (!mod) { return 0; } diff --git a/src/uobject/package.c b/src/uobject/package.c index bd97135..af4ebb3 100644 --- a/src/uobject/package.c +++ b/src/uobject/package.c @@ -25,6 +25,7 @@ #include #include +#include #include "upkg.h" #define U_PKG_GET_PRIV(o) \ @@ -82,74 +83,31 @@ static void dl_print_errors(const char *prefix) } } -/* - * Function for use with lt_dlforeachfile. The user data must point to - * a UPkg with the GTypeModule name field initialized. If a suitable file - * is found and could be successfully opened for reading, the pkg class member - * will be filled in. - */ -static int find_package_by_name(const char *filename, void *data) -{ - GTypeModule *m = G_TYPE_MODULE(data); - UPkg *upkg = U_PKG(data); - - const char *base; - size_t len; - char *buf; - - base = strrchr(filename, '/'); - if (base) { - base++; - } else { - base = filename; - } - - if (str_cmp_lower(base, m->name) != 0) - return 0; - - len = strlen(filename); - buf = malloc(len + sizeof **u_pkg_exts); - if (!buf) - return 0; - strcpy(buf, filename); - - for (unsigned i = 0; i < sizeof u_pkg_exts / sizeof *u_pkg_exts; i++) { - strcpy(buf+len, u_pkg_exts[i]); - upkg->pkg = upkg_fopen(buf); - if (!upkg->pkg) { - free(buf); - return 1; - } - } - - free(buf); - return 0; -} - static gboolean u_pkg_load(GTypeModule *m) { struct upkg_priv *priv = U_PKG_GET_PRIV(m); int (*init_func)(GTypeModule *); + const char *file; - if (m->name) { - priv->native = lt_dlopenext(m->name); - if (!priv->native) { - dl_print_errors(m->name); + file = u_pkg_vfs_lookup(m->name); + if (file) { + U_PKG(m)->pkg = upkg_fopen(file); + if (!U_PKG(m)->pkg) { return FALSE; } + } + priv->native = lt_dlopenext(m->name); + if (priv->native) { init_func = lt_dlsym(priv->native, "init"); if (!init_func || init_func(m) != 0) { dl_print_errors(__func__); lt_dlclose(priv->native); + upkg_close(U_PKG(m)->pkg); return FALSE; } } - if (!U_PKG(m)->pkg) { - lt_dlforeachfile(u_pkg_get_search_path(), find_package_by_name, m); - } - return TRUE; } @@ -168,6 +126,8 @@ static void u_pkg_unload(GTypeModule *m) if (lt_dlclose(priv->native) != 0) { dl_print_errors(__func__); } + + priv->native = NULL; } if (upkg->pkg) { @@ -183,11 +143,7 @@ static void u_pkg_init(UPkg *pkg) static void u_pkg_finalize(GObject *o) { - UPkg *upkg = U_PKG(o); - - if (upkg->pkg) { - upkg_close(upkg->pkg); - } + u_pkg_unload(G_TYPE_MODULE(o)); } static void u_pkg_class_init(UPkgClass *class) @@ -264,7 +220,7 @@ int u_pkg_add_search_dir(const char *path) return 0; } -GTypeModule *u_pkg_new_by_name(const char *name) +GTypeModule *u_pkg_open(const char *name) { g_return_val_if_fail(name != NULL, NULL); @@ -282,21 +238,3 @@ GTypeModule *u_pkg_new_by_name(const char *name) mod->name = str_cpy_lower(pkgname, name); return mod; } - -GTypeModule *u_pkg_new_by_file(const char *filename) -{ - struct upkg *pkg = upkg_fopen(filename); - if (!pkg) { - return NULL; - } - - GTypeModule *mod = g_object_new(U_PKG_TYPE, NULL); - if (!mod) { - upkg_close(pkg); - return NULL; - } - - mod->name = NULL; - U_PKG(mod)->pkg = pkg; - return mod; -} diff --git a/src/uobject/package.h b/src/uobject/package.h index 5e60a18..03d9b01 100644 --- a/src/uobject/package.h +++ b/src/uobject/package.h @@ -46,8 +46,7 @@ struct UPkgClass { }; GType u_pkg_get_type(void); -GTypeModule *u_pkg_new_by_name(const char *name); -GTypeModule *u_pkg_new_by_file(const char *filename); +GTypeModule *u_pkg_open(const char *name); const char *u_pkg_get_search_path(void); int u_pkg_set_search_path(const char *path); diff --git a/src/uobject/vfs.c b/src/uobject/vfs.c new file mode 100644 index 0000000..5465144 --- /dev/null +++ b/src/uobject/vfs.c @@ -0,0 +1,162 @@ +/* + * Functions for handling UObject package search paths. + * Copyright (C) 2009 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include + +#include +#include + +#include "avl.h" + +/* Check if a character is a directory separator. */ +#ifdef LT_DIRSEP_CHAR +# define IS_DIRSEP(x) ((x) == '/' || (x) == LT_DIRSEP_CHAR) +#else +# define IS_DIRSEP(x) ((x) == '/') +#endif + +struct local_pkg { + char *file, *name; +}; + +static struct avl_table *local_tree; +static int initialized; + +static int localcmp(const void *_a, const void *_b, void *_data) +{ + const struct local_pkg *a = _a, *b = _b; + return strcasecmp(a->name, b->name); +} + +static const char *pkgname_base(const char *file) +{ + const char *base = NULL; + int slash = 1; + + for (size_t i = 0; file[i]; i++) { + if (IS_DIRSEP(file[i])) { + slash = 1; + } else if (slash == 1) { + base = file+i; + slash = 0; + } + } + + return base; +} + +static size_t pkgname_len(const char *base) +{ + size_t i; + + for (i = 0; base[i]; i++) { + if (IS_DIRSEP(base[i]) || base[i] == '.') + break; + } + + return i; +} + +const char *u_pkg_vfs_add_local(const char *name, const char *file) +{ + size_t filelen = strlen(file)+1, namelen; + struct local_pkg *spec; + + if (!name) + name = pkgname_base(file); + if (!name) + return NULL; + namelen = pkgname_len(name); + + /* For simplicity, stuff everything in a single allocation. */ + spec = malloc(sizeof *spec + filelen + namelen + 1); + if (!spec) { + return NULL; + } + + spec->file = (char *)spec + sizeof *spec; + memcpy(spec->file, file, filelen); + + spec->name = (char *)spec + sizeof *spec + filelen; + memcpy(spec->name, name, namelen); + spec->name[namelen] = 0; + + if (avl_find(local_tree, spec)) { + fprintf(stderr, "%s: attempted to add duplicate local package.\n", __func__); + name = spec->name; + free(spec); + return name; /* "Success"-ish. */ + } + + if (avl_probe(local_tree, spec) == NULL) { + free(spec); + return NULL; + } + + return spec->name; +} + +void u_pkg_vfs_del_local(const char *name) +{ + struct local_pkg spec = { .name = (char *)name }, *item; + + item = avl_find(local_tree, &spec); + free(item); +} + +const char *u_pkg_vfs_lookup(const char *name) +{ + struct local_pkg spec = { .name = (char *)name }, *item; + + if (!local_tree) + return NULL; + + item = avl_find(local_tree, &spec); + if (!item) + return NULL; + return item->file; +} + +int u_pkg_vfs_init(void) +{ + if (!initialized) { + local_tree = avl_create(localcmp, NULL, NULL); + if (!local_tree) { + fprintf(stderr, "%s: failed to create local module tree.\n", __func__); + return -1; + } + } + + initialized++; + return 0; +} + +static void local_destroy(void *item, void *data) +{ + free(item); +} + +void u_pkg_vfs_exit(void) +{ + if (--initialized == 0) + avl_destroy(local_tree, local_destroy); +} diff --git a/src/uobject/vfs.h b/src/uobject/vfs.h new file mode 100644 index 0000000..d986aec --- /dev/null +++ b/src/uobject/vfs.h @@ -0,0 +1,55 @@ +/* + * Functions for handling UObject package search paths. + * Copyright (C) 2009 Nick Bowler + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#ifndef U_OBJECT_VFS_H_ +#define U_OBJECT_VFS_H_ + +/* + * Insert a local package to the VFS. A "local package" is an explicit + * association of a name to a file, and thus can be located outside the normal + * search path. Local packages are searched before any other location. + * + * Returns a pointer to an internal copy of name on success, or NULL on + * failure. The returned name must not be modified or freed. If name is + * NULL, it is determined automatically from the filename. + */ +const char *u_pkg_vfs_add_local(const char *name, const char *file); + +/* + * Remove a local package from the VFS by name. + */ +void u_pkg_vfs_del_local(const char *name); + +/* + * Find a package by name in the VFS. Names are case-insensitive. + */ +const char *u_pkg_vfs_lookup(const char *name); + +/* + * Initialize the UObject VFS system. Returns 0 on success, -1 otherwise. + * The VFS system can be safely initialized multiple times. + */ +int u_pkg_vfs_init(void); + +/* + * Shutdown the UObject VFS system. + * The VFS system is only shut down when this function has been called as + * many times as u_pkg_vfs_init. + */ +void u_pkg_vfs_exit(void); + +#endif diff --git a/src/upkg.c b/src/upkg.c index 2c268b4..d3a946b 100644 --- a/src/upkg.c +++ b/src/upkg.c @@ -29,6 +29,7 @@ #include #include #include +#include enum { MODE_INFO, @@ -286,7 +287,8 @@ int package_export(struct upkg *pkg) int main(int argc, char **argv) { - UPkg *upkg; + GTypeModule *pkg; + const char *pkgname; unsigned mode = MODE_INFO; int opt, rc = EXIT_FAILURE; @@ -320,25 +322,39 @@ int main(int argc, char **argv) return EXIT_FAILURE; } + if (u_pkg_vfs_init() != 0) + return EXIT_FAILURE; if (u_object_module_init() != 0) return EXIT_FAILURE; - upkg = U_PKG(u_pkg_new_by_file(argv[optind])); - if (!upkg) { + pkgname = u_pkg_vfs_add_local(NULL, argv[optind]); + if (!pkgname) { + fprintf(stderr, "failed to add package `%s'.\n", argv[optind]); + return EXIT_FAILURE; + } + + pkg = u_pkg_open(pkgname); + if (!pkg) { fprintf(stderr, "failed to open package!\n"); return EXIT_FAILURE; } + if (!g_type_module_use(pkg)) { + fprintf(stderr, "failed to load package: %s\n", pkg->name); + return EXIT_FAILURE; + } + switch (mode) { case MODE_INFO: - rc = package_info(upkg->pkg); + rc = package_info(U_PKG(pkg)->pkg); break; case MODE_EXPORT: - rc = package_export(upkg->pkg); + rc = package_export(U_PKG(pkg)->pkg); break; } - g_object_unref(upkg); + g_object_unref(pkg); u_object_module_exit(); + u_pkg_vfs_exit(); return rc; } -- 2.43.0