Re: [PATCH v4 1/5] Adding an S-expression structured output printer.
authorAustin Clements <aclements@csail.mit.edu>
Thu, 6 Dec 2012 15:44:05 +0000 (10:44 +1900)
committerW. Trevor King <wking@tremily.us>
Fri, 7 Nov 2014 17:51:35 +0000 (09:51 -0800)
af/0d34e61d244b3a62c6d575be9371625491bd49 [new file with mode: 0644]

diff --git a/af/0d34e61d244b3a62c6d575be9371625491bd49 b/af/0d34e61d244b3a62c6d575be9371625491bd49
new file mode 100644 (file)
index 0000000..fb539e5
--- /dev/null
@@ -0,0 +1,390 @@
+Return-Path: <amdragon@mit.edu>\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 1582D431FB6\r
+       for <notmuch@notmuchmail.org>; Thu,  6 Dec 2012 07:44:10 -0800 (PST)\r
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
+X-Spam-Flag: NO\r
+X-Spam-Score: -0.7\r
+X-Spam-Level: \r
+X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5\r
+       tests=[RCVD_IN_DNSWL_LOW=-0.7] 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 xNSGtffUoBDR for <notmuch@notmuchmail.org>;\r
+       Thu,  6 Dec 2012 07:44:09 -0800 (PST)\r
+Received: from dmz-mailsec-scanner-8.mit.edu (DMZ-MAILSEC-SCANNER-8.MIT.EDU\r
+       [18.7.68.37])\r
+       by olra.theworths.org (Postfix) with ESMTP id BB48B431FAE\r
+       for <notmuch@notmuchmail.org>; Thu,  6 Dec 2012 07:44:08 -0800 (PST)\r
+X-AuditID: 12074425-b7f606d0000008ea-06-50c0bd48dfca\r
+Received: from mailhub-auth-2.mit.edu ( [18.7.62.36])\r
+       by dmz-mailsec-scanner-8.mit.edu (Symantec Messaging Gateway) with SMTP\r
+       id A8.D6.02282.84DB0C05; Thu,  6 Dec 2012 10:44:08 -0500 (EST)\r
+Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
+       by mailhub-auth-2.mit.edu (8.13.8/8.9.2) with ESMTP id qB6Fi7WX015398; \r
+       Thu, 6 Dec 2012 10:44:08 -0500\r
+Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
+       (authenticated bits=0)\r
+       (User authenticated as amdragon@ATHENA.MIT.EDU)\r
+       by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id qB6Fi5KG023835\r
+       (version=TLSv1/SSLv3 cipher=DHE-RSA-AES128-SHA bits=128 verify=NOT);\r
+       Thu, 6 Dec 2012 10:44:06 -0500 (EST)\r
+Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.80)\r
+       (envelope-from <amdragon@mit.edu>)\r
+       id 1TgdcL-0005zu-4p; Thu, 06 Dec 2012 10:44:05 -0500\r
+From: Austin Clements <aclements@csail.mit.edu>\r
+To: Peter Feigl <craven@gmx.net>, notmuch@notmuchmail.org\r
+Subject: Re: [PATCH v4 1/5] Adding an S-expression structured output printer.\r
+In-Reply-To:\r
+ <1409adf6cbd310d7313bdc3a69e7e78c25859d81.1354794428.git.craven@gmx.net>\r
+References: <cover.1354794428.git.craven@gmx.net>\r
+       <1409adf6cbd310d7313bdc3a69e7e78c25859d81.1354794428.git.craven@gmx.net>\r
+User-Agent: Notmuch/0.14+159~g6895fee (http://notmuchmail.org) Emacs/23.4.1\r
+       (i486-pc-linux-gnu)\r
+Date: Thu, 06 Dec 2012 10:44:05 -0500\r
+Message-ID: <87hanzp4my.fsf@awakening.csail.mit.edu>\r
+MIME-Version: 1.0\r
+Content-Type: text/plain; charset=utf-8\r
+Content-Transfer-Encoding: quoted-printable\r
+X-Brightmail-Tracker:\r
+ H4sIAAAAAAAAA+NgFmphleLIzCtJLcpLzFFi42IRYrdT0fXYeyDA4NF5Lou9De2MFtdvzmR2\r
+       YPJYvGk/m8ezVbeYA5iiuGxSUnMyy1KL9O0SuDLaXv5mKzjhWfHhmlkD4y3zLkZODgkBE4kZ\r
+       RzeyQNhiEhfurWfrYuTiEBLYxyjx5fNuRghnPaPE7+4OdgjnApPEwm2NUM4SRokVD7vB+tkE\r
+       9CVWrJ3ECmKLCFhKTP1yiQ3EFhbwlTiz5Q4ziM0pECqxtHk6I4gtJFAlcf/bUaAaDg5RgXiJ\r
+       2ed8QMIsAqoSD679ZAKxeYHOa959GcoWlDg58wnYKmYBdYk/8y4xQ9jaEssWvmaewCg4C0nZ\r
+       LCRls5CULWBkXsUom5JbpZubmJlTnJqsW5ycmJeXWqRroZebWaKXmlK6iREcwC6qOxgnHFI6\r
+       xCjAwajEw2tRvT9AiDWxrLgy9xCjJAeTkihv9vYDAUJ8SfkplRmJxRnxRaU5qcWHGCU4mJVE\r
+       eGM6gHK8KYmVValF+TApaQ4WJXHeGyk3/YUE0hNLUrNTUwtSi2CyMhwcShK8m3cDNQoWpaan\r
+       VqRl5pQgpJk4OEGG8wANjwap4S0uSMwtzkyHyJ9i1OVYuKb9CaMQS15+XqqUOO9ZkCIBkKKM\r
+       0jy4ObDE84pRHOgtYd75IFU8wKQFN+kV0BImoCVR7PtBlpQkIqSkGhj3KU77EnXJwqKm+JdU\r
+       ol/1L8+7DHsDb4vlPG8qS/3JE+Aup3d9Q3/hp6sy37gETZYm2lxIycp3vDr/QWF4wiFRjY/r\r
+       ZgqyrD/l+UFv2sfKW4IWxwq918/jMI95yZI0/b3eVLF426PHzhzzfLPjy5qa433Rf4+U7ZG1\r
+       /+HNcPzGjzl2/40Mz/QosRRnJBpqMRcVJwIAOJZnlBcDAAA=\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: Thu, 06 Dec 2012 15:44:10 -0000\r
+\r
+On Thu, 06 Dec 2012, Peter Feigl <craven@gmx.net> wrote:\r
+> This commit adds a structured output printer for Lisp\r
+> S-Expressions. Later commits will use this printer in notmuch search,\r
+> show and reply.\r
+>\r
+> The structure is the same as json, but:\r
+> - arrays are written as lists: ("foo" "bar" "baaz" 1 2 3)\r
+> - maps are written as p-lists: (:key "value" :other-key "other-value")\r
+> - true is written as t\r
+> - false is written as nil\r
+> - null is written as nil\r
+> ---\r
+>  Makefile.local  |   1 +\r
+>  sprinter-sexp.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++=\r
+++++++\r
+>  sprinter.h      |   4 +\r
+>  3 files changed, 243 insertions(+)\r
+>  create mode 100644 sprinter-sexp.c\r
+>\r
+> diff --git a/Makefile.local b/Makefile.local\r
+> index 2b91946..0db1713 100644\r
+> --- a/Makefile.local\r
+> +++ b/Makefile.local\r
+> @@ -270,6 +270,7 @@ notmuch_client_srcs =3D          \\r
+>      notmuch-tag.c           \\r
+>      notmuch-time.c          \\r
+>      sprinter-json.c         \\r
+> +    sprinter-sexp.c         \\r
+>      sprinter-text.c         \\r
+>      query-string.c          \\r
+>      mime-node.c             \\r
+> diff --git a/sprinter-sexp.c b/sprinter-sexp.c\r
+> new file mode 100644\r
+> index 0000000..8f84eed\r
+> --- /dev/null\r
+> +++ b/sprinter-sexp.c\r
+> @@ -0,0 +1,238 @@\r
+> +/* notmuch - Not much of an email program, (just index and search)\r
+> + *\r
+> + * Copyright =C2=A9 2012 Peter Feigl\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: Peter Feigl <peter.feigl@gmx.at>\r
+> + */\r
+> +\r
+> +#include <stdbool.h>\r
+> +#include <stdio.h>\r
+> +#include <talloc.h>\r
+> +#include "sprinter.h"\r
+> +\r
+> +struct sprinter_sexp {\r
+> +    struct sprinter vtable;\r
+> +    FILE *stream;\r
+> +    /* Top of the state stack, or NULL if the printer is not currently\r
+> +     * inside any aggregate types. */\r
+> +    struct sexp_state *state;\r
+> +\r
+> +    /* A flag to signify that a separator should be inserted in the\r
+> +     * output as soon as possible. */\r
+> +    notmuch_bool_t insert_separator;\r
+> +};\r
+> +\r
+> +struct sexp_state {\r
+> +    struct sexp_state *parent;\r
+> +\r
+> +    /* True if nothing has been printed in this aggregate yet.\r
+> +     * Suppresses the space before a value. */\r
+> +    notmuch_bool_t first;\r
+> +};\r
+> +\r
+> +/* Helper function to set up the stream to print a value.  If this\r
+> + * value follows another value, prints a space. */\r
+> +static struct sprinter_sexp *\r
+> +sexp_begin_value (struct sprinter *sp)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D (struct sprinter_sexp *) sp;\r
+> +\r
+> +    if (sps->state) {\r
+> +    if (! sps->state->first) {\r
+> +        if (sps->insert_separator) {\r
+> +            fputc ('\n', sps->stream);\r
+> +            sps->insert_separator =3D FALSE;\r
+> +        } else {\r
+> +            fputc (' ', sps->stream);\r
+> +        }\r
+> +    } else {\r
+> +        sps->state->first =3D FALSE;\r
+> +    }\r
+> +    }\r
+> +    return sps;\r
+> +}\r
+> +\r
+> +/* Helper function to begin an aggregate type.  Prints the open\r
+> + * character and pushes a new state frame. */\r
+> +static void\r
+> +sexp_begin_aggregate (struct sprinter *sp)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D sexp_begin_value (sp);\r
+> +    struct sexp_state *state =3D talloc (sps, struct sexp_state);\r
+> +    fputc ('(', sps->stream);\r
+> +    state->parent =3D sps->state;\r
+> +    state->first =3D TRUE;\r
+> +    sps->state =3D state;\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_begin_map (struct sprinter *sp)\r
+> +{\r
+> +    sexp_begin_aggregate (sp);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_begin_list (struct sprinter *sp)\r
+> +{\r
+> +    sexp_begin_aggregate (sp);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_end (struct sprinter *sp)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D (struct sprinter_sexp *) sp;\r
+> +    struct sexp_state *state =3D sps->state;\r
+> +\r
+> +    fputc (')', sps->stream);\r
+> +    sps->state =3D state->parent;\r
+> +    talloc_free (state);\r
+> +    if (sps->state =3D=3D NULL)\r
+> +    fputc ('\n', sps->stream);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_string_len (struct sprinter *sp, const char *val, size_t len)\r
+> +{\r
+> +    /* Some characters need escaping. " and \ work fine in all Lisps,\r
+> +     * \n is not supported in CL, but all others work fine.\r
+> +     * Characters below 32 are printed as \123o (three-digit=20\r
+> +     * octals), which work fine in most Schemes and Emacs. */\r
+> +    static const char *const escapes[] =3D {\r
+> +    ['\"'] =3D "\\\"", ['\\'] =3D "\\\\",  ['\n'] =3D "\\n"\r
+> +    };\r
+> +    struct sprinter_sexp *sps =3D sexp_begin_value (sp);\r
+> +\r
+> +    fputc ('"', sps->stream);\r
+> +    for (; len; ++val, --len) {\r
+> +    unsigned char ch =3D *val;\r
+> +    if (ch < ARRAY_SIZE (escapes) && escapes[ch])\r
+> +        fputs (escapes[ch], sps->stream);\r
+> +    else if (ch >=3D 32)\r
+> +        fputc (ch, sps->stream);\r
+> +    else\r
+> +        fprintf (sps->stream, "\\%03oo", ch);\r
+\r
+Should be "\\%03o" (one less o).\r
+\r
+> +    }\r
+> +    fputc ('"', sps->stream);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_string (struct sprinter *sp, const char *val)\r
+> +{\r
+> +    if (val =3D=3D NULL)\r
+> +    val =3D "";\r
+> +    sexp_string_len (sp, val, strlen (val));\r
+> +}\r
+> +\r
+> +/* Prints a symbol, i.e. the name preceded by a colon. This should work\r
+> + * in all Lisps, at least as a symbol, if not as a proper keyword */\r
+> +static void\r
+> +sexp_symbol (struct sprinter *sp, const char *val)\r
+> +{\r
+> +    static const char illegal_characters[] =3D {\r
+> +    ' ', '\t', '\n'\r
+\r
+There are a large number of illegal symbol characters.  I would go the\r
+conservative route here (since we get to choose our symbols) and only\r
+allow\r
+  isalnum(x) || (x =3D=3D '-') || (x =3D=3D '_')\r
+(I would prefer not to allow _ but it's already in the schema.)\r
+\r
+> +    };\r
+> +    unsigned int i =3D 0;\r
+> +    struct sprinter_sexp *sps =3D (struct sprinter_sexp *) sp;\r
+> +\r
+> +    if (val =3D=3D NULL)\r
+> +    INTERNAL_ERROR ("illegal symbol NULL");\r
+> +\r
+> +    for(i =3D 0; i < ARRAY_SIZE (illegal_characters); i++) {\r
+> +    if(strchr(val, illegal_characters[i]) !=3D NULL) {\r
+> +        INTERNAL_ERROR ("illegal character in symbol %s", val);\r
+> +    }\r
+> +    }\r
+> +    fputc (':', sps->stream);\r
+> +    fputs (val, sps->stream);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_integer (struct sprinter *sp, int val)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D sexp_begin_value (sp);\r
+> +\r
+> +    fprintf (sps->stream, "%d", val);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_boolean (struct sprinter *sp, notmuch_bool_t val)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D sexp_begin_value (sp);\r
+> +\r
+> +    fputs (val ? "t" : "nil", sps->stream);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_null (struct sprinter *sp)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D sexp_begin_value (sp);\r
+> +\r
+> +    fputs ("nil", sps->stream);\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_map_key (struct sprinter *sp, const char *key)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D (struct sprinter_sexp *) sp;\r
+> +    if (sps->state && ! sps->state->first)\r
+> +    fputc (' ', sps->stream);\r
+> +\r
+> +    sps->state->first =3D FALSE;\r
+> +    sexp_symbol (sp, key);\r
+> +}\r
+\r
+How about\r
+    struct sprinter_sexp *sps =3D sexp_begin_value (sp);\r
+\r
+    sexp_symbol (sp, key);\r
+?\r
+\r
+> +\r
+> +static void\r
+> +sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))\r
+> +{\r
+> +}\r
+> +\r
+> +static void\r
+> +sexp_separator (struct sprinter *sp)\r
+> +{\r
+> +    struct sprinter_sexp *sps =3D (struct sprinter_sexp *) sp;\r
+> +\r
+> +    sps->insert_separator =3D TRUE;\r
+> +}\r
+> +\r
+> +struct sprinter *\r
+> +sprinter_sexp_create (const void *ctx, FILE *stream)\r
+> +{\r
+> +    static const struct sprinter_sexp template =3D {\r
+> +    .vtable =3D {\r
+> +        .begin_map =3D sexp_begin_map,\r
+> +        .begin_list =3D sexp_begin_list,\r
+> +        .end =3D sexp_end,\r
+> +        .string =3D sexp_string,\r
+> +        .string_len =3D sexp_string_len,\r
+> +        .integer =3D sexp_integer,\r
+> +        .boolean =3D sexp_boolean,\r
+> +        .null =3D sexp_null,\r
+> +        .map_key =3D sexp_map_key,\r
+> +        .separator =3D sexp_separator,\r
+> +        .set_prefix =3D sexp_set_prefix,\r
+> +        .is_text_printer =3D FALSE,\r
+> +    }\r
+> +    };\r
+> +    struct sprinter_sexp *res;\r
+> +\r
+> +    res =3D talloc (ctx, struct sprinter_sexp);\r
+> +    if (! res)\r
+> +    return NULL;\r
+> +\r
+> +    *res =3D template;\r
+> +    res->stream =3D stream;\r
+> +    return &res->vtable;\r
+> +}\r
+> diff --git a/sprinter.h b/sprinter.h\r
+> index 912a526..59776a9 100644\r
+> --- a/sprinter.h\r
+> +++ b/sprinter.h\r
+> @@ -70,4 +70,8 @@ sprinter_text_create (const void *ctx, FILE *stream);\r
+>  struct sprinter *\r
+>  sprinter_json_create (const void *ctx, FILE *stream);\r
+>=20=20\r
+> +/* Create a new structure printer that emits S-Expressions. */\r
+> +struct sprinter *\r
+> +sprinter_sexp_create (const void *ctx, FILE *stream);\r
+> +\r
+>  #endif // NOTMUCH_SPRINTER_H\r
+> --=20\r
+> 1.8.0\r
+>\r
+> _______________________________________________\r
+> notmuch mailing list\r
+> notmuch@notmuchmail.org\r
+> http://notmuchmail.org/mailman/listinfo/notmuch\r