[PATCH v3 2/3] Adding a structured formatter for JSON.

craven at gmx.net craven at gmx.net
Wed Jul 11 01:26:34 PDT 2012


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 <cworth at cworth.org>
+ */
+
+#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



More information about the notmuch mailing list