unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show.
       [not found] <id:1262249616-sup-3911@elise>
@ 2009-12-31 15:17 ` david
  2009-12-31 20:27   ` Scott Robinson
                     ` (2 more replies)
  0 siblings, 3 replies; 16+ messages in thread
From: david @ 2009-12-31 15:17 UTC (permalink / raw)
  To: notmuch

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain, Size: 26860 bytes --]

From: Scott Robinson <scott@quadhome.com>

In the case of notmuch-show, "--format=json" also implies
"--entire-thread" as the thread structure is implicit in the emitted
document tree.

As a coincidence to the implementation, multipart message ID numbers are
now incremented with each part printed. This changes the previous
semantics, which were unclear and not necessary related to the actual
ordering of the message parts.
---

This is an updated version of Scott Robinson's patch of
id:1261114167-sup-8228@lisa. All of the hard work is Scott's. My
changes to Scotts patch are 
 - change option to --format from --output
 - make a couple of calls to function points conditional on the 
   function pointers being non-null
 - updated the built-in documentation and the man page

I also changed the copyright of json.c to Scott. Hope that's OK with
Scott.

 Makefile.local   |    3 +-
 json.c           |   72 ++++++++++++++
 notmuch-client.h |    3 +
 notmuch-search.c |  163 +++++++++++++++++++++++++++++---
 notmuch-show.c   |  278 ++++++++++++++++++++++++++++++++++++++++++++++--------
 notmuch.1        |   27 +++++-
 notmuch.c        |   29 +++++--
 show-message.c   |    4 +-
 8 files changed, 514 insertions(+), 65 deletions(-)
 create mode 100644 json.c

diff --git a/Makefile.local b/Makefile.local
index 933ff4c..53b474b 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -18,7 +18,8 @@ notmuch_client_srcs =		\
 	notmuch-tag.c		\
 	notmuch-time.c		\
 	query-string.c		\
-	show-message.c
+	show-message.c		\
+	json.c
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
 notmuch: $(notmuch_client_modules) lib/notmuch.a
diff --git a/json.c b/json.c
new file mode 100644
index 0000000..3960750
--- /dev/null
+++ b/json.c
@@ -0,0 +1,72 @@
+/* notmuch - Not much of an email program, (just index and search)
+ *
+ * Copyright © 2009 Scott Robinson
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Authors: Scott Robinson <scott@quadhome.com>
+ *
+ */
+
+#include "notmuch-client.h"
+
+/*
+ * json_quote_str derived from cJSON's print_string_ptr,
+ * Copyright (c) 2009 Dave Gamble
+ */
+
+char *
+json_quote_str(const void *ctx, const char *str)
+{
+    const char *ptr;
+    char *ptr2;
+    char *out;
+    int len = 0;
+
+    if (!str)
+	return NULL;
+
+    for (ptr = str; *ptr; len++, ptr++) {
+	if (*ptr < 32 || *ptr == '\"' || *ptr == '\\')
+	    len++;
+    }
+
+    out = talloc_array (ctx, char, len + 3);
+
+    ptr = str;
+    ptr2 = out;
+
+    *ptr2++ = '\"';
+    while (*ptr) {
+	    if (*ptr > 31 && *ptr != '\"' && *ptr != '\\') {
+		*ptr2++ = *ptr++;
+	    } else {
+		*ptr2++ = '\\';
+		switch (*ptr++) {
+		    case '\"':	*ptr2++ = '\"';	break;
+		    case '\\':	*ptr2++ = '\\';	break;
+		    case '\b':	*ptr2++ = 'b';	break;
+		    case '\f':	*ptr2++ = 'f';	break;
+		    case '\n':	*ptr2++ = 'n';	break;
+		    case '\r':	*ptr2++ = 'r';	break;
+		    case '\t':	*ptr2++ = 't';	break;
+		    default:	 ptr2--;	break;
+		}
+	    }
+    }
+    *ptr2++ = '\"';
+    *ptr2++ = '\0';
+
+    return out;
+}
diff --git a/notmuch-client.h b/notmuch-client.h
index 50a30fe..7b844b9 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -143,6 +143,9 @@ notmuch_status_t
 show_message_body (const char *filename,
 		   void (*show_part) (GMimeObject *part, int *part_count));
 
+char *
+json_quote_str (const void *ctx, const char *str);
+
 /* notmuch-config.c */
 
 typedef struct _notmuch_config notmuch_config_t;
diff --git a/notmuch-search.c b/notmuch-search.c
index dc44eb6..482c6e8 100644
--- a/notmuch-search.c
+++ b/notmuch-search.c
@@ -20,8 +20,120 @@
 
 #include "notmuch-client.h"
 
+typedef struct search_format {
+    const char *results_start;
+    const char *thread_start;
+    void (*thread) (const void *ctx,
+		    const char *id,
+		    const time_t date,
+		    const int matched,
+		    const int total,
+		    const char *authors,
+		    const char *subject);
+    const char *tag_start;
+    const char *tag;
+    const char *tag_sep;
+    const char *tag_end;
+    const char *thread_sep;
+    const char *thread_end;
+    const char *results_end;
+} search_format_t;
+
+static void
+format_thread_text (const void *ctx,
+		    const char *id,
+		    const time_t date,
+		    const int matched,
+		    const int total,
+		    const char *authors,
+		    const char *subject);
+static const search_format_t format_text = {
+    "",
+	"",
+	    format_thread_text,
+	    " (",
+		"%s", " ",
+	    ")", "",
+	"\n",
+    "",
+};
+
+static void
+format_thread_json (const void *ctx,
+		    const char *id,
+		    const time_t date,
+		    const int matched,
+		    const int total,
+		    const char *authors,
+		    const char *subject);
+static const search_format_t format_json = {
+    "[",
+	"{",
+	    format_thread_json,
+	    "\"tags\": [",
+		"\"%s\"", ", ",
+	    "]", ",\n",
+	"}",
+    "]\n",
+};
+
+static void
+format_thread_text (const void *ctx,
+		    const char *id,
+		    const time_t date,
+		    const int matched,
+		    const int total,
+		    const char *authors,
+		    const char *subject)
+{
+    printf ("thread:%s %12s [%d/%d] %s; %s",
+	    id,
+	    notmuch_time_relative_date (ctx, date),
+	    matched,
+	    total,
+	    authors,
+	    subject);
+}
+
+static void
+format_thread_json (const void *ctx,
+		    const char *id,
+		    const time_t date,
+		    const int matched,
+		    const int total,
+		    const char *authors,
+		    const char *subject)
+{
+    struct tm *tm;
+    char timestamp[40];
+    void *ctx_quote = talloc_new (ctx);
+
+    tm = gmtime (&date);
+    if (tm == NULL)
+	INTERNAL_ERROR ("gmtime failed on thread %s.", id);
+
+    if (strftime (timestamp, sizeof (timestamp), "%s", tm) == 0)
+	INTERNAL_ERROR ("strftime failed on thread %s.", id);
+
+    printf ("\"id\": %s,\n"
+	    "\"timestamp\": %s,\n"
+	    "\"matched\": %d,\n"
+	    "\"total\": %d,\n"
+	    "\"authors\": %s,\n"
+	    "\"subject\": %s,\n",
+	    json_quote_str (ctx_quote, id),
+	    timestamp,
+	    matched,
+	    total,
+	    json_quote_str (ctx_quote, authors),
+	    json_quote_str (ctx_quote, subject));
+
+    talloc_free (ctx_quote);
+}
+
 static void
 do_search_threads (const void *ctx,
+		   const search_format_t *format,
 		   notmuch_query_t *query,
 		   notmuch_sort_t sort)
 {
@@ -29,7 +141,9 @@ do_search_threads (const void *ctx,
     notmuch_threads_t *threads;
     notmuch_tags_t *tags;
     time_t date;
-    const char *relative_date;
+    int first_thread = 1;
+
+    fputs (format->results_start, stdout);
 
     for (threads = notmuch_query_search_threads (query);
 	 notmuch_threads_has_more (threads);
@@ -37,6 +151,9 @@ do_search_threads (const void *ctx,
     {
 	int first_tag = 1;
 
+	if (! first_thread)
+	    fputs (format->thread_sep, stdout);
+
 	thread = notmuch_threads_get (threads);
 
 	if (sort == NOTMUCH_SORT_OLDEST_FIRST)
@@ -44,30 +161,37 @@ do_search_threads (const void *ctx,
 	else
 	    date = notmuch_thread_get_newest_date (thread);
 
-	relative_date = notmuch_time_relative_date (ctx, date);
+	fputs (format->thread_start, stdout);
+
+	format->thread (ctx,
+			notmuch_thread_get_thread_id (thread),
+			date,
+			notmuch_thread_get_matched_messages (thread),
+			notmuch_thread_get_total_messages (thread),
+			notmuch_thread_get_authors (thread),
+			notmuch_thread_get_subject (thread));
 
-	printf ("thread:%s %12s [%d/%d] %s; %s",
-		notmuch_thread_get_thread_id (thread),
-		relative_date,
-		notmuch_thread_get_matched_messages (thread),
-		notmuch_thread_get_total_messages (thread),
-		notmuch_thread_get_authors (thread),
-		notmuch_thread_get_subject (thread));
+	fputs (format->tag_start, stdout);
 
-	printf (" (");
 	for (tags = notmuch_thread_get_tags (thread);
 	     notmuch_tags_has_more (tags);
 	     notmuch_tags_advance (tags))
 	{
 	    if (! first_tag)
-		printf (" ");
-	    printf ("%s", notmuch_tags_get (tags));
+		fputs (format->tag_sep, stdout);
+	    printf (format->tag, notmuch_tags_get (tags));
 	    first_tag = 0;
 	}
-	printf (")\n");
+
+	fputs (format->tag_end, stdout);
+	fputs (format->thread_end, stdout);
+
+	first_thread = 0;
 
 	notmuch_thread_destroy (thread);
     }
+
+    fputs (format->results_end, stdout);
 }
 
 int
@@ -79,6 +203,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
     char *query_str;
     char *opt;
     notmuch_sort_t sort = NOTMUCH_SORT_NEWEST_FIRST;
+    const search_format_t *format = &format_text;
     int i;
 
     for (i = 0; i < argc && argv[i][0] == '-'; i++) {
@@ -96,6 +221,16 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
 		fprintf (stderr, "Invalid value for --sort: %s\n", opt);
 		return 1;
 	    }
+	} else if (STRNCMP_LITERAL (argv[i], "--format=") == 0) {
+	    opt = argv[i] + sizeof ("--format=") - 1;
+	    if (strcmp (opt, "text") == 0) {
+		format = &format_text;
+	    } else if (strcmp (opt, "json") == 0) {
+		format = &format_json;
+	    } else {
+		fprintf (stderr, "Invalid value for --format: %s\n", opt);
+		return 1;
+	    }
 	} else {
 	    fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
 	    return 1;
@@ -132,7 +267,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
 
     notmuch_query_set_sort (query, sort);
 
-    do_search_threads (ctx, query, sort);
+    do_search_threads (ctx, format, query, sort);
 
     notmuch_query_destroy (query);
     notmuch_database_close (notmuch);
diff --git a/notmuch-show.c b/notmuch-show.c
index 376aacd..1a1d601 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -20,8 +20,65 @@
 
 #include "notmuch-client.h"
 
+typedef struct show_format {
+    const char *message_set_start;
+    const char *message_start;
+    void (*message) (const void *ctx,
+		     notmuch_message_t *message,
+		     int indent);
+    const char *header_start;
+    void (*header) (const void *ctx,
+		    notmuch_message_t *message);
+    const char *header_end;
+    const char *body_start;
+    void (*part) (GMimeObject *part,
+		  int *part_count);
+    const char *body_end;
+    const char *message_end;
+    const char *message_set_sep;
+    const char *message_set_end;
+} show_format_t;
+
+static void
+format_message_text (unused (const void *ctx),
+		     notmuch_message_t *message,
+		     int indent);
+static void
+format_headers_text (const void *ctx,
+		     notmuch_message_t *message);
+static void
+format_part_text (GMimeObject *part,
+		  int *part_count);
+static const show_format_t format_text = {
+    "",
+	"\fmessage{ ", format_message_text,
+	    "\fheader{\n", format_headers_text, "\fheader}\n",
+	    "\fbody{\n", format_part_text, "\fbody}\n",
+	"\fmessage}\n", "",
+    ""
+};
+
+static void
+format_message_json (const void *ctx,
+		     notmuch_message_t *message,
+		     unused (int indent));
+static void
+format_headers_json (const void *ctx,
+		     notmuch_message_t *message);
+static void
+format_part_json (GMimeObject *part,
+		  int *part_count);
+static const show_format_t format_json = {
+    "[",
+	"{", format_message_json,
+	    ", \"headers\": {", format_headers_json, "}",
+	    ", \"body\": [", format_part_json, "]",
+	"}", ", ",
+    "]"
+};
+
 static const char *
-_get_tags_as_string (void *ctx, notmuch_message_t *message)
+_get_tags_as_string (const void *ctx, notmuch_message_t *message)
 {
     notmuch_tags_t *tags;
     int first = 1;
@@ -48,7 +105,7 @@ _get_tags_as_string (void *ctx, notmuch_message_t *message)
 
 /* Get a nice, single-line summary of message. */
 static const char *
-_get_one_line_summary (void *ctx, notmuch_message_t *message)
+_get_one_line_summary (const void *ctx, notmuch_message_t *message)
 {
     const char *from;
     time_t date;
@@ -67,18 +124,87 @@ _get_one_line_summary (void *ctx, notmuch_message_t *message)
 }
 
 static void
-show_part_content (GMimeObject *part)
+format_message_text (unused (const void *ctx), notmuch_message_t *message, int indent)
+{
+    printf ("id:%s depth:%d match:%d filename:%s\n",
+	    notmuch_message_get_message_id (message),
+	    indent,
+	    notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH),
+	    notmuch_message_get_filename (message));
+}
+
+static void
+format_message_json (const void *ctx, notmuch_message_t *message, unused (int indent))
+{
+    void *ctx_quote = talloc_new (ctx);
+
+    printf ("\"id\": %s, \"match\": %s, \"filename\": %s",
+	    json_quote_str (ctx_quote, notmuch_message_get_message_id (message)),
+	    notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH) ? "true" : "false",
+	    json_quote_str (ctx_quote, notmuch_message_get_filename (message)));
+
+    talloc_free (ctx_quote);
+}
+
+static void
+format_headers_text (const void *ctx, notmuch_message_t *message)
+{
+    const char *headers[] = {
+	"Subject", "From", "To", "Cc", "Bcc", "Date"
+    };
+    const char *name, *value;
+    unsigned int i;
+
+    printf ("%s\n", _get_one_line_summary (ctx, message));
+
+    for (i = 0; i < ARRAY_SIZE (headers); i++) {
+	name = headers[i];
+	value = notmuch_message_get_header (message, name);
+	if (value)
+	    printf ("%s: %s\n", name, value);
+    }
+}
+
+static void
+format_headers_json (const void *ctx, notmuch_message_t *message)
+{
+    const char *headers[] = {
+	"Subject", "From", "To", "Cc", "Bcc", "Date"
+    };
+    const char *name, *value;
+    unsigned int i;
+    int first_header = 1;
+    void *ctx_quote = talloc_new (ctx);
+
+    for (i = 0; i < ARRAY_SIZE (headers); i++) {
+	name = headers[i];
+	value = notmuch_message_get_header (message, name);
+	if (value)
+	{
+	    if (!first_header)
+		fputs (", ", stdout);
+	    first_header = 0;
+
+	    printf ("%s: %s",
+		    json_quote_str (ctx_quote, name),
+		    json_quote_str (ctx_quote, value));
+	}
+    }
+
+    talloc_free (ctx_quote);
+}
+
+static void
+show_part_content (GMimeObject *part, GMimeStream *stream_out)
 {
-    GMimeStream *stream_stdout = g_mime_stream_file_new (stdout);
     GMimeStream *stream_filter = NULL;
     GMimeDataWrapper *wrapper;
     const char *charset;
 
     charset = g_mime_object_get_content_type_parameter (part, "charset");
 
-    if (stream_stdout) {
-	g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE);
-	stream_filter = g_mime_stream_filter_new(stream_stdout);
+    if (stream_out) {
+	stream_filter = g_mime_stream_filter_new(stream_out);
 	g_mime_stream_filter_add(GMIME_STREAM_FILTER(stream_filter),
 				 g_mime_filter_crlf_new(FALSE, FALSE));
         if (charset) {
@@ -92,15 +218,16 @@ show_part_content (GMimeObject *part)
 	g_mime_data_wrapper_write_to_stream (wrapper, stream_filter);
     if (stream_filter)
 	g_object_unref(stream_filter);
-    if (stream_stdout)
-	g_object_unref(stream_stdout);
 }
 
 static void
-show_part (GMimeObject *part, int *part_count)
+format_part_text (GMimeObject *part, int *part_count)
 {
     GMimeContentDisposition *disposition;
     GMimeContentType *content_type;
+    GMimeStream *stream_stdout = g_mime_stream_file_new (stdout);
+
+    g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE);
 
     disposition = g_mime_object_get_content_disposition (part);
     if (disposition &&
@@ -118,11 +245,14 @@ show_part (GMimeObject *part, int *part_count)
 	if (g_mime_content_type_is_type (content_type, "text", "*") &&
 	    !g_mime_content_type_is_type (content_type, "text", "html"))
 	{
-	    show_part_content (part);
+	    show_part_content (part, stream_stdout);
 	}
 
 	printf ("\fattachment}\n");
 
+	if (stream_stdout)
+	    g_object_unref(stream_stdout);
+
 	return;
     }
 
@@ -135,7 +265,7 @@ show_part (GMimeObject *part, int *part_count)
     if (g_mime_content_type_is_type (content_type, "text", "*") &&
 	!g_mime_content_type_is_type (content_type, "text", "html"))
     {
-	show_part_content (part);
+	show_part_content (part, stream_stdout);
     }
     else
     {
@@ -144,57 +274,96 @@ show_part (GMimeObject *part, int *part_count)
     }
 
     printf ("\fpart}\n");
+
+    if (stream_stdout)
+	g_object_unref(stream_stdout);
 }
 
 static void
-show_message (void *ctx, notmuch_message_t *message, int indent)
+format_part_json (GMimeObject *part, int *part_count)
 {
-    const char *headers[] = {
-	"Subject", "From", "To", "Cc", "Bcc", "Date"
-    };
-    const char *name, *value;
-    unsigned int i;
+    GMimeContentType *content_type;
+    GMimeContentDisposition *disposition;
+    void *ctx = talloc_new (NULL);
+    GMimeStream *stream_memory = g_mime_stream_mem_new ();
+    GByteArray *part_content;
 
-    printf ("\fmessage{ id:%s depth:%d match:%d filename:%s\n",
-	    notmuch_message_get_message_id (message),
-	    indent,
-	    notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH),
-	    notmuch_message_get_filename (message));
+    content_type = g_mime_object_get_content_type (GMIME_OBJECT (part));
 
-    printf ("\fheader{\n");
+    if (*part_count > 1)
+	fputs (", ", stdout);
 
-    printf ("%s\n", _get_one_line_summary (ctx, message));
+    printf ("{\"id\": %d, \"content-type\": %s",
+	    *part_count,
+	    json_quote_str (ctx, g_mime_content_type_to_string (content_type)));
 
-    for (i = 0; i < ARRAY_SIZE (headers); i++) {
-	name = headers[i];
-	value = notmuch_message_get_header (message, name);
-	if (value)
-	    printf ("%s: %s\n", name, value);
+    disposition = g_mime_object_get_content_disposition (part);
+    if (disposition &&
+	strcmp (disposition->disposition, GMIME_DISPOSITION_ATTACHMENT) == 0)
+    {
+	const char *filename = g_mime_part_get_filename (GMIME_PART (part));
+
+	printf (", \"filename\": %s", json_quote_str (ctx, filename));
     }
 
-    printf ("\fheader}\n");
-    printf ("\fbody{\n");
+    if (g_mime_content_type_is_type (content_type, "text", "*") &&
+	!g_mime_content_type_is_type (content_type, "text", "html"))
+    {
+	show_part_content (part, stream_memory);
+	part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
+
+	printf (", \"content\": %s", json_quote_str (ctx, (char *) part_content->data));
+    }
+
+    fputs ("}", stdout);
+
+    talloc_free (ctx);
+    if (stream_memory)
+	g_object_unref (stream_memory);
+}
+
+static void
+show_message (void *ctx, const show_format_t *format, notmuch_message_t *message, int indent)
+{
+    fputs (format->message_start, stdout);
+    if (format->message)
+	format->message(ctx, message, indent);
 
-    show_message_body (notmuch_message_get_filename (message), show_part);
+    fputs (format->header_start, stdout);
+    if (format->header)
+	format->header(ctx, message);
+    fputs (format->header_end, stdout);
 
-    printf ("\fbody}\n");
+    fputs (format->body_start, stdout);
+    if (format->part)
+	show_message_body (notmuch_message_get_filename (message), format->part);
+    fputs (format->body_end, stdout);
 
-    printf ("\fmessage}\n");
+    fputs (format->message_end, stdout);
 }
 
 
 static void
-show_messages (void *ctx, notmuch_messages_t *messages, int indent,
+show_messages (void *ctx, const show_format_t *format, notmuch_messages_t *messages, int indent,
 	       notmuch_bool_t entire_thread)
 {
     notmuch_message_t *message;
     notmuch_bool_t match;
+    int first_set = 1;
     int next_indent;
 
+    fputs (format->message_set_start, stdout);
+
     for (;
 	 notmuch_messages_has_more (messages);
 	 notmuch_messages_advance (messages))
     {
+	if (!first_set)
+	    fputs (format->message_set_sep, stdout);
+	first_set = 0;
+
+	fputs (format->message_set_start, stdout);
+
 	message = notmuch_messages_get (messages);
 
 	match = notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH);
@@ -202,15 +371,21 @@ show_messages (void *ctx, notmuch_messages_t *messages, int indent,
 	next_indent = indent;
 
 	if (match || entire_thread) {
-	    show_message (ctx, message, indent);
+	    show_message (ctx, format, message, indent);
 	    next_indent = indent + 1;
+
+	    fputs (format->message_set_sep, stdout);
 	}
 
-	show_messages (ctx, notmuch_message_get_replies (message),
+	show_messages (ctx, format, notmuch_message_get_replies (message),
 		       next_indent, entire_thread);
 
 	notmuch_message_destroy (message);
+
+	fputs (format->message_set_end, stdout);
     }
+
+    fputs (format->message_set_end, stdout);
 }
 
 int
@@ -223,15 +398,29 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
     notmuch_thread_t *thread;
     notmuch_messages_t *messages;
     char *query_string;
+    char *opt;
+    const show_format_t *format = &format_text;
     int entire_thread = 0;
     int i;
+    int first_toplevel = 1;
 
     for (i = 0; i < argc && argv[i][0] == '-'; i++) {
 	if (strcmp (argv[i], "--") == 0) {
 	    i++;
 	    break;
 	}
-        if (strcmp(argv[i], "--entire-thread") == 0) {
+	if (STRNCMP_LITERAL (argv[i], "--format=") == 0) {
+	    opt = argv[i] + sizeof ("--format=") - 1;
+	    if (strcmp (opt, "text") == 0) {
+		format = &format_text;
+	    } else if (strcmp (opt, "json") == 0) {
+		format = &format_json;
+		entire_thread = 1;
+	    } else {
+		fprintf (stderr, "Invalid value for --format: %s\n", opt);
+		return 1;
+	    }
+	} else if (STRNCMP_LITERAL (argv[i], "--entire-thread") == 0) {
 	    entire_thread = 1;
 	} else {
 	    fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
@@ -268,6 +457,8 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
 	return 1;
     }
 
+    fputs (format->message_set_start, stdout);
+
     for (threads = notmuch_query_search_threads (query);
 	 notmuch_threads_has_more (threads);
 	 notmuch_threads_advance (threads))
@@ -280,11 +471,18 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
 	    INTERNAL_ERROR ("Thread %s has no toplevel messages.\n",
 			    notmuch_thread_get_thread_id (thread));
 
-	show_messages (ctx, messages, 0, entire_thread);
+	if (!first_toplevel)
+	    fputs (format->message_set_sep, stdout);
+	first_toplevel = 0;
+
+	show_messages (ctx, format, messages, 0, entire_thread);
 
 	notmuch_thread_destroy (thread);
+
     }
 
+    fputs (format->message_set_end, stdout);
+
     notmuch_query_destroy (query);
     notmuch_database_close (notmuch);
 
diff --git a/notmuch.1 b/notmuch.1
index 369ecba..e3d5acb 100644
--- a/notmuch.1
+++ b/notmuch.1
@@ -154,6 +154,12 @@ Supported options for
 include
 .RS 4
 .TP 4
+.BR \-\-format= ( json | text )
+
+Presents the results in either JSON or plain-text (default).
+.RE
+.RS 4
+.TP 4
 .BR \-\-sort= ( newest\-first | oldest\-first )
 
 This option can be used to present results in either chronological order
@@ -202,7 +208,14 @@ matched message will be displayed.
 .RE
 
 .RS 4
-The  output format  is plain-text,  with all  text-content  MIME parts
+.TP 4
+.B \-\-format=(json|text)
+
+.RS 4
+.TP 4
+.B text
+
+The default plain-text format  has  text-content  MIME parts
 decoded. Various components in the output,
 .RB ( message ", " header ", " body ", " attachment ", and MIME " part ),
 will be delimited by easily-parsed markers. Each marker consists of a
@@ -210,6 +223,18 @@ Control-L character (ASCII decimal 12), the name of the marker, and
 then either an opening or closing brace, ('{' or '}'), to either open
 or close the component.
 
+.RE
+.RS 4
+.TP 4
+.B json
+
+Format output as Javascript Object Notation (JSON). JSON output always
+includes all messages in a matching thread; in effect
+.B \-\-format=json
+implies
+.B \-\-entire\-thread
+
+.RE
 A common use of
 .B notmuch show
 is to display a single thread of email messages. For this, use a
diff --git a/notmuch.c b/notmuch.c
index 2ac8a59..eaa6308 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -162,6 +162,11 @@ command_t commands[] = {
       "\n"
       "\t\tSupported options for search include:\n"
       "\n"
+      "\t\t--format=(json|text)\n"
+      "\n"
+      "\t\t\tPresents the results in either JSON or\n"
+      "\t\t\tplain-text (default)\n"
+      "\n"
       "\t\t--sort=(newest-first|oldest-first)\n"
       "\n"
       "\t\t\tPresent results in either chronological order\n"
@@ -186,13 +191,23 @@ command_t commands[] = {
       "\t\t\tall messages in the same thread as any matched\n"
       "\t\t\tmessage will be displayed.\n"
       "\n"
-      "\t\tThe output format is plain-text, with all text-content\n"
-      "\t\tMIME parts decoded. Various components in the output,\n"
-      "\t\t('message', 'header', 'body', 'attachment', and MIME 'part')\n"
-      "\t\tare delimited by easily-parsed markers. Each marker consists\n"
-      "\t\tof a Control-L character (ASCII decimal 12), the name of\n"
-      "\t\tthe marker, and then either an opening or closing brace,\n"
-      "\t\t'{' or '}' to either open or close the component.\n"
+      "\t\t--format=(json|text)\n"
+      "\n"
+      "\t\t\ttext\t(default)\n"
+      "\n"
+      "\t\t\tThe plain-text has all text-content MIME parts decoded.\n"
+      "\t\t\tVarious components in the output, ('message', 'header',\n"
+      "\t\t\t'body', 'attachment', and MIME 'part') are delimited by\n"
+      "\t\t\teasily-parsed markers. Each marker consists of a Control-L\n"
+      "\t\t\tcharacter (ASCII decimal 12), the name of the marker, and\n"
+      "\t\t\tthen either an opening or closing brace, '{' or '}' to\n"
+      "\t\t\teither open or close the component.\n"
+      "\n"
+      "\t\t\tjson\n"
+      "\n"
+      "\t\t\tFormat output as Javascript Object Notation (JSON).\n"
+      "\t\t\tJSON output always includes all messages in a matching,\n"
+      "\t\t\tthread i.e. '--output=json' implies '--entire-thread'\n"
       "\n"
       "\t\tA common use of \"notmuch show\" is to display a single\n"
       "\t\tthread of email messages. For this, use a search term of\n"
diff --git a/show-message.c b/show-message.c
index 784981b..05ced9c 100644
--- a/show-message.c
+++ b/show-message.c
@@ -26,8 +26,6 @@ static void
 show_message_part (GMimeObject *part, int *part_count,
 		   void (*show_part) (GMimeObject *part, int *part_count))
 {
-    *part_count = *part_count + 1;
-
     if (GMIME_IS_MULTIPART (part)) {
 	GMimeMultipart *multipart = GMIME_MULTIPART (part);
 	int i;
@@ -56,6 +54,8 @@ show_message_part (GMimeObject *part, int *part_count,
 	return;
     }
 
+    *part_count = *part_count + 1;
+
     (*show_part) (part, part_count);
 }
 
-- 
1.6.5

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show.
  2009-12-31 15:17 ` [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show david
@ 2009-12-31 20:27   ` Scott Robinson
  2010-02-10 13:51   ` David Bremner
  2010-02-23 20:56   ` Carl Worth
  2 siblings, 0 replies; 16+ messages in thread
From: Scott Robinson @ 2009-12-31 20:27 UTC (permalink / raw)
  To: david; +Cc: notmuch

Excerpts from david's message of Thu Dec 31 07:17:40 -0800 2009:
> I also changed the copyright of json.c to Scott. Hope that's OK with
> Scott.

That is totally OK.

Thank you for carrying this patch to conclusion!

Can't wait to see how notmuch has evolved in six or so months...

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show.
  2009-12-31 15:17 ` [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show david
  2009-12-31 20:27   ` Scott Robinson
@ 2010-02-10 13:51   ` David Bremner
  2010-02-10 17:54     ` Scott Robinson
  2010-02-23 20:56   ` Carl Worth
  2 siblings, 1 reply; 16+ messages in thread
From: David Bremner @ 2010-02-10 13:51 UTC (permalink / raw)
  To: notmuch, scott

On Thu, 31 Dec 2009 11:17:40 -0400, david@tethera.net wrote:
> From: Scott Robinson <scott@quadhome.com>
> 
> In the case of notmuch-show, "--format=json" also implies
> "--entire-thread" as the thread structure is implicit in the emitted
> document tree.
> 

Dear Scott and other interested parties;

As I play with json output a bit more, it seems like having
--entire-thread optional would be useful for quite a few applications.
For example, if I want to get the From address for a given message-ID, I
with the current setup I have to post process the output. This is
doable, but I wonder if the better option isn't to make --entire-thread
optional for --format=json as well. 

Any thoughts?

David

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show.
  2010-02-10 13:51   ` David Bremner
@ 2010-02-10 17:54     ` Scott Robinson
  0 siblings, 0 replies; 16+ messages in thread
From: Scott Robinson @ 2010-02-10 17:54 UTC (permalink / raw)
  To: notmuch

Excerpts from David Bremner's message of Wed Feb 10 07:51:47 -0600 2010:
> Dear Scott and other interested parties;
> 
> As I play with json output a bit more, it seems like having
> --entire-thread optional would be useful for quite a few applications.
> For example, if I want to get the From address for a given message-ID, I
> with the current setup I have to post process the output. This is
> doable, but I wonder if the better option isn't to make --entire-thread
> optional for --format=json as well. 
> 
> Any thoughts?

I made --entire-thread forced becuase it kept the JSON patch minimal. The way
the thread structure is calculated is closely tied into the message tree
recursion. It would have required a much more significant refactoring to get
both ways; and, I wanted the patch to have as few barriers to being merged.

However, as patch still hasn't been merged and probably needs to be rebased,
there doesn't seem to be any good reason anymore to avoid the work.

Except, I can't do it. As I noted in an earlier e-mail, I have to abandon this
piece of code. I'm a bit busy backpacking through Central America. :-)

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show.
  2009-12-31 15:17 ` [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show david
  2009-12-31 20:27   ` Scott Robinson
  2010-02-10 13:51   ` David Bremner
@ 2010-02-23 20:56   ` Carl Worth
  2010-03-01 14:58     ` [Sebastian Spaeth] " Sebastian Spaeth
  2 siblings, 1 reply; 16+ messages in thread
From: Carl Worth @ 2010-02-23 20:56 UTC (permalink / raw)
  To: david, notmuch

[-- Attachment #1: Type: text/plain, Size: 1127 bytes --]

On Thu, 31 Dec 2009 11:17:40 -0400, david@tethera.net wrote:
> This is an updated version of Scott Robinson's patch of
> id:1261114167-sup-8228@lisa. All of the hard work is Scott's. My
> changes to Scotts patch are 
>  - change option to --format from --output
>  - make a couple of calls to function points conditional on the 
>    function pointers being non-null
>  - updated the built-in documentation and the man page
> 
> I also changed the copyright of json.c to Scott. Hope that's OK with
> Scott.

Well that's embarrassing. I just went through the effort of reviewing
the old patch and basically re-constructing that same list of tasks,
(only to find out that this updated patch was already sitting in my
queue, but later).

Anyway, very well done! I've applied this now with one change:

I'm now printing "thread" instead of "id" as the JSON name for the
field with the thread id in it.

I also added a mention to the TODO file that we should fix --format=json
to not imply --entire-thread.

Thanks again, David and Scott (and we hope that all the backpacking goes
well, Scott!).

-Carl

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [Sebastian Spaeth] Re: [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show.
  2010-02-23 20:56   ` Carl Worth
@ 2010-03-01 14:58     ` Sebastian Spaeth
  2010-03-01 15:39       ` [PATCH] notmuch-show: add tags to json output Sebastian Spaeth
  2010-03-01 15:59       ` more json woes Sebastian Spaeth
  0 siblings, 2 replies; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-01 14:58 UTC (permalink / raw)
  To: Notmuch development list

On Tue, 23 Feb 2010 12:56:15 -0800, Carl Worth <cworth@cworth.org> wrote:
[JSON patch]
> Anyway, very well done! I've applied this now with one change:

I might be dumb. But does the json output really not provide the notmuch
tags of a message as the text output does, and is that intentional?

I was trying to make notmuchsync work with json, but tag syncing is hard
without getting access to the notmuch tags.

If that is an oversight, I will have a look at the tag output.

Sebastian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH] notmuch-show: add tags to json output
  2010-03-01 14:58     ` [Sebastian Spaeth] " Sebastian Spaeth
@ 2010-03-01 15:39       ` Sebastian Spaeth
  2010-03-01 17:26         ` David Bremner
  2010-03-01 15:59       ` more json woes Sebastian Spaeth
  1 sibling, 1 reply; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-01 15:39 UTC (permalink / raw)
  To: notmuch

The previous json patches forgot to add the notmuch tags to the json output. This is inconsistent to the text output so here they are. We just output a 'tags' field that sends a space delimited list of json-encoded notmuch tags.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
---
 notmuch-show.c |    5 +++--
 1 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 1a1d601..0f10797 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -138,10 +138,11 @@ format_message_json (const void *ctx, notmuch_message_t *message, unused (int in
 {
     void *ctx_quote = talloc_new (ctx);
 
-    printf ("\"id\": %s, \"match\": %s, \"filename\": %s",
+    printf ("\"id\": %s, \"match\": %s, \"filename\": %s, \"tags\": %s",
 	    json_quote_str (ctx_quote, notmuch_message_get_message_id (message)),
 	    notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH) ? "true" : "false",
-	    json_quote_str (ctx_quote, notmuch_message_get_filename (message)));
+	    json_quote_str (ctx_quote, notmuch_message_get_filename (message)),
+	    json_quote_str (ctx_quote, _get_tags_as_string (ctx_quote, message)));
 
     talloc_free (ctx_quote);
 }
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* more json woes
  2010-03-01 14:58     ` [Sebastian Spaeth] " Sebastian Spaeth
  2010-03-01 15:39       ` [PATCH] notmuch-show: add tags to json output Sebastian Spaeth
@ 2010-03-01 15:59       ` Sebastian Spaeth
  2010-03-01 16:12         ` [PATCH] notmuch-show: Do not output (null) when the message body is empty Sebastian Spaeth
  1 sibling, 1 reply; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-01 15:59 UTC (permalink / raw)
  To: Notmuch development list

More json woes... This mail makes json parser commit suicide:

 "body": [{"id": 1, "content-type": "text/html"}, {"id": 2,
 "content-type": "text/plain", "content": (null)}

and yes, it has empty mime parts.

Sebastian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH] notmuch-show: Do not output (null) when the message body is empty
  2010-03-01 15:59       ` more json woes Sebastian Spaeth
@ 2010-03-01 16:12         ` Sebastian Spaeth
  0 siblings, 0 replies; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-01 16:12 UTC (permalink / raw)
  To: notmuch

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
---
 notmuch-show.c |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 0f10797..4c794c4 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -310,10 +310,12 @@ format_part_json (GMimeObject *part, int *part_count)
     if (g_mime_content_type_is_type (content_type, "text", "*") &&
 	!g_mime_content_type_is_type (content_type, "text", "html"))
     {
-	show_part_content (part, stream_memory);
+        show_part_content (part, stream_memory);
 	part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
-
-	printf (", \"content\": %s", json_quote_str (ctx, (char *) part_content->data));
+	if (part_content->data != NULL)
+	  printf (", \"content\": %s", json_quote_str (ctx, (char *) part_content->data));
+	else
+	  printf (", \"content\": \"\"");
     }
 
     fputs ("}", stdout);
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH] notmuch-show: add tags to json output
  2010-03-01 15:39       ` [PATCH] notmuch-show: add tags to json output Sebastian Spaeth
@ 2010-03-01 17:26         ` David Bremner
  2010-03-01 17:35           ` Jameson Rollins
  0 siblings, 1 reply; 16+ messages in thread
From: David Bremner @ 2010-03-01 17:26 UTC (permalink / raw)
  To: Sebastian Spaeth, notmuch

On Mon,  1 Mar 2010 16:39:06 +0100, Sebastian Spaeth <Sebastian@SSpaeth.de> wrote:
> The previous json patches forgot to add the notmuch tags to the json
> output. This is inconsistent to the text output so here they are. We
> just output a 'tags' field that sends a space delimited list of
> json-encoded notmuch tags.
> 
> Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>

I agree we need tags in json output, but I think that making the client
parse the string of (space delimited?) tags defeats a bit the purpose of
having json output.  What do you think about 

       "tags": [ "inbox", "unread" ] 

instead of 

        "tags": "inbox unread"

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [PATCH] notmuch-show: add tags to json output
  2010-03-01 17:26         ` David Bremner
@ 2010-03-01 17:35           ` Jameson Rollins
  2010-03-02 10:52             ` [PATCH 1/2] notmuch-show: Do not output (null) when the message body is empty Sebastian Spaeth
  0 siblings, 1 reply; 16+ messages in thread
From: Jameson Rollins @ 2010-03-01 17:35 UTC (permalink / raw)
  To: David Bremner, Sebastian Spaeth, notmuch

[-- Attachment #1: Type: text/plain, Size: 890 bytes --]

On Mon, 01 Mar 2010 13:26:52 -0400, David Bremner <bremner@unb.ca> wrote:
> On Mon,  1 Mar 2010 16:39:06 +0100, Sebastian Spaeth <Sebastian@SSpaeth.de> wrote:
> > The previous json patches forgot to add the notmuch tags to the json
> > output. This is inconsistent to the text output so here they are. We
> > just output a 'tags' field that sends a space delimited list of
> > json-encoded notmuch tags.
> > 
> > Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
> 
> I agree we need tags in json output, but I think that making the client
> parse the string of (space delimited?) tags defeats a bit the purpose of
> having json output.  What do you think about 
> 
>        "tags": [ "inbox", "unread" ] 
> 
> instead of 
> 
>         "tags": "inbox unread"

I agree that having the JSON "tags" output field be an array of strings
makes the most sense.

jamie.

[-- Attachment #2: Type: application/pgp-signature, Size: 835 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH 1/2] notmuch-show: Do not output (null) when the message body is empty
  2010-03-01 17:35           ` Jameson Rollins
@ 2010-03-02 10:52             ` Sebastian Spaeth
  2010-03-02 10:52               ` [PATCH 2/2] notmuch-show: add tags to json output Sebastian Spaeth
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-02 10:52 UTC (permalink / raw)
  To: notmuch

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
---
I actually agree that we should output tags as array, it was just that the get_tags_as_string function was already existing, so that was more convenient. Here comes the second iteration that outputs the tags as json array.

 notmuch-show.c |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index 1a1d601..f21914f 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -311,8 +311,10 @@ format_part_json (GMimeObject *part, int *part_count)
     {
 	show_part_content (part, stream_memory);
 	part_content = g_mime_stream_mem_get_byte_array (GMIME_STREAM_MEM (stream_memory));
-
-	printf (", \"content\": %s", json_quote_str (ctx, (char *) part_content->data));
+	if (part_content->data != NULL)
+	  printf (", \"content\": %s", json_quote_str (ctx, (char *) part_content->data));
+	else
+	  printf (", \"content\": \"\"");
     }
 
     fputs ("}", stdout);
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [PATCH 2/2] notmuch-show: add tags to json output
  2010-03-02 10:52             ` [PATCH 1/2] notmuch-show: Do not output (null) when the message body is empty Sebastian Spaeth
@ 2010-03-02 10:52               ` Sebastian Spaeth
  2010-03-10  9:41                 ` Sebastian Spaeth
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-02 10:52 UTC (permalink / raw)
  To: notmuch

The previous json patches forgot to add the notmuch tags to the json output. This is inconsistent to the text output so here they are. We just output a 'tags' field that sends a space delimited list of json-encoded notmuch tags.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
---
 notmuch-show.c |   13 ++++++++++++-
 1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index f21914f..d3a4e9f 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -136,13 +136,24 @@ format_message_text (unused (const void *ctx), notmuch_message_t *message, int i
 static void
 format_message_json (const void *ctx, notmuch_message_t *message, unused (int indent))
 {
+    notmuch_tags_t *tags;
+    int first = 1;
     void *ctx_quote = talloc_new (ctx);
 
-    printf ("\"id\": %s, \"match\": %s, \"filename\": %s",
+    printf ("\"id\": %s, \"match\": %s, \"filename\": %s, \"tags\": [",
 	    json_quote_str (ctx_quote, notmuch_message_get_message_id (message)),
 	    notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH) ? "true" : "false",
 	    json_quote_str (ctx_quote, notmuch_message_get_filename (message)));
 
+    for (tags = notmuch_message_get_tags (message);
+	 notmuch_tags_has_more (tags);
+	 notmuch_tags_advance (tags))
+    {
+         printf("%s%s", first ? "" : ",",
+	        json_quote_str (ctx_quote, notmuch_tags_get (tags)));
+         first = 0;
+    }
+    printf("]");
     talloc_free (ctx_quote);
 }
 
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH 2/2] notmuch-show: add tags to json output
  2010-03-02 10:52               ` [PATCH 2/2] notmuch-show: add tags to json output Sebastian Spaeth
@ 2010-03-10  9:41                 ` Sebastian Spaeth
  2010-03-11 12:11                   ` [PATCH] " Sebastian Spaeth
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-10  9:41 UTC (permalink / raw)
  To: notmuch

On Tue,  2 Mar 2010 11:52:33 +0100, Sebastian Spaeth <Sebastian@SSpaeth.de> wrote:

I just updated this patch to reflect the new
notmuch_tags_valid/notmuch_tags_to_next(?) syntax.

It is commit e7cfd46505e8b1c4124a8fa88ea0fbee286d9360 on 
git://github.com/spaetz/notmuch-all-feature.git

(http://github.com/spaetz/notmuch-all-feature/commit/e7cfd46505e8b1c4124a8fa88ea0fbee286d9360)

Sebastian

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [PATCH] notmuch-show: add tags to json output
  2010-03-10  9:41                 ` Sebastian Spaeth
@ 2010-03-11 12:11                   ` Sebastian Spaeth
  2010-03-11 12:34                     ` Carl Worth
  0 siblings, 1 reply; 16+ messages in thread
From: Sebastian Spaeth @ 2010-03-11 12:11 UTC (permalink / raw)
  To: notmuch

The previous json patches forgot to add the notmuch tags to the json output. This is inconsistent to the text output so here they are. We just output a 'tags' field that sends notmuch tags as a json array.

Signed-off-by: Sebastian Spaeth <Sebastian@SSpaeth.de>
---
 notmuch-show.c |   13 ++++++++++++-
 1 files changed, 12 insertions(+), 1 deletions(-)

diff --git a/notmuch-show.c b/notmuch-show.c
index f21914f..bf9e022 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -136,13 +136,24 @@ format_message_text (unused (const void *ctx), notmuch_message_t *message, int i
 static void
 format_message_json (const void *ctx, notmuch_message_t *message, unused (int indent))
 {
+    notmuch_tags_t *tags;
+    int first = 1;
     void *ctx_quote = talloc_new (ctx);
 
-    printf ("\"id\": %s, \"match\": %s, \"filename\": %s",
+    printf ("\"id\": %s, \"match\": %s, \"filename\": %s, \"tags\": [",
 	    json_quote_str (ctx_quote, notmuch_message_get_message_id (message)),
 	    notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH) ? "true" : "false",
 	    json_quote_str (ctx_quote, notmuch_message_get_filename (message)));
 
+    for (tags = notmuch_message_get_tags (message);
+	 notmuch_tags_valid (tags);
+	 notmuch_tags_move_to_next (tags))
+    {
+         printf("%s%s", first ? "" : ",",
+               json_quote_str (ctx_quote, notmuch_tags_get (tags)));
+         first = 0;
+    }
+    printf("]");
     talloc_free (ctx_quote);
 }
 
-- 
1.6.3.3

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [PATCH] notmuch-show: add tags to json output
  2010-03-11 12:11                   ` [PATCH] " Sebastian Spaeth
@ 2010-03-11 12:34                     ` Carl Worth
  0 siblings, 0 replies; 16+ messages in thread
From: Carl Worth @ 2010-03-11 12:34 UTC (permalink / raw)
  To: Sebastian Spaeth, notmuch

[-- Attachment #1: Type: text/plain, Size: 376 bytes --]

On Thu, 11 Mar 2010 13:11:43 +0100, Sebastian Spaeth <Sebastian@SSpaeth.de> wrote:
> The previous json patches forgot to add the notmuch tags to the json output. This is inconsistent to the text output so here they are. We just output a 'tags' field that sends notmuch tags as a json array.

Thanks. I pushed this, (after wrapping the above commit message just a
bit).

-Carl

[-- Attachment #2: Type: application/pgp-signature, Size: 189 bytes --]

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2010-03-11 12:34 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <id:1262249616-sup-3911@elise>
2009-12-31 15:17 ` [PATCH] Add an "--format=(json|text)" command-line option to both notmuch-search and notmuch-show david
2009-12-31 20:27   ` Scott Robinson
2010-02-10 13:51   ` David Bremner
2010-02-10 17:54     ` Scott Robinson
2010-02-23 20:56   ` Carl Worth
2010-03-01 14:58     ` [Sebastian Spaeth] " Sebastian Spaeth
2010-03-01 15:39       ` [PATCH] notmuch-show: add tags to json output Sebastian Spaeth
2010-03-01 17:26         ` David Bremner
2010-03-01 17:35           ` Jameson Rollins
2010-03-02 10:52             ` [PATCH 1/2] notmuch-show: Do not output (null) when the message body is empty Sebastian Spaeth
2010-03-02 10:52               ` [PATCH 2/2] notmuch-show: add tags to json output Sebastian Spaeth
2010-03-10  9:41                 ` Sebastian Spaeth
2010-03-11 12:11                   ` [PATCH] " Sebastian Spaeth
2010-03-11 12:34                     ` Carl Worth
2010-03-01 15:59       ` more json woes Sebastian Spaeth
2010-03-01 16:12         ` [PATCH] notmuch-show: Do not output (null) when the message body is empty Sebastian Spaeth

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).