/* * 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); }