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 | |
37 | const 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 |
48 | int 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. |
95 | static 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. |
107 | static 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. |
208 | bool 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. |
253 | int 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 |
549 | static const char escapes[][3] = { |
550 | [BS] = "\\b" , |
551 | [TAB] = "\\t" , |
552 | [NL] = "\\n" , |
553 | [CAR] = "\\r" , |
554 | ['"'] = "\\\"" , |
555 | ['\\'] = "\\\\" , |
556 | [FF] = "\\f" , |
557 | }; |
558 | |
559 | static 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. |
568 | static 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. |
711 | bool 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. |
798 | char *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. |
822 | char *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. |
850 | char *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 | |