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