--- /dev/null
+Return-Path: <peter.feigl@gmx.at>\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 69BE2431FC0\r
+ for <notmuch@notmuchmail.org>; Tue, 10 Jul 2012 11:56:49 -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 yMOhQ3kgmDbP for <notmuch@notmuchmail.org>;\r
+ Tue, 10 Jul 2012 11:56:48 -0700 (PDT)\r
+Received: from mailout-de.gmx.net (mailout-de.gmx.net [213.165.64.23])\r
+ by olra.theworths.org (Postfix) with SMTP id 61CE1431FBF\r
+ for <notmuch@notmuchmail.org>; Tue, 10 Jul 2012 11:56:48 -0700 (PDT)\r
+Received: (qmail invoked by alias); 10 Jul 2012 18:56:46 -0000\r
+Received: from www.nexoid.at (EHLO mail.nexoid.at) [178.79.130.240]\r
+ by mail.gmx.net (mp041) with SMTP; 10 Jul 2012 20:56:46 +0200\r
+X-Authenticated: #4563876\r
+X-Provags-ID: V01U2FsdGVkX18D5ZtgJdoDpVbribBLKxrNVd64ms3Eu0lWmXSckZ\r
+ MjfNwa30lEc1cA\r
+Received: from nexoid (localhost [127.0.0.1])\r
+ by mail.nexoid.at (Postfix) with ESMTP id C9C63E00C\r
+ for <notmuch@notmuchmail.org>; Tue, 10 Jul 2012 20:56:45 +0200 (CEST)\r
+From: <craven@gmx.net>\r
+To: notmuch@notmuchmail.org\r
+Subject: [PATCH v3 2/3] Adding structured output formatter for JSON.\r
+User-Agent: Notmuch/0.11+77~gad6d0d5 (http://notmuchmail.org) Emacs/24.1.50.2\r
+ (i686-pc-linux-gnu)\r
+Date: Tue, 10 Jul 2012 20:56:45 +0200\r
+Message-ID: <87zk77tpg2.fsf@nexoid.at>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=utf-8\r
+Content-Transfer-Encoding: quoted-printable\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: Tue, 10 Jul 2012 18:56:49 -0000\r
+\r
+This formatter prints exactly the same structure as the existing JSON forma=\r
+tter, but uses the newly introduced structured formatting primitives.\r
+---\r
+ structured-output.c | 159 +++++++++++++++++++++++++++++++++++++++++++++++=\r
+++++\r
+ structured-output.h | 48 ++++++++++++++++\r
+ 2 files changed, 207 insertions(+)\r
+ create mode 100644 structured-output.c\r
+\r
+diff --git a/structured-output.c b/structured-output.c\r
+new file mode 100644\r
+index 0000000..e10fba4\r
+--- /dev/null\r
++++ b/structured-output.c\r
+@@ -0,0 +1,159 @@\r
++/* notmuch - Not much of an email program, (just index and search)\r
++ *\r
++ * Copyright =C2=A9 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
++\r
++structure_printer_t *\r
++unstructured_text_printer =3D NULL;\r
++\r
++structure_printer_t=20\r
++json_structure_printer =3D {\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
++static int\r
++enter_level(void *st, const char *marker, int type) {\r
++ json_state_t *state =3D (json_state_t*)st;\r
++ FILE *output =3D state->output;\r
++ if (state->stack !=3D NULL && state->stack->type =3D=3D TYPE_JSON_ARRA=\r
+Y && state->stack->first_already_seen) {\r
++ fputs(",", output);\r
++ if (state->level =3D=3D 1)\r
++ fputs("\n", output);\r
++ else\r
++ fputs(" ", output);\r
++ }\r
++ if (state->stack !=3D NULL) {\r
++ state->stack->first_already_seen =3D TRUE;\r
++ }\r
++ fputs(marker, output);\r
++ void *ctx_json_map =3D talloc_new (0);\r
++ json_list_t *el =3D talloc(ctx_json_map, json_list_t);\r
++ el->type =3D type;\r
++ el->first_already_seen =3D FALSE;\r
++ el->rest =3D state->stack;\r
++ state->stack =3D el;\r
++ return state->level++;\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)=20\r
++{\r
++ int i;\r
++ json_state_t *state =3D (json_state_t*)st;\r
++ FILE *output =3D state->output;\r
++ for (i =3D state->level; i > level; i--) {\r
++ json_list_t *tos =3D state->stack;\r
++ if (tos->type =3D=3D TYPE_JSON_MAP) {\r
++ fputs("}", output);\r
++ }\r
++ if (tos->type =3D=3D TYPE_JSON_ARRAY) {\r
++ fputs("]", output);\r
++ }\r
++ state->stack =3D tos->rest;\r
++ state->level--;\r
++ talloc_free(tos);\r
++ }\r
++ if (state->level =3D=3D 0)\r
++ fputs("\n", output);\r
++}\r
++\r
++void\r
++json_map_key(void *st, const char *key)\r
++{\r
++ json_state_t *state =3D (json_state_t*)st;\r
++ FILE *output =3D state->output;\r
++ if (state->stack !=3D NULL && state->stack->first_already_seen) {\r
++ fputs(",\n", output);\r
++ }\r
++ fputs("\"", output);\r
++ fputs(key, output);\r
++ fputs("\": ", output);\r
++}\r
++\r
++void\r
++json_number(void *st, int val)\r
++{\r
++ json_state_t *state =3D (json_state_t*)st;\r
++ FILE *output =3D state->output;\r
++ if (state->stack !=3D NULL && state->stack->type =3D=3D TYPE_JSON_ARRA=\r
+Y && state->stack->first_already_seen) {\r
++ fputs(", ", output);\r
++ }\r
++ state->stack->first_already_seen =3D TRUE;\r
++ fprintf(output, "%i", val);\r
++}\r
++\r
++void\r
++json_string(void *st, const char *val)\r
++{\r
++ json_state_t *state =3D (json_state_t*)st;\r
++ FILE *output =3D state->output;\r
++ void *ctx =3D talloc_new(0);\r
++ if (state->stack !=3D NULL && state->stack->type =3D=3D TYPE_JSON_ARRA=\r
+Y && state->stack->first_already_seen) {\r
++ fputs(",", output);\r
++ if (state->level =3D=3D 1)\r
++ fputs("\n", output);\r
++ else\r
++ fputs(" ", output);\r
++ }\r
++\r
++ state->stack->first_already_seen =3D TRUE;\r
++ fprintf(output, "%s", json_quote_str(ctx, val));\r
++ talloc_free(ctx);\r
++}\r
++\r
++void\r
++json_bool(void *st, notmuch_bool_t val)\r
++{\r
++ json_state_t *state =3D (json_state_t*)st;\r
++ FILE *output =3D state->output;\r
++ if (val)\r
++ fputs("true", output);\r
++ else\r
++ fputs("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 =3D talloc(0, json_state_t);\r
++ st->level =3D 0;\r
++ st->stack =3D NULL;\r
++ st->output =3D output;\r
++ return st;\r
++}\r
+diff --git a/structured-output.h b/structured-output.h\r
+index b43afe0..cba3882 100644\r
+--- a/structured-output.h\r
++++ b/structured-output.h\r
+@@ -62,3 +62,51 @@ typedef struct structure_printer {\r
+ } structure_printer_t;\r
+=20\r
+=20\r
++/* JSON structure printer */\r
++\r
++/* single linked list implementation for keeping track of the array/map ne=\r
+sting state */\r
++\r
++typedef struct json_list {\r
++ int type;\r
++ int first_already_seen;\r
++ struct json_list *rest;\r
++} json_list_t;\r
++\r
++#define TYPE_JSON_MAP 1\r
++#define TYPE_JSON_ARRAY 2\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
++structure_printer_t\r
++json_structure_printer;\r
++\r
++structure_printer_t *\r
++unstructured_text_printer;\r
+--=20\r
+1.7.10.4\r
+\r