// Copyright 2007-2019 David Robillard // SPDX-License-Identifier: ISC #include "lilv_internal.h" #include "lilv/lilv.h" #include "serd/serd.h" #include "sord/sord.h" #include "zix/tree.h" #include "lv2/core/lv2.h" #include "lv2/ui/ui.h" #ifdef LILV_DYN_MANIFEST # include "lv2/dynmanifest/dynmanifest.h" #endif #include #include #include #include #include #include #include #define NS_DOAP (const uint8_t*)"http://usefulinc.com/ns/doap#" #define NS_FOAF (const uint8_t*)"http://xmlns.com/foaf/0.1/" static void lilv_plugin_init(LilvPlugin* plugin, LilvNode* bundle_uri) { plugin->bundle_uri = bundle_uri; plugin->binary_uri = NULL; #ifdef LILV_DYN_MANIFEST plugin->dynmanifest = NULL; #endif plugin->plugin_class = NULL; plugin->data_uris = lilv_nodes_new(); plugin->ports = NULL; plugin->num_ports = 0; plugin->loaded = false; plugin->parse_errors = false; plugin->replaced = false; } /** Ownership of `uri` and `bundle` is taken */ LilvPlugin* lilv_plugin_new(LilvWorld* world, LilvNode* uri, LilvNode* bundle_uri) { LilvPlugin* plugin = (LilvPlugin*)malloc(sizeof(LilvPlugin)); plugin->world = world; plugin->plugin_uri = uri; lilv_plugin_init(plugin, bundle_uri); return plugin; } void lilv_plugin_clear(LilvPlugin* plugin, LilvNode* bundle_uri) { lilv_node_free(plugin->bundle_uri); lilv_node_free(plugin->binary_uri); lilv_nodes_free(plugin->data_uris); lilv_plugin_init(plugin, bundle_uri); } static void lilv_plugin_free_ports(LilvPlugin* plugin) { if (plugin->ports) { for (uint32_t i = 0; i < plugin->num_ports; ++i) { lilv_port_free(plugin, plugin->ports[i]); } free(plugin->ports); plugin->num_ports = 0; plugin->ports = NULL; } } void lilv_plugin_free(LilvPlugin* plugin) { #ifdef LILV_DYN_MANIFEST if (plugin->dynmanifest && --plugin->dynmanifest->refs == 0) { lilv_dynmanifest_free(plugin->dynmanifest); } #endif lilv_node_free(plugin->plugin_uri); plugin->plugin_uri = NULL; lilv_node_free(plugin->bundle_uri); plugin->bundle_uri = NULL; lilv_node_free(plugin->binary_uri); plugin->binary_uri = NULL; lilv_plugin_free_ports(plugin); lilv_nodes_free(plugin->data_uris); plugin->data_uris = NULL; free(plugin); } static LilvNode* lilv_plugin_get_one(const LilvPlugin* plugin, const SordNode* subject, const SordNode* predicate) { /* TODO: This is slower than it could be in some cases, but it's simpler to use the existing i18n code. */ SordIter* stream = lilv_world_query_internal(plugin->world, subject, predicate, NULL); LilvNodes* nodes = lilv_nodes_from_stream_objects(plugin->world, stream, SORD_OBJECT); if (nodes) { LilvNode* value = lilv_node_duplicate(lilv_nodes_get_first(nodes)); lilv_nodes_free(nodes); return value; } return NULL; } LilvNode* lilv_plugin_get_unique(const LilvPlugin* plugin, const SordNode* subject, const SordNode* predicate) { LilvNode* ret = lilv_plugin_get_one(plugin, subject, predicate); if (!ret) { LILV_ERRORF("No value found for (%s %s ...) property\n", sord_node_get_string(subject), sord_node_get_string(predicate)); } return ret; } static void lilv_plugin_load(LilvPlugin* plugin) { SordNode* bundle_uri_node = plugin->bundle_uri->node; const SerdNode* bundle_uri_snode = sord_node_to_serd_node(bundle_uri_node); SerdEnv* env = serd_env_new(bundle_uri_snode); SerdReader* reader = sord_new_reader(plugin->world->model, env, SERD_TURTLE, bundle_uri_node); SordModel* prots = lilv_world_filter_model(plugin->world, plugin->world->model, plugin->plugin_uri->node, plugin->world->uris.lv2_prototype, NULL, NULL); SordModel* skel = sord_new(plugin->world->world, SORD_SPO, false); SordIter* iter = sord_begin(prots); for (; !sord_iter_end(iter); sord_iter_next(iter)) { const SordNode* t = sord_iter_get_node(iter, SORD_OBJECT); LilvNode* prototype = lilv_node_new_from_node(plugin->world, t); lilv_world_load_resource(plugin->world, prototype); SordIter* statements = sord_search(plugin->world->model, prototype->node, NULL, NULL, NULL); FOREACH_MATCH (statements) { SordQuad quad; sord_iter_get(statements, quad); quad[0] = plugin->plugin_uri->node; sord_add(skel, quad); } sord_iter_free(statements); lilv_node_free(prototype); } sord_iter_free(iter); for (iter = sord_begin(skel); !sord_iter_end(iter); sord_iter_next(iter)) { SordQuad quad; sord_iter_get(iter, quad); sord_add(plugin->world->model, quad); } sord_iter_free(iter); sord_free(skel); sord_free(prots); // Parse all the plugin's data files into RDF model SerdStatus st = SERD_SUCCESS; LILV_FOREACH (nodes, i, plugin->data_uris) { const LilvNode* data_uri = lilv_nodes_get(plugin->data_uris, i); serd_env_set_base_uri(env, sord_node_to_serd_node(data_uri->node)); st = lilv_world_load_file(plugin->world, reader, data_uri); if (st > SERD_FAILURE) { break; } } if (st > SERD_FAILURE) { plugin->loaded = true; plugin->parse_errors = true; serd_reader_free(reader); serd_env_free(env); return; } #ifdef LILV_DYN_MANIFEST // Load and parse dynamic manifest data, if this is a library if (plugin->dynmanifest) { typedef int (*GetDataFunc)( LV2_Dyn_Manifest_Handle handle, FILE * fp, const char* uri); GetDataFunc get_data_func = (GetDataFunc)lilv_dlfunc( plugin->dynmanifest->lib, "lv2_dyn_manifest_get_data"); if (get_data_func) { const SordNode* bundle = plugin->dynmanifest->bundle->node; serd_env_set_base_uri(env, sord_node_to_serd_node(bundle)); FILE* fd = tmpfile(); get_data_func(plugin->dynmanifest->handle, fd, lilv_node_as_string(plugin->plugin_uri)); rewind(fd); serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(plugin->world)); serd_reader_read_file_handle( reader, fd, (const uint8_t*)"(dyn-manifest)"); fclose(fd); } } #endif serd_reader_free(reader); serd_env_free(env); plugin->loaded = true; } static bool is_symbol(const char* str) { for (const char* s = str; *s; ++s) { if (!((*s >= 'a' && *s <= 'z') || (*s >= 'A' && *s <= 'Z') || (s > str && *s >= '0' && *s <= '9') || *s == '_')) { return false; } } return true; } static void lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_plugin) { LilvPlugin* plugin = (LilvPlugin*)const_plugin; lilv_plugin_load_if_necessary(plugin); if (!plugin->ports) { plugin->ports = (LilvPort**)malloc(sizeof(LilvPort*)); plugin->ports[0] = NULL; SordIter* ports = lilv_world_query_internal(plugin->world, plugin->plugin_uri->node, plugin->world->uris.lv2_port, NULL); FOREACH_MATCH (ports) { const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT); LilvNode* index = lilv_plugin_get_unique(plugin, port, plugin->world->uris.lv2_index); LilvNode* symbol = lilv_plugin_get_unique(plugin, port, plugin->world->uris.lv2_symbol); if (!lilv_node_is_string(symbol) || !is_symbol((const char*)sord_node_get_string(symbol->node))) { LILV_ERRORF("Plugin <%s> port symbol `%s' is invalid\n", lilv_node_as_uri(plugin->plugin_uri), lilv_node_as_string(symbol)); lilv_node_free(symbol); lilv_node_free(index); lilv_plugin_free_ports(plugin); break; } if (!lilv_node_is_int(index)) { LILV_ERRORF("Plugin <%s> port index is not an integer\n", lilv_node_as_uri(plugin->plugin_uri)); lilv_node_free(symbol); lilv_node_free(index); lilv_plugin_free_ports(plugin); break; } uint32_t this_index = lilv_node_as_int(index); LilvPort* this_port = NULL; if (plugin->num_ports > this_index) { this_port = plugin->ports[this_index]; } else { plugin->ports = (LilvPort**)realloc( plugin->ports, (this_index + 1) * sizeof(LilvPort*)); memset(plugin->ports + plugin->num_ports, '\0', (this_index - plugin->num_ports) * sizeof(LilvPort*)); plugin->num_ports = this_index + 1; } // Havn't seen this port yet, add it to array if (!this_port) { this_port = lilv_port_new( plugin->world, port, this_index, lilv_node_as_string(symbol)); plugin->ports[this_index] = this_port; } SordIter* types = lilv_world_query_internal( plugin->world, port, plugin->world->uris.rdf_a, NULL); FOREACH_MATCH (types) { const SordNode* type = sord_iter_get_node(types, SORD_OBJECT); if (sord_node_get_type(type) == SORD_URI) { zix_tree_insert((ZixTree*)this_port->classes, lilv_node_new_from_node(plugin->world, type), NULL); } else { LILV_WARNF("Plugin <%s> port type is not a URI\n", lilv_node_as_uri(plugin->plugin_uri)); } } sord_iter_free(types); lilv_node_free(symbol); lilv_node_free(index); } sord_iter_free(ports); // Check sanity for (uint32_t i = 0; i < plugin->num_ports; ++i) { if (!plugin->ports[i]) { LILV_ERRORF("Plugin <%s> is missing port %u/%u\n", lilv_node_as_uri(plugin->plugin_uri), i, plugin->num_ports); lilv_plugin_free_ports(plugin); break; } } } } void lilv_plugin_load_if_necessary(const LilvPlugin* plugin) { if (!plugin->loaded) { lilv_plugin_load((LilvPlugin*)plugin); } } const LilvNode* lilv_plugin_get_uri(const LilvPlugin* plugin) { return plugin->plugin_uri; } const LilvNode* lilv_plugin_get_bundle_uri(const LilvPlugin* plugin) { return plugin->bundle_uri; } const LilvNode* lilv_plugin_get_library_uri(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary((LilvPlugin*)plugin); if (!plugin->binary_uri) { // lv2:binary ?binary SordIter* i = lilv_world_query_internal(plugin->world, plugin->plugin_uri->node, plugin->world->uris.lv2_binary, NULL); FOREACH_MATCH (i) { const SordNode* binary_node = sord_iter_get_node(i, SORD_OBJECT); if (sord_node_get_type(binary_node) == SORD_URI) { ((LilvPlugin*)plugin)->binary_uri = lilv_node_new_from_node(plugin->world, binary_node); break; } } sord_iter_free(i); } if (!plugin->binary_uri) { LILV_WARNF("Plugin <%s> has no lv2:binary\n", lilv_node_as_uri(lilv_plugin_get_uri(plugin))); } return plugin->binary_uri; } const LilvNodes* lilv_plugin_get_data_uris(const LilvPlugin* plugin) { return plugin->data_uris; } const LilvPluginClass* lilv_plugin_get_class(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary((LilvPlugin*)plugin); if (!plugin->plugin_class) { // a ?class SordIter* c = lilv_world_query_internal( plugin->world, plugin->plugin_uri->node, plugin->world->uris.rdf_a, NULL); FOREACH_MATCH (c) { const SordNode* class_node = sord_iter_get_node(c, SORD_OBJECT); if (sord_node_get_type(class_node) != SORD_URI) { continue; } LilvNode* klass = lilv_node_new_from_node(plugin->world, class_node); if (!lilv_node_equals(klass, plugin->world->lv2_plugin_class->uri)) { const LilvPluginClass* pclass = lilv_plugin_classes_get_by_uri(plugin->world->plugin_classes, klass); if (pclass) { ((LilvPlugin*)plugin)->plugin_class = pclass; lilv_node_free(klass); break; } } lilv_node_free(klass); } sord_iter_free(c); if (plugin->plugin_class == NULL) { ((LilvPlugin*)plugin)->plugin_class = plugin->world->lv2_plugin_class; } } return plugin->plugin_class; } static LilvNodes* lilv_plugin_get_value_internal(const LilvPlugin* plugin, const SordNode* predicate) { lilv_plugin_load_if_necessary(plugin); return lilv_world_find_nodes_internal( plugin->world, plugin->plugin_uri->node, predicate, NULL); } bool lilv_plugin_verify(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary(plugin); if (plugin->parse_errors) { return false; } LilvNode* rdf_type = lilv_new_uri(plugin->world, LILV_NS_RDF "type"); LilvNodes* results = lilv_plugin_get_value(plugin, rdf_type); lilv_node_free(rdf_type); if (!results) { return false; } lilv_nodes_free(results); results = lilv_plugin_get_value_internal(plugin, plugin->world->uris.doap_name); if (!results) { return false; } lilv_nodes_free(results); LilvNode* lv2_port = lilv_new_uri(plugin->world, LV2_CORE__port); results = lilv_plugin_get_value(plugin, lv2_port); lilv_node_free(lv2_port); if (!results) { return false; } lilv_nodes_free(results); return true; } LilvNode* lilv_plugin_get_name(const LilvPlugin* plugin) { LilvNodes* results = lilv_plugin_get_value_internal(plugin, plugin->world->uris.doap_name); LilvNode* ret = NULL; if (results) { LilvNode* val = lilv_nodes_get_first(results); if (lilv_node_is_string(val)) { ret = lilv_node_duplicate(val); } lilv_nodes_free(results); } if (!ret) { LILV_WARNF("Plugin <%s> has no (mandatory) doap:name\n", lilv_node_as_string(lilv_plugin_get_uri(plugin))); } return ret; } LilvNodes* lilv_plugin_get_value(const LilvPlugin* plugin, const LilvNode* predicate) { lilv_plugin_load_if_necessary(plugin); return lilv_world_find_nodes( plugin->world, plugin->plugin_uri, predicate, NULL); } uint32_t lilv_plugin_get_num_ports(const LilvPlugin* plugin) { lilv_plugin_load_ports_if_necessary(plugin); return plugin->num_ports; } void lilv_plugin_get_port_ranges_float(const LilvPlugin* plugin, float* min_values, float* max_values, float* def_values) { lilv_plugin_load_ports_if_necessary(plugin); LilvNode* min = NULL; LilvNode* max = NULL; LilvNode* def = NULL; LilvNode** minptr = min_values ? &min : NULL; LilvNode** maxptr = max_values ? &max : NULL; LilvNode** defptr = def_values ? &def : NULL; for (uint32_t i = 0; i < plugin->num_ports; ++i) { lilv_port_get_range(plugin, plugin->ports[i], defptr, minptr, maxptr); if (min_values) { if (lilv_node_is_float(min) || lilv_node_is_int(min)) { min_values[i] = lilv_node_as_float(min); } else { min_values[i] = NAN; } } if (max_values) { if (lilv_node_is_float(max) || lilv_node_is_int(max)) { max_values[i] = lilv_node_as_float(max); } else { max_values[i] = NAN; } } if (def_values) { if (lilv_node_is_float(def) || lilv_node_is_int(def)) { def_values[i] = lilv_node_as_float(def); } else { def_values[i] = NAN; } } lilv_node_free(def); lilv_node_free(min); lilv_node_free(max); } } uint32_t lilv_plugin_get_num_ports_of_class_va( const LilvPlugin* plugin, const LilvNode* class_1, va_list args // NOLINT(readability-non-const-parameter) ) { lilv_plugin_load_ports_if_necessary(plugin); uint32_t count = 0; // Build array of classes from args so we can walk it several times size_t n_classes = 0; const LilvNode** classes = NULL; for (LilvNode* c = NULL; (c = va_arg(args, LilvNode*));) { classes = (const LilvNode**)realloc(classes, ++n_classes * sizeof(LilvNode*)); classes[n_classes - 1] = c; } // Check each port against every type for (unsigned i = 0; i < plugin->num_ports; ++i) { LilvPort* port = plugin->ports[i]; if (port && lilv_port_is_a(plugin, port, class_1)) { bool matches = true; for (size_t j = 0; j < n_classes; ++j) { if (!lilv_port_is_a(plugin, port, classes[j])) { matches = false; break; } } if (matches) { ++count; } } } free(classes); return count; } uint32_t lilv_plugin_get_num_ports_of_class(const LilvPlugin* plugin, const LilvNode* class_1, ...) { va_list args; // NOLINT(cppcoreguidelines-init-variables) va_start(args, class_1); uint32_t count = lilv_plugin_get_num_ports_of_class_va(plugin, class_1, args); va_end(args); return count; } bool lilv_plugin_has_latency(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary(plugin); SordIter* ports = lilv_world_query_internal(plugin->world, plugin->plugin_uri->node, plugin->world->uris.lv2_port, NULL); bool ret = false; FOREACH_MATCH (ports) { const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT); SordIter* prop = lilv_world_query_internal(plugin->world, port, plugin->world->uris.lv2_portProperty, plugin->world->uris.lv2_reportsLatency); SordIter* des = lilv_world_query_internal(plugin->world, port, plugin->world->uris.lv2_designation, plugin->world->uris.lv2_latency); const bool latent = !sord_iter_end(prop) || !sord_iter_end(des); sord_iter_free(prop); sord_iter_free(des); if (latent) { ret = true; break; } } sord_iter_free(ports); return ret; } static const LilvPort* lilv_plugin_get_port_by_property(const LilvPlugin* plugin, const SordNode* port_property) { lilv_plugin_load_ports_if_necessary(plugin); for (uint32_t i = 0; i < plugin->num_ports; ++i) { LilvPort* port = plugin->ports[i]; SordIter* iter = lilv_world_query_internal(plugin->world, port->node->node, plugin->world->uris.lv2_portProperty, port_property); const bool found = !sord_iter_end(iter); sord_iter_free(iter); if (found) { return port; } } return NULL; } const LilvPort* lilv_plugin_get_port_by_designation(const LilvPlugin* plugin, const LilvNode* port_class, const LilvNode* designation) { LilvWorld* world = plugin->world; lilv_plugin_load_ports_if_necessary(plugin); for (uint32_t i = 0; i < plugin->num_ports; ++i) { LilvPort* port = plugin->ports[i]; SordIter* iter = lilv_world_query_internal( world, port->node->node, world->uris.lv2_designation, designation->node); const bool found = !sord_iter_end(iter) && (!port_class || lilv_port_is_a(plugin, port, port_class)); sord_iter_free(iter); if (found) { return port; } } return NULL; } uint32_t lilv_plugin_get_latency_port_index(const LilvPlugin* plugin) { LilvNode* lv2_OutputPort = lilv_new_uri(plugin->world, LV2_CORE__OutputPort); LilvNode* lv2_latency = lilv_new_uri(plugin->world, LV2_CORE__latency); const LilvPort* prop_port = lilv_plugin_get_port_by_property( plugin, plugin->world->uris.lv2_reportsLatency); const LilvPort* des_port = lilv_plugin_get_port_by_designation(plugin, lv2_OutputPort, lv2_latency); lilv_node_free(lv2_latency); lilv_node_free(lv2_OutputPort); if (prop_port) { return prop_port->index; } if (des_port) { return des_port->index; } return (uint32_t)-1; } bool lilv_plugin_has_feature(const LilvPlugin* plugin, const LilvNode* feature) { lilv_plugin_load_if_necessary(plugin); const SordNode* predicates[] = {plugin->world->uris.lv2_requiredFeature, plugin->world->uris.lv2_optionalFeature, NULL}; for (const SordNode** pred = predicates; *pred; ++pred) { if (lilv_world_ask_internal( plugin->world, plugin->plugin_uri->node, *pred, feature->node)) { return true; } } return false; } LilvNodes* lilv_plugin_get_supported_features(const LilvPlugin* plugin) { LilvNodes* optional = lilv_plugin_get_optional_features(plugin); LilvNodes* required = lilv_plugin_get_required_features(plugin); LilvNodes* result = lilv_nodes_merge(optional, required); lilv_nodes_free(optional); lilv_nodes_free(required); return result; } LilvNodes* lilv_plugin_get_optional_features(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary(plugin); return lilv_world_find_nodes_internal(plugin->world, plugin->plugin_uri->node, plugin->world->uris.lv2_optionalFeature, NULL); } LilvNodes* lilv_plugin_get_required_features(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary(plugin); return lilv_world_find_nodes_internal(plugin->world, plugin->plugin_uri->node, plugin->world->uris.lv2_requiredFeature, NULL); } bool lilv_plugin_has_extension_data(const LilvPlugin* plugin, const LilvNode* uri) { if (!lilv_node_is_uri(uri)) { LILV_ERRORF("Extension data `%s' is not a URI\n", sord_node_get_string(uri->node)); return false; } lilv_plugin_load_if_necessary(plugin); return lilv_world_ask_internal(plugin->world, plugin->plugin_uri->node, plugin->world->uris.lv2_extensionData, uri->node); } LilvNodes* lilv_plugin_get_extension_data(const LilvPlugin* plugin) { return lilv_plugin_get_value_internal(plugin, plugin->world->uris.lv2_extensionData); } const LilvPort* lilv_plugin_get_port_by_index(const LilvPlugin* plugin, uint32_t index) { lilv_plugin_load_ports_if_necessary(plugin); if (index < plugin->num_ports) { return plugin->ports[index]; } return NULL; } const LilvPort* lilv_plugin_get_port_by_symbol(const LilvPlugin* plugin, const LilvNode* symbol) { lilv_plugin_load_ports_if_necessary(plugin); for (uint32_t i = 0; i < plugin->num_ports; ++i) { LilvPort* port = plugin->ports[i]; if (lilv_node_equals(port->symbol, symbol)) { return port; } } return NULL; } LilvNode* lilv_plugin_get_project(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary(plugin); SordNode* lv2_project = sord_new_uri(plugin->world->world, (const uint8_t*)LV2_CORE__project); SordIter* projects = lilv_world_query_internal( plugin->world, plugin->plugin_uri->node, lv2_project, NULL); sord_node_free(plugin->world->world, lv2_project); if (sord_iter_end(projects)) { sord_iter_free(projects); return NULL; } const SordNode* project = sord_iter_get_node(projects, SORD_OBJECT); sord_iter_free(projects); return lilv_node_new_from_node(plugin->world, project); } static const SordNode* lilv_plugin_get_author(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary(plugin); SordNode* doap_maintainer = sord_new_uri(plugin->world->world, NS_DOAP "maintainer"); SordIter* maintainers = lilv_world_query_internal( plugin->world, plugin->plugin_uri->node, doap_maintainer, NULL); if (sord_iter_end(maintainers)) { sord_iter_free(maintainers); LilvNode* project = lilv_plugin_get_project(plugin); if (!project) { sord_node_free(plugin->world->world, doap_maintainer); return NULL; } maintainers = lilv_world_query_internal( plugin->world, project->node, doap_maintainer, NULL); lilv_node_free(project); } sord_node_free(plugin->world->world, doap_maintainer); if (sord_iter_end(maintainers)) { sord_iter_free(maintainers); return NULL; } const SordNode* author = sord_iter_get_node(maintainers, SORD_OBJECT); sord_iter_free(maintainers); return author; } static LilvNode* lilv_plugin_get_author_property(const LilvPlugin* plugin, const uint8_t* uri) { const SordNode* author = lilv_plugin_get_author(plugin); if (author) { SordWorld* sworld = plugin->world->world; SordNode* pred = sord_new_uri(sworld, uri); LilvNode* ret = lilv_plugin_get_one(plugin, author, pred); sord_node_free(sworld, pred); return ret; } return NULL; } LilvNode* lilv_plugin_get_author_name(const LilvPlugin* plugin) { return lilv_plugin_get_author_property(plugin, NS_FOAF "name"); } LilvNode* lilv_plugin_get_author_email(const LilvPlugin* plugin) { return lilv_plugin_get_author_property(plugin, NS_FOAF "mbox"); } LilvNode* lilv_plugin_get_author_homepage(const LilvPlugin* plugin) { return lilv_plugin_get_author_property(plugin, NS_FOAF "homepage"); } bool lilv_plugin_is_replaced(const LilvPlugin* plugin) { return plugin->replaced; } LilvUIs* lilv_plugin_get_uis(const LilvPlugin* plugin) { lilv_plugin_load_if_necessary(plugin); SordNode* ui_ui_node = sord_new_uri(plugin->world->world, (const uint8_t*)LV2_UI__ui); SordNode* ui_binary_node = sord_new_uri(plugin->world->world, (const uint8_t*)LV2_UI__binary); LilvUIs* result = lilv_uis_new(); SordIter* uis = lilv_world_query_internal( plugin->world, plugin->plugin_uri->node, ui_ui_node, NULL); FOREACH_MATCH (uis) { const SordNode* ui = sord_iter_get_node(uis, SORD_OBJECT); LilvNode* type = lilv_plugin_get_unique(plugin, ui, plugin->world->uris.rdf_a); LilvNode* binary = lilv_plugin_get_one(plugin, ui, plugin->world->uris.lv2_binary); if (!binary) { binary = lilv_plugin_get_unique(plugin, ui, ui_binary_node); } if (sord_node_get_type(ui) != SORD_URI || !lilv_node_is_uri(type) || !lilv_node_is_uri(binary)) { lilv_node_free(binary); lilv_node_free(type); LILV_ERRORF("Corrupt UI <%s>\n", sord_node_get_string(ui)); continue; } LilvUI* lilv_ui = lilv_ui_new( plugin->world, lilv_node_new_from_node(plugin->world, ui), type, binary); zix_tree_insert((ZixTree*)result, lilv_ui, NULL); } sord_iter_free(uis); sord_node_free(plugin->world->world, ui_binary_node); sord_node_free(plugin->world->world, ui_ui_node); if (lilv_uis_size(result) > 0) { return result; } lilv_uis_free(result); return NULL; } LilvNodes* lilv_plugin_get_related(const LilvPlugin* plugin, const LilvNode* type) { lilv_plugin_load_if_necessary(plugin); LilvWorld* const world = plugin->world; LilvNodes* const related = lilv_world_find_nodes_internal( world, NULL, world->uris.lv2_appliesTo, lilv_plugin_get_uri(plugin)->node); if (!type) { return related; } LilvNodes* matches = lilv_nodes_new(); LILV_FOREACH (nodes, i, related) { LilvNode* node = (LilvNode*)lilv_collection_get((ZixTree*)related, i); if (lilv_world_ask_internal( world, node->node, world->uris.rdf_a, type->node)) { zix_tree_insert( (ZixTree*)matches, lilv_node_new_from_node(world, node->node), NULL); } } lilv_nodes_free(related); return matches; } static SerdEnv* new_lv2_env(const SerdNode* base) { SerdEnv* env = serd_env_new(base); #define USTR(s) ((const uint8_t*)(s)) serd_env_set_prefix_from_strings(env, USTR("doap"), USTR(LILV_NS_DOAP)); serd_env_set_prefix_from_strings(env, USTR("foaf"), USTR(LILV_NS_FOAF)); serd_env_set_prefix_from_strings(env, USTR("lv2"), USTR(LILV_NS_LV2)); serd_env_set_prefix_from_strings(env, USTR("owl"), USTR(LILV_NS_OWL)); 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)); serd_env_set_prefix_from_strings(env, USTR("xsd"), USTR(LILV_NS_XSD)); return env; } static void maybe_write_prefixes(SerdWriter* writer, SerdEnv* env, FILE* file) { fseek(file, 0, SEEK_END); if (ftell(file) == 0) { serd_env_foreach(env, (SerdPrefixSink)serd_writer_set_prefix, writer); } else { fprintf(file, "\n"); } } void lilv_plugin_write_description(LilvWorld* world, const LilvPlugin* plugin, const LilvNode* base_uri, FILE* plugin_file) { const LilvNode* subject = lilv_plugin_get_uri(plugin); const uint32_t num_ports = lilv_plugin_get_num_ports(plugin); const SerdNode* base = sord_node_to_serd_node(base_uri->node); SerdEnv* env = new_lv2_env(base); SerdWriter* writer = serd_writer_new(SERD_TURTLE, (SerdStyle)(SERD_STYLE_ABBREVIATED | SERD_STYLE_CURIED), env, NULL, serd_file_sink, plugin_file); // Write prefixes if this is a new file maybe_write_prefixes(writer, env, plugin_file); // Write plugin description SordIter* plug_iter = lilv_world_query_internal(world, subject->node, NULL, NULL); sord_write_iter(plug_iter, writer); // Write port descriptions for (uint32_t i = 0; i < num_ports; ++i) { const LilvPort* port = plugin->ports[i]; SordIter* port_iter = lilv_world_query_internal(world, port->node->node, NULL, NULL); sord_write_iter(port_iter, writer); } serd_writer_free(writer); serd_env_free(env); } void lilv_plugin_write_manifest_entry(LilvWorld* world, const LilvPlugin* plugin, const LilvNode* base_uri, FILE* manifest_file, const char* plugin_file_path) { (void)world; const LilvNode* subject = lilv_plugin_get_uri(plugin); const SerdNode* base = sord_node_to_serd_node(base_uri->node); SerdEnv* env = new_lv2_env(base); SerdWriter* writer = serd_writer_new(SERD_TURTLE, (SerdStyle)(SERD_STYLE_ABBREVIATED | SERD_STYLE_CURIED), env, NULL, serd_file_sink, manifest_file); // Write prefixes if this is a new file maybe_write_prefixes(writer, env, manifest_file); // Write manifest entry serd_writer_write_statement( writer, 0, NULL, sord_node_to_serd_node(subject->node), 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->node), sord_node_to_serd_node(plugin->world->uris.rdfs_seeAlso), &file_node, 0, 0); serd_writer_free(writer); serd_env_free(env); }