folder: search-term problems
[notmuch-archives.git] / b2 / 0c2452dc9c6016a9e40e8fa84785d2f86a3168
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 BCFC942116A\r
6         for <notmuch@notmuchmail.org>; Sat, 31 Mar 2012 15:18:14 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Amavis-Alert: BAD HEADER SECTION, Duplicate header field: "References"\r
9 X-Spam-Flag: NO\r
10 X-Spam-Score: -0.7\r
11 X-Spam-Level: \r
12 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
13         tests=[RCVD_IN_DNSWL_LOW=-0.7] 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 2r-5R4GUH8Mm for <notmuch@notmuchmail.org>;\r
17         Sat, 31 Mar 2012 15:18:11 -0700 (PDT)\r
18 Received: from mail-bk0-f53.google.com (mail-bk0-f53.google.com\r
19         [209.85.214.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id E82C5421178\r
22         for <notmuch@notmuchmail.org>; Sat, 31 Mar 2012 15:17:57 -0700 (PDT)\r
23 Received: by mail-bk0-f53.google.com with SMTP id j4so1547751bkw.26\r
24         for <notmuch@notmuchmail.org>; Sat, 31 Mar 2012 15:17:57 -0700 (PDT)\r
25 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
26         d=google.com; s=20120113;\r
27         h=from:to:cc:subject:date:message-id:x-mailer:in-reply-to:references\r
28         :in-reply-to:references:x-gm-message-state;\r
29         bh=/UaCCC+hBf9uZWlbGQ8AotmRIGrXBkZdjJA2/ym6AuY=;\r
30         b=XWdvm3ozTPsWfqK9WjIoxIKHzhfV1IZ+1z8Nr6y8UMBRuDgSGt0swW4tV01qfueWKJ\r
31         b8ntEZoDfud5CpBiWpsBziiTQrcyT2IAH6tewqjdDZJQFZ6sxkdS+IWsxthdxCeKKCkz\r
32         RzUWNOkSbZ4q5Nz+B8fEpEJVUggJCqxHms61rIw2d8x80L7tLNiET29zoR+8hoHzc+Zw\r
33         OQaa0+Y0tG1etDvGbyxv1M7ejpzDzl6NlvoeB9MTUUDeyjUV6JXWKBCjsb9aRJyUx0ao\r
34         D/RIV6MlOzbf+47DgDSDcqRDsbaa9VqUiQCISho51E75Ga2g4PLD3lJI5mGu0mCnuIB5\r
35         +pOg==\r
36 Received: by 10.204.133.204 with SMTP id g12mr1385964bkt.64.1333232277504;\r
37         Sat, 31 Mar 2012 15:17:57 -0700 (PDT)\r
38 Received: from localhost (dsl-hkibrasgw4-fe50f800-253.dhcp.inet.fi.\r
39         [84.248.80.253]) by mx.google.com with ESMTPS id\r
40         zx16sm28521136bkb.13.2012.03.31.15.17.55\r
41         (version=SSLv3 cipher=OTHER); Sat, 31 Mar 2012 15:17:56 -0700 (PDT)\r
42 From: Jani Nikula <jani@nikula.org>\r
43 To: notmuch@notmuchmail.org\r
44 Subject: [PATCH 6/8] cli: add support for batch tagging operations to "notmuch\r
45         tag"\r
46 Date: Sun,  1 Apr 2012 01:17:26 +0300\r
47 Message-Id:\r
48  <f360a40bed50208d146aee8b06946b1b8315e818.1333231401.git.jani@nikula.org>\r
49 X-Mailer: git-send-email 1.7.5.4\r
50 In-Reply-To: <cover.1333231401.git.jani@nikula.org>\r
51 References: <cover.1333231401.git.jani@nikula.org>\r
52 In-Reply-To: <cover.1333231401.git.jani@nikula.org>\r
53 References: <cover.1333231401.git.jani@nikula.org>\r
54 X-Gm-Message-State:\r
55  ALoCoQkhctEMCFHoQJ8ESesmTYqwPfFbxYrWe0t3innvzxFR9k4AloFJpYCd7fnDhdmxfcpZ5aoG\r
56 X-BeenThere: notmuch@notmuchmail.org\r
57 X-Mailman-Version: 2.1.13\r
58 Precedence: list\r
59 List-Id: "Use and development of the notmuch mail system."\r
60         <notmuch.notmuchmail.org>\r
61 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
62         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
63 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
64 List-Post: <mailto:notmuch@notmuchmail.org>\r
65 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
66 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
67         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
68 X-List-Received-Date: Sat, 31 Mar 2012 22:18:15 -0000\r
69 \r
70 Add support for batch tagging operations through stdin to "notmuch\r
71 tag". This can be enabled with the new --stdin command line option to\r
72 "notmuch new". The input must consist of lines of the format:\r
73 \r
74 T +<tag>|-<tag> [...] [--] <search-terms>\r
75 \r
76 Each line is interpreted similarly to "notmuch tag" command line\r
77 arguments. The delimiter is one or more spaces ' '. Any characters in\r
78 <tag> and <search-terms> MAY be hex encoded with %NN where NN is the\r
79 hexadecimal ASCII value of the character. Any ' ' and '%' characters\r
80 in <tag> and <search-terms> MUST be hex encoded (using %20 and %25,\r
81 respectively). Any characters that are not part of <tag> or\r
82 <search-terms> MUST NOT be hex encoded.\r
83 \r
84 Leading and trailing space ' ' is ignored. Empty lines and lines\r
85 beginning with '#' are ignored.\r
86 \r
87 Signed-off-by: Jani Nikula <jani@nikula.org>\r
88 ---\r
89  notmuch-tag.c |  244 +++++++++++++++++++++++++++++++++++++++++++++++++-------\r
90  1 files changed, 213 insertions(+), 31 deletions(-)\r
91 \r
92 diff --git a/notmuch-tag.c b/notmuch-tag.c\r
93 index 05feed3..32c7167 100644\r
94 --- a/notmuch-tag.c\r
95 +++ b/notmuch-tag.c\r
96 @@ -19,6 +19,7 @@\r
97   */\r
98  \r
99  #include "notmuch-client.h"\r
100 +#include "hex-escape.h"\r
101  \r
102  static volatile sig_atomic_t interrupted;\r
103  \r
104 @@ -167,17 +168,181 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
105      return interrupted;\r
106  }\r
107  \r
108 +/* like strtok(3), but without state, and doesn't modify s. usage pattern:\r
109 + *\r
110 + * const char *tok = input;\r
111 + * const char *delim = " \t";\r
112 + * size_t tok_len = 0;\r
113 + *\r
114 + * while ((tok = strtok_len (tok + tok_len, delim, &tok_len)) != NULL) {\r
115 + *     // do stuff with string tok of length tok_len\r
116 + * }\r
117 + */\r
118 +static\r
119 +char *strtok_len(char *s, const char *delim, size_t *len)\r
120 +{\r
121 +    /* skip initial delims */\r
122 +    s += strspn (s, delim);\r
123 +\r
124 +    /* length of token */\r
125 +    *len = strcspn (s, delim);\r
126 +\r
127 +    return *len ? s : NULL;\r
128 +}\r
129 +\r
130 +/* Tag messages according to 'input', which must consist of lines of\r
131 + * the format:\r
132 + *\r
133 + * T +<tag>|-<tag> [...] [--] <search-terms>\r
134 + *\r
135 + * Each line is interpreted similarly to "notmuch tag" command line\r
136 + * arguments. The delimiter is one or more spaces ' '. Any characters\r
137 + * in <tag> and <search-terms> MAY be hex encoded with %NN where NN is\r
138 + * the hexadecimal ASCII value of the character. Any ' ' and '%'\r
139 + * characters in <tag> and <search-terms> MUST be hex encoded (using\r
140 + * %20 and %25, respectively). Any characters that are not part of\r
141 + * <tag> or <search-terms> MUST NOT be hex encoded.\r
142 + *\r
143 + * Leading and trailing space ' ' is ignored. Empty lines and lines\r
144 + * beginning with '#' are ignored.\r
145 + */\r
146 +static int\r
147 +tag_file (void *ctx, notmuch_database_t *notmuch, FILE *input,\r
148 +         notmuch_bool_t synchronize_flags)\r
149 +{\r
150 +    char *line = NULL;\r
151 +    size_t line_size;\r
152 +    ssize_t line_len;\r
153 +    tag_operation_t *tag_ops;\r
154 +    int tag_ops_array_size = 10;\r
155 +    int ret = 0;\r
156 +\r
157 +    /* Array of tagging operations (add or remove), terminated with an\r
158 +     * empty element. Size will be increased as necessary. */\r
159 +    tag_ops = talloc_array (ctx, tag_operation_t, tag_ops_array_size);\r
160 +    if (tag_ops == NULL) {\r
161 +       fprintf (stderr, "Out of memory.\n");\r
162 +       return 1;\r
163 +    }\r
164 +\r
165 +    while ((line_len = getline (&line, &line_size, input)) != -1 &&\r
166 +          !interrupted) {\r
167 +       char *tok;\r
168 +       size_t tok_len;\r
169 +       int tag_ops_count = 0;\r
170 +\r
171 +       chomp_newline (line);\r
172 +\r
173 +       tok = strtok_len (line, " ", &tok_len);\r
174 +\r
175 +       /* Skip empty and comment lines. */\r
176 +       if (tok == NULL || *tok == '#')\r
177 +           continue;\r
178 +\r
179 +       /* T for tagging is the only recognized action for now. */\r
180 +       if (strncmp (tok, "T", tok_len) != 0) {\r
181 +           fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
182 +                    line);\r
183 +           continue;\r
184 +       }\r
185 +\r
186 +       /* Parse tags. */\r
187 +       while ((tok = strtok_len (tok + tok_len, " ", &tok_len)) != NULL) {\r
188 +           notmuch_bool_t remove;\r
189 +           char *tag;\r
190 +\r
191 +           /* Optional explicit end of tags marker. */\r
192 +           if (strncmp (tok, "--", tok_len) == 0) {\r
193 +               tok = strtok_len (tok + tok_len, " ", &tok_len);\r
194 +               break;\r
195 +           }\r
196 +\r
197 +           /* Implicit end of tags. */\r
198 +           if (*tok != '-' && *tok != '+')\r
199 +               break;\r
200 +\r
201 +           /* If tag is terminated by NUL, there's no query string. */\r
202 +           if (*(tok + tok_len) == '\0') {\r
203 +               tok = NULL;\r
204 +               break;\r
205 +           }\r
206 +\r
207 +           /* Terminate, and start next token after terminator. */\r
208 +           *(tok + tok_len++) = '\0';\r
209 +\r
210 +           remove = (*tok == '-');\r
211 +           tag = tok + 1;\r
212 +\r
213 +           /* Refuse empty tags. */\r
214 +           if (*tag == '\0') {\r
215 +               tok = NULL;\r
216 +               break;\r
217 +           }\r
218 +\r
219 +           /* Decode tag. */\r
220 +           if (hex_decode_inplace (tag) != HEX_SUCCESS) {\r
221 +               tok = NULL;\r
222 +               break;\r
223 +           }\r
224 +\r
225 +           tag_ops[tag_ops_count].tag = tag;\r
226 +           tag_ops[tag_ops_count].remove = remove;\r
227 +           tag_ops_count++;\r
228 +\r
229 +           /* Make room for terminating empty element and potential\r
230 +            * new tags, if necessary. This should be a fairly rare\r
231 +            * case, considering the initial array size. */\r
232 +           if (tag_ops_count == tag_ops_array_size) {\r
233 +               tag_ops_array_size *= 2;\r
234 +               tag_ops = talloc_realloc (ctx, tag_ops, tag_operation_t,\r
235 +                                         tag_ops_array_size);\r
236 +               if (tag_ops == NULL) {\r
237 +                   fprintf (stderr, "Out of memory.\n");\r
238 +                   return 1;\r
239 +               }\r
240 +           }\r
241 +       }\r
242 +\r
243 +       if (tok == NULL || tag_ops_count == 0) {\r
244 +           /* FIXME: line has been modified! */\r
245 +           fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
246 +                    line);\r
247 +           continue;\r
248 +       }\r
249 +\r
250 +       tag_ops[tag_ops_count].tag = NULL;\r
251 +\r
252 +       /* tok now points to the query string */\r
253 +       if (hex_decode_inplace (tok) != HEX_SUCCESS) {\r
254 +           /* FIXME: line has been modified! */\r
255 +           fprintf (stderr, "Warning: Ignoring invalid input line: %s\n",\r
256 +                    line);\r
257 +           continue;\r
258 +       }\r
259 +\r
260 +       ret = tag_query (ctx, notmuch, tok, tag_ops, synchronize_flags);\r
261 +       if (ret)\r
262 +           break;\r
263 +    }\r
264 +\r
265 +    if (line)\r
266 +       free (line);\r
267 +\r
268 +    return ret || interrupted;\r
269 +}\r
270 +\r
271  int\r
272  notmuch_tag_command (void *ctx, int argc, char *argv[])\r
273  {\r
274 -    tag_operation_t *tag_ops;\r
275 +    tag_operation_t *tag_ops = NULL;\r
276      int tag_ops_count = 0;\r
277 -    char *query_string;\r
278 +    char *query_string = NULL;\r
279      notmuch_config_t *config;\r
280      notmuch_database_t *notmuch;\r
281      struct sigaction action;\r
282      notmuch_bool_t synchronize_flags;\r
283 -    int i;\r
284 +    notmuch_bool_t use_stdin = FALSE;\r
285 +    int i, opt_index;\r
286      int ret;\r
287  \r
288      /* Setup our handler for SIGINT */\r
289 @@ -187,42 +352,56 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
290      action.sa_flags = SA_RESTART;\r
291      sigaction (SIGINT, &action, NULL);\r
292  \r
293 -    argc--; argv++; /* skip subcommand argument */\r
294 +    notmuch_opt_desc_t options[] = {\r
295 +       { NOTMUCH_OPT_BOOLEAN, &use_stdin, "stdin", 0, 0 },\r
296 +       { 0, 0, 0, 0, 0 }\r
297 +    };\r
298  \r
299 -    /* Array of tagging operations (add or remove), terminated with an\r
300 -     * empty element. */\r
301 -    tag_ops = talloc_array (ctx, tag_operation_t, argc + 1);\r
302 -    if (tag_ops == NULL) {\r
303 -       fprintf (stderr, "Out of memory.\n");\r
304 +    opt_index = parse_arguments (argc, argv, options, 1);\r
305 +    if (opt_index < 0)\r
306         return 1;\r
307 -    }\r
308  \r
309 -    for (i = 0; i < argc; i++) {\r
310 -       if (strcmp (argv[i], "--") == 0) {\r
311 -           i++;\r
312 -           break;\r
313 +    if (use_stdin) {\r
314 +       if (opt_index != argc) {\r
315 +           fprintf (stderr, "Can't specify both cmdline and stdin!\n");\r
316 +           return 1;\r
317         }\r
318 -       if (argv[i][0] == '+' || argv[i][0] == '-') {\r
319 -           tag_ops[tag_ops_count].tag = argv[i] + 1;\r
320 -           tag_ops[tag_ops_count].remove = (argv[i][0] == '-');\r
321 -           tag_ops_count++;\r
322 -       } else {\r
323 -           break;\r
324 +    } else {\r
325 +       /* Array of tagging operations (add or remove), terminated with an\r
326 +        * empty element. */\r
327 +       tag_ops = talloc_array (ctx, tag_operation_t, argc - opt_index + 1);\r
328 +       if (tag_ops == NULL) {\r
329 +           fprintf (stderr, "Out of memory.\n");\r
330 +           return 1;\r
331         }\r
332 -    }\r
333  \r
334 -    tag_ops[tag_ops_count].tag = NULL;\r
335 +       for (i = opt_index; i < argc; i++) {\r
336 +           if (strcmp (argv[i], "--") == 0) {\r
337 +               i++;\r
338 +               break;\r
339 +           }\r
340 +           if (argv[i][0] == '+' || argv[i][0] == '-') {\r
341 +               tag_ops[tag_ops_count].tag = argv[i] + 1;\r
342 +               tag_ops[tag_ops_count].remove = (argv[i][0] == '-');\r
343 +               tag_ops_count++;\r
344 +           } else {\r
345 +               break;\r
346 +           }\r
347 +       }\r
348  \r
349 -    if (tag_ops_count == 0) {\r
350 -       fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");\r
351 -       return 1;\r
352 -    }\r
353 +       tag_ops[tag_ops_count].tag = NULL;\r
354  \r
355 -    query_string = query_string_from_args (ctx, argc - i, &argv[i]);\r
356 +       if (tag_ops_count == 0) {\r
357 +           fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");\r
358 +           return 1;\r
359 +       }\r
360  \r
361 -    if (*query_string == '\0') {\r
362 -       fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");\r
363 -       return 1;\r
364 +       query_string = query_string_from_args (ctx, argc - i, &argv[i]);\r
365 +\r
366 +       if (*query_string == '\0') {\r
367 +           fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");\r
368 +           return 1;\r
369 +       }\r
370      }\r
371  \r
372      config = notmuch_config_open (ctx, NULL, NULL);\r
373 @@ -236,7 +415,10 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
374  \r
375      synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);\r
376  \r
377 -    ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);\r
378 +    if (use_stdin)\r
379 +       ret = tag_file (ctx, notmuch, stdin, synchronize_flags);\r
380 +    else\r
381 +       ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);\r
382  \r
383      notmuch_database_close (notmuch);\r
384  \r
385 -- \r
386 1.7.5.4\r
387 \r