]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 7.4.1394 v7.4.1394
authorBram Moolenaar <Bram@vim.org>
Mon, 22 Feb 2016 21:51:33 +0000 (22:51 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 22 Feb 2016 21:51:33 +0000 (22:51 +0100)
Problem:    Can't sort inside a sort function.
Solution:   Use a struct to store the sort parameters. (Jacob Niehus)

src/eval.c
src/testdir/test_sort.vim
src/version.c

index 1a569bef158904bfc29decf5b01f647ddfa506b0..bf3bcb948cfa0a55acc89b73ac0dcab556ff735a 100644 (file)
@@ -18790,16 +18790,21 @@ typedef struct
     int                idx;
 } sortItem_T;
 
-static int     item_compare_ic;
-static int     item_compare_numeric;
-static int     item_compare_numbers;
+/* struct storing information about current sort */
+typedef struct
+{
+    int                item_compare_ic;
+    int                item_compare_numeric;
+    int                item_compare_numbers;
 #ifdef FEAT_FLOAT
-static int     item_compare_float;
-#endif
-static char_u  *item_compare_func;
-static dict_T  *item_compare_selfdict;
-static int     item_compare_func_err;
-static int     item_compare_keep_zero;
+    int                item_compare_float;
+#endif
+    char_u     *item_compare_func;
+    dict_T     *item_compare_selfdict;
+    int                item_compare_func_err;
+    int                item_compare_keep_zero;
+} sortinfo_T;
+static sortinfo_T      *sortinfo = NULL;
 static void    do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort);
 #define ITEM_COMPARE_FAIL 999
 
@@ -18825,7 +18830,7 @@ item_compare(const void *s1, const void *s2)
     tv1 = &si1->item->li_tv;
     tv2 = &si2->item->li_tv;
 
-    if (item_compare_numbers)
+    if (sortinfo->item_compare_numbers)
     {
        long    v1 = get_tv_number(tv1);
        long    v2 = get_tv_number(tv2);
@@ -18834,7 +18839,7 @@ item_compare(const void *s1, const void *s2)
     }
 
 #ifdef FEAT_FLOAT
-    if (item_compare_float)
+    if (sortinfo->item_compare_float)
     {
        float_T v1 = get_tv_float(tv1);
        float_T v2 = get_tv_float(tv2);
@@ -18848,7 +18853,7 @@ item_compare(const void *s1, const void *s2)
      * non-string to do what the docs promise. */
     if (tv1->v_type == VAR_STRING)
     {
-       if (tv2->v_type != VAR_STRING || item_compare_numeric)
+       if (tv2->v_type != VAR_STRING || sortinfo->item_compare_numeric)
            p1 = (char_u *)"'";
        else
            p1 = tv1->vval.v_string;
@@ -18857,7 +18862,7 @@ item_compare(const void *s1, const void *s2)
        p1 = tv2string(tv1, &tofree1, numbuf1, 0);
     if (tv2->v_type == VAR_STRING)
     {
-       if (tv1->v_type != VAR_STRING || item_compare_numeric)
+       if (tv1->v_type != VAR_STRING || sortinfo->item_compare_numeric)
            p2 = (char_u *)"'";
        else
            p2 = tv2->vval.v_string;
@@ -18868,9 +18873,9 @@ item_compare(const void *s1, const void *s2)
        p1 = (char_u *)"";
     if (p2 == NULL)
        p2 = (char_u *)"";
-    if (!item_compare_numeric)
+    if (!sortinfo->item_compare_numeric)
     {
-       if (item_compare_ic)
+       if (sortinfo->item_compare_ic)
            res = STRICMP(p1, p2);
        else
            res = STRCMP(p1, p2);
@@ -18885,7 +18890,7 @@ item_compare(const void *s1, const void *s2)
 
     /* When the result would be zero, compare the item indexes.  Makes the
      * sort stable. */
-    if (res == 0 && !item_compare_keep_zero)
+    if (res == 0 && !sortinfo->item_compare_keep_zero)
        res = si1->idx > si2->idx ? 1 : -1;
 
     vim_free(tofree1);
@@ -18906,7 +18911,7 @@ item_compare2(const void *s1, const void *s2)
     int                dummy;
 
     /* shortcut after failure in previous call; compare all items equal */
-    if (item_compare_func_err)
+    if (sortinfo->item_compare_func_err)
        return 0;
 
     si1 = (sortItem_T *)s1;
@@ -18918,23 +18923,24 @@ item_compare2(const void *s1, const void *s2)
     copy_tv(&si2->item->li_tv, &argv[1]);
 
     rettv.v_type = VAR_UNKNOWN;                /* clear_tv() uses this */
-    res = call_func(item_compare_func, (int)STRLEN(item_compare_func),
+    res = call_func(sortinfo->item_compare_func,
+                                (int)STRLEN(sortinfo->item_compare_func),
                                 &rettv, 2, argv, 0L, 0L, &dummy, TRUE,
-                                item_compare_selfdict);
+                                sortinfo->item_compare_selfdict);
     clear_tv(&argv[0]);
     clear_tv(&argv[1]);
 
     if (res == FAIL)
        res = ITEM_COMPARE_FAIL;
     else
-       res = get_tv_number_chk(&rettv, &item_compare_func_err);
-    if (item_compare_func_err)
+       res = get_tv_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 && !item_compare_keep_zero)
+    if (res == 0 && !sortinfo->item_compare_keep_zero)
        res = si1->idx > si2->idx ? 1 : -1;
 
     return res;
@@ -18949,9 +18955,16 @@ 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)
        EMSG2(_(e_listarg), sort ? "sort()" : "uniq()");
     else
@@ -18960,62 +18973,62 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
        if (l == NULL || tv_check_lock(l->lv_lock,
             (char_u *)(sort ? N_("sort() argument") : N_("uniq() argument")),
                                                                        TRUE))
-           return;
+           goto theend;
        rettv->vval.v_list = l;
        rettv->v_type = VAR_LIST;
        ++l->lv_refcount;
 
        len = list_len(l);
        if (len <= 1)
-           return;     /* short list sorts pretty quickly */
+           goto theend;        /* short list sorts pretty quickly */
 
-       item_compare_ic = FALSE;
-       item_compare_numeric = FALSE;
-       item_compare_numbers = FALSE;
+       info.item_compare_ic = FALSE;
+       info.item_compare_numeric = FALSE;
+       info.item_compare_numbers = FALSE;
 #ifdef FEAT_FLOAT
-       item_compare_float = FALSE;
+       info.item_compare_float = FALSE;
 #endif
-       item_compare_func = NULL;
-       item_compare_selfdict = NULL;
+       info.item_compare_func = NULL;
+       info.item_compare_selfdict = NULL;
        if (argvars[1].v_type != VAR_UNKNOWN)
        {
            /* optional second argument: {func} */
            if (argvars[1].v_type == VAR_FUNC)
-               item_compare_func = argvars[1].vval.v_string;
+               info.item_compare_func = argvars[1].vval.v_string;
            else
            {
                int         error = FALSE;
 
                i = get_tv_number_chk(&argvars[1], &error);
                if (error)
-                   return;             /* type error; errmsg already given */
+                   goto theend;        /* type error; errmsg already given */
                if (i == 1)
-                   item_compare_ic = TRUE;
+                   info.item_compare_ic = TRUE;
                else
-                   item_compare_func = get_tv_string(&argvars[1]);
-               if (item_compare_func != NULL)
+                   info.item_compare_func = get_tv_string(&argvars[1]);
+               if (info.item_compare_func != NULL)
                {
-                   if (STRCMP(item_compare_func, "n") == 0)
+                   if (STRCMP(info.item_compare_func, "n") == 0)
                    {
-                       item_compare_func = NULL;
-                       item_compare_numeric = TRUE;
+                       info.item_compare_func = NULL;
+                       info.item_compare_numeric = TRUE;
                    }
-                   else if (STRCMP(item_compare_func, "N") == 0)
+                   else if (STRCMP(info.item_compare_func, "N") == 0)
                    {
-                       item_compare_func = NULL;
-                       item_compare_numbers = TRUE;
+                       info.item_compare_func = NULL;
+                       info.item_compare_numbers = TRUE;
                    }
 #ifdef FEAT_FLOAT
-                   else if (STRCMP(item_compare_func, "f") == 0)
+                   else if (STRCMP(info.item_compare_func, "f") == 0)
                    {
-                       item_compare_func = NULL;
-                       item_compare_float = TRUE;
+                       info.item_compare_func = NULL;
+                       info.item_compare_float = TRUE;
                    }
 #endif
-                   else if (STRCMP(item_compare_func, "i") == 0)
+                   else if (STRCMP(info.item_compare_func, "i") == 0)
                    {
-                       item_compare_func = NULL;
-                       item_compare_ic = TRUE;
+                       info.item_compare_func = NULL;
+                       info.item_compare_ic = TRUE;
                    }
                }
            }
@@ -19026,16 +19039,16 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
                if (argvars[2].v_type != VAR_DICT)
                {
                    EMSG(_(e_dictreq));
-                   return;
+                   goto theend;
                }
-               item_compare_selfdict = argvars[2].vval.v_dict;
+               info.item_compare_selfdict = argvars[2].vval.v_dict;
            }
        }
 
        /* Make an array with each entry pointing to an item in the List. */
        ptrs = (sortItem_T *)alloc((int)(len * sizeof(sortItem_T)));
        if (ptrs == NULL)
-           return;
+           goto theend;
 
        i = 0;
        if (sort)
@@ -19048,10 +19061,10 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
                ++i;
            }
 
-           item_compare_func_err = FALSE;
-           item_compare_keep_zero = FALSE;
+           info.item_compare_func_err = FALSE;
+           info.item_compare_keep_zero = FALSE;
            /* test the compare function */
-           if (item_compare_func != NULL
+           if (info.item_compare_func != NULL
                    && item_compare2((void *)&ptrs[0], (void *)&ptrs[1])
                                                         == ITEM_COMPARE_FAIL)
                EMSG(_("E702: Sort compare function failed"));
@@ -19059,9 +19072,10 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
            {
                /* Sort the array with item pointers. */
                qsort((void *)ptrs, (size_t)len, sizeof(sortItem_T),
-                   item_compare_func == NULL ? item_compare : item_compare2);
+                   info.item_compare_func == NULL
+                                              ? item_compare : item_compare2);
 
-               if (!item_compare_func_err)
+               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;
@@ -19076,9 +19090,9 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
            int (*item_compare_func_ptr)(const void *, const void *);
 
            /* f_uniq(): ptrs will be a stack of items to remove */
-           item_compare_func_err = FALSE;
-           item_compare_keep_zero = TRUE;
-           item_compare_func_ptr = item_compare_func
+           info.item_compare_func_err = FALSE;
+           info.item_compare_keep_zero = TRUE;
+           item_compare_func_ptr = info.item_compare_func
                                               ? item_compare2 : item_compare;
 
            for (li = l->lv_first; li != NULL && li->li_next != NULL;
@@ -19087,14 +19101,14 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
                if (item_compare_func_ptr((void *)&li, (void *)&li->li_next)
                                                                         == 0)
                    ptrs[i++].item = li;
-               if (item_compare_func_err)
+               if (info.item_compare_func_err)
                {
                    EMSG(_("E882: Uniq compare function failed"));
                    break;
                }
            }
 
-           if (!item_compare_func_err)
+           if (!info.item_compare_func_err)
            {
                while (--i >= 0)
                {
@@ -19113,6 +19127,8 @@ do_sort_uniq(typval_T *argvars, typval_T *rettv, int sort)
 
        vim_free(ptrs);
     }
+theend:
+    sortinfo = old_sortinfo;
 }
 
 /*
index 32ad7f8ad59cb8bdc6f6f5a2e6f94e6d8e09fa3f..68021f6de6be40b73ab5b0426fd9fda5a52cc952 100644 (file)
@@ -1,5 +1,14 @@
 " Test sort()
 
+:func Compare1(a, b) abort
+    call sort(range(3), 'Compare2')
+    return a:a ># a:b
+:endfunc
+
+:func Compare2(a, b) abort
+    return a:a <# a:b
+:endfunc
+
 func Test_sort_strings()
   " numbers compared as strings
   call assert_equal([1, 2, 3], sort([3, 2, 1]))
@@ -21,3 +30,8 @@ endfunc
 func Test_sort_float()
   call assert_equal([0.28, 3, 13.5], sort([13.5, 0.28, 3], 'f'))
 endfunc
+
+func Test_sort_nested()
+  " test ability to call sort() from a compare function
+  call assert_equal([1, 3, 5], sort([3, 1, 5], 'Compare1'))
+endfunc
index 15409a6b3387c19f6c735add14032a5dbc2cf437..39b76438f043a6708ed4c4c4d09c57203cda6389 100644 (file)
@@ -748,6 +748,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1394,
 /**/
     1393,
 /**/