VirtualBox

source: kBuild/trunk/src/kash/parser.c

Last change on this file was 3573, checked in by bird, 2 years ago

kash: addressed some pedantic warnings

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette