]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch-show.c
test: wrap 'wc -l' results in arithmetic evaluation to strip whitespace
[notmuch] / notmuch-show.c
index afbd9d06848beb044fb5632725cd100e75439e47..2fa229257ea99645d778a134c35861c3398665e5 100644 (file)
@@ -37,11 +37,7 @@ format_part_json_entry (const void *ctx, sprinter_t *sp, mime_node_t *node,
 
 static const notmuch_show_format_t format_json = {
     .new_sprinter = sprinter_json_create,
-    .message_set_start = "[",
     .part = format_part_json_entry,
-    .message_set_sep = ", ",
-    .message_set_end = "]",
-    .null_message = "null"
 };
 
 static notmuch_status_t
@@ -109,35 +105,48 @@ _get_one_line_summary (const void *ctx, notmuch_message_t *message)
                            from, relative_date, tags);
 }
 
+/* Emit a sequence of key/value pairs for the metadata of message.
+ * The caller should begin a map before calling this. */
 static void
-format_message_json (const void *ctx, notmuch_message_t *message)
+format_message_json (sprinter_t *sp, notmuch_message_t *message)
 {
+    /* Any changes to the JSON format should be reflected in the file
+     * devel/schemata. */
+
+    void *local = talloc_new (NULL);
     notmuch_tags_t *tags;
-    int first = 1;
-    void *ctx_quote = talloc_new (ctx);
     time_t date;
     const char *relative_date;
 
+    sp->map_key (sp, "id");
+    sp->string (sp, notmuch_message_get_message_id (message));
+
+    sp->map_key (sp, "match");
+    sp->boolean (sp, notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH));
+
+    sp->map_key (sp, "excluded");
+    sp->boolean (sp, notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_EXCLUDED));
+
+    sp->map_key (sp, "filename");
+    sp->string (sp, notmuch_message_get_filename (message));
+
+    sp->map_key (sp, "timestamp");
     date = notmuch_message_get_date (message);
-    relative_date = notmuch_time_relative_date (ctx, date);
+    sp->integer (sp, date);
 
-    printf ("\"id\": %s, \"match\": %s, \"excluded\": %s, \"filename\": %s, \"timestamp\": %ld, \"date_relative\": \"%s\", \"tags\": [",
-           json_quote_str (ctx_quote, notmuch_message_get_message_id (message)),
-           notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH) ? "true" : "false",
-           notmuch_message_get_flag (message, NOTMUCH_MESSAGE_FLAG_EXCLUDED) ? "true" : "false",
-           json_quote_str (ctx_quote, notmuch_message_get_filename (message)),
-           date, relative_date);
+    sp->map_key (sp, "date_relative");
+    relative_date = notmuch_time_relative_date (local, date);
+    sp->string (sp, relative_date);
 
+    sp->map_key (sp, "tags");
+    sp->begin_list (sp);
     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);
+       sp->string (sp, notmuch_tags_get (tags));
+    sp->end (sp);
+
+    talloc_free (local);
 }
 
 /* Extract just the email address from the contents of a From:
@@ -202,8 +211,12 @@ void
 format_headers_json (sprinter_t *sp, GMimeMessage *message,
                     notmuch_bool_t reply)
 {
+    /* Any changes to the JSON format should be reflected in the file
+     * devel/schemata. */
+
     InternetAddressList *recipients;
     const char *recipients_string;
+    const char *reply_to_string;
 
     sp->begin_map (sp);
 
@@ -227,6 +240,19 @@ format_headers_json (sprinter_t *sp, GMimeMessage *message,
        sp->string (sp, recipients_string);
     }
 
+    recipients = g_mime_message_get_recipients (message, GMIME_RECIPIENT_TYPE_BCC);
+    recipients_string = internet_address_list_to_string (recipients, 0);
+    if (recipients_string) {
+       sp->map_key (sp, "Bcc");
+       sp->string (sp, recipients_string);
+    }
+
+    reply_to_string = g_mime_message_get_reply_to (message);
+    if (reply_to_string) {
+       sp->map_key (sp, "Reply-To");
+       sp->string (sp, reply_to_string);
+    }
+
     if (reply) {
        sp->map_key (sp, "In-reply-to");
        sp->string (sp, g_mime_object_get_header (GMIME_OBJECT (message), "In-reply-to"));
@@ -339,6 +365,9 @@ signer_status_to_string (GMimeSignerStatus x)
 static void
 format_part_sigstatus_json (sprinter_t *sp, mime_node_t *node)
 {
+    /* Any changes to the JSON format should be reflected in the file
+     * devel/schemata. */
+
     GMimeSignatureList *siglist = node->sig_list;
 
     sp->begin_list (sp);
@@ -573,18 +602,19 @@ format_part_json (const void *ctx, sprinter_t *sp, mime_node_t *node,
      * devel/schemata. */
 
     if (node->envelope_file) {
-       printf ("{");
-       format_message_json (ctx, node->envelope_file);
+       sp->begin_map (sp);
+       format_message_json (sp, node->envelope_file);
 
-       printf ("\"headers\": ");
+       sp->map_key (sp, "headers");
        format_headers_json (sp, GMIME_MESSAGE (node->part), FALSE);
 
        if (output_body) {
-           printf (", \"body\": [");
+           sp->map_key (sp, "body");
+           sp->begin_list (sp);
            format_part_json (ctx, sp, mime_node_child (node, 0), first, TRUE);
-           printf ("]");
+           sp->end (sp);
        }
-       printf ("}");
+       sp->end (sp);
        return;
     }
 
@@ -830,15 +860,6 @@ format_part_raw (unused (const void *ctx), unused (sprinter_t *sp),
     return NOTMUCH_STATUS_SUCCESS;
 }
 
-static notmuch_status_t
-show_null_message (const notmuch_show_format_t *format)
-{
-    /* Output a null message. Currently empty for all formats except Json */
-    if (format->null_message)
-       printf ("%s", format->null_message);
-    return NOTMUCH_STATUS_SUCCESS;
-}
-
 static notmuch_status_t
 show_message (void *ctx,
              const notmuch_show_format_t *format,
@@ -873,23 +894,16 @@ show_messages (void *ctx,
     notmuch_message_t *message;
     notmuch_bool_t match;
     notmuch_bool_t excluded;
-    int first_set = 1;
     int next_indent;
     notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
 
-    if (format->message_set_start)
-       fputs (format->message_set_start, stdout);
+    sp->begin_list (sp);
 
     for (;
         notmuch_messages_valid (messages);
         notmuch_messages_move_to_next (messages))
     {
-       if (!first_set && format->message_set_sep)
-           fputs (format->message_set_sep, stdout);
-       first_set = 0;
-
-       if (format->message_set_start)
-           fputs (format->message_set_start, stdout);
+       sp->begin_list (sp);
 
        message = notmuch_messages_get (messages);
 
@@ -904,12 +918,9 @@ show_messages (void *ctx,
                res = status;
            next_indent = indent + 1;
        } else {
-           status = show_null_message (format);
+           sp->null (sp);
        }
 
-       if (!status && format->message_set_sep)
-           fputs (format->message_set_sep, stdout);
-
        status = show_messages (ctx,
                                format, sp,
                                notmuch_message_get_replies (message),
@@ -920,12 +931,10 @@ show_messages (void *ctx,
 
        notmuch_message_destroy (message);
 
-       if (format->message_set_end)
-           fputs (format->message_set_end, stdout);
+       sp->end (sp);
     }
 
-    if (format->message_set_end)
-       fputs (format->message_set_end, stdout);
+    sp->end (sp);
 
     return res;
 }
@@ -971,11 +980,9 @@ do_show (void *ctx,
     notmuch_threads_t *threads;
     notmuch_thread_t *thread;
     notmuch_messages_t *messages;
-    int first_toplevel = 1;
     notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
 
-    if (format->message_set_start)
-       fputs (format->message_set_start, stdout);
+    sp->begin_list (sp);
 
     for (threads = notmuch_query_search_threads (query);
         notmuch_threads_valid (threads);
@@ -989,10 +996,6 @@ do_show (void *ctx,
            INTERNAL_ERROR ("Thread %s has no toplevel messages.\n",
                            notmuch_thread_get_thread_id (thread));
 
-       if (!first_toplevel && format->message_set_sep)
-           fputs (format->message_set_sep, stdout);
-       first_toplevel = 0;
-
        status = show_messages (ctx, format, sp, messages, 0, params);
        if (status && !res)
            res = status;
@@ -1001,8 +1004,7 @@ do_show (void *ctx,
 
     }
 
-    if (format->message_set_end)
-       fputs (format->message_set_end, stdout);
+    sp->end (sp);
 
     return res != NOTMUCH_STATUS_SUCCESS;
 }