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

Re: sh compatibility issue



On Thu, 17 Feb 2011 21:55:07 -0600
Vincent Stemen <vince.lists@xxxxxxxxxxx> wrote:
> I have encountered a minor compatibility issue when using zsh in place
> of /bin/sh on FreeBSD.
> The error comes from this piece of code in genscripts.sh
> 
>     # XXX: arm hack : until those file are merged back into the FSF
> repo, # just
>     # use the version in this directory.
>     if !(test -f ${CUSTOMIZER_SCRIPT}"";) then
>     CUSTOMIZER_SCRIPT="${srcdir}/emulparams/${EMULATION_NAME}.sh"
>     fi
> 
> What is happening is that zsh does not like the '!' being next to the
> opening '(' without a space in the if condition.  In my opinion, this
> is kind of an unorthodox syntax.  I'm not even sure if it is
> traditionally legal sh or ksh syntax.  Nevertheless, it works with
> BSD sh and bash. A simple test is
> 
>     if !(echo hello); then echo "XXXX"; fi
>     if ! (echo hello); then echo "XXXX"; fi
> 
> Zsh works fine in the second case, with a space after the '!'.

I don't think it is standard for this to work.  POSIX defines "!" as a
"reserved word", and if it's not followed by whitespace it's not a
word.

However, it looks like it's possible to get this to work specially in
this case without disrupting anything else; because the parentheses are
always special in one way another to zsh, the only other meaning (with
the exception below) would be a string "!" followed by something like a
globbing expression.  I can't think of a case where that other meaning
is useful (and it's sure as heck thoroughly confusing if you use it,
given all there are at least three meanings for "!" when not used
as a plain string).

The slightly odd handling is down to the fact ! isn't treated as
a token at this point since it's usually handled as a reserved word.

> I also tested without the space under ksh on NetBSD and got a strange
> result.  It ran the mail utility.  Weird.

That's because !(...) is globbing syntax in ksh.  It means "find all
files not matching the pattern in the parentheses".  So that can do
pretty much anything in command position.  So we need to avoid doing
this if the KSH_GLOB option is set (although the result isn't
particularly useful in practice).  Luckily KSH_GLOB isn't set for
emulating sh.

Index: Src/lex.c
===================================================================
RCS file: /cvsroot/zsh/zsh/Src/lex.c,v
retrieving revision 1.63
diff -p -u -r1.63 lex.c
--- Src/lex.c	19 Dec 2010 17:42:10 -0000	1.63
+++ Src/lex.c	18 Feb 2011 10:18:13 -0000
@@ -487,6 +487,7 @@ ctxtlex(void)
 #define LX1_COMMENT 1
 #define LX1_NEWLIN 2
 #define LX1_SEMI 3
+#define LX1_BANG 4
 #define LX1_AMPER 5
 #define LX1_BAR 6
 #define LX1_INPAR 7
@@ -835,6 +836,30 @@ gettok(void)
 	hungetc(d);
 	lexstop = 0;
 	return SEMI;
+    case LX1_BANG:
+	/*
+	 * In command position, treat "!(" or "!{"
+	 * as "! (" or "! {".  "!" is a reserved word,
+	 * so not handled as a token at this level.
+	 * This is for compatibility; a "real"
+	 * reserved word wouldn't behave like this.
+	 *
+	 * With ksh globbing, !(...) is a special syntax.
+	 * Although it doesn't do anything very useful
+	 * in command position, we shouldn't disrupt it.
+	 */
+	if (incmdpos && !isset(KSHGLOB) && reswdtab->getnode(reswdtab, "!")) {
+	    d = hgetc();
+	    hungetc(d);
+	    lexstop = 0;
+	    if (d == '(' || d == '{') {
+		bptr = tokstr = (char *)hcalloc(2);
+		*bptr++ = '!';
+		*bptr++ = '\0';
+		return STRING;
+	    }
+	}
+	break;
     case LX1_AMPER:
 	d = hgetc();
 	if (d == '&')
Index: Test/A01grammar.ztst
===================================================================
RCS file: /cvsroot/zsh/zsh/Test/A01grammar.ztst,v
retrieving revision 1.27
diff -p -u -r1.27 A01grammar.ztst
--- Test/A01grammar.ztst	18 Mar 2010 16:30:58 -0000	1.27
+++ Test/A01grammar.ztst	18 Feb 2011 10:18:13 -0000
@@ -577,3 +577,11 @@
 0:$0 is traditionally if bizarrely set to the first argument with -c
 >myargzero
 >myargone
+
+  if ! (echo success1); then echo failure1; fi
+  if !(echo success2); then echo failure2; fi
+  if !{echo success3}; then echo failure3; fi
+0:Special handling of ! in command position.
+>success1
+>success2
+>success3

-- 
Peter Stephenson <pws@xxxxxxx>            Software Engineer
Tel: +44 (0)1223 692070                   Cambridge Silicon Radio Limited
Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, UK


Member of the CSR plc group of companies. CSR plc registered in England and Wales, registered number 4187346, registered office Churchill House, Cambridge Business Park, Cowley Road, Cambridge, CB4 0WZ, United Kingdom



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