Re: [feature request] emacs: use `notmuch insert` for FCC
[notmuch-archives.git] / 18 / ff7b4f0d65d8401b20cc17b011907c13895cb8
1 Return-Path: <m.walters@qmul.ac.uk>\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 6B616431FAF\r
6         for <notmuch@notmuchmail.org>; Sun, 25 Nov 2012 14:20:07 -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: -1.098\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-1.098 tagged_above=-999 required=5\r
12         tests=[DKIM_ADSP_CUSTOM_MED=0.001, FREEMAIL_FROM=0.001,\r
13         NML_ADSP_CUSTOM_MED=1.2, RCVD_IN_DNSWL_MED=-2.3] autolearn=disabled\r
14 Received: from olra.theworths.org ([127.0.0.1])\r
15         by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
16         with ESMTP id nGY1VD+DAb1G for <notmuch@notmuchmail.org>;\r
17         Sun, 25 Nov 2012 14:20:05 -0800 (PST)\r
18 Received: from mail2.qmul.ac.uk (mail2.qmul.ac.uk [138.37.6.6])\r
19         (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id 00876431FAE\r
22         for <notmuch@notmuchmail.org>; Sun, 25 Nov 2012 14:20:04 -0800 (PST)\r
23 Received: from smtp.qmul.ac.uk ([138.37.6.40])\r
24         by mail2.qmul.ac.uk with esmtp (Exim 4.71)\r
25         (envelope-from <m.walters@qmul.ac.uk>)\r
26         id 1TckYQ-0003qo-7A; Sun, 25 Nov 2012 22:20:00 +0000\r
27 Received: from 93-97-24-31.zone5.bethere.co.uk ([93.97.24.31] helo=localhost)\r
28         by smtp.qmul.ac.uk with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69)\r
29         (envelope-from <m.walters@qmul.ac.uk>)\r
30         id 1TckYP-00071z-0U; Sun, 25 Nov 2012 22:19:57 +0000\r
31 From: Mark Walters <markwalters1009@gmail.com>\r
32 To: david@tethera.net, notmuch@notmuchmail.org\r
33 Subject: Re: [Patch v2 09/17] tag-util.[ch]: New files for common tagging\r
34         routines\r
35 In-Reply-To: <1353792017-31459-10-git-send-email-david@tethera.net>\r
36 References: <1353792017-31459-1-git-send-email-david@tethera.net>\r
37         <1353792017-31459-10-git-send-email-david@tethera.net>\r
38 User-Agent: Notmuch/0.14+81~g9730584 (http://notmuchmail.org) Emacs/23.4.1\r
39         (x86_64-pc-linux-gnu)\r
40 Date: Sun, 25 Nov 2012 22:19:57 +0000\r
41 Message-ID: <87vcctiauq.fsf@qmul.ac.uk>\r
42 MIME-Version: 1.0\r
43 Content-Type: text/plain; charset=us-ascii\r
44 X-Sender-Host-Address: 93.97.24.31\r
45 X-QM-SPAM-Info: Sender has good ham record.  :)\r
46 X-QM-Body-MD5: 256d34c0175bebcd9326e23cd24716e7 (of first 20000 bytes)\r
47 X-SpamAssassin-Score: -1.8\r
48 X-SpamAssassin-SpamBar: -\r
49 X-SpamAssassin-Report: The QM spam filters have analysed this message to\r
50         determine if it is\r
51         spam. We require at least 5.0 points to mark a message as spam.\r
52         This message scored -1.8 points.\r
53         Summary of the scoring: \r
54         * -2.3 RCVD_IN_DNSWL_MED RBL: Sender listed at http://www.dnswl.org/,\r
55         *      medium trust\r
56         *      [138.37.6.40 listed in list.dnswl.org]\r
57         * 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail\r
58         provider *      (markwalters1009[at]gmail.com)\r
59         *  0.5 AWL AWL: From: address is in the auto white-list\r
60 X-QM-Scan-Virus: ClamAV says the message is clean\r
61 Cc: David Bremner <bremner@debian.org>\r
62 X-BeenThere: notmuch@notmuchmail.org\r
63 X-Mailman-Version: 2.1.13\r
64 Precedence: list\r
65 List-Id: "Use and development of the notmuch mail system."\r
66         <notmuch.notmuchmail.org>\r
67 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
68         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
69 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
70 List-Post: <mailto:notmuch@notmuchmail.org>\r
71 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
72 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
73         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
74 X-List-Received-Date: Sun, 25 Nov 2012 22:20:07 -0000\r
75 \r
76 \r
77 This looks good. A couple of typos and a small queries (and I\r
78 agree with Tomi but I think you have already included that).\r
79 \r
80 On Sat, 24 Nov 2012, david@tethera.net wrote:\r
81 > From: David Bremner <bremner@debian.org>\r
82 >\r
83 > These are meant to be shared between notmuch-tag and notmuch-restore.\r
84 >\r
85 > The bulk of the routines implement a "tag operation list" abstract\r
86 > data type act as a structured representation of a set of tag\r
87 > operations (typically coming from a single tag command or line of\r
88 > input).\r
89 > ---\r
90 >  Makefile.local |    1 +\r
91 >  tag-util.c     |  264 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
92 >  tag-util.h     |  120 ++++++++++++++++++++++++++\r
93 >  3 files changed, 385 insertions(+)\r
94 >  create mode 100644 tag-util.c\r
95 >  create mode 100644 tag-util.h\r
96 >\r
97 > diff --git a/Makefile.local b/Makefile.local\r
98 > index 2b91946..854867d 100644\r
99 > --- a/Makefile.local\r
100 > +++ b/Makefile.local\r
101 > @@ -274,6 +274,7 @@ notmuch_client_srcs =             \\r
102 >       query-string.c          \\r
103 >       mime-node.c             \\r
104 >       crypto.c                \\r
105 > +     tag-util.c\r
106 >  \r
107 >  notmuch_client_modules = $(notmuch_client_srcs:.c=.o)\r
108 >  \r
109 > diff --git a/tag-util.c b/tag-util.c\r
110 > new file mode 100644\r
111 > index 0000000..287cc67\r
112 > --- /dev/null\r
113 > +++ b/tag-util.c\r
114 > @@ -0,0 +1,264 @@\r
115 > +#include "string-util.h"\r
116 > +#include "tag-util.h"\r
117 > +#include "hex-escape.h"\r
118 > +\r
119 > +struct _tag_operation_t {\r
120 > +    const char *tag;\r
121 > +    notmuch_bool_t remove;\r
122 > +};\r
123 > +\r
124 > +struct _tag_op_list_t {\r
125 > +    tag_operation_t *ops;\r
126 > +    int count;\r
127 > +    int size;\r
128 > +};\r
129 > +\r
130 > +int\r
131 > +parse_tag_line (void *ctx, char *line,\r
132 > +             tag_op_flag_t flags,\r
133 > +             char **query_string,\r
134 > +             tag_op_list_t *tag_ops)\r
135 > +{\r
136 > +    char *tok = line;\r
137 > +    size_t tok_len = 0;\r
138 > +\r
139 > +    chomp_newline (line);\r
140 > +\r
141 > +    /* remove leading space */\r
142 > +    while (*tok == ' ' || *tok == '\t')\r
143 > +     tok++;\r
144 > +\r
145 > +    /* Skip empty and comment lines. */\r
146 > +    if (*tok == '\0' || *tok == '#')\r
147 > +         return 1;\r
148 > +\r
149 > +    tag_op_list_reset (tag_ops);\r
150 > +\r
151 > +    /* Parse tags. */\r
152 > +    while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) {\r
153 > +     notmuch_bool_t remove;\r
154 > +     char *tag;\r
155 > +\r
156 > +     /* Optional explicit end of tags marker. */\r
157 > +     if (strncmp (tok, "--", tok_len) == 0) {\r
158 > +         tok = strtok_len (tok + tok_len, " ", &tok_len);\r
159 > +         break;\r
160 > +     }\r
161 > +\r
162 > +     /* Implicit end of tags. */\r
163 > +     if (*tok != '-' && *tok != '+')\r
164 > +         break;\r
165 > +\r
166 > +     /* If tag is terminated by NUL, there's no query string. */\r
167 > +     if (*(tok + tok_len) == '\0') {\r
168 > +         tok = NULL;\r
169 > +         break;\r
170 > +     }\r
171 > +\r
172 > +     /* Terminate, and start next token after terminator. */\r
173 > +     *(tok + tok_len++) = '\0';\r
174 > +\r
175 > +     remove = (*tok == '-');\r
176 > +     tag = tok + 1;\r
177 > +\r
178 > +     /* Maybe refuse empty tags. */\r
179 > +     if (!(flags & TAG_FLAG_BE_GENEROUS) && *tag == '\0') {\r
180 > +         tok = NULL;\r
181 > +         break;\r
182 > +     }\r
183 > +\r
184 > +     /* Decode tag. */\r
185 > +     if (hex_decode_inplace (tag) != HEX_SUCCESS) {\r
186 > +         tok = NULL;\r
187 > +         break;\r
188 > +     }\r
189 > +\r
190 > +     if (tag_op_list_append (ctx, tag_ops, tag, remove))\r
191 > +         return -1;\r
192 > +    }\r
193 > +\r
194 > +    if (tok == NULL || tag_ops->count == 0) {\r
195 > +     /* FIXME: line has been modified! */\r
196 > +     fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
197 > +              line);\r
198 > +     return 1;\r
199 > +    }\r
200 > +\r
201 > +    /* tok now points to the query string */\r
202 > +    if (hex_decode_inplace (tok) != HEX_SUCCESS) {\r
203 > +     /* FIXME: line has been modified! */\r
204 > +     fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
205 > +              line);\r
206 > +     return 1;\r
207 > +    }\r
208 > +\r
209 > +    *query_string = tok;\r
210 > +\r
211 > +    return 0;\r
212 > +}\r
213 > +\r
214 > +static inline void\r
215 > +message_error (notmuch_message_t *message,\r
216 > +            notmuch_status_t status,\r
217 > +            const char *format, ...)\r
218 > +{\r
219 > +    va_list va_args;\r
220 > +\r
221 > +    va_start (va_args, format);\r
222 > +\r
223 > +    vfprintf (stderr, format, va_args);\r
224 > +    fprintf (stderr, "Message-ID: %s\n", notmuch_message_get_message_id (message));\r
225 > +    fprintf (stderr, "Status: %s\n", notmuch_status_to_string (status));\r
226 > +}\r
227 > +\r
228 > +notmuch_status_t\r
229 > +tag_op_list_apply (notmuch_message_t *message,\r
230 > +                tag_op_list_t *list,\r
231 > +                tag_op_flag_t flags)\r
232 > +{\r
233 > +    int i;\r
234 > +\r
235 > +    notmuch_status_t status = 0;\r
236 > +    tag_operation_t *tag_ops = list->ops;\r
237 > +\r
238 > +    status = notmuch_message_freeze (message);\r
239 > +    if (status) {\r
240 > +     message_error (message, status, "freezing message");\r
241 > +     return status;\r
242 > +    }\r
243 > +\r
244 > +    if (flags & TAG_FLAG_REMOVE_ALL) {\r
245 > +     status = notmuch_message_remove_all_tags (message);\r
246 > +     if (status) {\r
247 > +         message_error (message, status, "removing all tags" );\r
248 > +         return status;\r
249 > +     }\r
250 > +    }\r
251 > +\r
252 > +    for (i = 0; i < list->count; i++) {\r
253 > +     if (tag_ops[i].remove) {\r
254 > +         status = notmuch_message_remove_tag (message, tag_ops[i].tag);\r
255 > +         if (status) {\r
256 > +             message_error (message, status, "removing tag %s", tag_ops[i].tag);\r
257 > +             return status;\r
258 > +         }\r
259 > +     } else {\r
260 > +         status = notmuch_message_add_tag (message, tag_ops[i].tag);\r
261 > +         if (status) {\r
262 > +             message_error (message, status, "adding tag %s", tag_ops[i].tag);\r
263 > +             return status;\r
264 > +         }\r
265 > +\r
266 > +     }\r
267 > +    }\r
268 > +\r
269 > +    status = notmuch_message_thaw (message);\r
270 \r
271 I don't know how freeze/thaw work but does it matter that you don't thaw\r
272 when there is an error?\r
273 \r
274 > +    if (status) {\r
275 > +     message_error (message, status, "thawing message");\r
276 > +     return status;\r
277 > +    }\r
278 > +\r
279 > +\r
280 > +    if (flags & TAG_FLAG_MAILDIR_SYNC) {\r
281 > +     status = notmuch_message_tags_to_maildir_flags (message);\r
282 > +     if (status) {\r
283 > +         message_error (message, status, "synching tags to maildir");\r
284 > +         return status;\r
285 > +     }\r
286 > +    }\r
287 > +\r
288 > +    return NOTMUCH_STATUS_SUCCESS;\r
289 > +\r
290 > +}\r
291 > +\r
292 > +\r
293 > +/* Array of tagging operations (add or remove), terminated with an\r
294 > + * empty element. Size will be increased as necessary. */\r
295 > +\r
296 > +tag_op_list_t *\r
297 > +tag_op_list_create (void *ctx)\r
298 > +{\r
299 > +    tag_op_list_t *list;\r
300 > +\r
301 > +    list = talloc (ctx, tag_op_list_t);\r
302 > +    if (list == NULL)\r
303 > +     return NULL;\r
304 > +\r
305 > +    list->size = TAG_OP_LIST_INITIAL_SIZE;\r
306 > +    list->count = 0;\r
307 > +\r
308 > +    list->ops = talloc_array (ctx, tag_operation_t, list->size);\r
309 > +    if (list->ops == NULL)\r
310 > +     return NULL;\r
311 > +\r
312 > +    return list;\r
313 > +}\r
314 > +\r
315 > +\r
316 > +int\r
317 > +tag_op_list_append (void *ctx,\r
318 > +                 tag_op_list_t *list,\r
319 > +                 const char *tag,\r
320 > +                 notmuch_bool_t remove)\r
321 > +{\r
322 > +    /* Make room if current array is full.  This should be a fairly\r
323 > +     * rare case, considering the initial array size.\r
324 > +     */\r
325 > +\r
326 > +    if (list->count == list->size) {\r
327 > +     list->size *= 2;\r
328 > +     list->ops = talloc_realloc (ctx, list->ops, tag_operation_t,\r
329 > +                                 list->size);\r
330 > +     if (list->ops == NULL) {\r
331 > +         fprintf (stderr, "Out of memory.\n");\r
332 > +         return 1;\r
333 > +     }\r
334 > +    }\r
335 > +\r
336 > +    /* add the new operation */\r
337 > +\r
338 > +    list->ops[list->count].tag = tag;\r
339 > +    list->ops[list->count].remove = remove;\r
340 > +    list->count++;\r
341 > +    return 0;\r
342 > +}\r
343 > +\r
344 > +/*\r
345 > + *   Is the i'th tag operation a remove?\r
346 > + */\r
347 > +\r
348 > +notmuch_bool_t\r
349 > +tag_op_list_isremove (const tag_op_list_t *list, size_t i)\r
350 > +{\r
351 > +    return list->ops[i].remove;\r
352 > +}\r
353 > +\r
354 > +/*\r
355 > + * Reset a list to contain no operations\r
356 > + */\r
357 > +\r
358 > +void\r
359 > +tag_op_list_reset (tag_op_list_t *list)\r
360 > +{\r
361 > +    list->count = 0;\r
362 > +}\r
363 > +\r
364 > +/*\r
365 > + * Return the number of operations in a list\r
366 > + */\r
367 > +\r
368 > +size_t\r
369 > +tag_op_list_size (const tag_op_list_t *list)\r
370 > +{\r
371 > +    return list->count;\r
372 > +}\r
373 > +\r
374 > +/*\r
375 > + *   return the i'th tag in the list\r
376 > + */\r
377 > +\r
378 > +const char *\r
379 > +tag_op_list_tag (const tag_op_list_t *list, size_t i)\r
380 > +{\r
381 > +    return list->ops[i].tag;\r
382 > +}\r
383 > diff --git a/tag-util.h b/tag-util.h\r
384 > new file mode 100644\r
385 > index 0000000..508806f\r
386 > --- /dev/null\r
387 > +++ b/tag-util.h\r
388 > @@ -0,0 +1,120 @@\r
389 > +#ifndef _TAG_UTIL_H\r
390 > +#define _TAG_UTIL_H\r
391 > +\r
392 > +#include "notmuch-client.h"\r
393 > +\r
394 > +typedef struct _tag_operation_t tag_operation_t;\r
395 > +typedef struct _tag_op_list_t tag_op_list_t;\r
396 > +\r
397 > +#define TAG_OP_LIST_INITIAL_SIZE 10\r
398 > +\r
399 > +/* Use powers of 2 */\r
400 > +typedef enum  { TAG_FLAG_NONE = 0,\r
401 > +             /* Operations are synced to maildir, if possible */\r
402 > +\r
403 > +             TAG_FLAG_MAILDIR_SYNC = 1,\r
404 > +\r
405 > +             /* Remove all tags from message before applying\r
406 > +              * list */\r
407 > +\r
408 > +             TAG_FLAG_REMOVE_ALL = 2,\r
409 > +\r
410 > +             /* Don't try to avoid database operations.  Useful\r
411 > +              * when we know that message passed needs these\r
412 > +              *  operations. */\r
413 > +\r
414 > +             TAG_FLAG_PRE_OPTIMIZED = 4,\r
415 > +\r
416 > +             /* Accept strange tags that might be user error;\r
417 > +                intended for use by notmuch-restore.\r
418 > +             */\r
419 > +\r
420 > +             TAG_FLAG_BE_GENEROUS = 8} tag_op_flag_t;\r
421 > +\r
422 > +/* Parse a string of the following format:\r
423 > + *\r
424 > + * +<tag>|-<tag> [...] [--] <search-terms>\r
425 > + *\r
426 > + * Each line is interpreted similarly to "notmuch tag" command line\r
427 > + * arguments. The delimiter is one or more spaces ' '. Any characters\r
428 > + * in <tag> and <search-terms> MAY be hex encoded with %NN where NN is\r
429 > + * the hexadecimal value of the character. Any ' ' and '%' characters\r
430 > + * in <tag> and <search-terms> MUST be hex encoded (using %20 and %25,\r
431 > + * respectively). Any characters that are not part of <tag> or\r
432 > + * <search-terms> MUST NOT be hex encoded.\r
433 > + *\r
434 > + * Leading and trailing space ' ' is ignored. Empty lines and lines\r
435 > + * beginning with '#' are ignored.\r
436 > + *\r
437 > + * Returns: 0 for OK, 1 for skipped line, -1 for fatal(ish) error.\r
438 > + *\r
439 > + * Output Parameters:\r
440 > + *   ops     contains a list of tag operations\r
441 > + *   query_str the search terms.\r
442 > + */\r
443 > +int\r
444 > +parse_tag_line (void *ctx, char *line,\r
445 > +             tag_op_flag_t flags,\r
446 > +             char **query_str, tag_op_list_t *ops);\r
447 > +\r
448 > +/*\r
449 > + * Create an empty list of tag operations\r
450 > + *\r
451 > + * ctx is passed to talloc\r
452 > + */\r
453 > +\r
454 > +tag_op_list_t\r
455 > +*tag_op_list_create (void *ctx);\r
456 > +\r
457 > +/*\r
458 > + * Add a tag operation (delete iff remote == TRUE) to a list.\r
459 > + * The list is expanded as necessary.\r
460 > + */\r
461 \r
462 Typo s/remote/remove/\r
463 \r
464 > +int\r
465 > +tag_op_list_append (void *ctx,\r
466 > +                 tag_op_list_t *list,\r
467 > +                 const char *tag,\r
468 > +                 notmuch_bool_t remove);\r
469 > +\r
470 > +/*\r
471 > + * Apply a list of tag operations, in order to a message.\r
472 \r
473 I found the comment awkward to parse: as "in order to <do something>"\r
474 \r
475 Best wishes\r
476 \r
477 Mark\r
478 \r
479 > + *\r
480 > + * Flags can be bitwise ORed; see enum above for possibilies.\r
481 > + */\r
482 > +\r
483 > +notmuch_status_t\r
484 > +tag_op_list_apply (notmuch_message_t *message,\r
485 > +                tag_op_list_t *tag_ops,\r
486 > +                tag_op_flag_t flags);\r
487 > +\r
488 > +/*\r
489 > + * Return the number of operations in a list\r
490 > + */\r
491 > +\r
492 > +size_t\r
493 > +tag_op_list_size (const tag_op_list_t *list);\r
494 > +\r
495 > +/*\r
496 > + * Reset a list to contain no operations\r
497 > + */\r
498 > +\r
499 > +void\r
500 > +tag_op_list_reset (tag_op_list_t *list);\r
501 > +\r
502 > +\r
503 > + /*\r
504 > +  *   return the i'th tag in the list\r
505 > +  */\r
506 > +\r
507 > +const char *\r
508 > +tag_op_list_tag (const tag_op_list_t *list, size_t i);\r
509 > +\r
510 > +/*\r
511 > + *   Is the i'th tag operation a remove?\r
512 > + */\r
513 > +\r
514 > +notmuch_bool_t\r
515 > +tag_op_list_isremove (const tag_op_list_t *list, size_t i);\r
516 > +\r
517 > +#endif\r
518 > -- \r
519 > 1.7.10.4\r
520 >\r
521 > _______________________________________________\r
522 > notmuch mailing list\r
523 > notmuch@notmuchmail.org\r
524 > http://notmuchmail.org/mailman/listinfo/notmuch\r