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