diff src/ex_cmds.c @ 826:1cdd2661f34c v7.0d01

updated for version 7.0d01
author vimboss
date Tue, 11 Apr 2006 21:38:50 +0000
parents 6675076019ae
children 5117153003bd
line wrap: on
line diff
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -261,18 +261,23 @@ linelen(has_tab)
     return len;
 }
 
-/* Buffer for one line used during sorting.  It's allocated to contain the
- * longest line being sorted. */
-static char_u	*sortbuf;
+/* Buffer for two lines used during sorting.  They are allocated to
+ * contain the longest line being sorted. */
+static char_u	*sortbuf1;
+static char_u	*sortbuf2;
 
 static int	sort_ic;		/* ignore case */
 static int	sort_nr;		/* sort on number */
+static int	sort_rx;		/* sort on regex instead of skipping it */
+
+static int	sort_abort;		/* flag to indicate if sorting has been interrupted */
 
 /* Struct to store info to be sorted. */
 typedef struct
 {
     linenr_T	lnum;			/* line number */
-    long	col_nr;			/* column number or number */
+    long	start_col_nr;		/* starting column number or number */
+    long	end_col_nr;		/* ending column number */
 } sorti_T;
 
 static int
@@ -291,18 +296,35 @@ sort_compare(s1, s2)
 {
     sorti_T	l1 = *(sorti_T *)s1;
     sorti_T	l2 = *(sorti_T *)s2;
-    char_u	*s;
-
-    /* When sorting numbers "col_nr" is the number, not the column number. */
+    int		result = 0;
+
+    /* If the user interrupts, there's no way to stop qsort() immediately, but
+     * if we return 0 every time, qsort will assume it's done sorting and exit */
+    if (sort_abort)
+	return 0;
+    fast_breakcheck();
+    if (got_int)
+	sort_abort = TRUE;
+
+    /* When sorting numbers "start_col_nr" is the number, not the column number. */
     if (sort_nr)
-	return l1.col_nr - l2.col_nr;
-
-    /* We need to copy one line into "sortbuf", because there is no guarantee
-     * that the first pointer becomes invalid when obtaining the second one. */
-    STRCPY(sortbuf, ml_get(l1.lnum) + l1.col_nr);
-    s = ml_get(l2.lnum) + l2.col_nr;
-
-    return sort_ic ? STRICMP(sortbuf, s) : STRCMP(sortbuf, s);
+	result = l1.start_col_nr - l2.start_col_nr;
+    else
+    {
+	/* We need to copy one line into "sortbuf1", because there is no guarantee
+	 * that the first pointer becomes invalid when obtaining the second one. */
+	STRNCPY(sortbuf1, ml_get(l1.lnum) + l1.start_col_nr, l1.end_col_nr - l1.start_col_nr + 1);
+	sortbuf1[l1.end_col_nr - l1.start_col_nr] = 0;
+	STRNCPY(sortbuf2, ml_get(l2.lnum) + l2.start_col_nr, l2.end_col_nr - l2.start_col_nr + 1);
+	sortbuf2[l2.end_col_nr - l2.start_col_nr] = 0;
+
+	result = sort_ic ? STRICMP(sortbuf1, sortbuf2) : STRCMP(sortbuf1, sortbuf2);
+    }
+    /* If the two lines have the same value, preserve the original line order */
+    if (result == 0)
+	return (int) (l1.lnum - l2.lnum);
+    else
+	return result;
 }
 
 /*
@@ -321,21 +343,25 @@ ex_sort(eap)
     size_t	i;
     char_u	*p;
     char_u	*s;
+    char_u	*s2;
+    char_u	c;			/* temporary character storage */
     int		unique = FALSE;
     long	deleted;
-    colnr_T	col;
+    colnr_T	start_col;
+    colnr_T	end_col;
     int		sort_oct;		/* sort on octal number */
     int		sort_hex;		/* sort on hex number */
 
     if (u_save((linenr_T)(eap->line1 - 1), (linenr_T)(eap->line2 + 1)) == FAIL)
 	return;
-    sortbuf = NULL;
+    sortbuf1 = NULL;
+    sortbuf2 = NULL;
     regmatch.regprog = NULL;
     nrs = (sorti_T *)lalloc((long_u)(count * sizeof(sorti_T)), TRUE);
     if (nrs == NULL)
-	goto theend;
-
-    sort_ic = sort_nr = sort_oct = sort_hex = 0;
+	goto sortend;
+
+    sort_abort = sort_ic = sort_rx = sort_nr = sort_oct = sort_hex = 0;
 
     for (p = eap->arg; *p != NUL; ++p)
     {
@@ -343,6 +369,8 @@ ex_sort(eap)
 	    ;
 	else if (*p == 'i')
 	    sort_ic = TRUE;
+	else if (*p == 'r')
+	    sort_rx = TRUE;
 	else if (*p == 'n')
 	    sort_nr = 2;
 	else if (*p == 'o')
@@ -364,19 +392,19 @@ ex_sort(eap)
 	    if (*s != *p)
 	    {
 		EMSG(_(e_invalpat));
-		goto theend;
+		goto sortend;
 	    }
 	    *s = NUL;
 	    regmatch.regprog = vim_regcomp(p + 1, RE_MAGIC);
 	    if (regmatch.regprog == NULL)
-		goto theend;
+		goto sortend;
 	    p = s;		/* continue after the regexp */
 	    regmatch.rm_ic = p_ic;
 	}
 	else
 	{
 	    EMSG2(_(e_invarg2), p);
-	    goto theend;
+	    goto sortend;
 	}
     }
 
@@ -384,7 +412,7 @@ ex_sort(eap)
     if (sort_nr + sort_oct + sort_hex > 2)
     {
 	EMSG(_(e_invarg));
-	goto theend;
+	goto sortend;
     }
 
     /* From here on "sort_nr" is used as a flag for any number sorting. */
@@ -393,9 +421,9 @@ ex_sort(eap)
     /*
      * Make an array with all line numbers.  This avoids having to copy all
      * the lines into allocated memory.
-     * When sorting on strings "col_nr" is de offset in the line, for numbers
-     * sorting it's the number to sort on.  This means the pattern matching
-     * and number conversion only has to be done once per line.
+     * When sorting on strings "start_col_nr" is the offset in the line, for
+     * numbers sorting it's the number to sort on.  This means the pattern
+     * matching and number conversion only has to be done once per line.
      * Also get the longest line length for allocating "sortbuf".
      */
     for (lnum = eap->line1; lnum <= eap->line2; ++lnum)
@@ -405,57 +433,83 @@ ex_sort(eap)
 	if (maxlen < len)
 	    maxlen = len;
 
+	start_col = 0;
+	end_col = len;
 	if (regmatch.regprog != NULL && vim_regexec(&regmatch, s, 0))
-	    col = regmatch.endp[0] - s;
+	{
+	    if (sort_rx)
+	    {
+		start_col = regmatch.startp[0] - s;
+		end_col = regmatch.endp[0] - s;
+	    }
+	    else
+		start_col = regmatch.endp[0] - s;
+	}
 	else
-	    col = 0;
+	    if (regmatch.regprog != NULL)
+		end_col = 0;
 
 	if (sort_nr)
 	{
+	    /* Make sure vim_str2nr doesn't read any digits past the end
+	     * of the match, by temporarily terminating the string there */
+	    s2 = s + end_col;
+	    c = *s2;
+	    (*s2) = 0;
 	    /* Sorting on number: Store the number itself. */
 	    if (sort_hex)
-		s = skiptohex(s + col);
+		s = skiptohex(s + start_col);
 	    else
-		s = skiptodigit(s + col);
+		s = skiptodigit(s + start_col);
 	    vim_str2nr(s, NULL, NULL, sort_oct, sort_hex,
-					&nrs[lnum - eap->line1].col_nr, NULL);
+					&nrs[lnum - eap->line1].start_col_nr, NULL);
+	    (*s2) = c;
 	}
 	else
+	{
 	    /* Store the column to sort at. */
-	    nrs[lnum - eap->line1].col_nr = col;
+	    nrs[lnum - eap->line1].start_col_nr = start_col;
+	    nrs[lnum - eap->line1].end_col_nr = end_col;
+	}
 
 	nrs[lnum - eap->line1].lnum = lnum;
 
 	if (regmatch.regprog != NULL)
 	    fast_breakcheck();
 	if (got_int)
-	    goto theend;
+	    goto sortend;
     }
 
     /* Allocate a buffer that can hold the longest line. */
-    sortbuf = alloc((unsigned)maxlen + 1);
-    if (sortbuf == NULL)
-	goto theend;
+    sortbuf1 = alloc((unsigned)maxlen + 1);
+    if (sortbuf1 == NULL)
+	goto sortend;
+    sortbuf2 = alloc((unsigned)maxlen + 1);
+    if (sortbuf2 == NULL)
+	goto sortend;
 
     /* Sort the array of line numbers.  Note: can't be interrupted! */
     qsort((void *)nrs, count, sizeof(sorti_T), sort_compare);
 
+    if (sort_abort)
+	goto sortend;
+
     /* Insert the lines in the sorted order below the last one. */
     lnum = eap->line2;
     for (i = 0; i < count; ++i)
     {
 	s = ml_get(nrs[eap->forceit ? count - i - 1 : i].lnum);
 	if (!unique || i == 0
-		|| (sort_ic ? STRICMP(s, sortbuf) : STRCMP(s, sortbuf)) != 0)
+		|| (sort_ic ? STRICMP(s, sortbuf1) : STRCMP(s, sortbuf1)) != 0)
 	{
 	    if (ml_append(lnum++, s, (colnr_T)0, FALSE) == FAIL)
 		break;
 	    if (unique)
-		STRCPY(sortbuf, s);
+		STRCPY(sortbuf1, s);
 	}
 	fast_breakcheck();
 	if (got_int)
-	    goto theend;
+	    goto sortend;
     }
 
     /* delete the original lines if appending worked */
@@ -476,9 +530,10 @@ ex_sort(eap)
     curwin->w_cursor.lnum = eap->line1;
     beginline(BL_WHITE | BL_FIX);
 
-theend:
+sortend:
     vim_free(nrs);
-    vim_free(sortbuf);
+    vim_free(sortbuf1);
+    vim_free(sortbuf2);
     vim_free(regmatch.regprog);
     if (got_int)
 	EMSG(_(e_interr));