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:
parent
44a27fa8e0
commit
2fb9df56e4
7
check.t
7
check.t
@ -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
143
funcs.c
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
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++;
|
||||
} else
|
||||
++invert;
|
||||
goto ptest_one;
|
||||
}
|
||||
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);
|
||||
}
|
||||
/* 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;
|
||||
}
|
||||
te.pos.wp = owp + 1;
|
||||
te.wp_end = owpend;
|
||||
}
|
||||
|
||||
/* "The results are unspecified." */
|
||||
te.pos.wp = wp + 1;
|
||||
return (test_parse(&te));
|
||||
}
|
||||
|
||||
|
33
mksh.1
33
mksh.1
@ -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
4
sh.h
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user