blob: 55069783f288781b8cb928d58ac65bb862d300f9 [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/*
330 * Add a number or string entry to dictionary "d".
331 * When "str" is NULL use number "nr", otherwise use "str".
332 * Returns FAIL when out of memory and when key already exists.
333 */
334 int
335dict_add_nr_str(
336 dict_T *d,
337 char *key,
338 varnumber_T nr,
339 char_u *str)
340{
341 dictitem_T *item;
342
343 item = dictitem_alloc((char_u *)key);
344 if (item == NULL)
345 return FAIL;
346 item->di_tv.v_lock = 0;
347 if (str == NULL)
348 {
349 item->di_tv.v_type = VAR_NUMBER;
350 item->di_tv.vval.v_number = nr;
351 }
352 else
353 {
354 item->di_tv.v_type = VAR_STRING;
355 item->di_tv.vval.v_string = vim_strsave(str);
356 }
357 if (dict_add(d, item) == FAIL)
358 {
359 dictitem_free(item);
360 return FAIL;
361 }
362 return OK;
363}
364
365/*
366 * Add a list entry to dictionary "d".
367 * Returns FAIL when out of memory and when key already exists.
368 */
369 int
370dict_add_list(dict_T *d, char *key, list_T *list)
371{
372 dictitem_T *item;
373
374 item = dictitem_alloc((char_u *)key);
375 if (item == NULL)
376 return FAIL;
377 item->di_tv.v_lock = 0;
378 item->di_tv.v_type = VAR_LIST;
379 item->di_tv.vval.v_list = list;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100380 ++list->lv_refcount;
Bram Moolenaarcd524592016-07-17 14:57:05 +0200381 if (dict_add(d, item) == FAIL)
382 {
383 dictitem_free(item);
384 return FAIL;
385 }
Bram Moolenaarcd524592016-07-17 14:57:05 +0200386 return OK;
387}
388
389/*
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200390 * Add a dict entry to dictionary "d".
391 * Returns FAIL when out of memory and when key already exists.
392 */
393 int
394dict_add_dict(dict_T *d, char *key, dict_T *dict)
395{
396 dictitem_T *item;
397
398 item = dictitem_alloc((char_u *)key);
399 if (item == NULL)
400 return FAIL;
401 item->di_tv.v_lock = 0;
402 item->di_tv.v_type = VAR_DICT;
403 item->di_tv.vval.v_dict = dict;
Bram Moolenaar42f45b82017-03-14 22:17:14 +0100404 ++dict->dv_refcount;
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200405 if (dict_add(d, item) == FAIL)
406 {
407 dictitem_free(item);
408 return FAIL;
409 }
Bram Moolenaarb5ae48e2016-08-12 22:23:25 +0200410 return OK;
411}
412
413/*
Bram Moolenaarcd524592016-07-17 14:57:05 +0200414 * Get the number of items in a Dictionary.
415 */
416 long
417dict_len(dict_T *d)
418{
419 if (d == NULL)
420 return 0L;
421 return (long)d->dv_hashtab.ht_used;
422}
423
424/*
425 * Find item "key[len]" in Dictionary "d".
426 * If "len" is negative use strlen(key).
427 * Returns NULL when not found.
428 */
429 dictitem_T *
430dict_find(dict_T *d, char_u *key, int len)
431{
432#define AKEYLEN 200
433 char_u buf[AKEYLEN];
434 char_u *akey;
435 char_u *tofree = NULL;
436 hashitem_T *hi;
437
438 if (d == NULL)
439 return NULL;
440 if (len < 0)
441 akey = key;
442 else if (len >= AKEYLEN)
443 {
444 tofree = akey = vim_strnsave(key, len);
445 if (akey == NULL)
446 return NULL;
447 }
448 else
449 {
450 /* Avoid a malloc/free by using buf[]. */
451 vim_strncpy(buf, key, len);
452 akey = buf;
453 }
454
455 hi = hash_find(&d->dv_hashtab, akey);
456 vim_free(tofree);
457 if (HASHITEM_EMPTY(hi))
458 return NULL;
459 return HI2DI(hi);
460}
461
462/*
463 * Get a string item from a dictionary.
464 * When "save" is TRUE allocate memory for it.
Bram Moolenaar7dc5e2e2016-08-05 22:22:06 +0200465 * When FALSE a shared buffer is used, can only be used once!
Bram Moolenaarcd524592016-07-17 14:57:05 +0200466 * Returns NULL if the entry doesn't exist or out of memory.
467 */
468 char_u *
469get_dict_string(dict_T *d, char_u *key, int save)
470{
471 dictitem_T *di;
472 char_u *s;
473
474 di = dict_find(d, key, -1);
475 if (di == NULL)
476 return NULL;
477 s = get_tv_string(&di->di_tv);
478 if (save && s != NULL)
479 s = vim_strsave(s);
480 return s;
481}
482
483/*
484 * Get a number item from a dictionary.
485 * Returns 0 if the entry doesn't exist.
486 */
487 varnumber_T
488get_dict_number(dict_T *d, char_u *key)
489{
490 dictitem_T *di;
491
492 di = dict_find(d, key, -1);
493 if (di == NULL)
494 return 0;
495 return get_tv_number(&di->di_tv);
496}
497
498/*
499 * Return an allocated string with the string representation of a Dictionary.
500 * May return NULL.
501 */
502 char_u *
503dict2string(typval_T *tv, int copyID, int restore_copyID)
504{
505 garray_T ga;
506 int first = TRUE;
507 char_u *tofree;
508 char_u numbuf[NUMBUFLEN];
509 hashitem_T *hi;
510 char_u *s;
511 dict_T *d;
512 int todo;
513
514 if ((d = tv->vval.v_dict) == NULL)
515 return NULL;
516 ga_init2(&ga, (int)sizeof(char), 80);
517 ga_append(&ga, '{');
518
519 todo = (int)d->dv_hashtab.ht_used;
520 for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi)
521 {
522 if (!HASHITEM_EMPTY(hi))
523 {
524 --todo;
525
526 if (first)
527 first = FALSE;
528 else
529 ga_concat(&ga, (char_u *)", ");
530
531 tofree = string_quote(hi->hi_key, FALSE);
532 if (tofree != NULL)
533 {
534 ga_concat(&ga, tofree);
535 vim_free(tofree);
536 }
537 ga_concat(&ga, (char_u *)": ");
538 s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID,
539 FALSE, restore_copyID, TRUE);
540 if (s != NULL)
541 ga_concat(&ga, s);
542 vim_free(tofree);
543 if (s == NULL || did_echo_string_emsg)
544 break;
545 line_breakcheck();
546
547 }
548 }
549 if (todo > 0)
550 {
551 vim_free(ga.ga_data);
552 return NULL;
553 }
554
555 ga_append(&ga, '}');
556 ga_append(&ga, NUL);
557 return (char_u *)ga.ga_data;
558}
559
560/*
561 * Allocate a variable for a Dictionary and fill it from "*arg".
562 * Return OK or FAIL. Returns NOTDONE for {expr}.
563 */
564 int
565get_dict_tv(char_u **arg, typval_T *rettv, int evaluate)
566{
567 dict_T *d = NULL;
568 typval_T tvkey;
569 typval_T tv;
570 char_u *key = NULL;
571 dictitem_T *item;
572 char_u *start = skipwhite(*arg + 1);
573 char_u buf[NUMBUFLEN];
574
575 /*
576 * First check if it's not a curly-braces thing: {expr}.
577 * Must do this without evaluating, otherwise a function may be called
578 * twice. Unfortunately this means we need to call eval1() twice for the
579 * first item.
580 * But {} is an empty Dictionary.
581 */
582 if (*start != '}')
583 {
584 if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */
585 return FAIL;
586 if (*start == '}')
587 return NOTDONE;
588 }
589
590 if (evaluate)
591 {
592 d = dict_alloc();
593 if (d == NULL)
594 return FAIL;
595 }
596 tvkey.v_type = VAR_UNKNOWN;
597 tv.v_type = VAR_UNKNOWN;
598
599 *arg = skipwhite(*arg + 1);
600 while (**arg != '}' && **arg != NUL)
601 {
602 if (eval1(arg, &tvkey, evaluate) == FAIL) /* recursive! */
603 goto failret;
604 if (**arg != ':')
605 {
606 EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg);
607 clear_tv(&tvkey);
608 goto failret;
609 }
610 if (evaluate)
611 {
612 key = get_tv_string_buf_chk(&tvkey, buf);
613 if (key == NULL)
614 {
615 /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */
616 clear_tv(&tvkey);
617 goto failret;
618 }
619 }
620
621 *arg = skipwhite(*arg + 1);
622 if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */
623 {
624 if (evaluate)
625 clear_tv(&tvkey);
626 goto failret;
627 }
628 if (evaluate)
629 {
630 item = dict_find(d, key, -1);
631 if (item != NULL)
632 {
633 EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key);
634 clear_tv(&tvkey);
635 clear_tv(&tv);
636 goto failret;
637 }
638 item = dictitem_alloc(key);
639 clear_tv(&tvkey);
640 if (item != NULL)
641 {
642 item->di_tv = tv;
643 item->di_tv.v_lock = 0;
644 if (dict_add(d, item) == FAIL)
645 dictitem_free(item);
646 }
647 }
648
649 if (**arg == '}')
650 break;
651 if (**arg != ',')
652 {
653 EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg);
654 goto failret;
655 }
656 *arg = skipwhite(*arg + 1);
657 }
658
659 if (**arg != '}')
660 {
661 EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
662failret:
663 if (evaluate)
664 dict_free(d);
665 return FAIL;
666 }
667
668 *arg = skipwhite(*arg + 1);
669 if (evaluate)
Bram Moolenaar45cf6e92017-04-30 20:25:19 +0200670 rettv_dict_set(rettv, d);
Bram Moolenaarcd524592016-07-17 14:57:05 +0200671
672 return OK;
673}
674
675/*
676 * Go over all entries in "d2" and add them to "d1".
677 * When "action" is "error" then a duplicate key is an error.
678 * When "action" is "force" then a duplicate key is overwritten.
679 * Otherwise duplicate keys are ignored ("action" is "keep").
680 */
681 void
682dict_extend(dict_T *d1, dict_T *d2, char_u *action)
683{
684 dictitem_T *di1;
685 hashitem_T *hi2;
686 int todo;
687 char_u *arg_errmsg = (char_u *)N_("extend() argument");
688
689 todo = (int)d2->dv_hashtab.ht_used;
690 for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
691 {
692 if (!HASHITEM_EMPTY(hi2))
693 {
694 --todo;
695 di1 = dict_find(d1, hi2->hi_key, -1);
696 if (d1->dv_scope != 0)
697 {
698 /* Disallow replacing a builtin function in l: and g:.
699 * Check the key to be valid when adding to any scope. */
700 if (d1->dv_scope == VAR_DEF_SCOPE
701 && HI2DI(hi2)->di_tv.v_type == VAR_FUNC
702 && var_check_func_name(hi2->hi_key, di1 == NULL))
703 break;
704 if (!valid_varname(hi2->hi_key))
705 break;
706 }
707 if (di1 == NULL)
708 {
709 di1 = dictitem_copy(HI2DI(hi2));
710 if (di1 != NULL && dict_add(d1, di1) == FAIL)
711 dictitem_free(di1);
712 }
713 else if (*action == 'e')
714 {
715 EMSG2(_("E737: Key already exists: %s"), hi2->hi_key);
716 break;
717 }
718 else if (*action == 'f' && HI2DI(hi2) != di1)
719 {
720 if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE)
721 || var_check_ro(di1->di_flags, arg_errmsg, TRUE))
722 break;
723 clear_tv(&di1->di_tv);
724 copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv);
725 }
726 }
727 }
728}
729
730/*
731 * Return the dictitem that an entry in a hashtable points to.
732 */
733 dictitem_T *
734dict_lookup(hashitem_T *hi)
735{
736 return HI2DI(hi);
737}
738
739/*
740 * Return TRUE when two dictionaries have exactly the same key/values.
741 */
742 int
743dict_equal(
744 dict_T *d1,
745 dict_T *d2,
746 int ic, /* ignore case for strings */
747 int recursive) /* TRUE when used recursively */
748{
749 hashitem_T *hi;
750 dictitem_T *item2;
751 int todo;
752
753 if (d1 == NULL && d2 == NULL)
754 return TRUE;
755 if (d1 == NULL || d2 == NULL)
756 return FALSE;
757 if (d1 == d2)
758 return TRUE;
759 if (dict_len(d1) != dict_len(d2))
760 return FALSE;
761
762 todo = (int)d1->dv_hashtab.ht_used;
763 for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi)
764 {
765 if (!HASHITEM_EMPTY(hi))
766 {
767 item2 = dict_find(d2, hi->hi_key, -1);
768 if (item2 == NULL)
769 return FALSE;
770 if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive))
771 return FALSE;
772 --todo;
773 }
774 }
775 return TRUE;
776}
777
778/*
779 * Turn a dict into a list:
780 * "what" == 0: list of keys
781 * "what" == 1: list of values
782 * "what" == 2: list of items
783 */
784 void
785dict_list(typval_T *argvars, typval_T *rettv, int what)
786{
787 list_T *l2;
788 dictitem_T *di;
789 hashitem_T *hi;
790 listitem_T *li;
791 listitem_T *li2;
792 dict_T *d;
793 int todo;
794
795 if (argvars[0].v_type != VAR_DICT)
796 {
797 EMSG(_(e_dictreq));
798 return;
799 }
800 if ((d = argvars[0].vval.v_dict) == NULL)
801 return;
802
803 if (rettv_list_alloc(rettv) == FAIL)
804 return;
805
806 todo = (int)d->dv_hashtab.ht_used;
807 for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi)
808 {
809 if (!HASHITEM_EMPTY(hi))
810 {
811 --todo;
812 di = HI2DI(hi);
813
814 li = listitem_alloc();
815 if (li == NULL)
816 break;
817 list_append(rettv->vval.v_list, li);
818
819 if (what == 0)
820 {
821 /* keys() */
822 li->li_tv.v_type = VAR_STRING;
823 li->li_tv.v_lock = 0;
824 li->li_tv.vval.v_string = vim_strsave(di->di_key);
825 }
826 else if (what == 1)
827 {
828 /* values() */
829 copy_tv(&di->di_tv, &li->li_tv);
830 }
831 else
832 {
833 /* items() */
834 l2 = list_alloc();
835 li->li_tv.v_type = VAR_LIST;
836 li->li_tv.v_lock = 0;
837 li->li_tv.vval.v_list = l2;
838 if (l2 == NULL)
839 break;
840 ++l2->lv_refcount;
841
842 li2 = listitem_alloc();
843 if (li2 == NULL)
844 break;
845 list_append(l2, li2);
846 li2->li_tv.v_type = VAR_STRING;
847 li2->li_tv.v_lock = 0;
848 li2->li_tv.vval.v_string = vim_strsave(di->di_key);
849
850 li2 = listitem_alloc();
851 if (li2 == NULL)
852 break;
853 list_append(l2, li2);
854 copy_tv(&di->di_tv, &li2->li_tv);
855 }
856 }
857 }
858}
859
Bram Moolenaar7e1652c2017-12-16 18:27:02 +0100860/*
861 * Make each item in the dict readonly (not the value of the item).
862 */
863 void
864dict_set_items_ro(dict_T *di)
865{
866 int todo = (int)di->dv_hashtab.ht_used;
867 hashitem_T *hi;
868
869 /* Set readonly */
870 for (hi = di->dv_hashtab.ht_array; todo > 0 ; ++hi)
871 {
872 if (HASHITEM_EMPTY(hi))
873 continue;
874 --todo;
875 HI2DI(hi)->di_flags |= DI_FLAGS_RO | DI_FLAGS_FIX;
876 }
877}
878
Bram Moolenaarcd524592016-07-17 14:57:05 +0200879#endif /* defined(FEAT_EVAL) */