sort: Fix line comparison when col buffer contains data from longer line

I'm not sure if there are other implications of this or not, but
the issue is that columns() uses len to store the allocated buffer
size, but linecmp() compares up to len bytes. If those trailing
bytes do not match, the line is considered not matching, even though
the relevant parts of the buffer do match.

To resolve this, also keep track of column capacity. Additionally,
since there is no reason to keep the existing data when resizing,
just use free and emalloc rather than erealloc.

The simplest case I could reduce it to is this:

if [ "$(printf '%s\n' a a xxb xxc | ./sort -u)" = "$(printf '%s\n' a xxb xxc)" ] ; then
	echo pass
else
	echo fail
fi
This commit is contained in:
Michael Forney 2016-05-14 18:56:54 -07:00 committed by sin
parent d827fcace2
commit a944b682a6

37
sort.c
View File

@ -18,6 +18,11 @@ struct keydef {
TAILQ_ENTRY(keydef) entry; TAILQ_ENTRY(keydef) entry;
}; };
struct column {
struct line line;
size_t cap;
};
enum { enum {
MOD_N = 1 << 0, MOD_N = 1 << 0,
MOD_STARTB = 1 << 1, MOD_STARTB = 1 << 1,
@ -33,7 +38,7 @@ static TAILQ_HEAD(kdhead, keydef) kdhead = TAILQ_HEAD_INITIALIZER(kdhead);
static int Cflag = 0, cflag = 0, uflag = 0; static int Cflag = 0, cflag = 0, uflag = 0;
static char *fieldsep = NULL; static char *fieldsep = NULL;
static size_t fieldseplen = 0; static size_t fieldseplen = 0;
static struct line col1, col2; static struct column col1, col2;
static void static void
skipblank(struct line *a) skipblank(struct line *a)
@ -76,12 +81,12 @@ skipcolumn(struct line *a, int skip_to_next_col)
} }
} }
static size_t static void
columns(struct line *line, const struct keydef *kd, struct line *col) columns(struct line *line, const struct keydef *kd, struct column *col)
{ {
Rune r; Rune r;
struct line start, end; struct line start, end;
size_t len, utflen, rlen; size_t utflen, rlen;
int i; int i;
start.data = line->data; start.data = line->data;
@ -118,15 +123,13 @@ columns(struct line *line, const struct keydef *kd, struct line *col)
end.data += end.len - 1; end.data += end.len - 1;
end.len = 1; end.len = 1;
} }
len = MAX(0, end.data - start.data); col->line.len = MAX(0, end.data - start.data);
if (!(col->data) || col->len < len) if (!(col->line.data) || col->cap < col->line.len + 1) {
col->data = erealloc(col->data, len + 1); free(col->line.data);
memcpy(col->data, start.data, len); col->line.data = emalloc(col->line.len + 1);
col->data[len] = '\0'; }
if (col->len < len) memcpy(col->line.data, start.data, col->line.len);
col->len = len; col->line.data[col->line.len] = '\0';
return len;
} }
static int static int
@ -187,13 +190,13 @@ slinecmp(struct line *a, struct line *b)
TAILQ_LAST(&kdhead, kdhead) != TAILQ_FIRST(&kdhead)) { TAILQ_LAST(&kdhead, kdhead) != TAILQ_FIRST(&kdhead)) {
res = 0; res = 0;
} else if (kd->flags & MOD_N) { } else if (kd->flags & MOD_N) {
x = strtold(col1.data, NULL); x = strtold(col1.line.data, NULL);
y = strtold(col2.data, NULL); y = strtold(col2.line.data, NULL);
res = (x < y) ? -1 : (x > y); res = (x < y) ? -1 : (x > y);
} else if (kd->flags & (MOD_D | MOD_F | MOD_I)) { } else if (kd->flags & (MOD_D | MOD_F | MOD_I)) {
res = skipmodcmp(&col1, &col2, kd->flags); res = skipmodcmp(&col1.line, &col2.line, kd->flags);
} else { } else {
res = linecmp(&col1, &col2); res = linecmp(&col1.line, &col2.line);
} }
if (kd->flags & MOD_R) if (kd->flags & MOD_R)