diff options
Diffstat (limited to 'core.lv2/lv2config.c')
-rw-r--r-- | core.lv2/lv2config.c | 461 |
1 files changed, 0 insertions, 461 deletions
diff --git a/core.lv2/lv2config.c b/core.lv2/lv2config.c deleted file mode 100644 index df647e7..0000000 --- a/core.lv2/lv2config.c +++ /dev/null @@ -1,461 +0,0 @@ -/* - Copyright 2011 David Robillard <http://drobilla.net> - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, - OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF - THE POSSIBILITY OF SUCH DAMAGE. -*/ - -#define _XOPEN_SOURCE 500 - -#include <errno.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include <dirent.h> -#include <sys/stat.h> -#include <sys/types.h> -#include <unistd.h> - -#ifdef __WIN32__ -#define _WIN32_WINNT 0x0600 -#include <windows.h> -#define symlink(src, dst) (!CreateSymbolicLink((src), (dst), 0)) -#define mkdir(path, mode) mkdir(path) -#endif - -#include "serd-0.1.0.h" - -#include "lv2-config.h" - -#ifdef HAVE_WORDEXP -#include <wordexp.h> -#endif - -#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -#define NS_LV2 "http://lv2plug.in/ns/lv2core#" - -static const size_t file_scheme_len = 7; /* strlen("file://") */ - -/** Record of an LV2 specification. */ -typedef struct _Spec { - SerdNode uri; - char* manifest; - const char* inc_dir; -} Spec; - -/** Global lv2config state. */ -typedef struct { - SerdReader reader; - SerdReadState state; - const char* destdir; - const uint8_t* current_file; - const char* current_inc_dir; - Spec* specs; - size_t n_specs; -} World; - -/** Append a discovered specification to world->specs. */ -static void -specs_add(World* world, - SerdNode* uri, - const uint8_t* manifest, - const char* inc_dir) -{ - world->specs = realloc(world->specs, sizeof(Spec) * (world->n_specs + 1)); - world->specs[world->n_specs].uri = *uri; - world->specs[world->n_specs].manifest = strdup((const char*)manifest); - world->specs[world->n_specs].inc_dir = inc_dir; - ++world->n_specs; -} - -/** Free world->specs. */ -static void -specs_free(World* world) -{ - for (size_t i = 0; i < world->n_specs; ++i) { - Spec* spec = &world->specs[i]; - serd_node_free(&spec->uri); - free(spec->manifest); - } - free(world->specs); - world->specs = NULL; - world->n_specs = 0; -} - -/** Reader base directive handler. */ -static bool -on_base(void* handle, - const SerdNode* uri_node) -{ - World* const world = (World*)handle; - return serd_read_state_set_base_uri(world->state, uri_node); -} - -/** Reader prefix directive handler. */ -static bool -on_prefix(void* handle, - const SerdNode* name, - const SerdNode* uri_node) -{ - World* const world = (World*)handle; - return serd_read_state_set_prefix(world->state, name, uri_node); -} - -/** Reader statement handler. */ -static bool -on_statement(void* handle, - const SerdNode* graph, - const SerdNode* subject, - const SerdNode* predicate, - const SerdNode* object, - const SerdNode* object_datatype, - const SerdNode* object_lang) -{ - World* world = (World*)handle; - SerdReadState state = world->state; - SerdNode abs_s = serd_read_state_expand(state, subject); - SerdNode abs_p = serd_read_state_expand(state, predicate); - SerdNode abs_o = serd_read_state_expand(state, object); - - if (abs_s.buf && abs_p.buf && abs_o.buf - && !strcmp((const char*)abs_p.buf, NS_RDF "type") - && !strcmp((const char*)abs_o.buf, NS_LV2 "Specification")) { - specs_add(world, &abs_s, world->current_file, world->current_inc_dir); - } else { - serd_node_free(&abs_s); - } - serd_node_free(&abs_p); - serd_node_free(&abs_o); - return true; -} - -/** Add any specifications found in a manifest.ttl to world->specs. */ -static void -discover_manifest(World* world, const char* uri) -{ - SerdEnv env = serd_env_new(); - - world->state = serd_read_state_new(env, (const uint8_t*)uri); - - const char* const path = uri + file_scheme_len; - FILE* fd = fopen(path, "r"); - if (fd) { - world->current_file = (const uint8_t*)uri; - if (!serd_reader_read_file(world->reader, fd, (const uint8_t*)uri)) { - fprintf(stderr, "lv2config: error reading <%s>\n", path); - } - world->current_file = NULL; - fclose(fd); - } else { - fprintf(stderr, "lv2config: failed to open <%s>\n", path); - } - - serd_read_state_free(world->state); - serd_env_free(env); - world->state = NULL; -} - -/** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in @a path. */ -static char* -expand(const char* path) -{ -#ifdef HAVE_WORDEXP - char* ret = NULL; - wordexp_t p; - wordexp(path, &p, 0); - if (p.we_wordc == 0) { - /* Literal directory path (e.g. no variables or ~) */ - ret = strdup(path); - } else if (p.we_wordc == 1) { - /* Directory path expands (e.g. contains ~ or $FOO) */ - ret = strdup(p.we_wordv[0]); - } else { - /* Multiple expansions in a single directory path? */ - fprintf(stderr, "lv2config: malformed path `%s' ignored\n", path); - } - wordfree(&p); -#elif defined(__WIN32__) - static const size_t len = 32767; - char* ret = malloc(len); - ExpandEnvironmentStrings(path, ret, len); -#else - char* ret = strdup(path); -#endif - return ret; -} - -/** Return the corresponding output path (prepend destdir if applicable). */ -static char* -output_dir(const char* path, const char* destdir) -{ - if (destdir) { - size_t len = strlen(destdir) + strlen(path); - char* ret = malloc(len + 1); - snprintf(ret, len + 1, "%s%s", destdir, path); - return ret; - } else { - return strdup(path); - } -} - -/** Scan all bundles in path (i.e. scan all path/foo.lv2/manifest.ttl). */ -static void -discover_dir(World* world, const char* dir_path, const char* inc_dir) -{ - char* expanded_path = expand(dir_path); - if (!expanded_path) { - return; - } - - char* full_path = output_dir(expanded_path, world->destdir); - free(expanded_path); - - DIR* dir = opendir(full_path); - if (!dir) { - free(full_path); - return; - } - - world->current_inc_dir = inc_dir; - - struct dirent* file; - while ((file = readdir(dir))) { - if (!strcmp(file->d_name, ".") || !strcmp(file->d_name, "..")) { - continue; - } - - char* uri = malloc(file_scheme_len - + strlen(full_path) + 1 - + strlen(file->d_name) + 1 - + strlen("manifest.ttl") + 1); - - sprintf(uri, "file://%s/%s/manifest.ttl", - full_path, file->d_name); - - discover_manifest(world, uri); - free(uri); - } - - closedir(dir); - free(full_path); -} - -/** Add all specifications in lv2_path to world->specs. */ -static void -discover_path(World* world, const char* lv2_path, const char* inc_dir) -{ - /* Call discover_dir for each component of lv2_path, - which will build world->specs (a linked list of struct Spec). - */ - while (lv2_path[0] != '\0') { - const char* const sep = strchr(lv2_path, LV2CORE_PATH_SEP[0]); - if (sep) { - const size_t dir_len = sep - lv2_path; - char* const dir = malloc(dir_len + 1); - memcpy(dir, lv2_path, dir_len); - dir[dir_len] = '\0'; - discover_dir(world, dir, inc_dir); - free(dir); - lv2_path += dir_len + 1; - } else { - discover_dir(world, lv2_path, inc_dir); - lv2_path = "\0"; - } - } - - /* TODO: Check revisions */ -} - -/** Create all parent directories of dir_path, but not dir_path itself. */ -static int -mkdir_parents(const char* dir_path) -{ - char* path = strdup(dir_path); - const size_t path_len = strlen(path); - for (size_t i = 1; i <= path_len; ++i) { - if (path[i] == LV2CORE_DIR_SEP[0]) { - path[i] = '\0'; - if (mkdir(path, 0755) && errno != EEXIST) { - fprintf(stderr, "lv2config: Failed to create %s (%s)\n", - path, strerror(errno)); - free(path); - return 1; - } - path[i] = LV2CORE_DIR_SEP[0]; - } - } - - free(path); - return 0; -} - -/** Order specifications by URI. */ -static int -spec_cmp(const void* a_ptr, const void* b_ptr) -{ - const Spec* a = (const Spec*)a_ptr; - const Spec* b = (const Spec*)b_ptr; - return strcmp((const char*)a->uri.buf, (const char*)b->uri.buf); -} - - -/** Build an LV2 include tree for all specifications. */ -static void -output_includes(World* world) -{ - /* Sort specs array. */ - qsort(world->specs, world->n_specs, sizeof(Spec), spec_cmp); - - /* Make a link in the include tree for each specification bundle. */ - const Spec* last_spec = NULL; - for (unsigned i = 0; i < world->n_specs; ++i) { - const Spec* const spec = &world->specs[i]; - - /* Skip duplicate extensions with a warning. */ - if (last_spec && !strcmp((const char*)last_spec->uri.buf, - (const char*)spec->uri.buf)) { - fprintf(stderr, - "lv2config: %s: warning: Duplicate extension <%s>.\n" - "lv2config: %s: note: Using this version.\n", - last_spec->manifest + file_scheme_len, - (const char*)spec->uri.buf, - spec->manifest + file_scheme_len); - - continue; - } - last_spec = spec; - - /* Build path to include directory for this specification. */ - - const char* path = strchr((const char*)spec->uri.buf, ':'); - if (!path) { - fprintf(stderr, "lv2config: Invalid URI <%s>\n", spec->uri.buf); - continue; - } - for (++path; (path[0] == '/' && path[0] != '\0'); ++path) {} - - const char* bundle_uri = spec->manifest; - char* bundle_path = strdup(bundle_uri + file_scheme_len); - char* last_sep = strrchr(bundle_path, LV2CORE_DIR_SEP[0]); - if (last_sep) { - *(last_sep + 1) = '\0'; - } - - char* full_dest = output_dir(spec->inc_dir, world->destdir); - size_t len = strlen(full_dest) + 1 + strlen(path); - char* rel_inc_path = malloc(len + 1); - snprintf(rel_inc_path, len + 1, "%s/%s", full_dest, path); - free(full_dest); - - char* inc_path = expand(rel_inc_path); - free(rel_inc_path); - printf("%s => %s\n", inc_path, bundle_path); - - /* Create parent directory. */ - if (mkdir_parents(inc_path)) { - continue; - } - - /* Remove existing link for this bundle. */ - if (!access(inc_path, F_OK) && unlink(inc_path)) { - fprintf(stderr, "lv2config: Failed to remove %s (%s)\n", - inc_path, strerror(errno)); - free(inc_path); - free(bundle_path); - continue; - } - - char* link_path = bundle_path; - if (world->destdir) { - const size_t destdir_len = strlen(world->destdir); - if (!strncmp(link_path, world->destdir, destdir_len)) { - link_path += destdir_len; - } - } - - /* Create link to this bundle in include tree. */ - if (symlink(link_path, inc_path)) { - fprintf(stderr, "lv2config: Failed to create link (%s)\n", - strerror(errno)); - } - - free(inc_path); - free(bundle_path); - } -} - -static int -usage(const char* name, bool error) -{ - FILE* out = (error ? stderr : stdout); - fprintf(out, "Usage: %s\n", name); - fprintf(out, "Build the default system LV2 include directories.\n\n"); - - fprintf(out, "Usage: %s INCLUDE_DIR\n", name); - fprintf(out, "Build an LV2 include directory tree at INCLUDE_DIR\n"); - fprintf(out, "for all extensions found in $LV2_PATH.\n\n"); - - fprintf(out, "Usage: %s INCLUDE_DIR BUNDLES_DIR\n", name); - fprintf(out, "Build an lv2 include directory tree at INCLUDE_DIR\n"); - fprintf(out, "for all extensions found in bundles under BUNDLES_DIR.\n"); - return (error ? EXIT_FAILURE : EXIT_SUCCESS); -} - -int -main(int argc, char** argv) -{ - World world = { NULL, NULL, NULL, NULL, NULL, NULL, 0 }; - world.reader = serd_reader_new( - SERD_TURTLE, &world, on_base, on_prefix, on_statement, NULL); - - const char* destdir = getenv("DESTDIR"); - if (destdir && destdir[0] != '\0') { - world.destdir = destdir; - } - - if (argc == 1) { - /* lv2_config */ - discover_dir(&world, "/usr/local/lib/lv2", "/usr/local/include/lv2"); - discover_dir(&world, "/usr/lib/lv2", "/usr/include/lv2"); - } else if (argv[1][0] == '-') { - return usage(argv[0], false); - } else if (argc == 2) { - /* lv2_config INCLUDE_DIR */ - const char* lv2_path = getenv("LV2_PATH"); - if (!lv2_path) { - lv2_path = LV2CORE_DEFAULT_LV2_PATH; - } - discover_path(&world, lv2_path, argv[1]); - } else if (argc == 3) { - /* lv2_config INCLUDE_DIR LV2_PATH */ - discover_path(&world, argv[2], argv[1]); - } else { - return usage(argv[0], true); - } - - output_includes(&world); - - specs_free(&world); - serd_reader_free(world.reader); - - return 0; -} |