rewrite funcs.c:c_test(), i.e. test(1) and [(1), to follow POSIX and XSI

in the cases where they are defined unambiguously; bug reported by
Jilles Tjoelker in <20111129232526.GC14357@stack.nl> due to a report
by Stefano Lattarini on bug-autoconf

in the ambiguous case, I stick to traditional pdksh behaviour, which means
	test ! a = b	vs.	test a = b
and
	test ! a -o b	vs.	test a -o b
behave different from each other (in the second case, the NOT operator
binds strong; POSIX demands a reduction to 3 arguments and negating
that result in the first case), so we're at two known not-ok in the
FreeBSD® testsuite. (81 and 82 in regress.sh,v 1.3)
This commit is contained in:
tg 2011-11-30 21:34:15 +00:00
parent 44a27fa8e0
commit 2fb9df56e4
4 changed files with 113 additions and 74 deletions

View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.496 2011/11/26 00:45:18 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.497 2011/11/30 21:34:10 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -23,9 +23,12 @@
#-
# You may also want to test IFS with the script at
# http://www.research.att.com/~gsf/public/ifs.sh
#
# More testsuites at:
# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
expected-stdout:
@(#)MIRBSD KSH R40 2011/11/25
@(#)MIRBSD KSH R40 2011/11/30
description:
Check version of shell.
stdin:

143
funcs.c
View File

@ -38,7 +38,7 @@
#endif
#endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.199 2011/11/19 17:42:24 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.200 2011/11/30 21:34:12 tg Exp $");
#if HAVE_KILLPG
/*
@ -2724,8 +2724,10 @@ c_mknod(const char **wp)
int
c_test(const char **wp)
{
int argc, res;
int argc, rv, invert = 0;
Test_env te;
Test_op op;
const char *lhs, **swp;
te.flags = 0;
te.isa = ptest_isa;
@ -2747,63 +2749,96 @@ c_test(const char **wp)
te.wp_end = wp + argc;
/*
* Handle the special cases from POSIX.2, section 4.62.4.
* Implementation of all the rules isn't necessary since
* our parser does the right thing for the omitted steps.
* Attempt to conform to POSIX special cases. This is pretty
* dumb code straight-forward from the 2008 spec, but unless
* the old pdksh code doesn't live from so many assumptions.
*/
if (argc <= 5) {
const char **owp = wp, **owpend = te.wp_end;
int invert = 0;
Test_op op;
const char *opnd1, *opnd2;
if (argc >= 2 && ((*te.isa)(&te, TM_OPAREN))) {
te.pos.wp = te.wp_end - 1;
if ((*te.isa)(&te, TM_CPAREN)) {
argc -= 2;
te.wp_end--;
te.pos.wp = owp + 2;
} else {
te.pos.wp = owp + 1;
te.wp_end = owpend;
}
switch (argc - 1) {
case 0:
return (1);
case 1:
ptest_one:
op = TO_STNZE;
goto ptest_unary;
case 2:
ptest_two:
if ((*te.isa)(&te, TM_NOT)) {
++invert;
goto ptest_one;
}
while (--argc >= 0) {
if ((*te.isa)(&te, TM_END))
return (!0);
if (argc == 3) {
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
if ((op = (*te.isa)(&te, TM_BINOP))) {
opnd2 = (*te.getopnd)(&te, op, 1);
res = (*te.eval)(&te, op, opnd1,
opnd2, 1);
if (te.flags & TEF_ERROR)
return (T_ERR_EXIT);
if (invert & 1)
res = !res;
return (!res);
}
/* back up to opnd1 */
te.pos.wp--;
}
if (argc == 1) {
opnd1 = (*te.getopnd)(&te, TO_NONOP, 1);
res = (*te.eval)(&te, TO_STNZE, opnd1,
NULL, 1);
if (invert & 1)
res = !res;
return (!res);
}
if ((*te.isa)(&te, TM_NOT)) {
invert++;
} else
break;
if ((op = (*te.isa)(&te, TM_UNOP))) {
ptest_unary:
rv = (*te.eval)(&te, op,
(*te.getopnd)(&te, op, true), NULL, true);
ptest_out:
return ((invert & 1) ? rv : !rv);
}
te.pos.wp = owp + 1;
te.wp_end = owpend;
/* let the parser deal with anything else */
break;
case 3:
ptest_three:
swp = te.pos.wp;
/* skip lhs, without evaluation */
(*te.getopnd)(&te, TO_NONOP, false);
if ((op = (*te.isa)(&te, TM_BINOP))) {
const char *rhs;
/* read rhs, with evaluation */
rhs = (*te.getopnd)(&te, op, true);
/* back up to lhs */
te.pos.wp = swp;
/* re-read lhs, with evaluation */
lhs = (*te.getopnd)(&te, op, true);
/* finally run the test of lhs op rhs */
rv = (*te.eval)(&te, op, lhs, rhs, true);
goto ptest_out;
}
/* back up to lhs */
te.pos.wp = swp;
if ((*te.isa)(&te, TM_NOT)) {
++invert;
goto ptest_two;
}
if ((*te.isa)(&te, TM_OPAREN)) {
swp = te.pos.wp;
/* skip operand, without evaluation */
(*te.getopnd)(&te, TO_NONOP, false);
/* check for closing parenthesis */
op = (*te.isa)(&te, TM_CPAREN);
/* back up to operand */
te.pos.wp = swp;
/* if there was a closing paren, handle it */
if (op)
goto ptest_one;
/* backing up is done before calling the parser */
}
/* let the parser deal with it */
break;
case 4:
if ((*te.isa)(&te, TM_NOT)) {
++invert;
goto ptest_three;
}
if ((*te.isa)(&te, TM_OPAREN)) {
swp = te.pos.wp;
/* skip two operands, without evaluation */
(*te.getopnd)(&te, TO_NONOP, false);
(*te.getopnd)(&te, TO_NONOP, false);
/* check for closing parenthesis */
op = (*te.isa)(&te, TM_CPAREN);
/* back up to first operand */
te.pos.wp = swp;
/* if there was a closing paren, handle it */
if (op)
goto ptest_two;
/* backing up is done before calling the parser */
}
/* defer this to the parser */
break;
}
/* "The results are unspecified." */
te.pos.wp = wp + 1;
return (test_parse(&te));
}

33
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.278 2011/11/11 22:14:02 tg Exp $
.\" $MirOS: src/bin/mksh/mksh.1,v 1.279 2011/11/30 21:34:14 tg Exp $
.\" $OpenBSD: ksh.1,v 1.141 2011/09/03 22:59:08 jmc Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -72,7 +72,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
.Dd $Mdocdate: November 11 2011 $
.Dd $Mdocdate: November 30 2011 $
.\"
.\" Check which macro package we use
.\"
@ -3549,7 +3549,7 @@ The file type may be
(character type device),
or
.Cm p
(named pipe).
.Pq named pipe , Tn FIFO .
The file created may be modified according to its
.Ar mode
(via the
@ -4296,7 +4296,8 @@ instead of
.Ql xtrace .
.It Fl p Ar file
.Ar file
is a named pipe.
is a named pipe
.Pq Tn FIFO .
.It Fl r Ar file
.Ar file
exists and is readable.
@ -4394,22 +4395,22 @@ a mathematical term or the name of an integer variable:
x=1; [ "x" \-eq 1 ] evaluates to true
.Ed
.Pp
Note that some special rules are applied (courtesy of POSIX)
if the number of
arguments to
Note that some special rules are applied (courtesy of
.Px
) if the number of arguments to
.Ic test
or
or inside the brackets
.Ic \&[ ... \&]
is less than five: if leading
.Ql \&!
arguments can be stripped such that only one argument remains then a string
length test is performed (again, even if the argument is a unary operator); if
leading
.Ql \&!
arguments can be stripped such that three arguments remain and the second
argument is a binary operator, then the binary operation is performed (even
if the first argument is a unary operator, including an unstripped
.Ql \&! ) .
arguments can be stripped such that only one to three arguments remain,
then the lowered comparison is executed; (thanks to XSI) parentheses
.Ic \e( ... \e)
lower four- and three-argument forms to two- and one-argument forms,
respectively; three-argument forms ultimately prefer binary operations,
followed by negation and parenthesis lowering; two- and four-argument forms
prefer negation followed by parenthesis; the one-argument form always implies
.Fl n .
.Pp
.Sy Note :
A common mistake is to use

4
sh.h
View File

@ -151,9 +151,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.505 2011/11/26 00:45:21 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.506 2011/11/30 21:34:15 tg Exp $");
#endif
#define MKSH_VERSION "R40 2011/11/25"
#define MKSH_VERSION "R40 2011/11/30"
#ifndef MKSH_INCLUDES_ONLY