blob: 7b67994ddd7c939b1cc29241be0b52879f33a6d1 [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)
223 goto fail;
224 ++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:
256 res->v_type = VAR_SPECIAL;
257 res->vval.v_number = VVAL_NONE;
258}
259
260 static void
261json_decode_object(js_read_T *reader, typval_T *res)
262{
263 char_u *p;
264 typval_T tvkey;
265 typval_T item;
266 dictitem_T *di;
267 char_u buf[NUMBUFLEN];
268 char_u *key;
269
270 if (rettv_dict_alloc(res) == FAIL)
271 goto fail;
272 ++reader->js_used; /* consume the '{' */
273
274 while (TRUE)
275 {
276 json_skip_white(reader);
277 p = reader->js_buf + reader->js_used;
278 if (*p == NUL)
279 goto fail;
280 if (*p == '}')
281 {
282 ++reader->js_used; /* consume the '}' */
283 return;
284 }
285
286 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
287 json_fill_buffer(reader);
288 json_decode_item(reader, &tvkey);
289 key = get_tv_string_buf_chk(&tvkey, buf);
290 if (key == NULL || *key == NUL)
291 {
292 /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
293 if (key != NULL)
294 EMSG(_(e_emptykey));
295 clear_tv(&tvkey);
296 goto fail;
297 }
298
299 json_skip_white(reader);
300 p = reader->js_buf + reader->js_used;
301 if (*p != ':')
302 {
303 clear_tv(&tvkey);
304 goto fail;
305 }
306 ++reader->js_used;
307 json_skip_white(reader);
308
309 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
310 json_fill_buffer(reader);
311 json_decode_item(reader, &item);
312
313 di = dictitem_alloc(key);
314 clear_tv(&tvkey);
315 if (di == NULL)
316 {
317 clear_tv(&item);
318 goto fail;
319 }
320 di->di_tv = item;
321 dict_add(res->vval.v_dict, di);
322
323 json_skip_white(reader);
324 p = reader->js_buf + reader->js_used;
325 if (*p == ',')
326 ++reader->js_used;
327 else if (*p != '}')
328 goto fail;
329 }
330fail:
331 res->v_type = VAR_SPECIAL;
332 res->vval.v_number = VVAL_NONE;
333}
334
335 static void
336json_decode_string(js_read_T *reader, typval_T *res)
337{
338 garray_T ga;
339 int len;
340 char_u *p = reader->js_buf + reader->js_used + 1;
341 int c;
342 long nr;
343 char_u buf[NUMBUFLEN];
344
345 ga_init2(&ga, 1, 200);
346
347 /* TODO: fill buffer when needed. */
348 while (*p != NUL && *p != '"')
349 {
350 if (*p == '\\')
351 {
352 c = -1;
353 switch (p[1])
354 {
355 case 'b': c = BS; break;
356 case 't': c = TAB; break;
357 case 'n': c = NL; break;
358 case 'f': c = FF; break;
359 case 'r': c = CAR; break;
360 case 'u':
361 vim_str2nr(p + 2, NULL, &len,
362 STR2NR_HEX + STR2NR_FORCE, &nr, NULL, 4);
363 p += len + 2;
364#ifdef FEAT_MBYTE
365 buf[(*mb_char2bytes)((int)nr, buf)] = NUL;
366 ga_concat(&ga, buf);
367#else
368 ga_append(&ga, nr);
369#endif
370 break;
371 default: c = p[1]; break;
372 }
373 if (c > 0)
374 {
375 p += 2;
376 ga_append(&ga, c);
377 }
378 }
379 else
380 {
381 len = MB_PTR2LEN(p);
382 if (ga_grow(&ga, len) == OK)
383 {
384 mch_memmove((char *)ga.ga_data + ga.ga_len, p, (size_t)len);
385 ga.ga_len += len;
386 }
387 p += len;
388 }
389 if (!reader->js_eof && (int)(reader->js_end - p) < NUMBUFLEN)
390 {
391 reader->js_used = (int)(p - reader->js_buf);
392 json_fill_buffer(reader);
393 p = reader->js_buf + reader->js_used;
394 }
395 }
396 reader->js_used = (int)(p - reader->js_buf);
397 if (*p == '"')
398 {
399 ++reader->js_used;
400 res->v_type = VAR_STRING;
401 res->vval.v_string = vim_strsave(ga.ga_data);
402 }
403 else
404 {
405 res->v_type = VAR_SPECIAL;
406 res->vval.v_number = VVAL_NONE;
407 }
408 ga_clear(&ga);
409}
410
411/*
412 * Decode one item and put it in "result".
413 * Must already have skipped white space.
414 */
415 static void
416json_decode_item(js_read_T *reader, typval_T *res)
417{
418 char_u *p = reader->js_buf + reader->js_used;
419
420 switch (*p)
421 {
422 case '[': /* array */
423 json_decode_array(reader, res);
424 return;
425
426 case '{': /* object */
427 json_decode_object(reader, res);
428 return;
429
430 case '"': /* string */
431 json_decode_string(reader, res);
432 return;
433
434 case ',': /* comma: empty item */
435 case NUL: /* empty */
436 res->v_type = VAR_SPECIAL;
437 res->vval.v_number = VVAL_NONE;
438 return;
439
440 default:
441 if (VIM_ISDIGIT(*p) || *p == '-')
442 {
443 int len;
444 char_u *sp = p;
445#ifdef FEAT_FLOAT
446 if (*sp == '-')
447 ++sp;
448 sp = skipdigits(sp);
449 if (*sp == '.' || *sp == 'e' || *sp == 'E')
450 {
451 res->v_type = VAR_FLOAT;
452 len = string2float(p, &res->vval.v_float);
453 }
454 else
455#endif
456 {
457 long nr;
458
459 res->v_type = VAR_NUMBER;
460 vim_str2nr(reader->js_buf + reader->js_used,
461 NULL, &len, 0, /* what */
462 &nr, NULL, 0);
463 res->vval.v_number = nr;
464 }
465 reader->js_used += len;
466 return;
467 }
468 if (STRNICMP((char *)p, "false", 5) == 0)
469 {
470 reader->js_used += 5;
471 res->v_type = VAR_SPECIAL;
472 res->vval.v_number = VVAL_FALSE;
473 return;
474 }
475 if (STRNICMP((char *)p, "true", 4) == 0)
476 {
477 reader->js_used += 4;
478 res->v_type = VAR_SPECIAL;
479 res->vval.v_number = VVAL_TRUE;
480 return;
481 }
482 if (STRNICMP((char *)p, "null", 4) == 0)
483 {
484 reader->js_used += 4;
485 res->v_type = VAR_SPECIAL;
486 res->vval.v_number = VVAL_NULL;
487 return;
488 }
489 break;
490 }
491
492 EMSG(_(e_invarg));
493 res->v_type = VAR_SPECIAL;
494 res->vval.v_number = VVAL_NONE;
495}
496
497/*
498 * Decode the JSON from "reader" and store the result in "res".
499 */
500 void
501json_decode(js_read_T *reader, typval_T *res)
502{
503 json_skip_white(reader);
504 json_decode_item(reader, res);
505 json_skip_white(reader);
506 if (reader->js_buf[reader->js_used] != NUL)
507 EMSG(_(e_invarg));
508}
509#endif