parser.c revision 25905
11556Srgrimes/*- 21556Srgrimes * Copyright (c) 1991, 1993 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Kenneth Almquist. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 353044Sdg * 3625905Ssteve * $Id: parser.c,v 1.20 1997/04/28 03:22:09 steve Exp $ 371556Srgrimes */ 381556Srgrimes 391556Srgrimes#ifndef lint 4020425Sstevestatic char const sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; 411556Srgrimes#endif /* not lint */ 421556Srgrimes 4317987Speter#include <stdlib.h> 4417987Speter 451556Srgrimes#include "shell.h" 461556Srgrimes#include "parser.h" 471556Srgrimes#include "nodes.h" 481556Srgrimes#include "expand.h" /* defines rmescapes() */ 491556Srgrimes#include "redir.h" /* defines copyfd() */ 501556Srgrimes#include "syntax.h" 511556Srgrimes#include "options.h" 521556Srgrimes#include "input.h" 531556Srgrimes#include "output.h" 541556Srgrimes#include "var.h" 551556Srgrimes#include "error.h" 561556Srgrimes#include "memalloc.h" 571556Srgrimes#include "mystring.h" 581556Srgrimes#include "alias.h" 5917987Speter#include "show.h" 6017987Speter#ifndef NO_HISTORY 611556Srgrimes#include "myhistedit.h" 6217987Speter#endif 631556Srgrimes 641556Srgrimes/* 651556Srgrimes * Shell command parser. 661556Srgrimes */ 671556Srgrimes 681556Srgrimes#define EOFMARKLEN 79 691556Srgrimes 701556Srgrimes/* values returned by readtoken */ 7117987Speter#include "token.h" 721556Srgrimes 731556Srgrimes 741556Srgrimes 751556Srgrimesstruct heredoc { 761556Srgrimes struct heredoc *next; /* next here document in list */ 771556Srgrimes union node *here; /* redirection node */ 781556Srgrimes char *eofmark; /* string indicating end of input */ 791556Srgrimes int striptabs; /* if set, strip leading tabs */ 801556Srgrimes}; 811556Srgrimes 821556Srgrimes 831556Srgrimes 841556Srgrimesstruct heredoc *heredoclist; /* list of here documents to read */ 851556Srgrimesint parsebackquote; /* nonzero if we are inside backquotes */ 861556Srgrimesint doprompt; /* if set, prompt the user */ 871556Srgrimesint needprompt; /* true if interactive and at start of line */ 881556Srgrimesint lasttoken; /* last token read */ 891556SrgrimesMKINIT int tokpushback; /* last token pushed back */ 901556Srgrimeschar *wordtext; /* text of last word returned by readtoken */ 911556SrgrimesMKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ 921556Srgrimesstruct nodelist *backquotelist; 931556Srgrimesunion node *redirnode; 941556Srgrimesstruct heredoc *heredoc; 951556Srgrimesint quoteflag; /* set if (part of) last token was quoted */ 961556Srgrimesint startlinno; /* line # where last token started */ 971556Srgrimes 9818018Speter/* XXX When 'noaliases' is set to one, no alias expansion takes place. */ 9918018Speterstatic int noaliases = 0; 1001556Srgrimes 1011556Srgrimes#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */ 1021556Srgrimes#ifdef GDB_HACK 1031556Srgrimesstatic const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'}; 1041556Srgrimesstatic const char types[] = "}-+?="; 1051556Srgrimes#endif 1061556Srgrimes 1071556Srgrimes 1081556SrgrimesSTATIC union node *list __P((int)); 1091556SrgrimesSTATIC union node *andor __P((void)); 1101556SrgrimesSTATIC union node *pipeline __P((void)); 1111556SrgrimesSTATIC union node *command __P((void)); 1121556SrgrimesSTATIC union node *simplecmd __P((union node **, union node *)); 11317987SpeterSTATIC union node *makename __P((void)); 1141556SrgrimesSTATIC void parsefname __P((void)); 1151556SrgrimesSTATIC void parseheredoc __P((void)); 11617987SpeterSTATIC int peektoken __P((void)); 1171556SrgrimesSTATIC int readtoken __P((void)); 11817987SpeterSTATIC int xxreadtoken __P((void)); 1191556SrgrimesSTATIC int readtoken1 __P((int, char const *, char *, int)); 1201556SrgrimesSTATIC int noexpand __P((char *)); 1211556SrgrimesSTATIC void synexpect __P((int)); 1221556SrgrimesSTATIC void synerror __P((char *)); 12320425SsteveSTATIC void setprompt __P((int)); 1241556Srgrimes 12517987Speter 1261556Srgrimes/* 1271556Srgrimes * Read and parse a command. Returns NEOF on end of file. (NULL is a 1281556Srgrimes * valid parse tree indicating a blank line.) 1291556Srgrimes */ 1301556Srgrimes 1311556Srgrimesunion node * 13220425Ssteveparsecmd(interact) 13317987Speter int interact; 13417987Speter{ 1351556Srgrimes int t; 1361556Srgrimes 1371556Srgrimes doprompt = interact; 1381556Srgrimes if (doprompt) 1391556Srgrimes setprompt(1); 1401556Srgrimes else 1411556Srgrimes setprompt(0); 1421556Srgrimes needprompt = 0; 1431556Srgrimes t = readtoken(); 1441556Srgrimes if (t == TEOF) 1451556Srgrimes return NEOF; 1461556Srgrimes if (t == TNL) 1471556Srgrimes return NULL; 1481556Srgrimes tokpushback++; 1491556Srgrimes return list(1); 1501556Srgrimes} 1511556Srgrimes 1521556Srgrimes 1531556SrgrimesSTATIC union node * 15420425Sstevelist(nlflag) 15517987Speter int nlflag; 15617987Speter{ 1571556Srgrimes union node *n1, *n2, *n3; 15817987Speter int tok; 1591556Srgrimes 1601556Srgrimes checkkwd = 2; 1611556Srgrimes if (nlflag == 0 && tokendlist[peektoken()]) 1621556Srgrimes return NULL; 16317987Speter n1 = NULL; 1641556Srgrimes for (;;) { 16517987Speter n2 = andor(); 16617987Speter tok = readtoken(); 16717987Speter if (tok == TBACKGND) { 16817987Speter if (n2->type == NCMD || n2->type == NPIPE) { 16917987Speter n2->ncmd.backgnd = 1; 17017987Speter } else if (n2->type == NREDIR) { 17117987Speter n2->type = NBACKGND; 17217987Speter } else { 17317987Speter n3 = (union node *)stalloc(sizeof (struct nredir)); 17417987Speter n3->type = NBACKGND; 17517987Speter n3->nredir.n = n2; 17617987Speter n3->nredir.redirect = NULL; 17717987Speter n2 = n3; 17817987Speter } 17917987Speter } 18017987Speter if (n1 == NULL) { 18117987Speter n1 = n2; 18217987Speter } 18317987Speter else { 18417987Speter n3 = (union node *)stalloc(sizeof (struct nbinary)); 18517987Speter n3->type = NSEMI; 18617987Speter n3->nbinary.ch1 = n1; 18717987Speter n3->nbinary.ch2 = n2; 18817987Speter n1 = n3; 18917987Speter } 19017987Speter switch (tok) { 19113882Sjoerg case TBACKGND: 19217987Speter case TSEMI: 19317987Speter tok = readtoken(); 19417987Speter /* fall through */ 1951556Srgrimes case TNL: 19617987Speter if (tok == TNL) { 19717987Speter parseheredoc(); 19817987Speter if (nlflag) 19917987Speter return n1; 20017987Speter } else { 20117987Speter tokpushback++; 20217987Speter } 2031556Srgrimes checkkwd = 2; 2041556Srgrimes if (tokendlist[peektoken()]) 2051556Srgrimes return n1; 2061556Srgrimes break; 2071556Srgrimes case TEOF: 2081556Srgrimes if (heredoclist) 2091556Srgrimes parseheredoc(); 2101556Srgrimes else 2111556Srgrimes pungetc(); /* push back EOF on input */ 2121556Srgrimes return n1; 2131556Srgrimes default: 2141556Srgrimes if (nlflag) 2151556Srgrimes synexpect(-1); 2161556Srgrimes tokpushback++; 2171556Srgrimes return n1; 2181556Srgrimes } 2191556Srgrimes } 2201556Srgrimes} 2211556Srgrimes 2221556Srgrimes 2231556Srgrimes 2241556SrgrimesSTATIC union node * 2251556Srgrimesandor() { 2261556Srgrimes union node *n1, *n2, *n3; 2271556Srgrimes int t; 2281556Srgrimes 2291556Srgrimes n1 = pipeline(); 2301556Srgrimes for (;;) { 2311556Srgrimes if ((t = readtoken()) == TAND) { 2321556Srgrimes t = NAND; 2331556Srgrimes } else if (t == TOR) { 2341556Srgrimes t = NOR; 2351556Srgrimes } else { 2361556Srgrimes tokpushback++; 2371556Srgrimes return n1; 2381556Srgrimes } 2391556Srgrimes n2 = pipeline(); 2401556Srgrimes n3 = (union node *)stalloc(sizeof (struct nbinary)); 2411556Srgrimes n3->type = t; 2421556Srgrimes n3->nbinary.ch1 = n1; 2431556Srgrimes n3->nbinary.ch2 = n2; 2441556Srgrimes n1 = n3; 2451556Srgrimes } 2461556Srgrimes} 2471556Srgrimes 2481556Srgrimes 2491556Srgrimes 2501556SrgrimesSTATIC union node * 2511556Srgrimespipeline() { 25225230Ssteve union node *n1, *pipenode, *notnode; 2531556Srgrimes struct nodelist *lp, *prev; 25425230Ssteve int negate = 0; 2551556Srgrimes 2561556Srgrimes TRACE(("pipeline: entered\n")); 25725230Ssteve while (readtoken() == TNOT) { 25825230Ssteve TRACE(("pipeline: TNOT recognized\n")); 25925230Ssteve negate = !negate; 26025230Ssteve } 26125230Ssteve tokpushback++; 2621556Srgrimes n1 = command(); 2631556Srgrimes if (readtoken() == TPIPE) { 2641556Srgrimes pipenode = (union node *)stalloc(sizeof (struct npipe)); 2651556Srgrimes pipenode->type = NPIPE; 2661556Srgrimes pipenode->npipe.backgnd = 0; 2671556Srgrimes lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); 2681556Srgrimes pipenode->npipe.cmdlist = lp; 2691556Srgrimes lp->n = n1; 2701556Srgrimes do { 2711556Srgrimes prev = lp; 2721556Srgrimes lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); 2731556Srgrimes lp->n = command(); 2741556Srgrimes prev->next = lp; 2751556Srgrimes } while (readtoken() == TPIPE); 2761556Srgrimes lp->next = NULL; 2771556Srgrimes n1 = pipenode; 2781556Srgrimes } 2791556Srgrimes tokpushback++; 28025230Ssteve if (negate) { 28125230Ssteve notnode = (union node *)stalloc(sizeof(struct nnot)); 28225230Ssteve notnode->type = NNOT; 28325230Ssteve notnode->nnot.com = n1; 28425230Ssteve n1 = notnode; 28525230Ssteve } 2861556Srgrimes return n1; 2871556Srgrimes} 2881556Srgrimes 2891556Srgrimes 2901556Srgrimes 2911556SrgrimesSTATIC union node * 2921556Srgrimescommand() { 2931556Srgrimes union node *n1, *n2; 2941556Srgrimes union node *ap, **app; 2951556Srgrimes union node *cp, **cpp; 2961556Srgrimes union node *redir, **rpp; 29725230Ssteve int t; 2981556Srgrimes 2991556Srgrimes checkkwd = 2; 30017987Speter redir = NULL; 30117987Speter n1 = NULL; 3021556Srgrimes rpp = &redir; 30320425Ssteve 3041556Srgrimes /* Check for redirection which may precede command */ 3051556Srgrimes while (readtoken() == TREDIR) { 3061556Srgrimes *rpp = n2 = redirnode; 3071556Srgrimes rpp = &n2->nfile.next; 3081556Srgrimes parsefname(); 3091556Srgrimes } 3101556Srgrimes tokpushback++; 3111556Srgrimes 3121556Srgrimes switch (readtoken()) { 3131556Srgrimes case TIF: 3141556Srgrimes n1 = (union node *)stalloc(sizeof (struct nif)); 3151556Srgrimes n1->type = NIF; 3161556Srgrimes n1->nif.test = list(0); 3171556Srgrimes if (readtoken() != TTHEN) 3181556Srgrimes synexpect(TTHEN); 3191556Srgrimes n1->nif.ifpart = list(0); 3201556Srgrimes n2 = n1; 3211556Srgrimes while (readtoken() == TELIF) { 3221556Srgrimes n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); 3231556Srgrimes n2 = n2->nif.elsepart; 3241556Srgrimes n2->type = NIF; 3251556Srgrimes n2->nif.test = list(0); 3261556Srgrimes if (readtoken() != TTHEN) 3271556Srgrimes synexpect(TTHEN); 3281556Srgrimes n2->nif.ifpart = list(0); 3291556Srgrimes } 3301556Srgrimes if (lasttoken == TELSE) 3311556Srgrimes n2->nif.elsepart = list(0); 3321556Srgrimes else { 3331556Srgrimes n2->nif.elsepart = NULL; 3341556Srgrimes tokpushback++; 3351556Srgrimes } 3361556Srgrimes if (readtoken() != TFI) 3371556Srgrimes synexpect(TFI); 3381556Srgrimes checkkwd = 1; 3391556Srgrimes break; 3401556Srgrimes case TWHILE: 3411556Srgrimes case TUNTIL: { 3421556Srgrimes int got; 3431556Srgrimes n1 = (union node *)stalloc(sizeof (struct nbinary)); 3441556Srgrimes n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; 3451556Srgrimes n1->nbinary.ch1 = list(0); 3461556Srgrimes if ((got=readtoken()) != TDO) { 3471556SrgrimesTRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); 3481556Srgrimes synexpect(TDO); 3491556Srgrimes } 3501556Srgrimes n1->nbinary.ch2 = list(0); 3511556Srgrimes if (readtoken() != TDONE) 3521556Srgrimes synexpect(TDONE); 3531556Srgrimes checkkwd = 1; 3541556Srgrimes break; 3551556Srgrimes } 3561556Srgrimes case TFOR: 3571556Srgrimes if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) 3581556Srgrimes synerror("Bad for loop variable"); 3591556Srgrimes n1 = (union node *)stalloc(sizeof (struct nfor)); 3601556Srgrimes n1->type = NFOR; 3611556Srgrimes n1->nfor.var = wordtext; 3621556Srgrimes if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { 3631556Srgrimes app = ≈ 3641556Srgrimes while (readtoken() == TWORD) { 3651556Srgrimes n2 = (union node *)stalloc(sizeof (struct narg)); 3661556Srgrimes n2->type = NARG; 3671556Srgrimes n2->narg.text = wordtext; 3681556Srgrimes n2->narg.backquote = backquotelist; 3691556Srgrimes *app = n2; 3701556Srgrimes app = &n2->narg.next; 3711556Srgrimes } 3721556Srgrimes *app = NULL; 3731556Srgrimes n1->nfor.args = ap; 3741556Srgrimes if (lasttoken != TNL && lasttoken != TSEMI) 3751556Srgrimes synexpect(-1); 3761556Srgrimes } else { 3771556Srgrimes#ifndef GDB_HACK 3781556Srgrimes static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, 3791556Srgrimes '@', '=', '\0'}; 3801556Srgrimes#endif 3811556Srgrimes n2 = (union node *)stalloc(sizeof (struct narg)); 3821556Srgrimes n2->type = NARG; 3831556Srgrimes n2->narg.text = (char *)argvars; 3841556Srgrimes n2->narg.backquote = NULL; 3851556Srgrimes n2->narg.next = NULL; 3861556Srgrimes n1->nfor.args = n2; 3871556Srgrimes /* 3881556Srgrimes * Newline or semicolon here is optional (but note 3891556Srgrimes * that the original Bourne shell only allowed NL). 3901556Srgrimes */ 3911556Srgrimes if (lasttoken != TNL && lasttoken != TSEMI) 3921556Srgrimes tokpushback++; 3931556Srgrimes } 3941556Srgrimes checkkwd = 2; 3951556Srgrimes if ((t = readtoken()) == TDO) 3961556Srgrimes t = TDONE; 3971556Srgrimes else if (t == TBEGIN) 3981556Srgrimes t = TEND; 3991556Srgrimes else 4001556Srgrimes synexpect(-1); 4011556Srgrimes n1->nfor.body = list(0); 4021556Srgrimes if (readtoken() != t) 4031556Srgrimes synexpect(t); 4041556Srgrimes checkkwd = 1; 4051556Srgrimes break; 4061556Srgrimes case TCASE: 4071556Srgrimes n1 = (union node *)stalloc(sizeof (struct ncase)); 4081556Srgrimes n1->type = NCASE; 4091556Srgrimes if (readtoken() != TWORD) 4101556Srgrimes synexpect(TWORD); 4111556Srgrimes n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); 4121556Srgrimes n2->type = NARG; 4131556Srgrimes n2->narg.text = wordtext; 4141556Srgrimes n2->narg.backquote = backquotelist; 4151556Srgrimes n2->narg.next = NULL; 4161556Srgrimes while (readtoken() == TNL); 4171556Srgrimes if (lasttoken != TWORD || ! equal(wordtext, "in")) 4181556Srgrimes synerror("expecting \"in\""); 4191556Srgrimes cpp = &n1->ncase.cases; 42018018Speter noaliases = 1; /* turn off alias expansion */ 4212760Ssef checkkwd = 2, readtoken(); 4222760Ssef do { 4231556Srgrimes *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); 4241556Srgrimes cp->type = NCLIST; 4251556Srgrimes app = &cp->nclist.pattern; 4261556Srgrimes for (;;) { 4271556Srgrimes *app = ap = (union node *)stalloc(sizeof (struct narg)); 4281556Srgrimes ap->type = NARG; 4291556Srgrimes ap->narg.text = wordtext; 4301556Srgrimes ap->narg.backquote = backquotelist; 4312760Ssef if (checkkwd = 2, readtoken() != TPIPE) 4321556Srgrimes break; 4331556Srgrimes app = &ap->narg.next; 4342760Ssef readtoken(); 4351556Srgrimes } 4361556Srgrimes ap->narg.next = NULL; 4371556Srgrimes if (lasttoken != TRP) 43818018Speter noaliases = 0, synexpect(TRP); 4391556Srgrimes cp->nclist.body = list(0); 4402760Ssef 4412760Ssef checkkwd = 2; 4422760Ssef if ((t = readtoken()) != TESAC) { 4432760Ssef if (t != TENDCASE) 44418018Speter noaliases = 0, synexpect(TENDCASE); 4452760Ssef else 4462760Ssef checkkwd = 2, readtoken(); 4472760Ssef } 4481556Srgrimes cpp = &cp->nclist.next; 4492760Ssef } while(lasttoken != TESAC); 45018018Speter noaliases = 0; /* reset alias expansion */ 4511556Srgrimes *cpp = NULL; 4521556Srgrimes checkkwd = 1; 4531556Srgrimes break; 4541556Srgrimes case TLP: 4551556Srgrimes n1 = (union node *)stalloc(sizeof (struct nredir)); 4561556Srgrimes n1->type = NSUBSHELL; 4571556Srgrimes n1->nredir.n = list(0); 4581556Srgrimes n1->nredir.redirect = NULL; 4591556Srgrimes if (readtoken() != TRP) 4601556Srgrimes synexpect(TRP); 4611556Srgrimes checkkwd = 1; 4621556Srgrimes break; 4631556Srgrimes case TBEGIN: 4641556Srgrimes n1 = list(0); 4651556Srgrimes if (readtoken() != TEND) 4661556Srgrimes synexpect(TEND); 4671556Srgrimes checkkwd = 1; 4681556Srgrimes break; 4691556Srgrimes /* Handle an empty command like other simple commands. */ 47017987Speter case TSEMI: 47117987Speter /* 47217987Speter * An empty command before a ; doesn't make much sense, and 47317987Speter * should certainly be disallowed in the case of `if ;'. 47417987Speter */ 47517987Speter if (!redir) 47617987Speter synexpect(-1); 47720425Ssteve case TAND: 47820425Ssteve case TOR: 4791556Srgrimes case TNL: 48010399Sjoerg case TEOF: 4811556Srgrimes case TWORD: 48217987Speter case TRP: 4831556Srgrimes tokpushback++; 48425230Ssteve return simplecmd(rpp, redir); 4851556Srgrimes default: 4861556Srgrimes synexpect(-1); 4871556Srgrimes } 4881556Srgrimes 4891556Srgrimes /* Now check for redirection which may follow command */ 4901556Srgrimes while (readtoken() == TREDIR) { 4911556Srgrimes *rpp = n2 = redirnode; 4921556Srgrimes rpp = &n2->nfile.next; 4931556Srgrimes parsefname(); 4941556Srgrimes } 4951556Srgrimes tokpushback++; 4961556Srgrimes *rpp = NULL; 4971556Srgrimes if (redir) { 4981556Srgrimes if (n1->type != NSUBSHELL) { 4991556Srgrimes n2 = (union node *)stalloc(sizeof (struct nredir)); 5001556Srgrimes n2->type = NREDIR; 5011556Srgrimes n2->nredir.n = n1; 5021556Srgrimes n1 = n2; 5031556Srgrimes } 5041556Srgrimes n1->nredir.redirect = redir; 5051556Srgrimes } 50625230Ssteve return n1; 5071556Srgrimes} 5081556Srgrimes 5091556Srgrimes 5101556SrgrimesSTATIC union node * 5118855Srgrimessimplecmd(rpp, redir) 5121556Srgrimes union node **rpp, *redir; 5131556Srgrimes { 5141556Srgrimes union node *args, **app; 5151556Srgrimes union node **orig_rpp = rpp; 51625230Ssteve union node *n = NULL; 5171556Srgrimes 5181556Srgrimes /* If we don't have any redirections already, then we must reset */ 5191556Srgrimes /* rpp to be the address of the local redir variable. */ 5201556Srgrimes if (redir == 0) 5211556Srgrimes rpp = &redir; 5221556Srgrimes 5231556Srgrimes args = NULL; 5241556Srgrimes app = &args; 5258855Srgrimes /* 5261556Srgrimes * We save the incoming value, because we need this for shell 5271556Srgrimes * functions. There can not be a redirect or an argument between 5288855Srgrimes * the function name and the open parenthesis. 5291556Srgrimes */ 5301556Srgrimes orig_rpp = rpp; 5311556Srgrimes 5321556Srgrimes for (;;) { 5331556Srgrimes if (readtoken() == TWORD) { 5341556Srgrimes n = (union node *)stalloc(sizeof (struct narg)); 5351556Srgrimes n->type = NARG; 5361556Srgrimes n->narg.text = wordtext; 5371556Srgrimes n->narg.backquote = backquotelist; 5381556Srgrimes *app = n; 5391556Srgrimes app = &n->narg.next; 5401556Srgrimes } else if (lasttoken == TREDIR) { 5411556Srgrimes *rpp = n = redirnode; 5421556Srgrimes rpp = &n->nfile.next; 5431556Srgrimes parsefname(); /* read name of redirection file */ 5441556Srgrimes } else if (lasttoken == TLP && app == &args->narg.next 5451556Srgrimes && rpp == orig_rpp) { 5461556Srgrimes /* We have a function */ 5471556Srgrimes if (readtoken() != TRP) 5481556Srgrimes synexpect(TRP); 5491556Srgrimes#ifdef notdef 5501556Srgrimes if (! goodname(n->narg.text)) 5511556Srgrimes synerror("Bad function name"); 5521556Srgrimes#endif 5531556Srgrimes n->type = NDEFUN; 5541556Srgrimes n->narg.next = command(); 55525230Ssteve return n; 5561556Srgrimes } else { 5571556Srgrimes tokpushback++; 5581556Srgrimes break; 5591556Srgrimes } 5601556Srgrimes } 5611556Srgrimes *app = NULL; 5621556Srgrimes *rpp = NULL; 5631556Srgrimes n = (union node *)stalloc(sizeof (struct ncmd)); 5641556Srgrimes n->type = NCMD; 5651556Srgrimes n->ncmd.backgnd = 0; 5661556Srgrimes n->ncmd.args = args; 5671556Srgrimes n->ncmd.redirect = redir; 56825230Ssteve return n; 5691556Srgrimes} 5701556Srgrimes 57117987SpeterSTATIC union node * 57217987Spetermakename() { 57317987Speter union node *n; 5741556Srgrimes 57517987Speter n = (union node *)stalloc(sizeof (struct narg)); 57617987Speter n->type = NARG; 57717987Speter n->narg.next = NULL; 57817987Speter n->narg.text = wordtext; 57917987Speter n->narg.backquote = backquotelist; 58017987Speter return n; 58117987Speter} 58217987Speter 58317987Spetervoid fixredir(n, text, err) 58417987Speter union node *n; 58517987Speter const char *text; 58617987Speter int err; 58717987Speter { 58817987Speter TRACE(("Fix redir %s %d\n", text, err)); 58917987Speter if (!err) 59017987Speter n->ndup.vname = NULL; 59117987Speter 59217987Speter if (is_digit(text[0]) && text[1] == '\0') 59317987Speter n->ndup.dupfd = digit_val(text[0]); 59417987Speter else if (text[0] == '-' && text[1] == '\0') 59517987Speter n->ndup.dupfd = -1; 59617987Speter else { 59720425Ssteve 59817987Speter if (err) 59917987Speter synerror("Bad fd number"); 60017987Speter else 60117987Speter n->ndup.vname = makename(); 60217987Speter } 60317987Speter} 60417987Speter 60517987Speter 6061556SrgrimesSTATIC void 6071556Srgrimesparsefname() { 6081556Srgrimes union node *n = redirnode; 6091556Srgrimes 6101556Srgrimes if (readtoken() != TWORD) 6111556Srgrimes synexpect(-1); 6121556Srgrimes if (n->type == NHERE) { 6131556Srgrimes struct heredoc *here = heredoc; 6141556Srgrimes struct heredoc *p; 6151556Srgrimes int i; 6161556Srgrimes 6171556Srgrimes if (quoteflag == 0) 6181556Srgrimes n->type = NXHERE; 6191556Srgrimes TRACE(("Here document %d\n", n->type)); 6201556Srgrimes if (here->striptabs) { 6211556Srgrimes while (*wordtext == '\t') 6221556Srgrimes wordtext++; 6231556Srgrimes } 6241556Srgrimes if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) 6251556Srgrimes synerror("Illegal eof marker for << redirection"); 6261556Srgrimes rmescapes(wordtext); 6271556Srgrimes here->eofmark = wordtext; 6281556Srgrimes here->next = NULL; 6291556Srgrimes if (heredoclist == NULL) 6301556Srgrimes heredoclist = here; 6311556Srgrimes else { 6321556Srgrimes for (p = heredoclist ; p->next ; p = p->next); 6331556Srgrimes p->next = here; 6341556Srgrimes } 6351556Srgrimes } else if (n->type == NTOFD || n->type == NFROMFD) { 63617987Speter fixredir(n, wordtext, 0); 6371556Srgrimes } else { 63817987Speter n->nfile.fname = makename(); 6391556Srgrimes } 6401556Srgrimes} 6411556Srgrimes 6421556Srgrimes 6431556Srgrimes/* 6441556Srgrimes * Input any here documents. 6451556Srgrimes */ 6461556Srgrimes 6471556SrgrimesSTATIC void 6481556Srgrimesparseheredoc() { 6491556Srgrimes struct heredoc *here; 6501556Srgrimes union node *n; 6511556Srgrimes 6521556Srgrimes while (heredoclist) { 6531556Srgrimes here = heredoclist; 6541556Srgrimes heredoclist = here->next; 6551556Srgrimes if (needprompt) { 6561556Srgrimes setprompt(2); 6571556Srgrimes needprompt = 0; 6581556Srgrimes } 6591556Srgrimes readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, 6601556Srgrimes here->eofmark, here->striptabs); 6611556Srgrimes n = (union node *)stalloc(sizeof (struct narg)); 6621556Srgrimes n->narg.type = NARG; 6631556Srgrimes n->narg.next = NULL; 6641556Srgrimes n->narg.text = wordtext; 6651556Srgrimes n->narg.backquote = backquotelist; 6661556Srgrimes here->here->nhere.doc = n; 6671556Srgrimes } 6681556Srgrimes} 6691556Srgrimes 6701556SrgrimesSTATIC int 6711556Srgrimespeektoken() { 6721556Srgrimes int t; 6731556Srgrimes 6741556Srgrimes t = readtoken(); 6751556Srgrimes tokpushback++; 6761556Srgrimes return (t); 6771556Srgrimes} 6781556Srgrimes 6791556SrgrimesSTATIC int xxreadtoken(); 6801556Srgrimes 6811556SrgrimesSTATIC int 6821556Srgrimesreadtoken() { 6831556Srgrimes int t; 6841556Srgrimes int savecheckkwd = checkkwd; 6851556Srgrimes struct alias *ap; 6861556Srgrimes#ifdef DEBUG 6871556Srgrimes int alreadyseen = tokpushback; 6881556Srgrimes#endif 6898855Srgrimes 6901556Srgrimes top: 6911556Srgrimes t = xxreadtoken(); 6921556Srgrimes 6931556Srgrimes if (checkkwd) { 6941556Srgrimes /* 6951556Srgrimes * eat newlines 6961556Srgrimes */ 6971556Srgrimes if (checkkwd == 2) { 6981556Srgrimes checkkwd = 0; 6991556Srgrimes while (t == TNL) { 7001556Srgrimes parseheredoc(); 7011556Srgrimes t = xxreadtoken(); 7021556Srgrimes } 7031556Srgrimes } else 7041556Srgrimes checkkwd = 0; 7051556Srgrimes /* 7061556Srgrimes * check for keywords and aliases 7071556Srgrimes */ 70820425Ssteve if (t == TWORD && !quoteflag) 70917987Speter { 71025230Ssteve char * const *pp; 7111556Srgrimes 7121556Srgrimes for (pp = (char **)parsekwd; *pp; pp++) { 71320425Ssteve if (**pp == *wordtext && equal(*pp, wordtext)) 71417987Speter { 7151556Srgrimes lasttoken = t = pp - parsekwd + KWDOFFSET; 7161556Srgrimes TRACE(("keyword %s recognized\n", tokname[t])); 7171556Srgrimes goto out; 7181556Srgrimes } 7191556Srgrimes } 72018018Speter if (noaliases == 0 && 72118018Speter (ap = lookupalias(wordtext, 1)) != NULL) { 7221556Srgrimes pushstring(ap->val, strlen(ap->val), ap); 7231556Srgrimes checkkwd = savecheckkwd; 7241556Srgrimes goto top; 7251556Srgrimes } 7261556Srgrimes } 7271556Srgrimesout: 72825230Ssteve checkkwd = 0; 7291556Srgrimes } 7301556Srgrimes#ifdef DEBUG 7311556Srgrimes if (!alreadyseen) 7321556Srgrimes TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); 7331556Srgrimes else 7341556Srgrimes TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); 7351556Srgrimes#endif 7361556Srgrimes return (t); 7371556Srgrimes} 7381556Srgrimes 7391556Srgrimes 7401556Srgrimes/* 7411556Srgrimes * Read the next input token. 7421556Srgrimes * If the token is a word, we set backquotelist to the list of cmds in 7431556Srgrimes * backquotes. We set quoteflag to true if any part of the word was 7441556Srgrimes * quoted. 7451556Srgrimes * If the token is TREDIR, then we set redirnode to a structure containing 7461556Srgrimes * the redirection. 7471556Srgrimes * In all cases, the variable startlinno is set to the number of the line 7481556Srgrimes * on which the token starts. 7491556Srgrimes * 7501556Srgrimes * [Change comment: here documents and internal procedures] 7511556Srgrimes * [Readtoken shouldn't have any arguments. Perhaps we should make the 7521556Srgrimes * word parsing code into a separate routine. In this case, readtoken 7531556Srgrimes * doesn't need to have any internal procedures, but parseword does. 7541556Srgrimes * We could also make parseoperator in essence the main routine, and 7551556Srgrimes * have parseword (readtoken1?) handle both words and redirection.] 7561556Srgrimes */ 7571556Srgrimes 7581556Srgrimes#define RETURN(token) return lasttoken = token 7591556Srgrimes 7601556SrgrimesSTATIC int 7611556Srgrimesxxreadtoken() { 76225230Ssteve int c; 7631556Srgrimes 7641556Srgrimes if (tokpushback) { 7651556Srgrimes tokpushback = 0; 7661556Srgrimes return lasttoken; 7671556Srgrimes } 7681556Srgrimes if (needprompt) { 7691556Srgrimes setprompt(2); 7701556Srgrimes needprompt = 0; 7711556Srgrimes } 7721556Srgrimes startlinno = plinno; 7731556Srgrimes for (;;) { /* until token or start of word found */ 7741556Srgrimes c = pgetc_macro(); 7751556Srgrimes if (c == ' ' || c == '\t') 7761556Srgrimes continue; /* quick check for white space first */ 7771556Srgrimes switch (c) { 7781556Srgrimes case ' ': case '\t': 7791556Srgrimes continue; 7801556Srgrimes case '#': 7811556Srgrimes while ((c = pgetc()) != '\n' && c != PEOF); 7821556Srgrimes pungetc(); 7831556Srgrimes continue; 7841556Srgrimes case '\\': 7851556Srgrimes if (pgetc() == '\n') { 7861556Srgrimes startlinno = ++plinno; 7871556Srgrimes if (doprompt) 7881556Srgrimes setprompt(2); 7891556Srgrimes else 7901556Srgrimes setprompt(0); 7911556Srgrimes continue; 7921556Srgrimes } 7931556Srgrimes pungetc(); 7941556Srgrimes goto breakloop; 7951556Srgrimes case '\n': 7961556Srgrimes plinno++; 7971556Srgrimes needprompt = doprompt; 7981556Srgrimes RETURN(TNL); 7991556Srgrimes case PEOF: 8001556Srgrimes RETURN(TEOF); 8011556Srgrimes case '&': 8021556Srgrimes if (pgetc() == '&') 8031556Srgrimes RETURN(TAND); 8041556Srgrimes pungetc(); 8051556Srgrimes RETURN(TBACKGND); 8061556Srgrimes case '|': 8071556Srgrimes if (pgetc() == '|') 8081556Srgrimes RETURN(TOR); 8091556Srgrimes pungetc(); 8101556Srgrimes RETURN(TPIPE); 8111556Srgrimes case ';': 8121556Srgrimes if (pgetc() == ';') 8131556Srgrimes RETURN(TENDCASE); 8141556Srgrimes pungetc(); 8151556Srgrimes RETURN(TSEMI); 8161556Srgrimes case '(': 8171556Srgrimes RETURN(TLP); 8181556Srgrimes case ')': 8191556Srgrimes RETURN(TRP); 8201556Srgrimes default: 8211556Srgrimes goto breakloop; 8221556Srgrimes } 8231556Srgrimes } 8241556Srgrimesbreakloop: 8251556Srgrimes return readtoken1(c, BASESYNTAX, (char *)NULL, 0); 8261556Srgrimes#undef RETURN 8271556Srgrimes} 8281556Srgrimes 8291556Srgrimes 8301556Srgrimes 8311556Srgrimes/* 8321556Srgrimes * If eofmark is NULL, read a word or a redirection symbol. If eofmark 8331556Srgrimes * is not NULL, read a here document. In the latter case, eofmark is the 8341556Srgrimes * word which marks the end of the document and striptabs is true if 8351556Srgrimes * leading tabs should be stripped from the document. The argument firstc 8361556Srgrimes * is the first character of the input token or document. 8371556Srgrimes * 8381556Srgrimes * Because C does not have internal subroutines, I have simulated them 8391556Srgrimes * using goto's to implement the subroutine linkage. The following macros 8401556Srgrimes * will run code that appears at the end of readtoken1. 8411556Srgrimes */ 8421556Srgrimes 8431556Srgrimes#define CHECKEND() {goto checkend; checkend_return:;} 8441556Srgrimes#define PARSEREDIR() {goto parseredir; parseredir_return:;} 8451556Srgrimes#define PARSESUB() {goto parsesub; parsesub_return:;} 8461556Srgrimes#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} 8471556Srgrimes#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} 8481556Srgrimes#define PARSEARITH() {goto parsearith; parsearith_return:;} 8491556Srgrimes 8501556SrgrimesSTATIC int 8511556Srgrimesreadtoken1(firstc, syntax, eofmark, striptabs) 8521556Srgrimes int firstc; 8531556Srgrimes char const *syntax; 8541556Srgrimes char *eofmark; 8551556Srgrimes int striptabs; 8561556Srgrimes { 85717987Speter int c = firstc; 85817987Speter char *out; 8591556Srgrimes int len; 8601556Srgrimes char line[EOFMARKLEN + 1]; 8611556Srgrimes struct nodelist *bqlist; 8621556Srgrimes int quotef; 8631556Srgrimes int dblquote; 8641556Srgrimes int varnest; /* levels of variables expansion */ 8651556Srgrimes int arinest; /* levels of arithmetic expansion */ 8661556Srgrimes int parenlevel; /* levels of parens in arithmetic */ 8671556Srgrimes int oldstyle; 8681556Srgrimes char const *prevsyntax; /* syntax before arithmetic */ 86917987Speter#if __GNUC__ 87017987Speter /* Avoid longjmp clobbering */ 87117987Speter (void) &out; 87217987Speter (void) "ef; 87317987Speter (void) &dblquote; 87417987Speter (void) &varnest; 87517987Speter (void) &arinest; 87617987Speter (void) &parenlevel; 87717987Speter (void) &oldstyle; 87817987Speter (void) &prevsyntax; 87917987Speter (void) &syntax; 88017987Speter#endif 8811556Srgrimes 8821556Srgrimes startlinno = plinno; 8831556Srgrimes dblquote = 0; 8841556Srgrimes if (syntax == DQSYNTAX) 8851556Srgrimes dblquote = 1; 8861556Srgrimes quotef = 0; 8871556Srgrimes bqlist = NULL; 8881556Srgrimes varnest = 0; 8891556Srgrimes arinest = 0; 8901556Srgrimes parenlevel = 0; 8911556Srgrimes 8921556Srgrimes STARTSTACKSTR(out); 8931556Srgrimes loop: { /* for each line, until end of word */ 8941556Srgrimes#if ATTY 8951556Srgrimes if (c == '\034' && doprompt 8961556Srgrimes && attyset() && ! equal(termval(), "emacs")) { 8971556Srgrimes attyline(); 8981556Srgrimes if (syntax == BASESYNTAX) 8991556Srgrimes return readtoken(); 9001556Srgrimes c = pgetc(); 9011556Srgrimes goto loop; 9021556Srgrimes } 9031556Srgrimes#endif 9041556Srgrimes CHECKEND(); /* set c to PEOF if at end of here document */ 9051556Srgrimes for (;;) { /* until end of line or end of word */ 9061556Srgrimes CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ 9071556Srgrimes switch(syntax[c]) { 9081556Srgrimes case CNL: /* '\n' */ 9091556Srgrimes if (syntax == BASESYNTAX) 9101556Srgrimes goto endword; /* exit outer loop */ 9111556Srgrimes USTPUTC(c, out); 9121556Srgrimes plinno++; 9131556Srgrimes if (doprompt) 9141556Srgrimes setprompt(2); 9151556Srgrimes else 9161556Srgrimes setprompt(0); 9171556Srgrimes c = pgetc(); 9181556Srgrimes goto loop; /* continue outer loop */ 9191556Srgrimes case CWORD: 9201556Srgrimes USTPUTC(c, out); 9211556Srgrimes break; 9221556Srgrimes case CCTL: 9231556Srgrimes if (eofmark == NULL || dblquote) 9241556Srgrimes USTPUTC(CTLESC, out); 9251556Srgrimes USTPUTC(c, out); 9261556Srgrimes break; 9271556Srgrimes case CBACK: /* backslash */ 9281556Srgrimes c = pgetc(); 9291556Srgrimes if (c == PEOF) { 9301556Srgrimes USTPUTC('\\', out); 9311556Srgrimes pungetc(); 9321556Srgrimes } else if (c == '\n') { 9331556Srgrimes if (doprompt) 9341556Srgrimes setprompt(2); 9351556Srgrimes else 9361556Srgrimes setprompt(0); 9371556Srgrimes } else { 9381556Srgrimes if (dblquote && c != '\\' && c != '`' && c != '$' 9391556Srgrimes && (c != '"' || eofmark != NULL)) 9401556Srgrimes USTPUTC('\\', out); 9411556Srgrimes if (SQSYNTAX[c] == CCTL) 9421556Srgrimes USTPUTC(CTLESC, out); 9431556Srgrimes USTPUTC(c, out); 9441556Srgrimes quotef++; 9451556Srgrimes } 9461556Srgrimes break; 9471556Srgrimes case CSQUOTE: 9481556Srgrimes syntax = SQSYNTAX; 9491556Srgrimes break; 9501556Srgrimes case CDQUOTE: 9511556Srgrimes syntax = DQSYNTAX; 9521556Srgrimes dblquote = 1; 9531556Srgrimes break; 9541556Srgrimes case CENDQUOTE: 9551556Srgrimes if (eofmark) { 9561556Srgrimes USTPUTC(c, out); 9571556Srgrimes } else { 9581556Srgrimes if (arinest) 9591556Srgrimes syntax = ARISYNTAX; 9601556Srgrimes else 9611556Srgrimes syntax = BASESYNTAX; 9621556Srgrimes quotef++; 9631556Srgrimes dblquote = 0; 9641556Srgrimes } 9651556Srgrimes break; 9661556Srgrimes case CVAR: /* '$' */ 9671556Srgrimes PARSESUB(); /* parse substitution */ 9681556Srgrimes break; 9691556Srgrimes case CENDVAR: /* '}' */ 9701556Srgrimes if (varnest > 0) { 9711556Srgrimes varnest--; 9721556Srgrimes USTPUTC(CTLENDVAR, out); 9731556Srgrimes } else { 9741556Srgrimes USTPUTC(c, out); 9751556Srgrimes } 9761556Srgrimes break; 9771556Srgrimes case CLP: /* '(' in arithmetic */ 9781556Srgrimes parenlevel++; 9791556Srgrimes USTPUTC(c, out); 9801556Srgrimes break; 9811556Srgrimes case CRP: /* ')' in arithmetic */ 9821556Srgrimes if (parenlevel > 0) { 9831556Srgrimes USTPUTC(c, out); 9841556Srgrimes --parenlevel; 9851556Srgrimes } else { 9861556Srgrimes if (pgetc() == ')') { 9871556Srgrimes if (--arinest == 0) { 9881556Srgrimes USTPUTC(CTLENDARI, out); 9891556Srgrimes syntax = prevsyntax; 9901556Srgrimes } else 9911556Srgrimes USTPUTC(')', out); 9921556Srgrimes } else { 9938855Srgrimes /* 9941556Srgrimes * unbalanced parens 9951556Srgrimes * (don't 2nd guess - no error) 9961556Srgrimes */ 9971556Srgrimes pungetc(); 9981556Srgrimes USTPUTC(')', out); 9991556Srgrimes } 10001556Srgrimes } 10011556Srgrimes break; 10021556Srgrimes case CBQUOTE: /* '`' */ 10031556Srgrimes PARSEBACKQOLD(); 10041556Srgrimes break; 10051556Srgrimes case CEOF: 10061556Srgrimes goto endword; /* exit outer loop */ 10071556Srgrimes default: 10081556Srgrimes if (varnest == 0) 10091556Srgrimes goto endword; /* exit outer loop */ 10101556Srgrimes USTPUTC(c, out); 10111556Srgrimes } 10121556Srgrimes c = pgetc_macro(); 10131556Srgrimes } 10141556Srgrimes } 10151556Srgrimesendword: 10161556Srgrimes if (syntax == ARISYNTAX) 10171556Srgrimes synerror("Missing '))'"); 10181556Srgrimes if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) 10191556Srgrimes synerror("Unterminated quoted string"); 10201556Srgrimes if (varnest != 0) { 10211556Srgrimes startlinno = plinno; 10221556Srgrimes synerror("Missing '}'"); 10231556Srgrimes } 10241556Srgrimes USTPUTC('\0', out); 10251556Srgrimes len = out - stackblock(); 10261556Srgrimes out = stackblock(); 10271556Srgrimes if (eofmark == NULL) { 10281556Srgrimes if ((c == '>' || c == '<') 10291556Srgrimes && quotef == 0 10301556Srgrimes && len <= 2 10311556Srgrimes && (*out == '\0' || is_digit(*out))) { 10321556Srgrimes PARSEREDIR(); 10331556Srgrimes return lasttoken = TREDIR; 10341556Srgrimes } else { 10351556Srgrimes pungetc(); 10361556Srgrimes } 10371556Srgrimes } 10381556Srgrimes quoteflag = quotef; 10391556Srgrimes backquotelist = bqlist; 10401556Srgrimes grabstackblock(len); 10411556Srgrimes wordtext = out; 10421556Srgrimes return lasttoken = TWORD; 10431556Srgrimes/* end of readtoken routine */ 10441556Srgrimes 10451556Srgrimes 10461556Srgrimes 10471556Srgrimes/* 10481556Srgrimes * Check to see whether we are at the end of the here document. When this 10491556Srgrimes * is called, c is set to the first character of the next input line. If 10501556Srgrimes * we are at the end of the here document, this routine sets the c to PEOF. 10511556Srgrimes */ 10521556Srgrimes 10531556Srgrimescheckend: { 10541556Srgrimes if (eofmark) { 10551556Srgrimes if (striptabs) { 10561556Srgrimes while (c == '\t') 10571556Srgrimes c = pgetc(); 10581556Srgrimes } 10591556Srgrimes if (c == *eofmark) { 10601556Srgrimes if (pfgets(line, sizeof line) != NULL) { 106125230Ssteve char *p, *q; 10621556Srgrimes 10631556Srgrimes p = line; 10641556Srgrimes for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); 10651556Srgrimes if (*p == '\n' && *q == '\0') { 10661556Srgrimes c = PEOF; 10671556Srgrimes plinno++; 10681556Srgrimes needprompt = doprompt; 10691556Srgrimes } else { 10701556Srgrimes pushstring(line, strlen(line), NULL); 10711556Srgrimes } 10721556Srgrimes } 10731556Srgrimes } 10741556Srgrimes } 10751556Srgrimes goto checkend_return; 10761556Srgrimes} 10771556Srgrimes 10781556Srgrimes 10791556Srgrimes/* 10801556Srgrimes * Parse a redirection operator. The variable "out" points to a string 10811556Srgrimes * specifying the fd to be redirected. The variable "c" contains the 10821556Srgrimes * first character of the redirection operator. 10831556Srgrimes */ 10841556Srgrimes 10851556Srgrimesparseredir: { 10861556Srgrimes char fd = *out; 10871556Srgrimes union node *np; 10881556Srgrimes 10891556Srgrimes np = (union node *)stalloc(sizeof (struct nfile)); 10901556Srgrimes if (c == '>') { 10911556Srgrimes np->nfile.fd = 1; 10921556Srgrimes c = pgetc(); 10931556Srgrimes if (c == '>') 10941556Srgrimes np->type = NAPPEND; 10951556Srgrimes else if (c == '&') 10961556Srgrimes np->type = NTOFD; 10971556Srgrimes else { 10981556Srgrimes np->type = NTO; 10991556Srgrimes pungetc(); 11001556Srgrimes } 11011556Srgrimes } else { /* c == '<' */ 11021556Srgrimes np->nfile.fd = 0; 11031556Srgrimes c = pgetc(); 11041556Srgrimes if (c == '<') { 11051556Srgrimes if (sizeof (struct nfile) != sizeof (struct nhere)) { 11061556Srgrimes np = (union node *)stalloc(sizeof (struct nhere)); 11071556Srgrimes np->nfile.fd = 0; 11081556Srgrimes } 11091556Srgrimes np->type = NHERE; 11101556Srgrimes heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); 11111556Srgrimes heredoc->here = np; 11121556Srgrimes if ((c = pgetc()) == '-') { 11131556Srgrimes heredoc->striptabs = 1; 11141556Srgrimes } else { 11151556Srgrimes heredoc->striptabs = 0; 11161556Srgrimes pungetc(); 11171556Srgrimes } 11181556Srgrimes } else if (c == '&') 11191556Srgrimes np->type = NFROMFD; 11201556Srgrimes else { 11211556Srgrimes np->type = NFROM; 11221556Srgrimes pungetc(); 11231556Srgrimes } 11241556Srgrimes } 11251556Srgrimes if (fd != '\0') 11261556Srgrimes np->nfile.fd = digit_val(fd); 11271556Srgrimes redirnode = np; 11281556Srgrimes goto parseredir_return; 11291556Srgrimes} 11301556Srgrimes 11311556Srgrimes 11321556Srgrimes/* 11331556Srgrimes * Parse a substitution. At this point, we have read the dollar sign 11341556Srgrimes * and nothing else. 11351556Srgrimes */ 11361556Srgrimes 11371556Srgrimesparsesub: { 11381556Srgrimes int subtype; 11391556Srgrimes int typeloc; 11401556Srgrimes int flags; 11411556Srgrimes char *p; 11421556Srgrimes#ifndef GDB_HACK 11431556Srgrimes static const char types[] = "}-+?="; 11441556Srgrimes#endif 114518202Speter int bracketed_name = 0; /* used to handle ${[0-9]*} variables */ 11461556Srgrimes 11471556Srgrimes c = pgetc(); 11481556Srgrimes if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { 11491556Srgrimes USTPUTC('$', out); 11501556Srgrimes pungetc(); 11511556Srgrimes } else if (c == '(') { /* $(command) or $((arith)) */ 11521556Srgrimes if (pgetc() == '(') { 11531556Srgrimes PARSEARITH(); 11541556Srgrimes } else { 11551556Srgrimes pungetc(); 11561556Srgrimes PARSEBACKQNEW(); 11571556Srgrimes } 11581556Srgrimes } else { 11591556Srgrimes USTPUTC(CTLVAR, out); 11601556Srgrimes typeloc = out - stackblock(); 11611556Srgrimes USTPUTC(VSNORMAL, out); 11621556Srgrimes subtype = VSNORMAL; 11631556Srgrimes if (c == '{') { 116418202Speter bracketed_name = 1; 11651556Srgrimes c = pgetc(); 116617987Speter if (c == '#') { 116717987Speter if ((c = pgetc()) == '}') 116817987Speter c = '#'; 116917987Speter else 117017987Speter subtype = VSLENGTH; 117117987Speter } 117217987Speter else 117317987Speter subtype = 0; 11741556Srgrimes } 11751556Srgrimes if (is_name(c)) { 11761556Srgrimes do { 11771556Srgrimes STPUTC(c, out); 11781556Srgrimes c = pgetc(); 11791556Srgrimes } while (is_in_name(c)); 118018202Speter } else if (is_digit(c)) { 118118202Speter if (bracketed_name) { 118218202Speter do { 118318202Speter STPUTC(c, out); 118418202Speter c = pgetc(); 118518202Speter } while (is_digit(c)); 118618202Speter } else { 118718202Speter STPUTC(c, out); 118818202Speter c = pgetc(); 118918202Speter } 11901556Srgrimes } else { 11911556Srgrimes if (! is_special(c)) 11921556Srgrimesbadsub: synerror("Bad substitution"); 11931556Srgrimes USTPUTC(c, out); 11941556Srgrimes c = pgetc(); 11951556Srgrimes } 11961556Srgrimes STPUTC('=', out); 11971556Srgrimes flags = 0; 11981556Srgrimes if (subtype == 0) { 119917987Speter switch (c) { 120017987Speter case ':': 12011556Srgrimes flags = VSNUL; 12021556Srgrimes c = pgetc(); 120317987Speter /*FALLTHROUGH*/ 120417987Speter default: 120517987Speter p = strchr(types, c); 120617987Speter if (p == NULL) 120717987Speter goto badsub; 120817987Speter subtype = p - types + VSNORMAL; 120917987Speter break; 121017987Speter case '%': 121120425Ssteve case '#': 121217987Speter { 121317987Speter int cc = c; 121417987Speter subtype = c == '#' ? VSTRIMLEFT : 121517987Speter VSTRIMRIGHT; 121617987Speter c = pgetc(); 121717987Speter if (c == cc) 121817987Speter subtype++; 121917987Speter else 122017987Speter pungetc(); 122117987Speter break; 122217987Speter } 12231556Srgrimes } 12241556Srgrimes } else { 12251556Srgrimes pungetc(); 12261556Srgrimes } 12271556Srgrimes if (dblquote || arinest) 12281556Srgrimes flags |= VSQUOTE; 12291556Srgrimes *(stackblock() + typeloc) = subtype | flags; 12301556Srgrimes if (subtype != VSNORMAL) 12311556Srgrimes varnest++; 12321556Srgrimes } 12331556Srgrimes goto parsesub_return; 12341556Srgrimes} 12351556Srgrimes 12361556Srgrimes 12371556Srgrimes/* 12381556Srgrimes * Called to parse command substitutions. Newstyle is set if the command 12391556Srgrimes * is enclosed inside $(...); nlpp is a pointer to the head of the linked 12401556Srgrimes * list of commands (passed by reference), and savelen is the number of 12411556Srgrimes * characters on the top of the stack which must be preserved. 12421556Srgrimes */ 12431556Srgrimes 12441556Srgrimesparsebackq: { 12451556Srgrimes struct nodelist **nlpp; 12461556Srgrimes int savepbq; 12471556Srgrimes union node *n; 12481556Srgrimes char *volatile str; 12491556Srgrimes struct jmploc jmploc; 12501556Srgrimes struct jmploc *volatile savehandler; 12511556Srgrimes int savelen; 125220425Ssteve int saveprompt; 125320425Ssteve#if __GNUC__ 125420425Ssteve /* Avoid longjmp clobbering */ 125520425Ssteve (void) &saveprompt; 125620425Ssteve#endif 12571556Srgrimes 12581556Srgrimes savepbq = parsebackquote; 12591556Srgrimes if (setjmp(jmploc.loc)) { 12601556Srgrimes if (str) 12611556Srgrimes ckfree(str); 12621556Srgrimes parsebackquote = 0; 12631556Srgrimes handler = savehandler; 12641556Srgrimes longjmp(handler->loc, 1); 12651556Srgrimes } 12661556Srgrimes INTOFF; 12671556Srgrimes str = NULL; 12681556Srgrimes savelen = out - stackblock(); 12691556Srgrimes if (savelen > 0) { 12701556Srgrimes str = ckmalloc(savelen); 127117987Speter memcpy(str, stackblock(), savelen); 12721556Srgrimes } 12731556Srgrimes savehandler = handler; 12741556Srgrimes handler = &jmploc; 12751556Srgrimes INTON; 12761556Srgrimes if (oldstyle) { 12771556Srgrimes /* We must read until the closing backquote, giving special 12781556Srgrimes treatment to some slashes, and then push the string and 12791556Srgrimes reread it as input, interpreting it normally. */ 128025230Ssteve char *out; 128125230Ssteve int c; 12821556Srgrimes int savelen; 12831556Srgrimes char *str; 12848855Srgrimes 128520425Ssteve 12861556Srgrimes STARTSTACKSTR(out); 128720425Ssteve for (;;) { 128820425Ssteve if (needprompt) { 128920425Ssteve setprompt(2); 129020425Ssteve needprompt = 0; 129120425Ssteve } 129220425Ssteve switch (c = pgetc()) { 129320425Ssteve case '`': 129420425Ssteve goto done; 129520425Ssteve 129620425Ssteve case '\\': 129720425Ssteve if ((c = pgetc()) == '\n') { 129820425Ssteve plinno++; 129920425Ssteve if (doprompt) 130020425Ssteve setprompt(2); 130120425Ssteve else 130220425Ssteve setprompt(0); 130320425Ssteve /* 130420425Ssteve * If eating a newline, avoid putting 130520425Ssteve * the newline into the new character 130620425Ssteve * stream (via the STPUTC after the 130720425Ssteve * switch). 130820425Ssteve */ 130920425Ssteve continue; 131020425Ssteve } 131117987Speter if (c != '\\' && c != '`' && c != '$' 13121556Srgrimes && (!dblquote || c != '"')) 13131556Srgrimes STPUTC('\\', out); 131420425Ssteve break; 131520425Ssteve 131620425Ssteve case '\n': 131720425Ssteve plinno++; 131820425Ssteve needprompt = doprompt; 131920425Ssteve break; 132020425Ssteve 132120425Ssteve case PEOF: 132220425Ssteve startlinno = plinno; 132320425Ssteve synerror("EOF in backquote substitution"); 132420425Ssteve break; 132520425Ssteve 132620425Ssteve default: 132720425Ssteve break; 132820425Ssteve } 132920425Ssteve STPUTC(c, out); 13301556Srgrimes } 133120425Sstevedone: 13321556Srgrimes STPUTC('\0', out); 13331556Srgrimes savelen = out - stackblock(); 13341556Srgrimes if (savelen > 0) { 13351556Srgrimes str = ckmalloc(savelen); 133617987Speter memcpy(str, stackblock(), savelen); 133717987Speter setinputstring(str, 1); 13381556Srgrimes } 13391556Srgrimes } 13401556Srgrimes nlpp = &bqlist; 13411556Srgrimes while (*nlpp) 13421556Srgrimes nlpp = &(*nlpp)->next; 13431556Srgrimes *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); 13441556Srgrimes (*nlpp)->next = NULL; 13451556Srgrimes parsebackquote = oldstyle; 134620425Ssteve 134720425Ssteve if (oldstyle) { 134820425Ssteve saveprompt = doprompt; 134920425Ssteve doprompt = 0; 135020425Ssteve } 135120425Ssteve 13521556Srgrimes n = list(0); 135320425Ssteve 135420425Ssteve if (oldstyle) 135520425Ssteve doprompt = saveprompt; 135620425Ssteve else { 135720425Ssteve if (readtoken() != TRP) 135820425Ssteve synexpect(TRP); 135920425Ssteve } 136020425Ssteve 13611556Srgrimes (*nlpp)->n = n; 136220425Ssteve if (oldstyle) { 136320425Ssteve /* 136420425Ssteve * Start reading from old file again, ignoring any pushed back 136520425Ssteve * tokens left from the backquote parsing 136620425Ssteve */ 13671556Srgrimes popfile(); 136820425Ssteve tokpushback = 0; 136920425Ssteve } 13701556Srgrimes while (stackblocksize() <= savelen) 13711556Srgrimes growstackblock(); 13721556Srgrimes STARTSTACKSTR(out); 13731556Srgrimes if (str) { 137417987Speter memcpy(out, str, savelen); 13751556Srgrimes STADJUST(savelen, out); 13761556Srgrimes INTOFF; 13771556Srgrimes ckfree(str); 13781556Srgrimes str = NULL; 13791556Srgrimes INTON; 13801556Srgrimes } 13811556Srgrimes parsebackquote = savepbq; 13821556Srgrimes handler = savehandler; 13831556Srgrimes if (arinest || dblquote) 13841556Srgrimes USTPUTC(CTLBACKQ | CTLQUOTE, out); 13851556Srgrimes else 13861556Srgrimes USTPUTC(CTLBACKQ, out); 13871556Srgrimes if (oldstyle) 13881556Srgrimes goto parsebackq_oldreturn; 13891556Srgrimes else 13901556Srgrimes goto parsebackq_newreturn; 13911556Srgrimes} 13921556Srgrimes 13931556Srgrimes/* 13941556Srgrimes * Parse an arithmetic expansion (indicate start of one and set state) 13951556Srgrimes */ 13961556Srgrimesparsearith: { 13971556Srgrimes 13981556Srgrimes if (++arinest == 1) { 13991556Srgrimes prevsyntax = syntax; 14001556Srgrimes syntax = ARISYNTAX; 14011556Srgrimes USTPUTC(CTLARI, out); 14021556Srgrimes } else { 14031556Srgrimes /* 14041556Srgrimes * we collapse embedded arithmetic expansion to 14051556Srgrimes * parenthesis, which should be equivalent 14061556Srgrimes */ 14071556Srgrimes USTPUTC('(', out); 14081556Srgrimes } 14091556Srgrimes goto parsearith_return; 14101556Srgrimes} 14111556Srgrimes 14121556Srgrimes} /* end of readtoken */ 14131556Srgrimes 14141556Srgrimes 14151556Srgrimes 14161556Srgrimes#ifdef mkinit 14171556SrgrimesRESET { 14181556Srgrimes tokpushback = 0; 14191556Srgrimes checkkwd = 0; 14201556Srgrimes} 14211556Srgrimes#endif 14221556Srgrimes 14231556Srgrimes/* 14241556Srgrimes * Returns true if the text contains nothing to expand (no dollar signs 14251556Srgrimes * or backquotes). 14261556Srgrimes */ 14271556Srgrimes 14281556SrgrimesSTATIC int 14291556Srgrimesnoexpand(text) 14301556Srgrimes char *text; 14311556Srgrimes { 143225230Ssteve char *p; 143325230Ssteve char c; 14341556Srgrimes 14351556Srgrimes p = text; 14361556Srgrimes while ((c = *p++) != '\0') { 14371556Srgrimes if (c == CTLESC) 14381556Srgrimes p++; 14391556Srgrimes else if (BASESYNTAX[c] == CCTL) 14401556Srgrimes return 0; 14411556Srgrimes } 14421556Srgrimes return 1; 14431556Srgrimes} 14441556Srgrimes 14451556Srgrimes 14461556Srgrimes/* 14471556Srgrimes * Return true if the argument is a legal variable name (a letter or 14481556Srgrimes * underscore followed by zero or more letters, underscores, and digits). 14491556Srgrimes */ 14501556Srgrimes 14511556Srgrimesint 14521556Srgrimesgoodname(name) 14531556Srgrimes char *name; 14541556Srgrimes { 145525230Ssteve char *p; 14561556Srgrimes 14571556Srgrimes p = name; 14581556Srgrimes if (! is_name(*p)) 14591556Srgrimes return 0; 14601556Srgrimes while (*++p) { 14611556Srgrimes if (! is_in_name(*p)) 14621556Srgrimes return 0; 14631556Srgrimes } 14641556Srgrimes return 1; 14651556Srgrimes} 14661556Srgrimes 14671556Srgrimes 14681556Srgrimes/* 14691556Srgrimes * Called when an unexpected token is read during the parse. The argument 14701556Srgrimes * is the token that is expected, or -1 if more than one type of token can 14711556Srgrimes * occur at this point. 14721556Srgrimes */ 14731556Srgrimes 14741556SrgrimesSTATIC void 147520425Sstevesynexpect(token) 147617987Speter int token; 147717987Speter{ 14781556Srgrimes char msg[64]; 14791556Srgrimes 14801556Srgrimes if (token >= 0) { 14811556Srgrimes fmtstr(msg, 64, "%s unexpected (expecting %s)", 14821556Srgrimes tokname[lasttoken], tokname[token]); 14831556Srgrimes } else { 14841556Srgrimes fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); 14851556Srgrimes } 14861556Srgrimes synerror(msg); 14871556Srgrimes} 14881556Srgrimes 14891556Srgrimes 14901556SrgrimesSTATIC void 14911556Srgrimessynerror(msg) 14921556Srgrimes char *msg; 14931556Srgrimes { 14941556Srgrimes if (commandname) 14951556Srgrimes outfmt(&errout, "%s: %d: ", commandname, startlinno); 14961556Srgrimes outfmt(&errout, "Syntax error: %s\n", msg); 14971556Srgrimes error((char *)NULL); 14981556Srgrimes} 14991556Srgrimes 15001556SrgrimesSTATIC void 15011556Srgrimessetprompt(which) 15021556Srgrimes int which; 15031556Srgrimes { 15041556Srgrimes whichprompt = which; 15051556Srgrimes 150617987Speter#ifndef NO_HISTORY 15071556Srgrimes if (!el) 150817987Speter#endif 15091556Srgrimes out2str(getprompt(NULL)); 15101556Srgrimes} 15111556Srgrimes 15121556Srgrimes/* 15131556Srgrimes * called by editline -- any expansions to the prompt 15141556Srgrimes * should be added here. 15151556Srgrimes */ 15161556Srgrimeschar * 15171556Srgrimesgetprompt(unused) 151825905Ssteve void *unused __unused; 151925905Ssteve{ 15201556Srgrimes switch (whichprompt) { 15211556Srgrimes case 0: 15221556Srgrimes return ""; 15231556Srgrimes case 1: 15241556Srgrimes return ps1val(); 15251556Srgrimes case 2: 15261556Srgrimes return ps2val(); 15271556Srgrimes default: 15281556Srgrimes return "<internal prompt error>"; 15291556Srgrimes } 15301556Srgrimes} 1531