Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: Parser issues and and [[ $var ]]



Looking at this a bit further ...

On Apr 26,  1:30pm, Bart Schaefer wrote:
} Subject: Parser issues and and [[ $var ]]
}
} Hmm.  Looks like both bash and ksh93 accept [[ $foo ]] as valid syntax.
} This turns out to be surprisingly difficult to fix, and poking at it,
} I think we may have broken some things in parse.c.
} 
} For example, [ -t ] is supposed to be equivalent to [ -t 1 ] and there
} is code late in par_cond_2() that handles this, but that code is never
} reached because the /* POSIX 1003.2 */ code at the top of par_cond_2()
} preemptively turns it into [ -n -t ].

Further prodding seems to indicate that bash and ksh disagree on this
point.  [ -t ] in bash (and in the "test" external command) is treated
like [ "-t" ], so

    [ -t ] >/dev/null

is true in bash3.2 but false in ksh93 (including builtin test).  As far
as I can tell, this is the only operator that behaves this way, both
bash and ksh treat all other binary operators as strings when they
have no argument.

Zsh has traditionally had the ksh behavior here, though as noted this
was probably unintentionally changed a couple of revisions ago.

Ksh93 has one other interesting feature that I wasn't able to make work
in zsh:

    ksh$ [ -t ] >/dev/null || print tested FD 1 not a terminal
    tested FD 1 not a terminal

    ksh$ x=t
    ksh$ [ -$x ] >/dev/null && print tested as a string
    tested as a string

So it appears ksh is tokenizing the arguments of "[" (and builtin test),
whereas zsh will expand $x first in that case.  This is similar to ksh
treating "typeset" as special in command position.

} The same unreached code for [ -t ] also tries to handle [ $var ] but is
} not usable for [[ $var ]] because of the different lexing conventions
} for "test"/"[" vs. [[ ... ]].  Also [[ -t ]] is NOT supposed to behave
} like either of [[ -t 1 ]] or [[ -n -t ]], so that part of the code in
} question must be avoided.

That code turns out to be reachable after all if the expression is more
complex than just [ -a ], e.g. [ -a -a -a ].  This plus examination of
ecadd() leads me to belive that ...

} Also, if you compare the later code to the earlier code, sometimes we
} call dupstring() on the first argument to par_cond_double() and other
} times we do not.  Either we don't need dupstring(), or not using it is
} a bug waiting to happen.

... calling dupstring() on literals before passing to par_cond_double()
is the right thing to do.

I think the following produces syntax errors in all the places it should
and works in the rest of the cases, but I have not yet tested it with
new conditionals added by modules -- e.g., the recomputation of dble
for s2 (last hunk) may need knowledge of added conditions.  So I won't
commit until others have a chance to play with this.


diff --git a/Src/parse.c b/Src/parse.c
index 530a070..6ea997d 100644
--- a/Src/parse.c
+++ b/Src/parse.c
@@ -2068,6 +2068,9 @@ par_cond_2(void)
 	    /* one argument: [ foo ] is equivalent to [ -n foo ] */
 	    s1 = tokstr;
 	    condlex();
+	    /* ksh behavior: [ -t ] means [ -t 1 ]; bash disagrees */
+	    if (unset(POSIXBUILTINS) && !strcmp(s1, "-t"))
+		return par_cond_double(s1, dupstring("1"));
 	    return par_cond_double(dupstring("-n"), s1);
 	}
 	if (testargs[1]) {
@@ -2086,6 +2089,9 @@ par_cond_2(void)
 		return par_cond_triple(s1, s2, s3);
 	    }
 	}
+	/*
+	 * We may fall through here in oddball cases like [ = -a ! ]
+	 */
     }
     if (tok == BANG) {
 	/*
@@ -2114,18 +2120,17 @@ par_cond_2(void)
 	condlex();
 	return r;
     }
+    if ((s1 = tokstr) != 0)
+	dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
+		&& !s1[2]);
     if (tok != STRING) {
-	if (tok && tok != LEXERR && condlex == testlex) {
-	    s1 = tokstr;
+	/* Check first argument for [[ string ]] re-interpretation */
+	if (tokstr && tok != LEXERR && (!dble || condlex == testlex)) {
 	    condlex();
-	    return par_cond_double("-n", s1);
+	    return par_cond_double(dupstring("-n"), s1);
 	} else
 	    YYERROR(ecused);
     }
-    s1 = tokstr;
-    if (condlex == testlex)
-	dble = (*s1 == '-' && strspn(s1+1, "abcdefghknoprstuwxzLONGS") == 1
-		  && !s1[2]);
     condlex();
     if (tok == INANG || tok == OUTANG) {
 	enum lextok xtok = tok;
@@ -2140,15 +2145,19 @@ par_cond_2(void)
 	return 1;
     }
     if (tok != STRING) {
-	if (tok != LEXERR && condlex == testlex) {
-	    if (!dble)
-		return par_cond_double("-n", s1);
-	    else if (!strcmp(s1, "-t"))
-		return par_cond_double(s1, "1");
+	/*
+	 * Check second argument in case semantics e.g. [ = -a = ]
+	 * mean we have to go back and fix up the first one
+	 */
+	if (tok != LEXERR) {
+	    if (!dble || condlex == testlex)
+		return par_cond_double(dupstring("-n"), s1);
 	} else
 	    YYERROR(ecused);
     }
-    s2 = tokstr;
+    if ((s2 = tokstr) != 0 && condlex != testlex)
+	dble = (*s2 == '-' && strspn(s2+1, "abcdefghknoprstuwxzLONGS") == 1
+		&& !s2[2]);
     incond++;			/* parentheses do globbing */
     condlex();
     incond--;			/* parentheses do grouping */



Messages sorted by: Reverse Date, Date, Thread, Author