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