4.4BSD snapshot (revision 8.1); add 1993 to copyright
[unix-history] / usr / src / bin / sh / parser.c
index d3e72de..8e27e8f 100644 (file)
@@ -1,6 +1,6 @@
 /*-
 /*-
- * Copyright (c) 1991 The Regents of the University of California.
- * All rights reserved.
+ * Copyright (c) 1991, 1993
+ *     The Regents of the University of California.  All rights reserved.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
  *
  * This code is derived from software contributed to Berkeley by
  * Kenneth Almquist.
@@ -9,7 +9,7 @@
  */
 
 #ifndef lint
  */
 
 #ifndef lint
-static char sccsid[] = "@(#)parser.c   5.7 (Berkeley) %G%";
+static char sccsid[] = "@(#)parser.c   8.1 (Berkeley) %G%";
 #endif /* not lint */
 
 #include "shell.h"
 #endif /* not lint */
 
 #include "shell.h"
@@ -75,7 +75,7 @@ STATIC union node *list __P((int));
 STATIC union node *andor __P((void));
 STATIC union node *pipeline __P((void));
 STATIC union node *command __P((void));
 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 union node *simplecmd __P((union node **, union node *));
 STATIC void parsefname __P((void));
 STATIC void parseheredoc __P((void));
 STATIC int readtoken __P((void));
 STATIC void parsefname __P((void));
 STATIC void parseheredoc __P((void));
 STATIC int readtoken __P((void));
@@ -248,6 +248,16 @@ command() {
        int t;
 
        checkkwd = 2;
        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));
        switch (readtoken()) {
        case TIF:
                n1 = (union node *)stalloc(sizeof (struct nif));
@@ -310,6 +320,8 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
                        }
                        *app = NULL;
                        n1->nfor.args = ap;
                        }
                        *app = NULL;
                        n1->nfor.args = ap;
+                       if (lasttoken != TNL && lasttoken != TSEMI)
+                               synexpect(-1);
                } else {
 #ifndef GDB_HACK
                        static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
                } else {
 #ifndef GDB_HACK
                        static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
@@ -321,9 +333,13 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
                        n2->narg.backquote = NULL;
                        n2->narg.next = NULL;
                        n1->nfor.args = n2;
                        n2->narg.backquote = NULL;
                        n2->narg.next = NULL;
                        n1->nfor.args = n2;
+                       /*
+                        * Newline or semicolon here is optional (but note
+                        * that the original Bourne shell only allowed NL).
+                        */
+                       if (lasttoken != TNL && lasttoken != TSEMI)
+                               tokpushback++;
                }
                }
-               if (lasttoken != TNL && lasttoken != TSEMI)
-                       synexpect(-1);
                checkkwd = 2;
                if ((t = readtoken()) == TDO)
                        t = TDONE;
                checkkwd = 2;
                if ((t = readtoken()) == TDO)
                        t = TDONE;
@@ -395,16 +411,16 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
                        synexpect(TEND);
                checkkwd = 1;
                break;
                        synexpect(TEND);
                checkkwd = 1;
                break;
+       /* Handle an empty command like other simple commands.  */
+       case TNL:
        case TWORD:
        case TWORD:
-       case TREDIR:
                tokpushback++;
                tokpushback++;
-               return simplecmd();
+               return simplecmd(rpp, redir);
        default:
                synexpect(-1);
        }
 
        /* Now check for redirection which may follow command */
        default:
                synexpect(-1);
        }
 
        /* Now check for redirection which may follow command */
-       rpp = &redir;
        while (readtoken() == TREDIR) {
                *rpp = n2 = redirnode;
                rpp = &n2->nfile.next;
        while (readtoken() == TREDIR) {
                *rpp = n2 = redirnode;
                rpp = &n2->nfile.next;
@@ -426,14 +442,27 @@ TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
 
 
 STATIC union node *
 
 
 STATIC union node *
-simplecmd() {
+simplecmd(rpp, redir) 
+       union node **rpp, *redir;
+       {
        union node *args, **app;
        union node *args, **app;
-       union node *redir, **rpp;
+       union node **orig_rpp = rpp;
        union node *n;
 
        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;
        args = NULL;
        app = &args;
-       rpp = &redir;
+       /* 
+        * 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));
        for (;;) {
                if (readtoken() == TWORD) {
                        n = (union node *)stalloc(sizeof (struct narg));
@@ -447,7 +476,7 @@ simplecmd() {
                        rpp = &n->nfile.next;
                        parsefname();   /* read name of redirection file */
                } else if (lasttoken == TLP && app == &args->narg.next
                        rpp = &n->nfile.next;
                        parsefname();   /* read name of redirection file */
                } else if (lasttoken == TLP && app == &args->narg.next
-                                           && rpp == &redir) {
+                                           && rpp == orig_rpp) {
                        /* We have a function */
                        if (readtoken() != TRP)
                                synexpect(TRP);
                        /* We have a function */
                        if (readtoken() != TRP)
                                synexpect(TRP);
@@ -593,7 +622,7 @@ readtoken() {
                if (t == TWORD && !quoteflag) {
                        register char * const *pp, *s;
 
                if (t == TWORD && !quoteflag) {
                        register char * const *pp, *s;
 
-                       for (pp = parsekwd; *pp; pp++) {
+                       for (pp = (char **)parsekwd; *pp; pp++) {
                                if (**pp == *wordtext && equal(*pp, wordtext)) {
                                        lasttoken = t = pp - parsekwd + KWDOFFSET;
                                        TRACE(("keyword %s recognized\n", tokname[t]));
                                if (**pp == *wordtext && equal(*pp, wordtext)) {
                                        lasttoken = t = pp - parsekwd + KWDOFFSET;
                                        TRACE(("keyword %s recognized\n", tokname[t]));
@@ -774,6 +803,13 @@ readtoken1(firstc, syntax, eofmark, striptabs)
                CHECKEND();     /* set c to PEOF if at end of here document */
                for (;;) {      /* until end of line or end of word */
                        CHECKSTRSPACE(3, out);  /* permit 3 calls to USTPUTC */
                CHECKEND();     /* set c to PEOF if at end of here document */
                for (;;) {      /* until end of line or end of word */
                        CHECKSTRSPACE(3, out);  /* permit 3 calls to USTPUTC */
+                       if (parsebackquote && c == '\\') {
+                               c = pgetc();    /* XXX - compat with old /bin/sh */
+                               if (c != '\\' && c != '`' && c != '$') {
+                                       pungetc();
+                                       c = '\\';
+                               }
+                       }
                        switch(syntax[c]) {
                        case CNL:       /* '\n' */
                                if (syntax == BASESYNTAX)
                        switch(syntax[c]) {
                        case CNL:       /* '\n' */
                                if (syntax == BASESYNTAX)
@@ -870,12 +906,6 @@ readtoken1(firstc, syntax, eofmark, striptabs)
                                }
                                break;
                        case CBQUOTE:   /* '`' */
                                }
                                break;
                        case CBQUOTE:   /* '`' */
-                               if (parsebackquote && syntax == BASESYNTAX) {
-                                       if (out == stackblock())
-                                               return lasttoken = TENDBQUOTE;
-                                       else
-                                               goto endword;   /* exit outer loop */
-                               }
                                PARSEBACKQOLD();
                                break;
                        case CEOF:
                                PARSEBACKQOLD();
                                break;
                        case CEOF:
@@ -891,7 +921,7 @@ readtoken1(firstc, syntax, eofmark, striptabs)
 endword:
        if (syntax == ARISYNTAX)
                synerror("Missing '))'");
 endword:
        if (syntax == ARISYNTAX)
                synerror("Missing '))'");
-       if (syntax != BASESYNTAX && eofmark == NULL)
+       if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
                synerror("Unterminated quoted string");
        if (varnest != 0) {
                startlinno = plinno;
                synerror("Unterminated quoted string");
        if (varnest != 0) {
                startlinno = plinno;
@@ -1089,7 +1119,6 @@ parsebackq: {
        struct jmploc jmploc;
        struct jmploc *volatile savehandler;
        int savelen;
        struct jmploc jmploc;
        struct jmploc *volatile savehandler;
        int savelen;
-       int t;
 
        savepbq = parsebackquote;
        if (setjmp(jmploc.loc)) {
 
        savepbq = parsebackquote;
        if (setjmp(jmploc.loc)) {
@@ -1109,6 +1138,33 @@ parsebackq: {
        savehandler = handler;
        handler = &jmploc;
        INTON;
        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;
        nlpp = &bqlist;
        while (*nlpp)
                nlpp = &(*nlpp)->next;
@@ -1116,10 +1172,12 @@ parsebackq: {
        (*nlpp)->next = NULL;
        parsebackquote = oldstyle;
        n = list(0);
        (*nlpp)->next = NULL;
        parsebackquote = oldstyle;
        n = list(0);
-       t = oldstyle? TENDBQUOTE : TRP;
-       if (readtoken() != t)
-               synexpect(t);
+        if (!oldstyle && (readtoken() != TRP))
+                synexpect(TRP);
        (*nlpp)->n = n;
        (*nlpp)->n = n;
+        /* Start reading from old file again.  */
+        if (oldstyle)
+                popfile();
        while (stackblocksize() <= savelen)
                growstackblock();
        STARTSTACKSTR(out);
        while (stackblocksize() <= savelen)
                growstackblock();
        STARTSTACKSTR(out);