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 | #include <stdio.h> |
5 | #include <stddef.h> |
6 | #include <stdlib.h> |
7 | #include <string.h> |
8 | #include <assert.h> |
9 | #include <stdbool.h> |
10 | |
11 | #include "nvim/lib/queue.h" |
12 | #include "nvim/eval/typval.h" |
13 | #include "nvim/eval/gc.h" |
14 | #include "nvim/eval/executor.h" |
15 | #include "nvim/eval/encode.h" |
16 | #include "nvim/eval/typval_encode.h" |
17 | #include "nvim/eval.h" |
18 | #include "nvim/types.h" |
19 | #include "nvim/assert.h" |
20 | #include "nvim/memory.h" |
21 | #include "nvim/globals.h" |
22 | #include "nvim/hashtab.h" |
23 | #include "nvim/vim.h" |
24 | #include "nvim/ascii.h" |
25 | #include "nvim/pos.h" |
26 | #include "nvim/charset.h" |
27 | #include "nvim/garray.h" |
28 | #include "nvim/gettext.h" |
29 | #include "nvim/macros.h" |
30 | #include "nvim/mbyte.h" |
31 | #include "nvim/message.h" |
32 | // TODO(ZyX-I): Move line_breakcheck out of misc1 |
33 | #include "nvim/misc1.h" // For line_breakcheck |
34 | #include "nvim/os/fileio.h" |
35 | |
36 | #ifdef INCLUDE_GENERATED_DECLARATIONS |
37 | # include "eval/typval.c.generated.h" |
38 | #endif |
39 | |
40 | bool tv_in_free_unref_items = false; |
41 | |
42 | // TODO(ZyX-I): Remove DICT_MAXNEST, make users be non-recursive instead |
43 | |
44 | #define DICT_MAXNEST 100 |
45 | |
46 | const char *const tv_empty_string = "" ; |
47 | |
48 | //{{{1 Lists |
49 | //{{{2 List log |
50 | #ifdef LOG_LIST_ACTIONS |
51 | ListLog *list_log_first = NULL; |
52 | ListLog *list_log_last = NULL; |
53 | |
54 | /// Write list log to the given file |
55 | /// |
56 | /// @param[in] fname File to write log to. Will be appended to if already |
57 | /// present. |
58 | void list_write_log(const char *const fname) |
59 | FUNC_ATTR_NONNULL_ALL |
60 | { |
61 | FileDescriptor fp; |
62 | const int fo_ret = file_open(&fp, fname, kFileCreate|kFileAppend, 0600); |
63 | if (fo_ret != 0) { |
64 | emsgf(_("E5142: Failed to open file %s: %s" ), fname, os_strerror(fo_ret)); |
65 | return; |
66 | } |
67 | for (ListLog *chunk = list_log_first; chunk != NULL;) { |
68 | for (size_t i = 0; i < chunk->size; i++) { |
69 | char buf[10 + 1 + ((16 + 3) * 3) + (8 + 2) + 2]; |
70 | // act : hex " c:" len "[]" "\n\0" |
71 | const ListLogEntry entry = chunk->entries[i]; |
72 | const size_t snp_len = (size_t)snprintf( |
73 | buf, sizeof(buf), |
74 | "%-10.10s: l:%016" PRIxPTR "[%08d] 1:%016" PRIxPTR " 2:%016" PRIxPTR |
75 | "\n" , |
76 | entry.action, entry.l, entry.len, entry.li1, entry.li2); |
77 | assert(snp_len + 1 == sizeof(buf)); |
78 | const ptrdiff_t fw_ret = file_write(&fp, buf, snp_len); |
79 | if (fw_ret != (ptrdiff_t)snp_len) { |
80 | assert(fw_ret < 0); |
81 | if (i) { |
82 | memmove(chunk->entries, chunk->entries + i, |
83 | sizeof(chunk->entries[0]) * (chunk->size - i)); |
84 | chunk->size -= i; |
85 | } |
86 | emsgf(_("E5143: Failed to write to file %s: %s" ), |
87 | fname, os_strerror((int)fw_ret)); |
88 | return; |
89 | } |
90 | } |
91 | list_log_first = chunk->next; |
92 | xfree(chunk); |
93 | chunk = list_log_first; |
94 | } |
95 | const int fc_ret = file_close(&fp, true); |
96 | if (fc_ret != 0) { |
97 | emsgf(_("E5144: Failed to close file %s: %s" ), fname, os_strerror(fc_ret)); |
98 | } |
99 | } |
100 | |
101 | #ifdef EXITFREE |
102 | /// Free list log |
103 | void list_free_log(void) |
104 | { |
105 | for (ListLog *chunk = list_log_first; chunk != NULL;) { |
106 | list_log_first = chunk->next; |
107 | xfree(chunk); |
108 | chunk = list_log_first; |
109 | } |
110 | } |
111 | #endif |
112 | #endif |
113 | //{{{2 List item |
114 | |
115 | /// Allocate a list item |
116 | /// |
117 | /// @warning Allocated item is not initialized, do not forget to initialize it |
118 | /// and specifically set lv_lock. |
119 | /// |
120 | /// @return [allocated] new list item. |
121 | static listitem_T *tv_list_item_alloc(void) |
122 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC |
123 | { |
124 | return xmalloc(sizeof(listitem_T)); |
125 | } |
126 | |
127 | /// Remove a list item from a List and free it |
128 | /// |
129 | /// Also clears the value. |
130 | /// |
131 | /// @param[out] l List to remove item from. |
132 | /// @param[in,out] item Item to remove. |
133 | /// |
134 | /// @return Pointer to the list item just after removed one, NULL if removed |
135 | /// item was the last one. |
136 | listitem_T *tv_list_item_remove(list_T *const l, listitem_T *const item) |
137 | FUNC_ATTR_NONNULL_ALL |
138 | { |
139 | listitem_T *const next_item = TV_LIST_ITEM_NEXT(l, item); |
140 | tv_list_drop_items(l, item, item); |
141 | tv_clear(TV_LIST_ITEM_TV(item)); |
142 | xfree(item); |
143 | return next_item; |
144 | } |
145 | |
146 | //{{{2 List watchers |
147 | |
148 | /// Add a watcher to a list |
149 | /// |
150 | /// @param[out] l List to add watcher to. |
151 | /// @param[in] lw Watcher to add. |
152 | void tv_list_watch_add(list_T *const l, listwatch_T *const lw) |
153 | FUNC_ATTR_NONNULL_ALL |
154 | { |
155 | lw->lw_next = l->lv_watch; |
156 | l->lv_watch = lw; |
157 | } |
158 | |
159 | /// Remove a watcher from a list |
160 | /// |
161 | /// Does not give a warning if watcher was not found. |
162 | /// |
163 | /// @param[out] l List to remove watcher from. |
164 | /// @param[in] lwrem Watcher to remove. |
165 | void tv_list_watch_remove(list_T *const l, listwatch_T *const lwrem) |
166 | FUNC_ATTR_NONNULL_ALL |
167 | { |
168 | listwatch_T **lwp = &l->lv_watch; |
169 | for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { |
170 | if (lw == lwrem) { |
171 | *lwp = lw->lw_next; |
172 | break; |
173 | } |
174 | lwp = &lw->lw_next; |
175 | } |
176 | } |
177 | |
178 | /// Advance watchers to the next item |
179 | /// |
180 | /// Used just before removing an item from a list. |
181 | /// |
182 | /// @param[out] l List from which item is removed. |
183 | /// @param[in] item List item being removed. |
184 | void tv_list_watch_fix(list_T *const l, const listitem_T *const item) |
185 | FUNC_ATTR_NONNULL_ALL |
186 | { |
187 | for (listwatch_T *lw = l->lv_watch; lw != NULL; lw = lw->lw_next) { |
188 | if (lw->lw_item == item) { |
189 | lw->lw_item = item->li_next; |
190 | } |
191 | } |
192 | } |
193 | |
194 | //{{{2 Alloc/free |
195 | |
196 | /// Allocate an empty list |
197 | /// |
198 | /// Caller should take care of the reference count. |
199 | /// |
200 | /// @param[in] len Expected number of items to be populated before list |
201 | /// becomes accessible from VimL. It is still valid to |
202 | /// underpopulate a list, value only controls how many elements |
203 | /// will be allocated in advance. Currently does nothing. |
204 | /// @see ListLenSpecials. |
205 | /// |
206 | /// @return [allocated] new list. |
207 | list_T *tv_list_alloc(const ptrdiff_t len) |
208 | FUNC_ATTR_NONNULL_RET |
209 | { |
210 | list_T *const list = xcalloc(1, sizeof(list_T)); |
211 | |
212 | // Prepend the list to the list of lists for garbage collection. |
213 | if (gc_first_list != NULL) { |
214 | gc_first_list->lv_used_prev = list; |
215 | } |
216 | list->lv_used_prev = NULL; |
217 | list->lv_used_next = gc_first_list; |
218 | gc_first_list = list; |
219 | list_log(list, NULL, (void *)(uintptr_t)len, "alloc" ); |
220 | return list; |
221 | } |
222 | |
223 | /// Initialize a static list with 10 items |
224 | /// |
225 | /// @param[out] sl Static list to initialize. |
226 | void tv_list_init_static10(staticList10_T *const sl) |
227 | FUNC_ATTR_NONNULL_ALL |
228 | { |
229 | #define SL_SIZE ARRAY_SIZE(sl->sl_items) |
230 | list_T *const l = &sl->sl_list; |
231 | |
232 | memset(sl, 0, sizeof(staticList10_T)); |
233 | l->lv_first = &sl->sl_items[0]; |
234 | l->lv_last = &sl->sl_items[SL_SIZE - 1]; |
235 | l->lv_refcount = DO_NOT_FREE_CNT; |
236 | tv_list_set_lock(l, VAR_FIXED); |
237 | sl->sl_list.lv_len = 10; |
238 | |
239 | sl->sl_items[0].li_prev = NULL; |
240 | sl->sl_items[0].li_next = &sl->sl_items[1]; |
241 | sl->sl_items[SL_SIZE - 1].li_prev = &sl->sl_items[SL_SIZE - 2]; |
242 | sl->sl_items[SL_SIZE - 1].li_next = NULL; |
243 | |
244 | for (size_t i = 1; i < SL_SIZE - 1; i++) { |
245 | listitem_T *const li = &sl->sl_items[i]; |
246 | li->li_prev = li - 1; |
247 | li->li_next = li + 1; |
248 | } |
249 | list_log((const list_T *)sl, &sl->sl_items[0], &sl->sl_items[SL_SIZE - 1], |
250 | "s10init" ); |
251 | #undef SL_SIZE |
252 | } |
253 | |
254 | /// Initialize static list with undefined number of elements |
255 | /// |
256 | /// @param[out] l List to initialize. |
257 | void tv_list_init_static(list_T *const l) |
258 | FUNC_ATTR_NONNULL_ALL |
259 | { |
260 | memset(l, 0, sizeof(*l)); |
261 | l->lv_refcount = DO_NOT_FREE_CNT; |
262 | list_log(l, NULL, NULL, "sinit" ); |
263 | } |
264 | |
265 | /// Free items contained in a list |
266 | /// |
267 | /// @param[in,out] l List to clear. |
268 | void tv_list_free_contents(list_T *const l) |
269 | FUNC_ATTR_NONNULL_ALL |
270 | { |
271 | list_log(l, NULL, NULL, "freecont" ); |
272 | for (listitem_T *item = l->lv_first; item != NULL; item = l->lv_first) { |
273 | // Remove the item before deleting it. |
274 | l->lv_first = item->li_next; |
275 | tv_clear(&item->li_tv); |
276 | xfree(item); |
277 | } |
278 | l->lv_len = 0; |
279 | l->lv_idx_item = NULL; |
280 | l->lv_last = NULL; |
281 | assert(l->lv_watch == NULL); |
282 | } |
283 | |
284 | /// Free a list itself, ignoring items it contains |
285 | /// |
286 | /// Ignores the reference count. |
287 | /// |
288 | /// @param[in,out] l List to free. |
289 | void tv_list_free_list(list_T *const l) |
290 | FUNC_ATTR_NONNULL_ALL |
291 | { |
292 | // Remove the list from the list of lists for garbage collection. |
293 | if (l->lv_used_prev == NULL) { |
294 | gc_first_list = l->lv_used_next; |
295 | } else { |
296 | l->lv_used_prev->lv_used_next = l->lv_used_next; |
297 | } |
298 | if (l->lv_used_next != NULL) { |
299 | l->lv_used_next->lv_used_prev = l->lv_used_prev; |
300 | } |
301 | list_log(l, NULL, NULL, "freelist" ); |
302 | |
303 | xfree(l); |
304 | } |
305 | |
306 | /// Free a list, including all items it points to |
307 | /// |
308 | /// Ignores the reference count. Does not do anything if |
309 | /// tv_in_free_unref_items is true. |
310 | /// |
311 | /// @param[in,out] l List to free. |
312 | void tv_list_free(list_T *const l) |
313 | FUNC_ATTR_NONNULL_ALL |
314 | { |
315 | if (!tv_in_free_unref_items) { |
316 | tv_list_free_contents(l); |
317 | tv_list_free_list(l); |
318 | } |
319 | } |
320 | |
321 | /// Unreference a list |
322 | /// |
323 | /// Decrements the reference count and frees when it becomes zero or less. |
324 | /// |
325 | /// @param[in,out] l List to unreference. |
326 | void tv_list_unref(list_T *const l) |
327 | { |
328 | if (l != NULL && --l->lv_refcount <= 0) { |
329 | tv_list_free(l); |
330 | } |
331 | } |
332 | |
333 | //{{{2 Add/remove |
334 | |
335 | /// Remove items "item" to "item2" from list "l" |
336 | /// |
337 | /// @warning Does not free the listitem or the value! |
338 | /// |
339 | /// @param[out] l List to remove from. |
340 | /// @param[in] item First item to remove. |
341 | /// @param[in] item2 Last item to remove. |
342 | void tv_list_drop_items(list_T *const l, listitem_T *const item, |
343 | listitem_T *const item2) |
344 | FUNC_ATTR_NONNULL_ALL |
345 | { |
346 | list_log(l, item, item2, "drop" ); |
347 | // Notify watchers. |
348 | for (listitem_T *ip = item; ip != item2->li_next; ip = ip->li_next) { |
349 | l->lv_len--; |
350 | tv_list_watch_fix(l, ip); |
351 | } |
352 | |
353 | if (item2->li_next == NULL) { |
354 | l->lv_last = item->li_prev; |
355 | } else { |
356 | item2->li_next->li_prev = item->li_prev; |
357 | } |
358 | if (item->li_prev == NULL) { |
359 | l->lv_first = item2->li_next; |
360 | } else { |
361 | item->li_prev->li_next = item2->li_next; |
362 | } |
363 | l->lv_idx_item = NULL; |
364 | list_log(l, l->lv_first, l->lv_last, "afterdrop" ); |
365 | } |
366 | |
367 | /// Like tv_list_drop_items, but also frees all removed items |
368 | void tv_list_remove_items(list_T *const l, listitem_T *const item, |
369 | listitem_T *const item2) |
370 | FUNC_ATTR_NONNULL_ALL |
371 | { |
372 | list_log(l, item, item2, "remove" ); |
373 | tv_list_drop_items(l, item, item2); |
374 | for (listitem_T *li = item;;) { |
375 | tv_clear(TV_LIST_ITEM_TV(li)); |
376 | listitem_T *const nli = li->li_next; |
377 | xfree(li); |
378 | if (li == item2) { |
379 | break; |
380 | } |
381 | li = nli; |
382 | } |
383 | } |
384 | |
385 | /// Move items "item" to "item2" from list "l" to the end of the list "tgt_l" |
386 | /// |
387 | /// @param[out] l List to move from. |
388 | /// @param[in] item First item to move. |
389 | /// @param[in] item2 Last item to move. |
390 | /// @param[out] tgt_l List to move to. |
391 | /// @param[in] cnt Number of items moved. |
392 | void tv_list_move_items(list_T *const l, listitem_T *const item, |
393 | listitem_T *const item2, list_T *const tgt_l, |
394 | const int cnt) |
395 | FUNC_ATTR_NONNULL_ALL |
396 | { |
397 | list_log(l, item, item2, "move" ); |
398 | tv_list_drop_items(l, item, item2); |
399 | item->li_prev = tgt_l->lv_last; |
400 | item2->li_next = NULL; |
401 | if (tgt_l->lv_last == NULL) { |
402 | tgt_l->lv_first = item; |
403 | } else { |
404 | tgt_l->lv_last->li_next = item; |
405 | } |
406 | tgt_l->lv_last = item2; |
407 | tgt_l->lv_len += cnt; |
408 | list_log(tgt_l, tgt_l->lv_first, tgt_l->lv_last, "movetgt" ); |
409 | } |
410 | |
411 | /// Insert list item |
412 | /// |
413 | /// @param[out] l List to insert to. |
414 | /// @param[in,out] ni Item to insert. |
415 | /// @param[in] item Item to insert before. If NULL, inserts at the end of the |
416 | /// list. |
417 | void tv_list_insert(list_T *const l, listitem_T *const ni, |
418 | listitem_T *const item) |
419 | FUNC_ATTR_NONNULL_ARG(1, 2) |
420 | { |
421 | if (item == NULL) { |
422 | // Append new item at end of list. |
423 | tv_list_append(l, ni); |
424 | } else { |
425 | // Insert new item before existing item. |
426 | ni->li_prev = item->li_prev; |
427 | ni->li_next = item; |
428 | if (item->li_prev == NULL) { |
429 | l->lv_first = ni; |
430 | l->lv_idx++; |
431 | } else { |
432 | item->li_prev->li_next = ni; |
433 | l->lv_idx_item = NULL; |
434 | } |
435 | item->li_prev = ni; |
436 | l->lv_len++; |
437 | list_log(l, ni, item, "insert" ); |
438 | } |
439 | } |
440 | |
441 | /// Insert VimL value into a list |
442 | /// |
443 | /// @param[out] l List to insert to. |
444 | /// @param[in,out] tv Value to insert. Is copied (@see tv_copy()) to an |
445 | /// allocated listitem_T and inserted. |
446 | /// @param[in] item Item to insert before. If NULL, inserts at the end of the |
447 | /// list. |
448 | void tv_list_insert_tv(list_T *const l, typval_T *const tv, |
449 | listitem_T *const item) |
450 | { |
451 | listitem_T *const ni = tv_list_item_alloc(); |
452 | |
453 | tv_copy(tv, &ni->li_tv); |
454 | tv_list_insert(l, ni, item); |
455 | } |
456 | |
457 | /// Append item to the end of list |
458 | /// |
459 | /// @param[out] l List to append to. |
460 | /// @param[in,out] item Item to append. |
461 | void tv_list_append(list_T *const l, listitem_T *const item) |
462 | FUNC_ATTR_NONNULL_ALL |
463 | { |
464 | list_log(l, item, NULL, "append" ); |
465 | if (l->lv_last == NULL) { |
466 | // empty list |
467 | l->lv_first = item; |
468 | l->lv_last = item; |
469 | item->li_prev = NULL; |
470 | } else { |
471 | l->lv_last->li_next = item; |
472 | item->li_prev = l->lv_last; |
473 | l->lv_last = item; |
474 | } |
475 | l->lv_len++; |
476 | item->li_next = NULL; |
477 | } |
478 | |
479 | /// Append VimL value to the end of list |
480 | /// |
481 | /// @param[out] l List to append to. |
482 | /// @param[in,out] tv Value to append. Is copied (@see tv_copy()) to an |
483 | /// allocated listitem_T. |
484 | void tv_list_append_tv(list_T *const l, typval_T *const tv) |
485 | FUNC_ATTR_NONNULL_ALL |
486 | { |
487 | listitem_T *const li = tv_list_item_alloc(); |
488 | tv_copy(tv, TV_LIST_ITEM_TV(li)); |
489 | tv_list_append(l, li); |
490 | } |
491 | |
492 | /// Like tv_list_append_tv(), but tv is moved to a list |
493 | /// |
494 | /// This means that it is no longer valid to use contents of the typval_T after |
495 | /// function exits. |
496 | void tv_list_append_owned_tv(list_T *const l, typval_T tv) |
497 | FUNC_ATTR_NONNULL_ALL |
498 | { |
499 | listitem_T *const li = tv_list_item_alloc(); |
500 | *TV_LIST_ITEM_TV(li) = tv; |
501 | tv_list_append(l, li); |
502 | } |
503 | |
504 | /// Append a list to a list as one item |
505 | /// |
506 | /// @param[out] l List to append to. |
507 | /// @param[in,out] itemlist List to append. Reference count is increased. |
508 | void tv_list_append_list(list_T *const l, list_T *const itemlist) |
509 | FUNC_ATTR_NONNULL_ARG(1) |
510 | { |
511 | tv_list_append_owned_tv(l, (typval_T) { |
512 | .v_type = VAR_LIST, |
513 | .v_lock = VAR_UNLOCKED, |
514 | .vval.v_list = itemlist, |
515 | }); |
516 | tv_list_ref(itemlist); |
517 | } |
518 | |
519 | /// Append a dictionary to a list |
520 | /// |
521 | /// @param[out] l List to append to. |
522 | /// @param[in,out] dict Dictionary to append. Reference count is increased. |
523 | void tv_list_append_dict(list_T *const l, dict_T *const dict) |
524 | FUNC_ATTR_NONNULL_ARG(1) |
525 | { |
526 | tv_list_append_owned_tv(l, (typval_T) { |
527 | .v_type = VAR_DICT, |
528 | .v_lock = VAR_UNLOCKED, |
529 | .vval.v_dict = dict, |
530 | }); |
531 | if (dict != NULL) { |
532 | dict->dv_refcount++; |
533 | } |
534 | } |
535 | |
536 | /// Make a copy of "str" and append it as an item to list "l" |
537 | /// |
538 | /// @param[out] l List to append to. |
539 | /// @param[in] str String to append. |
540 | /// @param[in] len Length of the appended string. May be -1, in this |
541 | /// case string is considered to be usual zero-terminated |
542 | /// string or NULL “empty” string. |
543 | void tv_list_append_string(list_T *const l, const char *const str, |
544 | const ssize_t len) |
545 | FUNC_ATTR_NONNULL_ARG(1) |
546 | { |
547 | tv_list_append_owned_tv(l, (typval_T) { |
548 | .v_type = VAR_STRING, |
549 | .v_lock = VAR_UNLOCKED, |
550 | .vval.v_string = (str == NULL |
551 | ? NULL |
552 | : (len >= 0 |
553 | ? xmemdupz(str, (size_t)len) |
554 | : xstrdup(str))), |
555 | }); |
556 | } |
557 | |
558 | /// Append given string to the list |
559 | /// |
560 | /// Unlike list_append_string this function does not copy the string. |
561 | /// |
562 | /// @param[out] l List to append to. |
563 | /// @param[in] str String to append. |
564 | void tv_list_append_allocated_string(list_T *const l, char *const str) |
565 | FUNC_ATTR_NONNULL_ARG(1) |
566 | { |
567 | tv_list_append_owned_tv(l, (typval_T) { |
568 | .v_type = VAR_STRING, |
569 | .v_lock = VAR_UNLOCKED, |
570 | .vval.v_string = (char_u *)str, |
571 | }); |
572 | } |
573 | |
574 | /// Append number to the list |
575 | /// |
576 | /// @param[out] l List to append to. |
577 | /// @param[in] n Number to append. Will be recorded in the allocated |
578 | /// listitem_T. |
579 | void tv_list_append_number(list_T *const l, const varnumber_T n) |
580 | { |
581 | tv_list_append_owned_tv(l, (typval_T) { |
582 | .v_type = VAR_NUMBER, |
583 | .v_lock = VAR_UNLOCKED, |
584 | .vval.v_number = n, |
585 | }); |
586 | } |
587 | |
588 | //{{{2 Operations on the whole list |
589 | |
590 | /// Make a copy of list |
591 | /// |
592 | /// @param[in] conv If non-NULL, then all internal strings will be converted. |
593 | /// Only used when `deep` is true. |
594 | /// @param[in] orig Original list to copy. |
595 | /// @param[in] deep If false, then shallow copy will be done. |
596 | /// @param[in] copyID See var_item_copy(). |
597 | /// |
598 | /// @return Copied list. May be NULL in case original list is NULL or some |
599 | /// failure happens. The refcount of the new list is set to 1. |
600 | list_T *tv_list_copy(const vimconv_T *const conv, list_T *const orig, |
601 | const bool deep, const int copyID) |
602 | FUNC_ATTR_WARN_UNUSED_RESULT |
603 | { |
604 | if (orig == NULL) { |
605 | return NULL; |
606 | } |
607 | |
608 | list_T *copy = tv_list_alloc(tv_list_len(orig)); |
609 | tv_list_ref(copy); |
610 | if (copyID != 0) { |
611 | // Do this before adding the items, because one of the items may |
612 | // refer back to this list. |
613 | orig->lv_copyID = copyID; |
614 | orig->lv_copylist = copy; |
615 | } |
616 | TV_LIST_ITER(orig, item, { |
617 | if (got_int) { |
618 | break; |
619 | } |
620 | listitem_T *const ni = tv_list_item_alloc(); |
621 | if (deep) { |
622 | if (var_item_copy(conv, TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni), |
623 | deep, copyID) == FAIL) { |
624 | xfree(ni); |
625 | goto tv_list_copy_error; |
626 | } |
627 | } else { |
628 | tv_copy(TV_LIST_ITEM_TV(item), TV_LIST_ITEM_TV(ni)); |
629 | } |
630 | tv_list_append(copy, ni); |
631 | }); |
632 | |
633 | return copy; |
634 | |
635 | tv_list_copy_error: |
636 | tv_list_unref(copy); |
637 | return NULL; |
638 | } |
639 | |
640 | /// Extend first list with the second |
641 | /// |
642 | /// @param[out] l1 List to extend. |
643 | /// @param[in] l2 List to extend with. |
644 | /// @param[in] bef If not NULL, extends before this item. |
645 | void tv_list_extend(list_T *const l1, list_T *const l2, |
646 | listitem_T *const bef) |
647 | FUNC_ATTR_NONNULL_ARG(1) |
648 | { |
649 | int todo = tv_list_len(l2); |
650 | listitem_T *const befbef = (bef == NULL ? NULL : bef->li_prev); |
651 | listitem_T *const saved_next = (befbef == NULL ? NULL : befbef->li_next); |
652 | // We also quit the loop when we have inserted the original item count of |
653 | // the list, avoid a hang when we extend a list with itself. |
654 | for (listitem_T *item = tv_list_first(l2) |
655 | ; item != NULL && todo-- |
656 | ; item = (item == befbef ? saved_next : item->li_next)) { |
657 | tv_list_insert_tv(l1, TV_LIST_ITEM_TV(item), bef); |
658 | } |
659 | } |
660 | |
661 | /// Concatenate lists into a new list |
662 | /// |
663 | /// @param[in] l1 First list. |
664 | /// @param[in] l2 Second list. |
665 | /// @param[out] ret_tv Location where new list is saved. |
666 | /// |
667 | /// @return OK or FAIL. |
668 | int tv_list_concat(list_T *const l1, list_T *const l2, typval_T *const tv) |
669 | FUNC_ATTR_WARN_UNUSED_RESULT |
670 | { |
671 | list_T *l; |
672 | |
673 | tv->v_type = VAR_LIST; |
674 | |
675 | if (l1 == NULL && l2 == NULL) { |
676 | l = NULL; |
677 | } else if (l1 == NULL) { |
678 | l = tv_list_copy(NULL, l2, false, 0); |
679 | } else { |
680 | l = tv_list_copy(NULL, l1, false, 0); |
681 | if (l != NULL && l2 != NULL) { |
682 | tv_list_extend(l, l2, NULL); |
683 | } |
684 | } |
685 | if (l == NULL && !(l1 == NULL && l2 == NULL)) { |
686 | return FAIL; |
687 | } |
688 | |
689 | tv->vval.v_list = l; |
690 | return OK; |
691 | } |
692 | |
693 | typedef struct { |
694 | char_u *s; |
695 | char_u *tofree; |
696 | } Join; |
697 | |
698 | /// Join list into a string, helper function |
699 | /// |
700 | /// @param[out] gap Garray where result will be saved. |
701 | /// @param[in] l List to join. |
702 | /// @param[in] sep Used separator. |
703 | /// @param[in] join_gap Garray to keep each list item string. |
704 | /// |
705 | /// @return OK in case of success, FAIL otherwise. |
706 | static int list_join_inner(garray_T *const gap, list_T *const l, |
707 | const char *const sep, garray_T *const join_gap) |
708 | FUNC_ATTR_NONNULL_ALL |
709 | { |
710 | size_t sumlen = 0; |
711 | bool first = true; |
712 | |
713 | // Stringify each item in the list. |
714 | TV_LIST_ITER(l, item, { |
715 | if (got_int) { |
716 | break; |
717 | } |
718 | char *s; |
719 | size_t len; |
720 | s = encode_tv2echo(TV_LIST_ITEM_TV(item), &len); |
721 | if (s == NULL) { |
722 | return FAIL; |
723 | } |
724 | |
725 | sumlen += len; |
726 | |
727 | Join *const p = GA_APPEND_VIA_PTR(Join, join_gap); |
728 | p->tofree = p->s = (char_u *)s; |
729 | |
730 | line_breakcheck(); |
731 | }); |
732 | |
733 | // Allocate result buffer with its total size, avoid re-allocation and |
734 | // multiple copy operations. Add 2 for a tailing ']' and NUL. |
735 | if (join_gap->ga_len >= 2) { |
736 | sumlen += strlen(sep) * (size_t)(join_gap->ga_len - 1); |
737 | } |
738 | ga_grow(gap, (int)sumlen + 2); |
739 | |
740 | for (int i = 0; i < join_gap->ga_len && !got_int; i++) { |
741 | if (first) { |
742 | first = false; |
743 | } else { |
744 | ga_concat(gap, (const char_u *)sep); |
745 | } |
746 | const Join *const p = ((const Join *)join_gap->ga_data) + i; |
747 | |
748 | if (p->s != NULL) { |
749 | ga_concat(gap, p->s); |
750 | } |
751 | line_breakcheck(); |
752 | } |
753 | |
754 | return OK; |
755 | } |
756 | |
757 | /// Join list into a string using given separator |
758 | /// |
759 | /// @param[out] gap Garray where result will be saved. |
760 | /// @param[in] l Joined list. |
761 | /// @param[in] sep Separator. |
762 | /// |
763 | /// @return OK in case of success, FAIL otherwise. |
764 | int tv_list_join(garray_T *const gap, list_T *const l, const char *const sep) |
765 | FUNC_ATTR_NONNULL_ARG(1) |
766 | { |
767 | if (!tv_list_len(l)) { |
768 | return OK; |
769 | } |
770 | |
771 | garray_T join_ga; |
772 | int retval; |
773 | |
774 | ga_init(&join_ga, (int)sizeof(Join), tv_list_len(l)); |
775 | retval = list_join_inner(gap, l, sep, &join_ga); |
776 | |
777 | #define FREE_JOIN_TOFREE(join) xfree((join)->tofree) |
778 | GA_DEEP_CLEAR(&join_ga, Join, FREE_JOIN_TOFREE); |
779 | #undef FREE_JOIN_TOFREE |
780 | |
781 | return retval; |
782 | } |
783 | |
784 | /// Chech whether two lists are equal |
785 | /// |
786 | /// @param[in] l1 First list to compare. |
787 | /// @param[in] l2 Second list to compare. |
788 | /// @param[in] ic True if case is to be ignored. |
789 | /// @param[in] recursive True when used recursively. |
790 | /// |
791 | /// @return True if lists are equal, false otherwise. |
792 | bool tv_list_equal(list_T *const l1, list_T *const l2, const bool ic, |
793 | const bool recursive) |
794 | FUNC_ATTR_WARN_UNUSED_RESULT |
795 | { |
796 | if (l1 == l2) { |
797 | return true; |
798 | } |
799 | if (l1 == NULL || l2 == NULL) { |
800 | return false; |
801 | } |
802 | if (tv_list_len(l1) != tv_list_len(l2)) { |
803 | return false; |
804 | } |
805 | |
806 | listitem_T *item1 = tv_list_first(l1); |
807 | listitem_T *item2 = tv_list_first(l2); |
808 | for (; item1 != NULL && item2 != NULL |
809 | ; (item1 = TV_LIST_ITEM_NEXT(l1, item1), |
810 | item2 = TV_LIST_ITEM_NEXT(l2, item2))) { |
811 | if (!tv_equal(TV_LIST_ITEM_TV(item1), TV_LIST_ITEM_TV(item2), ic, |
812 | recursive)) { |
813 | return false; |
814 | } |
815 | } |
816 | assert(item1 == NULL && item2 == NULL); |
817 | return true; |
818 | } |
819 | |
820 | /// Reverse list in-place |
821 | /// |
822 | /// @param[in,out] l List to reverse. |
823 | void tv_list_reverse(list_T *const l) |
824 | { |
825 | if (tv_list_len(l) <= 1) { |
826 | return; |
827 | } |
828 | list_log(l, NULL, NULL, "reverse" ); |
829 | #define SWAP(a, b) \ |
830 | do { \ |
831 | tmp = a; \ |
832 | a = b; \ |
833 | b = tmp; \ |
834 | } while (0) |
835 | listitem_T *tmp; |
836 | |
837 | SWAP(l->lv_first, l->lv_last); |
838 | for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) { |
839 | SWAP(li->li_next, li->li_prev); |
840 | } |
841 | #undef SWAP |
842 | |
843 | l->lv_idx = l->lv_len - l->lv_idx - 1; |
844 | } |
845 | |
846 | // FIXME Add unit tests for tv_list_item_sort(). |
847 | |
848 | /// Sort list using libc qsort |
849 | /// |
850 | /// @param[in,out] l List to sort, will be sorted in-place. |
851 | /// @param ptrs Preallocated array of items to sort, must have at least |
852 | /// tv_list_len(l) entries. Should not be initialized. |
853 | /// @param[in] item_compare_func Function used to compare list items. |
854 | /// @param errp Location where information about whether error occurred is |
855 | /// saved by item_compare_func. If boolean there appears to be |
856 | /// true list will not be modified. Must be initialized to false |
857 | /// by the caller. |
858 | void tv_list_item_sort(list_T *const l, ListSortItem *const ptrs, |
859 | const ListSorter item_compare_func, |
860 | bool *errp) |
861 | FUNC_ATTR_NONNULL_ARG(3, 4) |
862 | { |
863 | const int len = tv_list_len(l); |
864 | if (len <= 1) { |
865 | return; |
866 | } |
867 | list_log(l, NULL, NULL, "sort" ); |
868 | int i = 0; |
869 | TV_LIST_ITER(l, li, { |
870 | ptrs[i].item = li; |
871 | ptrs[i].idx = i; |
872 | i++; |
873 | }); |
874 | // Sort the array with item pointers. |
875 | qsort(ptrs, (size_t)len, sizeof(ListSortItem), item_compare_func); |
876 | if (!(*errp)) { |
877 | // Clear the list and append the items in the sorted order. |
878 | l->lv_first = NULL; |
879 | l->lv_last = NULL; |
880 | l->lv_idx_item = NULL; |
881 | l->lv_len = 0; |
882 | for (i = 0; i < len; i++) { |
883 | tv_list_append(l, ptrs[i].item); |
884 | } |
885 | } |
886 | } |
887 | |
888 | //{{{2 Indexing/searching |
889 | |
890 | /// Locate item with a given index in a list and return it |
891 | /// |
892 | /// @param[in] l List to index. |
893 | /// @param[in] n Index. Negative index is counted from the end, -1 is the last |
894 | /// item. |
895 | /// |
896 | /// @return Item at the given index or NULL if `n` is out of range. |
897 | listitem_T *tv_list_find(list_T *const l, int n) |
898 | FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT |
899 | { |
900 | STATIC_ASSERT(sizeof(n) == sizeof(l->lv_idx), |
901 | "n and lv_idx sizes do not match" ); |
902 | if (l == NULL) { |
903 | return NULL; |
904 | } |
905 | |
906 | n = tv_list_uidx(l, n); |
907 | if (n == -1) { |
908 | return NULL; |
909 | } |
910 | |
911 | int idx; |
912 | listitem_T *item; |
913 | |
914 | // When there is a cached index may start search from there. |
915 | if (l->lv_idx_item != NULL) { |
916 | if (n < l->lv_idx / 2) { |
917 | // Closest to the start of the list. |
918 | item = l->lv_first; |
919 | idx = 0; |
920 | } else if (n > (l->lv_idx + l->lv_len) / 2) { |
921 | // Closest to the end of the list. |
922 | item = l->lv_last; |
923 | idx = l->lv_len - 1; |
924 | } else { |
925 | // Closest to the cached index. |
926 | item = l->lv_idx_item; |
927 | idx = l->lv_idx; |
928 | } |
929 | } else { |
930 | if (n < l->lv_len / 2) { |
931 | // Closest to the start of the list. |
932 | item = l->lv_first; |
933 | idx = 0; |
934 | } else { |
935 | // Closest to the end of the list. |
936 | item = l->lv_last; |
937 | idx = l->lv_len - 1; |
938 | } |
939 | } |
940 | |
941 | while (n > idx) { |
942 | // Search forward. |
943 | item = item->li_next; |
944 | idx++; |
945 | } |
946 | while (n < idx) { |
947 | // Search backward. |
948 | item = item->li_prev; |
949 | idx--; |
950 | } |
951 | |
952 | assert(idx == n); |
953 | // Cache the used index. |
954 | l->lv_idx = idx; |
955 | l->lv_idx_item = item; |
956 | list_log(l, l->lv_idx_item, (void *)(uintptr_t)l->lv_idx, "find" ); |
957 | |
958 | return item; |
959 | } |
960 | |
961 | /// Get list item l[n] as a number |
962 | /// |
963 | /// @param[in] l List to index. |
964 | /// @param[in] n Index in a list. |
965 | /// @param[out] ret_error Location where 1 will be saved if index was not |
966 | /// found. May be NULL. If everything is OK, |
967 | /// `*ret_error` is not touched. |
968 | /// |
969 | /// @return Integer value at the given index or -1. |
970 | varnumber_T tv_list_find_nr(list_T *const l, const int n, bool *const ret_error) |
971 | FUNC_ATTR_WARN_UNUSED_RESULT |
972 | { |
973 | const listitem_T *const li = tv_list_find(l, n); |
974 | if (li == NULL) { |
975 | if (ret_error != NULL) { |
976 | *ret_error = true; |
977 | } |
978 | return -1; |
979 | } |
980 | return tv_get_number_chk(TV_LIST_ITEM_TV(li), ret_error); |
981 | } |
982 | |
983 | /// Get list item l[n] as a string |
984 | /// |
985 | /// @param[in] l List to index. |
986 | /// @param[in] n Index in a list. |
987 | /// |
988 | /// @return List item string value or NULL in case of error. |
989 | const char *tv_list_find_str(list_T *const l, const int n) |
990 | FUNC_ATTR_WARN_UNUSED_RESULT |
991 | { |
992 | const listitem_T *const li = tv_list_find(l, n); |
993 | if (li == NULL) { |
994 | EMSG2(_(e_listidx), (int64_t)n); |
995 | return NULL; |
996 | } |
997 | return tv_get_string(TV_LIST_ITEM_TV(li)); |
998 | } |
999 | |
1000 | /// Locate item in a list and return its index |
1001 | /// |
1002 | /// @param[in] l List to search. |
1003 | /// @param[in] item Item to search for. |
1004 | /// |
1005 | /// @return Index of an item or -1 if item is not in the list. |
1006 | long tv_list_idx_of_item(const list_T *const l, const listitem_T *const item) |
1007 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE |
1008 | { |
1009 | if (l == NULL) { |
1010 | return -1; |
1011 | } |
1012 | int idx = 0; |
1013 | TV_LIST_ITER_CONST(l, li, { |
1014 | if (li == item) { |
1015 | return idx; |
1016 | } |
1017 | idx++; |
1018 | }); |
1019 | return -1; |
1020 | } |
1021 | |
1022 | //{{{1 Dictionaries |
1023 | //{{{2 Dictionary watchers |
1024 | |
1025 | /// Perform all necessary cleanup for a `DictWatcher` instance |
1026 | /// |
1027 | /// @param watcher Watcher to free. |
1028 | static void tv_dict_watcher_free(DictWatcher *watcher) |
1029 | FUNC_ATTR_NONNULL_ALL |
1030 | { |
1031 | callback_free(&watcher->callback); |
1032 | xfree(watcher->key_pattern); |
1033 | xfree(watcher); |
1034 | } |
1035 | |
1036 | /// Add watcher to a dictionary |
1037 | /// |
1038 | /// @param[in] dict Dictionary to add watcher to. |
1039 | /// @param[in] key_pattern Pattern to watch for. |
1040 | /// @param[in] key_pattern_len Key pattern length. |
1041 | /// @param callback Function to be called on events. |
1042 | void tv_dict_watcher_add(dict_T *const dict, const char *const key_pattern, |
1043 | const size_t key_pattern_len, Callback callback) |
1044 | FUNC_ATTR_NONNULL_ARG(2) |
1045 | { |
1046 | if (dict == NULL) { |
1047 | return; |
1048 | } |
1049 | DictWatcher *const watcher = xmalloc(sizeof(DictWatcher)); |
1050 | watcher->key_pattern = xmemdupz(key_pattern, key_pattern_len); |
1051 | watcher->key_pattern_len = key_pattern_len; |
1052 | watcher->callback = callback; |
1053 | watcher->busy = false; |
1054 | QUEUE_INSERT_TAIL(&dict->watchers, &watcher->node); |
1055 | } |
1056 | |
1057 | /// Check whether two callbacks are equal |
1058 | /// |
1059 | /// @param[in] cb1 First callback to check. |
1060 | /// @param[in] cb2 Second callback to check. |
1061 | /// |
1062 | /// @return True if they are equal, false otherwise. |
1063 | bool tv_callback_equal(const Callback *cb1, const Callback *cb2) |
1064 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
1065 | { |
1066 | if (cb1->type != cb2->type) { |
1067 | return false; |
1068 | } |
1069 | switch (cb1->type) { |
1070 | case kCallbackFuncref: { |
1071 | return STRCMP(cb1->data.funcref, cb2->data.funcref) == 0; |
1072 | } |
1073 | case kCallbackPartial: { |
1074 | // FIXME: this is inconsistent with tv_equal but is needed for precision |
1075 | // maybe change dictwatcheradd to return a watcher id instead? |
1076 | return cb1->data.partial == cb2->data.partial; |
1077 | } |
1078 | case kCallbackNone: { |
1079 | return true; |
1080 | } |
1081 | } |
1082 | abort(); |
1083 | return false; |
1084 | } |
1085 | |
1086 | /// Unref/free callback |
1087 | void callback_free(Callback *callback) |
1088 | FUNC_ATTR_NONNULL_ALL |
1089 | { |
1090 | switch (callback->type) { |
1091 | case kCallbackFuncref: { |
1092 | func_unref(callback->data.funcref); |
1093 | xfree(callback->data.funcref); |
1094 | break; |
1095 | } |
1096 | case kCallbackPartial: { |
1097 | partial_unref(callback->data.partial); |
1098 | break; |
1099 | } |
1100 | case kCallbackNone: { |
1101 | break; |
1102 | } |
1103 | } |
1104 | callback->type = kCallbackNone; |
1105 | } |
1106 | |
1107 | /// Remove watcher from a dictionary |
1108 | /// |
1109 | /// @param dict Dictionary to remove watcher from. |
1110 | /// @param[in] key_pattern Pattern to remove watcher for. |
1111 | /// @param[in] key_pattern_len Pattern length. |
1112 | /// @param callback Callback to remove watcher for. |
1113 | /// |
1114 | /// @return True on success, false if relevant watcher was not found. |
1115 | bool tv_dict_watcher_remove(dict_T *const dict, const char *const key_pattern, |
1116 | const size_t key_pattern_len, |
1117 | Callback callback) |
1118 | FUNC_ATTR_NONNULL_ARG(2) |
1119 | { |
1120 | if (dict == NULL) { |
1121 | return false; |
1122 | } |
1123 | |
1124 | QUEUE *w = NULL; |
1125 | DictWatcher *watcher = NULL; |
1126 | bool matched = false; |
1127 | QUEUE_FOREACH(w, &dict->watchers) { |
1128 | watcher = tv_dict_watcher_node_data(w); |
1129 | if (tv_callback_equal(&watcher->callback, &callback) |
1130 | && watcher->key_pattern_len == key_pattern_len |
1131 | && memcmp(watcher->key_pattern, key_pattern, key_pattern_len) == 0) { |
1132 | matched = true; |
1133 | break; |
1134 | } |
1135 | } |
1136 | |
1137 | if (!matched) { |
1138 | return false; |
1139 | } |
1140 | |
1141 | QUEUE_REMOVE(w); |
1142 | tv_dict_watcher_free(watcher); |
1143 | return true; |
1144 | } |
1145 | |
1146 | /// Test if `key` matches with with `watcher->key_pattern` |
1147 | /// |
1148 | /// @param[in] watcher Watcher to check key pattern from. |
1149 | /// @param[in] key Key to check. |
1150 | /// |
1151 | /// @return true if key matches, false otherwise. |
1152 | static bool tv_dict_watcher_matches(DictWatcher *watcher, const char *const key) |
1153 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE |
1154 | { |
1155 | // For now only allow very simple globbing in key patterns: a '*' at the end |
1156 | // of the string means it should match everything up to the '*' instead of the |
1157 | // whole string. |
1158 | const size_t len = watcher->key_pattern_len; |
1159 | if (len && watcher->key_pattern[len - 1] == '*') { |
1160 | return strncmp(key, watcher->key_pattern, len - 1) == 0; |
1161 | } else { |
1162 | return strcmp(key, watcher->key_pattern) == 0; |
1163 | } |
1164 | } |
1165 | |
1166 | /// Send a change notification to all dictionary watchers that match given key |
1167 | /// |
1168 | /// @param[in] dict Dictionary which was modified. |
1169 | /// @param[in] key Key which was modified. |
1170 | /// @param[in] newtv New key value. |
1171 | /// @param[in] oldtv Old key value. |
1172 | void tv_dict_watcher_notify(dict_T *const dict, const char *const key, |
1173 | typval_T *const newtv, typval_T *const oldtv) |
1174 | FUNC_ATTR_NONNULL_ARG(1, 2) |
1175 | { |
1176 | typval_T argv[3]; |
1177 | |
1178 | argv[0].v_type = VAR_DICT; |
1179 | argv[0].v_lock = VAR_UNLOCKED; |
1180 | argv[0].vval.v_dict = dict; |
1181 | argv[1].v_type = VAR_STRING; |
1182 | argv[1].v_lock = VAR_UNLOCKED; |
1183 | argv[1].vval.v_string = (char_u *)xstrdup(key); |
1184 | argv[2].v_type = VAR_DICT; |
1185 | argv[2].v_lock = VAR_UNLOCKED; |
1186 | argv[2].vval.v_dict = tv_dict_alloc(); |
1187 | argv[2].vval.v_dict->dv_refcount++; |
1188 | |
1189 | if (newtv) { |
1190 | dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("new" )); |
1191 | tv_copy(newtv, &v->di_tv); |
1192 | tv_dict_add(argv[2].vval.v_dict, v); |
1193 | } |
1194 | |
1195 | if (oldtv) { |
1196 | dictitem_T *const v = tv_dict_item_alloc_len(S_LEN("old" )); |
1197 | tv_copy(oldtv, &v->di_tv); |
1198 | tv_dict_add(argv[2].vval.v_dict, v); |
1199 | } |
1200 | |
1201 | typval_T rettv; |
1202 | |
1203 | QUEUE *w; |
1204 | QUEUE_FOREACH(w, &dict->watchers) { |
1205 | DictWatcher *watcher = tv_dict_watcher_node_data(w); |
1206 | if (!watcher->busy && tv_dict_watcher_matches(watcher, key)) { |
1207 | rettv = TV_INITIAL_VALUE; |
1208 | watcher->busy = true; |
1209 | callback_call(&watcher->callback, 3, argv, &rettv); |
1210 | watcher->busy = false; |
1211 | tv_clear(&rettv); |
1212 | } |
1213 | } |
1214 | |
1215 | for (size_t i = 1; i < ARRAY_SIZE(argv); i++) { |
1216 | tv_clear(argv + i); |
1217 | } |
1218 | } |
1219 | |
1220 | //{{{2 Dictionary item |
1221 | |
1222 | /// Allocate a dictionary item |
1223 | /// |
1224 | /// @note that the type and value of the item (->di_tv) still needs to |
1225 | /// be initialized. |
1226 | /// |
1227 | /// @param[in] key Key, is copied to the new item. |
1228 | /// @param[in] key_len Key length. |
1229 | /// |
1230 | /// @return [allocated] new dictionary item. |
1231 | dictitem_T *tv_dict_item_alloc_len(const char *const key, const size_t key_len) |
1232 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
1233 | FUNC_ATTR_MALLOC |
1234 | { |
1235 | dictitem_T *const di = xmalloc(offsetof(dictitem_T, di_key) + key_len + 1); |
1236 | memcpy(di->di_key, key, key_len); |
1237 | di->di_key[key_len] = NUL; |
1238 | di->di_flags = DI_FLAGS_ALLOC; |
1239 | di->di_tv.v_lock = VAR_UNLOCKED; |
1240 | return di; |
1241 | } |
1242 | |
1243 | /// Allocate a dictionary item |
1244 | /// |
1245 | /// @note that the type and value of the item (->di_tv) still needs to |
1246 | /// be initialized. |
1247 | /// |
1248 | /// @param[in] key Key, is copied to the new item. |
1249 | /// |
1250 | /// @return [allocated] new dictionary item. |
1251 | dictitem_T *tv_dict_item_alloc(const char *const key) |
1252 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
1253 | FUNC_ATTR_MALLOC |
1254 | { |
1255 | return tv_dict_item_alloc_len(key, strlen(key)); |
1256 | } |
1257 | |
1258 | /// Free a dictionary item, also clearing the value |
1259 | /// |
1260 | /// @param item Item to free. |
1261 | void tv_dict_item_free(dictitem_T *const item) |
1262 | FUNC_ATTR_NONNULL_ALL |
1263 | { |
1264 | tv_clear(&item->di_tv); |
1265 | if (item->di_flags & DI_FLAGS_ALLOC) { |
1266 | xfree(item); |
1267 | } |
1268 | } |
1269 | |
1270 | /// Make a copy of a dictionary item |
1271 | /// |
1272 | /// @param[in] di Item to copy. |
1273 | /// |
1274 | /// @return [allocated] new dictionary item. |
1275 | dictitem_T *tv_dict_item_copy(dictitem_T *const di) |
1276 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
1277 | { |
1278 | dictitem_T *const new_di = tv_dict_item_alloc((const char *)di->di_key); |
1279 | tv_copy(&di->di_tv, &new_di->di_tv); |
1280 | return new_di; |
1281 | } |
1282 | |
1283 | /// Remove item from dictionary and free it |
1284 | /// |
1285 | /// @param dict Dictionary to remove item from. |
1286 | /// @param item Item to remove. |
1287 | void tv_dict_item_remove(dict_T *const dict, dictitem_T *const item) |
1288 | FUNC_ATTR_NONNULL_ALL |
1289 | { |
1290 | hashitem_T *const hi = hash_find(&dict->dv_hashtab, item->di_key); |
1291 | if (HASHITEM_EMPTY(hi)) { |
1292 | emsgf(_(e_intern2), "tv_dict_item_remove()" ); |
1293 | } else { |
1294 | hash_remove(&dict->dv_hashtab, hi); |
1295 | } |
1296 | tv_dict_item_free(item); |
1297 | } |
1298 | |
1299 | //{{{2 Alloc/free |
1300 | |
1301 | /// Allocate an empty dictionary |
1302 | /// |
1303 | /// @return [allocated] new dictionary. |
1304 | dict_T *tv_dict_alloc(void) |
1305 | FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT |
1306 | { |
1307 | dict_T *const d = xmalloc(sizeof(dict_T)); |
1308 | |
1309 | // Add the dict to the list of dicts for garbage collection. |
1310 | if (gc_first_dict != NULL) { |
1311 | gc_first_dict->dv_used_prev = d; |
1312 | } |
1313 | d->dv_used_next = gc_first_dict; |
1314 | d->dv_used_prev = NULL; |
1315 | gc_first_dict = d; |
1316 | |
1317 | hash_init(&d->dv_hashtab); |
1318 | d->dv_lock = VAR_UNLOCKED; |
1319 | d->dv_scope = VAR_NO_SCOPE; |
1320 | d->dv_refcount = 0; |
1321 | d->dv_copyID = 0; |
1322 | QUEUE_INIT(&d->watchers); |
1323 | |
1324 | return d; |
1325 | } |
1326 | |
1327 | /// Free items contained in a dictionary |
1328 | /// |
1329 | /// @param[in,out] d Dictionary to clear. |
1330 | void tv_dict_free_contents(dict_T *const d) |
1331 | FUNC_ATTR_NONNULL_ALL |
1332 | { |
1333 | // Lock the hashtab, we don't want it to resize while freeing items. |
1334 | hash_lock(&d->dv_hashtab); |
1335 | assert(d->dv_hashtab.ht_locked > 0); |
1336 | HASHTAB_ITER(&d->dv_hashtab, hi, { |
1337 | // Remove the item before deleting it, just in case there is |
1338 | // something recursive causing trouble. |
1339 | dictitem_T *const di = TV_DICT_HI2DI(hi); |
1340 | hash_remove(&d->dv_hashtab, hi); |
1341 | tv_dict_item_free(di); |
1342 | }); |
1343 | |
1344 | while (!QUEUE_EMPTY(&d->watchers)) { |
1345 | QUEUE *w = QUEUE_HEAD(&d->watchers); |
1346 | QUEUE_REMOVE(w); |
1347 | DictWatcher *watcher = tv_dict_watcher_node_data(w); |
1348 | tv_dict_watcher_free(watcher); |
1349 | } |
1350 | |
1351 | hash_clear(&d->dv_hashtab); |
1352 | d->dv_hashtab.ht_locked--; |
1353 | hash_init(&d->dv_hashtab); |
1354 | } |
1355 | |
1356 | /// Free a dictionary itself, ignoring items it contains |
1357 | /// |
1358 | /// Ignores the reference count. |
1359 | /// |
1360 | /// @param[in,out] d Dictionary to free. |
1361 | void tv_dict_free_dict(dict_T *const d) |
1362 | FUNC_ATTR_NONNULL_ALL |
1363 | { |
1364 | // Remove the dict from the list of dicts for garbage collection. |
1365 | if (d->dv_used_prev == NULL) { |
1366 | gc_first_dict = d->dv_used_next; |
1367 | } else { |
1368 | d->dv_used_prev->dv_used_next = d->dv_used_next; |
1369 | } |
1370 | if (d->dv_used_next != NULL) { |
1371 | d->dv_used_next->dv_used_prev = d->dv_used_prev; |
1372 | } |
1373 | |
1374 | xfree(d); |
1375 | } |
1376 | |
1377 | /// Free a dictionary, including all items it contains |
1378 | /// |
1379 | /// Ignores the reference count. |
1380 | /// |
1381 | /// @param d Dictionary to free. |
1382 | void tv_dict_free(dict_T *const d) |
1383 | FUNC_ATTR_NONNULL_ALL |
1384 | { |
1385 | if (!tv_in_free_unref_items) { |
1386 | tv_dict_free_contents(d); |
1387 | tv_dict_free_dict(d); |
1388 | } |
1389 | } |
1390 | |
1391 | |
1392 | /// Unreference a dictionary |
1393 | /// |
1394 | /// Decrements the reference count and frees dictionary when it becomes zero. |
1395 | /// |
1396 | /// @param[in] d Dictionary to operate on. |
1397 | void tv_dict_unref(dict_T *const d) |
1398 | { |
1399 | if (d != NULL && --d->dv_refcount <= 0) { |
1400 | tv_dict_free(d); |
1401 | } |
1402 | } |
1403 | |
1404 | //{{{2 Indexing/searching |
1405 | |
1406 | /// Find item in dictionary |
1407 | /// |
1408 | /// @param[in] d Dictionary to check. |
1409 | /// @param[in] key Dictionary key. |
1410 | /// @param[in] len Key length. If negative, then strlen(key) is used. |
1411 | /// |
1412 | /// @return found item or NULL if nothing was found. |
1413 | dictitem_T *tv_dict_find(const dict_T *const d, const char *const key, |
1414 | const ptrdiff_t len) |
1415 | FUNC_ATTR_NONNULL_ARG(2) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT |
1416 | { |
1417 | if (d == NULL) { |
1418 | return NULL; |
1419 | } |
1420 | hashitem_T *const hi = (len < 0 |
1421 | ? hash_find(&d->dv_hashtab, (const char_u *)key) |
1422 | : hash_find_len(&d->dv_hashtab, key, (size_t)len)); |
1423 | if (HASHITEM_EMPTY(hi)) { |
1424 | return NULL; |
1425 | } |
1426 | return TV_DICT_HI2DI(hi); |
1427 | } |
1428 | |
1429 | /// Get a number item from a dictionary |
1430 | /// |
1431 | /// Returns 0 if the entry does not exist. |
1432 | /// |
1433 | /// @param[in] d Dictionary to get item from. |
1434 | /// @param[in] key Key to find in dictionary. |
1435 | /// |
1436 | /// @return Dictionary item. |
1437 | varnumber_T tv_dict_get_number(const dict_T *const d, const char *const key) |
1438 | FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT |
1439 | { |
1440 | dictitem_T *const di = tv_dict_find(d, key, -1); |
1441 | if (di == NULL) { |
1442 | return 0; |
1443 | } |
1444 | return tv_get_number(&di->di_tv); |
1445 | } |
1446 | |
1447 | /// Get a string item from a dictionary |
1448 | /// |
1449 | /// @param[in] d Dictionary to get item from. |
1450 | /// @param[in] key Dictionary key. |
1451 | /// @param[in] save If true, returned string will be placed in the allocated |
1452 | /// memory. |
1453 | /// |
1454 | /// @return NULL if key does not exist, empty string in case of type error, |
1455 | /// string item value otherwise. If returned value is not NULL, it may |
1456 | /// be allocated depending on `save` argument. |
1457 | char *tv_dict_get_string(const dict_T *const d, const char *const key, |
1458 | const bool save) |
1459 | FUNC_ATTR_WARN_UNUSED_RESULT |
1460 | { |
1461 | static char numbuf[NUMBUFLEN]; |
1462 | const char *const s = tv_dict_get_string_buf(d, key, numbuf); |
1463 | if (save && s != NULL) { |
1464 | return xstrdup(s); |
1465 | } |
1466 | return (char *)s; |
1467 | } |
1468 | |
1469 | /// Get a string item from a dictionary |
1470 | /// |
1471 | /// @param[in] d Dictionary to get item from. |
1472 | /// @param[in] key Dictionary key. |
1473 | /// @param[in] numbuf Buffer for non-string items converted to strings, at |
1474 | /// least of #NUMBUFLEN length. |
1475 | /// |
1476 | /// @return NULL if key does not exist, empty string in case of type error, |
1477 | /// string item value otherwise. |
1478 | const char *tv_dict_get_string_buf(const dict_T *const d, const char *const key, |
1479 | char *const numbuf) |
1480 | FUNC_ATTR_WARN_UNUSED_RESULT |
1481 | { |
1482 | const dictitem_T *const di = tv_dict_find(d, key, -1); |
1483 | if (di == NULL) { |
1484 | return NULL; |
1485 | } |
1486 | return tv_get_string_buf(&di->di_tv, numbuf); |
1487 | } |
1488 | |
1489 | /// Get a string item from a dictionary |
1490 | /// |
1491 | /// @param[in] d Dictionary to get item from. |
1492 | /// @param[in] key Dictionary key. |
1493 | /// @param[in] key_len Key length. |
1494 | /// @param[in] numbuf Buffer for non-string items converted to strings, at |
1495 | /// least of #NUMBUFLEN length. |
1496 | /// @param[in] def Default return when key does not exist. |
1497 | /// |
1498 | /// @return `def` when key does not exist, |
1499 | /// NULL in case of type error, |
1500 | /// string item value in case of success. |
1501 | const char *tv_dict_get_string_buf_chk(const dict_T *const d, |
1502 | const char *const key, |
1503 | const ptrdiff_t key_len, |
1504 | char *const numbuf, |
1505 | const char *const def) |
1506 | FUNC_ATTR_WARN_UNUSED_RESULT |
1507 | { |
1508 | const dictitem_T *const di = tv_dict_find(d, key, key_len); |
1509 | if (di == NULL) { |
1510 | return def; |
1511 | } |
1512 | return tv_get_string_buf_chk(&di->di_tv, numbuf); |
1513 | } |
1514 | |
1515 | /// Get a function from a dictionary |
1516 | /// |
1517 | /// @param[in] d Dictionary to get callback from. |
1518 | /// @param[in] key Dictionary key. |
1519 | /// @param[in] key_len Key length, may be -1 to use strlen(). |
1520 | /// @param[out] result The address where a pointer to the wanted callback |
1521 | /// will be left. |
1522 | /// |
1523 | /// @return true/false on success/failure. |
1524 | bool tv_dict_get_callback(dict_T *const d, |
1525 | const char *const key, const ptrdiff_t key_len, |
1526 | Callback *const result) |
1527 | FUNC_ATTR_NONNULL_ARG(2, 4) FUNC_ATTR_WARN_UNUSED_RESULT |
1528 | { |
1529 | result->type = kCallbackNone; |
1530 | |
1531 | dictitem_T *const di = tv_dict_find(d, key, key_len); |
1532 | |
1533 | if (di == NULL) { |
1534 | return true; |
1535 | } |
1536 | |
1537 | if (!tv_is_func(di->di_tv) && di->di_tv.v_type != VAR_STRING) { |
1538 | EMSG(_("E6000: Argument is not a function or function name" )); |
1539 | return false; |
1540 | } |
1541 | |
1542 | typval_T tv; |
1543 | tv_copy(&di->di_tv, &tv); |
1544 | set_selfdict(&tv, d); |
1545 | const bool res = callback_from_typval(result, &tv); |
1546 | tv_clear(&tv); |
1547 | return res; |
1548 | } |
1549 | |
1550 | //{{{2 dict_add* |
1551 | |
1552 | /// Add item to dictionary |
1553 | /// |
1554 | /// @param[out] d Dictionary to add to. |
1555 | /// @param[in] item Item to add. |
1556 | /// |
1557 | /// @return FAIL if key already exists. |
1558 | int tv_dict_add(dict_T *const d, dictitem_T *const item) |
1559 | FUNC_ATTR_NONNULL_ALL |
1560 | { |
1561 | return hash_add(&d->dv_hashtab, item->di_key); |
1562 | } |
1563 | |
1564 | /// Add a list entry to dictionary |
1565 | /// |
1566 | /// @param[out] d Dictionary to add entry to. |
1567 | /// @param[in] key Key to add. |
1568 | /// @param[in] key_len Key length. |
1569 | /// @param list List to add. Will have reference count incremented. |
1570 | /// |
1571 | /// @return OK in case of success, FAIL when key already exists. |
1572 | int tv_dict_add_list(dict_T *const d, const char *const key, |
1573 | const size_t key_len, list_T *const list) |
1574 | FUNC_ATTR_NONNULL_ALL |
1575 | { |
1576 | dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); |
1577 | |
1578 | item->di_tv.v_type = VAR_LIST; |
1579 | item->di_tv.vval.v_list = list; |
1580 | tv_list_ref(list); |
1581 | if (tv_dict_add(d, item) == FAIL) { |
1582 | tv_dict_item_free(item); |
1583 | return FAIL; |
1584 | } |
1585 | return OK; |
1586 | } |
1587 | |
1588 | /// Add a dictionary entry to dictionary |
1589 | /// |
1590 | /// @param[out] d Dictionary to add entry to. |
1591 | /// @param[in] key Key to add. |
1592 | /// @param[in] key_len Key length. |
1593 | /// @param dict Dictionary to add. Will have reference count incremented. |
1594 | /// |
1595 | /// @return OK in case of success, FAIL when key already exists. |
1596 | int tv_dict_add_dict(dict_T *const d, const char *const key, |
1597 | const size_t key_len, dict_T *const dict) |
1598 | FUNC_ATTR_NONNULL_ALL |
1599 | { |
1600 | dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); |
1601 | |
1602 | item->di_tv.v_type = VAR_DICT; |
1603 | item->di_tv.vval.v_dict = dict; |
1604 | dict->dv_refcount++; |
1605 | if (tv_dict_add(d, item) == FAIL) { |
1606 | tv_dict_item_free(item); |
1607 | return FAIL; |
1608 | } |
1609 | return OK; |
1610 | } |
1611 | |
1612 | /// Add a number entry to dictionary |
1613 | /// |
1614 | /// @param[out] d Dictionary to add entry to. |
1615 | /// @param[in] key Key to add. |
1616 | /// @param[in] key_len Key length. |
1617 | /// @param[in] nr Number to add. |
1618 | /// |
1619 | /// @return OK in case of success, FAIL when key already exists. |
1620 | int tv_dict_add_nr(dict_T *const d, const char *const key, |
1621 | const size_t key_len, const varnumber_T nr) |
1622 | { |
1623 | dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); |
1624 | |
1625 | item->di_tv.v_type = VAR_NUMBER; |
1626 | item->di_tv.vval.v_number = nr; |
1627 | if (tv_dict_add(d, item) == FAIL) { |
1628 | tv_dict_item_free(item); |
1629 | return FAIL; |
1630 | } |
1631 | return OK; |
1632 | } |
1633 | |
1634 | /// Add a special entry to dictionary |
1635 | /// |
1636 | /// @param[out] d Dictionary to add entry to. |
1637 | /// @param[in] key Key to add. |
1638 | /// @param[in] key_len Key length. |
1639 | /// @param[in] val SpecialVarValue to add. |
1640 | /// |
1641 | /// @return OK in case of success, FAIL when key already exists. |
1642 | int tv_dict_add_special(dict_T *const d, const char *const key, |
1643 | const size_t key_len, SpecialVarValue val) |
1644 | { |
1645 | dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); |
1646 | |
1647 | item->di_tv.v_type = VAR_SPECIAL; |
1648 | item->di_tv.vval.v_special = val; |
1649 | if (tv_dict_add(d, item) == FAIL) { |
1650 | tv_dict_item_free(item); |
1651 | return FAIL; |
1652 | } |
1653 | return OK; |
1654 | } |
1655 | |
1656 | /// Add a string entry to dictionary |
1657 | /// |
1658 | /// @see tv_dict_add_allocated_str |
1659 | int tv_dict_add_str(dict_T *const d, |
1660 | const char *const key, const size_t key_len, |
1661 | const char *const val) |
1662 | FUNC_ATTR_NONNULL_ALL |
1663 | { |
1664 | return tv_dict_add_allocated_str(d, key, key_len, xstrdup(val)); |
1665 | } |
1666 | |
1667 | /// Add a string entry to dictionary |
1668 | /// |
1669 | /// @param[out] d Dictionary to add entry to. |
1670 | /// @param[in] key Key to add. |
1671 | /// @param[in] key_len Key length. |
1672 | /// @param[in] val String to add. NULL adds empty string. |
1673 | /// @param[in] len Use this many bytes from `val`, or -1 for whole string. |
1674 | /// |
1675 | /// @return OK in case of success, FAIL when key already exists. |
1676 | int tv_dict_add_str_len(dict_T *const d, |
1677 | const char *const key, const size_t key_len, |
1678 | char *const val, int len) |
1679 | FUNC_ATTR_NONNULL_ARG(1, 2) |
1680 | { |
1681 | char *s = val ? val : "" ; |
1682 | if (val != NULL) { |
1683 | s = (len < 0) ? xstrdup(val) : xstrndup(val, (size_t)len); |
1684 | } |
1685 | return tv_dict_add_allocated_str(d, key, key_len, s); |
1686 | } |
1687 | |
1688 | /// Add a string entry to dictionary |
1689 | /// |
1690 | /// Unlike tv_dict_add_str() saves val to the new dictionary item in place of |
1691 | /// creating a new copy. |
1692 | /// |
1693 | /// @warning String will be freed even in case addition fails. |
1694 | /// |
1695 | /// @param[out] d Dictionary to add entry to. |
1696 | /// @param[in] key Key to add. |
1697 | /// @param[in] key_len Key length. |
1698 | /// @param[in] val String to add. |
1699 | /// |
1700 | /// @return OK in case of success, FAIL when key already exists. |
1701 | int tv_dict_add_allocated_str(dict_T *const d, |
1702 | const char *const key, const size_t key_len, |
1703 | char *const val) |
1704 | FUNC_ATTR_NONNULL_ALL |
1705 | { |
1706 | dictitem_T *const item = tv_dict_item_alloc_len(key, key_len); |
1707 | |
1708 | item->di_tv.v_type = VAR_STRING; |
1709 | item->di_tv.vval.v_string = (char_u *)val; |
1710 | if (tv_dict_add(d, item) == FAIL) { |
1711 | tv_dict_item_free(item); |
1712 | return FAIL; |
1713 | } |
1714 | return OK; |
1715 | } |
1716 | |
1717 | //{{{2 Operations on the whole dict |
1718 | |
1719 | /// Clear all the keys of a Dictionary. "d" remains a valid empty Dictionary. |
1720 | /// |
1721 | /// @param d The Dictionary to clear |
1722 | void tv_dict_clear(dict_T *const d) |
1723 | FUNC_ATTR_NONNULL_ALL |
1724 | { |
1725 | hash_lock(&d->dv_hashtab); |
1726 | assert(d->dv_hashtab.ht_locked > 0); |
1727 | |
1728 | HASHTAB_ITER(&d->dv_hashtab, hi, { |
1729 | tv_dict_item_free(TV_DICT_HI2DI(hi)); |
1730 | hash_remove(&d->dv_hashtab, hi); |
1731 | }); |
1732 | |
1733 | hash_unlock(&d->dv_hashtab); |
1734 | } |
1735 | |
1736 | /// Extend dictionary with items from another dictionary |
1737 | /// |
1738 | /// @param d1 Dictionary to extend. |
1739 | /// @param[in] d2 Dictionary to extend with. |
1740 | /// @param[in] action "error", "force", "keep": |
1741 | /// |
1742 | /// e*, including "error": duplicate key gives an error. |
1743 | /// f*, including "force": duplicate d2 keys override d1. |
1744 | /// other, including "keep": duplicate d2 keys ignored. |
1745 | void tv_dict_extend(dict_T *const d1, dict_T *const d2, |
1746 | const char *const action) |
1747 | FUNC_ATTR_NONNULL_ALL |
1748 | { |
1749 | const bool watched = tv_dict_is_watched(d1); |
1750 | const char *const arg_errmsg = _("extend() argument" ); |
1751 | const size_t arg_errmsg_len = strlen(arg_errmsg); |
1752 | |
1753 | TV_DICT_ITER(d2, di2, { |
1754 | dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1); |
1755 | if (d1->dv_scope != VAR_NO_SCOPE) { |
1756 | // Disallow replacing a builtin function in l: and g:. |
1757 | // Check the key to be valid when adding to any scope. |
1758 | if (d1->dv_scope == VAR_DEF_SCOPE |
1759 | && tv_is_func(di2->di_tv) |
1760 | && !var_check_func_name((const char *)di2->di_key, di1 == NULL)) { |
1761 | break; |
1762 | } |
1763 | if (!valid_varname((const char *)di2->di_key)) { |
1764 | break; |
1765 | } |
1766 | } |
1767 | if (di1 == NULL) { |
1768 | dictitem_T *const new_di = tv_dict_item_copy(di2); |
1769 | if (tv_dict_add(d1, new_di) == FAIL) { |
1770 | tv_dict_item_free(new_di); |
1771 | } else if (watched) { |
1772 | tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, |
1773 | NULL); |
1774 | } |
1775 | } else if (*action == 'e') { |
1776 | emsgf(_("E737: Key already exists: %s" ), di2->di_key); |
1777 | break; |
1778 | } else if (*action == 'f' && di2 != di1) { |
1779 | typval_T oldtv; |
1780 | |
1781 | if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, arg_errmsg_len) |
1782 | || var_check_ro(di1->di_flags, arg_errmsg, arg_errmsg_len)) { |
1783 | break; |
1784 | } |
1785 | |
1786 | if (watched) { |
1787 | tv_copy(&di1->di_tv, &oldtv); |
1788 | } |
1789 | |
1790 | tv_clear(&di1->di_tv); |
1791 | tv_copy(&di2->di_tv, &di1->di_tv); |
1792 | |
1793 | if (watched) { |
1794 | tv_dict_watcher_notify(d1, (const char *)di1->di_key, &di1->di_tv, |
1795 | &oldtv); |
1796 | tv_clear(&oldtv); |
1797 | } |
1798 | } |
1799 | }); |
1800 | } |
1801 | |
1802 | /// Compare two dictionaries |
1803 | /// |
1804 | /// @param[in] d1 First dictionary. |
1805 | /// @param[in] d2 Second dictionary. |
1806 | /// @param[in] ic True if case is to be ignored. |
1807 | /// @param[in] recursive True when used recursively. |
1808 | bool tv_dict_equal(dict_T *const d1, dict_T *const d2, |
1809 | const bool ic, const bool recursive) |
1810 | FUNC_ATTR_WARN_UNUSED_RESULT |
1811 | { |
1812 | if (d1 == d2) { |
1813 | return true; |
1814 | } |
1815 | if (d1 == NULL || d2 == NULL) { |
1816 | return false; |
1817 | } |
1818 | if (tv_dict_len(d1) != tv_dict_len(d2)) { |
1819 | return false; |
1820 | } |
1821 | |
1822 | TV_DICT_ITER(d1, di1, { |
1823 | dictitem_T *const di2 = tv_dict_find(d2, (const char *)di1->di_key, -1); |
1824 | if (di2 == NULL) { |
1825 | return false; |
1826 | } |
1827 | if (!tv_equal(&di1->di_tv, &di2->di_tv, ic, recursive)) { |
1828 | return false; |
1829 | } |
1830 | }); |
1831 | return true; |
1832 | } |
1833 | |
1834 | /// Make a copy of dictionary |
1835 | /// |
1836 | /// @param[in] conv If non-NULL, then all internal strings will be converted. |
1837 | /// @param[in] orig Original dictionary to copy. |
1838 | /// @param[in] deep If false, then shallow copy will be done. |
1839 | /// @param[in] copyID See var_item_copy(). |
1840 | /// |
1841 | /// @return Copied dictionary. May be NULL in case original dictionary is NULL |
1842 | /// or some failure happens. The refcount of the new dictionary is set |
1843 | /// to 1. |
1844 | dict_T *tv_dict_copy(const vimconv_T *const conv, |
1845 | dict_T *const orig, |
1846 | const bool deep, |
1847 | const int copyID) |
1848 | { |
1849 | if (orig == NULL) { |
1850 | return NULL; |
1851 | } |
1852 | |
1853 | dict_T *copy = tv_dict_alloc(); |
1854 | if (copyID != 0) { |
1855 | orig->dv_copyID = copyID; |
1856 | orig->dv_copydict = copy; |
1857 | } |
1858 | TV_DICT_ITER(orig, di, { |
1859 | if (got_int) { |
1860 | break; |
1861 | } |
1862 | dictitem_T *new_di; |
1863 | if (conv == NULL || conv->vc_type == CONV_NONE) { |
1864 | new_di = tv_dict_item_alloc((const char *)di->di_key); |
1865 | } else { |
1866 | size_t len = STRLEN(di->di_key); |
1867 | char *const key = (char *)string_convert(conv, di->di_key, &len); |
1868 | if (key == NULL) { |
1869 | new_di = tv_dict_item_alloc_len((const char *)di->di_key, len); |
1870 | } else { |
1871 | new_di = tv_dict_item_alloc_len(key, len); |
1872 | xfree(key); |
1873 | } |
1874 | } |
1875 | if (deep) { |
1876 | if (var_item_copy(conv, &di->di_tv, &new_di->di_tv, deep, |
1877 | copyID) == FAIL) { |
1878 | xfree(new_di); |
1879 | break; |
1880 | } |
1881 | } else { |
1882 | tv_copy(&di->di_tv, &new_di->di_tv); |
1883 | } |
1884 | if (tv_dict_add(copy, new_di) == FAIL) { |
1885 | tv_dict_item_free(new_di); |
1886 | break; |
1887 | } |
1888 | }); |
1889 | |
1890 | copy->dv_refcount++; |
1891 | if (got_int) { |
1892 | tv_dict_unref(copy); |
1893 | copy = NULL; |
1894 | } |
1895 | |
1896 | return copy; |
1897 | } |
1898 | |
1899 | /// Set all existing keys in "dict" as read-only. |
1900 | /// |
1901 | /// This does not protect against adding new keys to the Dictionary. |
1902 | /// |
1903 | /// @param dict The dict whose keys should be frozen. |
1904 | void tv_dict_set_keys_readonly(dict_T *const dict) |
1905 | FUNC_ATTR_NONNULL_ALL |
1906 | { |
1907 | TV_DICT_ITER(dict, di, { |
1908 | di->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX; |
1909 | }); |
1910 | } |
1911 | |
1912 | //{{{1 Generic typval operations |
1913 | //{{{2 Init/alloc/clear |
1914 | //{{{3 Alloc |
1915 | |
1916 | /// Allocate an empty list for a return value |
1917 | /// |
1918 | /// Also sets reference count. |
1919 | /// |
1920 | /// @param[out] ret_tv Structure where list is saved. |
1921 | /// @param[in] len Expected number of items to be populated before list |
1922 | /// becomes accessible from VimL. It is still valid to |
1923 | /// underpopulate a list, value only controls how many elements |
1924 | /// will be allocated in advance. @see ListLenSpecials. |
1925 | /// |
1926 | /// @return [allocated] pointer to the created list. |
1927 | list_T *tv_list_alloc_ret(typval_T *const ret_tv, const ptrdiff_t len) |
1928 | FUNC_ATTR_NONNULL_ALL |
1929 | { |
1930 | list_T *const l = tv_list_alloc(len); |
1931 | tv_list_set_ret(ret_tv, l); |
1932 | ret_tv->v_lock = VAR_UNLOCKED; |
1933 | return l; |
1934 | } |
1935 | |
1936 | /// Allocate an empty dictionary for a return value |
1937 | /// |
1938 | /// Also sets reference count. |
1939 | /// |
1940 | /// @param[out] ret_tv Structure where dictionary is saved. |
1941 | void tv_dict_alloc_ret(typval_T *const ret_tv) |
1942 | FUNC_ATTR_NONNULL_ALL |
1943 | { |
1944 | dict_T *const d = tv_dict_alloc(); |
1945 | tv_dict_set_ret(ret_tv, d); |
1946 | ret_tv->v_lock = VAR_UNLOCKED; |
1947 | } |
1948 | |
1949 | //{{{3 Clear |
1950 | #define TYPVAL_ENCODE_ALLOW_SPECIALS false |
1951 | |
1952 | #define TYPVAL_ENCODE_CONV_NIL(tv) \ |
1953 | do { \ |
1954 | tv->vval.v_special = kSpecialVarFalse; \ |
1955 | tv->v_lock = VAR_UNLOCKED; \ |
1956 | } while (0) |
1957 | |
1958 | #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ |
1959 | TYPVAL_ENCODE_CONV_NIL(tv) |
1960 | |
1961 | #define TYPVAL_ENCODE_CONV_NUMBER(tv, num) \ |
1962 | do { \ |
1963 | (void)num; \ |
1964 | tv->vval.v_number = 0; \ |
1965 | tv->v_lock = VAR_UNLOCKED; \ |
1966 | } while (0) |
1967 | |
1968 | #define TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER(tv, num) |
1969 | |
1970 | #define TYPVAL_ENCODE_CONV_FLOAT(tv, flt) \ |
1971 | do { \ |
1972 | tv->vval.v_float = 0; \ |
1973 | tv->v_lock = VAR_UNLOCKED; \ |
1974 | } while (0) |
1975 | |
1976 | #define TYPVAL_ENCODE_CONV_STRING(tv, buf, len) \ |
1977 | do { \ |
1978 | xfree(buf); \ |
1979 | tv->vval.v_string = NULL; \ |
1980 | tv->v_lock = VAR_UNLOCKED; \ |
1981 | } while (0) |
1982 | |
1983 | #define TYPVAL_ENCODE_CONV_STR_STRING(tv, buf, len) |
1984 | |
1985 | #define TYPVAL_ENCODE_CONV_EXT_STRING(tv, buf, len, type) |
1986 | |
1987 | static inline int _nothing_conv_func_start(typval_T *const tv, |
1988 | char_u *const fun) |
1989 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(1) |
1990 | { |
1991 | tv->v_lock = VAR_UNLOCKED; |
1992 | if (tv->v_type == VAR_PARTIAL) { |
1993 | partial_T *const pt_ = tv->vval.v_partial; |
1994 | if (pt_ != NULL && pt_->pt_refcount > 1) { |
1995 | pt_->pt_refcount--; |
1996 | tv->vval.v_partial = NULL; |
1997 | return OK; |
1998 | } |
1999 | } else { |
2000 | func_unref(fun); |
2001 | if ((const char *)fun != tv_empty_string) { |
2002 | xfree(fun); |
2003 | } |
2004 | tv->vval.v_string = NULL; |
2005 | } |
2006 | return NOTDONE; |
2007 | } |
2008 | #define TYPVAL_ENCODE_CONV_FUNC_START(tv, fun) \ |
2009 | do { \ |
2010 | if (_nothing_conv_func_start(tv, fun) != NOTDONE) { \ |
2011 | return OK; \ |
2012 | } \ |
2013 | } while (0) |
2014 | |
2015 | #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS(tv, len) |
2016 | #define TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF(tv, len) |
2017 | |
2018 | static inline void _nothing_conv_func_end(typval_T *const tv, const int copyID) |
2019 | FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ALL |
2020 | { |
2021 | if (tv->v_type == VAR_PARTIAL) { |
2022 | partial_T *const pt = tv->vval.v_partial; |
2023 | if (pt == NULL) { |
2024 | return; |
2025 | } |
2026 | // Dictionary should already be freed by the time. |
2027 | // If it was not freed then it is a part of the reference cycle. |
2028 | assert(pt->pt_dict == NULL || pt->pt_dict->dv_copyID == copyID); |
2029 | pt->pt_dict = NULL; |
2030 | // As well as all arguments. |
2031 | pt->pt_argc = 0; |
2032 | assert(pt->pt_refcount <= 1); |
2033 | partial_unref(pt); |
2034 | tv->vval.v_partial = NULL; |
2035 | assert(tv->v_lock == VAR_UNLOCKED); |
2036 | } |
2037 | } |
2038 | #define TYPVAL_ENCODE_CONV_FUNC_END(tv) _nothing_conv_func_end(tv, copyID) |
2039 | |
2040 | #define TYPVAL_ENCODE_CONV_EMPTY_LIST(tv) \ |
2041 | do { \ |
2042 | tv_list_unref(tv->vval.v_list); \ |
2043 | tv->vval.v_list = NULL; \ |
2044 | tv->v_lock = VAR_UNLOCKED; \ |
2045 | } while (0) |
2046 | |
2047 | static inline void _nothing_conv_empty_dict(typval_T *const tv, |
2048 | dict_T **const dictp) |
2049 | FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_NONNULL_ARG(2) |
2050 | { |
2051 | tv_dict_unref(*dictp); |
2052 | *dictp = NULL; |
2053 | if (tv != NULL) { |
2054 | tv->v_lock = VAR_UNLOCKED; |
2055 | } |
2056 | } |
2057 | #define TYPVAL_ENCODE_CONV_EMPTY_DICT(tv, dict) \ |
2058 | do { \ |
2059 | assert((void *)&dict != (void *)&TYPVAL_ENCODE_NODICT_VAR); \ |
2060 | _nothing_conv_empty_dict(tv, ((dict_T **)&dict)); \ |
2061 | } while (0) |
2062 | |
2063 | static inline int _nothing_conv_real_list_after_start( |
2064 | typval_T *const tv, MPConvStackVal *const mpsv) |
2065 | FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT |
2066 | { |
2067 | assert(tv != NULL); |
2068 | tv->v_lock = VAR_UNLOCKED; |
2069 | if (tv->vval.v_list->lv_refcount > 1) { |
2070 | tv->vval.v_list->lv_refcount--; |
2071 | tv->vval.v_list = NULL; |
2072 | mpsv->data.l.li = NULL; |
2073 | return OK; |
2074 | } |
2075 | return NOTDONE; |
2076 | } |
2077 | #define TYPVAL_ENCODE_CONV_LIST_START(tv, len) |
2078 | |
2079 | #define TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START(tv, mpsv) \ |
2080 | do { \ |
2081 | if (_nothing_conv_real_list_after_start(tv, &mpsv) != NOTDONE) { \ |
2082 | goto typval_encode_stop_converting_one_item; \ |
2083 | } \ |
2084 | } while (0) |
2085 | |
2086 | #define TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS(tv) |
2087 | |
2088 | static inline void _nothing_conv_list_end(typval_T *const tv) |
2089 | FUNC_ATTR_ALWAYS_INLINE |
2090 | { |
2091 | if (tv == NULL) { |
2092 | return; |
2093 | } |
2094 | assert(tv->v_type == VAR_LIST); |
2095 | list_T *const list = tv->vval.v_list; |
2096 | tv_list_unref(list); |
2097 | tv->vval.v_list = NULL; |
2098 | } |
2099 | #define TYPVAL_ENCODE_CONV_LIST_END(tv) _nothing_conv_list_end(tv) |
2100 | |
2101 | static inline int _nothing_conv_real_dict_after_start( |
2102 | typval_T *const tv, dict_T **const dictp, const void *const nodictvar, |
2103 | MPConvStackVal *const mpsv) |
2104 | FUNC_ATTR_ALWAYS_INLINE FUNC_ATTR_WARN_UNUSED_RESULT |
2105 | { |
2106 | if (tv != NULL) { |
2107 | tv->v_lock = VAR_UNLOCKED; |
2108 | } |
2109 | if ((const void *)dictp != nodictvar && (*dictp)->dv_refcount > 1) { |
2110 | (*dictp)->dv_refcount--; |
2111 | *dictp = NULL; |
2112 | mpsv->data.d.todo = 0; |
2113 | return OK; |
2114 | } |
2115 | return NOTDONE; |
2116 | } |
2117 | #define TYPVAL_ENCODE_CONV_DICT_START(tv, dict, len) |
2118 | |
2119 | #define TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START(tv, dict, mpsv) \ |
2120 | do { \ |
2121 | if (_nothing_conv_real_dict_after_start( \ |
2122 | tv, (dict_T **)&dict, (void *)&TYPVAL_ENCODE_NODICT_VAR, \ |
2123 | &mpsv) != NOTDONE) { \ |
2124 | goto typval_encode_stop_converting_one_item; \ |
2125 | } \ |
2126 | } while (0) |
2127 | |
2128 | #define TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK(tv, dict) |
2129 | #define TYPVAL_ENCODE_CONV_DICT_AFTER_KEY(tv, dict) |
2130 | #define TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS(tv, dict) |
2131 | |
2132 | static inline void _nothing_conv_dict_end(typval_T *const tv, |
2133 | dict_T **const dictp, |
2134 | const void *const nodictvar) |
2135 | FUNC_ATTR_ALWAYS_INLINE |
2136 | { |
2137 | if ((const void *)dictp != nodictvar) { |
2138 | tv_dict_unref(*dictp); |
2139 | *dictp = NULL; |
2140 | } |
2141 | } |
2142 | #define TYPVAL_ENCODE_CONV_DICT_END(tv, dict) \ |
2143 | _nothing_conv_dict_end(tv, (dict_T **)&dict, \ |
2144 | (void *)&TYPVAL_ENCODE_NODICT_VAR) |
2145 | |
2146 | #define TYPVAL_ENCODE_CONV_RECURSE(val, conv_type) |
2147 | |
2148 | #define TYPVAL_ENCODE_SCOPE static |
2149 | #define TYPVAL_ENCODE_NAME nothing |
2150 | #define TYPVAL_ENCODE_FIRST_ARG_TYPE const void *const |
2151 | #define TYPVAL_ENCODE_FIRST_ARG_NAME ignored |
2152 | #define TYPVAL_ENCODE_TRANSLATE_OBJECT_NAME |
2153 | #include "nvim/eval/typval_encode.c.h" |
2154 | #undef TYPVAL_ENCODE_SCOPE |
2155 | #undef TYPVAL_ENCODE_NAME |
2156 | #undef TYPVAL_ENCODE_FIRST_ARG_TYPE |
2157 | #undef TYPVAL_ENCODE_FIRST_ARG_NAME |
2158 | #undef TYPVAL_ENCODE_TRANSLATE_OBJECT_NAME |
2159 | |
2160 | #undef TYPVAL_ENCODE_ALLOW_SPECIALS |
2161 | #undef TYPVAL_ENCODE_CONV_NIL |
2162 | #undef TYPVAL_ENCODE_CONV_BOOL |
2163 | #undef TYPVAL_ENCODE_CONV_NUMBER |
2164 | #undef TYPVAL_ENCODE_CONV_UNSIGNED_NUMBER |
2165 | #undef TYPVAL_ENCODE_CONV_FLOAT |
2166 | #undef TYPVAL_ENCODE_CONV_STRING |
2167 | #undef TYPVAL_ENCODE_CONV_STR_STRING |
2168 | #undef TYPVAL_ENCODE_CONV_EXT_STRING |
2169 | #undef TYPVAL_ENCODE_CONV_FUNC_START |
2170 | #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_ARGS |
2171 | #undef TYPVAL_ENCODE_CONV_FUNC_BEFORE_SELF |
2172 | #undef TYPVAL_ENCODE_CONV_FUNC_END |
2173 | #undef TYPVAL_ENCODE_CONV_EMPTY_LIST |
2174 | #undef TYPVAL_ENCODE_CONV_EMPTY_DICT |
2175 | #undef TYPVAL_ENCODE_CONV_LIST_START |
2176 | #undef TYPVAL_ENCODE_CONV_REAL_LIST_AFTER_START |
2177 | #undef TYPVAL_ENCODE_CONV_LIST_BETWEEN_ITEMS |
2178 | #undef TYPVAL_ENCODE_CONV_LIST_END |
2179 | #undef TYPVAL_ENCODE_CONV_DICT_START |
2180 | #undef TYPVAL_ENCODE_CONV_REAL_DICT_AFTER_START |
2181 | #undef TYPVAL_ENCODE_SPECIAL_DICT_KEY_CHECK |
2182 | #undef TYPVAL_ENCODE_CONV_DICT_AFTER_KEY |
2183 | #undef TYPVAL_ENCODE_CONV_DICT_BETWEEN_ITEMS |
2184 | #undef TYPVAL_ENCODE_CONV_DICT_END |
2185 | #undef TYPVAL_ENCODE_CONV_RECURSE |
2186 | |
2187 | /// Free memory for a variable value and set the value to NULL or 0 |
2188 | /// |
2189 | /// @param[in,out] tv Value to free. |
2190 | void tv_clear(typval_T *const tv) |
2191 | { |
2192 | if (tv != NULL && tv->v_type != VAR_UNKNOWN) { |
2193 | // WARNING: do not translate the string here, gettext is slow and function |
2194 | // is used *very* often. At the current state encode_vim_to_nothing() does |
2195 | // not error out and does not use the argument anywhere. |
2196 | // |
2197 | // If situation changes and this argument will be used, translate it in the |
2198 | // place where it is used. |
2199 | const int evn_ret = encode_vim_to_nothing(NULL, tv, "tv_clear() argument" ); |
2200 | (void)evn_ret; |
2201 | assert(evn_ret == OK); |
2202 | } |
2203 | } |
2204 | |
2205 | //{{{3 Free |
2206 | |
2207 | /// Free allocated VimL object and value stored inside |
2208 | /// |
2209 | /// @param tv Object to free. |
2210 | void tv_free(typval_T *tv) |
2211 | { |
2212 | if (tv != NULL) { |
2213 | switch (tv->v_type) { |
2214 | case VAR_PARTIAL: { |
2215 | partial_unref(tv->vval.v_partial); |
2216 | break; |
2217 | } |
2218 | case VAR_FUNC: { |
2219 | func_unref(tv->vval.v_string); |
2220 | FALLTHROUGH; |
2221 | } |
2222 | case VAR_STRING: { |
2223 | xfree(tv->vval.v_string); |
2224 | break; |
2225 | } |
2226 | case VAR_LIST: { |
2227 | tv_list_unref(tv->vval.v_list); |
2228 | break; |
2229 | } |
2230 | case VAR_DICT: { |
2231 | tv_dict_unref(tv->vval.v_dict); |
2232 | break; |
2233 | } |
2234 | case VAR_SPECIAL: |
2235 | case VAR_NUMBER: |
2236 | case VAR_FLOAT: |
2237 | case VAR_UNKNOWN: { |
2238 | break; |
2239 | } |
2240 | } |
2241 | xfree(tv); |
2242 | } |
2243 | } |
2244 | |
2245 | //{{{3 Copy |
2246 | |
2247 | /// Copy typval from one location to another |
2248 | /// |
2249 | /// When needed allocates string or increases reference count. Does not make |
2250 | /// a copy of a container, but copies its reference! |
2251 | /// |
2252 | /// It is OK for `from` and `to` to point to the same location; this is used to |
2253 | /// make a copy later. |
2254 | /// |
2255 | /// @param[in] from Location to copy from. |
2256 | /// @param[out] to Location to copy to. |
2257 | void tv_copy(const typval_T *const from, typval_T *const to) |
2258 | { |
2259 | to->v_type = from->v_type; |
2260 | to->v_lock = VAR_UNLOCKED; |
2261 | memmove(&to->vval, &from->vval, sizeof(to->vval)); |
2262 | switch (from->v_type) { |
2263 | case VAR_NUMBER: |
2264 | case VAR_FLOAT: |
2265 | case VAR_SPECIAL: { |
2266 | break; |
2267 | } |
2268 | case VAR_STRING: |
2269 | case VAR_FUNC: { |
2270 | if (from->vval.v_string != NULL) { |
2271 | to->vval.v_string = vim_strsave(from->vval.v_string); |
2272 | if (from->v_type == VAR_FUNC) { |
2273 | func_ref(to->vval.v_string); |
2274 | } |
2275 | } |
2276 | break; |
2277 | } |
2278 | case VAR_PARTIAL: { |
2279 | if (to->vval.v_partial != NULL) { |
2280 | to->vval.v_partial->pt_refcount++; |
2281 | } |
2282 | break; |
2283 | } |
2284 | case VAR_LIST: { |
2285 | tv_list_ref(to->vval.v_list); |
2286 | break; |
2287 | } |
2288 | case VAR_DICT: { |
2289 | if (from->vval.v_dict != NULL) { |
2290 | to->vval.v_dict->dv_refcount++; |
2291 | } |
2292 | break; |
2293 | } |
2294 | case VAR_UNKNOWN: { |
2295 | emsgf(_(e_intern2), "tv_copy(UNKNOWN)" ); |
2296 | break; |
2297 | } |
2298 | } |
2299 | } |
2300 | |
2301 | //{{{2 Locks |
2302 | |
2303 | /// Lock or unlock an item |
2304 | /// |
2305 | /// @param[out] tv Item to (un)lock. |
2306 | /// @param[in] deep Levels to (un)lock, -1 to (un)lock everything. |
2307 | /// @param[in] lock True if it is needed to lock an item, false to unlock. |
2308 | void tv_item_lock(typval_T *const tv, const int deep, const bool lock) |
2309 | FUNC_ATTR_NONNULL_ALL |
2310 | { |
2311 | // TODO(ZyX-I): Make this not recursive |
2312 | static int recurse = 0; |
2313 | |
2314 | if (recurse >= DICT_MAXNEST) { |
2315 | EMSG(_("E743: variable nested too deep for (un)lock" )); |
2316 | return; |
2317 | } |
2318 | if (deep == 0) { |
2319 | return; |
2320 | } |
2321 | recurse++; |
2322 | |
2323 | // lock/unlock the item itself |
2324 | #define CHANGE_LOCK(lock, var) \ |
2325 | do { \ |
2326 | var = ((VarLockStatus[]) { \ |
2327 | [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ |
2328 | [VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \ |
2329 | [VAR_FIXED] = VAR_FIXED, \ |
2330 | })[var]; \ |
2331 | } while (0) |
2332 | CHANGE_LOCK(lock, tv->v_lock); |
2333 | |
2334 | switch (tv->v_type) { |
2335 | case VAR_LIST: { |
2336 | list_T *const l = tv->vval.v_list; |
2337 | if (l != NULL) { |
2338 | CHANGE_LOCK(lock, l->lv_lock); |
2339 | if (deep < 0 || deep > 1) { |
2340 | // Recursive: lock/unlock the items the List contains. |
2341 | TV_LIST_ITER(l, li, { |
2342 | tv_item_lock(TV_LIST_ITEM_TV(li), deep - 1, lock); |
2343 | }); |
2344 | } |
2345 | } |
2346 | break; |
2347 | } |
2348 | case VAR_DICT: { |
2349 | dict_T *const d = tv->vval.v_dict; |
2350 | if (d != NULL) { |
2351 | CHANGE_LOCK(lock, d->dv_lock); |
2352 | if (deep < 0 || deep > 1) { |
2353 | // recursive: lock/unlock the items the List contains |
2354 | TV_DICT_ITER(d, di, { |
2355 | tv_item_lock(&di->di_tv, deep - 1, lock); |
2356 | }); |
2357 | } |
2358 | } |
2359 | break; |
2360 | } |
2361 | case VAR_NUMBER: |
2362 | case VAR_FLOAT: |
2363 | case VAR_STRING: |
2364 | case VAR_FUNC: |
2365 | case VAR_PARTIAL: |
2366 | case VAR_SPECIAL: { |
2367 | break; |
2368 | } |
2369 | case VAR_UNKNOWN: { |
2370 | assert(false); |
2371 | } |
2372 | } |
2373 | #undef CHANGE_LOCK |
2374 | recurse--; |
2375 | } |
2376 | |
2377 | /// Check whether VimL value is locked itself or refers to a locked container |
2378 | /// |
2379 | /// @warning Fixed container is not the same as locked. |
2380 | /// |
2381 | /// @param[in] tv Value to check. |
2382 | /// |
2383 | /// @return True if value is locked, false otherwise. |
2384 | bool tv_islocked(const typval_T *const tv) |
2385 | FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
2386 | { |
2387 | return ((tv->v_lock == VAR_LOCKED) |
2388 | || (tv->v_type == VAR_LIST |
2389 | && (tv_list_locked(tv->vval.v_list) == VAR_LOCKED)) |
2390 | || (tv->v_type == VAR_DICT |
2391 | && tv->vval.v_dict != NULL |
2392 | && (tv->vval.v_dict->dv_lock == VAR_LOCKED))); |
2393 | } |
2394 | |
2395 | /// Return true if typval is locked |
2396 | /// |
2397 | /// Also gives an error message when typval is locked. |
2398 | /// |
2399 | /// @param[in] lock Lock status. |
2400 | /// @param[in] name Variable name, used in the error message. |
2401 | /// @param[in] name_len Variable name length. Use #TV_TRANSLATE to translate |
2402 | /// variable name and compute the length. Use #TV_CSTRING |
2403 | /// to compute the length with strlen() without |
2404 | /// translating. |
2405 | /// |
2406 | /// Both #TV_… values are used for optimization purposes: |
2407 | /// variable name with its length is needed only in case |
2408 | /// of error, when no error occurs computing them is |
2409 | /// a waste of CPU resources. This especially applies to |
2410 | /// gettext. |
2411 | /// |
2412 | /// @return true if variable is locked, false otherwise. |
2413 | bool tv_check_lock(const VarLockStatus lock, const char *name, |
2414 | size_t name_len) |
2415 | FUNC_ATTR_WARN_UNUSED_RESULT |
2416 | { |
2417 | const char *error_message = NULL; |
2418 | switch (lock) { |
2419 | case VAR_UNLOCKED: { |
2420 | return false; |
2421 | } |
2422 | case VAR_LOCKED: { |
2423 | error_message = N_("E741: Value is locked: %.*s" ); |
2424 | break; |
2425 | } |
2426 | case VAR_FIXED: { |
2427 | error_message = N_("E742: Cannot change value of %.*s" ); |
2428 | break; |
2429 | } |
2430 | } |
2431 | assert(error_message != NULL); |
2432 | |
2433 | if (name == NULL) { |
2434 | name = _("Unknown" ); |
2435 | name_len = strlen(name); |
2436 | } else if (name_len == TV_TRANSLATE) { |
2437 | name = _(name); |
2438 | name_len = strlen(name); |
2439 | } else if (name_len == TV_CSTRING) { |
2440 | name_len = strlen(name); |
2441 | } |
2442 | |
2443 | emsgf(_(error_message), (int)name_len, name); |
2444 | |
2445 | return true; |
2446 | } |
2447 | |
2448 | //{{{2 Comparison |
2449 | |
2450 | static int tv_equal_recurse_limit; |
2451 | |
2452 | /// Compare two VimL values |
2453 | /// |
2454 | /// Like "==", but strings and numbers are different, as well as floats and |
2455 | /// numbers. |
2456 | /// |
2457 | /// @warning Too nested structures may be considered equal even if they are not. |
2458 | /// |
2459 | /// @param[in] tv1 First value to compare. |
2460 | /// @param[in] tv2 Second value to compare. |
2461 | /// @param[in] ic True if case is to be ignored. |
2462 | /// @param[in] recursive True when used recursively. |
2463 | /// |
2464 | /// @return true if values are equal. |
2465 | bool tv_equal(typval_T *const tv1, typval_T *const tv2, const bool ic, |
2466 | const bool recursive) |
2467 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
2468 | { |
2469 | // TODO(ZyX-I): Make this not recursive |
2470 | static int recursive_cnt = 0; // Catch recursive loops. |
2471 | |
2472 | if (!(tv_is_func(*tv1) && tv_is_func(*tv2)) && tv1->v_type != tv2->v_type) { |
2473 | return false; |
2474 | } |
2475 | |
2476 | // Catch lists and dicts that have an endless loop by limiting |
2477 | // recursiveness to a limit. We guess they are equal then. |
2478 | // A fixed limit has the problem of still taking an awful long time. |
2479 | // Reduce the limit every time running into it. That should work fine for |
2480 | // deeply linked structures that are not recursively linked and catch |
2481 | // recursiveness quickly. |
2482 | if (!recursive) { |
2483 | tv_equal_recurse_limit = 1000; |
2484 | } |
2485 | if (recursive_cnt >= tv_equal_recurse_limit) { |
2486 | tv_equal_recurse_limit--; |
2487 | return true; |
2488 | } |
2489 | |
2490 | switch (tv1->v_type) { |
2491 | case VAR_LIST: { |
2492 | recursive_cnt++; |
2493 | const bool r = tv_list_equal(tv1->vval.v_list, tv2->vval.v_list, ic, |
2494 | true); |
2495 | recursive_cnt--; |
2496 | return r; |
2497 | } |
2498 | case VAR_DICT: { |
2499 | recursive_cnt++; |
2500 | const bool r = tv_dict_equal(tv1->vval.v_dict, tv2->vval.v_dict, ic, |
2501 | true); |
2502 | recursive_cnt--; |
2503 | return r; |
2504 | } |
2505 | case VAR_PARTIAL: |
2506 | case VAR_FUNC: { |
2507 | if ((tv1->v_type == VAR_PARTIAL && tv1->vval.v_partial == NULL) |
2508 | || (tv2->v_type == VAR_PARTIAL && tv2->vval.v_partial == NULL)) { |
2509 | return false; |
2510 | } |
2511 | recursive_cnt++; |
2512 | const bool r = func_equal(tv1, tv2, ic); |
2513 | recursive_cnt--; |
2514 | return r; |
2515 | } |
2516 | case VAR_NUMBER: { |
2517 | return tv1->vval.v_number == tv2->vval.v_number; |
2518 | } |
2519 | case VAR_FLOAT: { |
2520 | return tv1->vval.v_float == tv2->vval.v_float; |
2521 | } |
2522 | case VAR_STRING: { |
2523 | char buf1[NUMBUFLEN]; |
2524 | char buf2[NUMBUFLEN]; |
2525 | const char *s1 = tv_get_string_buf(tv1, buf1); |
2526 | const char *s2 = tv_get_string_buf(tv2, buf2); |
2527 | return mb_strcmp_ic((bool)ic, s1, s2) == 0; |
2528 | } |
2529 | case VAR_SPECIAL: { |
2530 | return tv1->vval.v_special == tv2->vval.v_special; |
2531 | } |
2532 | case VAR_UNKNOWN: { |
2533 | // VAR_UNKNOWN can be the result of an invalid expression, let’s say it |
2534 | // does not equal anything, not even self. |
2535 | return false; |
2536 | } |
2537 | } |
2538 | |
2539 | assert(false); |
2540 | return false; |
2541 | } |
2542 | |
2543 | //{{{2 Type checks |
2544 | |
2545 | /// Check that given value is a number or string |
2546 | /// |
2547 | /// Error messages are compatible with tv_get_number() previously used for the |
2548 | /// same purpose in buf*() functions. Special values are not accepted (previous |
2549 | /// behaviour: silently fail to find buffer). |
2550 | /// |
2551 | /// @param[in] tv Value to check. |
2552 | /// |
2553 | /// @return true if everything is OK, false otherwise. |
2554 | bool tv_check_str_or_nr(const typval_T *const tv) |
2555 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL |
2556 | { |
2557 | switch (tv->v_type) { |
2558 | case VAR_NUMBER: |
2559 | case VAR_STRING: { |
2560 | return true; |
2561 | } |
2562 | case VAR_FLOAT: { |
2563 | EMSG(_("E805: Expected a Number or a String, Float found" )); |
2564 | return false; |
2565 | } |
2566 | case VAR_PARTIAL: |
2567 | case VAR_FUNC: { |
2568 | EMSG(_("E703: Expected a Number or a String, Funcref found" )); |
2569 | return false; |
2570 | } |
2571 | case VAR_LIST: { |
2572 | EMSG(_("E745: Expected a Number or a String, List found" )); |
2573 | return false; |
2574 | } |
2575 | case VAR_DICT: { |
2576 | EMSG(_("E728: Expected a Number or a String, Dictionary found" )); |
2577 | return false; |
2578 | } |
2579 | case VAR_SPECIAL: { |
2580 | EMSG(_("E5300: Expected a Number or a String" )); |
2581 | return false; |
2582 | } |
2583 | case VAR_UNKNOWN: { |
2584 | EMSG2(_(e_intern2), "tv_check_str_or_nr(UNKNOWN)" ); |
2585 | return false; |
2586 | } |
2587 | } |
2588 | assert(false); |
2589 | return false; |
2590 | } |
2591 | |
2592 | #define FUNC_ERROR "E703: Using a Funcref as a Number" |
2593 | |
2594 | static const char *const num_errors[] = { |
2595 | [VAR_PARTIAL]=N_(FUNC_ERROR), |
2596 | [VAR_FUNC]=N_(FUNC_ERROR), |
2597 | [VAR_LIST]=N_("E745: Using a List as a Number" ), |
2598 | [VAR_DICT]=N_("E728: Using a Dictionary as a Number" ), |
2599 | [VAR_FLOAT]=N_("E805: Using a Float as a Number" ), |
2600 | [VAR_UNKNOWN]=N_("E685: using an invalid value as a Number" ), |
2601 | }; |
2602 | |
2603 | #undef FUNC_ERROR |
2604 | |
2605 | /// Check that given value is a number or can be converted to it |
2606 | /// |
2607 | /// Error messages are compatible with tv_get_number_chk() previously used for |
2608 | /// the same purpose. |
2609 | /// |
2610 | /// @param[in] tv Value to check. |
2611 | /// |
2612 | /// @return true if everything is OK, false otherwise. |
2613 | bool tv_check_num(const typval_T *const tv) |
2614 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
2615 | { |
2616 | switch (tv->v_type) { |
2617 | case VAR_NUMBER: |
2618 | case VAR_SPECIAL: |
2619 | case VAR_STRING: { |
2620 | return true; |
2621 | } |
2622 | case VAR_FUNC: |
2623 | case VAR_PARTIAL: |
2624 | case VAR_LIST: |
2625 | case VAR_DICT: |
2626 | case VAR_FLOAT: |
2627 | case VAR_UNKNOWN: { |
2628 | EMSG(_(num_errors[tv->v_type])); |
2629 | return false; |
2630 | } |
2631 | } |
2632 | assert(false); |
2633 | return false; |
2634 | } |
2635 | |
2636 | #define FUNC_ERROR "E729: using Funcref as a String" |
2637 | |
2638 | static const char *const str_errors[] = { |
2639 | [VAR_PARTIAL]=N_(FUNC_ERROR), |
2640 | [VAR_FUNC]=N_(FUNC_ERROR), |
2641 | [VAR_LIST]=N_("E730: using List as a String" ), |
2642 | [VAR_DICT]=N_("E731: using Dictionary as a String" ), |
2643 | [VAR_FLOAT]=((const char *)e_float_as_string), |
2644 | [VAR_UNKNOWN]=N_("E908: using an invalid value as a String" ), |
2645 | }; |
2646 | |
2647 | #undef FUNC_ERROR |
2648 | |
2649 | /// Check that given value is a VimL String or can be "cast" to it. |
2650 | /// |
2651 | /// Error messages are compatible with tv_get_string_chk() previously used for |
2652 | /// the same purpose. |
2653 | /// |
2654 | /// @param[in] tv Value to check. |
2655 | /// |
2656 | /// @return true if everything is OK, false otherwise. |
2657 | bool tv_check_str(const typval_T *const tv) |
2658 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
2659 | { |
2660 | switch (tv->v_type) { |
2661 | case VAR_NUMBER: |
2662 | case VAR_SPECIAL: |
2663 | case VAR_STRING: { |
2664 | return true; |
2665 | } |
2666 | case VAR_PARTIAL: |
2667 | case VAR_FUNC: |
2668 | case VAR_LIST: |
2669 | case VAR_DICT: |
2670 | case VAR_FLOAT: |
2671 | case VAR_UNKNOWN: { |
2672 | EMSG(_(str_errors[tv->v_type])); |
2673 | return false; |
2674 | } |
2675 | } |
2676 | assert(false); |
2677 | return false; |
2678 | } |
2679 | |
2680 | //{{{2 Get |
2681 | |
2682 | /// Get the number value of a VimL object |
2683 | /// |
2684 | /// @note Use tv_get_number_chk() if you need to determine whether there was an |
2685 | /// error. |
2686 | /// |
2687 | /// @param[in] tv Object to get value from. |
2688 | /// |
2689 | /// @return Number value: vim_str2nr() output for VAR_STRING objects, value |
2690 | /// for VAR_NUMBER objects, -1 for other types. |
2691 | varnumber_T tv_get_number(const typval_T *const tv) |
2692 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
2693 | { |
2694 | bool error = false; |
2695 | return tv_get_number_chk(tv, &error); |
2696 | } |
2697 | |
2698 | /// Get the number value of a VimL object |
2699 | /// |
2700 | /// @param[in] tv Object to get value from. |
2701 | /// @param[out] ret_error If type error occurred then `true` will be written |
2702 | /// to this location. Otherwise it is not touched. |
2703 | /// |
2704 | /// @note Needs to be initialized to `false` to be |
2705 | /// useful. |
2706 | /// |
2707 | /// @return Number value: vim_str2nr() output for VAR_STRING objects, value |
2708 | /// for VAR_NUMBER objects, -1 (ret_error == NULL) or 0 (otherwise) for |
2709 | /// other types. |
2710 | varnumber_T tv_get_number_chk(const typval_T *const tv, bool *const ret_error) |
2711 | FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) |
2712 | { |
2713 | switch (tv->v_type) { |
2714 | case VAR_FUNC: |
2715 | case VAR_PARTIAL: |
2716 | case VAR_LIST: |
2717 | case VAR_DICT: |
2718 | case VAR_FLOAT: { |
2719 | EMSG(_(num_errors[tv->v_type])); |
2720 | break; |
2721 | } |
2722 | case VAR_NUMBER: { |
2723 | return tv->vval.v_number; |
2724 | } |
2725 | case VAR_STRING: { |
2726 | varnumber_T n = 0; |
2727 | if (tv->vval.v_string != NULL) { |
2728 | vim_str2nr(tv->vval.v_string, NULL, NULL, STR2NR_ALL, &n, NULL, 0); |
2729 | } |
2730 | return n; |
2731 | } |
2732 | case VAR_SPECIAL: { |
2733 | switch (tv->vval.v_special) { |
2734 | case kSpecialVarTrue: { |
2735 | return 1; |
2736 | } |
2737 | case kSpecialVarFalse: |
2738 | case kSpecialVarNull: { |
2739 | return 0; |
2740 | } |
2741 | } |
2742 | break; |
2743 | } |
2744 | case VAR_UNKNOWN: { |
2745 | emsgf(_(e_intern2), "tv_get_number(UNKNOWN)" ); |
2746 | break; |
2747 | } |
2748 | } |
2749 | if (ret_error != NULL) { |
2750 | *ret_error = true; |
2751 | } |
2752 | return (ret_error == NULL ? -1 : 0); |
2753 | } |
2754 | |
2755 | /// Get the line number from VimL object |
2756 | /// |
2757 | /// @param[in] tv Object to get value from. Is expected to be a number or |
2758 | /// a special string like ".", "$", … (works with current buffer |
2759 | /// only). |
2760 | /// |
2761 | /// @return Line number or -1 or 0. |
2762 | linenr_T tv_get_lnum(const typval_T *const tv) |
2763 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
2764 | { |
2765 | linenr_T lnum = (linenr_T)tv_get_number_chk(tv, NULL); |
2766 | if (lnum == 0) { // No valid number, try using same function as line() does. |
2767 | int fnum; |
2768 | pos_T *const fp = var2fpos(tv, true, &fnum); |
2769 | if (fp != NULL) { |
2770 | lnum = fp->lnum; |
2771 | } |
2772 | } |
2773 | return lnum; |
2774 | } |
2775 | |
2776 | /// Get the floating-point value of a VimL object |
2777 | /// |
2778 | /// Raises an error if object is not number or floating-point. |
2779 | /// |
2780 | /// @param[in] tv Object to get value of. |
2781 | /// |
2782 | /// @return Floating-point value of the variable or zero. |
2783 | float_T tv_get_float(const typval_T *const tv) |
2784 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
2785 | { |
2786 | switch (tv->v_type) { |
2787 | case VAR_NUMBER: { |
2788 | return (float_T)(tv->vval.v_number); |
2789 | } |
2790 | case VAR_FLOAT: { |
2791 | return tv->vval.v_float; |
2792 | } |
2793 | case VAR_PARTIAL: |
2794 | case VAR_FUNC: { |
2795 | EMSG(_("E891: Using a Funcref as a Float" )); |
2796 | break; |
2797 | } |
2798 | case VAR_STRING: { |
2799 | EMSG(_("E892: Using a String as a Float" )); |
2800 | break; |
2801 | } |
2802 | case VAR_LIST: { |
2803 | EMSG(_("E893: Using a List as a Float" )); |
2804 | break; |
2805 | } |
2806 | case VAR_DICT: { |
2807 | EMSG(_("E894: Using a Dictionary as a Float" )); |
2808 | break; |
2809 | } |
2810 | case VAR_SPECIAL: { |
2811 | EMSG(_("E907: Using a special value as a Float" )); |
2812 | break; |
2813 | } |
2814 | case VAR_UNKNOWN: { |
2815 | emsgf(_(e_intern2), "tv_get_float(UNKNOWN)" ); |
2816 | break; |
2817 | } |
2818 | } |
2819 | return 0; |
2820 | } |
2821 | |
2822 | /// Get the string value of a "stringish" VimL object. |
2823 | /// |
2824 | /// @param[in] tv Object to get value of. |
2825 | /// @param buf Buffer used to hold numbers and special variables converted to |
2826 | /// string. When function encounters one of these stringified value |
2827 | /// will be written to buf and buf will be returned. |
2828 | /// |
2829 | /// Buffer must have NUMBUFLEN size. |
2830 | /// |
2831 | /// @return Object value if it is VAR_STRING object, number converted to |
2832 | /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. |
2833 | const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) |
2834 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
2835 | { |
2836 | switch (tv->v_type) { |
2837 | case VAR_NUMBER: { |
2838 | snprintf(buf, NUMBUFLEN, "%" PRIdVARNUMBER, tv->vval.v_number); // -V576 |
2839 | return buf; |
2840 | } |
2841 | case VAR_STRING: { |
2842 | if (tv->vval.v_string != NULL) { |
2843 | return (const char *)tv->vval.v_string; |
2844 | } |
2845 | return "" ; |
2846 | } |
2847 | case VAR_SPECIAL: { |
2848 | STRCPY(buf, encode_special_var_names[tv->vval.v_special]); |
2849 | return buf; |
2850 | } |
2851 | case VAR_PARTIAL: |
2852 | case VAR_FUNC: |
2853 | case VAR_LIST: |
2854 | case VAR_DICT: |
2855 | case VAR_FLOAT: |
2856 | case VAR_UNKNOWN: { |
2857 | EMSG(_(str_errors[tv->v_type])); |
2858 | return false; |
2859 | } |
2860 | } |
2861 | return NULL; |
2862 | } |
2863 | |
2864 | /// Get the string value of a "stringish" VimL object. |
2865 | /// |
2866 | /// @warning For number and special values it uses a single, static buffer. It |
2867 | /// may be used only once, next call to tv_get_string may reuse it. Use |
2868 | /// tv_get_string_buf() if you need to use tv_get_string() output after |
2869 | /// calling it again. |
2870 | /// |
2871 | /// @param[in] tv Object to get value of. |
2872 | /// |
2873 | /// @return Object value if it is VAR_STRING object, number converted to |
2874 | /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or NULL. |
2875 | const char *tv_get_string_chk(const typval_T *const tv) |
2876 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT |
2877 | { |
2878 | static char mybuf[NUMBUFLEN]; |
2879 | |
2880 | return tv_get_string_buf_chk(tv, mybuf); |
2881 | } |
2882 | |
2883 | /// Get the string value of a "stringish" VimL object. |
2884 | /// |
2885 | /// @warning For number and special values it uses a single, static buffer. It |
2886 | /// may be used only once, next call to tv_get_string may reuse it. Use |
2887 | /// tv_get_string_buf() if you need to use tv_get_string() output after |
2888 | /// calling it again. |
2889 | /// |
2890 | /// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but |
2891 | /// return NULL on error. |
2892 | /// |
2893 | /// @param[in] tv Object to get value of. |
2894 | /// |
2895 | /// @return Object value if it is VAR_STRING object, number converted to |
2896 | /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty |
2897 | /// string. |
2898 | const char *tv_get_string(const typval_T *const tv) |
2899 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT |
2900 | { |
2901 | static char mybuf[NUMBUFLEN]; |
2902 | return tv_get_string_buf((typval_T *)tv, mybuf); |
2903 | } |
2904 | |
2905 | /// Get the string value of a "stringish" VimL object. |
2906 | /// |
2907 | /// @note tv_get_string_chk() and tv_get_string_buf_chk() are similar, but |
2908 | /// return NULL on error. |
2909 | /// |
2910 | /// @param[in] tv Object to get value of. |
2911 | /// @param buf Buffer used to hold numbers and special variables converted to |
2912 | /// string. When function encounters one of these stringified value |
2913 | /// will be written to buf and buf will be returned. |
2914 | /// |
2915 | /// Buffer must have NUMBUFLEN size. |
2916 | /// |
2917 | /// @return Object value if it is VAR_STRING object, number converted to |
2918 | /// a string for VAR_NUMBER, v: variable name for VAR_SPECIAL or empty |
2919 | /// string. |
2920 | const char *tv_get_string_buf(const typval_T *const tv, char *const buf) |
2921 | FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT |
2922 | { |
2923 | const char *const res = (const char *)tv_get_string_buf_chk(tv, buf); |
2924 | |
2925 | return res != NULL ? res : "" ; |
2926 | } |
2927 | |