summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lilv_internal.h1
-rw-r--r--src/plugin.c17
-rw-r--r--src/state.c115
-rw-r--r--src/util.c39
-rw-r--r--src/world.c40
-rw-r--r--test/lilv_test.c34
-rw-r--r--wscript49
7 files changed, 194 insertions, 101 deletions
diff --git a/src/lilv_internal.h b/src/lilv_internal.h
index 9f19c1c..7191daa 100644
--- a/src/lilv_internal.h
+++ b/src/lilv_internal.h
@@ -330,6 +330,7 @@ LilvNodes* lilv_nodes_from_stream_objects(LilvWorld* w,
char* lilv_strjoin(const char* first, ...);
char* lilv_strdup(const char* str);
char* lilv_get_lang(void);
+char* lilv_expand(const char* path);
typedef void (*VoidFunc)(void);
diff --git a/src/plugin.c b/src/plugin.c
index 2a7ccee..2bb70c4 100644
--- a/src/plugin.c
+++ b/src/plugin.c
@@ -217,17 +217,20 @@ lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_p)
LilvNode* symbol = lilv_plugin_get_unique(
p, port, p->world->lv2_symbol_node);
+ bool error = false;
if (!lilv_node_is_string(symbol) || !is_symbol(symbol->str_val)) {
LILV_ERRORF("Plugin <%s> port symbol `%s' is invalid\n",
lilv_node_as_uri(p->plugin_uri),
lilv_node_as_string(symbol));
- goto error;
+ error = true;
+ goto done;
}
if (!lilv_node_is_int(index)) {
LILV_ERRORF("Plugin <%s> port index is not an integer\n",
lilv_node_as_uri(p->plugin_uri));
- goto error;
+ error = true;
+ goto done;
}
uint32_t this_index = lilv_node_as_int(index);
@@ -265,13 +268,13 @@ lilv_plugin_load_ports_if_necessary(const LilvPlugin* const_p)
}
lilv_match_end(types);
- continue;
-
- error:
+ done:
lilv_node_free(symbol);
lilv_node_free(index);
- lilv_plugin_free_ports(p);
- break; // Invalid plugin
+ if (error) { // Invalid plugin
+ lilv_plugin_free_ports(p);
+ break;
+ }
}
lilv_match_end(ports);
diff --git a/src/state.c b/src/state.c
index c747937..05570cd 100644
--- a/src/state.c
+++ b/src/state.c
@@ -307,6 +307,7 @@ new_state_from_model(LilvWorld* world,
state->label = lilv_strdup(
(const char*)sord_node_get_string(lilv_match_object(i)));
sord_iter_free(i);
+ } else {
}
// Get port values
@@ -609,6 +610,70 @@ pathify(const char* in)
return out;
}
+static int
+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;
+}
+
+static int
+lilv_default_state_path(LilvWorld* world,
+ const LilvState* state,
+ char** path,
+ char** manifest_path)
+{
+#ifdef HAVE_MKDIR
+ if (!state->label) {
+ LILV_ERROR("Attempt to save state with no label or path.\n");
+ return 1;
+ }
+
+ char* state_bundle = getenv("LV2_STATE_BUNDLE");
+ if (!state_bundle) {
+ state_bundle = LILV_DEFAULT_STATE_BUNDLE;
+ }
+
+ // Create ~/.lv2/presets.lv2/
+ char* const bundle = lilv_expand(state_bundle);
+ if (mkdir_p(bundle)) {
+ free(bundle);
+ return 3;
+ }
+
+ char* const filename = pathify(state->label);
+
+ *path = lilv_strjoin(
+ bundle, LILV_DIR_SEP, filename, ".ttl", NULL);
+
+ *manifest_path = lilv_strjoin(
+ bundle, LILV_DIR_SEP, "manifest.ttl", NULL);
+
+ free(bundle);
+ free(filename);
+
+ return 0;
+#else
+ LILV_ERROR("Save to default state path but mkdir is unavailable.\n");
+ return 4;
+#endif
+}
+
LILV_API
int
lilv_state_save(LilvWorld* world,
@@ -621,49 +686,13 @@ lilv_state_save(LilvWorld* world,
char* default_path = NULL;
char* default_manifest_path = NULL;
if (!path) {
-#ifdef HAVE_MKDIR
- if (!state->label) {
- LILV_ERROR("Attempt to save state with no label or path.\n");
+ if (lilv_default_state_path(
+ world, state, &default_path, &default_manifest_path)) {
return 1;
}
-
- const char* const home = getenv("HOME");
- if (!home) {
- LILV_ERROR("$HOME is undefined\n");
- return 2;
- }
-
- // Create ~/.lv2/
- char* const lv2dir = lilv_strjoin(home, "/.lv2/", NULL);
- if (mkdir(lv2dir, 0755) && errno != EEXIST) {
- LILV_ERRORF("Unable to create %s (%s)\n", lv2dir, strerror(errno));
- free(lv2dir);
- return 3;
- }
-
- // Create ~/.lv2/presets.lv2/
- char* const bundle = lilv_strjoin(lv2dir, "presets.lv2/", NULL);
- if (mkdir(bundle, 0755) && errno != EEXIST) {
- LILV_ERRORF("Unable to create %s (%s)\n", lv2dir, strerror(errno));
- free(lv2dir);
- free(bundle);
- return 4;
- }
-
- char* const filename = pathify(state->label);
- default_path = lilv_strjoin(bundle, filename, ".ttl", NULL);
- default_manifest_path = lilv_strjoin(bundle, "manifest.ttl", NULL);
-
+
path = default_path;
manifest_path = default_manifest_path;
-
- free(lv2dir);
- free(bundle);
- free(filename);
-#else
- LILV_ERROR("Save to default state path but mkdir is unavailable.\n");
- return 1;
-#endif
}
FILE* fd = fopen(path, "w");
@@ -779,6 +808,7 @@ lilv_state_save(LilvWorld* world,
serd_writer_write_statement(
writer, SERD_ANON_CONT, NULL,
&state_node, &p, &o, &t, NULL);
+ lilv_node_free(node);
} else {
char name[16];
snprintf(name, sizeof(name), "b%u", i);
@@ -835,6 +865,13 @@ void
lilv_state_free(LilvState* state)
{
if (state) {
+ for (uint32_t i = 0; i < state->num_props; ++i) {
+ free(state->props[i].value);
+ }
+ for (uint32_t i = 0; i < state->num_values; ++i) {
+ lilv_node_free(state->values[i].value);
+ free(state->values[i].symbol);
+ }
lilv_node_free(state->plugin_uri);
free(state->props);
free(state->values);
diff --git a/src/util.c b/src/util.c
index 0af0e1a..1c9a46e 100644
--- a/src/util.c
+++ b/src/util.c
@@ -14,6 +14,8 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define _POSIX_SOURCE 1 /* for wordexp */
+
#include <assert.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -21,6 +23,10 @@
#include "lilv_internal.h"
+#ifdef HAVE_WORDEXP
+# include <wordexp.h>
+#endif
+
char*
lilv_strjoin(const char* first, ...)
{
@@ -99,3 +105,36 @@ lilv_get_lang(void)
return lang;
}
+
+/** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in @a path. */
+char*
+lilv_expand(const char* path)
+{
+#ifdef HAVE_WORDEXP
+ char* ret = NULL;
+ wordexp_t p;
+ if (wordexp(path, &p, 0)) {
+ LILV_ERRORF("Error expanding path `%s'\n", path);
+ return lilv_strdup(path);
+ }
+ if (p.we_wordc == 0) {
+ /* Literal directory path (e.g. no variables or ~) */
+ ret = lilv_strdup(path);
+ } else if (p.we_wordc == 1) {
+ /* Directory path expands (e.g. contains ~ or $FOO) */
+ ret = lilv_strdup(p.we_wordv[0]);
+ } else {
+ /* Multiple expansions in a single directory path? */
+ LILV_ERRORF("Malformed path `%s'\n", path);
+ ret = lilv_strdup(path);
+ }
+ wordfree(&p);
+#elif defined(__WIN32__)
+ static const size_t len = 32767;
+ char* ret = malloc(len);
+ ExpandEnvironmentStrings(path, ret, len);
+#else
+ char* ret = lilv_strdup(path);
+#endif
+ return ret;
+}
diff --git a/src/world.c b/src/world.c
index 2029136..8151a73 100644
--- a/src/world.c
+++ b/src/world.c
@@ -14,7 +14,7 @@
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-#define _POSIX_SOURCE 1 /* for wordexp */
+#define _POSIX_SOURCE 1 /* for readdir_r */
#include <assert.h>
#include <errno.h>
@@ -25,10 +25,6 @@
#include "lilv_internal.h"
-#ifdef HAVE_WORDEXP
-# include <wordexp.h>
-#endif
-
LILV_API
LilvWorld*
lilv_world_new(void)
@@ -582,43 +578,11 @@ lilv_world_load_bundle(LilvWorld* world, LilvNode* bundle_uri)
serd_node_free(&manifest_uri);
}
-/** Expand variables (e.g. POSIX ~ or $FOO, Windows %FOO%) in @a path. */
-static char*
-expand(const char* path)
-{
-#ifdef HAVE_WORDEXP
- char* ret = NULL;
- wordexp_t p;
- if (wordexp(path, &p, 0)) {
- LILV_ERRORF("Error expanding path `%s'\n", path);
- return lilv_strdup(path);
- }
- if (p.we_wordc == 0) {
- /* Literal directory path (e.g. no variables or ~) */
- ret = lilv_strdup(path);
- } else if (p.we_wordc == 1) {
- /* Directory path expands (e.g. contains ~ or $FOO) */
- ret = lilv_strdup(p.we_wordv[0]);
- } else {
- /* Multiple expansions in a single directory path? */
- LILV_ERRORF("Malformed path `%s' ignored\n", path);
- }
- wordfree(&p);
-#elif defined(__WIN32__)
- static const size_t len = 32767;
- char* ret = malloc(len);
- ExpandEnvironmentStrings(path, ret, len);
-#else
- char* ret = lilv_strdup(path);
-#endif
- return ret;
-}
-
/** Load all bundles in the directory at @a dir_path. */
static void
lilv_world_load_directory(LilvWorld* world, const char* dir_path)
{
- char* path = expand(dir_path);
+ char* path = lilv_expand(dir_path);
if (!path) {
LILV_WARNF("Empty path `%s'\n", path);
return;
diff --git a/test/lilv_test.c b/test/lilv_test.c
index 6751be4..0a8a226 100644
--- a/test/lilv_test.c
+++ b/test/lilv_test.c
@@ -1140,15 +1140,49 @@ test_state(void)
// Load state from file
LilvState* state5 = lilv_state_new_from_file(world, &map, NULL, "state.ttl");
TEST_ASSERT(lilv_state_equals(state, state5)); // Round trip accuracy
+
+ // Save state to default bundle
+ setenv("LV2_STATE_BUNDLE", "lv2/lilv-test-state.lv2", 1);
+ const char* state_uri = "http://example.org/test-state";
+ ret = lilv_state_save(world, &unmap, state, state_uri, NULL, NULL);
+ TEST_ASSERT(!ret);
+
+ // Load default bundle into world and load state from it
+ LilvNode* test_state_bundle = lilv_new_uri(world, "lv2/lilv-test-state.lv2/");
+ 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);
+
+ LilvState* state6 = lilv_state_new_from_world(world, &map, test_state_node);
+ TEST_ASSERT(lilv_state_equals(state, state6)); // Round trip accuracy
+ unsetenv("LV2_STATE_BUNDLE");
+
+ LilvNode* num = lilv_new_int(world, 5);
+ LilvState* nostate = lilv_state_new_from_file(world, &map, num, "/junk");
+ TEST_ASSERT(!nostate);
+
+ lilv_node_free(num);
+ lilv_node_free(test_state_bundle);
+ lilv_node_free(test_state_node);
+
lilv_state_free(state);
lilv_state_free(state2);
lilv_state_free(state3);
lilv_state_free(state4);
lilv_state_free(state5);
+ lilv_state_free(state6);
+
+ // Free URI map
+ for (size_t i = 0; i < n_uris; ++i) {
+ free(uris[i]);
+ }
+ free(uris);
+ n_uris = 0;
lilv_instance_free(instance);
+ lilv_node_free(plugin_uri);
lilv_node_free(bundle_uri);
lilv_world_free(world);
diff --git a/wscript b/wscript
index a774f0f..3a047af 100644
--- a/wscript
+++ b/wscript
@@ -46,6 +46,9 @@ def options(opt):
opt.add_option('--default-lv2-path', type='string', default='',
dest='default_lv2_path',
help="Default LV2 path to use if $LV2_PATH is unset (globs and ~ supported)")
+ opt.add_option('--default-state-bundle', type='string', default='',
+ dest='default_state_bundle',
+ help="Default path to use for user-writable state/preset bundle")
opt.add_option('--static', action='store_true', default=False, dest='static',
help="Build static library")
@@ -114,28 +117,38 @@ def configure(conf):
autowaf.define(conf, 'LILV_PATH_SEP', lilv_path_sep)
autowaf.define(conf, 'LILV_DIR_SEP', lilv_dir_sep)
- if Options.options.default_lv2_path == '':
+ # Set default LV2 path
+ lv2_path = Options.options.default_lv2_path
+ if lv2_path == '':
if Options.platform == 'darwin':
- Options.options.default_lv2_path = lilv_path_sep.join([
- '~/Library/Audio/Plug-Ins/LV2',
- '~/.lv2',
- '/usr/local/lib/lv2',
- '/usr/lib/lv2',
- '/Library/Audio/Plug-Ins/LV2'])
+ lv2_path = lilv_path_sep.join(['~/Library/Audio/Plug-Ins/LV2',
+ '~/.lv2',
+ '/usr/local/lib/lv2',
+ '/usr/lib/lv2',
+ '/Library/Audio/Plug-Ins/LV2'])
elif Options.platform == 'haiku':
- Options.options.default_lv2_path = lilv_path_sep.join([
- '~/.lv2',
- '/boot/common/add-ons/lv2'])
+ lv2_path = lilv_path_sep.join(['~/.lv2',
+ '/boot/common/add-ons/lv2'])
elif Options.platform == 'win32':
- Options.options.default_lv2_path = '%APPDATA%\\\\LV2;%PROGRAMFILES%\\\\LV2'
+ lv2_path = lilv_path_sep.join(['%APPDATA%\\\\LV2',
+ '%PROGRAMFILES%\\\\LV2'])
else:
libdirname = os.path.basename(conf.env['LIBDIR'])
- Options.options.default_lv2_path = lilv_path_sep.join([
- '~/.lv2',
- '/usr/%s/lv2' % libdirname,
- '/usr/local/%s/lv2' % libdirname])
-
- autowaf.define(conf, 'LILV_DEFAULT_LV2_PATH', Options.options.default_lv2_path)
+ lv2_path = lilv_path_sep.join(['~/.lv2',
+ '/usr/%s/lv2' % libdirname,
+ '/usr/local/%s/lv2' % libdirname])
+ autowaf.define(conf, 'LILV_DEFAULT_LV2_PATH', lv2_path)
+
+ # Set default state bundle
+ state_bundle = Options.options.default_state_bundle
+ if state_bundle == '':
+ if Options.platform == 'darwin':
+ state_bundle = '~/Library/Audio/Plug-Ins/LV2/presets.lv2'
+ elif Options.platform == 'win32':
+ state_bundle = '%APPDATA%\\\\LV2\\\\presets.lv2'
+ else:
+ state_bundle = '~/.lv2/presets.lv2'
+ autowaf.define(conf, 'LILV_DEFAULT_STATE_BUNDLE', state_bundle)
conf.env['BUILD_TESTS'] = Options.options.build_tests
conf.env['BUILD_UTILS'] = not Options.options.no_utils
@@ -148,6 +161,8 @@ def configure(conf):
autowaf.display_msg(conf, "Default LV2_PATH",
conf.env['LILV_DEFAULT_LV2_PATH'])
+ autowaf.display_msg(conf, "Default state/preset bundle",
+ conf.env['LILV_DEFAULT_STATE_BUNDLE'])
autowaf.display_msg(conf, "Utilities",
bool(conf.env['BUILD_UTILS']))
autowaf.display_msg(conf, "Unit tests",