[PATCH 8/8] cli: optionally restore message properties from dump file
[notmuch-archives.git] / a5 / 481c3265928f44bcf91e175ec14a6fa8779538
1 Return-Path: <nex@nexoid.at>\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 6AA54431FAF\r
6         for <notmuch@notmuchmail.org>; Sat, 21 Jan 2012 13:23:40 -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.001\r
10 X-Spam-Level: \r
11 X-Spam-Status: No, score=0.001 tagged_above=-999 required=5\r
12         tests=[FREEMAIL_FROM=0.001] 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 nnklLtVj2yCc for <notmuch@notmuchmail.org>;\r
16         Sat, 21 Jan 2012 13:23:39 -0800 (PST)\r
17 X-Greylist: delayed 442 seconds by postgrey-1.32 at olra;\r
18         Sat, 21 Jan 2012 13:23:38 PST\r
19 Received: from mail.nexoid.at (www.nexoid.at [178.79.130.240])\r
20         (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))\r
21         (No client certificate requested)\r
22         by olra.theworths.org (Postfix) with ESMTPS id C3AF7431FAE\r
23         for <notmuch@notmuchmail.org>; Sat, 21 Jan 2012 13:23:38 -0800 (PST)\r
24 Received: by mail.nexoid.at (Postfix, from userid 1000)\r
25         id B9B9457019; Sat, 21 Jan 2012 22:16:14 +0100 (CET)\r
26 From: Peter Feigl <craven@gmx.net>\r
27 To: notmuch@notmuchmail.org\r
28 Subject: [PATCH] rewriting notmuch-search for structured output to make other\r
29         output formats easier\r
30 Date: Sat, 21 Jan 2012 22:16:08 +0100\r
31 Message-Id: <1327180568-30385-1-git-send-email-craven@gmx.net>\r
32 X-Mailer: git-send-email 1.7.8.4\r
33 X-BeenThere: notmuch@notmuchmail.org\r
34 X-Mailman-Version: 2.1.13\r
35 Precedence: list\r
36 List-Id: "Use and development of the notmuch mail system."\r
37         <notmuch.notmuchmail.org>\r
38 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
39         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
40 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
41 List-Post: <mailto:notmuch@notmuchmail.org>\r
42 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
43 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
44         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
45 X-List-Received-Date: Sat, 21 Jan 2012 21:23:40 -0000\r
46 \r
47 The output routines have been rewritten so that logical structure\r
48 (objects with key/value pairs, arrays, strings and numbers) are\r
49 written instead of ad-hoc printfs. This allows for easier adaptation\r
50 of other output formats, as only the routines that start/end an object\r
51 etc. have to be rewritten. The logic is the same for all formats.\r
52 The default text output is handled differently, special cases are\r
53 inserted at the proper places, as it differs too much from the\r
54 structured output.\r
55 ---\r
56  notmuch-search.c |  493 ++++++++++++++++++++++++++++++++++--------------------\r
57  1 files changed, 309 insertions(+), 184 deletions(-)\r
58 \r
59 diff --git a/notmuch-search.c b/notmuch-search.c\r
60 index 8867aab..bce44c2 100644\r
61 --- a/notmuch-search.c\r
62 +++ b/notmuch-search.c\r
63 @@ -29,88 +29,221 @@ typedef enum {\r
64  } output_t;\r
65  \r
66  typedef struct search_format {\r
67 -    const char *results_start;\r
68 -    const char *item_start;\r
69 -    void (*item_id) (const void *ctx,\r
70 -                    const char *item_type,\r
71 -                    const char *item_id);\r
72 -    void (*thread_summary) (const void *ctx,\r
73 -                           const char *thread_id,\r
74 -                           const time_t date,\r
75 -                           const int matched,\r
76 -                           const int total,\r
77 -                           const char *authors,\r
78 -                           const char *subject);\r
79 -    const char *tag_start;\r
80 -    const char *tag;\r
81 -    const char *tag_sep;\r
82 -    const char *tag_end;\r
83 -    const char *item_sep;\r
84 -    const char *item_end;\r
85 -    const char *results_end;\r
86 -    const char *results_null;\r
87 +    void (*start_object) (const void *ctx, FILE *stream);\r
88 +    void (*end_object) (const void *ctx, FILE *stream);\r
89 +    void (*start_attribute) (const void *ctx, FILE *stream);\r
90 +    void (*attribute_key) (const void *ctx, FILE *stream, const char *key);\r
91 +    void (*attribute_key_value_separator) (const void *ctx, FILE *stream);\r
92 +    void (*end_attribute) (const void *ctx, FILE *stream);\r
93 +    void (*attribute_separator) (const void *ctx, FILE *stream);\r
94 +    void (*start_array) (const void *ctx, FILE *stream);\r
95 +    void (*array_item_separator) (const void *ctx, FILE *stream);\r
96 +    void (*end_array) (const void *ctx, FILE *stream);\r
97 +    void (*number) (const void *ctx, FILE *stream, int number);\r
98 +    void (*string) (const void *ctx, FILE *stream, const char *string);\r
99 +    void (*boolean) (const void *ctx, FILE *stream, int boolean);\r
100  } search_format_t;\r
101  \r
102 -static void\r
103 -format_item_id_text (const void *ctx,\r
104 -                    const char *item_type,\r
105 -                    const char *item_id);\r
106 -\r
107 -static void\r
108 -format_thread_text (const void *ctx,\r
109 -                   const char *thread_id,\r
110 -                   const time_t date,\r
111 -                   const int matched,\r
112 -                   const int total,\r
113 -                   const char *authors,\r
114 -                   const char *subject);\r
115 +/* dummy format */\r
116  static const search_format_t format_text = {\r
117 -    "",\r
118 -       "",\r
119 -           format_item_id_text,\r
120 -           format_thread_text,\r
121 -           " (",\r
122 -               "%s", " ",\r
123 -           ")", "\n",\r
124 -       "",\r
125 -    "\n",\r
126 -    "",\r
127 -};\r
128 +    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};\r
129 +\r
130 +void json_start_object(const void *ctx, FILE *stream);\r
131 +void json_end_object(const void *ctx, FILE *stream);\r
132 +void json_start_attribute(const void *ctx, FILE *stream);\r
133 +void json_attribute_key(const void *ctx, FILE *stream, const char *key);\r
134 +void json_attribute_key_value_separator(const void *ctx, FILE *stream);\r
135 +void json_end_attribute(const void *ctx, FILE *stream);\r
136 +void json_attribute_separator(const void *ctx, FILE *stream);\r
137 +void json_start_array(const void *ctx, FILE *stream);\r
138 +void json_array_item_separator(const void *ctx, FILE *stream);\r
139 +void json_end_array(const void *ctx, FILE *stream);\r
140 +void json_number(const void *ctx, FILE *stream, int number);\r
141 +void json_string(const void *ctx, FILE *stream, const char *string);\r
142 +void json_boolean(const void *ctx, FILE *stream, int boolean);\r
143 +\r
144 +\r
145 +void json_start_object(const void *ctx, FILE *stream) {\r
146 +    (void)ctx;\r
147 +    fputs("{", stream);\r
148 +}\r
149 +void json_end_object(const void *ctx, FILE *stream) {\r
150 +    (void)ctx;\r
151 +    fputs("}\n", stream);\r
152 +}\r
153 +void json_start_attribute(const void *ctx, FILE *stream) {\r
154 +    (void)ctx;\r
155 +    (void)stream;\r
156 +}\r
157 +void json_attribute_key(const void *ctx, FILE *stream, const char *key) {\r
158 +    (void)ctx;\r
159 +    fprintf(stream, "\"%s\"", key);\r
160 +}\r
161 +void json_attribute_key_value_separator(const void *ctx, FILE *stream) {\r
162 +    (void)ctx;\r
163 +    fputs(": ", stream);\r
164 +}\r
165 +void json_end_attribute(const void *ctx, FILE *stream) {\r
166 +    (void)ctx;\r
167 +    (void)stream;\r
168 +}\r
169 +void json_attribute_separator(const void *ctx, FILE *stream) {\r
170 +    (void)ctx;\r
171 +    fputs(",\n", stream);\r
172 +}\r
173 +void json_start_array(const void *ctx, FILE *stream) {\r
174 +    (void)ctx;\r
175 +    fputs("[", stream);\r
176 +}\r
177 +void json_array_item_separator(const void *ctx, FILE *stream) {\r
178 +    (void)ctx;\r
179 +    fputs(", ", stream);\r
180 +}\r
181 +void json_end_array(const void *ctx, FILE *stream) {\r
182 +    (void)ctx;\r
183 +    fputs("]", stream);\r
184 +}\r
185 +void json_number(const void *ctx, FILE *stream, int number) {\r
186 +    (void)ctx;\r
187 +    fprintf(stream, "%i", number);\r
188 +}\r
189 +void json_string(const void *ctx, FILE *stream, const char *string) {\r
190 +    void *ctx_quote = talloc_new (ctx);    \r
191 +    fprintf(stream, "%s", json_quote_str (ctx_quote, string));\r
192 +    talloc_free (ctx_quote);\r
193 +}\r
194 +void json_boolean(const void *ctx, FILE *stream, int boolean) {\r
195 +    (void)ctx;\r
196 +    if(boolean)\r
197 +       fputs("true", stream);\r
198 +    else\r
199 +       fputs("false", stream);\r
200 +}\r
201 +\r
202 +/* helper functions for attributes */\r
203 +void format_attribute_string(const void *ctx, FILE *stream, const search_format_t *format, const char *key, const char *value);\r
204 +void format_attribute_number(const void *ctx, FILE *stream, const search_format_t *format, const char *key, int value);\r
205 +void format_attribute_boolean(const void *ctx, FILE *stream, const search_format_t *format, const char *key, const char *value);\r
206 +\r
207 +void format_attribute_string(const void *ctx, FILE *stream, const search_format_t *format, const char *key, const char *value) {\r
208 +    format->start_attribute(ctx, stream);\r
209 +    format->attribute_key(ctx, stream, key);\r
210 +    format->attribute_key_value_separator(ctx, stream);\r
211 +    format->string(ctx, stream, value);\r
212 +    format->end_attribute(ctx, stream);\r
213 +}\r
214 +\r
215 +void format_attribute_number(const void *ctx, FILE *stream, const search_format_t *format, const char *key, int value) {\r
216 +    format->start_attribute(ctx, stream);\r
217 +    format->attribute_key(ctx, stream, key);\r
218 +    format->attribute_key_value_separator(ctx, stream);\r
219 +    format->number(ctx, stream, value);\r
220 +    format->end_attribute(ctx, stream);\r
221 +}\r
222  \r
223 -static void\r
224 -format_item_id_json (const void *ctx,\r
225 -                    const char *item_type,\r
226 -                    const char *item_id);\r
227 -\r
228 -static void\r
229 -format_thread_json (const void *ctx,\r
230 -                   const char *thread_id,\r
231 -                   const time_t date,\r
232 -                   const int matched,\r
233 -                   const int total,\r
234 -                   const char *authors,\r
235 -                   const char *subject);\r
236  static const search_format_t format_json = {\r
237 -    "[",\r
238 -       "{",\r
239 -           format_item_id_json,\r
240 -           format_thread_json,\r
241 -           "\"tags\": [",\r
242 -               "\"%s\"", ", ",\r
243 -           "]", ",\n",\r
244 -       "}",\r
245 -    "]\n",\r
246 -    "]\n",\r
247 +    json_start_object,\r
248 +    json_end_object,\r
249 +    json_start_attribute,\r
250 +    json_attribute_key,\r
251 +    json_attribute_key_value_separator,\r
252 +    json_end_attribute,\r
253 +    json_attribute_separator,\r
254 +    json_start_array,\r
255 +    json_array_item_separator,\r
256 +    json_end_array,\r
257 +    json_number,\r
258 +    json_string,\r
259 +    json_boolean,\r
260  };\r
261  \r
262 -static void\r
263 -format_item_id_text (unused (const void *ctx),\r
264 -                    const char *item_type,\r
265 -                    const char *item_id)\r
266 -{\r
267 -    printf ("%s%s", item_type, item_id);\r
268 +void sexp_start_object(const void *ctx, FILE *stream);\r
269 +void sexp_end_object(const void *ctx, FILE *stream);\r
270 +void sexp_start_attribute(const void *ctx, FILE *stream);\r
271 +void sexp_attribute_key(const void *ctx, FILE *stream, const char *key);\r
272 +void sexp_attribute_key_value_separator(const void *ctx, FILE *stream);\r
273 +void sexp_end_attribute(const void *ctx, FILE *stream);\r
274 +void sexp_attribute_separator(const void *ctx, FILE *stream);\r
275 +void sexp_start_array(const void *ctx, FILE *stream);\r
276 +void sexp_array_item_separator(const void *ctx, FILE *stream);\r
277 +void sexp_end_array(const void *ctx, FILE *stream);\r
278 +void sexp_number(const void *ctx, FILE *stream, int number);\r
279 +void sexp_string(const void *ctx, FILE *stream, const char *string);\r
280 +void sexp_boolean(const void *ctx, FILE *stream, int boolean);\r
281 +\r
282 +void sexp_start_object(const void *ctx, FILE *stream) {\r
283 +    (void)ctx;\r
284 +    fputs("(", stream);\r
285 +}\r
286 +void sexp_end_object(const void *ctx, FILE *stream) {\r
287 +    (void)ctx;\r
288 +    fputs(")\n", stream);\r
289 +}\r
290 +void sexp_start_attribute(const void *ctx, FILE *stream) {\r
291 +    (void)ctx;\r
292 +    fputs("(", stream);\r
293 +}\r
294 +void sexp_attribute_key(const void *ctx, FILE *stream, const char *key) {\r
295 +    (void)ctx;\r
296 +    fprintf(stream, "%s", key);\r
297 +}\r
298 +void sexp_attribute_key_value_separator(const void *ctx, FILE *stream) {\r
299 +    (void)ctx;\r
300 +    fputs(" ", stream);\r
301 +}\r
302 +void sexp_end_attribute(const void *ctx, FILE *stream) {\r
303 +    (void)ctx;\r
304 +    fputs(")\n", stream);\r
305 +}\r
306 +void sexp_attribute_separator(const void *ctx, FILE *stream) {\r
307 +    (void)ctx;\r
308 +    fputs(" ", stream);\r
309 +}\r
310 +void sexp_start_array(const void *ctx, FILE *stream) {\r
311 +    (void)ctx;\r
312 +    fputs("(", stream);\r
313 +}\r
314 +void sexp_array_item_separator(const void *ctx, FILE *stream) {\r
315 +    (void)ctx;\r
316 +    fputs(" ", stream);\r
317 +}\r
318 +void sexp_end_array(const void *ctx, FILE *stream) {\r
319 +    (void)ctx;\r
320 +    fputs(")", stream);\r
321 +}\r
322 +void sexp_number(const void *ctx, FILE *stream, int number) {\r
323 +    (void)ctx;\r
324 +    fprintf(stream, "%i", number);\r
325 +}\r
326 +void sexp_string(const void *ctx, FILE *stream, const char *string) {\r
327 +    void *ctx_quote = talloc_new (ctx);    \r
328 +    fprintf(stream, "%s", json_quote_str (ctx_quote, string));\r
329 +    talloc_free (ctx_quote);\r
330 +}\r
331 +void sexp_boolean(const void *ctx, FILE *stream, int boolean) {\r
332 +    (void)ctx;\r
333 +    if(boolean)\r
334 +       fputs("#t", stream);\r
335 +    else\r
336 +       fputs("#f", stream);\r
337  }\r
338  \r
339 +static const search_format_t format_sexp = {\r
340 +    sexp_start_object,\r
341 +    sexp_end_object,\r
342 +    sexp_start_attribute,\r
343 +    sexp_attribute_key,\r
344 +    sexp_attribute_key_value_separator,\r
345 +    sexp_end_attribute,\r
346 +    sexp_attribute_separator,\r
347 +    sexp_start_array,\r
348 +    sexp_array_item_separator,\r
349 +    sexp_end_array,\r
350 +    sexp_number,\r
351 +    sexp_string,\r
352 +    sexp_boolean,\r
353 +};\r
354 +\r
355  static char *\r
356  sanitize_string (const void *ctx, const char *str)\r
357  {\r
358 @@ -128,70 +261,6 @@ sanitize_string (const void *ctx, const char *str)\r
359      return out;\r
360  }\r
361  \r
362 -static void\r
363 -format_thread_text (const void *ctx,\r
364 -                   const char *thread_id,\r
365 -                   const time_t date,\r
366 -                   const int matched,\r
367 -                   const int total,\r
368 -                   const char *authors,\r
369 -                   const char *subject)\r
370 -{\r
371 -    void *ctx_quote = talloc_new (ctx);\r
372 -\r
373 -    printf ("thread:%s %12s [%d/%d] %s; %s",\r
374 -           thread_id,\r
375 -           notmuch_time_relative_date (ctx, date),\r
376 -           matched,\r
377 -           total,\r
378 -           sanitize_string (ctx_quote, authors),\r
379 -           sanitize_string (ctx_quote, subject));\r
380 -\r
381 -    talloc_free (ctx_quote);\r
382 -}\r
383 -\r
384 -static void\r
385 -format_item_id_json (const void *ctx,\r
386 -                    unused (const char *item_type),\r
387 -                    const char *item_id)\r
388 -{\r
389 -    void *ctx_quote = talloc_new (ctx);\r
390 -\r
391 -    printf ("%s", json_quote_str (ctx_quote, item_id));\r
392 -\r
393 -    talloc_free (ctx_quote);\r
394 -    \r
395 -}\r
396 -\r
397 -static void\r
398 -format_thread_json (const void *ctx,\r
399 -                   const char *thread_id,\r
400 -                   const time_t date,\r
401 -                   const int matched,\r
402 -                   const int total,\r
403 -                   const char *authors,\r
404 -                   const char *subject)\r
405 -{\r
406 -    void *ctx_quote = talloc_new (ctx);\r
407 -\r
408 -    printf ("\"thread\": %s,\n"\r
409 -           "\"timestamp\": %ld,\n"\r
410 -           "\"date_relative\": \"%s\",\n"\r
411 -           "\"matched\": %d,\n"\r
412 -           "\"total\": %d,\n"\r
413 -           "\"authors\": %s,\n"\r
414 -           "\"subject\": %s,\n",\r
415 -           json_quote_str (ctx_quote, thread_id),\r
416 -           date,\r
417 -           notmuch_time_relative_date (ctx, date),\r
418 -           matched,\r
419 -           total,\r
420 -           json_quote_str (ctx_quote, authors),\r
421 -           json_quote_str (ctx_quote, subject));\r
422 -\r
423 -    talloc_free (ctx_quote);\r
424 -}\r
425 -\r
426  static int\r
427  do_search_threads (const search_format_t *format,\r
428                    notmuch_query_t *query,\r
429 @@ -217,7 +286,8 @@ do_search_threads (const search_format_t *format,\r
430      if (threads == NULL)\r
431         return 1;\r
432  \r
433 -    fputs (format->results_start, stdout);\r
434 +    if(format != &format_text)\r
435 +       format->start_array(threads, stdout);\r
436  \r
437      for (i = 0;\r
438          notmuch_threads_valid (threads) && (limit < 0 || i < offset + limit);\r
439 @@ -232,43 +302,82 @@ do_search_threads (const search_format_t *format,\r
440             continue;\r
441         }\r
442  \r
443 -       if (! first_thread)\r
444 -           fputs (format->item_sep, stdout);\r
445 +       if (! first_thread && format != &format_text)\r
446 +           format->array_item_separator(thread, stdout);\r
447  \r
448         if (output == OUTPUT_THREADS) {\r
449 -           format->item_id (thread, "thread:",\r
450 -                            notmuch_thread_get_thread_id (thread));\r
451 +           const char *thread_id = notmuch_thread_get_thread_id (thread);\r
452 +           if(format != &format_text)\r
453 +               //format_attribute_string(thread, stdout, format, "thread", thread_id);\r
454 +               format->string(thread, stdout, thread_id);\r
455 +           else /* text format */\r
456 +               printf("thread:%s\n", notmuch_thread_get_thread_id (thread));\r
457         } else { /* output == OUTPUT_SUMMARY */\r
458 -           fputs (format->item_start, stdout);\r
459 +           if(format != &format_text)\r
460 +               format->start_object(thread, stdout);\r
461  \r
462             if (sort == NOTMUCH_SORT_OLDEST_FIRST)\r
463                 date = notmuch_thread_get_oldest_date (thread);\r
464             else\r
465                 date = notmuch_thread_get_newest_date (thread);\r
466  \r
467 -           format->thread_summary (thread,\r
468 -                                   notmuch_thread_get_thread_id (thread),\r
469 -                                   date,\r
470 -                                   notmuch_thread_get_matched_messages (thread),\r
471 -                                   notmuch_thread_get_total_messages (thread),\r
472 -                                   notmuch_thread_get_authors (thread),\r
473 -                                   notmuch_thread_get_subject (thread));\r
474 -\r
475 -           fputs (format->tag_start, stdout);\r
476 +           if(format != &format_text) {\r
477 +               format_attribute_string(thread, stdout, format, "thread", notmuch_thread_get_thread_id (thread));\r
478 +               format->attribute_separator(thread, stdout);\r
479 +               format_attribute_number(thread, stdout, format, "timestamp", date);\r
480 +               format->attribute_separator(thread, stdout);\r
481 +/*             format_attribute_string(thread, stdout, format, "date_relative", notmuch_time_relative_date (thread, date)); */\r
482 +/*             format->attribute_separator(thread, stdout); */\r
483 +               format_attribute_number(thread, stdout, format, "matched", notmuch_thread_get_matched_messages (thread));\r
484 +               format->attribute_separator(thread, stdout);\r
485 +               format_attribute_number(thread, stdout, format, "total", notmuch_thread_get_total_messages (thread));\r
486 +               format->attribute_separator(thread, stdout);\r
487 +               format_attribute_string(thread, stdout, format, "authors", notmuch_thread_get_authors (thread));\r
488 +               format->attribute_separator(thread, stdout);\r
489 +               format_attribute_string(thread, stdout, format, "subject", notmuch_thread_get_subject (thread));\r
490 +               format->attribute_separator(thread, stdout);\r
491 +\r
492 +               format->start_attribute(thread, stdout);\r
493 +               format->attribute_key(thread, stdout, "tags");\r
494 +               format->attribute_key_value_separator(thread, stdout);\r
495 +               format->start_array(thread, stdout);\r
496 +           } else { /* text format */\r
497 +               void *ctx_quote = talloc_new (thread);\r
498 +               printf ("thread:%s %12s [%d/%d] %s; %s (",\r
499 +                       notmuch_thread_get_thread_id (thread),\r
500 +                       notmuch_time_relative_date (ctx_quote, date),\r
501 +                       notmuch_thread_get_matched_messages (thread),\r
502 +                       notmuch_thread_get_total_messages (thread),\r
503 +                       sanitize_string (ctx_quote, notmuch_thread_get_authors (thread)),\r
504 +                       sanitize_string (ctx_quote, notmuch_thread_get_subject (thread)));\r
505 +               talloc_free (ctx_quote);\r
506 +           }\r
507  \r
508             for (tags = notmuch_thread_get_tags (thread);\r
509                  notmuch_tags_valid (tags);\r
510                  notmuch_tags_move_to_next (tags))\r
511             {\r
512 -               if (! first_tag)\r
513 -                   fputs (format->tag_sep, stdout);\r
514 -               printf (format->tag, notmuch_tags_get (tags));\r
515 +               if (! first_tag) {\r
516 +                   if(format != &format_text)\r
517 +                       format->array_item_separator(thread, stdout);\r
518 +                   else /* text format */\r
519 +                       printf(" ");\r
520 +               }\r
521 +               if(format != &format_text)\r
522 +                   format->string(thread, stdout, notmuch_tags_get(tags));\r
523 +               else /* text format */\r
524 +                   printf("%s", notmuch_tags_get(tags));\r
525                 first_tag = 0;\r
526             }\r
527  \r
528 -           fputs (format->tag_end, stdout);\r
529 +           if(format != &format_text) {\r
530 +               format->end_array(thread, stdout);\r
531 +               format->end_attribute(thread, stdout);\r
532 +               format->end_object(thread, stdout);\r
533 +           } else {\r
534 +               printf(")\n");\r
535 +           }\r
536  \r
537 -           fputs (format->item_end, stdout);\r
538         }\r
539  \r
540         first_thread = 0;\r
541 @@ -276,10 +385,10 @@ do_search_threads (const search_format_t *format,\r
542         notmuch_thread_destroy (thread);\r
543      }\r
544  \r
545 -    if (first_thread)\r
546 -       fputs (format->results_null, stdout);\r
547 -    else\r
548 -       fputs (format->results_end, stdout);\r
549 +    if(format != &format_text) {\r
550 +       format->end_array(threads, stdout);\r
551 +       fputs("\n", stdout);\r
552 +    }\r
553  \r
554      return 0;\r
555  }\r
556 @@ -307,7 +416,8 @@ do_search_messages (const search_format_t *format,\r
557      if (messages == NULL)\r
558         return 1;\r
559  \r
560 -    fputs (format->results_start, stdout);\r
561 +    if(format != &format_text)\r
562 +       format->start_array(messages, stdout);\r
563  \r
564      for (i = 0;\r
565          notmuch_messages_valid (messages) && (limit < 0 || i < offset + limit);\r
566 @@ -325,11 +435,15 @@ do_search_messages (const search_format_t *format,\r
567                  notmuch_filenames_valid (filenames);\r
568                  notmuch_filenames_move_to_next (filenames))\r
569             {\r
570 -               if (! first_message)\r
571 -                   fputs (format->item_sep, stdout);\r
572 +               if (! first_message) {\r
573 +                   if(format != &format_text)\r
574 +                       format->array_item_separator(message, stdout);\r
575 +               }\r
576  \r
577 -               format->item_id (message, "",\r
578 -                                notmuch_filenames_get (filenames));\r
579 +               if(format != &format_text)\r
580 +                   format->string(message, stdout, notmuch_filenames_get (filenames));\r
581 +               else\r
582 +                   printf("%s\n", notmuch_filenames_get (filenames));\r
583  \r
584                 first_message = 0;\r
585             }\r
586 @@ -337,11 +451,15 @@ do_search_messages (const search_format_t *format,\r
587             notmuch_filenames_destroy( filenames );\r
588  \r
589         } else { /* output == OUTPUT_MESSAGES */\r
590 -           if (! first_message)\r
591 -               fputs (format->item_sep, stdout);\r
592 +           if (! first_message) {\r
593 +           if(format != &format_text)\r
594 +               format->array_item_separator(message, stdout);\r
595 +           }\r
596  \r
597 -           format->item_id (message, "id:",\r
598 -                            notmuch_message_get_message_id (message));\r
599 +           if(format != &format_text)\r
600 +               format->string(message, stdout, notmuch_message_get_message_id (message));\r
601 +           else /* text format */\r
602 +               printf("id:%s\n", notmuch_message_get_message_id (message));\r
603             first_message = 0;\r
604         }\r
605  \r
606 @@ -350,11 +468,11 @@ do_search_messages (const search_format_t *format,\r
607  \r
608      notmuch_messages_destroy (messages);\r
609  \r
610 -    if (first_message)\r
611 -       fputs (format->results_null, stdout);\r
612 -    else\r
613 -       fputs (format->results_end, stdout);\r
614 -\r
615 +    if(format != &format_text) {\r
616 +       format->end_array(messages, stdout);\r
617 +       fputs("\n", stdout);\r
618 +    }\r
619 +    \r
620      return 0;\r
621  }\r
622  \r
623 @@ -381,7 +499,8 @@ do_search_tags (notmuch_database_t *notmuch,\r
624      if (tags == NULL)\r
625         return 1;\r
626  \r
627 -    fputs (format->results_start, stdout);\r
628 +    if(format != &format_text)\r
629 +       format->start_array(tags, stdout);\r
630  \r
631      for (;\r
632          notmuch_tags_valid (tags);\r
633 @@ -389,10 +508,15 @@ do_search_tags (notmuch_database_t *notmuch,\r
634      {\r
635         tag = notmuch_tags_get (tags);\r
636  \r
637 -       if (! first_tag)\r
638 -           fputs (format->item_sep, stdout);\r
639 +       if (! first_tag) {\r
640 +           if(format != &format_text)\r
641 +               format->array_item_separator(tag, stdout);\r
642 +       }\r
643  \r
644 -       format->item_id (tags, "", tag);\r
645 +       if(format != &format_text)\r
646 +           format->string(tags, stdout, tag);\r
647 +       else\r
648 +           printf("%s\n", tag);\r
649  \r
650         first_tag = 0;\r
651      }\r
652 @@ -402,10 +526,8 @@ do_search_tags (notmuch_database_t *notmuch,\r
653      if (messages)\r
654         notmuch_messages_destroy (messages);\r
655  \r
656 -    if (first_tag)\r
657 -       fputs (format->results_null, stdout);\r
658 -    else\r
659 -       fputs (format->results_end, stdout);\r
660 +    if(format != &format_text)\r
661 +       format->end_array(tags, stdout);\r
662  \r
663      return 0;\r
664  }\r
665 @@ -427,7 +549,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
666      size_t auto_exclude_tags_length;\r
667      unsigned int i;\r
668  \r
669 -    enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT }\r
670 +    enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT, NOTMUCH_FORMAT_SEXP }\r
671         format_sel = NOTMUCH_FORMAT_TEXT;\r
672  \r
673      notmuch_opt_desc_t options[] = {\r
674 @@ -437,6 +559,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
675                                   { 0, 0 } } },\r
676         { NOTMUCH_OPT_KEYWORD, &format_sel, "format", 'f',\r
677           (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },\r
678 +                                 { "sexp", NOTMUCH_FORMAT_SEXP },\r
679                                   { "text", NOTMUCH_FORMAT_TEXT },\r
680                                   { 0, 0 } } },\r
681         { NOTMUCH_OPT_KEYWORD, &output, "output", 'o',\r
682 @@ -464,6 +587,8 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
683      case NOTMUCH_FORMAT_JSON:\r
684         format = &format_json;\r
685         break;\r
686 +    case NOTMUCH_FORMAT_SEXP:\r
687 +       format = &format_sexp;\r
688      }\r
689  \r
690      config = notmuch_config_open (ctx, NULL, NULL);\r
691 -- \r
692 1.7.7.3\r
693 \r