diff options
-rw-r--r-- | src/sratom/sratom.c | 69 | ||||
-rw-r--r-- | src/sratom/sratom.h | 5 | ||||
-rw-r--r-- | src/state.c | 99 | ||||
-rw-r--r-- | test/lilv_test.c | 44 |
4 files changed, 135 insertions, 82 deletions
diff --git a/src/sratom/sratom.c b/src/sratom/sratom.c index e3238e1..2117705 100644 --- a/src/sratom/sratom.c +++ b/src/sratom/sratom.c @@ -15,6 +15,7 @@ */ #include <assert.h> +#include <ctype.h> #include <stdlib.h> #include <string.h> @@ -146,8 +147,16 @@ start_object(Sratom* sratom, } } +static bool +path_is_absolute(const char* path) +{ + return (path[0] == '/' + || (isalpha(path[0]) && path[1] == ':' + && (path[2] == '/' || path[2] == '\\'))); +} + SRATOM_API -void +int sratom_write(Sratom* sratom, LV2_URID_Unmap* unmap, SerdWriter* writer, @@ -168,7 +177,7 @@ sratom_write(Sratom* sratom, SerdNode language = SERD_NODE_NULL; bool new_node = false; if (type_urid == 0 && size == 0) { - object = serd_node_from_string(SERD_BLANK, USTR("null")); + object = serd_node_from_string(SERD_URI, USTR(NS_RDF "nil")); } else if (type_urid == sratom->forge.String) { object = serd_node_from_string(SERD_LITERAL, (const uint8_t*)body); } else if (type_urid == sratom->forge.Literal) { @@ -195,8 +204,25 @@ sratom_write(Sratom* sratom, object = serd_node_from_string(SERD_URI, str); } else if (type_urid == sratom->forge.Path) { const uint8_t* str = USTR(body); - object = serd_node_from_string(SERD_LITERAL, str); - datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path)); + if (path_is_absolute((const char*)str)) { + new_node = true; + object = serd_node_new_uri_from_path(str, NULL, NULL); + } else { + SerdEnv* env = serd_writer_get_env(writer); + SerdURI base_uri = SERD_URI_NULL; + const SerdNode* base = serd_env_get_base_uri(env, &base_uri); + if (strncmp((const char*)base->buf, "file://", 7)) { + fprintf(stderr, "warning: Relative path but base is not a file URI.\n"); + fprintf(stderr, "warning: Writing ambiguous atom:Path literal.\n"); + object = serd_node_from_string(SERD_LITERAL, str); + datatype = serd_node_from_string(SERD_URI, USTR(LV2_ATOM__Path)); + } else { + new_node = true; + SerdNode rel = serd_node_new_uri_from_path(str, NULL, NULL); + object = serd_node_new_uri_from_node(&rel, &base_uri, NULL); + serd_node_free(&rel); + } + } } else if (type_urid == sratom->forge.URI) { const uint8_t* str = USTR(body); object = serd_node_from_string(SERD_URI, str); @@ -331,21 +357,25 @@ sratom_write(Sratom* sratom, if (new_node) { serd_node_free(&object); } + + return 0; } SRATOM_API char* sratom_to_turtle(Sratom* sratom, LV2_URID_Unmap* unmap, + const char* base_uri, const SerdNode* subject, const SerdNode* predicate, uint32_t type, uint32_t size, const void* body) { - SerdURI base_uri = SERD_URI_NULL; - SerdEnv* env = serd_env_new(NULL); - SerdChunk str = { NULL, 0 }; + SerdURI buri = SERD_URI_NULL; + SerdNode base = serd_node_new_uri_from_string(USTR(base_uri), NULL, &buri); + SerdEnv* env = serd_env_new(&base); + SerdChunk str = { NULL, 0 }; serd_env_set_prefix_from_strings(env, USTR("midi"), NS_MIDI); serd_env_set_prefix_from_strings(env, USTR("atom"), @@ -358,7 +388,7 @@ sratom_to_turtle(Sratom* sratom, SerdWriter* writer = serd_writer_new( SERD_TURTLE, SERD_STYLE_ABBREVIATED|SERD_STYLE_RESOLVED|SERD_STYLE_CURIED, - env, &base_uri, serd_chunk_sink, &str); + env, &buri, serd_chunk_sink, &str); // Write @prefix directives serd_env_foreach(env, @@ -485,10 +515,14 @@ read_node(Sratom* sratom, lv2_atom_forge_string(forge, (const uint8_t*)str, len); } } else if (sord_node_get_type(node) == SORD_URI) { - if (serd_uri_string_has_scheme((const uint8_t*)str)) { - lv2_atom_forge_urid(forge, map->map(map->handle, str)); + if (!strcmp(str, (const char*)NS_RDF "nil")) { + lv2_atom_forge_atom(forge, 0, 0); + } else if (!strncmp(str, "file://", 7)) { + uint8_t* path = serd_file_uri_parse((const uint8_t*)str, NULL); + lv2_atom_forge_path(forge, path, strlen((const char*)path)); + free(path); } else { - lv2_atom_forge_uri(forge, (const uint8_t*)str, len); + lv2_atom_forge_urid(forge, map->map(map->handle, str)); } } else { const SordNode* type = get_object(model, node, sratom->nodes.rdf_type); @@ -613,16 +647,17 @@ sratom_forge_deref(LV2_Atom_Forge_Sink_Handle handle, LV2_Atom_Forge_Ref ref) SRATOM_API LV2_Atom* sratom_from_turtle(Sratom* sratom, + const char* base_uri, const SerdNode* subject, const SerdNode* predicate, const char* str) { - SerdChunk out = { NULL, 0 }; - SerdNode base_uri_node = SERD_NODE_NULL; - SordWorld* world = sord_world_new(); - SordModel* model = sord_new(world, SORD_SPO, false); - SerdEnv* env = serd_env_new(&base_uri_node); - SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); + SerdChunk out = { NULL, 0 }; + SerdNode base = serd_node_new_uri_from_string(USTR(base_uri), NULL, NULL); + SordWorld* world = sord_world_new(); + SordModel* model = sord_new(world, SORD_SPO, false); + SerdEnv* env = serd_env_new(&base); + SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); if (!serd_reader_read_string(reader, (const uint8_t*)str)) { SordNode* s = sord_node_from_serd_node(world, env, subject, 0, 0); diff --git a/src/sratom/sratom.h b/src/sratom/sratom.h index 6398135..b1ef071 100644 --- a/src/sratom/sratom.h +++ b/src/sratom/sratom.h @@ -77,9 +77,10 @@ sratom_free(Sratom* sratom); /** Write an Atom to RDF. The resulting serialised atom is written to @p writer. + @return 0 on success, or a non-zero error code otherwise. */ SRATOM_API -void +int sratom_write(Sratom* sratom, LV2_URID_Unmap* unmap, SerdWriter* writer, @@ -110,6 +111,7 @@ SRATOM_API char* sratom_to_turtle(Sratom* sratom, LV2_URID_Unmap* unmap, + const char* base_uri, const SerdNode* subject, const SerdNode* predicate, uint32_t type, @@ -123,6 +125,7 @@ sratom_to_turtle(Sratom* sratom, SRATOM_API LV2_Atom* sratom_from_turtle(Sratom* sratom, + const char* base_uri, const SerdNode* subject, const SerdNode* predicate, const char* str); diff --git a/src/state.c b/src/state.c index 0baa76c..266f7d3 100644 --- a/src/state.c +++ b/src/state.c @@ -464,8 +464,7 @@ new_state_from_model(LilvWorld* world, state->atom_Path = map->map(map->handle, LV2_ATOM__Path); // Get the plugin URI this state applies to - const SordQuad upat = { - node, world->uris.lv2_appliesTo, NULL, NULL }; + const SordQuad upat = { node, world->uris.lv2_appliesTo, NULL, NULL }; SordIter* i = sord_find(model, upat); if (i) { const SordNode* object = sord_iter_get_node(i, SORD_OBJECT); @@ -561,17 +560,13 @@ new_state_from_model(LilvWorld* world, #endif Property prop = { NULL, 0, 0, 0, flags }; - prop.key = map->map(map->handle, (const char*)sord_node_get_string(p)); - - prop.type = atom->type; + prop.key = map->map(map->handle, (const char*)sord_node_get_string(p)); + prop.type = atom->type; + prop.size = atom->size; + prop.value = malloc(atom->size); + memcpy(prop.value, LV2_ATOM_BODY(atom), atom->size); if (atom->type == forge.Path) { prop.flags = LV2_STATE_IS_PORTABLE; - prop.value = lilv_path_join(state->dir, LV2_ATOM_BODY(atom)); - prop.size = strlen(prop.value) + 1; - } else { - prop.size = atom->size; - prop.value = malloc(atom->size); - memcpy(prop.value, LV2_ATOM_BODY(atom), atom->size); } if (prop.value) { @@ -587,6 +582,7 @@ new_state_from_model(LilvWorld* world, sord_node_free(world->world, atom_path_node); #endif + free((void*)chunk.buf); sratom_free(sratom); qsort(state->props, state->num_props, sizeof(Property), property_cmp); @@ -627,17 +623,17 @@ lilv_state_new_from_file(LilvWorld* world, return NULL; } - uint8_t* uri = (uint8_t*)lilv_strjoin("file://", path, NULL); - SerdNode base = serd_node_from_string(SERD_URI, uri); - SerdEnv* env = serd_env_new(&base); - SordModel* model = sord_new(world->world, SORD_SPO, false); - SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); + uint8_t* abs_path = (uint8_t*)lilv_path_absolute(path); + SerdNode node = serd_node_new_uri_from_path(abs_path, NULL, NULL); + SerdEnv* env = serd_env_new(&node); + SordModel* model = sord_new(world->world, SORD_SPO, false); + SerdReader* reader = sord_new_reader(model, env, SERD_TURTLE, NULL); - serd_reader_read_file(reader, uri); + serd_reader_read_file(reader, node.buf); SordNode* subject_node = (subject) ? subject->val.uri_val - : sord_node_from_serd_node(world->world, env, &base, NULL, NULL); + : sord_node_from_serd_node(world->world, env, &node, NULL, NULL); char* dirname = lilv_dirname(path); char* real_path = lilv_realpath(dirname); @@ -646,10 +642,11 @@ lilv_state_new_from_file(LilvWorld* world, free(dirname); free(real_path); + serd_node_free(&node); + free(abs_path); serd_reader_free(reader); sord_free(model); serd_env_free(env); - free(uri); return state; } @@ -687,6 +684,7 @@ lilv_state_new_from_string(LilvWorld* world, LilvState* state = new_state_from_model(world, map, model, s, NULL); + sord_iter_free(i); serd_reader_free(reader); sord_free(model); serd_env_free(env); @@ -695,35 +693,32 @@ lilv_state_new_from_string(LilvWorld* world, } static SerdWriter* -ttl_writer(SerdSink sink, void* stream, const uint8_t* uri, SerdEnv** new_env) +ttl_writer(SerdSink sink, void* stream, const SerdNode* base, SerdEnv** new_env) { - SerdURI base_uri = SERD_URI_NULL; - SerdNode base = SERD_NODE_NULL; - if (uri) { - base = serd_node_new_uri_from_string(uri, NULL, &base_uri); + SerdURI base_uri = SERD_URI_NULL; + if (base) { + serd_uri_parse(base->buf, &base_uri); } - - SerdEnv* env = serd_env_new(&base); + + SerdEnv* env = serd_env_new(base); set_prefixes(env); SerdWriter* writer = serd_writer_new( SERD_TURTLE, - (SerdStyle)(SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED), + (SerdStyle)(SERD_STYLE_RESOLVED|SERD_STYLE_ABBREVIATED|SERD_STYLE_CURIED), env, &base_uri, sink, stream); - serd_node_free(&base); - *new_env = env; return writer; } static SerdWriter* -ttl_file_writer(FILE* fd, const uint8_t* uri, SerdEnv** env) +ttl_file_writer(FILE* fd, const SerdNode* node, SerdEnv** env) { - SerdWriter* writer = ttl_writer(serd_file_sink, fd, uri, env); + SerdWriter* writer = ttl_writer(serd_file_sink, fd, node, env); fseek(fd, 0, SEEK_END); if (ftell(fd) == 0) { @@ -739,7 +734,7 @@ static int add_state_to_manifest(const LilvNode* plugin_uri, const char* manifest_path, const char* state_uri, - const char* state_file_uri) + const char* state_path) { FILE* fd = fopen((char*)manifest_path, "a"); if (!fd) { @@ -748,29 +743,20 @@ add_state_to_manifest(const LilvNode* plugin_uri, return 4; } - // Make path relative if it is in the same directory as manifest - const char* last_slash = strrchr(state_file_uri, '/'); - if (last_slash) { - const size_t len = last_slash - state_file_uri; - if (!strncmp(manifest_path, state_file_uri, len)) { - state_file_uri = last_slash + 1; - } - } + SerdNode file = serd_node_new_uri_from_path(USTR(state_path), 0, 0); + SerdNode manifest = serd_node_new_uri_from_path(USTR(manifest_path), 0, 0); lilv_flock(fd, true); - char* const manifest_uri = lilv_strjoin("file://", manifest_path, NULL); - SerdEnv* env = NULL; - SerdWriter* writer = ttl_file_writer(fd, USTR(manifest_uri), &env); + SerdEnv* env = NULL; + SerdWriter* writer = ttl_file_writer(fd, &manifest, &env); if (!state_uri) { - state_uri = state_file_uri; + state_uri = (const char*)file.buf; } - SerdNode s = serd_node_from_string(SERD_URI, USTR(state_uri)); - SerdNode file = serd_node_from_string(SERD_URI, USTR(state_file_uri)); - // <state> a pset:Preset + 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(NS_PSET "Preset")); serd_writer_write_statement(writer, 0, NULL, &s, &p, &o, NULL, NULL); @@ -785,9 +771,10 @@ add_state_to_manifest(const LilvNode* plugin_uri, SERD_URI, USTR(lilv_node_as_string(plugin_uri))); serd_writer_write_statement(writer, 0, NULL, &s, &p, &o, NULL, NULL); + serd_node_free(&file); + serd_node_free(&manifest); serd_writer_free(writer); serd_env_free(env); - free(manifest_uri); lilv_flock(fd, false); fclose(fd); @@ -885,7 +872,7 @@ lilv_state_write(LilvWorld* world, Property* prop = &state->props[i]; const char* key = unmap->unmap(unmap->handle, prop->key); - p = serd_node_from_string(SERD_URI, (const uint8_t*)key); + p = serd_node_from_string(SERD_URI, USTR(key)); if (prop->type == state->atom_Path && !dir) { const char* abs_path = lilv_state_rel2abs(state, prop->value); sratom_write(sratom, unmap, writer, SERD_ANON_CONT, @@ -900,6 +887,7 @@ lilv_state_write(LilvWorld* world, serd_writer_end_anon(writer, &state_node); } + sratom_free(sratom); return 0; } @@ -983,11 +971,19 @@ lilv_state_save(LilvWorld* world, lilv_state_make_links(state, abs_dir); // Write state to Turtle file + SerdNode file = serd_node_new_uri_from_path(USTR(path), NULL, NULL); SerdEnv* env = NULL; - SerdWriter* writer = ttl_file_writer(fd, USTR(path), &env); + SerdWriter* writer = ttl_file_writer(fd, &file, &env); + + SerdNode node = SERD_NODE_NULL; + if (!uri) { + node = serd_node_new_uri_from_path(USTR(path), NULL, NULL); + uri = (const char*)node.buf; + } int ret = lilv_state_write(world, map, unmap, state, writer, uri, dir); + serd_node_free(&file); serd_writer_free(writer); serd_env_free(env); fclose(fd); @@ -996,6 +992,7 @@ lilv_state_save(LilvWorld* world, add_state_to_manifest(state->plugin_uri, manifest, uri, path); } + serd_node_free(&node); free(abs_dir); free(manifest); free(path); @@ -1041,6 +1038,8 @@ lilv_state_free(LilvState* state) free(state->label); free(state->dir); free(state->file_dir); + free(state->copy_dir); + free(state->link_dir); free(state); } } diff --git a/test/lilv_test.c b/test/lilv_test.c index 1031685..6d5ba9d 100644 --- a/test/lilv_test.c +++ b/test/lilv_test.c @@ -617,6 +617,7 @@ test_plugin(void) "http://lv2plug.in/ns/lv2core#latency"); LilvPort* latency_port = lilv_plugin_get_port_by_parameter( plug, out_class, lv2_latency); + lilv_node_free(lv2_latency); TEST_ASSERT(latency_port); TEST_ASSERT(lilv_port_get_index(plug, latency_port) == 2); @@ -1126,6 +1127,7 @@ map_uri(LV2_URID_Map_Handle handle, } } + assert(serd_uri_string_has_scheme((const uint8_t*)uri)); uris = (char**)realloc(uris, ++n_uris * sizeof(char*)); uris[n_uris - 1] = lilv_strdup(uri); return n_uris; @@ -1141,7 +1143,7 @@ unmap_uri(LV2_URID_Map_Handle handle, return NULL; } -static const char* temp_dir = NULL; +static char* temp_dir = NULL; char* lilv_make_path(LV2_State_Make_Path_Handle handle, @@ -1153,11 +1155,15 @@ lilv_make_path(LV2_State_Make_Path_Handle handle, int test_state(void) { + uint8_t* abs_bundle = (uint8_t*)lilv_path_absolute(LILV_TEST_BUNDLE); + SerdNode bundle = serd_node_new_uri_from_path(abs_bundle, 0, 0); LilvWorld* world = lilv_world_new(); - LilvNode* bundle_uri = lilv_new_uri(world, LILV_TEST_BUNDLE); + LilvNode* bundle_uri = lilv_new_uri(world, (const char*)bundle.buf); LilvNode* plugin_uri = lilv_new_uri(world, "http://example.org/lilv-test-plugin"); lilv_world_load_bundle(world, bundle_uri); + free(abs_bundle); + serd_node_free(&bundle); const LilvPlugins* plugins = lilv_world_get_all_plugins(world); const LilvPlugin* plugin = lilv_plugins_get_by_uri(plugins, plugin_uri); @@ -1187,9 +1193,9 @@ test_state(void) temp_dir = lilv_realpath("temp"); const char* file_dir = NULL; - const char* copy_dir = NULL; - const char* link_dir = NULL; - const char* save_dir = NULL; + char* copy_dir = NULL; + char* link_dir = NULL; + char* save_dir = NULL; // Get instance state state LilvState* state = lilv_state_new_from_instance( @@ -1215,6 +1221,7 @@ test_state(void) // Ensure they are equal TEST_ASSERT(lilv_state_equals(state, from_str)); + free(state1_str); const LilvNode* state_plugin_uri = lilv_state_get_plugin_uri(state); TEST_ASSERT(lilv_node_equals(state_plugin_uri, plugin_uri)); @@ -1248,35 +1255,41 @@ test_state(void) // Save state to a directory int ret = lilv_state_save(world, &map, &unmap, state, NULL, - "./state.lv2", "state.ttl"); + "state.lv2", "state.ttl"); TEST_ASSERT(!ret); // Load state from directory LilvState* state5 = lilv_state_new_from_file(world, &map, NULL, - "./state.lv2/state.ttl"); + "state.lv2/state.ttl"); TEST_ASSERT(lilv_state_equals(state, state5)); // Round trip accuracy // Save state with URI to a directory const char* state_uri = "http://example.org/state"; ret = lilv_state_save(world, &map, &unmap, state, state_uri, - "./state6.lv2", "state6.ttl"); + "state6.lv2", "state6.ttl"); TEST_ASSERT(!ret); // Load default bundle into world and load state from it - LilvNode* test_state_bundle = lilv_new_uri(world, "./state6.lv2/"); + uint8_t* state6_path = (uint8_t*)lilv_path_absolute("state6.lv2/"); + SerdNode state6_uri = serd_node_new_uri_from_path(state6_path, 0, 0); + LilvNode* test_state_bundle = lilv_new_uri(world, (const char*)state6_uri.buf); LilvNode* test_state_node = lilv_new_uri(world, state_uri); lilv_world_load_bundle(world, test_state_bundle); lilv_world_load_resource(world, test_state_node); + serd_node_free(&state6_uri); + free(state6_path); LilvState* state6 = lilv_state_new_from_world(world, &map, test_state_node); TEST_ASSERT(lilv_state_equals(state, state6)); // Round trip accuracy + lilv_node_free(test_state_bundle); + lilv_node_free(test_state_node); unsetenv("LV2_STATE_BUNDLE"); // Make directories and test files support mkdir("temp", 0700); - file_dir = temp_dir = lilv_realpath("temp"); + file_dir = temp_dir; mkdir("files", 0700); copy_dir = lilv_realpath("files"); mkdir("links", 0700); @@ -1342,12 +1355,12 @@ test_state(void) // Save state to a (different) directory again ret = lilv_state_save(world, &map, &unmap, fstate, NULL, - "./fstate6.lv2", "fstate6.ttl"); + "fstate6.lv2", "fstate6.ttl"); TEST_ASSERT(!ret); // Reload it and ensure it's identical to the other loaded version LilvState* fstate6 = lilv_state_new_from_file(world, &map, NULL, - "./fstate6.lv2/fstate6.ttl"); + "fstate6.lv2/fstate6.ttl"); TEST_ASSERT(lilv_state_equals(fstate4, fstate6)); // Run, changing rec file (without changing size) @@ -1362,12 +1375,12 @@ test_state(void) // Save the changed state to a (different) directory again ret = lilv_state_save(world, &map, &unmap, fstate7, NULL, - "./fstate7.lv2", "fstate7.ttl"); + "fstate7.lv2", "fstate7.ttl"); TEST_ASSERT(!ret); // Reload it and ensure it's changed LilvState* fstate72 = lilv_state_new_from_file(world, &map, NULL, - "./fstate7.lv2/fstate7.ttl"); + "fstate7.lv2/fstate7.ttl"); TEST_ASSERT(lilv_state_equals(fstate72, fstate7)); TEST_ASSERT(!lilv_state_equals(fstate6, fstate72)); @@ -1402,6 +1415,9 @@ test_state(void) lilv_node_free(plugin_uri); lilv_node_free(bundle_uri); lilv_world_free(world); + free(link_dir); + free(copy_dir); + free(temp_dir); cleanup_uris(); return 1; |