[WIP 3/3] lib: add configuration framework.
David Bremner
david at tethera.net
Sun Sep 28 11:28:19 PDT 2014
Allow clients to atomically get and set key value pairs.
---
lib/Makefile.local | 1 +
lib/config.c | 156 ++++++++++++++++++++++++++++++++++++++++++++++++
lib/notmuch.h | 7 +++
test/Makefile.local | 7 +++
test/T560-lib-config.sh | 15 +++++
test/config-test.c | 28 +++++++++
6 files changed, 214 insertions(+)
create mode 100644 lib/config.c
create mode 100755 test/T560-lib-config.sh
create mode 100644 test/config-test.c
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 4120390..7ca2b3b 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -54,6 +54,7 @@ lib := $(dir)
libnotmuch_c_srcs = \
$(notmuch_compat_srcs) \
+ $(dir)/config.c \
$(dir)/filenames.c \
$(dir)/string-list.c \
$(dir)/libsha1.c \
diff --git a/lib/config.c b/lib/config.c
new file mode 100644
index 0000000..c3b8f39
--- /dev/null
+++ b/lib/config.c
@@ -0,0 +1,156 @@
+#include "notmuch-private.h"
+#include "string-util.h"
+#include "file-util.h"
+#include <talloc.h>
+
+static notmuch_status_t
+compute_paths (void *ctx, const char *notmuch_path, const char *key,
+ char **parent_out, char **dest_out) {
+
+ char *parent, *dest, *final_component, *last_slash;
+
+ parent = talloc_asprintf (ctx, "%s/config/%s", notmuch_path, key);
+ if (!parent)
+ return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+ last_slash = strrchr (parent, '/');
+ *last_slash = '\0';
+
+ final_component = talloc_strdup (ctx, last_slash + 1);
+
+ dest = talloc_asprintf(ctx, "%s/_%s", parent, final_component);
+ if (!dest)
+ return NOTMUCH_STATUS_OUT_OF_MEMORY;
+
+ *parent_out = parent;
+ *dest_out = dest;
+
+ return NOTMUCH_STATUS_SUCCESS;
+
+}
+
+notmuch_status_t
+notmuch_config_get (const char *notmuch_path, const char *key, const char **val){
+
+ char *line = NULL;
+ size_t line_size;
+ ssize_t line_len;
+ char *buf = NULL;
+ char *file_name, *parent;
+ notmuch_status_t status;
+ void *local = NULL;
+ FILE *file_ptr = NULL;
+
+ if (notmuch_path == NULL || key == NULL || val == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ local = talloc_new (NULL);
+
+ status = compute_paths (local, notmuch_path, key, &parent, &file_name);
+ if (status)
+ goto DONE;
+
+ file_ptr = fopen (file_name, "r");
+ if (file_ptr == NULL) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ while ((line_len = getline (&line, &line_size, file_ptr)) != -1) {
+
+ if (buf)
+ buf = talloc_asprintf (local, "%s%s", buf, line);
+ else
+ buf = talloc_strdup (local, line);
+
+ if (buf == NULL) {
+ status = NOTMUCH_STATUS_OUT_OF_MEMORY;
+ goto DONE;
+ }
+
+ }
+
+
+ /* remove the last newline. Convenient for the single line case. */
+ chomp_newline (buf);
+
+ *val = buf;
+ status = NOTMUCH_STATUS_SUCCESS;
+
+ DONE:
+ if (line)
+ free (line);
+
+ if (file_ptr)
+ fclose (file_ptr);
+
+ talloc_free (local);
+
+ return status;
+}
+
+notmuch_status_t
+notmuch_config_set (const char *notmuch_path, const char *key, const char *val){
+
+ char *parent, *path, *temp_path;
+ int out_fd = -1;
+ notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+ void *local = NULL;
+ FILE *out_file;
+
+ if (notmuch_path == NULL || key == NULL || val == NULL)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ if (has_double_dot_component (key))
+ return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;
+
+ local = talloc_new (NULL);
+
+ status = compute_paths (local, notmuch_path, key, &parent, &path);
+ if (status)
+ goto DONE;
+
+ if (! mkdir_recursive (local, parent, 0700)) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ temp_path = talloc_asprintf (local, "%s/tmp.XXXXXX", parent);
+ if (temp_path == NULL) {
+ status = NOTMUCH_STATUS_OUT_OF_MEMORY;
+ goto DONE;
+ }
+
+ out_fd = mkstemp (temp_path);
+ if (out_fd == -1) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ out_file = fdopen (out_fd, "w");
+ if (out_file == NULL) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ if (fputs (val, out_file) == EOF) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ if (fclose (out_file)) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ if (rename (temp_path, path) < 0) {
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+
+ DONE:
+
+ talloc_free(local);
+
+ return status;
+}
diff --git a/lib/notmuch.h b/lib/notmuch.h
index fe2340b..9a5f9df 100644
--- a/lib/notmuch.h
+++ b/lib/notmuch.h
@@ -192,6 +192,13 @@ typedef struct _notmuch_directory notmuch_directory_t;
typedef struct _notmuch_filenames notmuch_filenames_t;
#endif /* __DOXYGEN__ */
+
+notmuch_status_t
+notmuch_config_get (const char *notmuch_path, const char *key, const char **val);
+
+notmuch_status_t
+notmuch_config_set (const char *notmuch_path, const char *key, const char *val);
+
/**
* Create a new, empty notmuch database located at 'path'.
*
diff --git a/test/Makefile.local b/test/Makefile.local
index a2d58fc..8a203f0 100644
--- a/test/Makefile.local
+++ b/test/Makefile.local
@@ -23,6 +23,9 @@ random_corpus_deps = $(dir)/random-corpus.o $(dir)/database-test.o \
lib/libnotmuch.a util/libutil.a \
parse-time-string/libparse-time-string.a
+config_test_deps = $(dir)/config-test.o \
+ lib/libnotmuch.a util/libutil.a
+
$(dir)/random-corpus: $(random_corpus_deps)
$(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)
@@ -38,6 +41,9 @@ $(dir)/parse-time: $(dir)/parse-time.o parse-time-string/parse-time-string.o
$(dir)/make-db-version: $(dir)/make-db-version.o
$(call quiet,CXX) $^ -o $@ $(XAPIAN_LDFLAGS)
+$(dir)/config-test: $(config_test_deps)
+ $(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)
+
.PHONY: test check
test_main_srcs=$(dir)/arg-test.c \
@@ -47,6 +53,7 @@ test_main_srcs=$(dir)/arg-test.c \
$(dir)/smtp-dummy.c \
$(dir)/symbol-test.cc \
$(dir)/make-db-version.cc \
+ $(dir)/config-test.c \
test_srcs=$(test_main_srcs) $(dir)/database-test.c
diff --git a/test/T560-lib-config.sh b/test/T560-lib-config.sh
new file mode 100755
index 0000000..ec8ddbe
--- /dev/null
+++ b/test/T560-lib-config.sh
@@ -0,0 +1,15 @@
+#!/usr/bin/env bash
+test_description="library config handling"
+
+. ./test-lib.sh
+
+test_begin_subtest "getting and setting"
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a foo
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a/b bar
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set b/a fub
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a >> OUTPUT
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a/b >> OUTPUT
+${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get b/a >> OUTPUT
+test_expect_equal "$(cat OUTPUT)" "foobarfub"
+
+test_done
diff --git a/test/config-test.c b/test/config-test.c
new file mode 100644
index 0000000..d9a1116
--- /dev/null
+++ b/test/config-test.c
@@ -0,0 +1,28 @@
+#include <stdio.h>
+#include <string.h>
+
+#include "notmuch.h"
+
+int
+main (int argc, char **argv) {
+ const char *val;
+ notmuch_status_t status;
+
+ if (argc == 4 && strcmp (argv[2], "get") == 0) {
+
+ status = notmuch_config_get (argv[1], argv[3], &val);
+ if (status)
+ return status;
+ fputs (val, stdout);
+ return 0;
+
+ } else if (argc == 5 && strcmp (argv[2], "set") == 0) {
+
+ status = notmuch_config_set (argv[1], argv[3], argv[4]);
+ if (status)
+ return status;
+ return 0;
+ }
+
+ return 1;
+}
--
2.1.0
More information about the notmuch
mailing list