blob: 7256a8ceda9ed0a19a75e55b388f711def164c5c [file] [log] [blame]
Bram Moolenaar520e1e42016-01-23 19:46:28 +01001/* vi:set ts=8 sts=4 sw=4:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * json.c: Encoding and decoding JSON.
12 *
Bram Moolenaar009d84a2016-01-28 14:12:00 +010013 * Follows this standard: https://tools.ietf.org/html/rfc7159.html
Bram Moolenaar520e1e42016-01-23 19:46:28 +010014 */
15
16#include "vim.h"
17
18#if defined(FEAT_EVAL) || defined(PROTO)
Bram Moolenaarfcaaae62016-01-24 16:49:11 +010019static int json_encode_item(garray_T *gap, typval_T *val, int copyID);
Bram Moolenaar520e1e42016-01-23 19:46:28 +010020static void json_decode_item(js_read_T *reader, typval_T *res);
21
22/*
23 * Encode "val" into a JSON format string.
24 */
25 char_u *
26json_encode(typval_T *val)
27{
28 garray_T ga;
29
30 /* Store bytes in the growarray. */
31 ga_init2(&ga, 1, 4000);
32 json_encode_item(&ga, val, get_copyID());
33 return ga.ga_data;
34}
35
36 static void
37write_string(garray_T *gap, char_u *str)
38{
39 char_u *res = str;
40 char_u numbuf[NUMBUFLEN];
41
42 if (res == NULL)
43 ga_concat(gap, (char_u *)"null");
44 else
45 {
46 ga_append(gap, '"');
47 while (*res != NUL)
48 {
49 int c = PTR2CHAR(res);
50
51 switch (c)
52 {
53 case 0x08:
54 ga_append(gap, '\\'); ga_append(gap, 'b'); break;
55 case 0x09:
56 ga_append(gap, '\\'); ga_append(gap, 't'); break;
57 case 0x0a:
58 ga_append(gap, '\\'); ga_append(gap, 'n'); break;
59 case 0x0c:
60 ga_append(gap, '\\'); ga_append(gap, 'f'); break;
61 case 0x0d:
62 ga_append(gap, '\\'); ga_append(gap, 'r'); break;
63 case 0x22: /* " */
64 case 0x5c: /* \ */
65 ga_append(gap, '\\');
66 ga_append(gap, c);
67 break;
68 default:
69 if (c >= 0x20)
70 {
Bram Moolenaarfa06a512016-01-28 22:46:58 +010071#ifdef FEAT_MBYTE
Bram Moolenaar520e1e42016-01-23 19:46:28 +010072 numbuf[mb_char2bytes(c, numbuf)] = NUL;
Bram Moolenaarfa06a512016-01-28 22:46:58 +010073#else
74 numbuf[0] = c;
75 numbuf[1] = NUL;
76#endif
Bram Moolenaar520e1e42016-01-23 19:46:28 +010077 ga_concat(gap, numbuf);
78 }
79 else
80 {
81 vim_snprintf((char *)numbuf, NUMBUFLEN,
82 "\\u%04lx", (long)c);
83 ga_concat(gap, numbuf);
84 }
85 }
86 mb_cptr_adv(res);
87 }
88 ga_append(gap, '"');
89 }
90}
91
Bram Moolenaarfcaaae62016-01-24 16:49:11 +010092/*
93 * Encode "val" into "gap".
94 * Return FAIL or OK.
95 */
96 static int
Bram Moolenaar520e1e42016-01-23 19:46:28 +010097json_encode_item(garray_T *gap, typval_T *val, int copyID)
98{
99 char_u numbuf[NUMBUFLEN];
100 char_u *res;
101 list_T *l;
102 dict_T *d;
103
104 switch (val->v_type)
105 {
106 case VAR_SPECIAL:
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100107 switch (val->vval.v_number)
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100108 {
109 case VVAL_FALSE: ga_concat(gap, (char_u *)"false"); break;
110 case VVAL_TRUE: ga_concat(gap, (char_u *)"true"); break;
111 case VVAL_NONE: break;
112 case VVAL_NULL: ga_concat(gap, (char_u *)"null"); break;
113 }
114 break;
115
116 case VAR_NUMBER:
117 vim_snprintf((char *)numbuf, NUMBUFLEN, "%ld",
118 (long)val->vval.v_number);
119 ga_concat(gap, numbuf);
120 break;
121
122 case VAR_STRING:
123 res = val->vval.v_string;
124 write_string(gap, res);
125 break;
126
127 case VAR_FUNC:
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100128 /* no JSON equivalent */
129 EMSG(_(e_invarg));
130 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100131
132 case VAR_LIST:
133 l = val->vval.v_list;
134 if (l == NULL)
135 ga_concat(gap, (char_u *)"null");
136 else
137 {
138 if (l->lv_copyID == copyID)
139 ga_concat(gap, (char_u *)"[]");
140 else
141 {
142 listitem_T *li;
143
144 l->lv_copyID = copyID;
145 ga_append(gap, '[');
146 for (li = l->lv_first; li != NULL && !got_int; )
147 {
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100148 if (json_encode_item(gap, &li->li_tv, copyID) == FAIL)
149 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100150 li = li->li_next;
151 if (li != NULL)
152 ga_append(gap, ',');
153 }
154 ga_append(gap, ']');
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100155 l->lv_copyID = 0;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100156 }
157 }
158 break;
159
160 case VAR_DICT:
161 d = val->vval.v_dict;
162 if (d == NULL)
163 ga_concat(gap, (char_u *)"null");
164 else
165 {
166 if (d->dv_copyID == copyID)
167 ga_concat(gap, (char_u *)"{}");
168 else
169 {
170 int first = TRUE;
171 int todo = (int)d->dv_hashtab.ht_used;
172 hashitem_T *hi;
173
174 d->dv_copyID = copyID;
175 ga_append(gap, '{');
176
177 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int;
178 ++hi)
179 if (!HASHITEM_EMPTY(hi))
180 {
181 --todo;
182 if (first)
183 first = FALSE;
184 else
185 ga_append(gap, ',');
186 write_string(gap, hi->hi_key);
187 ga_append(gap, ':');
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100188 if (json_encode_item(gap, &dict_lookup(hi)->di_tv,
189 copyID) == FAIL)
190 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100191 }
192 ga_append(gap, '}');
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100193 d->dv_copyID = 0;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100194 }
195 }
196 break;
197
198#ifdef FEAT_FLOAT
199 case VAR_FLOAT:
200 vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", val->vval.v_float);
201 ga_concat(gap, numbuf);
202 break;
203#endif
204 default: EMSG2(_(e_intern2), "json_encode_item()"); break;
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100205 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100206 }
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100207 return OK;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100208}
209
210/*
211 * Skip white space in "reader".
212 */
213 static void
214json_skip_white(js_read_T *reader)
215{
216 int c;
217
218 while ((c = reader->js_buf[reader->js_used]) == ' '
219 || c == TAB || c == NL || c == CAR)
220 ++reader->js_used;
221}
222
223/*
224 * Make sure there are at least enough characters buffered to read a number.
225 */
226 static void
227json_fill_buffer(js_read_T *reader UNUSED)
228{
229 /* TODO */
230}
231
232 static void
233json_decode_array(js_read_T *reader, typval_T *res)
234{
235 char_u *p;
236 typval_T item;
237 listitem_T *li;
238
239 if (rettv_list_alloc(res) == FAIL)
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100240 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100241 ++reader->js_used; /* consume the '[' */
242
243 while (TRUE)
244 {
245 json_skip_white(reader);
246 p = reader->js_buf + reader->js_used;
247 if (*p == NUL)
248 goto fail;
249 if (*p == ']')
250 {
251 ++reader->js_used; /* consume the ']' */
252 return;
253 }
254
255 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
256 json_fill_buffer(reader);
257
258 json_decode_item(reader, &item);
259 li = listitem_alloc();
260 if (li == NULL)
261 return;
262 li->li_tv = item;
263 list_append(res->vval.v_list, li);
264
265 json_skip_white(reader);
266 p = reader->js_buf + reader->js_used;
267 if (*p == ',')
268 ++reader->js_used;
269 else if (*p != ']')
270 goto fail;
271 }
272fail:
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100273 EMSG(_(e_invarg));
274failsilent:
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100275 res->v_type = VAR_SPECIAL;
276 res->vval.v_number = VVAL_NONE;
277}
278
279 static void
280json_decode_object(js_read_T *reader, typval_T *res)
281{
282 char_u *p;
283 typval_T tvkey;
284 typval_T item;
285 dictitem_T *di;
286 char_u buf[NUMBUFLEN];
287 char_u *key;
288
289 if (rettv_dict_alloc(res) == FAIL)
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100290 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100291 ++reader->js_used; /* consume the '{' */
292
293 while (TRUE)
294 {
295 json_skip_white(reader);
296 p = reader->js_buf + reader->js_used;
297 if (*p == NUL)
298 goto fail;
299 if (*p == '}')
300 {
301 ++reader->js_used; /* consume the '}' */
302 return;
303 }
304
305 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
306 json_fill_buffer(reader);
307 json_decode_item(reader, &tvkey);
308 key = get_tv_string_buf_chk(&tvkey, buf);
309 if (key == NULL || *key == NUL)
310 {
311 /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
312 if (key != NULL)
313 EMSG(_(e_emptykey));
314 clear_tv(&tvkey);
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100315 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100316 }
317
318 json_skip_white(reader);
319 p = reader->js_buf + reader->js_used;
320 if (*p != ':')
321 {
322 clear_tv(&tvkey);
323 goto fail;
324 }
325 ++reader->js_used;
326 json_skip_white(reader);
327
328 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
329 json_fill_buffer(reader);
330 json_decode_item(reader, &item);
331
332 di = dictitem_alloc(key);
333 clear_tv(&tvkey);
334 if (di == NULL)
335 {
336 clear_tv(&item);
337 goto fail;
338 }
339 di->di_tv = item;
Bram Moolenaar2dedb452016-01-23 21:38:51 +0100340 if (dict_add(res->vval.v_dict, di) == FAIL)
341 dictitem_free(di);
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100342
343 json_skip_white(reader);
344 p = reader->js_buf + reader->js_used;
345 if (*p == ',')
346 ++reader->js_used;
347 else if (*p != '}')
348 goto fail;
349 }
350fail:
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100351 EMSG(_(e_invarg));
352failsilent:
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100353 res->v_type = VAR_SPECIAL;
354 res->vval.v_number = VVAL_NONE;
355}
356
357 static void
358json_decode_string(js_read_T *reader, typval_T *res)
359{
360 garray_T ga;
361 int len;
362 char_u *p = reader->js_buf + reader->js_used + 1;
363 int c;
364 long nr;
365 char_u buf[NUMBUFLEN];
366
367 ga_init2(&ga, 1, 200);
368
369 /* TODO: fill buffer when needed. */
370 while (*p != NUL && *p != '"')
371 {
372 if (*p == '\\')
373 {
374 c = -1;
375 switch (p[1])
376 {
377 case 'b': c = BS; break;
378 case 't': c = TAB; break;
379 case 'n': c = NL; break;
380 case 'f': c = FF; break;
381 case 'r': c = CAR; break;
382 case 'u':
383 vim_str2nr(p + 2, NULL, &len,
384 STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
385 p += len + 2;
386#ifdef FEAT_MBYTE
387 buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
388 ga_concat(&ga, buf);
389#else
390 ga_append(&ga, nr);
391#endif
392 break;
393 default: c = p[1]; break;
394 }
395 if (c > 0)
396 {
397 p += 2;
398 ga_append(&ga, c);
399 }
400 }
401 else
402 {
403 len = MB_PTR2LEN(p);
404 if (ga_grow(&ga, len) == OK)
405 {
406 mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
407 ga.ga_len += len;
408 }
409 p += len;
410 }
411 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
412 {
413 reader->js_used = (int)(p - reader->js_buf);
414 json_fill_buffer(reader);
415 p = reader->js_buf + reader->js_used;
416 }
417 }
418 reader->js_used = (int)(p - reader->js_buf);
419 if (*p == '"')
420 {
421 ++reader->js_used;
422 res->v_type = VAR_STRING;
Bram Moolenaar2dedb452016-01-23 21:38:51 +0100423 if (ga.ga_data == NULL)
424 res->vval.v_string = NULL;
425 else
426 res->vval.v_string = vim_strsave(ga.ga_data);
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100427 }
428 else
429 {
Bram Moolenaarbd4593f2016-01-23 22:51:07 +0100430 EMSG(_(e_invarg));
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100431 res->v_type = VAR_SPECIAL;
432 res->vval.v_number = VVAL_NONE;
433 }
434 ga_clear(&ga);
435}
436
437/*
438 * Decode one item and put it in "result".
439 * Must already have skipped white space.
440 */
441 static void
442json_decode_item(js_read_T *reader, typval_T *res)
443{
444 char_u *p = reader->js_buf + reader->js_used;
445
446 switch (*p)
447 {
448 case '[': /* array */
449 json_decode_array(reader, res);
450 return;
451
452 case '{': /* object */
453 json_decode_object(reader, res);
454 return;
455
456 case '"': /* string */
457 json_decode_string(reader, res);
458 return;
459
460 case ',': /* comma: empty item */
461 case NUL: /* empty */
462 res->v_type = VAR_SPECIAL;
463 res->vval.v_number = VVAL_NONE;
464 return;
465
466 default:
467 if (VIM_ISDIGIT(*p) || *p == '-')
468 {
469 int len;
470 char_u *sp = p;
471#ifdef FEAT_FLOAT
472 if (*sp == '-')
473 ++sp;
474 sp = skipdigits(sp);
475 if (*sp == '.' || *sp == 'e' || *sp == 'E')
476 {
477 res->v_type = VAR_FLOAT;
478 len = string2float(p, &res->vval.v_float);
479 }
480 else
481#endif
482 {
483 long nr;
484
485 res->v_type = VAR_NUMBER;
486 vim_str2nr(reader->js_buf + reader->js_used,
487 NULL, &len, 0, /* what */
488 &nr, NULL, 0);
489 res->vval.v_number = nr;
490 }
491 reader->js_used += len;
492 return;
493 }
494 if (STRNICMP((char *)p, "false", 5) == 0)
495 {
496 reader->js_used += 5;
497 res->v_type = VAR_SPECIAL;
498 res->vval.v_number = VVAL_FALSE;
499 return;
500 }
501 if (STRNICMP((char *)p, "true", 4) == 0)
502 {
503 reader->js_used += 4;
504 res->v_type = VAR_SPECIAL;
505 res->vval.v_number = VVAL_TRUE;
506 return;
507 }
508 if (STRNICMP((char *)p, "null", 4) == 0)
509 {
510 reader->js_used += 4;
511 res->v_type = VAR_SPECIAL;
512 res->vval.v_number = VVAL_NULL;
513 return;
514 }
515 break;
516 }
517
518 EMSG(_(e_invarg));
519 res->v_type = VAR_SPECIAL;
520 res->vval.v_number = VVAL_NONE;
521}
522
523/*
524 * Decode the JSON from "reader" and store the result in "res".
525 */
526 void
527json_decode(js_read_T *reader, typval_T *res)
528{
529 json_skip_white(reader);
530 json_decode_item(reader, res);
531 json_skip_white(reader);
532 if (reader->js_buf[reader->js_used] != NUL)
533 EMSG(_(e_invarg));
534}
535#endif