386BSD 0.1 development
[unix-history] / usr / src / bin / sh / parser.c
CommitLineData
af3e82f7
WJ
1/*-
2 * Copyright (c) 1991 The Regents of the University of California.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37#ifndef lint
38static char sccsid[] = "@(#)parser.c 5.3 (Berkeley) 4/12/91";
39#endif /* not lint */
40
41#include "shell.h"
42#include "parser.h"
43#include "nodes.h"
44#include "expand.h" /* defines rmescapes() */
45#include "redir.h" /* defines copyfd() */
46#include "syntax.h"
47#include "options.h"
48#include "input.h"
49#include "output.h"
50#include "var.h"
51#include "error.h"
52#include "memalloc.h"
53#include "mystring.h"
54
55
56/*
57 * Shell command parser.
58 */
59
60#define EOFMARKLEN 79
61
62/* values returned by readtoken */
63#include "token.def"
64
65
66
67struct heredoc {
68 struct heredoc *next; /* next here document in list */
69 union node *here; /* redirection node */
70 char *eofmark; /* string indicating end of input */
71 int striptabs; /* if set, strip leading tabs */
72};
73
74
75
76struct heredoc *heredoclist; /* list of here documents to read */
77int parsebackquote; /* nonzero if we are inside backquotes */
78int doprompt; /* if set, prompt the user */
79int needprompt; /* true if interactive and at start of line */
80int lasttoken; /* last token read */
81MKINIT int tokpushback; /* last token pushed back */
82char *wordtext; /* text of last word returned by readtoken */
83int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
84struct nodelist *backquotelist;
85union node *redirnode;
86struct heredoc *heredoc;
87int quoteflag; /* set if (part of) last token was quoted */
88int startlinno; /* line # where last token started */
89
90
91#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */
92#ifdef GDB_HACK
93static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'};
94static const char types[] = "}-+?=";
95#endif
96
97
98STATIC union node *list __P((int));
99STATIC union node *andor __P((void));
100STATIC union node *pipeline __P((void));
101STATIC union node *command __P((void));
102STATIC union node *simplecmd __P((void));
103STATIC void parsefname __P((void));
104STATIC void parseheredoc __P((void));
105STATIC int readtoken __P((void));
106STATIC int readtoken1 __P((int, char const *, char *, int));
107STATIC void attyline __P((void));
108STATIC int noexpand __P((char *));
109STATIC void synexpect __P((int));
110STATIC void synerror __P((char *));
111
112#if ATTY
113STATIC void putprompt __P((char *));
114#else /* not ATTY */
115#define putprompt(s) out2str(s)
116#endif
117
118
119
120
121/*
122 * Read and parse a command. Returns NEOF on end of file. (NULL is a
123 * valid parse tree indicating a blank line.)
124 */
125
126union node *
127parsecmd(interact) {
128 int t;
129
130 doprompt = interact;
131 if (doprompt)
132 putprompt(ps1val());
133 needprompt = 0;
134 if ((t = readtoken()) == TEOF)
135 return NEOF;
136 if (t == TNL)
137 return NULL;
138 tokpushback++;
139 return list(1);
140}
141
142
143STATIC union node *
144list(nlflag) {
145 union node *n1, *n2, *n3;
146
147 checkkwd = 2;
148 if (nlflag == 0 && tokendlist[peektoken()])
149 return NULL;
150 n1 = andor();
151 for (;;) {
152 switch (readtoken()) {
153 case TBACKGND:
154 if (n1->type == NCMD || n1->type == NPIPE) {
155 n1->ncmd.backgnd = 1;
156 } else if (n1->type == NREDIR) {
157 n1->type = NBACKGND;
158 } else {
159 n3 = (union node *)stalloc(sizeof (struct nredir));
160 n3->type = NBACKGND;
161 n3->nredir.n = n1;
162 n3->nredir.redirect = NULL;
163 n1 = n3;
164 }
165 goto tsemi;
166 case TNL:
167 tokpushback++;
168 /* fall through */
169tsemi: case TSEMI:
170 if (readtoken() == TNL) {
171 parseheredoc();
172 if (nlflag)
173 return n1;
174 } else {
175 tokpushback++;
176 }
177 checkkwd = 2;
178 if (tokendlist[peektoken()])
179 return n1;
180 n2 = andor();
181 n3 = (union node *)stalloc(sizeof (struct nbinary));
182 n3->type = NSEMI;
183 n3->nbinary.ch1 = n1;
184 n3->nbinary.ch2 = n2;
185 n1 = n3;
186 break;
187 case TEOF:
188 if (heredoclist)
189 parseheredoc();
190 else
191 pungetc(); /* push back EOF on input */
192 return n1;
193 default:
194 if (nlflag)
195 synexpect(-1);
196 tokpushback++;
197 return n1;
198 }
199 }
200}
201
202
203
204STATIC union node *
205andor() {
206 union node *n1, *n2, *n3;
207 int t;
208
209 n1 = pipeline();
210 for (;;) {
211 if ((t = readtoken()) == TAND) {
212 t = NAND;
213 } else if (t == TOR) {
214 t = NOR;
215 } else {
216 tokpushback++;
217 return n1;
218 }
219 n2 = pipeline();
220 n3 = (union node *)stalloc(sizeof (struct nbinary));
221 n3->type = t;
222 n3->nbinary.ch1 = n1;
223 n3->nbinary.ch2 = n2;
224 n1 = n3;
225 }
226}
227
228
229
230STATIC union node *
231pipeline() {
232 union node *n1, *pipenode;
233 struct nodelist *lp, *prev;
234
235 n1 = command();
236 if (readtoken() == TPIPE) {
237 pipenode = (union node *)stalloc(sizeof (struct npipe));
238 pipenode->type = NPIPE;
239 pipenode->npipe.backgnd = 0;
240 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
241 pipenode->npipe.cmdlist = lp;
242 lp->n = n1;
243 do {
244 prev = lp;
245 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
246 lp->n = command();
247 prev->next = lp;
248 } while (readtoken() == TPIPE);
249 lp->next = NULL;
250 n1 = pipenode;
251 }
252 tokpushback++;
253 return n1;
254}
255
256
257
258STATIC union node *
259command() {
260 union node *n1, *n2;
261 union node *ap, **app;
262 union node *cp, **cpp;
263 union node *redir, **rpp;
264 int t;
265
266 checkkwd = 2;
267 switch (readtoken()) {
268 case TIF:
269 n1 = (union node *)stalloc(sizeof (struct nif));
270 n1->type = NIF;
271 n1->nif.test = list(0);
272 if (readtoken() != TTHEN)
273 synexpect(TTHEN);
274 n1->nif.ifpart = list(0);
275 n2 = n1;
276 while (readtoken() == TELIF) {
277 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
278 n2 = n2->nif.elsepart;
279 n2->type = NIF;
280 n2->nif.test = list(0);
281 if (readtoken() != TTHEN)
282 synexpect(TTHEN);
283 n2->nif.ifpart = list(0);
284 }
285 if (lasttoken == TELSE)
286 n2->nif.elsepart = list(0);
287 else {
288 n2->nif.elsepart = NULL;
289 tokpushback++;
290 }
291 if (readtoken() != TFI)
292 synexpect(TFI);
293 checkkwd = 1;
294 break;
295 case TWHILE:
296 case TUNTIL: {
297 int got;
298 n1 = (union node *)stalloc(sizeof (struct nbinary));
299 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
300 n1->nbinary.ch1 = list(0);
301 if ((got=readtoken()) != TDO) {
302TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
303 synexpect(TDO);
304 }
305 n1->nbinary.ch2 = list(0);
306 if (readtoken() != TDONE)
307 synexpect(TDONE);
308 checkkwd = 1;
309 break;
310 }
311 case TFOR:
312 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
313 synerror("Bad for loop variable");
314 n1 = (union node *)stalloc(sizeof (struct nfor));
315 n1->type = NFOR;
316 n1->nfor.var = wordtext;
317 if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
318 app = ≈
319 while (readtoken() == TWORD) {
320 n2 = (union node *)stalloc(sizeof (struct narg));
321 n2->type = NARG;
322 n2->narg.text = wordtext;
323 n2->narg.backquote = backquotelist;
324 *app = n2;
325 app = &n2->narg.next;
326 }
327 *app = NULL;
328 n1->nfor.args = ap;
329 } else {
330#ifndef GDB_HACK
331 static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
332 '@', '=', '\0'};
333#endif
334 n2 = (union node *)stalloc(sizeof (struct narg));
335 n2->type = NARG;
336 n2->narg.text = (char *)argvars;
337 n2->narg.backquote = NULL;
338 n2->narg.next = NULL;
339 n1->nfor.args = n2;
340 }
341 if (lasttoken != TNL && lasttoken != TSEMI)
342 synexpect(-1);
343 checkkwd = 2;
344 if ((t = readtoken()) == TDO)
345 t = TDONE;
346 else if (t == TBEGIN)
347 t = TEND;
348 else
349 synexpect(-1);
350 n1->nfor.body = list(0);
351 if (readtoken() != t)
352 synexpect(t);
353 checkkwd = 1;
354 break;
355 case TCASE:
356 n1 = (union node *)stalloc(sizeof (struct ncase));
357 n1->type = NCASE;
358 if (readtoken() != TWORD)
359 synexpect(TWORD);
360 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
361 n2->type = NARG;
362 n2->narg.text = wordtext;
363 n2->narg.backquote = backquotelist;
364 n2->narg.next = NULL;
365 while (readtoken() == TNL);
366 if (lasttoken != TWORD || ! equal(wordtext, "in"))
367 synerror("expecting \"in\"");
368 cpp = &n1->ncase.cases;
369 while (checkkwd = 2, readtoken() == TWORD) {
370 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
371 cp->type = NCLIST;
372 app = &cp->nclist.pattern;
373 for (;;) {
374 *app = ap = (union node *)stalloc(sizeof (struct narg));
375 ap->type = NARG;
376 ap->narg.text = wordtext;
377 ap->narg.backquote = backquotelist;
378 if (readtoken() != TPIPE)
379 break;
380 app = &ap->narg.next;
381 if (readtoken() != TWORD)
382 synexpect(TWORD);
383 }
384 ap->narg.next = NULL;
385 if (lasttoken != TRP)
386 synexpect(TRP);
387 cp->nclist.body = list(0);
388 if ((t = readtoken()) == TESAC)
389 tokpushback++;
390 else if (t != TENDCASE)
391 synexpect(TENDCASE);
392 cpp = &cp->nclist.next;
393 }
394 *cpp = NULL;
395 if (lasttoken != TESAC)
396 synexpect(TESAC);
397 checkkwd = 1;
398 break;
399 case TLP:
400 n1 = (union node *)stalloc(sizeof (struct nredir));
401 n1->type = NSUBSHELL;
402 n1->nredir.n = list(0);
403 n1->nredir.redirect = NULL;
404 if (readtoken() != TRP)
405 synexpect(TRP);
406 checkkwd = 1;
407 break;
408 case TBEGIN:
409 n1 = list(0);
410 if (readtoken() != TEND)
411 synexpect(TEND);
412 checkkwd = 1;
413 break;
414 case TWORD:
415 case TREDIR:
416 tokpushback++;
417 return simplecmd();
418 default:
419 synexpect(-1);
420 }
421
422 /* Now check for redirection which may follow command */
423 rpp = &redir;
424 while (readtoken() == TREDIR) {
425 *rpp = n2 = redirnode;
426 rpp = &n2->nfile.next;
427 parsefname();
428 }
429 tokpushback++;
430 *rpp = NULL;
431 if (redir) {
432 if (n1->type != NSUBSHELL) {
433 n2 = (union node *)stalloc(sizeof (struct nredir));
434 n2->type = NREDIR;
435 n2->nredir.n = n1;
436 n1 = n2;
437 }
438 n1->nredir.redirect = redir;
439 }
440 return n1;
441}
442
443
444STATIC union node *
445simplecmd() {
446 union node *args, **app;
447 union node *redir, **rpp;
448 union node *n;
449
450 args = NULL;
451 app = &args;
452 rpp = &redir;
453 for (;;) {
454 if (readtoken() == TWORD) {
455 n = (union node *)stalloc(sizeof (struct narg));
456 n->type = NARG;
457 n->narg.text = wordtext;
458 n->narg.backquote = backquotelist;
459 *app = n;
460 app = &n->narg.next;
461 } else if (lasttoken == TREDIR) {
462 *rpp = n = redirnode;
463 rpp = &n->nfile.next;
464 parsefname(); /* read name of redirection file */
465 } else if (lasttoken == TLP && app == &args->narg.next
466 && rpp == &redir) {
467 /* We have a function */
468 if (readtoken() != TRP)
469 synexpect(TRP);
470#ifdef notdef
471 if (! goodname(n->narg.text))
472 synerror("Bad function name");
473#endif
474 n->type = NDEFUN;
475 n->narg.next = command();
476 return n;
477 } else {
478 tokpushback++;
479 break;
480 }
481 }
482 *app = NULL;
483 *rpp = NULL;
484 n = (union node *)stalloc(sizeof (struct ncmd));
485 n->type = NCMD;
486 n->ncmd.backgnd = 0;
487 n->ncmd.args = args;
488 n->ncmd.redirect = redir;
489 return n;
490}
491
492
493STATIC void
494parsefname() {
495 union node *n = redirnode;
496
497 if (readtoken() != TWORD)
498 synexpect(-1);
499 if (n->type == NHERE) {
500 struct heredoc *here = heredoc;
501 struct heredoc *p;
502 int i;
503
504 if (quoteflag == 0)
505 n->type = NXHERE;
506 TRACE(("Here document %d\n", n->type));
507 if (here->striptabs) {
508 while (*wordtext == '\t')
509 wordtext++;
510 }
511 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
512 synerror("Illegal eof marker for << redirection");
513 rmescapes(wordtext);
514 here->eofmark = wordtext;
515 here->next = NULL;
516 if (heredoclist == NULL)
517 heredoclist = here;
518 else {
519 for (p = heredoclist ; p->next ; p = p->next);
520 p->next = here;
521 }
522 } else if (n->type == NTOFD || n->type == NFROMFD) {
523 if (is_digit(wordtext[0]))
524 n->ndup.dupfd = digit_val(wordtext[0]);
525 else if (wordtext[0] == '-')
526 n->ndup.dupfd = -1;
527 else
528 goto bad;
529 if (wordtext[1] != '\0') {
530bad:
531 synerror("Bad fd number");
532 }
533 } else {
534 n->nfile.fname = (union node *)stalloc(sizeof (struct narg));
535 n = n->nfile.fname;
536 n->type = NARG;
537 n->narg.next = NULL;
538 n->narg.text = wordtext;
539 n->narg.backquote = backquotelist;
540 }
541}
542
543
544/*
545 * Input any here documents.
546 */
547
548STATIC void
549parseheredoc() {
550 struct heredoc *here;
551 union node *n;
552
553 while (heredoclist) {
554 here = heredoclist;
555 heredoclist = here->next;
556 if (needprompt) {
557 putprompt(ps2val());
558 needprompt = 0;
559 }
560 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
561 here->eofmark, here->striptabs);
562 n = (union node *)stalloc(sizeof (struct narg));
563 n->narg.type = NARG;
564 n->narg.next = NULL;
565 n->narg.text = wordtext;
566 n->narg.backquote = backquotelist;
567 here->here->nhere.doc = n;
568 }
569}
570
571STATIC int
572peektoken() {
573 int t;
574
575 t = readtoken();
576 tokpushback++;
577 return (t);
578}
579
580STATIC int xxreadtoken();
581
582STATIC int
583readtoken() {
584 int t;
585#ifdef DEBUG
586 int alreadyseen = tokpushback;
587#endif
588
589 t = xxreadtoken();
590
591 if (checkkwd) {
592 /*
593 * eat newlines
594 */
595 if (checkkwd == 2) {
596 checkkwd = 0;
597 while (t == TNL) {
598 parseheredoc();
599 t = xxreadtoken();
600 }
601 } else
602 checkkwd = 0;
603 /*
604 * check for keywords
605 */
606 if (t == TWORD && !quoteflag) {
607 register char **pp;
608
609 for (pp = parsekwd; *pp; pp++) {
610 if (**pp == *wordtext && equal(*pp, wordtext)) {
611 lasttoken = t = pp - parsekwd + KWDOFFSET;
612 TRACE(("keyword %s recognized\n", tokname[t]));
613 break;
614 }
615 }
616 }
617 }
618#ifdef DEBUG
619 if (!alreadyseen)
620 TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
621 else
622 TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
623#endif
624 return (t);
625}
626
627
628/*
629 * Read the next input token.
630 * If the token is a word, we set backquotelist to the list of cmds in
631 * backquotes. We set quoteflag to true if any part of the word was
632 * quoted.
633 * If the token is TREDIR, then we set redirnode to a structure containing
634 * the redirection.
635 * In all cases, the variable startlinno is set to the number of the line
636 * on which the token starts.
637 *
638 * [Change comment: here documents and internal procedures]
639 * [Readtoken shouldn't have any arguments. Perhaps we should make the
640 * word parsing code into a separate routine. In this case, readtoken
641 * doesn't need to have any internal procedures, but parseword does.
642 * We could also make parseoperator in essence the main routine, and
643 * have parseword (readtoken1?) handle both words and redirection.]
644 */
645
646#define RETURN(token) return lasttoken = token
647
648STATIC int
649xxreadtoken() {
650 register c;
651
652 if (tokpushback) {
653 tokpushback = 0;
654 return lasttoken;
655 }
656 if (needprompt) {
657 putprompt(ps2val());
658 needprompt = 0;
659 }
660 startlinno = plinno;
661 for (;;) { /* until token or start of word found */
662 c = pgetc_macro();
663 if (c == ' ' || c == '\t')
664 continue; /* quick check for white space first */
665 switch (c) {
666 case ' ': case '\t':
667 continue;
668 case '#':
669 while ((c = pgetc()) != '\n' && c != PEOF);
670 pungetc();
671 continue;
672 case '\\':
673 if (pgetc() == '\n') {
674 startlinno = ++plinno;
675 if (doprompt)
676 putprompt(ps2val());
677 continue;
678 }
679 pungetc();
680 goto breakloop;
681 case '\n':
682 plinno++;
683 needprompt = doprompt;
684 RETURN(TNL);
685 case PEOF:
686 RETURN(TEOF);
687 case '&':
688 if (pgetc() == '&')
689 RETURN(TAND);
690 pungetc();
691 RETURN(TBACKGND);
692 case '|':
693 if (pgetc() == '|')
694 RETURN(TOR);
695 pungetc();
696 RETURN(TPIPE);
697 case ';':
698 if (pgetc() == ';')
699 RETURN(TENDCASE);
700 pungetc();
701 RETURN(TSEMI);
702 case '(':
703 RETURN(TLP);
704 case ')':
705 RETURN(TRP);
706 default:
707 goto breakloop;
708 }
709 }
710breakloop:
711 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
712#undef RETURN
713}
714
715
716
717/*
718 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
719 * is not NULL, read a here document. In the latter case, eofmark is the
720 * word which marks the end of the document and striptabs is true if
721 * leading tabs should be stripped from the document. The argument firstc
722 * is the first character of the input token or document.
723 *
724 * Because C does not have internal subroutines, I have simulated them
725 * using goto's to implement the subroutine linkage. The following macros
726 * will run code that appears at the end of readtoken1.
727 */
728
729#define CHECKEND() {goto checkend; checkend_return:;}
730#define PARSEREDIR() {goto parseredir; parseredir_return:;}
731#define PARSESUB() {goto parsesub; parsesub_return:;}
732#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
733#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
734
735STATIC int
736readtoken1(firstc, syntax, eofmark, striptabs)
737 int firstc;
738 char const *syntax;
739 char *eofmark;
740 int striptabs;
741 {
742 register c = firstc;
743 register char *out;
744 int len;
745 char line[EOFMARKLEN + 1];
746 struct nodelist *bqlist;
747 int quotef;
748 int dblquote;
749 int varnest;
750 int oldstyle;
751
752 startlinno = plinno;
753 dblquote = 0;
754 if (syntax == DQSYNTAX)
755 dblquote = 1;
756 quotef = 0;
757 bqlist = NULL;
758 varnest = 0;
759 STARTSTACKSTR(out);
760 loop: { /* for each line, until end of word */
761#if ATTY
762 if (c == '\034' && doprompt
763 && attyset() && ! equal(termval(), "emacs")) {
764 attyline();
765 if (syntax == BASESYNTAX)
766 return readtoken();
767 c = pgetc();
768 goto loop;
769 }
770#endif
771 CHECKEND(); /* set c to PEOF if at end of here document */
772 for (;;) { /* until end of line or end of word */
773 CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
774 switch(syntax[c]) {
775 case CNL: /* '\n' */
776 if (syntax == BASESYNTAX)
777 goto endword; /* exit outer loop */
778 USTPUTC(c, out);
779 plinno++;
780 if (doprompt) {
781 putprompt(ps2val());
782 }
783 c = pgetc();
784 goto loop; /* continue outer loop */
785 case CWORD:
786 USTPUTC(c, out);
787 break;
788 case CCTL:
789 if (eofmark == NULL || dblquote)
790 USTPUTC(CTLESC, out);
791 USTPUTC(c, out);
792 break;
793 case CBACK: /* backslash */
794 c = pgetc();
795 if (c == PEOF) {
796 USTPUTC('\\', out);
797 pungetc();
798 } else if (c == '\n') {
799 if (doprompt)
800 putprompt(ps2val());
801 } else {
802 if (dblquote && c != '\\' && c != '`' && c != '$'
803 && (c != '"' || eofmark != NULL))
804 USTPUTC('\\', out);
805 if (SQSYNTAX[c] == CCTL)
806 USTPUTC(CTLESC, out);
807 USTPUTC(c, out);
808 quotef++;
809 }
810 break;
811 case CSQUOTE:
812 syntax = SQSYNTAX;
813 break;
814 case CDQUOTE:
815 syntax = DQSYNTAX;
816 dblquote = 1;
817 break;
818 case CENDQUOTE:
819 if (eofmark) {
820 USTPUTC(c, out);
821 } else {
822 syntax = BASESYNTAX;
823 quotef++;
824 dblquote = 0;
825 }
826 break;
827 case CVAR: /* '$' */
828 PARSESUB(); /* parse substitution */
829 break;
830 case CENDVAR: /* '}' */
831 if (varnest > 0) {
832 varnest--;
833 USTPUTC(CTLENDVAR, out);
834 } else {
835 USTPUTC(c, out);
836 }
837 break;
838 case CBQUOTE: /* '`' */
839 if (parsebackquote && syntax == BASESYNTAX) {
840 if (out == stackblock())
841 return lasttoken = TENDBQUOTE;
842 else
843 goto endword; /* exit outer loop */
844 }
845 PARSEBACKQOLD();
846 break;
847 case CEOF:
848 goto endword; /* exit outer loop */
849 default:
850 if (varnest == 0)
851 goto endword; /* exit outer loop */
852 USTPUTC(c, out);
853 }
854 c = pgetc_macro();
855 }
856 }
857endword:
858 if (syntax != BASESYNTAX && eofmark == NULL)
859 synerror("Unterminated quoted string");
860 if (varnest != 0) {
861 startlinno = plinno;
862 synerror("Missing '}'");
863 }
864 USTPUTC('\0', out);
865 len = out - stackblock();
866 out = stackblock();
867 if (eofmark == NULL) {
868 if ((c == '>' || c == '<')
869 && quotef == 0
870 && len <= 2
871 && (*out == '\0' || is_digit(*out))) {
872 PARSEREDIR();
873 return lasttoken = TREDIR;
874 } else {
875 pungetc();
876 }
877 }
878 quoteflag = quotef;
879 backquotelist = bqlist;
880 grabstackblock(len);
881 wordtext = out;
882 return lasttoken = TWORD;
883/* end of readtoken routine */
884
885
886
887/*
888 * Check to see whether we are at the end of the here document. When this
889 * is called, c is set to the first character of the next input line. If
890 * we are at the end of the here document, this routine sets the c to PEOF.
891 */
892
893checkend: {
894 if (eofmark) {
895 if (striptabs) {
896 while (c == '\t')
897 c = pgetc();
898 }
899 if (c == *eofmark) {
900 if (pfgets(line, sizeof line) != NULL) {
901 register char *p, *q;
902
903 p = line;
904 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
905 if (*p == '\n' && *q == '\0') {
906 c = PEOF;
907 plinno++;
908 needprompt = doprompt;
909 } else {
910 ppushback(line, strlen(line));
911 }
912 }
913 }
914 }
915 goto checkend_return;
916}
917
918
919/*
920 * Parse a redirection operator. The variable "out" points to a string
921 * specifying the fd to be redirected. The variable "c" contains the
922 * first character of the redirection operator.
923 */
924
925parseredir: {
926 char fd = *out;
927 union node *np;
928
929 np = (union node *)stalloc(sizeof (struct nfile));
930 if (c == '>') {
931 np->nfile.fd = 1;
932 c = pgetc();
933 if (c == '>')
934 np->type = NAPPEND;
935 else if (c == '&')
936 np->type = NTOFD;
937 else {
938 np->type = NTO;
939 pungetc();
940 }
941 } else { /* c == '<' */
942 np->nfile.fd = 0;
943 c = pgetc();
944 if (c == '<') {
945 if (sizeof (struct nfile) != sizeof (struct nhere)) {
946 np = (union node *)stalloc(sizeof (struct nhere));
947 np->nfile.fd = 0;
948 }
949 np->type = NHERE;
950 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
951 heredoc->here = np;
952 if ((c = pgetc()) == '-') {
953 heredoc->striptabs = 1;
954 } else {
955 heredoc->striptabs = 0;
956 pungetc();
957 }
958 } else if (c == '&')
959 np->type = NFROMFD;
960 else {
961 np->type = NFROM;
962 pungetc();
963 }
964 }
965 if (fd != '\0')
966 np->nfile.fd = digit_val(fd);
967 redirnode = np;
968 goto parseredir_return;
969}
970
971
972/*
973 * Parse a substitution. At this point, we have read the dollar sign
974 * and nothing else.
975 */
976
977parsesub: {
978 int subtype;
979 int typeloc;
980 int flags;
981 char *p;
982#ifndef GDB_HACK
983 static const char types[] = "}-+?=";
984#endif
985
986 c = pgetc();
987 if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
988 USTPUTC('$', out);
989 pungetc();
990 } else if (c == '(') { /* $(command) */
991 PARSEBACKQNEW();
992 } else {
993 USTPUTC(CTLVAR, out);
994 typeloc = out - stackblock();
995 USTPUTC(VSNORMAL, out);
996 subtype = VSNORMAL;
997 if (c == '{') {
998 c = pgetc();
999 subtype = 0;
1000 }
1001 if (is_name(c)) {
1002 do {
1003 STPUTC(c, out);
1004 c = pgetc();
1005 } while (is_in_name(c));
1006 } else {
1007 if (! is_special(c))
1008badsub: synerror("Bad substitution");
1009 USTPUTC(c, out);
1010 c = pgetc();
1011 }
1012 STPUTC('=', out);
1013 flags = 0;
1014 if (subtype == 0) {
1015 if (c == ':') {
1016 flags = VSNUL;
1017 c = pgetc();
1018 }
1019 p = strchr(types, c);
1020 if (p == NULL)
1021 goto badsub;
1022 subtype = p - types + VSNORMAL;
1023 } else {
1024 pungetc();
1025 }
1026 if (dblquote)
1027 flags |= VSQUOTE;
1028 *(stackblock() + typeloc) = subtype | flags;
1029 if (subtype != VSNORMAL)
1030 varnest++;
1031 }
1032 goto parsesub_return;
1033}
1034
1035
1036/*
1037 * Called to parse command substitutions. Newstyle is set if the command
1038 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
1039 * list of commands (passed by reference), and savelen is the number of
1040 * characters on the top of the stack which must be preserved.
1041 */
1042
1043parsebackq: {
1044 struct nodelist **nlpp;
1045 int savepbq;
1046 union node *n;
1047 char *volatile str;
1048 struct jmploc jmploc;
1049 struct jmploc *volatile savehandler;
1050 int savelen;
1051 int t;
1052
1053 savepbq = parsebackquote;
1054 if (setjmp(jmploc.loc)) {
1055 if (str)
1056 ckfree(str);
1057 parsebackquote = 0;
1058 handler = savehandler;
1059 longjmp(handler, 1);
1060 }
1061 INTOFF;
1062 str = NULL;
1063 savelen = out - stackblock();
1064 if (savelen > 0) {
1065 str = ckmalloc(savelen);
1066 bcopy(stackblock(), str, savelen);
1067 }
1068 savehandler = handler;
1069 handler = &jmploc;
1070 INTON;
1071 nlpp = &bqlist;
1072 while (*nlpp)
1073 nlpp = &(*nlpp)->next;
1074 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
1075 (*nlpp)->next = NULL;
1076 parsebackquote = oldstyle;
1077 n = list(0);
1078 t = oldstyle? TENDBQUOTE : TRP;
1079 if (readtoken() != t)
1080 synexpect(t);
1081 (*nlpp)->n = n;
1082 while (stackblocksize() <= savelen)
1083 growstackblock();
1084 STARTSTACKSTR(out);
1085 if (str) {
1086 bcopy(str, out, savelen);
1087 STADJUST(savelen, out);
1088 INTOFF;
1089 ckfree(str);
1090 str = NULL;
1091 INTON;
1092 }
1093 parsebackquote = savepbq;
1094 handler = savehandler;
1095 USTPUTC(CTLBACKQ + dblquote, out);
1096 if (oldstyle)
1097 goto parsebackq_oldreturn;
1098 else
1099 goto parsebackq_newreturn;
1100}
1101
1102} /* end of readtoken */
1103
1104
1105
1106#ifdef mkinit
1107RESET {
1108 tokpushback = 0;
1109}
1110#endif
1111
1112
1113#if ATTY
1114/*
1115 * Called to process a command generated by atty. We execute the line,
1116 * and catch any errors that occur so they don't propagate outside of
1117 * this routine.
1118 */
1119
1120STATIC void
1121attyline() {
1122 char line[256];
1123 struct stackmark smark;
1124 struct jmploc jmploc;
1125 struct jmploc *volatile savehandler;
1126
1127 if (pfgets(line, sizeof line) == NULL)
1128 return; /* "can't happen" */
1129 if (setjmp(jmploc.loc)) {
1130 if (exception == EXERROR)
1131 out2str("\033]D\n");
1132 handler = savehandler;
1133 longjmp(handler, 1);
1134 }
1135 savehandler = handler;
1136 handler = &jmploc;
1137 setstackmark(&smark);
1138 evalstring(line);
1139 popstackmark(&smark);
1140 handler = savehandler;
1141 doprompt = 1;
1142}
1143
1144
1145/*
1146 * Output a prompt for atty. We output the prompt as part of the
1147 * appropriate escape sequence.
1148 */
1149
1150STATIC void
1151putprompt(s)
1152 char *s;
1153 {
1154 register char *p;
1155
1156 if (attyset() && ! equal(termval(), "emacs")) {
1157 if (strchr(s, '\7'))
1158 out2c('\7');
1159 out2str("\033]P1;");
1160 for (p = s ; *p ; p++) {
1161 if ((unsigned)(*p - ' ') <= '~' - ' ')
1162 out2c(*p);
1163 }
1164 out2c('\n');
1165 } else {
1166 out2str(s);
1167 }
1168}
1169#endif
1170
1171
1172
1173/*
1174 * Returns true if the text contains nothing to expand (no dollar signs
1175 * or backquotes).
1176 */
1177
1178STATIC int
1179noexpand(text)
1180 char *text;
1181 {
1182 register char *p;
1183 register char c;
1184
1185 p = text;
1186 while ((c = *p++) != '\0') {
1187 if (c == CTLESC)
1188 p++;
1189 else if (BASESYNTAX[c] == CCTL)
1190 return 0;
1191 }
1192 return 1;
1193}
1194
1195
1196/*
1197 * Return true if the argument is a legal variable name (a letter or
1198 * underscore followed by zero or more letters, underscores, and digits).
1199 */
1200
1201int
1202goodname(name)
1203 char *name;
1204 {
1205 register char *p;
1206
1207 p = name;
1208 if (! is_name(*p))
1209 return 0;
1210 while (*++p) {
1211 if (! is_in_name(*p))
1212 return 0;
1213 }
1214 return 1;
1215}
1216
1217
1218/*
1219 * Called when an unexpected token is read during the parse. The argument
1220 * is the token that is expected, or -1 if more than one type of token can
1221 * occur at this point.
1222 */
1223
1224STATIC void
1225synexpect(token) {
1226 char msg[64];
1227
1228 if (token >= 0) {
1229 fmtstr(msg, 64, "%s unexpected (expecting %s)",
1230 tokname[lasttoken], tokname[token]);
1231 } else {
1232 fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1233 }
1234 synerror(msg);
1235}
1236
1237
1238STATIC void
1239synerror(msg)
1240 char *msg;
1241 {
1242 if (commandname)
1243 outfmt(&errout, "%s: %d: ", commandname, startlinno);
1244 outfmt(&errout, "Syntax error: %s\n", msg);
1245 error((char *)NULL);
1246}