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
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
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
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
56 notmuch-search.c | 493 ++++++++++++++++++++++++++++++++++--------------------
\r
57 1 files changed, 309 insertions(+), 184 deletions(-)
\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
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
77 - const char *authors,
\r
78 - const char *subject);
\r
79 - const char *tag_start;
\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
103 -format_item_id_text (const void *ctx,
\r
104 - const char *item_type,
\r
105 - const char *item_id);
\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
113 - const char *authors,
\r
114 - const char *subject);
\r
115 +/* dummy format */
\r
116 static const search_format_t format_text = {
\r
119 - format_item_id_text,
\r
120 - format_thread_text,
\r
128 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
\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
145 +void json_start_object(const void *ctx, FILE *stream) {
\r
147 + fputs("{", stream);
\r
149 +void json_end_object(const void *ctx, FILE *stream) {
\r
151 + fputs("}\n", stream);
\r
153 +void json_start_attribute(const void *ctx, FILE *stream) {
\r
157 +void json_attribute_key(const void *ctx, FILE *stream, const char *key) {
\r
159 + fprintf(stream, "\"%s\"", key);
\r
161 +void json_attribute_key_value_separator(const void *ctx, FILE *stream) {
\r
163 + fputs(": ", stream);
\r
165 +void json_end_attribute(const void *ctx, FILE *stream) {
\r
169 +void json_attribute_separator(const void *ctx, FILE *stream) {
\r
171 + fputs(",\n", stream);
\r
173 +void json_start_array(const void *ctx, FILE *stream) {
\r
175 + fputs("[", stream);
\r
177 +void json_array_item_separator(const void *ctx, FILE *stream) {
\r
179 + fputs(", ", stream);
\r
181 +void json_end_array(const void *ctx, FILE *stream) {
\r
183 + fputs("]", stream);
\r
185 +void json_number(const void *ctx, FILE *stream, int number) {
\r
187 + fprintf(stream, "%i", number);
\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
194 +void json_boolean(const void *ctx, FILE *stream, int boolean) {
\r
197 + fputs("true", stream);
\r
199 + fputs("false", stream);
\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
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
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
224 -format_item_id_json (const void *ctx,
\r
225 - const char *item_type,
\r
226 - const char *item_id);
\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
234 - const char *authors,
\r
235 - const char *subject);
\r
236 static const search_format_t format_json = {
\r
239 - format_item_id_json,
\r
240 - format_thread_json,
\r
247 + json_start_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
263 -format_item_id_text (unused (const void *ctx),
\r
264 - const char *item_type,
\r
265 - const char *item_id)
\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
282 +void sexp_start_object(const void *ctx, FILE *stream) {
\r
284 + fputs("(", stream);
\r
286 +void sexp_end_object(const void *ctx, FILE *stream) {
\r
288 + fputs(")\n", stream);
\r
290 +void sexp_start_attribute(const void *ctx, FILE *stream) {
\r
292 + fputs("(", stream);
\r
294 +void sexp_attribute_key(const void *ctx, FILE *stream, const char *key) {
\r
296 + fprintf(stream, "%s", key);
\r
298 +void sexp_attribute_key_value_separator(const void *ctx, FILE *stream) {
\r
300 + fputs(" ", stream);
\r
302 +void sexp_end_attribute(const void *ctx, FILE *stream) {
\r
304 + fputs(")\n", stream);
\r
306 +void sexp_attribute_separator(const void *ctx, FILE *stream) {
\r
308 + fputs(" ", stream);
\r
310 +void sexp_start_array(const void *ctx, FILE *stream) {
\r
312 + fputs("(", stream);
\r
314 +void sexp_array_item_separator(const void *ctx, FILE *stream) {
\r
316 + fputs(" ", stream);
\r
318 +void sexp_end_array(const void *ctx, FILE *stream) {
\r
320 + fputs(")", stream);
\r
322 +void sexp_number(const void *ctx, FILE *stream, int number) {
\r
324 + fprintf(stream, "%i", number);
\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
331 +void sexp_boolean(const void *ctx, FILE *stream, int boolean) {
\r
334 + fputs("#t", stream);
\r
336 + fputs("#f", stream);
\r
339 +static const search_format_t format_sexp = {
\r
340 + sexp_start_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
356 sanitize_string (const void *ctx, const char *str)
\r
358 @@ -128,70 +261,6 @@ sanitize_string (const void *ctx, const char *str)
\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
368 - const char *authors,
\r
369 - const char *subject)
\r
371 - void *ctx_quote = talloc_new (ctx);
\r
373 - printf ("thread:%s %12s [%d/%d] %s; %s",
\r
375 - notmuch_time_relative_date (ctx, date),
\r
378 - sanitize_string (ctx_quote, authors),
\r
379 - sanitize_string (ctx_quote, subject));
\r
381 - talloc_free (ctx_quote);
\r
385 -format_item_id_json (const void *ctx,
\r
386 - unused (const char *item_type),
\r
387 - const char *item_id)
\r
389 - void *ctx_quote = talloc_new (ctx);
\r
391 - printf ("%s", json_quote_str (ctx_quote, item_id));
\r
393 - talloc_free (ctx_quote);
\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
403 - const char *authors,
\r
404 - const char *subject)
\r
406 - void *ctx_quote = talloc_new (ctx);
\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
417 - notmuch_time_relative_date (ctx, date),
\r
420 - json_quote_str (ctx_quote, authors),
\r
421 - json_quote_str (ctx_quote, subject));
\r
423 - talloc_free (ctx_quote);
\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
433 - fputs (format->results_start, stdout);
\r
434 + if(format != &format_text)
\r
435 + format->start_array(threads, stdout);
\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
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
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
462 if (sort == NOTMUCH_SORT_OLDEST_FIRST)
\r
463 date = notmuch_thread_get_oldest_date (thread);
\r
465 date = notmuch_thread_get_newest_date (thread);
\r
467 - format->thread_summary (thread,
\r
468 - notmuch_thread_get_thread_id (thread),
\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
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
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
508 for (tags = notmuch_thread_get_tags (thread);
\r
509 notmuch_tags_valid (tags);
\r
510 notmuch_tags_move_to_next (tags))
\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
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
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
537 - fputs (format->item_end, stdout);
\r
541 @@ -276,10 +385,10 @@ do_search_threads (const search_format_t *format,
\r
542 notmuch_thread_destroy (thread);
\r
545 - if (first_thread)
\r
546 - fputs (format->results_null, stdout);
\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
556 @@ -307,7 +416,8 @@ do_search_messages (const search_format_t *format,
\r
557 if (messages == NULL)
\r
560 - fputs (format->results_start, stdout);
\r
561 + if(format != &format_text)
\r
562 + format->start_array(messages, stdout);
\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
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
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
582 + printf("%s\n", notmuch_filenames_get (filenames));
\r
586 @@ -337,11 +451,15 @@ do_search_messages (const search_format_t *format,
\r
587 notmuch_filenames_destroy( filenames );
\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
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
606 @@ -350,11 +468,11 @@ do_search_messages (const search_format_t *format,
\r
608 notmuch_messages_destroy (messages);
\r
610 - if (first_message)
\r
611 - fputs (format->results_null, stdout);
\r
613 - fputs (format->results_end, stdout);
\r
615 + if(format != &format_text) {
\r
616 + format->end_array(messages, stdout);
\r
617 + fputs("\n", stdout);
\r
623 @@ -381,7 +499,8 @@ do_search_tags (notmuch_database_t *notmuch,
\r
627 - fputs (format->results_start, stdout);
\r
628 + if(format != &format_text)
\r
629 + format->start_array(tags, stdout);
\r
632 notmuch_tags_valid (tags);
\r
633 @@ -389,10 +508,15 @@ do_search_tags (notmuch_database_t *notmuch,
\r
635 tag = notmuch_tags_get (tags);
\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
644 - format->item_id (tags, "", tag);
\r
645 + if(format != &format_text)
\r
646 + format->string(tags, stdout, tag);
\r
648 + printf("%s\n", tag);
\r
652 @@ -402,10 +526,8 @@ do_search_tags (notmuch_database_t *notmuch,
\r
654 notmuch_messages_destroy (messages);
\r
657 - fputs (format->results_null, stdout);
\r
659 - fputs (format->results_end, stdout);
\r
660 + if(format != &format_text)
\r
661 + format->end_array(tags, stdout);
\r
665 @@ -427,7 +549,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
\r
666 size_t auto_exclude_tags_length;
\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
673 notmuch_opt_desc_t options[] = {
\r
674 @@ -437,6 +559,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
\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
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
686 + case NOTMUCH_FORMAT_SEXP:
\r
687 + format = &format_sexp;
\r
690 config = notmuch_config_open (ctx, NULL, NULL);
\r