[WIP 3/3] lib: add configuration framework.
[notmuch-archives.git] / 97 / 313a08552156acb53f824234d7d4b6bfb85610
1 Return-Path: <bremner@tesseract.cs.unb.ca>\r
2 X-Original-To: notmuch@notmuchmail.org\r
3 Delivered-To: notmuch@notmuchmail.org\r
4 Received: from localhost (localhost [127.0.0.1])\r
5         by olra.theworths.org (Postfix) with ESMTP id AEFF1431FD0\r
6         for <notmuch@notmuchmail.org>; Sun, 28 Sep 2014 11:29:13 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: 0\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]\r
12         autolearn=disabled\r
13 Received: from olra.theworths.org ([127.0.0.1])\r
14         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
15         with ESMTP id vttyfHCfwrR7 for <notmuch@notmuchmail.org>;\r
16         Sun, 28 Sep 2014 11:29:08 -0700 (PDT)\r
17 Received: from yantan.tethera.net (yantan.tethera.net [199.188.72.155])\r
18         (using TLSv1 with cipher DHE-RSA-AES128-SHA (128/128 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id 7F2FE431FD2\r
21         for <notmuch@notmuchmail.org>; Sun, 28 Sep 2014 11:29:01 -0700 (PDT)\r
22 Received: from remotemail by yantan.tethera.net with local (Exim 4.80)\r
23         (envelope-from <bremner@tesseract.cs.unb.ca>)\r
24         id 1XYJDR-0005gE-6N; Sun, 28 Sep 2014 15:29:01 -0300\r
25 Received: (nullmailer pid 31290 invoked by uid 1000); Sun, 28 Sep 2014\r
26         18:28:49 -0000\r
27 From: David Bremner <david@tethera.net>\r
28 To: notmuch@notmuchmail.org\r
29 Subject: [WIP 3/3] lib: add configuration framework.\r
30 Date: Sun, 28 Sep 2014 20:28:19 +0200\r
31 Message-Id: <1411928899-29625-4-git-send-email-david@tethera.net>\r
32 X-Mailer: git-send-email 2.1.0\r
33 In-Reply-To: <1411928899-29625-1-git-send-email-david@tethera.net>\r
34 References: <87iok8vog6.fsf@steelpick.2x.cz>\r
35         <1411928899-29625-1-git-send-email-david@tethera.net>\r
36 X-BeenThere: notmuch@notmuchmail.org\r
37 X-Mailman-Version: 2.1.13\r
38 Precedence: list\r
39 List-Id: "Use and development of the notmuch mail system."\r
40         <notmuch.notmuchmail.org>\r
41 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
42         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
43 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
44 List-Post: <mailto:notmuch@notmuchmail.org>\r
45 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
46 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
47         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
48 X-List-Received-Date: Sun, 28 Sep 2014 18:29:14 -0000\r
49 \r
50 Allow clients to atomically get and set key value pairs.\r
51 ---\r
52  lib/Makefile.local      |   1 +\r
53  lib/config.c            | 156 ++++++++++++++++++++++++++++++++++++++++++++++++\r
54  lib/notmuch.h           |   7 +++\r
55  test/Makefile.local     |   7 +++\r
56  test/T560-lib-config.sh |  15 +++++\r
57  test/config-test.c      |  28 +++++++++\r
58  6 files changed, 214 insertions(+)\r
59  create mode 100644 lib/config.c\r
60  create mode 100755 test/T560-lib-config.sh\r
61  create mode 100644 test/config-test.c\r
62 \r
63 diff --git a/lib/Makefile.local b/lib/Makefile.local\r
64 index 4120390..7ca2b3b 100644\r
65 --- a/lib/Makefile.local\r
66 +++ b/lib/Makefile.local\r
67 @@ -54,6 +54,7 @@ lib := $(dir)\r
68  \r
69  libnotmuch_c_srcs =            \\r
70         $(notmuch_compat_srcs)  \\r
71 +       $(dir)/config.c         \\r
72         $(dir)/filenames.c      \\r
73         $(dir)/string-list.c    \\r
74         $(dir)/libsha1.c        \\r
75 diff --git a/lib/config.c b/lib/config.c\r
76 new file mode 100644\r
77 index 0000000..c3b8f39\r
78 --- /dev/null\r
79 +++ b/lib/config.c\r
80 @@ -0,0 +1,156 @@\r
81 +#include "notmuch-private.h"\r
82 +#include "string-util.h"\r
83 +#include "file-util.h"\r
84 +#include <talloc.h>\r
85 +\r
86 +static notmuch_status_t \r
87 +compute_paths (void *ctx, const char *notmuch_path, const char *key,\r
88 +              char **parent_out, char **dest_out) {\r
89 +\r
90 +    char *parent, *dest, *final_component, *last_slash;\r
91 +\r
92 +    parent = talloc_asprintf (ctx, "%s/config/%s", notmuch_path, key);\r
93 +    if (!parent)\r
94 +       return NOTMUCH_STATUS_OUT_OF_MEMORY;\r
95 +\r
96 +    last_slash = strrchr (parent, '/');\r
97 +    *last_slash = '\0';\r
98 +\r
99 +    final_component = talloc_strdup (ctx, last_slash + 1);\r
100 +    \r
101 +    dest = talloc_asprintf(ctx, "%s/_%s", parent, final_component);\r
102 +    if (!dest)\r
103 +       return NOTMUCH_STATUS_OUT_OF_MEMORY;\r
104 +\r
105 +    *parent_out = parent;\r
106 +    *dest_out = dest;\r
107 +       \r
108 +    return NOTMUCH_STATUS_SUCCESS;\r
109 +\r
110 +}\r
111 +\r
112 +notmuch_status_t\r
113 +notmuch_config_get (const char *notmuch_path, const char *key, const char **val){\r
114 +\r
115 +    char *line = NULL;\r
116 +    size_t line_size;\r
117 +    ssize_t line_len;\r
118 +    char *buf = NULL;\r
119 +    char *file_name, *parent;\r
120 +    notmuch_status_t status;\r
121 +    void *local = NULL;\r
122 +    FILE *file_ptr = NULL;\r
123 +\r
124 +    if (notmuch_path == NULL || key == NULL || val == NULL)\r
125 +       return NOTMUCH_STATUS_NULL_POINTER;\r
126 +\r
127 +    local = talloc_new (NULL);\r
128 +    \r
129 +    status = compute_paths (local, notmuch_path, key, &parent, &file_name);\r
130 +    if (status) \r
131 +       goto DONE;\r
132 +\r
133 +    file_ptr = fopen (file_name, "r");\r
134 +    if (file_ptr == NULL) {\r
135 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
136 +       goto DONE;\r
137 +    }\r
138 +\r
139 +    while ((line_len = getline (&line, &line_size, file_ptr)) != -1) {\r
140 +\r
141 +       if (buf)\r
142 +           buf = talloc_asprintf (local, "%s%s", buf, line);\r
143 +       else \r
144 +           buf = talloc_strdup (local, line);\r
145 +       \r
146 +       if (buf == NULL) {\r
147 +           status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
148 +           goto DONE;\r
149 +       }\r
150 +\r
151 +    }\r
152 +\r
153 +\r
154 +    /* remove the last newline. Convenient for the single line case. */\r
155 +    chomp_newline (buf);\r
156 +\r
157 +    *val = buf;\r
158 +    status =  NOTMUCH_STATUS_SUCCESS;\r
159 +       \r
160 + DONE:\r
161 +    if (line)\r
162 +       free (line);\r
163 +    \r
164 +    if (file_ptr)\r
165 +       fclose (file_ptr);\r
166 +\r
167 +    talloc_free (local);\r
168 +\r
169 +    return status;\r
170 +}\r
171 +\r
172 +notmuch_status_t\r
173 +notmuch_config_set (const char *notmuch_path, const char *key, const char *val){\r
174 +\r
175 +    char *parent, *path, *temp_path;\r
176 +    int out_fd = -1;\r
177 +    notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;\r
178 +    void *local = NULL;\r
179 +    FILE *out_file;\r
180 +    \r
181 +    if (notmuch_path == NULL || key == NULL || val == NULL)\r
182 +       return NOTMUCH_STATUS_NULL_POINTER;\r
183 +\r
184 +    if (has_double_dot_component (key))\r
185 +       return NOTMUCH_STATUS_UNSUPPORTED_OPERATION;\r
186 +\r
187 +    local = talloc_new (NULL);\r
188 +\r
189 +    status = compute_paths (local, notmuch_path, key, &parent, &path);\r
190 +    if (status) \r
191 +       goto DONE;\r
192 +\r
193 +    if (! mkdir_recursive (local, parent, 0700)) {\r
194 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
195 +       goto DONE;\r
196 +    }\r
197 +\r
198 +    temp_path = talloc_asprintf (local, "%s/tmp.XXXXXX", parent);\r
199 +    if (temp_path == NULL) {\r
200 +       status = NOTMUCH_STATUS_OUT_OF_MEMORY;\r
201 +       goto DONE;\r
202 +    }\r
203 +\r
204 +    out_fd = mkstemp (temp_path);\r
205 +    if (out_fd == -1) {\r
206 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
207 +       goto DONE;\r
208 +    }\r
209 +    \r
210 +    out_file = fdopen (out_fd, "w");\r
211 +    if (out_file == NULL) {\r
212 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
213 +       goto DONE;\r
214 +    }\r
215 +\r
216 +    if (fputs (val, out_file) == EOF) {\r
217 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
218 +       goto DONE;\r
219 +    }\r
220 +\r
221 +    if (fclose (out_file)) {\r
222 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
223 +       goto DONE;\r
224 +    }\r
225 +\r
226 +    if (rename (temp_path, path) < 0) {\r
227 +       status = NOTMUCH_STATUS_FILE_ERROR;\r
228 +       goto DONE;\r
229 +    }\r
230 +       \r
231 + DONE:\r
232 +\r
233 +    talloc_free(local);\r
234 +    \r
235 +    return status;\r
236 +}\r
237 diff --git a/lib/notmuch.h b/lib/notmuch.h\r
238 index fe2340b..9a5f9df 100644\r
239 --- a/lib/notmuch.h\r
240 +++ b/lib/notmuch.h\r
241 @@ -192,6 +192,13 @@ typedef struct _notmuch_directory notmuch_directory_t;\r
242  typedef struct _notmuch_filenames notmuch_filenames_t;\r
243  #endif /* __DOXYGEN__ */\r
244  \r
245 +\r
246 +notmuch_status_t\r
247 +notmuch_config_get (const char *notmuch_path, const char *key, const char **val);\r
248 +\r
249 +notmuch_status_t\r
250 +notmuch_config_set (const char *notmuch_path, const char *key, const char *val);\r
251 +\r
252  /**\r
253   * Create a new, empty notmuch database located at 'path'.\r
254   *\r
255 diff --git a/test/Makefile.local b/test/Makefile.local\r
256 index a2d58fc..8a203f0 100644\r
257 --- a/test/Makefile.local\r
258 +++ b/test/Makefile.local\r
259 @@ -23,6 +23,9 @@ random_corpus_deps =  $(dir)/random-corpus.o  $(dir)/database-test.o \\r
260                         lib/libnotmuch.a util/libutil.a \\r
261                         parse-time-string/libparse-time-string.a\r
262  \r
263 +config_test_deps =  $(dir)/config-test.o  \\r
264 +                       lib/libnotmuch.a util/libutil.a\r
265 +\r
266  $(dir)/random-corpus: $(random_corpus_deps)\r
267         $(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)\r
268  \r
269 @@ -38,6 +41,9 @@ $(dir)/parse-time: $(dir)/parse-time.o parse-time-string/parse-time-string.o\r
270  $(dir)/make-db-version: $(dir)/make-db-version.o\r
271         $(call quiet,CXX) $^ -o $@ $(XAPIAN_LDFLAGS)\r
272  \r
273 +$(dir)/config-test: $(config_test_deps)\r
274 +       $(call quiet,CXX) $(CFLAGS_FINAL) $^ -o $@ $(CONFIGURE_LDFLAGS)\r
275 +\r
276  .PHONY: test check\r
277  \r
278  test_main_srcs=$(dir)/arg-test.c \\r
279 @@ -47,6 +53,7 @@ test_main_srcs=$(dir)/arg-test.c \\r
280               $(dir)/smtp-dummy.c \\r
281               $(dir)/symbol-test.cc \\r
282               $(dir)/make-db-version.cc \\r
283 +             $(dir)/config-test.c \\r
284  \r
285  test_srcs=$(test_main_srcs) $(dir)/database-test.c\r
286  \r
287 diff --git a/test/T560-lib-config.sh b/test/T560-lib-config.sh\r
288 new file mode 100755\r
289 index 0000000..ec8ddbe\r
290 --- /dev/null\r
291 +++ b/test/T560-lib-config.sh\r
292 @@ -0,0 +1,15 @@\r
293 +#!/usr/bin/env bash\r
294 +test_description="library config handling"\r
295 +\r
296 +. ./test-lib.sh\r
297 +\r
298 +test_begin_subtest "getting and setting"\r
299 +${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a foo\r
300 +${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set a/b bar\r
301 +${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch set b/a fub\r
302 +${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a  >> OUTPUT\r
303 +${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get a/b  >> OUTPUT\r
304 +${TEST_DIRECTORY}/config-test ${MAIL_DIR}/.notmuch get b/a  >> OUTPUT\r
305 +test_expect_equal "$(cat OUTPUT)" "foobarfub"\r
306 +\r
307 +test_done\r
308 diff --git a/test/config-test.c b/test/config-test.c\r
309 new file mode 100644\r
310 index 0000000..d9a1116\r
311 --- /dev/null\r
312 +++ b/test/config-test.c\r
313 @@ -0,0 +1,28 @@\r
314 +#include <stdio.h>\r
315 +#include <string.h>\r
316 +\r
317 +#include "notmuch.h"\r
318 +\r
319 +int\r
320 +main (int argc, char **argv) {\r
321 +    const char *val;\r
322 +    notmuch_status_t status;\r
323 +\r
324 +    if (argc == 4 && strcmp (argv[2], "get") == 0) {\r
325 +       \r
326 +       status = notmuch_config_get (argv[1], argv[3], &val);\r
327 +       if (status) \r
328 +           return status;\r
329 +       fputs (val, stdout);\r
330 +       return 0;\r
331 +\r
332 +    } else  if (argc == 5 && strcmp (argv[2], "set") == 0) {\r
333 +       \r
334 +       status = notmuch_config_set (argv[1], argv[3], argv[4]);\r
335 +       if (status) \r
336 +           return status;\r
337 +       return 0;\r
338 +    }\r
339 +\r
340 +    return 1;\r
341 +}\r
342 -- \r
343 2.1.0\r
344 \r