From 78b1b1b07ac2784f67bc65bb1d86cc2559a12446 Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Mon, 31 Jul 2017 19:20:43 +0200 Subject: test: fix 4-argument case Upstream dash commit: Date: Sun, 13 Jul 2008 19:20:10 +0800 Fixed 3,4-argument cases for test per POSIX ----- Forwarded message from Gerrit Pape ----- Subject: Bug#455828: dash: 4-argument test "test \( ! -e \)" yields an error Date: Fri, 28 Dec 2007 08:53:29 +0000 From: Gerrit Pape To: Vincent Lefevre , 455828@bugs.debian.org On Thu, Dec 27, 2007 at 06:23:20PM +0100, Vincent Lefevre wrote: > On 2007-12-27 16:00:06 +0000, Gerrit Pape wrote: > > On Wed, Dec 12, 2007 at 02:18:47AM +0100, Vincent Lefevre wrote: > > > According to POSIX[*], "test \( ! -e \)" is a 4-argument test and is > > > here equivalent to "test ! -e". But dash (like ksh93 and bash) yields > > > an error: > > > > > > $ test \( ! -e \) || echo $? > > > test: 1: closing paren expected > > > 2 > > > $ test ! -e || echo $? > > > 1 > > > > Hi Vincent, > > > > the -e switch to test takes an argument, a pathname. > > According to POSIX, in both above examples, "-e" is *not* a switch, > just a string. > > test \( ! -e \) > > means: return true if the string "-e" is empty, otherwhise return false. > The error in dash is that it incorrectly thinks that "-e" is a switch in > this context. I see, you're right. Thanks, Gerrit. ----- End forwarded message ----- This patch hard-codes the 3,4-argument cases in the way required by POSIX. Signed-off-by: Herbert Xu function old new delta test_main 370 421 +51 Signed-off-by: Denys Vlasenko --- coreutils/test.c | 48 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/coreutils/test.c b/coreutils/test.c index d8ac42e22..a8286525a 100644 --- a/coreutils/test.c +++ b/coreutils/test.c @@ -879,18 +879,48 @@ int test_main(int argc, char **argv) res = (argv[0][0] == '\0'); goto ret_special; } - if (argv[2] && !argv[3]) { - check_operator(argv[1]); - if (last_operator->op_type == BINOP) { - /* "test [!] arg1 arg2" */ - args = argv; - res = (binop() == 0); + if (argv[2]) { + if (!argv[3]) { + /* + * http://pubs.opengroup.org/onlinepubs/009695399/utilities/test.html + * """ 3 arguments: + * If $2 is a binary primary, perform the binary test of $1 and $3. + * """ + */ + check_operator(argv[1]); + if (last_operator->op_type == BINOP) { + /* "test [!] arg1 arg2" */ + args = argv; + res = (binop() == 0); ret_special: - /* If there was leading "!" op... */ - res ^= negate; - goto ret; + /* If there was leading "!" op... */ + res ^= negate; + goto ret; + } + /* """If $1 is '(' and $3 is ')', perform the unary test of $2.""" + * Looks like this works without additional coding. + */ + goto check_negate; + } + /* argv[3] exists (at least 4 args), is it exactly 4 args? */ + if (!argv[4]) { + /* + * """ 4 arguments: + * If $1 is '!', negate the three-argument test of $2, $3, and $4. + * If $1 is '(' and $4 is ')', perform the two-argument test of $2 and $3. + * """ + * Example why code below is necessary: test '(' ! -e ')' + */ + if (LONE_CHAR(argv[0], '(') + && LONE_CHAR(argv[3], ')') + ) { + /* "test [!] ( x y )" */ + argv[3] = NULL; + argv++; + } } } + check_negate: if (LONE_CHAR(argv[0], '!')) { argv++; negate ^= 1; -- cgit v1.2.3