Re: Forwarding a mail, with a non-ASCII signature
[notmuch-archives.git] / 83 / 9d825735741e921b17e2866f2f94f7aea97b59
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 CEAF2431FB6\r
6         for <notmuch@notmuchmail.org>; Sat, 24 Nov 2012 13:20:53 -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 eQFHJMp7kgJ4 for <notmuch@notmuchmail.org>;\r
16         Sat, 24 Nov 2012 13:20:51 -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 CA4FC431FC0\r
21         for <notmuch@notmuchmail.org>; Sat, 24 Nov 2012 13:20:38 -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 1TcN9P-0006SZ-Mn; Sat, 24 Nov 2012 17:20:38 -0400\r
28 Received: from bremner by zancas.localnet with local (Exim 4.80)\r
29         (envelope-from <bremner@tethera.net>)\r
30         id 1TcN9H-0008D8-3Y; Sat, 24 Nov 2012 17:20:27 -0400\r
31 From: david@tethera.net\r
32 To: notmuch@notmuchmail.org\r
33 Subject: [Patch v2 10/17] cli: add support for batch tagging operations to\r
34         "notmuch tag"\r
35 Date: Sat, 24 Nov 2012 17:20:10 -0400\r
36 Message-Id: <1353792017-31459-11-git-send-email-david@tethera.net>\r
37 X-Mailer: git-send-email 1.7.10.4\r
38 In-Reply-To: <1353792017-31459-1-git-send-email-david@tethera.net>\r
39 References: <1353792017-31459-1-git-send-email-david@tethera.net>\r
40 X-Spam_bar: -\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:54 -0000\r
54 \r
55 From: Jani Nikula <jani@nikula.org>\r
56 \r
57 Add support for batch tagging operations through stdin to "notmuch\r
58 tag". This can be enabled with the new --stdin command line option to\r
59 "notmuch tag". The input must consist of lines of the format:\r
60 \r
61 +<tag>|-<tag> [...] [--] <search-terms>\r
62 \r
63 Each line is interpreted similarly to "notmuch tag" command line\r
64 arguments. The delimiter is one or more spaces ' '. Any characters in\r
65 <tag> and <search-terms> MAY be hex encoded with %NN where NN is the\r
66 hexadecimal value of the character. Any ' ' and '%' characters in\r
67 <tag> and <search-terms> MUST be hex encoded (using %20 and %25,\r
68 respectively). Any characters that are not part of <tag> or\r
69 <search-terms> MUST NOT be hex encoded.\r
70 \r
71 Leading and trailing space ' ' is ignored. Empty lines and lines\r
72 beginning with '#' are ignored.\r
73 \r
74 Signed-off-by: Jani Nikula <jani@nikula.org>\r
75 \r
76 Hacked-like-crazy-by: David Bremner <david@tethera.net>\r
77 ---\r
78  notmuch-tag.c |  194 +++++++++++++++++++++++++++++++++++----------------------\r
79  1 file changed, 118 insertions(+), 76 deletions(-)\r
80 \r
81 diff --git a/notmuch-tag.c b/notmuch-tag.c\r
82 index 88d559b..8a8af0b 100644\r
83 --- a/notmuch-tag.c\r
84 +++ b/notmuch-tag.c\r
85 @@ -19,6 +19,7 @@\r
86   */\r
87  \r
88  #include "notmuch-client.h"\r
89 +#include "tag-util.h"\r
90  \r
91  static volatile sig_atomic_t interrupted;\r
92  \r
93 @@ -54,14 +55,9 @@ _escape_tag (char *buf, const char *tag)\r
94      return buf;\r
95  }\r
96  \r
97 -typedef struct {\r
98 -    const char *tag;\r
99 -    notmuch_bool_t remove;\r
100 -} tag_operation_t;\r
101 -\r
102  static char *\r
103  _optimize_tag_query (void *ctx, const char *orig_query_string,\r
104 -                    const tag_operation_t *tag_ops)\r
105 +                    const tag_op_list_t *list)\r
106  {\r
107      /* This is subtler than it looks.  Xapian ignores the '-' operator\r
108       * at the beginning both queries and parenthesized groups and,\r
109 @@ -73,19 +69,20 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,\r
110  \r
111      char *escaped, *query_string;\r
112      const char *join = "";\r
113 -    int i;\r
114 +    size_t i;\r
115      unsigned int max_tag_len = 0;\r
116  \r
117      /* Don't optimize if there are no tag changes. */\r
118 -    if (tag_ops[0].tag == NULL)\r
119 +    if (tag_op_list_size (list) == 0)\r
120         return talloc_strdup (ctx, orig_query_string);\r
121  \r
122      /* Allocate a buffer for escaping tags.  This is large enough to\r
123       * hold a fully escaped tag with every character doubled plus\r
124       * enclosing quotes and a NUL. */\r
125 -    for (i = 0; tag_ops[i].tag; i++)\r
126 -       if (strlen (tag_ops[i].tag) > max_tag_len)\r
127 -           max_tag_len = strlen (tag_ops[i].tag);\r
128 +    for (i = 0; i < tag_op_list_size (list); i++)\r
129 +       if (strlen (tag_op_list_tag (list, i)) > max_tag_len)\r
130 +           max_tag_len = strlen (tag_op_list_tag (list, i));\r
131 +\r
132      escaped = talloc_array (ctx, char, max_tag_len * 2 + 3);\r
133      if (! escaped)\r
134         return NULL;\r
135 @@ -96,11 +93,11 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,\r
136      else\r
137         query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);\r
138  \r
139 -    for (i = 0; tag_ops[i].tag && query_string; i++) {\r
140 +    for (i = 0; i < tag_op_list_size (list) && query_string; i++) {\r
141         query_string = talloc_asprintf_append_buffer (\r
142             query_string, "%s%stag:%s", join,\r
143 -           tag_ops[i].remove ? "" : "not ",\r
144 -           _escape_tag (escaped, tag_ops[i].tag));\r
145 +           tag_op_list_isremove (list, i) ? "" : "not ",\r
146 +           _escape_tag (escaped, tag_op_list_tag (list, i)));\r
147         join = " or ";\r
148      }\r
149  \r
150 @@ -116,12 +113,11 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,\r
151   * element. */\r
152  static int\r
153  tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
154 -          tag_operation_t *tag_ops, notmuch_bool_t synchronize_flags)\r
155 +          tag_op_list_t *tag_ops, tag_op_flag_t flags)\r
156  {\r
157      notmuch_query_t *query;\r
158      notmuch_messages_t *messages;\r
159      notmuch_message_t *message;\r
160 -    int i;\r
161  \r
162      /* Optimize the query so it excludes messages that already have\r
163       * the specified set of tags. */\r
164 @@ -144,21 +140,7 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
165          notmuch_messages_valid (messages) && ! interrupted;\r
166          notmuch_messages_move_to_next (messages)) {\r
167         message = notmuch_messages_get (messages);\r
168 -\r
169 -       notmuch_message_freeze (message);\r
170 -\r
171 -       for (i = 0; tag_ops[i].tag; i++) {\r
172 -           if (tag_ops[i].remove)\r
173 -               notmuch_message_remove_tag (message, tag_ops[i].tag);\r
174 -           else\r
175 -               notmuch_message_add_tag (message, tag_ops[i].tag);\r
176 -       }\r
177 -\r
178 -       notmuch_message_thaw (message);\r
179 -\r
180 -       if (synchronize_flags)\r
181 -           notmuch_message_tags_to_maildir_flags (message);\r
182 -\r
183 +       tag_op_list_apply (message, tag_ops, flags);\r
184         notmuch_message_destroy (message);\r
185      }\r
186  \r
187 @@ -170,15 +152,17 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
188  int\r
189  notmuch_tag_command (void *ctx, int argc, char *argv[])\r
190  {\r
191 -    tag_operation_t *tag_ops;\r
192 -    int tag_ops_count = 0;\r
193 -    char *query_string;\r
194 +    tag_op_list_t *tag_ops = NULL;\r
195 +    char *query_string = NULL;\r
196      notmuch_config_t *config;\r
197      notmuch_database_t *notmuch;\r
198      struct sigaction action;\r
199 -    notmuch_bool_t synchronize_flags;\r
200 -    int i;\r
201 -    int ret;\r
202 +    tag_op_flag_t synchronize_flags = TAG_FLAG_NONE;\r
203 +    notmuch_bool_t batch = FALSE;\r
204 +    FILE *input = stdin;\r
205 +    char *input_file_name = NULL;\r
206 +    int i, opt_index;\r
207 +    int ret = 0;\r
208  \r
209      /* Setup our handler for SIGINT */\r
210      memset (&action, 0, sizeof (struct sigaction));\r
211 @@ -187,53 +171,76 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
212      action.sa_flags = SA_RESTART;\r
213      sigaction (SIGINT, &action, NULL);\r
214  \r
215 -    argc--; argv++; /* skip subcommand argument */\r
216 +    notmuch_opt_desc_t options[] = {\r
217 +       { NOTMUCH_OPT_BOOLEAN, &batch, "batch", 0, 0 },\r
218 +       { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },\r
219 +       { 0, 0, 0, 0, 0 }\r
220 +    };\r
221  \r
222 -    /* Array of tagging operations (add or remove), terminated with an\r
223 -     * empty element. */\r
224 -    tag_ops = talloc_array (ctx, tag_operation_t, argc + 1);\r
225 -    if (tag_ops == NULL) {\r
226 -       fprintf (stderr, "Out of memory.\n");\r
227 +    opt_index = parse_arguments (argc, argv, options, 1);\r
228 +    if (opt_index < 0)\r
229         return 1;\r
230 +\r
231 +    if (input_file_name) {\r
232 +       batch = TRUE;\r
233 +       input = fopen (input_file_name, "r");\r
234 +       if (input == NULL) {\r
235 +           fprintf (stderr, "Error opening %s for reading: %s\n",\r
236 +                    input_file_name, strerror (errno));\r
237 +           return 1;\r
238 +       }\r
239      }\r
240  \r
241 -    for (i = 0; i < argc; i++) {\r
242 -       if (strcmp (argv[i], "--") == 0) {\r
243 -           i++;\r
244 -           break;\r
245 +    if (batch) {\r
246 +       if (opt_index != argc) {\r
247 +           fprintf (stderr, "Can't specify both cmdline and stdin!\n");\r
248 +           return 1;\r
249 +       }\r
250 +    } else {\r
251 +       /* Array of tagging operations (add or remove), terminated with an\r
252 +        * empty element. */\r
253 +       tag_ops = tag_op_list_create (ctx);\r
254 +       if (tag_ops == NULL) {\r
255 +           fprintf (stderr, "Out of memory.\n");\r
256 +           return 1;\r
257         }\r
258 -       if (argv[i][0] == '+' || argv[i][0] == '-') {\r
259 -           if (argv[i][0] == '+' && argv[i][1] == '\0') {\r
260 -               fprintf (stderr, "Error: tag names cannot be empty.\n");\r
261 -               return 1;\r
262 +\r
263 +       for (i = opt_index; i < argc; i++) {\r
264 +           if (strcmp (argv[i], "--") == 0) {\r
265 +               i++;\r
266 +               break;\r
267             }\r
268 -           if (argv[i][0] == '+' && argv[i][1] == '-') {\r
269 -               /* This disallows adding the non-removable tag "-" and\r
270 -                * enables notmuch tag to take long options in the\r
271 -                * future. */\r
272 -               fprintf (stderr, "Error: tag names must not start with '-'.\n");\r
273 -               return 1;\r
274 +           if (argv[i][0] == '+' || argv[i][0] == '-') {\r
275 +               /* FIXME: checks should be consistent with those in batch tagging */\r
276 +               if (argv[i][0] == '+' && argv[i][1] == '\0') {\r
277 +                   fprintf (stderr, "Error: tag names cannot be empty.\n");\r
278 +                   return 1;\r
279 +               }\r
280 +               if (argv[i][0] == '+' && argv[i][1] == '-') {\r
281 +                   /* This disallows adding the non-removable tag "-" and\r
282 +                    * enables notmuch tag to take long options in the\r
283 +                    * future. */\r
284 +                   fprintf (stderr, "Error: tag names must not start with '-'.\n");\r
285 +                   return 1;\r
286 +               }\r
287 +               tag_op_list_append (ctx, tag_ops,\r
288 +                                   argv[i] + 1, (argv[i][0] == '-'));\r
289 +           } else {\r
290 +               break;\r
291             }\r
292 -           tag_ops[tag_ops_count].tag = argv[i] + 1;\r
293 -           tag_ops[tag_ops_count].remove = (argv[i][0] == '-');\r
294 -           tag_ops_count++;\r
295 -       } else {\r
296 -           break;\r
297         }\r
298 -    }\r
299  \r
300 -    tag_ops[tag_ops_count].tag = NULL;\r
301 -\r
302 -    if (tag_ops_count == 0) {\r
303 -       fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");\r
304 -       return 1;\r
305 -    }\r
306 +       if (tag_op_list_size (tag_ops) == 0) {\r
307 +           fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");\r
308 +           return 1;\r
309 +       }\r
310  \r
311 -    query_string = query_string_from_args (ctx, argc - i, &argv[i]);\r
312 +       query_string = query_string_from_args (ctx, argc - i, &argv[i]);\r
313  \r
314 -    if (*query_string == '\0') {\r
315 -       fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");\r
316 -       return 1;\r
317 +       if (*query_string == '\0') {\r
318 +           fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");\r
319 +           return 1;\r
320 +       }\r
321      }\r
322  \r
323      config = notmuch_config_open (ctx, NULL, NULL);\r
324 @@ -244,11 +251,46 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
325                                NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))\r
326         return 1;\r
327  \r
328 -    synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);\r
329 +    if (notmuch_config_get_maildir_synchronize_flags (config))\r
330 +       synchronize_flags = TAG_FLAG_MAILDIR_SYNC;\r
331 +\r
332 +    if (batch) {\r
333 +\r
334 +       char *line = NULL;\r
335 +       size_t line_size = 0;\r
336 +       ssize_t line_len;\r
337 +       int ret = 0;\r
338 +       tag_op_list_t *tag_ops;\r
339 +\r
340 +       tag_ops = tag_op_list_create (ctx);\r
341 +       if (tag_ops == NULL) {\r
342 +           fprintf (stderr, "Out of memory.\n");\r
343 +           return 1;\r
344 +       }\r
345  \r
346 -    ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);\r
347 +       while ((line_len = getline (&line, &line_size, input)) != -1 &&\r
348 +              ! interrupted) {\r
349 +\r
350 +           char *query_string;\r
351 +\r
352 +           ret =  parse_tag_line (ctx, line, TAG_FLAG_NONE,\r
353 +                                  &query_string, tag_ops);\r
354 +\r
355 +           if (ret > 0)\r
356 +               continue;\r
357 +\r
358 +           if (ret < 0 || tag_query (ctx, notmuch, query_string,\r
359 +                                     tag_ops, synchronize_flags))\r
360 +               break;\r
361 +       }\r
362 +\r
363 +       if (line)\r
364 +           free (line);\r
365 +    } else\r
366 +       ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);\r
367  \r
368      notmuch_database_destroy (notmuch);\r
369  \r
370 -    return ret;\r
371 +    return ret || interrupted;\r
372 +\r
373  }\r
374 -- \r
375 1.7.10.4\r
376 \r