From: craven Date: Thu, 12 Jul 2012 07:43:23 +0000 (+0200) Subject: [PATCH v4 2/3] Add structured output formatter for JSON. X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=44487ca3d376e6e607e06ae11133c61cd77001ea;p=notmuch-archives.git [PATCH v4 2/3] Add structured output formatter for JSON. --- diff --git a/ee/2db05adf2b50449bb79e816e0074f5671bc2f4 b/ee/2db05adf2b50449bb79e816e0074f5671bc2f4 new file mode 100644 index 000000000..61270b188 --- /dev/null +++ b/ee/2db05adf2b50449bb79e816e0074f5671bc2f4 @@ -0,0 +1,258 @@ +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 53310431FAE + for ; Thu, 12 Jul 2012 00:42:18 -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 SFck2vzs2WeI for ; + Thu, 12 Jul 2012 00:42:15 -0700 (PDT) +Received: from mailout-de.gmx.net (mailout-de.gmx.net [213.165.64.22]) + by olra.theworths.org (Postfix) with SMTP id 79522431FC9 + for ; Thu, 12 Jul 2012 00:42:11 -0700 (PDT) +Received: (qmail invoked by alias); 12 Jul 2012 07:42:02 -0000 +Received: from gw.arelion.cust.net.lagis.at (EHLO dodekanex.arelion.at) + [83.164.197.182] + by mail.gmx.net (mp028) with SMTP; 12 Jul 2012 09:42:02 +0200 +X-Authenticated: #201305 +X-Provags-ID: V01U2FsdGVkX19uyYcosdlugZbxWUIO4vqOl3GmgQ8WephdZfN7M7 + 4ENuSjvnTyIjUi +Received: by dodekanex.arelion.at (Postfix, from userid 1000) + id 577933011C1; Thu, 12 Jul 2012 09:43:31 +0200 (CEST) +From: +To: notmuch@notmuchmail.org +Subject: [PATCH v4 2/3] Add structured output formatter for JSON. +Date: Thu, 12 Jul 2012 09:43:23 +0200 +Message-Id: <1342079004-5300-3-git-send-email-craven@gmx.net> +X-Mailer: git-send-email 1.7.11.1 +In-Reply-To: <1342079004-5300-1-git-send-email-craven@gmx.net> +References: <87d34hsdx8.fsf@awakening.csail.mit.edu> + <1342079004-5300-1-git-send-email-craven@gmx.net> +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: Thu, 12 Jul 2012 07:42:18 -0000 + +Using the new structured printer support in sprinter.h, implement +sprinter_json_new, which returns a new JSON structured output +formatter. + +The formatter prints output similar to the existing JSON, but with +differences in whitespace (mostly newlines). +--- + Makefile.local | 1 + + sprinter.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 173 insertions(+) + create mode 100644 sprinter.c + +diff --git a/Makefile.local b/Makefile.local +index a890df2..8baf0c2 100644 +--- a/Makefile.local ++++ b/Makefile.local +@@ -290,6 +290,7 @@ notmuch_client_srcs = \ + notmuch-show.c \ + notmuch-tag.c \ + notmuch-time.c \ ++ sprinter.c \ + query-string.c \ + mime-node.c \ + crypto.c \ +diff --git a/sprinter.c b/sprinter.c +new file mode 100644 +index 0000000..649f79a +--- /dev/null ++++ b/sprinter.c +@@ -0,0 +1,172 @@ ++#include ++#include ++#include ++#include "sprinter.h" ++ ++#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0])) ++ ++struct sprinter * ++sprinter_text = NULL; ++ ++/* ++ * Every below here is the implementation of the JSON printer. ++ */ ++ ++struct sprinter_json ++{ ++ struct sprinter vtable; ++ FILE *stream; ++ /* Top of the state stack, or NULL if the printer is not currently ++ * inside any aggregate types. */ ++ struct json_state *state; ++}; ++ ++struct json_state ++{ ++ struct json_state *parent; ++ /* True if nothing has been printed in this aggregate yet. ++ * Suppresses the comma before a value. */ ++ notmuch_bool_t first; ++ /* The character that closes the current aggregate. */ ++ char close; ++}; ++ ++/* Helper function to set up the stream to print a value. If this ++ * value follows another value, prints a comma. */ ++static struct sprinter_json * ++json_begin_value(struct sprinter *sp) ++{ ++ struct sprinter_json *spj = (struct sprinter_json*)sp; ++ if (spj->state) { ++ if (!spj->state->first) ++ fputs (", ", spj->stream); ++ else ++ spj->state->first = false; ++ } ++ return spj; ++} ++ ++/* Helper function to begin an aggregate type. Prints the open ++ * character and pushes a new state frame. */ ++static void ++json_begin_aggregate(struct sprinter *sp, char open, char close) ++{ ++ struct sprinter_json *spj = json_begin_value (sp); ++ struct json_state *state = talloc (spj, struct json_state); ++ ++ fputc (open, spj->stream); ++ state->parent = spj->state; ++ state->first = true; ++ state->close = close; ++ spj->state = state; ++} ++ ++static void ++json_begin_map(struct sprinter *sp) ++{ ++ json_begin_aggregate (sp, '{', '}'); ++} ++ ++static void ++json_begin_list(struct sprinter *sp) ++{ ++ json_begin_aggregate (sp, '[', ']'); ++} ++ ++static void ++json_end(struct sprinter *sp) ++{ ++ struct sprinter_json *spj = (struct sprinter_json*)sp; ++ struct json_state *state = spj->state; ++ ++ fputc (spj->state->close, spj->stream); ++ spj->state = state->parent; ++ talloc_free (state); ++ if(spj->state == NULL) ++ fputc ('\n', spj->stream); ++} ++ ++static void ++json_string(struct sprinter *sp, const char *val) ++{ ++ static const char * const escapes[] = { ++ ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b", ++ ['\f'] = "\\f", ['\n'] = "\\n", ['\t'] = "\\t" ++ }; ++ struct sprinter_json *spj = json_begin_value (sp); ++ fputc ('"', spj->stream); ++ for (; *val; ++val) { ++ unsigned char ch = *val; ++ if (ch < ARRAY_SIZE(escapes) && escapes[ch]) ++ fputs (escapes[ch], spj->stream); ++ else if (ch >= 32) ++ fputc (ch, spj->stream); ++ else ++ fprintf (spj->stream, "\\u%04x", ch); ++ } ++ fputc ('"', spj->stream); ++} ++ ++static void ++json_integer(struct sprinter *sp, int val) ++{ ++ struct sprinter_json *spj = json_begin_value (sp); ++ fprintf (spj->stream, "%d", val); ++} ++ ++static void ++json_boolean(struct sprinter *sp, notmuch_bool_t val) ++{ ++ struct sprinter_json *spj = json_begin_value (sp); ++ fputs (val ? "true" : "false", spj->stream); ++} ++ ++static void ++json_null(struct sprinter *sp) ++{ ++ struct sprinter_json *spj = json_begin_value (sp); ++ fputs ("null", spj->stream); ++} ++ ++static void ++json_map_key(struct sprinter *sp, const char *key) ++{ ++ struct sprinter_json *spj = (struct sprinter_json*)sp; ++ json_string (sp, key); ++ fputs (": ", spj->stream); ++ spj->state->first = true; ++} ++ ++static void ++json_frame(struct sprinter *sp) ++{ ++ struct sprinter_json *spj = (struct sprinter_json*)sp; ++ fputc ('\n', spj->stream); ++} ++ ++struct sprinter * ++sprinter_json_new(const void *ctx, FILE *stream) ++{ ++ static const struct sprinter_json template = { ++ .vtable = { ++ .begin_map = json_begin_map, ++ .begin_list = json_begin_list, ++ .end = json_end, ++ .string = json_string, ++ .integer = json_integer, ++ .boolean = json_boolean, ++ .null = json_null, ++ .map_key = json_map_key, ++ .frame = json_frame, ++ } ++ }; ++ struct sprinter_json *res; ++ ++ res = talloc (ctx, struct sprinter_json); ++ if (!res) ++ return NULL; ++ ++ *res = template; ++ res->stream = stream; ++ return &res->vtable; ++} +-- +1.7.11.1 +