# HG changeset patch # User Christian Brabandt # Date 1468761305 -7200 # Node ID afaff1d283d3d2103fd2ecee807a872489474079 # Parent 9560a5b782ee84c65a9636d812167400bd55e9e9 commit https://github.com/vim/vim/commit/cd52459c387785796713826c63174cdeed295dd4 Author: Bram Moolenaar Date: Sun Jul 17 14:57:05 2016 +0200 patch 7.4.2055 Problem: eval.c is too big. Solution: Move Dictionary functions to dict.c. diff --git a/Filelist b/Filelist --- a/Filelist +++ b/Filelist @@ -18,6 +18,7 @@ SRC_ALL = \ src/charset.c \ src/crypt.c \ src/crypt_zip.c \ + src/dict.c \ src/diff.c \ src/digraph.c \ src/edit.c \ @@ -53,6 +54,7 @@ SRC_ALL = \ src/memline.c \ src/menu.c \ src/message.c \ + src/message_test.c \ src/misc1.c \ src/misc2.c \ src/move.c \ @@ -125,6 +127,7 @@ SRC_ALL = \ src/proto/charset.pro \ src/proto/crypt.pro \ src/proto/crypt_zip.pro \ + src/proto/dict.pro \ src/proto/diff.pro \ src/proto/digraph.pro \ src/proto/edit.pro \ diff --git a/src/Makefile b/src/Makefile --- a/src/Makefile +++ b/src/Makefile @@ -1484,6 +1484,7 @@ BASIC_SRC = \ charset.c \ crypt.c \ crypt_zip.c \ + dict.c \ diff.c \ digraph.c \ edit.c \ @@ -1585,6 +1586,7 @@ OBJ_COMMON = \ objects/charset.o \ objects/crypt.o \ objects/crypt_zip.o \ + objects/dict.o \ objects/diff.o \ objects/digraph.o \ objects/edit.o \ @@ -1672,6 +1674,7 @@ PRO_AUTO = \ charset.pro \ crypt.pro \ crypt_zip.pro \ + dict.pro \ diff.pro \ digraph.pro \ edit.pro \ @@ -2806,6 +2809,9 @@ objects/crypt.o: crypt.c objects/crypt_zip.o: crypt_zip.c $(CCC) -o $@ crypt_zip.c +objects/dict.o: dict.c + $(CCC) -o $@ dict.c + objects/diff.o: diff.c $(CCC) -o $@ diff.c @@ -3182,6 +3188,10 @@ objects/crypt_zip.o: crypt_zip.c vim.h a auto/osdef.h ascii.h keymap.h term.h macros.h option.h structs.h \ regexp.h gui.h gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h \ globals.h farsi.h arabic.h +objects/dict.o: dict.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ + ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ + gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ + farsi.h arabic.h objects/diff.o: diff.c vim.h auto/config.h feature.h os_unix.h auto/osdef.h \ ascii.h keymap.h term.h macros.h option.h structs.h regexp.h gui.h \ gui_beval.h proto/gui_beval.pro alloc.h ex_cmds.h proto.h globals.h \ diff --git a/src/dict.c b/src/dict.c new file mode 100644 --- /dev/null +++ b/src/dict.c @@ -0,0 +1,820 @@ +/* vi:set ts=8 sts=4 sw=4: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * dict.c: Dictionary support + */ +#define USING_FLOAT_STUFF + +#include "vim.h" + +#if defined(FEAT_EVAL) || defined(PROTO) + +/* List head for garbage collection. Although there can be a reference loop + * from partial to dict to partial, we don't need to keep track of the partial, + * since it will get freed when the dict is unused and gets freed. */ +static dict_T *first_dict = NULL; /* list of all dicts */ + +/* + * Allocate an empty header for a dictionary. + */ + dict_T * +dict_alloc(void) +{ + dict_T *d; + + d = (dict_T *)alloc(sizeof(dict_T)); + if (d != NULL) + { + /* Add the dict to the list of dicts for garbage collection. */ + if (first_dict != NULL) + first_dict->dv_used_prev = d; + d->dv_used_next = first_dict; + d->dv_used_prev = NULL; + first_dict = d; + + hash_init(&d->dv_hashtab); + d->dv_lock = 0; + d->dv_scope = 0; + d->dv_refcount = 0; + d->dv_copyID = 0; + } + return d; +} + +/* + * Allocate an empty dict for a return value. + * Returns OK or FAIL. + */ + int +rettv_dict_alloc(typval_T *rettv) +{ + dict_T *d = dict_alloc(); + + if (d == NULL) + return FAIL; + + rettv->vval.v_dict = d; + rettv->v_type = VAR_DICT; + rettv->v_lock = 0; + ++d->dv_refcount; + return OK; +} + +/* + * Free a Dictionary, including all non-container items it contains. + * Ignores the reference count. + */ + static void +dict_free_contents(dict_T *d) +{ + int todo; + hashitem_T *hi; + dictitem_T *di; + + /* Lock the hashtab, we don't want it to resize while freeing items. */ + hash_lock(&d->dv_hashtab); + todo = (int)d->dv_hashtab.ht_used; + for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + /* Remove the item before deleting it, just in case there is + * something recursive causing trouble. */ + di = HI2DI(hi); + hash_remove(&d->dv_hashtab, hi); + clear_tv(&di->di_tv); + vim_free(di); + --todo; + } + } + hash_clear(&d->dv_hashtab); +} + + static void +dict_free_dict(dict_T *d) +{ + /* Remove the dict from the list of dicts for garbage collection. */ + if (d->dv_used_prev == NULL) + first_dict = d->dv_used_next; + else + d->dv_used_prev->dv_used_next = d->dv_used_next; + if (d->dv_used_next != NULL) + d->dv_used_next->dv_used_prev = d->dv_used_prev; + vim_free(d); +} + + static void +dict_free(dict_T *d) +{ + if (!in_free_unref_items) + { + dict_free_contents(d); + dict_free_dict(d); + } +} + +/* + * Unreference a Dictionary: decrement the reference count and free it when it + * becomes zero. + */ + void +dict_unref(dict_T *d) +{ + if (d != NULL && --d->dv_refcount <= 0) + dict_free(d); +} + +/* + * Go through the list of dicts and free items without the copyID. + * Returns TRUE if something was freed. + */ + int +dict_free_nonref(int copyID) +{ + dict_T *dd; + int did_free = FALSE; + + for (dd = first_dict; dd != NULL; dd = dd->dv_used_next) + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) + { + /* Free the Dictionary and ordinary items it contains, but don't + * recurse into Lists and Dictionaries, they will be in the list + * of dicts or list of lists. */ + dict_free_contents(dd); + did_free = TRUE; + } + return did_free; +} + + void +dict_free_items(int copyID) +{ + dict_T *dd, *dd_next; + + for (dd = first_dict; dd != NULL; dd = dd_next) + { + dd_next = dd->dv_used_next; + if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) + dict_free_dict(dd); + } +} + +/* + * Allocate a Dictionary item. + * The "key" is copied to the new item. + * Note that the value of the item "di_tv" still needs to be initialized! + * Returns NULL when out of memory. + */ + dictitem_T * +dictitem_alloc(char_u *key) +{ + dictitem_T *di; + + di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + STRLEN(key))); + if (di != NULL) + { + STRCPY(di->di_key, key); + di->di_flags = DI_FLAGS_ALLOC; + } + return di; +} + +/* + * Make a copy of a Dictionary item. + */ + static dictitem_T * +dictitem_copy(dictitem_T *org) +{ + dictitem_T *di; + + di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + + STRLEN(org->di_key))); + if (di != NULL) + { + STRCPY(di->di_key, org->di_key); + di->di_flags = DI_FLAGS_ALLOC; + copy_tv(&org->di_tv, &di->di_tv); + } + return di; +} + +/* + * Remove item "item" from Dictionary "dict" and free it. + */ + void +dictitem_remove(dict_T *dict, dictitem_T *item) +{ + hashitem_T *hi; + + hi = hash_find(&dict->dv_hashtab, item->di_key); + if (HASHITEM_EMPTY(hi)) + EMSG2(_(e_intern2), "dictitem_remove()"); + else + hash_remove(&dict->dv_hashtab, hi); + dictitem_free(item); +} + +/* + * Free a dict item. Also clears the value. + */ + void +dictitem_free(dictitem_T *item) +{ + clear_tv(&item->di_tv); + if (item->di_flags & DI_FLAGS_ALLOC) + vim_free(item); +} + +/* + * Make a copy of dict "d". Shallow if "deep" is FALSE. + * The refcount of the new dict is set to 1. + * See item_copy() for "copyID". + * Returns NULL when out of memory. + */ + dict_T * +dict_copy(dict_T *orig, int deep, int copyID) +{ + dict_T *copy; + dictitem_T *di; + int todo; + hashitem_T *hi; + + if (orig == NULL) + return NULL; + + copy = dict_alloc(); + if (copy != NULL) + { + if (copyID != 0) + { + orig->dv_copyID = copyID; + orig->dv_copydict = copy; + } + todo = (int)orig->dv_hashtab.ht_used; + for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + + di = dictitem_alloc(hi->hi_key); + if (di == NULL) + break; + if (deep) + { + if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep, + copyID) == FAIL) + { + vim_free(di); + break; + } + } + else + copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); + if (dict_add(copy, di) == FAIL) + { + dictitem_free(di); + break; + } + } + } + + ++copy->dv_refcount; + if (todo > 0) + { + dict_unref(copy); + copy = NULL; + } + } + + return copy; +} + +/* + * Add item "item" to Dictionary "d". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add(dict_T *d, dictitem_T *item) +{ + return hash_add(&d->dv_hashtab, item->di_key); +} + +/* + * Add a number or string entry to dictionary "d". + * When "str" is NULL use number "nr", otherwise use "str". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_nr_str( + dict_T *d, + char *key, + varnumber_T nr, + char_u *str) +{ + dictitem_T *item; + + item = dictitem_alloc((char_u *)key); + if (item == NULL) + return FAIL; + item->di_tv.v_lock = 0; + if (str == NULL) + { + item->di_tv.v_type = VAR_NUMBER; + item->di_tv.vval.v_number = nr; + } + else + { + item->di_tv.v_type = VAR_STRING; + item->di_tv.vval.v_string = vim_strsave(str); + } + if (dict_add(d, item) == FAIL) + { + dictitem_free(item); + return FAIL; + } + return OK; +} + +/* + * Add a list entry to dictionary "d". + * Returns FAIL when out of memory and when key already exists. + */ + int +dict_add_list(dict_T *d, char *key, list_T *list) +{ + dictitem_T *item; + + item = dictitem_alloc((char_u *)key); + if (item == NULL) + return FAIL; + item->di_tv.v_lock = 0; + item->di_tv.v_type = VAR_LIST; + item->di_tv.vval.v_list = list; + if (dict_add(d, item) == FAIL) + { + dictitem_free(item); + return FAIL; + } + ++list->lv_refcount; + return OK; +} + +/* + * Get the number of items in a Dictionary. + */ + long +dict_len(dict_T *d) +{ + if (d == NULL) + return 0L; + return (long)d->dv_hashtab.ht_used; +} + +/* + * Find item "key[len]" in Dictionary "d". + * If "len" is negative use strlen(key). + * Returns NULL when not found. + */ + dictitem_T * +dict_find(dict_T *d, char_u *key, int len) +{ +#define AKEYLEN 200 + char_u buf[AKEYLEN]; + char_u *akey; + char_u *tofree = NULL; + hashitem_T *hi; + + if (d == NULL) + return NULL; + if (len < 0) + akey = key; + else if (len >= AKEYLEN) + { + tofree = akey = vim_strnsave(key, len); + if (akey == NULL) + return NULL; + } + else + { + /* Avoid a malloc/free by using buf[]. */ + vim_strncpy(buf, key, len); + akey = buf; + } + + hi = hash_find(&d->dv_hashtab, akey); + vim_free(tofree); + if (HASHITEM_EMPTY(hi)) + return NULL; + return HI2DI(hi); +} + +/* + * Get a string item from a dictionary. + * When "save" is TRUE allocate memory for it. + * Returns NULL if the entry doesn't exist or out of memory. + */ + char_u * +get_dict_string(dict_T *d, char_u *key, int save) +{ + dictitem_T *di; + char_u *s; + + di = dict_find(d, key, -1); + if (di == NULL) + return NULL; + s = get_tv_string(&di->di_tv); + if (save && s != NULL) + s = vim_strsave(s); + return s; +} + +/* + * Get a number item from a dictionary. + * Returns 0 if the entry doesn't exist. + */ + varnumber_T +get_dict_number(dict_T *d, char_u *key) +{ + dictitem_T *di; + + di = dict_find(d, key, -1); + if (di == NULL) + return 0; + return get_tv_number(&di->di_tv); +} + +/* + * Return an allocated string with the string representation of a Dictionary. + * May return NULL. + */ + char_u * +dict2string(typval_T *tv, int copyID, int restore_copyID) +{ + garray_T ga; + int first = TRUE; + char_u *tofree; + char_u numbuf[NUMBUFLEN]; + hashitem_T *hi; + char_u *s; + dict_T *d; + int todo; + + if ((d = tv->vval.v_dict) == NULL) + return NULL; + ga_init2(&ga, (int)sizeof(char), 80); + ga_append(&ga, '{'); + + todo = (int)d->dv_hashtab.ht_used; + for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + + if (first) + first = FALSE; + else + ga_concat(&ga, (char_u *)", "); + + tofree = string_quote(hi->hi_key, FALSE); + if (tofree != NULL) + { + ga_concat(&ga, tofree); + vim_free(tofree); + } + ga_concat(&ga, (char_u *)": "); + s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID, + FALSE, restore_copyID, TRUE); + if (s != NULL) + ga_concat(&ga, s); + vim_free(tofree); + if (s == NULL || did_echo_string_emsg) + break; + line_breakcheck(); + + } + } + if (todo > 0) + { + vim_free(ga.ga_data); + return NULL; + } + + ga_append(&ga, '}'); + ga_append(&ga, NUL); + return (char_u *)ga.ga_data; +} + +/* + * Allocate a variable for a Dictionary and fill it from "*arg". + * Return OK or FAIL. Returns NOTDONE for {expr}. + */ + int +get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) +{ + dict_T *d = NULL; + typval_T tvkey; + typval_T tv; + char_u *key = NULL; + dictitem_T *item; + char_u *start = skipwhite(*arg + 1); + char_u buf[NUMBUFLEN]; + + /* + * First check if it's not a curly-braces thing: {expr}. + * Must do this without evaluating, otherwise a function may be called + * twice. Unfortunately this means we need to call eval1() twice for the + * first item. + * But {} is an empty Dictionary. + */ + if (*start != '}') + { + if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */ + return FAIL; + if (*start == '}') + return NOTDONE; + } + + if (evaluate) + { + d = dict_alloc(); + if (d == NULL) + return FAIL; + } + tvkey.v_type = VAR_UNKNOWN; + tv.v_type = VAR_UNKNOWN; + + *arg = skipwhite(*arg + 1); + while (**arg != '}' && **arg != NUL) + { + if (eval1(arg, &tvkey, evaluate) == FAIL) /* recursive! */ + goto failret; + if (**arg != ':') + { + EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); + clear_tv(&tvkey); + goto failret; + } + if (evaluate) + { + key = get_tv_string_buf_chk(&tvkey, buf); + if (key == NULL) + { + /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */ + clear_tv(&tvkey); + goto failret; + } + } + + *arg = skipwhite(*arg + 1); + if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ + { + if (evaluate) + clear_tv(&tvkey); + goto failret; + } + if (evaluate) + { + item = dict_find(d, key, -1); + if (item != NULL) + { + EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); + clear_tv(&tvkey); + clear_tv(&tv); + goto failret; + } + item = dictitem_alloc(key); + clear_tv(&tvkey); + if (item != NULL) + { + item->di_tv = tv; + item->di_tv.v_lock = 0; + if (dict_add(d, item) == FAIL) + dictitem_free(item); + } + } + + if (**arg == '}') + break; + if (**arg != ',') + { + EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg); + goto failret; + } + *arg = skipwhite(*arg + 1); + } + + if (**arg != '}') + { + EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); +failret: + if (evaluate) + dict_free(d); + return FAIL; + } + + *arg = skipwhite(*arg + 1); + if (evaluate) + { + rettv->v_type = VAR_DICT; + rettv->vval.v_dict = d; + ++d->dv_refcount; + } + + return OK; +} + +/* + * Go over all entries in "d2" and add them to "d1". + * When "action" is "error" then a duplicate key is an error. + * When "action" is "force" then a duplicate key is overwritten. + * Otherwise duplicate keys are ignored ("action" is "keep"). + */ + void +dict_extend(dict_T *d1, dict_T *d2, char_u *action) +{ + dictitem_T *di1; + hashitem_T *hi2; + int todo; + char_u *arg_errmsg = (char_u *)N_("extend() argument"); + + todo = (int)d2->dv_hashtab.ht_used; + for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) + { + if (!HASHITEM_EMPTY(hi2)) + { + --todo; + di1 = dict_find(d1, hi2->hi_key, -1); + if (d1->dv_scope != 0) + { + /* Disallow replacing a builtin function in l: and g:. + * Check the key to be valid when adding to any scope. */ + if (d1->dv_scope == VAR_DEF_SCOPE + && HI2DI(hi2)->di_tv.v_type == VAR_FUNC + && var_check_func_name(hi2->hi_key, di1 == NULL)) + break; + if (!valid_varname(hi2->hi_key)) + break; + } + if (di1 == NULL) + { + di1 = dictitem_copy(HI2DI(hi2)); + if (di1 != NULL && dict_add(d1, di1) == FAIL) + dictitem_free(di1); + } + else if (*action == 'e') + { + EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); + break; + } + else if (*action == 'f' && HI2DI(hi2) != di1) + { + if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) + || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) + break; + clear_tv(&di1->di_tv); + copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); + } + } + } +} + +/* + * Return the dictitem that an entry in a hashtable points to. + */ + dictitem_T * +dict_lookup(hashitem_T *hi) +{ + return HI2DI(hi); +} + +/* + * Return TRUE when two dictionaries have exactly the same key/values. + */ + int +dict_equal( + dict_T *d1, + dict_T *d2, + int ic, /* ignore case for strings */ + int recursive) /* TRUE when used recursively */ +{ + hashitem_T *hi; + dictitem_T *item2; + int todo; + + if (d1 == NULL && d2 == NULL) + return TRUE; + if (d1 == NULL || d2 == NULL) + return FALSE; + if (d1 == d2) + return TRUE; + if (dict_len(d1) != dict_len(d2)) + return FALSE; + + todo = (int)d1->dv_hashtab.ht_used; + for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + item2 = dict_find(d2, hi->hi_key, -1); + if (item2 == NULL) + return FALSE; + if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive)) + return FALSE; + --todo; + } + } + return TRUE; +} + +/* + * Turn a dict into a list: + * "what" == 0: list of keys + * "what" == 1: list of values + * "what" == 2: list of items + */ + void +dict_list(typval_T *argvars, typval_T *rettv, int what) +{ + list_T *l2; + dictitem_T *di; + hashitem_T *hi; + listitem_T *li; + listitem_T *li2; + dict_T *d; + int todo; + + if (argvars[0].v_type != VAR_DICT) + { + EMSG(_(e_dictreq)); + return; + } + if ((d = argvars[0].vval.v_dict) == NULL) + return; + + if (rettv_list_alloc(rettv) == FAIL) + return; + + todo = (int)d->dv_hashtab.ht_used; + for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + di = HI2DI(hi); + + li = listitem_alloc(); + if (li == NULL) + break; + list_append(rettv->vval.v_list, li); + + if (what == 0) + { + /* keys() */ + li->li_tv.v_type = VAR_STRING; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_string = vim_strsave(di->di_key); + } + else if (what == 1) + { + /* values() */ + copy_tv(&di->di_tv, &li->li_tv); + } + else + { + /* items() */ + l2 = list_alloc(); + li->li_tv.v_type = VAR_LIST; + li->li_tv.v_lock = 0; + li->li_tv.vval.v_list = l2; + if (l2 == NULL) + break; + ++l2->lv_refcount; + + li2 = listitem_alloc(); + if (li2 == NULL) + break; + list_append(l2, li2); + li2->li_tv.v_type = VAR_STRING; + li2->li_tv.v_lock = 0; + li2->li_tv.vval.v_string = vim_strsave(di->di_key); + + li2 = listitem_alloc(); + if (li2 == NULL) + break; + list_append(l2, li2); + copy_tv(&di->di_tv, &li2->li_tv); + } + } + } +} + +#endif /* defined(FEAT_EVAL) */ diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -34,18 +34,6 @@ be freed. */ /* - * In a hashtab item "hi_key" points to "di_key" in a dictitem. - * This avoids adding a pointer to the hashtab item. - * DI2HIKEY() converts a dictitem pointer to a hashitem key pointer. - * HIKEY2DI() converts a hashitem key pointer to a dictitem pointer. - * HI2DI() converts a hashitem pointer to a dictitem pointer. - */ -static dictitem_T dumdi; -#define DI2HIKEY(di) ((di)->di_key) -#define HIKEY2DI(p) ((dictitem_T *)(p - (dumdi.di_key - (char_u *)&dumdi))) -#define HI2DI(hi) HIKEY2DI((hi)->hi_key) - -/* * Structure returned by get_lval() and used by set_var_lval(). * For a plain name: * "name" points to the variable name. @@ -97,7 +85,6 @@ static char *e_missbrac = N_("E111: Miss static char *e_listarg = N_("E686: Argument of %s must be a List"); static char *e_listdictarg = N_("E712: Argument of %s must be a List or Dictionary"); static char *e_listreq = N_("E714: List required"); -static char *e_dictreq = N_("E715: Dictionary required"); #ifdef FEAT_QUICKFIX static char *e_stringreq = N_("E928: String required"); #endif @@ -131,11 +118,6 @@ static hashtab_T compat_hashtab; * The last bit is used for previous_funccal, ignored when comparing. */ static int current_copyID = 0; -#define COPYID_INC 2 -#define COPYID_MASK (~0x1) - -/* Abort conversion to string after a recursion error. */ -static int did_echo_string_emsg = FALSE; /* * Array to hold the hashtab with variables local to each sourced script. @@ -215,7 +197,6 @@ static garray_T ga_loaded = {0, 0, size /* List heads for garbage collection. Although there can be a reference loop * from partial to dict to partial, we don't need to keep track of the partial, * since it will get freed when the dict is unused and gets freed. */ -static dict_T *first_dict = NULL; /* list of all dicts */ static list_T *first_list = NULL; /* list of all lists */ /* From user function to hashitem and back. */ @@ -423,7 +404,6 @@ static void item_lock(typval_T *tv, int static int tv_islocked(typval_T *tv); static int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate); -static int eval1(char_u **arg, typval_T *rettv, int evaluate); static int eval2(char_u **arg, typval_T *rettv, int evaluate); static int eval3(char_u **arg, typval_T *rettv, int evaluate); static int eval4(char_u **arg, typval_T *rettv, int evaluate); @@ -440,8 +420,6 @@ static void list_free_contents(list_T * static void list_free_list(list_T *l); static long list_len(list_T *l); static int list_equal(list_T *l1, list_T *l2, int ic, int recursive); -static int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive); -static int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); static long list_find_nr(list_T *l, long idx, int *errorp); static long list_idx_of_item(list_T *l, listitem_T *item); static int list_extend(list_T *l1, list_T *l2, listitem_T *bef); @@ -451,17 +429,9 @@ static char_u *list2string(typval_T *tv, static int list_join_inner(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID, garray_T *join_gap); static int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); static int free_unref_items(int copyID); -static dictitem_T *dictitem_copy(dictitem_T *org); -static void dictitem_remove(dict_T *dict, dictitem_T *item); -static dict_T *dict_copy(dict_T *orig, int deep, int copyID); -static long dict_len(dict_T *d); -static char_u *dict2string(typval_T *tv, int copyID, int restore_copyID); -static int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, int *varargs, int skip); static int get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate); -static char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int dict_val); static char_u *echo_string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); -static char_u *string_quote(char_u *str, int function); static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate); static int find_internal_func(char_u *name); static char_u *deref_func_name(char_u *name, int *lenp, partial_T **partial, int no_autoload); @@ -469,9 +439,6 @@ static int get_func_tv(char_u *name, int static void emsg_funcname(char *ermsg, char_u *name); static int non_zero_arg(typval_T *argvars); -static void dict_free_contents(dict_T *d); -static void dict_free_dict(dict_T *d); - #ifdef FEAT_FLOAT static void f_abs(typval_T *argvars, typval_T *rettv); static void f_acos(typval_T *argvars, typval_T *rettv); @@ -898,12 +865,7 @@ static void delete_var(hashtab_T *ht, ha static void list_one_var(dictitem_T *v, char_u *prefix, int *first); static void list_one_var_a(char_u *prefix, char_u *name, int type, char_u *string, int *first); static void set_var(char_u *name, typval_T *varp, int copy); -static int var_check_ro(int flags, char_u *name, int use_gettext); static int var_check_fixed(int flags, char_u *name, int use_gettext); -static int var_check_func_name(char_u *name, int new_var); -static int valid_varname(char_u *varname); -static int tv_check_lock(int lock, char_u *name, int use_gettext); -static int item_copy(typval_T *from, typval_T *to, int deep, int copyID); static char_u *find_option_end(char_u **arg, int *opt_flags); static char_u *trans_function_name(char_u **pp, int skip, int flags, funcdict_T *fd, partial_T **partial); static int eval_fname_script(char_u *p); @@ -4260,7 +4222,7 @@ eval0( * * Return OK or FAIL. */ - static int + int eval1(char_u **arg, typval_T *rettv, int evaluate) { int result; @@ -6259,54 +6221,6 @@ list_equal( return item1 == NULL && item2 == NULL; } -/* - * Return the dictitem that an entry in a hashtable points to. - */ - dictitem_T * -dict_lookup(hashitem_T *hi) -{ - return HI2DI(hi); -} - -/* - * Return TRUE when two dictionaries have exactly the same key/values. - */ - static int -dict_equal( - dict_T *d1, - dict_T *d2, - int ic, /* ignore case for strings */ - int recursive) /* TRUE when used recursively */ -{ - hashitem_T *hi; - dictitem_T *item2; - int todo; - - if (d1 == NULL && d2 == NULL) - return TRUE; - if (d1 == NULL || d2 == NULL) - return FALSE; - if (d1 == d2) - return TRUE; - if (dict_len(d1) != dict_len(d2)) - return FALSE; - - todo = (int)d1->dv_hashtab.ht_used; - for (hi = d1->dv_hashtab.ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - item2 = dict_find(d2, hi->hi_key, -1); - if (item2 == NULL) - return FALSE; - if (!tv_equal(&HI2DI(hi)->di_tv, &item2->di_tv, ic, recursive)) - return FALSE; - --todo; - } - } - return TRUE; -} - static int tv_equal_recurse_limit; static int @@ -6366,7 +6280,7 @@ func_equal( * Compares the items just like "==" would compare them, but strings and * numbers are different. Floats and numbers are also different. */ - static int + int tv_equal( typval_T *tv1, typval_T *tv2, @@ -7198,7 +7112,6 @@ garbage_collect(int testing) static int free_unref_items(int copyID) { - dict_T *dd, *dd_next; list_T *ll, *ll_next; int did_free = FALSE; @@ -7212,18 +7125,8 @@ free_unref_items(int copyID) * themselves yet, so that it is possible to decrement refcount counters */ - /* - * Go through the list of dicts and free items without the copyID. - */ - for (dd = first_dict; dd != NULL; dd = dd->dv_used_next) - if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) - { - /* Free the Dictionary and ordinary items it contains, but don't - * recurse into Lists and Dictionaries, they will be in the list - * of dicts or list of lists. */ - dict_free_contents(dd); - did_free = TRUE; - } + /* Go through the list of dicts and free items without the copyID. */ + did_free |= dict_free_nonref(copyID); /* * Go through the list of lists and free items without the copyID. @@ -7254,12 +7157,7 @@ free_unref_items(int copyID) /* * PASS 2: free the items themselves. */ - for (dd = first_dict; dd != NULL; dd = dd_next) - { - dd_next = dd->dv_used_next; - if ((dd->dv_copyID & COPYID_MASK) != (copyID & COPYID_MASK)) - dict_free_dict(dd); - } + dict_free_items(copyID); for (ll = first_list; ll != NULL; ll = ll_next) { @@ -7538,583 +7436,6 @@ set_ref_in_item( return abort; } -/* - * Allocate an empty header for a dictionary. - */ - dict_T * -dict_alloc(void) -{ - dict_T *d; - - d = (dict_T *)alloc(sizeof(dict_T)); - if (d != NULL) - { - /* Add the dict to the list of dicts for garbage collection. */ - if (first_dict != NULL) - first_dict->dv_used_prev = d; - d->dv_used_next = first_dict; - d->dv_used_prev = NULL; - first_dict = d; - - hash_init(&d->dv_hashtab); - d->dv_lock = 0; - d->dv_scope = 0; - d->dv_refcount = 0; - d->dv_copyID = 0; - } - return d; -} - -/* - * Allocate an empty dict for a return value. - * Returns OK or FAIL. - */ - int -rettv_dict_alloc(typval_T *rettv) -{ - dict_T *d = dict_alloc(); - - if (d == NULL) - return FAIL; - - rettv->vval.v_dict = d; - rettv->v_type = VAR_DICT; - rettv->v_lock = 0; - ++d->dv_refcount; - return OK; -} - - -/* - * Unreference a Dictionary: decrement the reference count and free it when it - * becomes zero. - */ - void -dict_unref(dict_T *d) -{ - if (d != NULL && --d->dv_refcount <= 0) - dict_free(d); -} - -/* - * Free a Dictionary, including all non-container items it contains. - * Ignores the reference count. - */ - static void -dict_free_contents(dict_T *d) -{ - int todo; - hashitem_T *hi; - dictitem_T *di; - - /* Lock the hashtab, we don't want it to resize while freeing items. */ - hash_lock(&d->dv_hashtab); - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - /* Remove the item before deleting it, just in case there is - * something recursive causing trouble. */ - di = HI2DI(hi); - hash_remove(&d->dv_hashtab, hi); - clear_tv(&di->di_tv); - vim_free(di); - --todo; - } - } - hash_clear(&d->dv_hashtab); -} - - static void -dict_free_dict(dict_T *d) -{ - /* Remove the dict from the list of dicts for garbage collection. */ - if (d->dv_used_prev == NULL) - first_dict = d->dv_used_next; - else - d->dv_used_prev->dv_used_next = d->dv_used_next; - if (d->dv_used_next != NULL) - d->dv_used_next->dv_used_prev = d->dv_used_prev; - vim_free(d); -} - - void -dict_free(dict_T *d) -{ - if (!in_free_unref_items) - { - dict_free_contents(d); - dict_free_dict(d); - } -} - -/* - * Allocate a Dictionary item. - * The "key" is copied to the new item. - * Note that the value of the item "di_tv" still needs to be initialized! - * Returns NULL when out of memory. - */ - dictitem_T * -dictitem_alloc(char_u *key) -{ - dictitem_T *di; - - di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) + STRLEN(key))); - if (di != NULL) - { - STRCPY(di->di_key, key); - di->di_flags = DI_FLAGS_ALLOC; - } - return di; -} - -/* - * Make a copy of a Dictionary item. - */ - static dictitem_T * -dictitem_copy(dictitem_T *org) -{ - dictitem_T *di; - - di = (dictitem_T *)alloc((unsigned)(sizeof(dictitem_T) - + STRLEN(org->di_key))); - if (di != NULL) - { - STRCPY(di->di_key, org->di_key); - di->di_flags = DI_FLAGS_ALLOC; - copy_tv(&org->di_tv, &di->di_tv); - } - return di; -} - -/* - * Remove item "item" from Dictionary "dict" and free it. - */ - static void -dictitem_remove(dict_T *dict, dictitem_T *item) -{ - hashitem_T *hi; - - hi = hash_find(&dict->dv_hashtab, item->di_key); - if (HASHITEM_EMPTY(hi)) - EMSG2(_(e_intern2), "dictitem_remove()"); - else - hash_remove(&dict->dv_hashtab, hi); - dictitem_free(item); -} - -/* - * Free a dict item. Also clears the value. - */ - void -dictitem_free(dictitem_T *item) -{ - clear_tv(&item->di_tv); - if (item->di_flags & DI_FLAGS_ALLOC) - vim_free(item); -} - -/* - * Make a copy of dict "d". Shallow if "deep" is FALSE. - * The refcount of the new dict is set to 1. - * See item_copy() for "copyID". - * Returns NULL when out of memory. - */ - static dict_T * -dict_copy(dict_T *orig, int deep, int copyID) -{ - dict_T *copy; - dictitem_T *di; - int todo; - hashitem_T *hi; - - if (orig == NULL) - return NULL; - - copy = dict_alloc(); - if (copy != NULL) - { - if (copyID != 0) - { - orig->dv_copyID = copyID; - orig->dv_copydict = copy; - } - todo = (int)orig->dv_hashtab.ht_used; - for (hi = orig->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - - di = dictitem_alloc(hi->hi_key); - if (di == NULL) - break; - if (deep) - { - if (item_copy(&HI2DI(hi)->di_tv, &di->di_tv, deep, - copyID) == FAIL) - { - vim_free(di); - break; - } - } - else - copy_tv(&HI2DI(hi)->di_tv, &di->di_tv); - if (dict_add(copy, di) == FAIL) - { - dictitem_free(di); - break; - } - } - } - - ++copy->dv_refcount; - if (todo > 0) - { - dict_unref(copy); - copy = NULL; - } - } - - return copy; -} - -/* - * Add item "item" to Dictionary "d". - * Returns FAIL when out of memory and when key already exists. - */ - int -dict_add(dict_T *d, dictitem_T *item) -{ - return hash_add(&d->dv_hashtab, item->di_key); -} - -/* - * Add a number or string entry to dictionary "d". - * When "str" is NULL use number "nr", otherwise use "str". - * Returns FAIL when out of memory and when key already exists. - */ - int -dict_add_nr_str( - dict_T *d, - char *key, - varnumber_T nr, - char_u *str) -{ - dictitem_T *item; - - item = dictitem_alloc((char_u *)key); - if (item == NULL) - return FAIL; - item->di_tv.v_lock = 0; - if (str == NULL) - { - item->di_tv.v_type = VAR_NUMBER; - item->di_tv.vval.v_number = nr; - } - else - { - item->di_tv.v_type = VAR_STRING; - item->di_tv.vval.v_string = vim_strsave(str); - } - if (dict_add(d, item) == FAIL) - { - dictitem_free(item); - return FAIL; - } - return OK; -} - -/* - * Add a list entry to dictionary "d". - * Returns FAIL when out of memory and when key already exists. - */ - int -dict_add_list(dict_T *d, char *key, list_T *list) -{ - dictitem_T *item; - - item = dictitem_alloc((char_u *)key); - if (item == NULL) - return FAIL; - item->di_tv.v_lock = 0; - item->di_tv.v_type = VAR_LIST; - item->di_tv.vval.v_list = list; - if (dict_add(d, item) == FAIL) - { - dictitem_free(item); - return FAIL; - } - ++list->lv_refcount; - return OK; -} - -/* - * Get the number of items in a Dictionary. - */ - static long -dict_len(dict_T *d) -{ - if (d == NULL) - return 0L; - return (long)d->dv_hashtab.ht_used; -} - -/* - * Find item "key[len]" in Dictionary "d". - * If "len" is negative use strlen(key). - * Returns NULL when not found. - */ - dictitem_T * -dict_find(dict_T *d, char_u *key, int len) -{ -#define AKEYLEN 200 - char_u buf[AKEYLEN]; - char_u *akey; - char_u *tofree = NULL; - hashitem_T *hi; - - if (d == NULL) - return NULL; - if (len < 0) - akey = key; - else if (len >= AKEYLEN) - { - tofree = akey = vim_strnsave(key, len); - if (akey == NULL) - return NULL; - } - else - { - /* Avoid a malloc/free by using buf[]. */ - vim_strncpy(buf, key, len); - akey = buf; - } - - hi = hash_find(&d->dv_hashtab, akey); - vim_free(tofree); - if (HASHITEM_EMPTY(hi)) - return NULL; - return HI2DI(hi); -} - -/* - * Get a string item from a dictionary. - * When "save" is TRUE allocate memory for it. - * Returns NULL if the entry doesn't exist or out of memory. - */ - char_u * -get_dict_string(dict_T *d, char_u *key, int save) -{ - dictitem_T *di; - char_u *s; - - di = dict_find(d, key, -1); - if (di == NULL) - return NULL; - s = get_tv_string(&di->di_tv); - if (save && s != NULL) - s = vim_strsave(s); - return s; -} - -/* - * Get a number item from a dictionary. - * Returns 0 if the entry doesn't exist. - */ - varnumber_T -get_dict_number(dict_T *d, char_u *key) -{ - dictitem_T *di; - - di = dict_find(d, key, -1); - if (di == NULL) - return 0; - return get_tv_number(&di->di_tv); -} - -/* - * Return an allocated string with the string representation of a Dictionary. - * May return NULL. - */ - static char_u * -dict2string(typval_T *tv, int copyID, int restore_copyID) -{ - garray_T ga; - int first = TRUE; - char_u *tofree; - char_u numbuf[NUMBUFLEN]; - hashitem_T *hi; - char_u *s; - dict_T *d; - int todo; - - if ((d = tv->vval.v_dict) == NULL) - return NULL; - ga_init2(&ga, (int)sizeof(char), 80); - ga_append(&ga, '{'); - - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0 && !got_int; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - - if (first) - first = FALSE; - else - ga_concat(&ga, (char_u *)", "); - - tofree = string_quote(hi->hi_key, FALSE); - if (tofree != NULL) - { - ga_concat(&ga, tofree); - vim_free(tofree); - } - ga_concat(&ga, (char_u *)": "); - s = echo_string_core(&HI2DI(hi)->di_tv, &tofree, numbuf, copyID, - FALSE, restore_copyID, TRUE); - if (s != NULL) - ga_concat(&ga, s); - vim_free(tofree); - if (s == NULL || did_echo_string_emsg) - break; - line_breakcheck(); - - } - } - if (todo > 0) - { - vim_free(ga.ga_data); - return NULL; - } - - ga_append(&ga, '}'); - ga_append(&ga, NUL); - return (char_u *)ga.ga_data; -} - -/* - * Allocate a variable for a Dictionary and fill it from "*arg". - * Return OK or FAIL. Returns NOTDONE for {expr}. - */ - static int -get_dict_tv(char_u **arg, typval_T *rettv, int evaluate) -{ - dict_T *d = NULL; - typval_T tvkey; - typval_T tv; - char_u *key = NULL; - dictitem_T *item; - char_u *start = skipwhite(*arg + 1); - char_u buf[NUMBUFLEN]; - - /* - * First check if it's not a curly-braces thing: {expr}. - * Must do this without evaluating, otherwise a function may be called - * twice. Unfortunately this means we need to call eval1() twice for the - * first item. - * But {} is an empty Dictionary. - */ - if (*start != '}') - { - if (eval1(&start, &tv, FALSE) == FAIL) /* recursive! */ - return FAIL; - if (*start == '}') - return NOTDONE; - } - - if (evaluate) - { - d = dict_alloc(); - if (d == NULL) - return FAIL; - } - tvkey.v_type = VAR_UNKNOWN; - tv.v_type = VAR_UNKNOWN; - - *arg = skipwhite(*arg + 1); - while (**arg != '}' && **arg != NUL) - { - if (eval1(arg, &tvkey, evaluate) == FAIL) /* recursive! */ - goto failret; - if (**arg != ':') - { - EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg); - clear_tv(&tvkey); - goto failret; - } - if (evaluate) - { - key = get_tv_string_buf_chk(&tvkey, buf); - if (key == NULL) - { - /* "key" is NULL when get_tv_string_buf_chk() gave an errmsg */ - clear_tv(&tvkey); - goto failret; - } - } - - *arg = skipwhite(*arg + 1); - if (eval1(arg, &tv, evaluate) == FAIL) /* recursive! */ - { - if (evaluate) - clear_tv(&tvkey); - goto failret; - } - if (evaluate) - { - item = dict_find(d, key, -1); - if (item != NULL) - { - EMSG2(_("E721: Duplicate key in Dictionary: \"%s\""), key); - clear_tv(&tvkey); - clear_tv(&tv); - goto failret; - } - item = dictitem_alloc(key); - clear_tv(&tvkey); - if (item != NULL) - { - item->di_tv = tv; - item->di_tv.v_lock = 0; - if (dict_add(d, item) == FAIL) - dictitem_free(item); - } - } - - if (**arg == '}') - break; - if (**arg != ',') - { - EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg); - goto failret; - } - *arg = skipwhite(*arg + 1); - } - - if (**arg != '}') - { - EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg); -failret: - if (evaluate) - dict_free(d); - return FAIL; - } - - *arg = skipwhite(*arg + 1); - if (evaluate) - { - rettv->v_type = VAR_DICT; - rettv->vval.v_dict = d; - ++d->dv_refcount; - } - - return OK; -} - /* Get function arguments. */ static int get_function_args( @@ -8228,7 +7549,7 @@ get_lambda_tv(char_u **arg, typval_T *re ga_init(&newargs); ga_init(&newlines); - /* First, check if this is a lambda expression. "->" must exists. */ + /* First, check if this is a lambda expression. "->" must exist. */ ret = get_function_args(&start, '-', NULL, NULL, TRUE); if (ret == FAIL || *start != '>') return NOTDONE; @@ -8337,7 +7658,7 @@ get_var_special_name(int nr) * are replaced with "...". * May return NULL. */ - static char_u * + char_u * echo_string_core( typval_T *tv, char_u **tofree, @@ -8553,7 +7874,7 @@ tv2string( * If "str" is NULL an empty string is assumed. * If "function" is TRUE make it function('string'). */ - static char_u * + char_u * string_quote(char_u *str, int function) { unsigned len; @@ -11885,63 +11206,6 @@ f_expand(typval_T *argvars, typval_T *re } /* - * Go over all entries in "d2" and add them to "d1". - * When "action" is "error" then a duplicate key is an error. - * When "action" is "force" then a duplicate key is overwritten. - * Otherwise duplicate keys are ignored ("action" is "keep"). - */ - void -dict_extend(dict_T *d1, dict_T *d2, char_u *action) -{ - dictitem_T *di1; - hashitem_T *hi2; - int todo; - char_u *arg_errmsg = (char_u *)N_("extend() argument"); - - todo = (int)d2->dv_hashtab.ht_used; - for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) - { - if (!HASHITEM_EMPTY(hi2)) - { - --todo; - di1 = dict_find(d1, hi2->hi_key, -1); - if (d1->dv_scope != 0) - { - /* Disallow replacing a builtin function in l: and g:. - * Check the key to be valid when adding to any - * scope. */ - if (d1->dv_scope == VAR_DEF_SCOPE - && HI2DI(hi2)->di_tv.v_type == VAR_FUNC - && var_check_func_name(hi2->hi_key, - di1 == NULL)) - break; - if (!valid_varname(hi2->hi_key)) - break; - } - if (di1 == NULL) - { - di1 = dictitem_copy(HI2DI(hi2)); - if (di1 != NULL && dict_add(d1, di1) == FAIL) - dictitem_free(di1); - } - else if (*action == 'e') - { - EMSG2(_("E737: Key already exists: %s"), hi2->hi_key); - break; - } - else if (*action == 'f' && HI2DI(hi2) != di1) - { - if (tv_check_lock(di1->di_tv.v_lock, arg_errmsg, TRUE) - || var_check_ro(di1->di_flags, arg_errmsg, TRUE)) - break; - clear_tv(&di1->di_tv); - copy_tv(&HI2DI(hi2)->di_tv, &di1->di_tv); - } - } - } -} - -/* * "extend(list, list [, idx])" function * "extend(dict, dict [, action])" function */ @@ -15538,90 +14802,6 @@ f_isnan(typval_T *argvars, typval_T *ret } #endif -static void dict_list(typval_T *argvars, typval_T *rettv, int what); - -/* - * Turn a dict into a list: - * "what" == 0: list of keys - * "what" == 1: list of values - * "what" == 2: list of items - */ - static void -dict_list(typval_T *argvars, typval_T *rettv, int what) -{ - list_T *l2; - dictitem_T *di; - hashitem_T *hi; - listitem_T *li; - listitem_T *li2; - dict_T *d; - int todo; - - if (argvars[0].v_type != VAR_DICT) - { - EMSG(_(e_dictreq)); - return; - } - if ((d = argvars[0].vval.v_dict) == NULL) - return; - - if (rettv_list_alloc(rettv) == FAIL) - return; - - todo = (int)d->dv_hashtab.ht_used; - for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - di = HI2DI(hi); - - li = listitem_alloc(); - if (li == NULL) - break; - list_append(rettv->vval.v_list, li); - - if (what == 0) - { - /* keys() */ - li->li_tv.v_type = VAR_STRING; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_string = vim_strsave(di->di_key); - } - else if (what == 1) - { - /* values() */ - copy_tv(&di->di_tv, &li->li_tv); - } - else - { - /* items() */ - l2 = list_alloc(); - li->li_tv.v_type = VAR_LIST; - li->li_tv.v_lock = 0; - li->li_tv.vval.v_list = l2; - if (l2 == NULL) - break; - ++l2->lv_refcount; - - li2 = listitem_alloc(); - if (li2 == NULL) - break; - list_append(l2, li2); - li2->li_tv.v_type = VAR_STRING; - li2->li_tv.v_lock = 0; - li2->li_tv.vval.v_string = vim_strsave(di->di_key); - - li2 = listitem_alloc(); - if (li2 == NULL) - break; - list_append(l2, li2); - copy_tv(&di->di_tv, &li2->li_tv); - } - } - } -} - /* * "items(dict)" function */ @@ -23962,7 +23142,7 @@ set_var( * Return TRUE if di_flags "flags" indicates variable "name" is read-only. * Also give an error message. */ - static int + int var_check_ro(int flags, char_u *name, int use_gettext) { if (flags & DI_FLAGS_RO) @@ -23998,7 +23178,7 @@ var_check_fixed(int flags, char_u *name, * Check if a funcref is assigned to a valid variable name. * Return TRUE and give an error if not. */ - static int + int var_check_func_name( char_u *name, /* points to start of variable name */ int new_var) /* TRUE when creating the variable */ @@ -24028,7 +23208,7 @@ var_check_func_name( * Check if a variable name is valid. * Return FALSE and give an error if not. */ - static int + int valid_varname(char_u *varname) { char_u *p; @@ -24048,7 +23228,7 @@ valid_varname(char_u *varname) * Also give an error message, using "name" or _("name") when use_gettext is * TRUE. */ - static int + int tv_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) @@ -24158,7 +23338,7 @@ copy_tv(typval_T *from, typval_T *to) * reference to an already copied list/dict can be used. * Returns FAIL or OK. */ - static int + int item_copy( typval_T *from, typval_T *to, diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1534,6 +1534,7 @@ EXTERN char_u e_readonly[] INIT(= N_("E4 EXTERN char_u e_readonlyvar[] INIT(= N_("E46: Cannot change read-only variable \"%s\"")); EXTERN char_u e_readonlysbx[] INIT(= N_("E794: Cannot set variable in the sandbox: \"%s\"")); EXTERN char_u e_emptykey[] INIT(= N_("E713: Cannot use empty key for Dictionary")); +EXTERN char_u e_dictreq[] INIT(= N_("E715: Dictionary required")); #endif #ifdef FEAT_QUICKFIX EXTERN char_u e_readerrf[] INIT(= N_("E47: Error while reading errorfile")); @@ -1644,6 +1645,21 @@ EXTERN int did_add_timer INIT(= FALSE); #ifdef FEAT_EVAL EXTERN time_T time_for_testing INIT(= 0); + +/* + * In a hashtab item "hi_key" points to "di_key" in a dictitem. + * This avoids adding a pointer to the hashtab item. + * DI2HIKEY() converts a dictitem pointer to a hashitem key pointer. + * HIKEY2DI() converts a hashitem key pointer to a dictitem pointer. + * HI2DI() converts a hashitem pointer to a dictitem pointer. + */ +EXTERN dictitem_T dumdi; +# define DI2HIKEY(di) ((di)->di_key) +# define HIKEY2DI(p) ((dictitem_T *)(p - (dumdi.di_key - (char_u *)&dumdi))) +# define HI2DI(hi) HIKEY2DI((hi)->hi_key) + +/* Abort conversion to string after a recursion error. */ +EXTERN int did_echo_string_emsg INIT(= FALSE); #endif /* diff --git a/src/proto/dict.pro b/src/proto/dict.pro new file mode 100644 --- /dev/null +++ b/src/proto/dict.pro @@ -0,0 +1,24 @@ +/* dict.c */ +dict_T *dict_alloc(void); +int rettv_dict_alloc(typval_T *rettv); +void dict_unref(dict_T *d); +int dict_free_nonref(int copyID); +void dict_free_items(int copyID); +dictitem_T *dictitem_alloc(char_u *key); +void dictitem_remove(dict_T *dict, dictitem_T *item); +void dictitem_free(dictitem_T *item); +dict_T *dict_copy(dict_T *orig, int deep, int copyID); +int dict_add(dict_T *d, dictitem_T *item); +int dict_add_nr_str(dict_T *d, char *key, varnumber_T nr, char_u *str); +int dict_add_list(dict_T *d, char *key, list_T *list); +long dict_len(dict_T *d); +dictitem_T *dict_find(dict_T *d, char_u *key, int len); +char_u *get_dict_string(dict_T *d, char_u *key, int save); +varnumber_T get_dict_number(dict_T *d, char_u *key); +char_u *dict2string(typval_T *tv, int copyID, int restore_copyID); +int get_dict_tv(char_u **arg, typval_T *rettv, int evaluate); +void dict_extend(dict_T *d1, dict_T *d2, char_u *action); +dictitem_T *dict_lookup(hashitem_T *hi); +int dict_equal(dict_T *d1, dict_T *d2, int ic, int recursive); +void dict_list(typval_T *argvars, typval_T *rettv, int what); +/* vim: set ft=c : */ diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -45,6 +45,7 @@ void ex_lockvar(exarg_T *eap); int do_unlet(char_u *name, int forceit); void del_menutrans_vars(void); char_u *get_user_var_name(expand_T *xp, int idx); +int eval1(char_u **arg, typval_T *rettv, int evaluate); void partial_unref(partial_T *pt); list_T *list_alloc(void); int rettv_list_alloc(typval_T *rettv); @@ -53,7 +54,7 @@ void list_free(list_T *l); listitem_T *listitem_alloc(void); void listitem_free(listitem_T *item); void listitem_remove(list_T *l, listitem_T *item); -dictitem_T *dict_lookup(hashitem_T *hi); +int tv_equal(typval_T *tv1, typval_T *tv2, int ic, int recursive); listitem_T *list_find(list_T *l, long n); char_u *list_find_str(list_T *l, long idx); void list_append(list_T *l, listitem_T *item); @@ -69,19 +70,9 @@ int garbage_collect(int testing); int set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack); int set_ref_in_list(list_T *l, int copyID, ht_stack_T **ht_stack); int set_ref_in_item(typval_T *tv, int copyID, ht_stack_T **ht_stack, list_stack_T **list_stack); -dict_T *dict_alloc(void); -int rettv_dict_alloc(typval_T *rettv); -void dict_unref(dict_T *d); -void dict_free(dict_T *d); -dictitem_T *dictitem_alloc(char_u *key); -void dictitem_free(dictitem_T *item); -int dict_add(dict_T *d, dictitem_T *item); -int dict_add_nr_str(dict_T *d, char *key, varnumber_T nr, char_u *str); -int dict_add_list(dict_T *d, char *key, list_T *list); -dictitem_T *dict_find(dict_T *d, char_u *key, int len); -char_u *get_dict_string(dict_T *d, char_u *key, int save); -varnumber_T get_dict_number(dict_T *d, char_u *key); +char_u *echo_string_core(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID, int echo_style, int restore_copyID, int dict_val); char_u *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); +char_u *string_quote(char_u *str, int function); int string2float(char_u *text, float_T *value); char_u *get_function_name(expand_T *xp, int idx); char_u *get_expr_name(expand_T *xp, int idx); @@ -89,7 +80,6 @@ int call_func(char_u *funcname, int len, buf_T *buflist_find_by_name(char_u *name, int curtab_only); int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict, typval_T *rettv); void execute_redir_str(char_u *value, int value_len); -void dict_extend(dict_T *d1, dict_T *d2, char_u *action); void mzscheme_call_vim(char_u *name, typval_T *args, typval_T *rettv); float_T vim_round(float_T f); long do_searchpair(char_u *spat, char_u *mpat, char_u *epat, int dir, char_u *skip, int flags, pos_T *match_pos, linenr_T lnum_stop, long time_limit); @@ -122,7 +112,12 @@ void new_script_vars(scid_T id); void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope); void unref_var_dict(dict_T *dict); void vars_clear(hashtab_T *ht); +int var_check_ro(int flags, char_u *name, int use_gettext); +int var_check_func_name(char_u *name, int new_var); +int valid_varname(char_u *varname); +int tv_check_lock(int lock, char_u *name, int use_gettext); void copy_tv(typval_T *from, typval_T *to); +int item_copy(typval_T *from, typval_T *to, int deep, int copyID); void ex_echo(exarg_T *eap); void ex_echohl(exarg_T *eap); void ex_execute(exarg_T *eap); diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -759,6 +759,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2055, +/**/ 2054, /**/ 2053, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -932,6 +932,7 @@ extern char *(*dyn_libintl_textdomain)(c #define BLN_DUMMY 4 /* allocating dummy buffer */ #define BLN_NEW 8 /* create a new buffer */ #define BLN_NOOPT 16 /* don't copy options to existing buffer */ +#define BLN_DUMMY_OK 32 /* also find an existing dummy buffer */ /* Values for in_cinkeys() */ #define KEY_OPEN_FORW 0x101 @@ -2402,4 +2403,8 @@ int vim_main2(int argc, char **argv); /* Lowest number used for window ID. Cannot have this many windows. */ #define LOWEST_WIN_ID 1000 +/* Used by the garbage collector. */ +#define COPYID_INC 2 +#define COPYID_MASK (~0x1) + #endif /* VIM__H */