summary refs log tree commit diff
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2008-07-13 19:20:10 +0800
committerHerbert Xu <herbert@gondor.apana.org.au>2008-07-13 19:20:10 +0800
commit4df1e776cd079357f877f0d491a80a234f670452 (patch)
tree43ffff6e67db84fd7759dc3908fb34cce2507612
parent[EXPAND] Fixed non-leading slash treatment in expmeta (diff)
downloaddash-4df1e776cd079357f877f0d491a80a234f670452.tar.gz
dash-4df1e776cd079357f877f0d491a80a234f670452.zip
[BUILTIN] Fixed 3,4-argument cases for test per POSIX
----- Forwarded message from Gerrit Pape <pape@smarden.org> -----

Subject: Bug#455828: dash: 4-argument test "test \( ! -e \)" yields an error
Date: Fri, 28 Dec 2007 08:53:29 +0000
From: Gerrit Pape <pape@smarden.org>
To: Vincent Lefevre <vincent@vinc17.org>, 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 <herbert@gondor.apana.org.au>
-rw-r--r--ChangeLog4
-rw-r--r--src/bltin/test.c81
2 files changed, 61 insertions, 24 deletions
diff --git a/ChangeLog b/ChangeLog
index 7cf15c0..b2f7333 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2008-06-13  Herbert Xu <herbert@gondor.apana.org.au>
+
+	* Fixed 3,4-argument cases for test per POSIX.
+
 2008-05-19  Herbert Xu <herbert@gondor.apana.org.au>
 
 	* Fixed non-leading slash treatment in expmeta.
diff --git a/src/bltin/test.c b/src/bltin/test.c
index bc8b175..89d8d86 100644
--- a/src/bltin/test.c
+++ b/src/bltin/test.c
@@ -155,9 +155,23 @@ static inline intmax_t getn(const char *s)
 	return atomax10(s);
 }
 
+static const struct t_op *getop(const char *s)
+{
+	const struct t_op *op;
+
+	for (op = ops; op->op_text; op++) {
+		if (strcmp(s, op->op_text) == 0)
+			return op;
+	}
+
+	return NULL;
+}
+
 int
 testcmd(int argc, char **argv)
 {
+	const struct t_op *op;
+	enum token n;
 	int res;
 
 	if (*argv[0] == '[') {
@@ -166,14 +180,42 @@ testcmd(int argc, char **argv)
 		argv[argc] = NULL;
 	}
 
-	if (argc < 2)
+	argv++;
+	argc--;
+
+	if (argc < 1)
 		return 1;
 
-	t_wp = &argv[1];
-	res = !oexpr(t_lex(*t_wp));
+	/*
+	 * POSIX prescriptions: he who wrote this deserves the Nobel
+	 * peace prize.
+	 */
+	switch (argc) {
+	case 3:
+		op = getop(argv[1]);
+		if (op && op->op_type == BINOP) {
+			n = OPERAND;
+			goto eval;
+		}
+		/* fall through */
 
-	if (*t_wp != NULL && *++t_wp != NULL)
-		syntax(*t_wp, "unexpected operator");
+	case 4:
+		if (!strcmp(argv[0], "(") && !strcmp(argv[argc - 1], ")")) {
+			argv[--argc] = NULL;
+			argv++;
+			argc--;
+		}
+	}
+
+	n = t_lex(*argv);
+
+eval:
+	t_wp = argv;
+	res = !oexpr(n);
+	argv = t_wp;
+
+	if (argv[0] != NULL && argv[1] != NULL)
+		syntax(argv[0], "unexpected operator");
 
 	return res;
 }
@@ -359,22 +401,18 @@ t_lex(char *s)
 {
 	struct t_op const *op;
 
-	op = ops;
-
 	if (s == 0) {
 		t_wp_op = (struct t_op *)0;
 		return EOI;
 	}
-	while (op->op_text) {
-		if (strcmp(s, op->op_text) == 0) {
-			if ((op->op_type == UNOP && isoperand()) ||
-			    (op->op_num == LPAREN && *(t_wp+1) == 0))
-				break;
-			t_wp_op = op;
-			return op->op_num;
-		}
-		op++;
+
+	op = getop(s);
+	if (op && !(op->op_type == UNOP && isoperand()) &&
+	    !(op->op_num == LPAREN && *(t_wp+1) == 0)) {
+		t_wp_op = op;
+		return op->op_num;
 	}
+
 	t_wp_op = (struct t_op *)0;
 	return OPERAND;
 }
@@ -385,18 +423,13 @@ isoperand(void)
 	struct t_op const *op;
 	char *s, *t;
 
-	op = ops;
 	if ((s  = *(t_wp+1)) == 0)
 		return 1;
 	if ((t = *(t_wp+2)) == 0)
 		return 0;
-	while (op->op_text) {
-		if (strcmp(s, op->op_text) == 0)
-			return op->op_type == BINOP &&
-			    (t[0] != ')' || t[1] != '\0');
-		op++;
-	}
-	return 0;
+
+	op = getop(s);
+	return op && op->op_type == BINOP;
 }
 
 static int