database error
[notmuch-archives.git] / 44 / ef7ef5cfff31d73e894ec436b1ccc9d4f75e5e
1 Return-Path: <jani@nikula.org>\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 B8226431FAF\r
6         for <notmuch@notmuchmail.org>; Fri, 30 Nov 2012 11:11:53 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: -0.7\r
10 X-Spam-Level: \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 SM37fGwOVq7p for <notmuch@notmuchmail.org>;\r
16         Fri, 30 Nov 2012 11:11:49 -0800 (PST)\r
17 Received: from mail-la0-f53.google.com (mail-la0-f53.google.com\r
18         [209.85.215.53]) (using TLSv1 with cipher RC4-SHA (128/128 bits))\r
19         (No client certificate requested)\r
20         by olra.theworths.org (Postfix) with ESMTPS id 422F9431FAE\r
21         for <notmuch@notmuchmail.org>; Fri, 30 Nov 2012 11:11:49 -0800 (PST)\r
22 Received: by mail-la0-f53.google.com with SMTP id w12so675603lag.26\r
23         for <notmuch@notmuchmail.org>; Fri, 30 Nov 2012 11:11:47 -0800 (PST)\r
24 X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;\r
25         d=google.com; s=20120113;\r
26         h=from:to:subject:in-reply-to:references:user-agent:date:message-id\r
27         :mime-version:content-type:x-gm-message-state;\r
28         bh=hGsHK8uZZiXqFWd/4WRNvd2BcsyaLP4JF8XC2w4HvxQ=;\r
29         b=ODcjzpYKy1uCjFalG2F4OCPz6MfPruAmMphsBu8v0irBPExHsNd/838ULqW6POWh75\r
30         PvsbcU6Y2NiARxNHKEZ+2VRYIR2qMTjgx9vWugJQg7Pucq6YF+A/zcygX5xPNku0icUY\r
31         dBFhJdKROv4EBYxAxqJhoZ59dMgUh2/ZNpU4BbnXvqdnmiYHK58TFS2fjIV35ydY2o2y\r
32         iph8iwZI+qWPPuaCKT2SqqQh1opt55y+3XtL8ijiWwfW1psd0sb7dQWxQAtwsZ4sGzpr\r
33         qLoXSLqAnYOM2IZmXxf3WrMs2sm3eVizE16c38vt0nTB5H1FDxJB1U/MM656SDG/6jDJ\r
34         f2+A==\r
35 Received: by 10.152.144.201 with SMTP id so9mr2300540lab.24.1354302706506;\r
36         Fri, 30 Nov 2012 11:11:46 -0800 (PST)\r
37 Received: from localhost (dsl-hkibrasgw4-fe51df00-27.dhcp.inet.fi.\r
38         [80.223.81.27])\r
39         by mx.google.com with ESMTPS id d5sm2407084lbk.10.2012.11.30.11.11.44\r
40         (version=SSLv3 cipher=OTHER); Fri, 30 Nov 2012 11:11:45 -0800 (PST)\r
41 From: Jani Nikula <jani@nikula.org>\r
42 To: Peter Feigl <craven@gmx.net>, notmuch@notmuchmail.org\r
43 Subject: Re: [PATCH 1/3] Adding an S-expression structured output printer.\r
44 In-Reply-To: <1354264143-30173-1-git-send-email-craven@gmx.net>\r
45 References: <1354264143-30173-1-git-send-email-craven@gmx.net>\r
46 User-Agent: Notmuch/0.14+124~g3b17402 (http://notmuchmail.org) Emacs/23.4.1\r
47         (i686-pc-linux-gnu)\r
48 Date: Fri, 30 Nov 2012 21:11:43 +0200\r
49 Message-ID: <87zk1yj47k.fsf@nikula.org>\r
50 MIME-Version: 1.0\r
51 Content-Type: text/plain; charset=us-ascii\r
52 X-Gm-Message-State:\r
53  ALoCoQls9cfnR8npdFLCu7SkQ0yxCJKnCs/Tzsq5lrhYgTefYmqDQp6UCduRK3jAEcVJjdN11OW5\r
54 X-BeenThere: notmuch@notmuchmail.org\r
55 X-Mailman-Version: 2.1.13\r
56 Precedence: list\r
57 List-Id: "Use and development of the notmuch mail system."\r
58         <notmuch.notmuchmail.org>\r
59 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
60         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
61 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
62 List-Post: <mailto:notmuch@notmuchmail.org>\r
63 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
64 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
65         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
66 X-List-Received-Date: Fri, 30 Nov 2012 19:11:53 -0000\r
67 \r
68 On Fri, 30 Nov 2012, Peter Feigl <craven@gmx.net> wrote:\r
69 > This commit adds an sprinter for Lisp S-Expressions. Later commits will\r
70 > use this printer.\r
71 \r
72 Overall the series looks good, apart from the small trivial\r
73 nitpicks. We'll also need tests and man pages for the cli command\r
74 changes. IMO small smoke tests are a good start and better than\r
75 nothing. This is especially important as the sexp format is not used by\r
76 other components yet. Man pages will be mostly copy paste from the json\r
77 sections.\r
78 \r
79 BR,\r
80 Jani.\r
81 \r
82 >\r
83 > The structure is the same as json, but:\r
84 > - arrays are written as lists: ("foo" "bar" "baaz" 1 2 3)\r
85 > - maps are written as a-lists: ((key "value") (other-key "other-value"))\r
86 > - true is written as t\r
87 > - false is written as nil\r
88 > - null is written as nil\r
89 > ---\r
90 >  Makefile.local  |   1 +\r
91 >  sprinter-sexp.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
92 >  2 files changed, 236 insertions(+)\r
93 >  create mode 100644 sprinter-sexp.c\r
94 >\r
95 > diff --git a/Makefile.local b/Makefile.local\r
96 > index 2b91946..0db1713 100644\r
97 > --- a/Makefile.local\r
98 > +++ b/Makefile.local\r
99 > @@ -270,6 +270,7 @@ notmuch_client_srcs =             \\r
100 >       notmuch-tag.c           \\r
101 >       notmuch-time.c          \\r
102 >       sprinter-json.c         \\r
103 > +     sprinter-sexp.c         \\r
104 >       sprinter-text.c         \\r
105 >       query-string.c          \\r
106 >       mime-node.c             \\r
107 > diff --git a/sprinter-sexp.c b/sprinter-sexp.c\r
108 > new file mode 100644\r
109 > index 0000000..8401c52\r
110 > --- /dev/null\r
111 > +++ b/sprinter-sexp.c\r
112 > @@ -0,0 +1,235 @@\r
113 > +#include <stdbool.h>\r
114 > +#include <stdio.h>\r
115 > +#include <talloc.h>\r
116 > +#include "sprinter.h"\r
117 > +\r
118 > +struct sprinter_sexp {\r
119 > +    struct sprinter vtable;\r
120 > +    FILE *stream;\r
121 > +    /* Top of the state stack, or NULL if the printer is not currently\r
122 > +     * inside any aggregate types. */\r
123 > +    struct sexp_state *state;\r
124 > +\r
125 > +    /* A flag to signify that a separator should be inserted in the\r
126 > +     * output as soon as possible.\r
127 > +     */\r
128 > +    notmuch_bool_t insert_separator;\r
129 > +};\r
130 > +\r
131 > +struct sexp_state {\r
132 > +    struct sexp_state *parent;\r
133 > +\r
134 > +    /* True if nothing has been printed in this aggregate yet.\r
135 > +     * Suppresses the space before a value. */\r
136 > +    notmuch_bool_t first;\r
137 > +\r
138 > +    /* True if the state is a map state.\r
139 > +       Used to add a space between key/value pairs. */\r
140 > +    notmuch_bool_t in_map;\r
141 > +\r
142 > +    /* The character that closes the current aggregate. */\r
143 > +    char close;\r
144 > +};\r
145 > +\r
146 > +/* Helper function to set up the stream to print a value.  If this\r
147 > + * value follows another value, prints a space. */\r
148 > +static struct sprinter_sexp *\r
149 > +sexp_begin_value (struct sprinter *sp)\r
150 > +{\r
151 > +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
152 > +\r
153 > +    if (sps->state) {\r
154 > +        if (! sps->state->first) {\r
155 > +            if (sps->insert_separator) {\r
156 > +                fputc ('\n', sps->stream);\r
157 > +                sps->insert_separator = FALSE;\r
158 > +            } else {\r
159 > +                if( ! sps->state->in_map)\r
160 > +                    fputc (' ', sps->stream);\r
161 > +            }\r
162 > +        } else {\r
163 > +            sps->state->first = FALSE;\r
164 > +        }\r
165 > +    }\r
166 > +    return sps;\r
167 > +}\r
168 > +\r
169 > +/* Helper function to begin an aggregate type.  Prints the open\r
170 > + * character and pushes a new state frame. */\r
171 > +static void\r
172 > +sexp_begin_aggregate (struct sprinter *sp, char open, char close)\r
173 > +{\r
174 > +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
175 > +    struct sexp_state *state = talloc (sps, struct sexp_state);\r
176 > +    fputc (open, sps->stream);\r
177 > +    state->parent = sps->state;\r
178 > +    state->first = TRUE;\r
179 > +    state->in_map = FALSE;\r
180 > +    state->close = close;\r
181 > +    sps->state = state;\r
182 > +}\r
183 > +\r
184 > +static void\r
185 > +sexp_begin_map (struct sprinter *sp)\r
186 > +{\r
187 > +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
188 > +    sexp_begin_aggregate (sp, '(', ')');\r
189 > +    sps->state->in_map = TRUE;\r
190 > +}\r
191 > +\r
192 > +static void\r
193 > +sexp_begin_list (struct sprinter *sp)\r
194 > +{\r
195 > +    sexp_begin_aggregate (sp, '(', ')');\r
196 > +}\r
197 > +\r
198 > +static void\r
199 > +sexp_end (struct sprinter *sp)\r
200 > +{\r
201 > +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
202 > +    struct sexp_state *state = sps->state;\r
203 > +\r
204 > +    if (sps->state->in_map)\r
205 > +        fputc (')', sps->stream);\r
206 > +    fputc (sps->state->close, sps->stream);\r
207 > +    sps->state = state->parent;\r
208 > +    talloc_free (state);\r
209 > +    if (sps->state == NULL)\r
210 > +        fputc ('\n', sps->stream);\r
211 > +}\r
212 > +\r
213 > +/* This implementation supports embedded NULs as allowed by the JSON\r
214 > + * specification and Unicode.  Support for *parsing* embedded NULs\r
215 > + * varies, but is generally not a problem outside of C-based parsers\r
216 > + * (Python's json module and Emacs' json.el take embedded NULs in\r
217 > + * stride). */\r
218 > +static void\r
219 > +sexp_string_len_internal (struct sprinter *sp, const char *val, size_t len, notmuch_bool_t quote)\r
220 > +{\r
221 > +    static const char *const escapes[] = {\r
222 > +        ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",\r
223 > +        ['\f'] = "\\f",  ['\n'] = "\\n",  ['\t'] = "\\t"\r
224 > +    };\r
225 > +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
226 > +\r
227 > +    if(quote)\r
228 > +        fputc ('"', sps->stream);\r
229 > +    for (; len; ++val, --len) {\r
230 > +        unsigned char ch = *val;\r
231 > +        if (ch < ARRAY_SIZE (escapes) && escapes[ch])\r
232 > +            fputs (escapes[ch], sps->stream);\r
233 > +        else if (ch >= 32)\r
234 > +            fputc (ch, sps->stream);\r
235 > +        else\r
236 > +            fprintf (sps->stream, "\\u%04x", ch);\r
237 > +    }\r
238 > +    if(quote)\r
239 > +        fputc ('"', sps->stream);\r
240 > +}\r
241 > +\r
242 > +static void\r
243 > +sexp_string_len (struct sprinter *sp, const char *val, size_t len)\r
244 > +{\r
245 > +    sexp_string_len_internal (sp, val, len, TRUE); /* print quoted */\r
246 > +}\r
247 > +\r
248 > +static void\r
249 > +sexp_symbol_len (struct sprinter *sp, const char *val, size_t len)\r
250 > +{\r
251 > +    sexp_string_len_internal (sp, val, len, FALSE); /* print unquoted */\r
252 > +}\r
253 > +\r
254 > +static void\r
255 > +sexp_string (struct sprinter *sp, const char *val)\r
256 > +{\r
257 > +    if (val == NULL)\r
258 > +        val = "";\r
259 > +    sexp_string_len (sp, val, strlen (val));\r
260 > +}\r
261 > +\r
262 > +static void\r
263 > +sexp_symbol (struct sprinter *sp, const char *val)\r
264 > +{\r
265 > +    if (val == NULL)\r
266 > +        val = "";\r
267 > +    sexp_symbol_len (sp, val, strlen (val));\r
268 > +}\r
269 > +\r
270 > +static void\r
271 > +sexp_integer (struct sprinter *sp, int val)\r
272 > +{\r
273 > +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
274 > +\r
275 > +    fprintf (sps->stream, "%d", val);\r
276 > +}\r
277 > +\r
278 > +static void\r
279 > +sexp_boolean (struct sprinter *sp, notmuch_bool_t val)\r
280 > +{\r
281 > +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
282 > +\r
283 > +    fputs (val ? "t" : "nil", sps->stream);\r
284 > +}\r
285 > +\r
286 > +static void\r
287 > +sexp_null (struct sprinter *sp)\r
288 > +{\r
289 > +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
290 > +\r
291 > +    fputs ("nil", sps->stream);\r
292 > +}\r
293 > +\r
294 > +static void\r
295 > +sexp_map_key (struct sprinter *sp, const char *key)\r
296 > +{\r
297 > +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
298 > +\r
299 > +    if( sps->state->in_map && ! sps->state->first)\r
300 > +        fputs (") ", sps->stream);\r
301 > +    fputc ('(', sps->stream);\r
302 > +    sexp_symbol (sp, key);\r
303 > +    fputc (' ', sps->stream);\r
304 > +}\r
305 > +\r
306 > +static void\r
307 > +sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))\r
308 > +{\r
309 > +}\r
310 > +\r
311 > +static void\r
312 > +sexp_separator (struct sprinter *sp)\r
313 > +{\r
314 > +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
315 > +\r
316 > +    sps->insert_separator = TRUE;\r
317 > +}\r
318 > +\r
319 > +struct sprinter *\r
320 > +sprinter_sexp_create (const void *ctx, FILE *stream)\r
321 > +{\r
322 > +    static const struct sprinter_sexp template = {\r
323 > +        .vtable = {\r
324 > +            .begin_map = sexp_begin_map,\r
325 > +            .begin_list = sexp_begin_list,\r
326 > +            .end = sexp_end,\r
327 > +            .string = sexp_string,\r
328 > +            .string_len = sexp_string_len,\r
329 > +            .integer = sexp_integer,\r
330 > +            .boolean = sexp_boolean,\r
331 > +            .null = sexp_null,\r
332 > +            .map_key = sexp_map_key,\r
333 > +            .separator = sexp_separator,\r
334 > +            .set_prefix = sexp_set_prefix,\r
335 > +            .is_text_printer = FALSE,\r
336 > +        }\r
337 > +    };\r
338 > +    struct sprinter_sexp *res;\r
339 > +\r
340 > +    res = talloc (ctx, struct sprinter_sexp);\r
341 > +    if (! res)\r
342 > +        return NULL;\r
343 > +\r
344 > +    *res = template;\r
345 > +    res->stream = stream;\r
346 > +    return &res->vtable;\r
347 > +}\r
348 > -- \r
349 > 1.8.0\r
350 >\r
351 > _______________________________________________\r
352 > notmuch mailing list\r
353 > notmuch@notmuchmail.org\r
354 > http://notmuchmail.org/mailman/listinfo/notmuch\r