To: vim_dev@googlegroups.com Subject: Patch 7.4.2055 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 7.4.2055 Problem: eval.c is too big. Solution: Move Dictionary functions to dict.c. Files: src/eval.c, src/dict.c, src/vim.h, src/globals.h, src/proto/eval.pro, src/proto/dict.pro, src/Makefile, Filelist *** ../vim-7.4.2054/src/eval.c 2016-07-15 21:24:41.193452608 +0200 --- src/eval.c 2016-07-17 14:09:52.591172188 +0200 *************** *** 34,51 **** 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. --- 34,39 ---- *************** *** 97,103 **** 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 --- 85,90 ---- *************** *** 131,141 **** * 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. --- 118,123 ---- *************** *** 215,221 **** /* 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. */ --- 197,202 ---- *************** *** 423,429 **** 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); --- 404,409 ---- *************** *** 440,447 **** 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); --- 420,425 ---- *************** *** 451,467 **** 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); --- 429,437 ---- *************** *** 469,477 **** 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); --- 439,444 ---- *************** *** 898,909 **** 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); --- 865,871 ---- *************** *** 4260,4266 **** * * Return OK or FAIL. */ ! static int eval1(char_u **arg, typval_T *rettv, int evaluate) { int result; --- 4222,4228 ---- * * Return OK or FAIL. */ ! int eval1(char_u **arg, typval_T *rettv, int evaluate) { int result; *************** *** 6259,6312 **** 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 --- 6221,6226 ---- *************** *** 6366,6372 **** * Compares the items just like "==" would compare them, but strings and * numbers are different. Floats and numbers are also different. */ ! static int tv_equal( typval_T *tv1, typval_T *tv2, --- 6280,6286 ---- * Compares the items just like "==" would compare them, but strings and * numbers are different. Floats and numbers are also different. */ ! int tv_equal( typval_T *tv1, typval_T *tv2, *************** *** 7198,7204 **** static int free_unref_items(int copyID) { - dict_T *dd, *dd_next; list_T *ll, *ll_next; int did_free = FALSE; --- 7112,7117 ---- *************** *** 7212,7229 **** * 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 lists and free items without the copyID. --- 7125,7132 ---- * themselves yet, so that it is possible to decrement refcount counters */ ! /* 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,7265 **** /* * 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); ! } for (ll = first_list; ll != NULL; ll = ll_next) { --- 7157,7163 ---- /* * PASS 2: free the items themselves. */ ! dict_free_items(copyID); for (ll = first_list; ll != NULL; ll = ll_next) { *************** *** 7538,8120 **** 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( --- 7436,7441 ---- *************** *** 8228,8234 **** ga_init(&newargs); ga_init(&newlines); ! /* First, check if this is a lambda expression. "->" must exists. */ ret = get_function_args(&start, '-', NULL, NULL, TRUE); if (ret == FAIL || *start != '>') return NOTDONE; --- 7549,7555 ---- ga_init(&newargs); ga_init(&newlines); ! /* 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,8343 **** * are replaced with "...". * May return NULL. */ ! static char_u * echo_string_core( typval_T *tv, char_u **tofree, --- 7658,7664 ---- * are replaced with "...". * May return NULL. */ ! char_u * echo_string_core( typval_T *tv, char_u **tofree, *************** *** 8553,8559 **** * If "str" is NULL an empty string is assumed. * If "function" is TRUE make it function('string'). */ ! static char_u * string_quote(char_u *str, int function) { unsigned len; --- 7874,7880 ---- * If "str" is NULL an empty string is assumed. * If "function" is TRUE make it function('string'). */ ! char_u * string_quote(char_u *str, int function) { unsigned len; *************** *** 11885,11947 **** } /* - * 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 */ --- 11206,11211 ---- *************** *** 15538,15627 **** } #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 */ --- 14802,14807 ---- *************** *** 23962,23968 **** * Return TRUE if di_flags "flags" indicates variable "name" is read-only. * Also give an error message. */ ! static int var_check_ro(int flags, char_u *name, int use_gettext) { if (flags & DI_FLAGS_RO) --- 23142,23148 ---- * Return TRUE if di_flags "flags" indicates variable "name" is read-only. * Also give an error message. */ ! int var_check_ro(int flags, char_u *name, int use_gettext) { if (flags & DI_FLAGS_RO) *************** *** 23998,24004 **** * Check if a funcref is assigned to a valid variable name. * Return TRUE and give an error if not. */ ! static int var_check_func_name( char_u *name, /* points to start of variable name */ int new_var) /* TRUE when creating the variable */ --- 23178,23184 ---- * Check if a funcref is assigned to a valid variable name. * Return TRUE and give an error if not. */ ! int var_check_func_name( char_u *name, /* points to start of variable name */ int new_var) /* TRUE when creating the variable */ *************** *** 24028,24034 **** * Check if a variable name is valid. * Return FALSE and give an error if not. */ ! static int valid_varname(char_u *varname) { char_u *p; --- 23208,23214 ---- * Check if a variable name is valid. * Return FALSE and give an error if not. */ ! int valid_varname(char_u *varname) { char_u *p; *************** *** 24048,24054 **** * Also give an error message, using "name" or _("name") when use_gettext is * TRUE. */ ! static int tv_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) --- 23228,23234 ---- * Also give an error message, using "name" or _("name") when use_gettext is * TRUE. */ ! int tv_check_lock(int lock, char_u *name, int use_gettext) { if (lock & VAR_LOCKED) *************** *** 24158,24164 **** * reference to an already copied list/dict can be used. * Returns FAIL or OK. */ ! static int item_copy( typval_T *from, typval_T *to, --- 23338,23344 ---- * reference to an already copied list/dict can be used. * Returns FAIL or OK. */ ! int item_copy( typval_T *from, typval_T *to, *** ../vim-7.4.2054/src/dict.c 2016-07-17 14:52:16.673265322 +0200 --- src/dict.c 2016-07-17 14:25:20.677764256 +0200 *************** *** 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) */ *** ../vim-7.4.2054/src/vim.h 2016-07-16 14:46:51.135240543 +0200 --- src/vim.h 2016-07-17 13:56:12.727504173 +0200 *************** *** 932,937 **** --- 932,938 ---- #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,2405 **** --- 2403,2410 ---- /* 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 */ *** ../vim-7.4.2054/src/globals.h 2016-07-16 14:46:51.127240626 +0200 --- src/globals.h 2016-07-17 14:09:19.355509535 +0200 *************** *** 1534,1539 **** --- 1534,1540 ---- 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,1649 **** --- 1645,1665 ---- #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 /* *** ../vim-7.4.2054/src/proto/eval.pro 2016-07-11 22:41:09.580781837 +0200 --- src/proto/eval.pro 2016-07-17 14:07:52.224394087 +0200 *************** *** 45,50 **** --- 45,51 ---- 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,59 **** 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); 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); --- 54,60 ---- listitem_T *listitem_alloc(void); void listitem_free(listitem_T *item); void listitem_remove(list_T *l, listitem_T *item); ! 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,87 **** 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 *tv2string(typval_T *tv, char_u **tofree, char_u *numbuf, int copyID); 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); --- 70,78 ---- 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); ! 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,95 **** 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); --- 80,85 ---- *************** *** 122,128 **** --- 112,123 ---- 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); *** ../vim-7.4.2054/src/proto/dict.pro 2016-07-17 14:52:16.701265036 +0200 --- src/proto/dict.pro 2016-07-17 14:27:48.256270264 +0200 *************** *** 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 : */ *** ../vim-7.4.2054/src/Makefile 2016-07-16 20:37:42.147101489 +0200 --- src/Makefile 2016-07-17 14:56:02.746954686 +0200 *************** *** 1484,1489 **** --- 1484,1490 ---- charset.c \ crypt.c \ crypt_zip.c \ + dict.c \ diff.c \ digraph.c \ edit.c \ *************** *** 1585,1590 **** --- 1586,1592 ---- objects/charset.o \ objects/crypt.o \ objects/crypt_zip.o \ + objects/dict.o \ objects/diff.o \ objects/digraph.o \ objects/edit.o \ *************** *** 1672,1677 **** --- 1674,1680 ---- charset.pro \ crypt.pro \ crypt_zip.pro \ + dict.pro \ diff.pro \ digraph.pro \ edit.pro \ *************** *** 2806,2811 **** --- 2809,2817 ---- 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,3187 **** --- 3188,3197 ---- 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 \ *** ../vim-7.4.2054/Filelist 2016-07-15 17:11:32.968323804 +0200 --- Filelist 2016-07-17 14:55:47.395111546 +0200 *************** *** 18,23 **** --- 18,24 ---- src/charset.c \ src/crypt.c \ src/crypt_zip.c \ + src/dict.c \ src/diff.c \ src/digraph.c \ src/edit.c \ *************** *** 53,58 **** --- 54,60 ---- src/memline.c \ src/menu.c \ src/message.c \ + src/message_test.c \ src/misc1.c \ src/misc2.c \ src/move.c \ *************** *** 125,130 **** --- 127,133 ---- 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 \ *** ../vim-7.4.2054/src/version.c 2016-07-16 21:52:42.210088767 +0200 --- src/version.c 2016-07-17 14:50:32.998325464 +0200 *************** *** 760,761 **** --- 760,763 ---- { /* Add new patch number below this line */ + /**/ + 2055, /**/ -- Overflow on /dev/null, please empty the bit bucket. /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///