1// This is an open source non-commercial project. Dear PVS-Studio, please check
2// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
4/// @file encode.c
5///
6/// File containing functions for encoding and decoding VimL values.
7///
8/// Split out from eval.c.
9
10#include <msgpack.h>
11#include <inttypes.h>
12#include <stddef.h>
13#include <assert.h>
14#include <math.h>
15
16#include "nvim/eval/encode.h"
17#include "nvim/buffer_defs.h"
18#include "nvim/eval.h"
19#include "nvim/eval/typval.h"
20#include "nvim/garray.h"
21#include "nvim/mbyte.h"
22#include "nvim/math.h"
23#include "nvim/message.h"
24#include "nvim/memory.h"
25#include "nvim/charset.h" // vim_isprintc()
26#include "nvim/macros.h"
27#include "nvim/ascii.h"
28#include "nvim/vim.h" // For _()
29#include "nvim/lib/kvec.h"
30#include "nvim/eval/typval_encode.h"
31
32#define ga_concat(a, b) ga_concat(a, (char_u *)b)
33#define utf_ptr2char(b) utf_ptr2char((char_u *)b)
34#define utf_ptr2len(b) ((size_t)utf_ptr2len((char_u *)b))
35#define utf_char2len(b) ((size_t)utf_char2len(b))
36
37const char *const encode_special_var_names[] = {
38 [kSpecialVarNull] = "null",
39 [kSpecialVarTrue] = "true",
40 [kSpecialVarFalse] = "false",
41};
42
43#ifdef INCLUDE_GENERATED_DECLARATIONS
44# include "eval/encode.c.generated.h"
45#endif
46
47/// Msgpack callback for writing to readfile()-style list
48int encode_list_write(void *const data, const char *const buf, const size_t len)
49 FUNC_ATTR_NONNULL_ARG(1)
50{
51 if (len == 0) {
52 return 0;
53 }
54 list_T *const list = (list_T *) data;
55 const char *const end = buf + len;
56 const char *line_end = buf;
57 listitem_T *li = tv_list_last(list);
58
59 // Continue the last list element
60 if (li != NULL) {
61 line_end = xmemscan(buf, NL, len);
62 if (line_end != buf) {
63 const size_t line_length = (size_t)(line_end - buf);
64 char *str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string;
65 const size_t li_len = (str == NULL ? 0 : strlen(str));
66 TV_LIST_ITEM_TV(li)->vval.v_string = xrealloc(
67 str, li_len + line_length + 1);
68 str = (char *)TV_LIST_ITEM_TV(li)->vval.v_string + li_len;
69 memcpy(str, buf, line_length);
70 str[line_length] = 0;
71 memchrsub(str, NUL, NL, line_length);
72 }
73 line_end++;
74 }
75
76 while (line_end < end) {
77 const char *line_start = line_end;
78 line_end = xmemscan(line_start, NL, (size_t) (end - line_start));
79 char *str = NULL;
80 if (line_end != line_start) {
81 const size_t line_length = (size_t)(line_end - line_start);
82 str = xmemdupz(line_start, line_length);
83 memchrsub(str, NUL, NL, line_length);
84 }
85 tv_list_append_allocated_string(list, str);
86 line_end++;
87 }
88 if (line_end == end) {
89 tv_list_append_allocated_string(list, NULL);
90 }
91 return 0;
92}
93
94/// Abort conversion to string after a recursion error.
95static bool did_echo_string_emsg = false;
96
97/// Show a error message when converting to msgpack value
98///
99/// @param[in] msg Error message to dump. Must contain exactly two %s that
100/// will be replaced with what was being dumped: first with
101/// something like “F” or “function argument”, second with path
102/// to the failed value.
103/// @param[in] mpstack Path to the failed value.
104/// @param[in] objname Dumped object name.
105///
106/// @return FAIL.
107static int conv_error(const char *const msg, const MPConvStack *const mpstack,
108 const char *const objname)
109 FUNC_ATTR_NONNULL_ALL
110{
111 garray_T msg_ga;
112 ga_init(&msg_ga, (int)sizeof(char), 80);
113 const char *const key_msg = _("key %s");
114 const char *const key_pair_msg = _("key %s at index %i from special map");
115 const char *const idx_msg = _("index %i");
116 const char *const partial_arg_msg = _("partial");
117 const char *const partial_arg_i_msg = _("argument %i");
118 const char *const partial_self_msg = _("partial self dictionary");
119 for (size_t i = 0; i < kv_size(*mpstack); i++) {
120 if (i != 0) {
121 ga_concat(&msg_ga, ", ");
122 }
123 MPConvStackVal v = kv_A(*mpstack, i);
124 switch (v.type) {
125 case kMPConvDict: {
126 typval_T key_tv = {
127 .v_type = VAR_STRING,
128 .vval = { .v_string = (v.data.d.hi == NULL
129 ? v.data.d.dict->dv_hashtab.ht_array
130 : (v.data.d.hi - 1))->hi_key },
131 };
132 char *const key = encode_tv2string(&key_tv, NULL);
133 vim_snprintf((char *) IObuff, IOSIZE, key_msg, key);
134 xfree(key);
135 ga_concat(&msg_ga, IObuff);
136 break;
137 }
138 case kMPConvPairs:
139 case kMPConvList: {
140 const int idx = (v.data.l.li == tv_list_first(v.data.l.list)
141 ? 0
142 : (v.data.l.li == NULL
143 ? tv_list_len(v.data.l.list) - 1
144 : (int)tv_list_idx_of_item(
145 v.data.l.list,
146 TV_LIST_ITEM_PREV(v.data.l.list,
147 v.data.l.li))));
148 const listitem_T *const li = (v.data.l.li == NULL
149 ? tv_list_last(v.data.l.list)
150 : TV_LIST_ITEM_PREV(v.data.l.list,
151 v.data.l.li));
152 if (v.type == kMPConvList
153 || li == NULL
154 || (TV_LIST_ITEM_TV(li)->v_type != VAR_LIST
155 && tv_list_len(TV_LIST_ITEM_TV(li)->vval.v_list) <= 0)) {
156 vim_snprintf((char *)IObuff, IOSIZE, idx_msg, idx);
157 ga_concat(&msg_ga, IObuff);
158 } else {
159 typval_T key_tv = *TV_LIST_ITEM_TV(
160 tv_list_first(TV_LIST_ITEM_TV(li)->vval.v_list));
161 char *const key = encode_tv2echo(&key_tv, NULL);
162 vim_snprintf((char *) IObuff, IOSIZE, key_pair_msg, key, idx);
163 xfree(key);
164 ga_concat(&msg_ga, IObuff);
165 }
166 break;
167 }
168 case kMPConvPartial: {
169 switch (v.data.p.stage) {
170 case kMPConvPartialArgs: {
171 assert(false);
172 break;
173 }
174 case kMPConvPartialSelf: {
175 ga_concat(&msg_ga, partial_arg_msg);
176 break;
177 }
178 case kMPConvPartialEnd: {
179 ga_concat(&msg_ga, partial_self_msg);
180 break;
181 }
182 }
183 break;
184 }
185 case kMPConvPartialList: {
186 const int idx = (int)(v.data.a.arg - v.data.a.argv) - 1;
187 vim_snprintf((char *)IObuff, IOSIZE, partial_arg_i_msg, idx);
188 ga_concat(&msg_ga, IObuff);
189 break;
190 }
191 }
192 }
193 emsgf(msg, _(objname), (kv_size(*mpstack) == 0
194 ? _("itself")
195 : (char *)msg_ga.ga_data));
196 ga_clear(&msg_ga);
197 return FAIL;
198}
199
200/// Convert readfile()-style list to a char * buffer with length
201///
202/// @param[in] list Converted list.
203/// @param[out] ret_len Resulting buffer length.
204/// @param[out] ret_buf Allocated buffer with the result or NULL if ret_len is
205/// zero.
206///
207/// @return true in case of success, false in case of failure.
208bool encode_vim_list_to_buf(const list_T *const list, size_t *const ret_len,
209 char **const ret_buf)
210 FUNC_ATTR_NONNULL_ARG(2, 3) FUNC_ATTR_WARN_UNUSED_RESULT
211{
212 size_t len = 0;
213 TV_LIST_ITER_CONST(list, li, {
214 if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
215 return false;
216 }
217 len++;
218 if (TV_LIST_ITEM_TV(li)->vval.v_string != NULL) {
219 len += STRLEN(TV_LIST_ITEM_TV(li)->vval.v_string);
220 }
221 });
222 if (len) {
223 len--;
224 }
225 *ret_len = len;
226 if (len == 0) {
227 *ret_buf = NULL;
228 return true;
229 }
230 ListReaderState lrstate = encode_init_lrstate(list);
231 char *const buf = xmalloc(len);
232 size_t read_bytes;
233 if (encode_read_from_list(&lrstate, buf, len, &read_bytes) != OK) {
234 assert(false);
235 }
236 assert(len == read_bytes);
237 *ret_buf = buf;
238 return true;
239}
240
241/// Read bytes from list
242///
243/// @param[in,out] state Structure describing position in list from which
244/// reading should start. Is updated to reflect position
245/// at which reading ended.
246/// @param[out] buf Buffer to write to.
247/// @param[in] nbuf Buffer length.
248/// @param[out] read_bytes Is set to amount of bytes read.
249///
250/// @return OK when reading was finished, FAIL in case of error (i.e. list item
251/// was not a string), NOTDONE if reading was successfull, but there are
252/// more bytes to read.
253int encode_read_from_list(ListReaderState *const state, char *const buf,
254 const size_t nbuf, size_t *const read_bytes)
255 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT
256{
257 char *const buf_end = buf + nbuf;
258 char *p = buf;
259 while (p < buf_end) {
260 assert(state->li_length == 0
261 || TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
262 for (size_t i = state->offset; i < state->li_length && p < buf_end; i++) {
263 assert(TV_LIST_ITEM_TV(state->li)->vval.v_string != NULL);
264 const char ch = (char)(
265 TV_LIST_ITEM_TV(state->li)->vval.v_string[state->offset++]);
266 *p++ = (char)((char)ch == (char)NL ? (char)NUL : (char)ch);
267 }
268 if (p < buf_end) {
269 state->li = TV_LIST_ITEM_NEXT(state->list, state->li);
270 if (state->li == NULL) {
271 *read_bytes = (size_t) (p - buf);
272 return OK;
273 }
274 *p++ = NL;
275 if (TV_LIST_ITEM_TV(state->li)->v_type != VAR_STRING) {
276 *read_bytes = (size_t)(p - buf);
277 return FAIL;
278 }
279 state->offset = 0;
280 state->li_length = (TV_LIST_ITEM_TV(state->li)->vval.v_string == NULL
281 ? 0
282 : STRLEN(TV_LIST_ITEM_TV(state->li)->vval.v_string));
283 }
284 }
285 *read_bytes = nbuf;
286 return ((state->offset < state->li_length
287 || TV_LIST_ITEM_NEXT(state->list, state->li) != NULL)
288 ? NOTDONE
289 : OK);
290}
291
292#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
293 do { \
294 const char *const buf_ = (const char *) buf; \
295 if (buf == NULL) { \
296 ga_concat(gap, "''"); \
297 } else { \
298 const size_t len_ = (len); \
299 ga_grow(gap, (int) (2 + len_ + memcnt(buf_, '\'', len_))); \
300 ga_append(gap, '\''); \
301 for (size_t i_ = 0; i_ < len_; i_++) { \
302 if (buf_[i_] == '\'') { \
303 ga_append(gap, '\''); \
304 } \
305 ga_append(gap, buf_[i_]); \
306 } \
307 ga_append(gap, '\''); \
308 } \
309 } while (0)
310
311#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
312 TYPVAL_ENCODE_CONV_STRING(tv, buf, len)
313
314#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type)
315
316#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
317 do { \
318 char numbuf[NUMBUFLEN]; \
319 vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRId64, (int64_t) (num)); \
320 ga_concat(gap, numbuf); \
321 } while (0)
322
323#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
324 do { \
325 const float_T flt_ = (flt); \
326 switch (xfpclassify(flt_)) { \
327 case FP_NAN: { \
328 ga_concat(gap, (char_u *) "str2float('nan')"); \
329 break; \
330 } \
331 case FP_INFINITE: { \
332 if (flt_ < 0) { \
333 ga_append(gap, '-'); \
334 } \
335 ga_concat(gap, (char_u *) "str2float('inf')"); \
336 break; \
337 } \
338 default: { \
339 char numbuf[NUMBUFLEN]; \
340 vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
341 ga_concat(gap, (char_u *) numbuf); \
342 } \
343 } \
344 } while (0)
345
346#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
347 do { \
348 const char *const fun_ = (const char *)(fun); \
349 if (fun_ == NULL) { \
350 internal_error("string(): NULL function name"); \
351 ga_concat(gap, "function(NULL"); \
352 } else { \
353 ga_concat(gap, "function("); \
354 TYPVAL_ENCODE_CONV_STRING(tv, fun_, strlen(fun_)); \
355 }\
356 } while (0)
357
358#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) \
359 do { \
360 if (len != 0) { \
361 ga_concat(gap, ", "); \
362 } \
363 } while (0)
364
365#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) \
366 do { \
367 if ((ptrdiff_t)len != -1) { \
368 ga_concat(gap, ", "); \
369 } \
370 } while (0)
371
372#define TYPVAL_ENCODE_CONV_FUNC_END(tv) \
373 ga_append(gap, ')')
374
375#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
376 ga_concat(gap, "[]")
377
378#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
379 ga_append(gap, '[')
380
381#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
382
383#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
384 ga_concat(gap, "{}")
385
386#define TYPVAL_ENCODE_CONV_NIL(tv) \
387 ga_concat(gap, "v:null")
388
389#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
390 ga_concat(gap, ((num)? "v:true": "v:false"))
391
392#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num)
393
394#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
395 ga_append(gap, '{')
396
397#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
398
399#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \
400 ga_append(gap, '}')
401
402#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) \
403 ga_concat(gap, ": ")
404
405#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) \
406 ga_concat(gap, ", ")
407
408#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
409
410#define TYPVAL_ENCODE_CONV_LIST_END(tv) \
411 ga_append(gap, ']')
412
413#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) \
414 TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, NULL)
415
416#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
417 do { \
418 if (!did_echo_string_emsg) { \
419 /* Only give this message once for a recursive call to avoid */ \
420 /* flooding the user with errors. */ \
421 did_echo_string_emsg = true; \
422 EMSG(_("E724: unable to correctly dump variable " \
423 "with self-referencing container")); \
424 } \
425 char ebuf[NUMBUFLEN + 7]; \
426 size_t backref = 0; \
427 for (; backref < kv_size(*mpstack); backref++) { \
428 const MPConvStackVal mpval = kv_A(*mpstack, backref); \
429 if (mpval.type == conv_type) { \
430 if (conv_type == kMPConvDict) { \
431 if ((void *) mpval.data.d.dict == (void *) (val)) { \
432 break; \
433 } \
434 } else if (conv_type == kMPConvList) { \
435 if ((void *) mpval.data.l.list == (void *) (val)) { \
436 break; \
437 } \
438 } \
439 } \
440 } \
441 vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{E724@%zu}", backref); \
442 ga_concat(gap, &ebuf[0]); \
443 } while (0)
444
445#define TYPVAL_ENCODE_ALLOW_SPECIALS false
446
447#define TYPVAL_ENCODE_SCOPE static
448#define TYPVAL_ENCODE_NAME string
449#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
450#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
451#include "nvim/eval/typval_encode.c.h"
452#undef TYPVAL_ENCODE_SCOPE
453#undef TYPVAL_ENCODE_NAME
454#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
455#undef TYPVAL_ENCODE_FIRST_ARG_NAME
456
457#undef TYPVAL_ENCODE_CONV_RECURSE
458#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
459 do { \
460 char ebuf[NUMBUFLEN + 7]; \
461 size_t backref = 0; \
462 for (; backref < kv_size(*mpstack); backref++) { \
463 const MPConvStackVal mpval = kv_A(*mpstack, backref); \
464 if (mpval.type == conv_type) { \
465 if (conv_type == kMPConvDict) { \
466 if ((void *) mpval.data.d.dict == (void *) val) { \
467 break; \
468 } \
469 } else if (conv_type == kMPConvList) { \
470 if ((void *) mpval.data.l.list == (void *) val) { \
471 break; \
472 } \
473 } \
474 } \
475 } \
476 if (conv_type == kMPConvDict) { \
477 vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "{...@%zu}", backref); \
478 } else { \
479 vim_snprintf(ebuf, ARRAY_SIZE(ebuf), "[...@%zu]", backref); \
480 } \
481 ga_concat(gap, &ebuf[0]); \
482 return OK; \
483 } while (0)
484
485#define TYPVAL_ENCODE_SCOPE
486#define TYPVAL_ENCODE_NAME echo
487#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
488#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
489#include "nvim/eval/typval_encode.c.h"
490#undef TYPVAL_ENCODE_SCOPE
491#undef TYPVAL_ENCODE_NAME
492#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
493#undef TYPVAL_ENCODE_FIRST_ARG_NAME
494
495#undef TYPVAL_ENCODE_CONV_RECURSE
496#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
497 do { \
498 if (!did_echo_string_emsg) { \
499 /* Only give this message once for a recursive call to avoid */ \
500 /* flooding the user with errors. */ \
501 did_echo_string_emsg = true; \
502 EMSG(_("E724: unable to correctly dump variable " \
503 "with self-referencing container")); \
504 } \
505 } while (0)
506
507#undef TYPVAL_ENCODE_ALLOW_SPECIALS
508#define TYPVAL_ENCODE_ALLOW_SPECIALS true
509
510#undef TYPVAL_ENCODE_CONV_NIL
511#define TYPVAL_ENCODE_CONV_NIL(tv) \
512 ga_concat(gap, "null")
513
514#undef TYPVAL_ENCODE_CONV_BOOL
515#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
516 ga_concat(gap, ((num)? "true": "false"))
517
518#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
519#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
520 do { \
521 char numbuf[NUMBUFLEN]; \
522 vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%" PRIu64, (num)); \
523 ga_concat(gap, numbuf); \
524 } while (0)
525
526#undef TYPVAL_ENCODE_CONV_FLOAT
527#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
528 do { \
529 const float_T flt_ = (flt); \
530 switch (xfpclassify(flt_)) { \
531 case FP_NAN: { \
532 EMSG(_("E474: Unable to represent NaN value in JSON")); \
533 return FAIL; \
534 } \
535 case FP_INFINITE: { \
536 EMSG(_("E474: Unable to represent infinity in JSON")); \
537 return FAIL; \
538 } \
539 default: { \
540 char numbuf[NUMBUFLEN]; \
541 vim_snprintf(numbuf, ARRAY_SIZE(numbuf), "%g", flt_); \
542 ga_concat(gap, (char_u *) numbuf); \
543 break; \
544 } \
545 } \
546 } while (0)
547
548/// Escape sequences used in JSON
549static const char escapes[][3] = {
550 [BS] = "\\b",
551 [TAB] = "\\t",
552 [NL] = "\\n",
553 [CAR] = "\\r",
554 ['"'] = "\\\"",
555 ['\\'] = "\\\\",
556 [FF] = "\\f",
557};
558
559static const char xdigits[] = "0123456789ABCDEF";
560
561/// Convert given string to JSON string
562///
563/// @param[out] gap Garray where result will be saved.
564/// @param[in] buf Converted string.
565/// @param[in] len Converted string length.
566///
567/// @return OK in case of success, FAIL otherwise.
568static inline int convert_to_json_string(garray_T *const gap,
569 const char *const buf,
570 const size_t len)
571 FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_ALWAYS_INLINE
572{
573 const char *utf_buf = buf;
574 if (utf_buf == NULL) {
575 ga_concat(gap, "\"\"");
576 } else {
577 size_t utf_len = len;
578 char *tofree = NULL;
579 size_t str_len = 0;
580 // Encode character as \uNNNN if
581 // 1. It is an ASCII control character (0x0 .. 0x1F; 0x7F not
582 // utf_printable and thus not checked specially).
583 // 2. Code point is not printable according to utf_printable().
584 // This is done to make resulting values displayable on screen also not from
585 // Neovim.
586#define ENCODE_RAW(ch) \
587 (ch >= 0x20 && utf_printable(ch))
588 for (size_t i = 0; i < utf_len;) {
589 const int ch = utf_ptr2char(utf_buf + i);
590 const size_t shift = (ch == 0? 1: utf_ptr2len(utf_buf + i));
591 assert(shift > 0);
592 i += shift;
593 switch (ch) {
594 case BS:
595 case TAB:
596 case NL:
597 case FF:
598 case CAR:
599 case '"':
600 case '\\': {
601 str_len += 2;
602 break;
603 }
604 default: {
605 if (ch > 0x7F && shift == 1) {
606 emsgf(_("E474: String \"%.*s\" contains byte that does not start "
607 "any UTF-8 character"),
608 (int)(utf_len - (i - shift)), utf_buf + i - shift);
609 xfree(tofree);
610 return FAIL;
611 } else if ((SURROGATE_HI_START <= ch && ch <= SURROGATE_HI_END)
612 || (SURROGATE_LO_START <= ch && ch <= SURROGATE_LO_END)) {
613 emsgf(_("E474: UTF-8 string contains code point which belongs "
614 "to a surrogate pair: %.*s"),
615 (int)(utf_len - (i - shift)), utf_buf + i - shift);
616 xfree(tofree);
617 return FAIL;
618 } else if (ENCODE_RAW(ch)) {
619 str_len += shift;
620 } else {
621 str_len += ((sizeof("\\u1234") - 1)
622 * (size_t) (1 + (ch >= SURROGATE_FIRST_CHAR)));
623 }
624 break;
625 }
626 }
627 }
628 ga_append(gap, '"');
629 ga_grow(gap, (int) str_len);
630 for (size_t i = 0; i < utf_len;) {
631 const int ch = utf_ptr2char(utf_buf + i);
632 const size_t shift = (ch == 0? 1: utf_char2len(ch));
633 assert(shift > 0);
634 // Is false on invalid unicode, but this should already be handled.
635 assert(ch == 0 || shift == utf_ptr2len(utf_buf + i));
636 switch (ch) {
637 case BS:
638 case TAB:
639 case NL:
640 case FF:
641 case CAR:
642 case '"':
643 case '\\': {
644 ga_concat_len(gap, escapes[ch], 2);
645 break;
646 }
647 default: {
648 if (ENCODE_RAW(ch)) {
649 ga_concat_len(gap, utf_buf + i, shift);
650 } else if (ch < SURROGATE_FIRST_CHAR) {
651 ga_concat_len(gap, ((const char[]) {
652 '\\', 'u',
653 xdigits[(ch >> (4 * 3)) & 0xF],
654 xdigits[(ch >> (4 * 2)) & 0xF],
655 xdigits[(ch >> (4 * 1)) & 0xF],
656 xdigits[(ch >> (4 * 0)) & 0xF],
657 }), sizeof("\\u1234") - 1);
658 } else {
659 const int tmp = ch - SURROGATE_FIRST_CHAR;
660 const int hi = SURROGATE_HI_START + ((tmp >> 10) & ((1 << 10) - 1));
661 const int lo = SURROGATE_LO_END + ((tmp >> 0) & ((1 << 10) - 1));
662 ga_concat_len(gap, ((const char[]) {
663 '\\', 'u',
664 xdigits[(hi >> (4 * 3)) & 0xF],
665 xdigits[(hi >> (4 * 2)) & 0xF],
666 xdigits[(hi >> (4 * 1)) & 0xF],
667 xdigits[(hi >> (4 * 0)) & 0xF],
668 '\\', 'u',
669 xdigits[(lo >> (4 * 3)) & 0xF],
670 xdigits[(lo >> (4 * 2)) & 0xF],
671 xdigits[(lo >> (4 * 1)) & 0xF],
672 xdigits[(lo >> (4 * 0)) & 0xF],
673 }), (sizeof("\\u1234") - 1) * 2);
674 }
675 break;
676 }
677 }
678 i += shift;
679 }
680 ga_append(gap, '"');
681 xfree(tofree);
682 }
683 return OK;
684}
685
686#undef TYPVAL_ENCODE_CONV_STRING
687#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
688 do { \
689 if (convert_to_json_string(gap, (const char *) (buf), (len)) != OK) { \
690 return FAIL; \
691 } \
692 } while (0)
693
694#undef TYPVAL_ENCODE_CONV_EXT_STRING
695#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
696 do { \
697 xfree(buf); \
698 EMSG(_("E474: Unable to convert EXT string to JSON")); \
699 return FAIL; \
700 } while (0)
701
702#undef TYPVAL_ENCODE_CONV_FUNC_START
703#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
704 return conv_error(_("E474: Error while dumping %s, %s: " \
705 "attempt to dump function reference"), \
706 mpstack, objname)
707
708/// Check whether given key can be used in json_encode()
709///
710/// @param[in] tv Key to check.
711bool encode_check_json_key(const typval_T *const tv)
712 FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE
713{
714 if (tv->v_type == VAR_STRING) {
715 return true;
716 }
717 if (tv->v_type != VAR_DICT) {
718 return false;
719 }
720 const dict_T *const spdict = tv->vval.v_dict;
721 if (spdict->dv_hashtab.ht_used != 2) {
722 return false;
723 }
724 const dictitem_T *type_di;
725 const dictitem_T *val_di;
726 if ((type_di = tv_dict_find(spdict, S_LEN("_TYPE"))) == NULL
727 || type_di->di_tv.v_type != VAR_LIST
728 || (type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPString]
729 && type_di->di_tv.vval.v_list != eval_msgpack_type_lists[kMPBinary])
730 || (val_di = tv_dict_find(spdict, S_LEN("_VAL"))) == NULL
731 || val_di->di_tv.v_type != VAR_LIST) {
732 return false;
733 }
734 if (val_di->di_tv.vval.v_list == NULL) {
735 return true;
736 }
737 TV_LIST_ITER_CONST(val_di->di_tv.vval.v_list, li, {
738 if (TV_LIST_ITEM_TV(li)->v_type != VAR_STRING) {
739 return false;
740 }
741 });
742 return true;
743}
744
745#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
746#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key) \
747 do { \
748 if (!encode_check_json_key(&key)) { \
749 EMSG(_("E474: Invalid key in special dictionary")); \
750 goto label; \
751 } \
752 } while (0)
753
754#define TYPVAL_ENCODE_SCOPE static
755#define TYPVAL_ENCODE_NAME json
756#define TYPVAL_ENCODE_FIRST_ARG_TYPE garray_T *const
757#define TYPVAL_ENCODE_FIRST_ARG_NAME gap
758#include "nvim/eval/typval_encode.c.h"
759#undef TYPVAL_ENCODE_SCOPE
760#undef TYPVAL_ENCODE_NAME
761#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
762#undef TYPVAL_ENCODE_FIRST_ARG_NAME
763
764#undef TYPVAL_ENCODE_CONV_STRING
765#undef TYPVAL_ENCODE_CONV_STR_STRING
766#undef TYPVAL_ENCODE_CONV_EXT_STRING
767#undef TYPVAL_ENCODE_CONV_NUMBER
768#undef TYPVAL_ENCODE_CONV_FLOAT
769#undef TYPVAL_ENCODE_CONV_FUNC_START
770#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
771#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
772#undef TYPVAL_ENCODE_CONV_FUNC_END
773#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
774#undef TYPVAL_ENCODE_CONV_LIST_START
775#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
776#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
777#undef TYPVAL_ENCODE_CONV_NIL
778#undef TYPVAL_ENCODE_CONV_BOOL
779#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
780#undef TYPVAL_ENCODE_CONV_DICT_START
781#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
782#undef TYPVAL_ENCODE_CONV_DICT_END
783#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
784#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
785#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
786#undef TYPVAL_ENCODE_CONV_LIST_END
787#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
788#undef TYPVAL_ENCODE_CONV_RECURSE
789#undef TYPVAL_ENCODE_ALLOW_SPECIALS
790
791/// Return a string with the string representation of a variable.
792/// Puts quotes around strings, so that they can be parsed back by eval().
793///
794/// @param[in] tv typval_T to convert.
795/// @param[out] len Location where length of the result will be saved.
796///
797/// @return String representation of the variable or NULL.
798char *encode_tv2string(typval_T *tv, size_t *len)
799 FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
800{
801 garray_T ga;
802 ga_init(&ga, (int)sizeof(char), 80);
803 const int evs_ret = encode_vim_to_string(&ga, tv,
804 N_("encode_tv2string() argument"));
805 (void)evs_ret;
806 assert(evs_ret == OK);
807 did_echo_string_emsg = false;
808 if (len != NULL) {
809 *len = (size_t) ga.ga_len;
810 }
811 ga_append(&ga, '\0');
812 return (char *) ga.ga_data;
813}
814
815/// Return a string with the string representation of a variable.
816/// Does not put quotes around strings, as ":echo" displays values.
817///
818/// @param[in] tv typval_T to convert.
819/// @param[out] len Location where length of the result will be saved.
820///
821/// @return String representation of the variable or NULL.
822char *encode_tv2echo(typval_T *tv, size_t *len)
823 FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
824{
825 garray_T ga;
826 ga_init(&ga, (int)sizeof(char), 80);
827 if (tv->v_type == VAR_STRING || tv->v_type == VAR_FUNC) {
828 if (tv->vval.v_string != NULL) {
829 ga_concat(&ga, tv->vval.v_string);
830 }
831 } else {
832 const int eve_ret = encode_vim_to_echo(&ga, tv, N_(":echo argument"));
833 (void)eve_ret;
834 assert(eve_ret == OK);
835 }
836 if (len != NULL) {
837 *len = (size_t) ga.ga_len;
838 }
839 ga_append(&ga, '\0');
840 return (char *) ga.ga_data;
841}
842
843/// Return a string with the string representation of a variable.
844/// Puts quotes around strings, so that they can be parsed back by eval().
845///
846/// @param[in] tv typval_T to convert.
847/// @param[out] len Location where length of the result will be saved.
848///
849/// @return String representation of the variable or NULL.
850char *encode_tv2json(typval_T *tv, size_t *len)
851 FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_MALLOC
852{
853 garray_T ga;
854 ga_init(&ga, (int)sizeof(char), 80);
855 const int evj_ret = encode_vim_to_json(&ga, tv,
856 N_("encode_tv2json() argument"));
857 if (!evj_ret) {
858 ga_clear(&ga);
859 }
860 did_echo_string_emsg = false;
861 if (len != NULL) {
862 *len = (size_t)ga.ga_len;
863 }
864 ga_append(&ga, '\0');
865 return (char *)ga.ga_data;
866}
867
868#define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \
869 do { \
870 if (buf == NULL) { \
871 msgpack_pack_bin(packer, 0); \
872 } else { \
873 const size_t len_ = (len); \
874 msgpack_pack_bin(packer, len_); \
875 msgpack_pack_bin_body(packer, buf, len_); \
876 } \
877 } while (0)
878
879#define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) \
880 do { \
881 if (buf == NULL) { \
882 msgpack_pack_str(packer, 0); \
883 } else { \
884 const size_t len_ = (len); \
885 msgpack_pack_str(packer, len_); \
886 msgpack_pack_str_body(packer, buf, len_); \
887 } \
888 } while (0)
889
890#define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) \
891 do { \
892 if (buf == NULL) { \
893 msgpack_pack_ext(packer, 0, (int8_t) type); \
894 } else { \
895 const size_t len_ = (len); \
896 msgpack_pack_ext(packer, len_, (int8_t) type); \
897 msgpack_pack_ext_body(packer, buf, len_); \
898 } \
899 } while (0)
900
901#define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \
902 msgpack_pack_int64(packer, (int64_t)(num))
903
904#define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \
905 msgpack_pack_double(packer, (double)(flt))
906
907#define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \
908 return conv_error(_("E5004: Error while dumping %s, %s: " \
909 "attempt to dump function reference"), \
910 mpstack, objname)
911
912#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len)
913#define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len)
914#define TYPVAL_ENCODE_CONV_FUNC_END(tv)
915
916#define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \
917 msgpack_pack_array(packer, 0)
918
919#define TYPVAL_ENCODE_CONV_LIST_START(tv, len) \
920 msgpack_pack_array(packer, (size_t)(len))
921
922#define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv)
923
924#define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \
925 msgpack_pack_map(packer, 0)
926
927#define TYPVAL_ENCODE_CONV_NIL(tv) \
928 msgpack_pack_nil(packer)
929
930#define TYPVAL_ENCODE_CONV_BOOL(tv, num) \
931 do { \
932 if (num) { \
933 msgpack_pack_true(packer); \
934 } else { \
935 msgpack_pack_false(packer); \
936 } \
937 } while (0)
938
939#define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) \
940 msgpack_pack_uint64(packer, (num))
941
942#define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) \
943 msgpack_pack_map(packer, (size_t)(len))
944
945#define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv)
946
947#define TYPVAL_ENCODE_CONV_DICT_END(tv, dict)
948
949#define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict)
950
951#define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict)
952
953#define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(label, key)
954
955#define TYPVAL_ENCODE_CONV_LIST_END(tv)
956
957#define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv)
958
959#define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) \
960 return conv_error(_("E5005: Unable to dump %s: " \
961 "container references itself in %s"), \
962 mpstack, objname)
963
964#define TYPVAL_ENCODE_ALLOW_SPECIALS true
965
966#define TYPVAL_ENCODE_SCOPE
967#define TYPVAL_ENCODE_NAME msgpack
968#define TYPVAL_ENCODE_FIRST_ARG_TYPE msgpack_packer *const
969#define TYPVAL_ENCODE_FIRST_ARG_NAME packer
970#include "nvim/eval/typval_encode.c.h"
971#undef TYPVAL_ENCODE_SCOPE
972#undef TYPVAL_ENCODE_NAME
973#undef TYPVAL_ENCODE_FIRST_ARG_TYPE
974#undef TYPVAL_ENCODE_FIRST_ARG_NAME
975
976#undef TYPVAL_ENCODE_CONV_STRING
977#undef TYPVAL_ENCODE_CONV_STR_STRING
978#undef TYPVAL_ENCODE_CONV_EXT_STRING
979#undef TYPVAL_ENCODE_CONV_NUMBER
980#undef TYPVAL_ENCODE_CONV_FLOAT
981#undef TYPVAL_ENCODE_CONV_FUNC_START
982#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS
983#undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF
984#undef TYPVAL_ENCODE_CONV_FUNC_END
985#undef TYPVAL_ENCODE_CONV_EMPTY_LIST
986#undef TYPVAL_ENCODE_CONV_LIST_START
987#undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START
988#undef TYPVAL_ENCODE_CONV_EMPTY_DICT
989#undef TYPVAL_ENCODE_CONV_NIL
990#undef TYPVAL_ENCODE_CONV_BOOL
991#undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER
992#undef TYPVAL_ENCODE_CONV_DICT_START
993#undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START
994#undef TYPVAL_ENCODE_CONV_DICT_END
995#undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY
996#undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS
997#undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK
998#undef TYPVAL_ENCODE_CONV_LIST_END
999#undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS
1000#undef TYPVAL_ENCODE_CONV_RECURSE
1001#undef TYPVAL_ENCODE_ALLOW_SPECIALS
1002