1 Return-Path: <amdragon@mit.edu>
\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 CBB7F429E25
\r
6 for <notmuch@notmuchmail.org>; Thu, 12 Jul 2012 15:07:09 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=-0.7 tagged_above=-999 required=5
\r
12 tests=[RCVD_IN_DNSWL_LOW=-0.7] autolearn=disabled
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id jEGa1-+WJEDK for <notmuch@notmuchmail.org>;
\r
16 Thu, 12 Jul 2012 15:07:08 -0700 (PDT)
\r
17 Received: from dmz-mailsec-scanner-8.mit.edu (DMZ-MAILSEC-SCANNER-8.MIT.EDU
\r
19 by olra.theworths.org (Postfix) with ESMTP id 4F29C431FC4
\r
20 for <notmuch@notmuchmail.org>; Thu, 12 Jul 2012 15:07:08 -0700 (PDT)
\r
21 X-AuditID: 12074425-b7f9b6d0000008c4-2d-4fff4a8a9e35
\r
22 Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])
\r
23 by dmz-mailsec-scanner-8.mit.edu (Symantec Messaging Gateway) with SMTP
\r
24 id F6.63.02244.A8A4FFF4; Thu, 12 Jul 2012 18:07:06 -0400 (EDT)
\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])
\r
26 by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q6CM75Jq002528;
\r
27 Thu, 12 Jul 2012 18:07:05 -0400
\r
28 Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])
\r
29 (authenticated bits=0)
\r
30 (User authenticated as amdragon@ATHENA.MIT.EDU)
\r
31 by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q6CM744i028805
\r
32 (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);
\r
33 Thu, 12 Jul 2012 18:07:05 -0400 (EDT)
\r
34 Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)
\r
35 (envelope-from <amdragon@mit.edu>)
\r
36 id 1SpRXM-0004sD-2W; Thu, 12 Jul 2012 18:07:04 -0400
\r
37 Date: Thu, 12 Jul 2012 18:07:04 -0400
\r
38 From: Austin Clements <amdragon@MIT.EDU>
\r
40 Subject: Re: [PATCH v4 2/3] Add structured output formatter for JSON.
\r
41 Message-ID: <20120712220703.GI7332@mit.edu>
\r
42 References: <87d34hsdx8.fsf@awakening.csail.mit.edu>
\r
43 <1342079004-5300-1-git-send-email-craven@gmx.net>
\r
44 <1342079004-5300-3-git-send-email-craven@gmx.net>
\r
46 Content-Type: text/plain; charset=us-ascii
\r
47 Content-Disposition: inline
\r
48 In-Reply-To: <1342079004-5300-3-git-send-email-craven@gmx.net>
\r
49 User-Agent: Mutt/1.5.21 (2010-09-15)
\r
50 X-Brightmail-Tracker:
\r
51 H4sIAAAAAAAAA+NgFmplleLIzCtJLcpLzFFi42IRYrdT1+3y+u9v8H82q8XehnZGi+s3ZzI7
\r
52 MHks3rSfzePZqlvMAUxRXDYpqTmZZalF+nYJXBm7G3ewF0zTrzj09xRzA+M+5S5GTg4JAROJ
\r
53 49s+sUDYYhIX7q1n62Lk4hAS2McoseX9GXYIZwOjxMtt/1ghnJNMErP+wDhLGCX+Lv0D1s8i
\r
54 oCpx4c9GMJtNQENi2/7ljCC2iICQxKQvr8DizALSEt9+NzOB2MICrhKtT9+A2bwC2hKLL/yF
\r
55 2j2HUaL1+RtWiISgxMmZT6CatSRu/HsJ1MABNmj5Pw6QMKeAncSdGz3sILaogIrElJPb2CYw
\r
56 Cs1C0j0LSfcshO4FjMyrGGVTcqt0cxMzc4pTk3WLkxPz8lKLdC30cjNL9FJTSjcxgkPbRXUH
\r
57 44RDSocYBTgYlXh4d6776y/EmlhWXJl7iFGSg0lJlNfa9b+/EF9SfkplRmJxRnxRaU5q8SFG
\r
58 CQ5mJRHedfZAOd6UxMqq1KJ8mJQ0B4uSOO+NlJv+QgLpiSWp2ampBalFMFkZDg4lCd4tnkCN
\r
59 gkWp6akVaZk5JQhpJg5OkOE8QMNngdTwFhck5hZnpkPkTzEqSonzzgdJCIAkMkrz4HphqecV
\r
60 ozjQK8K8C0CqeIBpC677FdBgJpDBP/+BDC5JREhJNTBuWqHr58561eXNtkO/rv+vbjIIFJvN
\r
61 3nm1RiDCT6dtwuNlZ9Yp3xMvjhe/9CfIO818+45t/947TI+oDfywMqJX8cakN7P3Se2ebuIz
\r
62 wfrJ9V8MEqVLduX/k/auDZ/xl8/5zZq1dTVvl/qnSa0VLN6uJuDWtjY+z9lmS0LM6WexBXZS
\r
63 a/4f/2ekxFKckWioxVxUnAgAfQM9CRgDAAA=
\r
64 Cc: notmuch@notmuchmail.org
\r
65 X-BeenThere: notmuch@notmuchmail.org
\r
66 X-Mailman-Version: 2.1.13
\r
68 List-Id: "Use and development of the notmuch mail system."
\r
69 <notmuch.notmuchmail.org>
\r
70 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
71 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
72 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
73 List-Post: <mailto:notmuch@notmuchmail.org>
\r
74 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
75 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
76 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
77 X-List-Received-Date: Thu, 12 Jul 2012 22:07:10 -0000
\r
79 Quoth craven@gmx.net on Jul 12 at 9:43 am:
\r
80 > Using the new structured printer support in sprinter.h, implement
\r
81 > sprinter_json_new, which returns a new JSON structured output
\r
84 > The formatter prints output similar to the existing JSON, but with
\r
85 > differences in whitespace (mostly newlines).
\r
87 > Makefile.local | 1 +
\r
88 > sprinter.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
\r
89 > 2 files changed, 173 insertions(+)
\r
90 > create mode 100644 sprinter.c
\r
92 > diff --git a/Makefile.local b/Makefile.local
\r
93 > index a890df2..8baf0c2 100644
\r
94 > --- a/Makefile.local
\r
95 > +++ b/Makefile.local
\r
96 > @@ -290,6 +290,7 @@ notmuch_client_srcs = \
\r
104 > diff --git a/sprinter.c b/sprinter.c
\r
105 > new file mode 100644
\r
106 > index 0000000..649f79a
\r
109 > @@ -0,0 +1,172 @@
\r
110 > +#include <stdbool.h>
\r
111 > +#include <stdio.h>
\r
112 > +#include <talloc.h>
\r
113 > +#include "sprinter.h"
\r
115 > +#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))
\r
117 > +struct sprinter *
\r
118 > +sprinter_text = NULL;
\r
121 > + * Every below here is the implementation of the JSON printer.
\r
123 s/Every/Everything/
\r
125 Or this can be shortened to just
\r
131 If we wind up with separate files for the JSON and S-exp printers, I
\r
132 don't think this comment is necessary at all.
\r
136 > +struct sprinter_json
\r
138 > + struct sprinter vtable;
\r
140 > + /* Top of the state stack, or NULL if the printer is not currently
\r
141 > + * inside any aggregate types. */
\r
142 > + struct json_state *state;
\r
145 > +struct json_state
\r
147 > + struct json_state *parent;
\r
148 > + /* True if nothing has been printed in this aggregate yet.
\r
149 > + * Suppresses the comma before a value. */
\r
150 > + notmuch_bool_t first;
\r
151 > + /* The character that closes the current aggregate. */
\r
155 > +/* Helper function to set up the stream to print a value. If this
\r
156 > + * value follows another value, prints a comma. */
\r
157 > +static struct sprinter_json *
\r
158 > +json_begin_value(struct sprinter *sp)
\r
160 As Tomi pointed out, there should be spaces before the parameter
\r
164 > + struct sprinter_json *spj = (struct sprinter_json*)sp;
\r
165 > + if (spj->state) {
\r
166 > + if (!spj->state->first)
\r
167 > + fputs (", ", spj->stream);
\r
169 > + spj->state->first = false;
\r
174 > +/* Helper function to begin an aggregate type. Prints the open
\r
175 > + * character and pushes a new state frame. */
\r
177 > +json_begin_aggregate(struct sprinter *sp, char open, char close)
\r
179 > + struct sprinter_json *spj = json_begin_value (sp);
\r
180 > + struct json_state *state = talloc (spj, struct json_state);
\r
182 > + fputc (open, spj->stream);
\r
183 > + state->parent = spj->state;
\r
184 > + state->first = true;
\r
185 > + state->close = close;
\r
186 > + spj->state = state;
\r
190 > +json_begin_map(struct sprinter *sp)
\r
192 > + json_begin_aggregate (sp, '{', '}');
\r
196 > +json_begin_list(struct sprinter *sp)
\r
198 > + json_begin_aggregate (sp, '[', ']');
\r
202 > +json_end(struct sprinter *sp)
\r
204 > + struct sprinter_json *spj = (struct sprinter_json*)sp;
\r
205 > + struct json_state *state = spj->state;
\r
207 > + fputc (spj->state->close, spj->stream);
\r
208 > + spj->state = state->parent;
\r
209 > + talloc_free (state);
\r
210 > + if(spj->state == NULL)
\r
212 Ah, another missing space.
\r
214 > + fputc ('\n', spj->stream);
\r
218 > +json_string(struct sprinter *sp, const char *val)
\r
220 > + static const char * const escapes[] = {
\r
221 > + ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",
\r
222 > + ['\f'] = "\\f", ['\n'] = "\\n", ['\t'] = "\\t"
\r
224 > + struct sprinter_json *spj = json_begin_value (sp);
\r
225 > + fputc ('"', spj->stream);
\r
226 > + for (; *val; ++val) {
\r
227 > + unsigned char ch = *val;
\r
228 > + if (ch < ARRAY_SIZE(escapes) && escapes[ch])
\r
229 > + fputs (escapes[ch], spj->stream);
\r
230 > + else if (ch >= 32)
\r
231 > + fputc (ch, spj->stream);
\r
233 > + fprintf (spj->stream, "\\u%04x", ch);
\r
235 > + fputc ('"', spj->stream);
\r
239 > +json_integer(struct sprinter *sp, int val)
\r
241 > + struct sprinter_json *spj = json_begin_value (sp);
\r
242 > + fprintf (spj->stream, "%d", val);
\r
246 > +json_boolean(struct sprinter *sp, notmuch_bool_t val)
\r
248 > + struct sprinter_json *spj = json_begin_value (sp);
\r
249 > + fputs (val ? "true" : "false", spj->stream);
\r
253 > +json_null(struct sprinter *sp)
\r
255 > + struct sprinter_json *spj = json_begin_value (sp);
\r
256 > + fputs ("null", spj->stream);
\r
260 > +json_map_key(struct sprinter *sp, const char *key)
\r
262 > + struct sprinter_json *spj = (struct sprinter_json*)sp;
\r
263 > + json_string (sp, key);
\r
264 > + fputs (": ", spj->stream);
\r
265 > + spj->state->first = true;
\r
269 > +json_frame(struct sprinter *sp)
\r
271 > + struct sprinter_json *spj = (struct sprinter_json*)sp;
\r
272 > + fputc ('\n', spj->stream);
\r
275 > +struct sprinter *
\r
276 > +sprinter_json_new(const void *ctx, FILE *stream)
\r
278 > + static const struct sprinter_json template = {
\r
280 > + .begin_map = json_begin_map,
\r
281 > + .begin_list = json_begin_list,
\r
282 > + .end = json_end,
\r
283 > + .string = json_string,
\r
284 > + .integer = json_integer,
\r
285 > + .boolean = json_boolean,
\r
286 > + .null = json_null,
\r
287 > + .map_key = json_map_key,
\r
288 > + .frame = json_frame,
\r
291 > + struct sprinter_json *res;
\r
293 > + res = talloc (ctx, struct sprinter_json);
\r
297 > + *res = template;
\r
298 > + res->stream = stream;
\r
299 > + return &res->vtable;
\r