I noticed!

POSIX requires that the output of the "set" command (with no args -- it
gives a list of variables, and their values) be sorted according to
the collating sequence defined by the current locale.

Now I'm not aware of any locale where the collating sequence order of
ascii letters, digits, and '_' are any different than they are in the
C locale (and those are the only characters that can occur in variable
names - unless there is perhaps a locale that defines "dictionary" order
as the sort order) but never mind, that isn't the bug...

What "collating sequence order" does mean however, if not "collating
sequence order, except when we happen to have two variable names, where
one name is a prefix of the other (say X and XY) and the first character
of the 'Y' part of the longer name happens to be a digit..."

"set" is not a frequently used command (particularly in scripts where
it matters - that is, the no args form, nothing here alters anything
about any use of set with args) and is already a bit slow (sluggish...)
because of the sort requirement, so let's make it fractionally even
slower, but correct.
This commit is contained in:
kre 2017-05-10 06:18:43 +00:00
parent 913618cd04
commit be0a98abf6
1 changed files with 29 additions and 4 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: var.c,v 1.51 2017/05/03 00:39:40 kre Exp $ */
/* $NetBSD: var.c,v 1.52 2017/05/10 06:18:43 kre Exp $ */
/*-
* Copyright (c) 1991, 1993
@ -37,7 +37,7 @@
#if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95";
#else
__RCSID("$NetBSD: var.c,v 1.51 2017/05/03 00:39:40 kre Exp $");
__RCSID("$NetBSD: var.c,v 1.52 2017/05/10 06:18:43 kre Exp $");
#endif
#endif /* not lint */
@ -526,9 +526,34 @@ sort_var(const void *v_v1, const void *v_v2)
{
const struct var * const *v1 = v_v1;
const struct var * const *v2 = v_v2;
char *t1 = (*v1)->text, *t2 = (*v2)->text;
/* XXX Will anyone notice we include the '=' of the shorter name? */
return strcoll((*v1)->text, (*v2)->text);
if (*t1 == *t2) {
char *p, *s;
STARTSTACKSTR(p);
/*
* note: if lengths are equal, strings must be different
* so we don't care which string we pick for the \0 in
* that case.
*/
if ((strchr(t1, '=') - t1) <= (strchr(t2, '=') - t2)) {
s = t1;
t1 = p;
} else {
s = t2;
t2 = p;
}
while (*s && *s != '=') {
STPUTC(*s, p);
s++;
}
STPUTC('\0', p);
}
return strcoll(t1, t2);
}
/*