Re: [PATCH 0/4] Allow specifying alternate names for addresses in other_email
[notmuch-archives.git] / 75 / d7da3b8b7480835a9afd4cd3dd70a2beb2ac87
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 363B3431FD8\r
6         for <notmuch@notmuchmail.org>; Fri, 30 Nov 2012 00:35:13 -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.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] 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 XK83fmPPnAg1 for <notmuch@notmuchmail.org>;\r
16         Fri, 30 Nov 2012 00:35:11 -0800 (PST)\r
17 X-Greylist: delayed 352 seconds by postgrey-1.32 at olra;\r
18         Fri, 30 Nov 2012 00:35:10 PST\r
19 Received: from mail.nexoid.at (www.nexoid.at [178.79.130.240])\r
20         (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits))\r
21         (No client certificate requested)\r
22         by olra.theworths.org (Postfix) with ESMTPS id F0DF1431FAF\r
23         for <notmuch@notmuchmail.org>; Fri, 30 Nov 2012 00:35:10 -0800 (PST)\r
24 Received: by mail.nexoid.at (Postfix, from userid 1000)\r
25         id E8D2B11C0D0; Fri, 30 Nov 2012 09:29:16 +0100 (CET)\r
26 From: Peter Feigl <craven@gmx.net>\r
27 To: notmuch@notmuchmail.org\r
28 Subject: [PATCH 1/3] Adding an S-expression structured output printer.\r
29 Date: Fri, 30 Nov 2012 09:29:01 +0100\r
30 Message-Id: <1354264143-30173-1-git-send-email-craven@gmx.net>\r
31 X-Mailer: git-send-email 1.8.0\r
32 X-BeenThere: notmuch@notmuchmail.org\r
33 X-Mailman-Version: 2.1.13\r
34 Precedence: list\r
35 List-Id: "Use and development of the notmuch mail system."\r
36         <notmuch.notmuchmail.org>\r
37 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
38         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
39 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
40 List-Post: <mailto:notmuch@notmuchmail.org>\r
41 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
42 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
43         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
44 X-List-Received-Date: Fri, 30 Nov 2012 08:35:13 -0000\r
45 \r
46 This commit adds an sprinter for Lisp S-Expressions. Later commits will\r
47 use this printer.\r
48 \r
49 The structure is the same as json, but:\r
50 - arrays are written as lists: ("foo" "bar" "baaz" 1 2 3)\r
51 - maps are written as a-lists: ((key "value") (other-key "other-value"))\r
52 - true is written as t\r
53 - false is written as nil\r
54 - null is written as nil\r
55 ---\r
56  Makefile.local  |   1 +\r
57  sprinter-sexp.c | 235 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
58  2 files changed, 236 insertions(+)\r
59  create mode 100644 sprinter-sexp.c\r
60 \r
61 diff --git a/Makefile.local b/Makefile.local\r
62 index 2b91946..0db1713 100644\r
63 --- a/Makefile.local\r
64 +++ b/Makefile.local\r
65 @@ -270,6 +270,7 @@ notmuch_client_srcs =               \\r
66         notmuch-tag.c           \\r
67         notmuch-time.c          \\r
68         sprinter-json.c         \\r
69 +       sprinter-sexp.c         \\r
70         sprinter-text.c         \\r
71         query-string.c          \\r
72         mime-node.c             \\r
73 diff --git a/sprinter-sexp.c b/sprinter-sexp.c\r
74 new file mode 100644\r
75 index 0000000..8401c52\r
76 --- /dev/null\r
77 +++ b/sprinter-sexp.c\r
78 @@ -0,0 +1,235 @@\r
79 +#include <stdbool.h>\r
80 +#include <stdio.h>\r
81 +#include <talloc.h>\r
82 +#include "sprinter.h"\r
83 +\r
84 +struct sprinter_sexp {\r
85 +    struct sprinter vtable;\r
86 +    FILE *stream;\r
87 +    /* Top of the state stack, or NULL if the printer is not currently\r
88 +     * inside any aggregate types. */\r
89 +    struct sexp_state *state;\r
90 +\r
91 +    /* A flag to signify that a separator should be inserted in the\r
92 +     * output as soon as possible.\r
93 +     */\r
94 +    notmuch_bool_t insert_separator;\r
95 +};\r
96 +\r
97 +struct sexp_state {\r
98 +    struct sexp_state *parent;\r
99 +\r
100 +    /* True if nothing has been printed in this aggregate yet.\r
101 +     * Suppresses the space before a value. */\r
102 +    notmuch_bool_t first;\r
103 +\r
104 +    /* True if the state is a map state.\r
105 +       Used to add a space between key/value pairs. */\r
106 +    notmuch_bool_t in_map;\r
107 +\r
108 +    /* The character that closes the current aggregate. */\r
109 +    char close;\r
110 +};\r
111 +\r
112 +/* Helper function to set up the stream to print a value.  If this\r
113 + * value follows another value, prints a space. */\r
114 +static struct sprinter_sexp *\r
115 +sexp_begin_value (struct sprinter *sp)\r
116 +{\r
117 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
118 +\r
119 +    if (sps->state) {\r
120 +        if (! sps->state->first) {\r
121 +            if (sps->insert_separator) {\r
122 +                fputc ('\n', sps->stream);\r
123 +                sps->insert_separator = FALSE;\r
124 +            } else {\r
125 +                if( ! sps->state->in_map)\r
126 +                    fputc (' ', sps->stream);\r
127 +            }\r
128 +        } else {\r
129 +            sps->state->first = FALSE;\r
130 +        }\r
131 +    }\r
132 +    return sps;\r
133 +}\r
134 +\r
135 +/* Helper function to begin an aggregate type.  Prints the open\r
136 + * character and pushes a new state frame. */\r
137 +static void\r
138 +sexp_begin_aggregate (struct sprinter *sp, char open, char close)\r
139 +{\r
140 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
141 +    struct sexp_state *state = talloc (sps, struct sexp_state);\r
142 +    fputc (open, sps->stream);\r
143 +    state->parent = sps->state;\r
144 +    state->first = TRUE;\r
145 +    state->in_map = FALSE;\r
146 +    state->close = close;\r
147 +    sps->state = state;\r
148 +}\r
149 +\r
150 +static void\r
151 +sexp_begin_map (struct sprinter *sp)\r
152 +{\r
153 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
154 +    sexp_begin_aggregate (sp, '(', ')');\r
155 +    sps->state->in_map = TRUE;\r
156 +}\r
157 +\r
158 +static void\r
159 +sexp_begin_list (struct sprinter *sp)\r
160 +{\r
161 +    sexp_begin_aggregate (sp, '(', ')');\r
162 +}\r
163 +\r
164 +static void\r
165 +sexp_end (struct sprinter *sp)\r
166 +{\r
167 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
168 +    struct sexp_state *state = sps->state;\r
169 +\r
170 +    if (sps->state->in_map)\r
171 +        fputc (')', sps->stream);\r
172 +    fputc (sps->state->close, sps->stream);\r
173 +    sps->state = state->parent;\r
174 +    talloc_free (state);\r
175 +    if (sps->state == NULL)\r
176 +        fputc ('\n', sps->stream);\r
177 +}\r
178 +\r
179 +/* This implementation supports embedded NULs as allowed by the JSON\r
180 + * specification and Unicode.  Support for *parsing* embedded NULs\r
181 + * varies, but is generally not a problem outside of C-based parsers\r
182 + * (Python's json module and Emacs' json.el take embedded NULs in\r
183 + * stride). */\r
184 +static void\r
185 +sexp_string_len_internal (struct sprinter *sp, const char *val, size_t len, notmuch_bool_t quote)\r
186 +{\r
187 +    static const char *const escapes[] = {\r
188 +        ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",\r
189 +        ['\f'] = "\\f",  ['\n'] = "\\n",  ['\t'] = "\\t"\r
190 +    };\r
191 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
192 +\r
193 +    if(quote)\r
194 +        fputc ('"', sps->stream);\r
195 +    for (; len; ++val, --len) {\r
196 +        unsigned char ch = *val;\r
197 +        if (ch < ARRAY_SIZE (escapes) && escapes[ch])\r
198 +            fputs (escapes[ch], sps->stream);\r
199 +        else if (ch >= 32)\r
200 +            fputc (ch, sps->stream);\r
201 +        else\r
202 +            fprintf (sps->stream, "\\u%04x", ch);\r
203 +    }\r
204 +    if(quote)\r
205 +        fputc ('"', sps->stream);\r
206 +}\r
207 +\r
208 +static void\r
209 +sexp_string_len (struct sprinter *sp, const char *val, size_t len)\r
210 +{\r
211 +    sexp_string_len_internal (sp, val, len, TRUE); /* print quoted */\r
212 +}\r
213 +\r
214 +static void\r
215 +sexp_symbol_len (struct sprinter *sp, const char *val, size_t len)\r
216 +{\r
217 +    sexp_string_len_internal (sp, val, len, FALSE); /* print unquoted */\r
218 +}\r
219 +\r
220 +static void\r
221 +sexp_string (struct sprinter *sp, const char *val)\r
222 +{\r
223 +    if (val == NULL)\r
224 +        val = "";\r
225 +    sexp_string_len (sp, val, strlen (val));\r
226 +}\r
227 +\r
228 +static void\r
229 +sexp_symbol (struct sprinter *sp, const char *val)\r
230 +{\r
231 +    if (val == NULL)\r
232 +        val = "";\r
233 +    sexp_symbol_len (sp, val, strlen (val));\r
234 +}\r
235 +\r
236 +static void\r
237 +sexp_integer (struct sprinter *sp, int val)\r
238 +{\r
239 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
240 +\r
241 +    fprintf (sps->stream, "%d", val);\r
242 +}\r
243 +\r
244 +static void\r
245 +sexp_boolean (struct sprinter *sp, notmuch_bool_t val)\r
246 +{\r
247 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
248 +\r
249 +    fputs (val ? "t" : "nil", sps->stream);\r
250 +}\r
251 +\r
252 +static void\r
253 +sexp_null (struct sprinter *sp)\r
254 +{\r
255 +    struct sprinter_sexp *sps = sexp_begin_value (sp);\r
256 +\r
257 +    fputs ("nil", sps->stream);\r
258 +}\r
259 +\r
260 +static void\r
261 +sexp_map_key (struct sprinter *sp, const char *key)\r
262 +{\r
263 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
264 +\r
265 +    if( sps->state->in_map && ! sps->state->first)\r
266 +        fputs (") ", sps->stream);\r
267 +    fputc ('(', sps->stream);\r
268 +    sexp_symbol (sp, key);\r
269 +    fputc (' ', sps->stream);\r
270 +}\r
271 +\r
272 +static void\r
273 +sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))\r
274 +{\r
275 +}\r
276 +\r
277 +static void\r
278 +sexp_separator (struct sprinter *sp)\r
279 +{\r
280 +    struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;\r
281 +\r
282 +    sps->insert_separator = TRUE;\r
283 +}\r
284 +\r
285 +struct sprinter *\r
286 +sprinter_sexp_create (const void *ctx, FILE *stream)\r
287 +{\r
288 +    static const struct sprinter_sexp template = {\r
289 +        .vtable = {\r
290 +            .begin_map = sexp_begin_map,\r
291 +            .begin_list = sexp_begin_list,\r
292 +            .end = sexp_end,\r
293 +            .string = sexp_string,\r
294 +            .string_len = sexp_string_len,\r
295 +            .integer = sexp_integer,\r
296 +            .boolean = sexp_boolean,\r
297 +            .null = sexp_null,\r
298 +            .map_key = sexp_map_key,\r
299 +            .separator = sexp_separator,\r
300 +            .set_prefix = sexp_set_prefix,\r
301 +            .is_text_printer = FALSE,\r
302 +        }\r
303 +    };\r
304 +    struct sprinter_sexp *res;\r
305 +\r
306 +    res = talloc (ctx, struct sprinter_sexp);\r
307 +    if (! res)\r
308 +        return NULL;\r
309 +\r
310 +    *res = template;\r
311 +    res->stream = stream;\r
312 +    return &res->vtable;\r
313 +}\r
314 -- \r
315 1.8.0\r
316 \r