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