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