From: Peter Feigl Date: Thu, 6 Dec 2012 07:33:05 +0000 (+0100) Subject: [PATCH v3 1/5] Adding an S-expression structured output printer. X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=cecbc52c747ade2efd8f16b8c3c2ba5803033566;p=notmuch-archives.git [PATCH v3 1/5] Adding an S-expression structured output printer. --- diff --git a/61/47f6de49c879b9fca064015df5aeb67de23ace b/61/47f6de49c879b9fca064015df5aeb67de23ace new file mode 100644 index 000000000..cf494e64f --- /dev/null +++ b/61/47f6de49c879b9fca064015df5aeb67de23ace @@ -0,0 +1,340 @@ +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 DD1C5429E2F + for ; Wed, 5 Dec 2012 23:33:28 -0800 (PST) +X-Virus-Scanned: Debian amavisd-new at olra.theworths.org +X-Amavis-Alert: BAD HEADER SECTION, Duplicate header field: "References" +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 jxEwh6i0UIeA for ; + Wed, 5 Dec 2012 23:33:24 -0800 (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 11484431FC4 + for ; Wed, 5 Dec 2012 23:33:19 -0800 (PST) +Received: by mail.nexoid.at (Postfix, from userid 1000) + id A1D3511C10B; Thu, 6 Dec 2012 08:33:12 +0100 (CET) +From: Peter Feigl +To: notmuch@notmuchmail.org +Subject: [PATCH v3 1/5] Adding an S-expression structured output printer. +Date: Thu, 6 Dec 2012 08:33:05 +0100 +Message-Id: <1354779189-12231-2-git-send-email-craven@gmx.net> +X-Mailer: git-send-email 1.8.0 +In-Reply-To: <1354779189-12231-1-git-send-email-craven@gmx.net> +References: <1354779189-12231-1-git-send-email-craven@gmx.net> +In-Reply-To: <1354632382-15609-1-git-send-email-craven@gmx.net> +References: <1354632382-15609-1-git-send-email-craven@gmx.net> +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit +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, 06 Dec 2012 07:33:29 -0000 + +This commit adds a structured output printer for Lisp +S-Expressions. Later commits will use this printer in notmuch search, +show and reply. + +The structure is the same as json, but: +- arrays are written as lists: ("foo" "bar" "baaz" 1 2 3) +- maps are written as p-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 | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + sprinter.h | 4 + + 3 files changed, 243 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..8f84eed +--- /dev/null ++++ b/sprinter-sexp.c +@@ -0,0 +1,238 @@ ++/* notmuch - Not much of an email program, (just index and search) ++ * ++ * Copyright © 2012 Peter Feigl ++ * ++ * 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: Peter Feigl ++ */ ++ ++#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; ++}; ++ ++/* 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 { ++ 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) ++{ ++ struct sprinter_sexp *sps = sexp_begin_value (sp); ++ struct sexp_state *state = talloc (sps, struct sexp_state); ++ fputc ('(', sps->stream); ++ state->parent = sps->state; ++ state->first = TRUE; ++ sps->state = state; ++} ++ ++static void ++sexp_begin_map (struct sprinter *sp) ++{ ++ sexp_begin_aggregate (sp); ++} ++ ++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; ++ ++ fputc (')', sps->stream); ++ sps->state = state->parent; ++ talloc_free (state); ++ if (sps->state == NULL) ++ fputc ('\n', sps->stream); ++} ++ ++static void ++sexp_string_len (struct sprinter *sp, const char *val, size_t len) ++{ ++ /* Some characters need escaping. " and \ work fine in all Lisps, ++ * \n is not supported in CL, but all others work fine. ++ * Characters below 32 are printed as \123o (three-digit ++ * octals), which work fine in most Schemes and Emacs. */ ++ static const char *const escapes[] = { ++ ['\"'] = "\\\"", ['\\'] = "\\\\", ['\n'] = "\\n" ++ }; ++ struct sprinter_sexp *sps = sexp_begin_value (sp); ++ ++ 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, "\\%03oo", ch); ++ } ++ fputc ('"', sps->stream); ++} ++ ++static void ++sexp_string (struct sprinter *sp, const char *val) ++{ ++ if (val == NULL) ++ val = ""; ++ sexp_string_len (sp, val, strlen (val)); ++} ++ ++/* Prints a symbol, i.e. the name preceded by a colon. This should work ++ * in all Lisps, at least as a symbol, if not as a proper keyword */ ++static void ++sexp_symbol (struct sprinter *sp, const char *val) ++{ ++ static const char illegal_characters[] = { ++ ' ', '\t', '\n' ++ }; ++ unsigned int i = 0; ++ struct sprinter_sexp *sps = (struct sprinter_sexp *) sp; ++ ++ if (val == NULL) ++ INTERNAL_ERROR ("illegal symbol NULL"); ++ ++ for(i = 0; i < ARRAY_SIZE (illegal_characters); i++) { ++ if(strchr(val, illegal_characters[i]) != NULL) { ++ INTERNAL_ERROR ("illegal character in symbol %s", val); ++ } ++ } ++ fputc (':', sps->stream); ++ fputs (val, sps->stream); ++} ++ ++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 && ! sps->state->first) ++ fputc (' ', sps->stream); ++ ++ sps->state->first = FALSE; ++ sexp_symbol (sp, key); ++} ++ ++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; ++} +diff --git a/sprinter.h b/sprinter.h +index 912a526..59776a9 100644 +--- a/sprinter.h ++++ b/sprinter.h +@@ -70,4 +70,8 @@ sprinter_text_create (const void *ctx, FILE *stream); + struct sprinter * + sprinter_json_create (const void *ctx, FILE *stream); + ++/* Create a new structure printer that emits S-Expressions. */ ++struct sprinter * ++sprinter_sexp_create (const void *ctx, FILE *stream); ++ + #endif // NOTMUCH_SPRINTER_H +-- +1.8.0 +