*BSD News Article 14675


Return to BSD News archive

Newsgroups: comp.os.386bsd.bugs
Path: sserve!newshost.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!agate!headwall.Stanford.EDU!kithrup.com!moria.cygnus.com!wilson
From: wilson@moria.cygnus.com (jim Wilson)
Subject: /bin/sh (aka ash) patches for C-news, etc)
Organization: Cygnus Support
Nntp-Posting-Host: moria.cygnus.com
Message-ID: <C5pyFv.r7@kithrup.com>
Sender: news@kithrup.com (Network News)
Date: Mon, 19 Apr 1993 07:06:52 GMT
Lines: 489


I have been fixing /bin/sh (aka ash) bugs.  If you know of shell bugs that
aren't fixed by this patch, or have problems with this patch, send me
a bug report and I will try to fix it.

This patch fixes 7 bugs:
1) correct handling of \ within backquotes
2) don't require \n or ; before do in `for var do'
3) accept redir before (non-simple) command
4) don't expand regexps as case pattern if inside string 
5) for a background command, don't redirect stdin to /dev/null if stdin has
   been previously redirected
6) set exitstatus for backquoted commands
7) correctly expand $n inside quotes

This patch should fix all problems with C-news.
It also fixes some problems with the SPEC benchmark suite, groff 1.07, etc.

I have test cases for each of these patches for anyone who is interested
in seeing them.

Jim Wilson
wilson@moria.cygnus.com

diff -pr /usr/src/bin/sh/expand.c sh/expand.c
*** /usr/src/bin/sh/expand.c	Sun Oct 25 00:07:38 1992
--- sh/expand.c	Sun Apr 18 23:34:55 1993
*************** expbackq(cmd, quoted, full)
*** 300,306 ****
  	if (in.buf)
  		ckfree(in.buf);
  	if (in.jp)
! 		waitforjob(in.jp);
  	if (quoted == 0)
  		recordregion(startloc, dest - stackblock(), 0);
  	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
--- 300,306 ----
  	if (in.buf)
  		ckfree(in.buf);
  	if (in.jp)
! 		exitstatus = waitforjob(in.jp);
  	if (quoted == 0)
  		recordregion(startloc, dest - stackblock(), 0);
  	TRACE(("evalbackq: size=%d: \"%.*s\"\n",
*************** numvar:
*** 499,505 ****
  	case '*':
  		sep = ' ';
  allargs:
! 		syntax = quoted? DQSYNTAX : BASESYNTAX;
  		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
  			/* should insert CTLESC characters */
  			while (*p) {
--- 499,507 ----
  	case '*':
  		sep = ' ';
  allargs:
! 		/* Only emit CTLESC if we will do further processing,
! 		   i.e. if allow_split is set.  */
! 		syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
  		for (ap = shellparam.p ; (p = *ap++) != NULL ; ) {
  			/* should insert CTLESC characters */
  			while (*p) {
*************** allargs:
*** 514,520 ****
  	case '0':
  		p = arg0;
  string:
! 		syntax = quoted? DQSYNTAX : BASESYNTAX;
  		while (*p) {
  			if (syntax[*p] == CCTL)
  				STPUTC(CTLESC, expdest);
--- 516,524 ----
  	case '0':
  		p = arg0;
  string:
! 		/* Only emit CTLESC if we will do further processing,
! 		   i.e. if allow_split is set.  */
! 		syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX;
  		while (*p) {
  			if (syntax[*p] == CCTL)
  				STPUTC(CTLESC, expdest);
*************** casematch(pattern, val)
*** 1095,1101 ****
  	argbackq = pattern->narg.backquote;
  	STARTSTACKSTR(expdest);
  	ifslastp = NULL;
! 	argstr(pattern->narg.text, 0);
  	STPUTC('\0', expdest);
  	p = grabstackstr(expdest);
  	result = patmatch(p, val);
--- 1099,1107 ----
  	argbackq = pattern->narg.backquote;
  	STARTSTACKSTR(expdest);
  	ifslastp = NULL;
! 	/* Preserve any CTLESC characters inserted previously, so that
! 	   we won't expand reg exps which are inside strings.  */
! 	argstr(pattern->narg.text, 1);
  	STPUTC('\0', expdest);
  	p = grabstackstr(expdest);
  	result = patmatch(p, val);
diff -pr /usr/src/bin/sh/jobs.c sh/jobs.c
*** /usr/src/bin/sh/jobs.c	Mon Apr 15 17:23:08 1991
--- sh/jobs.c	Sat Apr 17 16:16:35 1993
*************** static char sccsid[] = "@(#)jobs.c	5.1 (
*** 56,61 ****
--- 56,62 ----
  #include "memalloc.h"
  #include "error.h"
  #include "mystring.h"
+ #include "redir.h"
  #include <fcntl.h>
  #include <signal.h>
  #include <errno.h>
*************** forkshell(jp, n, mode)
*** 550,556 ****
  		} else if (mode == FORK_BG) {
  			ignoresig(SIGINT);
  			ignoresig(SIGQUIT);
! 			if (jp == NULL || jp->nprocs == 0) {
  				close(0);
  				if (open("/dev/null", O_RDONLY) != 0)
  					error("Can't open /dev/null");
--- 551,558 ----
  		} else if (mode == FORK_BG) {
  			ignoresig(SIGINT);
  			ignoresig(SIGQUIT);
! 			if ((jp == NULL || jp->nprocs == 0)
! 			    && ! fd0_redirected_p ()) {
  				close(0);
  				if (open("/dev/null", O_RDONLY) != 0)
  					error("Can't open /dev/null");
*************** forkshell(jp, n, mode)
*** 560,566 ****
  		if (mode == FORK_BG) {
  			ignoresig(SIGINT);
  			ignoresig(SIGQUIT);
! 			if (jp == NULL || jp->nprocs == 0) {
  				close(0);
  				if (open("/dev/null", O_RDONLY) != 0)
  					error("Can't open /dev/null");
--- 562,569 ----
  		if (mode == FORK_BG) {
  			ignoresig(SIGINT);
  			ignoresig(SIGQUIT);
! 			if ((jp == NULL || jp->nprocs == 0)
! 			    && ! fd0_redirected_p ()) {
  				close(0);
  				if (open("/dev/null", O_RDONLY) != 0)
  					error("Can't open /dev/null");
diff -pr /usr/src/bin/sh/parser.c sh/parser.c
*** /usr/src/bin/sh/parser.c	Mon Apr 15 17:23:22 1991
--- sh/parser.c	Sat Apr 17 14:58:56 1993
*************** STATIC union node *list __P((int));
*** 99,105 ****
  STATIC union node *andor __P((void));
  STATIC union node *pipeline __P((void));
  STATIC union node *command __P((void));
! STATIC union node *simplecmd __P((void));
  STATIC void parsefname __P((void));
  STATIC void parseheredoc __P((void));
  STATIC int readtoken __P((void));
--- 99,105 ----
  STATIC union node *andor __P((void));
  STATIC union node *pipeline __P((void));
  STATIC union node *command __P((void));
! STATIC union node *simplecmd __P((union node **, union node *));
  STATIC void parsefname __P((void));
  STATIC void parseheredoc __P((void));
  STATIC int readtoken __P((void));
*************** command() {
*** 264,269 ****
--- 264,279 ----
  	int t;
  
  	checkkwd = 2;
+ 	redir = 0;
+ 	rpp = &redir;
+ 	/* Check for redirection which may precede command */
+ 	while (readtoken() == TREDIR) {
+ 		*rpp = n2 = redirnode;
+ 		rpp = &n2->nfile.next;
+ 		parsefname();
+ 	}
+ 	tokpushback++;
+ 
  	switch (readtoken()) {
  	case TIF:
  		n1 = (union node *)stalloc(sizeof (struct nif));
*************** TRACE(("expecting DO got %s %s\n", tokna
*** 326,331 ****
--- 336,345 ----
  			}
  			*app = NULL;
  			n1->nfor.args = ap;
+ 			/* A newline or semicolon is required here to end
+ 			   the list.  */
+ 			if (lasttoken != TNL && lasttoken != TSEMI)
+ 				synexpect(-1);
  		} else {
  #ifndef GDB_HACK
  			static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
*************** TRACE(("expecting DO got %s %s\n", tokna
*** 337,345 ****
  			n2->narg.backquote = NULL;
  			n2->narg.next = NULL;
  			n1->nfor.args = n2;
  		}
- 		if (lasttoken != TNL && lasttoken != TSEMI)
- 			synexpect(-1);
  		checkkwd = 2;
  		if ((t = readtoken()) == TDO)
  			t = TDONE;
--- 351,361 ----
  			n2->narg.backquote = NULL;
  			n2->narg.next = NULL;
  			n1->nfor.args = n2;
+ 			/* A newline or semicolon is optional here. Anything
+ 			   else gets pushed back so we can read it again.  */
+ 			if (lasttoken != TNL && lasttoken != TSEMI)
+ 				tokpushback++;
  		}
  		checkkwd = 2;
  		if ((t = readtoken()) == TDO)
  			t = TDONE;
*************** TRACE(("expecting DO got %s %s\n", tokna
*** 411,426 ****
  			synexpect(TEND);
  		checkkwd = 1;
  		break;
  	case TWORD:
- 	case TREDIR:
  		tokpushback++;
! 		return simplecmd();
  	default:
  		synexpect(-1);
  	}
  
  	/* Now check for redirection which may follow command */
- 	rpp = &redir;
  	while (readtoken() == TREDIR) {
  		*rpp = n2 = redirnode;
  		rpp = &n2->nfile.next;
--- 427,442 ----
  			synexpect(TEND);
  		checkkwd = 1;
  		break;
+ 	/* Handle an empty command like other simple commands.  */
+ 	case TNL:
  	case TWORD:
  		tokpushback++;
! 		return simplecmd(rpp, redir);
  	default:
  		synexpect(-1);
  	}
  
  	/* Now check for redirection which may follow command */
  	while (readtoken() == TREDIR) {
  		*rpp = n2 = redirnode;
  		rpp = &n2->nfile.next;
*************** TRACE(("expecting DO got %s %s\n", tokna
*** 442,455 ****
  
  
  STATIC union node *
! simplecmd() {
  	union node *args, **app;
! 	union node *redir, **rpp;
  	union node *n;
  
  	args = NULL;
  	app = &args;
! 	rpp = &redir;
  	for (;;) {
  		if (readtoken() == TWORD) {
  			n = (union node *)stalloc(sizeof (struct narg));
--- 458,481 ----
  
  
  STATIC union node *
! simplecmd(rpp, redir)
! 	union node **rpp, *redir;
! 	{
  	union node *args, **app;
! 	union node **orig_rpp = rpp;
  	union node *n;
  
+ 	/* If we don't have any redirections already, then we must reset
+ 	   rpp to be the address of the local redir variable.  */
+ 	if (redir == 0)
+ 		rpp = &redir;
+ 
  	args = NULL;
  	app = &args;
! 	/* We save the incoming value, because we need this for shell
! 	   functions.  There can not be a redirect or an argument between
! 	   the function name and the open parenthesis.  */
! 	orig_rpp = rpp;
  	for (;;) {
  		if (readtoken() == TWORD) {
  			n = (union node *)stalloc(sizeof (struct narg));
*************** simplecmd() {
*** 463,469 ****
  			rpp = &n->nfile.next;
  			parsefname();	/* read name of redirection file */
  		} else if (lasttoken == TLP && app == &args->narg.next
! 					    && rpp == &redir) {
  			/* We have a function */
  			if (readtoken() != TRP)
  				synexpect(TRP);
--- 489,495 ----
  			rpp = &n->nfile.next;
  			parsefname();	/* read name of redirection file */
  		} else if (lasttoken == TLP && app == &args->narg.next
! 					    && rpp == orig_rpp) {
  			/* We have a function */
  			if (readtoken() != TRP)
  				synexpect(TRP);
*************** readtoken1(firstc, syntax, eofmark, stri
*** 836,847 ****
  				}
  				break;
  			case CBQUOTE:	/* '`' */
- 				if (parsebackquote && syntax == BASESYNTAX) {
- 					if (out == stackblock())
- 						return lasttoken = TENDBQUOTE;
- 					else
- 						goto endword;	/* exit outer loop */
- 				}
  				PARSEBACKQOLD();
  				break;
  			case CEOF:
--- 862,867 ----
*************** readtoken1(firstc, syntax, eofmark, stri
*** 855,861 ****
  		}
  	}
  endword:
! 	if (syntax != BASESYNTAX && eofmark == NULL)
  		synerror("Unterminated quoted string");
  	if (varnest != 0) {
  		startlinno = plinno;
--- 875,881 ----
  		}
  	}
  endword:
! 	if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
  		synerror("Unterminated quoted string");
  	if (varnest != 0) {
  		startlinno = plinno;
*************** parsebackq: {
*** 1048,1054 ****
  	struct jmploc jmploc;
  	struct jmploc *volatile savehandler;
  	int savelen;
- 	int t;
  
  	savepbq = parsebackquote;
  	if (setjmp(jmploc.loc)) {
--- 1068,1073 ----
*************** parsebackq: {
*** 1068,1073 ****
--- 1087,1119 ----
  	savehandler = handler;
  	handler = &jmploc;
  	INTON;
+ 	if (oldstyle) {
+ 		/* We must read until the closing backquote, giving special
+ 		   treatment to some slashes, and then push the string and
+ 		   reread it as input, interpreting it normally.  */
+ 		register char *out;
+ 		register c;
+ 		int savelen;
+ 		char *str;
+ 
+ 		STARTSTACKSTR(out);
+ 		while ((c = pgetc ()) != '`') {
+ 			if (c == '\\') {
+ 				c = pgetc ();
+ 				if (c != '\\' && c != '`' && c != '$'
+ 				    && (!dblquote || c != '"'))
+ 					STPUTC('\\', out);
+ 			}
+ 			STPUTC(c, out);
+ 		}
+ 		STPUTC('\0', out);
+ 		savelen = out - stackblock();
+ 		if (savelen > 0) {
+ 			str = ckmalloc(savelen);
+ 			bcopy(stackblock(), str, savelen);
+ 		}
+ 		setinputstring(str, 1);
+ 	}
  	nlpp = &bqlist;
  	while (*nlpp)
  		nlpp = &(*nlpp)->next;
*************** parsebackq: {
*** 1075,1084 ****
  	(*nlpp)->next = NULL;
  	parsebackquote = oldstyle;
  	n = list(0);
! 	t = oldstyle? TENDBQUOTE : TRP;
! 	if (readtoken() != t)
! 		synexpect(t);
  	(*nlpp)->n = n;
  	while (stackblocksize() <= savelen)
  		growstackblock();
  	STARTSTACKSTR(out);
--- 1121,1132 ----
  	(*nlpp)->next = NULL;
  	parsebackquote = oldstyle;
  	n = list(0);
! 	if (!oldstyle && (readtoken() != TRP))
! 		synexpect(TRP);
  	(*nlpp)->n = n;
+ 	/* Start reading from old file again.  */
+ 	if (oldstyle)
+ 		popfile();
  	while (stackblocksize() <= savelen)
  		growstackblock();
  	STARTSTACKSTR(out);
diff -pr /usr/src/bin/sh/redir.c sh/redir.c
*** /usr/src/bin/sh/redir.c	Mon Apr 15 17:23:23 1991
--- sh/redir.c	Sat Apr 17 16:15:10 1993
*************** struct redirtab {
*** 68,73 ****
--- 68,77 ----
  
  MKINIT struct redirtab *redirlist;
  
+ /* We keep track of whether or not fd0 has been redirected.  This is for
+    background commands, where we want to redirect fd0 to /dev/null only
+    if it hasn't already been redirected.  */
+ int fd0_redirected = 0;
  
  #ifdef __STDC__
  STATIC void openredirect(union node *, char *);
*************** redirect(redir, flags)
*** 122,127 ****
--- 126,133 ----
  		} else {
  			close(fd);
  		}
+ 		if (fd == 0)
+ 			fd0_redirected++;
  		openredirect(n, memory);
  	}
  	if (memory[1])
*************** popredir() {
*** 255,260 ****
--- 261,268 ----
  
  	for (i = 0 ; i < 10 ; i++) {
  		if (rp->renamed[i] != EMPTY) {
+ 			if (i == 0)
+ 				fd0_redirected--;
  			close(i);
  			if (rp->renamed[i] >= 0) {
  				copyfd(rp->renamed[i], i);
*************** copyfd(from, to) {
*** 347,350 ****
--- 355,364 ----
  		return EMPTY;
  	return newfd;
  #endif
+ }
+ 
+ /* Return true if fd 0 has already been redirected at least once.  */
+ int
+ fd0_redirected_p () {
+ 	return fd0_redirected != 0;
  }
diff -pr /usr/src/bin/sh/redir.h sh/redir.h
*** /usr/src/bin/sh/redir.h	Mon Apr 15 17:23:24 1991
--- sh/redir.h	Sat Apr 17 16:15:09 1993
*************** void redirect(union node *, int);
*** 46,54 ****
--- 46,56 ----
  void popredir(void);
  void clearredir(void);
  int copyfd(int, int);
+ int fd0_redirected_p(void);
  #else
  void redirect();
  void popredir();
  void clearredir();
  int copyfd();
+ int fd0_redirected_p();
  #endif