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