source: mainline/uspace/app/ash/parser.c@ c28a023

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since c28a023 was c28a023, checked in by Josef Cejka <malyzelenyhnus@…>, 17 years ago

Initial commit of ash shell.
It cannot be compiled yet.

  • Property mode set to 100644
File size: 33.1 KB
Line 
1/* $NetBSD: parser.c,v 1.45 2000/07/27 04:09:27 cgd Exp $ */
2
3/*-
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * This code is derived from software contributed to Berkeley by
8 * Kenneth Almquist.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the University of
21 * California, Berkeley and its contributors.
22 * 4. Neither the name of the University nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 * SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40#ifndef lint
41#if 0
42static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95";
43#else
44__RCSID("$NetBSD: parser.c,v 1.45 2000/07/27 04:09:27 cgd Exp $");
45#endif
46#endif /* not lint */
47
48#include <stdlib.h>
49
50#include "shell.h"
51#include "parser.h"
52#include "nodes.h"
53#include "expand.h" /* defines rmescapes() */
54#include "redir.h" /* defines copyfd() */
55#include "syntax.h"
56#include "options.h"
57#include "input.h"
58#include "output.h"
59#include "var.h"
60#include "error.h"
61#include "memalloc.h"
62#include "mystring.h"
63#include "alias.h"
64#include "show.h"
65#ifndef SMALL
66#include "myhistedit.h"
67#endif
68
69/*
70 * Shell command parser.
71 */
72
73#define EOFMARKLEN 79
74
75/* values returned by readtoken */
76#include "token.h"
77
78
79
80struct heredoc {
81 struct heredoc *next; /* next here document in list */
82 union node *here; /* redirection node */
83 char *eofmark; /* string indicating end of input */
84 int striptabs; /* if set, strip leading tabs */
85};
86
87
88
89struct heredoc *heredoclist; /* list of here documents to read */
90int parsebackquote; /* nonzero if we are inside backquotes */
91int doprompt; /* if set, prompt the user */
92int needprompt; /* true if interactive and at start of line */
93int lasttoken; /* last token read */
94MKINIT int tokpushback; /* last token pushed back */
95char *wordtext; /* text of last word returned by readtoken */
96MKINIT int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */
97struct nodelist *backquotelist;
98union node *redirnode;
99struct heredoc *heredoc;
100int quoteflag; /* set if (part of) last token was quoted */
101int startlinno; /* line # where last token started */
102
103
104STATIC union node *list (int);
105STATIC union node *andor (void);
106STATIC union node *pipeline (void);
107STATIC union node *command (void);
108STATIC union node *simplecmd (union node **, union node *);
109STATIC union node *makename (void);
110STATIC void parsefname (void);
111STATIC void parseheredoc (void);
112STATIC int peektoken (void);
113STATIC int readtoken (void);
114STATIC int xxreadtoken (void);
115STATIC int readtoken1 (int, char const *, char *, int);
116STATIC int noexpand (char *);
117STATIC void synexpect (int) __attribute__((noreturn));
118STATIC void synerror (const char *) __attribute__((noreturn));
119STATIC void setprompt (int);
120
121
122/*
123 * Read and parse a command. Returns NEOF on end of file. (NULL is a
124 * valid parse tree indicating a blank line.)
125 */
126
127union node *
128parsecmd(int interact)
129{
130 int t;
131
132 doprompt = interact;
133 if (doprompt)
134 setprompt(1);
135 else
136 setprompt(0);
137 needprompt = 0;
138 t = readtoken();
139 if (t == TEOF)
140 return NEOF;
141 if (t == TNL)
142 return NULL;
143 tokpushback++;
144 return list(1);
145}
146
147
148STATIC union node *
149list(nlflag)
150 int nlflag;
151{
152 union node *n1, *n2, *n3;
153 int tok;
154
155 checkkwd = 2;
156 if (nlflag == 0 && tokendlist[peektoken()])
157 return NULL;
158 n1 = NULL;
159 for (;;) {
160 n2 = andor();
161 tok = readtoken();
162 if (tok == TBACKGND) {
163 if (n2->type == NCMD || n2->type == NPIPE) {
164 n2->ncmd.backgnd = 1;
165 } else if (n2->type == NREDIR) {
166 n2->type = NBACKGND;
167 } else {
168 n3 = (union node *)stalloc(sizeof (struct nredir));
169 n3->type = NBACKGND;
170 n3->nredir.n = n2;
171 n3->nredir.redirect = NULL;
172 n2 = n3;
173 }
174 }
175 if (n1 == NULL) {
176 n1 = n2;
177 }
178 else {
179 n3 = (union node *)stalloc(sizeof (struct nbinary));
180 n3->type = NSEMI;
181 n3->nbinary.ch1 = n1;
182 n3->nbinary.ch2 = n2;
183 n1 = n3;
184 }
185 switch (tok) {
186 case TBACKGND:
187 case TSEMI:
188 tok = readtoken();
189 /* fall through */
190 case TNL:
191 if (tok == TNL) {
192 parseheredoc();
193 if (nlflag)
194 return n1;
195 } else {
196 tokpushback++;
197 }
198 checkkwd = 2;
199 if (tokendlist[peektoken()])
200 return n1;
201 break;
202 case TEOF:
203 if (heredoclist)
204 parseheredoc();
205 else
206 pungetc(); /* push back EOF on input */
207 return n1;
208 default:
209 if (nlflag)
210 synexpect(-1);
211 tokpushback++;
212 return n1;
213 }
214 }
215}
216
217
218
219STATIC union node *
220andor() {
221 union node *n1, *n2, *n3;
222 int t;
223
224 checkkwd = 1;
225 n1 = pipeline();
226 for (;;) {
227 if ((t = readtoken()) == TAND) {
228 t = NAND;
229 } else if (t == TOR) {
230 t = NOR;
231 } else {
232 tokpushback++;
233 return n1;
234 }
235 checkkwd = 2;
236 n2 = pipeline();
237 n3 = (union node *)stalloc(sizeof (struct nbinary));
238 n3->type = t;
239 n3->nbinary.ch1 = n1;
240 n3->nbinary.ch2 = n2;
241 n1 = n3;
242 }
243}
244
245
246
247STATIC union node *
248pipeline() {
249 union node *n1, *n2, *pipenode;
250 struct nodelist *lp, *prev;
251 int negate;
252
253 negate = 0;
254 TRACE(("pipeline: entered\n"));
255 if (readtoken() == TNOT) {
256 negate = !negate;
257 checkkwd = 1;
258 } else
259 tokpushback++;
260 n1 = command();
261 if (readtoken() == TPIPE) {
262 pipenode = (union node *)stalloc(sizeof (struct npipe));
263 pipenode->type = NPIPE;
264 pipenode->npipe.backgnd = 0;
265 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
266 pipenode->npipe.cmdlist = lp;
267 lp->n = n1;
268 do {
269 prev = lp;
270 lp = (struct nodelist *)stalloc(sizeof (struct nodelist));
271 checkkwd = 2;
272 lp->n = command();
273 prev->next = lp;
274 } while (readtoken() == TPIPE);
275 lp->next = NULL;
276 n1 = pipenode;
277 }
278 tokpushback++;
279 if (negate) {
280 n2 = (union node *)stalloc(sizeof (struct nnot));
281 n2->type = NNOT;
282 n2->nnot.com = n1;
283 return n2;
284 } else
285 return n1;
286}
287
288
289
290STATIC union node *
291command() {
292 union node *n1, *n2;
293 union node *ap, **app;
294 union node *cp, **cpp;
295 union node *redir, **rpp;
296 int t;
297
298 redir = NULL;
299 n1 = NULL;
300 rpp = &redir;
301
302 /* Check for redirection which may precede command */
303 while (readtoken() == TREDIR) {
304 *rpp = n2 = redirnode;
305 rpp = &n2->nfile.next;
306 parsefname();
307 }
308 tokpushback++;
309
310 switch (readtoken()) {
311 case TIF:
312 n1 = (union node *)stalloc(sizeof (struct nif));
313 n1->type = NIF;
314 n1->nif.test = list(0);
315 if (readtoken() != TTHEN)
316 synexpect(TTHEN);
317 n1->nif.ifpart = list(0);
318 n2 = n1;
319 while (readtoken() == TELIF) {
320 n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif));
321 n2 = n2->nif.elsepart;
322 n2->type = NIF;
323 n2->nif.test = list(0);
324 if (readtoken() != TTHEN)
325 synexpect(TTHEN);
326 n2->nif.ifpart = list(0);
327 }
328 if (lasttoken == TELSE)
329 n2->nif.elsepart = list(0);
330 else {
331 n2->nif.elsepart = NULL;
332 tokpushback++;
333 }
334 if (readtoken() != TFI)
335 synexpect(TFI);
336 checkkwd = 1;
337 break;
338 case TWHILE:
339 case TUNTIL: {
340 int got;
341 n1 = (union node *)stalloc(sizeof (struct nbinary));
342 n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL;
343 n1->nbinary.ch1 = list(0);
344 if ((got=readtoken()) != TDO) {
345TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : ""));
346 synexpect(TDO);
347 }
348 n1->nbinary.ch2 = list(0);
349 if (readtoken() != TDONE)
350 synexpect(TDONE);
351 checkkwd = 1;
352 break;
353 }
354 case TFOR:
355 if (readtoken() != TWORD || quoteflag || ! goodname(wordtext))
356 synerror("Bad for loop variable");
357 n1 = (union node *)stalloc(sizeof (struct nfor));
358 n1->type = NFOR;
359 n1->nfor.var = wordtext;
360 if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) {
361 app = &ap;
362 while (readtoken() == TWORD) {
363 n2 = (union node *)stalloc(sizeof (struct narg));
364 n2->type = NARG;
365 n2->narg.text = wordtext;
366 n2->narg.backquote = backquotelist;
367 *app = n2;
368 app = &n2->narg.next;
369 }
370 *app = NULL;
371 n1->nfor.args = ap;
372 if (lasttoken != TNL && lasttoken != TSEMI)
373 synexpect(-1);
374 } else {
375 static char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE,
376 '@', '=', '\0'};
377 n2 = (union node *)stalloc(sizeof (struct narg));
378 n2->type = NARG;
379 n2->narg.text = argvars;
380 n2->narg.backquote = NULL;
381 n2->narg.next = NULL;
382 n1->nfor.args = n2;
383 /*
384 * Newline or semicolon here is optional (but note
385 * that the original Bourne shell only allowed NL).
386 */
387 if (lasttoken != TNL && lasttoken != TSEMI)
388 tokpushback++;
389 }
390 checkkwd = 2;
391 if ((t = readtoken()) == TDO)
392 t = TDONE;
393 else if (t == TBEGIN)
394 t = TEND;
395 else
396 synexpect(-1);
397 n1->nfor.body = list(0);
398 if (readtoken() != t)
399 synexpect(t);
400 checkkwd = 1;
401 break;
402 case TCASE:
403 n1 = (union node *)stalloc(sizeof (struct ncase));
404 n1->type = NCASE;
405 if (readtoken() != TWORD)
406 synexpect(TWORD);
407 n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg));
408 n2->type = NARG;
409 n2->narg.text = wordtext;
410 n2->narg.backquote = backquotelist;
411 n2->narg.next = NULL;
412 while (readtoken() == TNL);
413 if (lasttoken != TWORD || ! equal(wordtext, "in"))
414 synerror("expecting \"in\"");
415 cpp = &n1->ncase.cases;
416 checkkwd = 2, readtoken();
417 do {
418 if (lasttoken == TLP)
419 readtoken();
420 *cpp = cp = (union node *)stalloc(sizeof (struct nclist));
421 cp->type = NCLIST;
422 app = &cp->nclist.pattern;
423 for (;;) {
424 *app = ap = (union node *)stalloc(sizeof (struct narg));
425 ap->type = NARG;
426 ap->narg.text = wordtext;
427 ap->narg.backquote = backquotelist;
428 if (checkkwd = 2, readtoken() != TPIPE)
429 break;
430 app = &ap->narg.next;
431 readtoken();
432 }
433 ap->narg.next = NULL;
434 if (lasttoken != TRP)
435 synexpect(TRP);
436 cp->nclist.body = list(0);
437
438 checkkwd = 2;
439 if ((t = readtoken()) != TESAC) {
440 if (t != TENDCASE)
441 synexpect(TENDCASE);
442 else
443 checkkwd = 2, readtoken();
444 }
445 cpp = &cp->nclist.next;
446 } while(lasttoken != TESAC);
447 *cpp = NULL;
448 checkkwd = 1;
449 break;
450 case TLP:
451 n1 = (union node *)stalloc(sizeof (struct nredir));
452 n1->type = NSUBSHELL;
453 n1->nredir.n = list(0);
454 n1->nredir.redirect = NULL;
455 if (readtoken() != TRP)
456 synexpect(TRP);
457 checkkwd = 1;
458 break;
459 case TBEGIN:
460 n1 = list(0);
461 if (readtoken() != TEND)
462 synexpect(TEND);
463 checkkwd = 1;
464 break;
465 /* Handle an empty command like other simple commands. */
466 case TSEMI:
467 case TAND:
468 case TOR:
469 case TNL:
470 case TEOF:
471 case TRP:
472 case TBACKGND:
473 /*
474 * An empty command before a ; doesn't make much sense, and
475 * should certainly be disallowed in the case of `if ;'.
476 */
477 if (!redir)
478 synexpect(-1);
479 case TWORD:
480 tokpushback++;
481 n1 = simplecmd(rpp, redir);
482 return n1;
483 default:
484 synexpect(-1);
485 /* NOTREACHED */
486 }
487
488 /* Now check for redirection which may follow command */
489 while (readtoken() == TREDIR) {
490 *rpp = n2 = redirnode;
491 rpp = &n2->nfile.next;
492 parsefname();
493 }
494 tokpushback++;
495 *rpp = NULL;
496 if (redir) {
497 if (n1->type != NSUBSHELL) {
498 n2 = (union node *)stalloc(sizeof (struct nredir));
499 n2->type = NREDIR;
500 n2->nredir.n = n1;
501 n1 = n2;
502 }
503 n1->nredir.redirect = redir;
504 }
505
506 return n1;
507}
508
509
510STATIC union node *
511simplecmd(rpp, redir)
512 union node **rpp, *redir;
513 {
514 union node *args, **app;
515 union node **orig_rpp = rpp;
516 union node *n = NULL;
517
518 /* If we don't have any redirections already, then we must reset */
519 /* rpp to be the address of the local redir variable. */
520 if (redir == 0)
521 rpp = &redir;
522
523 args = NULL;
524 app = &args;
525 /*
526 * We save the incoming value, because we need this for shell
527 * functions. There can not be a redirect or an argument between
528 * the function name and the open parenthesis.
529 */
530 orig_rpp = rpp;
531
532 for (;;) {
533 if (readtoken() == TWORD) {
534 n = (union node *)stalloc(sizeof (struct narg));
535 n->type = NARG;
536 n->narg.text = wordtext;
537 n->narg.backquote = backquotelist;
538 *app = n;
539 app = &n->narg.next;
540 } else if (lasttoken == TREDIR) {
541 *rpp = n = redirnode;
542 rpp = &n->nfile.next;
543 parsefname(); /* read name of redirection file */
544 } else if (lasttoken == TLP && app == &args->narg.next
545 && rpp == orig_rpp) {
546 /* We have a function */
547 if (readtoken() != TRP)
548 synexpect(TRP);
549#ifdef notdef
550 if (! goodname(n->narg.text))
551 synerror("Bad function name");
552#endif
553 n->type = NDEFUN;
554 checkkwd = 2;
555 n->narg.next = command();
556 return n;
557 } else {
558 tokpushback++;
559 break;
560 }
561 }
562 *app = NULL;
563 *rpp = NULL;
564 n = (union node *)stalloc(sizeof (struct ncmd));
565 n->type = NCMD;
566 n->ncmd.backgnd = 0;
567 n->ncmd.args = args;
568 n->ncmd.redirect = redir;
569 return n;
570}
571
572STATIC union node *
573makename() {
574 union node *n;
575
576 n = (union node *)stalloc(sizeof (struct narg));
577 n->type = NARG;
578 n->narg.next = NULL;
579 n->narg.text = wordtext;
580 n->narg.backquote = backquotelist;
581 return n;
582}
583
584void fixredir(union node *n, const char *text, int err)
585 {
586 TRACE(("Fix redir %s %d\n", text, err));
587 if (!err)
588 n->ndup.vname = NULL;
589
590 if (is_digit(text[0]) && text[1] == '\0')
591 n->ndup.dupfd = digit_val(text[0]);
592 else if (text[0] == '-' && text[1] == '\0')
593 n->ndup.dupfd = -1;
594 else {
595
596 if (err)
597 synerror("Bad fd number");
598 else
599 n->ndup.vname = makename();
600 }
601}
602
603
604STATIC void
605parsefname() {
606 union node *n = redirnode;
607
608 if (readtoken() != TWORD)
609 synexpect(-1);
610 if (n->type == NHERE) {
611 struct heredoc *here = heredoc;
612 struct heredoc *p;
613 int i;
614
615 if (quoteflag == 0)
616 n->type = NXHERE;
617 TRACE(("Here document %d\n", n->type));
618 if (here->striptabs) {
619 while (*wordtext == '\t')
620 wordtext++;
621 }
622 if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN)
623 synerror("Illegal eof marker for << redirection");
624 rmescapes(wordtext);
625 here->eofmark = wordtext;
626 here->next = NULL;
627 if (heredoclist == NULL)
628 heredoclist = here;
629 else {
630 for (p = heredoclist ; p->next ; p = p->next);
631 p->next = here;
632 }
633 } else if (n->type == NTOFD || n->type == NFROMFD) {
634 fixredir(n, wordtext, 0);
635 } else {
636 n->nfile.fname = makename();
637 }
638}
639
640
641/*
642 * Input any here documents.
643 */
644
645STATIC void
646parseheredoc() {
647 struct heredoc *here;
648 union node *n;
649
650 while (heredoclist) {
651 here = heredoclist;
652 heredoclist = here->next;
653 if (needprompt) {
654 setprompt(2);
655 needprompt = 0;
656 }
657 readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
658 here->eofmark, here->striptabs);
659 n = (union node *)stalloc(sizeof (struct narg));
660 n->narg.type = NARG;
661 n->narg.next = NULL;
662 n->narg.text = wordtext;
663 n->narg.backquote = backquotelist;
664 here->here->nhere.doc = n;
665 }
666}
667
668STATIC int
669peektoken() {
670 int t;
671
672 t = readtoken();
673 tokpushback++;
674 return (t);
675}
676
677STATIC int
678readtoken() {
679 int t;
680 int savecheckkwd = checkkwd;
681 struct alias *ap;
682#ifdef DEBUG
683 int alreadyseen = tokpushback;
684#endif
685
686 top:
687 t = xxreadtoken();
688
689 if (checkkwd) {
690 /*
691 * eat newlines
692 */
693 if (checkkwd == 2) {
694 checkkwd = 0;
695 while (t == TNL) {
696 parseheredoc();
697 t = xxreadtoken();
698 }
699 } else
700 checkkwd = 0;
701 /*
702 * check for keywords and aliases
703 */
704 if (t == TWORD && !quoteflag)
705 {
706 const char *const *pp;
707
708 for (pp = parsekwd; *pp; pp++) {
709 if (**pp == *wordtext && equal(*pp, wordtext))
710 {
711 lasttoken = t = pp -
712 parsekwd + KWDOFFSET;
713 TRACE(("keyword %s recognized\n", tokname[t]));
714 goto out;
715 }
716 }
717 if ((ap = lookupalias(wordtext, 1)) != NULL) {
718 pushstring(ap->val, strlen(ap->val), ap);
719 checkkwd = savecheckkwd;
720 goto top;
721 }
722 }
723out:
724 checkkwd = 0;
725 }
726#ifdef DEBUG
727 if (!alreadyseen)
728 TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
729 else
730 TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : ""));
731#endif
732 return (t);
733}
734
735
736/*
737 * Read the next input token.
738 * If the token is a word, we set backquotelist to the list of cmds in
739 * backquotes. We set quoteflag to true if any part of the word was
740 * quoted.
741 * If the token is TREDIR, then we set redirnode to a structure containing
742 * the redirection.
743 * In all cases, the variable startlinno is set to the number of the line
744 * on which the token starts.
745 *
746 * [Change comment: here documents and internal procedures]
747 * [Readtoken shouldn't have any arguments. Perhaps we should make the
748 * word parsing code into a separate routine. In this case, readtoken
749 * doesn't need to have any internal procedures, but parseword does.
750 * We could also make parseoperator in essence the main routine, and
751 * have parseword (readtoken1?) handle both words and redirection.]
752 */
753
754#define RETURN(token) return lasttoken = token
755
756STATIC int
757xxreadtoken() {
758 int c;
759
760 if (tokpushback) {
761 tokpushback = 0;
762 return lasttoken;
763 }
764 if (needprompt) {
765 setprompt(2);
766 needprompt = 0;
767 }
768 startlinno = plinno;
769 for (;;) { /* until token or start of word found */
770 c = pgetc_macro();
771 if (c == ' ' || c == '\t')
772 continue; /* quick check for white space first */
773 switch (c) {
774 case ' ': case '\t':
775 continue;
776 case '#':
777 while ((c = pgetc()) != '\n' && c != PEOF);
778 pungetc();
779 continue;
780 case '\\':
781 if (pgetc() == '\n') {
782 startlinno = ++plinno;
783 if (doprompt)
784 setprompt(2);
785 else
786 setprompt(0);
787 continue;
788 }
789 pungetc();
790 goto breakloop;
791 case '\n':
792 plinno++;
793 needprompt = doprompt;
794 RETURN(TNL);
795 case PEOF:
796 RETURN(TEOF);
797 case '&':
798 if (pgetc() == '&')
799 RETURN(TAND);
800 pungetc();
801 RETURN(TBACKGND);
802 case '|':
803 if (pgetc() == '|')
804 RETURN(TOR);
805 pungetc();
806 RETURN(TPIPE);
807 case ';':
808 if (pgetc() == ';')
809 RETURN(TENDCASE);
810 pungetc();
811 RETURN(TSEMI);
812 case '(':
813 RETURN(TLP);
814 case ')':
815 RETURN(TRP);
816 default:
817 goto breakloop;
818 }
819 }
820breakloop:
821 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
822#undef RETURN
823}
824
825
826
827/*
828 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
829 * is not NULL, read a here document. In the latter case, eofmark is the
830 * word which marks the end of the document and striptabs is true if
831 * leading tabs should be stripped from the document. The argument firstc
832 * is the first character of the input token or document.
833 *
834 * Because C does not have internal subroutines, I have simulated them
835 * using goto's to implement the subroutine linkage. The following macros
836 * will run code that appears at the end of readtoken1.
837 */
838
839#define CHECKEND() {goto checkend; checkend_return:;}
840#define PARSEREDIR() {goto parseredir; parseredir_return:;}
841#define PARSESUB() {goto parsesub; parsesub_return:;}
842#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
843#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
844#define PARSEARITH() {goto parsearith; parsearith_return:;}
845
846STATIC int
847readtoken1(firstc, syntax, eofmark, striptabs)
848 int firstc;
849 char const *syntax;
850 char *eofmark;
851 int striptabs;
852 {
853 int c = firstc;
854 char *out;
855 int len;
856 char line[EOFMARKLEN + 1];
857 struct nodelist *bqlist;
858 int quotef;
859 int dblquote;
860 int varnest; /* levels of variables expansion */
861 int arinest; /* levels of arithmetic expansion */
862 int parenlevel; /* levels of parens in arithmetic */
863 int dqvarnest; /* levels of variables expansion within double quotes */
864 int oldstyle;
865 char const *prevsyntax; /* syntax before arithmetic */
866#if __GNUC__
867 /* Avoid longjmp clobbering */
868 (void) &out;
869 (void) &quotef;
870 (void) &dblquote;
871 (void) &varnest;
872 (void) &arinest;
873 (void) &parenlevel;
874 (void) &dqvarnest;
875 (void) &oldstyle;
876 (void) &prevsyntax;
877 (void) &syntax;
878#endif
879
880 startlinno = plinno;
881 dblquote = 0;
882 if (syntax == DQSYNTAX)
883 dblquote = 1;
884 quotef = 0;
885 bqlist = NULL;
886 varnest = 0;
887 arinest = 0;
888 parenlevel = 0;
889 dqvarnest = 0;
890
891 STARTSTACKSTR(out);
892 loop: { /* for each line, until end of word */
893#if ATTY
894 if (c == '\034' && doprompt
895 && attyset() && ! equal(termval(), "emacs")) {
896 attyline();
897 if (syntax == BASESYNTAX)
898 return readtoken();
899 c = pgetc();
900 goto loop;
901 }
902#endif
903 CHECKEND(); /* set c to PEOF if at end of here document */
904 for (;;) { /* until end of line or end of word */
905 CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */
906 switch(syntax[c]) {
907 case CNL: /* '\n' */
908 if (syntax == BASESYNTAX)
909 goto endword; /* exit outer loop */
910 USTPUTC(c, out);
911 plinno++;
912 if (doprompt)
913 setprompt(2);
914 else
915 setprompt(0);
916 c = pgetc();
917 goto loop; /* continue outer loop */
918 case CWORD:
919 USTPUTC(c, out);
920 break;
921 case CCTL:
922 if ((eofmark == NULL || dblquote) &&
923 dqvarnest == 0)
924 USTPUTC(CTLESC, out);
925 USTPUTC(c, out);
926 break;
927 case CBACK: /* backslash */
928 c = pgetc();
929 if (c == PEOF) {
930 USTPUTC('\\', out);
931 pungetc();
932 } else if (c == '\n') {
933 if (doprompt)
934 setprompt(2);
935 else
936 setprompt(0);
937 } else {
938 if (dblquote && c != '\\' && c != '`' && c != '$'
939 && (c != '"' || eofmark != NULL))
940 USTPUTC('\\', out);
941 if (SQSYNTAX[c] == CCTL)
942 USTPUTC(CTLESC, out);
943 else if (eofmark == NULL)
944 USTPUTC(CTLQUOTEMARK, out);
945 USTPUTC(c, out);
946 quotef++;
947 }
948 break;
949 case CSQUOTE:
950 if (eofmark == NULL)
951 USTPUTC(CTLQUOTEMARK, out);
952 syntax = SQSYNTAX;
953 break;
954 case CDQUOTE:
955 if (eofmark == NULL)
956 USTPUTC(CTLQUOTEMARK, out);
957 syntax = DQSYNTAX;
958 dblquote = 1;
959 break;
960 case CENDQUOTE:
961 if (eofmark != NULL && arinest == 0 &&
962 varnest == 0) {
963 USTPUTC(c, out);
964 } else {
965 if (arinest) {
966 syntax = ARISYNTAX;
967 dblquote = 0;
968 } else if (eofmark == NULL &&
969 dqvarnest == 0) {
970 syntax = BASESYNTAX;
971 dblquote = 0;
972 }
973 quotef++;
974 }
975 break;
976 case CVAR: /* '$' */
977 PARSESUB(); /* parse substitution */
978 break;
979 case CENDVAR: /* '}' */
980 if (varnest > 0) {
981 varnest--;
982 if (dqvarnest > 0) {
983 dqvarnest--;
984 }
985 USTPUTC(CTLENDVAR, out);
986 } else {
987 USTPUTC(c, out);
988 }
989 break;
990 case CLP: /* '(' in arithmetic */
991 parenlevel++;
992 USTPUTC(c, out);
993 break;
994 case CRP: /* ')' in arithmetic */
995 if (parenlevel > 0) {
996 USTPUTC(c, out);
997 --parenlevel;
998 } else {
999 if (pgetc() == ')') {
1000 if (--arinest == 0) {
1001 USTPUTC(CTLENDARI, out);
1002 syntax = prevsyntax;
1003 if (syntax == DQSYNTAX)
1004 dblquote = 1;
1005 else
1006 dblquote = 0;
1007 } else
1008 USTPUTC(')', out);
1009 } else {
1010 /*
1011 * unbalanced parens
1012 * (don't 2nd guess - no error)
1013 */
1014 pungetc();
1015 USTPUTC(')', out);
1016 }
1017 }
1018 break;
1019 case CBQUOTE: /* '`' */
1020 PARSEBACKQOLD();
1021 break;
1022 case CEOF:
1023 goto endword; /* exit outer loop */
1024 default:
1025 if (varnest == 0)
1026 goto endword; /* exit outer loop */
1027 USTPUTC(c, out);
1028 }
1029 c = pgetc_macro();
1030 }
1031 }
1032endword:
1033 if (syntax == ARISYNTAX)
1034 synerror("Missing '))'");
1035 if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL)
1036 synerror("Unterminated quoted string");
1037 if (varnest != 0) {
1038 startlinno = plinno;
1039 synerror("Missing '}'");
1040 }
1041 USTPUTC('\0', out);
1042 len = out - stackblock();
1043 out = stackblock();
1044 if (eofmark == NULL) {
1045 if ((c == '>' || c == '<')
1046 && quotef == 0
1047 && len <= 2
1048 && (*out == '\0' || is_digit(*out))) {
1049 PARSEREDIR();
1050 return lasttoken = TREDIR;
1051 } else {
1052 pungetc();
1053 }
1054 }
1055 quoteflag = quotef;
1056 backquotelist = bqlist;
1057 grabstackblock(len);
1058 wordtext = out;
1059 return lasttoken = TWORD;
1060/* end of readtoken routine */
1061
1062
1063
1064/*
1065 * Check to see whether we are at the end of the here document. When this
1066 * is called, c is set to the first character of the next input line. If
1067 * we are at the end of the here document, this routine sets the c to PEOF.
1068 */
1069
1070checkend: {
1071 if (eofmark) {
1072 if (striptabs) {
1073 while (c == '\t')
1074 c = pgetc();
1075 }
1076 if (c == *eofmark) {
1077 if (pfgets(line, sizeof line) != NULL) {
1078 char *p, *q;
1079
1080 p = line;
1081 for (q = eofmark + 1 ; *q && *p == *q ; p++, q++);
1082 if (*p == '\n' && *q == '\0') {
1083 c = PEOF;
1084 plinno++;
1085 needprompt = doprompt;
1086 } else {
1087 pushstring(line, strlen(line), NULL);
1088 }
1089 }
1090 }
1091 }
1092 goto checkend_return;
1093}
1094
1095
1096/*
1097 * Parse a redirection operator. The variable "out" points to a string
1098 * specifying the fd to be redirected. The variable "c" contains the
1099 * first character of the redirection operator.
1100 */
1101
1102parseredir: {
1103 char fd = *out;
1104 union node *np;
1105
1106 np = (union node *)stalloc(sizeof (struct nfile));
1107 if (c == '>') {
1108 np->nfile.fd = 1;
1109 c = pgetc();
1110 if (c == '>')
1111 np->type = NAPPEND;
1112 else if (c == '&')
1113 np->type = NTOFD;
1114 else if (c == '|')
1115 np->type = NTOOV;
1116 else {
1117 np->type = NTO;
1118 pungetc();
1119 }
1120 } else { /* c == '<' */
1121 np->nfile.fd = 0;
1122 switch (c = pgetc()) {
1123 case '<':
1124 if (sizeof (struct nfile) != sizeof (struct nhere)) {
1125 np = (union node *)stalloc(sizeof (struct nhere));
1126 np->nfile.fd = 0;
1127 }
1128 np->type = NHERE;
1129 heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc));
1130 heredoc->here = np;
1131 if ((c = pgetc()) == '-') {
1132 heredoc->striptabs = 1;
1133 } else {
1134 heredoc->striptabs = 0;
1135 pungetc();
1136 }
1137 break;
1138
1139 case '&':
1140 np->type = NFROMFD;
1141 break;
1142
1143 case '>':
1144 np->type = NFROMTO;
1145 break;
1146
1147 default:
1148 np->type = NFROM;
1149 pungetc();
1150 break;
1151 }
1152 }
1153 if (fd != '\0')
1154 np->nfile.fd = digit_val(fd);
1155 redirnode = np;
1156 goto parseredir_return;
1157}
1158
1159
1160/*
1161 * Parse a substitution. At this point, we have read the dollar sign
1162 * and nothing else.
1163 */
1164
1165parsesub: {
1166 int subtype;
1167 int typeloc;
1168 int flags;
1169 char *p;
1170 static const char types[] = "}-+?=";
1171
1172 c = pgetc();
1173 if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) {
1174 USTPUTC('$', out);
1175 pungetc();
1176 } else if (c == '(') { /* $(command) or $((arith)) */
1177 if (pgetc() == '(') {
1178 PARSEARITH();
1179 } else {
1180 pungetc();
1181 PARSEBACKQNEW();
1182 }
1183 } else {
1184 USTPUTC(CTLVAR, out);
1185 typeloc = out - stackblock();
1186 USTPUTC(VSNORMAL, out);
1187 subtype = VSNORMAL;
1188 if (c == '{') {
1189 c = pgetc();
1190 if (c == '#') {
1191 if ((c = pgetc()) == '}')
1192 c = '#';
1193 else
1194 subtype = VSLENGTH;
1195 }
1196 else
1197 subtype = 0;
1198 }
1199 if (is_name(c)) {
1200 do {
1201 STPUTC(c, out);
1202 c = pgetc();
1203 } while (is_in_name(c));
1204 } else if (is_digit(c)) {
1205 do {
1206 USTPUTC(c, out);
1207 c = pgetc();
1208 } while (is_digit(c));
1209 }
1210 else if (is_special(c)) {
1211 USTPUTC(c, out);
1212 c = pgetc();
1213 }
1214 else
1215badsub: synerror("Bad substitution");
1216
1217 STPUTC('=', out);
1218 flags = 0;
1219 if (subtype == 0) {
1220 switch (c) {
1221 case ':':
1222 flags = VSNUL;
1223 c = pgetc();
1224 /*FALLTHROUGH*/
1225 default:
1226 p = strchr(types, c);
1227 if (p == NULL)
1228 goto badsub;
1229 subtype = p - types + VSNORMAL;
1230 break;
1231 case '%':
1232 case '#':
1233 {
1234 int cc = c;
1235 subtype = c == '#' ? VSTRIMLEFT :
1236 VSTRIMRIGHT;
1237 c = pgetc();
1238 if (c == cc)
1239 subtype++;
1240 else
1241 pungetc();
1242 break;
1243 }
1244 }
1245 } else {
1246 pungetc();
1247 }
1248 if (dblquote || arinest)
1249 flags |= VSQUOTE;
1250 *(stackblock() + typeloc) = subtype | flags;
1251 if (subtype != VSNORMAL) {
1252 varnest++;
1253 if (dblquote) {
1254 dqvarnest++;
1255 }
1256 }
1257 }
1258 goto parsesub_return;
1259}
1260
1261
1262/*
1263 * Called to parse command substitutions. Newstyle is set if the command
1264 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
1265 * list of commands (passed by reference), and savelen is the number of
1266 * characters on the top of the stack which must be preserved.
1267 */
1268
1269parsebackq: {
1270 struct nodelist **nlpp;
1271 int savepbq;
1272 union node *n;
1273 char *volatile str;
1274 struct jmploc jmploc;
1275 struct jmploc *volatile savehandler;
1276 int savelen;
1277 int saveprompt;
1278#ifdef __GNUC__
1279 (void) &saveprompt;
1280#endif
1281
1282 savepbq = parsebackquote;
1283 if (setjmp(jmploc.loc)) {
1284 if (str)
1285 ckfree(str);
1286 parsebackquote = 0;
1287 handler = savehandler;
1288 longjmp(handler->loc, 1);
1289 }
1290 INTOFF;
1291 str = NULL;
1292 savelen = out - stackblock();
1293 if (savelen > 0) {
1294 str = ckmalloc(savelen);
1295 memcpy(str, stackblock(), savelen);
1296 }
1297 savehandler = handler;
1298 handler = &jmploc;
1299 INTON;
1300 if (oldstyle) {
1301 /* We must read until the closing backquote, giving special
1302 treatment to some slashes, and then push the string and
1303 reread it as input, interpreting it normally. */
1304 char *pout;
1305 int pc;
1306 int psavelen;
1307 char *pstr;
1308
1309
1310 STARTSTACKSTR(pout);
1311 for (;;) {
1312 if (needprompt) {
1313 setprompt(2);
1314 needprompt = 0;
1315 }
1316 switch (pc = pgetc()) {
1317 case '`':
1318 goto done;
1319
1320 case '\\':
1321 if ((pc = pgetc()) == '\n') {
1322 plinno++;
1323 if (doprompt)
1324 setprompt(2);
1325 else
1326 setprompt(0);
1327 /*
1328 * If eating a newline, avoid putting
1329 * the newline into the new character
1330 * stream (via the STPUTC after the
1331 * switch).
1332 */
1333 continue;
1334 }
1335 if (pc != '\\' && pc != '`' && pc != '$'
1336 && (!dblquote || pc != '"'))
1337 STPUTC('\\', pout);
1338 break;
1339
1340 case '\n':
1341 plinno++;
1342 needprompt = doprompt;
1343 break;
1344
1345 case PEOF:
1346 startlinno = plinno;
1347 synerror("EOF in backquote substitution");
1348 break;
1349
1350 default:
1351 break;
1352 }
1353 STPUTC(pc, pout);
1354 }
1355done:
1356 STPUTC('\0', pout);
1357 psavelen = pout - stackblock();
1358 if (psavelen > 0) {
1359 pstr = grabstackstr(pout);
1360 setinputstring(pstr, 1);
1361 }
1362 }
1363 nlpp = &bqlist;
1364 while (*nlpp)
1365 nlpp = &(*nlpp)->next;
1366 *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist));
1367 (*nlpp)->next = NULL;
1368 parsebackquote = oldstyle;
1369
1370 if (oldstyle) {
1371 saveprompt = doprompt;
1372 doprompt = 0;
1373 }
1374
1375 n = list(0);
1376
1377 if (oldstyle)
1378 doprompt = saveprompt;
1379 else {
1380 if (readtoken() != TRP)
1381 synexpect(TRP);
1382 }
1383
1384 (*nlpp)->n = n;
1385 if (oldstyle) {
1386 /*
1387 * Start reading from old file again, ignoring any pushed back
1388 * tokens left from the backquote parsing
1389 */
1390 popfile();
1391 tokpushback = 0;
1392 }
1393 while (stackblocksize() <= savelen)
1394 growstackblock();
1395 STARTSTACKSTR(out);
1396 if (str) {
1397 memcpy(out, str, savelen);
1398 STADJUST(savelen, out);
1399 INTOFF;
1400 ckfree(str);
1401 str = NULL;
1402 INTON;
1403 }
1404 parsebackquote = savepbq;
1405 handler = savehandler;
1406 if (arinest || dblquote)
1407 USTPUTC(CTLBACKQ | CTLQUOTE, out);
1408 else
1409 USTPUTC(CTLBACKQ, out);
1410 if (oldstyle)
1411 goto parsebackq_oldreturn;
1412 else
1413 goto parsebackq_newreturn;
1414}
1415
1416/*
1417 * Parse an arithmetic expansion (indicate start of one and set state)
1418 */
1419parsearith: {
1420
1421 if (++arinest == 1) {
1422 prevsyntax = syntax;
1423 syntax = ARISYNTAX;
1424 USTPUTC(CTLARI, out);
1425 if (dblquote)
1426 USTPUTC('"',out);
1427 else
1428 USTPUTC(' ',out);
1429 } else {
1430 /*
1431 * we collapse embedded arithmetic expansion to
1432 * parenthesis, which should be equivalent
1433 */
1434 USTPUTC('(', out);
1435 }
1436 goto parsearith_return;
1437}
1438
1439} /* end of readtoken */
1440
1441
1442
1443#ifdef mkinit
1444RESET {
1445 tokpushback = 0;
1446 checkkwd = 0;
1447}
1448#endif
1449
1450/*
1451 * Returns true if the text contains nothing to expand (no dollar signs
1452 * or backquotes).
1453 */
1454
1455STATIC int
1456noexpand(text)
1457 char *text;
1458 {
1459 char *p;
1460 char c;
1461
1462 p = text;
1463 while ((c = *p++) != '\0') {
1464 if (c == CTLQUOTEMARK)
1465 continue;
1466 if (c == CTLESC)
1467 p++;
1468 else if (BASESYNTAX[(int)c] == CCTL)
1469 return 0;
1470 }
1471 return 1;
1472}
1473
1474
1475/*
1476 * Return true if the argument is a legal variable name (a letter or
1477 * underscore followed by zero or more letters, underscores, and digits).
1478 */
1479
1480int
1481goodname(char *name)
1482 {
1483 char *p;
1484
1485 p = name;
1486 if (! is_name(*p))
1487 return 0;
1488 while (*++p) {
1489 if (! is_in_name(*p))
1490 return 0;
1491 }
1492 return 1;
1493}
1494
1495
1496/*
1497 * Called when an unexpected token is read during the parse. The argument
1498 * is the token that is expected, or -1 if more than one type of token can
1499 * occur at this point.
1500 */
1501
1502STATIC void
1503synexpect(token)
1504 int token;
1505{
1506 char msg[64];
1507
1508 if (token >= 0) {
1509 fmtstr(msg, 64, "%s unexpected (expecting %s)",
1510 tokname[lasttoken], tokname[token]);
1511 } else {
1512 fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]);
1513 }
1514 synerror(msg);
1515 /* NOTREACHED */
1516}
1517
1518
1519STATIC void
1520synerror(msg)
1521 const char *msg;
1522 {
1523 if (commandname)
1524 outfmt(&errout, "%s: %d: ", commandname, startlinno);
1525 outfmt(&errout, "Syntax error: %s\n", msg);
1526 error((char *)NULL);
1527 /* NOTREACHED */
1528}
1529
1530STATIC void
1531setprompt(which)
1532 int which;
1533 {
1534 whichprompt = which;
1535
1536#ifndef SMALL
1537 if (!el)
1538#endif
1539 out2str(getprompt(NULL));
1540}
1541
1542/*
1543 * called by editline -- any expansions to the prompt
1544 * should be added here.
1545 */
1546const char *
1547getprompt(void *unused)
1548 {
1549 switch (whichprompt) {
1550 case 0:
1551 return "";
1552 case 1:
1553 return ps1val();
1554 case 2:
1555 return ps2val();
1556 default:
1557 return "<internal prompt error>";
1558 }
1559}
Note: See TracBrowser for help on using the repository browser.