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