blob: 3b965d0ac222c67f1a287b93ee5cb51fd9242750 [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);
110}
111
112/*
113 * Clear hashtab "ht" and dict items it contains.
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100114 * If "ht" is not freed then you should call hash_init() next!
Bram Moolenaar89483d42020-05-10 15:24:44 +0200115 */
116 void
117hashtab_free_contents(hashtab_T *ht)
118{
Bram Moolenaarcd524592016-07-17 14:57:05 +0200119 int todo;
120 hashitem_T *hi;
121 dictitem_T *di;
122
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100123 // Lock the hashtab, we don't want it to resize while freeing items.
Bram Moolenaar89483d42020-05-10 15:24:44 +0200124 hash_lock(ht);
125 todo = (int)ht->ht_used;
126 for (hi = ht->ht_array; todo > 0; ++hi)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200127 {
128 if (!HASHITEM_EMPTY(hi))
129 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100130 // Remove the item before deleting it, just in case there is
131 // something recursive causing trouble.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200132 di = HI2DI(hi);
Bram Moolenaar89483d42020-05-10 15:24:44 +0200133 hash_remove(ht, hi);
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100134 dictitem_free(di);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200135 --todo;
136 }
137 }
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100138
Bram Moolenaar89483d42020-05-10 15:24:44 +0200139 // The hashtab is still locked, it has to be re-initialized anyway.
140 hash_clear(ht);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200141}
142
143 static void
144dict_free_dict(dict_T *d)
145{
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100146 // Remove the dict from the list of dicts for garbage collection.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200147 if (d->dv_used_prev == NULL)
148 first_dict = d->dv_used_next;
149 else
150 d->dv_used_prev->dv_used_next = d->dv_used_next;
151 if (d->dv_used_next != NULL)
152 d->dv_used_next->dv_used_prev = d->dv_used_prev;
153 vim_free(d);
154}
155
156 static void
157dict_free(dict_T *d)
158{
159 if (!in_free_unref_items)
160 {
161 dict_free_contents(d);
162 dict_free_dict(d);
163 }
164}
165
166/*
167 * Unreference a Dictionary: decrement the reference count and free it when it
168 * becomes zero.
169 */
170 void
171dict_unref(dict_T *d)
172{
173 if (d != NULL && --d->dv_refcount <= 0)
174 dict_free(d);
175}
176
177/*
178 * Go through the list of dicts and free items without the copyID.
179 * Returns TRUE if something was freed.
180 */
181 int
182dict_free_nonref(int copyID)
183{
184 dict_T *dd;
185 int did_free = FALSE;
186
187 for (dd = first_dict; dd != NULL; dd = dd->dv_used_next)
188 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
189 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100190 // Free the Dictionary and ordinary items it contains, but don't
191 // recurse into Lists and Dictionaries, they will be in the list
192 // of dicts or list of lists.
Bram Moolenaarcd524592016-07-17 14:57:05 +0200193 dict_free_contents(dd);
194 did_free = TRUE;
195 }
196 return did_free;
197}
198
199 void
200dict_free_items(int copyID)
201{
202 dict_T *dd, *dd_next;
203
204 for (dd = first_dict; dd != NULL; dd = dd_next)
205 {
206 dd_next = dd->dv_used_next;
207 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
208 dict_free_dict(dd);
209 }
210}
211
212/*
213 * Allocate a Dictionary item.
214 * The "key" is copied to the new item.
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200215 * Note that the type and value of the item "di_tv" still needs to be
216 * initialized!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200217 * Returns NULL when out of memory.
218 */
219 dictitem_T *
220dictitem_alloc(char_u *key)
221{
222 dictitem_T *di;
223
Bram Moolenaarb59e7352019-08-07 21:42:24 +0200224 di = alloc(offsetof(dictitem_T, di_key) + STRLEN(key) + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200225 if (di != NULL)
226 {
227 STRCPY(di->di_key, key);
228 di->di_flags = DI_FLAGS_ALLOC;
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200229 di->di_tv.v_lock = 0;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200230 }
231 return di;
232}
233
234/*
235 * Make a copy of a Dictionary item.
236 */
237 static dictitem_T *
238dictitem_copy(dictitem_T *org)
239{
240 dictitem_T *di;
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200241 size_t len = STRLEN(org->di_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200242
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200243 di = alloc(offsetof(dictitem_T, di_key) + len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200244 if (di != NULL)
245 {
Bram Moolenaar3f974ff2020-10-02 18:11:56 +0200246 mch_memmove(di->di_key, org->di_key, len + 1);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200247 di->di_flags = DI_FLAGS_ALLOC;
248 copy_tv(&org->di_tv, &di->di_tv);
249 }
250 return di;
251}
252
253/*
254 * Remove item "item" from Dictionary "dict" and free it.
255 */
256 void
257dictitem_remove(dict_T *dict, dictitem_T *item)
258{
259 hashitem_T *hi;
260
261 hi = hash_find(&dict->dv_hashtab, item->di_key);
262 if (HASHITEM_EMPTY(hi))
Bram Moolenaar95f09602016-11-10 20:01:45 +0100263 internal_error("dictitem_remove()");
Bram Moolenaarcd524592016-07-17 14:57:05 +0200264 else
265 hash_remove(&dict->dv_hashtab, hi);
266 dictitem_free(item);
267}
268
269/*
270 * Free a dict item. Also clears the value.
271 */
272 void
273dictitem_free(dictitem_T *item)
274{
275 clear_tv(&item->di_tv);
276 if (item->di_flags & DI_FLAGS_ALLOC)
277 vim_free(item);
278}
279
280/*
281 * Make a copy of dict "d". Shallow if "deep" is FALSE.
282 * The refcount of the new dict is set to 1.
283 * See item_copy() for "copyID".
284 * Returns NULL when out of memory.
285 */
286 dict_T *
287dict_copy(dict_T *orig, int deep, int copyID)
288{
289 dict_T *copy;
290 dictitem_T *di;
291 int todo;
292 hashitem_T *hi;
293
294 if (orig == NULL)
295 return NULL;
296
297 copy = dict_alloc();
298 if (copy != NULL)
299 {
300 if (copyID != 0)
301 {
302 orig->dv_copyID = copyID;
303 orig->dv_copydict = copy;
304 }
305 todo = (int)orig->dv_hashtab.ht_used;
306 for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
307 {
308 if (!HASHITEM_EMPTY(hi))
309 {
310 --todo;
311
312 di = dictitem_alloc(hi->hi_key);
313 if (di == NULL)
314 break;
315 if (deep)
316 {
317 if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep,
318 copyID) == FAIL)
319 {
320 vim_free(di);
321 break;
322 }
323 }
324 else
325 copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
326 if (dict_add(copy, di) == FAIL)
327 {
328 dictitem_free(di);
329 break;
330 }
331 }
332 }
333
334 ++copy->dv_refcount;
335 if (todo > 0)
336 {
337 dict_unref(copy);
338 copy = NULL;
339 }
340 }
341
342 return copy;
343}
344
345/*
346 * Add item "item" to Dictionary "d".
347 * Returns FAIL when out of memory and when key already exists.
348 */
349 int
350dict_add(dict_T *d, dictitem_T *item)
351{
352 return hash_add(&d->dv_hashtab, item->di_key);
353}
354
355/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200356 * Add a number or special entry to dictionary "d".
Bram Moolenaarcd524592016-07-17 14:57:05 +0200357 * Returns FAIL when out of memory and when key already exists.
358 */
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200359 static int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100360dict_add_number_special(dict_T *d, char *key, varnumber_T nr, vartype_T vartype)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200361{
362 dictitem_T *item;
363
364 item = dictitem_alloc((char_u *)key);
365 if (item == NULL)
366 return FAIL;
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100367 item->di_tv.v_type = vartype;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200368 item->di_tv.vval.v_number = nr;
369 if (dict_add(d, item) == FAIL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200370 {
Bram Moolenaare0be1672018-07-08 16:50:37 +0200371 dictitem_free(item);
372 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200373 }
Bram Moolenaare0be1672018-07-08 16:50:37 +0200374 return OK;
375}
376
377/*
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200378 * Add a number entry to dictionary "d".
379 * Returns FAIL when out of memory and when key already exists.
380 */
381 int
382dict_add_number(dict_T *d, char *key, varnumber_T nr)
383{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100384 return dict_add_number_special(d, key, nr, VAR_NUMBER);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200385}
386
387/*
388 * Add a special entry to dictionary "d".
389 * Returns FAIL when out of memory and when key already exists.
390 */
391 int
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100392dict_add_bool(dict_T *d, char *key, varnumber_T nr)
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200393{
Bram Moolenaar9b4a15d2020-01-11 16:05:23 +0100394 return dict_add_number_special(d, key, nr, VAR_BOOL);
Bram Moolenaard7f246c2019-04-08 18:15:41 +0200395}
396
397/*
Bram Moolenaare0be1672018-07-08 16:50:37 +0200398 * Add a string entry to dictionary "d".
399 * Returns FAIL when out of memory and when key already exists.
400 */
401 int
402dict_add_string(dict_T *d, char *key, char_u *str)
403{
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100404 return dict_add_string_len(d, key, str, -1);
405}
406
407/*
408 * Add a string entry to dictionary "d".
409 * "str" will be copied to allocated memory.
410 * When "len" is -1 use the whole string, otherwise only this many bytes.
411 * Returns FAIL when out of memory and when key already exists.
412 */
413 int
414dict_add_string_len(dict_T *d, char *key, char_u *str, int len)
415{
Bram Moolenaare0be1672018-07-08 16:50:37 +0200416 dictitem_T *item;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100417 char_u *val = NULL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200418
419 item = dictitem_alloc((char_u *)key);
420 if (item == NULL)
421 return FAIL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200422 item->di_tv.v_type = VAR_STRING;
Bram Moolenaare6fdf792018-12-26 22:57:42 +0100423 if (str != NULL)
424 {
425 if (len == -1)
426 val = vim_strsave(str);
427 else
428 val = vim_strnsave(str, len);
429 }
430 item->di_tv.vval.v_string = val;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200431 if (dict_add(d, item) == FAIL)
432 {
433 dictitem_free(item);
434 return FAIL;
435 }
436 return OK;
437}
438
439/*
440 * Add a list entry to dictionary "d".
441 * Returns FAIL when out of memory and when key already exists.
442 */
443 int
444dict_add_list(dict_T *d, char *key, list_T *list)
445{
446 dictitem_T *item;
447
448 item = dictitem_alloc((char_u *)key);
449 if (item == NULL)
450 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200451 item->di_tv.v_type = VAR_LIST;
452 item->di_tv.vval.v_list = list;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100453 ++list->lv_refcount;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200454 if (dict_add(d, item) == FAIL)
455 {
456 dictitem_free(item);
457 return FAIL;
458 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200459 return OK;
460}
461
462/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100463 * Add a typval_T entry to dictionary "d".
464 * Returns FAIL when out of memory and when key already exists.
465 */
466 int
467dict_add_tv(dict_T *d, char *key, typval_T *tv)
468{
469 dictitem_T *item;
470
471 item = dictitem_alloc((char_u *)key);
472 if (item == NULL)
473 return FAIL;
474 copy_tv(tv, &item->di_tv);
475 if (dict_add(d, item) == FAIL)
476 {
477 dictitem_free(item);
478 return FAIL;
479 }
480 return OK;
481}
482
483/*
Bram Moolenaarae943152019-06-16 22:54:14 +0200484 * Add a callback to dictionary "d".
485 * Returns FAIL when out of memory and when key already exists.
486 */
487 int
488dict_add_callback(dict_T *d, char *key, callback_T *cb)
489{
490 dictitem_T *item;
491
492 item = dictitem_alloc((char_u *)key);
493 if (item == NULL)
494 return FAIL;
495 put_callback(cb, &item->di_tv);
496 if (dict_add(d, item) == FAIL)
497 {
498 dictitem_free(item);
499 return FAIL;
500 }
501 return OK;
502}
503
504/*
Bram Moolenaar45e18cb2019-04-28 18:05:35 +0200505 * Initializes "iter" for iterating over dictionary items with
506 * dict_iterate_next().
507 * If "var" is not a Dict or an empty Dict then there will be nothing to
508 * iterate over, no error is given.
509 * NOTE: The dictionary must not change until iterating is finished!
510 */
511 void
512dict_iterate_start(typval_T *var, dict_iterator_T *iter)
513{
514 if (var->v_type != VAR_DICT || var->vval.v_dict == NULL)
515 iter->dit_todo = 0;
516 else
517 {
518 dict_T *d = var->vval.v_dict;
519
520 iter->dit_todo = d->dv_hashtab.ht_used;
521 iter->dit_hi = d->dv_hashtab.ht_array;
522 }
523}
524
525/*
526 * Iterate over the items referred to by "iter". It should be initialized with
527 * dict_iterate_start().
528 * Returns a pointer to the key.
529 * "*tv_result" is set to point to the value for that key.
530 * If there are no more items, NULL is returned.
531 */
532 char_u *
533dict_iterate_next(dict_iterator_T *iter, typval_T **tv_result)
534{
535 dictitem_T *di;
536 char_u *result;
537
538 if (iter->dit_todo == 0)
539 return NULL;
540
541 while (HASHITEM_EMPTY(iter->dit_hi))
542 ++iter->dit_hi;
543
544 di = HI2DI(iter->dit_hi);
545 result = di->di_key;
546 *tv_result = &di->di_tv;
547
548 --iter->dit_todo;
549 ++iter->dit_hi;
550 return result;
551}
552
553/*
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200554 * Add a dict entry to dictionary "d".
555 * Returns FAIL when out of memory and when key already exists.
556 */
557 int
558dict_add_dict(dict_T *d, char *key, dict_T *dict)
559{
560 dictitem_T *item;
561
562 item = dictitem_alloc((char_u *)key);
563 if (item == NULL)
564 return FAIL;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200565 item->di_tv.v_type = VAR_DICT;
566 item->di_tv.vval.v_dict = dict;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100567 ++dict->dv_refcount;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200568 if (dict_add(d, item) == FAIL)
569 {
570 dictitem_free(item);
571 return FAIL;
572 }
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200573 return OK;
574}
575
576/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200577 * Get the number of items in a Dictionary.
578 */
579 long
580dict_len(dict_T *d)
581{
582 if (d == NULL)
583 return 0L;
584 return (long)d->dv_hashtab.ht_used;
585}
586
587/*
588 * Find item "key[len]" in Dictionary "d".
589 * If "len" is negative use strlen(key).
590 * Returns NULL when not found.
591 */
592 dictitem_T *
593dict_find(dict_T *d, char_u *key, int len)
594{
595#define AKEYLEN 200
596 char_u buf[AKEYLEN];
597 char_u *akey;
598 char_u *tofree = NULL;
599 hashitem_T *hi;
600
601 if (d == NULL)
602 return NULL;
603 if (len < 0)
604 akey = key;
605 else if (len >= AKEYLEN)
606 {
607 tofree = akey = vim_strnsave(key, len);
608 if (akey == NULL)
609 return NULL;
610 }
611 else
612 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100613 // Avoid a malloc/free by using buf[].
Bram Moolenaarcd524592016-07-17 14:57:05 +0200614 vim_strncpy(buf, key, len);
615 akey = buf;
616 }
617
618 hi = hash_find(&d->dv_hashtab, akey);
619 vim_free(tofree);
620 if (HASHITEM_EMPTY(hi))
621 return NULL;
622 return HI2DI(hi);
623}
624
625/*
Bram Moolenaar08928322020-01-04 14:32:48 +0100626 * Get a typval_T item from a dictionary and copy it into "rettv".
627 * Returns FAIL if the entry doesn't exist or out of memory.
628 */
629 int
630dict_get_tv(dict_T *d, char_u *key, typval_T *rettv)
631{
632 dictitem_T *di;
Bram Moolenaar08928322020-01-04 14:32:48 +0100633
634 di = dict_find(d, key, -1);
635 if (di == NULL)
636 return FAIL;
637 copy_tv(&di->di_tv, rettv);
638 return OK;
639}
640
641/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200642 * Get a string item from a dictionary.
643 * When "save" is TRUE allocate memory for it.
Bram Moolenaar7dc5e2e2016-08-05 22:22:06 +0200644 * When FALSE a shared buffer is used, can only be used once!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200645 * Returns NULL if the entry doesn't exist or out of memory.
646 */
647 char_u *
Bram Moolenaar8f667172018-12-14 15:38:31 +0100648dict_get_string(dict_T *d, char_u *key, int save)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200649{
650 dictitem_T *di;
651 char_u *s;
652
653 di = dict_find(d, key, -1);
654 if (di == NULL)
655 return NULL;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100656 s = tv_get_string(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200657 if (save && s != NULL)
658 s = vim_strsave(s);
659 return s;
660}
661
662/*
663 * Get a number item from a dictionary.
664 * Returns 0 if the entry doesn't exist.
665 */
666 varnumber_T
Bram Moolenaar8f667172018-12-14 15:38:31 +0100667dict_get_number(dict_T *d, char_u *key)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200668{
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200669 return dict_get_number_def(d, key, 0);
670}
671
672/*
673 * Get a number item from a dictionary.
674 * Returns "def" if the entry doesn't exist.
675 */
676 varnumber_T
677dict_get_number_def(dict_T *d, char_u *key, int def)
678{
Bram Moolenaarcd524592016-07-17 14:57:05 +0200679 dictitem_T *di;
680
681 di = dict_find(d, key, -1);
682 if (di == NULL)
Bram Moolenaar8c6173c2019-08-30 22:08:34 +0200683 return def;
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100684 return tv_get_number(&di->di_tv);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200685}
686
687/*
Bram Moolenaarb0ebbda2019-06-02 16:51:21 +0200688 * Get a number item from a dictionary.
689 * Returns 0 if the entry doesn't exist.
690 * Give an error if the entry is not a number.
691 */
692 varnumber_T
693dict_get_number_check(dict_T *d, char_u *key)
694{
695 dictitem_T *di;
696
697 di = dict_find(d, key, -1);
698 if (di == NULL)
699 return 0;
700 if (di->di_tv.v_type != VAR_NUMBER)
701 {
702 semsg(_(e_invarg2), tv_get_string(&di->di_tv));
703 return 0;
704 }
705 return tv_get_number(&di->di_tv);
706}
707
708/*
Bram Moolenaar55881332020-08-18 13:04:15 +0200709 * Get a bool item (number or true/false) from a dictionary.
710 * Returns "def" if the entry doesn't exist.
711 */
712 varnumber_T
713dict_get_bool(dict_T *d, char_u *key, int def)
714{
715 dictitem_T *di;
716
717 di = dict_find(d, key, -1);
718 if (di == NULL)
719 return def;
720 return tv_get_bool(&di->di_tv);
721}
722
723/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200724 * Return an allocated string with the string representation of a Dictionary.
725 * May return NULL.
726 */
727 char_u *
728dict2string(typval_T *tv, int copyID, int restore_copyID)
729{
730 garray_T ga;
731 int first = TRUE;
732 char_u *tofree;
733 char_u numbuf[NUMBUFLEN];
734 hashitem_T *hi;
735 char_u *s;
736 dict_T *d;
737 int todo;
738
739 if ((d = tv->vval.v_dict) == NULL)
740 return NULL;
741 ga_init2(&ga, (int)sizeof(char), 80);
742 ga_append(&ga, '{');
743
744 todo = (int)d->dv_hashtab.ht_used;
745 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
746 {
747 if (!HASHITEM_EMPTY(hi))
748 {
749 --todo;
750
751 if (first)
752 first = FALSE;
753 else
754 ga_concat(&ga, (char_u *)", ");
755
756 tofree = string_quote(hi->hi_key, FALSE);
757 if (tofree != NULL)
758 {
759 ga_concat(&ga, tofree);
760 vim_free(tofree);
761 }
762 ga_concat(&ga, (char_u *)": ");
763 s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID,
764 FALSE, restore_copyID, TRUE);
765 if (s != NULL)
766 ga_concat(&ga, s);
767 vim_free(tofree);
768 if (s == NULL || did_echo_string_emsg)
769 break;
770 line_breakcheck();
771
772 }
773 }
774 if (todo > 0)
775 {
776 vim_free(ga.ga_data);
777 return NULL;
778 }
779
780 ga_append(&ga, '}');
781 ga_append(&ga, NUL);
782 return (char_u *)ga.ga_data;
783}
784
785/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200786 * Get the key for #{key: val} into "tv" and advance "arg".
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200787 * Return FAIL when there is no valid key.
788 */
789 static int
790get_literal_key(char_u **arg, typval_T *tv)
791{
792 char_u *p;
793
794 if (!ASCII_ISALNUM(**arg) && **arg != '_' && **arg != '-')
795 return FAIL;
796
797 for (p = *arg; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; ++p)
798 ;
799 tv->v_type = VAR_STRING;
Bram Moolenaardf44a272020-06-07 20:49:05 +0200800 tv->vval.v_string = vim_strnsave(*arg, p - *arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200801
Bram Moolenaardb199212020-08-12 18:01:53 +0200802 *arg = p;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200803 return OK;
804}
805
806/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200807 * Allocate a variable for a Dictionary and fill it from "*arg".
Bram Moolenaar962d7212020-07-04 14:15:00 +0200808 * "*arg" points to the "{".
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200809 * "literal" is TRUE for #{key: val}
Bram Moolenaarcd524592016-07-17 14:57:05 +0200810 * Return OK or FAIL. Returns NOTDONE for {expr}.
811 */
812 int
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200813eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200814{
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200815 int evaluate = evalarg == NULL ? FALSE
816 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200817 dict_T *d = NULL;
818 typval_T tvkey;
819 typval_T tv;
820 char_u *key = NULL;
821 dictitem_T *item;
822 char_u *start = skipwhite(*arg + 1);
823 char_u buf[NUMBUFLEN];
Bram Moolenaareb6880b2020-07-12 17:07:05 +0200824 int vim9script = in_vim9script();
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200825 int had_comma;
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200826
Bram Moolenaarcd524592016-07-17 14:57:05 +0200827 /*
828 * First check if it's not a curly-braces thing: {expr}.
829 * Must do this without evaluating, otherwise a function may be called
830 * twice. Unfortunately this means we need to call eval1() twice for the
831 * first item.
832 * But {} is an empty Dictionary.
833 */
Bram Moolenaar32e35112020-05-14 22:41:15 +0200834 if (!vim9script && *start != '}')
Bram Moolenaarcd524592016-07-17 14:57:05 +0200835 {
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200836 if (eval1(&start, &tv, NULL) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200837 return FAIL;
Bram Moolenaarae95a392020-08-06 16:38:12 +0200838 if (*skipwhite(start) == '}')
Bram Moolenaarcd524592016-07-17 14:57:05 +0200839 return NOTDONE;
840 }
841
842 if (evaluate)
843 {
844 d = dict_alloc();
845 if (d == NULL)
846 return FAIL;
847 }
848 tvkey.v_type = VAR_UNKNOWN;
849 tv.v_type = VAR_UNKNOWN;
850
Bram Moolenaar962d7212020-07-04 14:15:00 +0200851 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200852 while (**arg != '}' && **arg != NUL)
853 {
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100854 char_u *p = to_name_end(*arg, FALSE);
855
856 if (literal || (vim9script && *p == ':'))
857 {
858 if (get_literal_key(arg, &tvkey) == FAIL)
859 goto failret;
860 }
861 else
862 {
863 int has_bracket = vim9script && **arg == '[';
864
865 if (has_bracket)
866 *arg = skipwhite(*arg + 1);
867 if (eval1(arg, &tvkey, evalarg) == FAIL) // recursive!
868 goto failret;
869 if (has_bracket)
870 {
871 *arg = skipwhite(*arg);
872 if (**arg != ']')
873 {
874 emsg(_(e_missing_matching_bracket_after_dict_key));
875 return FAIL;
876 }
877 ++*arg;
878 }
879 }
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200880
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200881 // the colon should come right after the key, but this wasn't checked
Bram Moolenaar9d489562020-07-30 20:08:50 +0200882 // previously, so only require it in Vim9 script.
883 if (!vim9script)
884 *arg = skipwhite(*arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200885 if (**arg != ':')
886 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200887 if (*skipwhite(*arg) == ':')
888 semsg(_(e_no_white_space_allowed_before_str), ":");
889 else
890 semsg(_(e_missing_dict_colon), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200891 clear_tv(&tvkey);
892 goto failret;
893 }
894 if (evaluate)
895 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100896 key = tv_get_string_buf_chk(&tvkey, buf);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200897 if (key == NULL)
898 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100899 // "key" is NULL when tv_get_string_buf_chk() gave an errmsg
Bram Moolenaarcd524592016-07-17 14:57:05 +0200900 clear_tv(&tvkey);
901 goto failret;
902 }
903 }
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200904 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
905 {
Bram Moolenaar7cb6fc22020-08-21 22:36:47 +0200906 semsg(_(e_white_space_required_after_str), ":");
Bram Moolenaarab19d492020-06-27 17:04:05 +0200907 clear_tv(&tvkey);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200908 goto failret;
909 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200910
Bram Moolenaar962d7212020-07-04 14:15:00 +0200911 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200912 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200913 {
914 if (evaluate)
915 clear_tv(&tvkey);
916 goto failret;
917 }
918 if (evaluate)
919 {
920 item = dict_find(d, key, -1);
921 if (item != NULL)
922 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200923 semsg(_(e_duplicate_key), key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200924 clear_tv(&tvkey);
925 clear_tv(&tv);
926 goto failret;
927 }
928 item = dictitem_alloc(key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200929 if (item != NULL)
930 {
931 item->di_tv = tv;
932 item->di_tv.v_lock = 0;
933 if (dict_add(d, item) == FAIL)
934 dictitem_free(item);
935 }
936 }
Bram Moolenaara8931942019-09-28 17:25:10 +0200937 clear_tv(&tvkey);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200938
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200939 // the comma should come right after the value, but this wasn't checked
940 // previously, so only require it in Vim9 script.
941 if (!vim9script)
942 *arg = skipwhite(*arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200943 had_comma = **arg == ',';
944 if (had_comma)
945 {
946 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
947 {
Bram Moolenaar7cb6fc22020-08-21 22:36:47 +0200948 semsg(_(e_white_space_required_after_str), ",");
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200949 goto failret;
950 }
951 *arg = skipwhite(*arg + 1);
952 }
953
954 // the "}" can be on the next line
Bram Moolenaar962d7212020-07-04 14:15:00 +0200955 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200956 if (**arg == '}')
957 break;
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200958 if (!had_comma)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200959 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200960 if (**arg == ',')
961 semsg(_(e_no_white_space_allowed_before_str), ",");
962 else
963 semsg(_(e_missing_dict_comma), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200964 goto failret;
965 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200966 }
967
968 if (**arg != '}')
969 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200970 semsg(_(e_missing_dict_end), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200971failret:
Bram Moolenaaradc67142019-06-22 01:40:42 +0200972 if (d != NULL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200973 dict_free(d);
974 return FAIL;
975 }
976
Bram Moolenaarb07a39d2020-10-22 19:00:01 +0200977 *arg = *arg + 1;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200978 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200979 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200980
981 return OK;
982}
983
984/*
985 * Go over all entries in "d2" and add them to "d1".
986 * When "action" is "error" then a duplicate key is an error.
987 * When "action" is "force" then a duplicate key is overwritten.
988 * Otherwise duplicate keys are ignored ("action" is "keep").
989 */
990 void
991dict_extend(dict_T *d1, dict_T *d2, char_u *action)
992{
993 dictitem_T *di1;
994 hashitem_T *hi2;
995 int todo;
996 char_u *arg_errmsg = (char_u *)N_("extend() argument");
997
998 todo = (int)d2->dv_hashtab.ht_used;
999 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
1000 {
1001 if (!HASHITEM_EMPTY(hi2))
1002 {
1003 --todo;
1004 di1 = dict_find(d1, hi2->hi_key, -1);
1005 if (d1->dv_scope != 0)
1006 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001007 // Disallow replacing a builtin function in l: and g:.
1008 // Check the key to be valid when adding to any scope.
Bram Moolenaarcd524592016-07-17 14:57:05 +02001009 if (d1->dv_scope == VAR_DEF_SCOPE
1010 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
Bram Moolenaar98b4f142020-08-08 15:46:01 +02001011 && var_wrong_func_name(hi2->hi_key, di1 == NULL))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001012 break;
1013 if (!valid_varname(hi2->hi_key))
1014 break;
1015 }
1016 if (di1 == NULL)
1017 {
1018 di1 = dictitem_copy(HI2DI(hi2));
1019 if (di1 != NULL && dict_add(d1, di1) == FAIL)
1020 dictitem_free(di1);
1021 }
1022 else if (*action == 'e')
1023 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001024 semsg(_("E737: Key already exists: %s"), hi2->hi_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001025 break;
1026 }
1027 else if (*action == 'f' && HI2DI(hi2) != di1)
1028 {
Bram Moolenaara187c432020-09-16 21:08:28 +02001029 if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
Bram Moolenaar05c00c02019-02-11 22:00:11 +01001030 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001031 break;
1032 clear_tv(&di1->di_tv);
1033 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
1034 }
1035 }
1036 }
1037}
1038
1039/*
1040 * Return the dictitem that an entry in a hashtable points to.
1041 */
1042 dictitem_T *
1043dict_lookup(hashitem_T *hi)
1044{
1045 return HI2DI(hi);
1046}
1047
1048/*
1049 * Return TRUE when two dictionaries have exactly the same key/values.
1050 */
1051 int
1052dict_equal(
1053 dict_T *d1,
1054 dict_T *d2,
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001055 int ic, // ignore case for strings
1056 int recursive) // TRUE when used recursively
Bram Moolenaarcd524592016-07-17 14:57:05 +02001057{
1058 hashitem_T *hi;
1059 dictitem_T *item2;
1060 int todo;
1061
Bram Moolenaarcd524592016-07-17 14:57:05 +02001062 if (d1 == d2)
1063 return TRUE;
1064 if (dict_len(d1) != dict_len(d2))
1065 return FALSE;
Bram Moolenaarea04a6e2020-04-23 13:38:02 +02001066 if (dict_len(d1) == 0)
1067 // empty and NULL dicts are considered equal
1068 return TRUE;
1069 if (d1 == NULL || d2 == NULL)
1070 return FALSE;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001071
1072 todo = (int)d1->dv_hashtab.ht_used;
1073 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
1074 {
1075 if (!HASHITEM_EMPTY(hi))
1076 {
1077 item2 = dict_find(d2, hi->hi_key, -1);
1078 if (item2 == NULL)
1079 return FALSE;
1080 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
1081 return FALSE;
1082 --todo;
1083 }
1084 }
1085 return TRUE;
1086}
1087
1088/*
1089 * Turn a dict into a list:
1090 * "what" == 0: list of keys
1091 * "what" == 1: list of values
1092 * "what" == 2: list of items
1093 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001094 static void
Bram Moolenaarcd524592016-07-17 14:57:05 +02001095dict_list(typval_T *argvars, typval_T *rettv, int what)
1096{
1097 list_T *l2;
1098 dictitem_T *di;
1099 hashitem_T *hi;
1100 listitem_T *li;
1101 listitem_T *li2;
1102 dict_T *d;
1103 int todo;
1104
1105 if (argvars[0].v_type != VAR_DICT)
1106 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001107 emsg(_(e_dictreq));
Bram Moolenaarcd524592016-07-17 14:57:05 +02001108 return;
1109 }
1110 if ((d = argvars[0].vval.v_dict) == NULL)
1111 return;
1112
1113 if (rettv_list_alloc(rettv) == FAIL)
1114 return;
1115
1116 todo = (int)d->dv_hashtab.ht_used;
1117 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1118 {
1119 if (!HASHITEM_EMPTY(hi))
1120 {
1121 --todo;
1122 di = HI2DI(hi);
1123
1124 li = listitem_alloc();
1125 if (li == NULL)
1126 break;
1127 list_append(rettv->vval.v_list, li);
1128
1129 if (what == 0)
1130 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001131 // keys()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001132 li->li_tv.v_type = VAR_STRING;
1133 li->li_tv.v_lock = 0;
1134 li->li_tv.vval.v_string = vim_strsave(di->di_key);
1135 }
1136 else if (what == 1)
1137 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001138 // values()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001139 copy_tv(&di->di_tv, &li->li_tv);
1140 }
1141 else
1142 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001143 // items()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001144 l2 = list_alloc();
1145 li->li_tv.v_type = VAR_LIST;
1146 li->li_tv.v_lock = 0;
1147 li->li_tv.vval.v_list = l2;
1148 if (l2 == NULL)
1149 break;
1150 ++l2->lv_refcount;
1151
1152 li2 = listitem_alloc();
1153 if (li2 == NULL)
1154 break;
1155 list_append(l2, li2);
1156 li2->li_tv.v_type = VAR_STRING;
1157 li2->li_tv.v_lock = 0;
1158 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
1159
1160 li2 = listitem_alloc();
1161 if (li2 == NULL)
1162 break;
1163 list_append(l2, li2);
1164 copy_tv(&di->di_tv, &li2->li_tv);
1165 }
1166 }
1167 }
1168}
1169
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001170/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001171 * "items(dict)" function
1172 */
1173 void
1174f_items(typval_T *argvars, typval_T *rettv)
1175{
1176 dict_list(argvars, rettv, 2);
1177}
1178
1179/*
1180 * "keys()" function
1181 */
1182 void
1183f_keys(typval_T *argvars, typval_T *rettv)
1184{
1185 dict_list(argvars, rettv, 0);
1186}
1187
1188/*
1189 * "values(dict)" function
1190 */
1191 void
1192f_values(typval_T *argvars, typval_T *rettv)
1193{
1194 dict_list(argvars, rettv, 1);
1195}
1196
1197/*
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001198 * Make each item in the dict readonly (not the value of the item).
1199 */
1200 void
1201dict_set_items_ro(dict_T *di)
1202{
1203 int todo = (int)di->dv_hashtab.ht_used;
1204 hashitem_T *hi;
1205
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001206 // Set readonly
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001207 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
1208 {
1209 if (HASHITEM_EMPTY(hi))
1210 continue;
1211 --todo;
1212 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
1213 }
1214}
1215
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001216/*
1217 * "has_key()" function
1218 */
1219 void
1220f_has_key(typval_T *argvars, typval_T *rettv)
1221{
1222 if (argvars[0].v_type != VAR_DICT)
1223 {
1224 emsg(_(e_dictreq));
1225 return;
1226 }
1227 if (argvars[0].vval.v_dict == NULL)
1228 return;
1229
1230 rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
1231 tv_get_string(&argvars[1]), -1) != NULL;
1232}
1233
1234/*
1235 * "remove({dict})" function
1236 */
1237 void
1238dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1239{
1240 dict_T *d;
1241 char_u *key;
1242 dictitem_T *di;
1243
1244 if (argvars[2].v_type != VAR_UNKNOWN)
1245 semsg(_(e_toomanyarg), "remove()");
1246 else if ((d = argvars[0].vval.v_dict) != NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02001247 && !value_check_lock(d->dv_lock, arg_errmsg, TRUE))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001248 {
1249 key = tv_get_string_chk(&argvars[1]);
1250 if (key != NULL)
1251 {
1252 di = dict_find(d, key, -1);
1253 if (di == NULL)
1254 semsg(_(e_dictkey), key);
1255 else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1256 && !var_check_ro(di->di_flags, arg_errmsg, TRUE))
1257 {
1258 *rettv = di->di_tv;
1259 init_tv(&di->di_tv);
1260 dictitem_remove(d, di);
1261 }
1262 }
1263 }
1264}
1265
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001266#endif // defined(FEAT_EVAL)