📄 parser.c
字号:
/*- * Copyright (c) 1991 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */#ifndef lintstatic char sccsid[] = "@(#)parser.c 5.3 (Berkeley) 4/12/91";#endif /* not lint */#include "shell.h"#include "parser.h"#include "nodes.h"#include "expand.h" /* defines rmescapes() */#include "redir.h" /* defines copyfd() */#include "syntax.h"#include "options.h"#include "input.h"#include "output.h"#include "var.h"#include "error.h"#include "memalloc.h"#include "mystring.h"/* * Shell command parser. */#define EOFMARKLEN 79/* values returned by readtoken */#include "token.def"struct heredoc { struct heredoc *next; /* next here document in list */ union node *here; /* redirection node */ char *eofmark; /* string indicating end of input */ int striptabs; /* if set, strip leading tabs */};struct heredoc *heredoclist; /* list of here documents to read */int parsebackquote; /* nonzero if we are inside backquotes */int doprompt; /* if set, prompt the user */int needprompt; /* true if interactive and at start of line */int lasttoken; /* last token read */MKINIT int tokpushback; /* last token pushed back */char *wordtext; /* text of last word returned by readtoken */int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */struct nodelist *backquotelist;union node *redirnode;struct heredoc *heredoc;int quoteflag; /* set if (part of) last token was quoted */int startlinno; /* line # where last token started */#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */#ifdef GDB_HACKstatic const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};static const char types[] = "}-+?=";#endifSTATIC 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 *simplecmd __P((union node **, union node *));STATIC void parsefname __P((void));STATIC void parseheredoc __P((void));STATIC int readtoken __P((void));STATIC int readtoken1 __P((int, char const *, char *, int));STATIC void attyline __P((void));STATIC int noexpand __P((char *));STATIC void synexpect __P((int));STATIC void synerror __P((char *));#if ATTY || READLINESTATIC void putprompt __P((char *));#else /* not ATTY */#define putprompt(s) out2str(s)#endif/* * Read and parse a command. Returns NEOF on end of file. (NULL is a * valid parse tree indicating a blank line.) */union node *parsecmd(interact) { int t; extern int exitstatus; doprompt = interact; if (doprompt) putprompt(exitstatus == 0 || vpse.flags & VUNSET ? ps1val() : pseval()); needprompt = 0; if ((t = readtoken()) == TEOF) return NEOF; if (t == TNL) return NULL; tokpushback++; return list(1);}STATIC union node *list(nlflag) { union node *n1, *n2, *n3, **pn; int first; checkkwd = 2; if (nlflag == 0 && tokendlist[peektoken()]) return NULL; n1 = andor(); for (first = 1; ; first = 0) { switch (readtoken()) { case TBACKGND: pn = &n1; if (!first && n1->type == NSEMI) pn = &n1->nbinary.ch2; if ((*pn)->type == NCMD || (*pn)->type == NPIPE) { (*pn)->ncmd.backgnd = 1; } else if ((*pn)->type == NREDIR) { (*pn)->type = NBACKGND; } else { n3 = (union node *)stalloc(sizeof (struct nredir)); n3->type = NBACKGND; n3->nredir.n = *pn; n3->nredir.redirect = NULL; *pn = n3; } goto tsemi; case TNL: tokpushback++; /* fall through */tsemi: case TSEMI: if (readtoken() == TNL) { parseheredoc(); if (nlflag) return n1; } else { tokpushback++; } checkkwd = 2; if (tokendlist[peektoken()]) return n1; n2 = andor(); n3 = (union node *)stalloc(sizeof (struct nbinary)); n3->type = NSEMI; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; n1 = n3; break; case TEOF: if (heredoclist) parseheredoc(); else pungetc(); /* push back EOF on input */ return n1; default: if (nlflag) synexpect(-1); tokpushback++; return n1; } }}STATIC union node *andor() { union node *n1, *n2, *n3; int t; n1 = pipeline(); for (;;) { if ((t = readtoken()) == TAND) { t = NAND; } else if (t == TOR) { t = NOR; } else { tokpushback++; return n1; } n2 = pipeline(); n3 = (union node *)stalloc(sizeof (struct nbinary)); n3->type = t; n3->nbinary.ch1 = n1; n3->nbinary.ch2 = n2; n1 = n3; }}STATIC union node *pipeline() { union node *n1, *pipenode; struct nodelist *lp, *prev; n1 = command(); if (readtoken() == TPIPE) { pipenode = (union node *)stalloc(sizeof (struct npipe)); pipenode->type = NPIPE; pipenode->npipe.backgnd = 0; lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); pipenode->npipe.cmdlist = lp; lp->n = n1; do { prev = lp; lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); lp->n = command(); prev->next = lp; } while (readtoken() == TPIPE); lp->next = NULL; n1 = pipenode; } tokpushback++; return n1;}STATIC union node *command() { union node *n1, *n2; union node *ap, **app; union node *cp, **cpp; union node *redir, **rpp; 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)); n1->type = NIF; n1->nif.test = list(0); if (readtoken() != TTHEN) synexpect(TTHEN); n1->nif.ifpart = list(0); n2 = n1; while (readtoken() == TELIF) { n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); n2 = n2->nif.elsepart; n2->type = NIF; n2->nif.test = list(0); if (readtoken() != TTHEN) synexpect(TTHEN); n2->nif.ifpart = list(0); } if (lasttoken == TELSE) n2->nif.elsepart = list(0); else { n2->nif.elsepart = NULL; tokpushback++; } if (readtoken() != TFI) synexpect(TFI); checkkwd = 1; break; case TWHILE: case TUNTIL: { int got; n1 = (union node *)stalloc(sizeof (struct nbinary)); n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; n1->nbinary.ch1 = list(0); if ((got=readtoken()) != TDO) {TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); synexpect(TDO); } n1->nbinary.ch2 = list(0); if (readtoken() != TDONE) synexpect(TDONE); checkkwd = 1; break; } case TFOR: if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) synerror("Bad for loop variable"); n1 = (union node *)stalloc(sizeof (struct nfor)); n1->type = NFOR; n1->nfor.var = wordtext; if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { app = ≈ while (readtoken() == TWORD) { n2 = (union node *)stalloc(sizeof (struct narg)); n2->type = NARG; n2->narg.text = wordtext; n2->narg.backquote = backquotelist; *app = n2; app = &n2->narg.next; } *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, '@', '=', '\0'};#endif n2 = (union node *)stalloc(sizeof (struct narg)); n2->type = NARG; n2->narg.text = (char *)argvars; 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; else if (t == TBEGIN) t = TEND; else synexpect(-1); n1->nfor.body = list(0); if (readtoken() != t) synexpect(t); checkkwd = 1; break; case TCASE: n1 = (union node *)stalloc(sizeof (struct ncase)); n1->type = NCASE; if (readtoken() != TWORD) synexpect(TWORD); n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); n2->type = NARG; n2->narg.text = wordtext; n2->narg.backquote = backquotelist; n2->narg.next = NULL; while (readtoken() == TNL); if (lasttoken != TWORD || ! equal(wordtext, "in")) synerror("expecting \"in\""); cpp = &n1->ncase.cases; while (checkkwd = 2, readtoken() == TWORD) { *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); cp->type = NCLIST; app = &cp->nclist.pattern; for (;;) { *app = ap = (union node *)stalloc(sizeof (struct narg)); ap->type = NARG; ap->narg.text = wordtext; ap->narg.backquote = backquotelist; if (readtoken() != TPIPE) break; app = &ap->narg.next; if (readtoken() != TWORD) synexpect(TWORD); } ap->narg.next = NULL; if (lasttoken != TRP) synexpect(TRP); cp->nclist.body = list(0); if ((t = readtoken()) == TESAC) tokpushback++; else if (t != TENDCASE) synexpect(TENDCASE); cpp = &cp->nclist.next; } *cpp = NULL; if (lasttoken != TESAC) synexpect(TESAC); checkkwd = 1; break; case TLP: n1 = (union node *)stalloc(sizeof (struct nredir)); n1->type = NSUBSHELL; n1->nredir.n = list(0); n1->nredir.redirect = NULL; if (readtoken() != TRP) synexpect(TRP); checkkwd = 1; break; case TBEGIN: n1 = list(0); if (readtoken() != TEND) synexpect(TEND); checkkwd = 1; break; /* Handle an empty command like other simple commands. */ case TNL: case TSEMI: case TEND: case TRP: case TEOF: 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; parsefname(); } tokpushback++; *rpp = NULL; if (redir) { if (n1->type != NSUBSHELL) { n2 = (union node *)stalloc(sizeof (struct nredir)); n2->type = NREDIR; n2->nredir.n = n1; n1 = n2; } n1->nredir.redirect = redir; } return n1;}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)); n->type = NARG; n->narg.text = wordtext; n->narg.backquote = backquotelist; *app = n; app = &n->narg.next; } else if (lasttoken == TREDIR) { *rpp = n = redirnode; 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);#ifdef notdef if (! goodname(n->narg.text)) synerror("Bad function name");#endif n->type = NDEFUN; n->narg.next = command(); return n; } else { tokpushback++; break; } } *app = NULL; *rpp = NULL; n = (union node *)stalloc(sizeof (struct ncmd)); n->type = NCMD; n->ncmd.backgnd = 0; n->ncmd.args = args; n->ncmd.redirect = redir; return n;}STATIC voidparsefname() { union node *n = redirnode; if (readtoken() != TWORD) synexpect(-1); if (n->type == NHERE) { struct heredoc *here = heredoc; struct heredoc *p; int i; if (quoteflag == 0) n->type = NXHERE; TRACE(("Here document %d\n", n->type)); if (here->striptabs) { while (*wordtext == '\t') wordtext++; } if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) synerror("Illegal eof marker for << redirection"); rmescapes(wordtext); here->eofmark = wordtext; here->next = NULL; if (heredoclist == NULL) heredoclist = here; else { for (p = heredoclist ; p->next ; p = p->next); p->next = here; } } else if (n->type == NTOFD || n->type == NFROMFD) { if (is_digit(wordtext[0])) n->ndup.dupfd = digit_val(wordtext[0]); else if (wordtext[0] == '-') n->ndup.dupfd = -1; else goto bad; if (wordtext[1] != '\0') {bad: synerror("Bad fd number"); } } else { n->nfile.fname = (union node *)stalloc(sizeof (struct narg)); n = n->nfile.fname; n->type = NARG; n->narg.next = NULL; n->narg.text = wordtext; n->narg.backquote = backquotelist; }}/* * Input any here documents. */STATIC voidparseheredoc() { struct heredoc *here; union node *n; while (heredoclist) { here = heredoclist; heredoclist = here->next; if (needprompt) { putprompt(ps2val()); needprompt = 0; } readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, here->eofmark, here->striptabs); n = (union node *)stalloc(sizeof (struct narg)); n->narg.type = NARG; n->narg.next = NULL; n->narg.text = wordtext; n->narg.backquote = backquotelist; here->here->nhere.doc = n; }}STATIC intpeektoken() { int t; t = readtoken(); tokpushback++; return (t);}STATIC int xxreadtoken();STATIC intreadtoken() { int t;#if DEBUG int alreadyseen = tokpushback;#endif t = xxreadtoken(); if (checkkwd) { /* * eat newlines */ if (checkkwd == 2) { checkkwd = 0; while (t == TNL) { parseheredoc(); t = xxreadtoken(); } } else checkkwd = 0; /* * check for keywords */ if (t == TWORD && !quoteflag) { register char *const *pp; for (pp = parsekwd; *pp; pp++) { if (**pp == *wordtext && equal(*pp, wordtext)) { lasttoken = t = pp - parsekwd + KWDOFFSET; TRACE(("keyword %s recognized\n", tokname[t])); break; } } } }#if DEBUG if (!alreadyseen) TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); else TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));#endif return (t);}/* * Read the next input token. * If the token is a word, we set backquotelist to the list of cmds in * backquotes. We set quoteflag to true if any part of the word was
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -