1 #include <inttypes.h>
2 #include <stdbool.h>
3 #include <stdio.h>
4 #include <talloc.h>
5 #include "sprinter.h"
6
7 struct sprinter_json {
8 struct sprinter vtable;
9 FILE *stream;
10 /* Top of the state stack, or NULL if the printer is not currently
11 * inside any aggregate types. */
12 struct json_state *state;
13
14 /* A flag to signify that a separator should be inserted in the
15 * output as soon as possible.
16 */
17 bool insert_separator;
18 };
19
20 struct json_state {
21 struct json_state *parent;
22 /* True if nothing has been printed in this aggregate yet.
23 * Suppresses the comma before a value. */
24 bool first;
25 /* The character that closes the current aggregate. */
26 char close;
27 };
28
29 /* Helper function to set up the stream to print a value. If this
30 * value follows another value, prints a comma. */
31 static struct sprinter_json *
json_begin_value(struct sprinter * sp)32 json_begin_value (struct sprinter *sp)
33 {
34 struct sprinter_json *spj = (struct sprinter_json *) sp;
35
36 if (spj->state) {
37 if (! spj->state->first) {
38 fputc (',', spj->stream);
39 if (spj->insert_separator) {
40 fputc ('\n', spj->stream);
41 spj->insert_separator = false;
42 } else {
43 fputc (' ', spj->stream);
44 }
45 } else {
46 spj->state->first = false;
47 }
48 }
49 return spj;
50 }
51
52 /* Helper function to begin an aggregate type. Prints the open
53 * character and pushes a new state frame. */
54 static void
json_begin_aggregate(struct sprinter * sp,char open,char close)55 json_begin_aggregate (struct sprinter *sp, char open, char close)
56 {
57 struct sprinter_json *spj = json_begin_value (sp);
58 struct json_state *state = talloc (spj, struct json_state);
59
60 fputc (open, spj->stream);
61 state->parent = spj->state;
62 state->first = true;
63 state->close = close;
64 spj->state = state;
65 }
66
67 static void
json_begin_map(struct sprinter * sp)68 json_begin_map (struct sprinter *sp)
69 {
70 json_begin_aggregate (sp, '{', '}');
71 }
72
73 static void
json_begin_list(struct sprinter * sp)74 json_begin_list (struct sprinter *sp)
75 {
76 json_begin_aggregate (sp, '[', ']');
77 }
78
79 static void
json_end(struct sprinter * sp)80 json_end (struct sprinter *sp)
81 {
82 struct sprinter_json *spj = (struct sprinter_json *) sp;
83 struct json_state *state = spj->state;
84
85 fputc (spj->state->close, spj->stream);
86 spj->state = state->parent;
87 talloc_free (state);
88 if (spj->state == NULL)
89 fputc ('\n', spj->stream);
90 }
91
92 /* This implementation supports embedded NULs as allowed by the JSON
93 * specification and Unicode. Support for *parsing* embedded NULs
94 * varies, but is generally not a problem outside of C-based parsers
95 * (Python's json module and Emacs' json.el take embedded NULs in
96 * stride). */
97 static void
json_string_len(struct sprinter * sp,const char * val,size_t len)98 json_string_len (struct sprinter *sp, const char *val, size_t len)
99 {
100 static const char *const escapes[] = {
101 ['\"'] = "\\\"", ['\\'] = "\\\\", ['\b'] = "\\b",
102 ['\f'] = "\\f", ['\n'] = "\\n", ['\t'] = "\\t"
103 };
104 struct sprinter_json *spj = json_begin_value (sp);
105
106 fputc ('"', spj->stream);
107 for (; len; ++val, --len) {
108 unsigned char ch = *val;
109 if (ch < ARRAY_SIZE (escapes) && escapes[ch])
110 fputs (escapes[ch], spj->stream);
111 else if (ch >= 32)
112 fputc (ch, spj->stream);
113 else
114 fprintf (spj->stream, "\\u%04x", ch);
115 }
116 fputc ('"', spj->stream);
117 }
118
119 static void
json_string(struct sprinter * sp,const char * val)120 json_string (struct sprinter *sp, const char *val)
121 {
122 if (val == NULL)
123 val = "";
124 json_string_len (sp, val, strlen (val));
125 }
126
127 static void
json_integer(struct sprinter * sp,int64_t val)128 json_integer (struct sprinter *sp, int64_t val)
129 {
130 struct sprinter_json *spj = json_begin_value (sp);
131
132 fprintf (spj->stream, "%" PRId64, val);
133 }
134
135 static void
json_boolean(struct sprinter * sp,bool val)136 json_boolean (struct sprinter *sp, bool val)
137 {
138 struct sprinter_json *spj = json_begin_value (sp);
139
140 fputs (val ? "true" : "false", spj->stream);
141 }
142
143 static void
json_null(struct sprinter * sp)144 json_null (struct sprinter *sp)
145 {
146 struct sprinter_json *spj = json_begin_value (sp);
147
148 fputs ("null", spj->stream);
149 }
150
151 static void
json_map_key(struct sprinter * sp,const char * key)152 json_map_key (struct sprinter *sp, const char *key)
153 {
154 struct sprinter_json *spj = (struct sprinter_json *) sp;
155
156 json_string (sp, key);
157 fputs (": ", spj->stream);
158 spj->state->first = true;
159 }
160
161 static void
json_set_prefix(unused (struct sprinter * sp),unused (const char * name))162 json_set_prefix (unused (struct sprinter *sp), unused (const char *name))
163 {
164 }
165
166 static void
json_separator(struct sprinter * sp)167 json_separator (struct sprinter *sp)
168 {
169 struct sprinter_json *spj = (struct sprinter_json *) sp;
170
171 spj->insert_separator = true;
172 }
173
174 struct sprinter *
sprinter_json_create(const void * ctx,FILE * stream)175 sprinter_json_create (const void *ctx, FILE *stream)
176 {
177 static const struct sprinter_json template = {
178 .vtable = {
179 .begin_map = json_begin_map,
180 .begin_list = json_begin_list,
181 .end = json_end,
182 .string = json_string,
183 .string_len = json_string_len,
184 .integer = json_integer,
185 .boolean = json_boolean,
186 .null = json_null,
187 .map_key = json_map_key,
188 .separator = json_separator,
189 .set_prefix = json_set_prefix,
190 .is_text_printer = false,
191 }
192 };
193 struct sprinter_json *res;
194
195 res = talloc (ctx, struct sprinter_json);
196 if (! res)
197 return NULL;
198
199 *res = template;
200 res->stream = stream;
201 return &res->vtable;
202 }
203