summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/lilv_internal.h92
-rw-r--r--src/node.c10
-rw-r--r--src/plugin.c50
-rw-r--r--src/port.c16
-rw-r--r--src/state.c482
-rw-r--r--src/util.c224
-rw-r--r--src/world.c133
-rw-r--r--src/zix/common.h2
-rw-r--r--src/zix/tree.c6
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);
diff --git a/src/node.c b/src/node.c
index 38655a4..8f73df6 100644
--- a/src/node.c
+++ b/src/node.c
@@ -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);
diff --git a/src/port.c b/src/port.c
index 727911a..b14f43a 100644
--- a/src/port.c
+++ b/src/port.c
@@ -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;
}
}
diff --git a/src/util.c b/src/util.c
index 1c9a46e..b421660 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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