[Patch v7 01/14] parse_tag_line: use enum for return value.
[notmuch-archives.git] / a3 / 9602fe804fac1b1e019ee6b7218f5f2116ec99
1 Return-Path: <jani@nikula.org>\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 99958431FAF\r
6         for <notmuch@notmuchmail.org>; Sun,  2 Dec 2012 05:29: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.7\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
12         tests=[RCVD_IN_DNSWL_LOW=-0.7] 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 1GhUazsQcveL for <notmuch@notmuchmail.org>;\r
16         Sun,  2 Dec 2012 05:29:49 -0800 (PST)\r
17 Received: from mail-lb0-f181.google.com (mail-lb0-f181.google.com\r
18         [209.85.217.181]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id AF574431FAE\r
21         for <notmuch@notmuchmail.org>; Sun,  2 Dec 2012 05:29:48 -0800 (PST)\r
22 Received: by mail-lb0-f181.google.com with SMTP id ge1so1763467lbb.26\r
23         for <notmuch@notmuchmail.org>; Sun, 02 Dec 2012 05:29:47 -0800 (PST)\r
24 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
25         d=google.com; s=20120113;\r
26         h=from:to:cc:subject:in-reply-to:references:user-agent:date\r
27         :message-id:mime-version:content-type:x-gm-message-state;\r
28         bh=4pG3dsHLro2a6t1zGdq7KCFVB5k2FuL7Aixh8+mW97k=;\r
29         b=iOG8OULvDABOB7FJf7Wzj4oBmL8rG323NkNMRj+etCZsvLo9H+4gjNIptLqD25o34k\r
30         nBmw7naYTezYcET5v5HTojICOYoTbE01b7mHW3c2rN/OzcU1DpdwMMQSEdOZD5Gn/VJq\r
31         +16FGNtf3QBlUE44eAsx2m0jTWJRL66vXr9Olk+pn+m95Dug3OnMYScjSsbvJxNimj9x\r
32         G//hiLAcxNZxUNSkg5plYVT7RiamSkJpGSpJWseQfgqGMoOkWU/bAv3dXtMm2914uYuf\r
33         bzlgoyR4xVWoX+GVUKRykl6rYYhUb1HdQQCmAYZhlHLXjHLygYFnUrDm2vNQg+0ACfp5\r
34         WYNg==\r
35 Received: by 10.152.114.65 with SMTP id je1mr6695753lab.33.1354454987058;\r
36         Sun, 02 Dec 2012 05:29:47 -0800 (PST)\r
37 Received: from localhost (dsl-hkibrasgw4-fe51df00-27.dhcp.inet.fi.\r
38         [80.223.81.27])\r
39         by mx.google.com with ESMTPS id b4sm4150390lbi.0.2012.12.02.05.29.45\r
40         (version=SSLv3 cipher=OTHER); Sun, 02 Dec 2012 05:29:46 -0800 (PST)\r
41 From: Jani Nikula <jani@nikula.org>\r
42 To: david@tethera.net, notmuch@notmuchmail.org\r
43 Subject: Re: [Patch v2 13/17] notmuch-restore: add support for input format\r
44         'batch-tag'\r
45 In-Reply-To: <1353792017-31459-14-git-send-email-david@tethera.net>\r
46 References: <1353792017-31459-1-git-send-email-david@tethera.net>\r
47         <1353792017-31459-14-git-send-email-david@tethera.net>\r
48 User-Agent: Notmuch/0.14+124~g3b17402 (http://notmuchmail.org) Emacs/23.4.1\r
49         (i686-pc-linux-gnu)\r
50 Date: Sun, 02 Dec 2012 15:29:43 +0200\r
51 Message-ID: <87zk1wd1ko.fsf@nikula.org>\r
52 MIME-Version: 1.0\r
53 Content-Type: text/plain; charset=us-ascii\r
54 X-Gm-Message-State:\r
55  ALoCoQnkuhDlQ10YCjDeEjMG8q7DRMmvK6smTHhMB09E9uuzdYSSICajMJXHt3UXjYtQLy0EcQ+N\r
56 Cc: David Bremner <bremner@debian.org>\r
57 X-BeenThere: notmuch@notmuchmail.org\r
58 X-Mailman-Version: 2.1.13\r
59 Precedence: list\r
60 List-Id: "Use and development of the notmuch mail system."\r
61         <notmuch.notmuchmail.org>\r
62 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
63         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
64 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
65 List-Post: <mailto:notmuch@notmuchmail.org>\r
66 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
67 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
68         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
69 X-List-Received-Date: Sun, 02 Dec 2012 13:29:53 -0000\r
70 \r
71 On Sat, 24 Nov 2012, david@tethera.net wrote:\r
72 > From: David Bremner <bremner@debian.org>\r
73 >\r
74 > This is the same as the batch input for notmuch tag, except by default\r
75 > it removes all tags before modifying a given message id and only "id:"\r
76 > is supported.\r
77 > ---\r
78 >  notmuch-restore.c |  199 +++++++++++++++++++++++++++++++++--------------------\r
79 >  1 file changed, 125 insertions(+), 74 deletions(-)\r
80 >\r
81 > diff --git a/notmuch-restore.c b/notmuch-restore.c\r
82 > index f03dcac..22fcd2d 100644\r
83 > --- a/notmuch-restore.c\r
84 > +++ b/notmuch-restore.c\r
85 > @@ -19,18 +19,22 @@\r
86 >   */\r
87 >  \r
88 >  #include "notmuch-client.h"\r
89 > +#include "dump-restore-private.h"\r
90 > +#include "tag-util.h"\r
91 > +#include "string-util.h"\r
92 > +\r
93 > +static volatile sig_atomic_t interrupted;\r
94 > +static regex_t regex;\r
95 >  \r
96 >  static int\r
97 > -tag_message (notmuch_database_t *notmuch, const char *message_id,\r
98 > -          char *file_tags, notmuch_bool_t remove_all,\r
99 > -          notmuch_bool_t synchronize_flags)\r
100 > +tag_message (unused (void *ctx),\r
101 > +          notmuch_database_t *notmuch,\r
102 > +          const char *message_id,\r
103 > +          tag_op_list_t *tag_ops,\r
104 > +          tag_op_flag_t flags)\r
105 >  {\r
106 >      notmuch_status_t status;\r
107 > -    notmuch_tags_t *db_tags;\r
108 > -    char *db_tags_str;\r
109 >      notmuch_message_t *message = NULL;\r
110 > -    const char *tag;\r
111 > -    char *next;\r
112 >      int ret = 0;\r
113 >  \r
114 >      status = notmuch_database_find_message (notmuch, message_id, &message);\r
115 > @@ -44,55 +48,63 @@ tag_message (notmuch_database_t *notmuch, const char *message_id,\r
116 >  \r
117 >      /* In order to detect missing messages, this check/optimization is\r
118 >       * intentionally done *after* first finding the message. */\r
119 > -    if (! remove_all && (file_tags == NULL || *file_tags == '\0'))\r
120 > -     goto DONE;\r
121 > -\r
122 > -    db_tags_str = NULL;\r
123 > -    for (db_tags = notmuch_message_get_tags (message);\r
124 > -      notmuch_tags_valid (db_tags);\r
125 > -      notmuch_tags_move_to_next (db_tags)) {\r
126 > -     tag = notmuch_tags_get (db_tags);\r
127 > -\r
128 > -     if (db_tags_str)\r
129 > -         db_tags_str = talloc_asprintf_append (db_tags_str, " %s", tag);\r
130 > -     else\r
131 > -         db_tags_str = talloc_strdup (message, tag);\r
132 > -    }\r
133 > +    if ( (flags & TAG_FLAG_REMOVE_ALL) || (tag_op_list_size (tag_ops)))\r
134 \r
135 Extra space between ('s, and no need to wrap tag_op_list_size call in\r
136 braces.\r
137 \r
138 > +     tag_op_list_apply (message, tag_ops, flags);\r
139 >  \r
140 > -    if (((file_tags == NULL || *file_tags == '\0') &&\r
141 > -      (db_tags_str == NULL || *db_tags_str == '\0')) ||\r
142 > -     (file_tags && db_tags_str && strcmp (file_tags, db_tags_str) == 0))\r
143 \r
144 This is a necessary optimization you're throwing away, but I'll get back\r
145 to this after checking the last patch in the series.\r
146 \r
147 > -     goto DONE;\r
148 > +    if (message)\r
149 > +     notmuch_message_destroy (message);\r
150 \r
151 message != NULL always, you can remove the if.\r
152 \r
153 >  \r
154 > -    notmuch_message_freeze (message);\r
155 > +    return ret;\r
156 > +}\r
157 >  \r
158 > -    if (remove_all)\r
159 > -     notmuch_message_remove_all_tags (message);\r
160 > +static int\r
161 > +parse_sup_line (void *ctx, char *line,\r
162 > +             char **query_str, tag_op_list_t *tag_ops)\r
163 > +{\r
164 >  \r
165 > -    next = file_tags;\r
166 > -    while (next) {\r
167 > -     tag = strsep (&next, " ");\r
168 > -     if (*tag == '\0')\r
169 > -         continue;\r
170 > -     status = notmuch_message_add_tag (message, tag);\r
171 > -     if (status) {\r
172 > -         fprintf (stderr, "Error applying tag %s to message %s:\n",\r
173 > -                  tag, message_id);\r
174 > -         fprintf (stderr, "%s\n", notmuch_status_to_string (status));\r
175 > -         ret = 1;\r
176 > -     }\r
177 > +    regmatch_t match[3];\r
178 > +    char *file_tags;\r
179 > +    int rerr;\r
180 > +\r
181 > +    tag_op_list_reset (tag_ops);\r
182 > +\r
183 > +    chomp_newline (line);\r
184 > +\r
185 > +    /* Silently ignore blank lines */\r
186 > +    if (line[0] == '\0') {\r
187 > +     return 1;\r
188 > +    }\r
189 > +\r
190 > +    rerr = xregexec (&regex, line, 3, match, 0);\r
191 > +    if (rerr == REG_NOMATCH) {\r
192 > +     fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
193 > +              line);\r
194 > +     return 1;\r
195 >      }\r
196 >  \r
197 > -    notmuch_message_thaw (message);\r
198 > +    *query_str = talloc_strndup (ctx, line + match[1].rm_so,\r
199 > +                              match[1].rm_eo - match[1].rm_so);\r
200 > +    file_tags = talloc_strndup (ctx, line + match[2].rm_so,\r
201 > +                             match[2].rm_eo - match[2].rm_so);\r
202 >  \r
203 > -    if (synchronize_flags)\r
204 > -     notmuch_message_tags_to_maildir_flags (message);\r
205 > +    char *tok = file_tags;\r
206 > +    size_t tok_len = 0;\r
207 >  \r
208 > -  DONE:\r
209 > -    if (message)\r
210 > -     notmuch_message_destroy (message);\r
211 > +    tag_op_list_reset (tag_ops);\r
212 > +\r
213 > +    while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) {\r
214 > +\r
215 > +     if (*(tok + tok_len) != '\0') {\r
216 > +         *(tok + tok_len) = '\0';\r
217 > +         tok_len++;\r
218 > +     }\r
219 > +\r
220 > +     if (tag_op_list_append (ctx, tag_ops, tok, FALSE))\r
221 > +         return -1;\r
222 > +    }\r
223 > +\r
224 > +    return 0;\r
225 >  \r
226 > -    return ret;\r
227 >  }\r
228 >  \r
229 >  int\r
230 > @@ -100,16 +112,19 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
231 >  {\r
232 >      notmuch_config_t *config;\r
233 >      notmuch_database_t *notmuch;\r
234 > -    notmuch_bool_t synchronize_flags;\r
235 >      notmuch_bool_t accumulate = FALSE;\r
236 > +    tag_op_flag_t flags = 0;\r
237 > +    tag_op_list_t *tag_ops;\r
238 > +\r
239 >      char *input_file_name = NULL;\r
240 >      FILE *input = stdin;\r
241 >      char *line = NULL;\r
242 >      size_t line_size;\r
243 >      ssize_t line_len;\r
244 > -    regex_t regex;\r
245 > -    int rerr;\r
246 > +\r
247 > +    int ret = 0;\r
248 >      int opt_index;\r
249 > +    int input_format = DUMP_FORMAT_AUTO;\r
250 >  \r
251 >      config = notmuch_config_open (ctx, NULL, NULL);\r
252 >      if (config == NULL)\r
253 > @@ -119,9 +134,15 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
254 >                              NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))\r
255 >       return 1;\r
256 >  \r
257 > -    synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);\r
258 > +    if (notmuch_config_get_maildir_synchronize_flags (config))\r
259 > +     flags |= TAG_FLAG_MAILDIR_SYNC;\r
260 >  \r
261 >      notmuch_opt_desc_t options[] = {\r
262 > +     { NOTMUCH_OPT_KEYWORD, &input_format, "format", 'f',\r
263 > +       (notmuch_keyword_t []){ { "auto", DUMP_FORMAT_AUTO },\r
264 > +                               { "batch-tag", DUMP_FORMAT_BATCH_TAG },\r
265 > +                               { "sup", DUMP_FORMAT_SUP },\r
266 > +                               { 0, 0 } } },\r
267 >       { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },\r
268 >       { NOTMUCH_OPT_BOOLEAN,  &accumulate, "accumulate", 'a', 0 },\r
269 >       { 0, 0, 0, 0, 0 }\r
270 > @@ -134,6 +155,9 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
271 >       return 1;\r
272 >      }\r
273 >  \r
274 > +    if (! accumulate)\r
275 > +     flags |= TAG_FLAG_REMOVE_ALL;\r
276 > +\r
277 >      if (input_file_name) {\r
278 >       input = fopen (input_file_name, "r");\r
279 >       if (input == NULL) {\r
280 > @@ -154,35 +178,61 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
281 >       * non-space characters for the message-id, then one or more\r
282 >       * spaces, then a list of space-separated tags as a sequence of\r
283 >       * characters within literal '(' and ')'. */\r
284 > -    if ( xregcomp (&regex,\r
285 > -                "^([^ ]+) \\(([^)]*)\\)$",\r
286 > -                REG_EXTENDED) )\r
287 > -     INTERNAL_ERROR ("compile time constant regex failed.");\r
288 > +    char *p;\r
289 >  \r
290 > -    while ((line_len = getline (&line, &line_size, input)) != -1) {\r
291 > -     regmatch_t match[3];\r
292 > -     char *message_id, *file_tags;\r
293 > +    line_len = getline (&line, &line_size, input);\r
294 > +    if (line_len == 0)\r
295 > +     return 0;\r
296 >  \r
297 > -     chomp_newline (line);\r
298 > +    for (p = line; *p; p++) {\r
299 > +     if (*p == '(')\r
300 > +         input_format = DUMP_FORMAT_SUP;\r
301 > +    }\r
302 >  \r
303 > -     rerr = xregexec (&regex, line, 3, match, 0);\r
304 > -     if (rerr == REG_NOMATCH) {\r
305 > -         fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
306 > -                  line);\r
307 > -         continue;\r
308 > +    if (input_format == DUMP_FORMAT_AUTO)\r
309 > +     input_format = DUMP_FORMAT_BATCH_TAG;\r
310 > +\r
311 > +    if (input_format == DUMP_FORMAT_SUP)\r
312 > +     if ( xregcomp (&regex,\r
313 > +                    "^([^ ]+) \\(([^)]*)\\)$",\r
314 > +                    REG_EXTENDED) )\r
315 > +         INTERNAL_ERROR ("compile time constant regex failed.");\r
316 > +\r
317 > +    tag_ops = tag_op_list_create (ctx);\r
318 > +    if (tag_ops == NULL) {\r
319 > +     fprintf (stderr, "Out of memory.\n");\r
320 > +     return 1;\r
321 > +    }\r
322 \r
323 Tag op list creation could be moved earlier to keep the parsing stuff\r
324 closer together.\r
325 \r
326 > +\r
327 > +    do {\r
328 > +     char *query_string;\r
329 > +\r
330 > +     if (input_format == DUMP_FORMAT_SUP) {\r
331 > +         ret =  parse_sup_line (ctx, line, &query_string, tag_ops);\r
332 > +     } else {\r
333 > +         ret =  parse_tag_line (ctx, line, TAG_FLAG_BE_GENEROUS,\r
334 > +                                &query_string, tag_ops);\r
335 \r
336 Extra spaces after each = above.\r
337 \r
338 > +\r
339 > +         if (ret == 0) {\r
340 > +             if ( strncmp ("id:", query_string, 3) != 0) {\r
341 > +                 fprintf (stderr, "Unsupported query: %s\n", query_string);\r
342 > +                 continue;\r
343 > +             }\r
344 \r
345 There should probably be a comment somewhere here saying that everything\r
346 after id: is taken to be the message id, i.e. a query of "id:foo AND\r
347 tag:bar" will not do what you want.\r
348 \r
349 An option is to add a TAG_FLAG to accept only message id as the query in\r
350 parse_tag_line. It could trim trailing whitespace (if it doesn't\r
351 already) and look for spaces in between individual search terms *before*\r
352 hex decoding the query string, and fail if it finds any, and then check\r
353 for id: prefix after hex decode.\r
354 \r
355 > +             /* delete id: from front of string; tag_message expects a\r
356 > +              * raw message-id */\r
357 > +             query_string = query_string + 3;\r
358 > +         }\r
359 >       }\r
360 >  \r
361 > -     message_id = xstrndup (line + match[1].rm_so,\r
362 > -                            match[1].rm_eo - match[1].rm_so);\r
363 > -     file_tags = xstrndup (line + match[2].rm_so,\r
364 > -                           match[2].rm_eo - match[2].rm_so);\r
365 > +     if (ret > 0)\r
366 > +         continue;\r
367 >  \r
368 > -     tag_message (notmuch, message_id, file_tags, ! accumulate,\r
369 > -                  synchronize_flags);\r
370 > +     if (ret < 0 || tag_message (ctx, notmuch, query_string,\r
371 > +                                 tag_ops, flags))\r
372 > +         break;\r
373 > +\r
374 > +    }  while ((line_len = getline (&line, &line_size, input)) != -1);\r
375 >  \r
376 > -     free (message_id);\r
377 > -     free (file_tags);\r
378 > -    }\r
379 >  \r
380 >      regfree (&regex);\r
381 \r
382 Only do this for sup format.\r
383 \r
384 BR,\r
385 Jani.\r
386 \r
387 >  \r
388 > @@ -190,8 +240,9 @@ notmuch_restore_command (unused (void *ctx), int argc, char *argv[])\r
389 >       free (line);\r
390 >  \r
391 >      notmuch_database_destroy (notmuch);\r
392 > +\r
393 >      if (input != stdin)\r
394 >       fclose (input);\r
395 >  \r
396 > -    return 0;\r
397 > +    return ret;\r
398 >  }\r
399 > -- \r
400 > 1.7.10.4\r
401 >\r
402 > _______________________________________________\r
403 > notmuch mailing list\r
404 > notmuch@notmuchmail.org\r
405 > http://notmuchmail.org/mailman/listinfo/notmuch\r