blob: e4473830a694c39f1d421acdf9257a463e3d49e8 [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 *
13 * Follows this standard: http://www.ietf.org/rfc/rfc4627.txt
14 */
15
16#include "vim.h"
17
18#if defined(FEAT_EVAL) || defined(PROTO)
19static void json_decode_item(js_read_T *reader, typval_T *res);
20
21/*
22 * Encode "val" into a JSON format string.
23 */
24 char_u *
25json_encode(typval_T *val)
26{
27 garray_T ga;
28
29 /* Store bytes in the growarray. */
30 ga_init2(&ga, 1, 4000);
31 json_encode_item(&ga, val, get_copyID());
32 return ga.ga_data;
33}
34
35 static void
36write_string(garray_T *gap, char_u *str)
37{
38 char_u *res = str;
39 char_u numbuf[NUMBUFLEN];
40
41 if (res == NULL)
42 ga_concat(gap, (char_u *)"null");
43 else
44 {
45 ga_append(gap, '"');
46 while (*res != NUL)
47 {
48 int c = PTR2CHAR(res);
49
50 switch (c)
51 {
52 case 0x08:
53 ga_append(gap, '\\'); ga_append(gap, 'b'); break;
54 case 0x09:
55 ga_append(gap, '\\'); ga_append(gap, 't'); break;
56 case 0x0a:
57 ga_append(gap, '\\'); ga_append(gap, 'n'); break;
58 case 0x0c:
59 ga_append(gap, '\\'); ga_append(gap, 'f'); break;
60 case 0x0d:
61 ga_append(gap, '\\'); ga_append(gap, 'r'); break;
62 case 0x22: /* " */
63 case 0x5c: /* \ */
64 ga_append(gap, '\\');
65 ga_append(gap, c);
66 break;
67 default:
68 if (c >= 0x20)
69 {
70 numbuf[mb_char2bytes(c, numbuf)] = NUL;
71 ga_concat(gap, numbuf);
72 }
73 else
74 {
75 vim_snprintf((char *)numbuf, NUMBUFLEN,
76 "\\u%04lx", (long)c);
77 ga_concat(gap, numbuf);
78 }
79 }
80 mb_cptr_adv(res);
81 }
82 ga_append(gap, '"');
83 }
84}
85
86 void
87json_encode_item(garray_T *gap, typval_T *val, int copyID)
88{
89 char_u numbuf[NUMBUFLEN];
90 char_u *res;
91 list_T *l;
92 dict_T *d;
93
94 switch (val->v_type)
95 {
96 case VAR_SPECIAL:
97 switch(val->vval.v_number)
98 {
99 case VVAL_FALSE: ga_concat(gap, (char_u *)"false"); break;
100 case VVAL_TRUE: ga_concat(gap, (char_u *)"true"); break;
101 case VVAL_NONE: break;
102 case VVAL_NULL: ga_concat(gap, (char_u *)"null"); break;
103 }
104 break;
105
106 case VAR_NUMBER:
107 vim_snprintf((char *)numbuf, NUMBUFLEN, "%ld",
108 (long)val->vval.v_number);
109 ga_concat(gap, numbuf);
110 break;
111
112 case VAR_STRING:
113 res = val->vval.v_string;
114 write_string(gap, res);
115 break;
116
117 case VAR_FUNC:
118 /* no JSON equivalent, skip */
119 break;
120
121 case VAR_LIST:
122 l = val->vval.v_list;
123 if (l == NULL)
124 ga_concat(gap, (char_u *)"null");
125 else
126 {
127 if (l->lv_copyID == copyID)
128 ga_concat(gap, (char_u *)"[]");
129 else
130 {
131 listitem_T *li;
132
133 l->lv_copyID = copyID;
134 ga_append(gap, '[');
135 for (li = l->lv_first; li != NULL && !got_int; )
136 {
137 json_encode_item(gap, &li->li_tv, copyID);
138 li = li->li_next;
139 if (li != NULL)
140 ga_append(gap, ',');
141 }
142 ga_append(gap, ']');
143 }
144 }
145 break;
146
147 case VAR_DICT:
148 d = val->vval.v_dict;
149 if (d == NULL)
150 ga_concat(gap, (char_u *)"null");
151 else
152 {
153 if (d->dv_copyID == copyID)
154 ga_concat(gap, (char_u *)"{}");
155 else
156 {
157 int first = TRUE;
158 int todo = (int)d->dv_hashtab.ht_used;
159 hashitem_T *hi;
160
161 d->dv_copyID = copyID;
162 ga_append(gap, '{');
163
164 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int;
165 ++hi)
166 if (!HASHITEM_EMPTY(hi))
167 {
168 --todo;
169 if (first)
170 first = FALSE;
171 else
172 ga_append(gap, ',');
173 write_string(gap, hi->hi_key);
174 ga_append(gap, ':');
175 json_encode_item(gap, &dict_lookup(hi)->di_tv,
176 copyID);
177 }
178 ga_append(gap, '}');
179 }
180 }
181 break;
182
183#ifdef FEAT_FLOAT
184 case VAR_FLOAT:
185 vim_snprintf((char *)numbuf, NUMBUFLEN, "%g", val->vval.v_float);
186 ga_concat(gap, numbuf);
187 break;
188#endif
189 default: EMSG2(_(e_intern2), "json_encode_item()"); break;
190 }
191}
192
193/*
194 * Skip white space in "reader".
195 */
196 static void
197json_skip_white(js_read_T *reader)
198{
199 int c;
200
201 while ((c = reader->js_buf[reader->js_used]) == ' '
202 || c == TAB || c == NL || c == CAR)
203 ++reader->js_used;
204}
205
206/*
207 * Make sure there are at least enough characters buffered to read a number.
208 */
209 static void
210json_fill_buffer(js_read_T *reader UNUSED)
211{
212 /* TODO */
213}
214
215 static void
216json_decode_array(js_read_T *reader, typval_T *res)
217{
218 char_u *p;
219 typval_T item;
220 listitem_T *li;
221
222 if (rettv_list_alloc(res) == FAIL)
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100223 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100224 ++reader->js_used; /* consume the '[' */
225
226 while (TRUE)
227 {
228 json_skip_white(reader);
229 p = reader->js_buf + reader->js_used;
230 if (*p == NUL)
231 goto fail;
232 if (*p == ']')
233 {
234 ++reader->js_used; /* consume the ']' */
235 return;
236 }
237
238 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
239 json_fill_buffer(reader);
240
241 json_decode_item(reader, &item);
242 li = listitem_alloc();
243 if (li == NULL)
244 return;
245 li->li_tv = item;
246 list_append(res->vval.v_list, li);
247
248 json_skip_white(reader);
249 p = reader->js_buf + reader->js_used;
250 if (*p == ',')
251 ++reader->js_used;
252 else if (*p != ']')
253 goto fail;
254 }
255fail:
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100256 EMSG(_(e_invarg));
257failsilent:
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100258 res->v_type = VAR_SPECIAL;
259 res->vval.v_number = VVAL_NONE;
260}
261
262 static void
263json_decode_object(js_read_T *reader, typval_T *res)
264{
265 char_u *p;
266 typval_T tvkey;
267 typval_T item;
268 dictitem_T *di;
269 char_u buf[NUMBUFLEN];
270 char_u *key;
271
272 if (rettv_dict_alloc(res) == FAIL)
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100273 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100274 ++reader->js_used; /* consume the '{' */
275
276 while (TRUE)
277 {
278 json_skip_white(reader);
279 p = reader->js_buf + reader->js_used;
280 if (*p == NUL)
281 goto fail;
282 if (*p == '}')
283 {
284 ++reader->js_used; /* consume the '}' */
285 return;
286 }
287
288 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
289 json_fill_buffer(reader);
290 json_decode_item(reader, &tvkey);
291 key = get_tv_string_buf_chk(&tvkey, buf);
292 if (key == NULL || *key == NUL)
293 {
294 /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
295 if (key != NULL)
296 EMSG(_(e_emptykey));
297 clear_tv(&tvkey);
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100298 goto failsilent;
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100299 }
300
301 json_skip_white(reader);
302 p = reader->js_buf + reader->js_used;
303 if (*p != ':')
304 {
305 clear_tv(&tvkey);
306 goto fail;
307 }
308 ++reader->js_used;
309 json_skip_white(reader);
310
311 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
312 json_fill_buffer(reader);
313 json_decode_item(reader, &item);
314
315 di = dictitem_alloc(key);
316 clear_tv(&tvkey);
317 if (di == NULL)
318 {
319 clear_tv(&item);
320 goto fail;
321 }
322 di->di_tv = item;
Bram Moolenaar2dedb452016-01-23 21:38:51 +0100323 if (dict_add(res->vval.v_dict, di) == FAIL)
324 dictitem_free(di);
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100325
326 json_skip_white(reader);
327 p = reader->js_buf + reader->js_used;
328 if (*p == ',')
329 ++reader->js_used;
330 else if (*p != '}')
331 goto fail;
332 }
333fail:
Bram Moolenaar6039c7f2016-01-24 15:05:32 +0100334 EMSG(_(e_invarg));
335failsilent:
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100336 res->v_type = VAR_SPECIAL;
337 res->vval.v_number = VVAL_NONE;
338}
339
340 static void
341json_decode_string(js_read_T *reader, typval_T *res)
342{
343 garray_T ga;
344 int len;
345 char_u *p = reader->js_buf + reader->js_used + 1;
346 int c;
347 long nr;
348 char_u buf[NUMBUFLEN];
349
350 ga_init2(&ga, 1, 200);
351
352 /* TODO: fill buffer when needed. */
353 while (*p != NUL && *p != '"')
354 {
355 if (*p == '\\')
356 {
357 c = -1;
358 switch (p[1])
359 {
360 case 'b': c = BS; break;
361 case 't': c = TAB; break;
362 case 'n': c = NL; break;
363 case 'f': c = FF; break;
364 case 'r': c = CAR; break;
365 case 'u':
366 vim_str2nr(p + 2, NULL, &len,
367 STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
368 p += len + 2;
369#ifdef FEAT_MBYTE
370 buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
371 ga_concat(&ga, buf);
372#else
373 ga_append(&ga, nr);
374#endif
375 break;
376 default: c = p[1]; break;
377 }
378 if (c > 0)
379 {
380 p += 2;
381 ga_append(&ga, c);
382 }
383 }
384 else
385 {
386 len = MB_PTR2LEN(p);
387 if (ga_grow(&ga, len) == OK)
388 {
389 mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
390 ga.ga_len += len;
391 }
392 p += len;
393 }
394 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
395 {
396 reader->js_used = (int)(p - reader->js_buf);
397 json_fill_buffer(reader);
398 p = reader->js_buf + reader->js_used;
399 }
400 }
401 reader->js_used = (int)(p - reader->js_buf);
402 if (*p == '"')
403 {
404 ++reader->js_used;
405 res->v_type = VAR_STRING;
Bram Moolenaar2dedb452016-01-23 21:38:51 +0100406 if (ga.ga_data == NULL)
407 res->vval.v_string = NULL;
408 else
409 res->vval.v_string = vim_strsave(ga.ga_data);
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100410 }
411 else
412 {
Bram Moolenaarbd4593f2016-01-23 22:51:07 +0100413 EMSG(_(e_invarg));
Bram Moolenaar520e1e42016-01-23 19:46:28 +0100414 res->v_type = VAR_SPECIAL;
415 res->vval.v_number = VVAL_NONE;
416 }
417 ga_clear(&ga);
418}
419
420/*
421 * Decode one item and put it in "result".
422 * Must already have skipped white space.
423 */
424 static void
425json_decode_item(js_read_T *reader, typval_T *res)
426{
427 char_u *p = reader->js_buf + reader->js_used;
428
429 switch (*p)
430 {
431 case '[': /* array */
432 json_decode_array(reader, res);
433 return;
434
435 case '{': /* object */
436 json_decode_object(reader, res);
437 return;
438
439 case '"': /* string */
440 json_decode_string(reader, res);
441 return;
442
443 case ',': /* comma: empty item */
444 case NUL: /* empty */
445 res->v_type = VAR_SPECIAL;
446 res->vval.v_number = VVAL_NONE;
447 return;
448
449 default:
450 if (VIM_ISDIGIT(*p) || *p == '-')
451 {
452 int len;
453 char_u *sp = p;
454#ifdef FEAT_FLOAT
455 if (*sp == '-')
456 ++sp;
457 sp = skipdigits(sp);
458 if (*sp == '.' || *sp == 'e' || *sp == 'E')
459 {
460 res->v_type = VAR_FLOAT;
461 len = string2float(p, &res->vval.v_float);
462 }
463 else
464#endif
465 {
466 long nr;
467
468 res->v_type = VAR_NUMBER;
469 vim_str2nr(reader->js_buf + reader->js_used,
470 NULL, &len, 0, /* what */
471 &nr, NULL, 0);
472 res->vval.v_number = nr;
473 }
474 reader->js_used += len;
475 return;
476 }
477 if (STRNICMP((char *)p, "false", 5) == 0)
478 {
479 reader->js_used += 5;
480 res->v_type = VAR_SPECIAL;
481 res->vval.v_number = VVAL_FALSE;
482 return;
483 }
484 if (STRNICMP((char *)p, "true", 4) == 0)
485 {
486 reader->js_used += 4;
487 res->v_type = VAR_SPECIAL;
488 res->vval.v_number = VVAL_TRUE;
489 return;
490 }
491 if (STRNICMP((char *)p, "null", 4) == 0)
492 {
493 reader->js_used += 4;
494 res->v_type = VAR_SPECIAL;
495 res->vval.v_number = VVAL_NULL;
496 return;
497 }
498 break;
499 }
500
501 EMSG(_(e_invarg));
502 res->v_type = VAR_SPECIAL;
503 res->vval.v_number = VVAL_NONE;
504}
505
506/*
507 * Decode the JSON from "reader" and store the result in "res".
508 */
509 void
510json_decode(js_read_T *reader, typval_T *res)
511{
512 json_skip_white(reader);
513 json_decode_item(reader, res);
514 json_skip_white(reader);
515 if (reader->js_buf[reader->js_used] != NUL)
516 EMSG(_(e_invarg));
517}
518#endif