Re: [PATCH 0/4] Allow specifying alternate names for addresses in other_email
[notmuch-archives.git] / 68 / 043d304414bb0f0ffabca0b18bedfdeb78f2a2
1 Return-Path: <amdragon@mit.edu>\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 E1014431FB6\r
6         for <notmuch@notmuchmail.org>; Fri, 29 Jun 2012 19:59:20 -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.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 ojSSQFZQVlNL for <notmuch@notmuchmail.org>;\r
16         Fri, 29 Jun 2012 19:59:20 -0700 (PDT)\r
17 Received: from dmz-mailsec-scanner-5.mit.edu (DMZ-MAILSEC-SCANNER-5.MIT.EDU\r
18         [18.7.68.34])\r
19         by olra.theworths.org (Postfix) with ESMTP id DC0DD431FAF\r
20         for <notmuch@notmuchmail.org>; Fri, 29 Jun 2012 19:59:19 -0700 (PDT)\r
21 X-AuditID: 12074422-b7f1f6d00000090b-38-4fee6b85923a\r
22 Received: from mailhub-auth-4.mit.edu ( [18.7.62.39])\r
23         by dmz-mailsec-scanner-5.mit.edu (Symantec Messaging Gateway) with SMTP\r
24         id FE.1E.02315.58B6EEF4; Fri, 29 Jun 2012 22:59:17 -0400 (EDT)\r
25 Received: from outgoing.mit.edu (OUTGOING-AUTH.MIT.EDU [18.7.22.103])\r
26         by mailhub-auth-4.mit.edu (8.13.8/8.9.2) with ESMTP id q5U2xGiF030961; \r
27         Fri, 29 Jun 2012 22:59:17 -0400\r
28 Received: from awakening.csail.mit.edu (awakening.csail.mit.edu [18.26.4.91])\r
29         (authenticated bits=0)\r
30         (User authenticated as amdragon@ATHENA.MIT.EDU)\r
31         by outgoing.mit.edu (8.13.6/8.12.4) with ESMTP id q5U2xFLm007059\r
32         (version=TLSv1/SSLv3 cipher=AES256-SHA bits=256 verify=NOT);\r
33         Fri, 29 Jun 2012 22:59:16 -0400 (EDT)\r
34 Received: from amthrax by awakening.csail.mit.edu with local (Exim 4.77)\r
35         (envelope-from <amdragon@mit.edu>)\r
36         id 1Skntz-0000P7-7D; Fri, 29 Jun 2012 22:59:15 -0400\r
37 From: Austin Clements <amdragon@MIT.EDU>\r
38 To: Peter Feigl <craven@gmx.net>,\r
39         Jameson Graef Rollins <jrollins@finestructure.net>\r
40 Subject: Proposed structure formatter API\r
41 User-Agent: Notmuch/0.13.2+59~g78b98ba (http://notmuchmail.org) Emacs/23.3.1\r
42         (i486-pc-linux-gnu)\r
43 Date: Fri, 29 Jun 2012 22:59:15 -0400\r
44 Message-ID: <87d34hsdx8.fsf@awakening.csail.mit.edu>\r
45 MIME-Version: 1.0\r
46 Content-Type: multipart/mixed; boundary="=-=-="\r
47 X-Brightmail-Tracker:\r
48  H4sIAAAAAAAAA+NgFvrMIsWRmVeSWpSXmKPExsUixG6nrtua/c7fYM9kbYu9De2MFnv2eVlc\r
49         vzmT2YHZ4+5pLo/Fm/azeTxbdYs5gDmKyyYlNSezLLVI3y6BK+P1+VXMBU9tK1pfH2VsYLyo\r
50         38XIySEhYCLRuOo3O4QtJnHh3nq2LkYuDiGBfYwSB+dOZoJwNjBKrJg1hRHCOckk8XvNGqjM\r
51         EkaJrUsfMoL0swloSGzbvxzMFhGIlZj7bi8ziM0sIC3x7XczE4gtLKApsfPwU6AdHByiAgkS\r
52         B4/XgJgsAqoS07qcQUxeoIsev7UHKeYVEJQ4OfMJC8QQfYnZ07tZJzDyz0KSmoUkBWFLSpya\r
53         tpQNwjaQeLX/IuMCRpZVjLIpuVW6uYmZOcWpybrFyYl5ealFuqZ6uZkleqkppZsYwWHrorSD\r
54         8edBpUOMAhyMSjy8UZff+guxJpYVV+YeYpTkYFIS5T2T+s5fiC8pP6UyI7E4I76oNCe1+BCj\r
55         BAezkgjvw8VA5bwpiZVVqUX5MClpDhYlcd5rKTf9hQTSE0tSs1NTC1KLYLIyHBxKEry/soCG\r
56         ChalpqdWpGXmlCCkmTg4QYbzAA1/AFLDW1yQmFucmQ6RP8WoKCXOex0kIQCSyCjNg+uFpZVX\r
57         jOJArwjzvgSp4gGmJLjuV0CDmYAGOwW8BhlckoiQkmpgrM+bsm/W5LxHz8ILV796dumAnGHB\r
58         N0GmV2cdni2x+H8gZ92i9sSLqv8fBjy6eZ/dQ1SdSftc5PnIGQr9H/8dT9c2exZ3JlXpzLor\r
59         +zxuPf/1asH565+3qUwW4Z7BaqcidOTBr0Win6/rrD4l4bjPj2fCGlbtWx/OFk/xzb3EftEn\r
60         Ofht8UIergYlluKMREMt5qLiRADJaRFhBgMAAA==\r
61 Cc: notmuch@notmuchmail.org\r
62 X-BeenThere: notmuch@notmuchmail.org\r
63 X-Mailman-Version: 2.1.13\r
64 Precedence: list\r
65 List-Id: "Use and development of the notmuch mail system."\r
66         <notmuch.notmuchmail.org>\r
67 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,\r
68         <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>\r
69 List-Archive: <http://notmuchmail.org/pipermail/notmuch>\r
70 List-Post: <mailto:notmuch@notmuchmail.org>\r
71 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>\r
72 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,\r
73         <mailto:notmuch-request@notmuchmail.org?subject=subscribe>\r
74 X-List-Received-Date: Sat, 30 Jun 2012 02:59:21 -0000\r
75 \r
76 --=-=-=\r
77 \r
78 On IRC today, Peter renewed discussions of supporting an S-expression\r
79 format, which I think would be a fantastic thing to have.\r
80 \r
81 A while back [1] I tried to explain an API I thought would form a good\r
82 foundation for this by abstracting away the differences between JSON and\r
83 S-expression output so both structured formats can use the same code,\r
84 but I didn't express the idea very clearly.  I built essentially this\r
85 API for another project recently and it worked well (and I learned a few\r
86 things), so I figured I would give it another shot.  It's simple enough\r
87 that I tossed together an example implementation, which is attached.\r
88 \r
89 The idea is similar to the existing format structures, but instead of\r
90 dealing with complex, high-level concepts like threads or messages, this\r
91 API deals with syntax-level concepts like lists and strings.  All of the\r
92 structured output formats can then be supported by a single high-level\r
93 formatter backed by different syntax formatters.\r
94 \r
95 There are a few ways something like this could be integrated with the\r
96 high-level formatters.  For show, the least invasive thing would be to\r
97 have two notmuch_show_format structures with different part functions.\r
98 These part functions would be thin wrappers that simply create the right\r
99 structure printer and then pass it to a common part function.\r
100 \r
101 It would be even nicer if we could get rid of the\r
102 message_set_{start,sep,end} fields, since those duplicate functionality\r
103 from the structure printer and their use is haphazard and overly\r
104 complicated.  We could do this by changing notmuch_show_format to\r
105 something like\r
106 \r
107 typedef struct notmuch_show_format {\r
108     struct sprinter *(*new_sprinter) (const void *ctx);\r
109     notmuch_status_t (*part) (const void *ctx, struct sprinter *sp,\r
110                               struct mime_node *node,\r
111                               const struct notmuch_show_params *params);\r
112 } notmuch_show_format_t;\r
113 \r
114 For the JSON and S-expression show formats, new_sprinter would be\r
115 sprinter_json_new and sprinter_sexp_new, respectively, and they could\r
116 share a part function.  For the text, mbox, and raw formatters,\r
117 new_sprinter could simply be NULL, or we could provide a "NULL structure\r
118 printer" implementation that does nothing.\r
119 \r
120 We could do something similar for reply.\r
121 \r
122 search_format is more complicated, but might also benefit more.  Most of\r
123 those fields have to do with how to print lists and objects and could be\r
124 removed if the format simply provided a new_sprinter method like the\r
125 notmuch_show_format I suggested above.\r
126 \r
127 [1] id:"20120121220407.GK16740@mit.edu"\r
128 \r
129 \r
130 --=-=-=\r
131 Content-Type: text/x-csrc\r
132 Content-Disposition: inline; filename=sprinter.c\r
133 \r
134 #include <stdbool.h>\r
135 #include <stdio.h>\r
136 #include <talloc.h>\r
137 #define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr[0]))\r
138 \r
139 /* Structure printer interface */\r
140 struct sprinter\r
141 {\r
142     void (*begin_map) (struct sprinter *);\r
143     void (*begin_list) (struct sprinter *);\r
144     void (*end) (struct sprinter *);\r
145 \r
146     void (*string) (struct sprinter *, const char *);\r
147     void (*integer) (struct sprinter *, int);\r
148     void (*boolean) (struct sprinter *, bool);\r
149     void (*null) (struct sprinter *);\r
150     void (*map_key) (struct sprinter *, const char *);\r
151 \r
152     void (*frame) (struct sprinter *);\r
153 };\r
154 \r
155 /* Create a new structure printer that emits JSON */\r
156 struct sprinter *\r
157 sprinter_json_new(const void *ctx, FILE *stream);\r
158 \r
159 int\r
160 main(int argc, char **argv)\r
161 {\r
162     struct sprinter *test = sprinter_json_new (NULL, stdout);\r
163     test->begin_list (test);\r
164     test->string (test, "test");\r
165     test->integer (test, 42);\r
166     test->boolean (test, true);\r
167     test->null (test);\r
168     test->frame (test);\r
169     test->begin_map (test);\r
170     test->map_key (test, "Hello");\r
171     test->string (test, "world\n");\r
172     test->end (test);\r
173     test->frame (test);\r
174     test->end (test);\r
175     talloc_free (test);\r
176 }\r
177 \r
178 /*\r
179  * Every below here is private implementation.\r
180  */\r
181 \r
182 struct sprinter_json\r
183 {\r
184     struct sprinter vtable;\r
185     FILE *stream;\r
186     /* Top of the state stack, or NULL if the printer is not currently\r
187      * inside any aggregate types. */\r
188     struct json_state *state;\r
189 };\r
190 \r
191 struct json_state\r
192 {\r
193     struct json_state *parent;\r
194     /* True if nothing has been printed in this aggregate yet.\r
195      * Suppresses the comma before a value. */\r
196     bool first;\r
197     /* The character that closes the current aggregate. */\r
198     char close;\r
199 };\r
200 \r
201 /* Helper function to set up the stream to print a value.  If this\r
202  * value follows another value, prints a comma. */\r
203 static struct sprinter_json *\r
204 json_begin_value(struct sprinter *sp)\r
205 {\r
206     struct sprinter_json *spj = (struct sprinter_json*)sp;\r
207     if (spj->state) {\r
208         if (!spj->state->first)\r
209             fputs (", ", spj->stream);\r
210         else\r
211             spj->state->first = false;\r
212     }\r
213     return spj;\r
214 }\r
215 \r
216 /* Helper function to begin an aggregate type.  Prints the open\r
217  * character and pushes a new state frame. */\r
218 static void\r
219 json_begin_aggregate(struct sprinter *sp, char open, char close)\r
220 {\r
221     struct sprinter_json *spj = json_begin_value (sp);\r
222     struct json_state *state = talloc (spj, struct json_state);\r
223 \r
224     fputc (open, spj->stream);\r
225     state->parent = spj->state;\r
226     state->first = true;\r
227     state->close = close;\r
228     spj->state = state;\r
229 }\r
230 \r
231 static void\r
232 json_begin_map(struct sprinter *sp)\r
233 {\r
234     json_begin_aggregate (sp, '{', '}');\r
235 }\r
236 \r
237 static void\r
238 json_begin_list(struct sprinter *sp)\r
239 {\r
240     json_begin_aggregate (sp, '[', ']');\r
241 }\r
242 \r
243 static void\r
244 json_end(struct sprinter *sp)\r
245 {\r
246     struct sprinter_json *spj = (struct sprinter_json*)sp;\r
247     struct json_state *state = spj->state;\r
248 \r
249     fputc (spj->state->close, spj->stream);\r
250     spj->state = state->parent;\r
251     talloc_free (state);\r
252     if(spj->state == NULL)\r
253         fputc ('\n', spj->stream);\r
254 }\r
255 \r
256 static void\r
257 json_string(struct sprinter *sp, const char *val)\r
258 {\r
259     const static char * const escapes[] = {\r
260         ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",\r
261         ['\f'] = "\\f",  ['\n'] = "\\n",  ['\t'] = "\\t"\r
262     };\r
263     struct sprinter_json *spj = json_begin_value (sp);\r
264     fputc ('"', spj->stream);\r
265     for (; *val; ++val) {\r
266         unsigned char ch = *val;\r
267         if (ch < ARRAY_SIZE(escapes) && escapes[ch])\r
268             fputs (escapes[ch], spj->stream);\r
269         else if (ch >= 32)\r
270             fputc (ch, spj->stream);\r
271         else\r
272             fprintf (spj->stream, "\\u%04x", ch);\r
273     }\r
274     fputc ('"', spj->stream);\r
275 }\r
276 \r
277 static void\r
278 json_integer(struct sprinter *sp, int val)\r
279 {\r
280     struct sprinter_json *spj = json_begin_value (sp);\r
281     fprintf (spj->stream, "%d", val);\r
282 }\r
283 \r
284 static void\r
285 json_boolean(struct sprinter *sp, bool val)\r
286 {\r
287     struct sprinter_json *spj = json_begin_value (sp);\r
288     fputs (val ? "true" : "false", spj->stream);\r
289 }\r
290 \r
291 static void\r
292 json_null(struct sprinter *sp)\r
293 {\r
294     struct sprinter_json *spj = json_begin_value (sp);\r
295     fputs ("null", spj->stream);\r
296 }\r
297 \r
298 static void\r
299 json_map_key(struct sprinter *sp, const char *key)\r
300 {\r
301     struct sprinter_json *spj = (struct sprinter_json*)sp;\r
302     json_string (sp, key);\r
303     fputs (": ", spj->stream);\r
304     spj->state->first = true;\r
305 }\r
306 \r
307 static void\r
308 json_frame(struct sprinter *sp)\r
309 {\r
310     struct sprinter_json *spj = (struct sprinter_json*)sp;\r
311     fputc ('\n', spj->stream);\r
312 }\r
313 \r
314 struct sprinter *\r
315 sprinter_json_new(const void *ctx, FILE *stream)\r
316 {\r
317     const static struct sprinter_json template = {\r
318         .vtable = {\r
319             .begin_map = json_begin_map,\r
320             .begin_list = json_begin_list,\r
321             .end = json_end,\r
322             .string = json_string,\r
323             .integer = json_integer,\r
324             .boolean = json_boolean,\r
325             .null = json_null,\r
326             .map_key = json_map_key,\r
327             .frame = json_frame,\r
328         }\r
329     };\r
330     struct sprinter_json *res;\r
331 \r
332     res = talloc (ctx, struct sprinter_json);\r
333     if (!res)\r
334         return NULL;\r
335 \r
336     *res = template;\r
337     res->stream = stream;\r
338     return &res->vtable;\r
339 }\r
340 \r
341 --=-=-=--\r