]>
Commit | Line | Data |
---|---|---|
71aad674 | 1 | /*- |
254f12f7 A |
2 | * SPDX-License-Identifier: BSD-3-Clause |
3 | * | |
71aad674 A |
4 | * Copyright (c) 1991, 1993 |
5 | * The Regents of the University of California. All rights reserved. | |
6 | * | |
7 | * This code is derived from software contributed to Berkeley by | |
8 | * Kenneth Almquist. | |
9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | |
15 | * 2. Redistributions in binary form must reproduce the above copyright | |
16 | * notice, this list of conditions and the following disclaimer in the | |
17 | * documentation and/or other materials provided with the distribution. | |
254f12f7 | 18 | * 3. Neither the name of the University nor the names of its contributors |
71aad674 A |
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 | #ifndef lint | |
36 | #if 0 | |
37 | static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; | |
38 | #endif | |
39 | #endif /* not lint */ | |
40 | #include <sys/cdefs.h> | |
254f12f7 | 41 | __FBSDID("$FreeBSD: head/bin/sh/parser.c 326025 2017-11-20 19:49:47Z pfg $"); |
71aad674 A |
42 | |
43 | #include <stdlib.h> | |
44 | #include <unistd.h> | |
45 | #include <stdio.h> | |
46 | ||
47 | #include "shell.h" | |
48 | #include "parser.h" | |
49 | #include "nodes.h" | |
50 | #include "expand.h" /* defines rmescapes() */ | |
51 | #include "syntax.h" | |
52 | #include "options.h" | |
53 | #include "input.h" | |
54 | #include "output.h" | |
55 | #include "var.h" | |
56 | #include "error.h" | |
57 | #include "memalloc.h" | |
58 | #include "mystring.h" | |
59 | #include "alias.h" | |
60 | #include "show.h" | |
61 | #include "eval.h" | |
62 | #include "exec.h" /* to check for special builtins */ | |
63 | #ifndef NO_HISTORY | |
64 | #include "myhistedit.h" | |
65 | #endif | |
66 | ||
67 | /* | |
68 | * Shell command parser. | |
69 | */ | |
70 | ||
71 | #define PROMPTLEN 128 | |
72 | ||
73 | /* values of checkkwd variable */ | |
74 | #define CHKALIAS 0x1 | |
75 | #define CHKKWD 0x2 | |
76 | #define CHKNL 0x4 | |
77 | ||
78 | /* values returned by readtoken */ | |
79 | #include "token.h" | |
80 | ||
81 | ||
82 | ||
83 | struct heredoc { | |
84 | struct heredoc *next; /* next here document in list */ | |
85 | union node *here; /* redirection node */ | |
86 | char *eofmark; /* string indicating end of input */ | |
87 | int striptabs; /* if set, strip leading tabs */ | |
88 | }; | |
89 | ||
90 | struct parser_temp { | |
91 | struct parser_temp *next; | |
92 | void *data; | |
93 | }; | |
94 | ||
95 | ||
96 | static struct heredoc *heredoclist; /* list of here documents to read */ | |
97 | static int doprompt; /* if set, prompt the user */ | |
98 | static int needprompt; /* true if interactive and at start of line */ | |
99 | static int lasttoken; /* last token read */ | |
100 | static int tokpushback; /* last token pushed back */ | |
101 | static char *wordtext; /* text of last word returned by readtoken */ | |
102 | static int checkkwd; | |
103 | static struct nodelist *backquotelist; | |
104 | static union node *redirnode; | |
105 | static struct heredoc *heredoc; | |
106 | static int quoteflag; /* set if (part of) last token was quoted */ | |
107 | static int startlinno; /* line # where last token started */ | |
108 | static int funclinno; /* line # where the current function started */ | |
109 | static struct parser_temp *parser_temp; | |
110 | ||
deb63bfb A |
111 | #define NOEOFMARK ((const char *)&heredoclist) |
112 | ||
71aad674 A |
113 | |
114 | static union node *list(int); | |
115 | static union node *andor(void); | |
116 | static union node *pipeline(void); | |
117 | static union node *command(void); | |
118 | static union node *simplecmd(union node **, union node *); | |
119 | static union node *makename(void); | |
120 | static union node *makebinary(int type, union node *n1, union node *n2); | |
121 | static void parsefname(void); | |
122 | static void parseheredoc(void); | |
123 | static int peektoken(void); | |
124 | static int readtoken(void); | |
125 | static int xxreadtoken(void); | |
126 | static int readtoken1(int, const char *, const char *, int); | |
127 | static int noexpand(char *); | |
128 | static void consumetoken(int); | |
129 | static void synexpect(int) __dead2; | |
130 | static void synerror(const char *) __dead2; | |
131 | static void setprompt(int); | |
132 | static int pgetc_linecont(void); | |
133 | ||
134 | ||
135 | static void * | |
136 | parser_temp_alloc(size_t len) | |
137 | { | |
138 | struct parser_temp *t; | |
139 | ||
140 | INTOFF; | |
141 | t = ckmalloc(sizeof(*t)); | |
142 | t->data = NULL; | |
143 | t->next = parser_temp; | |
144 | parser_temp = t; | |
145 | t->data = ckmalloc(len); | |
146 | INTON; | |
147 | return t->data; | |
148 | } | |
149 | ||
150 | ||
151 | static void * | |
152 | parser_temp_realloc(void *ptr, size_t len) | |
153 | { | |
154 | struct parser_temp *t; | |
155 | ||
156 | INTOFF; | |
157 | t = parser_temp; | |
158 | if (ptr != t->data) | |
159 | error("bug: parser_temp_realloc misused"); | |
160 | t->data = ckrealloc(t->data, len); | |
161 | INTON; | |
162 | return t->data; | |
163 | } | |
164 | ||
165 | ||
166 | static void | |
167 | parser_temp_free_upto(void *ptr) | |
168 | { | |
169 | struct parser_temp *t; | |
170 | int done = 0; | |
171 | ||
172 | INTOFF; | |
173 | while (parser_temp != NULL && !done) { | |
174 | t = parser_temp; | |
175 | parser_temp = t->next; | |
176 | done = t->data == ptr; | |
177 | ckfree(t->data); | |
178 | ckfree(t); | |
179 | } | |
180 | INTON; | |
181 | if (!done) | |
182 | error("bug: parser_temp_free_upto misused"); | |
183 | } | |
184 | ||
185 | ||
186 | static void | |
187 | parser_temp_free_all(void) | |
188 | { | |
189 | struct parser_temp *t; | |
190 | ||
191 | INTOFF; | |
192 | while (parser_temp != NULL) { | |
193 | t = parser_temp; | |
194 | parser_temp = t->next; | |
195 | ckfree(t->data); | |
196 | ckfree(t); | |
197 | } | |
198 | INTON; | |
199 | } | |
200 | ||
201 | ||
202 | /* | |
203 | * Read and parse a command. Returns NEOF on end of file. (NULL is a | |
204 | * valid parse tree indicating a blank line.) | |
205 | */ | |
206 | ||
207 | union node * | |
208 | parsecmd(int interact) | |
209 | { | |
210 | int t; | |
211 | ||
212 | /* This assumes the parser is not re-entered, | |
213 | * which could happen if we add command substitution on PS1/PS2. | |
214 | */ | |
215 | parser_temp_free_all(); | |
216 | heredoclist = NULL; | |
217 | ||
218 | tokpushback = 0; | |
219 | checkkwd = 0; | |
220 | doprompt = interact; | |
221 | if (doprompt) | |
222 | setprompt(1); | |
223 | else | |
224 | setprompt(0); | |
225 | needprompt = 0; | |
226 | t = readtoken(); | |
227 | if (t == TEOF) | |
228 | return NEOF; | |
229 | if (t == TNL) | |
230 | return NULL; | |
231 | tokpushback++; | |
232 | return list(1); | |
233 | } | |
234 | ||
235 | ||
deb63bfb A |
236 | /* |
237 | * Read and parse words for wordexp. | |
238 | * Returns a list of NARG nodes; NULL if there are no words. | |
239 | */ | |
240 | union node * | |
241 | parsewordexp(void) | |
242 | { | |
243 | union node *n, *first = NULL, **pnext; | |
244 | int t; | |
245 | ||
246 | /* This assumes the parser is not re-entered, | |
247 | * which could happen if we add command substitution on PS1/PS2. | |
248 | */ | |
249 | parser_temp_free_all(); | |
250 | heredoclist = NULL; | |
251 | ||
252 | tokpushback = 0; | |
253 | checkkwd = 0; | |
254 | doprompt = 0; | |
255 | setprompt(0); | |
256 | needprompt = 0; | |
257 | pnext = &first; | |
258 | while ((t = readtoken()) != TEOF) { | |
259 | if (t != TWORD) | |
260 | synexpect(TWORD); | |
261 | n = makename(); | |
262 | *pnext = n; | |
263 | pnext = &n->narg.next; | |
264 | } | |
265 | return first; | |
266 | } | |
267 | ||
268 | ||
71aad674 A |
269 | static union node * |
270 | list(int nlflag) | |
271 | { | |
272 | union node *ntop, *n1, *n2, *n3; | |
273 | int tok; | |
274 | ||
275 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | |
276 | if (!nlflag && tokendlist[peektoken()]) | |
277 | return NULL; | |
278 | ntop = n1 = NULL; | |
279 | for (;;) { | |
280 | n2 = andor(); | |
281 | tok = readtoken(); | |
282 | if (tok == TBACKGND) { | |
283 | if (n2 != NULL && n2->type == NPIPE) { | |
284 | n2->npipe.backgnd = 1; | |
285 | } else if (n2 != NULL && n2->type == NREDIR) { | |
286 | n2->type = NBACKGND; | |
287 | } else { | |
288 | n3 = (union node *)stalloc(sizeof (struct nredir)); | |
289 | n3->type = NBACKGND; | |
290 | n3->nredir.n = n2; | |
291 | n3->nredir.redirect = NULL; | |
292 | n2 = n3; | |
293 | } | |
294 | } | |
295 | if (ntop == NULL) | |
296 | ntop = n2; | |
297 | else if (n1 == NULL) { | |
298 | n1 = makebinary(NSEMI, ntop, n2); | |
299 | ntop = n1; | |
300 | } | |
301 | else { | |
302 | n3 = makebinary(NSEMI, n1->nbinary.ch2, n2); | |
303 | n1->nbinary.ch2 = n3; | |
304 | n1 = n3; | |
305 | } | |
306 | switch (tok) { | |
307 | case TBACKGND: | |
308 | case TSEMI: | |
309 | tok = readtoken(); | |
310 | /* FALLTHROUGH */ | |
311 | case TNL: | |
312 | if (tok == TNL) { | |
313 | parseheredoc(); | |
314 | if (nlflag) | |
315 | return ntop; | |
316 | } else if (tok == TEOF && nlflag) { | |
317 | parseheredoc(); | |
318 | return ntop; | |
319 | } else { | |
320 | tokpushback++; | |
321 | } | |
322 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | |
323 | if (!nlflag && tokendlist[peektoken()]) | |
324 | return ntop; | |
325 | break; | |
326 | case TEOF: | |
327 | if (heredoclist) | |
328 | parseheredoc(); | |
329 | else | |
330 | pungetc(); /* push back EOF on input */ | |
331 | return ntop; | |
332 | default: | |
333 | if (nlflag) | |
334 | synexpect(-1); | |
335 | tokpushback++; | |
336 | return ntop; | |
337 | } | |
338 | } | |
339 | } | |
340 | ||
341 | ||
342 | ||
343 | static union node * | |
344 | andor(void) | |
345 | { | |
346 | union node *n; | |
347 | int t; | |
348 | ||
349 | n = pipeline(); | |
350 | for (;;) { | |
351 | if ((t = readtoken()) == TAND) { | |
352 | t = NAND; | |
353 | } else if (t == TOR) { | |
354 | t = NOR; | |
355 | } else { | |
356 | tokpushback++; | |
357 | return n; | |
358 | } | |
359 | n = makebinary(t, n, pipeline()); | |
360 | } | |
361 | } | |
362 | ||
363 | ||
364 | ||
365 | static union node * | |
366 | pipeline(void) | |
367 | { | |
368 | union node *n1, *n2, *pipenode; | |
369 | struct nodelist *lp, *prev; | |
370 | int negate, t; | |
371 | ||
372 | negate = 0; | |
373 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | |
374 | TRACE(("pipeline: entered\n")); | |
375 | while (readtoken() == TNOT) | |
376 | negate = !negate; | |
377 | tokpushback++; | |
378 | n1 = command(); | |
379 | if (readtoken() == TPIPE) { | |
380 | pipenode = (union node *)stalloc(sizeof (struct npipe)); | |
381 | pipenode->type = NPIPE; | |
382 | pipenode->npipe.backgnd = 0; | |
383 | lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); | |
384 | pipenode->npipe.cmdlist = lp; | |
385 | lp->n = n1; | |
386 | do { | |
387 | prev = lp; | |
388 | lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); | |
389 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | |
390 | t = readtoken(); | |
391 | tokpushback++; | |
392 | if (t == TNOT) | |
393 | lp->n = pipeline(); | |
394 | else | |
395 | lp->n = command(); | |
396 | prev->next = lp; | |
397 | } while (readtoken() == TPIPE); | |
398 | lp->next = NULL; | |
399 | n1 = pipenode; | |
400 | } | |
401 | tokpushback++; | |
402 | if (negate) { | |
403 | n2 = (union node *)stalloc(sizeof (struct nnot)); | |
404 | n2->type = NNOT; | |
405 | n2->nnot.com = n1; | |
406 | return n2; | |
407 | } else | |
408 | return n1; | |
409 | } | |
410 | ||
411 | ||
412 | ||
413 | static union node * | |
414 | command(void) | |
415 | { | |
416 | union node *n1, *n2; | |
417 | union node *ap, **app; | |
418 | union node *cp, **cpp; | |
419 | union node *redir, **rpp; | |
420 | int t; | |
421 | int is_subshell; | |
422 | ||
423 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | |
424 | is_subshell = 0; | |
425 | redir = NULL; | |
426 | n1 = NULL; | |
427 | rpp = &redir; | |
428 | ||
429 | /* Check for redirection which may precede command */ | |
430 | while (readtoken() == TREDIR) { | |
431 | *rpp = n2 = redirnode; | |
432 | rpp = &n2->nfile.next; | |
433 | parsefname(); | |
434 | } | |
435 | tokpushback++; | |
436 | ||
437 | switch (readtoken()) { | |
438 | case TIF: | |
439 | n1 = (union node *)stalloc(sizeof (struct nif)); | |
440 | n1->type = NIF; | |
441 | if ((n1->nif.test = list(0)) == NULL) | |
442 | synexpect(-1); | |
443 | consumetoken(TTHEN); | |
444 | n1->nif.ifpart = list(0); | |
445 | n2 = n1; | |
446 | while (readtoken() == TELIF) { | |
447 | n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); | |
448 | n2 = n2->nif.elsepart; | |
449 | n2->type = NIF; | |
450 | if ((n2->nif.test = list(0)) == NULL) | |
451 | synexpect(-1); | |
452 | consumetoken(TTHEN); | |
453 | n2->nif.ifpart = list(0); | |
454 | } | |
455 | if (lasttoken == TELSE) | |
456 | n2->nif.elsepart = list(0); | |
457 | else { | |
458 | n2->nif.elsepart = NULL; | |
459 | tokpushback++; | |
460 | } | |
461 | consumetoken(TFI); | |
462 | checkkwd = CHKKWD | CHKALIAS; | |
463 | break; | |
464 | case TWHILE: | |
465 | case TUNTIL: | |
466 | t = lasttoken; | |
467 | if ((n1 = list(0)) == NULL) | |
468 | synexpect(-1); | |
469 | consumetoken(TDO); | |
470 | n1 = makebinary((t == TWHILE)? NWHILE : NUNTIL, n1, list(0)); | |
471 | consumetoken(TDONE); | |
472 | checkkwd = CHKKWD | CHKALIAS; | |
473 | break; | |
474 | case TFOR: | |
475 | if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) | |
476 | synerror("Bad for loop variable"); | |
477 | n1 = (union node *)stalloc(sizeof (struct nfor)); | |
478 | n1->type = NFOR; | |
479 | n1->nfor.var = wordtext; | |
480 | while (readtoken() == TNL) | |
481 | ; | |
482 | if (lasttoken == TWORD && ! quoteflag && equal(wordtext, "in")) { | |
483 | app = ≈ | |
484 | while (readtoken() == TWORD) { | |
485 | n2 = makename(); | |
486 | *app = n2; | |
487 | app = &n2->narg.next; | |
488 | } | |
489 | *app = NULL; | |
490 | n1->nfor.args = ap; | |
491 | if (lasttoken != TNL && lasttoken != TSEMI) | |
492 | synexpect(-1); | |
493 | } else { | |
494 | static char argvars[5] = { | |
495 | CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' | |
496 | }; | |
497 | n2 = (union node *)stalloc(sizeof (struct narg)); | |
498 | n2->type = NARG; | |
499 | n2->narg.text = argvars; | |
500 | n2->narg.backquote = NULL; | |
501 | n2->narg.next = NULL; | |
502 | n1->nfor.args = n2; | |
503 | /* | |
504 | * Newline or semicolon here is optional (but note | |
505 | * that the original Bourne shell only allowed NL). | |
506 | */ | |
507 | if (lasttoken != TNL && lasttoken != TSEMI) | |
508 | tokpushback++; | |
509 | } | |
510 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | |
511 | if ((t = readtoken()) == TDO) | |
512 | t = TDONE; | |
513 | else if (t == TBEGIN) | |
514 | t = TEND; | |
515 | else | |
516 | synexpect(-1); | |
517 | n1->nfor.body = list(0); | |
518 | consumetoken(t); | |
519 | checkkwd = CHKKWD | CHKALIAS; | |
520 | break; | |
521 | case TCASE: | |
522 | n1 = (union node *)stalloc(sizeof (struct ncase)); | |
523 | n1->type = NCASE; | |
524 | consumetoken(TWORD); | |
525 | n1->ncase.expr = makename(); | |
526 | while (readtoken() == TNL); | |
527 | if (lasttoken != TWORD || ! equal(wordtext, "in")) | |
528 | synerror("expecting \"in\""); | |
529 | cpp = &n1->ncase.cases; | |
530 | checkkwd = CHKNL | CHKKWD, readtoken(); | |
531 | while (lasttoken != TESAC) { | |
532 | *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); | |
533 | cp->type = NCLIST; | |
534 | app = &cp->nclist.pattern; | |
535 | if (lasttoken == TLP) | |
536 | readtoken(); | |
537 | for (;;) { | |
538 | *app = ap = makename(); | |
539 | checkkwd = CHKNL | CHKKWD; | |
540 | if (readtoken() != TPIPE) | |
541 | break; | |
542 | app = &ap->narg.next; | |
543 | readtoken(); | |
544 | } | |
545 | ap->narg.next = NULL; | |
546 | if (lasttoken != TRP) | |
547 | synexpect(TRP); | |
548 | cp->nclist.body = list(0); | |
549 | ||
550 | checkkwd = CHKNL | CHKKWD | CHKALIAS; | |
551 | if ((t = readtoken()) != TESAC) { | |
552 | if (t == TENDCASE) | |
553 | ; | |
554 | else if (t == TFALLTHRU) | |
555 | cp->type = NCLISTFALLTHRU; | |
556 | else | |
557 | synexpect(TENDCASE); | |
558 | checkkwd = CHKNL | CHKKWD, readtoken(); | |
559 | } | |
560 | cpp = &cp->nclist.next; | |
561 | } | |
562 | *cpp = NULL; | |
563 | checkkwd = CHKKWD | CHKALIAS; | |
564 | break; | |
565 | case TLP: | |
566 | n1 = (union node *)stalloc(sizeof (struct nredir)); | |
567 | n1->type = NSUBSHELL; | |
568 | n1->nredir.n = list(0); | |
569 | n1->nredir.redirect = NULL; | |
570 | consumetoken(TRP); | |
571 | checkkwd = CHKKWD | CHKALIAS; | |
572 | is_subshell = 1; | |
573 | break; | |
574 | case TBEGIN: | |
575 | n1 = list(0); | |
576 | consumetoken(TEND); | |
577 | checkkwd = CHKKWD | CHKALIAS; | |
578 | break; | |
579 | /* A simple command must have at least one redirection or word. */ | |
580 | case TBACKGND: | |
581 | case TSEMI: | |
582 | case TAND: | |
583 | case TOR: | |
584 | case TPIPE: | |
585 | case TENDCASE: | |
586 | case TFALLTHRU: | |
587 | case TEOF: | |
588 | case TNL: | |
589 | case TRP: | |
590 | if (!redir) | |
591 | synexpect(-1); | |
592 | case TWORD: | |
593 | tokpushback++; | |
594 | n1 = simplecmd(rpp, redir); | |
595 | return n1; | |
596 | default: | |
597 | synexpect(-1); | |
598 | } | |
599 | ||
600 | /* Now check for redirection which may follow command */ | |
601 | while (readtoken() == TREDIR) { | |
602 | *rpp = n2 = redirnode; | |
603 | rpp = &n2->nfile.next; | |
604 | parsefname(); | |
605 | } | |
606 | tokpushback++; | |
607 | *rpp = NULL; | |
608 | if (redir) { | |
609 | if (!is_subshell) { | |
610 | n2 = (union node *)stalloc(sizeof (struct nredir)); | |
611 | n2->type = NREDIR; | |
612 | n2->nredir.n = n1; | |
613 | n1 = n2; | |
614 | } | |
615 | n1->nredir.redirect = redir; | |
616 | } | |
617 | ||
618 | return n1; | |
619 | } | |
620 | ||
621 | ||
622 | static union node * | |
623 | simplecmd(union node **rpp, union node *redir) | |
624 | { | |
625 | union node *args, **app; | |
626 | union node **orig_rpp = rpp; | |
627 | union node *n = NULL; | |
628 | int special; | |
629 | int savecheckkwd; | |
630 | ||
631 | /* If we don't have any redirections already, then we must reset */ | |
632 | /* rpp to be the address of the local redir variable. */ | |
deb63bfb | 633 | if (redir == NULL) |
71aad674 A |
634 | rpp = &redir; |
635 | ||
636 | args = NULL; | |
637 | app = &args; | |
638 | /* | |
639 | * We save the incoming value, because we need this for shell | |
640 | * functions. There can not be a redirect or an argument between | |
641 | * the function name and the open parenthesis. | |
642 | */ | |
643 | orig_rpp = rpp; | |
644 | ||
645 | savecheckkwd = CHKALIAS; | |
646 | ||
647 | for (;;) { | |
648 | checkkwd = savecheckkwd; | |
649 | if (readtoken() == TWORD) { | |
650 | n = makename(); | |
651 | *app = n; | |
652 | app = &n->narg.next; | |
653 | if (savecheckkwd != 0 && !isassignment(wordtext)) | |
654 | savecheckkwd = 0; | |
655 | } else if (lasttoken == TREDIR) { | |
656 | *rpp = n = redirnode; | |
657 | rpp = &n->nfile.next; | |
658 | parsefname(); /* read name of redirection file */ | |
659 | } else if (lasttoken == TLP && app == &args->narg.next | |
660 | && rpp == orig_rpp) { | |
661 | /* We have a function */ | |
662 | consumetoken(TRP); | |
663 | funclinno = plinno; | |
664 | /* | |
665 | * - Require plain text. | |
666 | * - Functions with '/' cannot be called. | |
667 | * - Reject name=(). | |
668 | * - Reject ksh extended glob patterns. | |
669 | */ | |
670 | if (!noexpand(n->narg.text) || quoteflag || | |
671 | strchr(n->narg.text, '/') || | |
672 | strchr("!%*+-=?@}~", | |
673 | n->narg.text[strlen(n->narg.text) - 1])) | |
674 | synerror("Bad function name"); | |
675 | rmescapes(n->narg.text); | |
676 | if (find_builtin(n->narg.text, &special) >= 0 && | |
677 | special) | |
678 | synerror("Cannot override a special builtin with a function"); | |
679 | n->type = NDEFUN; | |
680 | n->narg.next = command(); | |
681 | funclinno = 0; | |
682 | return n; | |
683 | } else { | |
684 | tokpushback++; | |
685 | break; | |
686 | } | |
687 | } | |
688 | *app = NULL; | |
689 | *rpp = NULL; | |
690 | n = (union node *)stalloc(sizeof (struct ncmd)); | |
691 | n->type = NCMD; | |
692 | n->ncmd.args = args; | |
693 | n->ncmd.redirect = redir; | |
694 | return n; | |
695 | } | |
696 | ||
697 | static union node * | |
698 | makename(void) | |
699 | { | |
700 | union node *n; | |
701 | ||
702 | n = (union node *)stalloc(sizeof (struct narg)); | |
703 | n->type = NARG; | |
704 | n->narg.next = NULL; | |
705 | n->narg.text = wordtext; | |
706 | n->narg.backquote = backquotelist; | |
707 | return n; | |
708 | } | |
709 | ||
710 | static union node * | |
711 | makebinary(int type, union node *n1, union node *n2) | |
712 | { | |
713 | union node *n; | |
714 | ||
715 | n = (union node *)stalloc(sizeof (struct nbinary)); | |
716 | n->type = type; | |
717 | n->nbinary.ch1 = n1; | |
718 | n->nbinary.ch2 = n2; | |
719 | return (n); | |
720 | } | |
721 | ||
722 | void | |
723 | forcealias(void) | |
724 | { | |
725 | checkkwd |= CHKALIAS; | |
726 | } | |
727 | ||
728 | void | |
729 | fixredir(union node *n, const char *text, int err) | |
730 | { | |
731 | TRACE(("Fix redir %s %d\n", text, err)); | |
732 | if (!err) | |
733 | n->ndup.vname = NULL; | |
734 | ||
735 | if (is_digit(text[0]) && text[1] == '\0') | |
736 | n->ndup.dupfd = digit_val(text[0]); | |
737 | else if (text[0] == '-' && text[1] == '\0') | |
738 | n->ndup.dupfd = -1; | |
739 | else { | |
740 | ||
741 | if (err) | |
742 | synerror("Bad fd number"); | |
743 | else | |
744 | n->ndup.vname = makename(); | |
745 | } | |
746 | } | |
747 | ||
748 | ||
749 | static void | |
750 | parsefname(void) | |
751 | { | |
752 | union node *n = redirnode; | |
753 | ||
754 | consumetoken(TWORD); | |
755 | if (n->type == NHERE) { | |
756 | struct heredoc *here = heredoc; | |
757 | struct heredoc *p; | |
758 | ||
759 | if (quoteflag == 0) | |
760 | n->type = NXHERE; | |
761 | TRACE(("Here document %d\n", n->type)); | |
762 | if (here->striptabs) { | |
763 | while (*wordtext == '\t') | |
764 | wordtext++; | |
765 | } | |
766 | if (! noexpand(wordtext)) | |
767 | synerror("Illegal eof marker for << redirection"); | |
768 | rmescapes(wordtext); | |
769 | here->eofmark = wordtext; | |
770 | here->next = NULL; | |
771 | if (heredoclist == NULL) | |
772 | heredoclist = here; | |
773 | else { | |
774 | for (p = heredoclist ; p->next ; p = p->next); | |
775 | p->next = here; | |
776 | } | |
777 | } else if (n->type == NTOFD || n->type == NFROMFD) { | |
778 | fixredir(n, wordtext, 0); | |
779 | } else { | |
780 | n->nfile.fname = makename(); | |
781 | } | |
782 | } | |
783 | ||
784 | ||
785 | /* | |
786 | * Input any here documents. | |
787 | */ | |
788 | ||
789 | static void | |
790 | parseheredoc(void) | |
791 | { | |
792 | struct heredoc *here; | |
793 | union node *n; | |
794 | ||
795 | while (heredoclist) { | |
796 | here = heredoclist; | |
797 | heredoclist = here->next; | |
798 | if (needprompt) { | |
799 | setprompt(2); | |
800 | needprompt = 0; | |
801 | } | |
802 | readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, | |
803 | here->eofmark, here->striptabs); | |
804 | n = makename(); | |
805 | here->here->nhere.doc = n; | |
806 | } | |
807 | } | |
808 | ||
809 | static int | |
810 | peektoken(void) | |
811 | { | |
812 | int t; | |
813 | ||
814 | t = readtoken(); | |
815 | tokpushback++; | |
816 | return (t); | |
817 | } | |
818 | ||
819 | static int | |
820 | readtoken(void) | |
821 | { | |
822 | int t; | |
823 | struct alias *ap; | |
824 | #ifdef DEBUG | |
825 | int alreadyseen = tokpushback; | |
826 | #endif | |
827 | ||
828 | top: | |
829 | t = xxreadtoken(); | |
830 | ||
831 | /* | |
832 | * eat newlines | |
833 | */ | |
834 | if (checkkwd & CHKNL) { | |
835 | while (t == TNL) { | |
836 | parseheredoc(); | |
837 | t = xxreadtoken(); | |
838 | } | |
839 | } | |
840 | ||
841 | /* | |
842 | * check for keywords and aliases | |
843 | */ | |
844 | if (t == TWORD && !quoteflag) | |
845 | { | |
846 | const char * const *pp; | |
847 | ||
848 | if (checkkwd & CHKKWD) | |
849 | for (pp = parsekwd; *pp; pp++) { | |
850 | if (**pp == *wordtext && equal(*pp, wordtext)) | |
851 | { | |
852 | lasttoken = t = pp - parsekwd + KWDOFFSET; | |
853 | TRACE(("keyword %s recognized\n", tokname[t])); | |
854 | goto out; | |
855 | } | |
856 | } | |
857 | if (checkkwd & CHKALIAS && | |
858 | (ap = lookupalias(wordtext, 1)) != NULL) { | |
859 | pushstring(ap->val, strlen(ap->val), ap); | |
860 | goto top; | |
861 | } | |
862 | } | |
863 | out: | |
864 | if (t != TNOT) | |
865 | checkkwd = 0; | |
866 | ||
867 | #ifdef DEBUG | |
868 | if (!alreadyseen) | |
869 | TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); | |
870 | else | |
871 | TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); | |
872 | #endif | |
873 | return (t); | |
874 | } | |
875 | ||
876 | ||
877 | /* | |
878 | * Read the next input token. | |
879 | * If the token is a word, we set backquotelist to the list of cmds in | |
880 | * backquotes. We set quoteflag to true if any part of the word was | |
881 | * quoted. | |
882 | * If the token is TREDIR, then we set redirnode to a structure containing | |
883 | * the redirection. | |
884 | * In all cases, the variable startlinno is set to the number of the line | |
885 | * on which the token starts. | |
886 | * | |
887 | * [Change comment: here documents and internal procedures] | |
888 | * [Readtoken shouldn't have any arguments. Perhaps we should make the | |
889 | * word parsing code into a separate routine. In this case, readtoken | |
890 | * doesn't need to have any internal procedures, but parseword does. | |
891 | * We could also make parseoperator in essence the main routine, and | |
892 | * have parseword (readtoken1?) handle both words and redirection.] | |
893 | */ | |
894 | ||
895 | #define RETURN(token) return lasttoken = token | |
896 | ||
897 | static int | |
898 | xxreadtoken(void) | |
899 | { | |
900 | int c; | |
901 | ||
902 | if (tokpushback) { | |
903 | tokpushback = 0; | |
904 | return lasttoken; | |
905 | } | |
906 | if (needprompt) { | |
907 | setprompt(2); | |
908 | needprompt = 0; | |
909 | } | |
910 | startlinno = plinno; | |
911 | for (;;) { /* until token or start of word found */ | |
912 | c = pgetc_macro(); | |
913 | switch (c) { | |
914 | case ' ': case '\t': | |
915 | continue; | |
916 | case '#': | |
917 | while ((c = pgetc()) != '\n' && c != PEOF); | |
918 | pungetc(); | |
919 | continue; | |
920 | case '\\': | |
921 | if (pgetc() == '\n') { | |
922 | startlinno = ++plinno; | |
923 | if (doprompt) | |
924 | setprompt(2); | |
925 | else | |
926 | setprompt(0); | |
927 | continue; | |
928 | } | |
929 | pungetc(); | |
930 | /* FALLTHROUGH */ | |
931 | default: | |
932 | return readtoken1(c, BASESYNTAX, (char *)NULL, 0); | |
933 | case '\n': | |
934 | plinno++; | |
935 | needprompt = doprompt; | |
936 | RETURN(TNL); | |
937 | case PEOF: | |
938 | RETURN(TEOF); | |
939 | case '&': | |
940 | if (pgetc_linecont() == '&') | |
941 | RETURN(TAND); | |
942 | pungetc(); | |
943 | RETURN(TBACKGND); | |
944 | case '|': | |
945 | if (pgetc_linecont() == '|') | |
946 | RETURN(TOR); | |
947 | pungetc(); | |
948 | RETURN(TPIPE); | |
949 | case ';': | |
950 | c = pgetc_linecont(); | |
951 | if (c == ';') | |
952 | RETURN(TENDCASE); | |
953 | else if (c == '&') | |
954 | RETURN(TFALLTHRU); | |
955 | pungetc(); | |
956 | RETURN(TSEMI); | |
957 | case '(': | |
958 | RETURN(TLP); | |
959 | case ')': | |
960 | RETURN(TRP); | |
961 | } | |
962 | } | |
963 | #undef RETURN | |
964 | } | |
965 | ||
966 | ||
967 | #define MAXNEST_static 8 | |
968 | struct tokenstate | |
969 | { | |
970 | const char *syntax; /* *SYNTAX */ | |
971 | int parenlevel; /* levels of parentheses in arithmetic */ | |
972 | enum tokenstate_category | |
973 | { | |
974 | TSTATE_TOP, | |
975 | TSTATE_VAR_OLD, /* ${var+-=?}, inherits dquotes */ | |
976 | TSTATE_VAR_NEW, /* other ${var...}, own dquote state */ | |
977 | TSTATE_ARITH | |
978 | } category; | |
979 | }; | |
980 | ||
981 | ||
982 | /* | |
983 | * Check to see whether we are at the end of the here document. When this | |
984 | * is called, c is set to the first character of the next input line. If | |
985 | * we are at the end of the here document, this routine sets the c to PEOF. | |
986 | * The new value of c is returned. | |
987 | */ | |
988 | ||
989 | static int | |
990 | checkend(int c, const char *eofmark, int striptabs) | |
991 | { | |
992 | if (striptabs) { | |
993 | while (c == '\t') | |
994 | c = pgetc(); | |
995 | } | |
996 | if (c == *eofmark) { | |
997 | int c2; | |
998 | const char *q; | |
999 | ||
1000 | for (q = eofmark + 1; c2 = pgetc(), *q != '\0' && c2 == *q; q++) | |
1001 | ; | |
1002 | if ((c2 == PEOF || c2 == '\n') && *q == '\0') { | |
1003 | c = PEOF; | |
1004 | if (c2 == '\n') { | |
1005 | plinno++; | |
1006 | needprompt = doprompt; | |
1007 | } | |
1008 | } else { | |
1009 | pungetc(); | |
1010 | pushstring(eofmark + 1, q - (eofmark + 1), NULL); | |
1011 | } | |
deb63bfb A |
1012 | } else if (c == '\n' && *eofmark == '\0') { |
1013 | c = PEOF; | |
1014 | plinno++; | |
1015 | needprompt = doprompt; | |
71aad674 A |
1016 | } |
1017 | return (c); | |
1018 | } | |
1019 | ||
1020 | ||
1021 | /* | |
1022 | * Parse a redirection operator. The variable "out" points to a string | |
1023 | * specifying the fd to be redirected. The variable "c" contains the | |
1024 | * first character of the redirection operator. | |
1025 | */ | |
1026 | ||
1027 | static void | |
1028 | parseredir(char *out, int c) | |
1029 | { | |
1030 | char fd = *out; | |
1031 | union node *np; | |
1032 | ||
1033 | np = (union node *)stalloc(sizeof (struct nfile)); | |
1034 | if (c == '>') { | |
1035 | np->nfile.fd = 1; | |
1036 | c = pgetc_linecont(); | |
1037 | if (c == '>') | |
1038 | np->type = NAPPEND; | |
1039 | else if (c == '&') | |
1040 | np->type = NTOFD; | |
1041 | else if (c == '|') | |
1042 | np->type = NCLOBBER; | |
1043 | else { | |
1044 | np->type = NTO; | |
1045 | pungetc(); | |
1046 | } | |
1047 | } else { /* c == '<' */ | |
1048 | np->nfile.fd = 0; | |
1049 | c = pgetc_linecont(); | |
1050 | if (c == '<') { | |
1051 | if (sizeof (struct nfile) != sizeof (struct nhere)) { | |
1052 | np = (union node *)stalloc(sizeof (struct nhere)); | |
1053 | np->nfile.fd = 0; | |
1054 | } | |
1055 | np->type = NHERE; | |
1056 | heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); | |
1057 | heredoc->here = np; | |
1058 | if ((c = pgetc_linecont()) == '-') { | |
1059 | heredoc->striptabs = 1; | |
1060 | } else { | |
1061 | heredoc->striptabs = 0; | |
1062 | pungetc(); | |
1063 | } | |
1064 | } else if (c == '&') | |
1065 | np->type = NFROMFD; | |
1066 | else if (c == '>') | |
1067 | np->type = NFROMTO; | |
1068 | else { | |
1069 | np->type = NFROM; | |
1070 | pungetc(); | |
1071 | } | |
1072 | } | |
1073 | if (fd != '\0') | |
1074 | np->nfile.fd = digit_val(fd); | |
1075 | redirnode = np; | |
1076 | } | |
1077 | ||
1078 | /* | |
1079 | * Called to parse command substitutions. | |
1080 | */ | |
1081 | ||
1082 | static char * | |
1083 | parsebackq(char *out, struct nodelist **pbqlist, | |
1084 | int oldstyle, int dblquote, int quoted) | |
1085 | { | |
1086 | struct nodelist **nlpp; | |
1087 | union node *n; | |
1088 | char *volatile str; | |
1089 | struct jmploc jmploc; | |
1090 | struct jmploc *const savehandler = handler; | |
1091 | size_t savelen; | |
1092 | int saveprompt; | |
1093 | const int bq_startlinno = plinno; | |
1094 | char *volatile ostr = NULL; | |
1095 | struct parsefile *const savetopfile = getcurrentfile(); | |
1096 | struct heredoc *const saveheredoclist = heredoclist; | |
1097 | struct heredoc *here; | |
1098 | ||
1099 | str = NULL; | |
1100 | if (setjmp(jmploc.loc)) { | |
1101 | popfilesupto(savetopfile); | |
1102 | if (str) | |
1103 | ckfree(str); | |
1104 | if (ostr) | |
1105 | ckfree(ostr); | |
1106 | heredoclist = saveheredoclist; | |
1107 | handler = savehandler; | |
1108 | if (exception == EXERROR) { | |
1109 | startlinno = bq_startlinno; | |
1110 | synerror("Error in command substitution"); | |
1111 | } | |
1112 | longjmp(handler->loc, 1); | |
1113 | } | |
1114 | INTOFF; | |
1115 | savelen = out - stackblock(); | |
1116 | if (savelen > 0) { | |
1117 | str = ckmalloc(savelen); | |
1118 | memcpy(str, stackblock(), savelen); | |
1119 | } | |
1120 | handler = &jmploc; | |
1121 | heredoclist = NULL; | |
1122 | INTON; | |
1123 | if (oldstyle) { | |
1124 | /* We must read until the closing backquote, giving special | |
1125 | treatment to some slashes, and then push the string and | |
1126 | reread it as input, interpreting it normally. */ | |
1127 | char *oout; | |
1128 | int c; | |
1129 | int olen; | |
1130 | ||
1131 | ||
1132 | STARTSTACKSTR(oout); | |
1133 | for (;;) { | |
1134 | if (needprompt) { | |
1135 | setprompt(2); | |
1136 | needprompt = 0; | |
1137 | } | |
1138 | CHECKSTRSPACE(2, oout); | |
1139 | c = pgetc_linecont(); | |
1140 | if (c == '`') | |
1141 | break; | |
1142 | switch (c) { | |
1143 | case '\\': | |
1144 | c = pgetc(); | |
1145 | if (c != '\\' && c != '`' && c != '$' | |
1146 | && (!dblquote || c != '"')) | |
1147 | USTPUTC('\\', oout); | |
1148 | break; | |
1149 | ||
1150 | case '\n': | |
1151 | plinno++; | |
1152 | needprompt = doprompt; | |
1153 | break; | |
1154 | ||
1155 | case PEOF: | |
1156 | startlinno = plinno; | |
1157 | synerror("EOF in backquote substitution"); | |
1158 | break; | |
1159 | ||
1160 | default: | |
1161 | break; | |
1162 | } | |
1163 | USTPUTC(c, oout); | |
1164 | } | |
1165 | USTPUTC('\0', oout); | |
1166 | olen = oout - stackblock(); | |
1167 | INTOFF; | |
1168 | ostr = ckmalloc(olen); | |
1169 | memcpy(ostr, stackblock(), olen); | |
1170 | setinputstring(ostr, 1); | |
1171 | INTON; | |
1172 | } | |
1173 | nlpp = pbqlist; | |
1174 | while (*nlpp) | |
1175 | nlpp = &(*nlpp)->next; | |
1176 | *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); | |
1177 | (*nlpp)->next = NULL; | |
1178 | ||
1179 | if (oldstyle) { | |
1180 | saveprompt = doprompt; | |
1181 | doprompt = 0; | |
1182 | } | |
1183 | ||
1184 | n = list(0); | |
1185 | ||
1186 | if (oldstyle) { | |
1187 | if (peektoken() != TEOF) | |
1188 | synexpect(-1); | |
1189 | doprompt = saveprompt; | |
1190 | } else | |
1191 | consumetoken(TRP); | |
1192 | ||
1193 | (*nlpp)->n = n; | |
1194 | if (oldstyle) { | |
1195 | /* | |
1196 | * Start reading from old file again, ignoring any pushed back | |
1197 | * tokens left from the backquote parsing | |
1198 | */ | |
1199 | popfile(); | |
1200 | tokpushback = 0; | |
1201 | } | |
1202 | STARTSTACKSTR(out); | |
1203 | CHECKSTRSPACE(savelen + 1, out); | |
1204 | INTOFF; | |
1205 | if (str) { | |
1206 | memcpy(out, str, savelen); | |
1207 | STADJUST(savelen, out); | |
1208 | ckfree(str); | |
1209 | str = NULL; | |
1210 | } | |
1211 | if (ostr) { | |
1212 | ckfree(ostr); | |
1213 | ostr = NULL; | |
1214 | } | |
1215 | here = saveheredoclist; | |
1216 | if (here != NULL) { | |
1217 | while (here->next != NULL) | |
1218 | here = here->next; | |
1219 | here->next = heredoclist; | |
1220 | heredoclist = saveheredoclist; | |
1221 | } | |
1222 | handler = savehandler; | |
1223 | INTON; | |
1224 | if (quoted) | |
1225 | USTPUTC(CTLBACKQ | CTLQUOTE, out); | |
1226 | else | |
1227 | USTPUTC(CTLBACKQ, out); | |
1228 | return out; | |
1229 | } | |
1230 | ||
1231 | ||
1232 | /* | |
1233 | * Called to parse a backslash escape sequence inside $'...'. | |
1234 | * The backslash has already been read. | |
1235 | */ | |
1236 | static char * | |
1237 | readcstyleesc(char *out) | |
1238 | { | |
deb63bfb A |
1239 | int c, vc, i, n; |
1240 | unsigned int v; | |
71aad674 A |
1241 | |
1242 | c = pgetc(); | |
1243 | switch (c) { | |
1244 | case '\0': | |
1245 | synerror("Unterminated quoted string"); | |
1246 | case '\n': | |
1247 | plinno++; | |
1248 | if (doprompt) | |
1249 | setprompt(2); | |
1250 | else | |
1251 | setprompt(0); | |
1252 | return out; | |
1253 | case '\\': | |
1254 | case '\'': | |
1255 | case '"': | |
1256 | v = c; | |
1257 | break; | |
1258 | case 'a': v = '\a'; break; | |
1259 | case 'b': v = '\b'; break; | |
1260 | case 'e': v = '\033'; break; | |
1261 | case 'f': v = '\f'; break; | |
1262 | case 'n': v = '\n'; break; | |
1263 | case 'r': v = '\r'; break; | |
1264 | case 't': v = '\t'; break; | |
1265 | case 'v': v = '\v'; break; | |
1266 | case 'x': | |
1267 | v = 0; | |
1268 | for (;;) { | |
1269 | c = pgetc(); | |
1270 | if (c >= '0' && c <= '9') | |
1271 | v = (v << 4) + c - '0'; | |
1272 | else if (c >= 'A' && c <= 'F') | |
1273 | v = (v << 4) + c - 'A' + 10; | |
1274 | else if (c >= 'a' && c <= 'f') | |
1275 | v = (v << 4) + c - 'a' + 10; | |
1276 | else | |
1277 | break; | |
1278 | } | |
1279 | pungetc(); | |
1280 | break; | |
1281 | case '0': case '1': case '2': case '3': | |
1282 | case '4': case '5': case '6': case '7': | |
1283 | v = c - '0'; | |
1284 | c = pgetc(); | |
1285 | if (c >= '0' && c <= '7') { | |
1286 | v <<= 3; | |
1287 | v += c - '0'; | |
1288 | c = pgetc(); | |
1289 | if (c >= '0' && c <= '7') { | |
1290 | v <<= 3; | |
1291 | v += c - '0'; | |
1292 | } else | |
1293 | pungetc(); | |
1294 | } else | |
1295 | pungetc(); | |
1296 | break; | |
1297 | case 'c': | |
1298 | c = pgetc(); | |
1299 | if (c < 0x3f || c > 0x7a || c == 0x60) | |
1300 | synerror("Bad escape sequence"); | |
1301 | if (c == '\\' && pgetc() != '\\') | |
1302 | synerror("Bad escape sequence"); | |
1303 | if (c == '?') | |
1304 | v = 127; | |
1305 | else | |
1306 | v = c & 0x1f; | |
1307 | break; | |
1308 | case 'u': | |
1309 | case 'U': | |
1310 | n = c == 'U' ? 8 : 4; | |
1311 | v = 0; | |
1312 | for (i = 0; i < n; i++) { | |
1313 | c = pgetc(); | |
1314 | if (c >= '0' && c <= '9') | |
1315 | v = (v << 4) + c - '0'; | |
1316 | else if (c >= 'A' && c <= 'F') | |
1317 | v = (v << 4) + c - 'A' + 10; | |
1318 | else if (c >= 'a' && c <= 'f') | |
1319 | v = (v << 4) + c - 'a' + 10; | |
1320 | else | |
1321 | synerror("Bad escape sequence"); | |
1322 | } | |
1323 | if (v == 0 || (v >= 0xd800 && v <= 0xdfff)) | |
1324 | synerror("Bad escape sequence"); | |
1325 | /* We really need iconv here. */ | |
1326 | if (initial_localeisutf8 && v > 127) { | |
1327 | CHECKSTRSPACE(4, out); | |
1328 | /* | |
1329 | * We cannot use wctomb() as the locale may have | |
1330 | * changed. | |
1331 | */ | |
1332 | if (v <= 0x7ff) { | |
1333 | USTPUTC(0xc0 | v >> 6, out); | |
1334 | USTPUTC(0x80 | (v & 0x3f), out); | |
1335 | return out; | |
1336 | } else if (v <= 0xffff) { | |
1337 | USTPUTC(0xe0 | v >> 12, out); | |
1338 | USTPUTC(0x80 | ((v >> 6) & 0x3f), out); | |
1339 | USTPUTC(0x80 | (v & 0x3f), out); | |
1340 | return out; | |
1341 | } else if (v <= 0x10ffff) { | |
1342 | USTPUTC(0xf0 | v >> 18, out); | |
1343 | USTPUTC(0x80 | ((v >> 12) & 0x3f), out); | |
1344 | USTPUTC(0x80 | ((v >> 6) & 0x3f), out); | |
1345 | USTPUTC(0x80 | (v & 0x3f), out); | |
1346 | return out; | |
1347 | } | |
1348 | } | |
1349 | if (v > 127) | |
1350 | v = '?'; | |
1351 | break; | |
1352 | default: | |
1353 | synerror("Bad escape sequence"); | |
1354 | } | |
deb63bfb | 1355 | vc = (char)v; |
71aad674 A |
1356 | /* |
1357 | * We can't handle NUL bytes. | |
1358 | * POSIX says we should skip till the closing quote. | |
1359 | */ | |
deb63bfb | 1360 | if (vc == '\0') { |
71aad674 A |
1361 | while ((c = pgetc()) != '\'') { |
1362 | if (c == '\\') | |
1363 | c = pgetc(); | |
1364 | if (c == PEOF) | |
1365 | synerror("Unterminated quoted string"); | |
1366 | if (c == '\n') { | |
1367 | plinno++; | |
1368 | if (doprompt) | |
1369 | setprompt(2); | |
1370 | else | |
1371 | setprompt(0); | |
1372 | } | |
1373 | } | |
1374 | pungetc(); | |
1375 | return out; | |
1376 | } | |
deb63bfb | 1377 | if (SQSYNTAX[vc] == CCTL) |
71aad674 | 1378 | USTPUTC(CTLESC, out); |
deb63bfb | 1379 | USTPUTC(vc, out); |
71aad674 A |
1380 | return out; |
1381 | } | |
1382 | ||
1383 | ||
1384 | /* | |
1385 | * If eofmark is NULL, read a word or a redirection symbol. If eofmark | |
1386 | * is not NULL, read a here document. In the latter case, eofmark is the | |
1387 | * word which marks the end of the document and striptabs is true if | |
1388 | * leading tabs should be stripped from the document. The argument firstc | |
1389 | * is the first character of the input token or document. | |
1390 | * | |
1391 | * Because C does not have internal subroutines, I have simulated them | |
1392 | * using goto's to implement the subroutine linkage. The following macros | |
1393 | * will run code that appears at the end of readtoken1. | |
1394 | */ | |
1395 | ||
1396 | #define PARSESUB() {goto parsesub; parsesub_return:;} | |
1397 | #define PARSEARITH() {goto parsearith; parsearith_return:;} | |
1398 | ||
1399 | static int | |
1400 | readtoken1(int firstc, char const *initialsyntax, const char *eofmark, | |
1401 | int striptabs) | |
1402 | { | |
1403 | int c = firstc; | |
1404 | char *out; | |
1405 | int len; | |
1406 | struct nodelist *bqlist; | |
1407 | int quotef; | |
1408 | int newvarnest; | |
1409 | int level; | |
1410 | int synentry; | |
1411 | struct tokenstate state_static[MAXNEST_static]; | |
1412 | int maxnest = MAXNEST_static; | |
1413 | struct tokenstate *state = state_static; | |
1414 | int sqiscstyle = 0; | |
1415 | ||
1416 | startlinno = plinno; | |
1417 | quotef = 0; | |
1418 | bqlist = NULL; | |
1419 | newvarnest = 0; | |
1420 | level = 0; | |
1421 | state[level].syntax = initialsyntax; | |
1422 | state[level].parenlevel = 0; | |
1423 | state[level].category = TSTATE_TOP; | |
1424 | ||
1425 | STARTSTACKSTR(out); | |
1426 | loop: { /* for each line, until end of word */ | |
deb63bfb | 1427 | if (eofmark && eofmark != NOEOFMARK) |
71aad674 A |
1428 | /* set c to PEOF if at end of here document */ |
1429 | c = checkend(c, eofmark, striptabs); | |
1430 | for (;;) { /* until end of line or end of word */ | |
1431 | CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ | |
1432 | ||
1433 | synentry = state[level].syntax[c]; | |
1434 | ||
1435 | switch(synentry) { | |
1436 | case CNL: /* '\n' */ | |
1437 | if (state[level].syntax == BASESYNTAX) | |
1438 | goto endword; /* exit outer loop */ | |
1439 | USTPUTC(c, out); | |
1440 | plinno++; | |
1441 | if (doprompt) | |
1442 | setprompt(2); | |
1443 | else | |
1444 | setprompt(0); | |
1445 | c = pgetc(); | |
1446 | goto loop; /* continue outer loop */ | |
1447 | case CSBACK: | |
1448 | if (sqiscstyle) { | |
1449 | out = readcstyleesc(out); | |
1450 | break; | |
1451 | } | |
1452 | /* FALLTHROUGH */ | |
1453 | case CWORD: | |
1454 | USTPUTC(c, out); | |
1455 | break; | |
1456 | case CCTL: | |
1457 | if (eofmark == NULL || initialsyntax != SQSYNTAX) | |
1458 | USTPUTC(CTLESC, out); | |
1459 | USTPUTC(c, out); | |
1460 | break; | |
1461 | case CBACK: /* backslash */ | |
1462 | c = pgetc(); | |
1463 | if (c == PEOF) { | |
1464 | USTPUTC('\\', out); | |
1465 | pungetc(); | |
1466 | } else if (c == '\n') { | |
1467 | plinno++; | |
1468 | if (doprompt) | |
1469 | setprompt(2); | |
1470 | else | |
1471 | setprompt(0); | |
1472 | } else { | |
1473 | if (state[level].syntax == DQSYNTAX && | |
1474 | c != '\\' && c != '`' && c != '$' && | |
1475 | (c != '"' || (eofmark != NULL && | |
1476 | newvarnest == 0)) && | |
1477 | (c != '}' || state[level].category != TSTATE_VAR_OLD)) | |
1478 | USTPUTC('\\', out); | |
1479 | if ((eofmark == NULL || | |
1480 | newvarnest > 0) && | |
1481 | state[level].syntax == BASESYNTAX) | |
1482 | USTPUTC(CTLQUOTEMARK, out); | |
1483 | if (SQSYNTAX[c] == CCTL) | |
1484 | USTPUTC(CTLESC, out); | |
1485 | USTPUTC(c, out); | |
1486 | if ((eofmark == NULL || | |
1487 | newvarnest > 0) && | |
1488 | state[level].syntax == BASESYNTAX && | |
1489 | state[level].category == TSTATE_VAR_OLD) | |
1490 | USTPUTC(CTLQUOTEEND, out); | |
1491 | quotef++; | |
1492 | } | |
1493 | break; | |
1494 | case CSQUOTE: | |
1495 | USTPUTC(CTLQUOTEMARK, out); | |
1496 | state[level].syntax = SQSYNTAX; | |
1497 | sqiscstyle = 0; | |
1498 | break; | |
1499 | case CDQUOTE: | |
1500 | USTPUTC(CTLQUOTEMARK, out); | |
1501 | state[level].syntax = DQSYNTAX; | |
1502 | break; | |
1503 | case CENDQUOTE: | |
1504 | if (eofmark != NULL && newvarnest == 0) | |
1505 | USTPUTC(c, out); | |
1506 | else { | |
1507 | if (state[level].category == TSTATE_VAR_OLD) | |
1508 | USTPUTC(CTLQUOTEEND, out); | |
1509 | state[level].syntax = BASESYNTAX; | |
1510 | quotef++; | |
1511 | } | |
1512 | break; | |
1513 | case CVAR: /* '$' */ | |
1514 | PARSESUB(); /* parse substitution */ | |
1515 | break; | |
1516 | case CENDVAR: /* '}' */ | |
1517 | if (level > 0 && | |
1518 | ((state[level].category == TSTATE_VAR_OLD && | |
1519 | state[level].syntax == | |
1520 | state[level - 1].syntax) || | |
1521 | (state[level].category == TSTATE_VAR_NEW && | |
1522 | state[level].syntax == BASESYNTAX))) { | |
1523 | if (state[level].category == TSTATE_VAR_NEW) | |
1524 | newvarnest--; | |
1525 | level--; | |
1526 | USTPUTC(CTLENDVAR, out); | |
1527 | } else { | |
1528 | USTPUTC(c, out); | |
1529 | } | |
1530 | break; | |
1531 | case CLP: /* '(' in arithmetic */ | |
1532 | state[level].parenlevel++; | |
1533 | USTPUTC(c, out); | |
1534 | break; | |
1535 | case CRP: /* ')' in arithmetic */ | |
1536 | if (state[level].parenlevel > 0) { | |
1537 | USTPUTC(c, out); | |
1538 | --state[level].parenlevel; | |
1539 | } else { | |
1540 | if (pgetc_linecont() == ')') { | |
1541 | if (level > 0 && | |
1542 | state[level].category == TSTATE_ARITH) { | |
1543 | level--; | |
1544 | USTPUTC(CTLENDARI, out); | |
1545 | } else | |
1546 | USTPUTC(')', out); | |
1547 | } else { | |
1548 | /* | |
1549 | * unbalanced parens | |
1550 | * (don't 2nd guess - no error) | |
1551 | */ | |
1552 | pungetc(); | |
1553 | USTPUTC(')', out); | |
1554 | } | |
1555 | } | |
1556 | break; | |
1557 | case CBQUOTE: /* '`' */ | |
1558 | out = parsebackq(out, &bqlist, 1, | |
1559 | state[level].syntax == DQSYNTAX && | |
1560 | (eofmark == NULL || newvarnest > 0), | |
1561 | state[level].syntax == DQSYNTAX || state[level].syntax == ARISYNTAX); | |
1562 | break; | |
1563 | case CEOF: | |
1564 | goto endword; /* exit outer loop */ | |
1565 | case CIGN: | |
1566 | break; | |
1567 | default: | |
1568 | if (level == 0) | |
1569 | goto endword; /* exit outer loop */ | |
1570 | USTPUTC(c, out); | |
1571 | } | |
1572 | c = pgetc_macro(); | |
1573 | } | |
1574 | } | |
1575 | endword: | |
1576 | if (state[level].syntax == ARISYNTAX) | |
1577 | synerror("Missing '))'"); | |
1578 | if (state[level].syntax != BASESYNTAX && eofmark == NULL) | |
1579 | synerror("Unterminated quoted string"); | |
1580 | if (state[level].category == TSTATE_VAR_OLD || | |
1581 | state[level].category == TSTATE_VAR_NEW) { | |
1582 | startlinno = plinno; | |
1583 | synerror("Missing '}'"); | |
1584 | } | |
1585 | if (state != state_static) | |
1586 | parser_temp_free_upto(state); | |
1587 | USTPUTC('\0', out); | |
1588 | len = out - stackblock(); | |
1589 | out = stackblock(); | |
1590 | if (eofmark == NULL) { | |
1591 | if ((c == '>' || c == '<') | |
1592 | && quotef == 0 | |
1593 | && len <= 2 | |
1594 | && (*out == '\0' || is_digit(*out))) { | |
1595 | parseredir(out, c); | |
1596 | return lasttoken = TREDIR; | |
1597 | } else { | |
1598 | pungetc(); | |
1599 | } | |
1600 | } | |
1601 | quoteflag = quotef; | |
1602 | backquotelist = bqlist; | |
1603 | grabstackblock(len); | |
1604 | wordtext = out; | |
1605 | return lasttoken = TWORD; | |
1606 | /* end of readtoken routine */ | |
1607 | ||
1608 | ||
1609 | /* | |
1610 | * Parse a substitution. At this point, we have read the dollar sign | |
1611 | * and nothing else. | |
1612 | */ | |
1613 | ||
1614 | parsesub: { | |
71aad674 A |
1615 | int subtype; |
1616 | int typeloc; | |
1617 | int flags; | |
1618 | char *p; | |
1619 | static const char types[] = "}-+?="; | |
71aad674 A |
1620 | int linno; |
1621 | int length; | |
1622 | int c1; | |
1623 | ||
1624 | c = pgetc_linecont(); | |
1625 | if (c == '(') { /* $(command) or $((arith)) */ | |
1626 | if (pgetc_linecont() == '(') { | |
1627 | PARSEARITH(); | |
1628 | } else { | |
1629 | pungetc(); | |
1630 | out = parsebackq(out, &bqlist, 0, | |
1631 | state[level].syntax == DQSYNTAX && | |
1632 | (eofmark == NULL || newvarnest > 0), | |
1633 | state[level].syntax == DQSYNTAX || | |
1634 | state[level].syntax == ARISYNTAX); | |
1635 | } | |
1636 | } else if (c == '{' || is_name(c) || is_special(c)) { | |
1637 | USTPUTC(CTLVAR, out); | |
1638 | typeloc = out - stackblock(); | |
1639 | USTPUTC(VSNORMAL, out); | |
1640 | subtype = VSNORMAL; | |
1641 | flags = 0; | |
1642 | if (c == '{') { | |
71aad674 A |
1643 | c = pgetc_linecont(); |
1644 | subtype = 0; | |
1645 | } | |
1646 | varname: | |
1647 | if (!is_eof(c) && is_name(c)) { | |
1648 | length = 0; | |
1649 | do { | |
1650 | STPUTC(c, out); | |
1651 | c = pgetc_linecont(); | |
1652 | length++; | |
1653 | } while (!is_eof(c) && is_in_name(c)); | |
1654 | if (length == 6 && | |
1655 | strncmp(out - length, "LINENO", length) == 0) { | |
1656 | /* Replace the variable name with the | |
1657 | * current line number. */ | |
deb63bfb A |
1658 | STADJUST(-6, out); |
1659 | CHECKSTRSPACE(11, out); | |
71aad674 A |
1660 | linno = plinno; |
1661 | if (funclinno != 0) | |
1662 | linno -= funclinno - 1; | |
deb63bfb A |
1663 | length = snprintf(out, 11, "%d", linno); |
1664 | if (length > 10) | |
1665 | length = 10; | |
1666 | out += length; | |
71aad674 A |
1667 | flags |= VSLINENO; |
1668 | } | |
1669 | } else if (is_digit(c)) { | |
deb63bfb | 1670 | if (subtype != VSNORMAL) { |
71aad674 A |
1671 | do { |
1672 | STPUTC(c, out); | |
1673 | c = pgetc_linecont(); | |
1674 | } while (is_digit(c)); | |
1675 | } else { | |
deb63bfb | 1676 | USTPUTC(c, out); |
71aad674 A |
1677 | c = pgetc_linecont(); |
1678 | } | |
1679 | } else if (is_special(c)) { | |
1680 | c1 = c; | |
1681 | c = pgetc_linecont(); | |
1682 | if (subtype == 0 && c1 == '#') { | |
1683 | subtype = VSLENGTH; | |
1684 | if (strchr(types, c) == NULL && c != ':' && | |
1685 | c != '#' && c != '%') | |
1686 | goto varname; | |
1687 | c1 = c; | |
1688 | c = pgetc_linecont(); | |
1689 | if (c1 != '}' && c == '}') { | |
1690 | pungetc(); | |
1691 | c = c1; | |
1692 | goto varname; | |
1693 | } | |
1694 | pungetc(); | |
1695 | c = c1; | |
1696 | c1 = '#'; | |
1697 | subtype = 0; | |
1698 | } | |
1699 | USTPUTC(c1, out); | |
1700 | } else { | |
1701 | subtype = VSERROR; | |
1702 | if (c == '}') | |
1703 | pungetc(); | |
1704 | else if (c == '\n' || c == PEOF) | |
1705 | synerror("Unexpected end of line in substitution"); | |
deb63bfb | 1706 | else if (BASESYNTAX[c] != CCTL) |
71aad674 A |
1707 | USTPUTC(c, out); |
1708 | } | |
1709 | if (subtype == 0) { | |
1710 | switch (c) { | |
1711 | case ':': | |
1712 | flags |= VSNUL; | |
1713 | c = pgetc_linecont(); | |
1714 | /*FALLTHROUGH*/ | |
1715 | default: | |
1716 | p = strchr(types, c); | |
1717 | if (p == NULL) { | |
1718 | if (c == '\n' || c == PEOF) | |
1719 | synerror("Unexpected end of line in substitution"); | |
1720 | if (flags == VSNUL) | |
1721 | STPUTC(':', out); | |
deb63bfb A |
1722 | if (BASESYNTAX[c] != CCTL) |
1723 | STPUTC(c, out); | |
71aad674 A |
1724 | subtype = VSERROR; |
1725 | } else | |
1726 | subtype = p - types + VSNORMAL; | |
1727 | break; | |
1728 | case '%': | |
1729 | case '#': | |
1730 | { | |
1731 | int cc = c; | |
1732 | subtype = c == '#' ? VSTRIMLEFT : | |
1733 | VSTRIMRIGHT; | |
1734 | c = pgetc_linecont(); | |
1735 | if (c == cc) | |
1736 | subtype++; | |
1737 | else | |
1738 | pungetc(); | |
1739 | break; | |
1740 | } | |
1741 | } | |
1742 | } else if (subtype != VSERROR) { | |
1743 | if (subtype == VSLENGTH && c != '}') | |
1744 | subtype = VSERROR; | |
1745 | pungetc(); | |
1746 | } | |
1747 | STPUTC('=', out); | |
1748 | if (state[level].syntax == DQSYNTAX || | |
1749 | state[level].syntax == ARISYNTAX) | |
1750 | flags |= VSQUOTE; | |
1751 | *(stackblock() + typeloc) = subtype | flags; | |
1752 | if (subtype != VSNORMAL) { | |
1753 | if (level + 1 >= maxnest) { | |
1754 | maxnest *= 2; | |
1755 | if (state == state_static) { | |
1756 | state = parser_temp_alloc( | |
1757 | maxnest * sizeof(*state)); | |
1758 | memcpy(state, state_static, | |
1759 | MAXNEST_static * sizeof(*state)); | |
1760 | } else | |
1761 | state = parser_temp_realloc(state, | |
1762 | maxnest * sizeof(*state)); | |
1763 | } | |
1764 | level++; | |
1765 | state[level].parenlevel = 0; | |
1766 | if (subtype == VSMINUS || subtype == VSPLUS || | |
1767 | subtype == VSQUESTION || subtype == VSASSIGN) { | |
1768 | /* | |
1769 | * For operators that were in the Bourne shell, | |
1770 | * inherit the double-quote state. | |
1771 | */ | |
1772 | state[level].syntax = state[level - 1].syntax; | |
1773 | state[level].category = TSTATE_VAR_OLD; | |
1774 | } else { | |
1775 | /* | |
1776 | * The other operators take a pattern, | |
1777 | * so go to BASESYNTAX. | |
1778 | * Also, ' and " are now special, even | |
1779 | * in here documents. | |
1780 | */ | |
1781 | state[level].syntax = BASESYNTAX; | |
1782 | state[level].category = TSTATE_VAR_NEW; | |
1783 | newvarnest++; | |
1784 | } | |
1785 | } | |
1786 | } else if (c == '\'' && state[level].syntax == BASESYNTAX) { | |
1787 | /* $'cstylequotes' */ | |
1788 | USTPUTC(CTLQUOTEMARK, out); | |
1789 | state[level].syntax = SQSYNTAX; | |
1790 | sqiscstyle = 1; | |
1791 | } else { | |
1792 | USTPUTC('$', out); | |
1793 | pungetc(); | |
1794 | } | |
1795 | goto parsesub_return; | |
1796 | } | |
1797 | ||
1798 | ||
1799 | /* | |
1800 | * Parse an arithmetic expansion (indicate start of one and set state) | |
1801 | */ | |
1802 | parsearith: { | |
1803 | ||
1804 | if (level + 1 >= maxnest) { | |
1805 | maxnest *= 2; | |
1806 | if (state == state_static) { | |
1807 | state = parser_temp_alloc( | |
1808 | maxnest * sizeof(*state)); | |
1809 | memcpy(state, state_static, | |
1810 | MAXNEST_static * sizeof(*state)); | |
1811 | } else | |
1812 | state = parser_temp_realloc(state, | |
1813 | maxnest * sizeof(*state)); | |
1814 | } | |
1815 | level++; | |
1816 | state[level].syntax = ARISYNTAX; | |
1817 | state[level].parenlevel = 0; | |
1818 | state[level].category = TSTATE_ARITH; | |
1819 | USTPUTC(CTLARI, out); | |
1820 | if (state[level - 1].syntax == DQSYNTAX) | |
1821 | USTPUTC('"',out); | |
1822 | else | |
1823 | USTPUTC(' ',out); | |
1824 | goto parsearith_return; | |
1825 | } | |
1826 | ||
1827 | } /* end of readtoken */ | |
1828 | ||
1829 | ||
1830 | /* | |
1831 | * Returns true if the text contains nothing to expand (no dollar signs | |
1832 | * or backquotes). | |
1833 | */ | |
1834 | ||
1835 | static int | |
1836 | noexpand(char *text) | |
1837 | { | |
1838 | char *p; | |
1839 | char c; | |
1840 | ||
1841 | p = text; | |
1842 | while ((c = *p++) != '\0') { | |
1843 | if ( c == CTLQUOTEMARK) | |
1844 | continue; | |
1845 | if (c == CTLESC) | |
1846 | p++; | |
1847 | else if (BASESYNTAX[(int)c] == CCTL) | |
1848 | return 0; | |
1849 | } | |
1850 | return 1; | |
1851 | } | |
1852 | ||
1853 | ||
1854 | /* | |
1855 | * Return true if the argument is a legal variable name (a letter or | |
1856 | * underscore followed by zero or more letters, underscores, and digits). | |
1857 | */ | |
1858 | ||
1859 | int | |
1860 | goodname(const char *name) | |
1861 | { | |
1862 | const char *p; | |
1863 | ||
1864 | p = name; | |
1865 | if (! is_name(*p)) | |
1866 | return 0; | |
1867 | while (*++p) { | |
1868 | if (! is_in_name(*p)) | |
1869 | return 0; | |
1870 | } | |
1871 | return 1; | |
1872 | } | |
1873 | ||
1874 | ||
1875 | int | |
1876 | isassignment(const char *p) | |
1877 | { | |
1878 | if (!is_name(*p)) | |
1879 | return 0; | |
1880 | p++; | |
1881 | for (;;) { | |
1882 | if (*p == '=') | |
1883 | return 1; | |
1884 | else if (!is_in_name(*p)) | |
1885 | return 0; | |
1886 | p++; | |
1887 | } | |
1888 | } | |
1889 | ||
1890 | ||
1891 | static void | |
1892 | consumetoken(int token) | |
1893 | { | |
1894 | if (readtoken() != token) | |
1895 | synexpect(token); | |
1896 | } | |
1897 | ||
1898 | ||
1899 | /* | |
1900 | * Called when an unexpected token is read during the parse. The argument | |
1901 | * is the token that is expected, or -1 if more than one type of token can | |
1902 | * occur at this point. | |
1903 | */ | |
1904 | ||
1905 | static void | |
1906 | synexpect(int token) | |
1907 | { | |
1908 | char msg[64]; | |
1909 | ||
1910 | if (token >= 0) { | |
1911 | fmtstr(msg, 64, "%s unexpected (expecting %s)", | |
1912 | tokname[lasttoken], tokname[token]); | |
1913 | } else { | |
1914 | fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); | |
1915 | } | |
1916 | synerror(msg); | |
1917 | } | |
1918 | ||
1919 | ||
1920 | static void | |
1921 | synerror(const char *msg) | |
1922 | { | |
1923 | if (commandname) | |
1924 | outfmt(out2, "%s: %d: ", commandname, startlinno); | |
1925 | else if (arg0) | |
1926 | outfmt(out2, "%s: ", arg0); | |
1927 | outfmt(out2, "Syntax error: %s\n", msg); | |
1928 | error((char *)NULL); | |
1929 | } | |
1930 | ||
1931 | static void | |
1932 | setprompt(int which) | |
1933 | { | |
1934 | whichprompt = which; | |
deb63bfb A |
1935 | if (which == 0) |
1936 | return; | |
71aad674 A |
1937 | |
1938 | #ifndef NO_HISTORY | |
1939 | if (!el) | |
1940 | #endif | |
1941 | { | |
1942 | out2str(getprompt(NULL)); | |
1943 | flushout(out2); | |
1944 | } | |
1945 | } | |
1946 | ||
1947 | static int | |
1948 | pgetc_linecont(void) | |
1949 | { | |
1950 | int c; | |
1951 | ||
1952 | while ((c = pgetc_macro()) == '\\') { | |
1953 | c = pgetc(); | |
1954 | if (c == '\n') { | |
1955 | plinno++; | |
1956 | if (doprompt) | |
1957 | setprompt(2); | |
1958 | else | |
1959 | setprompt(0); | |
1960 | } else { | |
1961 | pungetc(); | |
1962 | /* Allow the backslash to be pushed back. */ | |
1963 | pushstring("\\", 1, NULL); | |
1964 | return (pgetc()); | |
1965 | } | |
1966 | } | |
1967 | return (c); | |
1968 | } | |
1969 | ||
1970 | /* | |
1971 | * called by editline -- any expansions to the prompt | |
1972 | * should be added here. | |
1973 | */ | |
1974 | char * | |
1975 | getprompt(void *unused __unused) | |
1976 | { | |
1977 | static char ps[PROMPTLEN]; | |
1978 | const char *fmt; | |
1979 | const char *pwd; | |
1980 | int i, trim; | |
1981 | static char internal_error[] = "??"; | |
1982 | ||
1983 | /* | |
1984 | * Select prompt format. | |
1985 | */ | |
1986 | switch (whichprompt) { | |
1987 | case 0: | |
1988 | fmt = ""; | |
1989 | break; | |
1990 | case 1: | |
1991 | fmt = ps1val(); | |
1992 | break; | |
1993 | case 2: | |
1994 | fmt = ps2val(); | |
1995 | break; | |
1996 | default: | |
1997 | return internal_error; | |
1998 | } | |
1999 | ||
2000 | /* | |
2001 | * Format prompt string. | |
2002 | */ | |
deb63bfb | 2003 | for (i = 0; (i < PROMPTLEN - 1) && (*fmt != '\0'); i++, fmt++) |
71aad674 A |
2004 | if (*fmt == '\\') |
2005 | switch (*++fmt) { | |
2006 | ||
2007 | /* | |
2008 | * Hostname. | |
2009 | * | |
2010 | * \h specifies just the local hostname, | |
2011 | * \H specifies fully-qualified hostname. | |
2012 | */ | |
2013 | case 'h': | |
2014 | case 'H': | |
2015 | ps[i] = '\0'; | |
deb63bfb A |
2016 | gethostname(&ps[i], PROMPTLEN - i - 1); |
2017 | ps[PROMPTLEN - 1] = '\0'; | |
71aad674 A |
2018 | /* Skip to end of hostname. */ |
2019 | trim = (*fmt == 'h') ? '.' : '\0'; | |
deb63bfb | 2020 | while ((ps[i] != '\0') && (ps[i] != trim)) |
71aad674 | 2021 | i++; |
deb63bfb | 2022 | --i; |
71aad674 A |
2023 | break; |
2024 | ||
2025 | /* | |
2026 | * Working directory. | |
2027 | * | |
2028 | * \W specifies just the final component, | |
2029 | * \w specifies the entire path. | |
2030 | */ | |
2031 | case 'W': | |
2032 | case 'w': | |
2033 | pwd = lookupvar("PWD"); | |
deb63bfb | 2034 | if (pwd == NULL || *pwd == '\0') |
71aad674 A |
2035 | pwd = "?"; |
2036 | if (*fmt == 'W' && | |
2037 | *pwd == '/' && pwd[1] != '\0') | |
2038 | strlcpy(&ps[i], strrchr(pwd, '/') + 1, | |
2039 | PROMPTLEN - i); | |
2040 | else | |
2041 | strlcpy(&ps[i], pwd, PROMPTLEN - i); | |
2042 | /* Skip to end of path. */ | |
2043 | while (ps[i + 1] != '\0') | |
2044 | i++; | |
2045 | break; | |
2046 | ||
2047 | /* | |
2048 | * Superuser status. | |
2049 | * | |
2050 | * '$' for normal users, '#' for root. | |
2051 | */ | |
2052 | case '$': | |
2053 | ps[i] = (geteuid() != 0) ? '$' : '#'; | |
2054 | break; | |
2055 | ||
2056 | /* | |
2057 | * A literal \. | |
2058 | */ | |
2059 | case '\\': | |
2060 | ps[i] = '\\'; | |
2061 | break; | |
2062 | ||
2063 | /* | |
2064 | * Emit unrecognized formats verbatim. | |
2065 | */ | |
2066 | default: | |
deb63bfb A |
2067 | ps[i] = '\\'; |
2068 | if (i < PROMPTLEN - 2) | |
2069 | ps[++i] = *fmt; | |
71aad674 A |
2070 | break; |
2071 | } | |
2072 | else | |
2073 | ps[i] = *fmt; | |
2074 | ps[i] = '\0'; | |
2075 | return (ps); | |
2076 | } | |
2077 | ||
2078 | ||
2079 | const char * | |
2080 | expandstr(const char *ps) | |
2081 | { | |
2082 | union node n; | |
2083 | struct jmploc jmploc; | |
2084 | struct jmploc *const savehandler = handler; | |
2085 | const int saveprompt = doprompt; | |
2086 | struct parsefile *const savetopfile = getcurrentfile(); | |
2087 | struct parser_temp *const saveparser_temp = parser_temp; | |
2088 | const char *result = NULL; | |
2089 | ||
2090 | if (!setjmp(jmploc.loc)) { | |
2091 | handler = &jmploc; | |
2092 | parser_temp = NULL; | |
2093 | setinputstring(ps, 1); | |
2094 | doprompt = 0; | |
deb63bfb | 2095 | readtoken1(pgetc(), DQSYNTAX, NOEOFMARK, 0); |
71aad674 A |
2096 | if (backquotelist != NULL) |
2097 | error("Command substitution not allowed here"); | |
2098 | ||
2099 | n.narg.type = NARG; | |
2100 | n.narg.next = NULL; | |
2101 | n.narg.text = wordtext; | |
2102 | n.narg.backquote = backquotelist; | |
2103 | ||
2104 | expandarg(&n, NULL, 0); | |
2105 | result = stackblock(); | |
2106 | INTOFF; | |
2107 | } | |
2108 | handler = savehandler; | |
2109 | doprompt = saveprompt; | |
2110 | popfilesupto(savetopfile); | |
2111 | if (parser_temp != saveparser_temp) { | |
2112 | parser_temp_free_all(); | |
2113 | parser_temp = saveparser_temp; | |
2114 | } | |
2115 | if (result != NULL) { | |
2116 | INTON; | |
2117 | } else if (exception == EXINT) | |
2118 | raise(SIGINT); | |
2119 | return result; | |
2120 | } |