blob: 819f5fa0ba41b675c25c9b72e27475281ae87e43 [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 Moolenaare0de1712020-12-02 17:36:54 +0100786 * Advance over a literal key, including "-". If the first character is not a
787 * literal key character then "key" is returned.
788 */
789 char_u *
790skip_literal_key(char_u *key)
791{
792 char_u *p;
793
794 for (p = key; ASCII_ISALNUM(*p) || *p == '_' || *p == '-'; ++p)
795 ;
796 return p;
797}
798
799/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200800 * Get the key for #{key: val} into "tv" and advance "arg".
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200801 * Return FAIL when there is no valid key.
802 */
803 static int
804get_literal_key(char_u **arg, typval_T *tv)
805{
Bram Moolenaare0de1712020-12-02 17:36:54 +0100806 char_u *p = skip_literal_key(*arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200807
Bram Moolenaare0de1712020-12-02 17:36:54 +0100808 if (p == *arg)
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200809 return FAIL;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200810 tv->v_type = VAR_STRING;
Bram Moolenaardf44a272020-06-07 20:49:05 +0200811 tv->vval.v_string = vim_strnsave(*arg, p - *arg);
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200812
Bram Moolenaardb199212020-08-12 18:01:53 +0200813 *arg = p;
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200814 return OK;
815}
816
817/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200818 * Allocate a variable for a Dictionary and fill it from "*arg".
Bram Moolenaar962d7212020-07-04 14:15:00 +0200819 * "*arg" points to the "{".
Bram Moolenaar9f9fe372019-07-27 23:12:12 +0200820 * "literal" is TRUE for #{key: val}
Bram Moolenaarcd524592016-07-17 14:57:05 +0200821 * Return OK or FAIL. Returns NOTDONE for {expr}.
822 */
823 int
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200824eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200825{
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200826 int evaluate = evalarg == NULL ? FALSE
827 : evalarg->eval_flags & EVAL_EVALUATE;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200828 dict_T *d = NULL;
829 typval_T tvkey;
830 typval_T tv;
831 char_u *key = NULL;
832 dictitem_T *item;
833 char_u *start = skipwhite(*arg + 1);
834 char_u buf[NUMBUFLEN];
Bram Moolenaareb6880b2020-07-12 17:07:05 +0200835 int vim9script = in_vim9script();
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200836 int had_comma;
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200837
Bram Moolenaarcd524592016-07-17 14:57:05 +0200838 /*
839 * First check if it's not a curly-braces thing: {expr}.
840 * Must do this without evaluating, otherwise a function may be called
841 * twice. Unfortunately this means we need to call eval1() twice for the
842 * first item.
843 * But {} is an empty Dictionary.
844 */
Bram Moolenaar32e35112020-05-14 22:41:15 +0200845 if (!vim9script && *start != '}')
Bram Moolenaarcd524592016-07-17 14:57:05 +0200846 {
Bram Moolenaar5409f5d2020-06-24 18:37:35 +0200847 if (eval1(&start, &tv, NULL) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200848 return FAIL;
Bram Moolenaarae95a392020-08-06 16:38:12 +0200849 if (*skipwhite(start) == '}')
Bram Moolenaarcd524592016-07-17 14:57:05 +0200850 return NOTDONE;
851 }
852
853 if (evaluate)
854 {
855 d = dict_alloc();
856 if (d == NULL)
857 return FAIL;
858 }
859 tvkey.v_type = VAR_UNKNOWN;
860 tv.v_type = VAR_UNKNOWN;
861
Bram Moolenaar962d7212020-07-04 14:15:00 +0200862 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200863 while (**arg != '}' && **arg != NUL)
864 {
Bram Moolenaare0de1712020-12-02 17:36:54 +0100865 int has_bracket = vim9script && **arg == '[';
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100866
Bram Moolenaare0de1712020-12-02 17:36:54 +0100867 if (literal || (vim9script && !has_bracket))
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100868 {
869 if (get_literal_key(arg, &tvkey) == FAIL)
870 goto failret;
871 }
872 else
873 {
Bram Moolenaar67d1c682020-11-19 19:01:43 +0100874 if (has_bracket)
875 *arg = skipwhite(*arg + 1);
876 if (eval1(arg, &tvkey, evalarg) == FAIL) // recursive!
877 goto failret;
878 if (has_bracket)
879 {
880 *arg = skipwhite(*arg);
881 if (**arg != ']')
882 {
883 emsg(_(e_missing_matching_bracket_after_dict_key));
884 return FAIL;
885 }
886 ++*arg;
887 }
888 }
Bram Moolenaard5abb4c2019-07-13 22:46:10 +0200889
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200890 // the colon should come right after the key, but this wasn't checked
Bram Moolenaar9d489562020-07-30 20:08:50 +0200891 // previously, so only require it in Vim9 script.
892 if (!vim9script)
893 *arg = skipwhite(*arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200894 if (**arg != ':')
895 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200896 if (*skipwhite(*arg) == ':')
897 semsg(_(e_no_white_space_allowed_before_str), ":");
898 else
899 semsg(_(e_missing_dict_colon), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200900 clear_tv(&tvkey);
901 goto failret;
902 }
903 if (evaluate)
904 {
Bram Moolenaard155d7a2018-12-21 16:04:21 +0100905 key = tv_get_string_buf_chk(&tvkey, buf);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200906 if (key == NULL)
907 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +0100908 // "key" is NULL when tv_get_string_buf_chk() gave an errmsg
Bram Moolenaarcd524592016-07-17 14:57:05 +0200909 clear_tv(&tvkey);
910 goto failret;
911 }
912 }
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200913 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
914 {
Bram Moolenaar7cb6fc22020-08-21 22:36:47 +0200915 semsg(_(e_white_space_required_after_str), ":");
Bram Moolenaarab19d492020-06-27 17:04:05 +0200916 clear_tv(&tvkey);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200917 goto failret;
918 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200919
Bram Moolenaar962d7212020-07-04 14:15:00 +0200920 *arg = skipwhite_and_linebreak(*arg + 1, evalarg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200921 if (eval1(arg, &tv, evalarg) == FAIL) // recursive!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200922 {
923 if (evaluate)
924 clear_tv(&tvkey);
925 goto failret;
926 }
927 if (evaluate)
928 {
929 item = dict_find(d, key, -1);
930 if (item != NULL)
931 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200932 semsg(_(e_duplicate_key), key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200933 clear_tv(&tvkey);
934 clear_tv(&tv);
935 goto failret;
936 }
937 item = dictitem_alloc(key);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200938 if (item != NULL)
939 {
940 item->di_tv = tv;
941 item->di_tv.v_lock = 0;
942 if (dict_add(d, item) == FAIL)
943 dictitem_free(item);
944 }
945 }
Bram Moolenaara8931942019-09-28 17:25:10 +0200946 clear_tv(&tvkey);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200947
Bram Moolenaar4d4d1cd2020-07-30 22:14:33 +0200948 // the comma should come right after the value, but this wasn't checked
949 // previously, so only require it in Vim9 script.
950 if (!vim9script)
951 *arg = skipwhite(*arg);
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200952 had_comma = **arg == ',';
953 if (had_comma)
954 {
955 if (vim9script && (*arg)[1] != NUL && !VIM_ISWHITE((*arg)[1]))
956 {
Bram Moolenaar7cb6fc22020-08-21 22:36:47 +0200957 semsg(_(e_white_space_required_after_str), ",");
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200958 goto failret;
959 }
960 *arg = skipwhite(*arg + 1);
961 }
962
963 // the "}" can be on the next line
Bram Moolenaar962d7212020-07-04 14:15:00 +0200964 *arg = skipwhite_and_linebreak(*arg, evalarg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200965 if (**arg == '}')
966 break;
Bram Moolenaar8ea93902020-06-27 14:11:53 +0200967 if (!had_comma)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200968 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200969 if (**arg == ',')
970 semsg(_(e_no_white_space_allowed_before_str), ",");
971 else
972 semsg(_(e_missing_dict_comma), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200973 goto failret;
974 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200975 }
976
977 if (**arg != '}')
978 {
Bram Moolenaara2c026d2020-10-18 18:03:17 +0200979 semsg(_(e_missing_dict_end), *arg);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200980failret:
Bram Moolenaaradc67142019-06-22 01:40:42 +0200981 if (d != NULL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200982 dict_free(d);
983 return FAIL;
984 }
985
Bram Moolenaarb07a39d2020-10-22 19:00:01 +0200986 *arg = *arg + 1;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200987 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200988 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200989
990 return OK;
991}
992
993/*
994 * Go over all entries in "d2" and add them to "d1".
995 * When "action" is "error" then a duplicate key is an error.
996 * When "action" is "force" then a duplicate key is overwritten.
997 * Otherwise duplicate keys are ignored ("action" is "keep").
998 */
999 void
1000dict_extend(dict_T *d1, dict_T *d2, char_u *action)
1001{
1002 dictitem_T *di1;
1003 hashitem_T *hi2;
1004 int todo;
1005 char_u *arg_errmsg = (char_u *)N_("extend() argument");
1006
1007 todo = (int)d2->dv_hashtab.ht_used;
1008 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
1009 {
1010 if (!HASHITEM_EMPTY(hi2))
1011 {
1012 --todo;
1013 di1 = dict_find(d1, hi2->hi_key, -1);
1014 if (d1->dv_scope != 0)
1015 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001016 // Disallow replacing a builtin function in l: and g:.
1017 // Check the key to be valid when adding to any scope.
Bram Moolenaarcd524592016-07-17 14:57:05 +02001018 if (d1->dv_scope == VAR_DEF_SCOPE
1019 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
Bram Moolenaar98b4f142020-08-08 15:46:01 +02001020 && var_wrong_func_name(hi2->hi_key, di1 == NULL))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001021 break;
1022 if (!valid_varname(hi2->hi_key))
1023 break;
1024 }
1025 if (di1 == NULL)
1026 {
1027 di1 = dictitem_copy(HI2DI(hi2));
1028 if (di1 != NULL && dict_add(d1, di1) == FAIL)
1029 dictitem_free(di1);
1030 }
1031 else if (*action == 'e')
1032 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001033 semsg(_("E737: Key already exists: %s"), hi2->hi_key);
Bram Moolenaarcd524592016-07-17 14:57:05 +02001034 break;
1035 }
1036 else if (*action == 'f' && HI2DI(hi2) != di1)
1037 {
Bram Moolenaara187c432020-09-16 21:08:28 +02001038 if (value_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
Bram Moolenaar05c00c02019-02-11 22:00:11 +01001039 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
Bram Moolenaarcd524592016-07-17 14:57:05 +02001040 break;
1041 clear_tv(&di1->di_tv);
1042 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
1043 }
1044 }
1045 }
1046}
1047
1048/*
1049 * Return the dictitem that an entry in a hashtable points to.
1050 */
1051 dictitem_T *
1052dict_lookup(hashitem_T *hi)
1053{
1054 return HI2DI(hi);
1055}
1056
1057/*
1058 * Return TRUE when two dictionaries have exactly the same key/values.
1059 */
1060 int
1061dict_equal(
1062 dict_T *d1,
1063 dict_T *d2,
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001064 int ic, // ignore case for strings
1065 int recursive) // TRUE when used recursively
Bram Moolenaarcd524592016-07-17 14:57:05 +02001066{
1067 hashitem_T *hi;
1068 dictitem_T *item2;
1069 int todo;
1070
Bram Moolenaarcd524592016-07-17 14:57:05 +02001071 if (d1 == d2)
1072 return TRUE;
1073 if (dict_len(d1) != dict_len(d2))
1074 return FALSE;
Bram Moolenaarea04a6e2020-04-23 13:38:02 +02001075 if (dict_len(d1) == 0)
1076 // empty and NULL dicts are considered equal
1077 return TRUE;
1078 if (d1 == NULL || d2 == NULL)
1079 return FALSE;
Bram Moolenaarcd524592016-07-17 14:57:05 +02001080
1081 todo = (int)d1->dv_hashtab.ht_used;
1082 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
1083 {
1084 if (!HASHITEM_EMPTY(hi))
1085 {
1086 item2 = dict_find(d2, hi->hi_key, -1);
1087 if (item2 == NULL)
1088 return FALSE;
1089 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
1090 return FALSE;
1091 --todo;
1092 }
1093 }
1094 return TRUE;
1095}
1096
1097/*
1098 * Turn a dict into a list:
1099 * "what" == 0: list of keys
1100 * "what" == 1: list of values
1101 * "what" == 2: list of items
1102 */
Bram Moolenaar5843f5f2019-08-20 20:13:45 +02001103 static void
Bram Moolenaarcd524592016-07-17 14:57:05 +02001104dict_list(typval_T *argvars, typval_T *rettv, int what)
1105{
1106 list_T *l2;
1107 dictitem_T *di;
1108 hashitem_T *hi;
1109 listitem_T *li;
1110 listitem_T *li2;
1111 dict_T *d;
1112 int todo;
1113
1114 if (argvars[0].v_type != VAR_DICT)
1115 {
Bram Moolenaarf9e3e092019-01-13 23:38:42 +01001116 emsg(_(e_dictreq));
Bram Moolenaarcd524592016-07-17 14:57:05 +02001117 return;
1118 }
1119 if ((d = argvars[0].vval.v_dict) == NULL)
1120 return;
1121
1122 if (rettv_list_alloc(rettv) == FAIL)
1123 return;
1124
1125 todo = (int)d->dv_hashtab.ht_used;
1126 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
1127 {
1128 if (!HASHITEM_EMPTY(hi))
1129 {
1130 --todo;
1131 di = HI2DI(hi);
1132
1133 li = listitem_alloc();
1134 if (li == NULL)
1135 break;
1136 list_append(rettv->vval.v_list, li);
1137
1138 if (what == 0)
1139 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001140 // keys()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001141 li->li_tv.v_type = VAR_STRING;
1142 li->li_tv.v_lock = 0;
1143 li->li_tv.vval.v_string = vim_strsave(di->di_key);
1144 }
1145 else if (what == 1)
1146 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001147 // values()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001148 copy_tv(&di->di_tv, &li->li_tv);
1149 }
1150 else
1151 {
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001152 // items()
Bram Moolenaarcd524592016-07-17 14:57:05 +02001153 l2 = list_alloc();
1154 li->li_tv.v_type = VAR_LIST;
1155 li->li_tv.v_lock = 0;
1156 li->li_tv.vval.v_list = l2;
1157 if (l2 == NULL)
1158 break;
1159 ++l2->lv_refcount;
1160
1161 li2 = listitem_alloc();
1162 if (li2 == NULL)
1163 break;
1164 list_append(l2, li2);
1165 li2->li_tv.v_type = VAR_STRING;
1166 li2->li_tv.v_lock = 0;
1167 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
1168
1169 li2 = listitem_alloc();
1170 if (li2 == NULL)
1171 break;
1172 list_append(l2, li2);
1173 copy_tv(&di->di_tv, &li2->li_tv);
1174 }
1175 }
1176 }
1177}
1178
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001179/*
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001180 * "items(dict)" function
1181 */
1182 void
1183f_items(typval_T *argvars, typval_T *rettv)
1184{
1185 dict_list(argvars, rettv, 2);
1186}
1187
1188/*
1189 * "keys()" function
1190 */
1191 void
1192f_keys(typval_T *argvars, typval_T *rettv)
1193{
1194 dict_list(argvars, rettv, 0);
1195}
1196
1197/*
1198 * "values(dict)" function
1199 */
1200 void
1201f_values(typval_T *argvars, typval_T *rettv)
1202{
1203 dict_list(argvars, rettv, 1);
1204}
1205
1206/*
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001207 * Make each item in the dict readonly (not the value of the item).
1208 */
1209 void
1210dict_set_items_ro(dict_T *di)
1211{
1212 int todo = (int)di->dv_hashtab.ht_used;
1213 hashitem_T *hi;
1214
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001215 // Set readonly
Bram Moolenaar7e1652c2017-12-16 18:27:02 +01001216 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
1217 {
1218 if (HASHITEM_EMPTY(hi))
1219 continue;
1220 --todo;
1221 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
1222 }
1223}
1224
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001225/*
1226 * "has_key()" function
1227 */
1228 void
1229f_has_key(typval_T *argvars, typval_T *rettv)
1230{
1231 if (argvars[0].v_type != VAR_DICT)
1232 {
1233 emsg(_(e_dictreq));
1234 return;
1235 }
1236 if (argvars[0].vval.v_dict == NULL)
1237 return;
1238
1239 rettv->vval.v_number = dict_find(argvars[0].vval.v_dict,
1240 tv_get_string(&argvars[1]), -1) != NULL;
1241}
1242
1243/*
1244 * "remove({dict})" function
1245 */
1246 void
1247dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg)
1248{
1249 dict_T *d;
1250 char_u *key;
1251 dictitem_T *di;
1252
1253 if (argvars[2].v_type != VAR_UNKNOWN)
1254 semsg(_(e_toomanyarg), "remove()");
1255 else if ((d = argvars[0].vval.v_dict) != NULL
Bram Moolenaara187c432020-09-16 21:08:28 +02001256 && !value_check_lock(d->dv_lock, arg_errmsg, TRUE))
Bram Moolenaar9f9fe372019-07-27 23:12:12 +02001257 {
1258 key = tv_get_string_chk(&argvars[1]);
1259 if (key != NULL)
1260 {
1261 di = dict_find(d, key, -1);
1262 if (di == NULL)
1263 semsg(_(e_dictkey), key);
1264 else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE)
1265 && !var_check_ro(di->di_flags, arg_errmsg, TRUE))
1266 {
1267 *rettv = di->di_tv;
1268 init_tv(&di->di_tv);
1269 dictitem_remove(d, di);
1270 }
1271 }
1272 }
1273}
1274
Bram Moolenaar5d18efe2019-12-01 21:11:22 +01001275#endif // defined(FEAT_EVAL)