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