1 /* notmuch - Not much of an email program, (just index and search)
3 * Copyright © 2012 Peter Feigl
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see https://www.gnu.org/licenses/ .
18 * Author: Peter Feigl <peter.feigl@gmx.at>
27 struct sprinter_sexp {
28 struct sprinter vtable;
30 /* Top of the state stack, or NULL if the printer is not currently
31 * inside any aggregate types. */
32 struct sexp_state *state;
34 /* A flag to signify that a separator should be inserted in the
35 * output as soon as possible. */
36 bool insert_separator;
40 struct sexp_state *parent;
42 /* True if nothing has been printed in this aggregate yet.
43 * Suppresses the space before a value. */
47 /* Helper function to set up the stream to print a value. If this
48 * value follows another value, prints a space. */
49 static struct sprinter_sexp *
50 sexp_begin_value (struct sprinter *sp)
52 struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
55 if (! sps->state->first) {
56 if (sps->insert_separator) {
57 fputc ('\n', sps->stream);
58 sps->insert_separator = false;
60 fputc (' ', sps->stream);
63 sps->state->first = false;
69 /* Helper function to begin an aggregate type. Prints the open
70 * character and pushes a new state frame. */
72 sexp_begin_aggregate (struct sprinter *sp)
74 struct sprinter_sexp *sps = sexp_begin_value (sp);
75 struct sexp_state *state = talloc (sps, struct sexp_state);
77 fputc ('(', sps->stream);
78 state->parent = sps->state;
84 sexp_begin_map (struct sprinter *sp)
86 sexp_begin_aggregate (sp);
90 sexp_begin_list (struct sprinter *sp)
92 sexp_begin_aggregate (sp);
96 sexp_end (struct sprinter *sp)
98 struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
99 struct sexp_state *state = sps->state;
101 fputc (')', sps->stream);
102 sps->state = state->parent;
104 if (sps->state == NULL)
105 fputc ('\n', sps->stream);
109 sexp_string_len (struct sprinter *sp, const char *val, size_t len)
111 /* Some characters need escaping. " and \ work fine in all Lisps,
112 * \n is not supported in CL, but all others work fine.
113 * Characters below 32 are printed as \123o (three-digit
114 * octals), which work fine in most Schemes and Emacs. */
115 static const char *const escapes[] = {
116 ['\"'] = "\\\"", ['\\'] = "\\\\", ['\n'] = "\\n"
118 struct sprinter_sexp *sps = sexp_begin_value (sp);
120 fputc ('"', sps->stream);
121 for (; len; ++val, --len) {
122 unsigned char ch = *val;
123 if (ch < ARRAY_SIZE (escapes) && escapes[ch])
124 fputs (escapes[ch], sps->stream);
126 fputc (ch, sps->stream);
128 fprintf (sps->stream, "\\%03o", ch);
130 fputc ('"', sps->stream);
134 sexp_string (struct sprinter *sp, const char *val)
138 sexp_string_len (sp, val, strlen (val));
141 /* Prints a symbol, i.e. the name preceded by a colon. This should work
142 * in all Lisps, at least as a symbol, if not as a proper keyword */
144 sexp_keyword (struct sprinter *sp, const char *val)
147 struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
151 INTERNAL_ERROR ("illegal symbol NULL");
153 for (i = 0; i < strlen (val); i++) {
155 if (! (isalnum (ch) || (ch == '-') || (ch == '_'))) {
156 INTERNAL_ERROR ("illegal character in symbol %s: %c", val, ch);
159 fputc (':', sps->stream);
160 fputs (val, sps->stream);
164 sexp_integer (struct sprinter *sp, int val)
166 struct sprinter_sexp *sps = sexp_begin_value (sp);
168 fprintf (sps->stream, "%d", val);
172 sexp_boolean (struct sprinter *sp, bool val)
174 struct sprinter_sexp *sps = sexp_begin_value (sp);
176 fputs (val ? "t" : "nil", sps->stream);
180 sexp_null (struct sprinter *sp)
182 struct sprinter_sexp *sps = sexp_begin_value (sp);
184 fputs ("nil", sps->stream);
188 sexp_map_key (struct sprinter *sp, const char *key)
190 sexp_begin_value (sp);
192 sexp_keyword (sp, key);
196 sexp_set_prefix (unused (struct sprinter *sp), unused (const char *name))
201 sexp_separator (struct sprinter *sp)
203 struct sprinter_sexp *sps = (struct sprinter_sexp *) sp;
205 sps->insert_separator = true;
209 sprinter_sexp_create (const void *ctx, FILE *stream)
211 static const struct sprinter_sexp template = {
213 .begin_map = sexp_begin_map,
214 .begin_list = sexp_begin_list,
216 .string = sexp_string,
217 .string_len = sexp_string_len,
218 .integer = sexp_integer,
219 .boolean = sexp_boolean,
221 .map_key = sexp_map_key,
222 .separator = sexp_separator,
223 .set_prefix = sexp_set_prefix,
224 .is_text_printer = false,
227 struct sprinter_sexp *res;
229 res = talloc (ctx, struct sprinter_sexp);
234 res->stream = stream;