summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-01-28 01:37:28 +0000
committerDavid Robillard <d@drobilla.net>2012-01-28 01:37:28 +0000
commitce2c5cc2036154c2b3ce8850c35e34c0bffeef1f (patch)
tree4dea45a4376110c75fdb8d1c6b9bf3b87b002635 /src
parente4d3f6140cc167580ec43de6fc3ea4562aaba63e (diff)
downloadlilv-ce2c5cc2036154c2b3ce8850c35e34c0bffeef1f.tar.gz
lilv-ce2c5cc2036154c2b3ce8850c35e34c0bffeef1f.tar.bz2
lilv-ce2c5cc2036154c2b3ce8850c35e34c0bffeef1f.zip
Update state interface to support multiple state snapshots with shared files.
git-svn-id: http://svn.drobilla.net/lad/trunk/lilv@3967 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src')
-rw-r--r--src/lilv_internal.h4
-rw-r--r--src/state.c277
-rw-r--r--src/util.c115
3 files changed, 260 insertions, 136 deletions
diff --git a/src/lilv_internal.h b/src/lilv_internal.h
index 0049d01..18aaed5 100644
--- a/src/lilv_internal.h
+++ b/src/lilv_internal.h
@@ -349,8 +349,9 @@ char* lilv_expand(const char* path);
char* lilv_dirname(const char* path);
int lilv_copy_file(const char* src, const char* dst);
bool lilv_path_exists(const char* path, void* ignored);
+char* lilv_path_absolute(const char* path);
bool lilv_path_is_absolute(const char* path);
-char* lilv_get_latest_copy(const char* path);
+char* lilv_get_latest_copy(const char* path, const char* copy_path);
char* lilv_path_relative_to(const char* path, const char* base);
bool lilv_path_is_child(const char* path, const char* dir);
int lilv_flock(FILE* file, bool lock);
@@ -358,6 +359,7 @@ char* lilv_realpath(const char* path);
int lilv_symlink(const char* oldpath, const char* newpath);
int lilv_mkdir_p(const char* path);
char* lilv_path_join(const char* a, const char* b);
+bool lilv_file_equals(const char* a_path, const char* b_path);
char*
lilv_find_free_path(const char* in_path,
diff --git a/src/state.c b/src/state.c
index 270b9bb..53479ff 100644
--- a/src/state.c
+++ b/src/state.c
@@ -52,7 +52,9 @@ typedef struct {
struct LilvStateImpl {
LilvNode* plugin_uri;
char* dir; ///< Save directory (if saved)
- char* file_dir; ///< Directory of files created by plugin
+ char* file_dir;
+ char* copy_dir;
+ char* link_dir;
char* label;
ZixTree* abs2rel; ///< PathMap sorted by abs
ZixTree* rel2abs; ///< PathMap sorted by rel
@@ -101,12 +103,15 @@ append_port_value(LilvState* state,
const char* port_symbol,
LilvNode* 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 = value;
- return pv;
+ 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 = value;
+ return pv;
+ }
+ return NULL;
}
static const char*
@@ -180,34 +185,57 @@ lilv_state_has_path(const char* path, void* state)
}
static char*
+make_path(LV2_State_Make_Path_Handle handle,
+ const char* path)
+{
+ LilvState* state = (LilvState*)handle;
+ if (!lilv_path_exists(state->dir, NULL)) {
+ lilv_mkdir_p(state->dir);
+ }
+
+ return lilv_path_join(state->dir, path);
+}
+
+static char*
abstract_path(LV2_State_Map_Path_Handle handle,
const char* absolute_path)
{
- LilvState* state = (LilvState*)handle;
- const size_t file_dir_len = state->file_dir ? strlen(state->file_dir) : 0;
- char* path = NULL;
- char* real_path = lilv_realpath(absolute_path);
- const PathMap key = { (char*)real_path, NULL };
- ZixTreeIter* iter = NULL;
-
- if (!zix_tree_find(state->abs2rel, &key, &iter)) {
+ LilvState* state = (LilvState*)handle;
+ char* path = NULL;
+ char* real_path = lilv_realpath(absolute_path);
+ const PathMap key = { (char*)real_path, NULL };
+ ZixTreeIter* iter = NULL;
+
+ if (absolute_path[0] == '\0') {
+ return lilv_strdup(absolute_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->file_dir)) {
- // File created by plugin
- char* copy = lilv_get_latest_copy(real_path);
- if (!copy) {
- // No recent enough copy, make a new one
- copy = lilv_find_free_path(real_path, lilv_path_exists, NULL);
- lilv_copy_file(real_path, copy);
- }
- free(real_path);
- real_path = copy;
+ } else if (lilv_path_is_child(absolute_path, state->dir)) {
+ // File in state directory (loaded, or created by plugin during save
+ path = lilv_path_relative_to(absolute_path, state->dir);
+ } else if (lilv_path_is_child(absolute_path, state->file_dir)) {
+ // File created by plugin earlier
+ path = lilv_path_relative_to(absolute_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(absolute_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(absolute_path, copy);
+ }
+ free(real_path);
+ free(cpath);
- // Refer to the latest copy in plugin state
- path = lilv_strdup(copy + file_dir_len + 1);
+ // Refer to the latest copy in plugin state
+ real_path = copy;
+ }
} else {
// New path outside state directory
const char* slash = strrchr(real_path, '/');
@@ -236,9 +264,12 @@ absolute_path(LV2_State_Map_Path_Handle handle,
if (lilv_path_is_absolute(abstract_path)) {
// Absolute path, return identical path
path = lilv_strdup(abstract_path);
- } else {
+ } else if (state->dir) {
// Relative path inside state directory
path = lilv_path_join(state->dir, abstract_path);
+ } else {
+ // State has not been saved, unmap
+ path = lilv_strdup(lilv_state_rel2abs(state, abstract_path));
}
return path;
@@ -248,49 +279,78 @@ absolute_path(LV2_State_Map_Path_Handle handle,
/** Return a new features array which is @c feature added to @c features. */
const LV2_Feature**
-add_feature(const LV2_Feature *const * features, const LV2_Feature* feature)
+add_features(const LV2_Feature *const * features,
+ const LV2_Feature* map, const LV2_Feature* make)
{
size_t n_features = 0;
for (; features && features[n_features]; ++n_features) {}
const LV2_Feature** ret = (const LV2_Feature**)malloc(
- (n_features + 2) * sizeof(LV2_Feature*));
+ (n_features + 3) * sizeof(LV2_Feature*));
- ret[0] = feature;
if (features) {
- memcpy(ret + 1, features, n_features * sizeof(LV2_Feature*));
+ memcpy(ret, features, n_features * sizeof(LV2_Feature*));
}
- ret[n_features + 1] = NULL;
+
+ for (size_t i = 0; i < n_features; ++i) {
+ if (map && !strcmp(ret[i]->URI, map->URI)) {
+ ret[i] = map;
+ map = NULL;
+ } else if (make && !strcmp(ret[i]->URI, make->URI)) {
+ ret[i] = make;
+ make = NULL;
+ }
+ }
+
+ ret[n_features] = map;
+ ret[n_features + 1] = make;
+ ret[n_features + 2] = NULL;
return ret;
}
+static char*
+absolute_dir(const char* path, bool resolve)
+{
+ char* abs_path = resolve ? lilv_realpath(path) : lilv_path_absolute(path);
+ char* base = lilv_path_join(abs_path, NULL);
+ free(abs_path);
+ return base;
+}
+
LILV_API
LilvState*
lilv_state_new_from_instance(const LilvPlugin* plugin,
LilvInstance* instance,
LV2_URID_Map* map,
- const char* dir,
+ const char* file_dir,
+ const char* copy_dir,
+ const char* link_dir,
+ const char* save_dir,
LilvGetPortValueFunc get_value,
void* user_data,
uint32_t flags,
const LV2_Feature *const * features)
{
- const LV2_Feature** local_features = NULL;
- LilvWorld* const world = plugin->world;
- LilvState* const state = (LilvState*)malloc(sizeof(LilvState));
+ const LV2_Feature** sfeatures = NULL;
+ LilvWorld* const world = plugin->world;
+ LilvState* const state = (LilvState*)malloc(sizeof(LilvState));
memset(state, '\0', 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);
- state->file_dir = dir ? lilv_realpath(dir) : NULL;
+ state->file_dir = file_dir ? absolute_dir(file_dir, false) : NULL;
+ state->copy_dir = copy_dir ? absolute_dir(copy_dir, false) : NULL;
+ state->link_dir = link_dir ? absolute_dir(link_dir, false) : NULL;
+ state->dir = save_dir ? absolute_dir(save_dir, false) : NULL;
#ifdef HAVE_LV2_STATE
state->state_Path = map->map(map->handle, LV2_STATE_PATH_URI);
- if (dir) {
- LV2_State_Map_Path map_path = { state, abstract_path, absolute_path };
- LV2_Feature feature = { LV2_STATE_MAP_PATH_URI, &map_path };
- features = local_features = add_feature(features, &feature);
- }
+ LV2_State_Map_Path pmap = { state, abstract_path, absolute_path };
+ LV2_Feature pmap_feature = { LV2_STATE_MAP_PATH_URI, &pmap };
+ LV2_State_Make_Path pmake = { state, make_path };
+ LV2_Feature pmake_feature = { LV2_STATE_MAKE_PATH_URI, &pmake };
+ features = sfeatures = add_features(features, &pmap_feature,
+ save_dir ? &pmake_feature : NULL);
#endif
// Store port values
@@ -323,7 +383,7 @@ lilv_state_new_from_instance(const LilvPlugin* plugin,
qsort(state->props, state->num_props, sizeof(Property), property_cmp);
qsort(state->values, state->num_values, sizeof(PortValue), value_cmp);
- free(local_features);
+ free(sfeatures);
return state;
}
@@ -337,14 +397,12 @@ lilv_state_restore(const LilvState* state,
const LV2_Feature *const * features)
{
#ifdef HAVE_LV2_STATE
- LV2_State_Map_Path map_path = { (LilvState*)state,
- abstract_path,
- absolute_path };
+ LV2_State_Map_Path map_path = {
+ (LilvState*)state, abstract_path, absolute_path };
+ LV2_Feature map_feature = { LV2_STATE_MAP_PATH_URI, &map_path };
- LV2_Feature feature = { LV2_STATE_MAP_PATH_URI, &map_path };
-
- const LV2_Feature** local_features = add_feature(features, &feature);
- features = local_features;
+ const LV2_Feature** sfeatures = add_features(features, &map_feature, NULL);
+ features = sfeatures;
const LV2_Descriptor* descriptor = instance->lv2_descriptor;
const LV2_State_Interface* iface = (descriptor->extension_data)
@@ -356,7 +414,7 @@ lilv_state_restore(const LilvState* state,
(LV2_State_Handle)state, flags, features);
}
- free(local_features);
+ free(sfeatures);
#endif // HAVE_LV2_STATE
@@ -785,35 +843,17 @@ add_state_to_manifest(const LilvNode* plugin_uri,
return 0;
}
-static char*
-pathify(const char* in)
-{
- const size_t in_len = strlen(in);
-
- char* out = (char*)calloc(in_len + 1, 1);
- for (size_t i = 0; i < in_len; ++i) {
- char c = in[i];
- if (!((c >= 'a' && c <= 'z')
- || (c >= 'A' && c <= 'Z')
- || (c >= '0' && c <= '9'))) {
- c = '-';
- }
- out[i] = c;
- }
- return out;
-}
-
-static char*
-lilv_default_state_dir(LilvWorld* world)
+static bool
+link_exists(const char* path, void* data)
{
- // Use environment variable or default value if it is unset
- char* state_bundle = getenv("LV2_STATE_BUNDLE");
- if (!state_bundle) {
- state_bundle = LILV_DEFAULT_STATE_BUNDLE;
+ const char* target = (const char*)data;
+ if (!lilv_path_exists(path, NULL)) {
+ return false;
}
-
- // Expand any variables and create if necessary
- return lilv_expand(state_bundle);
+ char* real_path = lilv_realpath(path);
+ bool matches = !strcmp(real_path, target);
+ free(real_path);
+ return !matches;
}
LILV_API
@@ -826,37 +866,27 @@ lilv_state_save(LilvWorld* world,
const char* filename,
const LV2_Feature *const * features)
{
- char* default_dir = NULL;
- char* default_filename = NULL;
- if (!dir) {
- dir = default_dir = lilv_default_state_dir(world);
- }
- if (lilv_mkdir_p(dir)) {
- free(default_dir);
+ if (!filename || !dir || lilv_mkdir_p(dir)) {
return 1;
}
- if (!filename) {
- char* gen_name = pathify(state->label);
- filename = default_filename = lilv_strjoin(gen_name, ".ttl", NULL);
- free(gen_name);
- }
+ char* abs_dir = absolute_dir(dir, true);
+ dir = abs_dir;
char* const path = lilv_path_join(dir, filename);
FILE* fd = fopen(path, "w");
if (!fd) {
LILV_ERRORF("Failed to open %s (%s)\n", path, strerror(errno));
- free(default_dir);
- free(default_filename);
+ free(abs_dir);
free(path);
return 4;
}
// FIXME: make parameter non-const?
- if (state->dir) {
+ if (state->dir && strcmp(state->dir, dir)) {
free(state->dir);
+ ((LilvState*)state)->dir = lilv_strdup(dir);
}
- ((LilvState*)state)->dir = lilv_strdup(dir);
char* const manifest = lilv_path_join(dir, "manifest.ttl");
@@ -919,27 +949,43 @@ lilv_state_save(LilvWorld* world,
serd_writer_end_anon(writer, &port);
}
- // Create symlinks to external files
+ // Create symlinks to files
#ifdef HAVE_LV2_STATE
for (ZixTreeIter* i = zix_tree_begin(state->abs2rel);
i != zix_tree_end(state->abs2rel);
i = zix_tree_iter_next(i)) {
const PathMap* pm = (const PathMap*)zix_tree_get(i);
- char* real_dir = lilv_realpath(dir);
- char* base = lilv_path_join(real_dir, NULL);
- char* rel_path = lilv_path_join(dir, pm->rel);
- char* target_path = lilv_path_is_child(pm->abs, state->file_dir)
- ? lilv_path_relative_to(pm->abs, base)
- : lilv_strdup(pm->abs);
- if (lilv_symlink(target_path, rel_path)) {
- LILV_ERRORF("Failed to link `%s' => `%s' (%s)\n",
- pm->abs, pm->rel, strerror(errno));
+ char* path = lilv_path_join(abs_dir, pm->rel);
+ if (lilv_path_is_child(pm->abs, state->copy_dir)
+ && strcmp(state->copy_dir, dir)) {
+ // Link directly to snapshot in the copy directory
+ char* target = lilv_path_relative_to(pm->abs, dir);
+ lilv_symlink(target, path);
+ free(target);
+ } else if (!lilv_path_is_child(pm->abs, dir)) {
+ const char* link_dir = state->link_dir ? state->link_dir : dir;
+ char* pat = lilv_path_join(link_dir, pm->rel);
+ if (!strcmp(dir, link_dir)) {
+ // Link directory is save directory, make link at exact path
+ remove(pat);
+ lilv_symlink(pm->abs, pat);
+ } else {
+ // Make a link in the link directory to external file
+ char* lpath = lilv_find_free_path(pat, link_exists, pm->abs);
+ if (!lilv_path_exists(lpath, NULL)) {
+ lilv_symlink(pm->abs, lpath);
+ }
+
+ // Make a link in the save directory to the external link
+ char* target = lilv_path_relative_to(lpath, dir);
+ lilv_symlink(target, path);
+ free(target);
+ free(lpath);
+ }
+ free(pat);
}
- free(target_path);
- free(rel_path);
- free(base);
- free(real_dir);
+ free(path);
}
#endif
@@ -1024,8 +1070,7 @@ lilv_state_save(LilvWorld* world,
add_state_to_manifest(state->plugin_uri, manifest, uri, path);
}
- free(default_dir);
- free(default_filename);
+ free(abs_dir);
free(manifest);
free(path);
return 0;
@@ -1088,14 +1133,8 @@ lilv_state_equals(const LilvState* a, const LilvState* b)
}
if (ap->type == a->state_Path) {
- const char* const a_abs = lilv_state_rel2abs(a, (char*)ap->value);
- const char* const b_abs = lilv_state_rel2abs(b, (char*)bp->value);
- char* const a_real = lilv_realpath(a_abs);
- char* const b_real = lilv_realpath(b_abs);
- const int cmp = strcmp(a_real, b_real);
- free(a_real);
- free(b_real);
- if (cmp) {
+ if (!lilv_file_equals(lilv_state_rel2abs(a, (char*)ap->value),
+ lilv_state_rel2abs(b, (char*)bp->value))) {
return false;
}
} else if (ap->size != bp->size
diff --git a/src/util.c b/src/util.c
index 47846da..a8f66be 100644
--- a/src/util.c
+++ b/src/util.c
@@ -228,8 +228,8 @@ lilv_path_exists(const char* path, void* ignored)
}
char*
-lilv_find_free_path(
- const char* in_path, bool (*exists)(const char*, void*), void* user_data)
+lilv_find_free_path(const char* in_path,
+ bool (*exists)(const char*, void*), void* user_data)
{
const size_t in_path_len = strlen(in_path);
char* path = (char*)malloc(in_path_len + 7);
@@ -239,7 +239,7 @@ lilv_find_free_path(
if (!exists(path, user_data)) {
return path;
}
- snprintf(path, in_path_len + 7, "%s%u", in_path, i);
+ snprintf(path, in_path_len + 7, "%s.%u", in_path, i);
}
return NULL;
@@ -306,8 +306,25 @@ lilv_path_is_absolute(const char* path)
}
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);
@@ -331,8 +348,12 @@ lilv_size_mtime(const char* path, off_t* size, time_t* time)
*size = *time = 0;
}
- *size = buf.st_size;
- *time = buf.st_mtime;
+ if (size) {
+ *size = buf.st_size;
+ }
+ if (time) {
+ *time = buf.st_mtime;
+ }
}
typedef struct {
@@ -346,7 +367,7 @@ static void
update_latest(const char* path, const char* name, void* data)
{
Latest* latest = (Latest*)data;
- char* entry_path = lilv_strjoin(path, "/", name, NULL);
+ char* entry_path = lilv_path_join(path, name);
unsigned num;
if (sscanf(entry_path, latest->pattern, &num) == 1) {
off_t entry_size;
@@ -364,16 +385,16 @@ update_latest(const char* path, const char* name, void* data)
/** Return the latest copy of the file at @c path that is newer. */
char*
-lilv_get_latest_copy(const char* path)
+lilv_get_latest_copy(const char* path, const char* copy_path)
{
- char* dirname = lilv_dirname(path);
- Latest latest = { lilv_strjoin(path, "%u", NULL), 0, 0, NULL };
+ char* copy_dir = lilv_dirname(copy_path);
+ Latest latest = { lilv_strjoin(copy_path, "%u", NULL), 0, 0, NULL };
lilv_size_mtime(path, &latest.orig_size, &latest.time);
- lilv_dir_for_each(dirname, &latest, update_latest);
+ lilv_dir_for_each(copy_dir, &latest, update_latest);
free(latest.pattern);
- free(dirname);
+ free(copy_dir);
return latest.latest;
}
@@ -392,11 +413,19 @@ lilv_realpath(const char* path)
int
lilv_symlink(const char* oldpath, const char* newpath)
{
+ int ret = 0;
+ if (strcmp(oldpath, newpath)) {
#ifdef _WIN32
- return !CreateSymbolicLink(newpath, oldpath, 0);
+ ret = !CreateSymbolicLink(newpath, oldpath, 0);
#else
- return symlink(oldpath, newpath);
+ ret = symlink(oldpath, newpath);
#endif
+ }
+ if (ret) {
+ LILV_ERRORF("Failed to link %s => %s (%s)\n",
+ newpath, oldpath, strerror(errno));
+ }
+ return ret;
}
char*
@@ -442,9 +471,12 @@ lilv_path_relative_to(const char* path, const char* base)
bool
lilv_path_is_child(const char* path, const char* dir)
{
- const size_t path_len = strlen(path);
- const size_t dir_len = strlen(dir);
- return dir && path_len >= dir_len && !strncmp(path, dir, dir_len);
+ if (path && dir) {
+ const size_t path_len = strlen(path);
+ const size_t dir_len = strlen(dir);
+ return dir && path_len >= dir_len && !strncmp(path, dir, dir_len);
+ }
+ return false;
}
int
@@ -504,3 +536,54 @@ lilv_mkdir_p(const char* dir_path)
free(path);
return 0;
}
+
+bool
+lilv_file_equals(const char* a_path, const char* b_path)
+{
+ if (!strcmp(a_path, b_path)) {
+ return true; // Paths match
+ }
+
+ char* const a_real = lilv_realpath(a_path);
+ char* const b_real = lilv_realpath(b_path);
+ if (!a_real || !b_real) {
+ return false; // Missing file matches nothing
+ }
+
+ if (!strcmp(a_real, b_real)) {
+ return true; // Real paths match
+ }
+
+ off_t a_size;
+ off_t b_size;
+ lilv_size_mtime(a_path, &a_size, NULL);
+ lilv_size_mtime(b_path, &b_size, NULL);
+ if (a_size != b_size) {
+ return false; // Sizes differ
+ }
+
+ FILE* a_file = fopen(a_real, "rb");
+ if (!a_file) {
+ return false; // Missing file matches nothing
+ }
+
+ FILE* b_file = fopen(b_real, "rb");
+ if (!b_file) {
+ fclose(a_file);
+ return false; // Missing file matches nothing
+ }
+
+ bool match = true;
+
+ // TODO: Improve performance by reading chunks
+ while (!feof(a_file) && !feof(b_file)) {
+ if (fgetc(a_file) != fgetc(b_file)) {
+ match = false;
+ break;
+ }
+ }
+
+ fclose(a_file);
+ fclose(b_file);
+ return match;
+}