147139Sbostic /*- 247139Sbostic * Copyright (c) 1991 The Regents of the University of California. 347139Sbostic * All rights reserved. 447139Sbostic * 547139Sbostic * This code is derived from software contributed to Berkeley by 647139Sbostic * Kenneth Almquist. 747139Sbostic * 847139Sbostic * %sccs.include.redist.c% 947139Sbostic */ 1047139Sbostic 1147139Sbostic #ifndef lint 12*59178Storek static char sccsid[] = "@(#)parser.c 5.10 (Berkeley) 04/20/93"; 1347139Sbostic #endif /* not lint */ 1447139Sbostic 1547139Sbostic #include "shell.h" 1647139Sbostic #include "parser.h" 1747139Sbostic #include "nodes.h" 1847139Sbostic #include "expand.h" /* defines rmescapes() */ 1947139Sbostic #include "redir.h" /* defines copyfd() */ 2047139Sbostic #include "syntax.h" 2147139Sbostic #include "options.h" 2247139Sbostic #include "input.h" 2347139Sbostic #include "output.h" 2447139Sbostic #include "var.h" 2547139Sbostic #include "error.h" 2647139Sbostic #include "memalloc.h" 2747139Sbostic #include "mystring.h" 2854322Smarc #include "alias.h" 2954330Smarc #include "myhistedit.h" 3047139Sbostic 3147139Sbostic 3247139Sbostic /* 3347139Sbostic * Shell command parser. 3447139Sbostic */ 3547139Sbostic 3647139Sbostic #define EOFMARKLEN 79 3747139Sbostic 3847139Sbostic /* values returned by readtoken */ 3947139Sbostic #include "token.def" 4047139Sbostic 4147139Sbostic 4247139Sbostic 4347139Sbostic struct heredoc { 4447139Sbostic struct heredoc *next; /* next here document in list */ 4547139Sbostic union node *here; /* redirection node */ 4647139Sbostic char *eofmark; /* string indicating end of input */ 4747139Sbostic int striptabs; /* if set, strip leading tabs */ 4847139Sbostic }; 4947139Sbostic 5047139Sbostic 5147139Sbostic 5247139Sbostic struct heredoc *heredoclist; /* list of here documents to read */ 5347139Sbostic int parsebackquote; /* nonzero if we are inside backquotes */ 5447139Sbostic int doprompt; /* if set, prompt the user */ 5547139Sbostic int needprompt; /* true if interactive and at start of line */ 5647139Sbostic int lasttoken; /* last token read */ 5747139Sbostic MKINIT int tokpushback; /* last token pushed back */ 5847139Sbostic char *wordtext; /* text of last word returned by readtoken */ 5954322Smarc MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ 6047139Sbostic struct nodelist *backquotelist; 6147139Sbostic union node *redirnode; 6247139Sbostic struct heredoc *heredoc; 6347139Sbostic int quoteflag; /* set if (part of) last token was quoted */ 6447139Sbostic int startlinno; /* line # where last token started */ 6547139Sbostic 6647139Sbostic 6747139Sbostic #define GDB_HACK 1 /* avoid local declarations which gdb can't handle */ 6847139Sbostic #ifdef GDB_HACK 6947139Sbostic static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'}; 7047139Sbostic static const char types[] = "}-+?="; 7147139Sbostic #endif 7247139Sbostic 7347139Sbostic 7447981Smarc STATIC union node *list __P((int)); 7547981Smarc STATIC union node *andor __P((void)); 7647981Smarc STATIC union node *pipeline __P((void)); 7747981Smarc STATIC union node *command __P((void)); 7847981Smarc STATIC union node *simplecmd __P((void)); 7947981Smarc STATIC void parsefname __P((void)); 8047981Smarc STATIC void parseheredoc __P((void)); 8147981Smarc STATIC int readtoken __P((void)); 8247981Smarc STATIC int readtoken1 __P((int, char const *, char *, int)); 8347981Smarc STATIC void attyline __P((void)); 8447981Smarc STATIC int noexpand __P((char *)); 8547981Smarc STATIC void synexpect __P((int)); 8647981Smarc STATIC void synerror __P((char *)); 8754322Smarc STATIC void setprompt __P((int)); 8847139Sbostic 8947139Sbostic /* 9047139Sbostic * Read and parse a command. Returns NEOF on end of file. (NULL is a 9147139Sbostic * valid parse tree indicating a blank line.) 9247139Sbostic */ 9347139Sbostic 9447139Sbostic union node * 9547139Sbostic parsecmd(interact) { 9647139Sbostic int t; 9747139Sbostic 9847139Sbostic doprompt = interact; 9947139Sbostic if (doprompt) 10054322Smarc setprompt(1); 10154322Smarc else 10254322Smarc setprompt(0); 10347139Sbostic needprompt = 0; 10454322Smarc t = readtoken(); 10554322Smarc if (t == TEOF) 10647139Sbostic return NEOF; 10747139Sbostic if (t == TNL) 10847139Sbostic return NULL; 10947139Sbostic tokpushback++; 11047139Sbostic return list(1); 11147139Sbostic } 11247139Sbostic 11347139Sbostic 11447139Sbostic STATIC union node * 11547139Sbostic list(nlflag) { 11647139Sbostic union node *n1, *n2, *n3; 11747139Sbostic 11847981Smarc checkkwd = 2; 11947981Smarc if (nlflag == 0 && tokendlist[peektoken()]) 12047139Sbostic return NULL; 12147139Sbostic n1 = andor(); 12247139Sbostic for (;;) { 12347139Sbostic switch (readtoken()) { 12447139Sbostic case TBACKGND: 12547139Sbostic if (n1->type == NCMD || n1->type == NPIPE) { 12647139Sbostic n1->ncmd.backgnd = 1; 12747139Sbostic } else if (n1->type == NREDIR) { 12847139Sbostic n1->type = NBACKGND; 12947139Sbostic } else { 13047139Sbostic n3 = (union node *)stalloc(sizeof (struct nredir)); 13147139Sbostic n3->type = NBACKGND; 13247139Sbostic n3->nredir.n = n1; 13347139Sbostic n3->nredir.redirect = NULL; 13447139Sbostic n1 = n3; 13547139Sbostic } 13647139Sbostic goto tsemi; 13747139Sbostic case TNL: 13847139Sbostic tokpushback++; 13947139Sbostic /* fall through */ 14047139Sbostic tsemi: case TSEMI: 14147139Sbostic if (readtoken() == TNL) { 14247139Sbostic parseheredoc(); 14347139Sbostic if (nlflag) 14447139Sbostic return n1; 14547139Sbostic } else { 14647139Sbostic tokpushback++; 14747139Sbostic } 14847981Smarc checkkwd = 2; 14947981Smarc if (tokendlist[peektoken()]) 15047139Sbostic return n1; 15147139Sbostic n2 = andor(); 15247139Sbostic n3 = (union node *)stalloc(sizeof (struct nbinary)); 15347139Sbostic n3->type = NSEMI; 15447139Sbostic n3->nbinary.ch1 = n1; 15547139Sbostic n3->nbinary.ch2 = n2; 15647139Sbostic n1 = n3; 15747139Sbostic break; 15847139Sbostic case TEOF: 15947139Sbostic if (heredoclist) 16047139Sbostic parseheredoc(); 16147139Sbostic else 16247139Sbostic pungetc(); /* push back EOF on input */ 16347139Sbostic return n1; 16447139Sbostic default: 16547139Sbostic if (nlflag) 16647139Sbostic synexpect(-1); 16747139Sbostic tokpushback++; 16847139Sbostic return n1; 16947139Sbostic } 17047139Sbostic } 17147139Sbostic } 17247139Sbostic 17347139Sbostic 17447139Sbostic 17547139Sbostic STATIC union node * 17647139Sbostic andor() { 17747139Sbostic union node *n1, *n2, *n3; 17847139Sbostic int t; 17947139Sbostic 18047139Sbostic n1 = pipeline(); 18147139Sbostic for (;;) { 18247139Sbostic if ((t = readtoken()) == TAND) { 18347139Sbostic t = NAND; 18447139Sbostic } else if (t == TOR) { 18547139Sbostic t = NOR; 18647139Sbostic } else { 18747139Sbostic tokpushback++; 18847139Sbostic return n1; 18947139Sbostic } 19047139Sbostic n2 = pipeline(); 19147139Sbostic n3 = (union node *)stalloc(sizeof (struct nbinary)); 19247139Sbostic n3->type = t; 19347139Sbostic n3->nbinary.ch1 = n1; 19447139Sbostic n3->nbinary.ch2 = n2; 19547139Sbostic n1 = n3; 19647139Sbostic } 19747139Sbostic } 19847139Sbostic 19947139Sbostic 20047139Sbostic 20147139Sbostic STATIC union node * 20247139Sbostic pipeline() { 20353178Smarc union node *n1, *pipenode, *notnode; 20447139Sbostic struct nodelist *lp, *prev; 20553178Smarc int negate = 0; 20647139Sbostic 20753178Smarc TRACE(("pipeline: entered\n")); 20853178Smarc while (readtoken() == TNOT) { 20953178Smarc TRACE(("pipeline: TNOT recognized\n")); 21053178Smarc negate = !negate; 21153178Smarc } 21253178Smarc tokpushback++; 21347139Sbostic n1 = command(); 21447139Sbostic if (readtoken() == TPIPE) { 21547139Sbostic pipenode = (union node *)stalloc(sizeof (struct npipe)); 21647139Sbostic pipenode->type = NPIPE; 21747139Sbostic pipenode->npipe.backgnd = 0; 21847139Sbostic lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); 21947139Sbostic pipenode->npipe.cmdlist = lp; 22047139Sbostic lp->n = n1; 22147139Sbostic do { 22247139Sbostic prev = lp; 22347139Sbostic lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); 22447139Sbostic lp->n = command(); 22547139Sbostic prev->next = lp; 22647139Sbostic } while (readtoken() == TPIPE); 22747139Sbostic lp->next = NULL; 22847139Sbostic n1 = pipenode; 22947139Sbostic } 23047139Sbostic tokpushback++; 23153178Smarc if (negate) { 23253178Smarc notnode = (union node *)stalloc(sizeof (struct nnot)); 23353178Smarc notnode->type = NNOT; 23453178Smarc notnode->nnot.com = n1; 23553178Smarc n1 = notnode; 23653178Smarc } 23747139Sbostic return n1; 23847139Sbostic } 23947139Sbostic 24047139Sbostic 24147139Sbostic 24247139Sbostic STATIC union node * 24347139Sbostic command() { 24447139Sbostic union node *n1, *n2; 24547139Sbostic union node *ap, **app; 24647139Sbostic union node *cp, **cpp; 24747139Sbostic union node *redir, **rpp; 24847139Sbostic int t; 24947139Sbostic 25047981Smarc checkkwd = 2; 25147139Sbostic switch (readtoken()) { 25247139Sbostic case TIF: 25347139Sbostic n1 = (union node *)stalloc(sizeof (struct nif)); 25447139Sbostic n1->type = NIF; 25547139Sbostic n1->nif.test = list(0); 25647139Sbostic if (readtoken() != TTHEN) 25747139Sbostic synexpect(TTHEN); 25847139Sbostic n1->nif.ifpart = list(0); 25947139Sbostic n2 = n1; 26047139Sbostic while (readtoken() == TELIF) { 26147139Sbostic n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); 26247139Sbostic n2 = n2->nif.elsepart; 26347139Sbostic n2->type = NIF; 26447139Sbostic n2->nif.test = list(0); 26547139Sbostic if (readtoken() != TTHEN) 26647139Sbostic synexpect(TTHEN); 26747139Sbostic n2->nif.ifpart = list(0); 26847139Sbostic } 26947139Sbostic if (lasttoken == TELSE) 27047139Sbostic n2->nif.elsepart = list(0); 27147139Sbostic else { 27247139Sbostic n2->nif.elsepart = NULL; 27347139Sbostic tokpushback++; 27447139Sbostic } 27547139Sbostic if (readtoken() != TFI) 27647139Sbostic synexpect(TFI); 27747981Smarc checkkwd = 1; 27847139Sbostic break; 27947139Sbostic case TWHILE: 28047981Smarc case TUNTIL: { 28147981Smarc int got; 28247139Sbostic n1 = (union node *)stalloc(sizeof (struct nbinary)); 28347139Sbostic n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; 28447139Sbostic n1->nbinary.ch1 = list(0); 28547981Smarc if ((got=readtoken()) != TDO) { 28647981Smarc TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); 28747139Sbostic synexpect(TDO); 28847981Smarc } 28947139Sbostic n1->nbinary.ch2 = list(0); 29047139Sbostic if (readtoken() != TDONE) 29147139Sbostic synexpect(TDONE); 29247981Smarc checkkwd = 1; 29347139Sbostic break; 29447981Smarc } 29547139Sbostic case TFOR: 29647139Sbostic if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) 29747139Sbostic synerror("Bad for loop variable"); 29847139Sbostic n1 = (union node *)stalloc(sizeof (struct nfor)); 29947139Sbostic n1->type = NFOR; 30047139Sbostic n1->nfor.var = wordtext; 30147139Sbostic if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { 30247139Sbostic app = ≈ 30347139Sbostic while (readtoken() == TWORD) { 30447139Sbostic n2 = (union node *)stalloc(sizeof (struct narg)); 30547139Sbostic n2->type = NARG; 30647139Sbostic n2->narg.text = wordtext; 30747139Sbostic n2->narg.backquote = backquotelist; 30847139Sbostic *app = n2; 30947139Sbostic app = &n2->narg.next; 31047139Sbostic } 31147139Sbostic *app = NULL; 31247139Sbostic n1->nfor.args = ap; 313*59178Storek if (lasttoken != TNL && lasttoken != TSEMI) 314*59178Storek synexpect(-1); 31547139Sbostic } else { 31647139Sbostic #ifndef GDB_HACK 31747139Sbostic static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, 31847139Sbostic '@', '=', '\0'}; 31947139Sbostic #endif 32047139Sbostic n2 = (union node *)stalloc(sizeof (struct narg)); 32147139Sbostic n2->type = NARG; 32247139Sbostic n2->narg.text = (char *)argvars; 32347139Sbostic n2->narg.backquote = NULL; 32447139Sbostic n2->narg.next = NULL; 32547139Sbostic n1->nfor.args = n2; 326*59178Storek /* 327*59178Storek * Newline or semicolon here is optional (but note 328*59178Storek * that the original Bourne shell only allowed NL). 329*59178Storek */ 330*59178Storek if (lasttoken != TNL && lasttoken != TSEMI) 331*59178Storek tokpushback++; 33247139Sbostic } 33347981Smarc checkkwd = 2; 33447139Sbostic if ((t = readtoken()) == TDO) 33547139Sbostic t = TDONE; 33647139Sbostic else if (t == TBEGIN) 33747139Sbostic t = TEND; 33847139Sbostic else 33947139Sbostic synexpect(-1); 34047139Sbostic n1->nfor.body = list(0); 34147139Sbostic if (readtoken() != t) 34247139Sbostic synexpect(t); 34347981Smarc checkkwd = 1; 34447139Sbostic break; 34547139Sbostic case TCASE: 34647139Sbostic n1 = (union node *)stalloc(sizeof (struct ncase)); 34747139Sbostic n1->type = NCASE; 34847139Sbostic if (readtoken() != TWORD) 34947139Sbostic synexpect(TWORD); 35047139Sbostic n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); 35147139Sbostic n2->type = NARG; 35247139Sbostic n2->narg.text = wordtext; 35347139Sbostic n2->narg.backquote = backquotelist; 35447139Sbostic n2->narg.next = NULL; 35547139Sbostic while (readtoken() == TNL); 35647139Sbostic if (lasttoken != TWORD || ! equal(wordtext, "in")) 35747139Sbostic synerror("expecting \"in\""); 35847139Sbostic cpp = &n1->ncase.cases; 35947981Smarc while (checkkwd = 2, readtoken() == TWORD) { 36047139Sbostic *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); 36147139Sbostic cp->type = NCLIST; 36247139Sbostic app = &cp->nclist.pattern; 36347139Sbostic for (;;) { 36447139Sbostic *app = ap = (union node *)stalloc(sizeof (struct narg)); 36547139Sbostic ap->type = NARG; 36647139Sbostic ap->narg.text = wordtext; 36747139Sbostic ap->narg.backquote = backquotelist; 36847139Sbostic if (readtoken() != TPIPE) 36947139Sbostic break; 37047139Sbostic app = &ap->narg.next; 37147139Sbostic if (readtoken() != TWORD) 37247139Sbostic synexpect(TWORD); 37347139Sbostic } 37447139Sbostic ap->narg.next = NULL; 37547139Sbostic if (lasttoken != TRP) 37647139Sbostic synexpect(TRP); 37747139Sbostic cp->nclist.body = list(0); 37847139Sbostic if ((t = readtoken()) == TESAC) 37947139Sbostic tokpushback++; 38047139Sbostic else if (t != TENDCASE) 38147139Sbostic synexpect(TENDCASE); 38247139Sbostic cpp = &cp->nclist.next; 38347139Sbostic } 38447139Sbostic *cpp = NULL; 38547139Sbostic if (lasttoken != TESAC) 38647139Sbostic synexpect(TESAC); 38747981Smarc checkkwd = 1; 38847139Sbostic break; 38947139Sbostic case TLP: 39047139Sbostic n1 = (union node *)stalloc(sizeof (struct nredir)); 39147139Sbostic n1->type = NSUBSHELL; 39247139Sbostic n1->nredir.n = list(0); 39347139Sbostic n1->nredir.redirect = NULL; 39447139Sbostic if (readtoken() != TRP) 39547139Sbostic synexpect(TRP); 39647981Smarc checkkwd = 1; 39747139Sbostic break; 39847139Sbostic case TBEGIN: 39947139Sbostic n1 = list(0); 40047139Sbostic if (readtoken() != TEND) 40147139Sbostic synexpect(TEND); 40247981Smarc checkkwd = 1; 40347139Sbostic break; 40447139Sbostic case TWORD: 40547139Sbostic case TREDIR: 40647139Sbostic tokpushback++; 40747139Sbostic return simplecmd(); 40847139Sbostic default: 40947139Sbostic synexpect(-1); 41047139Sbostic } 41147139Sbostic 41247139Sbostic /* Now check for redirection which may follow command */ 41347139Sbostic rpp = &redir; 41447139Sbostic while (readtoken() == TREDIR) { 41547139Sbostic *rpp = n2 = redirnode; 41647139Sbostic rpp = &n2->nfile.next; 41747139Sbostic parsefname(); 41847139Sbostic } 41947139Sbostic tokpushback++; 42047139Sbostic *rpp = NULL; 42147139Sbostic if (redir) { 42247139Sbostic if (n1->type != NSUBSHELL) { 42347139Sbostic n2 = (union node *)stalloc(sizeof (struct nredir)); 42447139Sbostic n2->type = NREDIR; 42547139Sbostic n2->nredir.n = n1; 42647139Sbostic n1 = n2; 42747139Sbostic } 42847139Sbostic n1->nredir.redirect = redir; 42947139Sbostic } 43047139Sbostic return n1; 43147139Sbostic } 43247139Sbostic 43347139Sbostic 43447139Sbostic STATIC union node * 43547139Sbostic simplecmd() { 43647139Sbostic union node *args, **app; 43747139Sbostic union node *redir, **rpp; 43847139Sbostic union node *n; 43947139Sbostic 44047139Sbostic args = NULL; 44147139Sbostic app = &args; 44247139Sbostic rpp = &redir; 44347139Sbostic for (;;) { 44447139Sbostic if (readtoken() == TWORD) { 44547139Sbostic n = (union node *)stalloc(sizeof (struct narg)); 44647139Sbostic n->type = NARG; 44747139Sbostic n->narg.text = wordtext; 44847139Sbostic n->narg.backquote = backquotelist; 44947139Sbostic *app = n; 45047139Sbostic app = &n->narg.next; 45147139Sbostic } else if (lasttoken == TREDIR) { 45247139Sbostic *rpp = n = redirnode; 45347139Sbostic rpp = &n->nfile.next; 45447139Sbostic parsefname(); /* read name of redirection file */ 45547139Sbostic } else if (lasttoken == TLP && app == &args->narg.next 45647139Sbostic && rpp == &redir) { 45747139Sbostic /* We have a function */ 45847139Sbostic if (readtoken() != TRP) 45947139Sbostic synexpect(TRP); 46047300Smarc #ifdef notdef 46147139Sbostic if (! goodname(n->narg.text)) 46247139Sbostic synerror("Bad function name"); 46347300Smarc #endif 46447139Sbostic n->type = NDEFUN; 46547139Sbostic n->narg.next = command(); 46647139Sbostic return n; 46747139Sbostic } else { 46847139Sbostic tokpushback++; 46947139Sbostic break; 47047139Sbostic } 47147139Sbostic } 47247139Sbostic *app = NULL; 47347139Sbostic *rpp = NULL; 47447139Sbostic n = (union node *)stalloc(sizeof (struct ncmd)); 47547139Sbostic n->type = NCMD; 47647139Sbostic n->ncmd.backgnd = 0; 47747139Sbostic n->ncmd.args = args; 47847139Sbostic n->ncmd.redirect = redir; 47947139Sbostic return n; 48047139Sbostic } 48147139Sbostic 48247139Sbostic 48347139Sbostic STATIC void 48447139Sbostic parsefname() { 48547139Sbostic union node *n = redirnode; 48647139Sbostic 48747139Sbostic if (readtoken() != TWORD) 48847139Sbostic synexpect(-1); 48947139Sbostic if (n->type == NHERE) { 49047139Sbostic struct heredoc *here = heredoc; 49147139Sbostic struct heredoc *p; 49247139Sbostic int i; 49347139Sbostic 49447139Sbostic if (quoteflag == 0) 49547139Sbostic n->type = NXHERE; 49647139Sbostic TRACE(("Here document %d\n", n->type)); 49747139Sbostic if (here->striptabs) { 49847139Sbostic while (*wordtext == '\t') 49947139Sbostic wordtext++; 50047139Sbostic } 50147139Sbostic if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) 50247139Sbostic synerror("Illegal eof marker for << redirection"); 50347139Sbostic rmescapes(wordtext); 50447139Sbostic here->eofmark = wordtext; 50547139Sbostic here->next = NULL; 50647139Sbostic if (heredoclist == NULL) 50747139Sbostic heredoclist = here; 50847139Sbostic else { 50947139Sbostic for (p = heredoclist ; p->next ; p = p->next); 51047139Sbostic p->next = here; 51147139Sbostic } 51247139Sbostic } else if (n->type == NTOFD || n->type == NFROMFD) { 51347139Sbostic if (is_digit(wordtext[0])) 51447139Sbostic n->ndup.dupfd = digit_val(wordtext[0]); 51547139Sbostic else if (wordtext[0] == '-') 51647139Sbostic n->ndup.dupfd = -1; 51747139Sbostic else 51847139Sbostic goto bad; 51947139Sbostic if (wordtext[1] != '\0') { 52047139Sbostic bad: 52147139Sbostic synerror("Bad fd number"); 52247139Sbostic } 52347139Sbostic } else { 52447139Sbostic n->nfile.fname = (union node *)stalloc(sizeof (struct narg)); 52547139Sbostic n = n->nfile.fname; 52647139Sbostic n->type = NARG; 52747139Sbostic n->narg.next = NULL; 52847139Sbostic n->narg.text = wordtext; 52947139Sbostic n->narg.backquote = backquotelist; 53047139Sbostic } 53147139Sbostic } 53247139Sbostic 53347139Sbostic 53447139Sbostic /* 53547139Sbostic * Input any here documents. 53647139Sbostic */ 53747139Sbostic 53847139Sbostic STATIC void 53947139Sbostic parseheredoc() { 54047139Sbostic struct heredoc *here; 54147139Sbostic union node *n; 54247139Sbostic 54347139Sbostic while (heredoclist) { 54447139Sbostic here = heredoclist; 54547139Sbostic heredoclist = here->next; 54647139Sbostic if (needprompt) { 54754322Smarc setprompt(2); 54847139Sbostic needprompt = 0; 54947139Sbostic } 55047139Sbostic readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, 55147139Sbostic here->eofmark, here->striptabs); 55247139Sbostic n = (union node *)stalloc(sizeof (struct narg)); 55347139Sbostic n->narg.type = NARG; 55447139Sbostic n->narg.next = NULL; 55547139Sbostic n->narg.text = wordtext; 55647139Sbostic n->narg.backquote = backquotelist; 55747139Sbostic here->here->nhere.doc = n; 55847139Sbostic } 55947139Sbostic } 56047139Sbostic 56147981Smarc STATIC int 56247981Smarc peektoken() { 56347139Sbostic int t; 56447139Sbostic 56547981Smarc t = readtoken(); 56647139Sbostic tokpushback++; 56747981Smarc return (t); 56847139Sbostic } 56947139Sbostic 57047139Sbostic STATIC int xxreadtoken(); 57147139Sbostic 57247139Sbostic STATIC int 57347139Sbostic readtoken() { 57447139Sbostic int t; 57554322Smarc int savecheckkwd = checkkwd; 57654322Smarc struct alias *ap; 57747981Smarc #ifdef DEBUG 57847981Smarc int alreadyseen = tokpushback; 57947981Smarc #endif 58047981Smarc 58154322Smarc top: 58247981Smarc t = xxreadtoken(); 58347139Sbostic 58447981Smarc if (checkkwd) { 58547981Smarc /* 58647981Smarc * eat newlines 58747981Smarc */ 58847981Smarc if (checkkwd == 2) { 58947981Smarc checkkwd = 0; 59047981Smarc while (t == TNL) { 59147981Smarc parseheredoc(); 59247981Smarc t = xxreadtoken(); 59347981Smarc } 59447981Smarc } else 59547981Smarc checkkwd = 0; 59647981Smarc /* 59754322Smarc * check for keywords and aliases 59847981Smarc */ 59947981Smarc if (t == TWORD && !quoteflag) { 60054322Smarc register char * const *pp, *s; 60147981Smarc 60247981Smarc for (pp = parsekwd; *pp; pp++) { 60347981Smarc if (**pp == *wordtext && equal(*pp, wordtext)) { 60447981Smarc lasttoken = t = pp - parsekwd + KWDOFFSET; 60547981Smarc TRACE(("keyword %s recognized\n", tokname[t])); 60654322Smarc goto out; 60747981Smarc } 60847981Smarc } 60954322Smarc if (ap = lookupalias(wordtext, 1)) { 61054322Smarc pushstring(ap->val, strlen(ap->val), ap); 61154322Smarc checkkwd = savecheckkwd; 61254322Smarc goto top; 61354322Smarc } 61447981Smarc } 61554322Smarc out: 61654322Smarc checkkwd = 0; 61747139Sbostic } 61847981Smarc #ifdef DEBUG 61947981Smarc if (!alreadyseen) 62047981Smarc TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); 62147981Smarc else 62247981Smarc TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); 62347981Smarc #endif 62447981Smarc return (t); 62547139Sbostic } 62647139Sbostic 62747139Sbostic 62847139Sbostic /* 62947139Sbostic * Read the next input token. 63047139Sbostic * If the token is a word, we set backquotelist to the list of cmds in 63147139Sbostic * backquotes. We set quoteflag to true if any part of the word was 63247139Sbostic * quoted. 63347139Sbostic * If the token is TREDIR, then we set redirnode to a structure containing 63447139Sbostic * the redirection. 63547139Sbostic * In all cases, the variable startlinno is set to the number of the line 63647139Sbostic * on which the token starts. 63747139Sbostic * 63847139Sbostic * [Change comment: here documents and internal procedures] 63947139Sbostic * [Readtoken shouldn't have any arguments. Perhaps we should make the 64047139Sbostic * word parsing code into a separate routine. In this case, readtoken 64147139Sbostic * doesn't need to have any internal procedures, but parseword does. 64247139Sbostic * We could also make parseoperator in essence the main routine, and 64347139Sbostic * have parseword (readtoken1?) handle both words and redirection.] 64447139Sbostic */ 64547139Sbostic 64647139Sbostic #define RETURN(token) return lasttoken = token 64747139Sbostic 64847139Sbostic STATIC int 64947139Sbostic xxreadtoken() { 65047139Sbostic register c; 65147139Sbostic 65247139Sbostic if (tokpushback) { 65347139Sbostic tokpushback = 0; 65447139Sbostic return lasttoken; 65547139Sbostic } 65647139Sbostic if (needprompt) { 65754322Smarc setprompt(2); 65847139Sbostic needprompt = 0; 65947139Sbostic } 66047139Sbostic startlinno = plinno; 66147139Sbostic for (;;) { /* until token or start of word found */ 66247139Sbostic c = pgetc_macro(); 66347139Sbostic if (c == ' ' || c == '\t') 66447139Sbostic continue; /* quick check for white space first */ 66547139Sbostic switch (c) { 66647139Sbostic case ' ': case '\t': 66747139Sbostic continue; 66847139Sbostic case '#': 66947139Sbostic while ((c = pgetc()) != '\n' && c != PEOF); 67047139Sbostic pungetc(); 67147139Sbostic continue; 67247139Sbostic case '\\': 67347139Sbostic if (pgetc() == '\n') { 67447139Sbostic startlinno = ++plinno; 67547139Sbostic if (doprompt) 67654322Smarc setprompt(2); 67754322Smarc else 67854322Smarc setprompt(0); 67947139Sbostic continue; 68047139Sbostic } 68147139Sbostic pungetc(); 68247139Sbostic goto breakloop; 68347139Sbostic case '\n': 68447139Sbostic plinno++; 68547139Sbostic needprompt = doprompt; 68647139Sbostic RETURN(TNL); 68747139Sbostic case PEOF: 68847139Sbostic RETURN(TEOF); 68947139Sbostic case '&': 69047139Sbostic if (pgetc() == '&') 69147139Sbostic RETURN(TAND); 69247139Sbostic pungetc(); 69347139Sbostic RETURN(TBACKGND); 69447139Sbostic case '|': 69547139Sbostic if (pgetc() == '|') 69647139Sbostic RETURN(TOR); 69747139Sbostic pungetc(); 69847139Sbostic RETURN(TPIPE); 69947139Sbostic case ';': 70047139Sbostic if (pgetc() == ';') 70147139Sbostic RETURN(TENDCASE); 70247139Sbostic pungetc(); 70347139Sbostic RETURN(TSEMI); 70447139Sbostic case '(': 70547139Sbostic RETURN(TLP); 70647139Sbostic case ')': 70747139Sbostic RETURN(TRP); 70847139Sbostic default: 70947139Sbostic goto breakloop; 71047139Sbostic } 71147139Sbostic } 71247139Sbostic breakloop: 71347139Sbostic return readtoken1(c, BASESYNTAX, (char *)NULL, 0); 71447139Sbostic #undef RETURN 71547139Sbostic } 71647139Sbostic 71747139Sbostic 71847139Sbostic 71947139Sbostic /* 72047139Sbostic * If eofmark is NULL, read a word or a redirection symbol. If eofmark 72147139Sbostic * is not NULL, read a here document. In the latter case, eofmark is the 72247139Sbostic * word which marks the end of the document and striptabs is true if 72347139Sbostic * leading tabs should be stripped from the document. The argument firstc 72447139Sbostic * is the first character of the input token or document. 72547139Sbostic * 72647139Sbostic * Because C does not have internal subroutines, I have simulated them 72747139Sbostic * using goto's to implement the subroutine linkage. The following macros 72847139Sbostic * will run code that appears at the end of readtoken1. 72947139Sbostic */ 73047139Sbostic 73147139Sbostic #define CHECKEND() {goto checkend; checkend_return:;} 73247139Sbostic #define PARSEREDIR() {goto parseredir; parseredir_return:;} 73347139Sbostic #define PARSESUB() {goto parsesub; parsesub_return:;} 73447139Sbostic #define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} 73547139Sbostic #define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} 73653302Smarc #define PARSEARITH() {goto parsearith; parsearith_return:;} 73747139Sbostic 73847139Sbostic STATIC int 73947139Sbostic readtoken1(firstc, syntax, eofmark, striptabs) 74047139Sbostic int firstc; 74147139Sbostic char const *syntax; 74247139Sbostic char *eofmark; 74347139Sbostic int striptabs; 74447139Sbostic { 74547139Sbostic register c = firstc; 74647139Sbostic register char *out; 74747139Sbostic int len; 74847139Sbostic char line[EOFMARKLEN + 1]; 74947139Sbostic struct nodelist *bqlist; 75047139Sbostic int quotef; 75147139Sbostic int dblquote; 75253302Smarc int varnest; /* levels of variables expansion */ 75353302Smarc int arinest; /* levels of arithmetic expansion */ 75453302Smarc int parenlevel; /* levels of parens in arithmetic */ 75547139Sbostic int oldstyle; 75653302Smarc char const *prevsyntax; /* syntax before arithmetic */ 75747139Sbostic 75847139Sbostic startlinno = plinno; 75947139Sbostic dblquote = 0; 76047139Sbostic if (syntax == DQSYNTAX) 76147139Sbostic dblquote = 1; 76247139Sbostic quotef = 0; 76347139Sbostic bqlist = NULL; 76447139Sbostic varnest = 0; 76553302Smarc arinest = 0; 76653302Smarc parenlevel = 0; 76753302Smarc 76847139Sbostic STARTSTACKSTR(out); 76947139Sbostic loop: { /* for each line, until end of word */ 77047139Sbostic #if ATTY 77147139Sbostic if (c == '\034' && doprompt 77247139Sbostic && attyset() && ! equal(termval(), "emacs")) { 77347139Sbostic attyline(); 77447139Sbostic if (syntax == BASESYNTAX) 77547139Sbostic return readtoken(); 77647139Sbostic c = pgetc(); 77747139Sbostic goto loop; 77847139Sbostic } 77947139Sbostic #endif 78047139Sbostic CHECKEND(); /* set c to PEOF if at end of here document */ 78147139Sbostic for (;;) { /* until end of line or end of word */ 78247139Sbostic CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ 78359177Smarc if (parsebackquote && c == '\\') { 78458381Smarc c = pgetc(); /* XXX - compat with old /bin/sh */ 78559177Smarc if (c != '\\' && c != '`' && c != '$') { 78659177Smarc pungetc(); 78759177Smarc c = '\\'; 78859177Smarc } 78959177Smarc } 79047139Sbostic switch(syntax[c]) { 79147139Sbostic case CNL: /* '\n' */ 79247139Sbostic if (syntax == BASESYNTAX) 79347139Sbostic goto endword; /* exit outer loop */ 79447139Sbostic USTPUTC(c, out); 79547139Sbostic plinno++; 79654322Smarc if (doprompt) 79754322Smarc setprompt(2); 79854322Smarc else 79954322Smarc setprompt(0); 80047139Sbostic c = pgetc(); 80147139Sbostic goto loop; /* continue outer loop */ 80247139Sbostic case CWORD: 80347139Sbostic USTPUTC(c, out); 80447139Sbostic break; 80547139Sbostic case CCTL: 80647139Sbostic if (eofmark == NULL || dblquote) 80747139Sbostic USTPUTC(CTLESC, out); 80847139Sbostic USTPUTC(c, out); 80947139Sbostic break; 81047139Sbostic case CBACK: /* backslash */ 81147139Sbostic c = pgetc(); 81247139Sbostic if (c == PEOF) { 81347139Sbostic USTPUTC('\\', out); 81447139Sbostic pungetc(); 81547139Sbostic } else if (c == '\n') { 81647139Sbostic if (doprompt) 81754322Smarc setprompt(2); 81854322Smarc else 81954322Smarc setprompt(0); 82047139Sbostic } else { 82147139Sbostic if (dblquote && c != '\\' && c != '`' && c != '$' 82247139Sbostic && (c != '"' || eofmark != NULL)) 82347139Sbostic USTPUTC('\\', out); 82447139Sbostic if (SQSYNTAX[c] == CCTL) 82547139Sbostic USTPUTC(CTLESC, out); 82647139Sbostic USTPUTC(c, out); 82747139Sbostic quotef++; 82847139Sbostic } 82947139Sbostic break; 83047139Sbostic case CSQUOTE: 83147139Sbostic syntax = SQSYNTAX; 83247139Sbostic break; 83347139Sbostic case CDQUOTE: 83447139Sbostic syntax = DQSYNTAX; 83547139Sbostic dblquote = 1; 83647139Sbostic break; 83747139Sbostic case CENDQUOTE: 83847139Sbostic if (eofmark) { 83947139Sbostic USTPUTC(c, out); 84047139Sbostic } else { 84153302Smarc if (arinest) 84253302Smarc syntax = ARISYNTAX; 84353302Smarc else 84453302Smarc syntax = BASESYNTAX; 84547139Sbostic quotef++; 84647139Sbostic dblquote = 0; 84747139Sbostic } 84847139Sbostic break; 84947139Sbostic case CVAR: /* '$' */ 85047139Sbostic PARSESUB(); /* parse substitution */ 85147139Sbostic break; 85247139Sbostic case CENDVAR: /* '}' */ 85347139Sbostic if (varnest > 0) { 85447139Sbostic varnest--; 85547139Sbostic USTPUTC(CTLENDVAR, out); 85647139Sbostic } else { 85747139Sbostic USTPUTC(c, out); 85847139Sbostic } 85947139Sbostic break; 86053302Smarc case CLP: /* '(' in arithmetic */ 86153302Smarc parenlevel++; 86253302Smarc USTPUTC(c, out); 86353302Smarc break; 86453302Smarc case CRP: /* ')' in arithmetic */ 86553302Smarc if (parenlevel > 0) { 86653302Smarc USTPUTC(c, out); 86753302Smarc --parenlevel; 86853302Smarc } else { 86953302Smarc if (pgetc() == ')') { 87053302Smarc if (--arinest == 0) { 87153302Smarc USTPUTC(CTLENDARI, out); 87253302Smarc syntax = prevsyntax; 87353302Smarc } else 87453302Smarc USTPUTC(')', out); 87553302Smarc } else { 87653302Smarc /* 87753302Smarc * unbalanced parens 87853302Smarc * (don't 2nd guess - no error) 87953302Smarc */ 88053302Smarc pungetc(); 88153302Smarc USTPUTC(')', out); 88253302Smarc } 88353302Smarc } 88453302Smarc break; 88547139Sbostic case CBQUOTE: /* '`' */ 88647139Sbostic if (parsebackquote && syntax == BASESYNTAX) { 88747139Sbostic if (out == stackblock()) 88847139Sbostic return lasttoken = TENDBQUOTE; 88947139Sbostic else 89047139Sbostic goto endword; /* exit outer loop */ 89147139Sbostic } 89247139Sbostic PARSEBACKQOLD(); 89347139Sbostic break; 89447139Sbostic case CEOF: 89547139Sbostic goto endword; /* exit outer loop */ 89647139Sbostic default: 89747139Sbostic if (varnest == 0) 89847139Sbostic goto endword; /* exit outer loop */ 89947139Sbostic USTPUTC(c, out); 90047139Sbostic } 90147139Sbostic c = pgetc_macro(); 90247139Sbostic } 90347139Sbostic } 90447139Sbostic endword: 90553302Smarc if (syntax == ARISYNTAX) 90653302Smarc synerror("Missing '))'"); 90747139Sbostic if (syntax != BASESYNTAX && eofmark == NULL) 90847139Sbostic synerror("Unterminated quoted string"); 90947139Sbostic if (varnest != 0) { 91047139Sbostic startlinno = plinno; 91147139Sbostic synerror("Missing '}'"); 91247139Sbostic } 91347139Sbostic USTPUTC('\0', out); 91447139Sbostic len = out - stackblock(); 91547139Sbostic out = stackblock(); 91647139Sbostic if (eofmark == NULL) { 91747139Sbostic if ((c == '>' || c == '<') 91847139Sbostic && quotef == 0 91947139Sbostic && len <= 2 92047139Sbostic && (*out == '\0' || is_digit(*out))) { 92147139Sbostic PARSEREDIR(); 92247139Sbostic return lasttoken = TREDIR; 92347139Sbostic } else { 92447139Sbostic pungetc(); 92547139Sbostic } 92647139Sbostic } 92747139Sbostic quoteflag = quotef; 92847139Sbostic backquotelist = bqlist; 92947139Sbostic grabstackblock(len); 93047139Sbostic wordtext = out; 93147139Sbostic return lasttoken = TWORD; 93247139Sbostic /* end of readtoken routine */ 93347139Sbostic 93447139Sbostic 93547139Sbostic 93647139Sbostic /* 93747139Sbostic * Check to see whether we are at the end of the here document. When this 93847139Sbostic * is called, c is set to the first character of the next input line. If 93947139Sbostic * we are at the end of the here document, this routine sets the c to PEOF. 94047139Sbostic */ 94147139Sbostic 94247139Sbostic checkend: { 94347139Sbostic if (eofmark) { 94447139Sbostic if (striptabs) { 94547139Sbostic while (c == '\t') 94647139Sbostic c = pgetc(); 94747139Sbostic } 94847139Sbostic if (c == *eofmark) { 94947139Sbostic if (pfgets(line, sizeof line) != NULL) { 95047139Sbostic register char *p, *q; 95147139Sbostic 95247139Sbostic p = line; 95347139Sbostic for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); 95447139Sbostic if (*p == '\n' && *q == '\0') { 95547139Sbostic c = PEOF; 95647139Sbostic plinno++; 95747139Sbostic needprompt = doprompt; 95847139Sbostic } else { 95954322Smarc pushstring(line, strlen(line), NULL); 96047139Sbostic } 96147139Sbostic } 96247139Sbostic } 96347139Sbostic } 96447139Sbostic goto checkend_return; 96547139Sbostic } 96647139Sbostic 96747139Sbostic 96847139Sbostic /* 96947139Sbostic * Parse a redirection operator. The variable "out" points to a string 97047139Sbostic * specifying the fd to be redirected. The variable "c" contains the 97147139Sbostic * first character of the redirection operator. 97247139Sbostic */ 97347139Sbostic 97447139Sbostic parseredir: { 97547139Sbostic char fd = *out; 97647139Sbostic union node *np; 97747139Sbostic 97847139Sbostic np = (union node *)stalloc(sizeof (struct nfile)); 97947139Sbostic if (c == '>') { 98047139Sbostic np->nfile.fd = 1; 98147139Sbostic c = pgetc(); 98247139Sbostic if (c == '>') 98347139Sbostic np->type = NAPPEND; 98447139Sbostic else if (c == '&') 98547139Sbostic np->type = NTOFD; 98647139Sbostic else { 98747139Sbostic np->type = NTO; 98847139Sbostic pungetc(); 98947139Sbostic } 99047139Sbostic } else { /* c == '<' */ 99147139Sbostic np->nfile.fd = 0; 99247139Sbostic c = pgetc(); 99347139Sbostic if (c == '<') { 99447139Sbostic if (sizeof (struct nfile) != sizeof (struct nhere)) { 99547139Sbostic np = (union node *)stalloc(sizeof (struct nhere)); 99647139Sbostic np->nfile.fd = 0; 99747139Sbostic } 99847139Sbostic np->type = NHERE; 99947139Sbostic heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); 100047139Sbostic heredoc->here = np; 100147139Sbostic if ((c = pgetc()) == '-') { 100247139Sbostic heredoc->striptabs = 1; 100347139Sbostic } else { 100447139Sbostic heredoc->striptabs = 0; 100547139Sbostic pungetc(); 100647139Sbostic } 100747139Sbostic } else if (c == '&') 100847139Sbostic np->type = NFROMFD; 100947139Sbostic else { 101047139Sbostic np->type = NFROM; 101147139Sbostic pungetc(); 101247139Sbostic } 101347139Sbostic } 101447139Sbostic if (fd != '\0') 101547139Sbostic np->nfile.fd = digit_val(fd); 101647139Sbostic redirnode = np; 101747139Sbostic goto parseredir_return; 101847139Sbostic } 101947139Sbostic 102047139Sbostic 102147139Sbostic /* 102247139Sbostic * Parse a substitution. At this point, we have read the dollar sign 102347139Sbostic * and nothing else. 102447139Sbostic */ 102547139Sbostic 102647139Sbostic parsesub: { 102747139Sbostic int subtype; 102847139Sbostic int typeloc; 102947139Sbostic int flags; 103047139Sbostic char *p; 103147139Sbostic #ifndef GDB_HACK 103247139Sbostic static const char types[] = "}-+?="; 103347139Sbostic #endif 103447139Sbostic 103547139Sbostic c = pgetc(); 103647300Smarc if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { 103747139Sbostic USTPUTC('$', out); 103847139Sbostic pungetc(); 103953302Smarc } else if (c == '(') { /* $(command) or $((arith)) */ 104053302Smarc if (pgetc() == '(') { 104153302Smarc PARSEARITH(); 104253302Smarc } else { 104353302Smarc pungetc(); 104453302Smarc PARSEBACKQNEW(); 104553302Smarc } 104647139Sbostic } else { 104747139Sbostic USTPUTC(CTLVAR, out); 104847139Sbostic typeloc = out - stackblock(); 104947139Sbostic USTPUTC(VSNORMAL, out); 105047139Sbostic subtype = VSNORMAL; 105147139Sbostic if (c == '{') { 105247139Sbostic c = pgetc(); 105347139Sbostic subtype = 0; 105447139Sbostic } 105547139Sbostic if (is_name(c)) { 105647139Sbostic do { 105747139Sbostic STPUTC(c, out); 105847139Sbostic c = pgetc(); 105947139Sbostic } while (is_in_name(c)); 106047139Sbostic } else { 106147139Sbostic if (! is_special(c)) 106247300Smarc badsub: synerror("Bad substitution"); 106347139Sbostic USTPUTC(c, out); 106447139Sbostic c = pgetc(); 106547139Sbostic } 106647139Sbostic STPUTC('=', out); 106747139Sbostic flags = 0; 106847139Sbostic if (subtype == 0) { 106947139Sbostic if (c == ':') { 107047139Sbostic flags = VSNUL; 107147139Sbostic c = pgetc(); 107247139Sbostic } 107347139Sbostic p = strchr(types, c); 107447139Sbostic if (p == NULL) 107547139Sbostic goto badsub; 107647139Sbostic subtype = p - types + VSNORMAL; 107747139Sbostic } else { 107847139Sbostic pungetc(); 107947139Sbostic } 108053302Smarc if (dblquote || arinest) 108147139Sbostic flags |= VSQUOTE; 108247139Sbostic *(stackblock() + typeloc) = subtype | flags; 108347139Sbostic if (subtype != VSNORMAL) 108447139Sbostic varnest++; 108547139Sbostic } 108647139Sbostic goto parsesub_return; 108747139Sbostic } 108847139Sbostic 108947139Sbostic 109047139Sbostic /* 109147139Sbostic * Called to parse command substitutions. Newstyle is set if the command 109247139Sbostic * is enclosed inside $(...); nlpp is a pointer to the head of the linked 109347139Sbostic * list of commands (passed by reference), and savelen is the number of 109447139Sbostic * characters on the top of the stack which must be preserved. 109547139Sbostic */ 109647139Sbostic 109747139Sbostic parsebackq: { 109847139Sbostic struct nodelist **nlpp; 109947139Sbostic int savepbq; 110047139Sbostic union node *n; 110147139Sbostic char *volatile str; 110247139Sbostic struct jmploc jmploc; 110347139Sbostic struct jmploc *volatile savehandler; 110447139Sbostic int savelen; 110547139Sbostic int t; 110647139Sbostic 110747139Sbostic savepbq = parsebackquote; 110847139Sbostic if (setjmp(jmploc.loc)) { 110947139Sbostic if (str) 111047139Sbostic ckfree(str); 111147139Sbostic parsebackquote = 0; 111247139Sbostic handler = savehandler; 111354322Smarc longjmp(handler->loc, 1); 111447139Sbostic } 111547139Sbostic INTOFF; 111647139Sbostic str = NULL; 111747139Sbostic savelen = out - stackblock(); 111847139Sbostic if (savelen > 0) { 111947139Sbostic str = ckmalloc(savelen); 112047139Sbostic bcopy(stackblock(), str, savelen); 112147139Sbostic } 112247139Sbostic savehandler = handler; 112347139Sbostic handler = &jmploc; 112447139Sbostic INTON; 112547139Sbostic nlpp = &bqlist; 112647139Sbostic while (*nlpp) 112747139Sbostic nlpp = &(*nlpp)->next; 112847139Sbostic *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); 112947139Sbostic (*nlpp)->next = NULL; 113047139Sbostic parsebackquote = oldstyle; 113147139Sbostic n = list(0); 113247139Sbostic t = oldstyle? TENDBQUOTE : TRP; 113347139Sbostic if (readtoken() != t) 113447139Sbostic synexpect(t); 113547139Sbostic (*nlpp)->n = n; 113647139Sbostic while (stackblocksize() <= savelen) 113747139Sbostic growstackblock(); 113847139Sbostic STARTSTACKSTR(out); 113947139Sbostic if (str) { 114047139Sbostic bcopy(str, out, savelen); 114147139Sbostic STADJUST(savelen, out); 114247139Sbostic INTOFF; 114347139Sbostic ckfree(str); 114447139Sbostic str = NULL; 114547139Sbostic INTON; 114647139Sbostic } 114747139Sbostic parsebackquote = savepbq; 114847139Sbostic handler = savehandler; 114953302Smarc if (arinest || dblquote) 115053302Smarc USTPUTC(CTLBACKQ | CTLQUOTE, out); 115153302Smarc else 115253302Smarc USTPUTC(CTLBACKQ, out); 115347139Sbostic if (oldstyle) 115447139Sbostic goto parsebackq_oldreturn; 115547139Sbostic else 115647139Sbostic goto parsebackq_newreturn; 115747139Sbostic } 115847139Sbostic 115953302Smarc /* 116053302Smarc * Parse an arithmetic expansion (indicate start of one and set state) 116153302Smarc */ 116253302Smarc parsearith: { 116353302Smarc 116453302Smarc if (++arinest == 1) { 116553302Smarc prevsyntax = syntax; 116653302Smarc syntax = ARISYNTAX; 116753302Smarc USTPUTC(CTLARI, out); 116853302Smarc } else { 116953302Smarc /* 117053302Smarc * we collapse embedded arithmetic expansion to 117153302Smarc * parenthesis, which should be equivalent 117253302Smarc */ 117353302Smarc USTPUTC('(', out); 117453302Smarc } 117553302Smarc goto parsearith_return; 117653302Smarc } 117753302Smarc 117847139Sbostic } /* end of readtoken */ 117947139Sbostic 118047139Sbostic 118147139Sbostic 118247139Sbostic #ifdef mkinit 118347139Sbostic RESET { 118447139Sbostic tokpushback = 0; 118554322Smarc checkkwd = 0; 118647139Sbostic } 118747139Sbostic #endif 118847139Sbostic 118947139Sbostic /* 119047139Sbostic * Returns true if the text contains nothing to expand (no dollar signs 119147139Sbostic * or backquotes). 119247139Sbostic */ 119347139Sbostic 119447139Sbostic STATIC int 119547139Sbostic noexpand(text) 119647139Sbostic char *text; 119747139Sbostic { 119847139Sbostic register char *p; 119947139Sbostic register char c; 120047139Sbostic 120147139Sbostic p = text; 120247139Sbostic while ((c = *p++) != '\0') { 120347139Sbostic if (c == CTLESC) 120447139Sbostic p++; 120547139Sbostic else if (BASESYNTAX[c] == CCTL) 120647139Sbostic return 0; 120747139Sbostic } 120847139Sbostic return 1; 120947139Sbostic } 121047139Sbostic 121147139Sbostic 121247139Sbostic /* 121347139Sbostic * Return true if the argument is a legal variable name (a letter or 121447139Sbostic * underscore followed by zero or more letters, underscores, and digits). 121547139Sbostic */ 121647139Sbostic 121747139Sbostic int 121847139Sbostic goodname(name) 121947139Sbostic char *name; 122047139Sbostic { 122147139Sbostic register char *p; 122247139Sbostic 122347139Sbostic p = name; 122447139Sbostic if (! is_name(*p)) 122547139Sbostic return 0; 122647139Sbostic while (*++p) { 122747139Sbostic if (! is_in_name(*p)) 122847139Sbostic return 0; 122947139Sbostic } 123047139Sbostic return 1; 123147139Sbostic } 123247139Sbostic 123347139Sbostic 123447139Sbostic /* 123547139Sbostic * Called when an unexpected token is read during the parse. The argument 123647139Sbostic * is the token that is expected, or -1 if more than one type of token can 123747139Sbostic * occur at this point. 123847139Sbostic */ 123947139Sbostic 124047139Sbostic STATIC void 124147139Sbostic synexpect(token) { 124247139Sbostic char msg[64]; 124347139Sbostic 124447139Sbostic if (token >= 0) { 124547139Sbostic fmtstr(msg, 64, "%s unexpected (expecting %s)", 124647139Sbostic tokname[lasttoken], tokname[token]); 124747139Sbostic } else { 124847139Sbostic fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); 124947139Sbostic } 125047139Sbostic synerror(msg); 125147139Sbostic } 125247139Sbostic 125347139Sbostic 125447139Sbostic STATIC void 125547139Sbostic synerror(msg) 125647139Sbostic char *msg; 125747139Sbostic { 125847139Sbostic if (commandname) 125947139Sbostic outfmt(&errout, "%s: %d: ", commandname, startlinno); 126047139Sbostic outfmt(&errout, "Syntax error: %s\n", msg); 126147139Sbostic error((char *)NULL); 126247139Sbostic } 126354322Smarc 126454322Smarc STATIC void 126554322Smarc setprompt(which) 126654322Smarc int which; 126754322Smarc { 126854322Smarc whichprompt = which; 126954322Smarc 127054322Smarc if (!el) 127154322Smarc out2str(getprompt(NULL)); 127254322Smarc } 127354322Smarc 127454322Smarc /* 127554322Smarc * called by editline -- any expansions to the prompt 127654322Smarc * should be added here. 127754322Smarc */ 127854322Smarc char * 127954322Smarc getprompt(unused) 128054322Smarc void *unused; 128154322Smarc { 128254322Smarc switch (whichprompt) { 128354322Smarc case 0: 128454322Smarc return ""; 128554322Smarc case 1: 128654322Smarc return ps1val(); 128754322Smarc case 2: 128854322Smarc return ps2val(); 128954322Smarc default: 129054322Smarc return "<internal prompt error>"; 129154322Smarc } 129254322Smarc } 1293