1 Return-Path: <peter.feigl@gmx.at>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id 0A88C429E25
\r
6 for <notmuch@notmuchmail.org>; Thu, 12 Jul 2012 04:22:23 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=0.001 tagged_above=-999 required=5
\r
12 tests=[FREEMAIL_FROM=0.001, RCVD_IN_DNSWL_NONE=-0.0001]
\r
14 Received: from olra.theworths.org ([127.0.0.1])
\r
15 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
16 with ESMTP id KfW9oxVJIt4B for <notmuch@notmuchmail.org>;
\r
17 Thu, 12 Jul 2012 04:22:21 -0700 (PDT)
\r
18 Received: from mailout-de.gmx.net (mailout-de.gmx.net [213.165.64.22])
\r
19 by olra.theworths.org (Postfix) with SMTP id 23812431FAE
\r
20 for <notmuch@notmuchmail.org>; Thu, 12 Jul 2012 04:22:20 -0700 (PDT)
\r
21 Received: (qmail invoked by alias); 12 Jul 2012 11:22:17 -0000
\r
22 Received: from unknown (EHLO mail.nexoid.at) [178.79.130.240]
\r
23 by mail.gmx.net (mp039) with SMTP; 12 Jul 2012 13:22:17 +0200
\r
24 X-Authenticated: #4563876
\r
25 X-Provags-ID: V01U2FsdGVkX18fyCZlHB0fXGjwommbl7N8DNJZYoaKpjEfaSpaIN
\r
27 Received: from nexoid (localhost [127.0.0.1])
\r
28 by mail.nexoid.at (Postfix) with ESMTP id 973BFE00C;
\r
29 Thu, 12 Jul 2012 13:22:12 +0200 (CEST)
\r
30 From: <craven@gmx.net>
\r
31 To: Mark Walters <markwalters1009@gmail.com>, notmuch@notmuchmail.org
\r
32 Subject: Re: [PATCH v4 1/3] Add support for structured output formatters.
\r
33 In-Reply-To: <87pq81tjv4.fsf@qmul.ac.uk>
\r
34 References: <87d34hsdx8.fsf@awakening.csail.mit.edu>
\r
35 <1342079004-5300-1-git-send-email-craven@gmx.net>
\r
36 <1342079004-5300-2-git-send-email-craven@gmx.net>
\r
37 <87pq81tjv4.fsf@qmul.ac.uk>
\r
38 User-Agent: Notmuch/0.11+77~gad6d0d5 (http://notmuchmail.org) Emacs/24.1.50.2
\r
40 Date: Thu, 12 Jul 2012 13:22:12 +0200
\r
41 Message-ID: <87a9z5teaj.fsf@nexoid.at>
\r
43 Content-Type: multipart/mixed; boundary="=-=-="
\r
45 X-BeenThere: notmuch@notmuchmail.org
\r
46 X-Mailman-Version: 2.1.13
\r
48 List-Id: "Use and development of the notmuch mail system."
\r
49 <notmuch.notmuchmail.org>
\r
50 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
51 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
52 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
53 List-Post: <mailto:notmuch@notmuchmail.org>
\r
54 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
55 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
56 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
57 X-List-Received-Date: Thu, 12 Jul 2012 11:22:23 -0000
\r
60 Content-Type: text/plain
\r
62 > what is the advantage of having this as one function rather than end_map
\r
63 > and end_list? Indeed, my choice (but I think most other people would
\r
64 > disagree) would be to have both functions but still keep state as this
\r
65 > currently does and then throw an error if the code closes the wrong
\r
68 There's probably no advantage, one way or the other is fine, I'd say.
\r
69 I've thought about introducing checks into the formatter functions, to
\r
70 raise errors for improper closing, map_key not inside a map and things
\r
71 like that, I just wasn't sure that would be acceptable.
\r
73 > A second question: do you have an implementation in this style for
\r
74 > s-expressions. I find it hard to tell whether the interface is right
\r
75 > with just a single example. Even a completely hacky not ready for review
\r
76 > example would be helpful.
\r
78 See the attached patch :)
\r
80 Thanks for the suggestions!
\r
85 Content-Type: text/x-patch
\r
86 Content-Disposition: inline;
\r
87 filename=0001-Add-an-S-Expression-output-format.patch
\r
89 >From cf2c5eeab814970736510ca2210b909643a8cf19 Mon Sep 17 00:00:00 2001
\r
90 From: <craven@gmx.net>
\r
91 Date: Thu, 12 Jul 2012 10:17:05 +0200
\r
92 Subject: [PATCH] Add an S-Expression output format.
\r
95 notmuch-search.c | 7 ++-
\r
96 sprinter.c | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
\r
98 3 files changed, 180 insertions(+), 1 deletion(-)
\r
100 diff --git a/notmuch-search.c b/notmuch-search.c
\r
101 index b853f5f..f8bea9b 100644
\r
102 --- a/notmuch-search.c
\r
103 +++ b/notmuch-search.c
\r
104 @@ -77,6 +77,7 @@ do_search_threads (sprinter_t *format,
\r
106 if (format != sprinter_text) {
\r
107 format->begin_list (format);
\r
108 + format->frame (format);
\r
112 @@ -380,7 +381,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
\r
113 int exclude = EXCLUDE_TRUE;
\r
116 - enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT }
\r
117 + enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT, NOTMUCH_FORMAT_SEXP }
\r
118 format_sel = NOTMUCH_FORMAT_TEXT;
\r
120 notmuch_opt_desc_t options[] = {
\r
121 @@ -391,6 +392,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
\r
122 { NOTMUCH_OPT_KEYWORD, &format_sel, "format", 'f',
\r
123 (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
\r
124 { "text", NOTMUCH_FORMAT_TEXT },
\r
125 + { "sexp", NOTMUCH_FORMAT_SEXP },
\r
127 { NOTMUCH_OPT_KEYWORD, &output, "output", 'o',
\r
128 (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },
\r
129 @@ -422,6 +424,9 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
\r
130 case NOTMUCH_FORMAT_JSON:
\r
131 format = sprinter_json_new (ctx, stdout);
\r
133 + case NOTMUCH_FORMAT_SEXP:
\r
134 + format = sprinter_sexp_new (ctx, stdout);
\r
138 config = notmuch_config_open (ctx, NULL, NULL);
\r
139 diff --git a/sprinter.c b/sprinter.c
\r
140 index 649f79a..fce0f9b 100644
\r
143 @@ -170,3 +170,173 @@ sprinter_json_new(const void *ctx, FILE *stream)
\r
144 res->stream = stream;
\r
145 return &res->vtable;
\r
149 + * Every below here is the implementation of the SEXP printer.
\r
152 +typedef enum { MAP, LIST } aggregate_t;
\r
154 +struct sprinter_sexp
\r
156 + struct sprinter vtable;
\r
158 + /* Top of the state stack, or NULL if the printer is not currently
\r
159 + * inside any aggregate types. */
\r
160 + struct sexp_state *state;
\r
165 + struct sexp_state *parent;
\r
166 + /* True if nothing has been printed in this aggregate yet.
\r
167 + * Suppresses the comma before a value. */
\r
168 + notmuch_bool_t first;
\r
169 + /* The character that closes the current aggregate. */
\r
170 + aggregate_t type;
\r
173 +static struct sprinter_sexp *
\r
174 +sexp_begin_value(struct sprinter *sp)
\r
176 + struct sprinter_sexp *spsx = (struct sprinter_sexp*)sp;
\r
177 + if (spsx->state) {
\r
178 + if (!spsx->state->first)
\r
179 + fputc (' ', spsx->stream);
\r
181 + spsx->state->first = false;
\r
187 +sexp_begin_aggregate(struct sprinter *sp, aggregate_t type)
\r
189 + struct sprinter_sexp *spsx = (struct sprinter_sexp*)sp;
\r
190 + struct sexp_state *state = talloc (spsx, struct sexp_state);
\r
192 + fputc ('(', spsx->stream);
\r
193 + state->parent = spsx->state;
\r
194 + state->first = true;
\r
195 + spsx->state = state;
\r
196 + state->type = type;
\r
200 +sexp_begin_map(struct sprinter *sp)
\r
202 + sexp_begin_aggregate (sp, MAP);
\r
206 +sexp_begin_list(struct sprinter *sp)
\r
208 + sexp_begin_aggregate (sp, LIST);
\r
212 +sexp_end(struct sprinter *sp)
\r
214 + struct sprinter_sexp *spsx = (struct sprinter_sexp*)sp;
\r
215 + struct sexp_state *state = spsx->state;
\r
217 + fputc (')', spsx->stream);
\r
218 + spsx->state = state->parent;
\r
219 + talloc_free (state);
\r
220 + if(spsx->state == NULL)
\r
221 + fputc ('\n', spsx->stream);
\r
225 +sexp_string(struct sprinter *sp, const char *val)
\r
227 + static const char * const escapes[] = {
\r
228 + ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",
\r
229 + ['\f'] = "\\f", ['\n'] = "\\n", ['\t'] = "\\t"
\r
231 + struct sprinter_sexp *spsx = sexp_begin_value(sp);
\r
232 + fputc ('"', spsx->stream);
\r
233 + for (; *val; ++val) {
\r
234 + unsigned char ch = *val;
\r
235 + if (ch < ARRAY_SIZE(escapes) && escapes[ch])
\r
236 + fputs (escapes[ch], spsx->stream);
\r
237 + else if (ch >= 32)
\r
238 + fputc (ch, spsx->stream);
\r
240 + fprintf (spsx->stream, "\\u%04x", ch);
\r
242 + fputc ('"', spsx->stream);
\r
243 + if (spsx->state != NULL && spsx->state->type == MAP)
\r
244 + fputc (')', spsx->stream);
\r
245 + spsx->state->first = false;
\r
249 +sexp_integer(struct sprinter *sp, int val)
\r
251 + struct sprinter_sexp *spsx = sexp_begin_value(sp);
\r
252 + fprintf (spsx->stream, "%d", val);
\r
253 + if (spsx->state != NULL && spsx->state->type == MAP)
\r
254 + fputc (')', spsx->stream);
\r
258 +sexp_boolean(struct sprinter *sp, notmuch_bool_t val)
\r
260 + struct sprinter_sexp *spsx = sexp_begin_value(sp);
\r
261 + fputs (val ? "#t" : "#f", spsx->stream);
\r
262 + if (spsx->state != NULL && spsx->state->type == MAP)
\r
263 + fputc (')', spsx->stream);
\r
267 +sexp_null(struct sprinter *sp)
\r
269 + struct sprinter_sexp *spsx = sexp_begin_value(sp);
\r
270 + fputs ("'()", spsx->stream);
\r
271 + spsx->state->first = false;
\r
275 +sexp_map_key(struct sprinter *sp, const char *key)
\r
277 + struct sprinter_sexp *spsx = sexp_begin_value(sp);
\r
278 + fputc ('(', spsx->stream);
\r
279 + fputs (key, spsx->stream);
\r
280 + fputs (" . ", spsx->stream);
\r
281 + spsx->state->first = true;
\r
285 +sexp_frame(struct sprinter *sp)
\r
287 + struct sprinter_sexp *spsx = (struct sprinter_sexp*)sp;
\r
288 + fputc ('\n', spsx->stream);
\r
292 +sprinter_sexp_new(const void *ctx, FILE *stream)
\r
294 + static const struct sprinter_sexp template = {
\r
296 + .begin_map = sexp_begin_map,
\r
297 + .begin_list = sexp_begin_list,
\r
299 + .string = sexp_string,
\r
300 + .integer = sexp_integer,
\r
301 + .boolean = sexp_boolean,
\r
302 + .null = sexp_null,
\r
303 + .map_key = sexp_map_key,
\r
304 + .frame = sexp_frame,
\r
307 + struct sprinter_sexp *res;
\r
309 + res = talloc (ctx, struct sprinter_sexp);
\r
314 + res->stream = stream;
\r
315 + return &res->vtable;
\r
317 diff --git a/sprinter.h b/sprinter.h
\r
318 index 1dad9a0..a89eaa5 100644
\r
321 @@ -40,6 +40,10 @@ typedef struct sprinter
\r
323 sprinter_json_new(const void *ctx, FILE *stream);
\r
325 +/* Create a new structure printer that emits S-Expressions */
\r
327 +sprinter_sexp_new(const void *ctx, FILE *stream);
\r
329 /* A dummy structure printer that signifies that standard text output is
\r
330 * to be used instead of any structured format.
\r