chiark / gitweb /
util: loop_write - accept 0-length message
[elogind.git] / src / shared / json.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2014 Lennart Poettering
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/types.h>
23 #include <math.h>
24
25 #include "macro.h"
26 #include "util.h"
27 #include "utf8.h"
28 #include "json.h"
29
30 enum {
31         STATE_NULL,
32         STATE_VALUE,
33         STATE_VALUE_POST,
34 };
35
36 static void inc_lines(unsigned *line, const char *s, size_t n) {
37         const char *p = s;
38
39         if (!line)
40                 return;
41
42         for (;;) {
43                 const char *f;
44
45                 f = memchr(p, '\n', n);
46                 if (!f)
47                         return;
48
49                 n -= (f - p) + 1;
50                 p = f + 1;
51                 (*line)++;
52         }
53 }
54
55 static int unhex_ucs2(const char *c, uint16_t *ret) {
56         int aa, bb, cc, dd;
57         uint16_t x;
58
59         assert(c);
60         assert(ret);
61
62         aa = unhexchar(c[0]);
63         if (aa < 0)
64                 return -EINVAL;
65
66         bb = unhexchar(c[1]);
67         if (bb < 0)
68                 return -EINVAL;
69
70         cc = unhexchar(c[2]);
71         if (cc < 0)
72                 return -EINVAL;
73
74         dd = unhexchar(c[3]);
75         if (dd < 0)
76                 return -EINVAL;
77
78         x =     ((uint16_t) aa << 12) |
79                 ((uint16_t) bb << 8) |
80                 ((uint16_t) cc << 4) |
81                 ((uint16_t) dd);
82
83         if (x <= 0)
84                 return -EINVAL;
85
86         *ret = x;
87
88         return 0;
89 }
90
91 static int json_parse_string(const char **p, char **ret) {
92         _cleanup_free_ char *s = NULL;
93         size_t n = 0, allocated = 0;
94         const char *c;
95
96         assert(p);
97         assert(*p);
98         assert(ret);
99
100         c = *p;
101
102         if (*c != '"')
103                 return -EINVAL;
104
105         c++;
106
107         for (;;) {
108                 int len;
109
110                 /* Check for EOF */
111                 if (*c == 0)
112                         return -EINVAL;
113
114                 /* Check for control characters 0x00..0x1f */
115                 if (*c > 0 && *c < ' ')
116                         return -EINVAL;
117
118                 /* Check for control character 0x7f */
119                 if (*c == 0x7f)
120                         return -EINVAL;
121
122                 if (*c == '"') {
123                         if (!s) {
124                                 s = strdup("");
125                                 if (!s)
126                                         return -ENOMEM;
127                         } else
128                                 s[n] = 0;
129
130                         *p = c + 1;
131
132                         *ret = s;
133                         s = NULL;
134                         return JSON_STRING;
135                 }
136
137                 if (*c == '\\') {
138                         char ch = 0;
139                         c++;
140
141                         if (*c == 0)
142                                 return -EINVAL;
143
144                         if (IN_SET(*c, '"', '\\', '/'))
145                                 ch = *c;
146                         else if (*c == 'b')
147                                 ch = '\b';
148                         else if (*c == 'f')
149                                 ch = '\f';
150                         else if (*c == 'n')
151                                 ch = '\n';
152                         else if (*c == 'r')
153                                 ch = '\r';
154                         else if (*c == 't')
155                                 ch = '\t';
156                         else if (*c == 'u') {
157                                 uint16_t x;
158                                 int r;
159
160                                 r = unhex_ucs2(c + 1, &x);
161                                 if (r < 0)
162                                         return r;
163
164                                 c += 5;
165
166                                 if (!GREEDY_REALLOC(s, allocated, n + 4))
167                                         return -ENOMEM;
168
169                                 if (!utf16_is_surrogate(x))
170                                         n += utf8_encode_unichar(s + n, x);
171                                 else if (utf16_is_trailing_surrogate(x))
172                                         return -EINVAL;
173                                 else {
174                                         uint16_t y;
175
176                                         if (c[0] != '\\' || c[1] != 'u')
177                                                 return -EINVAL;
178
179                                         r = unhex_ucs2(c + 2, &y);
180                                         if (r < 0)
181                                                 return r;
182
183                                         c += 6;
184
185                                         if (!utf16_is_trailing_surrogate(y))
186                                                 return -EINVAL;
187
188                                         n += utf8_encode_unichar(s + n, utf16_surrogate_pair_to_unichar(x, y));
189                                 }
190
191                                 continue;
192                         } else
193                                 return -EINVAL;
194
195                         if (!GREEDY_REALLOC(s, allocated, n + 2))
196                                 return -ENOMEM;
197
198                         s[n++] = ch;
199                         c ++;
200                         continue;
201                 }
202
203                 len = utf8_encoded_valid_unichar(c);
204                 if (len < 0)
205                         return len;
206
207                 if (!GREEDY_REALLOC(s, allocated, n + len + 1))
208                         return -ENOMEM;
209
210                 memcpy(s + n, c, len);
211                 n += len;
212                 c += len;
213         }
214 }
215
216 static int json_parse_number(const char **p, union json_value *ret) {
217         bool negative = false, exponent_negative = false, is_double = false;
218         double x = 0.0, y = 0.0, exponent = 0.0, shift = 1.0;
219         intmax_t i = 0;
220         const char *c;
221
222         assert(p);
223         assert(*p);
224         assert(ret);
225
226         c = *p;
227
228         if (*c == '-') {
229                 negative = true;
230                 c++;
231         }
232
233         if (*c == '0')
234                 c++;
235         else {
236                 if (!strchr("123456789", *c) || *c == 0)
237                         return -EINVAL;
238
239                 do {
240                         if (!is_double) {
241                                 int64_t t;
242
243                                 t = 10 * i + (*c - '0');
244                                 if (t < i) /* overflow */
245                                         is_double = false;
246                                 else
247                                         i = t;
248                         }
249
250                         x = 10.0 * x + (*c - '0');
251                         c++;
252                 } while (strchr("0123456789", *c) && *c != 0);
253         }
254
255         if (*c == '.') {
256                 is_double = true;
257                 c++;
258
259                 if (!strchr("0123456789", *c) || *c == 0)
260                         return -EINVAL;
261
262                 do {
263                         y = 10.0 * y + (*c - '0');
264                         shift = 10.0 * shift;
265                         c++;
266                 } while (strchr("0123456789", *c) && *c != 0);
267         }
268
269         if (*c == 'e' || *c == 'E') {
270                 is_double = true;
271                 c++;
272
273                 if (*c == '-') {
274                         exponent_negative = true;
275                         c++;
276                 } else if (*c == '+')
277                         c++;
278
279                 if (!strchr("0123456789", *c) || *c == 0)
280                         return -EINVAL;
281
282                 do {
283                         exponent = 10.0 * exponent + (*c - '0');
284                         c++;
285                 } while (strchr("0123456789", *c) && *c != 0);
286         }
287
288         if (*c != 0)
289                 return -EINVAL;
290
291         *p = c;
292
293         if (is_double) {
294                 ret->real = ((negative ? -1.0 : 1.0) * (x + (y / shift))) * exp10((exponent_negative ? -1.0 : 1.0) * exponent);
295                 return JSON_REAL;
296         } else {
297                 ret->integer = negative ? -i : i;
298                 return JSON_INTEGER;
299         }
300 }
301
302 int json_tokenize(
303                 const char **p,
304                 char **ret_string,
305                 union json_value *ret_value,
306                 void **state,
307                 unsigned *line) {
308
309         const char *c;
310         int t;
311         int r;
312
313         assert(p);
314         assert(*p);
315         assert(ret_string);
316         assert(ret_value);
317         assert(state);
318
319         t = PTR_TO_INT(*state);
320         c = *p;
321
322         if (t == STATE_NULL) {
323                 if (line)
324                         *line = 1;
325                 t = STATE_VALUE;
326         }
327
328         for (;;) {
329                 const char *b;
330
331                 b = c + strspn(c, WHITESPACE);
332                 if (*b == 0)
333                         return JSON_END;
334
335                 inc_lines(line, c, b - c);
336                 c = b;
337
338                 switch (t) {
339
340                 case STATE_VALUE:
341
342                         if (*c == '{') {
343                                 *ret_string = NULL;
344                                 *ret_value = JSON_VALUE_NULL;
345                                 *p = c + 1;
346                                 *state = INT_TO_PTR(STATE_VALUE);
347                                 return JSON_OBJECT_OPEN;
348
349                         } else if (*c == '}') {
350                                 *ret_string = NULL;
351                                 *ret_value = JSON_VALUE_NULL;
352                                 *p = c + 1;
353                                 *state = INT_TO_PTR(STATE_VALUE_POST);
354                                 return JSON_OBJECT_CLOSE;
355
356                         } else if (*c == '[') {
357                                 *ret_string = NULL;
358                                 *ret_value = JSON_VALUE_NULL;
359                                 *p = c + 1;
360                                 *state = INT_TO_PTR(STATE_VALUE);
361                                 return JSON_ARRAY_OPEN;
362
363                         } else if (*c == ']') {
364                                 *ret_string = NULL;
365                                 *ret_value = JSON_VALUE_NULL;
366                                 *p = c + 1;
367                                 *state = INT_TO_PTR(STATE_VALUE_POST);
368                                 return JSON_ARRAY_CLOSE;
369
370                         } else if (*c == '"') {
371                                 r = json_parse_string(&c, ret_string);
372                                 if (r < 0)
373                                         return r;
374
375                                 *ret_value = JSON_VALUE_NULL;
376                                 *p = c;
377                                 *state = INT_TO_PTR(STATE_VALUE_POST);
378                                 return r;
379
380                         } else if (strchr("-0123456789", *c)) {
381                                 r = json_parse_number(&c, ret_value);
382                                 if (r < 0)
383                                         return r;
384
385                                 *ret_string = NULL;
386                                 *p = c;
387                                 *state = INT_TO_PTR(STATE_VALUE_POST);
388                                 return r;
389
390                         } else if (startswith(c, "true")) {
391                                 *ret_string = NULL;
392                                 ret_value->boolean = true;
393                                 *p = c + 4;
394                                 *state = INT_TO_PTR(STATE_VALUE_POST);
395                                 return JSON_BOOLEAN;
396
397                         } else if (startswith(c, "false")) {
398                                 *ret_string = NULL;
399                                 ret_value->boolean = false;
400                                 *p = c + 5;
401                                 *state = INT_TO_PTR(STATE_VALUE_POST);
402                                 return JSON_BOOLEAN;
403
404                         } else if (startswith(c, "null")) {
405                                 *ret_string = NULL;
406                                 *ret_value = JSON_VALUE_NULL;
407                                 *p = c + 4;
408                                 *state = INT_TO_PTR(STATE_VALUE_POST);
409                                 return JSON_NULL;
410
411                         } else
412                                 return -EINVAL;
413
414                 case STATE_VALUE_POST:
415
416                         if (*c == ':') {
417                                 *ret_string = NULL;
418                                 *ret_value = JSON_VALUE_NULL;
419                                 *p = c + 1;
420                                 *state = INT_TO_PTR(STATE_VALUE);
421                                 return JSON_COLON;
422                         } else if (*c == ',') {
423                                 *ret_string = NULL;
424                                 *ret_value = JSON_VALUE_NULL;
425                                 *p = c + 1;
426                                 *state = INT_TO_PTR(STATE_VALUE);
427                                 return JSON_COMMA;
428                         } else if (*c == '}') {
429                                 *ret_string = NULL;
430                                 *ret_value = JSON_VALUE_NULL;
431                                 *p = c + 1;
432                                 *state = INT_TO_PTR(STATE_VALUE_POST);
433                                 return JSON_OBJECT_CLOSE;
434                         } else if (*c == ']') {
435                                 *ret_string = NULL;
436                                 *ret_value = JSON_VALUE_NULL;
437                                 *p = c + 1;
438                                 *state = INT_TO_PTR(STATE_VALUE_POST);
439                                 return JSON_ARRAY_CLOSE;
440                         } else
441                                 return -EINVAL;
442                 }
443
444         }
445 }