diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lilv_internal.h | 92 | ||||
-rw-r--r-- | src/node.c | 10 | ||||
-rw-r--r-- | src/plugin.c | 50 | ||||
-rw-r--r-- | src/port.c | 16 | ||||
-rw-r--r-- | src/state.c | 482 | ||||
-rw-r--r-- | src/util.c | 224 | ||||
-rw-r--r-- | src/world.c | 133 | ||||
-rw-r--r-- | src/zix/common.h | 2 | ||||
-rw-r--r-- | src/zix/tree.c | 6 |
9 files changed, 720 insertions, 295 deletions
diff --git a/src/lilv_internal.h b/src/lilv_internal.h index 75c08de..9950a61 100644 --- a/src/lilv_internal.h +++ b/src/lilv_internal.h @@ -129,38 +129,41 @@ struct LilvWorldImpl { LilvSpec* specs; LilvPlugins* plugins; LilvNodes* loaded_files; - SordNode* dc_replaces_node; - SordNode* doap_name_node; - SordNode* dyn_manifest_node; - SordNode* lv2_appliesTo_node; - SordNode* lv2_binary_node; - SordNode* lv2_default_node; - SordNode* lv2_extensionData_node; - SordNode* lv2_index_node; - SordNode* lv2_maximum_node; - SordNode* lv2_minimum_node; - SordNode* lv2_name_node; - SordNode* lv2_optionalFeature_node; - SordNode* lv2_plugin_node; - SordNode* lv2_port_node; - SordNode* lv2_portproperty_node; - SordNode* lv2_reportslatency_node; - SordNode* lv2_requiredFeature_node; - SordNode* lv2_specification_node; - SordNode* lv2_symbol_node; - SordNode* pset_value_node; - SordNode* rdf_a_node; - SordNode* rdf_value_node; - SordNode* rdfs_class_node; - SordNode* rdfs_label_node; - SordNode* rdfs_seealso_node; - SordNode* rdfs_subclassof_node; - SordNode* xsd_base64Binary_node; - SordNode* xsd_boolean_node; - SordNode* xsd_decimal_node; - SordNode* xsd_double_node; - SordNode* xsd_integer_node; - LilvOptions opt; + struct { + SordNode* dc_replaces; + SordNode* doap_name; + SordNode* dman_DynManifest; + SordNode* lv2_appliesTo; + SordNode* lv2_binary; + SordNode* lv2_default; + SordNode* lv2_extensionData; + SordNode* lv2_index; + SordNode* lv2_maximum; + SordNode* lv2_minimum; + SordNode* lv2_name; + SordNode* lv2_optionalFeature; + SordNode* lv2_Plugin; + SordNode* lv2_port; + SordNode* lv2_portProperty; + SordNode* lv2_reportsLatency; + SordNode* lv2_requiredFeature; + SordNode* lv2_Specification; + SordNode* lv2_symbol; + SordNode* pset_value; + SordNode* rdf_a; + SordNode* rdf_value; + SordNode* rdfs_Class; + SordNode* rdfs_label; + SordNode* rdfs_seeAlso; + SordNode* rdfs_subClassOf; + SordNode* xsd_base64Binary; + SordNode* xsd_boolean; + SordNode* xsd_decimal; + SordNode* xsd_double; + SordNode* xsd_integer; + SordNode* null_uri; + } uris; + LilvOptions opt; }; typedef enum { @@ -309,6 +312,13 @@ lilv_match_object(SordIter* iter) { return tup[SORD_OBJECT]; } +static inline const SordNode* +lilv_match_graph(SordIter* iter) { + SordQuad tup; + sord_iter_get(iter, tup); + return tup[SORD_GRAPH]; +} + static inline void lilv_match_end(SordIter* iter) { @@ -327,10 +337,20 @@ LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* w, SordIter* stream, bool object); -char* lilv_strjoin(const char* first, ...); -char* lilv_strdup(const char* str); -char* lilv_get_lang(void); -char* lilv_expand(const char* path); +char* lilv_strjoin(const char* first, ...); +char* lilv_strdup(const char* str); +char* lilv_get_lang(void); +char* lilv_expand(const char* path); +char* lilv_dirname(const char* path); +char* lilv_find_free_path( + const char* in_path, bool (*exists)(const char*, void*), void* user_data); +int lilv_copy_file(const char* src, const char* dst); +bool lilv_path_exists(const char* path, void* ignored); +bool lilv_path_is_absolute(const char* path); +char* lilv_get_latest_copy(const char* path); +char* lilv_path_relative_to(const char* path, const char* base); +bool lilv_path_is_child(const char* path, const char* dir); +int lilv_flock(FILE* file, bool lock); typedef void (*VoidFunc)(void); @@ -104,14 +104,14 @@ lilv_node_new_from_node(LilvWorld* world, const SordNode* node) case SORD_LITERAL: datatype_uri = sord_node_get_datatype(node); if (datatype_uri) { - if (sord_node_equals(datatype_uri, world->xsd_boolean_node)) + if (sord_node_equals(datatype_uri, world->uris.xsd_boolean)) type = LILV_VALUE_BOOL; - else if (sord_node_equals(datatype_uri, world->xsd_decimal_node) - || sord_node_equals(datatype_uri, world->xsd_double_node)) + else if (sord_node_equals(datatype_uri, world->uris.xsd_decimal) + || sord_node_equals(datatype_uri, world->uris.xsd_double)) type = LILV_VALUE_FLOAT; - else if (sord_node_equals(datatype_uri, world->xsd_integer_node)) + else if (sord_node_equals(datatype_uri, world->uris.xsd_integer)) type = LILV_VALUE_INT; - else if (sord_node_equals(datatype_uri, world->xsd_base64Binary_node)) + else if (sord_node_equals(datatype_uri, world->uris.xsd_base64Binary)) type = LILV_VALUE_BLOB; else LILV_ERRORF("Unknown datatype `%s'\n", diff --git a/src/plugin.c b/src/plugin.c index 1daf23d..b3763cb 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -207,15 +207,15 @@ lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_p) SordIter* ports = lilv_world_query_internal( p->world, p->plugin_uri->val.uri_val, - p->world->lv2_port_node, + p->world->uris.lv2_port, NULL); FOREACH_MATCH(ports) { const SordNode* port = lilv_match_object(ports); LilvNode* index = lilv_plugin_get_unique( - p, port, p->world->lv2_index_node); + p, port, p->world->uris.lv2_index); LilvNode* symbol = lilv_plugin_get_unique( - p, port, p->world->lv2_symbol_node); + p, port, p->world->uris.lv2_symbol); bool error = false; if (!lilv_node_is_string(symbol) || !is_symbol(symbol->str_val)) { @@ -254,7 +254,7 @@ lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_p) } SordIter* types = lilv_world_query_internal( - p->world, port, p->world->rdf_a_node, NULL); + p->world, port, p->world->uris.rdf_a, NULL); FOREACH_MATCH(types) { const SordNode* type = lilv_match_object(types); if (sord_node_get_type(type) == SORD_URI) { @@ -326,7 +326,7 @@ lilv_plugin_get_library_uri(const LilvPlugin* const_p) SordIter* results = lilv_world_query_internal( p->world, p->plugin_uri->val.uri_val, - p->world->lv2_binary_node, + p->world->uris.lv2_binary, NULL); FOREACH_MATCH(results) { const SordNode* binary_node = lilv_match_object(results); @@ -362,7 +362,7 @@ lilv_plugin_get_class(const LilvPlugin* const_p) SordIter* results = lilv_world_query_internal( p->world, p->plugin_uri->val.uri_val, - p->world->rdf_a_node, + p->world->uris.rdf_a, NULL); FOREACH_MATCH(results) { const SordNode* class_node = lilv_match_object(results); @@ -414,7 +414,7 @@ lilv_plugin_verify(const LilvPlugin* plugin) lilv_nodes_free(results); results = lilv_plugin_get_value_internal(plugin, - plugin->world->doap_name_node); + plugin->world->uris.doap_name); if (!results) { return false; } @@ -436,7 +436,7 @@ LilvNode* lilv_plugin_get_name(const LilvPlugin* plugin) { LilvNodes* results = lilv_plugin_get_value_internal( - plugin, plugin->world->doap_name_node); + plugin, plugin->world->uris.doap_name); LilvNode* ret = NULL; if (results) { @@ -561,7 +561,7 @@ lilv_plugin_has_latency(const LilvPlugin* p) SordIter* ports = lilv_world_query_internal( p->world, p->plugin_uri->val.uri_val, - p->world->lv2_port_node, + p->world->uris.lv2_port, NULL); bool ret = false; @@ -570,8 +570,8 @@ lilv_plugin_has_latency(const LilvPlugin* p) SordIter* reports_latency = lilv_world_query_internal( p->world, port, - p->world->lv2_portproperty_node, - p->world->lv2_reportslatency_node); + p->world->uris.lv2_portProperty, + p->world->uris.lv2_reportsLatency); const bool end = lilv_matches_end(reports_latency); lilv_match_end(reports_latency); if (!end) { @@ -592,7 +592,7 @@ lilv_plugin_get_latency_port_index(const LilvPlugin* p) SordIter* ports = lilv_world_query_internal( p->world, p->plugin_uri->val.uri_val, - p->world->lv2_port_node, + p->world->uris.lv2_port, NULL); uint32_t ret = 0; @@ -601,11 +601,11 @@ lilv_plugin_get_latency_port_index(const LilvPlugin* p) SordIter* reports_latency = lilv_world_query_internal( p->world, port, - p->world->lv2_portproperty_node, - p->world->lv2_reportslatency_node); + p->world->uris.lv2_portProperty, + p->world->uris.lv2_reportsLatency); if (!lilv_matches_end(reports_latency)) { LilvNode* index = lilv_plugin_get_unique( - p, port, p->world->lv2_index_node); + p, port, p->world->uris.lv2_index); ret = lilv_node_as_int(index); lilv_node_free(index); @@ -657,14 +657,14 @@ LILV_API LilvNodes* lilv_plugin_get_optional_features(const LilvPlugin* p) { - return lilv_plugin_get_value_internal(p, p->world->lv2_optionalFeature_node); + return lilv_plugin_get_value_internal(p, p->world->uris.lv2_optionalFeature); } LILV_API LilvNodes* lilv_plugin_get_required_features(const LilvPlugin* p) { - return lilv_plugin_get_value_internal(p, p->world->lv2_requiredFeature_node); + return lilv_plugin_get_value_internal(p, p->world->uris.lv2_requiredFeature); } LILV_API @@ -680,7 +680,7 @@ lilv_plugin_has_extension_data(const LilvPlugin* p, SordIter* iter = lilv_world_query_internal( p->world, p->plugin_uri->val.uri_val, - p->world->lv2_extensionData_node, + p->world->uris.lv2_extensionData, uri->val.uri_val); if (iter) { @@ -695,7 +695,7 @@ LILV_API LilvNodes* lilv_plugin_get_extension_data(const LilvPlugin* p) { - return lilv_plugin_get_value_internal(p, p->world->lv2_extensionData_node); + return lilv_plugin_get_value_internal(p, p->world->uris.lv2_extensionData); } LILV_API @@ -815,7 +815,7 @@ lilv_plugin_get_uis(const LilvPlugin* p) FOREACH_MATCH(uis) { const SordNode* ui = lilv_match_object(uis); - LilvNode* type = lilv_plugin_get_unique(p, ui, p->world->rdf_a_node); + LilvNode* type = lilv_plugin_get_unique(p, ui, p->world->uris.rdf_a); LilvNode* binary = lilv_plugin_get_unique(p, ui, ui_binary_node); if (sord_node_get_type(ui) != SORD_URI @@ -856,7 +856,7 @@ lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type) LilvNodes* const related = lilv_world_query_values_internal( world, NULL, - world->lv2_appliesTo_node, + world->uris.lv2_appliesTo, lilv_plugin_get_uri(plugin)->val.uri_val); if (!type) { @@ -867,7 +867,7 @@ lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type) LILV_FOREACH(nodes, i, related) { LilvNode* node = lilv_collection_get(related, i); SordIter* titer = lilv_world_query_internal( - world, node->val.uri_val, world->rdf_a_node, type->val.uri_val); + world, node->val.uri_val, world->uris.rdf_a, type->val.uri_val); if (!sord_iter_end(titer)) { zix_tree_insert(matches, lilv_node_new_from_node(world, node->val.uri_val), @@ -976,15 +976,15 @@ lilv_plugin_write_manifest_entry(LilvWorld* world, serd_writer_write_statement( writer, 0, NULL, sord_node_to_serd_node(subject->val.uri_val), - sord_node_to_serd_node(plugin->world->rdf_a_node), - sord_node_to_serd_node(plugin->world->lv2_plugin_node), 0, 0); + sord_node_to_serd_node(plugin->world->uris.rdf_a), + sord_node_to_serd_node(plugin->world->uris.lv2_Plugin), 0, 0); const SerdNode file_node = serd_node_from_string( SERD_URI, (const uint8_t*)plugin_file_path); serd_writer_write_statement( writer, 0, NULL, sord_node_to_serd_node(subject->val.uri_val), - sord_node_to_serd_node(plugin->world->rdfs_seealso_node), + sord_node_to_serd_node(plugin->world->uris.rdfs_seeAlso), &file_node, 0, 0); serd_writer_free(writer); @@ -68,7 +68,7 @@ lilv_port_has_property(const LilvPlugin* p, SordIter* results = lilv_world_query_internal( p->world, port->node, - p->world->lv2_portproperty_node, + p->world->uris.lv2_portProperty, lilv_node_as_node(property)); const bool ret = !lilv_matches_end(results); @@ -142,7 +142,7 @@ lilv_port_get_name(const LilvPlugin* p, const LilvPort* port) { LilvNodes* results = lilv_port_get_value_by_node( - p, port, p->world->lv2_name_node); + p, port, p->world->uris.lv2_name); LilvNode* ret = NULL; if (results) { @@ -177,7 +177,7 @@ lilv_port_get_range(const LilvPlugin* p, { if (def) { LilvNodes* defaults = lilv_port_get_value_by_node( - p, port, p->world->lv2_default_node); + p, port, p->world->uris.lv2_default); *def = defaults ? lilv_node_duplicate(lilv_nodes_get_first(defaults)) : NULL; @@ -185,7 +185,7 @@ lilv_port_get_range(const LilvPlugin* p, } if (min) { LilvNodes* minimums = lilv_port_get_value_by_node( - p, port, p->world->lv2_minimum_node); + p, port, p->world->uris.lv2_minimum); *min = minimums ? lilv_node_duplicate(lilv_nodes_get_first(minimums)) : NULL; @@ -193,7 +193,7 @@ lilv_port_get_range(const LilvPlugin* p, } if (max) { LilvNodes* maximums = lilv_port_get_value_by_node( - p, port, p->world->lv2_maximum_node); + p, port, p->world->uris.lv2_maximum); *max = maximums ? lilv_node_duplicate(lilv_nodes_get_first(maximums)) : NULL; @@ -222,12 +222,12 @@ lilv_port_get_scale_points(const LilvPlugin* p, LilvNode* value = lilv_plugin_get_unique( p, point, - p->world->rdf_value_node); + p->world->uris.rdf_value); LilvNode* label = lilv_plugin_get_unique( p, point, - p->world->rdfs_label_node); + p->world->uris.rdfs_label); if (value && label) { zix_tree_insert(ret, lilv_scale_point_new(value, label), NULL); @@ -245,7 +245,7 @@ lilv_port_get_properties(const LilvPlugin* p, const LilvPort* port) { LilvNode* pred = lilv_node_new_from_node( - p->world, p->world->lv2_portproperty_node); + p->world, p->world->uris.lv2_portProperty); LilvNodes* ret = lilv_port_get_value(p, port, pred); lilv_node_free(pred); return ret; diff --git a/src/state.c b/src/state.c index 05570cd..512cccf 100644 --- a/src/state.c +++ b/src/state.c @@ -14,8 +14,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _POSIX_SOURCE 1 /* for fileno */ -#define _BSD_SOURCE 1 /* for lockf */ +#define _BSD_SOURCE 1 /* for realpath, symlink */ #include <errno.h> #include <stdio.h> @@ -28,15 +27,13 @@ # include "lv2/lv2plug.in/ns/ext/state/state.h" #endif -#if defined(HAVE_LOCKF) && defined(HAVE_FILENO) -# include <unistd.h> -#endif - #ifdef HAVE_MKDIR # include <sys/stat.h> # include <sys/types.h> #endif +#include <unistd.h> + #define NS_ATOM "http://lv2plug.in/ns/ext/atom#" #define NS_PSET "http://lv2plug.in/ns/ext/presets#" #define NS_STATE "http://lv2plug.in/ns/ext/state#" @@ -56,16 +53,48 @@ typedef struct { LilvNode* value; } PortValue; +typedef struct { + char* abs; ///< Absolute path of actual file + char* rel; ///< Abstract path (relative path in state dir) +} PathMap; + struct LilvStateImpl { LilvNode* plugin_uri; + char* dir; ///< Save directory (if saved) + char* file_dir; ///< Directory of files created by plugin + char* label; + ZixTree* abs2rel; ///< PathMap sorted by abs + ZixTree* rel2abs; ///< PathMap sorted by rel Property* props; PortValue* values; - char* label; + uint32_t state_Path; uint32_t num_props; uint32_t num_values; }; static int +abs_cmp(const void* a, const void* b, void* user_data) +{ + return strcmp(((const PathMap*)a)->abs, + ((const PathMap*)b)->abs); +} + +static int +rel_cmp(const void* a, const void* b, void* user_data) +{ + return strcmp(((const PathMap*)a)->rel, + ((const PathMap*)b)->rel); +} + +static void +path_rel_free(void* ptr) +{ + free(((PathMap*)ptr)->abs); + free(((PathMap*)ptr)->rel); + free(ptr); +} + +static int property_cmp(const void* a, const void* b) { const Property* pa = (const Property*)a; @@ -95,6 +124,7 @@ append_port_value(LilvState* state, } #ifdef HAVE_LV2_STATE + static int store_callback(void* handle, uint32_t key, @@ -103,19 +133,18 @@ store_callback(void* handle, uint32_t type, uint32_t flags) { - if (!(flags & LV2_STATE_IS_POD)) { - // TODO: A flag so we know if we can hold a reference would be nice - LILV_WARN("Non-POD property ignored.\n"); - return 1; - } - LilvState* const state = (LilvState*)handle; state->props = realloc(state->props, (++state->num_props) * sizeof(Property)); Property* const prop = &state->props[state->num_props - 1]; - prop->value = malloc(size); - memcpy(prop->value, value, size); + if ((flags & LV2_STATE_IS_POD) || type == state->state_Path) { + prop->value = malloc(size); + memcpy(prop->value, value, size); + } else { + LILV_WARN("Storing non-POD value\n"); + prop->value = (void*)value; + } prop->size = size; prop->key = key; @@ -124,21 +153,157 @@ store_callback(void* handle, return 0; } + +static const void* +retrieve_callback(void* handle, + uint32_t key, + size_t* size, + uint32_t* type, + uint32_t* flags) +{ + const LilvState* const state = (LilvState*)handle; + const Property search_key = { NULL, 0, key, 0, 0 }; + const Property* const prop = (Property*)bsearch( + &search_key, state->props, state->num_props, + sizeof(Property), property_cmp); + + if (prop) { + *size = prop->size; + *type = prop->type; + *flags = prop->flags; + return prop->value; + } + return NULL; +} + +static const char* +lilv_state_rel2abs(const LilvState* state, const char* path) +{ + ZixTreeIter* iter = NULL; + const PathMap key = { NULL, (char*)path }; + if (state->rel2abs && !zix_tree_find(state->rel2abs, &key, &iter)) { + return ((const PathMap*)zix_tree_get(iter))->abs; + } + return path; +} + +static bool +lilv_state_has_path(const char* path, void* state) +{ + return lilv_state_rel2abs((LilvState*)state, path) != path; +} + +static char* +abstract_path(LV2_State_Map_Path_Handle handle, + const char* absolute_path) +{ + LilvState* state = (LilvState*)handle; + const size_t file_dir_len = state->file_dir ? strlen(state->file_dir) : 0; + char* path = NULL; + char* real_path = realpath(absolute_path, NULL); + const PathMap key = { (char*)real_path, NULL }; + ZixTreeIter* iter = NULL; + + if (!zix_tree_find(state->abs2rel, &key, &iter)) { + // Already mapped path in a previous call + PathMap* pm = (PathMap*)zix_tree_get(iter); + free(real_path); + return lilv_strdup(pm->rel); + } else if (lilv_path_is_child(real_path, state->file_dir)) { + // File created by plugin + char* copy = lilv_get_latest_copy(real_path); + if (!copy) { + // No recent enough copy, make a new one + copy = lilv_find_free_path(real_path, lilv_path_exists, NULL); + lilv_copy_file(real_path, copy); + } + real_path = copy; + + // Refer to the latest copy in plugin state + path = lilv_strdup(copy + file_dir_len + 1); + } else { + // New path outside state directory + const char* slash = strrchr(real_path, '/'); + const char* name = slash ? (slash + 1) : real_path; + + // Find a free name in the (virtual) state directory + path = lilv_find_free_path(name, lilv_state_has_path, state); + } + + // Add record to path mapping + PathMap* pm = malloc(sizeof(PathMap)); + pm->abs = real_path; + pm->rel = lilv_strdup(path); + zix_tree_insert(state->abs2rel, pm, NULL); + zix_tree_insert(state->rel2abs, pm, NULL); + + return path; +} + +static char* +absolute_path(LV2_State_Map_Path_Handle handle, + const char* abstract_path) +{ + LilvState* state = (LilvState*)handle; + char* path = NULL; + if (lilv_path_is_absolute(abstract_path)) { + // Absolute path, return identical path + path = lilv_strdup(abstract_path); + } else { + // Relative path inside state directory + path = lilv_strjoin(state->dir, "/", abstract_path, NULL); + } + + return path; +} + #endif // HAVE_LV2_STATE +/** Return a new features array which is @c feature added to @c features. */ +const LV2_Feature** +add_feature(const LV2_Feature *const * features, const LV2_Feature* feature) +{ + size_t n_features = 0; + for (; features && features[n_features]; ++n_features) {} + + const LV2_Feature** ret = malloc((n_features + 2) * sizeof(LV2_Feature*)); + + ret[0] = feature; + if (features) { + memcpy(ret + 1, features, n_features * sizeof(LV2_Feature*)); + } + ret[n_features + 1] = NULL; + return ret; +} + LILV_API LilvState* lilv_state_new_from_instance(const LilvPlugin* plugin, LilvInstance* instance, + LV2_URID_Map* map, + const char* dir, LilvGetPortValueFunc get_value, void* user_data, uint32_t flags, const LV2_Feature *const * features) { - LilvWorld* const world = plugin->world; - LilvState* const state = malloc(sizeof(LilvState)); + const LV2_Feature** local_features = NULL; + LilvWorld* const world = plugin->world; + LilvState* const state = malloc(sizeof(LilvState)); memset(state, '\0', sizeof(LilvState)); state->plugin_uri = lilv_node_duplicate(lilv_plugin_get_uri(plugin)); + state->abs2rel = zix_tree_new(false, abs_cmp, NULL, path_rel_free); + state->rel2abs = zix_tree_new(false, rel_cmp, NULL, NULL); + state->file_dir = dir ? realpath(dir, NULL) : NULL; + state->state_Path = map->map(map->handle, LV2_STATE_PATH_URI); + +#ifdef HAVE_LV2_STATE + if (dir) { + LV2_State_Map_Path map_path = { state, abstract_path, absolute_path }; + LV2_Feature feature = { LV2_STATE_MAP_PATH_URI, &map_path }; + features = local_features = add_feature(features, &feature); + } +#endif // Store port values LilvNode* lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT); @@ -161,39 +326,18 @@ lilv_state_new_from_instance(const LilvPlugin* plugin, ? descriptor->extension_data(LV2_STATE_INTERFACE_URI) : NULL; - iface->save(instance->lv2_handle, store_callback, state, flags, features); + if (iface) { + iface->save(instance->lv2_handle, store_callback, state, flags, features); + } #endif // HAVE_LV2_STATE qsort(state->props, state->num_props, sizeof(Property), property_cmp); qsort(state->values, state->num_values, sizeof(PortValue), value_cmp); + free(local_features); return state; } -#ifdef HAVE_LV2_STATE -static const void* -retrieve_callback(void* handle, - uint32_t key, - size_t* size, - uint32_t* type, - uint32_t* flags) -{ - const LilvState* const state = (LilvState*)handle; - const Property search_key = { NULL, 0, key, 0, 0 }; - const Property* const prop = (Property*)bsearch( - &search_key, state->props, state->num_props, - sizeof(Property), property_cmp); - - if (prop) { - *size = prop->size; - *type = prop->type; - *flags = prop->flags; - return prop->value; - } - return NULL; -} -#endif // HAVE_LV2_STATE - LILV_API void lilv_state_restore(const LilvState* state, @@ -204,13 +348,25 @@ lilv_state_restore(const LilvState* state, const LV2_Feature *const * features) { #ifdef HAVE_LV2_STATE + LV2_State_Map_Path map_path = { (LilvState*)state, abstract_path, absolute_path }; + LV2_Feature feature = { LV2_STATE_MAP_PATH_URI, &map_path }; + + const LV2_Feature** local_features = add_feature(features, &feature); + features = local_features; + const LV2_Descriptor* descriptor = instance->lv2_descriptor; const LV2_State_Interface* iface = (descriptor->extension_data) ? descriptor->extension_data(LV2_STATE_INTERFACE_URI) : NULL; - iface->restore(instance->lv2_handle, retrieve_callback, - (LV2_State_Handle)state, flags, features); + if (iface) { + iface->restore(instance->lv2_handle, retrieve_callback, + (LV2_State_Handle)state, flags, features); + } + + free(local_features); + +#endif // HAVE_LV2_STATE if (set_value) { for (uint32_t i = 0; i < state->num_values; ++i) { @@ -219,7 +375,6 @@ lilv_state_restore(const LilvState* state, user_data); } } -#endif // HAVE_LV2_STATE } static SordNode* @@ -278,21 +433,28 @@ property_from_node(LilvWorld* world, } static LilvState* -new_state_from_model(LilvWorld* world, - LV2_URID_Map* map, - SordModel* model, - const SordNode* node) +new_state_from_model(LilvWorld* world, + LV2_URID_Map* map, + SordModel* model, + const SordNode* node, + const char* dir) { LilvState* const state = malloc(sizeof(LilvState)); memset(state, '\0', sizeof(LilvState)); + state->state_Path = map->map(map->handle, LV2_STATE_PATH_URI); + state->dir = dir ? lilv_strdup(dir) : NULL; // Get the plugin URI this state applies to const SordQuad upat = { - node, world->lv2_appliesTo_node, NULL, NULL }; + node, world->uris.lv2_appliesTo, NULL, NULL }; SordIter* i = sord_find(model, upat); if (i) { state->plugin_uri = lilv_node_new_from_node( world, lilv_match_object(i)); + if (!state->dir) { + state->dir = lilv_strdup( + (const char*)sord_node_get_string(lilv_match_graph(i))); + } sord_iter_free(i); } else { LILV_ERRORF("State %s missing lv2:appliesTo property\n", @@ -301,23 +463,26 @@ new_state_from_model(LilvWorld* world, // Get the state label const SordQuad lpat = { - node, world->rdfs_label_node, NULL, NULL }; + node, world->uris.rdfs_label, NULL, NULL }; i = sord_find(model, lpat); if (i) { state->label = lilv_strdup( (const char*)sord_node_get_string(lilv_match_object(i))); + if (!state->dir) { + state->dir = lilv_strdup( + (const char*)sord_node_get_string(lilv_match_graph(i))); + } sord_iter_free(i); - } else { } // Get port values - const SordQuad ppat = { node, world->lv2_port_node, NULL, NULL }; + const SordQuad ppat = { node, world->uris.lv2_port, NULL, NULL }; SordIter* ports = sord_find(model, ppat); FOREACH_MATCH(ports) { const SordNode* port = lilv_match_object(ports); - const SordNode* label = get_one(model, port, world->rdfs_label_node); - const SordNode* symbol = get_one(model, port, world->lv2_symbol_node); - const SordNode* value = get_one(model, port, world->pset_value_node); + const SordNode* label = get_one(model, port, world->uris.rdfs_label); + const SordNode* symbol = get_one(model, port, world->uris.lv2_symbol); + const SordNode* value = get_one(model, port, world->uris.pset_value); if (!symbol) { LILV_ERRORF("State `%s' port missing symbol.\n", sord_node_get_string(node)); @@ -338,10 +503,12 @@ new_state_from_model(LilvWorld* world, } sord_iter_free(ports); + SordNode* state_path_node = sord_new_uri(world->world, + USTR(LV2_STATE_PATH_URI)); // Get properties SordNode* statep = sord_new_uri(world->world, USTR(NS_STATE "state")); const SordNode* state_node = get_one(model, node, statep); - if (state_node) { + if (state) { const SordQuad spat = { state_node, NULL, NULL }; SordIter* props = sord_find(model, spat); FOREACH_MATCH(props) { @@ -357,8 +524,8 @@ new_state_from_model(LilvWorld* world, (const char*)sord_node_get_string(p)); if (sord_node_get_type(o) == SORD_BLANK) { - const SordNode* type = get_one(model, o, world->rdf_a_node); - const SordNode* value = get_one(model, o, world->rdf_value_node); + const SordNode* type = get_one(model, o, world->uris.rdf_a); + const SordNode* value = get_one(model, o, world->uris.rdf_value); if (type && value) { size_t len; const uint8_t* b64 = sord_node_get_string_counted(value, &len); @@ -369,6 +536,15 @@ new_state_from_model(LilvWorld* world, LILV_ERRORF("Unable to parse blank node property <%p>\n", sord_node_get_string(p)); } +#ifdef HAVE_LV2_STATE + } else if (sord_node_equals(sord_node_get_datatype(o), + state_path_node)) { + prop.size = strlen((const char*)sord_node_get_string(o)) + 1; + prop.type = map->map(map->handle, LV2_STATE_PATH_URI); + prop.flags = LV2_STATE_IS_PORTABLE; + prop.value = lilv_strjoin( + state->dir, "/", sord_node_get_string(o), NULL); +#endif } else { LilvNode* onode = lilv_node_new_from_node(world, o); property_from_node(world, map, onode, &prop); @@ -385,6 +561,7 @@ new_state_from_model(LilvWorld* world, sord_iter_free(props); } sord_node_free(world->world, statep); + sord_node_free(world->world, state_path_node); qsort(state->props, state->num_props, sizeof(Property), property_cmp); qsort(state->values, state->num_values, sizeof(PortValue), value_cmp); @@ -404,7 +581,10 @@ lilv_state_new_from_world(LilvWorld* world, return NULL; } - return new_state_from_model(world, map, world->model, node->val.uri_val); + LilvState* state = new_state_from_model( + world, map, world->model, node->val.uri_val, NULL); + + return state; } LILV_API @@ -433,7 +613,12 @@ lilv_state_new_from_file(LilvWorld* world, ? subject->val.uri_val : sord_node_from_serd_node(world->world, env, &base, NULL, NULL); - LilvState* state = new_state_from_model(world, map, model, subject_node); + char* dirname = lilv_dirname(path); + char* real_path = realpath(dirname, NULL); + LilvState* state = new_state_from_model( + world, map, model, subject_node, real_path); + free(dirname); + free(real_path); serd_reader_free(reader); sord_free(model); @@ -533,9 +718,7 @@ add_state_to_manifest(const LilvNode* plugin_uri, serd_env_set_prefix_from_strings(env, USTR("rdf"), USTR(LILV_NS_RDF)); serd_env_set_prefix_from_strings(env, USTR("rdfs"), USTR(LILV_NS_RDFS)); -#if defined(HAVE_LOCKF) && defined(HAVE_FILENO) - lockf(fileno(fd), F_LOCK, 0); -#endif + lilv_flock(fd, true); char* const manifest_uri = lilv_strjoin("file://", manifest_path, NULL); @@ -581,9 +764,7 @@ add_state_to_manifest(const LilvNode* plugin_uri, serd_writer_free(writer); serd_node_free(&base); -#ifdef HAVE_LOCKF - lockf(fileno(fd), F_ULOCK, 0); -#endif + lilv_flock(fd, false); fclose(fd); free(manifest_uri); @@ -632,77 +813,63 @@ mkdir_p(const char* dir_path) return 0; } -static int -lilv_default_state_path(LilvWorld* world, - const LilvState* state, - char** path, - char** manifest_path) +static char* +lilv_default_state_dir(LilvWorld* world) { #ifdef HAVE_MKDIR - if (!state->label) { - LILV_ERROR("Attempt to save state with no label or path.\n"); - return 1; - } - + // Use environment variable or default value if it is unset char* state_bundle = getenv("LV2_STATE_BUNDLE"); if (!state_bundle) { state_bundle = LILV_DEFAULT_STATE_BUNDLE; } - // Create ~/.lv2/presets.lv2/ - char* const bundle = lilv_expand(state_bundle); - if (mkdir_p(bundle)) { - free(bundle); - return 3; - } - - char* const filename = pathify(state->label); - - *path = lilv_strjoin( - bundle, LILV_DIR_SEP, filename, ".ttl", NULL); - - *manifest_path = lilv_strjoin( - bundle, LILV_DIR_SEP, "manifest.ttl", NULL); - - free(bundle); - free(filename); - - return 0; + // Expand any variables and create if necessary + return lilv_expand(state_bundle); #else LILV_ERROR("Save to default state path but mkdir is unavailable.\n"); - return 4; + return NULL; #endif } LILV_API int -lilv_state_save(LilvWorld* world, - LV2_URID_Unmap* unmap, - const LilvState* state, - const char* uri, - const char* path, - const char* manifest_path) -{ - char* default_path = NULL; - char* default_manifest_path = NULL; - if (!path) { - if (lilv_default_state_path( - world, state, &default_path, &default_manifest_path)) { - return 1; - } - - path = default_path; - manifest_path = default_manifest_path; +lilv_state_save(LilvWorld* world, + LV2_URID_Unmap* unmap, + const LilvState* state, + const char* uri, + const char* dir, + const char* filename, + const LV2_Feature *const * features) +{ + char* default_dir = NULL; + char* default_filename = NULL; + if (!dir) { + dir = default_dir = lilv_default_state_dir(world); + } + if (mkdir_p(dir)) { + free(default_dir); + return 1; } + if (!filename) { + filename = default_filename = pathify(state->label); + } + + char* const path = lilv_strjoin(dir, "/", filename, ".ttl", NULL); FILE* fd = fopen(path, "w"); if (!fd) { LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno)); - free(default_path); - free(default_manifest_path); + free(default_dir); + free(default_filename); + free(path); return 4; } + // FIXME: make parameter non-const? + ((LilvState*)state)->dir = lilv_strdup(dir); + + char* const manifest = lilv_strjoin(dir, "/manifest.ttl", NULL); + SerdEnv* env = serd_env_new(NULL); serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2)); serd_env_set_prefix_from_strings(env, USTR("pset"), USTR(NS_PSET)); @@ -776,6 +943,27 @@ lilv_state_save(LilvWorld* world, serd_writer_end_anon(writer, &port); } + // Create symlinks to external files +#ifdef HAVE_LV2_STATE + for (ZixTreeIter* i = zix_tree_begin(state->abs2rel); + i != zix_tree_end(state->abs2rel); + i = zix_tree_iter_next(i)) { + const PathMap* pm = (const PathMap*)zix_tree_get(i); + + char* real_dir = lilv_strjoin(realpath(dir, NULL), "/", NULL); + char* rel_path = lilv_strjoin(dir, "/", pm->rel, NULL); + char* target_path = lilv_path_is_child(pm->abs, state->file_dir) + ? lilv_path_relative_to(pm->abs, real_dir) + : lilv_strdup(pm->abs); + if (symlink(target_path, rel_path)) { + LILV_ERRORF("Failed to link `%s' => `%s' (%s)\n", + pm->abs, pm->rel, strerror(errno)); + } + free(target_path); + free(rel_path); + } +#endif + // Save properties const SerdNode state_node = serd_node_from_string(SERD_BLANK, USTR("2state")); @@ -793,11 +981,6 @@ lilv_state_save(LilvWorld* world, LILV_WARNF("Failed to unmap property key `%d'\n", prop->key); } else if (!type) { LILV_WARNF("Failed to unmap property type `%d'\n", prop->type); -#ifdef HAVE_LV2_STATE - } else if (!(prop->flags & LV2_STATE_IS_PORTABLE) - || !(prop->flags & LV2_STATE_IS_POD)) { - LILV_WARNF("Unable to save non-portable property <%s>\n", type); -#endif } else { SerdNode t; p = serd_node_from_string(SERD_URI, USTR(key)); @@ -805,10 +988,20 @@ lilv_state_save(LilvWorld* world, world, unmap, type, prop->value, prop->size); if (node) { node_to_serd(node, &o, &t); + // <state> <key> value serd_writer_write_statement( writer, SERD_ANON_CONT, NULL, &state_node, &p, &o, &t, NULL); lilv_node_free(node); +#ifdef HAVE_LV2_STATE + } else if (!strcmp(type, NS_STATE "Path")) { + o = serd_node_from_string(SERD_LITERAL, prop->value); + t = serd_node_from_string(SERD_URI, (const uint8_t*)type); + // <state> <key> "the/path"^^<state:Path> + serd_writer_write_statement( + writer, SERD_ANON_CONT, NULL, + &state_node, &p, &o, &t, NULL); +#endif } else { char name[16]; snprintf(name, sizeof(name), "b%u", i); @@ -817,24 +1010,22 @@ lilv_state_save(LilvWorld* world, // <state> <key> [ serd_writer_write_statement( - writer, SERD_ANON_O_BEGIN, NULL, + writer, SERD_ANON_CONT|SERD_ANON_O_BEGIN, NULL, &state_node, &p, &blank, NULL, NULL); // rdf:type <type> p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")); o = serd_node_from_string(SERD_URI, USTR(type)); - serd_writer_write_statement( - writer, SERD_ANON_CONT, NULL, - &blank, &p, &o, NULL, NULL); + serd_writer_write_statement(writer, SERD_ANON_CONT, NULL, + &blank, &p, &o, NULL, NULL); // rdf:value "string"^^<xsd:base64Binary> SerdNode blob = serd_node_new_blob(prop->value, prop->size, true); p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "value")); t = serd_node_from_string(SERD_URI, USTR(LILV_NS_XSD "base64Binary")); - serd_writer_write_statement( - writer, SERD_ANON_CONT, NULL, - &blank, &p, &blob, &t, NULL); + serd_writer_write_statement(writer, SERD_ANON_CONT, NULL, + &blank, &p, &blob, &t, NULL); serd_node_free(&blob); serd_writer_end_anon(writer, &blank); // ] @@ -850,13 +1041,12 @@ lilv_state_save(LilvWorld* world, fclose(fd); serd_env_free(env); - if (manifest_path) { - add_state_to_manifest( - state->plugin_uri, manifest_path, uri, path); + if (manifest) { + add_state_to_manifest(state->plugin_uri, manifest, uri, path); } - free(default_path); - free(default_manifest_path); + free(default_dir); + free(default_filename); return 0; } @@ -873,9 +1063,13 @@ lilv_state_free(LilvState* state) free(state->values[i].symbol); } lilv_node_free(state->plugin_uri); + zix_tree_free(state->abs2rel); + zix_tree_free(state->rel2abs); free(state->props); free(state->values); free(state->label); + free(state->dir); + free(state->file_dir); free(state); } } @@ -906,11 +1100,25 @@ lilv_state_equals(const LilvState* a, const LilvState* b) for (uint32_t i = 0; i < a->num_props; ++i) { Property* const ap = &a->props[i]; Property* const bp = &b->props[i]; - if (ap->size != bp->size - || ap->key != bp->key + if (ap->key != bp->key || ap->type != bp->type - || ap->flags != bp->flags - || memcmp(ap->value, bp->value, ap->size)) { + || ap->flags != bp->flags) { + return false; + } + + if (ap->type == a->state_Path) { + const char* const a_abs = lilv_state_rel2abs(a, ap->value); + const char* const b_abs = lilv_state_rel2abs(b, bp->value); + char* const a_real = realpath(a_abs, NULL); + char* const b_real = realpath(b_abs, NULL); + const int cmp = strcmp(a_real, b_real); + free(a_real); + free(b_real); + if (cmp) { + return false; + } + } else if (ap->size != bp->size + || memcmp(ap->value, bp->value, ap->size)) { return false; } } @@ -14,13 +14,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -#define _POSIX_SOURCE 1 /* for wordexp */ +#define _POSIX_SOURCE 1 /* for wordexp, fileno */ +#define _BSD_SOURCE 1 /* for lockf */ #include <assert.h> +#include <errno.h> #include <stdarg.h> #include <stdlib.h> #include <string.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + #include "lilv_internal.h" #ifdef HAVE_WORDEXP @@ -138,3 +145,218 @@ lilv_expand(const char* path) #endif return ret; } + +char* +lilv_dirname(const char* path) +{ + const char* s = path + strlen(path) - 1; // Last character + for (; s > path && *s == LILV_DIR_SEP[0]; --s) {} // Last non-slash + for (; s > path && *s != LILV_DIR_SEP[0]; --s) {} // Last internal slash + for (; s > path && *s == LILV_DIR_SEP[0]; --s) {} // Skip duplicates + + if (s == path) { // Hit beginning + return (*s == '/') ? lilv_strdup("/") : lilv_strdup("."); + } else { // Pointing to the last character of the result (inclusive) + char* dirname = malloc(s - path + 2); + memcpy(dirname, path, s - path + 1); + dirname[s - path + 1] = '\0'; + return dirname; + } +} + +bool +lilv_path_exists(const char* path, void* ignored) +{ + return !access(path, F_OK); +} + +char* +lilv_find_free_path( + const char* in_path, bool (*exists)(const char*, void*), void* user_data) +{ + const size_t in_path_len = strlen(in_path); + char* path = malloc(in_path_len + 7); + memcpy(path, in_path, in_path_len + 1); + + for (int i = 2; i < 1000000; ++i) { + if (!exists(path, user_data)) { + return path; + } + snprintf(path, in_path_len + 7, "%s%u", in_path, i); + } + + return NULL; +} + +int +lilv_copy_file(const char* src, const char* dst) +{ + FILE* in = fopen(src, "r"); + if (!in) { + LILV_ERRORF("error opening %s (%s)\n", src, strerror(errno)); + return 1; + } + + FILE* out = fopen(dst, "w"); + if (!out) { + LILV_ERRORF("error opening %s (%s)\n", dst, strerror(errno)); + fclose(in); + return 2; + } + + static const size_t PAGE_SIZE = 4096; + char* page = malloc(PAGE_SIZE); + size_t n_read = 0; + while ((n_read = fread(page, 1, PAGE_SIZE, in)) > 0) { + if (fwrite(page, 1, n_read, out) != n_read) { + LILV_ERRORF("write to %s failed (%s)\n", dst, strerror(errno)); + break; + } + } + + const int ret = ferror(in) || ferror(out); + if (ferror(in)) { + LILV_ERRORF("read from %s failed (%s)\n", src, strerror(errno)); + } + + free(page); + fclose(in); + fclose(out); + + return ret; +} + +static bool +lilv_is_dir_sep(const char c) +{ + return c == '/' || c == LILV_DIR_SEP[0]; +} + +bool +lilv_path_is_absolute(const char* path) +{ + if (lilv_is_dir_sep(path[0])) { + return true; + } + +#ifdef __WIN32__ + if (isalpha(path[0]) && path[1] == ':' && lilv_is_dir_sep(path[2])) { + return true; + } +#endif + + return false; +} + +static void +lilv_size_mtime(const char* path, off_t* size, time_t* time) +{ + struct stat buf; + if (stat(path, &buf)) { + LILV_ERRORF("stat(%s) (%s)\n", path, strerror(errno)); + *size = *time = 0; + } + + *size = buf.st_size; + *time = buf.st_mtime; +} + +/** Return the latest copy of the file at @c path that is newer. */ +char* +lilv_get_latest_copy(const char* path) +{ + char* dirname = lilv_dirname(path); + DIR* dir = opendir(dirname); + if (!dir) { + free(dirname); + return NULL; + } + + char* pat = lilv_strjoin(path, "%u", NULL); + char* latest = NULL; + + off_t path_size; + time_t path_time; + lilv_size_mtime(path, &path_size, &path_time); + + struct dirent entry; + struct dirent* result; + while (!readdir_r(dir, &entry, &result) && result) { + char* entry_path = lilv_strjoin(dirname, "/", entry.d_name, NULL); + unsigned num; + if (sscanf(entry_path, pat, &num) == 1) { + off_t entry_size; + time_t entry_time; + lilv_size_mtime(entry_path, &entry_size, &entry_time); + if (entry_size == path_size && entry_time >= path_time) { + free(latest); + latest = entry_path; + } + } + if (entry_path != latest) { + free(entry_path); + } + } + free(dirname); + free(pat); + + return latest; +} + +char* +lilv_path_relative_to(const char* path, const char* base) +{ + const size_t path_len = strlen(path); + const size_t base_len = strlen(base); + const size_t min_len = (path_len < base_len) ? path_len : base_len; + + // Find the last separator common to both paths + size_t last_shared_sep = 0; + for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) { + if (lilv_is_dir_sep(path[i])) { + last_shared_sep = i; + } + } + + if (last_shared_sep == 0) { + // No common components, return path + return lilv_strdup(path); + } + + // Find the number of up references ("..") required + size_t up = 0; + for (size_t i = last_shared_sep + 1; i < base_len; ++i) { + if (lilv_is_dir_sep(base[i])) { + ++up; + } + } + + // Write up references + const size_t suffix_len = path_len - last_shared_sep; + char* rel = calloc(1, suffix_len + (up * 3) + 1); + for (size_t i = 0; i < up; ++i) { + memcpy(rel + (i * 3), ".." LILV_DIR_SEP, 3); + } + + // Write suffix + memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); + return rel; +} + +bool +lilv_path_is_child(const char* path, const char* dir) +{ + const size_t path_len = strlen(path); + const size_t dir_len = strlen(dir); + return dir && path_len >= dir_len && !strncmp(path, dir, dir_len); +} + +int +lilv_flock(FILE* file, bool lock) +{ +#if defined(HAVE_LOCKF) && defined(HAVE_FILENO) + return lockf(fileno(file), lock ? F_LOCK : F_ULOCK, 0); +#else + return 0; +#endif +} diff --git a/src/world.c b/src/world.c index 1bf357a..32e4f63 100644 --- a/src/world.c +++ b/src/world.c @@ -51,40 +51,41 @@ lilv_world_new(void) #define NEW_URI(uri) sord_new_uri(world->world, (const uint8_t*)uri) - world->dc_replaces_node = NEW_URI(NS_DCTERMS "replaces"); - world->doap_name_node = NEW_URI(LILV_NS_DOAP "name"); - world->dyn_manifest_node = NEW_URI(NS_DYNMAN "DynManifest"); - world->lv2_appliesTo_node = NEW_URI(LILV_NS_LV2 "appliesTo"); - world->lv2_binary_node = NEW_URI(LILV_NS_LV2 "binary"); - world->lv2_default_node = NEW_URI(LILV_NS_LV2 "default"); - world->lv2_extensionData_node = NEW_URI(LILV_NS_LV2 "extensionData"); - world->lv2_index_node = NEW_URI(LILV_NS_LV2 "index"); - world->lv2_maximum_node = NEW_URI(LILV_NS_LV2 "maximum"); - world->lv2_minimum_node = NEW_URI(LILV_NS_LV2 "minimum"); - world->lv2_name_node = NEW_URI(LILV_NS_LV2 "name"); - world->lv2_optionalFeature_node = NEW_URI(LILV_NS_LV2 "optionalFeature"); - world->lv2_plugin_node = NEW_URI(LILV_NS_LV2 "Plugin"); - world->lv2_port_node = NEW_URI(LILV_NS_LV2 "port"); - world->lv2_portproperty_node = NEW_URI(LILV_NS_LV2 "portProperty"); - world->lv2_reportslatency_node = NEW_URI(LILV_NS_LV2 "reportsLatency"); - world->lv2_requiredFeature_node = NEW_URI(LILV_NS_LV2 "requiredFeature"); - world->lv2_specification_node = NEW_URI(LILV_NS_LV2 "Specification"); - world->lv2_symbol_node = NEW_URI(LILV_NS_LV2 "symbol"); - world->pset_value_node = NEW_URI(NS_PSET "value"); - world->rdf_a_node = NEW_URI(LILV_NS_RDF "type"); - world->rdf_value_node = NEW_URI(LILV_NS_RDF "value"); - world->rdfs_class_node = NEW_URI(LILV_NS_RDFS "Class"); - world->rdfs_label_node = NEW_URI(LILV_NS_RDFS "label"); - world->rdfs_seealso_node = NEW_URI(LILV_NS_RDFS "seeAlso"); - world->rdfs_subclassof_node = NEW_URI(LILV_NS_RDFS "subClassOf"); - world->xsd_base64Binary_node = NEW_URI(LILV_NS_XSD "base64Binary"); - world->xsd_boolean_node = NEW_URI(LILV_NS_XSD "boolean"); - world->xsd_decimal_node = NEW_URI(LILV_NS_XSD "decimal"); - world->xsd_double_node = NEW_URI(LILV_NS_XSD "double"); - world->xsd_integer_node = NEW_URI(LILV_NS_XSD "integer"); + world->uris.dc_replaces = NEW_URI(NS_DCTERMS "replaces"); + world->uris.doap_name = NEW_URI(LILV_NS_DOAP "name"); + world->uris.dman_DynManifest = NEW_URI(NS_DYNMAN "DynManifest"); + world->uris.lv2_appliesTo = NEW_URI(LILV_NS_LV2 "appliesTo"); + world->uris.lv2_binary = NEW_URI(LILV_NS_LV2 "binary"); + world->uris.lv2_default = NEW_URI(LILV_NS_LV2 "default"); + world->uris.lv2_extensionData = NEW_URI(LILV_NS_LV2 "extensionData"); + world->uris.lv2_index = NEW_URI(LILV_NS_LV2 "index"); + world->uris.lv2_maximum = NEW_URI(LILV_NS_LV2 "maximum"); + world->uris.lv2_minimum = NEW_URI(LILV_NS_LV2 "minimum"); + world->uris.lv2_name = NEW_URI(LILV_NS_LV2 "name"); + world->uris.lv2_optionalFeature = NEW_URI(LILV_NS_LV2 "optionalFeature"); + world->uris.lv2_Plugin = NEW_URI(LILV_NS_LV2 "Plugin"); + world->uris.lv2_port = NEW_URI(LILV_NS_LV2 "port"); + world->uris.lv2_portProperty = NEW_URI(LILV_NS_LV2 "portProperty"); + world->uris.lv2_reportsLatency = NEW_URI(LILV_NS_LV2 "reportsLatency"); + world->uris.lv2_requiredFeature = NEW_URI(LILV_NS_LV2 "requiredFeature"); + world->uris.lv2_Specification = NEW_URI(LILV_NS_LV2 "Specification"); + world->uris.lv2_symbol = NEW_URI(LILV_NS_LV2 "symbol"); + world->uris.pset_value = NEW_URI(NS_PSET "value"); + world->uris.rdf_a = NEW_URI(LILV_NS_RDF "type"); + world->uris.rdf_value = NEW_URI(LILV_NS_RDF "value"); + world->uris.rdfs_Class = NEW_URI(LILV_NS_RDFS "Class"); + world->uris.rdfs_label = NEW_URI(LILV_NS_RDFS "label"); + world->uris.rdfs_seeAlso = NEW_URI(LILV_NS_RDFS "seeAlso"); + world->uris.rdfs_subClassOf = NEW_URI(LILV_NS_RDFS "subClassOf"); + world->uris.xsd_base64Binary = NEW_URI(LILV_NS_XSD "base64Binary"); + world->uris.xsd_boolean = NEW_URI(LILV_NS_XSD "boolean"); + world->uris.xsd_decimal = NEW_URI(LILV_NS_XSD "decimal"); + world->uris.xsd_double = NEW_URI(LILV_NS_XSD "double"); + world->uris.xsd_integer = NEW_URI(LILV_NS_XSD "integer"); + world->uris.null_uri = NULL; world->lv2_plugin_class = lilv_plugin_class_new( - world, NULL, world->lv2_plugin_node, "Plugin"); + world, NULL, world->uris.lv2_Plugin, "Plugin"); assert(world->lv2_plugin_class); world->n_read_files = 0; @@ -109,37 +110,9 @@ lilv_world_free(LilvWorld* world) lilv_plugin_class_free(world->lv2_plugin_class); world->lv2_plugin_class = NULL; - sord_node_free(world->world, world->dc_replaces_node); - sord_node_free(world->world, world->doap_name_node); - sord_node_free(world->world, world->dyn_manifest_node); - sord_node_free(world->world, world->lv2_appliesTo_node); - sord_node_free(world->world, world->lv2_binary_node); - sord_node_free(world->world, world->lv2_default_node); - sord_node_free(world->world, world->lv2_extensionData_node); - sord_node_free(world->world, world->lv2_index_node); - sord_node_free(world->world, world->lv2_maximum_node); - sord_node_free(world->world, world->lv2_minimum_node); - sord_node_free(world->world, world->lv2_name_node); - sord_node_free(world->world, world->lv2_optionalFeature_node); - sord_node_free(world->world, world->lv2_plugin_node); - sord_node_free(world->world, world->lv2_port_node); - sord_node_free(world->world, world->lv2_portproperty_node); - sord_node_free(world->world, world->lv2_reportslatency_node); - sord_node_free(world->world, world->lv2_requiredFeature_node); - sord_node_free(world->world, world->lv2_specification_node); - sord_node_free(world->world, world->lv2_symbol_node); - sord_node_free(world->world, world->pset_value_node); - sord_node_free(world->world, world->rdf_a_node); - sord_node_free(world->world, world->rdf_value_node); - sord_node_free(world->world, world->rdfs_class_node); - sord_node_free(world->world, world->rdfs_label_node); - sord_node_free(world->world, world->rdfs_seealso_node); - sord_node_free(world->world, world->rdfs_subclassof_node); - sord_node_free(world->world, world->xsd_base64Binary_node); - sord_node_free(world->world, world->xsd_boolean_node); - sord_node_free(world->world, world->xsd_decimal_node); - sord_node_free(world->world, world->xsd_double_node); - sord_node_free(world->world, world->xsd_integer_node); + for (SordNode** n = (SordNode**)&world->uris; *n; ++n) { + sord_node_free(world->world, *n); + } for (LilvSpec* spec = world->specs; spec;) { LilvSpec* next = spec->next; @@ -325,7 +298,7 @@ lilv_world_add_spec(LilvWorld* world, SordIter* files = lilv_world_find_statements( world, world->model, specification_node, - world->rdfs_seealso_node, + world->uris.rdfs_seeAlso, NULL, NULL); FOREACH_MATCH(files) { @@ -381,7 +354,7 @@ lilv_world_add_plugin(LilvWorld* world, SordIter* files = lilv_world_find_statements( world, world->model, plugin_node, - world->rdfs_seealso_node, + world->uris.rdfs_seeAlso, NULL, NULL); FOREACH_MATCH(files) { @@ -413,8 +386,8 @@ lilv_world_load_dyn_manifest(LilvWorld* world, SordIter* dmanifests = lilv_world_find_statements( world, world->model, NULL, - world->rdf_a_node, - world->dyn_manifest_node, + world->uris.rdf_a, + world->uris.dyn_manifest, bundle_node); FOREACH_MATCH(dmanifests) { const SordNode* dmanifest = lilv_match_subject(dmanifests); @@ -423,7 +396,7 @@ lilv_world_load_dyn_manifest(LilvWorld* world, SordIter* binaries = lilv_world_find_statements( world, world->model, dmanifest, - world->lv2_binary_node, + world->uris.lv2_binary, NULL, bundle_node); if (lilv_matches_end(binaries)) { @@ -500,8 +473,8 @@ lilv_world_load_dyn_manifest(LilvWorld* world, SordIter* plug_results = lilv_world_find_statements( world, world->model, NULL, - world->rdf_a_node, - world->lv2_plugin_node, + world->uris.rdf_a, + world->uris.lv2_plugin, bundle_node); FOREACH_MATCH(plug_results) { const SordNode* plugin_node = lilv_match_subject(plug_results); @@ -548,8 +521,8 @@ lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri) SordIter* plug_results = lilv_world_find_statements( world, world->model, NULL, - world->rdf_a_node, - world->lv2_plugin_node, + world->uris.rdf_a, + world->uris.lv2_Plugin, bundle_node); FOREACH_MATCH(plug_results) { const SordNode* plugin_node = lilv_match_subject(plug_results); @@ -564,8 +537,8 @@ lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri) SordIter* spec_results = lilv_world_find_statements( world, world->model, NULL, - world->rdf_a_node, - world->lv2_specification_node, + world->uris.rdf_a, + world->uris.lv2_Specification, bundle_node); FOREACH_MATCH(spec_results) { const SordNode* spec = lilv_match_subject(spec_results); @@ -695,8 +668,8 @@ lilv_world_load_plugin_classes(LilvWorld* world) SordIter* classes = lilv_world_find_statements( world, world->model, NULL, - world->rdf_a_node, - world->rdfs_class_node, + world->uris.rdf_a, + world->uris.rdfs_Class, NULL); FOREACH_MATCH(classes) { const SordNode* class_node = lilv_match_subject(classes); @@ -705,7 +678,7 @@ lilv_world_load_plugin_classes(LilvWorld* world) SordIter* parents = lilv_world_find_statements( world, world->model, class_node, - world->rdfs_subclassof_node, + world->uris.rdfs_subClassOf, NULL, NULL); @@ -726,7 +699,7 @@ lilv_world_load_plugin_classes(LilvWorld* world) SordIter* labels = lilv_world_find_statements( world, world->model, class_node, - world->rdfs_label_node, + world->uris.rdfs_label, NULL, NULL); @@ -769,7 +742,7 @@ lilv_world_load_all(LilvWorld* world) SordIter* replacement = lilv_world_find_statements( world, world->model, NULL, - world->dc_replaces_node, + world->uris.dc_replaces, lilv_node_as_node(plugin_uri), NULL); if (!sord_iter_end(replacement)) { @@ -799,7 +772,7 @@ lilv_world_load_resource(LilvWorld* world, int n_read = 0; SordIter* files = lilv_world_find_statements(world, world->model, resource->val.uri_val, - world->rdfs_seealso_node, + world->uris.rdfs_seeAlso, NULL, NULL); FOREACH_MATCH(files) { const SordNode* file = lilv_match_object(files); diff --git a/src/zix/common.h b/src/zix/common.h index 052cb7b..c0dffeb 100644 --- a/src/zix/common.h +++ b/src/zix/common.h @@ -64,7 +64,7 @@ typedef bool (*ZixEqualFunc)(const void* a, const void* b); /** Function to destroy an element. */ -typedef void (*ZixDestroyFunc)(const void* ptr); +typedef void (*ZixDestroyFunc)(void* ptr); /**@} */ diff --git a/src/zix/tree.c b/src/zix/tree.c index 1189230..d22f438 100644 --- a/src/zix/tree.c +++ b/src/zix/tree.c @@ -101,8 +101,10 @@ ZIX_API void zix_tree_free(ZixTree* t) { - zix_tree_free_rec(t, t->root); - free(t); + if (t) { + zix_tree_free_rec(t, t->root); + free(t); + } } size_t |