blob: e1f7fa23e82bfca649e0ce24359ee2191f9163fe [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
18/* 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; /* list of all dicts */
22
23/*
24 * Allocate an empty header for a dictionary.
25 */
26 dict_T *
27dict_alloc(void)
28{
29 dict_T *d;
30
31 d = (dict_T *)alloc(sizeof(dict_T));
32 if (d != NULL)
33 {
34 /* Add the dict to the list of dicts for garbage collection. */
35 if (first_dict != NULL)
36 first_dict->dv_used_prev = d;
37 d->dv_used_next = first_dict;
38 d->dv_used_prev = NULL;
39 first_dict = d;
40
41 hash_init(&d->dv_hashtab);
42 d->dv_lock = 0;
43 d->dv_scope = 0;
44 d->dv_refcount = 0;
45 d->dv_copyID = 0;
46 }
47 return d;
48}
49
Bram Moolenaar7e1652c2017-12-16 18:27:02 +010050 dict_T *
51dict_alloc_lock(int lock)
52{
53 dict_T *d = dict_alloc();
54
55 if (d != NULL)
56 d->dv_lock = lock;
57 return d;
58}
59
Bram Moolenaarcd524592016-07-17 14:57:05 +020060/*
61 * Allocate an empty dict for a return value.
62 * Returns OK or FAIL.
63 */
64 int
65rettv_dict_alloc(typval_T *rettv)
66{
Bram Moolenaar7e1652c2017-12-16 18:27:02 +010067 dict_T *d = dict_alloc_lock(0);
Bram Moolenaarcd524592016-07-17 14:57:05 +020068
69 if (d == NULL)
70 return FAIL;
71
Bram Moolenaar45cf6e92017-04-30 20:25:19 +020072 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +020073 return OK;
74}
75
76/*
Bram Moolenaar45cf6e92017-04-30 20:25:19 +020077 * Set a dictionary as the return value
78 */
79 void
80rettv_dict_set(typval_T *rettv, dict_T *d)
81{
82 rettv->v_type = VAR_DICT;
83 rettv->vval.v_dict = d;
84 if (d != NULL)
85 ++d->dv_refcount;
86}
87
88/*
Bram Moolenaarcd524592016-07-17 14:57:05 +020089 * Free a Dictionary, including all non-container items it contains.
90 * Ignores the reference count.
91 */
Bram Moolenaar7e1652c2017-12-16 18:27:02 +010092 void
Bram Moolenaarcd524592016-07-17 14:57:05 +020093dict_free_contents(dict_T *d)
94{
95 int todo;
96 hashitem_T *hi;
97 dictitem_T *di;
98
99 /* Lock the hashtab, we don't want it to resize while freeing items. */
100 hash_lock(&d->dv_hashtab);
101 todo = (int)d->dv_hashtab.ht_used;
102 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
103 {
104 if (!HASHITEM_EMPTY(hi))
105 {
106 /* Remove the item before deleting it, just in case there is
107 * something recursive causing trouble. */
108 di = HI2DI(hi);
109 hash_remove(&d->dv_hashtab, hi);
Bram Moolenaar95c526e2017-02-25 14:59:34 +0100110 dictitem_free(di);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200111 --todo;
112 }
113 }
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100114
115 /* The hashtab is still locked, it has to be re-initialized anyway */
Bram Moolenaarcd524592016-07-17 14:57:05 +0200116 hash_clear(&d->dv_hashtab);
117}
118
119 static void
120dict_free_dict(dict_T *d)
121{
122 /* Remove the dict from the list of dicts for garbage collection. */
123 if (d->dv_used_prev == NULL)
124 first_dict = d->dv_used_next;
125 else
126 d->dv_used_prev->dv_used_next = d->dv_used_next;
127 if (d->dv_used_next != NULL)
128 d->dv_used_next->dv_used_prev = d->dv_used_prev;
129 vim_free(d);
130}
131
132 static void
133dict_free(dict_T *d)
134{
135 if (!in_free_unref_items)
136 {
137 dict_free_contents(d);
138 dict_free_dict(d);
139 }
140}
141
142/*
143 * Unreference a Dictionary: decrement the reference count and free it when it
144 * becomes zero.
145 */
146 void
147dict_unref(dict_T *d)
148{
149 if (d != NULL && --d->dv_refcount <= 0)
150 dict_free(d);
151}
152
153/*
154 * Go through the list of dicts and free items without the copyID.
155 * Returns TRUE if something was freed.
156 */
157 int
158dict_free_nonref(int copyID)
159{
160 dict_T *dd;
161 int did_free = FALSE;
162
163 for (dd = first_dict; dd != NULL; dd = dd->dv_used_next)
164 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
165 {
166 /* Free the Dictionary and ordinary items it contains, but don't
167 * recurse into Lists and Dictionaries, they will be in the list
168 * of dicts or list of lists. */
169 dict_free_contents(dd);
170 did_free = TRUE;
171 }
172 return did_free;
173}
174
175 void
176dict_free_items(int copyID)
177{
178 dict_T *dd, *dd_next;
179
180 for (dd = first_dict; dd != NULL; dd = dd_next)
181 {
182 dd_next = dd->dv_used_next;
183 if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK))
184 dict_free_dict(dd);
185 }
186}
187
188/*
189 * Allocate a Dictionary item.
190 * The "key" is copied to the new item.
191 * Note that the value of the item "di_tv" still needs to be initialized!
192 * Returns NULL when out of memory.
193 */
194 dictitem_T *
195dictitem_alloc(char_u *key)
196{
197 dictitem_T *di;
198
199 di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + STRLEN(key)));
200 if (di != NULL)
201 {
202 STRCPY(di->di_key, key);
203 di->di_flags = DI_FLAGS_ALLOC;
204 }
205 return di;
206}
207
208/*
209 * Make a copy of a Dictionary item.
210 */
211 static dictitem_T *
212dictitem_copy(dictitem_T *org)
213{
214 dictitem_T *di;
215
216 di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T)
217 + STRLEN(org->di_key)));
218 if (di != NULL)
219 {
220 STRCPY(di->di_key, org->di_key);
221 di->di_flags = DI_FLAGS_ALLOC;
222 copy_tv(&org->di_tv, &di->di_tv);
223 }
224 return di;
225}
226
227/*
228 * Remove item "item" from Dictionary "dict" and free it.
229 */
230 void
231dictitem_remove(dict_T *dict, dictitem_T *item)
232{
233 hashitem_T *hi;
234
235 hi = hash_find(&dict->dv_hashtab, item->di_key);
236 if (HASHITEM_EMPTY(hi))
Bram Moolenaar95f09602016-11-10 20:01:45 +0100237 internal_error("dictitem_remove()");
Bram Moolenaarcd524592016-07-17 14:57:05 +0200238 else
239 hash_remove(&dict->dv_hashtab, hi);
240 dictitem_free(item);
241}
242
243/*
244 * Free a dict item. Also clears the value.
245 */
246 void
247dictitem_free(dictitem_T *item)
248{
249 clear_tv(&item->di_tv);
250 if (item->di_flags & DI_FLAGS_ALLOC)
251 vim_free(item);
252}
253
254/*
255 * Make a copy of dict "d". Shallow if "deep" is FALSE.
256 * The refcount of the new dict is set to 1.
257 * See item_copy() for "copyID".
258 * Returns NULL when out of memory.
259 */
260 dict_T *
261dict_copy(dict_T *orig, int deep, int copyID)
262{
263 dict_T *copy;
264 dictitem_T *di;
265 int todo;
266 hashitem_T *hi;
267
268 if (orig == NULL)
269 return NULL;
270
271 copy = dict_alloc();
272 if (copy != NULL)
273 {
274 if (copyID != 0)
275 {
276 orig->dv_copyID = copyID;
277 orig->dv_copydict = copy;
278 }
279 todo = (int)orig->dv_hashtab.ht_used;
280 for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
281 {
282 if (!HASHITEM_EMPTY(hi))
283 {
284 --todo;
285
286 di = dictitem_alloc(hi->hi_key);
287 if (di == NULL)
288 break;
289 if (deep)
290 {
291 if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep,
292 copyID) == FAIL)
293 {
294 vim_free(di);
295 break;
296 }
297 }
298 else
299 copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
300 if (dict_add(copy, di) == FAIL)
301 {
302 dictitem_free(di);
303 break;
304 }
305 }
306 }
307
308 ++copy->dv_refcount;
309 if (todo > 0)
310 {
311 dict_unref(copy);
312 copy = NULL;
313 }
314 }
315
316 return copy;
317}
318
319/*
320 * Add item "item" to Dictionary "d".
321 * Returns FAIL when out of memory and when key already exists.
322 */
323 int
324dict_add(dict_T *d, dictitem_T *item)
325{
326 return hash_add(&d->dv_hashtab, item->di_key);
327}
328
329/*
Bram Moolenaare0be1672018-07-08 16:50:37 +0200330 * Add a number entry to dictionary "d".
Bram Moolenaarcd524592016-07-17 14:57:05 +0200331 * Returns FAIL when out of memory and when key already exists.
332 */
333 int
Bram Moolenaare0be1672018-07-08 16:50:37 +0200334dict_add_number(dict_T *d, char *key, varnumber_T nr)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200335{
336 dictitem_T *item;
337
338 item = dictitem_alloc((char_u *)key);
339 if (item == NULL)
340 return FAIL;
341 item->di_tv.v_lock = 0;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200342 item->di_tv.v_type = VAR_NUMBER;
343 item->di_tv.vval.v_number = nr;
344 if (dict_add(d, item) == FAIL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200345 {
Bram Moolenaare0be1672018-07-08 16:50:37 +0200346 dictitem_free(item);
347 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200348 }
Bram Moolenaare0be1672018-07-08 16:50:37 +0200349 return OK;
350}
351
352/*
353 * Add a string entry to dictionary "d".
354 * Returns FAIL when out of memory and when key already exists.
355 */
356 int
357dict_add_string(dict_T *d, char *key, char_u *str)
358{
359 dictitem_T *item;
360
361 item = dictitem_alloc((char_u *)key);
362 if (item == NULL)
363 return FAIL;
364 item->di_tv.v_lock = 0;
365 item->di_tv.v_type = VAR_STRING;
366 item->di_tv.vval.v_string = str != NULL ? vim_strsave(str) : NULL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200367 if (dict_add(d, item) == FAIL)
368 {
369 dictitem_free(item);
370 return FAIL;
371 }
372 return OK;
373}
374
375/*
376 * Add a list entry to dictionary "d".
377 * Returns FAIL when out of memory and when key already exists.
378 */
379 int
380dict_add_list(dict_T *d, char *key, list_T *list)
381{
382 dictitem_T *item;
383
384 item = dictitem_alloc((char_u *)key);
385 if (item == NULL)
386 return FAIL;
387 item->di_tv.v_lock = 0;
388 item->di_tv.v_type = VAR_LIST;
389 item->di_tv.vval.v_list = list;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100390 ++list->lv_refcount;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200391 if (dict_add(d, item) == FAIL)
392 {
393 dictitem_free(item);
394 return FAIL;
395 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200396 return OK;
397}
398
399/*
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200400 * Add a dict entry to dictionary "d".
401 * Returns FAIL when out of memory and when key already exists.
402 */
403 int
404dict_add_dict(dict_T *d, char *key, dict_T *dict)
405{
406 dictitem_T *item;
407
408 item = dictitem_alloc((char_u *)key);
409 if (item == NULL)
410 return FAIL;
411 item->di_tv.v_lock = 0;
412 item->di_tv.v_type = VAR_DICT;
413 item->di_tv.vval.v_dict = dict;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100414 ++dict->dv_refcount;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200415 if (dict_add(d, item) == FAIL)
416 {
417 dictitem_free(item);
418 return FAIL;
419 }
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200420 return OK;
421}
422
423/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200424 * Get the number of items in a Dictionary.
425 */
426 long
427dict_len(dict_T *d)
428{
429 if (d == NULL)
430 return 0L;
431 return (long)d->dv_hashtab.ht_used;
432}
433
434/*
435 * Find item "key[len]" in Dictionary "d".
436 * If "len" is negative use strlen(key).
437 * Returns NULL when not found.
438 */
439 dictitem_T *
440dict_find(dict_T *d, char_u *key, int len)
441{
442#define AKEYLEN 200
443 char_u buf[AKEYLEN];
444 char_u *akey;
445 char_u *tofree = NULL;
446 hashitem_T *hi;
447
448 if (d == NULL)
449 return NULL;
450 if (len < 0)
451 akey = key;
452 else if (len >= AKEYLEN)
453 {
454 tofree = akey = vim_strnsave(key, len);
455 if (akey == NULL)
456 return NULL;
457 }
458 else
459 {
460 /* Avoid a malloc/free by using buf[]. */
461 vim_strncpy(buf, key, len);
462 akey = buf;
463 }
464
465 hi = hash_find(&d->dv_hashtab, akey);
466 vim_free(tofree);
467 if (HASHITEM_EMPTY(hi))
468 return NULL;
469 return HI2DI(hi);
470}
471
472/*
473 * Get a string item from a dictionary.
474 * When "save" is TRUE allocate memory for it.
Bram Moolenaar7dc5e2e2016-08-05 22:22:06 +0200475 * When FALSE a shared buffer is used, can only be used once!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200476 * Returns NULL if the entry doesn't exist or out of memory.
477 */
478 char_u *
479get_dict_string(dict_T *d, char_u *key, int save)
480{
481 dictitem_T *di;
482 char_u *s;
483
484 di = dict_find(d, key, -1);
485 if (di == NULL)
486 return NULL;
487 s = get_tv_string(&di->di_tv);
488 if (save && s != NULL)
489 s = vim_strsave(s);
490 return s;
491}
492
493/*
494 * Get a number item from a dictionary.
495 * Returns 0 if the entry doesn't exist.
496 */
497 varnumber_T
498get_dict_number(dict_T *d, char_u *key)
499{
500 dictitem_T *di;
501
502 di = dict_find(d, key, -1);
503 if (di == NULL)
504 return 0;
505 return get_tv_number(&di->di_tv);
506}
507
508/*
509 * Return an allocated string with the string representation of a Dictionary.
510 * May return NULL.
511 */
512 char_u *
513dict2string(typval_T *tv, int copyID, int restore_copyID)
514{
515 garray_T ga;
516 int first = TRUE;
517 char_u *tofree;
518 char_u numbuf[NUMBUFLEN];
519 hashitem_T *hi;
520 char_u *s;
521 dict_T *d;
522 int todo;
523
524 if ((d = tv->vval.v_dict) == NULL)
525 return NULL;
526 ga_init2(&ga, (int)sizeof(char), 80);
527 ga_append(&ga, '{');
528
529 todo = (int)d->dv_hashtab.ht_used;
530 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
531 {
532 if (!HASHITEM_EMPTY(hi))
533 {
534 --todo;
535
536 if (first)
537 first = FALSE;
538 else
539 ga_concat(&ga, (char_u *)", ");
540
541 tofree = string_quote(hi->hi_key, FALSE);
542 if (tofree != NULL)
543 {
544 ga_concat(&ga, tofree);
545 vim_free(tofree);
546 }
547 ga_concat(&ga, (char_u *)": ");
548 s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID,
549 FALSE, restore_copyID, TRUE);
550 if (s != NULL)
551 ga_concat(&ga, s);
552 vim_free(tofree);
553 if (s == NULL || did_echo_string_emsg)
554 break;
555 line_breakcheck();
556
557 }
558 }
559 if (todo > 0)
560 {
561 vim_free(ga.ga_data);
562 return NULL;
563 }
564
565 ga_append(&ga, '}');
566 ga_append(&ga, NUL);
567 return (char_u *)ga.ga_data;
568}
569
570/*
571 * Allocate a variable for a Dictionary and fill it from "*arg".
572 * Return OK or FAIL. Returns NOTDONE for {expr}.
573 */
574 int
575get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
576{
577 dict_T *d = NULL;
578 typval_T tvkey;
579 typval_T tv;
580 char_u *key = NULL;
581 dictitem_T *item;
582 char_u *start = skipwhite(*arg + 1);
583 char_u buf[NUMBUFLEN];
584
585 /*
586 * First check if it's not a curly-braces thing: {expr}.
587 * Must do this without evaluating, otherwise a function may be called
588 * twice. Unfortunately this means we need to call eval1() twice for the
589 * first item.
590 * But {} is an empty Dictionary.
591 */
592 if (*start != '}')
593 {
594 if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */
595 return FAIL;
596 if (*start == '}')
597 return NOTDONE;
598 }
599
600 if (evaluate)
601 {
602 d = dict_alloc();
603 if (d == NULL)
604 return FAIL;
605 }
606 tvkey.v_type = VAR_UNKNOWN;
607 tv.v_type = VAR_UNKNOWN;
608
609 *arg = skipwhite(*arg + 1);
610 while (**arg != '}' && **arg != NUL)
611 {
612 if (eval1(arg, &tvkey, evaluate) == FAIL) /* recursive! */
613 goto failret;
614 if (**arg != ':')
615 {
616 EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg);
617 clear_tv(&tvkey);
618 goto failret;
619 }
620 if (evaluate)
621 {
622 key = get_tv_string_buf_chk(&tvkey, buf);
623 if (key == NULL)
624 {
625 /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
626 clear_tv(&tvkey);
627 goto failret;
628 }
629 }
630
631 *arg = skipwhite(*arg + 1);
632 if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
633 {
634 if (evaluate)
635 clear_tv(&tvkey);
636 goto failret;
637 }
638 if (evaluate)
639 {
640 item = dict_find(d, key, -1);
641 if (item != NULL)
642 {
643 EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key);
644 clear_tv(&tvkey);
645 clear_tv(&tv);
646 goto failret;
647 }
648 item = dictitem_alloc(key);
649 clear_tv(&tvkey);
650 if (item != NULL)
651 {
652 item->di_tv = tv;
653 item->di_tv.v_lock = 0;
654 if (dict_add(d, item) == FAIL)
655 dictitem_free(item);
656 }
657 }
658
659 if (**arg == '}')
660 break;
661 if (**arg != ',')
662 {
663 EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg);
664 goto failret;
665 }
666 *arg = skipwhite(*arg + 1);
667 }
668
669 if (**arg != '}')
670 {
671 EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
672failret:
673 if (evaluate)
674 dict_free(d);
675 return FAIL;
676 }
677
678 *arg = skipwhite(*arg + 1);
679 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200680 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200681
682 return OK;
683}
684
685/*
686 * Go over all entries in "d2" and add them to "d1".
687 * When "action" is "error" then a duplicate key is an error.
688 * When "action" is "force" then a duplicate key is overwritten.
689 * Otherwise duplicate keys are ignored ("action" is "keep").
690 */
691 void
692dict_extend(dict_T *d1, dict_T *d2, char_u *action)
693{
694 dictitem_T *di1;
695 hashitem_T *hi2;
696 int todo;
697 char_u *arg_errmsg = (char_u *)N_("extend() argument");
698
699 todo = (int)d2->dv_hashtab.ht_used;
700 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
701 {
702 if (!HASHITEM_EMPTY(hi2))
703 {
704 --todo;
705 di1 = dict_find(d1, hi2->hi_key, -1);
706 if (d1->dv_scope != 0)
707 {
708 /* Disallow replacing a builtin function in l: and g:.
709 * Check the key to be valid when adding to any scope. */
710 if (d1->dv_scope == VAR_DEF_SCOPE
711 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
712 && var_check_func_name(hi2->hi_key, di1 == NULL))
713 break;
714 if (!valid_varname(hi2->hi_key))
715 break;
716 }
717 if (di1 == NULL)
718 {
719 di1 = dictitem_copy(HI2DI(hi2));
720 if (di1 != NULL && dict_add(d1, di1) == FAIL)
721 dictitem_free(di1);
722 }
723 else if (*action == 'e')
724 {
725 EMSG2(_("E737: Key already exists: %s"), hi2->hi_key);
726 break;
727 }
728 else if (*action == 'f' && HI2DI(hi2) != di1)
729 {
730 if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
731 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
732 break;
733 clear_tv(&di1->di_tv);
734 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
735 }
736 }
737 }
738}
739
740/*
741 * Return the dictitem that an entry in a hashtable points to.
742 */
743 dictitem_T *
744dict_lookup(hashitem_T *hi)
745{
746 return HI2DI(hi);
747}
748
749/*
750 * Return TRUE when two dictionaries have exactly the same key/values.
751 */
752 int
753dict_equal(
754 dict_T *d1,
755 dict_T *d2,
756 int ic, /* ignore case for strings */
757 int recursive) /* TRUE when used recursively */
758{
759 hashitem_T *hi;
760 dictitem_T *item2;
761 int todo;
762
763 if (d1 == NULL && d2 == NULL)
764 return TRUE;
765 if (d1 == NULL || d2 == NULL)
766 return FALSE;
767 if (d1 == d2)
768 return TRUE;
769 if (dict_len(d1) != dict_len(d2))
770 return FALSE;
771
772 todo = (int)d1->dv_hashtab.ht_used;
773 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
774 {
775 if (!HASHITEM_EMPTY(hi))
776 {
777 item2 = dict_find(d2, hi->hi_key, -1);
778 if (item2 == NULL)
779 return FALSE;
780 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
781 return FALSE;
782 --todo;
783 }
784 }
785 return TRUE;
786}
787
788/*
789 * Turn a dict into a list:
790 * "what" == 0: list of keys
791 * "what" == 1: list of values
792 * "what" == 2: list of items
793 */
794 void
795dict_list(typval_T *argvars, typval_T *rettv, int what)
796{
797 list_T *l2;
798 dictitem_T *di;
799 hashitem_T *hi;
800 listitem_T *li;
801 listitem_T *li2;
802 dict_T *d;
803 int todo;
804
805 if (argvars[0].v_type != VAR_DICT)
806 {
807 EMSG(_(e_dictreq));
808 return;
809 }
810 if ((d = argvars[0].vval.v_dict) == NULL)
811 return;
812
813 if (rettv_list_alloc(rettv) == FAIL)
814 return;
815
816 todo = (int)d->dv_hashtab.ht_used;
817 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
818 {
819 if (!HASHITEM_EMPTY(hi))
820 {
821 --todo;
822 di = HI2DI(hi);
823
824 li = listitem_alloc();
825 if (li == NULL)
826 break;
827 list_append(rettv->vval.v_list, li);
828
829 if (what == 0)
830 {
831 /* keys() */
832 li->li_tv.v_type = VAR_STRING;
833 li->li_tv.v_lock = 0;
834 li->li_tv.vval.v_string = vim_strsave(di->di_key);
835 }
836 else if (what == 1)
837 {
838 /* values() */
839 copy_tv(&di->di_tv, &li->li_tv);
840 }
841 else
842 {
843 /* items() */
844 l2 = list_alloc();
845 li->li_tv.v_type = VAR_LIST;
846 li->li_tv.v_lock = 0;
847 li->li_tv.vval.v_list = l2;
848 if (l2 == NULL)
849 break;
850 ++l2->lv_refcount;
851
852 li2 = listitem_alloc();
853 if (li2 == NULL)
854 break;
855 list_append(l2, li2);
856 li2->li_tv.v_type = VAR_STRING;
857 li2->li_tv.v_lock = 0;
858 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
859
860 li2 = listitem_alloc();
861 if (li2 == NULL)
862 break;
863 list_append(l2, li2);
864 copy_tv(&di->di_tv, &li2->li_tv);
865 }
866 }
867 }
868}
869
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100870/*
871 * Make each item in the dict readonly (not the value of the item).
872 */
873 void
874dict_set_items_ro(dict_T *di)
875{
876 int todo = (int)di->dv_hashtab.ht_used;
877 hashitem_T *hi;
878
879 /* Set readonly */
880 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
881 {
882 if (HASHITEM_EMPTY(hi))
883 continue;
884 --todo;
885 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
886 }
887}
888
Bram Moolenaarcd524592016-07-17 14:57:05 +0200889#endif /* defined(FEAT_EVAL) */