[gnu.bash.bug] fixes for test.c

chet@kiwi.INS.CWRU.Edu (Chet Ramey) (07/01/89)

There are a few problems with the test builtin as released with bash 1.01.

1.  I claim that test handles the unary negation operator (`!') incorrectly.
    Here is the test program I used:

	if [ ! -d /usr -a ! -d /etc ] ; then
		echo "Bogus"
	else
		echo "OK"
	fi

    I claim that "OK" should be echoed (on any Unix system).  The following 
    Bourne-style shells agree with me:
		4.3 BSD 	/bin/sh
		SunOS 3.5 	/bin/sh	(s5r2-based)
		Ultrix 3.0	/bin/sh (4.2 BSD +)
		ksh version 06/03/86b
		Ultrix 3.0	/bin/sh5 (s5r2 shell)
		AIX 2.2.1	/bin/sh
		s5r2 		/bin/sh
		s5r3.2		/bin/sh
		ash			 (Kenneth Almquist's shell)

    Vanilla Bash 1.01 echos "[: too many arguments".  The problem is that
    "!" is recognized and handled in expr(), not in term().  The fix is
    simple.

2.  The precendeces of -a and -o are switched.  Fixes for this have already
    been posted by andy@csvax.caltech.edu (<8906240223.AA27273@csvax.caltech.edu>);
    I mention them only because they are also in the diffs.

3.  I posted a fix for the test_syntax_error problem yesterday (6/28); that
    patch is in here.

4.  The problem with -w, as reported by Peter Su (hugo@GRIGGS.DARTMOUTH.EDU),
    can be fixed by using access().  I changed -r to do so as well (-x already
    did).

*** bash-1.01/test.c	Fri Jun 23 00:43:13 1989
--- src-1.01/test.c	Fri Jun 30 12:57:05 1989
***************
*** 33,39 ****
  #include <sys/stat.h>
  #include <sys/file.h>
! #ifndef X_OK
  #define X_OK 1
! #endif
  #ifndef lint
  static char *rcsid="$Id: gtest.c,v 1.10 88/07/02 13:34:45 afb Exp Locker: afb $";
--- 33,42 ----
  #include <sys/stat.h>
  #include <sys/file.h>
! #ifndef R_OK
! #define R_OK 4
! #define W_OK 2
  #define X_OK 1
! #define F_OK 0
! #endif /* R_OK */
  #ifndef lint
  static char *rcsid="$Id: gtest.c,v 1.10 88/07/02 13:34:45 afb Exp Locker: afb $";
***************
*** 67,71 ****
--- 70,78 ----
  #endif  /* STANDALONE */
  
+ #ifndef SYSV
  int sys_v = 0;
+ #else
+ int sys_v = 1;
+ #endif
  
  static int pos;			/* position in list			*/
***************
*** 73,76 ****
--- 80,84 ----
  static char **argv;		/* the argument list			*/
  
+ #ifndef SYSV
  static void
  test_syntax_error (pchFmt, args)
***************
*** 84,87 ****
--- 92,110 ----
  }
  
+ #else
+ #include <varargs.h>
+ 
+ static void
+ test_syntax_error(fmt, va)
+ char *fmt;
+ va_list va;
+ {
+   (void) fprintf(stderr,"%s: ", argv[0]);
+   (void) vfprintf(stderr, fmt, va);
+   (void) fflush(stderr);                /* just in case */
+   test_exit(SHELL_BOOLEAN(FALSE));
+ }
+ #endif /* SYSV */
+ 
  test_io_error (name)
       char *name;
***************
*** 195,198 ****
--- 218,222 ----
   *
   * term ::= 
+  *	'!' term
   *	'-'('h'|'d'|'f'|'r'|'s'|'w'|'c'|'b'|'p'|'u'|'g'|'k') filename
   *	'-'('L'|'x') filename
***************
*** 217,221 ****
  				 * expressions of the form '-l string'? 
  				 */
!   auto int value, fd;
  
    if (pos >= argc)
--- 241,245 ----
  				 * expressions of the form '-l string'? 
  				 */
!   auto int value;
  
    if (pos >= argc)
***************
*** 222,225 ****
--- 246,262 ----
      beyond();
  
+   if ('!' == argv[pos][0] && '\000' == argv[pos][1])
+     {
+       value = FALSE;
+ 
+       /* Deal with leading "not"s. */
+       while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1])
+         {
+           advance(1);
+           value ^= (TRUE);
+ 	}
+       return (value ^ term());		/* recursive descent */
+     }
+ 
    if ('(' == argv[pos][0] && '\000' == argv[pos][1])
      {
***************
*** 469,476 ****
  	case 'r':		/* file is readable? */
  	  unary_advance ();
! 	  fd = open (argv[pos - 1], O_RDONLY, 0600);
! 	  if (value = fd != -1)
! 	    close (fd);
! 
  	  return (TRUE == value);
  
--- 506,510 ----
  	case 'r':		/* file is readable? */
  	  unary_advance ();
! 	  value = -1 != access (argv[pos - 1], R_OK);
  	  return (TRUE == value);
  
***************
*** 477,487 ****
  	case 'w':		/* File is writeable? */
  	  unary_advance ();
! 	  fd = open (argv[pos - 1], O_WRONLY, 0600);
! 	  if (value = fd != -1)
! 	    close (fd);
! 
  	  return (TRUE == value);
  
! 	case 'x':		/* File is executable? */
  	  unary_advance ();
  	  value = -1 != access (argv[pos - 1], X_OK);
--- 511,518 ----
  	case 'w':		/* File is writeable? */
  	  unary_advance ();
! 	  value = -1 != access (argv[pos - 1], W_OK);
  	  return (TRUE == value);
  
! 	case 'x':		/* File is executable?  Should we use executable_file() for this?*/
  	  unary_advance ();
  	  value = -1 != access (argv[pos - 1], X_OK);
***************
*** 600,609 ****
  
  /*
!  * or:
!  *	or '-o' term
   *	term
   */
  static int
! or ()
  {
    auto int value;
--- 631,640 ----
  
  /*
!  * and:
!  *	and '-a' term
   *	term
   */
  static int
! and ()
  {
    auto int value;
***************
*** 610,616 ****
  
    value = term ();
!   while (pos < argc && '-' == argv[pos][0] && 'o' == argv[pos][1] && '\000' == argv[pos][2]) {
      advance(0);
!     value = TRUTH_OR (value, term ());
    }
    return (TRUE == value);
--- 641,647 ----
  
    value = term ();
!   while (pos < argc && '-' == argv[pos][0] && 'a' == argv[pos][1] && '\000' == argv[pos][2]) {
      advance(0);
!     value = TRUTH_AND (value, term ());
    }
    return (TRUE == value);
***************
*** 618,634 ****
  
  /*
!  * and:
!  *	and '-a' or
!  *	or
   */
  static int
! and()
  {
    auto int value;
  
!   value = or();
!   while (pos < argc && '-' == argv[pos][0] && 'a' == argv[pos][1] && '\000' == argv[pos][2]) {
!     advance(1);
!     value = TRUTH_AND (value, or());
    }
    return (TRUE == value);
--- 649,665 ----
  
  /*
!  * or:
!  *	or '-o' and
!  *	and
   */
  static int
! or()
  {
    auto int value;
  
!   value = and();
!   while (pos < argc && '-' == argv[pos][0] && 'o' == argv[pos][1] && '\000' == argv[pos][2]) {
!     advance(0);
!     value = TRUTH_OR (value, and());
    }
    return (TRUE == value);
***************
*** 637,642 ****
  /*
   * expr:
!  *	'!' expr
!  *	and
   */
  int
--- 668,672 ----
  /*
   * expr:
!  *	or
   */
  int
***************
*** 643,660 ****
  expr()
  {
-   auto int value;
- 
    if (pos >= argc)
      beyond();
!   value = FALSE;
! 
!   /* Deal with leading "not"'s. */
!   while (pos < argc && '!' == argv[pos][0] && '\000' == argv[pos][1]) {
!     advance(1);
! 
!     /* This has to be rewritten to handle the TRUTH and FALSE stuff. */
!     value ^= (TRUE);
!   }
!   return value ^ and();		/* Same with this. */
  }
  
--- 673,679 ----
  expr()
  {
    if (pos >= argc)
      beyond();
!   return FALSE ^ or();		/* Same with this. */
  }
  
***************
*** 687,690 ****
--- 706,710 ----
        test_exit (SHELL_BOOLEAN (FALSE));
      if (']' != margv[margc][0] || '\000' != margv[margc][1]) {
+       argv = margv;				/* for test_syntax_error */
        test_syntax_error ("missing `]'\n");
      }

Chet Ramey			"We are preparing to think about contemplating 
Network Services Group, CWRU	 preliminary work on plans to develop a
chet@cwjcc.INS.CWRU.Edu		 schedule for producing the 10th Edition of 
				 the Unix Programmers Manual." -- Andrew Hume