To: vim_dev@googlegroups.com Subject: Patch 8.1.1763 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.1.1763 Problem: Evalfunc.c is still too big. Solution: Move dict and list functions to a better place. Files: src/evalfunc.c, src/dict.c, src/proto/dict.pro, src/list.c, src/proto/list.pro, src/blob.c, src/proto/blob.pro *** ../vim-8.1.1762/src/evalfunc.c 2019-07-27 17:38:55.474964950 +0200 --- src/evalfunc.c 2019-07-27 23:00:00.572303798 +0200 *************** *** 180,186 **** static void f_globpath(typval_T *argvars, typval_T *rettv); static void f_glob2regpat(typval_T *argvars, typval_T *rettv); static void f_has(typval_T *argvars, typval_T *rettv); - static void f_has_key(typval_T *argvars, typval_T *rettv); static void f_haslocaldir(typval_T *argvars, typval_T *rettv); static void f_hasmapto(typval_T *argvars, typval_T *rettv); static void f_histadd(typval_T *argvars, typval_T *rettv); --- 180,185 ---- *************** *** 207,215 **** static void f_isinf(typval_T *argvars, typval_T *rettv); static void f_isnan(typval_T *argvars, typval_T *rettv); #endif - static void f_items(typval_T *argvars, typval_T *rettv); - static void f_join(typval_T *argvars, typval_T *rettv); - static void f_keys(typval_T *argvars, typval_T *rettv); static void f_last_buffer_nr(typval_T *argvars, typval_T *rettv); static void f_len(typval_T *argvars, typval_T *rettv); static void f_libcall(typval_T *argvars, typval_T *rettv); --- 206,211 ---- *************** *** 217,223 **** static void f_line(typval_T *argvars, typval_T *rettv); static void f_line2byte(typval_T *argvars, typval_T *rettv); static void f_lispindent(typval_T *argvars, typval_T *rettv); - static void f_list2str(typval_T *argvars, typval_T *rettv); static void f_localtime(typval_T *argvars, typval_T *rettv); #ifdef FEAT_FLOAT static void f_log(typval_T *argvars, typval_T *rettv); --- 213,218 ---- *************** *** 330,336 **** static void f_sin(typval_T *argvars, typval_T *rettv); static void f_sinh(typval_T *argvars, typval_T *rettv); #endif - static void f_sort(typval_T *argvars, typval_T *rettv); static void f_soundfold(typval_T *argvars, typval_T *rettv); static void f_spellbadword(typval_T *argvars, typval_T *rettv); static void f_spellsuggest(typval_T *argvars, typval_T *rettv); --- 325,330 ---- *************** *** 392,399 **** static void f_type(typval_T *argvars, typval_T *rettv); static void f_undofile(typval_T *argvars, typval_T *rettv); static void f_undotree(typval_T *argvars, typval_T *rettv); - static void f_uniq(typval_T *argvars, typval_T *rettv); - static void f_values(typval_T *argvars, typval_T *rettv); static void f_virtcol(typval_T *argvars, typval_T *rettv); static void f_visualmode(typval_T *argvars, typval_T *rettv); static void f_wildmenumode(typval_T *argvars, typval_T *rettv); --- 386,391 ---- *************** *** 6581,6604 **** } /* - * "has_key()" function - */ - static void - f_has_key(typval_T *argvars, typval_T *rettv) - { - if (argvars[0].v_type != VAR_DICT) - { - emsg(_(e_dictreq)); - return; - } - if (argvars[0].vval.v_dict == NULL) - return; - - rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, - tv_get_string(&argvars[1]), -1) != NULL; - } - - /* * "haslocaldir()" function */ static void --- 6573,6578 ---- *************** *** 7241,7298 **** #endif /* - * "items(dict)" function - */ - static void - f_items(typval_T *argvars, typval_T *rettv) - { - dict_list(argvars, rettv, 2); - } - - /* - * "join()" function - */ - static void - f_join(typval_T *argvars, typval_T *rettv) - { - garray_T ga; - char_u *sep; - - if (argvars[0].v_type != VAR_LIST) - { - emsg(_(e_listreq)); - return; - } - if (argvars[0].vval.v_list == NULL) - return; - if (argvars[1].v_type == VAR_UNKNOWN) - sep = (char_u *)" "; - else - sep = tv_get_string_chk(&argvars[1]); - - rettv->v_type = VAR_STRING; - - if (sep != NULL) - { - ga_init2(&ga, (int)sizeof(char), 80); - list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); - ga_append(&ga, NUL); - rettv->vval.v_string = (char_u *)ga.ga_data; - } - else - rettv->vval.v_string = NULL; - } - - /* - * "keys()" function - */ - static void - f_keys(typval_T *argvars, typval_T *rettv) - { - dict_list(argvars, rettv, 0); - } - - /* * "last_buffer_nr()" function. */ static void --- 7215,7220 ---- *************** *** 7460,7520 **** } /* - * "list2str()" function - */ - static void - f_list2str(typval_T *argvars, typval_T *rettv) - { - list_T *l; - listitem_T *li; - garray_T ga; - int utf8 = FALSE; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - if (argvars[0].v_type != VAR_LIST) - { - emsg(_(e_invarg)); - return; - } - - l = argvars[0].vval.v_list; - if (l == NULL) - return; // empty list results in empty string - - if (argvars[1].v_type != VAR_UNKNOWN) - utf8 = (int)tv_get_number_chk(&argvars[1], NULL); - - ga_init2(&ga, 1, 80); - if (has_mbyte || utf8) - { - char_u buf[MB_MAXBYTES + 1]; - int (*char2bytes)(int, char_u *); - - if (utf8 || enc_utf8) - char2bytes = utf_char2bytes; - else - char2bytes = mb_char2bytes; - - for (li = l->lv_first; li != NULL; li = li->li_next) - { - buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL; - ga_concat(&ga, buf); - } - ga_append(&ga, NUL); - } - else if (ga_grow(&ga, list_len(l) + 1) == OK) - { - for (li = l->lv_first; li != NULL; li = li->li_next) - ga_append(&ga, tv_get_number(&li->li_tv)); - ga_append(&ga, NUL); - } - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = ga.ga_data; - } - - /* * "localtime()" function */ static void --- 7382,7387 ---- *************** *** 9237,9394 **** static void f_remove(typval_T *argvars, typval_T *rettv) { - list_T *l; - listitem_T *item, *item2; - listitem_T *li; - long idx; - long end; - char_u *key; - dict_T *d; - dictitem_T *di; char_u *arg_errmsg = (char_u *)N_("remove() argument"); - int error = FALSE; if (argvars[0].v_type == VAR_DICT) ! { ! if (argvars[2].v_type != VAR_UNKNOWN) ! semsg(_(e_toomanyarg), "remove()"); ! else if ((d = argvars[0].vval.v_dict) != NULL ! && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) ! { ! key = tv_get_string_chk(&argvars[1]); ! if (key != NULL) ! { ! di = dict_find(d, key, -1); ! if (di == NULL) ! semsg(_(e_dictkey), key); ! else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) ! && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) ! { ! *rettv = di->di_tv; ! init_tv(&di->di_tv); ! dictitem_remove(d, di); ! } ! } ! } ! } else if (argvars[0].v_type == VAR_BLOB) ! { ! idx = (long)tv_get_number_chk(&argvars[1], &error); ! if (!error) ! { ! blob_T *b = argvars[0].vval.v_blob; ! int len = blob_len(b); ! char_u *p; ! ! if (idx < 0) ! // count from the end ! idx = len + idx; ! if (idx < 0 || idx >= len) ! { ! semsg(_(e_blobidx), idx); ! return; ! } ! if (argvars[2].v_type == VAR_UNKNOWN) ! { ! // Remove one item, return its value. ! p = (char_u *)b->bv_ga.ga_data; ! rettv->vval.v_number = (varnumber_T) *(p + idx); ! mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); ! --b->bv_ga.ga_len; ! } ! else ! { ! blob_T *blob; ! ! // Remove range of items, return list with values. ! end = (long)tv_get_number_chk(&argvars[2], &error); ! if (error) ! return; ! if (end < 0) ! // count from the end ! end = len + end; ! if (end >= len || idx > end) ! { ! semsg(_(e_blobidx), end); ! return; ! } ! blob = blob_alloc(); ! if (blob == NULL) ! return; ! blob->bv_ga.ga_len = end - idx + 1; ! if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) ! { ! vim_free(blob); ! return; ! } ! p = (char_u *)b->bv_ga.ga_data; ! mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, ! (size_t)(end - idx + 1)); ! ++blob->bv_refcount; ! rettv->v_type = VAR_BLOB; ! rettv->vval.v_blob = blob; ! ! mch_memmove(p + idx, p + end + 1, (size_t)(len - end)); ! b->bv_ga.ga_len -= end - idx + 1; ! } ! } ! } ! else if (argvars[0].v_type != VAR_LIST) semsg(_(e_listdictblobarg), "remove()"); - else if ((l = argvars[0].vval.v_list) != NULL - && !var_check_lock(l->lv_lock, arg_errmsg, TRUE)) - { - idx = (long)tv_get_number_chk(&argvars[1], &error); - if (error) - ; // type error: do nothing, errmsg already given - else if ((item = list_find(l, idx)) == NULL) - semsg(_(e_listidx), idx); - else - { - if (argvars[2].v_type == VAR_UNKNOWN) - { - /* Remove one item, return its value. */ - vimlist_remove(l, item, item); - *rettv = item->li_tv; - vim_free(item); - } - else - { - // Remove range of items, return list with values. - end = (long)tv_get_number_chk(&argvars[2], &error); - if (error) - ; // type error: do nothing - else if ((item2 = list_find(l, end)) == NULL) - semsg(_(e_listidx), end); - else - { - int cnt = 0; - - for (li = item; li != NULL; li = li->li_next) - { - ++cnt; - if (li == item2) - break; - } - if (li == NULL) /* didn't find "item2" after "item" */ - emsg(_(e_invrange)); - else - { - vimlist_remove(l, item, item2); - if (rettv_list_alloc(rettv) == OK) - { - l = rettv->vval.v_list; - l->lv_first = item; - l->lv_last = item2; - item->li_prev = NULL; - item2->li_next = NULL; - l->lv_len = cnt; - } - } - } - } - } - } } /* --- 9104,9119 ---- static void f_remove(typval_T *argvars, typval_T *rettv) { char_u *arg_errmsg = (char_u *)N_("remove() argument"); if (argvars[0].v_type == VAR_DICT) ! dict_remove(argvars, rettv, arg_errmsg); else if (argvars[0].v_type == VAR_BLOB) ! blob_remove(argvars, rettv); ! else if (argvars[0].v_type == VAR_LIST) ! list_remove(argvars, rettv, arg_errmsg); ! else semsg(_(e_listdictblobarg), "remove()"); } /* *************** *** 11100,11487 **** } #endif - static int item_compare(const void *s1, const void *s2); - static int item_compare2(const void *s1, const void *s2); - - /* struct used in the array that's given to qsort() */ - typedef struct - { - listitem_T *item; - int idx; - } sortItem_T; - - /* struct storing information about current sort */ - typedef struct - { - int item_compare_ic; - int item_compare_numeric; - int item_compare_numbers; - #ifdef FEAT_FLOAT - int item_compare_float; - #endif - char_u *item_compare_func; - partial_T *item_compare_partial; - dict_T *item_compare_selfdict; - int item_compare_func_err; - int item_compare_keep_zero; - } sortinfo_T; - static sortinfo_T *sortinfo = NULL; - #define ITEM_COMPARE_FAIL 999 - - /* - * Compare functions for f_sort() and f_uniq() below. - */ - static int - item_compare(const void *s1, const void *s2) - { - sortItem_T *si1, *si2; - typval_T *tv1, *tv2; - char_u *p1, *p2; - char_u *tofree1 = NULL, *tofree2 = NULL; - int res; - char_u numbuf1[NUMBUFLEN]; - char_u numbuf2[NUMBUFLEN]; - - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - tv1 = &si1->item->li_tv; - tv2 = &si2->item->li_tv; - - if (sortinfo->item_compare_numbers) - { - varnumber_T v1 = tv_get_number(tv1); - varnumber_T v2 = tv_get_number(tv2); - - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - } - - #ifdef FEAT_FLOAT - if (sortinfo->item_compare_float) - { - float_T v1 = tv_get_float(tv1); - float_T v2 = tv_get_float(tv2); - - return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; - } - #endif - - /* tv2string() puts quotes around a string and allocates memory. Don't do - * that for string variables. Use a single quote when comparing with a - * non-string to do what the docs promise. */ - if (tv1->v_type == VAR_STRING) - { - if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) - p1 = (char_u *)"'"; - else - p1 = tv1->vval.v_string; - } - else - p1 = tv2string(tv1, &tofree1, numbuf1, 0); - if (tv2->v_type == VAR_STRING) - { - if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) - p2 = (char_u *)"'"; - else - p2 = tv2->vval.v_string; - } - else - p2 = tv2string(tv2, &tofree2, numbuf2, 0); - if (p1 == NULL) - p1 = (char_u *)""; - if (p2 == NULL) - p2 = (char_u *)""; - if (!sortinfo->item_compare_numeric) - { - if (sortinfo->item_compare_ic) - res = STRICMP(p1, p2); - else - res = STRCMP(p1, p2); - } - else - { - double n1, n2; - n1 = strtod((char *)p1, (char **)&p1); - n2 = strtod((char *)p2, (char **)&p2); - res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; - } - - /* When the result would be zero, compare the item indexes. Makes the - * sort stable. */ - if (res == 0 && !sortinfo->item_compare_keep_zero) - res = si1->idx > si2->idx ? 1 : -1; - - vim_free(tofree1); - vim_free(tofree2); - return res; - } - - static int - item_compare2(const void *s1, const void *s2) - { - sortItem_T *si1, *si2; - int res; - typval_T rettv; - typval_T argv[3]; - int dummy; - char_u *func_name; - partial_T *partial = sortinfo->item_compare_partial; - - /* shortcut after failure in previous call; compare all items equal */ - if (sortinfo->item_compare_func_err) - return 0; - - si1 = (sortItem_T *)s1; - si2 = (sortItem_T *)s2; - - if (partial == NULL) - func_name = sortinfo->item_compare_func; - else - func_name = partial_name(partial); - - /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED - * in the copy without changing the original list items. */ - copy_tv(&si1->item->li_tv, &argv[0]); - copy_tv(&si2->item->li_tv, &argv[1]); - - rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ - res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, - partial, sortinfo->item_compare_selfdict); - clear_tv(&argv[0]); - clear_tv(&argv[1]); - - if (res == FAIL) - res = ITEM_COMPARE_FAIL; - else - res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); - if (sortinfo->item_compare_func_err) - res = ITEM_COMPARE_FAIL; /* return value has wrong type */ - clear_tv(&rettv); - - /* When the result would be zero, compare the pointers themselves. Makes - * the sort stable. */ - if (res == 0 && !sortinfo->item_compare_keep_zero) - res = si1->idx > si2->idx ? 1 : -1; - - return res; - } - - /* - * "sort({list})" function - */ - static void - do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) - { - list_T *l; - listitem_T *li; - sortItem_T *ptrs; - sortinfo_T *old_sortinfo; - sortinfo_T info; - long len; - long i; - - /* Pointer to current info struct used in compare function. Save and - * restore the current one for nested calls. */ - old_sortinfo = sortinfo; - sortinfo = &info; - - if (argvars[0].v_type != VAR_LIST) - semsg(_(e_listarg), sort ? "sort()" : "uniq()"); - else - { - l = argvars[0].vval.v_list; - if (l == NULL || var_check_lock(l->lv_lock, - (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), - TRUE)) - goto theend; - rettv_list_set(rettv, l); - - len = list_len(l); - if (len <= 1) - goto theend; /* short list sorts pretty quickly */ - - info.item_compare_ic = FALSE; - info.item_compare_numeric = FALSE; - info.item_compare_numbers = FALSE; - #ifdef FEAT_FLOAT - info.item_compare_float = FALSE; - #endif - info.item_compare_func = NULL; - info.item_compare_partial = NULL; - info.item_compare_selfdict = NULL; - if (argvars[1].v_type != VAR_UNKNOWN) - { - /* optional second argument: {func} */ - if (argvars[1].v_type == VAR_FUNC) - info.item_compare_func = argvars[1].vval.v_string; - else if (argvars[1].v_type == VAR_PARTIAL) - info.item_compare_partial = argvars[1].vval.v_partial; - else - { - int error = FALSE; - - i = (long)tv_get_number_chk(&argvars[1], &error); - if (error) - goto theend; /* type error; errmsg already given */ - if (i == 1) - info.item_compare_ic = TRUE; - else if (argvars[1].v_type != VAR_NUMBER) - info.item_compare_func = tv_get_string(&argvars[1]); - else if (i != 0) - { - emsg(_(e_invarg)); - goto theend; - } - if (info.item_compare_func != NULL) - { - if (*info.item_compare_func == NUL) - { - /* empty string means default sort */ - info.item_compare_func = NULL; - } - else if (STRCMP(info.item_compare_func, "n") == 0) - { - info.item_compare_func = NULL; - info.item_compare_numeric = TRUE; - } - else if (STRCMP(info.item_compare_func, "N") == 0) - { - info.item_compare_func = NULL; - info.item_compare_numbers = TRUE; - } - #ifdef FEAT_FLOAT - else if (STRCMP(info.item_compare_func, "f") == 0) - { - info.item_compare_func = NULL; - info.item_compare_float = TRUE; - } - #endif - else if (STRCMP(info.item_compare_func, "i") == 0) - { - info.item_compare_func = NULL; - info.item_compare_ic = TRUE; - } - } - } - - if (argvars[2].v_type != VAR_UNKNOWN) - { - /* optional third argument: {dict} */ - if (argvars[2].v_type != VAR_DICT) - { - emsg(_(e_dictreq)); - goto theend; - } - info.item_compare_selfdict = argvars[2].vval.v_dict; - } - } - - /* Make an array with each entry pointing to an item in the List. */ - ptrs = ALLOC_MULT(sortItem_T, len); - if (ptrs == NULL) - goto theend; - - i = 0; - if (sort) - { - /* sort(): ptrs will be the list to sort */ - for (li = l->lv_first; li != NULL; li = li->li_next) - { - ptrs[i].item = li; - ptrs[i].idx = i; - ++i; - } - - info.item_compare_func_err = FALSE; - info.item_compare_keep_zero = FALSE; - /* test the compare function */ - if ((info.item_compare_func != NULL - || info.item_compare_partial != NULL) - && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) - == ITEM_COMPARE_FAIL) - emsg(_("E702: Sort compare function failed")); - else - { - /* Sort the array with item pointers. */ - qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), - info.item_compare_func == NULL - && info.item_compare_partial == NULL - ? item_compare : item_compare2); - - if (!info.item_compare_func_err) - { - /* Clear the List and append the items in sorted order. */ - l->lv_first = l->lv_last = l->lv_idx_item = NULL; - l->lv_len = 0; - for (i = 0; i < len; ++i) - list_append(l, ptrs[i].item); - } - } - } - else - { - int (*item_compare_func_ptr)(const void *, const void *); - - /* f_uniq(): ptrs will be a stack of items to remove */ - info.item_compare_func_err = FALSE; - info.item_compare_keep_zero = TRUE; - item_compare_func_ptr = info.item_compare_func != NULL - || info.item_compare_partial != NULL - ? item_compare2 : item_compare; - - for (li = l->lv_first; li != NULL && li->li_next != NULL; - li = li->li_next) - { - if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) - == 0) - ptrs[i++].item = li; - if (info.item_compare_func_err) - { - emsg(_("E882: Uniq compare function failed")); - break; - } - } - - if (!info.item_compare_func_err) - { - while (--i >= 0) - { - li = ptrs[i].item->li_next; - ptrs[i].item->li_next = li->li_next; - if (li->li_next != NULL) - li->li_next->li_prev = ptrs[i].item; - else - l->lv_last = ptrs[i].item; - list_fix_watch(l, li); - listitem_free(li); - l->lv_len--; - } - } - } - - vim_free(ptrs); - } - theend: - sortinfo = old_sortinfo; - } - - /* - * "sort({list})" function - */ - static void - f_sort(typval_T *argvars, typval_T *rettv) - { - do_sort_uniq(argvars, rettv, TRUE); - } - - /* - * "uniq({list})" function - */ - static void - f_uniq(typval_T *argvars, typval_T *rettv) - { - do_sort_uniq(argvars, rettv, FALSE); - } - /* * "soundfold({word})" function */ --- 10825,10830 ---- *************** *** 13482,13496 **** } /* - * "values(dict)" function - */ - static void - f_values(typval_T *argvars, typval_T *rettv) - { - dict_list(argvars, rettv, 1); - } - - /* * "virtcol(string)" function */ static void --- 12825,12830 ---- *** ../vim-8.1.1762/src/dict.c 2019-07-13 22:46:05.241628584 +0200 --- src/dict.c 2019-07-27 22:50:23.167117701 +0200 *************** *** 709,715 **** } /* ! * Get the key for *{key: val} into "tv" and advance "arg". * Return FAIL when there is no valid key. */ static int --- 709,715 ---- } /* ! * Get the key for #{key: val} into "tv" and advance "arg". * Return FAIL when there is no valid key. */ static int *************** *** 731,737 **** /* * Allocate a variable for a Dictionary and fill it from "*arg". ! * "literal" is TRUE for *{key: val} * Return OK or FAIL. Returns NOTDONE for {expr}. */ int --- 731,737 ---- /* * Allocate a variable for a Dictionary and fill it from "*arg". ! * "literal" is TRUE for #{key: val} * Return OK or FAIL. Returns NOTDONE for {expr}. */ int *************** *** 1034,1039 **** --- 1034,1066 ---- } /* + * "items(dict)" function + */ + void + f_items(typval_T *argvars, typval_T *rettv) + { + dict_list(argvars, rettv, 2); + } + + /* + * "keys()" function + */ + void + f_keys(typval_T *argvars, typval_T *rettv) + { + dict_list(argvars, rettv, 0); + } + + /* + * "values(dict)" function + */ + void + f_values(typval_T *argvars, typval_T *rettv) + { + dict_list(argvars, rettv, 1); + } + + /* * Make each item in the dict readonly (not the value of the item). */ void *************** *** 1052,1055 **** --- 1079,1132 ---- } } + /* + * "has_key()" function + */ + void + f_has_key(typval_T *argvars, typval_T *rettv) + { + if (argvars[0].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + return; + } + if (argvars[0].vval.v_dict == NULL) + return; + + rettv->vval.v_number = dict_find(argvars[0].vval.v_dict, + tv_get_string(&argvars[1]), -1) != NULL; + } + + /* + * "remove({dict})" function + */ + void + dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) + { + dict_T *d; + char_u *key; + dictitem_T *di; + + if (argvars[2].v_type != VAR_UNKNOWN) + semsg(_(e_toomanyarg), "remove()"); + else if ((d = argvars[0].vval.v_dict) != NULL + && !var_check_lock(d->dv_lock, arg_errmsg, TRUE)) + { + key = tv_get_string_chk(&argvars[1]); + if (key != NULL) + { + di = dict_find(d, key, -1); + if (di == NULL) + semsg(_(e_dictkey), key); + else if (!var_check_fixed(di->di_flags, arg_errmsg, TRUE) + && !var_check_ro(di->di_flags, arg_errmsg, TRUE)) + { + *rettv = di->di_tv; + init_tv(&di->di_tv); + dictitem_remove(d, di); + } + } + } + } + #endif /* defined(FEAT_EVAL) */ *** ../vim-8.1.1762/src/proto/dict.pro 2019-07-13 22:46:05.241628584 +0200 --- src/proto/dict.pro 2019-07-27 22:50:26.515101568 +0200 *************** *** 33,37 **** --- 33,42 ---- 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); + void f_items(typval_T *argvars, typval_T *rettv); + void f_keys(typval_T *argvars, typval_T *rettv); + void f_values(typval_T *argvars, typval_T *rettv); void dict_set_items_ro(dict_T *di); + void f_has_key(typval_T *argvars, typval_T *rettv); + void dict_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg); /* vim: set ft=c : */ *** ../vim-8.1.1762/src/list.c 2019-07-04 17:35:01.115169990 +0200 --- src/list.c 2019-07-27 23:00:08.020267209 +0200 *************** *** 875,880 **** --- 875,914 ---- } /* + * "join()" function + */ + void + f_join(typval_T *argvars, typval_T *rettv) + { + garray_T ga; + char_u *sep; + + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_listreq)); + return; + } + if (argvars[0].vval.v_list == NULL) + return; + if (argvars[1].v_type == VAR_UNKNOWN) + sep = (char_u *)" "; + else + sep = tv_get_string_chk(&argvars[1]); + + rettv->v_type = VAR_STRING; + + if (sep != NULL) + { + ga_init2(&ga, (int)sizeof(char), 80); + list_join(&ga, argvars[0].vval.v_list, sep, TRUE, FALSE, 0); + ga_append(&ga, NUL); + rettv->vval.v_string = (char_u *)ga.ga_data; + } + else + rettv->vval.v_string = NULL; + } + + /* * Allocate a variable for a List and fill it from "*arg". * Return OK or FAIL. */ *************** *** 1007,1010 **** --- 1041,1547 ---- } } + /* + * "list2str()" function + */ + void + f_list2str(typval_T *argvars, typval_T *rettv) + { + list_T *l; + listitem_T *li; + garray_T ga; + int utf8 = FALSE; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + if (argvars[0].v_type != VAR_LIST) + { + emsg(_(e_invarg)); + return; + } + + l = argvars[0].vval.v_list; + if (l == NULL) + return; // empty list results in empty string + + if (argvars[1].v_type != VAR_UNKNOWN) + utf8 = (int)tv_get_number_chk(&argvars[1], NULL); + + ga_init2(&ga, 1, 80); + if (has_mbyte || utf8) + { + char_u buf[MB_MAXBYTES + 1]; + int (*char2bytes)(int, char_u *); + + if (utf8 || enc_utf8) + char2bytes = utf_char2bytes; + else + char2bytes = mb_char2bytes; + + for (li = l->lv_first; li != NULL; li = li->li_next) + { + buf[(*char2bytes)(tv_get_number(&li->li_tv), buf)] = NUL; + ga_concat(&ga, buf); + } + ga_append(&ga, NUL); + } + else if (ga_grow(&ga, list_len(l) + 1) == OK) + { + for (li = l->lv_first; li != NULL; li = li->li_next) + ga_append(&ga, tv_get_number(&li->li_tv)); + ga_append(&ga, NUL); + } + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = ga.ga_data; + } + + void + list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg) + { + list_T *l; + listitem_T *item, *item2; + listitem_T *li; + int error = FALSE; + int idx; + + if ((l = argvars[0].vval.v_list) == NULL + || var_check_lock(l->lv_lock, arg_errmsg, TRUE)) + return; + + idx = (long)tv_get_number_chk(&argvars[1], &error); + if (error) + ; // type error: do nothing, errmsg already given + else if ((item = list_find(l, idx)) == NULL) + semsg(_(e_listidx), idx); + else + { + if (argvars[2].v_type == VAR_UNKNOWN) + { + /* Remove one item, return its value. */ + vimlist_remove(l, item, item); + *rettv = item->li_tv; + vim_free(item); + } + else + { + // Remove range of items, return list with values. + int end = (long)tv_get_number_chk(&argvars[2], &error); + + if (error) + ; // type error: do nothing + else if ((item2 = list_find(l, end)) == NULL) + semsg(_(e_listidx), end); + else + { + int cnt = 0; + + for (li = item; li != NULL; li = li->li_next) + { + ++cnt; + if (li == item2) + break; + } + if (li == NULL) /* didn't find "item2" after "item" */ + emsg(_(e_invrange)); + else + { + vimlist_remove(l, item, item2); + if (rettv_list_alloc(rettv) == OK) + { + l = rettv->vval.v_list; + l->lv_first = item; + l->lv_last = item2; + item->li_prev = NULL; + item2->li_next = NULL; + l->lv_len = cnt; + } + } + } + } + } + } + + static int item_compare(const void *s1, const void *s2); + static int item_compare2(const void *s1, const void *s2); + + /* struct used in the array that's given to qsort() */ + typedef struct + { + listitem_T *item; + int idx; + } sortItem_T; + + /* struct storing information about current sort */ + typedef struct + { + int item_compare_ic; + int item_compare_numeric; + int item_compare_numbers; + #ifdef FEAT_FLOAT + int item_compare_float; + #endif + char_u *item_compare_func; + partial_T *item_compare_partial; + dict_T *item_compare_selfdict; + int item_compare_func_err; + int item_compare_keep_zero; + } sortinfo_T; + static sortinfo_T *sortinfo = NULL; + #define ITEM_COMPARE_FAIL 999 + + /* + * Compare functions for f_sort() and f_uniq() below. + */ + static int + item_compare(const void *s1, const void *s2) + { + sortItem_T *si1, *si2; + typval_T *tv1, *tv2; + char_u *p1, *p2; + char_u *tofree1 = NULL, *tofree2 = NULL; + int res; + char_u numbuf1[NUMBUFLEN]; + char_u numbuf2[NUMBUFLEN]; + + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + tv1 = &si1->item->li_tv; + tv2 = &si2->item->li_tv; + + if (sortinfo->item_compare_numbers) + { + varnumber_T v1 = tv_get_number(tv1); + varnumber_T v2 = tv_get_number(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + + #ifdef FEAT_FLOAT + if (sortinfo->item_compare_float) + { + float_T v1 = tv_get_float(tv1); + float_T v2 = tv_get_float(tv2); + + return v1 == v2 ? 0 : v1 > v2 ? 1 : -1; + } + #endif + + /* tv2string() puts quotes around a string and allocates memory. Don't do + * that for string variables. Use a single quote when comparing with a + * non-string to do what the docs promise. */ + if (tv1->v_type == VAR_STRING) + { + if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric) + p1 = (char_u *)"'"; + else + p1 = tv1->vval.v_string; + } + else + p1 = tv2string(tv1, &tofree1, numbuf1, 0); + if (tv2->v_type == VAR_STRING) + { + if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric) + p2 = (char_u *)"'"; + else + p2 = tv2->vval.v_string; + } + else + p2 = tv2string(tv2, &tofree2, numbuf2, 0); + if (p1 == NULL) + p1 = (char_u *)""; + if (p2 == NULL) + p2 = (char_u *)""; + if (!sortinfo->item_compare_numeric) + { + if (sortinfo->item_compare_ic) + res = STRICMP(p1, p2); + else + res = STRCMP(p1, p2); + } + else + { + double n1, n2; + n1 = strtod((char *)p1, (char **)&p1); + n2 = strtod((char *)p2, (char **)&p2); + res = n1 == n2 ? 0 : n1 > n2 ? 1 : -1; + } + + /* When the result would be zero, compare the item indexes. Makes the + * sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + vim_free(tofree1); + vim_free(tofree2); + return res; + } + + static int + item_compare2(const void *s1, const void *s2) + { + sortItem_T *si1, *si2; + int res; + typval_T rettv; + typval_T argv[3]; + int dummy; + char_u *func_name; + partial_T *partial = sortinfo->item_compare_partial; + + /* shortcut after failure in previous call; compare all items equal */ + if (sortinfo->item_compare_func_err) + return 0; + + si1 = (sortItem_T *)s1; + si2 = (sortItem_T *)s2; + + if (partial == NULL) + func_name = sortinfo->item_compare_func; + else + func_name = partial_name(partial); + + /* Copy the values. This is needed to be able to set v_lock to VAR_FIXED + * in the copy without changing the original list items. */ + copy_tv(&si1->item->li_tv, &argv[0]); + copy_tv(&si2->item->li_tv, &argv[1]); + + rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */ + res = call_func(func_name, -1, &rettv, 2, argv, NULL, 0L, 0L, &dummy, TRUE, + partial, sortinfo->item_compare_selfdict); + clear_tv(&argv[0]); + clear_tv(&argv[1]); + + if (res == FAIL) + res = ITEM_COMPARE_FAIL; + else + res = (int)tv_get_number_chk(&rettv, &sortinfo->item_compare_func_err); + if (sortinfo->item_compare_func_err) + res = ITEM_COMPARE_FAIL; /* return value has wrong type */ + clear_tv(&rettv); + + /* When the result would be zero, compare the pointers themselves. Makes + * the sort stable. */ + if (res == 0 && !sortinfo->item_compare_keep_zero) + res = si1->idx > si2->idx ? 1 : -1; + + return res; + } + + /* + * "sort()" or "uniq()" function + */ + static void + do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort) + { + list_T *l; + listitem_T *li; + sortItem_T *ptrs; + sortinfo_T *old_sortinfo; + sortinfo_T info; + long len; + long i; + + /* Pointer to current info struct used in compare function. Save and + * restore the current one for nested calls. */ + old_sortinfo = sortinfo; + sortinfo = &info; + + if (argvars[0].v_type != VAR_LIST) + semsg(_(e_listarg), sort ? "sort()" : "uniq()"); + else + { + l = argvars[0].vval.v_list; + if (l == NULL || var_check_lock(l->lv_lock, + (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")), + TRUE)) + goto theend; + rettv_list_set(rettv, l); + + len = list_len(l); + if (len <= 1) + goto theend; /* short list sorts pretty quickly */ + + info.item_compare_ic = FALSE; + info.item_compare_numeric = FALSE; + info.item_compare_numbers = FALSE; + #ifdef FEAT_FLOAT + info.item_compare_float = FALSE; + #endif + info.item_compare_func = NULL; + info.item_compare_partial = NULL; + info.item_compare_selfdict = NULL; + if (argvars[1].v_type != VAR_UNKNOWN) + { + /* optional second argument: {func} */ + if (argvars[1].v_type == VAR_FUNC) + info.item_compare_func = argvars[1].vval.v_string; + else if (argvars[1].v_type == VAR_PARTIAL) + info.item_compare_partial = argvars[1].vval.v_partial; + else + { + int error = FALSE; + + i = (long)tv_get_number_chk(&argvars[1], &error); + if (error) + goto theend; /* type error; errmsg already given */ + if (i == 1) + info.item_compare_ic = TRUE; + else if (argvars[1].v_type != VAR_NUMBER) + info.item_compare_func = tv_get_string(&argvars[1]); + else if (i != 0) + { + emsg(_(e_invarg)); + goto theend; + } + if (info.item_compare_func != NULL) + { + if (*info.item_compare_func == NUL) + { + /* empty string means default sort */ + info.item_compare_func = NULL; + } + else if (STRCMP(info.item_compare_func, "n") == 0) + { + info.item_compare_func = NULL; + info.item_compare_numeric = TRUE; + } + else if (STRCMP(info.item_compare_func, "N") == 0) + { + info.item_compare_func = NULL; + info.item_compare_numbers = TRUE; + } + #ifdef FEAT_FLOAT + else if (STRCMP(info.item_compare_func, "f") == 0) + { + info.item_compare_func = NULL; + info.item_compare_float = TRUE; + } + #endif + else if (STRCMP(info.item_compare_func, "i") == 0) + { + info.item_compare_func = NULL; + info.item_compare_ic = TRUE; + } + } + } + + if (argvars[2].v_type != VAR_UNKNOWN) + { + /* optional third argument: {dict} */ + if (argvars[2].v_type != VAR_DICT) + { + emsg(_(e_dictreq)); + goto theend; + } + info.item_compare_selfdict = argvars[2].vval.v_dict; + } + } + + /* Make an array with each entry pointing to an item in the List. */ + ptrs = ALLOC_MULT(sortItem_T, len); + if (ptrs == NULL) + goto theend; + + i = 0; + if (sort) + { + /* sort(): ptrs will be the list to sort */ + for (li = l->lv_first; li != NULL; li = li->li_next) + { + ptrs[i].item = li; + ptrs[i].idx = i; + ++i; + } + + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = FALSE; + /* test the compare function */ + if ((info.item_compare_func != NULL + || info.item_compare_partial != NULL) + && item_compare2((void *)&ptrs[0], (void *)&ptrs[1]) + == ITEM_COMPARE_FAIL) + emsg(_("E702: Sort compare function failed")); + else + { + /* Sort the array with item pointers. */ + qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T), + info.item_compare_func == NULL + && info.item_compare_partial == NULL + ? item_compare : item_compare2); + + if (!info.item_compare_func_err) + { + /* Clear the List and append the items in sorted order. */ + l->lv_first = l->lv_last = l->lv_idx_item = NULL; + l->lv_len = 0; + for (i = 0; i < len; ++i) + list_append(l, ptrs[i].item); + } + } + } + else + { + int (*item_compare_func_ptr)(const void *, const void *); + + /* f_uniq(): ptrs will be a stack of items to remove */ + info.item_compare_func_err = FALSE; + info.item_compare_keep_zero = TRUE; + item_compare_func_ptr = info.item_compare_func != NULL + || info.item_compare_partial != NULL + ? item_compare2 : item_compare; + + for (li = l->lv_first; li != NULL && li->li_next != NULL; + li = li->li_next) + { + if (item_compare_func_ptr((void *)&li, (void *)&li->li_next) + == 0) + ptrs[i++].item = li; + if (info.item_compare_func_err) + { + emsg(_("E882: Uniq compare function failed")); + break; + } + } + + if (!info.item_compare_func_err) + { + while (--i >= 0) + { + li = ptrs[i].item->li_next; + ptrs[i].item->li_next = li->li_next; + if (li->li_next != NULL) + li->li_next->li_prev = ptrs[i].item; + else + l->lv_last = ptrs[i].item; + list_fix_watch(l, li); + listitem_free(li); + l->lv_len--; + } + } + } + + vim_free(ptrs); + } + theend: + sortinfo = old_sortinfo; + } + + /* + * "sort({list})" function + */ + void + f_sort(typval_T *argvars, typval_T *rettv) + { + do_sort_uniq(argvars, rettv, TRUE); + } + + /* + * "uniq({list})" function + */ + void + f_uniq(typval_T *argvars, typval_T *rettv) + { + do_sort_uniq(argvars, rettv, FALSE); + } + #endif /* defined(FEAT_EVAL) */ *** ../vim-8.1.1762/src/proto/list.pro 2018-12-21 15:16:57.479579788 +0100 --- src/proto/list.pro 2019-07-27 22:59:13.660534103 +0200 *************** *** 34,40 **** --- 34,45 ---- void vimlist_remove(list_T *l, listitem_T *item, listitem_T *item2); char_u *list2string(typval_T *tv, int copyID, int restore_copyID); int list_join(garray_T *gap, list_T *l, char_u *sep, int echo_style, int restore_copyID, int copyID); + void f_join(typval_T *argvars, typval_T *rettv); int get_list_tv(char_u **arg, typval_T *rettv, int evaluate); int write_list(FILE *fd, list_T *list, int binary); void init_static_list(staticList10_T *sl); + void f_list2str(typval_T *argvars, typval_T *rettv); + void list_remove(typval_T *argvars, typval_T *rettv, char_u *arg_errmsg); + void f_sort(typval_T *argvars, typval_T *rettv); + void f_uniq(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-8.1.1762/src/blob.c 2019-07-12 20:16:57.075879743 +0200 --- src/blob.c 2019-07-27 22:53:38.198173522 +0200 *************** *** 258,261 **** --- 258,331 ---- return NULL; } + /* + * "remove({blob})" function + */ + void + blob_remove(typval_T *argvars, typval_T *rettv) + { + int error = FALSE; + long idx = (long)tv_get_number_chk(&argvars[1], &error); + long end; + + if (!error) + { + blob_T *b = argvars[0].vval.v_blob; + int len = blob_len(b); + char_u *p; + + if (idx < 0) + // count from the end + idx = len + idx; + if (idx < 0 || idx >= len) + { + semsg(_(e_blobidx), idx); + return; + } + if (argvars[2].v_type == VAR_UNKNOWN) + { + // Remove one item, return its value. + p = (char_u *)b->bv_ga.ga_data; + rettv->vval.v_number = (varnumber_T) *(p + idx); + mch_memmove(p + idx, p + idx + 1, (size_t)len - idx - 1); + --b->bv_ga.ga_len; + } + else + { + blob_T *blob; + + // Remove range of items, return list with values. + end = (long)tv_get_number_chk(&argvars[2], &error); + if (error) + return; + if (end < 0) + // count from the end + end = len + end; + if (end >= len || idx > end) + { + semsg(_(e_blobidx), end); + return; + } + blob = blob_alloc(); + if (blob == NULL) + return; + blob->bv_ga.ga_len = end - idx + 1; + if (ga_grow(&blob->bv_ga, end - idx + 1) == FAIL) + { + vim_free(blob); + return; + } + p = (char_u *)b->bv_ga.ga_data; + mch_memmove((char_u *)blob->bv_ga.ga_data, p + idx, + (size_t)(end - idx + 1)); + ++blob->bv_refcount; + rettv->v_type = VAR_BLOB; + rettv->vval.v_blob = blob; + + mch_memmove(p + idx, p + end + 1, (size_t)(len - end)); + b->bv_ga.ga_len -= end - idx + 1; + } + } + } + #endif /* defined(FEAT_EVAL) */ *** ../vim-8.1.1762/src/proto/blob.pro 2019-01-24 12:31:40.752926550 +0100 --- src/proto/blob.pro 2019-07-27 22:53:56.682083624 +0200 *************** *** 13,16 **** --- 13,17 ---- int write_blob(FILE *fd, blob_T *blob); char_u *blob2string(blob_T *blob, char_u **tofree, char_u *numbuf); blob_T *string2blob(char_u *str); + void blob_remove(typval_T *argvars, typval_T *rettv); /* vim: set ft=c : */ *** ../vim-8.1.1762/src/version.c 2019-07-27 21:56:44.836171922 +0200 --- src/version.c 2019-07-27 23:11:10.720264034 +0200 *************** *** 779,780 **** --- 779,782 ---- { /* Add new patch number below this line */ + /**/ + 1763, /**/ -- BEDEVERE: Wait. Wait ... tell me, what also floats on water? ALL: Bread? No, no, no. Apples .... gravy ... very small rocks ... ARTHUR: A duck. "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// 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 ///