blob: aa3c6113723bda7fac0929d7440e1aec3e057102 [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;
894 char_u *start = skipwhite(*arg + 1);
895 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
Bram Moolenaarcd524592016-07-17 14:57:05 +0200899 /*
900 * First check if it's not a curly-braces thing: {expr}.
901 * Must do this without evaluating, otherwise a function may be called
902 * twice. Unfortunately this means we need to call eval1() twice for the
903 * first item.
904 * But {} is an empty Dictionary.
905 */
Bram Moolenaar32e35112020-05-14 22:41:15 +0200906 if (!vim9script && *start != '}')
Bram Moolenaarcd524592016-07-17 14:57:05 +0200907 {
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200908 if (eval1(&start, &tv, NULL) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200909 return FAIL;
Bram Moolenaarae95a392020-08-06 16:38:12 +0200910 if (*skipwhite(start) == '}')
Bram Moolenaarcd524592016-07-17 14:57:05 +0200911 return NOTDONE;
912 }
913
914 if (evaluate)
915 {
916 d = dict_alloc();
917 if (d == NULL)
918 return FAIL;
919 }
920 tvkey.v_type = VAR_UNKNOWN;
921 tv.v_type = VAR_UNKNOWN;
922
Bram Moolenaar962d7212020-07-04 14:15:00 +0200923 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200924 while (**arg != '}' && **arg != NUL)
925 {
Bram Moolenaare0de1712020-12-02 17:36:54 +0100926 int has_bracket = vim9script && **arg == '[';
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100927
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100928 if (literal)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100929 {
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100930 if (get_literal_key_tv(arg, &tvkey) == FAIL)
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100931 goto failret;
932 }
Bram Moolenaarc5e6a712020-12-04 19:12:14 +0100933 else if (vim9script && !has_bracket)
934 {
935 tvkey.vval.v_string = get_literal_key(arg);
936 if (tvkey.vval.v_string == NULL)
937 goto failret;
938 tvkey.v_type = VAR_STRING;
939 }
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100940 else
941 {
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100942 if (has_bracket)
943 *arg = skipwhite(*arg + 1);
944 if (eval1(arg, &tvkey, evalarg) == FAIL) // recursive!
945 goto failret;
946 if (has_bracket)
947 {
948 *arg = skipwhite(*arg);
949 if (**arg != ']')
950 {
951 emsg(_(e_missing_matching_bracket_after_dict_key));
Bram Moolenaar8bb0f542020-12-06 16:03:55 +0100952 clear_tv(&tvkey);
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100953 return FAIL;
954 }
955 ++*arg;
956 }
957 }
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200958
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200959 // the colon should come right after the key, but this wasn't checked
Bram Moolenaar9d489562020-07-30 20:08:50 +0200960 // previously, so only require it in Vim9 script.
961 if (!vim9script)
962 *arg = skipwhite(*arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200963 if (**arg != ':')
964 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200965 if (*skipwhite(*arg) == ':')
Bram Moolenaarba98fb52021-02-07 18:06:29 +0100966 semsg(_(e_no_white_space_allowed_before_str_str), ":", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200967 else
968 semsg(_(e_missing_dict_colon), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200969 clear_tv(&tvkey);
970 goto failret;
971 }
972 if (evaluate)
973 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100974#ifdef FEAT_FLOAT
975 if (tvkey.v_type == VAR_FLOAT)
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100976 {
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100977 tvkey.vval.v_string = typval_tostring(&tvkey, TRUE);
978 tvkey.v_type = VAR_STRING;
Bram Moolenaar9987fb02020-12-15 21:41:56 +0100979 }
Bram Moolenaar2e5910b2021-02-03 17:41:24 +0100980#endif
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100981 key = tv_get_string_buf_chk(&tvkey, buf);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200982 if (key == NULL)
983 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100984 // "key" is NULL when tv_get_string_buf_chk() gave an errmsg
Bram Moolenaarcd524592016-07-17 14:57:05 +0200985 clear_tv(&tvkey);
986 goto failret;
987 }
988 }
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200989 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
990 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +0100991 semsg(_(e_white_space_required_after_str_str), ":", *arg);
Bram Moolenaarab19d492020-06-27 17:04:05 +0200992 clear_tv(&tvkey);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200993 goto failret;
994 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200995
Bram Moolenaar962d7212020-07-04 14:15:00 +0200996 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200997 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200998 {
999 if (evaluate)
1000 clear_tv(&tvkey);
1001 goto failret;
1002 }
1003 if (evaluate)
1004 {
1005 item = dict_find(d, key, -1);
1006 if (item != NULL)
1007 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001008 semsg(_(e_duplicate_key), key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001009 clear_tv(&tvkey);
1010 clear_tv(&tv);
1011 goto failret;
1012 }
1013 item = dictitem_alloc(key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001014 if (item != NULL)
1015 {
1016 item->di_tv = tv;
1017 item->di_tv.v_lock = 0;
1018 if (dict_add(d, item) == FAIL)
1019 dictitem_free(item);
1020 }
1021 }
Bram Moolenaara8931942019-09-28 17:25:10 +02001022 clear_tv(&tvkey);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001023
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +02001024 // the comma should come right after the value, but this wasn't checked
1025 // previously, so only require it in Vim9 script.
1026 if (!vim9script)
1027 *arg = skipwhite(*arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001028 had_comma = **arg == ',';
1029 if (had_comma)
1030 {
1031 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
1032 {
Bram Moolenaarc3fc75d2021-02-07 15:28:09 +01001033 semsg(_(e_white_space_required_after_str_str), ",", *arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001034 goto failret;
1035 }
1036 *arg = skipwhite(*arg + 1);
1037 }
1038
1039 // the "}" can be on the next line
Bram Moolenaar962d7212020-07-04 14:15:00 +02001040 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001041 if (**arg == '}')
1042 break;
Bram Moolenaar8ea93902020-06-27 14:11:53 +02001043 if (!had_comma)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001044 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001045 if (**arg == ',')
Bram Moolenaarba98fb52021-02-07 18:06:29 +01001046 semsg(_(e_no_white_space_allowed_before_str_str), ",", *arg);
Bram Moolenaara2c026d2020-10-18 18:03:17 +02001047 else
1048 semsg(_(e_missing_dict_comma), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001049 goto failret;
1050 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001051 }
1052
1053 if (**arg != '}')
1054 {
Bram Moolenaar4bce26b2021-01-22 22:06:56 +01001055 if (evalarg != NULL)
1056 semsg(_(e_missing_dict_end), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001057failret:
Bram Moolenaaradc67142019-06-22 01:40:42 +02001058 if (d != NULL)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001059 dict_free(d);
1060 return FAIL;
1061 }
1062
Bram Moolenaarb07a39d2020-10-22 19:00:01 +02001063 *arg = *arg + 1;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001064 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +02001065 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001066
1067 return OK;
1068}
1069
1070/*
1071 * Go over all entries in "d2" and add them to "d1".
1072 * When "action" is "error" then a duplicate key is an error.
1073 * When "action" is "force" then a duplicate key is overwritten.
1074 * Otherwise duplicate keys are ignored ("action" is "keep").
1075 */
1076 void
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001077dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
Bram Moolenaarcd524592016-07-17 14:57:05 +02001078{
1079 dictitem_T *di1;
1080 hashitem_T *hi2;
1081 int todo;
1082 char_u *arg_errmsg = (char_u *)N_("extend() argument");
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001083 type_T *type;
1084
1085 if (d1->dv_type != NULL && d1->dv_type->tt_member != NULL)
1086 type = d1->dv_type->tt_member;
1087 else
1088 type = NULL;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001089
1090 todo = (int)d2->dv_hashtab.ht_used;
1091 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
1092 {
1093 if (!HASHITEM_EMPTY(hi2))
1094 {
1095 --todo;
1096 di1 = dict_find(d1, hi2->hi_key, -1);
1097 if (d1->dv_scope != 0)
1098 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001099 // Disallow replacing a builtin function in l: and g:.
1100 // Check the key to be valid when adding to any scope.
Bram Moolenaarcd524592016-07-17 14:57:05 +02001101 if (d1->dv_scope == VAR_DEF_SCOPE
1102 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
Bram Moolenaar98b4f142020-08-08 15:46:01 +02001103 && var_wrong_func_name(hi2->hi_key, di1 == NULL))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001104 break;
Bram Moolenaar03290b82020-12-19 16:30:44 +01001105 if (!valid_varname(hi2->hi_key, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001106 break;
1107 }
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001108
1109 if (type != NULL
Bram Moolenaar7a3fe3e2021-07-22 14:58:47 +02001110 && check_typval_arg_type(type, &HI2DI(hi2)->di_tv,
1111 func_name, 0) == FAIL)
Bram Moolenaaraa210a32021-01-02 15:41:03 +01001112 break;
1113
Bram Moolenaarcd524592016-07-17 14:57:05 +02001114 if (di1 == NULL)
1115 {
1116 di1 = dictitem_copy(HI2DI(hi2));
1117 if (di1 != NULL && dict_add(d1, di1) == FAIL)
1118 dictitem_free(di1);
1119 }
1120 else if (*action == 'e')
1121 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001122 semsg(_("E737: Key already exists: %s"), hi2->hi_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001123 break;
1124 }
1125 else if (*action == 'f' && HI2DI(hi2) != di1)
1126 {
Bram Moolenaara187c432020-09-16 21:08:28 +02001127 if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
Bram Moolenaar05c00c02019-02-11 22:00:11 +01001128 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001129 break;
Bram Moolenaar6f1d2aa2021-06-01 21:21:55 +02001130 if (dict_wrong_func_name(d1, &HI2DI(hi2)->di_tv, hi2->hi_key))
1131 break;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001132 clear_tv(&di1->di_tv);
1133 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
1134 }
1135 }
1136 }
1137}
1138
1139/*
1140 * Return the dictitem that an entry in a hashtable points to.
1141 */
1142 dictitem_T *
1143dict_lookup(hashitem_T *hi)
1144{
1145 return HI2DI(hi);
1146}
1147
1148/*
1149 * Return TRUE when two dictionaries have exactly the same key/values.
1150 */
1151 int
1152dict_equal(
1153 dict_T *d1,
1154 dict_T *d2,
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001155 int ic, // ignore case for strings
1156 int recursive) // TRUE when used recursively
Bram Moolenaarcd524592016-07-17 14:57:05 +02001157{
1158 hashitem_T *hi;
1159 dictitem_T *item2;
1160 int todo;
1161
Bram Moolenaarcd524592016-07-17 14:57:05 +02001162 if (d1 == d2)
1163 return TRUE;
1164 if (dict_len(d1) != dict_len(d2))
1165 return FALSE;
Bram Moolenaarea04a6e2020-04-23 13:38:02 +02001166 if (dict_len(d1) == 0)
1167 // empty and NULL dicts are considered equal
1168 return TRUE;
1169 if (d1 == NULL || d2 == NULL)
1170 return FALSE;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001171
1172 todo = (int)d1->dv_hashtab.ht_used;
1173 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
1174 {
1175 if (!HASHITEM_EMPTY(hi))
1176 {
1177 item2 = dict_find(d2, hi->hi_key, -1);
1178 if (item2 == NULL)
1179 return FALSE;
1180 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
1181 return FALSE;
1182 --todo;
1183 }
1184 }
1185 return TRUE;
1186}
1187
1188/*
1189 * Turn a dict into a list:
1190 * "what" == 0: list of keys
1191 * "what" == 1: list of values
1192 * "what" == 2: list of items
1193 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001194 static void
Bram Moolenaarcd524592016-07-17 14:57:05 +02001195dict_list(typval_T *argvars, typval_T *rettv, int what)
1196{
1197 list_T *l2;
1198 dictitem_T *di;
1199 hashitem_T *hi;
1200 listitem_T *li;
1201 listitem_T *li2;
1202 dict_T *d;
1203 int todo;
1204
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001205 if (in_vim9script() && check_for_dict_arg(argvars, 0) == FAIL)
1206 return;
1207
Bram Moolenaarcd524592016-07-17 14:57:05 +02001208 if (argvars[0].v_type != VAR_DICT)
1209 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001210 emsg(_(e_dictreq));
Bram Moolenaarcd524592016-07-17 14:57:05 +02001211 return;
1212 }
Bram Moolenaarcd524592016-07-17 14:57:05 +02001213
1214 if (rettv_list_alloc(rettv) == FAIL)
1215 return;
Bram Moolenaaref982572021-08-12 19:27:57 +02001216 if ((d = argvars[0].vval.v_dict) == NULL)
1217 // empty dict behaves like an empty dict
1218 return;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001219
1220 todo = (int)d->dv_hashtab.ht_used;
1221 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1222 {
1223 if (!HASHITEM_EMPTY(hi))
1224 {
1225 --todo;
1226 di = HI2DI(hi);
1227
1228 li = listitem_alloc();
1229 if (li == NULL)
1230 break;
1231 list_append(rettv->vval.v_list, li);
1232
1233 if (what == 0)
1234 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001235 // keys()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001236 li->li_tv.v_type = VAR_STRING;
1237 li->li_tv.v_lock = 0;
1238 li->li_tv.vval.v_string = vim_strsave(di->di_key);
1239 }
1240 else if (what == 1)
1241 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001242 // values()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001243 copy_tv(&di->di_tv, &li->li_tv);
1244 }
1245 else
1246 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001247 // items()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001248 l2 = list_alloc();
1249 li->li_tv.v_type = VAR_LIST;
1250 li->li_tv.v_lock = 0;
1251 li->li_tv.vval.v_list = l2;
1252 if (l2 == NULL)
1253 break;
1254 ++l2->lv_refcount;
1255
1256 li2 = listitem_alloc();
1257 if (li2 == NULL)
1258 break;
1259 list_append(l2, li2);
1260 li2->li_tv.v_type = VAR_STRING;
1261 li2->li_tv.v_lock = 0;
1262 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
1263
1264 li2 = listitem_alloc();
1265 if (li2 == NULL)
1266 break;
1267 list_append(l2, li2);
1268 copy_tv(&di->di_tv, &li2->li_tv);
1269 }
1270 }
1271 }
1272}
1273
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001274/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001275 * "items(dict)" function
1276 */
1277 void
1278f_items(typval_T *argvars, typval_T *rettv)
1279{
1280 dict_list(argvars, rettv, 2);
1281}
1282
1283/*
1284 * "keys()" function
1285 */
1286 void
1287f_keys(typval_T *argvars, typval_T *rettv)
1288{
1289 dict_list(argvars, rettv, 0);
1290}
1291
1292/*
1293 * "values(dict)" function
1294 */
1295 void
1296f_values(typval_T *argvars, typval_T *rettv)
1297{
1298 dict_list(argvars, rettv, 1);
1299}
1300
1301/*
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001302 * Make each item in the dict readonly (not the value of the item).
1303 */
1304 void
1305dict_set_items_ro(dict_T *di)
1306{
1307 int todo = (int)di->dv_hashtab.ht_used;
1308 hashitem_T *hi;
1309
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001310 // Set readonly
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001311 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
1312 {
1313 if (HASHITEM_EMPTY(hi))
1314 continue;
1315 --todo;
1316 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
1317 }
1318}
1319
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001320/*
1321 * "has_key()" function
1322 */
1323 void
1324f_has_key(typval_T *argvars, typval_T *rettv)
1325{
Yegappan Lakshmanan4490ec42021-07-27 22:00:44 +02001326 if (in_vim9script()
1327 && (check_for_dict_arg(argvars, 0) == FAIL
1328 || check_for_string_or_number_arg(argvars, 1) == FAIL))
1329 return;
1330
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001331 if (argvars[0].v_type != VAR_DICT)
1332 {
1333 emsg(_(e_dictreq));
1334 return;
1335 }
1336 if (argvars[0].vval.v_dict == NULL)
1337 return;
1338
1339 rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
1340 tv_get_string(&argvars[1]), -1) != NULL;
1341}
1342
1343/*
1344 * "remove({dict})" function
1345 */
1346 void
1347dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1348{
1349 dict_T *d;
1350 char_u *key;
1351 dictitem_T *di;
1352
1353 if (argvars[2].v_type != VAR_UNKNOWN)
1354 semsg(_(e_toomanyarg), "remove()");
1355 else if ((d = argvars[0].vval.v_dict) != NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02001356 && !value_check_lock(d->dv_lock, arg_errmsg, TRUE))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001357 {
1358 key = tv_get_string_chk(&argvars[1]);
1359 if (key != NULL)
1360 {
1361 di = dict_find(d, key, -1);
1362 if (di == NULL)
1363 semsg(_(e_dictkey), key);
1364 else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1365 && !var_check_ro(di->di_flags, arg_errmsg, TRUE))
1366 {
1367 *rettv = di->di_tv;
1368 init_tv(&di->di_tv);
1369 dictitem_remove(d, di);
1370 }
1371 }
1372 }
1373}
1374
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001375#endif // defined(FEAT_EVAL)