[WIP 3/3] lib: add configuration framework.
authorDavid Bremner <david@tethera.net>
Sun, 28 Sep 2014 18:28:19 +0000 (20:28 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 18:04:48 +0000 (10:04 -0800)
97/313a08552156acb53f824234d7d4b6bfb85610 [new file with mode: 0644]

diff --git a/97/313a08552156acb53f824234d7d4b6bfb85610 b/97/313a08552156acb53f824234d7d4b6bfb85610
new file mode 100644 (file)
index 0000000..8578d31
--- /dev/null
@@ -0,0 +1,344 @@
+Return-Path: <bremner@tesseract.cs.unb.ca>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id AEFF1431FD0\r
+       for <notmuch@notmuchmail.org>; Sun, 28 Sep 2014 11:29:13 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
+       autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id vttyfHCfwrR7 for <notmuch@notmuchmail.org>;\r
+       Sun, 28 Sep 2014 11:29:08 -0700 (PDT)\r
+Received: from yantan.tethera.net (yantan.tethera.net [199.188.72.155])\r
+       (using TLSv1 with cipher DHE-RSA-AES128-SHA (128/128 bits))\r
+       (No client certificate requested)\r
+       by olra.theworths.org (Postfix) with ESMTPS id 7F2FE431FD2\r
+       for <notmuch@notmuchmail.org>; Sun, 28 Sep 2014 11:29:01 -0700 (PDT)\r
+Received: from remotemail by yantan.tethera.net with local (Exim 4.80)\r
+       (envelope-from <bremner@tesseract.cs.unb.ca>)\r
+       id 1XYJDR-0005gE-6N; Sun, 28 Sep 2014 15:29:01 -0300\r
+Received: (nullmailer pid 31290 invoked by uid 1000); Sun, 28 Sep 2014\r
+       18:28:49 -0000\r
+From: David Bremner <david@tethera.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [WIP 3/3] lib: add configuration framework.\r
+Date: Sun, 28 Sep 2014 20:28:19 +0200\r
+Message-Id: <1411928899-29625-4-git-send-email-david@tethera.net>\r
+X-Mailer: git-send-email 2.1.0\r
+In-Reply-To: <1411928899-29625-1-git-send-email-david@tethera.net>\r
+References: <87iok8vog6.fsf@steelpick.2x.cz>\r
+       <1411928899-29625-1-git-send-email-david@tethera.net>\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Sun, 28 Sep 2014 18:29:14 -0000\r
+\r
+Allow clients to atomically get and set key value pairs.\r
+---\r
+ lib/Makefile.local      |   1 +\r
+ lib/config.c            | 156 ++++++++++++++++++++++++++++++++++++++++++++++++\r
+ lib/notmuch.h           |   7 +++\r
+ test/Makefile.local     |   7 +++\r
+ test/T560-lib-config.sh |  15 +++++\r
+ test/config-test.c      |  28 +++++++++\r
+ 6 files changed, 214 insertions(+)\r
+ create mode 100644 lib/config.c\r
+ create mode 100755 test/T560-lib-config.sh\r
+ create mode 100644 test/config-test.c\r
+\r
+diff --git a/lib/Makefile.local b/lib/Makefile.local\r
+index 4120390..7ca2b3b 100644\r
+--- a/lib/Makefile.local\r
++++ b/lib/Makefile.local\r
+@@ -54,6 +54,7 @@ lib := $(dir)\r
\r
+ libnotmuch_c_srcs =           \\r
+       $(notmuch_compat_srcs)  \\r
++      $(dir)/config.c         \\r
+       $(dir)/filenames.c      \\r
+       $(dir)/string-list.c    \\r
+       $(dir)/libsha1.c        \\r
+diff --git a/lib/config.c b/lib/config.c\r
+new file mode 100644\r
+index 0000000..c3b8f39\r
+--- /dev/null\r
++++ b/lib/config.c\r
+@@ -0,0 +1,156 @@\r
++#include "notmuch-private.h"\r
++#include "string-util.h"\r
++#include "file-util.h"\r
++#include <talloc.h>\r
++\r
++static notmuch_status_t \r
++compute_paths (void *ctx, const char *notmuch_path, const char *key,\r
++             char **parent_out, char **dest_out) {\r
++\r
++    char *parent, *dest, *final_component, *last_slash;\r
++\r
++    parent = talloc_asprintf (ctx, "%s/config/%s", notmuch_path, key);\r
++    if (!parent)\r
++      return NOTMUCH_STATUS_OUT_OF_MEMORY;\r
++\r
++    last_slash = strrchr (parent, '/');\r
++    *last_slash = '\0';\r
++\r
++    final_component = talloc_strdup (ctx, last_slash + 1);\r
++    \r
++    dest = talloc_asprintf(ctx, "%s/_%s", parent, final_component);\r
++    if (!dest)\r
++      return NOTMUCH_STATUS_OUT_OF_MEMORY;\r
++\r
++    *parent_out = parent;\r
++    *dest_out = dest;\r
++      \r
++    return NOTMUCH_STATUS_SUCCESS;\r
++\r
++}\r
++\r
++notmuch_status_t\r
++notmuch_config_get (const char *notmuch_path, const char *key, const char **val){\r
++\r
++    char *line = NULL;\r
++    size_t line_size;\r
++    ssize_t line_len;\r
++    char *buf = NULL;\r
++    char *file_name, *parent;\r
++    notmuch_status_t status;\r
++    void *local = NULL;\r
++    FILE *file_ptr = NULL;\r
++\r
++    if (notmuch_path == NULL || key == NULL || val == NULL)\r
++      return NOTMUCH_STATUS_NULL_POINTER;\r
++\r
++    local = talloc_new (NULL);\r
++    \r
++    status = compute_paths (local, notmuch_path, key, &parent, &file_name);\r
++    if (status) \r
++      goto DONE;\r
++\r
++    file_ptr = fopen (file_name, "r");\r
++    if (file_ptr == NULL) {\r
++      status = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++\r
++    while ((line_len = getline (&line, &line_size, file_ptr)) != -1) {\r
++\r
++      if (buf)\r
++          buf = talloc_asprintf (local, "%s%s", buf, line);\r
++      else \r
++          buf = talloc_strdup (local, line);\r
++      \r
++      if (buf == NULL) {\r
++          status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
++          goto DONE;\r
++      }\r
++\r
++    }\r
++\r
++\r
++    /* remove the last newline. Convenient for the single line case. */\r
++    chomp_newline (buf);\r
++\r
++    *val = buf;\r
++    status =  NOTMUCH_STATUS_SUCCESS;\r
++      \r
++ DONE:\r
++    if (line)\r
++      free (line);\r
++    \r
++    if (file_ptr)\r
++      fclose (file_ptr);\r
++\r
++    talloc_free (local);\r
++\r
++    return status;\r
++}\r
++\r
++notmuch_status_t\r
++notmuch_config_set (const char *notmuch_path, const char *key, const char *val){\r
++\r
++    char *parent, *path, *temp_path;\r
++    int out_fd = -1;\r
++    notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;\r
++    void *local = NULL;\r
++    FILE *out_file;\r
++    \r
++    if (notmuch_path == NULL || key == NULL || val == NULL)\r
++      return NOTMUCH_STATUS_NULL_POINTER;\r
++\r
++    if (has_double_dot_component (key))\r
++      return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;\r
++\r
++    local = talloc_new (NULL);\r
++\r
++    status = compute_paths (local, notmuch_path, key, &parent, &path);\r
++    if (status) \r
++      goto DONE;\r
++\r
++    if (! mkdir_recursive (local, parent, 0700)) {\r
++      status = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++\r
++    temp_path = talloc_asprintf (local, "%s/tmp.XXXXXX", parent);\r
++    if (temp_path == NULL) {\r
++      status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
++      goto DONE;\r
++    }\r
++\r
++    out_fd = mkstemp (temp_path);\r
++    if (out_fd == -1) {\r
++      status = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++    \r
++    out_file = fdopen (out_fd, "w");\r
++    if (out_file == NULL) {\r
++      status = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++\r
++    if (fputs (val, out_file) == EOF) {\r
++      status = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++\r
++    if (fclose (out_file)) {\r
++      status = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++\r
++    if (rename (temp_path, path) < 0) {\r
++      status = NOTMUCH_STATUS_FILE_ERROR;\r
++      goto DONE;\r
++    }\r
++      \r
++ DONE:\r
++\r
++    talloc_free(local);\r
++    \r
++    return status;\r
++}\r
+diff --git a/lib/notmuch.h b/lib/notmuch.h\r
+index fe2340b..9a5f9df 100644\r
+--- a/lib/notmuch.h\r
++++ b/lib/notmuch.h\r
+@@ -192,6 +192,13 @@ typedef struct _notmuch_directory notmuch_directory_t;\r
+ typedef struct _notmuch_filenames notmuch_filenames_t;\r
+ #endif /* __DOXYGEN__ */\r
\r
++\r
++notmuch_status_t\r
++notmuch_config_get (const char *notmuch_path, const char *key, const char **val);\r
++\r
++notmuch_status_t\r
++notmuch_config_set (const char *notmuch_path, const char *key, const char *val);\r
++\r
+ /**\r
+  * Create a new, empty notmuch database located at 'path'.\r
+  *\r
+diff --git a/test/Makefile.local b/test/Makefile.local\r
+index a2d58fc..8a203f0 100644\r
+--- a/test/Makefile.local\r
++++ b/test/Makefile.local\r
+@@ -23,6 +23,9 @@ random_corpus_deps =  $(dir)/random-corpus.o  $(dir)/database-test.o \\r
+                       lib/libnotmuch.a util/libutil.a \\r
+                       parse-time-string/libparse-time-string.a\r
\r
++config_test_deps =  $(dir)/config-test.o  \\r
++                      lib/libnotmuch.a util/libutil.a\r
++\r
+ $(dir)/random-corpus: $(random_corpus_deps)\r
+       $(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)\r
\r
+@@ -38,6 +41,9 @@ $(dir)/parse-time: $(dir)/parse-time.o parse-time-string/parse-time-string.o\r
+ $(dir)/make-db-version: $(dir)/make-db-version.o\r
+       $(call quiet,CXX) $^ -o $@ $(XAPIAN_LDFLAGS)\r
\r
++$(dir)/config-test: $(config_test_deps)\r
++      $(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)\r
++\r
+ .PHONY: test check\r
\r
+ test_main_srcs=$(dir)/arg-test.c \\r
+@@ -47,6 +53,7 @@ test_main_srcs=$(dir)/arg-test.c \\r
+             $(dir)/smtp-dummy.c \\r
+             $(dir)/symbol-test.cc \\r
+             $(dir)/make-db-version.cc \\r
++            $(dir)/config-test.c \\r
\r
+ test_srcs=$(test_main_srcs) $(dir)/database-test.c\r
\r
+diff --git a/test/T560-lib-config.sh b/test/T560-lib-config.sh\r
+new file mode 100755\r
+index 0000000..ec8ddbe\r
+--- /dev/null\r
++++ b/test/T560-lib-config.sh\r
+@@ -0,0 +1,15 @@\r
++#!/usr/bin/env bash\r
++test_description="library config handling"\r
++\r
++. ./test-lib.sh\r
++\r
++test_begin_subtest "getting and setting"\r
++${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a foo\r
++${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a/b bar\r
++${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set b/a fub\r
++${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a  >> OUTPUT\r
++${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a/b  >> OUTPUT\r
++${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get b/a  >> OUTPUT\r
++test_expect_equal "$(cat OUTPUT)" "foobarfub"\r
++\r
++test_done\r
+diff --git a/test/config-test.c b/test/config-test.c\r
+new file mode 100644\r
+index 0000000..d9a1116\r
+--- /dev/null\r
++++ b/test/config-test.c\r
+@@ -0,0 +1,28 @@\r
++#include <stdio.h>\r
++#include <string.h>\r
++\r
++#include "notmuch.h"\r
++\r
++int\r
++main (int argc, char **argv) {\r
++    const char *val;\r
++    notmuch_status_t status;\r
++\r
++    if (argc == 4 && strcmp (argv[2], "get") == 0) {\r
++      \r
++      status = notmuch_config_get (argv[1], argv[3], &val);\r
++      if (status) \r
++          return status;\r
++      fputs (val, stdout);\r
++      return 0;\r
++\r
++    } else  if (argc == 5 && strcmp (argv[2], "set") == 0) {\r
++      \r
++      status = notmuch_config_set (argv[1], argv[3], argv[4]);\r
++      if (status) \r
++          return status;\r
++      return 0;\r
++    }\r
++\r
++    return 1;\r
++}\r
+-- \r
+2.1.0\r
+\r