[PATCH 4/4] Update NEWS for user.other_name
[notmuch-archives.git] / fe / 6dc85e32d30e1d0cf4ed4a58990188a309b175
1 Return-Path: <peter.feigl@gmx.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 21294431FB6\r
6         for <notmuch@notmuchmail.org>; Fri, 13 Jul 2012 01:18:07 -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 Ssh-EgvKis61 for <notmuch@notmuchmail.org>;\r
17         Fri, 13 Jul 2012 01:18:06 -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 8E5B3431FAE\r
20         for <notmuch@notmuchmail.org>; Fri, 13 Jul 2012 01:18:05 -0700 (PDT)\r
21 Received: (qmail invoked by alias); 13 Jul 2012 08:17:59 -0000\r
22 Received: from www.nexoid.at (EHLO mail.nexoid.at) [178.79.130.240]\r
23         by mail.gmx.net (mp033) with SMTP; 13 Jul 2012 10:17:59 +0200\r
24 X-Authenticated: #4563876\r
25 X-Provags-ID: V01U2FsdGVkX18mSfJsQnom6dBKHkvNoucS8v0zN2qRATa2X2Iehy\r
26         tsWGQNsjSenVzp\r
27 Received: from nexoid (localhost [127.0.0.1])\r
28         by mail.nexoid.at (Postfix) with ESMTP id 42168E00C\r
29         for <notmuch@notmuchmail.org>; Fri, 13 Jul 2012 10:17:56 +0200 (CEST)\r
30 From: <craven@gmx.net>\r
31 To: notmuch@notmuchmail.org\r
32 Subject: Re: Proof of concept: S-Expression format\r
33 In-Reply-To: <1342167097-25012-1-git-send-email-craven@gmx.net>\r
34 References: <20120710191331.GE7332@mit.edu>\r
35         <1342167097-25012-1-git-send-email-craven@gmx.net>\r
36 User-Agent: Notmuch/0.11+77~gad6d0d5 (http://notmuchmail.org) Emacs/24.1.50.2\r
37         (i686-pc-linux-gnu)\r
38 Date: Fri, 13 Jul 2012 10:17:56 +0200\r
39 Message-ID: <877gu8t6q3.fsf@nexoid.at>\r
40 MIME-Version: 1.0\r
41 Content-Type: text/plain\r
42 X-Y-GMX-Trusted: 0\r
43 X-BeenThere: notmuch@notmuchmail.org\r
44 X-Mailman-Version: 2.1.13\r
45 Precedence: list\r
46 List-Id: "Use and development of the notmuch mail system."\r
47         <notmuch.notmuchmail.org>\r
48 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
49         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
50 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
51 List-Post: <mailto:notmuch@notmuchmail.org>\r
52 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
53 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
54         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
55 X-List-Received-Date: Fri, 13 Jul 2012 08:18:07 -0000\r
56 \r
57 This patch shows how to add a new output format to notmuch-search.c.\r
58 \r
59 As an example, it adds S-Expressions. The concrete formatting can\r
60 easily be changed, this is meant as a proof of concept that the\r
61 changes to core notmuch code are very few and all formatting state is\r
62 kept inside sprinter-sexp.c.\r
63 ---\r
64  Makefile.local   |   1 +\r
65  notmuch-search.c |   6 +-\r
66  sprinter-sexp.c  | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++\r
67  sprinter.h       |   4 ++\r
68  4 files changed, 195 insertions(+), 1 deletion(-)\r
69  create mode 100644 sprinter-sexp.c\r
70 \r
71 diff --git a/Makefile.local b/Makefile.local\r
72 index b6c7e0c..cc1d58a 100644\r
73 --- a/Makefile.local\r
74 +++ b/Makefile.local\r
75 @@ -292,6 +292,7 @@ notmuch_client_srcs =               \\r
76         notmuch-time.c          \\r
77         sprinter-json.c         \\r
78         sprinter-text-search.c  \\r
79 +       sprinter-sexp.c         \\r
80         query-string.c          \\r
81         mime-node.c             \\r
82         crypto.c                \\r
83 diff --git a/notmuch-search.c b/notmuch-search.c\r
84 index 99fddac..2db58a5 100644\r
85 --- a/notmuch-search.c\r
86 +++ b/notmuch-search.c\r
87 @@ -256,7 +256,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
88      int exclude = EXCLUDE_TRUE;\r
89      unsigned int i;\r
90  \r
91 -    enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT }\r
92 +    enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_TEXT, NOTMUCH_FORMAT_SEXP }\r
93         format_sel = NOTMUCH_FORMAT_TEXT;\r
94  \r
95      notmuch_opt_desc_t options[] = {\r
96 @@ -267,6 +267,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
97         { NOTMUCH_OPT_KEYWORD, &format_sel, "format", 'f',\r
98           (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },\r
99                                   { "text", NOTMUCH_FORMAT_TEXT },\r
100 +                                 { "sexp", NOTMUCH_FORMAT_SEXP },\r
101                                   { 0, 0 } } },\r
102         { NOTMUCH_OPT_KEYWORD, &output, "output", 'o',\r
103           (notmuch_keyword_t []){ { "summary", OUTPUT_SUMMARY },\r
104 @@ -298,6 +299,9 @@ notmuch_search_command (void *ctx, int argc, char *argv[])\r
105      case NOTMUCH_FORMAT_JSON:\r
106         format = sprinter_json_create (ctx, stdout);\r
107         break;\r
108 +    case NOTMUCH_FORMAT_SEXP:\r
109 +       format = sprinter_sexp_create (ctx, stdout);\r
110 +       break;\r
111      }\r
112  \r
113      config = notmuch_config_open (ctx, NULL, NULL);\r
114 diff --git a/sprinter-sexp.c b/sprinter-sexp.c\r
115 new file mode 100644\r
116 index 0000000..68a5db5\r
117 --- /dev/null\r
118 +++ b/sprinter-sexp.c\r
119 @@ -0,0 +1,185 @@\r
120 +#include <stdbool.h>\r
121 +#include <stdio.h>\r
122 +#include <talloc.h>\r
123 +#include "sprinter.h"\r
124 +\r
125 +typedef enum { MAP, LIST } aggregate_t;\r
126 +\r
127 +struct sprinter_sexp {\r
128 +    struct sprinter vtable;\r
129 +    FILE *stream;\r
130 +    /* Top of the state stack, or NULL if the printer is not currently\r
131 +     * inside any aggregate types. */\r
132 +    struct sexp_state *state;\r
133 +};\r
134 +\r
135 +struct sexp_state {\r
136 +    struct sexp_state *parent;\r
137 +    /* True if nothing has been printed in this aggregate yet.\r
138 +     * Suppresses the comma before a value. */\r
139 +    notmuch_bool_t first;\r
140 +    /* The character that closes the current aggregate. */\r
141 +    aggregate_t type;\r
142 +};\r
143 +\r
144 +static struct sprinter_sexp *\r
145 +sexp_begin_value (struct sprinter *sp)\r
146 +{\r
147 +    struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;\r
148 +\r
149 +    if (spsx->state) {\r
150 +       if (! spsx->state->first)\r
151 +           fputc (' ', spsx->stream);\r
152 +       else\r
153 +           spsx->state->first = FALSE;\r
154 +    }\r
155 +    return spsx;\r
156 +}\r
157 +\r
158 +static void\r
159 +sexp_begin_aggregate (struct sprinter *sp, aggregate_t type)\r
160 +{\r
161 +    struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;\r
162 +    struct sexp_state *state = talloc (spsx, struct sexp_state);\r
163 +\r
164 +    fputc ('(', spsx->stream);\r
165 +    state->parent = spsx->state;\r
166 +    state->first = TRUE;\r
167 +    state->type = type;\r
168 +\r
169 +    spsx->state = state;\r
170 +}\r
171 +\r
172 +static void\r
173 +sexp_begin_map (struct sprinter *sp)\r
174 +{\r
175 +    sexp_begin_aggregate (sp, MAP);\r
176 +}\r
177 +\r
178 +static void\r
179 +sexp_begin_list (struct sprinter *sp)\r
180 +{\r
181 +    sexp_begin_aggregate (sp, LIST);\r
182 +}\r
183 +\r
184 +static void\r
185 +sexp_end (struct sprinter *sp)\r
186 +{\r
187 +    struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;\r
188 +    struct sexp_state *state = spsx->state;\r
189 +\r
190 +    fputc (')', spsx->stream);\r
191 +    spsx->state = state->parent;\r
192 +    talloc_free (state);\r
193 +    if (spsx->state == NULL)\r
194 +       fputc ('\n', spsx->stream);\r
195 +    else\r
196 +       if (spsx->state->type == MAP)\r
197 +           fputc (')', spsx->stream);\r
198 +}\r
199 +\r
200 +static void\r
201 +sexp_string (struct sprinter *sp, const char *val)\r
202 +{\r
203 +    static const char *const escapes[] = {\r
204 +       ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",\r
205 +       ['\f'] = "\\f",  ['\n'] = "\\n",  ['\t'] = "\\t"\r
206 +    };\r
207 +    struct sprinter_sexp *spsx = sexp_begin_value (sp);\r
208 +\r
209 +    fputc ('"', spsx->stream);\r
210 +    for (; *val; ++val) {\r
211 +       unsigned char ch = *val;\r
212 +       if (ch < ARRAY_SIZE (escapes) && escapes[ch])\r
213 +           fputs (escapes[ch], spsx->stream);\r
214 +       else if (ch >= 32)\r
215 +           fputc (ch, spsx->stream);\r
216 +       else\r
217 +           fprintf (spsx->stream, "\\u%04x", ch);\r
218 +    }\r
219 +    fputc ('"', spsx->stream);\r
220 +    if (spsx->state != NULL &&  spsx->state->type == MAP)\r
221 +       fputc (')', spsx->stream);\r
222 +    spsx->state->first = FALSE;\r
223 +}\r
224 +\r
225 +static void\r
226 +sexp_integer (struct sprinter *sp, int val)\r
227 +{\r
228 +    struct sprinter_sexp *spsx = sexp_begin_value (sp);\r
229 +\r
230 +    fprintf (spsx->stream, "%d", val);\r
231 +    if (spsx->state != NULL &&  spsx->state->type == MAP)\r
232 +       fputc (')', spsx->stream);\r
233 +}\r
234 +\r
235 +static void\r
236 +sexp_boolean (struct sprinter *sp, notmuch_bool_t val)\r
237 +{\r
238 +    struct sprinter_sexp *spsx = sexp_begin_value (sp);\r
239 +\r
240 +    fputs (val ? "#t" : "#f", spsx->stream);\r
241 +    if (spsx->state != NULL &&  spsx->state->type == MAP)\r
242 +       fputc (')', spsx->stream);\r
243 +}\r
244 +\r
245 +static void\r
246 +sexp_null (struct sprinter *sp)\r
247 +{\r
248 +    struct sprinter_sexp *spsx = sexp_begin_value (sp);\r
249 +\r
250 +    fputs ("'()", spsx->stream);\r
251 +    spsx->state->first = FALSE;\r
252 +}\r
253 +\r
254 +static void\r
255 +sexp_map_key (struct sprinter *sp, const char *key)\r
256 +{\r
257 +    struct sprinter_sexp *spsx = sexp_begin_value (sp);\r
258 +\r
259 +    fputc ('(', spsx->stream);\r
260 +    fputs (key, spsx->stream);\r
261 +    fputs (" . ", spsx->stream);\r
262 +    spsx->state->first = TRUE;\r
263 +}\r
264 +\r
265 +static void\r
266 +sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))\r
267 +{\r
268 +}\r
269 +\r
270 +static void\r
271 +sexp_separator (struct sprinter *sp)\r
272 +{\r
273 +    struct sprinter_sexp *spsx = (struct sprinter_sexp *) sp;\r
274 +\r
275 +    fputc ('\n', spsx->stream);\r
276 +}\r
277 +\r
278 +struct sprinter *\r
279 +sprinter_sexp_create (const void *ctx, FILE *stream)\r
280 +{\r
281 +    static const struct sprinter_sexp template = {\r
282 +       .vtable = {\r
283 +           .begin_map = sexp_begin_map,\r
284 +           .begin_list = sexp_begin_list,\r
285 +           .end = sexp_end,\r
286 +           .string = sexp_string,\r
287 +           .integer = sexp_integer,\r
288 +           .boolean = sexp_boolean,\r
289 +           .null = sexp_null,\r
290 +           .map_key = sexp_map_key,\r
291 +           .separator = sexp_separator,\r
292 +           .set_prefix = sexp_set_prefix,\r
293 +       }\r
294 +    };\r
295 +    struct sprinter_sexp *res;\r
296 +\r
297 +    res = talloc (ctx, struct sprinter_sexp);\r
298 +    if (! res)\r
299 +       return NULL;\r
300 +\r
301 +    *res = template;\r
302 +    res->stream = stream;\r
303 +    return &res->vtable;\r
304 +}\r
305 diff --git a/sprinter.h b/sprinter.h\r
306 index 4241d65..c0146f6 100644\r
307 --- a/sprinter.h\r
308 +++ b/sprinter.h\r
309 @@ -55,4 +55,8 @@ sprinter_text_search_create (const void *ctx, FILE *stream);\r
310  struct sprinter *\r
311  sprinter_json_create (const void *ctx, FILE *stream);\r
312  \r
313 +/* Create a new structure printer that emits S-Expressions. */\r
314 +struct sprinter *\r
315 +sprinter_sexp_create (const void *ctx, FILE *stream);\r
316 +\r
317  #endif // NOTMUCH_SPRINTER_H\r
318 -- \r
319 1.7.11.1\r
320 \r