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