[net.bugs.4bsd] Argument-swap feature within csh

jeff (04/15/83)

I kinda like this one.

Here's a fun test for your next prospective job candidate...
Ask her what the following csh script does.  Just for fun, ask yourself, too.

#! /bin/csh -f
set noglob
echo '1' >/tmp/foo
echo `/bin/cat /tmp/foo` -2 3

STOP.  Do not turn the page until you have the answer.
===============================================================================

In case you don't have a 4.1BSD UNIX system, I'll reproduce the output below:

% bug
-2 1 3
%

Did you get that?  Good.

Now explain the following:
	- Suppose you were to comment out the "set noglob" statement.  Why
	  does this swap the first two arguments?
	- Suppose you change the second argument from "-2" to "2".
	  Why does this...?


You have 30 minutes to complete this exercise.  If you find the answer, would
you kindly let me know?

	Jeff Stearns     ...!decvax!microsoft!fluke!jeff
	John Fluke Mfg. Co., Everett WA.  (206) 356-5064

donn (04/16/83)

Reference: fluke.859

Jeff Stearns says:

	I kinda like this one.

So do I -- it reminds me of an experience I had when I was evaluating
the C-shell for use at Informatics (the contractor at NASA Life
Sciences at Moffett Field, Sunnyvale, California) way back in 1980.
Even though the bug I found at that time has since been repaired, I
can't resist telling everyone about it because it was so incredibly
weird.  It instilled in me great respect for the complexity of large
programs.  To make up for this di(trans?)gression I will first answer
Jeff's plea for an explanation of his own problem (and I will even give
the relevant fix!).  Yes, this takes less than 30 minutes to solve; if
you still want to solve it yourself, read no further.

	Here's a fun test for your next prospective job candidate...
	Ask her what the following csh script does.  Just for fun, ask
	yourself, too.

	#! /bin/csh -f
	set noglob
	echo '1' >/tmp/foo
	echo `/bin/cat /tmp/foo` -2 3

	STOP.  Do not turn the page until you have the answer.
	===============================================================

	In case you don't have a 4.1BSD UNIX system, I'll reproduce the
	output below:

	% bug
	-2 1 3
	%

	Did you get that?  Good...

	You have 30 minutes to complete this exercise.  If you find the
	answer, would you kindly let me know?

Actually this presentation of the bug is a bit misleading.  A little
experimentation shows that you can also do the following, interactively:

	% set noglob
	% echo `echo 5 4 3` 2 1
	2 3 4 5 1
	%

The problem here is that the C-shell has sorted the output of the
command expansion in backquotes as though it was the output of a "glob"
or pattern matching operation.  Interestingly, the sort also includes
the first argument following the command expansion.  What has happened
is that the C-shell has marked the command expansion for possible
"globbing" and hence sorting, but since "noglob" is set, it decides
that can't do the sort anyway and it skips on to the next argument.
Unfortunately it "forgets" to reset its sort pointer, and as a result
instead of the C-shell (redundantly) sorting the single argument
following the command expansion, it sorts the whole string consisting
of the command expansion plus the following argument.  The change to
fix this trivial bug must be made in sh.glob.c (I have prettied this
"diff" up a bit, mind you):

*** sh.glob.c.old	Sat Apr 16 01:13:23 1983
--- sh.glob.c		Sat Apr 16 01:16:31 1983
***************
*** 66,72
  		printf("backp done, acollect'ing\n");
  #endif
  		for (i = 0; i < pargc; i++)
! 			if (noglob)
  				Gcat(pargv[i], "");
! 			else
  				acollect(pargv[i]);

--- 66,77 -----
  		printf("backp done, acollect'ing\n");
  #endif
  		for (i = 0; i < pargc; i++)
! 			if (noglob) {
! 				/*
! 				 * Bug fixed -- prevent sorting of backquote
! 				 *	substitutions when noglob is set.
! 				 *	Donn Seeley, UCSD Chemistry, 4/15/83.
! 				 */
  				Gcat(pargv[i], "");
! 				sortbas = &gargv[gargc];
! 			} else
  				acollect(pargv[i]);

Now, with that out of the way, let me quote my own bug report from 1980:

------------------------------------------------------------------------

C-Shell Bugs / August 15, 1980 / Page 7

6.  Argument lists and nonomatch

The nonomatch cshell variable is supposed to prevent the cshell from
stopping with an error when it encounters an expression with *, ? or []
(wild cards) that matches no existing filename.  Strange things happen
to the ordering of elements in an argument list when this matching
process ("globbing") fails, however.  The cshell leaves the original
wild card expressions untouched when this occurs, but unfortunately it
still tries to sort the patterns just as though they had successfully
matched filenames.  In particular sublists consisting of zero or more
consecutive unmatched wild card arguments followed by an argument with
no wild cards are ASCII sorted in the list:

	% mkdir junk
	% cd junk
	% set nonomatch
	% echo 8* 7* 5* 6 1* 3* 2 4* 0*
	5* 6 7* 8* 1* 2 3* 4* 0*
	%

This has peculiar consequences for 'foreach' loops.  Since cshell input
is not parsed, left and right parentheses are just arguments which
receive certain special treatment.  The cshell checks to see that
parentheses are balanced, and the 'foreach' statement checks to make
sure that all its list arguments are in parentheses, but it does this
checking before it does argument sorting.  When unmatched wild card
arguments appear at the end of the argument list, the cshell sorts the
sublist of arguments that ends in the final right parenthesis.  Since
the right parenthesis comes before the alphabet in the ASCII character
set, it is usually transported by the sort to the beginning of the
sublist and some wild card argument comes to terminate the list; the
cshell throws this argument away, thinking it to be a right
parenthesis, and the right parenthesis becomes one of the values taken
on by the 'foreach' variable.  This can be very mysterious.

	% foreach i ( z* x* w* )
	? echo "$i"
	? end
	)
	w*
	x*
	%

------------------------------------------------------------------------

I guess I just have a strange sense of humor.

Donn Seeley  UCSD Chemistry Dept. RRCF  ucbvax!sdcsvax!sdchema!donn
             (619) 452-4016             sdamos!donn@nprdc