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