From 36522fca1cac6ca23c2c4c0280e3e20e96f7bfbb Mon Sep 17 00:00:00 2001 From: "craven@gmx.net" Date: Mon, 23 Jul 2012 12:39:45 +0200 Subject: Add structured output formatter for JSON and plain text (but don't use them yet). Using the new structured printer support in sprinter.h, implement sprinter_json_create, which returns a new JSON structured output formatter. The formatter prints output similar to the existing JSON, but with differences in whitespace (mostly newlines, --output=summary prints the entire message summary on one line, not split across multiple lines). Also implement a "structured" formatter for plain text that prints prefixed strings, to be used with notmuch-search.c plain text output. --- sprinter-json.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 sprinter-json.c (limited to 'sprinter-json.c') diff --git a/sprinter-json.c b/sprinter-json.c new file mode 100644 index 00000000..46496551 --- /dev/null +++ b/sprinter-json.c @@ -0,0 +1,187 @@ +#include +#include +#include +#include "sprinter.h" + +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; + + /* A flag to signify that a separator should be inserted in the + * output as soon as possible. + */ + notmuch_bool_t insert_separator; +}; + +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) { + fputc (',', spj->stream); + if (spj->insert_separator) { + fputc ('\n', spj->stream); + spj->insert_separator = FALSE; + } else { + fputc (' ', 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_set_prefix (unused (struct sprinter *sp), unused (const char *name)) +{ +} + +static void +json_separator (struct sprinter *sp) +{ + struct sprinter_json *spj = (struct sprinter_json *) sp; + + spj->insert_separator = TRUE; +} + +struct sprinter * +sprinter_json_create (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, + .separator = json_separator, + .set_prefix = json_set_prefix, + .is_text_printer = FALSE, + } + }; + struct sprinter_json *res; + + res = talloc (ctx, struct sprinter_json); + if (! res) + return NULL; + + *res = template; + res->stream = stream; + return &res->vtable; +} -- cgit v1.2.3