[PATCH 0/4] Allow specifying alternate names for addresses in other_email
[notmuch-archives.git] / 55 / 837f1219a49f99fd2c64d465d0e084fdf7efd7
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 olra.theworths.org (Postfix) with ESMTP id 71455431FB6\r
6         for <notmuch@notmuchmail.org>; Sat, 24 Nov 2012 13:20:47 -0800 (PST)\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 AqECwR8AU9vM for <notmuch@notmuchmail.org>;\r
16         Sat, 24 Nov 2012 13:20:44 -0800 (PST)\r
17 Received: from tesseract.cs.unb.ca (tesseract.cs.unb.ca [131.202.240.238])\r
18         (using TLSv1 with cipher AES256-SHA (256/256 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id 7946F431FDA\r
21         for <notmuch@notmuchmail.org>; Sat, 24 Nov 2012 13:20:33 -0800 (PST)\r
22 Received: from fctnnbsc30w-156034089108.dhcp-dynamic.fibreop.nb.bellaliant.net\r
23         ([156.34.89.108] helo=zancas.localnet)\r
24         by tesseract.cs.unb.ca with esmtpsa\r
25         (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.72)\r
26         (envelope-from <bremner@tethera.net>)\r
27         id 1TcN9M-0006SP-Eo; Sat, 24 Nov 2012 17:20:32 -0400\r
28 Received: from bremner by zancas.localnet with local (Exim 4.80)\r
29         (envelope-from <bremner@tethera.net>)\r
30         id 1TcN9G-0008D3-TD; Sat, 24 Nov 2012 17:20:26 -0400\r
31 From: david@tethera.net\r
32 To: notmuch@notmuchmail.org\r
33 Subject: [Patch v2 09/17] tag-util.[ch]: New files for common tagging routines\r
34 Date: Sat, 24 Nov 2012 17:20:09 -0400\r
35 Message-Id: <1353792017-31459-10-git-send-email-david@tethera.net>\r
36 X-Mailer: git-send-email 1.7.10.4\r
37 In-Reply-To: <1353792017-31459-1-git-send-email-david@tethera.net>\r
38 References: <1353792017-31459-1-git-send-email-david@tethera.net>\r
39 X-Spam_bar: -\r
40 Cc: David Bremner <bremner@debian.org>\r
41 X-BeenThere: notmuch@notmuchmail.org\r
42 X-Mailman-Version: 2.1.13\r
43 Precedence: list\r
44 List-Id: "Use and development of the notmuch mail system."\r
45         <notmuch.notmuchmail.org>\r
46 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
47         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
48 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
49 List-Post: <mailto:notmuch@notmuchmail.org>\r
50 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
51 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
52         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
53 X-List-Received-Date: Sat, 24 Nov 2012 21:20:48 -0000\r
54 \r
55 From: David Bremner <bremner@debian.org>\r
56 \r
57 These are meant to be shared between notmuch-tag and notmuch-restore.\r
58 \r
59 The bulk of the routines implement a "tag operation list" abstract\r
60 data type act as a structured representation of a set of tag\r
61 operations (typically coming from a single tag command or line of\r
62 input).\r
63 ---\r
64  Makefile.local |    1 +\r
65  tag-util.c     |  264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
66  tag-util.h     |  120 ++++++++++++++++++++++++++\r
67  3 files changed, 385 insertions(+)\r
68  create mode 100644 tag-util.c\r
69  create mode 100644 tag-util.h\r
70 \r
71 diff --git a/Makefile.local b/Makefile.local\r
72 index 2b91946..854867d 100644\r
73 --- a/Makefile.local\r
74 +++ b/Makefile.local\r
75 @@ -274,6 +274,7 @@ notmuch_client_srcs =               \\r
76         query-string.c          \\r
77         mime-node.c             \\r
78         crypto.c                \\r
79 +       tag-util.c\r
80  \r
81  notmuch_client_modules = $(notmuch_client_srcs:.c=.o)\r
82  \r
83 diff --git a/tag-util.c b/tag-util.c\r
84 new file mode 100644\r
85 index 0000000..287cc67\r
86 --- /dev/null\r
87 +++ b/tag-util.c\r
88 @@ -0,0 +1,264 @@\r
89 +#include "string-util.h"\r
90 +#include "tag-util.h"\r
91 +#include "hex-escape.h"\r
92 +\r
93 +struct _tag_operation_t {\r
94 +    const char *tag;\r
95 +    notmuch_bool_t remove;\r
96 +};\r
97 +\r
98 +struct _tag_op_list_t {\r
99 +    tag_operation_t *ops;\r
100 +    int count;\r
101 +    int size;\r
102 +};\r
103 +\r
104 +int\r
105 +parse_tag_line (void *ctx, char *line,\r
106 +               tag_op_flag_t flags,\r
107 +               char **query_string,\r
108 +               tag_op_list_t *tag_ops)\r
109 +{\r
110 +    char *tok = line;\r
111 +    size_t tok_len = 0;\r
112 +\r
113 +    chomp_newline (line);\r
114 +\r
115 +    /* remove leading space */\r
116 +    while (*tok == ' ' || *tok == '\t')\r
117 +       tok++;\r
118 +\r
119 +    /* Skip empty and comment lines. */\r
120 +    if (*tok == '\0' || *tok == '#')\r
121 +           return 1;\r
122 +\r
123 +    tag_op_list_reset (tag_ops);\r
124 +\r
125 +    /* Parse tags. */\r
126 +    while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) {\r
127 +       notmuch_bool_t remove;\r
128 +       char *tag;\r
129 +\r
130 +       /* Optional explicit end of tags marker. */\r
131 +       if (strncmp (tok, "--", tok_len) == 0) {\r
132 +           tok = strtok_len (tok + tok_len, " ", &tok_len);\r
133 +           break;\r
134 +       }\r
135 +\r
136 +       /* Implicit end of tags. */\r
137 +       if (*tok != '-' && *tok != '+')\r
138 +           break;\r
139 +\r
140 +       /* If tag is terminated by NUL, there's no query string. */\r
141 +       if (*(tok + tok_len) == '\0') {\r
142 +           tok = NULL;\r
143 +           break;\r
144 +       }\r
145 +\r
146 +       /* Terminate, and start next token after terminator. */\r
147 +       *(tok + tok_len++) = '\0';\r
148 +\r
149 +       remove = (*tok == '-');\r
150 +       tag = tok + 1;\r
151 +\r
152 +       /* Maybe refuse empty tags. */\r
153 +       if (!(flags & TAG_FLAG_BE_GENEROUS) && *tag == '\0') {\r
154 +           tok = NULL;\r
155 +           break;\r
156 +       }\r
157 +\r
158 +       /* Decode tag. */\r
159 +       if (hex_decode_inplace (tag) != HEX_SUCCESS) {\r
160 +           tok = NULL;\r
161 +           break;\r
162 +       }\r
163 +\r
164 +       if (tag_op_list_append (ctx, tag_ops, tag, remove))\r
165 +           return -1;\r
166 +    }\r
167 +\r
168 +    if (tok == NULL || tag_ops->count == 0) {\r
169 +       /* FIXME: line has been modified! */\r
170 +       fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
171 +                line);\r
172 +       return 1;\r
173 +    }\r
174 +\r
175 +    /* tok now points to the query string */\r
176 +    if (hex_decode_inplace (tok) != HEX_SUCCESS) {\r
177 +       /* FIXME: line has been modified! */\r
178 +       fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
179 +                line);\r
180 +       return 1;\r
181 +    }\r
182 +\r
183 +    *query_string = tok;\r
184 +\r
185 +    return 0;\r
186 +}\r
187 +\r
188 +static inline void\r
189 +message_error (notmuch_message_t *message,\r
190 +              notmuch_status_t status,\r
191 +              const char *format, ...)\r
192 +{\r
193 +    va_list va_args;\r
194 +\r
195 +    va_start (va_args, format);\r
196 +\r
197 +    vfprintf (stderr, format, va_args);\r
198 +    fprintf (stderr, "Message-ID: %s\n", notmuch_message_get_message_id (message));\r
199 +    fprintf (stderr, "Status: %s\n", notmuch_status_to_string (status));\r
200 +}\r
201 +\r
202 +notmuch_status_t\r
203 +tag_op_list_apply (notmuch_message_t *message,\r
204 +                  tag_op_list_t *list,\r
205 +                  tag_op_flag_t flags)\r
206 +{\r
207 +    int i;\r
208 +\r
209 +    notmuch_status_t status = 0;\r
210 +    tag_operation_t *tag_ops = list->ops;\r
211 +\r
212 +    status = notmuch_message_freeze (message);\r
213 +    if (status) {\r
214 +       message_error (message, status, "freezing message");\r
215 +       return status;\r
216 +    }\r
217 +\r
218 +    if (flags & TAG_FLAG_REMOVE_ALL) {\r
219 +       status = notmuch_message_remove_all_tags (message);\r
220 +       if (status) {\r
221 +           message_error (message, status, "removing all tags" );\r
222 +           return status;\r
223 +       }\r
224 +    }\r
225 +\r
226 +    for (i = 0; i < list->count; i++) {\r
227 +       if (tag_ops[i].remove) {\r
228 +           status = notmuch_message_remove_tag (message, tag_ops[i].tag);\r
229 +           if (status) {\r
230 +               message_error (message, status, "removing tag %s", tag_ops[i].tag);\r
231 +               return status;\r
232 +           }\r
233 +       } else {\r
234 +           status = notmuch_message_add_tag (message, tag_ops[i].tag);\r
235 +           if (status) {\r
236 +               message_error (message, status, "adding tag %s", tag_ops[i].tag);\r
237 +               return status;\r
238 +           }\r
239 +\r
240 +       }\r
241 +    }\r
242 +\r
243 +    status = notmuch_message_thaw (message);\r
244 +    if (status) {\r
245 +       message_error (message, status, "thawing message");\r
246 +       return status;\r
247 +    }\r
248 +\r
249 +\r
250 +    if (flags & TAG_FLAG_MAILDIR_SYNC) {\r
251 +       status = notmuch_message_tags_to_maildir_flags (message);\r
252 +       if (status) {\r
253 +           message_error (message, status, "synching tags to maildir");\r
254 +           return status;\r
255 +       }\r
256 +    }\r
257 +\r
258 +    return NOTMUCH_STATUS_SUCCESS;\r
259 +\r
260 +}\r
261 +\r
262 +\r
263 +/* Array of tagging operations (add or remove), terminated with an\r
264 + * empty element. Size will be increased as necessary. */\r
265 +\r
266 +tag_op_list_t *\r
267 +tag_op_list_create (void *ctx)\r
268 +{\r
269 +    tag_op_list_t *list;\r
270 +\r
271 +    list = talloc (ctx, tag_op_list_t);\r
272 +    if (list == NULL)\r
273 +       return NULL;\r
274 +\r
275 +    list->size = TAG_OP_LIST_INITIAL_SIZE;\r
276 +    list->count = 0;\r
277 +\r
278 +    list->ops = talloc_array (ctx, tag_operation_t, list->size);\r
279 +    if (list->ops == NULL)\r
280 +       return NULL;\r
281 +\r
282 +    return list;\r
283 +}\r
284 +\r
285 +\r
286 +int\r
287 +tag_op_list_append (void *ctx,\r
288 +                   tag_op_list_t *list,\r
289 +                   const char *tag,\r
290 +                   notmuch_bool_t remove)\r
291 +{\r
292 +    /* Make room if current array is full.  This should be a fairly\r
293 +     * rare case, considering the initial array size.\r
294 +     */\r
295 +\r
296 +    if (list->count == list->size) {\r
297 +       list->size *= 2;\r
298 +       list->ops = talloc_realloc (ctx, list->ops, tag_operation_t,\r
299 +                                   list->size);\r
300 +       if (list->ops == NULL) {\r
301 +           fprintf (stderr, "Out of memory.\n");\r
302 +           return 1;\r
303 +       }\r
304 +    }\r
305 +\r
306 +    /* add the new operation */\r
307 +\r
308 +    list->ops[list->count].tag = tag;\r
309 +    list->ops[list->count].remove = remove;\r
310 +    list->count++;\r
311 +    return 0;\r
312 +}\r
313 +\r
314 +/*\r
315 + *   Is the i'th tag operation a remove?\r
316 + */\r
317 +\r
318 +notmuch_bool_t\r
319 +tag_op_list_isremove (const tag_op_list_t *list, size_t i)\r
320 +{\r
321 +    return list->ops[i].remove;\r
322 +}\r
323 +\r
324 +/*\r
325 + * Reset a list to contain no operations\r
326 + */\r
327 +\r
328 +void\r
329 +tag_op_list_reset (tag_op_list_t *list)\r
330 +{\r
331 +    list->count = 0;\r
332 +}\r
333 +\r
334 +/*\r
335 + * Return the number of operations in a list\r
336 + */\r
337 +\r
338 +size_t\r
339 +tag_op_list_size (const tag_op_list_t *list)\r
340 +{\r
341 +    return list->count;\r
342 +}\r
343 +\r
344 +/*\r
345 + *   return the i'th tag in the list\r
346 + */\r
347 +\r
348 +const char *\r
349 +tag_op_list_tag (const tag_op_list_t *list, size_t i)\r
350 +{\r
351 +    return list->ops[i].tag;\r
352 +}\r
353 diff --git a/tag-util.h b/tag-util.h\r
354 new file mode 100644\r
355 index 0000000..508806f\r
356 --- /dev/null\r
357 +++ b/tag-util.h\r
358 @@ -0,0 +1,120 @@\r
359 +#ifndef _TAG_UTIL_H\r
360 +#define _TAG_UTIL_H\r
361 +\r
362 +#include "notmuch-client.h"\r
363 +\r
364 +typedef struct _tag_operation_t tag_operation_t;\r
365 +typedef struct _tag_op_list_t tag_op_list_t;\r
366 +\r
367 +#define TAG_OP_LIST_INITIAL_SIZE 10\r
368 +\r
369 +/* Use powers of 2 */\r
370 +typedef enum  { TAG_FLAG_NONE = 0,\r
371 +               /* Operations are synced to maildir, if possible */\r
372 +\r
373 +               TAG_FLAG_MAILDIR_SYNC = 1,\r
374 +\r
375 +               /* Remove all tags from message before applying\r
376 +                * list */\r
377 +\r
378 +               TAG_FLAG_REMOVE_ALL = 2,\r
379 +\r
380 +               /* Don't try to avoid database operations.  Useful\r
381 +                * when we know that message passed needs these\r
382 +                *  operations. */\r
383 +\r
384 +               TAG_FLAG_PRE_OPTIMIZED = 4,\r
385 +\r
386 +               /* Accept strange tags that might be user error;\r
387 +                  intended for use by notmuch-restore.\r
388 +               */\r
389 +\r
390 +               TAG_FLAG_BE_GENEROUS = 8} tag_op_flag_t;\r
391 +\r
392 +/* Parse a string of the following format:\r
393 + *\r
394 + * +<tag>|-<tag> [...] [--] <search-terms>\r
395 + *\r
396 + * Each line is interpreted similarly to "notmuch tag" command line\r
397 + * arguments. The delimiter is one or more spaces ' '. Any characters\r
398 + * in <tag> and <search-terms> MAY be hex encoded with %NN where NN is\r
399 + * the hexadecimal value of the character. Any ' ' and '%' characters\r
400 + * in <tag> and <search-terms> MUST be hex encoded (using %20 and %25,\r
401 + * respectively). Any characters that are not part of <tag> or\r
402 + * <search-terms> MUST NOT be hex encoded.\r
403 + *\r
404 + * Leading and trailing space ' ' is ignored. Empty lines and lines\r
405 + * beginning with '#' are ignored.\r
406 + *\r
407 + * Returns: 0 for OK, 1 for skipped line, -1 for fatal(ish) error.\r
408 + *\r
409 + * Output Parameters:\r
410 + *     ops     contains a list of tag operations\r
411 + *     query_str the search terms.\r
412 + */\r
413 +int\r
414 +parse_tag_line (void *ctx, char *line,\r
415 +               tag_op_flag_t flags,\r
416 +               char **query_str, tag_op_list_t *ops);\r
417 +\r
418 +/*\r
419 + * Create an empty list of tag operations\r
420 + *\r
421 + * ctx is passed to talloc\r
422 + */\r
423 +\r
424 +tag_op_list_t\r
425 +*tag_op_list_create (void *ctx);\r
426 +\r
427 +/*\r
428 + * Add a tag operation (delete iff remote == TRUE) to a list.\r
429 + * The list is expanded as necessary.\r
430 + */\r
431 +\r
432 +int\r
433 +tag_op_list_append (void *ctx,\r
434 +                   tag_op_list_t *list,\r
435 +                   const char *tag,\r
436 +                   notmuch_bool_t remove);\r
437 +\r
438 +/*\r
439 + * Apply a list of tag operations, in order to a message.\r
440 + *\r
441 + * Flags can be bitwise ORed; see enum above for possibilies.\r
442 + */\r
443 +\r
444 +notmuch_status_t\r
445 +tag_op_list_apply (notmuch_message_t *message,\r
446 +                  tag_op_list_t *tag_ops,\r
447 +                  tag_op_flag_t flags);\r
448 +\r
449 +/*\r
450 + * Return the number of operations in a list\r
451 + */\r
452 +\r
453 +size_t\r
454 +tag_op_list_size (const tag_op_list_t *list);\r
455 +\r
456 +/*\r
457 + * Reset a list to contain no operations\r
458 + */\r
459 +\r
460 +void\r
461 +tag_op_list_reset (tag_op_list_t *list);\r
462 +\r
463 +\r
464 + /*\r
465 +  *   return the i'th tag in the list\r
466 +  */\r
467 +\r
468 +const char *\r
469 +tag_op_list_tag (const tag_op_list_t *list, size_t i);\r
470 +\r
471 +/*\r
472 + *   Is the i'th tag operation a remove?\r
473 + */\r
474 +\r
475 +notmuch_bool_t\r
476 +tag_op_list_isremove (const tag_op_list_t *list, size_t i);\r
477 +\r
478 +#endif\r
479 -- \r
480 1.7.10.4\r
481 \r