[PATCH] RFC: all deleting all properties with a given key
[notmuch-archives.git] / c6 / 97c5df5a026a94ba3bb53230c24dbf1686d11c
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
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -0.012\r
10 X-Spam-Level: \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
13  autolearn=disabled\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
35 MIME-Version: 1.0\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
40 Precedence: list\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
51 \r
52 Initially, support get, set and remove of single key/value pair\r
53 ---\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
64 \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
71         $(dir)/index.cc         \\r
72         $(dir)/message.cc       \\r
73 +       $(dir)/message-property.cc \\r
74         $(dir)/query.cc         \\r
75         $(dir)/query-fp.cc      \\r
76         $(dir)/config.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
80 --- /dev/null\r
81 +++ b/lib/message-private.h\r
82 @@ -0,0 +1,10 @@\r
83 +#ifndef MESSAGE_PRIVATE_H\r
84 +#define MESSAGE_PRIVATE_H\r
85 +\r
86 +notmuch_string_map_t *\r
87 +_notmuch_message_property_map (notmuch_message_t *message);\r
88 +\r
89 +notmuch_bool_t\r
90 +_notmuch_message_frozen (notmuch_message_t *message);\r
91 +\r
92 +#endif\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
96 --- /dev/null\r
97 +++ b/lib/message-property.cc\r
98 @@ -0,0 +1,86 @@\r
99 +/* message-property.cc - Properties are like tags, but (key,value) pairs.\r
100 + * keys are allowed to repeat.\r
101 + *\r
102 + * This file is part of notmuch.\r
103 + *\r
104 + * Copyright © 2016 David Bremner\r
105 + *\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
110 + *\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
115 + *\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
118 + *\r
119 + * Author: David Bremner <david@tethera.net>\r
120 + */\r
121 +\r
122 +#include "notmuch-private.h"\r
123 +#include "database-private.h"\r
124 +#include "message-private.h"\r
125 +\r
126 +notmuch_status_t\r
127 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value)\r
128 +{\r
129 +    if (! value)\r
130 +       return NOTMUCH_STATUS_NULL_POINTER;\r
131 +\r
132 +    *value = _notmuch_string_map_get (_notmuch_message_property_map (message), key);\r
133 +\r
134 +    return NOTMUCH_STATUS_SUCCESS;\r
135 +}\r
136 +\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
140 +{\r
141 +    notmuch_private_status_t private_status;\r
142 +    notmuch_status_t status;\r
143 +    char *term = NULL;\r
144 +\r
145 +    status = _notmuch_database_ensure_writable (_notmuch_message_database (message));\r
146 +    if (status)\r
147 +       return status;\r
148 +\r
149 +    if (key == NULL || value == NULL)\r
150 +       return NOTMUCH_STATUS_NULL_POINTER;\r
151 +\r
152 +    if (index (key, '=') || index (value, '='))\r
153 +       return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;\r
154 +\r
155 +    term = talloc_asprintf (message, "%s=%s", key, value);\r
156 +\r
157 +    if (delete_it)\r
158 +       private_status = _notmuch_message_remove_term (message, "property", term);\r
159 +    else\r
160 +       private_status = _notmuch_message_add_term (message, "property", term);\r
161 +\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
167 +\r
168 +    if (term)\r
169 +       talloc_free (term);\r
170 +\r
171 +    return NOTMUCH_STATUS_SUCCESS;\r
172 +}\r
173 +\r
174 +notmuch_status_t\r
175 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value)\r
176 +{\r
177 +    return _notmuch_message_modify_property (message, key, value, FALSE);\r
178 +}\r
179 +\r
180 +notmuch_status_t\r
181 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value)\r
182 +{\r
183 +    return _notmuch_message_modify_property (message, key, value, TRUE);\r
184 +}\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
189 @@ -20,6 +20,7 @@\r
190  \r
191  #include "notmuch-private.h"\r
192  #include "database-private.h"\r
193 +#include "message-private.h"\r
194  \r
195  #include <stdint.h>\r
196  \r
197 @@ -1799,3 +1800,50 @@ _notmuch_message_database (notmuch_message_t *message)\r
198  {\r
199      return message->notmuch;\r
200  }\r
201 +\r
202 +void\r
203 +_notmuch_message_ensure_property_map (notmuch_message_t *message)\r
204 +{\r
205 +    notmuch_string_node_t *node;\r
206 +\r
207 +    if (message->property_map)\r
208 +       return;\r
209 +\r
210 +    if (!message->property_term_list)\r
211 +       _notmuch_message_ensure_metadata (message);\r
212 +\r
213 +    message->property_map = _notmuch_string_map_create (message);\r
214 +\r
215 +    for (node = message->property_term_list->head; node; node = node->next) {\r
216 +       const char *key;\r
217 +       char *value;\r
218 +\r
219 +       value = index(node->string, '=');\r
220 +       if (!value)\r
221 +           INTERNAL_ERROR ("malformed property term");\r
222 +\r
223 +       *value = '\0';\r
224 +       value++;\r
225 +       key = node->string;\r
226 +\r
227 +       _notmuch_string_map_append (message->property_map, key, value);\r
228 +\r
229 +    }\r
230 +\r
231 +    talloc_free (message->property_term_list);\r
232 +    message->property_term_list = NULL;\r
233 +}\r
234 +\r
235 +notmuch_string_map_t *\r
236 +_notmuch_message_property_map (notmuch_message_t *message)\r
237 +{\r
238 +    _notmuch_message_ensure_property_map (message);\r
239 +\r
240 +    return message->property_map;\r
241 +}\r
242 +\r
243 +notmuch_bool_t\r
244 +_notmuch_message_frozen (notmuch_message_t *message)\r
245 +{\r
246 +    return message->frozen;\r
247 +}\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
253       */\r
254      NOTMUCH_STATUS_PATH_ERROR,\r
255      /**\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
258 +     */\r
259 +    NOTMUCH_STATUS_ILLEGAL_ARGUMENT,\r
260 +    /**\r
261       * Not an actual status value. Just a way to find out how many\r
262       * valid status values there are.\r
263       */\r
264 @@ -1651,6 +1656,15 @@ notmuch_message_thaw (notmuch_message_t *message);\r
265  void\r
266  notmuch_message_destroy (notmuch_message_t *message);\r
267  \r
268 +notmuch_status_t\r
269 +notmuch_message_get_property (notmuch_message_t *message, const char *key, const char **value);\r
270 +\r
271 +notmuch_status_t\r
272 +notmuch_message_add_property (notmuch_message_t *message, const char *key, const char *value);\r
273 +\r
274 +notmuch_status_t\r
275 +notmuch_message_remove_property (notmuch_message_t *message, const char *key, const char *value);\r
276 +\r
277  /**\r
278   * Is the given 'tags' iterator pointing at a valid tag.\r
279   *\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
283 --- /dev/null\r
284 +++ b/test/T610-message-property.sh\r
285 @@ -0,0 +1,94 @@\r
286 +#!/usr/bin/env bash\r
287 +test_description="message property API"\r
288 +\r
289 +. ./test-lib.sh || exit 1\r
290 +\r
291 +add_email_corpus\r
292 +\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
298 +\r
299 +void run(int line, notmuch_status_t ret)\r
300 +{\r
301 +   if (ret) {\r
302 +       fprintf (stderr, "line %d: %s\n", line, ret);\r
303 +       exit (1);\r
304 +   }\r
305 +}\r
306 +\r
307 +#define RUN(v)  run(__LINE__, v);\r
308 +\r
309 +int main (int argc, char** argv)\r
310 +{\r
311 +   notmuch_database_t *db;\r
312 +   notmuch_message_t *message = NULL;\r
313 +   const char *val;\r
314 +   notmuch_status_t stat;\r
315 +\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
320 +       exit (1);\r
321 +   }\r
322 +EOF\r
323 +\r
324 +cat <<EOF > c_tail\r
325 +   RUN(notmuch_database_destroy(db));\r
326 +}\r
327 +EOF\r
328 +\r
329 +test_begin_subtest "notmuch_message_{add,get,remove}_property"\r
330 +cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}\r
331 +{\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
339 +\r
340 +   RUN(notmuch_message_get_property (message, "testkey2", &val));\r
341 +   printf("testkey2 = %s\n", val);\r
342 +\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
347 +\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
352 +\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
357 +\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
362 +}\r
363 +EOF\r
364 +cat <<'EOF' >EXPECTED\r
365 +== stdout ==\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
372 +testkey2 = NULL\r
373 +== stderr ==\r
374 +EOF\r
375 +test_expect_equal_file EXPECTED OUTPUT\r
376 +\r
377 +\r
378 +\r
379 +test_done\r
380 -- \r
381 2.8.1\r
382 \r