blob: c359e6f0f6d26023e52ebc3bc9018b953a3ef8b2 [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.
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200191 * Note that the type and value of the item "di_tv" still needs to be
192 * initialized!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200193 * Returns NULL when out of memory.
194 */
195 dictitem_T *
196dictitem_alloc(char_u *key)
197{
198 dictitem_T *di;
199
200 di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + STRLEN(key)));
201 if (di != NULL)
202 {
203 STRCPY(di->di_key, key);
204 di->di_flags = DI_FLAGS_ALLOC;
Bram Moolenaarc89d4b32018-07-08 17:19:02 +0200205 di->di_tv.v_lock = 0;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200206 }
207 return di;
208}
209
210/*
211 * Make a copy of a Dictionary item.
212 */
213 static dictitem_T *
214dictitem_copy(dictitem_T *org)
215{
216 dictitem_T *di;
217
218 di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T)
219 + STRLEN(org->di_key)));
220 if (di != NULL)
221 {
222 STRCPY(di->di_key, org->di_key);
223 di->di_flags = DI_FLAGS_ALLOC;
224 copy_tv(&org->di_tv, &di->di_tv);
225 }
226 return di;
227}
228
229/*
230 * Remove item "item" from Dictionary "dict" and free it.
231 */
232 void
233dictitem_remove(dict_T *dict, dictitem_T *item)
234{
235 hashitem_T *hi;
236
237 hi = hash_find(&dict->dv_hashtab, item->di_key);
238 if (HASHITEM_EMPTY(hi))
Bram Moolenaar95f09602016-11-10 20:01:45 +0100239 internal_error("dictitem_remove()");
Bram Moolenaarcd524592016-07-17 14:57:05 +0200240 else
241 hash_remove(&dict->dv_hashtab, hi);
242 dictitem_free(item);
243}
244
245/*
246 * Free a dict item. Also clears the value.
247 */
248 void
249dictitem_free(dictitem_T *item)
250{
251 clear_tv(&item->di_tv);
252 if (item->di_flags & DI_FLAGS_ALLOC)
253 vim_free(item);
254}
255
256/*
257 * Make a copy of dict "d". Shallow if "deep" is FALSE.
258 * The refcount of the new dict is set to 1.
259 * See item_copy() for "copyID".
260 * Returns NULL when out of memory.
261 */
262 dict_T *
263dict_copy(dict_T *orig, int deep, int copyID)
264{
265 dict_T *copy;
266 dictitem_T *di;
267 int todo;
268 hashitem_T *hi;
269
270 if (orig == NULL)
271 return NULL;
272
273 copy = dict_alloc();
274 if (copy != NULL)
275 {
276 if (copyID != 0)
277 {
278 orig->dv_copyID = copyID;
279 orig->dv_copydict = copy;
280 }
281 todo = (int)orig->dv_hashtab.ht_used;
282 for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
283 {
284 if (!HASHITEM_EMPTY(hi))
285 {
286 --todo;
287
288 di = dictitem_alloc(hi->hi_key);
289 if (di == NULL)
290 break;
291 if (deep)
292 {
293 if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep,
294 copyID) == FAIL)
295 {
296 vim_free(di);
297 break;
298 }
299 }
300 else
301 copy_tv(&HI2DI(hi)->di_tv, &di->di_tv);
302 if (dict_add(copy, di) == FAIL)
303 {
304 dictitem_free(di);
305 break;
306 }
307 }
308 }
309
310 ++copy->dv_refcount;
311 if (todo > 0)
312 {
313 dict_unref(copy);
314 copy = NULL;
315 }
316 }
317
318 return copy;
319}
320
321/*
322 * Add item "item" to Dictionary "d".
323 * Returns FAIL when out of memory and when key already exists.
324 */
325 int
326dict_add(dict_T *d, dictitem_T *item)
327{
328 return hash_add(&d->dv_hashtab, item->di_key);
329}
330
331/*
Bram Moolenaare0be1672018-07-08 16:50:37 +0200332 * Add a number entry to dictionary "d".
Bram Moolenaarcd524592016-07-17 14:57:05 +0200333 * Returns FAIL when out of memory and when key already exists.
334 */
335 int
Bram Moolenaare0be1672018-07-08 16:50:37 +0200336dict_add_number(dict_T *d, char *key, varnumber_T nr)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200337{
338 dictitem_T *item;
339
340 item = dictitem_alloc((char_u *)key);
341 if (item == NULL)
342 return FAIL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200343 item->di_tv.v_type = VAR_NUMBER;
344 item->di_tv.vval.v_number = nr;
345 if (dict_add(d, item) == FAIL)
Bram Moolenaarcd524592016-07-17 14:57:05 +0200346 {
Bram Moolenaare0be1672018-07-08 16:50:37 +0200347 dictitem_free(item);
348 return FAIL;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200349 }
Bram Moolenaare0be1672018-07-08 16:50:37 +0200350 return OK;
351}
352
353/*
354 * Add a string entry to dictionary "d".
355 * Returns FAIL when out of memory and when key already exists.
356 */
357 int
358dict_add_string(dict_T *d, char *key, char_u *str)
359{
360 dictitem_T *item;
361
362 item = dictitem_alloc((char_u *)key);
363 if (item == NULL)
364 return FAIL;
Bram Moolenaare0be1672018-07-08 16:50:37 +0200365 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;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200387 item->di_tv.v_type = VAR_LIST;
388 item->di_tv.vval.v_list = list;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100389 ++list->lv_refcount;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200390 if (dict_add(d, item) == FAIL)
391 {
392 dictitem_free(item);
393 return FAIL;
394 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200395 return OK;
396}
397
398/*
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200399 * Add a dict entry to dictionary "d".
400 * Returns FAIL when out of memory and when key already exists.
401 */
402 int
403dict_add_dict(dict_T *d, char *key, dict_T *dict)
404{
405 dictitem_T *item;
406
407 item = dictitem_alloc((char_u *)key);
408 if (item == NULL)
409 return FAIL;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200410 item->di_tv.v_type = VAR_DICT;
411 item->di_tv.vval.v_dict = dict;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100412 ++dict->dv_refcount;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200413 if (dict_add(d, item) == FAIL)
414 {
415 dictitem_free(item);
416 return FAIL;
417 }
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200418 return OK;
419}
420
421/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200422 * Get the number of items in a Dictionary.
423 */
424 long
425dict_len(dict_T *d)
426{
427 if (d == NULL)
428 return 0L;
429 return (long)d->dv_hashtab.ht_used;
430}
431
432/*
433 * Find item "key[len]" in Dictionary "d".
434 * If "len" is negative use strlen(key).
435 * Returns NULL when not found.
436 */
437 dictitem_T *
438dict_find(dict_T *d, char_u *key, int len)
439{
440#define AKEYLEN 200
441 char_u buf[AKEYLEN];
442 char_u *akey;
443 char_u *tofree = NULL;
444 hashitem_T *hi;
445
446 if (d == NULL)
447 return NULL;
448 if (len < 0)
449 akey = key;
450 else if (len >= AKEYLEN)
451 {
452 tofree = akey = vim_strnsave(key, len);
453 if (akey == NULL)
454 return NULL;
455 }
456 else
457 {
458 /* Avoid a malloc/free by using buf[]. */
459 vim_strncpy(buf, key, len);
460 akey = buf;
461 }
462
463 hi = hash_find(&d->dv_hashtab, akey);
464 vim_free(tofree);
465 if (HASHITEM_EMPTY(hi))
466 return NULL;
467 return HI2DI(hi);
468}
469
470/*
471 * Get a string item from a dictionary.
472 * When "save" is TRUE allocate memory for it.
Bram Moolenaar7dc5e2e2016-08-05 22:22:06 +0200473 * When FALSE a shared buffer is used, can only be used once!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200474 * Returns NULL if the entry doesn't exist or out of memory.
475 */
476 char_u *
477get_dict_string(dict_T *d, char_u *key, int save)
478{
479 dictitem_T *di;
480 char_u *s;
481
482 di = dict_find(d, key, -1);
483 if (di == NULL)
484 return NULL;
485 s = get_tv_string(&di->di_tv);
486 if (save && s != NULL)
487 s = vim_strsave(s);
488 return s;
489}
490
491/*
492 * Get a number item from a dictionary.
493 * Returns 0 if the entry doesn't exist.
494 */
495 varnumber_T
496get_dict_number(dict_T *d, char_u *key)
497{
498 dictitem_T *di;
499
500 di = dict_find(d, key, -1);
501 if (di == NULL)
502 return 0;
503 return get_tv_number(&di->di_tv);
504}
505
506/*
507 * Return an allocated string with the string representation of a Dictionary.
508 * May return NULL.
509 */
510 char_u *
511dict2string(typval_T *tv, int copyID, int restore_copyID)
512{
513 garray_T ga;
514 int first = TRUE;
515 char_u *tofree;
516 char_u numbuf[NUMBUFLEN];
517 hashitem_T *hi;
518 char_u *s;
519 dict_T *d;
520 int todo;
521
522 if ((d = tv->vval.v_dict) == NULL)
523 return NULL;
524 ga_init2(&ga, (int)sizeof(char), 80);
525 ga_append(&ga, '{');
526
527 todo = (int)d->dv_hashtab.ht_used;
528 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
529 {
530 if (!HASHITEM_EMPTY(hi))
531 {
532 --todo;
533
534 if (first)
535 first = FALSE;
536 else
537 ga_concat(&ga, (char_u *)", ");
538
539 tofree = string_quote(hi->hi_key, FALSE);
540 if (tofree != NULL)
541 {
542 ga_concat(&ga, tofree);
543 vim_free(tofree);
544 }
545 ga_concat(&ga, (char_u *)": ");
546 s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID,
547 FALSE, restore_copyID, TRUE);
548 if (s != NULL)
549 ga_concat(&ga, s);
550 vim_free(tofree);
551 if (s == NULL || did_echo_string_emsg)
552 break;
553 line_breakcheck();
554
555 }
556 }
557 if (todo > 0)
558 {
559 vim_free(ga.ga_data);
560 return NULL;
561 }
562
563 ga_append(&ga, '}');
564 ga_append(&ga, NUL);
565 return (char_u *)ga.ga_data;
566}
567
568/*
569 * Allocate a variable for a Dictionary and fill it from "*arg".
570 * Return OK or FAIL. Returns NOTDONE for {expr}.
571 */
572 int
573get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
574{
575 dict_T *d = NULL;
576 typval_T tvkey;
577 typval_T tv;
578 char_u *key = NULL;
579 dictitem_T *item;
580 char_u *start = skipwhite(*arg + 1);
581 char_u buf[NUMBUFLEN];
582
583 /*
584 * First check if it's not a curly-braces thing: {expr}.
585 * Must do this without evaluating, otherwise a function may be called
586 * twice. Unfortunately this means we need to call eval1() twice for the
587 * first item.
588 * But {} is an empty Dictionary.
589 */
590 if (*start != '}')
591 {
592 if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */
593 return FAIL;
594 if (*start == '}')
595 return NOTDONE;
596 }
597
598 if (evaluate)
599 {
600 d = dict_alloc();
601 if (d == NULL)
602 return FAIL;
603 }
604 tvkey.v_type = VAR_UNKNOWN;
605 tv.v_type = VAR_UNKNOWN;
606
607 *arg = skipwhite(*arg + 1);
608 while (**arg != '}' && **arg != NUL)
609 {
610 if (eval1(arg, &tvkey, evaluate) == FAIL) /* recursive! */
611 goto failret;
612 if (**arg != ':')
613 {
614 EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg);
615 clear_tv(&tvkey);
616 goto failret;
617 }
618 if (evaluate)
619 {
620 key = get_tv_string_buf_chk(&tvkey, buf);
621 if (key == NULL)
622 {
623 /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
624 clear_tv(&tvkey);
625 goto failret;
626 }
627 }
628
629 *arg = skipwhite(*arg + 1);
630 if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
631 {
632 if (evaluate)
633 clear_tv(&tvkey);
634 goto failret;
635 }
636 if (evaluate)
637 {
638 item = dict_find(d, key, -1);
639 if (item != NULL)
640 {
641 EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key);
642 clear_tv(&tvkey);
643 clear_tv(&tv);
644 goto failret;
645 }
646 item = dictitem_alloc(key);
647 clear_tv(&tvkey);
648 if (item != NULL)
649 {
650 item->di_tv = tv;
651 item->di_tv.v_lock = 0;
652 if (dict_add(d, item) == FAIL)
653 dictitem_free(item);
654 }
655 }
656
657 if (**arg == '}')
658 break;
659 if (**arg != ',')
660 {
661 EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg);
662 goto failret;
663 }
664 *arg = skipwhite(*arg + 1);
665 }
666
667 if (**arg != '}')
668 {
669 EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
670failret:
671 if (evaluate)
672 dict_free(d);
673 return FAIL;
674 }
675
676 *arg = skipwhite(*arg + 1);
677 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200678 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200679
680 return OK;
681}
682
683/*
684 * Go over all entries in "d2" and add them to "d1".
685 * When "action" is "error" then a duplicate key is an error.
686 * When "action" is "force" then a duplicate key is overwritten.
687 * Otherwise duplicate keys are ignored ("action" is "keep").
688 */
689 void
690dict_extend(dict_T *d1, dict_T *d2, char_u *action)
691{
692 dictitem_T *di1;
693 hashitem_T *hi2;
694 int todo;
695 char_u *arg_errmsg = (char_u *)N_("extend() argument");
696
697 todo = (int)d2->dv_hashtab.ht_used;
698 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
699 {
700 if (!HASHITEM_EMPTY(hi2))
701 {
702 --todo;
703 di1 = dict_find(d1, hi2->hi_key, -1);
704 if (d1->dv_scope != 0)
705 {
706 /* Disallow replacing a builtin function in l: and g:.
707 * Check the key to be valid when adding to any scope. */
708 if (d1->dv_scope == VAR_DEF_SCOPE
709 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
710 && var_check_func_name(hi2->hi_key, di1 == NULL))
711 break;
712 if (!valid_varname(hi2->hi_key))
713 break;
714 }
715 if (di1 == NULL)
716 {
717 di1 = dictitem_copy(HI2DI(hi2));
718 if (di1 != NULL && dict_add(d1, di1) == FAIL)
719 dictitem_free(di1);
720 }
721 else if (*action == 'e')
722 {
723 EMSG2(_("E737: Key already exists: %s"), hi2->hi_key);
724 break;
725 }
726 else if (*action == 'f' && HI2DI(hi2) != di1)
727 {
728 if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
729 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
730 break;
731 clear_tv(&di1->di_tv);
732 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
733 }
734 }
735 }
736}
737
738/*
739 * Return the dictitem that an entry in a hashtable points to.
740 */
741 dictitem_T *
742dict_lookup(hashitem_T *hi)
743{
744 return HI2DI(hi);
745}
746
747/*
748 * Return TRUE when two dictionaries have exactly the same key/values.
749 */
750 int
751dict_equal(
752 dict_T *d1,
753 dict_T *d2,
754 int ic, /* ignore case for strings */
755 int recursive) /* TRUE when used recursively */
756{
757 hashitem_T *hi;
758 dictitem_T *item2;
759 int todo;
760
761 if (d1 == NULL && d2 == NULL)
762 return TRUE;
763 if (d1 == NULL || d2 == NULL)
764 return FALSE;
765 if (d1 == d2)
766 return TRUE;
767 if (dict_len(d1) != dict_len(d2))
768 return FALSE;
769
770 todo = (int)d1->dv_hashtab.ht_used;
771 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
772 {
773 if (!HASHITEM_EMPTY(hi))
774 {
775 item2 = dict_find(d2, hi->hi_key, -1);
776 if (item2 == NULL)
777 return FALSE;
778 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
779 return FALSE;
780 --todo;
781 }
782 }
783 return TRUE;
784}
785
786/*
787 * Turn a dict into a list:
788 * "what" == 0: list of keys
789 * "what" == 1: list of values
790 * "what" == 2: list of items
791 */
792 void
793dict_list(typval_T *argvars, typval_T *rettv, int what)
794{
795 list_T *l2;
796 dictitem_T *di;
797 hashitem_T *hi;
798 listitem_T *li;
799 listitem_T *li2;
800 dict_T *d;
801 int todo;
802
803 if (argvars[0].v_type != VAR_DICT)
804 {
805 EMSG(_(e_dictreq));
806 return;
807 }
808 if ((d = argvars[0].vval.v_dict) == NULL)
809 return;
810
811 if (rettv_list_alloc(rettv) == FAIL)
812 return;
813
814 todo = (int)d->dv_hashtab.ht_used;
815 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
816 {
817 if (!HASHITEM_EMPTY(hi))
818 {
819 --todo;
820 di = HI2DI(hi);
821
822 li = listitem_alloc();
823 if (li == NULL)
824 break;
825 list_append(rettv->vval.v_list, li);
826
827 if (what == 0)
828 {
829 /* keys() */
830 li->li_tv.v_type = VAR_STRING;
831 li->li_tv.v_lock = 0;
832 li->li_tv.vval.v_string = vim_strsave(di->di_key);
833 }
834 else if (what == 1)
835 {
836 /* values() */
837 copy_tv(&di->di_tv, &li->li_tv);
838 }
839 else
840 {
841 /* items() */
842 l2 = list_alloc();
843 li->li_tv.v_type = VAR_LIST;
844 li->li_tv.v_lock = 0;
845 li->li_tv.vval.v_list = l2;
846 if (l2 == NULL)
847 break;
848 ++l2->lv_refcount;
849
850 li2 = listitem_alloc();
851 if (li2 == NULL)
852 break;
853 list_append(l2, li2);
854 li2->li_tv.v_type = VAR_STRING;
855 li2->li_tv.v_lock = 0;
856 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
857
858 li2 = listitem_alloc();
859 if (li2 == NULL)
860 break;
861 list_append(l2, li2);
862 copy_tv(&di->di_tv, &li2->li_tv);
863 }
864 }
865 }
866}
867
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100868/*
869 * Make each item in the dict readonly (not the value of the item).
870 */
871 void
872dict_set_items_ro(dict_T *di)
873{
874 int todo = (int)di->dv_hashtab.ht_used;
875 hashitem_T *hi;
876
877 /* Set readonly */
878 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
879 {
880 if (HASHITEM_EMPTY(hi))
881 continue;
882 --todo;
883 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
884 }
885}
886
Bram Moolenaarcd524592016-07-17 14:57:05 +0200887#endif /* defined(FEAT_EVAL) */