1 Return-Path: <bremner@tethera.net>
\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 arlo.cworth.org (Postfix) with ESMTP id 394A06DE0926
\r
6 for <notmuch@notmuchmail.org>; Mon, 30 May 2016 04:50:36 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at cworth.org
\r
11 X-Spam-Status: No, score=-0.012 tagged_above=-999 required=5
\r
12 tests=[AWL=-0.001, SPF_PASS=-0.001, T_RP_MATCHES_RCVD=-0.01]
\r
14 Received: from arlo.cworth.org ([127.0.0.1])
\r
15 by localhost (arlo.cworth.org [127.0.0.1]) (amavisd-new, port 10024)
\r
16 with ESMTP id mMopsA6eUKDo for <notmuch@notmuchmail.org>;
\r
17 Mon, 30 May 2016 04:50:27 -0700 (PDT)
\r
18 Received: from fethera.tethera.net (fethera.tethera.net [198.245.60.197])
\r
19 by arlo.cworth.org (Postfix) with ESMTPS id E60AE6DE02A6
\r
20 for <notmuch@notmuchmail.org>; Mon, 30 May 2016 04:50:12 -0700 (PDT)
\r
21 Received: from remotemail by fethera.tethera.net with local (Exim 4.84)
\r
22 (envelope-from <bremner@tethera.net>)
\r
23 id 1b7Lhq-0000Q4-20; Mon, 30 May 2016 07:50:02 -0400
\r
24 Received: (nullmailer pid 14852 invoked by uid 1000);
\r
25 Mon, 30 May 2016 11:50:06 -0000
\r
26 From: David Bremner <david@tethera.net>
\r
27 To: notmuch@notmuchmail.org
\r
28 Subject: [RFC2 Patch 3/5] lib: basic message-property API
\r
29 Date: Mon, 30 May 2016 08:49:57 -0300
\r
30 Message-Id: <1464608999-14774-4-git-send-email-david@tethera.net>
\r
31 X-Mailer: git-send-email 2.8.1
\r
32 In-Reply-To: <1464608999-14774-1-git-send-email-david@tethera.net>
\r
33 References: <1463927339-5441-1-git-send-email-david@tethera.net>
\r
34 <1464608999-14774-1-git-send-email-david@tethera.net>
\r
36 Content-Type: text/plain; charset=UTF-8
\r
37 Content-Transfer-Encoding: 8bit
\r
38 X-BeenThere: notmuch@notmuchmail.org
\r
39 X-Mailman-Version: 2.1.20
\r
41 List-Id: "Use and development of the notmuch mail system."
\r
42 <notmuch.notmuchmail.org>
\r
43 List-Unsubscribe: <https://notmuchmail.org/mailman/options/notmuch>,
\r
44 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
45 List-Archive: <http://notmuchmail.org/pipermail/notmuch/>
\r
46 List-Post: <mailto:notmuch@notmuchmail.org>
\r
47 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
48 List-Subscribe: <https://notmuchmail.org/mailman/listinfo/notmuch>,
\r
49 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
50 X-List-Received-Date: Mon, 30 May 2016 11:50:36 -0000
\r
52 Initially, support get, set and remove of single key/value pair
\r
54 lib/Makefile.local | 1 +
\r
55 lib/message-private.h | 10 +++++
\r
56 lib/message-property.cc | 86 +++++++++++++++++++++++++++++++++++++++
\r
57 lib/message.cc | 48 ++++++++++++++++++++++
\r
58 lib/notmuch.h | 14 +++++++
\r
59 test/T610-message-property.sh | 94 +++++++++++++++++++++++++++++++++++++++++++
\r
60 6 files changed, 253 insertions(+)
\r
61 create mode 100644 lib/message-private.h
\r
62 create mode 100644 lib/message-property.cc
\r
63 create mode 100755 test/T610-message-property.sh
\r
65 diff --git a/lib/Makefile.local b/lib/Makefile.local
\r
66 index 9280880..c012ed1 100644
\r
67 --- a/lib/Makefile.local
\r
68 +++ b/lib/Makefile.local
\r
69 @@ -49,6 +49,7 @@ libnotmuch_cxx_srcs = \
\r
70 $(dir)/directory.cc \
\r
73 + $(dir)/message-property.cc \
\r
75 $(dir)/query-fp.cc \
\r
77 diff --git a/lib/message-private.h b/lib/message-private.h
\r
78 new file mode 100644
\r
79 index 0000000..61e5bac
\r
81 +++ b/lib/message-private.h
\r
83 +#ifndef MESSAGE_PRIVATE_H
\r
84 +#define MESSAGE_PRIVATE_H
\r
86 +notmuch_string_map_t *
\r
87 +_notmuch_message_property_map (notmuch_message_t *message);
\r
90 +_notmuch_message_frozen (notmuch_message_t *message);
\r
93 diff --git a/lib/message-property.cc b/lib/message-property.cc
\r
94 new file mode 100644
\r
95 index 0000000..21348a3
\r
97 +++ b/lib/message-property.cc
\r
99 +/* message-property.cc - Properties are like tags, but (key,value) pairs.
\r
100 + * keys are allowed to repeat.
\r
102 + * This file is part of notmuch.
\r
104 + * Copyright © 2016 David Bremner
\r
106 + * This program is free software: you can redistribute it and/or modify
\r
107 + * it under the terms of the GNU General Public License as published by
\r
108 + * the Free Software Foundation, either version 3 of the License, or
\r
109 + * (at your option) any later version.
\r
111 + * This program is distributed in the hope that it will be useful,
\r
112 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
113 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
114 + * GNU General Public License for more details.
\r
116 + * You should have received a copy of the GNU General Public License
\r
117 + * along with this program. If not, see http://www.gnu.org/licenses/ .
\r
119 + * Author: David Bremner <david@tethera.net>
\r
122 +#include "notmuch-private.h"
\r
123 +#include "database-private.h"
\r
124 +#include "message-private.h"
\r
127 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)
\r
130 + return NOTMUCH_STATUS_NULL_POINTER;
\r
132 + *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key);
\r
134 + return NOTMUCH_STATUS_SUCCESS;
\r
137 +static notmuch_status_t
\r
138 +_notmuch_message_modify_property (notmuch_message_t *message, const char *key, const char *value,
\r
139 + notmuch_bool_t delete_it)
\r
141 + notmuch_private_status_t private_status;
\r
142 + notmuch_status_t status;
\r
143 + char *term = NULL;
\r
145 + status = _notmuch_database_ensure_writable (_notmuch_message_database (message));
\r
149 + if (key == NULL || value == NULL)
\r
150 + return NOTMUCH_STATUS_NULL_POINTER;
\r
152 + if (index (key, '=') || index (value, '='))
\r
153 + return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
\r
155 + term = talloc_asprintf (message, "%s=%s", key, value);
\r
158 + private_status = _notmuch_message_remove_term (message, "property", term);
\r
160 + private_status = _notmuch_message_add_term (message, "property", term);
\r
162 + if (private_status)
\r
163 + return COERCE_STATUS (private_status,
\r
164 + "Unhandled error modifying message property");
\r
165 + if (! _notmuch_message_frozen (message))
\r
166 + _notmuch_message_sync (message);
\r
169 + talloc_free (term);
\r
171 + return NOTMUCH_STATUS_SUCCESS;
\r
175 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value)
\r
177 + return _notmuch_message_modify_property (message, key, value, FALSE);
\r
181 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value)
\r
183 + return _notmuch_message_modify_property (message, key, value, TRUE);
\r
185 diff --git a/lib/message.cc b/lib/message.cc
\r
186 index 47946a3..0eb7e2c 100644
\r
187 --- a/lib/message.cc
\r
188 +++ b/lib/message.cc
\r
191 #include "notmuch-private.h"
\r
192 #include "database-private.h"
\r
193 +#include "message-private.h"
\r
195 #include <stdint.h>
\r
197 @@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message)
\r
199 return message->notmuch;
\r
203 +_notmuch_message_ensure_property_map (notmuch_message_t *message)
\r
205 + notmuch_string_node_t *node;
\r
207 + if (message->property_map)
\r
210 + if (!message->property_term_list)
\r
211 + _notmuch_message_ensure_metadata (message);
\r
213 + message->property_map = _notmuch_string_map_create (message);
\r
215 + for (node = message->property_term_list->head; node; node = node->next) {
\r
219 + value = index(node->string, '=');
\r
221 + INTERNAL_ERROR ("malformed property term");
\r
225 + key = node->string;
\r
227 + _notmuch_string_map_append (message->property_map, key, value);
\r
231 + talloc_free (message->property_term_list);
\r
232 + message->property_term_list = NULL;
\r
235 +notmuch_string_map_t *
\r
236 +_notmuch_message_property_map (notmuch_message_t *message)
\r
238 + _notmuch_message_ensure_property_map (message);
\r
240 + return message->property_map;
\r
244 +_notmuch_message_frozen (notmuch_message_t *message)
\r
246 + return message->frozen;
\r
248 diff --git a/lib/notmuch.h b/lib/notmuch.h
\r
249 index bd977c3..c9e654e 100644
\r
250 --- a/lib/notmuch.h
\r
251 +++ b/lib/notmuch.h
\r
252 @@ -180,6 +180,11 @@ typedef enum _notmuch_status {
\r
254 NOTMUCH_STATUS_PATH_ERROR,
\r
256 + * One of the arguments violates the preconditions for the
\r
257 + * function, in a way not covered by a more specific argument.
\r
259 + NOTMUCH_STATUS_ILLEGAL_ARGUMENT,
\r
261 * Not an actual status value. Just a way to find out how many
\r
262 * valid status values there are.
\r
264 @@ -1651,6 +1656,15 @@ notmuch_message_thaw (notmuch_message_t *message);
\r
266 notmuch_message_destroy (notmuch_message_t *message);
\r
269 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value);
\r
272 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value);
\r
275 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value);
\r
278 * Is the given 'tags' iterator pointing at a valid tag.
\r
280 diff --git a/test/T610-message-property.sh b/test/T610-message-property.sh
\r
281 new file mode 100755
\r
282 index 0000000..45ed66b
\r
284 +++ b/test/T610-message-property.sh
\r
286 +#!/usr/bin/env bash
\r
287 +test_description="message property API"
\r
289 +. ./test-lib.sh || exit 1
\r
293 +cat <<EOF > c_head
\r
294 +#include <stdio.h>
\r
295 +#include <string.h>
\r
296 +#include <stdlib.h>
\r
297 +#include <notmuch.h>
\r
299 +void run(int line, notmuch_status_t ret)
\r
302 + fprintf (stderr, "line %d: %s\n", line, ret);
\r
307 +#define RUN(v) run(__LINE__, v);
\r
309 +int main (int argc, char** argv)
\r
311 + notmuch_database_t *db;
\r
312 + notmuch_message_t *message = NULL;
\r
314 + notmuch_status_t stat;
\r
316 + RUN(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
\r
317 + RUN(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message));
\r
318 + if (message == NULL) {
\r
319 + fprintf (stderr, "unable to find message");
\r
324 +cat <<EOF > c_tail
\r
325 + RUN(notmuch_database_destroy(db));
\r
329 +test_begin_subtest "notmuch_message_{add,get,remove}_property"
\r
330 +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
\r
332 + RUN(notmuch_message_add_property (message, "testkey1", "testvalue1"));
\r
333 + RUN(notmuch_message_get_property (message, "testkey1", &val));
\r
334 + printf("testkey1[1] = %s\n", val);
\r
335 + RUN(notmuch_message_add_property (message, "testkey2", "testvalue2"));
\r
336 + RUN(notmuch_message_get_property (message, "testkey1", &val));
\r
337 + printf("testkey1[2] = %s\n", val);
\r
338 + RUN(notmuch_message_get_property (message, "testkey1", &val));
\r
340 + RUN(notmuch_message_get_property (message, "testkey2", &val));
\r
341 + printf("testkey2 = %s\n", val);
\r
343 + /* Add second value for key */
\r
344 + RUN(notmuch_message_add_property (message, "testkey2", "testvalue3"));
\r
345 + RUN(notmuch_message_get_property (message, "testkey2", &val));
\r
346 + printf("testkey2 = %s\n", val);
\r
348 + /* remove first value for key */
\r
349 + RUN(notmuch_message_remove_property (message, "testkey2", "testvalue2"));
\r
350 + RUN(notmuch_message_get_property (message, "testkey2", &val));
\r
351 + printf("testkey2 = %s\n", val);
\r
353 + /* remove non-existant value for key */
\r
354 + RUN(notmuch_message_remove_property (message, "testkey2", "testvalue2"));
\r
355 + RUN(notmuch_message_get_property (message, "testkey2", &val));
\r
356 + printf("testkey2 = %s\n", val);
\r
358 + /* remove only value for key */
\r
359 + RUN(notmuch_message_remove_property (message, "testkey2", "testvalue3"));
\r
360 + RUN(notmuch_message_get_property (message, "testkey2", &val));
\r
361 + printf("testkey2 = %s\n", val == NULL ? "NULL" : val);
\r
364 +cat <<'EOF' >EXPECTED
\r
366 +testkey1[1] = testvalue1
\r
367 +testkey1[2] = testvalue1
\r
368 +testkey2 = testvalue2
\r
369 +testkey2 = testvalue2
\r
370 +testkey2 = testvalue3
\r
371 +testkey2 = testvalue3
\r
375 +test_expect_equal_file EXPECTED OUTPUT
\r