From 231a78d410c2bd3fabf8027112180dbfdc0891f7 Mon Sep 17 00:00:00 2001 From: Peter Feigl Date: Fri, 30 Nov 2012 09:29:01 +0100 Subject: [PATCH] [PATCH 1/3] Adding an S-expression structured output printer. --- 75/d7da3b8b7480835a9afd4cd3dd70a2beb2ac87 | 316 ++++++++++++++++++++++ 1 file changed, 316 insertions(+) create mode 100644 75/d7da3b8b7480835a9afd4cd3dd70a2beb2ac87 diff --git a/75/d7da3b8b7480835a9afd4cd3dd70a2beb2ac87 b/75/d7da3b8b7480835a9afd4cd3dd70a2beb2ac87 new file mode 100644 index 000000000..1b83a7544 --- /dev/null +++ b/75/d7da3b8b7480835a9afd4cd3dd70a2beb2ac87 @@ -0,0 +1,316 @@ +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 363B3431FD8 + for ; Fri, 30 Nov 2012 00:35:13 -0800 (PST) +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] 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 XK83fmPPnAg1 for ; + Fri, 30 Nov 2012 00:35:11 -0800 (PST) +X-Greylist: delayed 352 seconds by postgrey-1.32 at olra; + Fri, 30 Nov 2012 00:35:10 PST +Received: from mail.nexoid.at (www.nexoid.at [178.79.130.240]) + (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) + (No client certificate requested) + by olra.theworths.org (Postfix) with ESMTPS id F0DF1431FAF + for ; Fri, 30 Nov 2012 00:35:10 -0800 (PST) +Received: by mail.nexoid.at (Postfix, from userid 1000) + id E8D2B11C0D0; Fri, 30 Nov 2012 09:29:16 +0100 (CET) +From: Peter Feigl +To: notmuch@notmuchmail.org +Subject: [PATCH 1/3] Adding an S-expression structured output printer. +Date: Fri, 30 Nov 2012 09:29:01 +0100 +Message-Id: <1354264143-30173-1-git-send-email-craven@gmx.net> +X-Mailer: git-send-email 1.8.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: Fri, 30 Nov 2012 08:35:13 -0000 + +This commit adds an sprinter for Lisp S-Expressions. Later commits will +use this printer. + +The structure is the same as json, but: +- arrays are written as lists: ("foo" "bar" "baaz" 1 2 3) +- maps are written as a-lists: ((key "value") (other-key "other-value")) +- true is written as t +- false is written as nil +- null is written as nil +--- + Makefile.local | 1 + + sprinter-sexp.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 236 insertions(+) + create mode 100644 sprinter-sexp.c + +diff --git a/Makefile.local b/Makefile.local +index 2b91946..0db1713 100644 +--- a/Makefile.local ++++ b/Makefile.local +@@ -270,6 +270,7 @@ notmuch_client_srcs = \ + notmuch-tag.c \ + notmuch-time.c \ + sprinter-json.c \ ++ sprinter-sexp.c \ + sprinter-text.c \ + query-string.c \ + mime-node.c \ +diff --git a/sprinter-sexp.c b/sprinter-sexp.c +new file mode 100644 +index 0000000..8401c52 +--- /dev/null ++++ b/sprinter-sexp.c +@@ -0,0 +1,235 @@ ++#include ++#include ++#include ++#include "sprinter.h" ++ ++struct sprinter_sexp { ++ struct sprinter vtable; ++ FILE *stream; ++ /* Top of the state stack, or NULL if the printer is not currently ++ * inside any aggregate types. */ ++ struct sexp_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 sexp_state { ++ struct sexp_state *parent; ++ ++ /* True if nothing has been printed in this aggregate yet. ++ * Suppresses the space before a value. */ ++ notmuch_bool_t first; ++ ++ /* True if the state is a map state. ++ Used to add a space between key/value pairs. */ ++ notmuch_bool_t in_map; ++ ++ /* 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 space. */ ++static struct sprinter_sexp * ++sexp_begin_value (struct sprinter *sp) ++{ ++ struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; ++ ++ if (sps->state) { ++ if (! sps->state->first) { ++ if (sps->insert_separator) { ++ fputc ('\n', sps->stream); ++ sps->insert_separator = FALSE; ++ } else { ++ if( ! sps->state->in_map) ++ fputc (' ', sps->stream); ++ } ++ } else { ++ sps->state->first = FALSE; ++ } ++ } ++ return sps; ++} ++ ++/* Helper function to begin an aggregate type. Prints the open ++ * character and pushes a new state frame. */ ++static void ++sexp_begin_aggregate (struct sprinter *sp, char open, char close) ++{ ++ struct sprinter_sexp *sps = sexp_begin_value (sp); ++ struct sexp_state *state = talloc (sps, struct sexp_state); ++ fputc (open, sps->stream); ++ state->parent = sps->state; ++ state->first = TRUE; ++ state->in_map = FALSE; ++ state->close = close; ++ sps->state = state; ++} ++ ++static void ++sexp_begin_map (struct sprinter *sp) ++{ ++ struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; ++ sexp_begin_aggregate (sp, '(', ')'); ++ sps->state->in_map = TRUE; ++} ++ ++static void ++sexp_begin_list (struct sprinter *sp) ++{ ++ sexp_begin_aggregate (sp, '(', ')'); ++} ++ ++static void ++sexp_end (struct sprinter *sp) ++{ ++ struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; ++ struct sexp_state *state = sps->state; ++ ++ if (sps->state->in_map) ++ fputc (')', sps->stream); ++ fputc (sps->state->close, sps->stream); ++ sps->state = state->parent; ++ talloc_free (state); ++ if (sps->state == NULL) ++ fputc ('\n', sps->stream); ++} ++ ++/* This implementation supports embedded NULs as allowed by the JSON ++ * specification and Unicode. Support for *parsing* embedded NULs ++ * varies, but is generally not a problem outside of C-based parsers ++ * (Python's json module and Emacs' json.el take embedded NULs in ++ * stride). */ ++static void ++sexp_string_len_internal (struct sprinter *sp, const char *val, size_t len, notmuch_bool_t quote) ++{ ++ static const char *const escapes[] = { ++ ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b", ++ ['\f'] = "\\f", ['\n'] = "\\n", ['\t'] = "\\t" ++ }; ++ struct sprinter_sexp *sps = sexp_begin_value (sp); ++ ++ if(quote) ++ fputc ('"', sps->stream); ++ for (; len; ++val, --len) { ++ unsigned char ch = *val; ++ if (ch < ARRAY_SIZE (escapes) && escapes[ch]) ++ fputs (escapes[ch], sps->stream); ++ else if (ch >= 32) ++ fputc (ch, sps->stream); ++ else ++ fprintf (sps->stream, "\\u%04x", ch); ++ } ++ if(quote) ++ fputc ('"', sps->stream); ++} ++ ++static void ++sexp_string_len (struct sprinter *sp, const char *val, size_t len) ++{ ++ sexp_string_len_internal (sp, val, len, TRUE); /* print quoted */ ++} ++ ++static void ++sexp_symbol_len (struct sprinter *sp, const char *val, size_t len) ++{ ++ sexp_string_len_internal (sp, val, len, FALSE); /* print unquoted */ ++} ++ ++static void ++sexp_string (struct sprinter *sp, const char *val) ++{ ++ if (val == NULL) ++ val = ""; ++ sexp_string_len (sp, val, strlen (val)); ++} ++ ++static void ++sexp_symbol (struct sprinter *sp, const char *val) ++{ ++ if (val == NULL) ++ val = ""; ++ sexp_symbol_len (sp, val, strlen (val)); ++} ++ ++static void ++sexp_integer (struct sprinter *sp, int val) ++{ ++ struct sprinter_sexp *sps = sexp_begin_value (sp); ++ ++ fprintf (sps->stream, "%d", val); ++} ++ ++static void ++sexp_boolean (struct sprinter *sp, notmuch_bool_t val) ++{ ++ struct sprinter_sexp *sps = sexp_begin_value (sp); ++ ++ fputs (val ? "t" : "nil", sps->stream); ++} ++ ++static void ++sexp_null (struct sprinter *sp) ++{ ++ struct sprinter_sexp *sps = sexp_begin_value (sp); ++ ++ fputs ("nil", sps->stream); ++} ++ ++static void ++sexp_map_key (struct sprinter *sp, const char *key) ++{ ++ struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; ++ ++ if( sps->state->in_map && ! sps->state->first) ++ fputs (") ", sps->stream); ++ fputc ('(', sps->stream); ++ sexp_symbol (sp, key); ++ fputc (' ', sps->stream); ++} ++ ++static void ++sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name)) ++{ ++} ++ ++static void ++sexp_separator (struct sprinter *sp) ++{ ++ struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; ++ ++ sps->insert_separator = TRUE; ++} ++ ++struct sprinter * ++sprinter_sexp_create (const void *ctx, FILE *stream) ++{ ++ static const struct sprinter_sexp template = { ++ .vtable = { ++ .begin_map = sexp_begin_map, ++ .begin_list = sexp_begin_list, ++ .end = sexp_end, ++ .string = sexp_string, ++ .string_len = sexp_string_len, ++ .integer = sexp_integer, ++ .boolean = sexp_boolean, ++ .null = sexp_null, ++ .map_key = sexp_map_key, ++ .separator = sexp_separator, ++ .set_prefix = sexp_set_prefix, ++ .is_text_printer = FALSE, ++ } ++ }; ++ struct sprinter_sexp *res; ++ ++ res = talloc (ctx, struct sprinter_sexp); ++ if (! res) ++ return NULL; ++ ++ *res = template; ++ res->stream = stream; ++ return &res->vtable; ++} +-- +1.8.0 + -- 2.26.2