blob: f2989fff21e47decf4a23ab560d434021e3fb0a0 [file] [log] [blame]
Bram Moolenaaredf3f972016-08-29 22:49:24 +02001/* vi:set ts=8 sts=4 sw=4 noet:
Bram Moolenaarcd524592016-07-17 14:57:05 +02002 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * dict.c: Dictionary support
12 */
Bram Moolenaarcd524592016-07-17 14:57:05 +020013
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
Bram Moolenaar5d18efe2019-12-01 21:11:22 +010018// List head for garbage collection. Although there can be a reference loop
19// from partial to dict to partial, we don't need to keep track of the partial,
20// since it will get freed when the dict is unused and gets freed.
21static dict_T *first_dict = NULL;
Bram Moolenaarcd524592016-07-17 14:57:05 +020022
23/*
24 * Allocate an empty header for a dictionary.
Bram Moolenaar22286892020-11-05 20:50:51 +010025 * Caller should take care of the reference count.
Bram Moolenaarcd524592016-07-17 14:57:05 +020026 */
27 dict_T *
28dict_alloc(void)
29{
30 dict_T *d;
31
Bram Moolenaaradc67142019-06-22 01:40:42 +020032 d = ALLOC_CLEAR_ONE(dict_T);
Bram Moolenaarcd524592016-07-17 14:57:05 +020033 if (d != NULL)
34 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +010035 // Add the dict to the list of dicts for garbage collection.
Bram Moolenaarcd524592016-07-17 14:57:05 +020036 if (first_dict != NULL)
37 first_dict->dv_used_prev = d;
38 d->dv_used_next = first_dict;
39 d->dv_used_prev = NULL;
40 first_dict = d;
41
42 hash_init(&d->dv_hashtab);
43 d->dv_lock = 0;
44 d->dv_scope = 0;
45 d->dv_refcount = 0;
46 d->dv_copyID = 0;
47 }
48 return d;
49}
50
Bram Moolenaarf49cc602018-11-11 15:21:05 +010051/*
52 * dict_alloc() with an ID for alloc_fail().
53 */
54 dict_T *
55dict_alloc_id(alloc_id_T id UNUSED)
56{
57#ifdef FEAT_EVAL
Bram Moolenaar51e14382019-05-25 20:21:28 +020058 if (alloc_fail_id == id && alloc_does_fail(sizeof(list_T)))
Bram Moolenaarf49cc602018-11-11 15:21:05 +010059 return NULL;
60#endif
61 return (dict_alloc());
62}
63
Bram Moolenaar7e1652c2017-12-16 18:27:02 +010064 dict_T *
65dict_alloc_lock(int lock)
66{
67 dict_T *d = dict_alloc();
68
69 if (d != NULL)
70 d->dv_lock = lock;
71 return d;
72}
73
Bram Moolenaarcd524592016-07-17 14:57:05 +020074/*
75 * Allocate an empty dict for a return value.
76 * Returns OK or FAIL.
77 */
78 int
79rettv_dict_alloc(typval_T *rettv)
80{
Bram Moolenaar7e1652c2017-12-16 18:27:02 +010081 dict_T *d = dict_alloc_lock(0);
Bram Moolenaarcd524592016-07-17 14:57:05 +020082
83 if (d == NULL)
84 return FAIL;
85
Bram Moolenaar45cf6e92017-04-30 20:25:19 +020086 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +020087 return OK;
88}
89
90/*
Bram Moolenaar45cf6e92017-04-30 20:25:19 +020091 * Set a dictionary as the return value
92 */
93 void
94rettv_dict_set(typval_T *rettv, dict_T *d)
95{
96 rettv->v_type = VAR_DICT;
97 rettv->vval.v_dict = d;
98 if (d != NULL)
99 ++d->dv_refcount;
100}
101
102/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200103 * Free a Dictionary, including all non-container items it contains.
104 * Ignores the reference count.
105 */
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100106 void
Bram Moolenaarcd524592016-07-17 14:57:05 +0200107dict_free_contents(dict_T *d)
108{
Bram Moolenaar89483d42020-05-10 15:24:44 +0200109 hashtab_free_contents(&d->dv_hashtab);
Bram Moolenaaraa210a32021-01-02 15:41:03 +0100110 free_type(d->dv_type);
111 d->dv_type = NULL;
Bram Moolenaar89483d42020-05-10 15:24:44 +0200112}
113
114/*
115 * Clear hashtab "ht" and dict items it contains.
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100116 * If "ht" is not freed then you should call hash_init() next!
Bram Moolenaar89483d42020-05-10 15:24:44 +0200117 */
118 void
119hashtab_free_contents(hashtab_T *ht)
120{
Bram Moolenaarcd524592016-07-17 14:57:05 +0200121 int todo;
122 hashitem_T *hi;
123 dictitem_T *di;
124
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100125 // Lock the hashtab, we don't want it to resize while freeing items.
Bram Moolenaar89483d42020-05-10 15:24:44 +0200126 hash_lock(ht);
127 todo = (int)ht->ht_used;
128 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200129 {
130 if (!HASHITEM_EMPTY(hi))
131 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100132 // Remove the item before deleting it, just in case there is
133 // something recursive causing trouble.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200134 di = HI2DI(hi);
Bram Moolenaar89483d42020-05-10 15:24:44 +0200135 hash_remove(ht, hi);
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100136 dictitem_free(di);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200137 --todo;
138 }
139 }
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100140
Bram Moolenaar89483d42020-05-10 15:24:44 +0200141 // The hashtab is still locked, it has to be re-initialized anyway.
142 hash_clear(ht);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200143}
144
145 static void
146dict_free_dict(dict_T *d)
147{
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100148 // Remove the dict from the list of dicts for garbage collection.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200149 if (d->dv_used_prev == NULL)
150 first_dict = d->dv_used_next;
151 else
152 d->dv_used_prev->dv_used_next = d->dv_used_next;
153 if (d->dv_used_next != NULL)
154 d->dv_used_next->dv_used_prev = d->dv_used_prev;
155 vim_free(d);
156}
157
158 static void
159dict_free(dict_T *d)
160{
161 if (!in_free_unref_items)
162 {
163 dict_free_contents(d);
164 dict_free_dict(d);
165 }
166}
167
168/*
169 * Unreference a Dictionary: decrement the reference count and free it when it
170 * becomes zero.
171 */
172 void
173dict_unref(dict_T *d)
174{
175 if (d != NULL && --d->dv_refcount <= 0)
176 dict_free(d);
177}
178
179/*
180 * Go through the list of dicts and free items without the copyID.
181 * Returns TRUE if something was freed.
182 */
183 int
184dict_free_nonref(int copyID)
185{
186 dict_T *dd;
187 int did_free = FALSE;
188
189 for (dd = first_dict; dd != NULL; dd = dd->dv_used_next)
190 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
191 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100192 // Free the Dictionary and ordinary items it contains, but don't
193 // recurse into Lists and Dictionaries, they will be in the list
194 // of dicts or list of lists.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200195 dict_free_contents(dd);
196 did_free = TRUE;
197 }
198 return did_free;
199}
200
201 void
202dict_free_items(int copyID)
203{
204 dict_T *dd, *dd_next;
205
206 for (dd = first_dict; dd != NULL; dd = dd_next)
207 {
208 dd_next = dd->dv_used_next;
209 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
210 dict_free_dict(dd);
211 }
212}
213
214/*
215 * Allocate a Dictionary item.
216 * The "key" is copied to the new item.
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200217 * Note that the type and value of the item "di_tv" still needs to be
218 * initialized!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200219 * Returns NULL when out of memory.
220 */
221 dictitem_T *
222dictitem_alloc(char_u *key)
223{
224 dictitem_T *di;
225
Bram Moolenaarb59e7352019-08-07 21:42:24 +0200226 di = alloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200227 if (di != NULL)
228 {
229 STRCPY(di->di_key, key);
230 di->di_flags = DI_FLAGS_ALLOC;
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200231 di->di_tv.v_lock = 0;
Bram Moolenaar3b318512021-09-06 19:19:45 +0200232 di->di_tv.v_type = VAR_UNKNOWN;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200233 }
234 return di;
235}
236
237/*
238 * Make a copy of a Dictionary item.
239 */
240 static dictitem_T *
241dictitem_copy(dictitem_T *org)
242{
243 dictitem_T *di;
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200244 size_t len = STRLEN(org->di_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200245
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200246 di = alloc(offsetof(dictitem_T, di_key) + len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200247 if (di != NULL)
248 {
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200249 mch_memmove(di->di_key, org->di_key, len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200250 di->di_flags = DI_FLAGS_ALLOC;
251 copy_tv(&org->di_tv, &di->di_tv);
252 }
253 return di;
254}
255
256/*
257 * Remove item "item" from Dictionary "dict" and free it.
258 */
259 void
260dictitem_remove(dict_T *dict, dictitem_T *item)
261{
262 hashitem_T *hi;
263
264 hi = hash_find(&dict->dv_hashtab, item->di_key);
265 if (HASHITEM_EMPTY(hi))
Bram Moolenaar95f09602016-11-10 20:01:45 +0100266 internal_error("dictitem_remove()");
Bram Moolenaarcd524592016-07-17 14:57:05 +0200267 else
268 hash_remove(&dict->dv_hashtab, hi);
269 dictitem_free(item);
270}
271
272/*
273 * Free a dict item. Also clears the value.
274 */
275 void
276dictitem_free(dictitem_T *item)
277{
278 clear_tv(&item->di_tv);
279 if (item->di_flags & DI_FLAGS_ALLOC)
280 vim_free(item);
281}
282
283/*
284 * Make a copy of dict "d". Shallow if "deep" is FALSE.
285 * The refcount of the new dict is set to 1.
286 * See item_copy() for "copyID".
287 * Returns NULL when out of memory.
288 */
289 dict_T *
290dict_copy(dict_T *orig, int deep, int copyID)
291{
292 dict_T *copy;
293 dictitem_T *di;
294 int todo;
295 hashitem_T *hi;
296
297 if (orig == NULL)
298 return NULL;
299
300 copy = dict_alloc();
301 if (copy != NULL)
302 {
303 if (copyID != 0)
304 {
305 orig->dv_copyID = copyID;
306 orig->dv_copydict = copy;
307 }
308 todo = (int)orig->dv_hashtab.ht_used;
309 for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
310 {
311 if (!HASHITEM_EMPTY(hi))
312 {
313 --todo;
314
315 di = dictitem_alloc(hi->hi_key);
316 if (di == NULL)
317 break;
318 if (deep)
319 {
320 if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep,
321 copyID) == FAIL)
322 {
323 vim_free(di);
324 break;
325 }
326 }
327 else
328 copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
329 if (dict_add(copy, di) == FAIL)
330 {
331 dictitem_free(di);
332 break;
333 }
334 }
335 }
336
337 ++copy->dv_refcount;
338 if (todo > 0)
339 {
340 dict_unref(copy);
341 copy = NULL;
342 }
343 }
344
345 return copy;
346}
347
348/*
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200349 * Check for adding a function to g: or s:.
350 * If the name is wrong give an error message and return TRUE.
351 */
352 int
353dict_wrong_func_name(dict_T *d, typval_T *tv, char_u *name)
354{
355 return (d == get_globvar_dict()
Bram Moolenaarb54abee2021-06-02 11:49:23 +0200356 || (in_vim9script() && SCRIPT_ID_VALID(current_sctx.sc_sid)
357 && d == &SCRIPT_ITEM(current_sctx.sc_sid)->sn_vars->sv_dict)
358 || &d->dv_hashtab == get_funccal_local_ht())
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200359 && (tv->v_type == VAR_FUNC || tv->v_type == VAR_PARTIAL)
360 && var_wrong_func_name(name, TRUE);
361}
362
363/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200364 * Add item "item" to Dictionary "d".
365 * Returns FAIL when out of memory and when key already exists.
366 */
367 int
368dict_add(dict_T *d, dictitem_T *item)
369{
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +0200370 if (dict_wrong_func_name(d, &item->di_tv, item->di_key))
371 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200372 return hash_add(&d->dv_hashtab, item->di_key);
373}
374
375/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200376 * Add a number or special entry to dictionary "d".
Bram Moolenaarcd524592016-07-17 14:57:05 +0200377 * Returns FAIL when out of memory and when key already exists.
378 */
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200379 static int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100380dict_add_number_special(dict_T *d, char *key, varnumber_T nr, vartype_T vartype)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200381{
382 dictitem_T *item;
383
384 item = dictitem_alloc((char_u *)key);
385 if (item == NULL)
386 return FAIL;
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100387 item->di_tv.v_type = vartype;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200388 item->di_tv.vval.v_number = nr;
389 if (dict_add(d, item) == FAIL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200390 {
Bram Moolenaare0be1672018-07-08 16:50:37 +0200391 dictitem_free(item);
392 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200393 }
Bram Moolenaare0be1672018-07-08 16:50:37 +0200394 return OK;
395}
396
397/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200398 * Add a number entry to dictionary "d".
399 * Returns FAIL when out of memory and when key already exists.
400 */
401 int
402dict_add_number(dict_T *d, char *key, varnumber_T nr)
403{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100404 return dict_add_number_special(d, key, nr, VAR_NUMBER);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200405}
406
407/*
408 * Add a special entry to dictionary "d".
409 * Returns FAIL when out of memory and when key already exists.
410 */
411 int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100412dict_add_bool(dict_T *d, char *key, varnumber_T nr)
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200413{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100414 return dict_add_number_special(d, key, nr, VAR_BOOL);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200415}
416
417/*
Bram Moolenaare0be1672018-07-08 16:50:37 +0200418 * Add a string entry to dictionary "d".
419 * Returns FAIL when out of memory and when key already exists.
420 */
421 int
422dict_add_string(dict_T *d, char *key, char_u *str)
423{
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100424 return dict_add_string_len(d, key, str, -1);
425}
426
427/*
428 * Add a string entry to dictionary "d".
429 * "str" will be copied to allocated memory.
430 * When "len" is -1 use the whole string, otherwise only this many bytes.
431 * Returns FAIL when out of memory and when key already exists.
432 */
433 int
434dict_add_string_len(dict_T *d, char *key, char_u *str, int len)
435{
Bram Moolenaare0be1672018-07-08 16:50:37 +0200436 dictitem_T *item;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100437 char_u *val = NULL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200438
439 item = dictitem_alloc((char_u *)key);
440 if (item == NULL)
441 return FAIL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200442 item->di_tv.v_type = VAR_STRING;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100443 if (str != NULL)
444 {
445 if (len == -1)
446 val = vim_strsave(str);
447 else
448 val = vim_strnsave(str, len);
449 }
450 item->di_tv.vval.v_string = val;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200451 if (dict_add(d, item) == FAIL)
452 {
453 dictitem_free(item);
454 return FAIL;
455 }
456 return OK;
457}
458
459/*
460 * Add a list entry to dictionary "d".
461 * Returns FAIL when out of memory and when key already exists.
462 */
463 int
464dict_add_list(dict_T *d, char *key, list_T *list)
465{
466 dictitem_T *item;
467
468 item = dictitem_alloc((char_u *)key);
469 if (item == NULL)
470 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200471 item->di_tv.v_type = VAR_LIST;
472 item->di_tv.vval.v_list = list;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100473 ++list->lv_refcount;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200474 if (dict_add(d, item) == FAIL)
475 {
476 dictitem_free(item);
477 return FAIL;
478 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200479 return OK;
480}
481
482/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100483 * Add a typval_T entry to dictionary "d".
484 * Returns FAIL when out of memory and when key already exists.
485 */
486 int
487dict_add_tv(dict_T *d, char *key, typval_T *tv)
488{
489 dictitem_T *item;
490
491 item = dictitem_alloc((char_u *)key);
492 if (item == NULL)
493 return FAIL;
494 copy_tv(tv, &item->di_tv);
495 if (dict_add(d, item) == FAIL)
496 {
497 dictitem_free(item);
498 return FAIL;
499 }
500 return OK;
501}
502
503/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200504 * Add a callback to dictionary "d".
505 * Returns FAIL when out of memory and when key already exists.
506 */
507 int
508dict_add_callback(dict_T *d, char *key, callback_T *cb)
509{
510 dictitem_T *item;
511
512 item = dictitem_alloc((char_u *)key);
513 if (item == NULL)
514 return FAIL;
515 put_callback(cb, &item->di_tv);
516 if (dict_add(d, item) == FAIL)
517 {
518 dictitem_free(item);
519 return FAIL;
520 }
521 return OK;
522}
523
524/*
Bram Moolenaar45e18cb2019-04-28 18:05:35 +0200525 * Initializes "iter" for iterating over dictionary items with
526 * dict_iterate_next().
527 * If "var" is not a Dict or an empty Dict then there will be nothing to
528 * iterate over, no error is given.
529 * NOTE: The dictionary must not change until iterating is finished!
530 */
531 void
532dict_iterate_start(typval_T *var, dict_iterator_T *iter)
533{
534 if (var->v_type != VAR_DICT || var->vval.v_dict == NULL)
535 iter->dit_todo = 0;
536 else
537 {
538 dict_T *d = var->vval.v_dict;
539
540 iter->dit_todo = d->dv_hashtab.ht_used;
541 iter->dit_hi = d->dv_hashtab.ht_array;
542 }
543}
544
545/*
546 * Iterate over the items referred to by "iter". It should be initialized with
547 * dict_iterate_start().
548 * Returns a pointer to the key.
549 * "*tv_result" is set to point to the value for that key.
550 * If there are no more items, NULL is returned.
551 */
552 char_u *
553dict_iterate_next(dict_iterator_T *iter, typval_T **tv_result)
554{
555 dictitem_T *di;
556 char_u *result;
557
558 if (iter->dit_todo == 0)
559 return NULL;
560
561 while (HASHITEM_EMPTY(iter->dit_hi))
562 ++iter->dit_hi;
563
564 di = HI2DI(iter->dit_hi);
565 result = di->di_key;
566 *tv_result = &di->di_tv;
567
568 --iter->dit_todo;
569 ++iter->dit_hi;
570 return result;
571}
572
573/*
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200574 * Add a dict entry to dictionary "d".
575 * Returns FAIL when out of memory and when key already exists.
576 */
577 int
578dict_add_dict(dict_T *d, char *key, dict_T *dict)
579{
580 dictitem_T *item;
581
582 item = dictitem_alloc((char_u *)key);
583 if (item == NULL)
584 return FAIL;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200585 item->di_tv.v_type = VAR_DICT;
586 item->di_tv.vval.v_dict = dict;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100587 ++dict->dv_refcount;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200588 if (dict_add(d, item) == FAIL)
589 {
590 dictitem_free(item);
591 return FAIL;
592 }
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200593 return OK;
594}
595
596/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200597 * Get the number of items in a Dictionary.
598 */
599 long
600dict_len(dict_T *d)
601{
602 if (d == NULL)
603 return 0L;
604 return (long)d->dv_hashtab.ht_used;
605}
606
607/*
608 * Find item "key[len]" in Dictionary "d".
609 * If "len" is negative use strlen(key).
610 * Returns NULL when not found.
611 */
612 dictitem_T *
613dict_find(dict_T *d, char_u *key, int len)
614{
615#define AKEYLEN 200
616 char_u buf[AKEYLEN];
617 char_u *akey;
618 char_u *tofree = NULL;
619 hashitem_T *hi;
620
621 if (d == NULL)
622 return NULL;
623 if (len < 0)
624 akey = key;
625 else if (len >= AKEYLEN)
626 {
627 tofree = akey = vim_strnsave(key, len);
628 if (akey == NULL)
629 return NULL;
630 }
631 else
632 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100633 // Avoid a malloc/free by using buf[].
Bram Moolenaarcd524592016-07-17 14:57:05 +0200634 vim_strncpy(buf, key, len);
635 akey = buf;
636 }
637
638 hi = hash_find(&d->dv_hashtab, akey);
639 vim_free(tofree);
640 if (HASHITEM_EMPTY(hi))
641 return NULL;
642 return HI2DI(hi);
643}
644
645/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100646 * Get a typval_T item from a dictionary and copy it into "rettv".
647 * Returns FAIL if the entry doesn't exist or out of memory.
648 */
649 int
650dict_get_tv(dict_T *d, char_u *key, typval_T *rettv)
651{
652 dictitem_T *di;
Bram Moolenaar08928322020-01-04 14:32:48 +0100653
654 di = dict_find(d, key, -1);
655 if (di == NULL)
656 return FAIL;
657 copy_tv(&di->di_tv, rettv);
658 return OK;
659}
660
661/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200662 * Get a string item from a dictionary.
663 * When "save" is TRUE allocate memory for it.
Bram Moolenaar7dc5e2e2016-08-05 22:22:06 +0200664 * When FALSE a shared buffer is used, can only be used once!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200665 * Returns NULL if the entry doesn't exist or out of memory.
666 */
667 char_u *
Bram Moolenaar8f667172018-12-14 15:38:31 +0100668dict_get_string(dict_T *d, char_u *key, int save)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200669{
670 dictitem_T *di;
671 char_u *s;
672
673 di = dict_find(d, key, -1);
674 if (di == NULL)
675 return NULL;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100676 s = tv_get_string(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200677 if (save && s != NULL)
678 s = vim_strsave(s);
679 return s;
680}
681
682/*
683 * Get a number item from a dictionary.
684 * Returns 0 if the entry doesn't exist.
685 */
686 varnumber_T
Bram Moolenaar8f667172018-12-14 15:38:31 +0100687dict_get_number(dict_T *d, char_u *key)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200688{
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200689 return dict_get_number_def(d, key, 0);
690}
691
692/*
693 * Get a number item from a dictionary.
694 * Returns "def" if the entry doesn't exist.
695 */
696 varnumber_T
697dict_get_number_def(dict_T *d, char_u *key, int def)
698{
Bram Moolenaarcd524592016-07-17 14:57:05 +0200699 dictitem_T *di;
700
701 di = dict_find(d, key, -1);
702 if (di == NULL)
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200703 return def;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100704 return tv_get_number(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200705}
706
707/*
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +0200708 * Get a number item from a dictionary.
709 * Returns 0 if the entry doesn't exist.
710 * Give an error if the entry is not a number.
711 */
712 varnumber_T
713dict_get_number_check(dict_T *d, char_u *key)
714{
715 dictitem_T *di;
716
717 di = dict_find(d, key, -1);
718 if (di == NULL)
719 return 0;
720 if (di->di_tv.v_type != VAR_NUMBER)
721 {
722 semsg(_(e_invarg2), tv_get_string(&di->di_tv));
723 return 0;
724 }
725 return tv_get_number(&di->di_tv);
726}
727
728/*
Bram Moolenaar55881332020-08-18 13:04:15 +0200729 * Get a bool item (number or true/false) from a dictionary.
730 * Returns "def" if the entry doesn't exist.
731 */
732 varnumber_T
733dict_get_bool(dict_T *d, char_u *key, int def)
734{
735 dictitem_T *di;
736
737 di = dict_find(d, key, -1);
738 if (di == NULL)
739 return def;
740 return tv_get_bool(&di->di_tv);
741}
742
743/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200744 * Return an allocated string with the string representation of a Dictionary.
745 * May return NULL.
746 */
747 char_u *
748dict2string(typval_T *tv, int copyID, int restore_copyID)
749{
750 garray_T ga;
751 int first = TRUE;
752 char_u *tofree;
753 char_u numbuf[NUMBUFLEN];
754 hashitem_T *hi;
755 char_u *s;
756 dict_T *d;
757 int todo;
758
759 if ((d = tv->vval.v_dict) == NULL)
760 return NULL;
761 ga_init2(&ga, (int)sizeof(char), 80);
762 ga_append(&ga, '{');
763
764 todo = (int)d->dv_hashtab.ht_used;
765 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
766 {
767 if (!HASHITEM_EMPTY(hi))
768 {
769 --todo;
770
771 if (first)
772 first = FALSE;
773 else
774 ga_concat(&ga, (char_u *)", ");
775
776 tofree = string_quote(hi->hi_key, FALSE);
777 if (tofree != NULL)
778 {
779 ga_concat(&ga, tofree);
780 vim_free(tofree);
781 }
782 ga_concat(&ga, (char_u *)": ");
783 s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID,
784 FALSE, restore_copyID, TRUE);
785 if (s != NULL)
786 ga_concat(&ga, s);
787 vim_free(tofree);
788 if (s == NULL || did_echo_string_emsg)
789 break;
790 line_breakcheck();
791
792 }
793 }
794 if (todo > 0)
795 {
796 vim_free(ga.ga_data);
797 return NULL;
798 }
799
800 ga_append(&ga, '}');
801 ga_append(&ga, NUL);
802 return (char_u *)ga.ga_data;
803}
804
805/*
Bram Moolenaare0de1712020-12-02 17:36:54 +0100806 * Advance over a literal key, including "-". If the first character is not a
807 * literal key character then "key" is returned.
808 */
Yegappan Lakshmanan8ee52af2021-08-09 19:59:06 +0200809 static char_u *
Bram Moolenaare0de1712020-12-02 17:36:54 +0100810skip_literal_key(char_u *key)
811{
812 char_u *p;
813
814 for (p = key; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; ++p)
815 ;
816 return p;
817}
818
819/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200820 * Get the key for #{key: val} into "tv" and advance "arg".
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200821 * Return FAIL when there is no valid key.
822 */
823 static int
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100824get_literal_key_tv(char_u **arg, typval_T *tv)
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200825{
Bram Moolenaare0de1712020-12-02 17:36:54 +0100826 char_u *p = skip_literal_key(*arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200827
Bram Moolenaare0de1712020-12-02 17:36:54 +0100828 if (p == *arg)
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200829 return FAIL;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200830 tv->v_type = VAR_STRING;
Bram Moolenaardf44a272020-06-07 20:49:05 +0200831 tv->vval.v_string = vim_strnsave(*arg, p - *arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200832
Bram Moolenaardb199212020-08-12 18:01:53 +0200833 *arg = p;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200834 return OK;
835}
836
837/*
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100838 * Get a literal key for a Vim9 dict:
839 * {"name": value},
840 * {'name': value},
841 * {name: value} use "name" as a literal key
842 * Return the key in allocated memory or NULL in the case of an error.
843 * "arg" is advanced to just after the key.
844 */
845 char_u *
846get_literal_key(char_u **arg)
847{
848 char_u *key;
849 char_u *end;
850 typval_T rettv;
851
852 if (**arg == '\'')
853 {
854 if (eval_lit_string(arg, &rettv, TRUE) == FAIL)
855 return NULL;
856 key = rettv.vval.v_string;
857 }
858 else if (**arg == '"')
859 {
860 if (eval_string(arg, &rettv, TRUE) == FAIL)
861 return NULL;
862 key = rettv.vval.v_string;
863 }
864 else
865 {
866 end = skip_literal_key(*arg);
867 if (end == *arg)
868 {
869 semsg(_(e_invalid_key_str), *arg);
870 return NULL;
871 }
872 key = vim_strnsave(*arg, end - *arg);
873 *arg = end;
874 }
875 return key;
876}
877
878/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200879 * Allocate a variable for a Dictionary and fill it from "*arg".
Bram Moolenaar962d7212020-07-04 14:15:00 +0200880 * "*arg" points to the "{".
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200881 * "literal" is TRUE for #{key: val}
Bram Moolenaarcd524592016-07-17 14:57:05 +0200882 * Return OK or FAIL. Returns NOTDONE for {expr}.
883 */
884 int
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200885eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200886{
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200887 int evaluate = evalarg == NULL ? FALSE
888 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200889 dict_T *d = NULL;
890 typval_T tvkey;
891 typval_T tv;
892 char_u *key = NULL;
893 dictitem_T *item;
Bram Moolenaar98cb90e2021-11-30 11:56:22 +0000894 char_u *curly_expr = skipwhite(*arg + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200895 char_u buf[NUMBUFLEN];
Bram Moolenaareb6880b2020-07-12 17:07:05 +0200896 int vim9script = in_vim9script();
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200897 int had_comma;
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200898
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +0000899 // First check if it's not a curly-braces thing: {expr}.
900 // Must do this without evaluating, otherwise a function may be called
901 // twice. Unfortunately this means we need to call eval1() twice for the
902 // first item.
903 // But {} is an empty Dictionary.
Bram Moolenaar98cb90e2021-11-30 11:56:22 +0000904 if (!vim9script
905 && *curly_expr != '}'
906 && eval1(&curly_expr, &tv, NULL) == OK
907 && *skipwhite(curly_expr) == '}')
908 return NOTDONE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200909
910 if (evaluate)
911 {
912 d = dict_alloc();
913 if (d == NULL)
914 return FAIL;
915 }
916 tvkey.v_type = VAR_UNKNOWN;
917 tv.v_type = VAR_UNKNOWN;
918
Bram Moolenaar962d7212020-07-04 14:15:00 +0200919 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200920 while (**arg != '}' && **arg != NUL)
921 {
Bram Moolenaare0de1712020-12-02 17:36:54 +0100922 int has_bracket = vim9script && **arg == '[';
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100923
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100924 if (literal)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100925 {
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100926 if (get_literal_key_tv(arg, &tvkey) == FAIL)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100927 goto failret;
928 }
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100929 else if (vim9script && !has_bracket)
930 {
931 tvkey.vval.v_string = get_literal_key(arg);
932 if (tvkey.vval.v_string == NULL)
933 goto failret;
934 tvkey.v_type = VAR_STRING;
935 }
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100936 else
937 {
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100938 if (has_bracket)
939 *arg = skipwhite(*arg + 1);
940 if (eval1(arg, &tvkey, evalarg) == FAIL) // recursive!
941 goto failret;
942 if (has_bracket)
943 {
944 *arg = skipwhite(*arg);
945 if (**arg != ']')
946 {
947 emsg(_(e_missing_matching_bracket_after_dict_key));
Bram Moolenaar8bb0f542020-12-06 16:03:55 +0100948 clear_tv(&tvkey);
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100949 return FAIL;
950 }
951 ++*arg;
952 }
953 }
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200954
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200955 // the colon should come right after the key, but this wasn't checked
Bram Moolenaar9d489562020-07-30 20:08:50 +0200956 // previously, so only require it in Vim9 script.
957 if (!vim9script)
958 *arg = skipwhite(*arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200959 if (**arg != ':')
960 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200961 if (*skipwhite(*arg) == ':')
Bram Moolenaarba98fb52021-02-07 18:06:29 +0100962 semsg(_(e_no_white_space_allowed_before_str_str), ":", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200963 else
964 semsg(_(e_missing_dict_colon), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200965 clear_tv(&tvkey);
966 goto failret;
967 }
968 if (evaluate)
969 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100970#ifdef FEAT_FLOAT
971 if (tvkey.v_type == VAR_FLOAT)
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100972 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100973 tvkey.vval.v_string = typval_tostring(&tvkey, TRUE);
974 tvkey.v_type = VAR_STRING;
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100975 }
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100976#endif
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100977 key = tv_get_string_buf_chk(&tvkey, buf);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200978 if (key == NULL)
979 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100980 // "key" is NULL when tv_get_string_buf_chk() gave an errmsg
Bram Moolenaarcd524592016-07-17 14:57:05 +0200981 clear_tv(&tvkey);
982 goto failret;
983 }
984 }
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200985 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
986 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +0100987 semsg(_(e_white_space_required_after_str_str), ":", *arg);
Bram Moolenaarab19d492020-06-27 17:04:05 +0200988 clear_tv(&tvkey);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200989 goto failret;
990 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200991
Bram Moolenaar962d7212020-07-04 14:15:00 +0200992 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200993 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200994 {
995 if (evaluate)
996 clear_tv(&tvkey);
997 goto failret;
998 }
999 if (evaluate)
1000 {
1001 item = dict_find(d, key, -1);
1002 if (item != NULL)
1003 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001004 semsg(_(e_duplicate_key), key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001005 clear_tv(&tvkey);
1006 clear_tv(&tv);
1007 goto failret;
1008 }
1009 item = dictitem_alloc(key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001010 if (item != NULL)
1011 {
1012 item->di_tv = tv;
1013 item->di_tv.v_lock = 0;
1014 if (dict_add(d, item) == FAIL)
1015 dictitem_free(item);
1016 }
1017 }
Bram Moolenaara8931942019-09-28 17:25:10 +02001018 clear_tv(&tvkey);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001019
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001020 // the comma should come right after the value, but this wasn't checked
1021 // previously, so only require it in Vim9 script.
1022 if (!vim9script)
1023 *arg = skipwhite(*arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001024 had_comma = **arg == ',';
1025 if (had_comma)
1026 {
1027 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
1028 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001029 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001030 goto failret;
1031 }
1032 *arg = skipwhite(*arg + 1);
1033 }
1034
1035 // the "}" can be on the next line
Bram Moolenaar962d7212020-07-04 14:15:00 +02001036 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001037 if (**arg == '}')
1038 break;
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001039 if (!had_comma)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001040 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001041 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001042 semsg(_(e_no_white_space_allowed_before_str_str), ",", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001043 else
1044 semsg(_(e_missing_dict_comma), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001045 goto failret;
1046 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001047 }
1048
1049 if (**arg != '}')
1050 {
Bram Moolenaar4bce26b2021-01-22 22:06:56 +01001051 if (evalarg != NULL)
1052 semsg(_(e_missing_dict_end), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001053failret:
Bram Moolenaaradc67142019-06-22 01:40:42 +02001054 if (d != NULL)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001055 dict_free(d);
1056 return FAIL;
1057 }
1058
Bram Moolenaarb07a39d2020-10-22 19:00:01 +02001059 *arg = *arg + 1;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001060 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001061 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001062
1063 return OK;
1064}
1065
1066/*
1067 * Go over all entries in "d2" and add them to "d1".
1068 * When "action" is "error" then a duplicate key is an error.
1069 * When "action" is "force" then a duplicate key is overwritten.
1070 * Otherwise duplicate keys are ignored ("action" is "keep").
1071 */
1072 void
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001073dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001074{
1075 dictitem_T *di1;
1076 hashitem_T *hi2;
1077 int todo;
1078 char_u *arg_errmsg = (char_u *)N_("extend() argument");
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001079 type_T *type;
1080
1081 if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL)
1082 type = d1->dv_type->tt_member;
1083 else
1084 type = NULL;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001085
1086 todo = (int)d2->dv_hashtab.ht_used;
1087 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
1088 {
1089 if (!HASHITEM_EMPTY(hi2))
1090 {
1091 --todo;
1092 di1 = dict_find(d1, hi2->hi_key, -1);
1093 if (d1->dv_scope != 0)
1094 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001095 // Disallow replacing a builtin function in l: and g:.
1096 // Check the key to be valid when adding to any scope.
Bram Moolenaarcd524592016-07-17 14:57:05 +02001097 if (d1->dv_scope == VAR_DEF_SCOPE
1098 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
Bram Moolenaar98b4f142020-08-08 15:46:01 +02001099 && var_wrong_func_name(hi2->hi_key, di1 == NULL))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001100 break;
Bram Moolenaar3b3755f2021-11-22 20:10:18 +00001101 if (!valid_varname(hi2->hi_key, -1, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001102 break;
1103 }
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001104
1105 if (type != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001106 && check_typval_arg_type(type, &HI2DI(hi2)->di_tv,
1107 func_name, 0) == FAIL)
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001108 break;
1109
Bram Moolenaarcd524592016-07-17 14:57:05 +02001110 if (di1 == NULL)
1111 {
1112 di1 = dictitem_copy(HI2DI(hi2));
1113 if (di1 != NULL && dict_add(d1, di1) == FAIL)
1114 dictitem_free(di1);
1115 }
1116 else if (*action == 'e')
1117 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001118 semsg(_("E737: Key already exists: %s"), hi2->hi_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001119 break;
1120 }
1121 else if (*action == 'f' && HI2DI(hi2) != di1)
1122 {
Bram Moolenaara187c432020-09-16 21:08:28 +02001123 if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
Bram Moolenaar05c00c02019-02-11 22:00:11 +01001124 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001125 break;
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +02001126 if (dict_wrong_func_name(d1, &HI2DI(hi2)->di_tv, hi2->hi_key))
1127 break;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001128 clear_tv(&di1->di_tv);
1129 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
1130 }
1131 }
1132 }
1133}
1134
1135/*
1136 * Return the dictitem that an entry in a hashtable points to.
1137 */
1138 dictitem_T *
1139dict_lookup(hashitem_T *hi)
1140{
1141 return HI2DI(hi);
1142}
1143
1144/*
1145 * Return TRUE when two dictionaries have exactly the same key/values.
1146 */
1147 int
1148dict_equal(
1149 dict_T *d1,
1150 dict_T *d2,
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001151 int ic, // ignore case for strings
1152 int recursive) // TRUE when used recursively
Bram Moolenaarcd524592016-07-17 14:57:05 +02001153{
1154 hashitem_T *hi;
1155 dictitem_T *item2;
1156 int todo;
1157
Bram Moolenaarcd524592016-07-17 14:57:05 +02001158 if (d1 == d2)
1159 return TRUE;
1160 if (dict_len(d1) != dict_len(d2))
1161 return FALSE;
Bram Moolenaarea04a6e2020-04-23 13:38:02 +02001162 if (dict_len(d1) == 0)
1163 // empty and NULL dicts are considered equal
1164 return TRUE;
1165 if (d1 == NULL || d2 == NULL)
1166 return FALSE;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001167
1168 todo = (int)d1->dv_hashtab.ht_used;
1169 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
1170 {
1171 if (!HASHITEM_EMPTY(hi))
1172 {
1173 item2 = dict_find(d2, hi->hi_key, -1);
1174 if (item2 == NULL)
1175 return FALSE;
1176 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
1177 return FALSE;
1178 --todo;
1179 }
1180 }
1181 return TRUE;
1182}
1183
1184/*
Yegappan Lakshmananf973eeb2021-12-22 18:19:26 +00001185 * Count the number of times item "needle" occurs in Dict "d". Case is ignored
1186 * if "ic" is TRUE.
1187 */
1188 long
1189dict_count(dict_T *d, typval_T *needle, int ic)
1190{
1191 int todo;
1192 hashitem_T *hi;
1193 long n = 0;
1194
1195 if (d == NULL)
1196 return 0;
1197
1198 todo = (int)d->dv_hashtab.ht_used;
1199 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1200 {
1201 if (!HASHITEM_EMPTY(hi))
1202 {
1203 --todo;
1204 if (tv_equal(&HI2DI(hi)->di_tv, needle, ic, FALSE))
1205 ++n;
1206 }
1207 }
1208
1209 return n;
1210}
1211
1212/*
1213 * extend() a Dict. Append Dict argvars[1] to Dict argvars[0] and return the
1214 * resulting Dict in "rettv". "is_new" is TRUE for extendnew().
1215 */
1216 void
1217dict_extend_func(
1218 typval_T *argvars,
1219 type_T *type,
1220 char *func_name,
1221 char_u *arg_errmsg,
1222 int is_new,
1223 typval_T *rettv)
1224{
1225 dict_T *d1, *d2;
1226 char_u *action;
1227 int i;
1228
1229 d1 = argvars[0].vval.v_dict;
1230 if (d1 == NULL)
1231 {
1232 emsg(_(e_cannot_extend_null_dict));
1233 return;
1234 }
1235 d2 = argvars[1].vval.v_dict;
1236 if ((is_new || !value_check_lock(d1->dv_lock, arg_errmsg, TRUE))
1237 && d2 != NULL)
1238 {
1239 if (is_new)
1240 {
1241 d1 = dict_copy(d1, FALSE, get_copyID());
1242 if (d1 == NULL)
1243 return;
1244 }
1245
1246 // Check the third argument.
1247 if (argvars[2].v_type != VAR_UNKNOWN)
1248 {
1249 static char *(av[]) = {"keep", "force", "error"};
1250
1251 action = tv_get_string_chk(&argvars[2]);
1252 if (action == NULL)
1253 return;
1254 for (i = 0; i < 3; ++i)
1255 if (STRCMP(action, av[i]) == 0)
1256 break;
1257 if (i == 3)
1258 {
1259 semsg(_(e_invarg2), action);
1260 return;
1261 }
1262 }
1263 else
1264 action = (char_u *)"force";
1265
1266 if (type != NULL && check_typval_arg_type(type, &argvars[1],
1267 func_name, 2) == FAIL)
1268 return;
1269 dict_extend(d1, d2, action, func_name);
1270
1271 if (is_new)
1272 {
1273 rettv->v_type = VAR_DICT;
1274 rettv->vval.v_dict = d1;
1275 rettv->v_lock = FALSE;
1276 }
1277 else
1278 copy_tv(&argvars[0], rettv);
1279 }
1280}
1281
1282/*
1283 * Implementation of map() and filter() for a Dict. Apply "expr" to every
1284 * item in Dict "d" and return the result in "rettv".
1285 */
1286 void
1287dict_filter_map(
1288 dict_T *d,
1289 filtermap_T filtermap,
1290 type_T *argtype,
1291 char *func_name,
1292 char_u *arg_errmsg,
1293 typval_T *expr,
1294 typval_T *rettv)
1295{
1296 int prev_lock;
1297 dict_T *d_ret = NULL;
1298 hashtab_T *ht;
1299 hashitem_T *hi;
1300 dictitem_T *di;
1301 int todo;
1302 int rem;
1303
1304 if (filtermap == FILTERMAP_MAPNEW)
1305 {
1306 rettv->v_type = VAR_DICT;
1307 rettv->vval.v_dict = NULL;
1308 }
1309 if (d == NULL
1310 || (filtermap == FILTERMAP_FILTER
1311 && value_check_lock(d->dv_lock, arg_errmsg, TRUE)))
1312 return;
1313
1314 prev_lock = d->dv_lock;
1315
1316 if (filtermap == FILTERMAP_MAPNEW)
1317 {
1318 if (rettv_dict_alloc(rettv) == FAIL)
1319 return;
1320 d_ret = rettv->vval.v_dict;
1321 }
1322
1323 if (filtermap != FILTERMAP_FILTER && d->dv_lock == 0)
1324 d->dv_lock = VAR_LOCKED;
1325 ht = &d->dv_hashtab;
1326 hash_lock(ht);
1327 todo = (int)ht->ht_used;
1328 for (hi = ht->ht_array; todo > 0; ++hi)
1329 {
1330 if (!HASHITEM_EMPTY(hi))
1331 {
1332 int r;
1333 typval_T newtv;
1334
1335 --todo;
1336 di = HI2DI(hi);
1337 if (filtermap == FILTERMAP_MAP
1338 && (value_check_lock(di->di_tv.v_lock,
1339 arg_errmsg, TRUE)
1340 || var_check_ro(di->di_flags,
1341 arg_errmsg, TRUE)))
1342 break;
1343 set_vim_var_string(VV_KEY, di->di_key, -1);
1344 newtv.v_type = VAR_UNKNOWN;
1345 r = filter_map_one(&di->di_tv, expr, filtermap,
1346 &newtv, &rem);
1347 clear_tv(get_vim_var_tv(VV_KEY));
1348 if (r == FAIL || did_emsg)
1349 {
1350 clear_tv(&newtv);
1351 break;
1352 }
1353 if (filtermap == FILTERMAP_MAP)
1354 {
1355 if (argtype != NULL && check_typval_arg_type(
1356 argtype->tt_member, &newtv,
1357 func_name, 0) == FAIL)
1358 {
1359 clear_tv(&newtv);
1360 break;
1361 }
1362 // map(): replace the dict item value
1363 clear_tv(&di->di_tv);
1364 newtv.v_lock = 0;
1365 di->di_tv = newtv;
1366 }
1367 else if (filtermap == FILTERMAP_MAPNEW)
1368 {
1369 // mapnew(): add the item value to the new dict
1370 r = dict_add_tv(d_ret, (char *)di->di_key, &newtv);
1371 clear_tv(&newtv);
1372 if (r == FAIL)
1373 break;
1374 }
1375 else if (filtermap == FILTERMAP_FILTER && rem)
1376 {
1377 // filter(false): remove the item from the dict
1378 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1379 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1380 break;
1381 dictitem_remove(d, di);
1382 }
1383 }
1384 }
1385 hash_unlock(ht);
1386 d->dv_lock = prev_lock;
1387}
1388
1389/*
1390 * "remove({dict})" function
1391 */
1392 void
1393dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1394{
1395 dict_T *d;
1396 char_u *key;
1397 dictitem_T *di;
1398
1399 if (argvars[2].v_type != VAR_UNKNOWN)
1400 {
1401 semsg(_(e_too_many_arguments_for_function_str), "remove()");
1402 return;
1403 }
1404
1405 d = argvars[0].vval.v_dict;
1406 if (d == NULL || value_check_lock(d->dv_lock, arg_errmsg, TRUE))
1407 return;
1408
1409 key = tv_get_string_chk(&argvars[1]);
1410 if (key == NULL)
1411 return;
1412
1413 di = dict_find(d, key, -1);
1414 if (di == NULL)
1415 {
1416 semsg(_(e_dictkey), key);
1417 return;
1418 }
1419
1420 if (var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1421 || var_check_ro(di->di_flags, arg_errmsg, TRUE))
1422 return;
1423
1424 *rettv = di->di_tv;
1425 init_tv(&di->di_tv);
1426 dictitem_remove(d, di);
1427}
1428
1429/*
Bram Moolenaarcd524592016-07-17 14:57:05 +02001430 * Turn a dict into a list:
1431 * "what" == 0: list of keys
1432 * "what" == 1: list of values
1433 * "what" == 2: list of items
1434 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001435 static void
Bram Moolenaarcd524592016-07-17 14:57:05 +02001436dict_list(typval_T *argvars, typval_T *rettv, int what)
1437{
1438 list_T *l2;
1439 dictitem_T *di;
1440 hashitem_T *hi;
1441 listitem_T *li;
1442 listitem_T *li2;
1443 dict_T *d;
1444 int todo;
1445
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001446 if (in_vim9script() && check_for_dict_arg(argvars, 0) == FAIL)
1447 return;
1448
Bram Moolenaarcd524592016-07-17 14:57:05 +02001449 if (argvars[0].v_type != VAR_DICT)
1450 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001451 emsg(_(e_dictreq));
Bram Moolenaarcd524592016-07-17 14:57:05 +02001452 return;
1453 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001454
1455 if (rettv_list_alloc(rettv) == FAIL)
1456 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001457 if ((d = argvars[0].vval.v_dict) == NULL)
1458 // empty dict behaves like an empty dict
1459 return;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001460
1461 todo = (int)d->dv_hashtab.ht_used;
1462 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1463 {
1464 if (!HASHITEM_EMPTY(hi))
1465 {
1466 --todo;
1467 di = HI2DI(hi);
1468
1469 li = listitem_alloc();
1470 if (li == NULL)
1471 break;
1472 list_append(rettv->vval.v_list, li);
1473
1474 if (what == 0)
1475 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001476 // keys()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001477 li->li_tv.v_type = VAR_STRING;
1478 li->li_tv.v_lock = 0;
1479 li->li_tv.vval.v_string = vim_strsave(di->di_key);
1480 }
1481 else if (what == 1)
1482 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001483 // values()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001484 copy_tv(&di->di_tv, &li->li_tv);
1485 }
1486 else
1487 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001488 // items()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001489 l2 = list_alloc();
1490 li->li_tv.v_type = VAR_LIST;
1491 li->li_tv.v_lock = 0;
1492 li->li_tv.vval.v_list = l2;
1493 if (l2 == NULL)
1494 break;
1495 ++l2->lv_refcount;
1496
1497 li2 = listitem_alloc();
1498 if (li2 == NULL)
1499 break;
1500 list_append(l2, li2);
1501 li2->li_tv.v_type = VAR_STRING;
1502 li2->li_tv.v_lock = 0;
1503 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
1504
1505 li2 = listitem_alloc();
1506 if (li2 == NULL)
1507 break;
1508 list_append(l2, li2);
1509 copy_tv(&di->di_tv, &li2->li_tv);
1510 }
1511 }
1512 }
1513}
1514
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001515/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001516 * "items(dict)" function
1517 */
1518 void
1519f_items(typval_T *argvars, typval_T *rettv)
1520{
1521 dict_list(argvars, rettv, 2);
1522}
1523
1524/*
1525 * "keys()" function
1526 */
1527 void
1528f_keys(typval_T *argvars, typval_T *rettv)
1529{
1530 dict_list(argvars, rettv, 0);
1531}
1532
1533/*
1534 * "values(dict)" function
1535 */
1536 void
1537f_values(typval_T *argvars, typval_T *rettv)
1538{
1539 dict_list(argvars, rettv, 1);
1540}
1541
1542/*
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001543 * Make each item in the dict readonly (not the value of the item).
1544 */
1545 void
1546dict_set_items_ro(dict_T *di)
1547{
1548 int todo = (int)di->dv_hashtab.ht_used;
1549 hashitem_T *hi;
1550
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001551 // Set readonly
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001552 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
1553 {
1554 if (HASHITEM_EMPTY(hi))
1555 continue;
1556 --todo;
1557 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
1558 }
1559}
1560
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001561/*
1562 * "has_key()" function
1563 */
1564 void
1565f_has_key(typval_T *argvars, typval_T *rettv)
1566{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001567 if (in_vim9script()
1568 && (check_for_dict_arg(argvars, 0) == FAIL
1569 || check_for_string_or_number_arg(argvars, 1) == FAIL))
1570 return;
1571
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001572 if (argvars[0].v_type != VAR_DICT)
1573 {
1574 emsg(_(e_dictreq));
1575 return;
1576 }
1577 if (argvars[0].vval.v_dict == NULL)
1578 return;
1579
1580 rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
1581 tv_get_string(&argvars[1]), -1) != NULL;
1582}
1583
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001584#endif // defined(FEAT_EVAL)