[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