diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | lilv/lilv.h | 38 | ||||
-rw-r--r-- | src/collections.c | 8 | ||||
-rw-r--r-- | src/lilv_internal.h | 8 | ||||
-rw-r--r-- | src/plugin.c | 29 | ||||
-rw-r--r-- | src/port.c | 2 | ||||
-rw-r--r-- | src/query.c | 22 | ||||
-rw-r--r-- | src/world.c | 79 | ||||
-rw-r--r-- | utils/lv2info.c | 55 | ||||
-rw-r--r-- | wscript | 2 |
10 files changed, 205 insertions, 43 deletions
@@ -9,6 +9,11 @@ lilv (UNRELEASED) unstable; urgency=low * Fix compilation on BSD * Fix crash in wordexp when LV2_PATH is corrupt * Only load dynmanifest libraries once per bundle, not once per plugin + * Fix lilv_world_find_nodes to work with wildcard subjects + * Add lilv_plugin_get_related to get resources related to plugins that + are not directly rdfs:seeAlso linked (e.g. presets) + * Add lilv_world_load_resource for related resources (e.g. presets) + * Print presets in lv2info -- David Robillard <d@drobilla.net> (UNRELEASED) diff --git a/lilv/lilv.h b/lilv/lilv.h index 649d341..ca3809d 100644 --- a/lilv/lilv.h +++ b/lilv/lilv.h @@ -583,6 +583,19 @@ lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri); /** + Load all the data associated with the given @c resource. + @param resource Must be a subject (i.e. a URI or a blank node). + @return The number of files parsed, or -1 on error + + All accessible data files linked to @c resource with rdfs:seeAlso will be + loaded into the world model. +*/ +LILV_API +int +lilv_world_load_resource(LilvWorld* world, + const LilvNode* resource); + +/** Get the parent of all other plugin classes, lv2:Plugin. */ LILV_API @@ -919,7 +932,7 @@ bool lilv_plugin_is_replaced(const LilvPlugin* plugin); /** - Write the Turtle description of @c plugin to @c file. + Write the Turtle description of @c plugin to @c plugin_file. This function is particularly useful for porting plugins in conjunction with an LV2 bridge such as NASPRO. @@ -931,6 +944,12 @@ lilv_plugin_write_description(LilvWorld* world, const LilvNode* base_uri, FILE* plugin_file); +/** + Write a manifest entry for @c plugin to @c manifest_file. + + This function is intended for use with lilv_plugin_write_description to + write a complete description of a plugin to a bundle. +*/ LILV_API void lilv_plugin_write_manifest_entry(LilvWorld* world, @@ -940,6 +959,23 @@ lilv_plugin_write_manifest_entry(LilvWorld* world, const char* plugin_file_path); /** + Get the resources related to @c plugin with lv2:appliesTo. + + Some plugin-related resources are not linked directly to the plugin with + rdfs:seeAlso and thus will not be automatically loaded along with the plugin + data (usually for performance reasons). All such resources of the given @c + type related to @c plugin can be accessed with this function. + + If @c type is NULL, all such resources will be returned, regardless of type. + + To actually load the data for each returned resource, use + lilv_world_load_resource. +*/ +LILV_API +LilvNodes* +lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type); + +/** @} @name Port @{ diff --git a/src/collections.c b/src/collections.c index e057c6f..77917f6 100644 --- a/src/collections.c +++ b/src/collections.c @@ -22,6 +22,14 @@ lilv_ptr_cmp(const void* a, const void* b, void* user_data) return (intptr_t)a - (intptr_t)b; } +int +lilv_resource_node_cmp(const void* a, const void* b, void* user_data) +{ + const SordNode* an = ((LilvNode*)a)->val.uri_val; + const SordNode* bn = ((LilvNode*)a)->val.uri_val; + return (intptr_t)an - (intptr_t)bn; +} + /* Generic collection functions */ static inline LilvCollection* diff --git a/src/lilv_internal.h b/src/lilv_internal.h index 710d00f..a700d6f 100644 --- a/src/lilv_internal.h +++ b/src/lilv_internal.h @@ -129,6 +129,7 @@ struct LilvWorldImpl { LilvPluginClasses* plugin_classes; LilvSpec* specs; LilvPlugins* plugins; + LilvNodes* loaded_files; SordNode* dc_replaces_node; SordNode* dyn_manifest_node; SordNode* lv2_binary_node; @@ -153,6 +154,7 @@ struct LilvWorldImpl { SordNode* xsd_double_node; SordNode* xsd_integer_node; LilvNode* doap_name_val; + LilvNode* lv2_applies_to_val; LilvNode* lv2_extensionData_val; LilvNode* lv2_name_val; LilvNode* lv2_optionalFeature_val; @@ -256,6 +258,9 @@ int lilv_header_compare_by_uri(const void* a, const void* b, void* user_data); int lilv_ptr_cmp(const void* a, const void* b, void* user_data); +int +lilv_resource_node_cmp(const void* a, const void* b, void* user_data); + struct LilvHeader* lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri); @@ -306,7 +311,8 @@ static inline bool lilv_matches_end(SordIter* matches) { } LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* w, - SordIter* stream); + SordIter* stream, + bool object); char* lilv_strjoin(const char* first, ...); char* lilv_strdup(const char* str); diff --git a/src/plugin.c b/src/plugin.c index f8cf054..b305291 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -828,6 +828,35 @@ lilv_plugin_get_uis(const LilvPlugin* p) } } +LILV_API +LilvNodes* +lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type) +{ + LilvWorld* const world = plugin->world; + LilvNodes* const related = lilv_world_find_nodes( + world, NULL, world->lv2_applies_to_val, lilv_plugin_get_uri(plugin)); + + if (!type) { + return related; + } + + LilvNodes* matches = lilv_nodes_new(); + 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); + if (!sord_iter_end(titer)) { + zix_tree_insert(matches, + lilv_node_new_from_node(world, node->val.uri_val), + NULL); + } + sord_iter_free(titer); + } + + lilv_nodes_free(related); + return matches; +} + static size_t file_sink(const void* buf, size_t len, void* stream) { @@ -109,7 +109,7 @@ lilv_port_get_value_by_node(const LilvPlugin* p, predicate, NULL); - return lilv_nodes_from_stream_objects(p->world, results); + return lilv_nodes_from_stream_objects(p->world, results, true); } LILV_API diff --git a/src/query.c b/src/query.c index e465a67..40387ef 100644 --- a/src/query.c +++ b/src/query.c @@ -53,14 +53,17 @@ lilv_lang_matches(const char* a, const char* b) LilvNodes* lilv_nodes_from_stream_objects_i18n(LilvWorld* world, - SordIter* stream) + SordIter* stream, + bool object) { LilvNodes* values = lilv_nodes_new(); const SordNode* nolang = NULL; // Untranslated value const SordNode* partial = NULL; // Partial language match char* syslang = lilv_get_lang(); FOREACH_MATCH(stream) { - const SordNode* value = lilv_match_object(stream); + const SordNode* value = object + ? lilv_match_object(stream) + : lilv_match_subject(stream); if (sord_node_get_type(value) == SORD_LITERAL) { const char* lang = sord_node_get_language(value); LilvLangMatch lm = LILV_LANG_MATCH_NONE; @@ -117,20 +120,23 @@ lilv_nodes_from_stream_objects_i18n(LilvWorld* world, LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* world, - SordIter* stream) + SordIter* stream, + bool object) { if (lilv_matches_end(stream)) { lilv_match_end(stream); return NULL; } else if (world->opt.filter_language) { - return lilv_nodes_from_stream_objects_i18n(world, stream); + return lilv_nodes_from_stream_objects_i18n(world, stream, object); } else { LilvNodes* values = lilv_nodes_new(); FOREACH_MATCH(stream) { - LilvNode* value = lilv_node_new_from_node( - world, lilv_match_object(stream)); - if (value) { - zix_tree_insert(values, value, NULL); + const SordNode* value = object + ? lilv_match_object(stream) + : lilv_match_subject(stream); + LilvNode* node = lilv_node_new_from_node(world, value); + if (node) { + zix_tree_insert(values, node, NULL); } } lilv_match_end(stream); diff --git a/src/world.c b/src/world.c index 7a51a3b..d5af0fe 100644 --- a/src/world.c +++ b/src/world.c @@ -45,6 +45,9 @@ lilv_world_new(void) world->specs = NULL; world->plugin_classes = lilv_plugin_classes_new(); world->plugins = lilv_plugins_new(); + world->loaded_files = zix_tree_new( + false, lilv_resource_node_cmp, NULL, (ZixDestroyFunc)lilv_node_free); + #define NS_DYNMAN "http://lv2plug.in/ns/ext/dynmanifest#" #define NS_DCTERMS "http://purl.org/dc/terms/" @@ -77,6 +80,7 @@ lilv_world_new(void) world->xsd_integer_node = NEW_URI(LILV_NS_XSD "integer"); world->doap_name_val = NEW_URI_VAL(LILV_NS_DOAP "name"); + world->lv2_applies_to_val = NEW_URI_VAL(LILV_NS_LV2 "appliesTo"); world->lv2_extensionData_val = NEW_URI_VAL(LILV_NS_LV2 "extensionData"); world->lv2_name_val = NEW_URI_VAL(LILV_NS_LV2 "name"); world->lv2_optionalFeature_val = NEW_URI_VAL(LILV_NS_LV2 "optionalFeature"); @@ -132,6 +136,7 @@ lilv_world_free(LilvWorld* world) sord_node_free(world->world, world->xsd_double_node); sord_node_free(world->world, world->xsd_integer_node); lilv_node_free(world->doap_name_val); + lilv_node_free(world->lv2_applies_to_val); lilv_node_free(world->lv2_extensionData_val); lilv_node_free(world->lv2_name_val); lilv_node_free(world->lv2_optionalFeature_val); @@ -154,6 +159,9 @@ lilv_world_free(LilvWorld* world) zix_tree_free(world->plugins); world->plugins = NULL; + zix_tree_free(world->loaded_files); + world->loaded_files = NULL; + zix_tree_free(world->plugin_classes); world->plugin_classes = NULL; @@ -205,7 +213,7 @@ lilv_world_find_nodes(LilvWorld* world, const LilvNode* predicate, const LilvNode* object) { - if (!lilv_node_is_uri(subject) && !lilv_node_is_blank(subject)) { + if (subject && !lilv_node_is_uri(subject) && !lilv_node_is_blank(subject)) { LILV_ERRORF("Subject `%s' is not a resource\n", subject->str_val); return NULL; } @@ -213,18 +221,24 @@ lilv_world_find_nodes(LilvWorld* world, LILV_ERRORF("Predicate `%s' is not a URI\n", predicate->str_val); return NULL; } + if (!subject && !object) { + LILV_ERROR("Both subject and object are NULL\n"); + return NULL; + } - SordNode* subject_node = (lilv_node_is_uri(subject)) + SordNode* const subject_node = subject ? sord_node_copy(subject->val.uri_val) - : sord_new_blank(world->world, - (const uint8_t*)lilv_node_as_blank(subject)); + : NULL; + + SordNode* const object_node = object + ? sord_node_copy(object->val.uri_val) + : NULL; - LilvNodes* ret = lilv_world_query_values_internal(world, - subject_node, - predicate->val.uri_val, - NULL); + LilvNodes* ret = lilv_world_query_values_internal( + world, subject_node, predicate->val.uri_val, object_node); sord_node_free(world->world, subject_node); + sord_node_free(world->world, object_node); return ret; } @@ -246,7 +260,8 @@ lilv_world_query_values_internal(LilvWorld* world, { return lilv_nodes_from_stream_objects( world, - lilv_world_query_internal(world, subject, predicate, object)); + lilv_world_query_internal(world, subject, predicate, object), + (object == NULL)); } static SerdNode @@ -805,6 +820,52 @@ lilv_world_load_all(LilvWorld* world) } LILV_API +int +lilv_world_load_resource(LilvWorld* world, + const LilvNode* resource) +{ + if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) { + LILV_ERRORF("Node `%s' is not a resource\n", resource->str_val); + return -1; + } + + int n_read = 0; + SordIter* files = lilv_world_find_statements(world, world->model, + resource->val.uri_val, + world->rdfs_seealso_node, + NULL, NULL); + FOREACH_MATCH(files) { + const SordNode* file = lilv_match_object(files); + const uint8_t* str = sord_node_get_string(file); + LilvNode* file_node = lilv_node_new_from_node(world, file); + ZixTreeIter* iter; + if (zix_tree_find(world->loaded_files, file, &iter)) { + if (sord_node_get_type(file) == SORD_URI) { + const SerdNode* base = sord_node_to_serd_node(file); + SerdEnv* env = serd_env_new(base); + SerdReader* reader = sord_new_reader( + world->model, env, SERD_TURTLE, (SordNode*)file); + if (!serd_reader_read_file(reader, str)) { + ++n_read; + zix_tree_insert(world->loaded_files, file_node, NULL); + file_node = NULL; // prevent deletion... + } else { + LILV_ERRORF("Error loading resource `%s'\n", str); + } + serd_reader_free(reader); + serd_env_free(env); + } else { + LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", str); + } + } + lilv_node_free(file_node); // ...here + } + lilv_match_end(files); + + return n_read; +} + +LILV_API const LilvPluginClass* lilv_world_get_plugin_class(const LilvWorld* world) { diff --git a/utils/lv2info.c b/utils/lv2info.c index d55b9cd..7f231f3 100644 --- a/utils/lv2info.c +++ b/utils/lv2info.c @@ -24,19 +24,20 @@ #include "lilv-config.h" -LilvNode* event_class = NULL; +LilvNode* applies_to_pred = NULL; LilvNode* control_class = NULL; +LilvNode* event_class = NULL; LilvNode* in_group_pred = NULL; -LilvNode* role_pred = NULL; -LilvNode* preset_pred = NULL; LilvNode* label_pred = NULL; +LilvNode* preset_class = NULL; +LilvNode* role_pred = NULL; LilvNode* supports_event_pred = NULL; void print_group(const LilvPlugin* p, - const LilvNode* group, - LilvNode* type, - LilvNode* symbol) + const LilvNode* group, + LilvNode* type, + LilvNode* symbol) { printf("\n\tGroup %s:\n", lilv_node_as_string(group)); printf("\t\tType: %s\n", lilv_node_as_string(type)); @@ -198,23 +199,23 @@ print_plugin(LilvWorld* world, LilvUIs* uis = lilv_plugin_get_uis(p); if (lilv_nodes_size(uis) > 0) { - printf("\tUI: "); + printf("\tUIs:\n"); LILV_FOREACH(uis, i, uis) { const LilvUI* ui = lilv_uis_get(uis, i); - printf("%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); + printf("\t\t%s\n", lilv_node_as_uri(lilv_ui_get_uri(ui))); const char* binary = lilv_node_as_uri(lilv_ui_get_binary_uri(ui)); const LilvNodes* types = lilv_ui_get_classes(ui); LILV_FOREACH(nodes, i, types) { - printf("\t Class: %s\n", + printf("\t\t\tClass: %s\n", lilv_node_as_uri(lilv_nodes_get(types, i))); } if (binary) - printf("\t Binary: %s\n", binary); + printf("\t\t\tBinary: %s\n", binary); - printf("\t Bundle: %s\n", + printf("\t\t\tBundle: %s\n", lilv_node_as_uri(lilv_ui_get_bundle_uri(ui))); } } @@ -267,6 +268,7 @@ print_plugin(LilvWorld* world, lilv_nodes_free(features); /* Extension Data */ + LilvNodes* data = lilv_plugin_get_extension_data(p); if (data) printf("\tExtension Data: "); @@ -278,25 +280,30 @@ print_plugin(LilvWorld* world, printf("%s", lilv_node_as_uri(lilv_nodes_get(data, i))); first = false; } - if (features) + if (data) printf("\n"); lilv_nodes_free(data); /* Presets */ - LilvNodes* presets = lilv_plugin_get_value(p, preset_pred); + LilvNodes* presets = lilv_plugin_get_related(p, preset_class); if (presets) printf("\tPresets: \n"); LILV_FOREACH(nodes, i, presets) { - LilvNodes* titles = lilv_world_find_nodes(world, - lilv_nodes_get(presets, i), - label_pred, - NULL); + const LilvNode* preset = lilv_nodes_get(presets, i); + lilv_world_load_resource(world, preset); + LilvNodes* titles = lilv_world_find_nodes( + world, preset, label_pred, NULL); if (titles) { - const LilvNode* title = lilv_nodes_get(titles, lilv_nodes_begin(titles)); + const LilvNode* title = lilv_nodes_get_first(titles); printf("\t %s\n", lilv_node_as_string(title)); + lilv_nodes_free(titles); + } else { + fprintf(stderr, "Preset <%s> has no rdfs:label\n", + lilv_node_as_string(lilv_nodes_get(presets, i))); } } + lilv_nodes_free(presets); /* Ports */ @@ -380,12 +387,13 @@ main(int argc, char** argv) #define NS_PSET "http://lv2plug.in/ns/ext/presets#" #define NS_EV "http://lv2plug.in/ns/ext/event#" + applies_to_pred = lilv_new_uri(world, LILV_NS_LV2 "appliesTo"); control_class = lilv_new_uri(world, LILV_URI_CONTROL_PORT); event_class = lilv_new_uri(world, LILV_URI_EVENT_PORT); in_group_pred = lilv_new_uri(world, NS_PG "inGroup"); - preset_pred = lilv_new_uri(world, NS_PSET "hasPreset"); - role_pred = lilv_new_uri(world, NS_PG "role"); label_pred = lilv_new_uri(world, LILV_NS_RDFS "label"); + preset_class = lilv_new_uri(world, NS_PSET "Preset"); + role_pred = lilv_new_uri(world, NS_PG "role"); supports_event_pred = lilv_new_uri(world, NS_EV "supportsEvent"); const LilvPlugins* plugins = lilv_world_get_all_plugins(world); @@ -416,12 +424,15 @@ main(int argc, char** argv) lilv_node_free(uri); - lilv_node_free(label_pred); + lilv_node_free(supports_event_pred); lilv_node_free(role_pred); - lilv_node_free(preset_pred); + lilv_node_free(preset_class); + lilv_node_free(label_pred); lilv_node_free(in_group_pred); lilv_node_free(event_class); lilv_node_free(control_class); + lilv_node_free(applies_to_pred); + lilv_world_free(world); return ret; } @@ -8,7 +8,7 @@ import waflib.Options as Options import waflib.Logs as Logs # Version of this package (even if built as a child) -LILV_VERSION = '0.7.0' +LILV_VERSION = '0.8.0' LILV_MAJOR_VERSION = '0' # Library version (UNIX style major, minor, micro) |