static char* abstract_path(LV2_State_Map_Path_Handle handle, const char* abs_path) { LilvState* state = (LilvState*)handle; char* path = NULL; char* real_path = lilv_realpath(abs_path); const PathMap key = { (char*)real_path, NULL }; ZixTreeIter* iter = NULL; if (abs_path[0] == '\0') { return lilv_strdup(abs_path); } else if (!zix_tree_find(state->abs2rel, &key, &iter)) { // Already mapped path in a previous call PathMap* pm = (PathMap*)zix_tree_get(iter); free(real_path); return lilv_strdup(pm->rel); } else if (lilv_path_is_child(real_path, state->dir)) { // File in state directory (loaded, or created by plugin during save path = lilv_path_relative_to(real_path, state->dir); } else if (lilv_path_is_child(real_path, state->file_dir)) { // File created by plugin earlier path = lilv_path_relative_to(real_path, state->file_dir); if (state->copy_dir) { if (!lilv_path_exists(state->copy_dir, NULL)) { lilv_mkdir_p(state->copy_dir); } char* cpath = lilv_path_join(state->copy_dir, path); char* copy = lilv_get_latest_copy(real_path, cpath); if (!copy || !lilv_file_equals(real_path, copy)) { // No recent enough copy, make a new one copy = lilv_find_free_path(cpath, lilv_path_exists, NULL); lilv_copy_file(real_path, copy); } free(real_path); free(cpath); // Refer to the latest copy in plugin state real_path = copy; } } else { // New path outside state directory const char* slash = strrchr(real_path, '/'); const char* name = slash ? (slash + 1) : real_path; // Find a free name in the (virtual) state directory path = lilv_find_free_path(name, lilv_state_has_path, state); } // Add record to path mapping PathMap* pm = (PathMap*)malloc(sizeof(PathMap)); pm->abs = real_path; pm->rel = lilv_strdup(path); zix_tree_insert(state->abs2rel, pm, NULL); zix_tree_insert(state->rel2abs, pm, NULL); return path; }
LilvUI* lilv_ui_new(LilvWorld* world, LilvNode* uri, LilvNode* type_uri, LilvNode* binary_uri) { assert(uri); assert(type_uri); assert(binary_uri); LilvUI* ui = (LilvUI*)malloc(sizeof(LilvUI)); ui->world = world; ui->uri = uri; ui->binary_uri = binary_uri; // FIXME: kludge char* bundle = lilv_strdup(lilv_node_as_string(ui->binary_uri)); char* last_slash = strrchr(bundle, '/') + 1; *last_slash = '\0'; ui->bundle_uri = lilv_new_uri(world, bundle); free(bundle); ui->classes = lilv_nodes_new(); zix_tree_insert((ZixTree*)ui->classes, type_uri, NULL); return ui; }
char* lilv_dirname(const char* path) { const char* s = path + strlen(path) - 1; // Last character for (; s > path && lilv_is_dir_sep(*s); --s) {} // Last non-slash for (; s > path && !lilv_is_dir_sep(*s); --s) {} // Last internal slash for (; s > path && lilv_is_dir_sep(*s); --s) {} // Skip duplicates if (s == path) { // Hit beginning return lilv_is_dir_sep(*s) ? lilv_strdup("/") : lilv_strdup("."); } else { // Pointing to the last character of the result (inclusive) char* dirname = (char*)malloc(s - path + 2); memcpy(dirname, path, s - path + 1); dirname[s - path + 1] = '\0'; return dirname; } }
static char* absolute_path(LV2_State_Map_Path_Handle handle, const char* state_path) { LilvState* state = (LilvState*)handle; char* path = NULL; if (lilv_path_is_absolute(state_path)) { // Absolute path, return identical path path = lilv_strdup(state_path); } else if (state->dir) { // Relative path inside state directory path = lilv_path_join(state->dir, state_path); } else { // State has not been saved, unmap path = lilv_strdup(lilv_state_rel2abs(state, state_path)); } return path; }
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) { if (!filename || !dir || lilv_mkdir_p(dir)) { return 1; } char* abs_dir = absolute_dir(dir); char* const path = lilv_path_join(abs_dir, filename); FILE* fd = fopen(path, "w"); if (!fd) { LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno)); free(abs_dir); free(path); 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); serd_node_free(&file); serd_writer_free(writer); serd_env_free(env); fclose(fd); char* const manifest = lilv_path_join(abs_dir, "manifest.ttl"); add_state_to_manifest(state->plugin_uri, manifest, uri, path); free(manifest); free(abs_dir); free(path); return ret; }
char* lilv_realpath(const char* path) { #ifdef _WIN32 char* out = (char*)malloc(MAX_PATH); GetFullPathName(path, MAX_PATH, out, NULL); return out; #else char* real_path = realpath(path, NULL); return real_path ? real_path : lilv_strdup(path); #endif }
char* lilv_path_absolute(const char* path) { if (lilv_path_is_absolute(path)) { return lilv_strdup(path); } else { char* cwd = getcwd(NULL, 0); char* abs_path = lilv_path_join(cwd, path); free(cwd); return abs_path; } }
char* lilv_path_join(const char* a, const char* b) { if (!a) { return lilv_strdup(b); } const size_t a_len = strlen(a); const size_t b_len = b ? strlen(b) : 0; const size_t pre_len = a_len - (lilv_is_dir_sep(a[a_len - 1]) ? 1 : 0); char* path = (char*)calloc(1, a_len + b_len + 2); memcpy(path, a, pre_len); path[pre_len] = '/'; if (b) { memcpy(path + pre_len + 1, b + (lilv_is_dir_sep(b[0]) ? 1 : 0), lilv_is_dir_sep(b[0]) ? b_len - 1 : b_len); } return path; }
static PortValue* append_port_value(LilvState* state, const char* port_symbol, const void* value, uint32_t size, uint32_t type) { if (value) { state->values = (PortValue*)realloc( state->values, (++state->num_values) * sizeof(PortValue)); PortValue* pv = &state->values[state->num_values - 1]; pv->symbol = lilv_strdup(port_symbol); pv->value = malloc(size); pv->size = size; pv->type = type; memcpy(pv->value, value, size); return pv; } return NULL; }
int lilv_mkdir_p(const char* dir_path) { char* path = lilv_strdup(dir_path); const size_t path_len = strlen(path); for (size_t i = 1; i <= path_len; ++i) { if (path[i] == LILV_DIR_SEP[0] || path[i] == '\0') { path[i] = '\0'; if (mkdir(path, 0755) && errno != EEXIST) { LILV_ERRORF("Failed to create %s (%s)\n", path, strerror(errno)); free(path); return 1; } path[i] = LILV_DIR_SEP[0]; } } free(path); return 0; }
char* lilv_path_relative_to(const char* path, const char* base) { const size_t path_len = strlen(path); const size_t base_len = strlen(base); const size_t min_len = (path_len < base_len) ? path_len : base_len; // Find the last separator common to both paths size_t last_shared_sep = 0; for (size_t i = 0; i < min_len && path[i] == base[i]; ++i) { if (lilv_is_dir_sep(path[i])) { last_shared_sep = i; } } if (last_shared_sep == 0) { // No common components, return path return lilv_strdup(path); } // Find the number of up references ("..") required size_t up = 0; for (size_t i = last_shared_sep + 1; i < base_len; ++i) { if (lilv_is_dir_sep(base[i])) { ++up; } } // Write up references const size_t suffix_len = path_len - last_shared_sep; char* rel = (char*)calloc(1, suffix_len + (up * 3) + 1); for (size_t i = 0; i < up; ++i) { memcpy(rel + (i * 3), "../", 3); } // Write suffix memcpy(rel + (up * 3), path + last_shared_sep + 1, suffix_len); return rel; }
LILV_API char* lilv_node_get_turtle_token(const LilvNode* value) { const char* str = (const char*)sord_node_get_string(value->node); size_t len = 0; char* result = NULL; SerdNode node; switch (value->type) { case LILV_VALUE_URI: len = strlen(str) + 3; result = (char*)calloc(len, 1); snprintf(result, len, "<%s>", str); break; case LILV_VALUE_BLANK: len = strlen(str) + 3; result = (char*)calloc(len, 1); snprintf(result, len, "_:%s", str); break; case LILV_VALUE_STRING: case LILV_VALUE_BOOL: case LILV_VALUE_BLOB: result = lilv_strdup(str); break; case LILV_VALUE_INT: node = serd_node_new_integer(value->val.int_val); result = (char*)node.buf; break; case LILV_VALUE_FLOAT: node = serd_node_new_decimal(value->val.float_val, 8); result = (char*)node.buf; break; } return result; }
static LilvState* new_state_from_model(LilvWorld* world, LV2_URID_Map* map, SordModel* model, const SordNode* node, const char* dir) { LilvState* const state = (LilvState*)malloc(sizeof(LilvState)); memset(state, '\0', sizeof(LilvState)); state->dir = lilv_strdup(dir); state->atom_Path = map->map(map->handle, LV2_ATOM__Path); // Get the plugin URI this state applies to SordIter* i = sord_search(model, node, world->uris.lv2_appliesTo, 0, 0); if (i) { const SordNode* object = sord_iter_get_node(i, SORD_OBJECT); const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH); state->plugin_uri = lilv_node_new_from_node(world, object); if (!state->dir && graph) { state->dir = lilv_strdup((const char*)sord_node_get_string(graph)); } sord_iter_free(i); } else if (sord_ask(model, node, world->uris.rdf_a, world->uris.lv2_Plugin, 0)) { // Loading plugin description as state (default state) state->plugin_uri = lilv_node_new_from_node(world, node); } else { LILV_ERRORF("State %s missing lv2:appliesTo property\n", sord_node_get_string(node)); } // Get the state label i = sord_search(model, node, world->uris.rdfs_label, NULL, NULL); if (i) { const SordNode* object = sord_iter_get_node(i, SORD_OBJECT); const SordNode* graph = sord_iter_get_node(i, SORD_GRAPH); state->label = lilv_strdup((const char*)sord_node_get_string(object)); if (!state->dir) { state->dir = lilv_strdup((const char*)sord_node_get_string(graph)); } sord_iter_free(i); } Sratom* sratom = sratom_new(map); SerdChunk chunk = { NULL, 0 }; LV2_Atom_Forge forge; lv2_atom_forge_init(&forge, map); lv2_atom_forge_set_sink( &forge, sratom_forge_sink, sratom_forge_deref, &chunk); // Get port values SordIter* ports = sord_search(model, node, world->uris.lv2_port, 0, 0); FOREACH_MATCH(ports) { const SordNode* port = sord_iter_get_node(ports, SORD_OBJECT); SordNode* label = sord_get(model, port, world->uris.rdfs_label, 0, 0); SordNode* symbol = sord_get(model, port, world->uris.lv2_symbol, 0, 0); SordNode* value = sord_get(model, port, world->uris.pset_value, 0, 0); if (!value) { value = sord_get(model, port, world->uris.lv2_default, 0, 0); } if (!symbol) { LILV_ERRORF("State `%s' port missing symbol.\n", sord_node_get_string(node)); } else if (value) { chunk.len = 0; sratom_read(sratom, &forge, world->world, model, value); LV2_Atom* atom = (LV2_Atom*)chunk.buf; append_port_value(state, (const char*)sord_node_get_string(symbol), LV2_ATOM_BODY(atom), atom->size, atom->type); if (label) { lilv_state_set_label(state, (const char*)sord_node_get_string(label)); } } sord_node_free(world->world, value); sord_node_free(world->world, symbol); sord_node_free(world->world, label); } sord_iter_free(ports); // Get properties SordNode* statep = sord_new_uri(world->world, USTR(LV2_STATE__state)); SordNode* state_node = sord_get(model, node, statep, NULL, NULL); if (state_node) { SordIter* props = sord_search(model, state_node, 0, 0, 0); FOREACH_MATCH(props) { const SordNode* p = sord_iter_get_node(props, SORD_PREDICATE); const SordNode* o = sord_iter_get_node(props, SORD_OBJECT); chunk.len = 0; lv2_atom_forge_set_sink( &forge, sratom_forge_sink, sratom_forge_deref, &chunk); sratom_read(sratom, &forge, world->world, model, o); LV2_Atom* atom = (LV2_Atom*)chunk.buf; uint32_t flags = LV2_STATE_IS_POD|LV2_STATE_IS_PORTABLE; 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.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; } if (prop.value) { state->props = (Property*)realloc( state->props, (++state->num_props) * sizeof(Property)); state->props[state->num_props - 1] = prop; } } sord_iter_free(props); } sord_node_free(world->world, state_node); sord_node_free(world->world, statep); free((void*)chunk.buf); sratom_free(sratom); qsort(state->props, state->num_props, sizeof(Property), property_cmp); qsort(state->values, state->num_values, sizeof(PortValue), value_cmp); return state; }