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