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 <errno.h>
18 #include <stdio.h>
19 #include <string.h>
20 
21 #include "lv2/atom.h"
22 #include "lv2/atom-forge.h"
23 #include "lv2/presets.h"
24 #include "lv2/state.h"
25 
26 #include "lilv_config.h"
27 #include "lilv_internal.h"
28 #include "sratom/sratom.h"
29 
30 #define USTR(s) ((const uint8_t*)(s))
31 
32 typedef struct {
33 	void*    value;  ///< Value/Object
34 	size_t   size;   ///< Size of value
35 	uint32_t key;    ///< Key/Predicate (URID)
36 	uint32_t type;   ///< Type of value (URID)
37 	uint32_t flags;  ///< State flags (POD, etc)
38 } Property;
39 
40 typedef struct {
41 	char*    symbol;  ///< Symbol of port
42 	void*    value;   ///< Value of port
43 	uint32_t size;    ///< Size of value
44 	uint32_t type;    ///< Type of value (URID)
45 } PortValue;
46 
47 typedef struct {
48 	char* abs;  ///< Absolute path of actual file
49 	char* rel;  ///< Abstract path (relative path in state dir)
50 } PathMap;
51 
52 typedef struct {
53 	size_t    n;
54 	Property* props;
55 } PropertyArray;
56 
57 struct LilvStateImpl {
58 	LilvNode*     plugin_uri;   ///< Plugin URI
59 	LilvNode*     uri;          ///< State/preset URI
60 	char*         dir;          ///< Save directory (if saved)
61 	char*         file_dir;     ///< Directory for files created by plugin
62 	char*         copy_dir;     ///< Directory for snapshots of external files
63 	char*         link_dir;     ///< Directory for links to external files
64 	char*         label;        ///< State/Preset label
65 	ZixTree*      abs2rel;      ///< PathMap sorted by abs
66 	ZixTree*      rel2abs;      ///< PathMap sorted by rel
67 	PropertyArray props;        ///< State properties
68 	PropertyArray metadata;     ///< State metadata
69 	PortValue*    values;       ///< Port values
70 	uint32_t      atom_Path;    ///< atom:Path URID
71 	uint32_t      n_values;     ///< Number of port values
72 };
73 
74 static int
abs_cmp(const void * a,const void * b,void * user_data)75 abs_cmp(const void* a, const void* b, void* user_data)
76 {
77 	return strcmp(((const PathMap*)a)->abs, ((const PathMap*)b)->abs);
78 }
79 
80 static int
rel_cmp(const void * a,const void * b,void * user_data)81 rel_cmp(const void* a, const void* b, void* user_data)
82 {
83 	return strcmp(((const PathMap*)a)->rel, ((const PathMap*)b)->rel);
84 }
85 
86 static int
property_cmp(const void * a,const void * b)87 property_cmp(const void* a, const void* b)
88 {
89 	return ((const Property*)a)->key - ((const Property*)b)->key;
90 }
91 
92 static int
value_cmp(const void * a,const void * b)93 value_cmp(const void* a, const void* b)
94 {
95 	return strcmp(((const PortValue*)a)->symbol,
96 	              ((const PortValue*)b)->symbol);
97 }
98 
99 static void
path_rel_free(void * ptr)100 path_rel_free(void* ptr)
101 {
102 	free(((PathMap*)ptr)->abs);
103 	free(((PathMap*)ptr)->rel);
104 	free(ptr);
105 }
106 
107 static PortValue*
append_port_value(LilvState * state,const char * port_symbol,const void * value,uint32_t size,uint32_t type)108 append_port_value(LilvState*  state,
109                   const char* port_symbol,
110                   const void* value,
111                   uint32_t    size,
112                   uint32_t    type)
113 {
114 	PortValue* pv = NULL;
115 	if (value) {
116 		state->values = (PortValue*)realloc(
117 			state->values, (++state->n_values) * sizeof(PortValue));
118 		pv         = &state->values[state->n_values - 1];
119 		pv->symbol = lilv_strdup(port_symbol);
120 		pv->value  = malloc(size);
121 		pv->size   = size;
122 		pv->type   = type;
123 		memcpy(pv->value, value, size);
124 	}
125 	return pv;
126 }
127 
128 static const char*
lilv_state_rel2abs(const LilvState * state,const char * path)129 lilv_state_rel2abs(const LilvState* state, const char* path)
130 {
131 	ZixTreeIter*  iter = NULL;
132 	const PathMap key  = { NULL, (char*)path };
133 	if (state->rel2abs && !zix_tree_find(state->rel2abs, &key, &iter)) {
134 		return ((const PathMap*)zix_tree_get(iter))->abs;
135 	}
136 	return path;
137 }
138 
139 static void
append_property(LilvState * state,PropertyArray * array,uint32_t key,const void * value,size_t size,uint32_t type,uint32_t flags)140 append_property(LilvState*     state,
141                 PropertyArray* array,
142                 uint32_t       key,
143                 const void*    value,
144                 size_t         size,
145                 uint32_t       type,
146                 uint32_t       flags)
147 {
148 	array->props = (Property*)realloc(
149 		array->props, (++array->n) * sizeof(Property));
150 
151 	Property* const prop = &array->props[array->n - 1];
152 	if ((flags & LV2_STATE_IS_POD) || type == state->atom_Path) {
153 		prop->value = malloc(size);
154 		memcpy(prop->value, value, size);
155 	} else {
156 		prop->value = (void*)value;
157 	}
158 
159 	prop->size  = size;
160 	prop->key   = key;
161 	prop->type  = type;
162 	prop->flags = flags;
163 }
164 
165 static LV2_State_Status
store_callback(LV2_State_Handle handle,uint32_t key,const void * value,size_t size,uint32_t type,uint32_t flags)166 store_callback(LV2_State_Handle handle,
167                uint32_t         key,
168                const void*      value,
169                size_t           size,
170                uint32_t         type,
171                uint32_t         flags)
172 {
173 	LilvState* const state = (LilvState*)handle;
174 	append_property(state, &state->props, key, value, size, type, flags);
175 	return LV2_STATE_SUCCESS;
176 }
177 
178 static const void*
retrieve_callback(LV2_State_Handle handle,uint32_t key,size_t * size,uint32_t * type,uint32_t * flags)179 retrieve_callback(LV2_State_Handle handle,
180                   uint32_t         key,
181                   size_t*          size,
182                   uint32_t*        type,
183                   uint32_t*        flags)
184 {
185 	const LilvState* const state      = (LilvState*)handle;
186 	const Property         search_key = { NULL, 0, key, 0, 0 };
187 	const Property* const  prop       = (Property*)bsearch(
188 		&search_key, state->props.props, state->props.n,
189 		sizeof(Property), property_cmp);
190 
191 	if (prop) {
192 		*size  = prop->size;
193 		*type  = prop->type;
194 		*flags = prop->flags;
195 		return prop->value;
196 	}
197 	return NULL;
198 }
199 
200 static bool
lilv_state_has_path(const char * path,void * state)201 lilv_state_has_path(const char* path, void* state)
202 {
203 	return lilv_state_rel2abs((LilvState*)state, path) != path;
204 }
205 
206 static char*
make_path(LV2_State_Make_Path_Handle handle,const char * path)207 make_path(LV2_State_Make_Path_Handle handle, const char* path)
208 {
209 	LilvState* state = (LilvState*)handle;
210 	if (!lilv_path_exists(state->dir, NULL)) {
211 		lilv_mkdir_p(state->dir);
212 	}
213 
214 	return lilv_path_join(state->dir, path);
215 }
216 
217 static char*
abstract_path(LV2_State_Map_Path_Handle handle,const char * abs_path)218 abstract_path(LV2_State_Map_Path_Handle handle,
219               const char*               abs_path)
220 {
221 	LilvState*    state     = (LilvState*)handle;
222 	char*         path      = NULL;
223 	char*         real_path = lilv_realpath(abs_path);
224 	const PathMap key       = { (char*)real_path, NULL };
225 	ZixTreeIter*  iter      = NULL;
226 
227 	if (abs_path[0] == '\0') {
228 		return lilv_strdup(abs_path);
229 	} else if (!zix_tree_find(state->abs2rel, &key, &iter)) {
230 		// Already mapped path in a previous call
231 		PathMap* pm = (PathMap*)zix_tree_get(iter);
232 		free(real_path);
233 		return lilv_strdup(pm->rel);
234 	} else if (lilv_path_is_child(real_path, state->dir)) {
235 		// File in state directory (loaded, or created by plugin during save)
236 		path = lilv_path_relative_to(real_path, state->dir);
237 	} else if (lilv_path_is_child(real_path, state->file_dir)) {
238 		// File created by plugin earlier
239 		path = lilv_path_relative_to(real_path, state->file_dir);
240 		if (state->copy_dir) {
241 			if (!lilv_path_exists(state->copy_dir, NULL)) {
242 				lilv_mkdir_p(state->copy_dir);
243 			}
244 			char* cpath = lilv_path_join(state->copy_dir, path);
245 			char* copy  = lilv_get_latest_copy(real_path, cpath);
246 			if (!copy || !lilv_file_equals(real_path, copy)) {
247 				// No recent enough copy, make a new one
248 				free(copy);
249 				copy = lilv_find_free_path(cpath, lilv_path_exists, NULL);
250 				const int st = lilv_copy_file(real_path, copy);
251 				if (st) {
252 					LILV_ERRORF("Error copying state file %s (%s)\n",
253 					            copy, strerror(st));
254 				}
255 			}
256 			free(real_path);
257 			free(cpath);
258 
259 			// Refer to the latest copy in plugin state
260 			real_path = copy;
261 		}
262 	} else if (state->link_dir) {
263 		// New path outside state directory, make a link
264 		const char* slash = strrchr(real_path, '/');
265 		const char* name  = slash ? (slash + 1) : real_path;
266 
267 		// Find a free name in the (virtual) state directory
268 		path = lilv_find_free_path(name, lilv_state_has_path, state);
269 	} else {
270 		// No link directory, preserve absolute path
271 		path = lilv_strdup(abs_path);
272 	}
273 
274 	// Add record to path mapping
275 	PathMap* pm = (PathMap*)malloc(sizeof(PathMap));
276 	pm->abs = real_path;
277 	pm->rel = lilv_strdup(path);
278 	zix_tree_insert(state->abs2rel, pm, NULL);
279 	zix_tree_insert(state->rel2abs, pm, NULL);
280 
281 	return path;
282 }
283 
284 static char*
absolute_path(LV2_State_Map_Path_Handle handle,const char * state_path)285 absolute_path(LV2_State_Map_Path_Handle handle,
286               const char*               state_path)
287 {
288 	LilvState* state = (LilvState*)handle;
289 	char*      path  = NULL;
290 	if (lilv_path_is_absolute(state_path)) {
291 		// Absolute path, return identical path
292 		path = lilv_strdup(state_path);
293 	} else if (state->dir) {
294 		// Relative path inside state directory
295 		path = lilv_path_join(state->dir, state_path);
296 	} else {
297 		// State has not been saved, unmap
298 		path = lilv_strdup(lilv_state_rel2abs(state, state_path));
299 	}
300 
301 	return path;
302 }
303 
304 /** Return a new features array which is `feature` added to `features`. */
305 static const LV2_Feature**
add_features(const LV2_Feature * const * features,const LV2_Feature * map,const LV2_Feature * make)306 add_features(const LV2_Feature *const * features,
307              const LV2_Feature* map, const LV2_Feature* make)
308 {
309 	size_t n_features = 0;
310 	for (; features && features[n_features]; ++n_features) {}
311 
312 	const LV2_Feature** ret = (const LV2_Feature**)calloc(
313 		n_features + 3, sizeof(LV2_Feature*));
314 
315 	if (features) {
316 		memcpy(ret, features, n_features * sizeof(LV2_Feature*));
317 	}
318 
319 	ret[n_features]     = map;
320 	ret[n_features + 1] = make;
321 	return ret;
322 }
323 
324 static char*
absolute_dir(const char * path)325 absolute_dir(const char* path)
326 {
327 	char* abs_path = lilv_path_absolute(path);
328 	char* base     = lilv_path_join(abs_path, NULL);
329 	free(abs_path);
330 	return base;
331 }
332 
333 static const char*
state_strerror(LV2_State_Status st)334 state_strerror(LV2_State_Status st)
335 {
336 	switch (st) {
337 	case LV2_STATE_SUCCESS:         return "Completed successfully";
338 	case LV2_STATE_ERR_BAD_TYPE:    return "Unsupported type";
339 	case LV2_STATE_ERR_BAD_FLAGS:   return "Unsupported flags";
340 	case LV2_STATE_ERR_NO_FEATURE:  return "Missing features";
341 	case LV2_STATE_ERR_NO_PROPERTY: return "Missing property";
342 	default:                        return "Unknown error";
343 	}
344 }
345 
346 LILV_API LilvState*
lilv_state_new_from_instance(const LilvPlugin * plugin,LilvInstance * instance,LV2_URID_Map * map,const char * file_dir,const char * copy_dir,const char * link_dir,const char * save_dir,LilvGetPortValueFunc get_value,void * user_data,uint32_t flags,const LV2_Feature * const * features)347 lilv_state_new_from_instance(const LilvPlugin*          plugin,
348                              LilvInstance*              instance,
349                              LV2_URID_Map*              map,
350                              const char*                file_dir,
351                              const char*                copy_dir,
352                              const char*                link_dir,
353                              const char*                save_dir,
354                              LilvGetPortValueFunc       get_value,
355                              void*                      user_data,
356                              uint32_t                   flags,
357                              const LV2_Feature *const * features)
358 {
359 	const LV2_Feature** sfeatures = NULL;
360 	LilvWorld* const    world     = plugin->world;
361 	LilvState* const    state     = (LilvState*)calloc(1, sizeof(LilvState));
362 	state->plugin_uri = lilv_node_duplicate(lilv_plugin_get_uri(plugin));
363 	state->abs2rel    = zix_tree_new(false, abs_cmp, NULL, path_rel_free);
364 	state->rel2abs    = zix_tree_new(false, rel_cmp, NULL, NULL);
365 	state->file_dir   = file_dir ? absolute_dir(file_dir) : NULL;
366 	state->copy_dir   = copy_dir ? absolute_dir(copy_dir) : NULL;
367 	state->link_dir   = link_dir ? absolute_dir(link_dir) : NULL;
368 	state->dir        = save_dir ? absolute_dir(save_dir) : NULL;
369 	state->atom_Path  = map->map(map->handle, LV2_ATOM__Path);
370 
371 	LV2_State_Map_Path  pmap          = { state, abstract_path, absolute_path };
372 	LV2_Feature         pmap_feature  = { LV2_STATE__mapPath, &pmap };
373 	LV2_State_Make_Path pmake         = { state, make_path };
374 	LV2_Feature         pmake_feature = { LV2_STATE__makePath, &pmake };
375 	features = sfeatures = add_features(features, &pmap_feature,
376 	                                    save_dir ? &pmake_feature : NULL);
377 
378 	// Store port values
379 	if (get_value) {
380 		LilvNode* lv2_ControlPort = lilv_new_uri(world, LILV_URI_CONTROL_PORT);
381 		LilvNode* lv2_InputPort   = lilv_new_uri(world, LILV_URI_INPUT_PORT);
382 		for (uint32_t i = 0; i < plugin->num_ports; ++i) {
383 			const LilvPort* const port = plugin->ports[i];
384 			if (lilv_port_is_a(plugin, port, lv2_ControlPort)
385 			    && lilv_port_is_a(plugin, port, lv2_InputPort)) {
386 				uint32_t size, type;
387 				const char* sym   = lilv_node_as_string(port->symbol);
388 				const void* value = get_value(sym, user_data, &size, &type);
389 				append_port_value(state, sym, value, size, type);
390 			}
391 		}
392 		lilv_node_free(lv2_ControlPort);
393 		lilv_node_free(lv2_InputPort);
394 	}
395 
396 	// Store properties
397 	const LV2_Descriptor*      desc  = instance->lv2_descriptor;
398 	const LV2_State_Interface* iface = (desc->extension_data)
399 		? (const LV2_State_Interface*)desc->extension_data(LV2_STATE__interface)
400 		: NULL;
401 
402 	if (iface) {
403 		LV2_State_Status st = iface->save(
404 			instance->lv2_handle, store_callback, state, flags, features);
405 		if (st) {
406 			LILV_ERRORF("Error saving plugin state: %s\n", state_strerror(st));
407 			free(state->props.props);
408 			state->props.props = NULL;
409 			state->props.n     = 0;
410 		} else {
411 			qsort(state->props.props, state->props.n, sizeof(Property), property_cmp);
412 		}
413 	}
414 
415 	qsort(state->values, state->n_values, sizeof(PortValue), value_cmp);
416 
417 	free(sfeatures);
418 	return state;
419 }
420 
421 LILV_API void
lilv_state_emit_port_values(const LilvState * state,LilvSetPortValueFunc set_value,void * user_data)422 lilv_state_emit_port_values(const LilvState*     state,
423                             LilvSetPortValueFunc set_value,
424                             void*                user_data)
425 {
426 	for (uint32_t i = 0; i < state->n_values; ++i) {
427 		const PortValue* val = &state->values[i];
428 		set_value(val->symbol, user_data, val->value, val->size, val->type);
429 	}
430 }
431 
432 LILV_API void
lilv_state_restore(const LilvState * state,const LV2_State_Interface * iface,LV2_Handle handle,LilvSetPortValueFunc set_value,void * user_data,uint32_t flags,const LV2_Feature * const * features)433 lilv_state_restore(const LilvState*           state,
434                    const LV2_State_Interface* iface,
435                    LV2_Handle                 handle,
436                    LilvSetPortValueFunc       set_value,
437                    void*                      user_data,
438                    uint32_t                   flags,
439                    const LV2_Feature *const * features)
440 {
441 	if (iface && iface->restore) {
442 		iface->restore(handle, retrieve_callback,
443 		               (LV2_State_Handle)state, flags, features);
444 	}
445 
446 	if (set_value) {
447 		lilv_state_emit_port_values(state, set_value, user_data);
448 	}
449 }
450 
451 static LilvState*
new_state_from_model(LilvWorld * world,LV2_URID_Map * map,SordModel * model,const SordNode * node,const char * dir)452 new_state_from_model(LilvWorld*       world,
453                      LV2_URID_Map*    map,
454                      SordModel*       model,
455                      const SordNode*  node,
456                      const char*      dir)
457 {
458 	// Check that we know at least something about this state subject
459 	if (!sord_ask(model, node, 0, 0, 0)) {
460 		return NULL;
461 	}
462 
463 	// Allocate state
464 	LilvState* const state = (LilvState*)calloc(1, sizeof(LilvState));
465 	state->dir       = lilv_strdup(dir);
466 	state->atom_Path = map->map(map->handle, LV2_ATOM__Path);
467 	state->uri       = lilv_node_new_from_node(world, node);
468 
469 	// Get the plugin URI this state applies to
470 	SordIter* i = sord_search(model, node, world->uris.lv2_appliesTo, 0, 0);
471 	if (i) {
472 		const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
473 		const SordNode* graph  = sord_iter_get_node(i, SORD_GRAPH);
474 		state->plugin_uri = lilv_node_new_from_node(world, object);
475 		if (!state->dir && graph) {
476 			state->dir = lilv_strdup((const char*)sord_node_get_string(graph));
477 		}
478 		sord_iter_free(i);
479 	} else if (sord_ask(model,
480 	                    node,
481 	                    world->uris.rdf_a,
482 	                    world->uris.lv2_Plugin, 0)) {
483 		// Loading plugin description as state (default state)
484 		state->plugin_uri = lilv_node_new_from_node(world, node);
485 	} else {
486 		LILV_ERRORF("State %s missing lv2:appliesTo property\n",
487 		            sord_node_get_string(node));
488 	}
489 
490 	// Get the state label
491 	i = sord_search(model, node, world->uris.rdfs_label, NULL, NULL);
492 	if (i) {
493 		const SordNode* object = sord_iter_get_node(i, SORD_OBJECT);
494 		const SordNode* graph  = sord_iter_get_node(i, SORD_GRAPH);
495 		state->label = lilv_strdup((const char*)sord_node_get_string(object));
496 		if (!state->dir && graph) {
497 			state->dir = lilv_strdup((const char*)sord_node_get_string(graph));
498 		}
499 		sord_iter_free(i);
500 	}
501 
502 	Sratom*        sratom = sratom_new(map);
503 	SerdChunk      chunk  = { NULL, 0 };
504 	LV2_Atom_Forge forge;
505 	lv2_atom_forge_init(&forge, map);
506 	lv2_atom_forge_set_sink(
507 		&forge, sratom_forge_sink, sratom_forge_deref, &chunk);
508 
509 	// Get port values
510 	SordIter* ports = sord_search(model, node, world->uris.lv2_port, 0, 0);
511 	FOREACH_MATCH(ports) {
512 		const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT);
513 
514 		SordNode* label  = sord_get(model, port, world->uris.rdfs_label, 0, 0);
515 		SordNode* symbol = sord_get(model, port, world->uris.lv2_symbol, 0, 0);
516 		SordNode* value  = sord_get(model, port, world->uris.pset_value, 0, 0);
517 		if (!value) {
518 			value = sord_get(model, port, world->uris.lv2_default, 0, 0);
519 		}
520 		if (!symbol) {
521 			LILV_ERRORF("State `%s' port missing symbol.\n",
522 			            sord_node_get_string(node));
523 		} else if (value) {
524 			chunk.len = 0;
525 			sratom_read(sratom, &forge, world->world, model, value);
526 			const LV2_Atom* atom = (const LV2_Atom*)chunk.buf;
527 
528 			append_port_value(state,
529 			                  (const char*)sord_node_get_string(symbol),
530 			                  LV2_ATOM_BODY_CONST(atom),
531 			                  atom->size, atom->type);
532 
533 			if (label) {
534 				lilv_state_set_label(state,
535 				                     (const char*)sord_node_get_string(label));
536 			}
537 		}
538 		sord_node_free(world->world, value);
539 		sord_node_free(world->world, symbol);
540 		sord_node_free(world->world, label);
541 	}
542 	sord_iter_free(ports);
543 
544 	// Get properties
545 	SordNode* statep     = sord_new_uri(world->world, USTR(LV2_STATE__state));
546 	SordNode* state_node = sord_get(model, node, statep, NULL, NULL);
547 	if (state_node) {
548 		SordIter* props = sord_search(model, state_node, 0, 0, 0);
549 		FOREACH_MATCH(props) {
550 			const SordNode* p   = sord_iter_get_node(props, SORD_PREDICATE);
551 			const SordNode* o   = sord_iter_get_node(props, SORD_OBJECT);
552 			const char*     key = (const char*)sord_node_get_string(p);
553 
554 			chunk.len = 0;
555 			lv2_atom_forge_set_sink(
556 				&forge, sratom_forge_sink, sratom_forge_deref, &chunk);
557 
558 			sratom_read(sratom, &forge, world->world, model, o);
559 			const LV2_Atom* atom  = (const LV2_Atom*)chunk.buf;
560 			uint32_t        flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE;
561 			Property        prop  = { NULL, 0, 0, 0, flags };
562 
563 			prop.key   = map->map(map->handle, key);
564 			prop.type  = atom->type;
565 			prop.size  = atom->size;
566 			prop.value = malloc(atom->size);
567 			memcpy(prop.value, LV2_ATOM_BODY_CONST(atom), atom->size);
568 			if (atom->type == forge.Path) {
569 				prop.flags = LV2_STATE_IS_POD;
570 			}
571 
572 			if (prop.value) {
573 				state->props.props = (Property*)realloc(
574 					state->props.props, (++state->props.n) * sizeof(Property));
575 				state->props.props[state->props.n - 1] = prop;
576 			}
577 		}
578 		sord_iter_free(props);
579 	}
580 	sord_node_free(world->world, state_node);
581 	sord_node_free(world->world, statep);
582 
583 	free((void*)chunk.buf);
584 	sratom_free(sratom);
585 
586 	if (state->props.props) {
587 		qsort(state->props.props, state->props.n, sizeof(Property), property_cmp);
588 	}
589 	if (state->values) {
590 		qsort(state->values, state->n_values, sizeof(PortValue), value_cmp);
591 	}
592 
593 	return state;
594 }
595 
596 LILV_API LilvState*
lilv_state_new_from_world(LilvWorld * world,const LV2_URID_Map * map,const LilvNode * node)597 lilv_state_new_from_world(LilvWorld*          world,
598                           const LV2_URID_Map* map,
599                           const LilvNode*     node)
600 {
601 	if (!lilv_node_is_uri(node) && !lilv_node_is_blank(node)) {
602 		LILV_ERRORF("Subject `%s' is not a URI or blank node.\n",
603 		            lilv_node_as_string(node));
604 		return NULL;
605 	}
606 
607 	return new_state_from_model(world, map, world->model, node->node, NULL);
608 }
609 
610 LILV_API LilvState*
lilv_state_new_from_file(LilvWorld * world,const LV2_URID_Map * map,const LilvNode * subject,const char * path)611 lilv_state_new_from_file(LilvWorld*          world,
612                          const LV2_URID_Map* map,
613                          const LilvNode*     subject,
614                          const char*         path)
615 {
616 	if (subject && !lilv_node_is_uri(subject)
617 	    && !lilv_node_is_blank(subject)) {
618 		LILV_ERRORF("Subject `%s' is not a URI or blank node.\n",
619 		            lilv_node_as_string(subject));
620 		return NULL;
621 	}
622 
623 	uint8_t*    abs_path = (uint8_t*)lilv_path_absolute(path);
624 	SerdNode    node     = serd_node_new_file_uri(abs_path, NULL, NULL, 0);
625 	SerdEnv*    env      = serd_env_new(&node);
626 	SordModel*  model    = sord_new(world->world, SORD_SPO, false);
627 	SerdReader* reader   = sord_new_reader(model, env, SERD_TURTLE, NULL);
628 
629 	serd_reader_read_file(reader, node.buf);
630 
631 	SordNode* subject_node = (subject)
632 		? subject->node
633 		: sord_node_from_serd_node(world->world, env, &node, NULL, NULL);
634 
635 	char* dirname   = lilv_dirname(path);
636 	char* real_path = lilv_realpath(dirname);
637 	LilvState* state = new_state_from_model(
638 		world, map, model, subject_node, real_path);
639 	free(dirname);
640 	free(real_path);
641 
642 	serd_node_free(&node);
643 	free(abs_path);
644 	serd_reader_free(reader);
645 	sord_free(model);
646 	serd_env_free(env);
647 	return state;
648 }
649 
650 static void
set_prefixes(SerdEnv * env)651 set_prefixes(SerdEnv* env)
652 {
653 #define SET_PSET(e, p, u) serd_env_set_prefix_from_strings(e, p, u)
654 	SET_PSET(env, USTR("atom"),  USTR(LV2_ATOM_PREFIX));
655 	SET_PSET(env, USTR("lv2"),   USTR(LV2_CORE_PREFIX));
656 	SET_PSET(env, USTR("pset"),  USTR(LV2_PRESETS_PREFIX));
657 	SET_PSET(env, USTR("rdf"),   USTR(LILV_NS_RDF));
658 	SET_PSET(env, USTR("rdfs"),  USTR(LILV_NS_RDFS));
659 	SET_PSET(env, USTR("state"), USTR(LV2_STATE_PREFIX));
660 	SET_PSET(env, USTR("xsd"),   USTR(LILV_NS_XSD));
661 }
662 
663 LILV_API LilvState*
lilv_state_new_from_string(LilvWorld * world,const LV2_URID_Map * map,const char * str)664 lilv_state_new_from_string(LilvWorld*          world,
665                            const LV2_URID_Map* map,
666                            const char*         str)
667 {
668 	if (!str) {
669 		return NULL;
670 	}
671 
672 	SerdNode    base   = SERD_NODE_NULL;
673 	SerdEnv*    env    = serd_env_new(&base);
674 	SordModel*  model  = sord_new(world->world, SORD_SPO|SORD_OPS, false);
675 	SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
676 
677 	set_prefixes(env);
678 	serd_reader_read_string(reader, USTR(str));
679 
680 	SordNode* o = sord_new_uri(world->world, USTR(LV2_PRESETS__Preset));
681 	SordNode* s = sord_get(model, NULL, world->uris.rdf_a, o, NULL);
682 
683 	LilvState* state = new_state_from_model(world, map, model, s, NULL);
684 
685 	sord_node_free(world->world, s);
686 	sord_node_free(world->world, o);
687 	serd_reader_free(reader);
688 	sord_free(model);
689 	serd_env_free(env);
690 
691 	return state;
692 }
693 
694 static SerdWriter*
ttl_writer(SerdSink sink,void * stream,const SerdNode * base,SerdEnv ** new_env)695 ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env)
696 {
697 	SerdURI base_uri = SERD_URI_NULL;
698 	if (base && base->buf) {
699 		serd_uri_parse(base->buf, &base_uri);
700 	}
701 
702 	SerdEnv* env = *new_env ? *new_env : serd_env_new(base);
703 	set_prefixes(env);
704 
705 	SerdWriter* writer = serd_writer_new(
706 		SERD_TURTLE,
707 		(SerdStyle)(SERD_STYLE_RESOLVED |
708 		            SERD_STYLE_ABBREVIATED|
709 		            SERD_STYLE_CURIED),
710 		env,
711 		&base_uri,
712 		sink,
713 		stream);
714 
715 	if (!*new_env) {
716 		*new_env = env;
717 	}
718 
719 	return writer;
720 }
721 
722 static SerdWriter*
ttl_file_writer(FILE * fd,const SerdNode * node,SerdEnv ** env)723 ttl_file_writer(FILE* fd, const SerdNode* node, SerdEnv** env)
724 {
725 	SerdWriter* writer = ttl_writer(serd_file_sink, fd, node, env);
726 
727 	fseek(fd, 0, SEEK_END);
728 	if (ftell(fd) == 0) {
729 		serd_env_foreach(*env, (SerdPrefixSink)serd_writer_set_prefix, writer);
730 	} else {
731 		fprintf(fd, "\n");
732 	}
733 
734 	return writer;
735 }
736 
737 static void
add_to_model(SordWorld * world,SerdEnv * env,SordModel * model,const SerdNode s,const SerdNode p,const SerdNode o)738 add_to_model(SordWorld*     world,
739              SerdEnv*       env,
740              SordModel*     model,
741              const SerdNode s,
742              const SerdNode p,
743              const SerdNode o)
744 {
745 	SordNode* ss = sord_node_from_serd_node(world, env, &s, NULL, NULL);
746 	SordNode* sp = sord_node_from_serd_node(world, env, &p, NULL, NULL);
747 	SordNode* so = sord_node_from_serd_node(world, env, &o, NULL, NULL);
748 
749 	SordQuad quad = { ss, sp, so, NULL };
750 	sord_add(model, quad);
751 
752 	sord_node_free(world, ss);
753 	sord_node_free(world, sp);
754 	sord_node_free(world, so);
755 }
756 
757 static void
remove_manifest_entry(SordWorld * world,SordModel * model,const char * subject)758 remove_manifest_entry(SordWorld* world, SordModel* model, const char* subject)
759 {
760 	SordNode* s = sord_new_uri(world, USTR(subject));
761 	SordIter* i = sord_search(model, s, NULL, NULL, NULL);
762 	while (!sord_iter_end(i)) {
763 		sord_erase(model, i);
764 	}
765 	sord_iter_free(i);
766 	sord_node_free(world, s);
767 }
768 
769 static int
add_state_to_manifest(LilvWorld * lworld,const LilvNode * plugin_uri,const char * manifest_path,const char * state_uri,const char * state_path)770 add_state_to_manifest(LilvWorld*      lworld,
771                       const LilvNode* plugin_uri,
772                       const char*     manifest_path,
773                       const char*     state_uri,
774                       const char*     state_path)
775 {
776 	SordWorld*  world    = lworld->world;
777 	SerdNode    manifest = serd_node_new_file_uri(USTR(manifest_path), 0, 0, 0);
778 	SerdNode    file     = serd_node_new_file_uri(USTR(state_path), 0, 0, 0);
779 	SerdEnv*    env      = serd_env_new(&manifest);
780 	SordModel*  model    = sord_new(world, SORD_SPO, false);
781 
782 	FILE* rfd = fopen(manifest_path, "r");
783 	if (rfd) {
784 		// Read manifest into model
785 		SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL);
786 		lilv_flock(rfd, true);
787 		serd_reader_read_file_handle(reader, rfd, manifest.buf);
788 		serd_reader_free(reader);
789 	}
790 
791 	// Choose state URI (use file URI if not given)
792 	if (!state_uri) {
793 		state_uri = (const char*)file.buf;
794 	}
795 
796 	// Remove any existing manifest entries for this state
797 	remove_manifest_entry(world, model, state_uri);
798 
799 	// Add manifest entry for this state to model
800 	SerdNode s = serd_node_from_string(SERD_URI, USTR(state_uri));
801 
802 	// <state> a pset:Preset
803 	add_to_model(world, env, model,
804 	             s,
805 	             serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")),
806 	             serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset)));
807 
808 	// <state> a pset:Preset
809 	add_to_model(world, env, model,
810 	             s,
811 	             serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")),
812 	             serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset)));
813 
814 	// <state> rdfs:seeAlso <file>
815 	add_to_model(world, env, model,
816 	             s,
817 	             serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "seeAlso")),
818 	             file);
819 
820 	// <state> lv2:appliesTo <plugin>
821 	add_to_model(world, env, model,
822 	             s,
823 	             serd_node_from_string(SERD_URI, USTR(LV2_CORE__appliesTo)),
824 	             serd_node_from_string(SERD_URI,
825 	                                   USTR(lilv_node_as_string(plugin_uri))));
826 
827 	// Write manifest model to file
828 	FILE* wfd = fopen(manifest_path, "w");
829 	if (wfd) {
830 		SerdWriter* writer = ttl_file_writer(wfd, &manifest, &env);
831 		sord_write(model, writer, NULL);
832 		serd_writer_free(writer);
833 		fclose(wfd);
834 	} else {
835 		LILV_ERRORF("Failed to open %s for writing (%s)\n",
836 		            manifest_path, strerror(errno));
837 	}
838 
839 	sord_free(model);
840 	serd_node_free(&file);
841 	serd_node_free(&manifest);
842 	serd_env_free(env);
843 
844 	if (rfd) {
845 		lilv_flock(rfd, false);
846 		fclose(rfd);
847 	}
848 
849 	return 0;
850 }
851 
852 static bool
link_exists(const char * path,void * data)853 link_exists(const char* path, void* data)
854 {
855 	const char* target = (const char*)data;
856 	if (!lilv_path_exists(path, NULL)) {
857 		return false;
858 	}
859 	char* real_path = lilv_realpath(path);
860 	bool  matches   = !strcmp(real_path, target);
861 	free(real_path);
862 	return !matches;
863 }
864 
865 static void
write_property_array(const LilvState * state,const PropertyArray * array,Sratom * sratom,uint32_t flags,const SerdNode * subject,LV2_URID_Unmap * unmap,const char * dir)866 write_property_array(const LilvState*     state,
867                      const PropertyArray* array,
868                      Sratom*              sratom,
869                      uint32_t             flags,
870                      const SerdNode*      subject,
871                      LV2_URID_Unmap*      unmap,
872                      const char*          dir)
873 {
874 	for (uint32_t i = 0; i < array->n; ++i) {
875 		Property*   prop = &array->props[i];
876 		const char* key  = unmap->unmap(unmap->handle, prop->key);
877 
878 		const SerdNode p = serd_node_from_string(SERD_URI, USTR(key));
879 		if (prop->type == state->atom_Path && !dir) {
880 			const char* path     = (const char*)prop->value;
881 			const char* abs_path = lilv_state_rel2abs(state, path);
882 			LILV_WARNF("Writing absolute path %s\n", abs_path);
883 			sratom_write(sratom, unmap, flags,
884 			             subject, &p, prop->type,
885 			             strlen(abs_path) + 1, abs_path);
886 		} else if (prop->flags & LV2_STATE_IS_POD ||
887 		           prop->type == state->atom_Path) {
888 			sratom_write(sratom, unmap, flags,
889 			             subject, &p, prop->type, prop->size, prop->value);
890 		} else {
891 			LILV_WARNF("Lost non-POD property <%s> on save\n", key);
892 		}
893 	}
894 }
895 
896 static int
lilv_state_write(LilvWorld * world,LV2_URID_Map * map,LV2_URID_Unmap * unmap,const LilvState * state,SerdWriter * writer,const char * uri,const char * dir)897 lilv_state_write(LilvWorld*       world,
898                  LV2_URID_Map*    map,
899                  LV2_URID_Unmap*  unmap,
900                  const LilvState* state,
901                  SerdWriter*      writer,
902                  const char*      uri,
903                  const char*      dir)
904 {
905 	SerdNode lv2_appliesTo = serd_node_from_string(
906 		SERD_CURIE, USTR("lv2:appliesTo"));
907 
908 	const SerdNode* plugin_uri = sord_node_to_serd_node(
909 		state->plugin_uri->node);
910 
911 	SerdNode subject = serd_node_from_string(SERD_URI, USTR(uri ? uri : ""));
912 
913 	// <subject> a pset:Preset
914 	SerdNode p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type"));
915 	SerdNode o = serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset));
916 	serd_writer_write_statement(writer, 0, NULL,
917 	                            &subject, &p, &o, NULL, NULL);
918 
919 	// <subject> lv2:appliesTo <http://example.org/plugin>
920 	serd_writer_write_statement(writer, 0, NULL,
921 	                            &subject,
922 	                            &lv2_appliesTo,
923 	                            plugin_uri, NULL, NULL);
924 
925 	// <subject> rdfs:label label
926 	if (state->label) {
927 		p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "label"));
928 		o = serd_node_from_string(SERD_LITERAL, USTR(state->label));
929 		serd_writer_write_statement(writer, 0,
930 		                            NULL, &subject, &p, &o, NULL, NULL);
931 	}
932 
933 	SerdEnv*        env  = serd_writer_get_env(writer);
934 	const SerdNode* base = serd_env_get_base_uri(env, NULL);
935 
936 	Sratom* sratom = sratom_new(map);
937 	sratom_set_sink(sratom, (const char*)base->buf,
938 	                (SerdStatementSink)serd_writer_write_statement,
939 	                (SerdEndSink)serd_writer_end_anon,
940 	                writer);
941 
942 	// Write metadata
943 	sratom_set_pretty_numbers(sratom, false);  // Use precise types
944 	write_property_array(state, &state->metadata, sratom, 0,
945 	                     &subject, unmap, dir);
946 
947 	// Write port values
948 	sratom_set_pretty_numbers(sratom, true);  // Use pretty numbers
949 	for (uint32_t i = 0; i < state->n_values; ++i) {
950 		PortValue* const value = &state->values[i];
951 
952 		const SerdNode port = serd_node_from_string(
953 			SERD_BLANK, USTR(value->symbol));
954 
955 		// <> lv2:port _:symbol
956 		p = serd_node_from_string(SERD_URI, USTR(LV2_CORE__port));
957 		serd_writer_write_statement(writer, SERD_ANON_O_BEGIN,
958 		                            NULL, &subject, &p, &port, NULL, NULL);
959 
960 		// _:symbol lv2:symbol "symbol"
961 		p = serd_node_from_string(SERD_URI, USTR(LV2_CORE__symbol));
962 		o = serd_node_from_string(SERD_LITERAL, USTR(value->symbol));
963 		serd_writer_write_statement(writer, SERD_ANON_CONT,
964 		                            NULL, &port, &p, &o, NULL, NULL);
965 
966 		// _:symbol pset:value value
967 		p = serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__value));
968 		sratom_write(sratom, unmap, SERD_ANON_CONT, &port, &p,
969 		             value->type, value->size, value->value);
970 
971 		serd_writer_end_anon(writer, &port);
972 	}
973 
974 	// Write properties
975 	const SerdNode body = serd_node_from_string(SERD_BLANK, USTR("body"));
976 	if (state->props.n > 0) {
977 		p = serd_node_from_string(SERD_URI, USTR(LV2_STATE__state));
978 		serd_writer_write_statement(writer, SERD_ANON_O_BEGIN, NULL,
979 		                            &subject, &p, &body, NULL, NULL);
980 	}
981 	sratom_set_pretty_numbers(sratom, false);  // Use precise types
982 	write_property_array(state, &state->props, sratom, SERD_ANON_CONT,
983 	                     &body, unmap, dir);
984 
985 	if (state->props.n > 0) {
986 		serd_writer_end_anon(writer, &body);
987 	}
988 
989 	sratom_free(sratom);
990 	return 0;
991 }
992 
993 static void
lilv_state_make_links(const LilvState * state,const char * dir)994 lilv_state_make_links(const LilvState* state, const char* dir)
995 {
996 	// Create symlinks to files
997 	for (ZixTreeIter* i = zix_tree_begin(state->abs2rel);
998 	     i != zix_tree_end(state->abs2rel);
999 	     i = zix_tree_iter_next(i)) {
1000 		const PathMap* pm = (const PathMap*)zix_tree_get(i);
1001 
1002 		char* path = lilv_path_join(dir, pm->rel);
1003 		if (lilv_path_is_child(pm->abs, state->copy_dir)
1004 		    && strcmp(state->copy_dir, dir)) {
1005 			// Link directly to snapshot in the copy directory
1006 			char* target = lilv_path_relative_to(pm->abs, dir);
1007 			lilv_symlink(target, path);
1008 			free(target);
1009 		} else if (!lilv_path_is_child(pm->abs, dir)) {
1010 			const char* link_dir = state->link_dir ? state->link_dir : dir;
1011 			char*       pat      = lilv_path_join(link_dir, pm->rel);
1012 			if (!strcmp(dir, link_dir)) {
1013 				// Link directory is save directory, make link at exact path
1014 				remove(pat);
1015 				lilv_symlink(pm->abs, pat);
1016 			} else {
1017 				// Make a link in the link directory to external file
1018 				char* lpath = lilv_find_free_path(pat, link_exists, pm->abs);
1019 				if (!lilv_path_exists(lpath, NULL)) {
1020 					lilv_symlink(pm->abs, lpath);
1021 				}
1022 
1023 				// Make a link in the save directory to the external link
1024 				char* target = lilv_path_relative_to(lpath, dir);
1025 				lilv_symlink(target, path);
1026 				free(target);
1027 				free(lpath);
1028 			}
1029 			free(pat);
1030 		}
1031 		free(path);
1032 	}
1033 }
1034 
1035 LILV_API int
lilv_state_save(LilvWorld * world,LV2_URID_Map * map,LV2_URID_Unmap * unmap,const LilvState * state,const char * uri,const char * dir,const char * filename)1036 lilv_state_save(LilvWorld*       world,
1037                 LV2_URID_Map*    map,
1038                 LV2_URID_Unmap*  unmap,
1039                 const LilvState* state,
1040                 const char*      uri,
1041                 const char*      dir,
1042                 const char*      filename)
1043 {
1044 	if (!filename || !dir || lilv_mkdir_p(dir)) {
1045 		return 1;
1046 	}
1047 
1048 	char*       abs_dir = absolute_dir(dir);
1049 	char* const path    = lilv_path_join(abs_dir, filename);
1050 	FILE*       fd      = fopen(path, "w");
1051 	if (!fd) {
1052 		LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno));
1053 		free(abs_dir);
1054 		free(path);
1055 		return 4;
1056 	}
1057 
1058 	// Create symlinks to files if necessary
1059 	lilv_state_make_links(state, abs_dir);
1060 
1061 	// Write state to Turtle file
1062 	SerdNode    file = serd_node_new_file_uri(USTR(path), NULL, NULL, false);
1063 	SerdNode    node = uri ? serd_node_from_string(SERD_URI, USTR(uri)) : file;
1064 	SerdEnv*    env  = NULL;
1065 	SerdWriter* ttl  = ttl_file_writer(fd, &file, &env);
1066 	int         ret  = lilv_state_write(
1067 		world, map, unmap, state, ttl, (const char*)node.buf, dir);
1068 
1069 	// Set saved dir and uri (FIXME: const violation)
1070 	SerdNode dir_uri = serd_node_new_file_uri(USTR(abs_dir), NULL, NULL, false);
1071 	free(state->dir);
1072 	lilv_node_free(state->uri);
1073 	((LilvState*)state)->dir = (char*)dir_uri.buf;
1074 	((LilvState*)state)->uri = lilv_new_uri(world, (const char*)node.buf);
1075 
1076 	serd_node_free(&file);
1077 	serd_writer_free(ttl);
1078 	serd_env_free(env);
1079 	fclose(fd);
1080 
1081 	// Add entry to manifest
1082 	char* const manifest = lilv_path_join(abs_dir, "manifest.ttl");
1083 	add_state_to_manifest(world, state->plugin_uri, manifest, uri, path);
1084 
1085 	free(manifest);
1086 	free(abs_dir);
1087 	free(path);
1088 	return ret;
1089 }
1090 
1091 LILV_API char*
lilv_state_to_string(LilvWorld * world,LV2_URID_Map * map,LV2_URID_Unmap * unmap,const LilvState * state,const char * uri,const char * base_uri)1092 lilv_state_to_string(LilvWorld*       world,
1093                      LV2_URID_Map*    map,
1094                      LV2_URID_Unmap*  unmap,
1095                      const LilvState* state,
1096                      const char*      uri,
1097                      const char*      base_uri)
1098 {
1099 	if (!uri) {
1100 		LILV_ERROR("Attempt to serialise state with no URI\n");
1101 		return NULL;
1102 	}
1103 
1104 	SerdChunk   chunk  = { NULL, 0 };
1105 	SerdEnv*    env    = NULL;
1106 	SerdNode    base   = serd_node_from_string(SERD_URI, USTR(base_uri));
1107 	SerdWriter* writer = ttl_writer(serd_chunk_sink, &chunk, &base, &env);
1108 
1109 	lilv_state_write(world, map, unmap, state, writer, uri, NULL);
1110 
1111 	serd_writer_free(writer);
1112 	serd_env_free(env);
1113 	return (char*)serd_chunk_sink_finish(&chunk);
1114 }
1115 
1116 LILV_API int
lilv_state_delete(LilvWorld * world,const LilvState * state)1117 lilv_state_delete(LilvWorld*       world,
1118                   const LilvState* state)
1119 {
1120 	if (!state->dir || !state->uri) {
1121 		LILV_ERROR("Attempt to delete unsaved state\n");
1122 		return -1;
1123 	}
1124 
1125 	LilvNode*  bundle        = lilv_new_uri(world, state->dir);
1126 	LilvNode*  manifest      = lilv_world_get_manifest_uri(world, bundle);
1127 	char*      manifest_path = lilv_node_get_path(manifest, NULL);
1128 	SordModel* model         = sord_new(world->world, SORD_SPO, false);
1129 
1130 	{
1131 		// Read manifest into temporary local model
1132 		SerdEnv*    env = serd_env_new(sord_node_to_serd_node(manifest->node));
1133 		SerdReader* ttl = sord_new_reader(model, env, SERD_TURTLE, NULL);
1134 		serd_reader_read_file(ttl, USTR(manifest_path));
1135 		serd_reader_free(ttl);
1136 		serd_env_free(env);
1137 	}
1138 
1139 	SordNode* file = sord_get(
1140 		model, state->uri->node, world->uris.rdfs_seeAlso, NULL, NULL);
1141 	if (file) {
1142 		// Remove state file
1143 		char* path = lilv_file_uri_parse(
1144 			(const char*)sord_node_get_string(file), NULL);
1145 		if (unlink(path)) {
1146 			LILV_ERRORF("Failed to remove %s (%s)\n", path, strerror(errno));
1147 		}
1148 		lilv_free(path);
1149 	}
1150 
1151 	// Remove any existing manifest entries for this state
1152 	remove_manifest_entry(
1153 		world->world, model, lilv_node_as_string(state->uri));
1154 	remove_manifest_entry(
1155 		world->world, world->model, lilv_node_as_string(state->uri));
1156 
1157 	// Drop bundle from model
1158 	lilv_world_unload_bundle(world, bundle);
1159 
1160 	if (sord_num_quads(model) == 0) {
1161 		// Manifest is empty, attempt to remove bundle entirely
1162 		if (unlink(manifest_path)) {
1163 			LILV_ERRORF("Failed to remove %s (%s)\n",
1164 			            manifest_path, strerror(errno));
1165 		}
1166 		char* dir_path = lilv_file_uri_parse(state->dir, NULL);
1167 		if (rmdir(dir_path)) {
1168 			LILV_ERRORF("Failed to remove %s (%s)\n",
1169 			            dir_path, strerror(errno));
1170 		}
1171 		lilv_free(dir_path);
1172 	} else {
1173 		// Still something in the manifest, reload bundle
1174 		lilv_world_load_bundle(world, bundle);
1175 	}
1176 
1177 	sord_free(model);
1178 	lilv_free(manifest_path);
1179 	lilv_node_free(manifest);
1180 	lilv_node_free(bundle);
1181 
1182 	return 0;
1183 }
1184 
1185 static void
free_property_array(LilvState * state,PropertyArray * array)1186 free_property_array(LilvState* state, PropertyArray* array)
1187 {
1188 	for (uint32_t i = 0; i < array->n; ++i) {
1189 		Property* prop = &array->props[i];
1190 		if ((prop->flags & LV2_STATE_IS_POD) ||
1191 		    prop->type == state->atom_Path) {
1192 			free(prop->value);
1193 		}
1194 	}
1195 	free(array->props);
1196 }
1197 
1198 LILV_API void
lilv_state_free(LilvState * state)1199 lilv_state_free(LilvState* state)
1200 {
1201 	if (state) {
1202 		free_property_array(state, &state->props);
1203 		free_property_array(state, &state->metadata);
1204 		for (uint32_t i = 0; i < state->n_values; ++i) {
1205 			free(state->values[i].value);
1206 			free(state->values[i].symbol);
1207 		}
1208 		lilv_node_free(state->plugin_uri);
1209 		lilv_node_free(state->uri);
1210 		zix_tree_free(state->abs2rel);
1211 		zix_tree_free(state->rel2abs);
1212 		free(state->values);
1213 		free(state->label);
1214 		free(state->dir);
1215 		free(state->file_dir);
1216 		free(state->copy_dir);
1217 		free(state->link_dir);
1218 		free(state);
1219 	}
1220 }
1221 
1222 LILV_API bool
lilv_state_equals(const LilvState * a,const LilvState * b)1223 lilv_state_equals(const LilvState* a, const LilvState* b)
1224 {
1225 	if (!lilv_node_equals(a->plugin_uri, b->plugin_uri)
1226 	    || (a->label && !b->label)
1227 	    || (b->label && !a->label)
1228 	    || (a->label && b->label && strcmp(a->label, b->label))
1229 	    || a->props.n != b->props.n
1230 	    || a->n_values != b->n_values) {
1231 		return false;
1232 	}
1233 
1234 	for (uint32_t i = 0; i < a->n_values; ++i) {
1235 		PortValue* const av = &a->values[i];
1236 		PortValue* const bv = &b->values[i];
1237 		if (av->size != bv->size || av->type != bv->type
1238 		    || strcmp(av->symbol, bv->symbol)
1239 		    || memcmp(av->value, bv->value, av->size)) {
1240 			return false;
1241 		}
1242 	}
1243 
1244 	for (uint32_t i = 0; i < a->props.n; ++i) {
1245 		Property* const ap = &a->props.props[i];
1246 		Property* const bp = &b->props.props[i];
1247 		if (ap->key != bp->key
1248 		    || ap->type != bp->type
1249 		    || ap->flags != bp->flags) {
1250 			return false;
1251 		} else if (ap->type == a->atom_Path) {
1252 			if (!lilv_file_equals(lilv_state_rel2abs(a, (char*)ap->value),
1253 			                      lilv_state_rel2abs(b, (char*)bp->value))) {
1254 				return false;
1255 			}
1256 		} else if (ap->size != bp->size
1257 		           || memcmp(ap->value, bp->value, ap->size)) {
1258 			return false;
1259 		}
1260 	}
1261 
1262 	return true;
1263 }
1264 
1265 LILV_API unsigned
lilv_state_get_num_properties(const LilvState * state)1266 lilv_state_get_num_properties(const LilvState* state)
1267 {
1268 	return state->props.n;
1269 }
1270 
1271 LILV_API const LilvNode*
lilv_state_get_plugin_uri(const LilvState * state)1272 lilv_state_get_plugin_uri(const LilvState* state)
1273 {
1274 	return state->plugin_uri;
1275 }
1276 
1277 LILV_API const LilvNode*
lilv_state_get_uri(const LilvState * state)1278 lilv_state_get_uri(const LilvState* state)
1279 {
1280 	return state->uri;
1281 }
1282 
1283 LILV_API const char*
lilv_state_get_label(const LilvState * state)1284 lilv_state_get_label(const LilvState* state)
1285 {
1286 	return state->label;
1287 }
1288 
1289 LILV_API void
lilv_state_set_label(LilvState * state,const char * label)1290 lilv_state_set_label(LilvState* state, const char* label)
1291 {
1292 	const size_t len = strlen(label);
1293 	state->label = (char*)realloc(state->label, len + 1);
1294 	memcpy(state->label, label, len + 1);
1295 }
1296 
1297 LILV_API int
lilv_state_set_metadata(LilvState * state,uint32_t key,const void * value,size_t size,uint32_t type,uint32_t flags)1298 lilv_state_set_metadata(LilvState*  state,
1299                         uint32_t    key,
1300                         const void* value,
1301                         size_t      size,
1302                         uint32_t    type,
1303                         uint32_t    flags)
1304 {
1305 	append_property(state, &state->metadata, key, value, size, type, flags);
1306 	return LV2_STATE_SUCCESS;
1307 }
1308