diff options
author | David Robillard <d@drobilla.net> | 2015-03-07 08:42:56 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2015-03-07 08:42:56 +0000 |
commit | 1b420b15b3c88ebbcd6789f78b69bd284e0a98f7 (patch) | |
tree | 2871ea66f0651b2a43ff98c6f107e1654ee3aeed /src/state.c | |
parent | cd320a7ccd392a5da2df31ec5edc9b07db5befab (diff) | |
download | lilv-1b420b15b3c88ebbcd6789f78b69bd284e0a98f7.tar.gz lilv-1b420b15b3c88ebbcd6789f78b69bd284e0a98f7.tar.bz2 lilv-1b420b15b3c88ebbcd6789f78b69bd284e0a98f7.zip |
Add support for state deletion.
Add lilv_node_get_path().
Add lilv_state_get_uri().
Add lilv_state_delete().
Fix creation of duplicate manifest entries when saving state.
git-svn-id: http://svn.drobilla.net/lad/trunk/lilv@5617 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/state.c')
-rw-r--r-- | src/state.c | 238 |
1 files changed, 191 insertions, 47 deletions
diff --git a/src/state.c b/src/state.c index f75a2bb..3205dcc 100644 --- a/src/state.c +++ b/src/state.c @@ -51,6 +51,7 @@ typedef struct { struct LilvStateImpl { LilvNode* plugin_uri; ///< Plugin URI + LilvNode* uri; ///< State/preset URI char* dir; ///< Save directory (if saved) char* file_dir; ///< Directory for files created by plugin char* copy_dir; ///< Directory for snapshots of external files @@ -337,8 +338,7 @@ lilv_state_new_from_instance(const LilvPlugin* plugin, { const LV2_Feature** sfeatures = NULL; LilvWorld* const world = plugin->world; - LilvState* const state = (LilvState*)malloc(sizeof(LilvState)); - memset(state, '\0', sizeof(LilvState)); + LilvState* const state = (LilvState*)calloc(1, sizeof(LilvState)); state->plugin_uri = lilv_node_duplicate(lilv_plugin_get_uri(plugin)); state->abs2rel = zix_tree_new(false, abs_cmp, NULL, path_rel_free); state->rel2abs = zix_tree_new(false, rel_cmp, NULL, NULL); @@ -458,10 +458,10 @@ new_state_from_model(LilvWorld* world, } // Allocate state - LilvState* const state = (LilvState*)malloc(sizeof(LilvState)); - memset(state, '\0', sizeof(LilvState)); + LilvState* const state = (LilvState*)calloc(1, sizeof(LilvState)); state->dir = lilv_strdup(dir); state->atom_Path = map->map(map->handle, LV2_ATOM__Path); + state->uri = lilv_node_new_from_node(world, node); // Get the plugin URI this state applies to SordIter* i = sord_search(model, node, world->uris.lv2_appliesTo, 0, 0); @@ -691,7 +691,7 @@ ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env) serd_uri_parse(base->buf, &base_uri); } - SerdEnv* env = serd_env_new(base); + SerdEnv* env = *new_env ? *new_env : serd_env_new(base); set_prefixes(env); SerdWriter* writer = serd_writer_new( @@ -702,7 +702,10 @@ ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env) sink, stream); - *new_env = env; + if (!*new_env) { + *new_env = env; + } + return writer; } @@ -721,53 +724,117 @@ ttl_file_writer(FILE* fd, const SerdNode* node, SerdEnv** env) return writer; } +static void +add_to_model(SordWorld* world, + SerdEnv* env, + SordModel* model, + const SerdNode s, + const SerdNode p, + const SerdNode o) +{ + SordNode* ss = sord_node_from_serd_node(world, env, &s, NULL, NULL); + SordNode* sp = sord_node_from_serd_node(world, env, &p, NULL, NULL); + SordNode* so = sord_node_from_serd_node(world, env, &o, NULL, NULL); + + SordQuad quad = { ss, sp, so, NULL }; + sord_add(model, quad); + + sord_node_free(world, ss); + sord_node_free(world, sp); + sord_node_free(world, so); +} + +static void +remove_manifest_entry(SordWorld* world, SordModel* model, const char* subject) +{ + SordNode* s = sord_new_uri(world, USTR(subject)); + SordIter* i = sord_search(model, s, NULL, NULL, NULL); + while (!sord_iter_end(i)) { + sord_erase(model, i); + } + sord_iter_free(i); + sord_node_free(world, s); +} + static int -add_state_to_manifest(const LilvNode* plugin_uri, +add_state_to_manifest(LilvWorld* lworld, + const LilvNode* plugin_uri, const char* manifest_path, const char* state_uri, const char* state_path) { - FILE* fd = fopen(manifest_path, "a"); - if (!fd) { - LILV_ERRORF("Failed to open %s (%s)\n", - manifest_path, strerror(errno)); - return 4; - } - - lilv_flock(fd, true); - - SerdNode file = serd_node_new_file_uri(USTR(state_path), 0, 0, 0); + SordWorld* world = lworld->world; SerdNode manifest = serd_node_new_file_uri(USTR(manifest_path), 0, 0, 0); - SerdEnv* env = NULL; - SerdWriter* writer = ttl_file_writer(fd, &manifest, &env); + SerdNode file = serd_node_new_file_uri(USTR(state_path), 0, 0, 0); + SerdEnv* env = serd_env_new(&manifest); + SordModel* model = sord_new(world, SORD_SPO, false); + + FILE* rfd = fopen(manifest_path, "r"); + if (rfd) { + // Read manifest into model + SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); + lilv_flock(rfd, true); + serd_reader_read_file_handle(reader, rfd, manifest.buf); + serd_reader_free(reader); + } + // Choose state URI (use file URI if not given) if (!state_uri) { state_uri = (const char*)file.buf; } - // <state> a pset:Preset + // Remove any existing manifest entries for this state + remove_manifest_entry(world, model, state_uri); + + // Add manifest entry for this state to model SerdNode s = serd_node_from_string(SERD_URI, USTR(state_uri)); - SerdNode p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")); - SerdNode o = serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset)); - serd_writer_write_statement(writer, 0, NULL, &s, &p, &o, NULL, NULL); + + // <state> a pset:Preset + add_to_model(world, env, model, + s, + serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")), + serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset))); + + // <state> a pset:Preset + add_to_model(world, env, model, + s, + serd_node_from_string(SERD_URI, USTR(LILV_NS_RDF "type")), + serd_node_from_string(SERD_URI, USTR(LV2_PRESETS__Preset))); // <state> rdfs:seeAlso <file> - p = serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "seeAlso")); - serd_writer_write_statement(writer, 0, NULL, &s, &p, &file, NULL, NULL); + add_to_model(world, env, model, + s, + serd_node_from_string(SERD_URI, USTR(LILV_NS_RDFS "seeAlso")), + file); // <state> lv2:appliesTo <plugin> - p = serd_node_from_string(SERD_URI, USTR(LV2_CORE__appliesTo)); - o = serd_node_from_string( - SERD_URI, USTR(lilv_node_as_string(plugin_uri))); - serd_writer_write_statement(writer, 0, NULL, &s, &p, &o, NULL, NULL); + add_to_model(world, env, model, + s, + serd_node_from_string(SERD_URI, USTR(LV2_CORE__appliesTo)), + serd_node_from_string(SERD_URI, + USTR(lilv_node_as_string(plugin_uri)))); + + // Write manifest model to file + FILE* wfd = fopen(manifest_path, "w"); + if (wfd) { + SerdWriter* writer = ttl_file_writer(wfd, &manifest, &env); + sord_write(model, writer, NULL); + serd_writer_free(writer); + fclose(wfd); + } else { + LILV_ERRORF("Failed to open %s for writing (%s)\n", + manifest_path, strerror(errno)); + } + sord_free(model); serd_node_free(&file); serd_node_free(&manifest); - serd_writer_free(writer); serd_env_free(env); - lilv_flock(fd, false); - fclose(fd); + if (rfd) { + lilv_flock(rfd, false); + fclose(rfd); + } return 0; } @@ -960,31 +1027,32 @@ lilv_state_save(LilvWorld* world, return 4; } - // FIXME: make parameter non-const? - if (state->dir && strcmp(state->dir, abs_dir)) { - free(state->dir); - ((LilvState*)state)->dir = lilv_strdup(abs_dir); - } - // Create symlinks to files if necessary lilv_state_make_links(state, abs_dir); // Write state to Turtle file - SerdNode file = serd_node_new_file_uri(USTR(path), NULL, NULL, false); - SerdEnv* env = NULL; - SerdWriter* writer = ttl_file_writer(fd, &file, &env); - - SerdNode node = uri ? serd_node_from_string(SERD_URI, USTR(uri)) : file; - int ret = lilv_state_write( - world, map, unmap, state, writer, (const char*)node.buf, dir); + SerdNode file = serd_node_new_file_uri(USTR(path), NULL, NULL, false); + SerdNode node = uri ? serd_node_from_string(SERD_URI, USTR(uri)) : file; + SerdEnv* env = NULL; + SerdWriter* ttl = ttl_file_writer(fd, &file, &env); + int ret = lilv_state_write( + world, map, unmap, state, ttl, (const char*)node.buf, dir); + + // Set saved dir and uri (FIXME: const violation) + SerdNode dir_uri = serd_node_new_file_uri(USTR(abs_dir), NULL, NULL, false); + free(state->dir); + lilv_node_free(state->uri); + ((LilvState*)state)->dir = (char*)dir_uri.buf; + ((LilvState*)state)->uri = lilv_new_uri(world, (const char*)node.buf); serd_node_free(&file); - serd_writer_free(writer); + serd_writer_free(ttl); serd_env_free(env); fclose(fd); + // Add entry to manifest char* const manifest = lilv_path_join(abs_dir, "manifest.ttl"); - add_state_to_manifest(state->plugin_uri, manifest, uri, path); + add_state_to_manifest(world, state->plugin_uri, manifest, uri, path); free(manifest); free(abs_dir); @@ -1017,6 +1085,75 @@ lilv_state_to_string(LilvWorld* world, return (char*)serd_chunk_sink_finish(&chunk); } +LILV_API int +lilv_state_delete(LilvWorld* world, + const LilvState* state) +{ + if (!state->dir || !state->uri) { + LILV_ERROR("Attempt to delete unsaved state\n"); + return -1; + } + + LilvNode* bundle = lilv_new_uri(world, state->dir); + LilvNode* manifest = lilv_world_get_manifest_uri(world, bundle); + char* manifest_path = lilv_node_get_path(manifest, NULL); + SordModel* model = sord_new(world->world, SORD_SPO, false); + + { + // Read manifest into model + SerdEnv* env = serd_env_new(sord_node_to_serd_node(manifest->node)); + SerdReader* ttl = sord_new_reader(model, env, SERD_TURTLE, NULL); + serd_reader_read_file(ttl, USTR(manifest_path)); + serd_reader_free(ttl); + serd_env_free(env); + } + + SordNode* file = sord_get( + model, state->uri->node, world->uris.rdfs_seeAlso, NULL, NULL); + if (file) { + // Remove state file + char* file_path = lilv_file_uri_parse( + (const char*)sord_node_get_string(file), NULL); + if (unlink(file_path)) { + LILV_ERRORF("Failed to remove %s (%s)\n", file_path, strerror(errno)); + } + free(file_path); + } + + // Remove any existing manifest entries for this state + remove_manifest_entry( + world->world, model, lilv_node_as_string(state->uri)); + remove_manifest_entry( + world->world, world->model, lilv_node_as_string(state->uri)); + + // Drop bundle from model + lilv_world_unload_bundle(world, bundle); + + if (sord_num_quads(model) == 0) { + // Manifest is empty, attempt to remove bundle entirely + if (unlink(manifest_path)) { + LILV_ERRORF("Failed to remove %s (%s)\n", + manifest_path, strerror(errno)); + } + char* dir_path = lilv_file_uri_parse(state->dir, NULL); + if (rmdir(dir_path)) { + LILV_ERRORF("Failed to remove %s (%s)\n", + dir_path, strerror(errno)); + } + free(dir_path); + } else { + // Still something in the manifest, reload bundle + lilv_world_load_bundle(world, bundle); + } + + sord_free(model); + free(manifest_path); + lilv_node_free(manifest); + lilv_node_free(bundle); + + return 0; +} + LILV_API void lilv_state_free(LilvState* state) { @@ -1029,6 +1166,7 @@ lilv_state_free(LilvState* state) free(state->values[i].symbol); } lilv_node_free(state->plugin_uri); + lilv_node_free(state->uri); zix_tree_free(state->abs2rel); zix_tree_free(state->rel2abs); free(state->props); @@ -1097,6 +1235,12 @@ lilv_state_get_plugin_uri(const LilvState* state) return state->plugin_uri; } +LILV_API const LilvNode* +lilv_state_get_uri(const LilvState* state) +{ + return state->uri; +} + LILV_API const char* lilv_state_get_label(const LilvState* state) { |