[PATCH v3 2/3] Adding a structured formatter for JSON.
authorcraven <craven@gmx.net>
Wed, 11 Jul 2012 08:26:34 +0000 (10:26 +0200)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:48:10 +0000 (09:48 -0800)
76/8066aab290337874b43535e7f376043821300a [new file with mode: 0644]

diff --git a/76/8066aab290337874b43535e7f376043821300a b/76/8066aab290337874b43535e7f376043821300a
new file mode 100644 (file)
index 0000000..e71527b
--- /dev/null
@@ -0,0 +1,322 @@
+Return-Path: <craven@gmx.net>\r
+X-Original-To: notmuch@notmuchmail.org\r
+Delivered-To: notmuch@notmuchmail.org\r
+Received: from localhost (localhost [127.0.0.1])\r
+       by olra.theworths.org (Postfix) with ESMTP id 26710429E3C\r
+       for <notmuch@notmuchmail.org>; Wed, 11 Jul 2012 01:25:26 -0700 (PDT)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: 0.001\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=0.001 tagged_above=-999 required=5\r
+       tests=[FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001]\r
+       autolearn=disabled\r
+Received: from olra.theworths.org ([127.0.0.1])\r
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)\r
+       with ESMTP id 9a9F7MtAQ7EV for <notmuch@notmuchmail.org>;\r
+       Wed, 11 Jul 2012 01:25:23 -0700 (PDT)\r
+Received: from mailout-de.gmx.net (mailout-de.gmx.net [213.165.64.22])\r
+       by olra.theworths.org (Postfix) with SMTP id DDF20429E26\r
+       for <notmuch@notmuchmail.org>; Wed, 11 Jul 2012 01:25:21 -0700 (PDT)\r
+Received: (qmail invoked by alias); 11 Jul 2012 08:25:14 -0000\r
+Received: from gw.arelion.cust.net.lagis.at (EHLO dodekanex.arelion.at)\r
+       [83.164.197.182]\r
+       by mail.gmx.net (mp016) with SMTP; 11 Jul 2012 10:25:14 +0200\r
+X-Authenticated: #201305\r
+X-Provags-ID: V01U2FsdGVkX1887tYf0eI8tllIWkVJ6Mvejk7/HvwFk7Ruqa/5x2\r
+       jalLZfI1VUHSon\r
+Received: by dodekanex.arelion.at (Postfix, from userid 1000)\r
+       id BD7F330313C; Wed, 11 Jul 2012 10:26:36 +0200 (CEST)\r
+From: <craven@gmx.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH v3 2/3] Adding a structured formatter for JSON.\r
+Date: Wed, 11 Jul 2012 10:26:34 +0200\r
+Message-Id: <1341995195-2497-3-git-send-email-craven@gmx.net>\r
+X-Mailer: git-send-email 1.7.11.1\r
+In-Reply-To: <1341995195-2497-1-git-send-email-craven@gmx.net>\r
+References: <20120710191331.GE7332@mit.edu>\r
+       <1341995195-2497-1-git-send-email-craven@gmx.net>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=UTF-8\r
+Content-Transfer-Encoding: 8bit\r
+X-Y-GMX-Trusted: 0\r
+X-BeenThere: notmuch@notmuchmail.org\r
+X-Mailman-Version: 2.1.13\r
+Precedence: list\r
+List-Id: "Use and development of the notmuch mail system."\r
+       <notmuch.notmuchmail.org>\r
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
+List-Post: <mailto:notmuch@notmuchmail.org>\r
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
+X-List-Received-Date: Wed, 11 Jul 2012 08:25:26 -0000\r
+\r
+This patch adds a structured formatter that prints exactly the same JSON as the built-in JSON printer. All tests pass without change.\r
+\r
+Notice that this formatter could be simpler by changing the whitespace\r
+and line-breaks in the generated JSON.\r
+---\r
+ Makefile.local      |   1 +\r
+ structured-output.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++\r
+ structured-output.h |  59 +++++++++++++++++++\r
+ 3 files changed, 227 insertions(+)\r
+ create mode 100644 structured-output.c\r
+\r
+diff --git a/Makefile.local b/Makefile.local\r
+index a890df2..9b989dc 100644\r
+--- a/Makefile.local\r
++++ b/Makefile.local\r
+@@ -291,6 +291,7 @@ notmuch_client_srcs =              \\r
+       notmuch-tag.c           \\r
+       notmuch-time.c          \\r
+       query-string.c          \\r
++      structured-output.c     \\r
+       mime-node.c             \\r
+       crypto.c                \\r
+       json.c\r
+diff --git a/structured-output.c b/structured-output.c\r
+new file mode 100644\r
+index 0000000..18a7306\r
+--- /dev/null\r
++++ b/structured-output.c\r
+@@ -0,0 +1,167 @@\r
++/* notmuch - Not much of an email program, (just index and search)\r
++ *\r
++ * Copyright © 2009 Carl Worth\r
++ *\r
++ * This program is free software: you can redistribute it and/or modify\r
++ * it under the terms of the GNU General Public License as published by\r
++ * the Free Software Foundation, either version 3 of the License, or\r
++ * (at your option) any later version.\r
++ *\r
++ * This program is distributed in the hope that it will be useful,\r
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
++ * GNU General Public License for more details.\r
++ *\r
++ * You should have received a copy of the GNU General Public License\r
++ * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
++ *\r
++ * Author: Carl Worth <cworth@cworth.org>\r
++ */\r
++\r
++#include "structured-output.h"\r
++#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))\r
++\r
++structure_printer_t *\r
++unstructured_text_printer = NULL;\r
++\r
++structure_printer_t\r
++json_structure_printer = {\r
++    &json_map,\r
++    &json_list,\r
++    &json_pop,\r
++    &json_map_key,\r
++    &json_number,\r
++    &json_string,\r
++    &json_bool,\r
++    &json_initial_state\r
++};\r
++\r
++static int\r
++enter_level (void *st, const char *marker, int type) {\r
++    json_state_t *state = (json_state_t*)st;\r
++    FILE *output = state->output;\r
++    json_list_t *el = talloc (st, json_list_t);\r
++\r
++    json_item_separator (state, (state->level == 1) ? "\n" : " ");\r
++    fputs (marker, output);\r
++\r
++    el->type = type;\r
++    el->first_seen = FALSE;\r
++    el->rest = state->stack;\r
++    state->stack = el;\r
++    return state->level++;\r
++}\r
++\r
++void\r
++json_print_escaped_string (FILE *output, const char *val)\r
++{\r
++    static const char * const escapes[] = {\r
++        ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",\r
++        ['\f'] = "\\f",  ['\n'] = "\\n",  ['\t'] = "\\t"\r
++    };\r
++    fputc ('"', output);\r
++    for (; *val; ++val) {\r
++        unsigned char ch = *val;\r
++        if (ch < ARRAY_SIZE(escapes) && escapes[ch])\r
++            fputs (escapes[ch], output);\r
++        else if (ch >= 32)\r
++            fputc (ch, output);\r
++        else\r
++            fprintf (output, "\\u%04x", ch);\r
++    }\r
++    fputc ('"', output);\r
++}\r
++\r
++int\r
++json_map (void *st)\r
++{\r
++    return enter_level (st, "{", TYPE_JSON_MAP);\r
++}\r
++\r
++int\r
++json_list (void *st)\r
++{\r
++    return enter_level (st, "[", TYPE_JSON_ARRAY);\r
++}\r
++\r
++void\r
++json_pop (void *st, int level)\r
++{\r
++    json_state_t *state = (json_state_t*)st;\r
++    FILE *output = state->output;\r
++    while (state->level > level) {\r
++      json_list_t *tos = state->stack;\r
++      fputs (tos->type == TYPE_JSON_MAP ? "}" : "]", output);\r
++      state->stack = tos->rest;\r
++      state->level--;\r
++      talloc_free (tos);\r
++    }\r
++    if (state->level == 0) {\r
++      fputs ("\n", output);\r
++    }\r
++}\r
++\r
++void\r
++json_map_key (void *st, const char *key)\r
++{\r
++    json_state_t *state = (json_state_t*)st;\r
++    FILE *output = state->output;\r
++    if (state->stack != NULL && state->stack->first_seen) {\r
++      fputs (",\n", output);\r
++    }\r
++    json_print_escaped_string (output, key);\r
++    fputs (": ", output);\r
++}\r
++\r
++void\r
++json_item_separator (json_state_t *state, const char *suffix)\r
++{\r
++    FILE *output = state->output;\r
++    if (state->stack != NULL\r
++      && state->stack->type == TYPE_JSON_ARRAY\r
++      && state->stack->first_seen) {\r
++\r
++      fputs (",", output);\r
++      fputs (suffix, output);\r
++    }\r
++    if (state->stack != NULL)\r
++      state->stack->first_seen = TRUE;\r
++}\r
++\r
++void\r
++json_number (void *st, int val)\r
++{\r
++    json_state_t *state = (json_state_t*)st;\r
++    FILE *output = state->output;\r
++    json_item_separator (state, state->level == 1 ? "\n" : " ");\r
++    fprintf (output, "%d", val);\r
++}\r
++\r
++void\r
++json_string (void *st, const char *val)\r
++{\r
++    json_state_t *state = (json_state_t *)st;\r
++    FILE *output = state->output;\r
++    json_item_separator (state, state->level == 1 ? "\n" : " ");\r
++    json_print_escaped_string (output, val);\r
++}\r
++\r
++void\r
++json_bool (void *st, notmuch_bool_t val)\r
++{\r
++    json_state_t *state = (json_state_t*)st;\r
++    FILE *output = state->output;\r
++    json_item_separator (state, state->level == 1 ? "\n" : " ");\r
++    fputs (val ? "true" : "false", output);\r
++}\r
++\r
++void *\r
++json_initial_state (const struct structure_printer *sp, FILE *output)\r
++{\r
++    (void)sp;\r
++    json_state_t *st = talloc (0, json_state_t);\r
++    st->level = 0;\r
++    st->stack = NULL;\r
++    st->output = output;\r
++    return st;\r
++}\r
+diff --git a/structured-output.h b/structured-output.h\r
+index 73029f1..b211ac6 100644\r
+--- a/structured-output.h\r
++++ b/structured-output.h\r
+@@ -88,3 +88,62 @@ typedef struct structure_printer {\r
+                           FILE *output);\r
+ \r
+ } structure_printer_t;\r
++\r
++/* dummy object to differentiate plain text from structured output */\r
++structure_printer_t *\r
++unstructured_text_printer;\r
++\r
++/* JSON structure printer\r
++ * An implementation of the JSON structure printer that produces\r
++ * exactly the same output as the previous JSON printer.\r
++ */\r
++\r
++/* single linked list implementation for keeping track of the array/map\r
++ * nesting state.\r
++ */\r
++typedef enum {TYPE_JSON_MAP, TYPE_JSON_ARRAY} JSON_TYPE;\r
++\r
++typedef struct json_list {\r
++    int type;\r
++    notmuch_bool_t first_seen;\r
++    struct json_list *rest;\r
++} json_list_t;\r
++\r
++typedef struct json_state {\r
++    FILE *output;\r
++    json_list_t *stack;\r
++    int level;\r
++} json_state_t;\r
++\r
++int\r
++json_map(void *state);\r
++\r
++int\r
++json_list(void *state);\r
++\r
++void\r
++json_pop(void *state, int level);\r
++\r
++void\r
++json_map_key(void *state, const char *key);\r
++\r
++void\r
++json_number(void *state, int val);\r
++\r
++void\r
++json_string(void *state, const char *val);\r
++\r
++void\r
++json_bool(void *state, notmuch_bool_t val);\r
++\r
++void *\r
++json_initial_state(const struct structure_printer *sp, FILE *output);\r
++\r
++void\r
++json_print_escaped_string(FILE *output, const char *val);\r
++\r
++void\r
++json_item_separator(json_state_t *state, const char *suffix);\r
++\r
++structure_printer_t\r
++json_structure_printer;\r
+-- \r
+1.7.11.1\r
+\r