[PATCH 4/4] pick: rename test emacs-pick to emacs-tree
[notmuch-archives.git] / cc / c0f01494c3705e321b074495b3e897d7c471dc
1 Return-Path: <craven@gmx.net>\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 6A997431FAE\r
6         for <notmuch@notmuchmail.org>; Thu, 19 Jul 2012 23:34:55 -0700 (PDT)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Spam-Flag: NO\r
9 X-Spam-Score: 0.001\r
10 X-Spam-Level: \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
13         autolearn=disabled\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 853Pk20EdPIS for <notmuch@notmuchmail.org>;\r
17         Thu, 19 Jul 2012 23:34:53 -0700 (PDT)\r
18 Received: from mailout-de.gmx.net (mailout-de.gmx.net [213.165.64.23])\r
19         by olra.theworths.org (Postfix) with SMTP id BC40A431FBF\r
20         for <notmuch@notmuchmail.org>; Thu, 19 Jul 2012 23:34:52 -0700 (PDT)\r
21 Received: (qmail invoked by alias); 20 Jul 2012 06:34:48 -0000\r
22 Received: from gw.arelion.cust.net.lagis.at (EHLO dodekanex.arelion.at)\r
23         [83.164.197.182]\r
24         by mail.gmx.net (mp031) with SMTP; 20 Jul 2012 08:34:48 +0200\r
25 X-Authenticated: #201305\r
26 X-Provags-ID: V01U2FsdGVkX19c4tBshzlc5pkjSUeSZk9QvwGfdexgT0o5CbW8Tt\r
27         /gzBf18aXz14O5\r
28 Received: by dodekanex.arelion.at (Postfix, from userid 1000)\r
29         id EAE4C301BF8; Fri, 20 Jul 2012 08:36:18 +0200 (CEST)\r
30 From: craven@gmx.net\r
31 To: notmuch@notmuchmail.org\r
32 Subject: [PATCH v7 2/3] Add structured output formatter for JSON and plain\r
33         text (but don't use them yet).\r
34 Date: Fri, 20 Jul 2012 08:36:12 +0200\r
35 Message-Id: <1342766173-1344-3-git-send-email-craven@gmx.net>\r
36 X-Mailer: git-send-email 1.7.11.2\r
37 In-Reply-To: <1342766173-1344-1-git-send-email-craven@gmx.net>\r
38 References: <20120718194819.GP31670@mit.edu>\r
39         <1342766173-1344-1-git-send-email-craven@gmx.net>\r
40 X-Y-GMX-Trusted: 0\r
41 X-BeenThere: notmuch@notmuchmail.org\r
42 X-Mailman-Version: 2.1.13\r
43 Precedence: list\r
44 List-Id: "Use and development of the notmuch mail system."\r
45         <notmuch.notmuchmail.org>\r
46 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
47         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
48 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
49 List-Post: <mailto:notmuch@notmuchmail.org>\r
50 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
51 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
52         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
53 X-List-Received-Date: Fri, 20 Jul 2012 06:34:55 -0000\r
54 \r
55 From: <craven@gmx.net>\r
56 \r
57 Using the new structured printer support in sprinter.h, implement\r
58 sprinter_json_create, which returns a new JSON structured output\r
59 formatter. The formatter prints output similar to the existing JSON, but\r
60 with differences in whitespace (mostly newlines, --output=summary prints\r
61 the entire message summary on one line, not split across multiple lines).\r
62 \r
63 Also implement a "structured" formatter for plain text that prints\r
64 prefixed strings, to be used with notmuch-search.c plain text output.\r
65 ---\r
66  Makefile.local  |   2 +\r
67  sprinter-json.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
68  sprinter-text.c | 126 ++++++++++++++++++++++++++++++++++++++\r
69  sprinter.h      |  10 +++\r
70  4 files changed, 323 insertions(+)\r
71  create mode 100644 sprinter-json.c\r
72  create mode 100644 sprinter-text.c\r
73 \r
74 diff --git a/Makefile.local b/Makefile.local\r
75 index a890df2..296995d 100644\r
76 --- a/Makefile.local\r
77 +++ b/Makefile.local\r
78 @@ -290,6 +290,8 @@ notmuch_client_srcs =               \\r
79         notmuch-show.c          \\r
80         notmuch-tag.c           \\r
81         notmuch-time.c          \\r
82 +       sprinter-json.c         \\r
83 +       sprinter-text.c         \\r
84         query-string.c          \\r
85         mime-node.c             \\r
86         crypto.c                \\r
87 diff --git a/sprinter-json.c b/sprinter-json.c\r
88 new file mode 100644\r
89 index 0000000..32daa5a\r
90 --- /dev/null\r
91 +++ b/sprinter-json.c\r
92 @@ -0,0 +1,185 @@\r
93 +#include <stdbool.h>\r
94 +#include <stdio.h>\r
95 +#include <talloc.h>\r
96 +#include "sprinter.h"\r
97 +\r
98 +struct sprinter_json {\r
99 +    struct sprinter vtable;\r
100 +    FILE *stream;\r
101 +    /* Top of the state stack, or NULL if the printer is not currently\r
102 +     * inside any aggregate types. */\r
103 +    struct json_state *state;\r
104 +\r
105 +    /* A flag to signify that a separator should be inserted in the\r
106 +     * output as soon as possible.\r
107 +     */\r
108 +    notmuch_bool_t insert_separator;\r
109 +};\r
110 +\r
111 +struct json_state {\r
112 +    struct json_state *parent;\r
113 +    /* True if nothing has been printed in this aggregate yet.\r
114 +     * Suppresses the comma before a value. */\r
115 +    notmuch_bool_t first;\r
116 +    /* The character that closes the current aggregate. */\r
117 +    char close;\r
118 +};\r
119 +\r
120 +/* Helper function to set up the stream to print a value.  If this\r
121 + * value follows another value, prints a comma. */\r
122 +static struct sprinter_json *\r
123 +json_begin_value (struct sprinter *sp)\r
124 +{\r
125 +    struct sprinter_json *spj = (struct sprinter_json *) sp;\r
126 +\r
127 +    if (spj->state) {\r
128 +       if (! spj->state->first) {\r
129 +           fputc (',', spj->stream);\r
130 +           if (spj->insert_separator) {\r
131 +               fputc ('\n', spj->stream);\r
132 +               spj->insert_separator = FALSE;\r
133 +           } else\r
134 +               fputc (' ', spj->stream);\r
135 +       } else\r
136 +           spj->state->first = FALSE;\r
137 +    }\r
138 +    return spj;\r
139 +}\r
140 +\r
141 +/* Helper function to begin an aggregate type.  Prints the open\r
142 + * character and pushes a new state frame. */\r
143 +static void\r
144 +json_begin_aggregate (struct sprinter *sp, char open, char close)\r
145 +{\r
146 +    struct sprinter_json *spj = json_begin_value (sp);\r
147 +    struct json_state *state = talloc (spj, struct json_state);\r
148 +\r
149 +    fputc (open, spj->stream);\r
150 +    state->parent = spj->state;\r
151 +    state->first = TRUE;\r
152 +    state->close = close;\r
153 +    spj->state = state;\r
154 +}\r
155 +\r
156 +static void\r
157 +json_begin_map (struct sprinter *sp)\r
158 +{\r
159 +    json_begin_aggregate (sp, '{', '}');\r
160 +}\r
161 +\r
162 +static void\r
163 +json_begin_list (struct sprinter *sp)\r
164 +{\r
165 +    json_begin_aggregate (sp, '[', ']');\r
166 +}\r
167 +\r
168 +static void\r
169 +json_end (struct sprinter *sp)\r
170 +{\r
171 +    struct sprinter_json *spj = (struct sprinter_json *) sp;\r
172 +    struct json_state *state = spj->state;\r
173 +\r
174 +    fputc (spj->state->close, spj->stream);\r
175 +    spj->state = state->parent;\r
176 +    talloc_free (state);\r
177 +    if (spj->state == NULL)\r
178 +       fputc ('\n', spj->stream);\r
179 +}\r
180 +\r
181 +static void\r
182 +json_string (struct sprinter *sp, const char *val)\r
183 +{\r
184 +    static const char *const escapes[] = {\r
185 +       ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",\r
186 +       ['\f'] = "\\f",  ['\n'] = "\\n",  ['\t'] = "\\t"\r
187 +    };\r
188 +    struct sprinter_json *spj = json_begin_value (sp);\r
189 +\r
190 +    fputc ('"', spj->stream);\r
191 +    for (; *val; ++val) {\r
192 +       unsigned char ch = *val;\r
193 +       if (ch < ARRAY_SIZE (escapes) && escapes[ch])\r
194 +           fputs (escapes[ch], spj->stream);\r
195 +       else if (ch >= 32)\r
196 +           fputc (ch, spj->stream);\r
197 +       else\r
198 +           fprintf (spj->stream, "\\u%04x", ch);\r
199 +    }\r
200 +    fputc ('"', spj->stream);\r
201 +}\r
202 +\r
203 +static void\r
204 +json_integer (struct sprinter *sp, int val)\r
205 +{\r
206 +    struct sprinter_json *spj = json_begin_value (sp);\r
207 +\r
208 +    fprintf (spj->stream, "%d", val);\r
209 +}\r
210 +\r
211 +static void\r
212 +json_boolean (struct sprinter *sp, notmuch_bool_t val)\r
213 +{\r
214 +    struct sprinter_json *spj = json_begin_value (sp);\r
215 +\r
216 +    fputs (val ? "true" : "false", spj->stream);\r
217 +}\r
218 +\r
219 +static void\r
220 +json_null (struct sprinter *sp)\r
221 +{\r
222 +    struct sprinter_json *spj = json_begin_value (sp);\r
223 +\r
224 +    fputs ("null", spj->stream);\r
225 +}\r
226 +\r
227 +static void\r
228 +json_map_key (struct sprinter *sp, const char *key)\r
229 +{\r
230 +    struct sprinter_json *spj = (struct sprinter_json *) sp;\r
231 +\r
232 +    json_string (sp, key);\r
233 +    fputs (": ", spj->stream);\r
234 +    spj->state->first = TRUE;\r
235 +}\r
236 +\r
237 +static void\r
238 +json_set_prefix (unused (struct sprinter *sp), unused (const char *name))\r
239 +{\r
240 +}\r
241 +\r
242 +static void\r
243 +json_separator (struct sprinter *sp)\r
244 +{\r
245 +    struct sprinter_json *spj = (struct sprinter_json *) sp;\r
246 +\r
247 +    spj->insert_separator = TRUE;\r
248 +}\r
249 +\r
250 +struct sprinter *\r
251 +sprinter_json_create (const void *ctx, FILE *stream)\r
252 +{\r
253 +    static const struct sprinter_json template = {\r
254 +       .vtable = {\r
255 +           .begin_map = json_begin_map,\r
256 +           .begin_list = json_begin_list,\r
257 +           .end = json_end,\r
258 +           .string = json_string,\r
259 +           .integer = json_integer,\r
260 +           .boolean = json_boolean,\r
261 +           .null = json_null,\r
262 +           .map_key = json_map_key,\r
263 +           .separator = json_separator,\r
264 +           .set_prefix = json_set_prefix,\r
265 +           .is_text_printer = FALSE,\r
266 +       }\r
267 +    };\r
268 +    struct sprinter_json *res;\r
269 +\r
270 +    res = talloc (ctx, struct sprinter_json);\r
271 +    if (! res)\r
272 +       return NULL;\r
273 +\r
274 +    *res = template;\r
275 +    res->stream = stream;\r
276 +    return &res->vtable;\r
277 +}\r
278 diff --git a/sprinter-text.c b/sprinter-text.c\r
279 new file mode 100644\r
280 index 0000000..b208840\r
281 --- /dev/null\r
282 +++ b/sprinter-text.c\r
283 @@ -0,0 +1,126 @@\r
284 +#include <stdbool.h>\r
285 +#include <stdio.h>\r
286 +#include <talloc.h>\r
287 +#include "sprinter.h"\r
288 +\r
289 +/* "Structured printer" interface for unstructured text printing.\r
290 + * Note that --output=summary is dispatched and formatted in\r
291 + * notmuch-search.c, the code in this file is only used for all other\r
292 + * output types.\r
293 + */\r
294 +\r
295 +struct sprinter_text {\r
296 +    struct sprinter vtable;\r
297 +    FILE *stream;\r
298 +\r
299 +    /* The current prefix to be printed with string/integer/boolean\r
300 +     * data.\r
301 +     */\r
302 +    const char *current_prefix;\r
303 +\r
304 +    /* A flag to indicate if this is the first tag. Used in list of tags\r
305 +     * for summary.\r
306 +     */\r
307 +    notmuch_bool_t first_tag;\r
308 +};\r
309 +\r
310 +static void\r
311 +text_string (struct sprinter *sp, const char *val)\r
312 +{\r
313 +    struct sprinter_text *sptxt = (struct sprinter_text *) sp;\r
314 +\r
315 +    if (sptxt->current_prefix != NULL)\r
316 +       fprintf (sptxt->stream, "%s:", sptxt->current_prefix);\r
317 +\r
318 +    fputs(val, sptxt->stream);\r
319 +}\r
320 +\r
321 +static void\r
322 +text_integer (struct sprinter *sp, int val)\r
323 +{\r
324 +    struct sprinter_text *sptxt = (struct sprinter_text *) sp;\r
325 +\r
326 +    fprintf (sptxt->stream, "%d", val);\r
327 +}\r
328 +\r
329 +static void\r
330 +text_boolean (struct sprinter *sp, notmuch_bool_t val)\r
331 +{\r
332 +    struct sprinter_text *sptxt = (struct sprinter_text *) sp;\r
333 +\r
334 +    fputs (val ? "true" : "false", sptxt->stream);\r
335 +}\r
336 +\r
337 +static void\r
338 +text_separator (struct sprinter *sp)\r
339 +{\r
340 +    struct sprinter_text *sptxt = (struct sprinter_text *) sp;\r
341 +\r
342 +    fputc ('\n', sptxt->stream);\r
343 +}\r
344 +\r
345 +static void\r
346 +text_set_prefix (struct sprinter *sp, const char *prefix)\r
347 +{\r
348 +    struct sprinter_text *sptxt = (struct sprinter_text *) sp;\r
349 +\r
350 +    sptxt->current_prefix = prefix;\r
351 +}\r
352 +\r
353 +/* The structure functions begin_map, begin_list, end and map_key\r
354 + * don't do anything in the text formatter.\r
355 + */\r
356 +\r
357 +static void\r
358 +text_begin_map (unused (struct sprinter *sp))\r
359 +{\r
360 +}\r
361 +\r
362 +static void\r
363 +text_begin_list (unused (struct sprinter *sp))\r
364 +{\r
365 +}\r
366 +\r
367 +static void\r
368 +text_end (unused (struct sprinter *sp))\r
369 +{\r
370 +}\r
371 +\r
372 +static void\r
373 +text_null (unused (struct sprinter *sp))\r
374 +{\r
375 +}\r
376 +\r
377 +static void\r
378 +text_map_key (unused (struct sprinter *sp), unused (const char *key))\r
379 +{\r
380 +}\r
381 +\r
382 +struct sprinter *\r
383 +sprinter_text_create (const void *ctx, FILE *stream)\r
384 +{\r
385 +    static const struct sprinter_text template = {\r
386 +       .vtable = {\r
387 +           .begin_map = text_begin_map,\r
388 +           .begin_list = text_begin_list,\r
389 +           .end = text_end,\r
390 +           .string = text_string,\r
391 +           .integer = text_integer,\r
392 +           .boolean = text_boolean,\r
393 +           .null = text_null,\r
394 +           .map_key = text_map_key,\r
395 +           .separator = text_separator,\r
396 +           .set_prefix = text_set_prefix,\r
397 +           .is_text_printer = TRUE,\r
398 +       },\r
399 +    };\r
400 +    struct sprinter_text *res;\r
401 +\r
402 +    res = talloc (ctx, struct sprinter_text);\r
403 +    if (! res)\r
404 +       return NULL;\r
405 +\r
406 +    *res = template;\r
407 +    res->stream = stream;\r
408 +    return &res->vtable;\r
409 +}\r
410 diff --git a/sprinter.h b/sprinter.h\r
411 index 77dc26f..6680d41 100644\r
412 --- a/sprinter.h\r
413 +++ b/sprinter.h\r
414 @@ -55,4 +55,14 @@ typedef struct sprinter {\r
415      notmuch_bool_t is_text_printer;\r
416  } sprinter_t;\r
417  \r
418 +\r
419 +/* Create a new unstructured printer that emits the default text format\r
420 + * for "notmuch search". */\r
421 +struct sprinter *\r
422 +sprinter_text_create (const void *ctx, FILE *stream);\r
423 +\r
424 +/* Create a new structure printer that emits JSON. */\r
425 +struct sprinter *\r
426 +sprinter_json_create (const void *ctx, FILE *stream);\r
427 +\r
428  #endif // NOTMUCH_SPRINTER_H\r
429 -- \r
430 1.7.11.2\r
431 \r