blob: a56104217c2e6d2adfd9b3da1b989491e2bcdcb6 [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
Bram Moolenaarfb1f6262016-01-31 20:24:32 +010036/*
37 * Encode ["nr", "val"] into a JSON format string.
38 * Returns NULL when out of memory.
39 */
40 char_u *
41json_encode_nr_expr(int nr, typval_T *val)
42{
43 typval_T listtv;
44 typval_T nrtv;
45 char_u *text;
46
47 nrtv.v_type = VAR_NUMBER;
48 nrtv.vval.v_number = nr;
49 if (rettv_list_alloc(&listtv) == FAIL)
50 return NULL;
51 if (list_append_tv(listtv.vval.v_list, &nrtv) == FAIL
52 || list_append_tv(listtv.vval.v_list, val) == FAIL)
53 {
54 list_unref(listtv.vval.v_list);
55 return NULL;
56 }
57
58 text = json_encode(&listtv);
59 list_unref(listtv.vval.v_list);
60 return text;
61}
62
Bram Moolenaar520e1e42016-01-23 19:46:28 +010063 static void
64write_string(garray_T *gap, char_u *str)
65{
66 char_u *res = str;
67 char_u numbuf[NUMBUFLEN];
68
69 if (res == NULL)
70 ga_concat(gap, (char_u *)"null");
71 else
72 {
73 ga_append(gap, '"');
74 while (*res != NUL)
75 {
76 int c = PTR2CHAR(res);
77
78 switch (c)
79 {
80 case 0x08:
81 ga_append(gap, '\\'); ga_append(gap, 'b'); break;
82 case 0x09:
83 ga_append(gap, '\\'); ga_append(gap, 't'); break;
84 case 0x0a:
85 ga_append(gap, '\\'); ga_append(gap, 'n'); break;
86 case 0x0c:
87 ga_append(gap, '\\'); ga_append(gap, 'f'); break;
88 case 0x0d:
89 ga_append(gap, '\\'); ga_append(gap, 'r'); break;
90 case 0x22: /* " */
91 case 0x5c: /* \ */
92 ga_append(gap, '\\');
93 ga_append(gap, c);
94 break;
95 default:
96 if (c >= 0x20)
97 {
Bram Moolenaarfa06a512016-01-28 22:46:58 +010098#ifdef FEAT_MBYTE
Bram Moolenaar520e1e42016-01-23 19:46:28 +010099 numbuf[mb_char2bytes(c, numbuf)] = NUL;
Bram Moolenaarfa06a512016-01-28 22:46:58 +0100100#else
101 numbuf[0] = c;
102 numbuf[1] = NUL;
103#endif
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100104 ga_concat(gap, numbuf);
105 }
106 else
107 {
108 vim_snprintf((char *)numbuf, NUMBUFLEN,
109 "\\u%04lx", (long)c);
110 ga_concat(gap, numbuf);
111 }
112 }
113 mb_cptr_adv(res);
114 }
115 ga_append(gap, '"');
116 }
117}
118
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100119/*
120 * Encode "val" into "gap".
121 * Return FAIL or OK.
122 */
123 static int
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100124json_encode_item(garray_T *gap, typval_T *val, int copyID)
125{
126 char_u numbuf[NUMBUFLEN];
127 char_u *res;
128 list_T *l;
129 dict_T *d;
130
131 switch (val->v_type)
132 {
133 case VAR_SPECIAL:
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100134 switch (val->vval.v_number)
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100135 {
136 case VVAL_FALSE: ga_concat(gap, (char_u *)"false"); break;
137 case VVAL_TRUE: ga_concat(gap, (char_u *)"true"); break;
138 case VVAL_NONE: break;
139 case VVAL_NULL: ga_concat(gap, (char_u *)"null"); break;
140 }
141 break;
142
143 case VAR_NUMBER:
144 vim_snprintf((char *)numbuf, NUMBUFLEN, "%ld",
145 (long)val->vval.v_number);
146 ga_concat(gap, numbuf);
147 break;
148
149 case VAR_STRING:
150 res = val->vval.v_string;
151 write_string(gap, res);
152 break;
153
154 case VAR_FUNC:
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100155 /* no JSON equivalent */
156 EMSG(_(e_invarg));
157 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100158
159 case VAR_LIST:
160 l = val->vval.v_list;
161 if (l == NULL)
162 ga_concat(gap, (char_u *)"null");
163 else
164 {
165 if (l->lv_copyID == copyID)
166 ga_concat(gap, (char_u *)"[]");
167 else
168 {
169 listitem_T *li;
170
171 l->lv_copyID = copyID;
172 ga_append(gap, '[');
173 for (li = l->lv_first; li != NULL && !got_int; )
174 {
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100175 if (json_encode_item(gap, &li->li_tv, copyID) == FAIL)
176 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100177 li = li->li_next;
178 if (li != NULL)
179 ga_append(gap, ',');
180 }
181 ga_append(gap, ']');
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100182 l->lv_copyID = 0;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100183 }
184 }
185 break;
186
187 case VAR_DICT:
188 d = val->vval.v_dict;
189 if (d == NULL)
190 ga_concat(gap, (char_u *)"null");
191 else
192 {
193 if (d->dv_copyID == copyID)
194 ga_concat(gap, (char_u *)"{}");
195 else
196 {
197 int first = TRUE;
198 int todo = (int)d->dv_hashtab.ht_used;
199 hashitem_T *hi;
200
201 d->dv_copyID = copyID;
202 ga_append(gap, '{');
203
204 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int;
205 ++hi)
206 if (!HASHITEM_EMPTY(hi))
207 {
208 --todo;
209 if (first)
210 first = FALSE;
211 else
212 ga_append(gap, ',');
213 write_string(gap, hi->hi_key);
214 ga_append(gap, ':');
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100215 if (json_encode_item(gap, &dict_lookup(hi)->di_tv,
216 copyID) == FAIL)
217 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100218 }
219 ga_append(gap, '}');
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100220 d->dv_copyID = 0;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100221 }
222 }
223 break;
224
225#ifdef FEAT_FLOAT
226 case VAR_FLOAT:
227 vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", val->vval.v_float);
228 ga_concat(gap, numbuf);
229 break;
230#endif
231 default: EMSG2(_(e_intern2), "json_encode_item()"); break;
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100232 return FAIL;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100233 }
Bram Moolenaarfcaaae62016-01-24 16:49:11 +0100234 return OK;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100235}
236
237/*
238 * Skip white space in "reader".
239 */
240 static void
241json_skip_white(js_read_T *reader)
242{
243 int c;
244
245 while ((c = reader->js_buf[reader->js_used]) == ' '
246 || c == TAB || c == NL || c == CAR)
247 ++reader->js_used;
248}
249
250/*
251 * Make sure there are at least enough characters buffered to read a number.
252 */
253 static void
254json_fill_buffer(js_read_T *reader UNUSED)
255{
256 /* TODO */
257}
258
259 static void
260json_decode_array(js_read_T *reader, typval_T *res)
261{
262 char_u *p;
263 typval_T item;
264 listitem_T *li;
265
266 if (rettv_list_alloc(res) == FAIL)
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100267 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100268 ++reader->js_used; /* consume the '[' */
269
270 while (TRUE)
271 {
272 json_skip_white(reader);
273 p = reader->js_buf + reader->js_used;
274 if (*p == NUL)
275 goto fail;
276 if (*p == ']')
277 {
278 ++reader->js_used; /* consume the ']' */
279 return;
280 }
281
282 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
283 json_fill_buffer(reader);
284
285 json_decode_item(reader, &item);
286 li = listitem_alloc();
287 if (li == NULL)
288 return;
289 li->li_tv = item;
290 list_append(res->vval.v_list, li);
291
292 json_skip_white(reader);
293 p = reader->js_buf + reader->js_used;
294 if (*p == ',')
295 ++reader->js_used;
296 else if (*p != ']')
297 goto fail;
298 }
299fail:
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100300 EMSG(_(e_invarg));
301failsilent:
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100302 res->v_type = VAR_SPECIAL;
303 res->vval.v_number = VVAL_NONE;
304}
305
306 static void
307json_decode_object(js_read_T *reader, typval_T *res)
308{
309 char_u *p;
310 typval_T tvkey;
311 typval_T item;
312 dictitem_T *di;
313 char_u buf[NUMBUFLEN];
314 char_u *key;
315
316 if (rettv_dict_alloc(res) == FAIL)
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100317 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100318 ++reader->js_used; /* consume the '{' */
319
320 while (TRUE)
321 {
322 json_skip_white(reader);
323 p = reader->js_buf + reader->js_used;
324 if (*p == NUL)
325 goto fail;
326 if (*p == '}')
327 {
328 ++reader->js_used; /* consume the '}' */
329 return;
330 }
331
332 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
333 json_fill_buffer(reader);
334 json_decode_item(reader, &tvkey);
335 key = get_tv_string_buf_chk(&tvkey, buf);
336 if (key == NULL || *key == NUL)
337 {
338 /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
339 if (key != NULL)
340 EMSG(_(e_emptykey));
341 clear_tv(&tvkey);
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100342 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100343 }
344
345 json_skip_white(reader);
346 p = reader->js_buf + reader->js_used;
347 if (*p != ':')
348 {
349 clear_tv(&tvkey);
350 goto fail;
351 }
352 ++reader->js_used;
353 json_skip_white(reader);
354
355 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
356 json_fill_buffer(reader);
357 json_decode_item(reader, &item);
358
359 di = dictitem_alloc(key);
360 clear_tv(&tvkey);
361 if (di == NULL)
362 {
363 clear_tv(&item);
364 goto fail;
365 }
366 di->di_tv = item;
Bram Moolenaar2dedb452016-01-23 21:38:51 +0100367 if (dict_add(res->vval.v_dict, di) == FAIL)
368 dictitem_free(di);
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100369
370 json_skip_white(reader);
371 p = reader->js_buf + reader->js_used;
372 if (*p == ',')
373 ++reader->js_used;
374 else if (*p != '}')
375 goto fail;
376 }
377fail:
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100378 EMSG(_(e_invarg));
379failsilent:
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100380 res->v_type = VAR_SPECIAL;
381 res->vval.v_number = VVAL_NONE;
382}
383
384 static void
385json_decode_string(js_read_T *reader, typval_T *res)
386{
387 garray_T ga;
388 int len;
389 char_u *p = reader->js_buf + reader->js_used + 1;
390 int c;
391 long nr;
392 char_u buf[NUMBUFLEN];
393
394 ga_init2(&ga, 1, 200);
395
396 /* TODO: fill buffer when needed. */
397 while (*p != NUL && *p != '"')
398 {
399 if (*p == '\\')
400 {
401 c = -1;
402 switch (p[1])
403 {
404 case 'b': c = BS; break;
405 case 't': c = TAB; break;
406 case 'n': c = NL; break;
407 case 'f': c = FF; break;
408 case 'r': c = CAR; break;
409 case 'u':
410 vim_str2nr(p + 2, NULL, &len,
411 STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
412 p += len + 2;
413#ifdef FEAT_MBYTE
414 buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
415 ga_concat(&ga, buf);
416#else
417 ga_append(&ga, nr);
418#endif
419 break;
420 default: c = p[1]; break;
421 }
422 if (c > 0)
423 {
424 p += 2;
425 ga_append(&ga, c);
426 }
427 }
428 else
429 {
430 len = MB_PTR2LEN(p);
431 if (ga_grow(&ga, len) == OK)
432 {
433 mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
434 ga.ga_len += len;
435 }
436 p += len;
437 }
438 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
439 {
440 reader->js_used = (int)(p - reader->js_buf);
441 json_fill_buffer(reader);
442 p = reader->js_buf + reader->js_used;
443 }
444 }
445 reader->js_used = (int)(p - reader->js_buf);
446 if (*p == '"')
447 {
448 ++reader->js_used;
449 res->v_type = VAR_STRING;
Bram Moolenaar2dedb452016-01-23 21:38:51 +0100450 if (ga.ga_data == NULL)
451 res->vval.v_string = NULL;
452 else
453 res->vval.v_string = vim_strsave(ga.ga_data);
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100454 }
455 else
456 {
Bram Moolenaarbd4593f2016-01-23 22:51:07 +0100457 EMSG(_(e_invarg));
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100458 res->v_type = VAR_SPECIAL;
459 res->vval.v_number = VVAL_NONE;
460 }
461 ga_clear(&ga);
462}
463
464/*
465 * Decode one item and put it in "result".
466 * Must already have skipped white space.
467 */
468 static void
469json_decode_item(js_read_T *reader, typval_T *res)
470{
471 char_u *p = reader->js_buf + reader->js_used;
472
473 switch (*p)
474 {
475 case '[': /* array */
476 json_decode_array(reader, res);
477 return;
478
479 case '{': /* object */
480 json_decode_object(reader, res);
481 return;
482
483 case '"': /* string */
484 json_decode_string(reader, res);
485 return;
486
487 case ',': /* comma: empty item */
488 case NUL: /* empty */
489 res->v_type = VAR_SPECIAL;
490 res->vval.v_number = VVAL_NONE;
491 return;
492
493 default:
494 if (VIM_ISDIGIT(*p) || *p == '-')
495 {
496 int len;
497 char_u *sp = p;
498#ifdef FEAT_FLOAT
499 if (*sp == '-')
500 ++sp;
501 sp = skipdigits(sp);
502 if (*sp == '.' || *sp == 'e' || *sp == 'E')
503 {
504 res->v_type = VAR_FLOAT;
505 len = string2float(p, &res->vval.v_float);
506 }
507 else
508#endif
509 {
510 long nr;
511
512 res->v_type = VAR_NUMBER;
513 vim_str2nr(reader->js_buf + reader->js_used,
514 NULL, &len, 0, /* what */
515 &nr, NULL, 0);
516 res->vval.v_number = nr;
517 }
518 reader->js_used += len;
519 return;
520 }
521 if (STRNICMP((char *)p, "false", 5) == 0)
522 {
523 reader->js_used += 5;
524 res->v_type = VAR_SPECIAL;
525 res->vval.v_number = VVAL_FALSE;
526 return;
527 }
528 if (STRNICMP((char *)p, "true", 4) == 0)
529 {
530 reader->js_used += 4;
531 res->v_type = VAR_SPECIAL;
532 res->vval.v_number = VVAL_TRUE;
533 return;
534 }
535 if (STRNICMP((char *)p, "null", 4) == 0)
536 {
537 reader->js_used += 4;
538 res->v_type = VAR_SPECIAL;
539 res->vval.v_number = VVAL_NULL;
540 return;
541 }
542 break;
543 }
544
545 EMSG(_(e_invarg));
546 res->v_type = VAR_SPECIAL;
547 res->vval.v_number = VVAL_NONE;
548}
549
550/*
551 * Decode the JSON from "reader" and store the result in "res".
552 */
553 void
554json_decode(js_read_T *reader, typval_T *res)
555{
556 json_skip_white(reader);
557 json_decode_item(reader, res);
558 json_skip_white(reader);
559 if (reader->js_buf[reader->js_used] != NUL)
560 EMSG(_(e_invarg));
561}
562#endif