Re: [PATCH] emacs: wash: make word-wrap bound message width
[notmuch-archives.git] / 61 / 47f6de49c879b9fca064015df5aeb67de23ace
1 Return-Path: <nex@nexoid.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 DD1C5429E2F\r
6         for <notmuch@notmuchmail.org>; Wed,  5 Dec 2012 23:33:28 -0800 (PST)\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org\r
8 X-Amavis-Alert: BAD HEADER SECTION, Duplicate header field: "References"\r
9 X-Spam-Flag: NO\r
10 X-Spam-Score: 0.001\r
11 X-Spam-Level: \r
12 X-Spam-Status: No, score=0.001 tagged_above=-999 required=5\r
13         tests=[FREEMAIL_FROM=0.001] 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 jxEwh6i0UIeA for <notmuch@notmuchmail.org>;\r
17         Wed,  5 Dec 2012 23:33:24 -0800 (PST)\r
18 Received: from mail.nexoid.at (www.nexoid.at [178.79.130.240])\r
19         (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))\r
20         (No client certificate requested)\r
21         by olra.theworths.org (Postfix) with ESMTPS id 11484431FC4\r
22         for <notmuch@notmuchmail.org>; Wed,  5 Dec 2012 23:33:19 -0800 (PST)\r
23 Received: by mail.nexoid.at (Postfix, from userid 1000)\r
24         id A1D3511C10B; Thu,  6 Dec 2012 08:33:12 +0100 (CET)\r
25 From: Peter Feigl <craven@gmx.net>\r
26 To: notmuch@notmuchmail.org\r
27 Subject: [PATCH v3 1/5] Adding an S-expression structured output printer.\r
28 Date: Thu,  6 Dec 2012 08:33:05 +0100\r
29 Message-Id: <1354779189-12231-2-git-send-email-craven@gmx.net>\r
30 X-Mailer: git-send-email 1.8.0\r
31 In-Reply-To: <1354779189-12231-1-git-send-email-craven@gmx.net>\r
32 References: <1354779189-12231-1-git-send-email-craven@gmx.net>\r
33 In-Reply-To: <1354632382-15609-1-git-send-email-craven@gmx.net>\r
34 References: <1354632382-15609-1-git-send-email-craven@gmx.net>\r
35 MIME-Version: 1.0\r
36 Content-Type: text/plain; charset=UTF-8\r
37 Content-Transfer-Encoding: 8bit\r
38 X-BeenThere: notmuch@notmuchmail.org\r
39 X-Mailman-Version: 2.1.13\r
40 Precedence: list\r
41 List-Id: "Use and development of the notmuch mail system."\r
42         <notmuch.notmuchmail.org>\r
43 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
44         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
45 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
46 List-Post: <mailto:notmuch@notmuchmail.org>\r
47 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
48 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
49         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
50 X-List-Received-Date: Thu, 06 Dec 2012 07:33:29 -0000\r
51 \r
52 This commit adds a structured output printer for Lisp\r
53 S-Expressions. Later commits will use this printer in notmuch search,\r
54 show and reply.\r
55 \r
56 The structure is the same as json, but:\r
57 - arrays are written as lists: ("foo" "bar" "baaz" 1 2 3)\r
58 - maps are written as p-lists: (:key "value" :other-key "other-value")\r
59 - true is written as t\r
60 - false is written as nil\r
61 - null is written as nil\r
62 ---\r
63  Makefile.local  |   1 +\r
64  sprinter-sexp.c | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
65  sprinter.h      |   4 +\r
66  3 files changed, 243 insertions(+)\r
67  create mode 100644 sprinter-sexp.c\r
68 \r
69 diff --git a/Makefile.local b/Makefile.local\r
70 index 2b91946..0db1713 100644\r
71 --- a/Makefile.local\r
72 +++ b/Makefile.local\r
73 @@ -270,6 +270,7 @@ notmuch_client_srcs =               \\r
74         notmuch-tag.c           \\r
75         notmuch-time.c          \\r
76         sprinter-json.c         \\r
77 +       sprinter-sexp.c         \\r
78         sprinter-text.c         \\r
79         query-string.c          \\r
80         mime-node.c             \\r
81 diff --git a/sprinter-sexp.c b/sprinter-sexp.c\r
82 new file mode 100644\r
83 index 0000000..8f84eed\r
84 --- /dev/null\r
85 +++ b/sprinter-sexp.c\r
86 @@ -0,0 +1,238 @@\r
87 +/* notmuch - Not much of an email program, (just index and search)\r
88 + *\r
89 + * Copyright © 2012 Peter Feigl\r
90 + *\r
91 + * This program is free software: you can redistribute it and/or modify\r
92 + * it under the terms of the GNU General Public License as published by\r
93 + * the Free Software Foundation, either version 3 of the License, or\r
94 + * (at your option) any later version.\r
95 + *\r
96 + * This program is distributed in the hope that it will be useful,\r
97 + * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
98 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
99 + * GNU General Public License for more details.\r
100 + *\r
101 + * You should have received a copy of the GNU General Public License\r
102 + * along with this program.  If not, see http://www.gnu.org/licenses/ .\r
103 + *\r
104 + * Author: Peter Feigl <peter.feigl@gmx.at>\r
105 + */\r
106 +\r
107 +#include <stdbool.h>\r
108 +#include <stdio.h>\r
109 +#include <talloc.h>\r
110 +#include "sprinter.h"\r
111 +\r
112 +struct sprinter_sexp {\r
113 +    struct sprinter vtable;\r
114 +    FILE *stream;\r
115 +    /* Top of the state stack, or NULL if the printer is not currently\r
116 +     * inside any aggregate types. */\r
117 +    struct sexp_state *state;\r
118 +\r
119 +    /* A flag to signify that a separator should be inserted in the\r
120 +     * output as soon as possible. */\r
121 +    notmuch_bool_t insert_separator;\r
122 +};\r
123 +\r
124 +struct sexp_state {\r
125 +    struct sexp_state *parent;\r
126 +\r
127 +    /* True if nothing has been printed in this aggregate yet.\r
128 +     * Suppresses the space before a value. */\r
129 +    notmuch_bool_t first;\r
130 +};\r
131 +\r
132 +/* Helper function to set up the stream to print a value.  If this\r
133 + * value follows another value, prints a space. */\r
134 +static struct sprinter_sexp *\r
135 +sexp_begin_value (struct sprinter *sp)\r
136 +{\r
137 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
138 +\r
139 +    if (sps->state) {\r
140 +       if (! sps->state->first) {\r
141 +           if (sps->insert_separator) {\r
142 +               fputc ('\n', sps->stream);\r
143 +               sps->insert_separator = FALSE;\r
144 +           } else {\r
145 +               fputc (' ', sps->stream);\r
146 +           }\r
147 +       } else {\r
148 +           sps->state->first = FALSE;\r
149 +       }\r
150 +    }\r
151 +    return sps;\r
152 +}\r
153 +\r
154 +/* Helper function to begin an aggregate type.  Prints the open\r
155 + * character and pushes a new state frame. */\r
156 +static void\r
157 +sexp_begin_aggregate (struct sprinter *sp)\r
158 +{\r
159 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
160 +    struct sexp_state *state = talloc (sps, struct sexp_state);\r
161 +    fputc ('(', sps->stream);\r
162 +    state->parent = sps->state;\r
163 +    state->first = TRUE;\r
164 +    sps->state = state;\r
165 +}\r
166 +\r
167 +static void\r
168 +sexp_begin_map (struct sprinter *sp)\r
169 +{\r
170 +    sexp_begin_aggregate (sp);\r
171 +}\r
172 +\r
173 +static void\r
174 +sexp_begin_list (struct sprinter *sp)\r
175 +{\r
176 +    sexp_begin_aggregate (sp);\r
177 +}\r
178 +\r
179 +static void\r
180 +sexp_end (struct sprinter *sp)\r
181 +{\r
182 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
183 +    struct sexp_state *state = sps->state;\r
184 +\r
185 +    fputc (')', sps->stream);\r
186 +    sps->state = state->parent;\r
187 +    talloc_free (state);\r
188 +    if (sps->state == NULL)\r
189 +       fputc ('\n', sps->stream);\r
190 +}\r
191 +\r
192 +static void\r
193 +sexp_string_len (struct sprinter *sp, const char *val, size_t len)\r
194 +{\r
195 +    /* Some characters need escaping. " and \ work fine in all Lisps,\r
196 +     * \n is not supported in CL, but all others work fine.\r
197 +     * Characters below 32 are printed as \123o (three-digit \r
198 +     * octals), which work fine in most Schemes and Emacs. */\r
199 +    static const char *const escapes[] = {\r
200 +       ['\"'] = "\\\"", ['\\'] = "\\\\",  ['\n'] = "\\n"\r
201 +    };\r
202 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
203 +\r
204 +    fputc ('"', sps->stream);\r
205 +    for (; len; ++val, --len) {\r
206 +       unsigned char ch = *val;\r
207 +       if (ch < ARRAY_SIZE (escapes) && escapes[ch])\r
208 +           fputs (escapes[ch], sps->stream);\r
209 +       else if (ch >= 32)\r
210 +           fputc (ch, sps->stream);\r
211 +       else\r
212 +           fprintf (sps->stream, "\\%03oo", ch);\r
213 +    }\r
214 +    fputc ('"', sps->stream);\r
215 +}\r
216 +\r
217 +static void\r
218 +sexp_string (struct sprinter *sp, const char *val)\r
219 +{\r
220 +    if (val == NULL)\r
221 +       val = "";\r
222 +    sexp_string_len (sp, val, strlen (val));\r
223 +}\r
224 +\r
225 +/* Prints a symbol, i.e. the name preceded by a colon. This should work\r
226 + * in all Lisps, at least as a symbol, if not as a proper keyword */\r
227 +static void\r
228 +sexp_symbol (struct sprinter *sp, const char *val)\r
229 +{\r
230 +    static const char illegal_characters[] = {\r
231 +       ' ', '\t', '\n'\r
232 +    };\r
233 +    unsigned int i = 0;\r
234 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
235 +\r
236 +    if (val == NULL)\r
237 +       INTERNAL_ERROR ("illegal symbol NULL");\r
238 +\r
239 +    for(i = 0; i < ARRAY_SIZE (illegal_characters); i++) {\r
240 +       if(strchr(val, illegal_characters[i]) != NULL) {\r
241 +           INTERNAL_ERROR ("illegal character in symbol %s", val);\r
242 +       }\r
243 +    }\r
244 +    fputc (':', sps->stream);\r
245 +    fputs (val, sps->stream);\r
246 +}\r
247 +\r
248 +static void\r
249 +sexp_integer (struct sprinter *sp, int val)\r
250 +{\r
251 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
252 +\r
253 +    fprintf (sps->stream, "%d", val);\r
254 +}\r
255 +\r
256 +static void\r
257 +sexp_boolean (struct sprinter *sp, notmuch_bool_t val)\r
258 +{\r
259 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
260 +\r
261 +    fputs (val ? "t" : "nil", sps->stream);\r
262 +}\r
263 +\r
264 +static void\r
265 +sexp_null (struct sprinter *sp)\r
266 +{\r
267 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
268 +\r
269 +    fputs ("nil", sps->stream);\r
270 +}\r
271 +\r
272 +static void\r
273 +sexp_map_key (struct sprinter *sp, const char *key)\r
274 +{\r
275 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
276 +    if (sps->state && ! sps->state->first)\r
277 +       fputc (' ', sps->stream);\r
278 +\r
279 +    sps->state->first = FALSE;\r
280 +    sexp_symbol (sp, key);\r
281 +}\r
282 +\r
283 +static void\r
284 +sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))\r
285 +{\r
286 +}\r
287 +\r
288 +static void\r
289 +sexp_separator (struct sprinter *sp)\r
290 +{\r
291 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
292 +\r
293 +    sps->insert_separator = TRUE;\r
294 +}\r
295 +\r
296 +struct sprinter *\r
297 +sprinter_sexp_create (const void *ctx, FILE *stream)\r
298 +{\r
299 +    static const struct sprinter_sexp template = {\r
300 +       .vtable = {\r
301 +           .begin_map = sexp_begin_map,\r
302 +           .begin_list = sexp_begin_list,\r
303 +           .end = sexp_end,\r
304 +           .string = sexp_string,\r
305 +           .string_len = sexp_string_len,\r
306 +           .integer = sexp_integer,\r
307 +           .boolean = sexp_boolean,\r
308 +           .null = sexp_null,\r
309 +           .map_key = sexp_map_key,\r
310 +           .separator = sexp_separator,\r
311 +           .set_prefix = sexp_set_prefix,\r
312 +           .is_text_printer = FALSE,\r
313 +       }\r
314 +    };\r
315 +    struct sprinter_sexp *res;\r
316 +\r
317 +    res = talloc (ctx, struct sprinter_sexp);\r
318 +    if (! res)\r
319 +       return NULL;\r
320 +\r
321 +    *res = template;\r
322 +    res->stream = stream;\r
323 +    return &res->vtable;\r
324 +}\r
325 diff --git a/sprinter.h b/sprinter.h\r
326 index 912a526..59776a9 100644\r
327 --- a/sprinter.h\r
328 +++ b/sprinter.h\r
329 @@ -70,4 +70,8 @@ sprinter_text_create (const void *ctx, FILE *stream);\r
330  struct sprinter *\r
331  sprinter_json_create (const void *ctx, FILE *stream);\r
332  \r
333 +/* Create a new structure printer that emits S-Expressions. */\r
334 +struct sprinter *\r
335 +sprinter_sexp_create (const void *ctx, FILE *stream);\r
336 +\r
337  #endif // NOTMUCH_SPRINTER_H\r
338 -- \r
339 1.8.0\r
340 \r