1 /*
2 Copyright 2007-2016 David Robillard <http://drobilla.net>
3
4 Permission to use, copy, modify, and/or distribute this software for any
5 purpose with or without fee is hereby granted, provided that the above
6 copyright notice and this permission notice appear in all copies.
7
8 THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17 #include <assert.h>
18 #include <errno.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "lv2/midi.h"
23 #include "lv2/presets.h"
24
25 #include "lilv_internal.h"
26
27 static int
28 lilv_world_drop_graph(LilvWorld* world, const SordNode* graph);
29
30 LILV_API LilvWorld*
lilv_world_new(void)31 lilv_world_new(void)
32 {
33 LilvWorld* world = (LilvWorld*)malloc(sizeof(LilvWorld));
34
35 world->world = sord_world_new();
36 if (!world->world)
37 goto fail;
38
39 world->model = sord_new(world->world, SORD_SPO|SORD_OPS, true);
40 if (!world->model)
41 goto fail;
42
43 world->specs = NULL;
44 world->plugin_classes = lilv_plugin_classes_new();
45 world->plugins = lilv_plugins_new();
46 world->zombies = lilv_plugins_new();
47 world->loaded_files = zix_tree_new(
48 false, lilv_resource_node_cmp, NULL, (ZixDestroyFunc)lilv_node_free);
49
50 world->libs = zix_tree_new(false, lilv_lib_compare, NULL, NULL);
51
52 #define NS_DCTERMS "http://purl.org/dc/terms/"
53 #define NS_DYNMAN "http://lv2plug.in/ns/ext/dynmanifest#"
54 #define NS_OWL "http://www.w3.org/2002/07/owl#"
55
56 #define NEW_URI(uri) sord_new_uri(world->world, (const uint8_t*)uri)
57
58 world->uris.dc_replaces = NEW_URI(NS_DCTERMS "replaces");
59 world->uris.dman_DynManifest = NEW_URI(NS_DYNMAN "DynManifest");
60 world->uris.doap_name = NEW_URI(LILV_NS_DOAP "name");
61 world->uris.lv2_Plugin = NEW_URI(LV2_CORE__Plugin);
62 world->uris.lv2_Specification = NEW_URI(LV2_CORE__Specification);
63 world->uris.lv2_appliesTo = NEW_URI(LV2_CORE__appliesTo);
64 world->uris.lv2_binary = NEW_URI(LV2_CORE__binary);
65 world->uris.lv2_default = NEW_URI(LV2_CORE__default);
66 world->uris.lv2_designation = NEW_URI(LV2_CORE__designation);
67 world->uris.lv2_extensionData = NEW_URI(LV2_CORE__extensionData);
68 world->uris.lv2_index = NEW_URI(LV2_CORE__index);
69 world->uris.lv2_latency = NEW_URI(LV2_CORE__latency);
70 world->uris.lv2_maximum = NEW_URI(LV2_CORE__maximum);
71 world->uris.lv2_microVersion = NEW_URI(LV2_CORE__microVersion);
72 world->uris.lv2_minimum = NEW_URI(LV2_CORE__minimum);
73 world->uris.lv2_minorVersion = NEW_URI(LV2_CORE__minorVersion);
74 world->uris.lv2_name = NEW_URI(LV2_CORE__name);
75 world->uris.lv2_optionalFeature = NEW_URI(LV2_CORE__optionalFeature);
76 world->uris.lv2_port = NEW_URI(LV2_CORE__port);
77 world->uris.lv2_portProperty = NEW_URI(LV2_CORE__portProperty);
78 world->uris.lv2_reportsLatency = NEW_URI(LV2_CORE__reportsLatency);
79 world->uris.lv2_requiredFeature = NEW_URI(LV2_CORE__requiredFeature);
80 world->uris.lv2_symbol = NEW_URI(LV2_CORE__symbol);
81 world->uris.lv2_prototype = NEW_URI(LV2_CORE__prototype);
82 world->uris.midi_Event = NEW_URI(LV2_MIDI__MidiEvent);
83 world->uris.mod_builderVersion = NEW_URI("http://moddevices.com/ns/mod#builderVersion");
84 world->uris.mod_releaseNumber = NEW_URI("http://moddevices.com/ns/mod#releaseNumber");
85 world->uris.owl_Ontology = NEW_URI(NS_OWL "Ontology");
86 world->uris.pset_value = NEW_URI(LV2_PRESETS__value);
87 world->uris.rdf_a = NEW_URI(LILV_NS_RDF "type");
88 world->uris.rdf_value = NEW_URI(LILV_NS_RDF "value");
89 world->uris.rdfs_Class = NEW_URI(LILV_NS_RDFS "Class");
90 world->uris.rdfs_label = NEW_URI(LILV_NS_RDFS "label");
91 world->uris.rdfs_seeAlso = NEW_URI(LILV_NS_RDFS "seeAlso");
92 world->uris.rdfs_subClassOf = NEW_URI(LILV_NS_RDFS "subClassOf");
93 world->uris.xsd_base64Binary = NEW_URI(LILV_NS_XSD "base64Binary");
94 world->uris.xsd_boolean = NEW_URI(LILV_NS_XSD "boolean");
95 world->uris.xsd_decimal = NEW_URI(LILV_NS_XSD "decimal");
96 world->uris.xsd_double = NEW_URI(LILV_NS_XSD "double");
97 world->uris.xsd_integer = NEW_URI(LILV_NS_XSD "integer");
98 world->uris.null_uri = NULL;
99
100 world->lv2_plugin_class = lilv_plugin_class_new(
101 world, NULL, world->uris.lv2_Plugin, "Plugin");
102 assert(world->lv2_plugin_class);
103
104 world->n_read_files = 0;
105 world->opt.filter_language = true;
106 world->opt.dyn_manifest = true;
107
108 return world;
109
110 fail:
111 /* keep on rockin' in the */ free(world);
112 return NULL;
113 }
114
115 LILV_API void
lilv_world_free(LilvWorld * world)116 lilv_world_free(LilvWorld* world)
117 {
118 if (!world) {
119 return;
120 }
121
122 lilv_plugin_class_free(world->lv2_plugin_class);
123 world->lv2_plugin_class = NULL;
124
125 for (SordNode** n = (SordNode**)&world->uris; *n; ++n) {
126 sord_node_free(world->world, *n);
127 }
128
129 for (LilvSpec* spec = world->specs; spec;) {
130 LilvSpec* next = spec->next;
131 sord_node_free(world->world, spec->spec);
132 sord_node_free(world->world, spec->bundle);
133 lilv_nodes_free(spec->data_uris);
134 free(spec);
135 spec = next;
136 }
137 world->specs = NULL;
138
139 LILV_FOREACH(plugins, i, world->plugins) {
140 const LilvPlugin* p = lilv_plugins_get(world->plugins, i);
141 lilv_plugin_free((LilvPlugin*)p);
142 }
143 zix_tree_free((ZixTree*)world->plugins);
144 world->plugins = NULL;
145
146 LILV_FOREACH(plugins, i, world->zombies) {
147 const LilvPlugin* p = lilv_plugins_get(world->zombies, i);
148 lilv_plugin_free((LilvPlugin*)p);
149 }
150 zix_tree_free((ZixTree*)world->zombies);
151 world->zombies = NULL;
152
153 zix_tree_free((ZixTree*)world->loaded_files);
154 world->loaded_files = NULL;
155
156 zix_tree_free((ZixTree*)world->libs);
157 world->libs = NULL;
158
159 zix_tree_free((ZixTree*)world->plugin_classes);
160 world->plugin_classes = NULL;
161
162 sord_free(world->model);
163 world->model = NULL;
164
165 sord_world_free(world->world);
166 world->world = NULL;
167
168 free(world);
169 }
170
171 LILV_API void
lilv_world_set_option(LilvWorld * world,const char * option,const LilvNode * value)172 lilv_world_set_option(LilvWorld* world,
173 const char* option,
174 const LilvNode* value)
175 {
176 if (!strcmp(option, LILV_OPTION_DYN_MANIFEST)) {
177 if (lilv_node_is_bool(value)) {
178 world->opt.dyn_manifest = lilv_node_as_bool(value);
179 return;
180 }
181 } else if (!strcmp(option, LILV_OPTION_FILTER_LANG)) {
182 if (lilv_node_is_bool(value)) {
183 world->opt.filter_language = lilv_node_as_bool(value);
184 return;
185 }
186 }
187 LILV_WARNF("Unrecognized or invalid option `%s'\n", option);
188 }
189
190 LILV_API LilvNodes*
lilv_world_find_nodes(LilvWorld * world,const LilvNode * subject,const LilvNode * predicate,const LilvNode * object)191 lilv_world_find_nodes(LilvWorld* world,
192 const LilvNode* subject,
193 const LilvNode* predicate,
194 const LilvNode* object)
195 {
196 if (subject && !lilv_node_is_uri(subject) && !lilv_node_is_blank(subject)) {
197 LILV_ERRORF("Subject `%s' is not a resource\n",
198 sord_node_get_string(subject->node));
199 return NULL;
200 } else if (!predicate) {
201 LILV_ERROR("Missing required predicate\n");
202 return NULL;
203 } else if (!lilv_node_is_uri(predicate)) {
204 LILV_ERRORF("Predicate `%s' is not a URI\n",
205 sord_node_get_string(predicate->node));
206 return NULL;
207 } else if (!subject && !object) {
208 LILV_ERROR("Both subject and object are NULL\n");
209 return NULL;
210 }
211
212 return lilv_world_find_nodes_internal(world,
213 subject ? subject->node : NULL,
214 predicate->node,
215 object ? object->node : NULL);
216 }
217
218 LILV_API LilvNode*
lilv_world_get(LilvWorld * world,const LilvNode * subject,const LilvNode * predicate,const LilvNode * object)219 lilv_world_get(LilvWorld* world,
220 const LilvNode* subject,
221 const LilvNode* predicate,
222 const LilvNode* object)
223 {
224 SordNode* snode = sord_get(world->model,
225 subject ? subject->node : NULL,
226 predicate ? predicate->node : NULL,
227 object ? object->node : NULL,
228 NULL);
229 LilvNode* lnode = lilv_node_new_from_node(world, snode);
230 sord_node_free(world->world, snode);
231 return lnode;
232 }
233
234 SordIter*
lilv_world_query_internal(LilvWorld * world,const SordNode * subject,const SordNode * predicate,const SordNode * object)235 lilv_world_query_internal(LilvWorld* world,
236 const SordNode* subject,
237 const SordNode* predicate,
238 const SordNode* object)
239 {
240 return sord_search(world->model, subject, predicate, object, NULL);
241 }
242
243 bool
lilv_world_ask_internal(LilvWorld * world,const SordNode * subject,const SordNode * predicate,const SordNode * object)244 lilv_world_ask_internal(LilvWorld* world,
245 const SordNode* subject,
246 const SordNode* predicate,
247 const SordNode* object)
248 {
249 return sord_ask(world->model, subject, predicate, object, NULL);
250 }
251
252 LILV_API bool
lilv_world_ask(LilvWorld * world,const LilvNode * subject,const LilvNode * predicate,const LilvNode * object)253 lilv_world_ask(LilvWorld* world,
254 const LilvNode* subject,
255 const LilvNode* predicate,
256 const LilvNode* object)
257 {
258 return sord_ask(world->model,
259 subject ? subject->node : NULL,
260 predicate ? predicate->node : NULL,
261 object ? object->node : NULL,
262 NULL);
263 }
264
265 SordModel*
lilv_world_filter_model(LilvWorld * world,SordModel * model,const SordNode * subject,const SordNode * predicate,const SordNode * object,const SordNode * graph)266 lilv_world_filter_model(LilvWorld* world,
267 SordModel* model,
268 const SordNode* subject,
269 const SordNode* predicate,
270 const SordNode* object,
271 const SordNode* graph)
272 {
273 SordModel* results = sord_new(world->world, SORD_SPO, false);
274 SordIter* i = sord_search(model, subject, predicate, object, graph);
275 for (; !sord_iter_end(i); sord_iter_next(i)) {
276 SordQuad quad;
277 sord_iter_get(i, quad);
278 sord_add(results, quad);
279 }
280 sord_iter_free(i);
281 return results;
282 }
283
284 LilvNodes*
lilv_world_find_nodes_internal(LilvWorld * world,const SordNode * subject,const SordNode * predicate,const SordNode * object)285 lilv_world_find_nodes_internal(LilvWorld* world,
286 const SordNode* subject,
287 const SordNode* predicate,
288 const SordNode* object)
289 {
290 return lilv_nodes_from_stream_objects(
291 world,
292 lilv_world_query_internal(world, subject, predicate, object),
293 (object == NULL) ? SORD_OBJECT : SORD_SUBJECT);
294 }
295
296 static SerdNode
lilv_new_uri_relative_to_base(const uint8_t * uri_str,const uint8_t * base_uri_str)297 lilv_new_uri_relative_to_base(const uint8_t* uri_str,
298 const uint8_t* base_uri_str)
299 {
300 SerdURI base_uri;
301 serd_uri_parse(base_uri_str, &base_uri);
302 return serd_node_new_uri_from_string(uri_str, &base_uri, NULL);
303 }
304
305 const uint8_t*
lilv_world_blank_node_prefix(LilvWorld * world)306 lilv_world_blank_node_prefix(LilvWorld* world)
307 {
308 static char str[32];
309 snprintf(str, sizeof(str), "%d", world->n_read_files++);
310 return (const uint8_t*)str;
311 }
312
313 /** Comparator for sequences (e.g. world->plugins). */
314 int
lilv_header_compare_by_uri(const void * a,const void * b,void * user_data)315 lilv_header_compare_by_uri(const void* a, const void* b, void* user_data)
316 {
317 const struct LilvHeader* const header_a = (const struct LilvHeader*)a;
318 const struct LilvHeader* const header_b = (const struct LilvHeader*)b;
319 return strcmp(lilv_node_as_uri(header_a->uri),
320 lilv_node_as_uri(header_b->uri));
321 }
322
323 /**
324 Comparator for libraries (world->libs).
325
326 Libraries do have a LilvHeader, but we must also compare the bundle to
327 handle the case where the same library is loaded with different bundles, and
328 consequently different contents (mainly plugins).
329 */
330 int
lilv_lib_compare(const void * a,const void * b,void * user_data)331 lilv_lib_compare(const void* a, const void* b, void* user_data)
332 {
333 const LilvLib* const lib_a = (const LilvLib*)a;
334 const LilvLib* const lib_b = (const LilvLib*)b;
335 int cmp = strcmp(lilv_node_as_uri(lib_a->uri),
336 lilv_node_as_uri(lib_b->uri));
337 return cmp ? cmp : strcmp(lib_a->bundle_path, lib_b->bundle_path);
338 }
339
340 /** Get an element of a collection of any object with an LilvHeader by URI. */
341 static ZixTreeIter*
lilv_collection_find_by_uri(const ZixTree * seq,const LilvNode * uri)342 lilv_collection_find_by_uri(const ZixTree* seq, const LilvNode* uri)
343 {
344 ZixTreeIter* i = NULL;
345 if (lilv_node_is_uri(uri)) {
346 struct LilvHeader key = { NULL, (LilvNode*)uri };
347 zix_tree_find(seq, &key, &i);
348 }
349 return i;
350 }
351
352 /** Get an element of a collection of any object with an LilvHeader by URI. */
353 struct LilvHeader*
lilv_collection_get_by_uri(const ZixTree * seq,const LilvNode * uri)354 lilv_collection_get_by_uri(const ZixTree* seq, const LilvNode* uri)
355 {
356 ZixTreeIter* const i = lilv_collection_find_by_uri(seq, uri);
357
358 return i ? (struct LilvHeader*)zix_tree_get(i) : NULL;
359 }
360
361 static void
lilv_world_add_spec(LilvWorld * world,const SordNode * specification_node,const SordNode * bundle_node)362 lilv_world_add_spec(LilvWorld* world,
363 const SordNode* specification_node,
364 const SordNode* bundle_node)
365 {
366 LilvSpec* spec = (LilvSpec*)malloc(sizeof(LilvSpec));
367 spec->spec = sord_node_copy(specification_node);
368 spec->bundle = sord_node_copy(bundle_node);
369 spec->data_uris = lilv_nodes_new();
370
371 // Add all data files (rdfs:seeAlso)
372 SordIter* files = sord_search(world->model,
373 specification_node,
374 world->uris.rdfs_seeAlso,
375 NULL,
376 NULL);
377 FOREACH_MATCH(files) {
378 const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT);
379 zix_tree_insert((ZixTree*)spec->data_uris,
380 lilv_node_new_from_node(world, file_node),
381 NULL);
382 }
383 sord_iter_free(files);
384
385 // Add specification to world specification list
386 spec->next = world->specs;
387 world->specs = spec;
388 }
389
390 static void
lilv_world_add_plugin(LilvWorld * world,const SordNode * plugin_node,const LilvNode * manifest_uri,void * dynmanifest,const SordNode * bundle)391 lilv_world_add_plugin(LilvWorld* world,
392 const SordNode* plugin_node,
393 const LilvNode* manifest_uri,
394 void* dynmanifest,
395 const SordNode* bundle)
396 {
397 LilvNode* plugin_uri = lilv_node_new_from_node(world, plugin_node);
398 ZixTreeIter* z = NULL;
399 LilvPlugin* plugin = (LilvPlugin*)lilv_plugins_get_by_uri(
400 world->plugins, plugin_uri);
401
402 if (plugin) {
403 // Existing plugin, if this is different bundle, ignore it
404 // (use the first plugin found in LV2_PATH)
405 const LilvNode* last_bundle = lilv_plugin_get_bundle_uri(plugin);
406 const char* plugin_uri_str = lilv_node_as_uri(plugin_uri);
407 if (sord_node_equals(bundle, last_bundle->node)) {
408 LILV_WARNF("Reloading plugin <%s>\n", plugin_uri_str);
409 plugin->loaded = false;
410 lilv_node_free(plugin_uri);
411 } else {
412 LILV_WARNF("Duplicate plugin <%s>\n", plugin_uri_str);
413 LILV_WARNF("... found in %s\n", lilv_node_as_string(last_bundle));
414 LILV_WARNF("... and %s (ignored)\n", sord_node_get_string(bundle));
415 lilv_node_free(plugin_uri);
416 return;
417 }
418 } else if ((z = lilv_collection_find_by_uri((const ZixTree*)world->zombies,
419 plugin_uri))) {
420 // Plugin bundle has been re-loaded, move from zombies to plugins
421 plugin = (LilvPlugin*)zix_tree_get(z);
422 zix_tree_remove((ZixTree*)world->zombies, z);
423 zix_tree_insert((ZixTree*)world->plugins, plugin, NULL);
424 lilv_node_free(plugin_uri);
425 lilv_plugin_clear(plugin, lilv_node_new_from_node(world, bundle));
426 } else {
427 // Add new plugin to the world
428 plugin = lilv_plugin_new(
429 world, plugin_uri, lilv_node_new_from_node(world, bundle));
430
431 // Add manifest as plugin data file (as if it were rdfs:seeAlso)
432 zix_tree_insert((ZixTree*)plugin->data_uris,
433 lilv_node_duplicate(manifest_uri),
434 NULL);
435
436 // Add plugin to world plugin sequence
437 zix_tree_insert((ZixTree*)world->plugins, plugin, NULL);
438 }
439
440
441 #ifdef LILV_DYN_MANIFEST
442 // Set dynamic manifest library URI, if applicable
443 if (dynmanifest) {
444 plugin->dynmanifest = (LilvDynManifest*)dynmanifest;
445 ++((LilvDynManifest*)dynmanifest)->refs;
446 }
447 #endif
448
449 // Add all plugin data files (rdfs:seeAlso)
450 SordIter* files = sord_search(world->model,
451 plugin_node,
452 world->uris.rdfs_seeAlso,
453 NULL,
454 NULL);
455 FOREACH_MATCH(files) {
456 const SordNode* file_node = sord_iter_get_node(files, SORD_OBJECT);
457 zix_tree_insert((ZixTree*)plugin->data_uris,
458 lilv_node_new_from_node(world, file_node),
459 NULL);
460 }
461 sord_iter_free(files);
462 }
463
464 SerdStatus
lilv_world_load_graph(LilvWorld * world,SordNode * graph,const LilvNode * uri)465 lilv_world_load_graph(LilvWorld* world, SordNode* graph, const LilvNode* uri)
466 {
467 const SerdNode* base = sord_node_to_serd_node(uri->node);
468 SerdEnv* env = serd_env_new(base);
469 SerdReader* reader = sord_new_reader(
470 world->model, env, SERD_TURTLE, graph);
471
472 const SerdStatus st = lilv_world_load_file(world, reader, uri);
473
474 serd_env_free(env);
475 serd_reader_free(reader);
476 return st;
477 }
478
479 static void
lilv_world_load_dyn_manifest(LilvWorld * world,SordNode * bundle_node,const LilvNode * manifest)480 lilv_world_load_dyn_manifest(LilvWorld* world,
481 SordNode* bundle_node,
482 const LilvNode* manifest)
483 {
484 #ifdef LILV_DYN_MANIFEST
485 if (!world->opt.dyn_manifest) {
486 return;
487 }
488
489 typedef void* LV2_Dyn_Manifest_Handle;
490 LV2_Dyn_Manifest_Handle handle = NULL;
491
492 // ?dman a dynman:DynManifest bundle_node
493 SordModel* model = lilv_world_filter_model(world,
494 world->model,
495 NULL,
496 world->uris.rdf_a,
497 world->uris.dman_DynManifest,
498 bundle_node);
499 SordIter* iter = sord_begin(model);
500 for (; !sord_iter_end(iter); sord_iter_next(iter)) {
501 const SordNode* dmanifest = sord_iter_get_node(iter, SORD_SUBJECT);
502
503 // ?dman lv2:binary ?binary
504 SordIter* binaries = sord_search(world->model,
505 dmanifest,
506 world->uris.lv2_binary,
507 NULL,
508 bundle_node);
509 if (sord_iter_end(binaries)) {
510 sord_iter_free(binaries);
511 LILV_ERRORF("Dynamic manifest in <%s> has no binaries, ignored\n",
512 sord_node_get_string(bundle_node));
513 continue;
514 }
515
516 // Get binary path
517 const SordNode* binary = sord_iter_get_node(binaries, SORD_OBJECT);
518 const uint8_t* lib_uri = sord_node_get_string(binary);
519 char* lib_path = lilv_file_uri_parse((const char*)lib_uri, 0);
520 if (!lib_path) {
521 LILV_ERROR("No dynamic manifest library path\n");
522 sord_iter_free(binaries);
523 continue;
524 }
525
526 // Open library
527 dlerror();
528 void* lib = dlopen(lib_path, RTLD_LAZY);
529 if (!lib) {
530 LILV_ERRORF("Failed to open dynmanifest library `%s' (%s)\n",
531 lib_path, dlerror());
532 sord_iter_free(binaries);
533 lilv_free(lib_path);
534 continue;
535 }
536
537 // Open dynamic manifest
538 typedef int (*OpenFunc)(LV2_Dyn_Manifest_Handle*,
539 const LV2_Feature *const *);
540 OpenFunc dmopen = (OpenFunc)lilv_dlfunc(lib, "lv2_dyn_manifest_open");
541 if (!dmopen || dmopen(&handle, &dman_features)) {
542 LILV_ERRORF("No `lv2_dyn_manifest_open' in `%s'\n", lib_path);
543 sord_iter_free(binaries);
544 dlclose(lib);
545 lilv_free(lib_path);
546 continue;
547 }
548
549 // Get subjects (the data that would be in manifest.ttl)
550 typedef int (*GetSubjectsFunc)(LV2_Dyn_Manifest_Handle, FILE*);
551 GetSubjectsFunc get_subjects_func = (GetSubjectsFunc)lilv_dlfunc(
552 lib, "lv2_dyn_manifest_get_subjects");
553 if (!get_subjects_func) {
554 LILV_ERRORF("No `lv2_dyn_manifest_get_subjects' in `%s'\n",
555 lib_path);
556 sord_iter_free(binaries);
557 dlclose(lib);
558 lilv_free(lib_path);
559 continue;
560 }
561
562 LilvDynManifest* desc = malloc(sizeof(LilvDynManifest));
563 desc->bundle = lilv_node_new_from_node(world, bundle_node);
564 desc->lib = lib;
565 desc->handle = handle;
566 desc->refs = 0;
567
568 sord_iter_free(binaries);
569
570 // Generate data file
571 FILE* fd = tmpfile();
572 get_subjects_func(handle, fd);
573 rewind(fd);
574
575 // Parse generated data file into temporary model
576 // FIXME
577 const SerdNode* base = sord_node_to_serd_node(dmanifest);
578 SerdEnv* env = serd_env_new(base);
579 SerdReader* reader = sord_new_reader(
580 world->model, env, SERD_TURTLE, sord_node_copy(dmanifest));
581 serd_reader_add_blank_prefix(reader,
582 lilv_world_blank_node_prefix(world));
583 serd_reader_read_file_handle(reader, fd,
584 (const uint8_t*)"(dyn-manifest)");
585 serd_reader_free(reader);
586 serd_env_free(env);
587
588 // Close (and automatically delete) temporary data file
589 fclose(fd);
590
591 // ?plugin a lv2:Plugin
592 SordModel* plugins = lilv_world_filter_model(world,
593 world->model,
594 NULL,
595 world->uris.rdf_a,
596 world->uris.lv2_Plugin,
597 dmanifest);
598 SordIter* p = sord_begin(plugins);
599 FOREACH_MATCH(p) {
600 const SordNode* plug = sord_iter_get_node(p, SORD_SUBJECT);
601 lilv_world_add_plugin(world, plug, manifest, desc, bundle_node);
602 }
603 sord_iter_free(p);
604 sord_free(plugins);
605 lilv_free(lib_path);
606 }
607 sord_iter_free(iter);
608 sord_free(model);
609 #endif // LILV_DYN_MANIFEST
610 }
611
612 LilvNode*
lilv_world_get_manifest_uri(LilvWorld * world,const LilvNode * bundle_uri)613 lilv_world_get_manifest_uri(LilvWorld* world, const LilvNode* bundle_uri)
614 {
615 SerdNode manifest_uri = lilv_new_uri_relative_to_base(
616 (const uint8_t*)"manifest.ttl",
617 (const uint8_t*)sord_node_get_string(bundle_uri->node));
618 LilvNode* manifest = lilv_new_uri(world, (const char*)manifest_uri.buf);
619 serd_node_free(&manifest_uri);
620 return manifest;
621 }
622
623 static SordModel*
load_plugin_model(LilvWorld * world,const LilvNode * bundle_uri,const LilvNode * plugin_uri)624 load_plugin_model(LilvWorld* world,
625 const LilvNode* bundle_uri,
626 const LilvNode* plugin_uri)
627 {
628 // Create model and reader for loading into it
629 SordNode* bundle_node = bundle_uri->node;
630 SordModel* model = sord_new(world->world, SORD_SPO|SORD_OPS, false);
631 SerdEnv* env = serd_env_new(sord_node_to_serd_node(bundle_node));
632 SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
633
634 // Load manifest
635 LilvNode* manifest_uri = lilv_world_get_manifest_uri(world, bundle_uri);
636 serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world));
637 serd_reader_read_file(
638 reader, (const uint8_t*)lilv_node_as_string(manifest_uri));
639
640 // Load any seeAlso files
641 SordModel* files = lilv_world_filter_model(
642 world, model, plugin_uri->node, world->uris.rdfs_seeAlso, NULL, NULL);
643
644 SordIter* f = sord_begin(files);
645 FOREACH_MATCH(f) {
646 const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
647 const uint8_t* file_str = sord_node_get_string(file);
648 if (sord_node_get_type(file) == SORD_URI) {
649 serd_reader_add_blank_prefix(
650 reader, lilv_world_blank_node_prefix(world));
651 serd_reader_read_file(reader, file_str);
652 }
653 }
654
655 sord_iter_free(f);
656 sord_free(files);
657 serd_reader_free(reader);
658 serd_env_free(env);
659 lilv_node_free(manifest_uri);
660
661 return model;
662 }
663
664 static LilvVersion
get_version(LilvWorld * world,SordModel * model,const LilvNode * subject)665 get_version(LilvWorld* world, SordModel* model, const LilvNode* subject)
666 {
667 const SordNode* builder_node = sord_get(
668 model, subject->node, world->uris.mod_builderVersion, NULL, NULL);
669 const SordNode* minor_node = sord_get(
670 model, subject->node, world->uris.lv2_minorVersion, NULL, NULL);
671 const SordNode* micro_node = sord_get(
672 model, subject->node, world->uris.lv2_microVersion, NULL, NULL);
673 const SordNode* release_node = sord_get(
674 model, subject->node, world->uris.mod_releaseNumber, NULL, NULL);
675
676
677 LilvVersion version = { 0, 0, 0, 0 };
678 if (builder_node) {
679 version.builder = atoi((const char*)sord_node_get_string(builder_node));
680 }
681 if (minor_node && micro_node) {
682 version.minor = atoi((const char*)sord_node_get_string(minor_node));
683 version.micro = atoi((const char*)sord_node_get_string(micro_node));
684 }
685 if (release_node) {
686 version.release = atoi((const char*)sord_node_get_string(release_node));
687 }
688
689 return version;
690 }
691
692 LILV_API void
lilv_world_load_bundle(LilvWorld * world,const LilvNode * bundle_uri)693 lilv_world_load_bundle(LilvWorld* world, const LilvNode* bundle_uri)
694 {
695 if (!lilv_node_is_uri(bundle_uri)) {
696 LILV_ERRORF("Bundle URI `%s' is not a URI\n",
697 sord_node_get_string(bundle_uri->node));
698 return;
699 }
700
701 SordNode* bundle_node = bundle_uri->node;
702 LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle_uri);
703
704 // Read manifest into model with graph = bundle_node
705 SerdStatus st = lilv_world_load_graph(world, bundle_node, manifest);
706 if (st > SERD_FAILURE) {
707 lilv_node_free(manifest);
708 return;
709 }
710
711 // ?plugin a lv2:Plugin
712 SordIter* plug_results = sord_search(world->model,
713 NULL,
714 world->uris.rdf_a,
715 world->uris.lv2_Plugin,
716 bundle_node);
717
718 // Find any loaded plugins that will be replaced with a newer version
719 LilvNodes* unload_uris = lilv_nodes_new();
720 FOREACH_MATCH(plug_results) {
721 const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT);
722
723 LilvNode* plugin_uri = lilv_node_new_from_node(world, plug);
724 const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, plugin_uri);
725 const LilvNode* last_bundle = plugin ? lilv_plugin_get_bundle_uri(plugin) : NULL;
726 if (!plugin || sord_node_equals(bundle_node, last_bundle->node)) {
727 // No previously loaded version, or it's from the same bundle
728 lilv_node_free(plugin_uri);
729 continue;
730 }
731
732 // Compare versions
733 SordModel* this_model = load_plugin_model(world, bundle_uri, plugin_uri);
734 LilvVersion this_version = get_version(world, this_model, plugin_uri);
735 SordModel* last_model = load_plugin_model(world, last_bundle, plugin_uri);
736 LilvVersion last_version = get_version(world, last_model, plugin_uri);
737 sord_free(this_model);
738 sord_free(last_model);
739 const int cmp = lilv_version_cmp_mod(&this_version, &last_version);
740 if (cmp > 0) {
741 zix_tree_insert((ZixTree*)unload_uris,
742 lilv_node_duplicate(plugin_uri),
743 NULL);
744 LILV_WARNF("Replacing version %d:%d.%d-%d of <%s> from <%s>\n",
745 last_version.builder, last_version.minor,
746 last_version.micro, last_version.release,
747 sord_node_get_string(plug),
748 sord_node_get_string(last_bundle->node));
749 LILV_NOTEF("New version %d:%d.%d-%d found in <%s>\n",
750 this_version.builder, this_version.minor,
751 this_version.micro, this_version.release,
752 sord_node_get_string(bundle_node));
753 } else if (cmp < 0) {
754 LILV_WARNF("Ignoring bundle <%s>\n",
755 sord_node_get_string(bundle_node));
756 LILV_NOTEF("Newer version of <%s> loaded from <%s>\n",
757 sord_node_get_string(plug),
758 sord_node_get_string(last_bundle->node));
759 lilv_node_free(plugin_uri);
760 sord_iter_free(plug_results);
761 lilv_world_drop_graph(world, bundle_node);
762 lilv_node_free(manifest);
763 lilv_nodes_free(unload_uris);
764 return;
765 }
766 lilv_node_free(plugin_uri);
767 }
768
769 sord_iter_free(plug_results);
770
771 // Unload any old conflicting plugins
772 LilvNodes* unload_bundles = lilv_nodes_new();
773 LILV_FOREACH(nodes, i, unload_uris) {
774 const LilvNode* uri = lilv_nodes_get(unload_uris, i);
775 const LilvPlugin* plugin = lilv_plugins_get_by_uri(world->plugins, uri);
776 const LilvNode* bundle = lilv_plugin_get_bundle_uri(plugin);
777
778 // Unload plugin and record bundle for later unloading
779 lilv_world_unload_resource(world, uri);
780 zix_tree_insert((ZixTree*)unload_bundles,
781 lilv_node_duplicate(bundle),
782 NULL);
783
784 }
785 lilv_nodes_free(unload_uris);
786
787 // Now unload the associated bundles
788 // This must be done last since several plugins could be in the same bundle
789 LILV_FOREACH(nodes, i, unload_bundles) {
790 lilv_world_unload_bundle(world, lilv_nodes_get(unload_bundles, i));
791 }
792 lilv_nodes_free(unload_bundles);
793
794 // Re-search for plugin results now that old plugins are gone
795 plug_results = sord_search(world->model,
796 NULL,
797 world->uris.rdf_a,
798 world->uris.lv2_Plugin,
799 bundle_node);
800
801 FOREACH_MATCH(plug_results) {
802 const SordNode* plug = sord_iter_get_node(plug_results, SORD_SUBJECT);
803 lilv_world_add_plugin(world, plug, manifest, NULL, bundle_node);
804 }
805 sord_iter_free(plug_results);
806
807 lilv_world_load_dyn_manifest(world, bundle_node, manifest);
808
809 // ?spec a lv2:Specification
810 // ?spec a owl:Ontology
811 const SordNode* spec_preds[] = { world->uris.lv2_Specification,
812 world->uris.owl_Ontology,
813 NULL };
814 for (const SordNode** p = spec_preds; *p; ++p) {
815 SordIter* i = sord_search(
816 world->model, NULL, world->uris.rdf_a, *p, bundle_node);
817 FOREACH_MATCH(i) {
818 const SordNode* spec = sord_iter_get_node(i, SORD_SUBJECT);
819 lilv_world_add_spec(world, spec, bundle_node);
820 }
821 sord_iter_free(i);
822 }
823
824 lilv_node_free(manifest);
825 }
826
827 static int
lilv_world_drop_graph(LilvWorld * world,const SordNode * graph)828 lilv_world_drop_graph(LilvWorld* world, const SordNode* graph)
829 {
830 SordIter* i = sord_search(world->model, NULL, NULL, NULL, graph);
831 while (!sord_iter_end(i)) {
832 const SerdStatus st = sord_erase(world->model, i);
833 if (st) {
834 LILV_ERRORF("Error removing statement from <%s> (%s)\n",
835 sord_node_get_string(graph), serd_strerror(st));
836 return st;
837 }
838 }
839 sord_iter_free(i);
840
841 return 0;
842 }
843
844 /** Remove loaded_files entry so file will be reloaded if requested. */
845 static int
lilv_world_unload_file(LilvWorld * world,const LilvNode * file)846 lilv_world_unload_file(LilvWorld* world, const LilvNode* file)
847 {
848 ZixTreeIter* iter;
849 if (!zix_tree_find((ZixTree*)world->loaded_files, file, &iter)) {
850 zix_tree_remove((ZixTree*)world->loaded_files, iter);
851 return 0;
852 }
853 return 1;
854 }
855
856 LILV_API int
lilv_world_unload_bundle(LilvWorld * world,const LilvNode * bundle_uri)857 lilv_world_unload_bundle(LilvWorld* world, const LilvNode* bundle_uri)
858 {
859 if (!bundle_uri) {
860 return 0;
861 }
862
863 // Find all loaded files that are inside the bundle
864 LilvNodes* files = lilv_nodes_new();
865 LILV_FOREACH(nodes, i, world->loaded_files) {
866 const LilvNode* file = lilv_nodes_get(world->loaded_files, i);
867 if (!strncmp(lilv_node_as_string(file),
868 lilv_node_as_string(bundle_uri),
869 strlen(lilv_node_as_string(bundle_uri)))) {
870 zix_tree_insert((ZixTree*)files,
871 lilv_node_duplicate(file),
872 NULL);
873 }
874 }
875
876 // Unload all loaded files in the bundle
877 LILV_FOREACH(nodes, i, files) {
878 const LilvNode* file = lilv_nodes_get(world->plugins, i);
879 lilv_world_unload_file(world, file);
880 }
881
882 lilv_nodes_free(files);
883
884 /* Remove any plugins in the bundle from the plugin list. Since the
885 application may still have a pointer to the LilvPlugin, it can not be
886 destroyed here. Instead, we move it to the zombie plugin list, so it
887 will not be in the list returned by lilv_world_get_all_plugins() but can
888 still be used.
889 */
890 ZixTreeIter* i = zix_tree_begin((ZixTree*)world->plugins);
891 while (i != zix_tree_end((ZixTree*)world->plugins)) {
892 LilvPlugin* p = (LilvPlugin*)zix_tree_get(i);
893 ZixTreeIter* next = zix_tree_iter_next(i);
894
895 if (lilv_node_equals(lilv_plugin_get_bundle_uri(p), bundle_uri)) {
896 zix_tree_remove((ZixTree*)world->plugins, i);
897 zix_tree_insert((ZixTree*)world->zombies, p, NULL);
898 }
899
900 i = next;
901 }
902
903 // Drop everything in bundle graph
904 return lilv_world_drop_graph(world, bundle_uri->node);
905 }
906
907 static void
load_dir_entry(const char * dir,const char * name,void * data)908 load_dir_entry(const char* dir, const char* name, void* data)
909 {
910 LilvWorld* world = (LilvWorld*)data;
911 if (!strcmp(name, ".") || !strcmp(name, ".."))
912 return;
913
914 char* path = lilv_strjoin(dir, "/", name, "/", NULL);
915 SerdNode suri = serd_node_new_file_uri((const uint8_t*)path, 0, 0, true);
916 LilvNode* node = lilv_new_uri(world, (const char*)suri.buf);
917
918 lilv_world_load_bundle(world, node);
919 lilv_node_free(node);
920 serd_node_free(&suri);
921 free(path);
922 }
923
924 /** Load all bundles in the directory at `dir_path`. */
925 static void
lilv_world_load_directory(LilvWorld * world,const char * dir_path)926 lilv_world_load_directory(LilvWorld* world, const char* dir_path)
927 {
928 char* path = lilv_expand(dir_path);
929 if (path) {
930 lilv_dir_for_each(path, world, load_dir_entry);
931 free(path);
932 }
933 }
934
935 static const char*
first_path_sep(const char * path)936 first_path_sep(const char* path)
937 {
938 for (const char* p = path; *p != '\0'; ++p) {
939 if (*p == LILV_PATH_SEP[0]) {
940 return p;
941 }
942 }
943 return NULL;
944 }
945
946 /** Load all bundles found in `lv2_path`.
947 * @param lv2_path A colon-delimited list of directories. These directories
948 * should contain LV2 bundle directories (ie the search path is a list of
949 * parent directories of bundles, not a list of bundle directories).
950 */
951 static void
lilv_world_load_path(LilvWorld * world,const char * lv2_path)952 lilv_world_load_path(LilvWorld* world,
953 const char* lv2_path)
954 {
955 while (lv2_path[0] != '\0') {
956 const char* const sep = first_path_sep(lv2_path);
957 if (sep) {
958 const size_t dir_len = sep - lv2_path;
959 char* const dir = (char*)malloc(dir_len + 1);
960 memcpy(dir, lv2_path, dir_len);
961 dir[dir_len] = '\0';
962 lilv_world_load_directory(world, dir);
963 free(dir);
964 lv2_path += dir_len + 1;
965 } else {
966 lilv_world_load_directory(world, lv2_path);
967 lv2_path = "\0";
968 }
969 }
970 }
971
972 void
lilv_world_load_specifications(LilvWorld * world)973 lilv_world_load_specifications(LilvWorld* world)
974 {
975 for (LilvSpec* spec = world->specs; spec; spec = spec->next) {
976 LILV_FOREACH(nodes, f, spec->data_uris) {
977 LilvNode* file = (LilvNode*)lilv_collection_get(spec->data_uris, f);
978 lilv_world_load_graph(world, NULL, file);
979 }
980 }
981 }
982
983 void
lilv_world_load_plugin_classes(LilvWorld * world)984 lilv_world_load_plugin_classes(LilvWorld* world)
985 {
986 /* FIXME: This loads all classes, not just lv2:Plugin subclasses.
987 However, if the host gets all the classes via lilv_plugin_class_get_children
988 starting with lv2:Plugin as the root (which is e.g. how a host would build
989 a menu), they won't be seen anyway...
990 */
991
992 SordIter* classes = sord_search(world->model,
993 NULL,
994 world->uris.rdf_a,
995 world->uris.rdfs_Class,
996 NULL);
997 FOREACH_MATCH(classes) {
998 const SordNode* class_node = sord_iter_get_node(classes, SORD_SUBJECT);
999
1000 SordNode* parent = sord_get(
1001 world->model, class_node, world->uris.rdfs_subClassOf, NULL, NULL);
1002 if (!parent || sord_node_get_type(parent) != SORD_URI) {
1003 continue;
1004 }
1005
1006 SordNode* label = sord_get(
1007 world->model, class_node, world->uris.rdfs_label, NULL, NULL);
1008 if (!label) {
1009 sord_node_free(world->world, parent);
1010 continue;
1011 }
1012
1013 LilvPluginClass* pclass = lilv_plugin_class_new(
1014 world, parent, class_node,
1015 (const char*)sord_node_get_string(label));
1016 if (pclass) {
1017 zix_tree_insert((ZixTree*)world->plugin_classes, pclass, NULL);
1018 }
1019
1020 sord_node_free(world->world, label);
1021 sord_node_free(world->world, parent);
1022 }
1023 sord_iter_free(classes);
1024 }
1025
1026 LILV_API void
lilv_world_load_all(LilvWorld * world,const char * lv2_path)1027 lilv_world_load_all(LilvWorld* world, const char* lv2_path)
1028 {
1029 // Discover bundles and read all manifest files into model
1030 lilv_world_load_path(world, lv2_path);
1031
1032 LILV_FOREACH(plugins, p, world->plugins) {
1033 const LilvPlugin* plugin = (const LilvPlugin*)lilv_collection_get(
1034 (ZixTree*)world->plugins, p);
1035
1036 // ?new dc:replaces plugin
1037 if (sord_ask(world->model,
1038 NULL,
1039 world->uris.dc_replaces,
1040 lilv_plugin_get_uri(plugin)->node,
1041 NULL)) {
1042 // TODO: Check if replacement is a known plugin? (expensive)
1043 ((LilvPlugin*)plugin)->replaced = true;
1044 }
1045 }
1046
1047 // Query out things to cache
1048 lilv_world_load_specifications(world);
1049 lilv_world_load_plugin_classes(world);
1050 }
1051
1052 SerdStatus
lilv_world_load_file(LilvWorld * world,SerdReader * reader,const LilvNode * uri)1053 lilv_world_load_file(LilvWorld* world, SerdReader* reader, const LilvNode* uri)
1054 {
1055 ZixTreeIter* iter;
1056 if (!zix_tree_find((ZixTree*)world->loaded_files, uri, &iter)) {
1057 return SERD_FAILURE; // File has already been loaded
1058 }
1059
1060 serd_reader_add_blank_prefix(reader, lilv_world_blank_node_prefix(world));
1061 const SerdStatus st = serd_reader_read_file(
1062 reader, sord_node_get_string(uri->node));
1063 if (st) {
1064 return st;
1065 }
1066
1067 zix_tree_insert((ZixTree*)world->loaded_files,
1068 lilv_node_duplicate(uri),
1069 NULL);
1070 return SERD_SUCCESS;
1071 }
1072
1073 LILV_API int
lilv_world_load_resource(LilvWorld * world,const LilvNode * resource)1074 lilv_world_load_resource(LilvWorld* world,
1075 const LilvNode* resource)
1076 {
1077 if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) {
1078 LILV_ERRORF("Node `%s' is not a resource\n",
1079 sord_node_get_string(resource->node));
1080 return -1;
1081 }
1082
1083 SordModel* files = lilv_world_filter_model(world,
1084 world->model,
1085 resource->node,
1086 world->uris.rdfs_seeAlso,
1087 NULL, NULL);
1088
1089 SordIter* f = sord_begin(files);
1090 int n_read = 0;
1091 FOREACH_MATCH(f) {
1092 const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
1093 const uint8_t* file_str = sord_node_get_string(file);
1094 LilvNode* file_node = lilv_node_new_from_node(world, file);
1095 if (sord_node_get_type(file) != SORD_URI) {
1096 LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n", file_str);
1097 } else if (!lilv_world_load_graph(world, (SordNode*)file, file_node)) {
1098 ++n_read;
1099 }
1100 lilv_node_free(file_node);
1101 }
1102 sord_iter_free(f);
1103
1104 sord_free(files);
1105 return n_read;
1106 }
1107
1108 LILV_API int
lilv_world_unload_resource(LilvWorld * world,const LilvNode * resource)1109 lilv_world_unload_resource(LilvWorld* world,
1110 const LilvNode* resource)
1111 {
1112 if (!lilv_node_is_uri(resource) && !lilv_node_is_blank(resource)) {
1113 LILV_ERRORF("Node `%s' is not a resource\n",
1114 sord_node_get_string(resource->node));
1115 return -1;
1116 }
1117
1118 SordModel* files = lilv_world_filter_model(world,
1119 world->model,
1120 resource->node,
1121 world->uris.rdfs_seeAlso,
1122 NULL, NULL);
1123
1124 SordIter* f = sord_begin(files);
1125 int n_dropped = 0;
1126 FOREACH_MATCH(f) {
1127 const SordNode* file = sord_iter_get_node(f, SORD_OBJECT);
1128 LilvNode* file_node = lilv_node_new_from_node(world, file);
1129 if (sord_node_get_type(file) != SORD_URI) {
1130 LILV_ERRORF("rdfs:seeAlso node `%s' is not a URI\n",
1131 sord_node_get_string(file));
1132 } else if (!lilv_world_drop_graph(world, file_node->node)) {
1133 lilv_world_unload_file(world, file_node);
1134 ++n_dropped;
1135 }
1136 lilv_node_free(file_node);
1137 }
1138 sord_iter_free(f);
1139
1140 sord_free(files);
1141 return n_dropped;
1142 }
1143
1144 LILV_API const LilvPluginClass*
lilv_world_get_plugin_class(const LilvWorld * world)1145 lilv_world_get_plugin_class(const LilvWorld* world)
1146 {
1147 return world->lv2_plugin_class;
1148 }
1149
1150 LILV_API const LilvPluginClasses*
lilv_world_get_plugin_classes(const LilvWorld * world)1151 lilv_world_get_plugin_classes(const LilvWorld* world)
1152 {
1153 return world->plugin_classes;
1154 }
1155
1156 LILV_API const LilvPlugins*
lilv_world_get_all_plugins(const LilvWorld * world)1157 lilv_world_get_all_plugins(const LilvWorld* world)
1158 {
1159 return world->plugins;
1160 }
1161
1162 LILV_API LilvNode*
lilv_world_get_symbol(LilvWorld * world,const LilvNode * subject)1163 lilv_world_get_symbol(LilvWorld* world, const LilvNode* subject)
1164 {
1165 // Check for explicitly given symbol
1166 SordNode* snode = sord_get(
1167 world->model, subject->node, world->uris.lv2_symbol, NULL, NULL);
1168
1169 if (snode) {
1170 LilvNode* ret = lilv_node_new_from_node(world, snode);
1171 sord_node_free(world->world, snode);
1172 return ret;
1173 }
1174
1175 if (!lilv_node_is_uri(subject)) {
1176 return NULL;
1177 }
1178
1179 // Find rightmost segment of URI
1180 SerdURI uri;
1181 serd_uri_parse((const uint8_t*)lilv_node_as_uri(subject), &uri);
1182 const char* str = "_";
1183 if (uri.fragment.buf) {
1184 str = (const char*)uri.fragment.buf + 1;
1185 } else if (uri.query.buf) {
1186 str = (const char*)uri.query.buf;
1187 } else if (uri.path.buf) {
1188 const char* last_slash = strrchr((const char*)uri.path.buf, '/');
1189 str = last_slash ? (last_slash + 1) : (const char*)uri.path.buf;
1190 }
1191
1192 // Replace invalid characters
1193 const size_t len = strlen(str);
1194 char* const sym = (char*)calloc(1, len + 1);
1195 for (size_t i = 0; i < len; ++i) {
1196 const char c = str[i];
1197 if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') ||
1198 (c == '_') || (i > 0 && c >= '0' && c <= '9'))) {
1199 sym[i] = '_';
1200 } else {
1201 sym[i] = str[i];
1202 }
1203 }
1204
1205 LilvNode* ret = lilv_new_string(world, sym);
1206 free(sym);
1207 return ret;
1208 }
1209