[net.unix-wizards] possible problem in csh if/then/endif

brighton@pixar.UUCP (Bill Carson) (10/03/86)

consider this:  (as run on a virgin 4.3BSD csh (vax))

% cat -n test.sh

     1	#! /bin/csh -fx
     2	
     3	set machine = string
     4	set host    = string
     5	set foo     = bar
     6	
     7	if ( $machine !~ $host ) then
     8		if ( $foo =~ bar ) then
     9			echo dummy if statement
    10		endif
    11		echo this should not happen
    12	else
    13		echo this should happen
    14	endif

running this works as you would expect, it will echo "this should happen"

Ok, on line 8, if you remove the space between the 'if' and the '(' so that
it looks like this:

     8	if( $foo =~ bar ) then

then it will echo "this should not happen".   I know this sounds confusing,
but extract the shell script, play with it, and you'll see what I mean.
This is most annoying, because I've just been bitten by this bug in our 
backup scripts.  Joe Bob says "pull your hair out"

Can anyone provide an explanation?

			-Bill      ...!{ucbvax,sun}!pixar!brighton

lcc.richard@LOCUS.UCLA.EDU (Richard Mathews) (10/08/86)

> The following script ends up echoing "this should not happen"
>
> #! /bin/csh -fx
>
> set machine = string
> set host    = string
> set foo     = bar
>
> if ( $machine !~ $host ) then
>	  if( $foo =~ bar ) then	# no space after "if"
>		  echo dummy if statement
>	  endif
>	  echo this should not happen
> else
>	  echo this should happen
> endif

The problem is in the functions search() and getword() in the C-shell.
After finding "if ( false-expression ) then", the function search() looks
for a matching else/endif.  It uses getword() to look at each word until
it finds either "else" or "endif".  If it finds another "if", it increments
a counter, and the search is not complete until the counter makes its way
back to zero.

So far, so good.  The problem is that getword() considers "if(" to be a
single word.  This means that the counter does not get incremented when
the "parser" sees this.  The result is that the FIRST "endif" in your
script terminates the FIRST "if" and the echo below that "endif" is
executed.

A separate bug results in an unexpected "else ... endif" being silently
ignored.  Try typing the following to a C-shell:

	else
	echo spam
	endif

Richard M. Mathews
Locus Computing Corporation		       lcc.richard@LOCUS.UCLA.EDU
					       lcc.richard@UCLA-CS
				 {ihnp4,trwrb}!lcc!richard
       {randvax,sdcrdcf,ucbvax,trwspp}!ucla-cs!lcc!richard

dv@well.UUCP (David W. Vezie) (10/09/86)

In article <113@pixar.UUCP> brighton@pixar.UUCP (Bill Carson) writes:
>consider this:  (as run on a virgin 4.3BSD csh (vax))
>
>     1	#! /bin/csh -fx
>     2	
>     3	set machine = string
>     4	set host    = string
>     5	set foo     = bar
>     6	
>     7	if ( $machine !~ $host ) then
>     8		if ( $foo =~ bar ) then
>     9			echo dummy if statement
>    10		endif
>    11		echo this should not happen
>    12	else
>    13		echo this should happen
>    14	endif
>
>Ok, on line 8, if you remove the space between the 'if' and the '(' so that
>it looks like this:
>
>     8	if( $foo =~ bar ) then
>
>then it will echo "this should not happen".   I know this sounds confusing,
>but extract the shell script, play with it, and you'll see what I mean.


What's happening is that it evaluates the first if, which in this
case returns false.  It then calls search() (for those of you with
source, this is all in sh.func.c), looking for an 'else' or an
'endif'.  It uses a very limited parser for this (the function
getword()), instead of using the full blown parsing routines in
sh.lex.c.  That parser knows about the basic word delimiter characters
(space, tab, single/double quote, pound-sign (why's it called
"sharp"?), etc).  Unfortunatelly, it doesn't know about '(', so when it
reads "if( $foo =~ bar ) then", it treats the "if(" as one word.  Since
it doesn't see it as "if", it treats the following "endif" as the
"endif" that corresponds to the original "if" and runs everything
after that.  By the way, it's technically not an error to have an
"else" without a corresponding "if".  It just ignores everything
between the "else" and the next "endif".

This has been a problem with csh since at least 4.1 (probably forever).
As far as fixes go, the fix would probably be to tell getword() about '('.
I havn't tried installing a fix, as I use a safer programming style :-)

> ...  Joe Bob says "pull your hair out"

Yes, well, that's what you get for asking HIM...  :-)

>			-Bill      ...!{ucbvax,sun}!pixar!brighton

--- 
David W. Vezie
	    {dual|hplabs}!well!dv - Whole Earth 'Lectronics Link, Sausalito, CA
(4 lines, 114 chars)

idallen@watmath.UUCP (10/20/86)

The parser for IF/ENDIF is even more interesting than that.  Try this one:

    if ( 0 ) then
	if ( 0 ) echo You just do not think then
    endif

The parser that skips to the ENDIF thinks that the middle line is an
if-then line, because it starts with IF and ends with THEN.  So it 
pairs it up with the ENDIF on the third line and goes looking for a
*second* ENDIF... 

Who wrote this stuff?
-- 
        -IAN!  (Ian! D. Allen)      University of Waterloo