source: trunk/minix/commands/ash/parser.c@ 9

Last change on this file since 9 was 9, checked in by Mattia Monga, 13 years ago

Minix 3.1.2a

File size: 29.2 KB
Line 
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((union node **, union node *));
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 || READLINE
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 extern int exitstatus;
130
131 doprompt = interact;
132 if (doprompt)
133 putprompt(exitstatus == 0 ? ps1val() : pseval());
134 needprompt = 0;
135 if ((t = readtoken()) == TEOF)
136 return NEOF;
137 if (t == TNL)
138 return NULL;
139 tokpushback++;
140 return list(1);
141}
142
143
144STATIC union node *
145list(nlflag) {
146 union node *n1, *n2, *n3, **pn;
147 int first;
148
149 checkkwd = 2;
150 if (nlflag == 0 && tokendlist[peektoken()])
151 return NULL;
152 n1 = andor();
153 for (first = 1; ; first = 0) {
154 switch (readtoken()) {
155 case TBACKGND:
156 pn = &n1;
157 if (!first && n1->type == NSEMI) pn = &n1->nbinary.ch2;
158 if ((*pn)->type == NCMD || (*pn)->type == NPIPE) {
159 (*pn)->ncmd.backgnd = 1;
160 } else if ((*pn)->type == NREDIR) {
161 (*pn)->type = NBACKGND;
162 } else {
163 n3 = (union node *)stalloc(sizeof (struct nredir));
164 n3->type = NBACKGND;
165 n3->nredir.n = *pn;
166 n3->nredir.redirect = NULL;
167 *pn = n3;
168 }
169 goto tsemi;
170 case TNL:
171 tokpushback++;
172 /* fall through */
173tsemi: case TSEMI:
174 if (readtoken() == TNL) {
175 parseheredoc();
176 if (nlflag)
177 return n1;
178 } else {
179 tokpushback++;
180 }
181 checkkwd = 2;
182 if (tokendlist[peektoken()])
183 return n1;
184 n2 = andor();
185 n3 = (union node *)stalloc(sizeof (struct nbinary));
186 n3->type = NSEMI;
187 n3->nbinary.ch1 = n1;
188 n3->nbinary.ch2 = n2;
189 n1 = n3;
190 break;
191 case TEOF:
192 if (heredoclist)
193 parseheredoc();
194 else
195 pungetc(); /* push back EOF on input */
196 return n1;
197 default:
198 if (nlflag)
199 synexpect(-1);
200 tokpushback++;
201 return n1;
202 }
203 }
204}
205
206
207
208STATIC union node *
209andor() {
210 union node *n1, *n2, *n3;
211 int t;
212
213 n1 = pipeline();
214 for (;;) {
215 if ((t = readtoken()) == TAND) {
216 t = NAND;
217 } else if (t == TOR) {
218 t = NOR;
219 } else {
220 tokpushback++;
221 return n1;
222 }
223 n2 = pipeline();
224 n3 = (union node *)stalloc(sizeof (struct nbinary));
225 n3->type = t;
226 n3->nbinary.ch1 = n1;
227 n3->nbinary.ch2 = n2;
228 n1 = n3;
229 }
230}
231
232
233
234STATIC union node *
235pipeline() {
236 union node *n1, *pipenode;
237 struct nodelist *lp, *prev;
238
239 n1 = command();
240 if (readtoken() == TPIPE) {
241 pipenode = (union node *)stalloc(sizeof (struct npipe));
242 pipenode->type = NPIPE;
243 pipenode->npipe.backgnd = 0;
244 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
245 pipenode->npipe.cmdlist = lp;
246 lp->n = n1;
247 do {
248 prev = lp;
249 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
250 lp->n = command();
251 prev->next = lp;
252 } while (readtoken() == TPIPE);
253 lp->next = NULL;
254 n1 = pipenode;
255 }
256 tokpushback++;
257 return n1;
258}
259
260
261
262STATIC union node *
263command() {
264 union node *n1, *n2;
265 union node *ap, **app;
266 union node *cp, **cpp;
267 union node *redir, **rpp;
268 int t;
269
270 checkkwd = 2;
271 redir = 0;
272 rpp = &redir;
273 /* Check for redirection which may precede command */
274 while (readtoken() == TREDIR) {
275 *rpp = n2 = redirnode;
276 rpp = &n2->nfile.next;
277 parsefname();
278 }
279 tokpushback++;
280
281 switch (readtoken()) {
282 case TIF:
283 n1 = (union node *)stalloc(sizeof (struct nif));
284 n1->type = NIF;
285 n1->nif.test = list(0);
286 if (readtoken() != TTHEN)
287 synexpect(TTHEN);
288 n1->nif.ifpart = list(0);
289 n2 = n1;
290 while (readtoken() == TELIF) {
291 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
292 n2 = n2->nif.elsepart;
293 n2->type = NIF;
294 n2->nif.test = list(0);
295 if (readtoken() != TTHEN)
296 synexpect(TTHEN);
297 n2->nif.ifpart = list(0);
298 }
299 if (lasttoken == TELSE)
300 n2->nif.elsepart = list(0);
301 else {
302 n2->nif.elsepart = NULL;
303 tokpushback++;
304 }
305 if (readtoken() != TFI)
306 synexpect(TFI);
307 checkkwd = 1;
308 break;
309 case TWHILE:
310 case TUNTIL: {
311 int got;
312 n1 = (union node *)stalloc(sizeof (struct nbinary));
313 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
314 n1->nbinary.ch1 = list(0);
315 if ((got=readtoken()) != TDO) {
316TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
317 synexpect(TDO);
318 }
319 n1->nbinary.ch2 = list(0);
320 if (readtoken() != TDONE)
321 synexpect(TDONE);
322 checkkwd = 1;
323 break;
324 }
325 case TFOR:
326 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
327 synerror("Bad for loop variable");
328 n1 = (union node *)stalloc(sizeof (struct nfor));
329 n1->type = NFOR;
330 n1->nfor.var = wordtext;
331 if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
332 app = ≈
333 while (readtoken() == TWORD) {
334 n2 = (union node *)stalloc(sizeof (struct narg));
335 n2->type = NARG;
336 n2->narg.text = wordtext;
337 n2->narg.backquote = backquotelist;
338 *app = n2;
339 app = &n2->narg.next;
340 }
341 *app = NULL;
342 n1->nfor.args = ap;
343 /* A newline or semicolon is required here to end
344 the list. */
345 if (lasttoken != TNL && lasttoken != TSEMI)
346 synexpect(-1);
347 } else {
348#ifndef GDB_HACK
349 static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
350 '@', '=', '\0'};
351#endif
352 n2 = (union node *)stalloc(sizeof (struct narg));
353 n2->type = NARG;
354 n2->narg.text = (char *)argvars;
355 n2->narg.backquote = NULL;
356 n2->narg.next = NULL;
357 n1->nfor.args = n2;
358 /* A newline or semicolon is optional here. Anything
359 else gets pushed back so we can read it again. */
360 if (lasttoken != TNL && lasttoken != TSEMI)
361 tokpushback++;
362 }
363 checkkwd = 2;
364 if ((t = readtoken()) == TDO)
365 t = TDONE;
366 else if (t == TBEGIN)
367 t = TEND;
368 else
369 synexpect(-1);
370 n1->nfor.body = list(0);
371 if (readtoken() != t)
372 synexpect(t);
373 checkkwd = 1;
374 break;
375 case TCASE:
376 n1 = (union node *)stalloc(sizeof (struct ncase));
377 n1->type = NCASE;
378 if (readtoken() != TWORD)
379 synexpect(TWORD);
380 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
381 n2->type = NARG;
382 n2->narg.text = wordtext;
383 n2->narg.backquote = backquotelist;
384 n2->narg.next = NULL;
385 while (readtoken() == TNL);
386 if (lasttoken != TWORD || ! equal(wordtext, "in"))
387 synerror("expecting \"in\"");
388 cpp = &n1->ncase.cases;
389 while (checkkwd = 2, readtoken() == TWORD) {
390 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
391 cp->type = NCLIST;
392 app = &cp->nclist.pattern;
393 for (;;) {
394 *app = ap = (union node *)stalloc(sizeof (struct narg));
395 ap->type = NARG;
396 ap->narg.text = wordtext;
397 ap->narg.backquote = backquotelist;
398 if (readtoken() != TPIPE)
399 break;
400 app = &ap->narg.next;
401 if (readtoken() != TWORD)
402 synexpect(TWORD);
403 }
404 ap->narg.next = NULL;
405 if (lasttoken != TRP)
406 synexpect(TRP);
407 cp->nclist.body = list(0);
408 if ((t = readtoken()) == TESAC)
409 tokpushback++;
410 else if (t != TENDCASE)
411 synexpect(TENDCASE);
412 cpp = &cp->nclist.next;
413 }
414 *cpp = NULL;
415 if (lasttoken != TESAC)
416 synexpect(TESAC);
417 checkkwd = 1;
418 break;
419 case TLP:
420 n1 = (union node *)stalloc(sizeof (struct nredir));
421 n1->type = NSUBSHELL;
422 n1->nredir.n = list(0);
423 n1->nredir.redirect = NULL;
424 if (readtoken() != TRP)
425 synexpect(TRP);
426 checkkwd = 1;
427 break;
428 case TBEGIN:
429 n1 = list(0);
430 if (readtoken() != TEND)
431 synexpect(TEND);
432 checkkwd = 1;
433 break;
434 /* Handle an empty command like other simple commands. */
435 case TNL:
436 case TSEMI:
437 case TEND:
438 case TRP:
439 case TEOF:
440 case TWORD:
441 tokpushback++;
442 return simplecmd(rpp, redir);
443 default:
444 synexpect(-1);
445 }
446
447 /* Now check for redirection which may follow command */
448 while (readtoken() == TREDIR) {
449 *rpp = n2 = redirnode;
450 rpp = &n2->nfile.next;
451 parsefname();
452 }
453 tokpushback++;
454 *rpp = NULL;
455 if (redir) {
456 if (n1->type != NSUBSHELL) {
457 n2 = (union node *)stalloc(sizeof (struct nredir));
458 n2->type = NREDIR;
459 n2->nredir.n = n1;
460 n1 = n2;
461 }
462 n1->nredir.redirect = redir;
463 }
464 return n1;
465}
466
467
468STATIC union node *
469simplecmd(rpp, redir)
470 union node **rpp, *redir;
471 {
472 union node *args, **app;
473 union node **orig_rpp = rpp;
474 union node *n;
475
476 /* If we don't have any redirections already, then we must reset
477 rpp to be the address of the local redir variable. */
478 if (redir == 0)
479 rpp = &redir;
480
481 args = NULL;
482 app = &args;
483 /* We save the incoming value, because we need this for shell
484 functions. There can not be a redirect or an argument between
485 the function name and the open parenthesis. */
486 orig_rpp = rpp;
487 for (;;) {
488 if (readtoken() == TWORD) {
489 n = (union node *)stalloc(sizeof (struct narg));
490 n->type = NARG;
491 n->narg.text = wordtext;
492 n->narg.backquote = backquotelist;
493 *app = n;
494 app = &n->narg.next;
495 } else if (lasttoken == TREDIR) {
496 *rpp = n = redirnode;
497 rpp = &n->nfile.next;
498 parsefname(); /* read name of redirection file */
499 } else if (lasttoken == TLP && app == &args->narg.next
500 && rpp == orig_rpp) {
501 /* We have a function */
502 if (readtoken() != TRP)
503 synexpect(TRP);
504#ifdef notdef
505 if (! goodname(n->narg.text))
506 synerror("Bad function name");
507#endif
508 n->type = NDEFUN;
509 n->narg.next = command();
510 return n;
511 } else {
512 tokpushback++;
513 break;
514 }
515 }
516 *app = NULL;
517 *rpp = NULL;
518 n = (union node *)stalloc(sizeof (struct ncmd));
519 n->type = NCMD;
520 n->ncmd.backgnd = 0;
521 n->ncmd.args = args;
522 n->ncmd.redirect = redir;
523 return n;
524}
525
526
527STATIC void
528parsefname() {
529 union node *n = redirnode;
530
531 if (readtoken() != TWORD)
532 synexpect(-1);
533 if (n->type == NHERE) {
534 struct heredoc *here = heredoc;
535 struct heredoc *p;
536 int i;
537
538 if (quoteflag == 0)
539 n->type = NXHERE;
540 TRACE(("Here document %d\n", n->type));
541 if (here->striptabs) {
542 while (*wordtext == '\t')
543 wordtext++;
544 }
545 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
546 synerror("Illegal eof marker for << redirection");
547 rmescapes(wordtext);
548 here->eofmark = wordtext;
549 here->next = NULL;
550 if (heredoclist == NULL)
551 heredoclist = here;
552 else {
553 for (p = heredoclist ; p->next ; p = p->next);
554 p->next = here;
555 }
556 } else if (n->type == NTOFD || n->type == NFROMFD) {
557 if (is_digit(wordtext[0]))
558 n->ndup.dupfd = digit_val(wordtext[0]);
559 else if (wordtext[0] == '-')
560 n->ndup.dupfd = -1;
561 else
562 goto bad;
563 if (wordtext[1] != '\0') {
564bad:
565 synerror("Bad fd number");
566 }
567 } else {
568 n->nfile.fname = (union node *)stalloc(sizeof (struct narg));
569 n = n->nfile.fname;
570 n->type = NARG;
571 n->narg.next = NULL;
572 n->narg.text = wordtext;
573 n->narg.backquote = backquotelist;
574 }
575}
576
577
578/*
579 * Input any here documents.
580 */
581
582STATIC void
583parseheredoc() {
584 struct heredoc *here;
585 union node *n;
586
587 while (heredoclist) {
588 here = heredoclist;
589 heredoclist = here->next;
590 if (needprompt) {
591 putprompt(ps2val());
592 needprompt = 0;
593 }
594 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
595 here->eofmark, here->striptabs);
596 n = (union node *)stalloc(sizeof (struct narg));
597 n->narg.type = NARG;
598 n->narg.next = NULL;
599 n->narg.text = wordtext;
600 n->narg.backquote = backquotelist;
601 here->here->nhere.doc = n;
602 }
603}
604
605STATIC int
606peektoken() {
607 int t;
608
609 t = readtoken();
610 tokpushback++;
611 return (t);
612}
613
614STATIC int xxreadtoken();
615
616STATIC int
617readtoken() {
618 int t;
619#if DEBUG
620 int alreadyseen = tokpushback;
621#endif
622
623 t = xxreadtoken();
624
625 if (checkkwd) {
626 /*
627 * eat newlines
628 */
629 if (checkkwd == 2) {
630 checkkwd = 0;
631 while (t == TNL) {
632 parseheredoc();
633 t = xxreadtoken();
634 }
635 } else
636 checkkwd = 0;
637 /*
638 * check for keywords
639 */
640 if (t == TWORD && !quoteflag) {
641 register char *const *pp;
642
643 for (pp = parsekwd; *pp; pp++) {
644 if (**pp == *wordtext && equal(*pp, wordtext)) {
645 lasttoken = t = pp - parsekwd + KWDOFFSET;
646 TRACE(("keyword %s recognized\n", tokname[t]));
647 break;
648 }
649 }
650 }
651 }
652#if DEBUG
653 if (!alreadyseen)
654 TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
655 else
656 TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
657#endif
658 return (t);
659}
660
661
662/*
663 * Read the next input token.
664 * If the token is a word, we set backquotelist to the list of cmds in
665 * backquotes. We set quoteflag to true if any part of the word was
666 * quoted.
667 * If the token is TREDIR, then we set redirnode to a structure containing
668 * the redirection.
669 * In all cases, the variable startlinno is set to the number of the line
670 * on which the token starts.
671 *
672 * [Change comment: here documents and internal procedures]
673 * [Readtoken shouldn't have any arguments. Perhaps we should make the
674 * word parsing code into a separate routine. In this case, readtoken
675 * doesn't need to have any internal procedures, but parseword does.
676 * We could also make parseoperator in essence the main routine, and
677 * have parseword (readtoken1?) handle both words and redirection.]
678 */
679
680#define RETURN(token) return lasttoken = token
681
682STATIC int
683xxreadtoken() {
684 register c;
685
686 if (tokpushback) {
687 tokpushback = 0;
688 return lasttoken;
689 }
690 if (needprompt) {
691 putprompt(ps2val());
692 needprompt = 0;
693 }
694 startlinno = plinno;
695 for (;;) { /* until token or start of word found */
696 c = pgetc_macro();
697 if (c == ' ' || c == '\t')
698 continue; /* quick check for white space first */
699 switch (c) {
700 case ' ': case '\t':
701 continue;
702 case '#':
703 while ((c = pgetc()) != '\n' && c != PEOF);
704 pungetc();
705 continue;
706 case '\\':
707 if (pgetc() == '\n') {
708 startlinno = ++plinno;
709 if (doprompt)
710 putprompt(ps2val());
711 continue;
712 }
713 pungetc();
714 goto breakloop;
715 case '\n':
716 plinno++;
717 needprompt = doprompt;
718 RETURN(TNL);
719 case PEOF:
720 RETURN(TEOF);
721 case '&':
722 if (pgetc() == '&')
723 RETURN(TAND);
724 pungetc();
725 RETURN(TBACKGND);
726 case '|':
727 if (pgetc() == '|')
728 RETURN(TOR);
729 pungetc();
730 RETURN(TPIPE);
731 case ';':
732 if (pgetc() == ';')
733 RETURN(TENDCASE);
734 pungetc();
735 RETURN(TSEMI);
736 case '(':
737 RETURN(TLP);
738 case ')':
739 RETURN(TRP);
740 default:
741 goto breakloop;
742 }
743 }
744breakloop:
745 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
746#undef RETURN
747}
748
749
750
751/*
752 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
753 * is not NULL, read a here document. In the latter case, eofmark is the
754 * word which marks the end of the document and striptabs is true if
755 * leading tabs should be stripped from the document. The argument firstc
756 * is the first character of the input token or document.
757 *
758 * Because C does not have internal subroutines, I have simulated them
759 * using goto's to implement the subroutine linkage. The following macros
760 * will run code that appears at the end of readtoken1.
761 */
762
763#define CHECKEND() {goto checkend; checkend_return:;}
764#define PARSEREDIR() {goto parseredir; parseredir_return:;}
765#define PARSESUB() {goto parsesub; parsesub_return:;}
766#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
767#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
768
769STATIC int
770readtoken1(firstc, syntax, eofmark, striptabs)
771 int firstc;
772 char const *syntax;
773 char *eofmark;
774 int striptabs;
775 {
776 register c = firstc;
777 register char *out;
778 int len;
779 char line[EOFMARKLEN + 1];
780 struct nodelist *bqlist;
781 int quotef;
782 int dblquote;
783 int varnest;
784 int oldstyle;
785
786 startlinno = plinno;
787 dblquote = 0;
788 if (syntax == DQSYNTAX)
789 dblquote = 1;
790 quotef = 0;
791 bqlist = NULL;
792 varnest = 0;
793 STARTSTACKSTR(out);
794 loop: { /* for each line, until end of word */
795#if ATTY
796 if (c == '\034' && doprompt
797 && attyset() && ! equal(termval(), "emacs")) {
798 attyline();
799 if (syntax == BASESYNTAX)
800 return readtoken();
801 c = pgetc();
802 goto loop;
803 }
804#endif
805 CHECKEND(); /* set c to PEOF if at end of here document */
806 for (;;) { /* until end of line or end of word */
807 CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
808 switch(syntax[c]) {
809 case CNL: /* '\n' */
810 if (syntax == BASESYNTAX)
811 goto endword; /* exit outer loop */
812 USTPUTC(c, out);
813 plinno++;
814 if (doprompt) {
815 putprompt(ps2val());
816 }
817 c = pgetc();
818 goto loop; /* continue outer loop */
819 case CWORD:
820 USTPUTC(c, out);
821 break;
822 case CCTL:
823 if (eofmark == NULL || dblquote)
824 USTPUTC(CTLESC, out);
825 USTPUTC(c, out);
826 break;
827 case CBACK: /* backslash */
828 c = pgetc();
829 if (c == PEOF) {
830 USTPUTC('\\', out);
831 pungetc();
832 } else if (c == '\n') {
833 if (doprompt)
834 putprompt(ps2val());
835 } else {
836 if (dblquote && c != '\\' && c != '`' && c != '$'
837 && (c != '"' || eofmark != NULL))
838 USTPUTC('\\', out);
839 if (SQSYNTAX[c] == CCTL)
840 USTPUTC(CTLESC, out);
841 USTPUTC(c, out);
842 quotef++;
843 }
844 break;
845 case CSQUOTE:
846 syntax = SQSYNTAX;
847 break;
848 case CDQUOTE:
849 syntax = DQSYNTAX;
850 dblquote = 1;
851 break;
852 case CENDQUOTE:
853 if (eofmark) {
854 USTPUTC(c, out);
855 } else {
856 syntax = BASESYNTAX;
857 quotef++;
858 dblquote = 0;
859 }
860 break;
861 case CVAR: /* '$' */
862 PARSESUB(); /* parse substitution */
863 break;
864 case CENDVAR: /* '}' */
865 if (varnest > 0) {
866 varnest--;
867 USTPUTC(CTLENDVAR, out);
868 } else {
869 USTPUTC(c, out);
870 }
871 break;
872 case CBQUOTE: /* '`' */
873 PARSEBACKQOLD();
874 break;
875 case CEOF:
876 goto endword; /* exit outer loop */
877 default:
878 if (varnest == 0)
879 goto endword; /* exit outer loop */
880 USTPUTC(c, out);
881 }
882 c = pgetc_macro();
883 }
884 }
885endword:
886 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
887 synerror("Unterminated quoted string");
888 if (varnest != 0) {
889 startlinno = plinno;
890 synerror("Missing '}'");
891 }
892 USTPUTC('\0', out);
893 len = out - stackblock();
894 out = stackblock();
895 if (eofmark == NULL) {
896 if ((c == '>' || c == '<')
897 && quotef == 0
898 && len <= 2
899 && (*out == '\0' || is_digit(*out))) {
900 PARSEREDIR();
901 return lasttoken = TREDIR;
902 } else {
903 pungetc();
904 }
905 }
906 quoteflag = quotef;
907 backquotelist = bqlist;
908 grabstackblock(len);
909 wordtext = out;
910 return lasttoken = TWORD;
911/* end of readtoken routine */
912
913
914
915/*
916 * Check to see whether we are at the end of the here document. When this
917 * is called, c is set to the first character of the next input line. If
918 * we are at the end of the here document, this routine sets the c to PEOF.
919 */
920
921checkend: {
922 if (eofmark) {
923 if (striptabs) {
924 while (c == '\t')
925 c = pgetc();
926 }
927 if (c == *eofmark) {
928 if (pfgets(line, sizeof line) != NULL) {
929 register char *p, *q;
930
931 p = line;
932 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
933 if (*p == '\n' && *q == '\0') {
934 c = PEOF;
935 plinno++;
936 needprompt = doprompt;
937 } else {
938 ppushback(line, strlen(line));
939 }
940 }
941 }
942 }
943 goto checkend_return;
944}
945
946
947/*
948 * Parse a redirection operator. The variable "out" points to a string
949 * specifying the fd to be redirected. The variable "c" contains the
950 * first character of the redirection operator.
951 */
952
953parseredir: {
954 char fd = *out;
955 union node *np;
956
957 np = (union node *)stalloc(sizeof (struct nfile));
958 if (c == '>') {
959 np->nfile.fd = 1;
960 c = pgetc();
961 if (c == '>')
962 np->type = NAPPEND;
963 else if (c == '&')
964 np->type = NTOFD;
965 else {
966 np->type = NTO;
967 pungetc();
968 }
969 } else { /* c == '<' */
970 np->nfile.fd = 0;
971 c = pgetc();
972 if (c == '<') {
973 if (sizeof (struct nfile) != sizeof (struct nhere)) {
974 np = (union node *)stalloc(sizeof (struct nhere));
975 np->nfile.fd = 0;
976 }
977 np->type = NHERE;
978 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
979 heredoc->here = np;
980 if ((c = pgetc()) == '-') {
981 heredoc->striptabs = 1;
982 } else {
983 heredoc->striptabs = 0;
984 pungetc();
985 }
986 } else if (c == '&')
987 np->type = NFROMFD;
988 else {
989 np->type = NFROM;
990 pungetc();
991 }
992 }
993 if (fd != '\0')
994 np->nfile.fd = digit_val(fd);
995 redirnode = np;
996 goto parseredir_return;
997}
998
999
1000/*
1001 * Parse a substitution. At this point, we have read the dollar sign
1002 * and nothing else.
1003 */
1004
1005parsesub: {
1006 int subtype;
1007 int typeloc;
1008 int flags;
1009 char *p;
1010#ifndef GDB_HACK
1011 static const char types[] = "}-+?=";
1012#endif
1013
1014 c = pgetc();
1015 if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
1016 USTPUTC('$', out);
1017 pungetc();
1018 } else if (c == '(') { /* $(command) */
1019 PARSEBACKQNEW();
1020 } else {
1021 USTPUTC(CTLVAR, out);
1022 typeloc = out - stackblock();
1023 USTPUTC(VSNORMAL, out);
1024 subtype = VSNORMAL;
1025 if (c == '{') {
1026 c = pgetc();
1027 subtype = 0;
1028 }
1029 if (is_name(c)) {
1030 do {
1031 STPUTC(c, out);
1032 c = pgetc();
1033 } while (is_in_name(c));
1034 } else {
1035 if (! is_special(c))
1036badsub: synerror("Bad substitution");
1037 USTPUTC(c, out);
1038 c = pgetc();
1039 }
1040 STPUTC('=', out);
1041 flags = 0;
1042 if (subtype == 0) {
1043 if (c == ':') {
1044 flags = VSNUL;
1045 c = pgetc();
1046 }
1047 p = strchr(types, c);
1048 if (p == NULL)
1049 goto badsub;
1050 subtype = p - types + VSNORMAL;
1051 } else {
1052 pungetc();
1053 }
1054 if (dblquote)
1055 flags |= VSQUOTE;
1056 *(stackblock() + typeloc) = subtype | flags;
1057 if (subtype != VSNORMAL)
1058 varnest++;
1059 }
1060 goto parsesub_return;
1061}
1062
1063
1064/*
1065 * Called to parse command substitutions. Newstyle is set if the command
1066 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
1067 * list of commands (passed by reference), and savelen is the number of
1068 * characters on the top of the stack which must be preserved.
1069 */
1070
1071parsebackq: {
1072 struct nodelist **nlpp;
1073 int savepbq;
1074 union node *n;
1075 char *volatile str;
1076 struct jmploc jmploc;
1077 struct jmploc *volatile savehandler;
1078 int savelen;
1079 int savedoprompt;
1080
1081 savepbq = parsebackquote;
1082 if (setjmp(jmploc.loc)) {
1083 if (str)
1084 ckfree(str);
1085 parsebackquote = 0;
1086 handler = savehandler;
1087 longjmp(handler->loc, 1);
1088 }
1089 INTOFF;
1090 str = NULL;
1091 savelen = out - stackblock();
1092 if (savelen > 0) {
1093 str = ckmalloc(savelen);
1094 bcopy(stackblock(), str, savelen);
1095 }
1096 savehandler = handler;
1097 handler = &jmploc;
1098 INTON;
1099 if (oldstyle) {
1100 /* We must read until the closing backquote, giving special
1101 treatment to some slashes, and then push the string and
1102 reread it as input, interpreting it normally. */
1103 register char *out;
1104 register c;
1105 int savelen;
1106 char *str;
1107
1108 STARTSTACKSTR(out);
1109 while ((c = pgetc ()) != '`') {
1110 if (c == '\\') {
1111 c = pgetc ();
1112 if (c != '\\' && c != '`' && c != '$'
1113 && (!dblquote || c != '"'))
1114 STPUTC('\\', out);
1115 }
1116 if (c == '\n') {
1117 plinno++;
1118 if (doprompt)
1119 putprompt(ps2val());
1120 }
1121 STPUTC(c, out);
1122 }
1123 STPUTC('\0', out);
1124 savelen = out - stackblock();
1125 if (savelen > 0) {
1126 str = ckmalloc(savelen);
1127 bcopy(stackblock(), str, savelen);
1128 }
1129 setinputstring(str, 1);
1130 savedoprompt = doprompt;
1131 doprompt = 0; /* no prompts while rereading string */
1132 }
1133 nlpp = &bqlist;
1134 while (*nlpp)
1135 nlpp = &(*nlpp)->next;
1136 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
1137 (*nlpp)->next = NULL;
1138 parsebackquote = oldstyle;
1139 n = list(0);
1140 if (!oldstyle && (readtoken() != TRP))
1141 synexpect(TRP);
1142 (*nlpp)->n = n;
1143 /* Start reading from old file again. */
1144 if (oldstyle) {
1145 popfile();
1146 doprompt = savedoprompt;
1147 }
1148 while (stackblocksize() <= savelen)
1149 growstackblock();
1150 STARTSTACKSTR(out);
1151 if (str) {
1152 bcopy(str, out, savelen);
1153 STADJUST(savelen, out);
1154 INTOFF;
1155 ckfree(str);
1156 str = NULL;
1157 INTON;
1158 }
1159 parsebackquote = savepbq;
1160 handler = savehandler;
1161 USTPUTC(CTLBACKQ + dblquote, out);
1162 if (oldstyle)
1163 goto parsebackq_oldreturn;
1164 else
1165 goto parsebackq_newreturn;
1166}
1167
1168} /* end of readtoken */
1169
1170
1171
1172#ifdef mkinit
1173RESET {
1174 tokpushback = 0;
1175}
1176#endif
1177
1178
1179#if READLINE
1180/*
1181 * Remember a prompt for use with readline if input and output is a terminal.
1182 */
1183
1184STATIC void
1185putprompt(s)
1186 char *s;
1187 {
1188 if (editable) {
1189 r_use_prompt = s;
1190 } else {
1191 out2str(s);
1192 }
1193}
1194#endif
1195
1196#if ATTY
1197/*
1198 * Called to process a command generated by atty. We execute the line,
1199 * and catch any errors that occur so they don't propagate outside of
1200 * this routine.
1201 */
1202
1203STATIC void
1204attyline() {
1205 char line[256];
1206 struct stackmark smark;
1207 struct jmploc jmploc;
1208 struct jmploc *volatile savehandler;
1209
1210 if (pfgets(line, sizeof line) == NULL)
1211 return; /* "can't happen" */
1212 if (setjmp(jmploc.loc)) {
1213 if (exception == EXERROR)
1214 out2str("\033]D\n");
1215 handler = savehandler;
1216 longjmp(handler->loc, 1);
1217 }
1218 savehandler = handler;
1219 handler = &jmploc;
1220 setstackmark(&smark);
1221 evalstring(line);
1222 popstackmark(&smark);
1223 handler = savehandler;
1224 doprompt = 1;
1225}
1226
1227
1228/*
1229 * Output a prompt for atty. We output the prompt as part of the
1230 * appropriate escape sequence.
1231 */
1232
1233STATIC void
1234putprompt(s)
1235 char *s;
1236 {
1237 register char *p;
1238
1239 if (attyset() && ! equal(termval(), "emacs")) {
1240 if (strchr(s, '\7'))
1241 out2c('\7');
1242 out2str("\033]P1;");
1243 for (p = s ; *p ; p++) {
1244 if ((unsigned)(*p - ' ') <= '~' - ' ')
1245 out2c(*p);
1246 }
1247 out2c('\n');
1248 } else {
1249 out2str(s);
1250 }
1251}
1252#endif
1253
1254
1255
1256/*
1257 * Returns true if the text contains nothing to expand (no dollar signs
1258 * or backquotes).
1259 */
1260
1261STATIC int
1262noexpand(text)
1263 char *text;
1264 {
1265 register char *p;
1266 register char c;
1267
1268 p = text;
1269 while ((c = *p++) != '\0') {
1270 if (c == CTLESC)
1271 p++;
1272 else if (BASESYNTAX[c] == CCTL)
1273 return 0;
1274 }
1275 return 1;
1276}
1277
1278
1279/*
1280 * Return true if the argument is a legal variable name (a letter or
1281 * underscore followed by zero or more letters, underscores, and digits).
1282 */
1283
1284int
1285goodname(name)
1286 char *name;
1287 {
1288 register char *p;
1289
1290 p = name;
1291 if (! is_name(*p))
1292 return 0;
1293 while (*++p) {
1294 if (! is_in_name(*p))
1295 return 0;
1296 }
1297 return 1;
1298}
1299
1300
1301/*
1302 * Called when an unexpected token is read during the parse. The argument
1303 * is the token that is expected, or -1 if more than one type of token can
1304 * occur at this point.
1305 */
1306
1307STATIC void
1308synexpect(token) {
1309 char msg[64];
1310
1311 if (token >= 0) {
1312 fmtstr(msg, 64, "%s unexpected (expecting %s)",
1313 tokname[lasttoken], tokname[token]);
1314 } else {
1315 fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1316 }
1317 synerror(msg);
1318}
1319
1320
1321STATIC void
1322synerror(msg)
1323 char *msg;
1324 {
1325 if (commandname)
1326 outfmt(&errout, "%s: %d: ", commandname, startlinno);
1327 outfmt(&errout, "Syntax error: %s\n", msg);
1328 error((char *)NULL);
1329}
Note: See TracBrowser for help on using the repository browser.