📄 parser.c
字号:
* quoted. * If the token is TREDIR, then we set redirnode to a structure containing * the redirection. * In all cases, the variable startlinno is set to the number of the line * on which the token starts. * * [Change comment: here documents and internal procedures] * [Readtoken shouldn't have any arguments. Perhaps we should make the * word parsing code into a separate routine. In this case, readtoken * doesn't need to have any internal procedures, but parseword does. * We could also make parseoperator in essence the main routine, and * have parseword (readtoken1?) handle both words and redirection.] */#define RETURN(token) return lasttoken = tokenSTATIC intxxreadtoken() { register c; if (tokpushback) { tokpushback = 0; return lasttoken; } if (needprompt) { putprompt(ps2val()); needprompt = 0; } startlinno = plinno; for (;;) { /* until token or start of word found */ c = pgetc_macro(); if (c == ' ' || c == '\t') continue; /* quick check for white space first */ switch (c) { case ' ': case '\t': continue; case '#': while ((c = pgetc()) != '\n' && c != PEOF); pungetc(); continue; case '\\': if (pgetc() == '\n') { startlinno = ++plinno; if (doprompt) putprompt(ps2val()); continue; } pungetc(); goto breakloop; case '\n': plinno++; needprompt = doprompt; RETURN(TNL); case PEOF: RETURN(TEOF); case '&': if (pgetc() == '&') RETURN(TAND); pungetc(); RETURN(TBACKGND); case '|': if (pgetc() == '|') RETURN(TOR); pungetc(); RETURN(TPIPE); case ';': if (pgetc() == ';') RETURN(TENDCASE); pungetc(); RETURN(TSEMI); case '(': RETURN(TLP); case ')': RETURN(TRP); default: goto breakloop; } }breakloop: return readtoken1(c, BASESYNTAX, (char *)NULL, 0);#undef RETURN}/* * If eofmark is NULL, read a word or a redirection symbol. If eofmark * is not NULL, read a here document. In the latter case, eofmark is the * word which marks the end of the document and striptabs is true if * leading tabs should be stripped from the document. The argument firstc * is the first character of the input token or document. * * Because C does not have internal subroutines, I have simulated them * using goto's to implement the subroutine linkage. The following macros * will run code that appears at the end of readtoken1. */#define CHECKEND() {goto checkend; checkend_return:;}#define PARSEREDIR() {goto parseredir; parseredir_return:;}#define PARSESUB() {goto parsesub; parsesub_return:;}#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}STATIC intreadtoken1(firstc, syntax, eofmark, striptabs) int firstc; char const *syntax; char *eofmark; int striptabs; { register c = firstc; register char *out; int len; char line[EOFMARKLEN + 1]; struct nodelist *bqlist; int quotef; int dblquote; int varnest; int oldstyle; startlinno = plinno; dblquote = 0; if (syntax == DQSYNTAX) dblquote = 1; quotef = 0; bqlist = NULL; varnest = 0; STARTSTACKSTR(out); loop: { /* for each line, until end of word */#if ATTY if (c == '\034' && doprompt && attyset() && ! equal(termval(), "emacs")) { attyline(); if (syntax == BASESYNTAX) return readtoken(); c = pgetc(); goto loop; }#endif 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 */ switch(syntax[c]) { case CNL: /* '\n' */ if (syntax == BASESYNTAX) goto endword; /* exit outer loop */ USTPUTC(c, out); plinno++; if (doprompt) { putprompt(ps2val()); } c = pgetc(); goto loop; /* continue outer loop */ case CWORD: USTPUTC(c, out); break; case CCTL: if (eofmark == NULL || dblquote) USTPUTC(CTLESC, out); USTPUTC(c, out); break; case CBACK: /* backslash */ c = pgetc(); if (c == PEOF) { USTPUTC('\\', out); pungetc(); } else if (c == '\n') { if (doprompt) putprompt(ps2val()); } else { if (dblquote && c != '\\' && c != '`' && c != '$' && (c != '"' || eofmark != NULL)) USTPUTC('\\', out); if (SQSYNTAX[c] == CCTL) USTPUTC(CTLESC, out); USTPUTC(c, out); quotef++; } break; case CSQUOTE: syntax = SQSYNTAX; break; case CDQUOTE: syntax = DQSYNTAX; dblquote = 1; break; case CENDQUOTE: if (eofmark) { USTPUTC(c, out); } else { syntax = BASESYNTAX; quotef++; dblquote = 0; } break; case CVAR: /* '$' */ PARSESUB(); /* parse substitution */ break; case CENDVAR: /* '}' */ if (varnest > 0) { varnest--; USTPUTC(CTLENDVAR, out); } else { USTPUTC(c, out); } break; case CBQUOTE: /* '`' */ PARSEBACKQOLD(); break; case CEOF: goto endword; /* exit outer loop */ default: if (varnest == 0) goto endword; /* exit outer loop */ USTPUTC(c, out); } c = pgetc_macro(); } }endword: if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) synerror("Unterminated quoted string"); if (varnest != 0) { startlinno = plinno; synerror("Missing '}'"); } USTPUTC('\0', out); len = out - stackblock(); out = stackblock(); if (eofmark == NULL) { if ((c == '>' || c == '<') && quotef == 0 && len <= 2 && (*out == '\0' || is_digit(*out))) { PARSEREDIR(); return lasttoken = TREDIR; } else { pungetc(); } } quoteflag = quotef; backquotelist = bqlist; grabstackblock(len); wordtext = out; return lasttoken = TWORD;/* end of readtoken routine *//* * Check to see whether we are at the end of the here document. When this * is called, c is set to the first character of the next input line. If * we are at the end of the here document, this routine sets the c to PEOF. */checkend: { if (eofmark) { if (striptabs) { while (c == '\t') c = pgetc(); } if (c == *eofmark) { if (pfgets(line, sizeof line) != NULL) { register char *p, *q; p = line; for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); if (*p == '\n' && *q == '\0') { c = PEOF; plinno++; needprompt = doprompt; } else { ppushback(line, strlen(line)); } } } } goto checkend_return;}/* * Parse a redirection operator. The variable "out" points to a string * specifying the fd to be redirected. The variable "c" contains the * first character of the redirection operator. */parseredir: { char fd = *out; union node *np; np = (union node *)stalloc(sizeof (struct nfile)); if (c == '>') { np->nfile.fd = 1; c = pgetc(); if (c == '>') np->type = NAPPEND; else if (c == '&') np->type = NTOFD; else { np->type = NTO; pungetc(); } } else { /* c == '<' */ np->nfile.fd = 0; c = pgetc(); if (c == '<') { if (sizeof (struct nfile) != sizeof (struct nhere)) { np = (union node *)stalloc(sizeof (struct nhere)); np->nfile.fd = 0; } np->type = NHERE; heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); heredoc->here = np; if ((c = pgetc()) == '-') { heredoc->striptabs = 1; } else { heredoc->striptabs = 0; pungetc(); } } else if (c == '&') np->type = NFROMFD; else { np->type = NFROM; pungetc(); } } if (fd != '\0') np->nfile.fd = digit_val(fd); redirnode = np; goto parseredir_return;}/* * Parse a substitution. At this point, we have read the dollar sign * and nothing else. */parsesub: { int subtype; int typeloc; int flags; char *p;#ifndef GDB_HACK static const char types[] = "}-+?=";#endif c = pgetc(); if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { USTPUTC('$', out); pungetc(); } else if (c == '(') { /* $(command) */ PARSEBACKQNEW(); } else { USTPUTC(CTLVAR, out); typeloc = out - stackblock(); USTPUTC(VSNORMAL, out); subtype = VSNORMAL; if (c == '{') { c = pgetc(); subtype = 0; } if (is_name(c)) { do { STPUTC(c, out); c = pgetc(); } while (is_in_name(c)); } else { if (! is_special(c))badsub: synerror("Bad substitution"); USTPUTC(c, out); c = pgetc(); } STPUTC('=', out); flags = 0; if (subtype == 0) { if (c == ':') { flags = VSNUL; c = pgetc(); } p = strchr(types, c); if (p == NULL) goto badsub; subtype = p - types + VSNORMAL; } else { pungetc(); } if (dblquote) flags |= VSQUOTE; *(stackblock() + typeloc) = subtype | flags; if (subtype != VSNORMAL) varnest++; } goto parsesub_return;}/* * Called to parse command substitutions. Newstyle is set if the command * is enclosed inside $(...); nlpp is a pointer to the head of the linked * list of commands (passed by reference), and savelen is the number of * characters on the top of the stack which must be preserved. */parsebackq: { struct nodelist **nlpp; int savepbq; union node *n; char *volatile str; struct jmploc jmploc; struct jmploc *volatile savehandler; int savelen; int savedoprompt; savepbq = parsebackquote; if (setjmp(jmploc.loc)) { if (str) ckfree(str); parsebackquote = 0; handler = savehandler; longjmp(handler->loc, 1); } INTOFF; str = NULL; savelen = out - stackblock(); if (savelen > 0) { str = ckmalloc(savelen); bcopy(stackblock(), str, savelen); } 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); } if (c == '\n') { plinno++; if (doprompt) putprompt(ps2val()); } STPUTC(c, out); } STPUTC('\0', out); savelen = out - stackblock(); if (savelen > 0) { str = ckmalloc(savelen); bcopy(stackblock(), str, savelen); } setinputstring(str, 1); savedoprompt = doprompt; doprompt = 0; /* no prompts while rereading string */ } nlpp = &bqlist; while (*nlpp) nlpp = &(*nlpp)->next; *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); (*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(); doprompt = savedoprompt; } while (stackblocksize() <= savelen) growstackblock(); STARTSTACKSTR(out); if (str) { bcopy(str, out, savelen); STADJUST(savelen, out); INTOFF; ckfree(str); str = NULL; INTON; } parsebackquote = savepbq; handler = savehandler; USTPUTC(CTLBACKQ + dblquote, out); if (oldstyle) goto parsebackq_oldreturn; else goto parsebackq_newreturn;}} /* end of readtoken */#ifdef mkinitRESET { tokpushback = 0;}#endif#if READLINE/* * Remember a prompt for use with readline if input and output is a terminal. */STATIC voidputprompt(s) char *s; { if (editable) { r_use_prompt = s; } else { out2str(s); }}#endif#if ATTY/* * Called to process a command generated by atty. We execute the line, * and catch any errors that occur so they don't propagate outside of * this routine. */STATIC voidattyline() { char line[256]; struct stackmark smark; struct jmploc jmploc; struct jmploc *volatile savehandler; if (pfgets(line, sizeof line) == NULL) return; /* "can't happen" */ if (setjmp(jmploc.loc)) { if (exception == EXERROR) out2str("\033]D\n"); handler = savehandler; longjmp(handler->loc, 1); } savehandler = handler; handler = &jmploc; setstackmark(&smark); evalstring(line); popstackmark(&smark); handler = savehandler; doprompt = 1;}/* * Output a prompt for atty. We output the prompt as part of the * appropriate escape sequence. */STATIC voidputprompt(s) char *s; { register char *p; if (attyset() && ! equal(termval(), "emacs")) { if (strchr(s, '\7')) out2c('\7'); out2str("\033]P1;"); for (p = s ; *p ; p++) { if ((unsigned)(*p - ' ') <= '~' - ' ') out2c(*p); } out2c('\n'); } else { out2str(s); }}#endif/* * Returns true if the text contains nothing to expand (no dollar signs * or backquotes). */STATIC intnoexpand(text) char *text; { register char *p; register char c; p = text; while ((c = *p++) != '\0') { if (c == CTLESC) p++; else if (BASESYNTAX[c] == CCTL) return 0; } return 1;}/* * Return true if the argument is a legal variable name (a letter or * underscore followed by zero or more letters, underscores, and digits). */intgoodname(name) char *name; { register char *p; p = name; if (! is_name(*p)) return 0; while (*++p) { if (! is_in_name(*p)) return 0; } return 1;}/* * Called when an unexpected token is read during the parse. The argument * is the token that is expected, or -1 if more than one type of token can * occur at this point. */STATIC voidsynexpect(token) { char msg[64]; if (token >= 0) { fmtstr(msg, 64, "%s unexpected (expecting %s)", tokname[lasttoken], tokname[token]); } else { fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); } synerror(msg);}STATIC voidsynerror(msg) char *msg; { if (commandname) outfmt(&errout, "%s: %d: ", commandname, startlinno); outfmt(&errout, "Syntax error: %s\n", msg); error((char *)NULL);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -