Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / 45 / 0b4bfbfde44ef1adf26bcb83f92e124a3b692b
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 E40F1431FBF\r
6         for <notmuch@notmuchmail.org>; Sun, 18 Nov 2012 11:05:38 -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 Sfnp1vpZ3h44 for <notmuch@notmuchmail.org>;\r
16         Sun, 18 Nov 2012 11:05:36 -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 EB9CB429E26\r
21         for <notmuch@notmuchmail.org>; Sun, 18 Nov 2012 11:05:19 -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 1TaABC-000289-8T; Sun, 18 Nov 2012 15:05:18 -0400\r
28 Received: from bremner by zancas.localnet with local (Exim 4.80)\r
29         (envelope-from <bremner@tethera.net>)\r
30         id 1TaAB6-0001Io-O1; Sun, 18 Nov 2012 15:05:12 -0400\r
31 From: david@tethera.net\r
32 To: notmuch@notmuchmail.org\r
33 Subject: [PATCH 09/16] cli: add support for batch tagging operations to\r
34         "notmuch tag"\r
35 Date: Sun, 18 Nov 2012 15:04:51 -0400\r
36 Message-Id: <1353265498-3839-10-git-send-email-david@tethera.net>\r
37 X-Mailer: git-send-email 1.7.10.4\r
38 In-Reply-To: <1353265498-3839-1-git-send-email-david@tethera.net>\r
39 References: <1353265498-3839-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: Sun, 18 Nov 2012 19:05:39 -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 new". 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 |  177 +++++++++++++++++++++++++++++++++++----------------------\r
79  1 file changed, 109 insertions(+), 68 deletions(-)\r
80 \r
81 diff --git a/notmuch-tag.c b/notmuch-tag.c\r
82 index 88d559b..ca120d5 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 @@ -75,6 +71,7 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,\r
110      const char *join = "";\r
111      int i;\r
112      unsigned int max_tag_len = 0;\r
113 +    const tag_operation_t *tag_ops = list->ops;\r
114  \r
115      /* Don't optimize if there are no tag changes. */\r
116      if (tag_ops[0].tag == NULL)\r
117 @@ -116,12 +113,11 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,\r
118   * element. */\r
119  static int\r
120  tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
121 -          tag_operation_t *tag_ops, notmuch_bool_t synchronize_flags)\r
122 +          tag_op_list_t *tag_ops, tag_op_flag_t flags)\r
123  {\r
124      notmuch_query_t *query;\r
125      notmuch_messages_t *messages;\r
126      notmuch_message_t *message;\r
127 -    int i;\r
128  \r
129      /* Optimize the query so it excludes messages that already have\r
130       * the specified set of tags. */\r
131 @@ -144,21 +140,7 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
132          notmuch_messages_valid (messages) && ! interrupted;\r
133          notmuch_messages_move_to_next (messages)) {\r
134         message = notmuch_messages_get (messages);\r
135 -\r
136 -       notmuch_message_freeze (message);\r
137 -\r
138 -       for (i = 0; tag_ops[i].tag; i++) {\r
139 -           if (tag_ops[i].remove)\r
140 -               notmuch_message_remove_tag (message, tag_ops[i].tag);\r
141 -           else\r
142 -               notmuch_message_add_tag (message, tag_ops[i].tag);\r
143 -       }\r
144 -\r
145 -       notmuch_message_thaw (message);\r
146 -\r
147 -       if (synchronize_flags)\r
148 -           notmuch_message_tags_to_maildir_flags (message);\r
149 -\r
150 +       tag_op_list_apply (message, tag_ops, flags);\r
151         notmuch_message_destroy (message);\r
152      }\r
153  \r
154 @@ -170,15 +152,17 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,\r
155  int\r
156  notmuch_tag_command (void *ctx, int argc, char *argv[])\r
157  {\r
158 -    tag_operation_t *tag_ops;\r
159 -    int tag_ops_count = 0;\r
160 -    char *query_string;\r
161 +    tag_op_list_t *tag_ops = NULL;\r
162 +    char *query_string = NULL;\r
163      notmuch_config_t *config;\r
164      notmuch_database_t *notmuch;\r
165      struct sigaction action;\r
166 -    notmuch_bool_t synchronize_flags;\r
167 -    int i;\r
168 -    int ret;\r
169 +    tag_op_flag_t synchronize_flags = TAG_FLAG_NONE;\r
170 +    notmuch_bool_t batch = FALSE;\r
171 +    FILE *input = stdin;\r
172 +    char *input_file_name = NULL;\r
173 +    int i, opt_index;\r
174 +    int ret = 0;\r
175  \r
176      /* Setup our handler for SIGINT */\r
177      memset (&action, 0, sizeof (struct sigaction));\r
178 @@ -187,53 +171,76 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
179      action.sa_flags = SA_RESTART;\r
180      sigaction (SIGINT, &action, NULL);\r
181  \r
182 -    argc--; argv++; /* skip subcommand argument */\r
183 +    notmuch_opt_desc_t options[] = {\r
184 +       { NOTMUCH_OPT_BOOLEAN, &batch, "batch", 0, 0 },\r
185 +       { NOTMUCH_OPT_STRING, &input_file_name, "input", 'i', 0 },\r
186 +       { 0, 0, 0, 0, 0 }\r
187 +    };\r
188  \r
189 -    /* Array of tagging operations (add or remove), terminated with an\r
190 -     * empty element. */\r
191 -    tag_ops = talloc_array (ctx, tag_operation_t, argc + 1);\r
192 -    if (tag_ops == NULL) {\r
193 -       fprintf (stderr, "Out of memory.\n");\r
194 +    opt_index = parse_arguments (argc, argv, options, 1);\r
195 +    if (opt_index < 0)\r
196         return 1;\r
197 +\r
198 +    if (input_file_name) {\r
199 +       batch = TRUE;\r
200 +       input = fopen (input_file_name, "r");\r
201 +       if (input == NULL) {\r
202 +           fprintf (stderr, "Error opening %s for reading: %s\n",\r
203 +                    input_file_name, strerror (errno));\r
204 +           return 1;\r
205 +       }\r
206      }\r
207  \r
208 -    for (i = 0; i < argc; i++) {\r
209 -       if (strcmp (argv[i], "--") == 0) {\r
210 -           i++;\r
211 -           break;\r
212 +    if (batch) {\r
213 +       if (opt_index != argc) {\r
214 +           fprintf (stderr, "Can't specify both cmdline and stdin!\n");\r
215 +           return 1;\r
216         }\r
217 -       if (argv[i][0] == '+' || argv[i][0] == '-') {\r
218 -           if (argv[i][0] == '+' && argv[i][1] == '\0') {\r
219 -               fprintf (stderr, "Error: tag names cannot be empty.\n");\r
220 -               return 1;\r
221 +    } else {\r
222 +       /* Array of tagging operations (add or remove), terminated with an\r
223 +        * empty element. */\r
224 +       tag_ops = tag_op_list_create (ctx);\r
225 +       if (tag_ops == NULL) {\r
226 +           fprintf (stderr, "Out of memory.\n");\r
227 +           return 1;\r
228 +       }\r
229 +\r
230 +       for (i = opt_index; i < argc; i++) {\r
231 +           if (strcmp (argv[i], "--") == 0) {\r
232 +               i++;\r
233 +               break;\r
234             }\r
235 -           if (argv[i][0] == '+' && argv[i][1] == '-') {\r
236 -               /* This disallows adding the non-removable tag "-" and\r
237 -                * enables notmuch tag to take long options in the\r
238 -                * future. */\r
239 -               fprintf (stderr, "Error: tag names must not start with '-'.\n");\r
240 -               return 1;\r
241 +           if (argv[i][0] == '+' || argv[i][0] == '-') {\r
242 +               /* FIXME: checks should be consistent with those in batch tagging */\r
243 +               if (argv[i][0] == '+' && argv[i][1] == '\0') {\r
244 +                   fprintf (stderr, "Error: tag names cannot be empty.\n");\r
245 +                   return 1;\r
246 +               }\r
247 +               if (argv[i][0] == '+' && argv[i][1] == '-') {\r
248 +                   /* This disallows adding the non-removable tag "-" and\r
249 +                    * enables notmuch tag to take long options in the\r
250 +                    * future. */\r
251 +                   fprintf (stderr, "Error: tag names must not start with '-'.\n");\r
252 +                   return 1;\r
253 +               }\r
254 +               tag_op_list_append (ctx, tag_ops,\r
255 +                                   argv[i] + 1, (argv[i][0] == '-'));\r
256 +           } else {\r
257 +               break;\r
258             }\r
259 -           tag_ops[tag_ops_count].tag = argv[i] + 1;\r
260 -           tag_ops[tag_ops_count].remove = (argv[i][0] == '-');\r
261 -           tag_ops_count++;\r
262 -       } else {\r
263 -           break;\r
264         }\r
265 -    }\r
266 -\r
267 -    tag_ops[tag_ops_count].tag = NULL;\r
268  \r
269 -    if (tag_ops_count == 0) {\r
270 -       fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");\r
271 -       return 1;\r
272 -    }\r
273 +       if (tag_op_list_empty (tag_ops)) {\r
274 +           fprintf (stderr, "Error: 'notmuch tag' requires at least one tag to add or remove.\n");\r
275 +           return 1;\r
276 +       }\r
277  \r
278 -    query_string = query_string_from_args (ctx, argc - i, &argv[i]);\r
279 +       query_string = query_string_from_args (ctx, argc - i, &argv[i]);\r
280  \r
281 -    if (*query_string == '\0') {\r
282 -       fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");\r
283 -       return 1;\r
284 +       if (*query_string == '\0') {\r
285 +           fprintf (stderr, "Error: notmuch tag requires at least one search term.\n");\r
286 +           return 1;\r
287 +       }\r
288      }\r
289  \r
290      config = notmuch_config_open (ctx, NULL, NULL);\r
291 @@ -244,11 +251,45 @@ notmuch_tag_command (void *ctx, int argc, char *argv[])\r
292                                NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))\r
293         return 1;\r
294  \r
295 -    synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);\r
296 +    if (notmuch_config_get_maildir_synchronize_flags (config))\r
297 +       synchronize_flags = TAG_FLAG_MAILDIR_SYNC;\r
298 +\r
299 +    if (batch) {\r
300 +\r
301 +       char *line = NULL;\r
302 +       size_t line_size = 0;\r
303 +       ssize_t line_len;\r
304 +       int ret = 0;\r
305 +       tag_op_list_t *tag_ops;\r
306 +\r
307 +       tag_ops = tag_op_list_create (ctx);\r
308 +       if (tag_ops == NULL) {\r
309 +           fprintf (stderr, "Out of memory.\n");\r
310 +           return 1;\r
311 +       }\r
312 +\r
313 +       while ((line_len = getline (&line, &line_size, input)) != -1 &&\r
314 +              ! interrupted) {\r
315 +\r
316 +           char *query_string;\r
317 +\r
318 +           ret =  parse_tag_line (ctx, line, &query_string, tag_ops);\r
319  \r
320 -    ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);\r
321 +           if (ret > 0)\r
322 +               continue;\r
323 +\r
324 +           if (ret < 0 || tag_query (ctx, notmuch, query_string,\r
325 +                                     tag_ops, synchronize_flags))\r
326 +               break;\r
327 +       }\r
328 +\r
329 +       if (line)\r
330 +           free (line);\r
331 +    } else\r
332 +       ret = tag_query (ctx, notmuch, query_string, tag_ops, synchronize_flags);\r
333  \r
334      notmuch_database_destroy (notmuch);\r
335  \r
336 -    return ret;\r
337 +    return ret || interrupted;\r
338 +\r
339  }\r
340 -- \r
341 1.7.10.4\r
342 \r