source: trunk/src/kash/parser.c

Last change on this file was 3573, checked in by bird, 3 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
RevLine 
[626]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
[1214]35#if 0
[626]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 $");
[1214]40#endif /* not lint */
[626]41#endif
42
[3456]43#define SH_MEMALLOC_NO_STACK
[626]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
[1214]63# include "myhistedit.h"
[626]64#endif
[1236]65#include "cd.h"
[1210]66#include "shinstance.h"
[626]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
[1210]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 */
[626]104
105
[1210]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 *);
[2298]119SH_NORETURN_1 STATIC void synexpect(shinstance *, int) SH_NORETURN_2;
120SH_NORETURN_1 STATIC void synerror(shinstance *, const char *) SH_NORETURN_2;
[1210]121STATIC void setprompt(shinstance *, int);
[626]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 *
[1210]130parsecmd(shinstance *psh, int interact)
[626]131{
[3456]132 union node *ret;
[626]133 int t;
[3457]134#ifdef KASH_SEPARATE_PARSER_ALLOCATOR
[3461]135 pstack_block *pst = pstackallocpush(psh);
[3457]136#endif
[3461]137 TRACE2((psh, "parsecmd(%d)\n", interact));
138
[1210]139 psh->tokpushback = 0;
140 psh->doprompt = interact;
141 if (psh->doprompt)
142 setprompt(psh, 1);
[626]143 else
[1210]144 setprompt(psh, 0);
145 psh->needprompt = 0;
146 t = readtoken(psh);
[626]147 if (t == TEOF)
148 return NEOF;
149 if (t == TNL)
150 return NULL;
[1210]151 psh->tokpushback++;
[3456]152 ret = list(psh, 1);
153#if 0 /*def DEBUG*/
154 TRACE2((psh, "parsecmd(%d) returns:\n", interact));
155 showtree(psh, ret);
156#endif
[3461]157#ifdef KASH_SEPARATE_PARSER_ALLOCATOR
158 pstackmarkdone(pst);
159#endif
[3456]160 return ret;
[626]161}
162
163
164STATIC union node *
[1210]165list(shinstance *psh, int nlflag)
[626]166{
167 union node *n1, *n2, *n3;
168 int tok;
169
[1210]170 psh->checkkwd = 2;
171 if (nlflag == 0 && tokendlist[peektoken(psh)])
[626]172 return NULL;
173 n1 = NULL;
174 for (;;) {
[1210]175 n2 = andor(psh);
176 tok = readtoken(psh);
[626]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 {
[3456]183 n3 = pstallocnode(psh, sizeof (struct nredir));
[626]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 {
[3456]194 n3 = pstallocnode(psh, sizeof (struct nbinary));
[626]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:
[1210]203 tok = readtoken(psh);
[626]204 /* fall through */
205 case TNL:
206 if (tok == TNL) {
[1210]207 parseheredoc(psh);
[626]208 if (nlflag)
209 return n1;
210 } else {
[1210]211 psh->tokpushback++;
[626]212 }
[1210]213 psh->checkkwd = 2;
214 if (tokendlist[peektoken(psh)])
[626]215 return n1;
216 break;
217 case TEOF:
[1210]218 if (psh->heredoclist)
219 parseheredoc(psh);
[626]220 else
[1201]221 pungetc(psh); /* push back EOF on input */
[626]222 return n1;
223 default:
224 if (nlflag)
[1210]225 synexpect(psh, -1);
226 psh->tokpushback++;
[626]227 return n1;
228 }
229 }
230}
231
232
233
234STATIC union node *
[1210]235andor(shinstance *psh)
[626]236{
237 union node *n1, *n2, *n3;
238 int t;
239
[1210]240 n1 = pipeline(psh);
[626]241 for (;;) {
[1210]242 if ((t = readtoken(psh)) == TAND) {
[626]243 t = NAND;
244 } else if (t == TOR) {
245 t = NOR;
246 } else {
[1210]247 psh->tokpushback++;
[626]248 return n1;
249 }
[1210]250 n2 = pipeline(psh);
[3456]251 n3 = pstallocnode(psh, sizeof (struct nbinary));
[626]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 *
[1210]262pipeline(shinstance *psh)
[626]263{
264 union node *n1, *n2, *pipenode;
265 struct nodelist *lp, *prev;
266 int negate;
267
268 negate = 0;
[1199]269 TRACE((psh, "pipeline: entered\n"));
[1210]270 while (readtoken(psh) == TNOT)
[626]271 negate = !negate;
[1210]272 psh->tokpushback++;
273 n1 = command(psh);
274 if (readtoken(psh) == TPIPE) {
[3456]275 pipenode = pstallocnode(psh, sizeof (struct npipe));
[626]276 pipenode->type = NPIPE;
277 pipenode->npipe.backgnd = 0;
[3456]278 lp = pstalloclist(psh);
[626]279 pipenode->npipe.cmdlist = lp;
280 lp->n = n1;
281 do {
282 prev = lp;
[3456]283 lp = pstalloclist(psh);
[1210]284 lp->n = command(psh);
[626]285 prev->next = lp;
[1210]286 } while (readtoken(psh) == TPIPE);
[626]287 lp->next = NULL;
288 n1 = pipenode;
289 }
[1210]290 psh->tokpushback++;
[626]291 if (negate) {
[3456]292 n2 = pstallocnode(psh, sizeof (struct nnot));
[626]293 n2->type = NNOT;
294 n2->nnot.com = n1;
295 return n2;
296 } else
297 return n1;
298}
299
300
301
302STATIC union node *
[1210]303command(shinstance *psh)
[626]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
[1210]311 psh->checkkwd = 2;
[626]312 redir = NULL;
313 n1 = NULL;
314 rpp = &redir;
315
316 /* Check for redirection which may precede command */
[1210]317 while (readtoken(psh) == TREDIR) {
318 *rpp = n2 = psh->redirnode;
[626]319 rpp = &n2->nfile.next;
[1210]320 parsefname(psh);
[626]321 }
[1210]322 psh->tokpushback++;
[626]323
[1210]324 while (readtoken(psh) == TNOT) {
[1199]325 TRACE((psh, "command: TNOT recognized\n"));
[626]326 negate = !negate;
327 }
[1210]328 psh->tokpushback++;
[626]329
[1210]330 switch (readtoken(psh)) {
[626]331 case TIF:
[3456]332 n1 = pstallocnode(psh, sizeof (struct nif));
[626]333 n1->type = NIF;
[1210]334 n1->nif.test = list(psh, 0);
335 if (readtoken(psh) != TTHEN)
336 synexpect(psh, TTHEN);
337 n1->nif.ifpart = list(psh, 0);
[626]338 n2 = n1;
[1210]339 while (readtoken(psh) == TELIF) {
[3456]340 n2->nif.elsepart = pstallocnode(psh, sizeof (struct nif));
[626]341 n2 = n2->nif.elsepart;
342 n2->type = NIF;
[1210]343 n2->nif.test = list(psh, 0);
344 if (readtoken(psh) != TTHEN)
345 synexpect(psh, TTHEN);
346 n2->nif.ifpart = list(psh, 0);
[626]347 }
[1210]348 if (psh->lasttoken == TELSE)
349 n2->nif.elsepart = list(psh, 0);
[626]350 else {
351 n2->nif.elsepart = NULL;
[1210]352 psh->tokpushback++;
[626]353 }
[1210]354 if (readtoken(psh) != TFI)
355 synexpect(psh, TFI);
356 psh->checkkwd = 1;
[626]357 break;
358 case TWHILE:
359 case TUNTIL: {
360 int got;
[3456]361 n1 = pstallocnode(psh, sizeof (struct nbinary));
[1210]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);
[626]367 }
[1210]368 n1->nbinary.ch2 = list(psh, 0);
369 if (readtoken(psh) != TDONE)
370 synexpect(psh, TDONE);
371 psh->checkkwd = 1;
[626]372 break;
373 }
374 case TFOR:
[1210]375 if (readtoken(psh) != TWORD || psh->quoteflag || ! goodname(psh->wordtext))
376 synerror(psh, "Bad for loop variable");
[3456]377 n1 = pstallocnode(psh, sizeof (struct nfor));
[626]378 n1->type = NFOR;
[1210]379 n1->nfor.var = psh->wordtext;
380 if (readtoken(psh) == TWORD && ! psh->quoteflag && equal(psh->wordtext, "in")) {
[626]381 app = &ap;
[1210]382 while (readtoken(psh) == TWORD) {
[3456]383 n2 = pstallocnode(psh, sizeof (struct narg));
[626]384 n2->type = NARG;
[1210]385 n2->narg.text = psh->wordtext;
386 n2->narg.backquote = psh->backquotelist;
[626]387 *app = n2;
388 app = &n2->narg.next;
389 }
390 *app = NULL;
391 n1->nfor.args = ap;
[1210]392 if (psh->lasttoken != TNL && psh->lasttoken != TSEMI)
393 synexpect(psh, -1);
[626]394 } else {
[3573]395 static char argvars[5] = {CTLVAR, (char)(unsigned char)(VSNORMAL|VSQUOTE),
[626]396 '@', '=', '\0'};
[3456]397 n2 = pstallocnode(psh, sizeof (struct narg));
[626]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 */
[1210]407 if (psh->lasttoken != TNL && psh->lasttoken != TSEMI)
408 psh->tokpushback++;
[626]409 }
[1210]410 psh->checkkwd = 2;
411 if ((t = readtoken(psh)) == TDO)
[626]412 t = TDONE;
413 else if (t == TBEGIN)
414 t = TEND;
415 else
[1210]416 synexpect(psh, -1);
417 n1->nfor.body = list(psh, 0);
418 if (readtoken(psh) != t)
419 synexpect(psh, t);
420 psh->checkkwd = 1;
[626]421 break;
422 case TCASE:
[3456]423 n1 = pstallocnode(psh, sizeof (struct ncase));
[626]424 n1->type = NCASE;
[1210]425 if (readtoken(psh) != TWORD)
426 synexpect(psh, TWORD);
[3456]427 n1->ncase.expr = n2 = pstallocnode(psh, sizeof (struct narg));
[626]428 n2->type = NARG;
[1210]429 n2->narg.text = psh->wordtext;
430 n2->narg.backquote = psh->backquotelist;
[626]431 n2->narg.next = NULL;
[1210]432 while (readtoken(psh) == TNL);
433 if (psh->lasttoken != TWORD || ! equal(psh->wordtext, "in"))
434 synerror(psh, "expecting \"in\"");
[626]435 cpp = &n1->ncase.cases;
[1210]436 psh->noalias = 1;
437 psh->checkkwd = 2, readtoken(psh);
[626]438 do {
[3456]439 *cpp = cp = pstallocnode(psh, sizeof (struct nclist));
[626]440 cp->type = NCLIST;
441 app = &cp->nclist.pattern;
442 for (;;) {
[3456]443 *app = ap = pstallocnode(psh, sizeof (struct narg));
[626]444 ap->type = NARG;
[1210]445 ap->narg.text = psh->wordtext;
446 ap->narg.backquote = psh->backquotelist;
447 if (psh->checkkwd = 2, readtoken(psh) != TPIPE)
[626]448 break;
449 app = &ap->narg.next;
[1210]450 readtoken(psh);
[626]451 }
452 ap->narg.next = NULL;
[1210]453 psh->noalias = 0;
454 if (psh->lasttoken != TRP) {
455 synexpect(psh, TRP);
[626]456 }
[1210]457 cp->nclist.body = list(psh, 0);
[626]458
[1210]459 psh->checkkwd = 2;
460 if ((t = readtoken(psh)) != TESAC) {
[626]461 if (t != TENDCASE) {
[1210]462 psh->noalias = 0;
463 synexpect(psh, TENDCASE);
[626]464 } else {
[1210]465 psh->noalias = 1;
466 psh->checkkwd = 2;
467 readtoken(psh);
[626]468 }
469 }
470 cpp = &cp->nclist.next;
[1210]471 } while(psh->lasttoken != TESAC);
472 psh->noalias = 0;
[626]473 *cpp = NULL;
[1210]474 psh->checkkwd = 1;
[626]475 break;
476 case TLP:
[3456]477 n1 = pstallocnode(psh, sizeof (struct nredir));
[626]478 n1->type = NSUBSHELL;
[1210]479 n1->nredir.n = list(psh, 0);
[626]480 n1->nredir.redirect = NULL;
[1210]481 if (readtoken(psh) != TRP)
482 synexpect(psh, TRP);
483 psh->checkkwd = 1;
[626]484 break;
485 case TBEGIN:
[1210]486 n1 = list(psh, 0);
487 if (readtoken(psh) != TEND)
488 synexpect(psh, TEND);
489 psh->checkkwd = 1;
[626]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)
[1210]498 synexpect(psh, -1);
[3573]499 /* FALLTHROUGH */
[626]500 case TAND:
501 case TOR:
502 case TNL:
503 case TEOF:
504 case TWORD:
505 case TRP:
[1210]506 psh->tokpushback++;
507 n1 = simplecmd(psh, rpp, redir);
[626]508 goto checkneg;
509 default:
[1210]510 synexpect(psh, -1);
[626]511 /* NOTREACHED */
512 }
513
514 /* Now check for redirection which may follow command */
[1210]515 while (readtoken(psh) == TREDIR) {
516 *rpp = n2 = psh->redirnode;
[626]517 rpp = &n2->nfile.next;
[1210]518 parsefname(psh);
[626]519 }
[1210]520 psh->tokpushback++;
[626]521 *rpp = NULL;
522 if (redir) {
523 if (n1->type != NSUBSHELL) {
[3456]524 n2 = pstallocnode(psh, sizeof (struct nredir));
[626]525 n2->type = NREDIR;
526 n2->nredir.n = n1;
527 n1 = n2;
528 }
529 n1->nredir.redirect = redir;
530 }
531
532checkneg:
533 if (negate) {
[3456]534 n2 = pstallocnode(psh, sizeof (struct nnot));
[626]535 n2->type = NNOT;
536 n2->nnot.com = n1;
537 return n2;
538 }
539 else
540 return n1;
541}
542
543
544STATIC union node *
[1210]545simplecmd(shinstance *psh, union node **rpp, union node *redir)
[626]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
[1210]566 while (readtoken(psh) == TNOT) {
[1199]567 TRACE((psh, "command: TNOT recognized\n"));
[626]568 negate = !negate;
569 }
[1210]570 psh->tokpushback++;
[626]571
572 for (;;) {
[1210]573 if (readtoken(psh) == TWORD) {
[3456]574 n = pstallocnode(psh, sizeof (struct narg));
[626]575 n->type = NARG;
[1210]576 n->narg.text = psh->wordtext;
577 n->narg.backquote = psh->backquotelist;
[626]578 *app = n;
579 app = &n->narg.next;
[1210]580 } else if (psh->lasttoken == TREDIR) {
581 *rpp = n = psh->redirnode;
[626]582 rpp = &n->nfile.next;
[1210]583 parsefname(psh); /* read name of redirection file */
584 } else if (psh->lasttoken == TLP && app == &args->narg.next
[626]585 && rpp == orig_rpp) {
586 /* We have a function */
[1210]587 if (readtoken(psh) != TRP)
588 synexpect(psh, TRP);
[626]589#ifdef notdef
590 if (! goodname(n->narg.text))
[1210]591 synerror(psh, "Bad function name");
[626]592#endif
593 n->type = NDEFUN;
[1210]594 n->narg.next = command(psh);
[626]595 goto checkneg;
596 } else {
[1210]597 psh->tokpushback++;
[626]598 break;
599 }
600 }
601 *app = NULL;
602 *rpp = NULL;
[3456]603 n = pstallocnode(psh, sizeof (struct ncmd));
[626]604 n->type = NCMD;
605 n->ncmd.backgnd = 0;
606 n->ncmd.args = args;
607 n->ncmd.redirect = redir;
608
609checkneg:
610 if (negate) {
[3456]611 n2 = pstallocnode(psh, sizeof (struct nnot));
[626]612 n2->type = NNOT;
613 n2->nnot.com = n;
614 return n2;
615 }
616 else
617 return n;
618}
619
620STATIC union node *
[1210]621makename(shinstance *psh)
[626]622{
623 union node *n;
624
[3456]625 n = pstallocnode(psh, sizeof (struct narg));
[626]626 n->type = NARG;
627 n->narg.next = NULL;
[1210]628 n->narg.text = psh->wordtext;
629 n->narg.backquote = psh->backquotelist;
[626]630 return n;
631}
632
[1210]633void fixredir(shinstance *psh, union node *n, const char *text, int err)
[3438]634{
[1199]635 TRACE((psh, "Fix redir %s %d\n", text, err));
[626]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)
[1210]646 synerror(psh, "Bad fd number");
[626]647 else
[1210]648 n->ndup.vname = makename(psh);
[626]649 }
650}
651
652
653STATIC void
[1210]654parsefname(shinstance *psh)
[626]655{
[1210]656 union node *n = psh->redirnode;
[626]657
[1210]658 if (readtoken(psh) != TWORD)
659 synexpect(psh, -1);
[626]660 if (n->type == NHERE) {
[1210]661 struct heredoc *here = psh->heredoc;
[626]662 struct heredoc *p;
[1210]663 size_t i;
[626]664
[1210]665 if (psh->quoteflag == 0)
[626]666 n->type = NXHERE;
[1199]667 TRACE((psh, "Here document %d\n", n->type));
[626]668 if (here->striptabs) {
[1210]669 while (*psh->wordtext == '\t')
670 psh->wordtext++;
[626]671 }
[1210]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;
[626]676 here->next = NULL;
[1210]677 if (psh->heredoclist == NULL)
678 psh->heredoclist = here;
[626]679 else {
[1210]680 for (p = psh->heredoclist ; p->next ; p = p->next);
[626]681 p->next = here;
682 }
683 } else if (n->type == NTOFD || n->type == NFROMFD) {
[1210]684 fixredir(psh, n, psh->wordtext, 0);
[626]685 } else {
[1210]686 n->nfile.fname = makename(psh);
[626]687 }
688}
689
690
691/*
692 * Input any here documents.
693 */
694
695STATIC void
[1210]696parseheredoc(shinstance *psh)
[626]697{
698 struct heredoc *here;
699 union node *n;
700
[1210]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;
[626]707 }
[1210]708 readtoken1(psh, pgetc(psh), here->here->type == NHERE? SQSYNTAX : DQSYNTAX,
[626]709 here->eofmark, here->striptabs);
[3456]710 n = pstallocnode(psh, sizeof (struct narg));
[626]711 n->narg.type = NARG;
712 n->narg.next = NULL;
[1210]713 n->narg.text = psh->wordtext;
714 n->narg.backquote = psh->backquotelist;
[626]715 here->here->nhere.doc = n;
716 }
717}
718
719STATIC int
[1210]720peektoken(shinstance *psh)
[626]721{
722 int t;
723
[1210]724 t = readtoken(psh);
725 psh->tokpushback++;
[626]726 return (t);
727}
728
729STATIC int
[1210]730readtoken(shinstance *psh)
[626]731{
732 int t;
[1210]733 int savecheckkwd = psh->checkkwd;
[626]734#ifdef DEBUG
[1210]735 int alreadyseen = psh->tokpushback;
[626]736#endif
737 struct alias *ap;
738
739 top:
[1210]740 t = xxreadtoken(psh);
[626]741
[1210]742 if (psh->checkkwd) {
[626]743 /*
744 * eat newlines
745 */
[1210]746 if (psh->checkkwd == 2) {
747 psh->checkkwd = 0;
[626]748 while (t == TNL) {
[1210]749 parseheredoc(psh);
750 t = xxreadtoken(psh);
[626]751 }
752 } else
[1210]753 psh->checkkwd = 0;
[626]754 /*
755 * check for keywords and aliases
756 */
[1210]757 if (t == TWORD && !psh->quoteflag)
[626]758 {
759 const char *const *pp;
760
761 for (pp = parsekwd; *pp; pp++) {
[1210]762 if (**pp == *psh->wordtext && equal(*pp, psh->wordtext))
[626]763 {
[1210]764 psh->lasttoken = t = (int)(pp -
765 parsekwd + KWDOFFSET);
[1199]766 TRACE((psh, "keyword %s recognized\n", tokname[t]));
[626]767 goto out;
768 }
769 }
[1210]770 if(!psh->noalias &&
771 (ap = lookupalias(psh, psh->wordtext, 1)) != NULL) {
[1201]772 pushstring(psh, ap->val, strlen(ap->val), ap);
[1210]773 psh->checkkwd = savecheckkwd;
[626]774 goto top;
775 }
776 }
777out:
[1210]778 psh->checkkwd = (t == TNOT) ? savecheckkwd : 0;
[626]779 }
780#ifdef DEBUG
781 if (!alreadyseen)
[1210]782 TRACE((psh, "token %s %s\n", tokname[t], t == TWORD ? psh->wordtext : ""));
[626]783 else
[2628]784 TRACE((psh, "reread token %s \"%s\"\n", tokname[t], t == TWORD ? psh->wordtext : ""));
[626]785#endif
786 return (t);
787}
788
789
790/*
791 * Read the next input token.
[1210]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
[626]794 * quoted.
[1210]795 * If the token is TREDIR, then we set psh->redirnode to a structure containing
[626]796 * the redirection.
[1210]797 * In all cases, the variable psh->startlinno is set to the number of the line
[626]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
[1210]808#define RETURN(token) return psh->lasttoken = token
[626]809
810STATIC int
[1210]811xxreadtoken(shinstance *psh)
[626]812{
813 int c;
814
[1210]815 if (psh->tokpushback) {
816 psh->tokpushback = 0;
817 return psh->lasttoken;
[626]818 }
[1210]819 if (psh->needprompt) {
820 setprompt(psh, 2);
821 psh->needprompt = 0;
[626]822 }
[1210]823 psh->startlinno = psh->plinno;
[626]824 for (;;) { /* until token or start of word found */
[1201]825 c = pgetc_macro(psh);
[626]826 if (c == ' ' || c == '\t')
827 continue; /* quick check for white space first */
828 switch (c) {
829 case ' ': case '\t':
830 continue;
831 case '#':
[1201]832 while ((c = pgetc(psh)) != '\n' && c != PEOF);
833 pungetc(psh);
[626]834 continue;
835 case '\\':
[1201]836 if (pgetc(psh) == '\n') {
[1210]837 psh->startlinno = ++psh->plinno;
838 if (psh->doprompt)
839 setprompt(psh, 2);
[626]840 else
[1210]841 setprompt(psh, 0);
[626]842 continue;
843 }
[1201]844 pungetc(psh);
[626]845 goto breakloop;
846 case '\n':
[1210]847 psh->plinno++;
848 psh->needprompt = psh->doprompt;
[626]849 RETURN(TNL);
850 case PEOF:
851 RETURN(TEOF);
852 case '&':
[1201]853 if (pgetc(psh) == '&')
[626]854 RETURN(TAND);
[1201]855 pungetc(psh);
[626]856 RETURN(TBACKGND);
857 case '|':
[1201]858 if (pgetc(psh) == '|')
[626]859 RETURN(TOR);
[1201]860 pungetc(psh);
[626]861 RETURN(TPIPE);
862 case ';':
[1201]863 if (pgetc(psh) == ';')
[626]864 RETURN(TENDCASE);
[1201]865 pungetc(psh);
[626]866 RETURN(TSEMI);
867 case '(':
868 RETURN(TLP);
869 case ')':
870 RETURN(TRP);
[2630]871 default:
[626]872 goto breakloop;
873 }
874 }
875breakloop:
[1210]876 return readtoken1(psh, c, BASESYNTAX, (char *)NULL, 0);
[626]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
[1210]924readtoken1(shinstance *psh, int firstc, char const *syntax, char *eofmark, int striptabs)
[626]925{
926 int c = firstc;
927 char *out;
928 char line[EOFMARKLEN + 1];
929 struct nodelist *bqlist;
[3065]930 int quotef = 0;
[626]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 */
[3065]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
[626]951#if __GNUC__
[3065]952 /* Try avoid longjmp clobbering */
[626]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
[3456]966 PSTARTSTACKSTR(psh, out);
[626]967 loop: { /* for each line, until end of word */
968#if ATTY
[1210]969 if (c == '\034' && psh->doprompt
[626]970 && attyset() && ! equal(termval(), "emacs")) {
971 attyline();
972 if (syntax == BASESYNTAX)
[1210]973 return readtoken(psh);
[1201]974 c = pgetc(psh);
[626]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 */
[3456]980 PSTCHECKSTRSPACE(psh, 4+1, out); /* permit 4 calls to PSTUPUTC, pluss terminator */
[626]981 switch(syntax[c]) {
982 case CNL: /* '\n' */
983 if (syntax == BASESYNTAX)
984 goto endword; /* exit outer loop */
[3456]985 PSTUPUTC(psh, c, out);
[1210]986 psh->plinno++;
987 if (psh->doprompt)
988 setprompt(psh, 2);
[626]989 else
[1210]990 setprompt(psh, 0);
[1201]991 c = pgetc(psh);
[626]992 goto loop; /* continue outer loop */
993 case CWORD:
[3456]994 PSTUPUTC(psh, c, out);
[626]995 break;
996 case CCTL:
997 if (eofmark == NULL || ISDBLQUOTE())
[3456]998 PSTUPUTC(psh, CTLESC, out);
999 PSTUPUTC(psh, c, out);
[626]1000 break;
1001 case CBACK: /* backslash */
[1201]1002 c = pgetc(psh);
[626]1003 if (c == PEOF) {
[3456]1004 PSTUPUTC(psh, '\\', out);
[1201]1005 pungetc(psh);
[626]1006 break;
1007 }
1008 if (c == '\n') {
[1210]1009 if (psh->doprompt)
1010 setprompt(psh, 2);
[626]1011 else
[1210]1012 setprompt(psh, 0);
[626]1013 break;
1014 }
1015 quotef = 1;
1016 if (ISDBLQUOTE() && c != '\\' &&
1017 c != '`' && c != '$' &&
1018 (c != '"' || eofmark != NULL))
[3456]1019 PSTUPUTC(psh, '\\', out);
[626]1020 if (SQSYNTAX[c] == CCTL)
[3456]1021 PSTUPUTC(psh, CTLESC, out);
[626]1022 else if (eofmark == NULL) {
[3456]1023 PSTUPUTC(psh, CTLQUOTEMARK, out);
1024 PSTUPUTC(psh, c, out);
[626]1025 if (varnest != 0)
[3456]1026 PSTUPUTC(psh, CTLQUOTEEND, out);
[626]1027 break;
1028 }
[3456]1029 PSTUPUTC(psh, c, out);
[626]1030 break;
1031 case CSQUOTE:
1032 if (syntax != SQSYNTAX) {
1033 if (eofmark == NULL)
[3456]1034 PSTUPUTC(psh, CTLQUOTEMARK, out);
[626]1035 quotef = 1;
1036 syntax = SQSYNTAX;
1037 break;
1038 }
1039 if (eofmark != NULL && arinest == 0 &&
1040 varnest == 0) {
1041 /* Ignore inside quoted here document */
[3456]1042 PSTUPUTC(psh, c, out);
[626]1043 break;
1044 }
1045 /* End of single quotes... */
1046 if (arinest)
1047 syntax = ARISYNTAX;
1048 else {
1049 syntax = BASESYNTAX;
1050 if (varnest != 0)
[3456]1051 PSTUPUTC(psh, CTLQUOTEEND, out);
[626]1052 }
1053 break;
1054 case CDQUOTE:
1055 if (eofmark != NULL && arinest == 0 &&
1056 varnest == 0) {
1057 /* Ignore inside here document */
[3456]1058 PSTUPUTC(psh, c, out);
[626]1059 break;
1060 }
1061 quotef = 1;
1062 if (arinest) {
1063 if (ISDBLQUOTE()) {
1064 syntax = ARISYNTAX;
1065 CLRDBLQUOTE();
1066 } else {
1067 syntax = DQSYNTAX;
1068 SETDBLQUOTE();
[3456]1069 PSTUPUTC(psh, CTLQUOTEMARK, out);
[626]1070 }
1071 break;
1072 }
1073 if (eofmark != NULL)
1074 break;
1075 if (ISDBLQUOTE()) {
1076 if (varnest != 0)
[3456]1077 PSTUPUTC(psh, CTLQUOTEEND, out);
[626]1078 syntax = BASESYNTAX;
1079 CLRDBLQUOTE();
1080 } else {
1081 syntax = DQSYNTAX;
1082 SETDBLQUOTE();
[3456]1083 PSTUPUTC(psh, CTLQUOTEMARK, out);
[626]1084 }
1085 break;
1086 case CVAR: /* '$' */
1087 PARSESUB(); /* parse substitution */
1088 break;
1089 case CENDVAR: /* CLOSEBRACE */
1090 if (varnest > 0 && !ISDBLQUOTE()) {
1091 varnest--;
[3456]1092 PSTUPUTC(psh, CTLENDVAR, out);
[626]1093 } else {
[3456]1094 PSTUPUTC(psh, c, out);
[626]1095 }
1096 break;
1097 case CLP: /* '(' in arithmetic */
1098 parenlevel++;
[3456]1099 PSTUPUTC(psh, c, out);
[626]1100 break;
1101 case CRP: /* ')' in arithmetic */
1102 if (parenlevel > 0) {
[3456]1103 PSTUPUTC(psh, c, out);
[626]1104 --parenlevel;
1105 } else {
[1201]1106 if (pgetc(psh) == ')') {
[626]1107 if (--arinest == 0) {
[3456]1108 PSTUPUTC(psh, CTLENDARI, out);
[626]1109 syntax = prevsyntax;
1110 if (syntax == DQSYNTAX)
1111 SETDBLQUOTE();
1112 else
1113 CLRDBLQUOTE();
1114 } else
[3456]1115 PSTUPUTC(psh, ')', out);
[626]1116 } else {
1117 /*
1118 * unbalanced parens
1119 * (don't 2nd guess - no error)
1120 */
[1201]1121 pungetc(psh);
[3456]1122 PSTUPUTC(psh, ')', out);
[626]1123 }
1124 }
1125 break;
1126 case CBQUOTE: /* '`' */
1127 PARSEBACKQOLD();
1128 break;
[1216]1129 case CSHEOF:
[626]1130 goto endword; /* exit outer loop */
1131 default:
1132 if (varnest == 0)
1133 goto endword; /* exit outer loop */
[3456]1134 PSTUPUTC(psh, c, out);
[626]1135 }
[1201]1136 c = pgetc_macro(psh);
[626]1137 }
1138 }
1139endword:
1140 if (syntax == ARISYNTAX)
[1210]1141 synerror(psh, "Missing '))'");
1142 if (syntax != BASESYNTAX && ! psh->parsebackquote && eofmark == NULL)
1143 synerror(psh, "Unterminated quoted string");
[626]1144 if (varnest != 0) {
[1210]1145 psh->startlinno = psh->plinno;
[626]1146 /* { */
[1210]1147 synerror(psh, "Missing '}'");
[626]1148 }
[3456]1149 PSTUPUTC(psh, '\0', out);
[626]1150 if (eofmark == NULL) {
[3456]1151 size_t len = (size_t)(out - PSTBLOCK(psh));
1152 char *start = PSTBLOCK(psh);
[626]1153 if ((c == '>' || c == '<')
1154 && quotef == 0
1155 && len <= 2
[3456]1156 && (*start == '\0' || is_digit(*start))) {
1157 out = start;
[626]1158 PARSEREDIR();
[1210]1159 return psh->lasttoken = TREDIR;
[626]1160 } else {
[1201]1161 pungetc(psh);
[626]1162 }
1163 }
[1210]1164 psh->quoteflag = quotef;
1165 psh->backquotelist = bqlist;
[3456]1166 psh->wordtext = pstgrabstr(psh, out);
[626]1167 if (dblquotep != NULL)
[2290]1168 ckfree(psh, dblquotep);
[1210]1169 return psh->lasttoken = TWORD;
[626]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')
[1201]1184 c = pgetc(psh);
[626]1185 }
1186 if (c == *eofmark) {
[1201]1187 if (pfgets(psh, line, sizeof line) != NULL) {
[626]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;
[1210]1194 psh->plinno++;
1195 psh->needprompt = psh->doprompt;
[626]1196 } else {
[1201]1197 pushstring(psh, line, strlen(line), NULL);
[626]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: {
[3437]1213 union node *np;
[626]1214 char fd = *out;
[3449]1215 char dummy[ sizeof(struct ndup) >= sizeof(struct nfile)
1216 && sizeof(struct ndup) >= sizeof(struct nhere) ? 1 : 0];
[3437]1217 (void)dummy;
[626]1218
[3456]1219 np = pstallocnode(psh, sizeof (struct ndup));
[626]1220 if (c == '>') {
1221 np->nfile.fd = 1;
[1201]1222 c = pgetc(psh);
[626]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;
[1201]1231 pungetc(psh);
[626]1232 }
1233 } else { /* c == '<' */
1234 np->nfile.fd = 0;
[1201]1235 switch (c = pgetc(psh)) {
[626]1236 case '<':
1237 np->type = NHERE;
[3456]1238 psh->heredoc = (struct heredoc *)pstalloc(psh, sizeof (struct heredoc));
[1210]1239 psh->heredoc->here = np;
[1201]1240 if ((c = pgetc(psh)) == '-') {
[1210]1241 psh->heredoc->striptabs = 1;
[626]1242 } else {
[1210]1243 psh->heredoc->striptabs = 0;
[1201]1244 pungetc(psh);
[626]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;
[1201]1258 pungetc(psh);
[626]1259 break;
1260 }
1261 }
1262 if (fd != '\0')
1263 np->nfile.fd = digit_val(fd);
[1210]1264 psh->redirnode = np;
[626]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
[1201]1281 c = pgetc(psh);
[626]1282 if (c != '(' && c != OPENBRACE && !is_name(c) && !is_special(c)) {
[3456]1283 PSTUPUTC(psh, '$', out);
[1201]1284 pungetc(psh);
[626]1285 } else if (c == '(') { /* $(command) or $((arith)) */
[1201]1286 if (pgetc(psh) == '(') {
[626]1287 PARSEARITH();
1288 } else {
[1201]1289 pungetc(psh);
[626]1290 PARSEBACKQNEW();
1291 }
1292 } else {
[3456]1293 PSTUPUTC(psh, CTLVAR, out);
1294 typeloc = (int)(out - PSTBLOCK(psh));
1295 PSTUPUTC(psh, VSNORMAL, out);
[626]1296 subtype = VSNORMAL;
1297 if (c == OPENBRACE) {
[1201]1298 c = pgetc(psh);
[626]1299 if (c == '#') {
[1201]1300 if ((c = pgetc(psh)) == CLOSEBRACE)
[626]1301 c = '#';
1302 else
1303 subtype = VSLENGTH;
1304 }
1305 else
1306 subtype = 0;
1307 }
1308 if (is_name(c)) {
1309 do {
[3456]1310 PSTPUTC(psh, c, out);
[1201]1311 c = pgetc(psh);
[626]1312 } while (is_in_name(c));
1313 } else if (is_digit(c)) {
1314 do {
[3456]1315 PSTUPUTC(psh, c, out);
[1201]1316 c = pgetc(psh);
[626]1317 } while (is_digit(c));
1318 }
1319 else if (is_special(c)) {
[3456]1320 PSTUPUTC(psh, c, out);
[1201]1321 c = pgetc(psh);
[626]1322 }
[3456]1323 else {
1324badsub:
1325 synerror(psh, "Bad substitution");
1326 }
[626]1327
[3456]1328 PSTPUTC(psh, '=', out);
[626]1329 flags = 0;
1330 if (subtype == 0) {
1331 switch (c) {
1332 case ':':
1333 flags = VSNUL;
[1201]1334 c = pgetc(psh);
[626]1335 /*FALLTHROUGH*/
1336 default:
1337 p = strchr(types, c);
1338 if (p == NULL)
1339 goto badsub;
[1210]1340 subtype = (int)(p - types + VSNORMAL);
[626]1341 break;
1342 case '%':
1343 case '#':
1344 {
1345 int cc = c;
1346 subtype = c == '#' ? VSTRIMLEFT :
1347 VSTRIMRIGHT;
[1201]1348 c = pgetc(psh);
[626]1349 if (c == cc)
1350 subtype++;
1351 else
[1201]1352 pungetc(psh);
[626]1353 break;
1354 }
1355 }
1356 } else {
[1201]1357 pungetc(psh);
[626]1358 }
1359 if (ISDBLQUOTE() || arinest)
1360 flags |= VSQUOTE;
[3456]1361 *(PSTBLOCK(psh) + typeloc) = subtype | flags;
[626]1362 if (subtype != VSNORMAL) {
1363 varnest++;
[2312]1364 if (varnest >= (int)maxnest) {
[2290]1365 dblquotep = ckrealloc(psh, dblquotep, maxnest / 8);
[626]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
[1210]1395 savepbq = psh->parsebackquote;
[626]1396 if (setjmp(jmploc.loc)) {
1397 if (str)
[2290]1398 ckfree(psh, str);
[1210]1399 psh->parsebackquote = 0;
[1208]1400 psh->handler = savehandler;
1401 longjmp(psh->handler->loc, 1);
[626]1402 }
1403 INTOFF;
1404 str = NULL;
[3456]1405 savelen = (int)(out - PSTBLOCK(psh));
[626]1406 if (savelen > 0) {
[2290]1407 str = ckmalloc(psh, savelen);
[3456]1408 memcpy(str, PSTBLOCK(psh), savelen);
[626]1409 }
[1208]1410 savehandler = psh->handler;
1411 psh->handler = &jmploc;
[626]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
[3456]1423 PSTARTSTACKSTR(psh, pout);
[626]1424 for (;;) {
[1210]1425 if (psh->needprompt) {
1426 setprompt(psh, 2);
1427 psh->needprompt = 0;
[626]1428 }
[1201]1429 switch (pc = pgetc(psh)) {
[626]1430 case '`':
1431 goto done;
1432
1433 case '\\':
[3437]1434 if ((pc = pgetc(psh)) == '\n') {
[1210]1435 psh->plinno++;
1436 if (psh->doprompt)
1437 setprompt(psh, 2);
[626]1438 else
[1210]1439 setprompt(psh, 0);
[626]1440 /*
1441 * If eating a newline, avoid putting
1442 * the newline into the new character
[3456]1443 * stream (via the PSTPUTC after the
[626]1444 * switch).
1445 */
1446 continue;
1447 }
[3437]1448 if (pc != '\\' && pc != '`' && pc != '$' && (!ISDBLQUOTE() || pc != '"'))
[3456]1449 PSTPUTC(psh, '\\', pout);
[626]1450 break;
1451
1452 case '\n':
[1210]1453 psh->plinno++;
1454 psh->needprompt = psh->doprompt;
[626]1455 break;
1456
1457 case PEOF:
[1210]1458 psh->startlinno = psh->plinno;
1459 synerror(psh, "EOF in backquote substitution");
[626]1460 break;
1461
1462 default:
1463 break;
1464 }
[3456]1465 PSTPUTC(psh, pc, pout);
[626]1466 }
1467done:
[3456]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*/);
[626]1473 }
1474 }
1475 nlpp = &bqlist;
1476 while (*nlpp)
1477 nlpp = &(*nlpp)->next;
[3456]1478 *nlpp = pstalloclist(psh);
[626]1479 (*nlpp)->next = NULL;
[1210]1480 psh->parsebackquote = oldstyle;
[626]1481
1482 if (oldstyle) {
[1210]1483 saveprompt = psh->doprompt;
1484 psh->doprompt = 0;
[626]1485 }
1486
[1210]1487 n = list(psh, 0);
[626]1488
1489 if (oldstyle)
[1210]1490 psh->doprompt = saveprompt;
[626]1491 else {
[1210]1492 if (readtoken(psh) != TRP)
1493 synexpect(psh, TRP);
[626]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 */
[1201]1502 popfile(psh);
[1210]1503 psh->tokpushback = 0;
[626]1504 }
[3456]1505 PSTARTSTACKSTR(psh, out);
[626]1506 if (str) {
[3456]1507 PSTPUTSTRN(psh, str, savelen, out);
[626]1508 INTOFF;
[2290]1509 ckfree(psh, str);
[626]1510 str = NULL;
1511 INTON;
1512 }
[1210]1513 psh->parsebackquote = savepbq;
[1208]1514 psh->handler = savehandler;
[626]1515 if (arinest || ISDBLQUOTE())
[3456]1516 PSTUPUTC(psh, CTLBACKQ | CTLQUOTE, out);
[626]1517 else
[3456]1518 PSTUPUTC(psh, CTLBACKQ, out);
[626]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;
[3456]1533 PSTUPUTC(psh, CTLARI, out);
[626]1534 if (ISDBLQUOTE())
[3456]1535 PSTUPUTC(psh, '"',out);
[626]1536 else
[3456]1537 PSTUPUTC(psh, ' ',out);
[626]1538 } else {
1539 /*
1540 * we collapse embedded arithmetic expansion to
1541 * parenthesis, which should be equivalent
1542 */
[3456]1543 PSTUPUTC(psh, '(', out);
[626]1544 }
1545 goto parsearith_return;
1546}
1547
1548} /* end of readtoken */
1549
1550
1551
1552#ifdef mkinit
1553RESET {
[1210]1554 psh->tokpushback = 0;
1555 psh->checkkwd = 0;
[626]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
[1210]1565noexpand(shinstance *psh, char *text)
[626]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
[1210]1589goodname(const char *name)
1590{
1591 const char *p;
[626]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
[2298]1610SH_NORETURN_1 STATIC void
[1210]1611synexpect(shinstance *psh, int token)
[626]1612{
1613 char msg[64];
1614
1615 if (token >= 0) {
1616 fmtstr(msg, 64, "%s unexpected (expecting %s)",
[1210]1617 tokname[psh->lasttoken], tokname[token]);
[626]1618 } else {
[1210]1619 fmtstr(msg, 64, "%s unexpected", tokname[psh->lasttoken]);
[626]1620 }
[1210]1621 synerror(psh, msg);
[626]1622 /* NOTREACHED */
1623}
1624
1625
[2298]1626SH_NORETURN_1 STATIC void
[1210]1627synerror(shinstance *psh, const char *msg)
[626]1628{
[2628]1629 if (psh->commandname) {
[2631]1630 TRACE((psh, "synerror: %s: %d: Syntax error: %s", psh->commandname, psh->startlinno, msg));
[1210]1631 outfmt(&psh->errout, "%s: %d: ", psh->commandname, psh->startlinno);
[2628]1632 } else {
[2631]1633 TRACE((psh, "synerror: Syntax error: %s\n", msg));
[2630]1634 }
[1199]1635 outfmt(&psh->errout, "Syntax error: %s\n", msg);
1636 error(psh, (char *)NULL);
[626]1637 /* NOTREACHED */
1638}
1639
[1236]1640STATIC const char *
1641my_basename(const char *argv0, unsigned *lenp)
1642{
1643 const char *tmp;
1644
[3573]1645 /* skip the path */
1646 for (tmp = strpbrk(argv0, "\\/:"); tmp; tmp = strpbrk(argv0, "\\/:"))
1647 argv0 = tmp + 1;
[1236]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
[626]1660STATIC void
[1210]1661setprompt(shinstance *psh, int which)
[626]1662{
[1201]1663 psh->whichprompt = which;
[626]1664
1665#ifndef SMALL
1666 if (!el)
1667#endif
[1236]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 }
[626]1772}
1773
1774/*
1775 * called by editline -- any expansions to the prompt
1776 * should be added here.
1777 */
1778const char *
[1210]1779getprompt(shinstance *psh, void *unused)
1780{
[1201]1781 switch (psh->whichprompt) {
[626]1782 case 0:
1783 return "";
1784 case 1:
[1210]1785 return ps1val(psh);
[626]1786 case 2:
[1210]1787 return ps2val(psh);
[626]1788 default:
1789 return "<internal prompt error>";
1790 }
1791}
[3437]1792
[3458]1793#ifndef KASH_SEPARATE_PARSER_ALLOCATOR
1794
[3457]1795static union node *copyparsetreeint(shinstance *psh, union node *src);
1796
[3437]1797/*
[3457]1798 * Helper to copyparsetreeint.
[3437]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) {
[3456]1807 struct nodelist *dst = pstalloclist(psh);
[3437]1808 dst->next = NULL;
1809 *ppnext = dst;
1810 ppnext = &dst->next;
[3457]1811 dst->n = copyparsetreeint(psh, src->n);
[3440]1812 src = src->next;
[3437]1813 }
1814 }
1815 return ret;
1816}
1817
1818/*
1819 * Duplicates a node tree.
1820 *
1821 * Note! This could probably be generated from nodelist.
1822 */
[3457]1823static union node *
1824copyparsetreeint(shinstance *psh, union node *src)
[3437]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:
[3456]1837 ret = pstallocnode(psh, sizeof(src->nbinary));
[3437]1838 ret->nbinary.type = type;
[3457]1839 ret->nbinary.ch1 = copyparsetreeint(psh, src->nbinary.ch1);
1840 ret->nbinary.ch2 = copyparsetreeint(psh, src->nbinary.ch2);
[3437]1841 break;
1842
1843 case NCMD:
[3456]1844 ret = pstallocnode(psh, sizeof(src->ncmd));
[3437]1845 ret->ncmd.type = NCMD;
1846 ret->ncmd.backgnd = src->ncmd.backgnd;
[3457]1847 ret->ncmd.args = copyparsetreeint(psh, src->ncmd.args);
1848 ret->ncmd.redirect = copyparsetreeint(psh, src->ncmd.redirect);
[3437]1849 break;
1850
1851 case NPIPE:
[3456]1852 ret = pstallocnode(psh, sizeof(src->npipe));
[3437]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:
[3456]1861 ret = pstallocnode(psh, sizeof(src->nredir));
[3437]1862 ret->nredir.type = type;
[3457]1863 ret->nredir.n = copyparsetreeint(psh, src->nredir.n);
1864 ret->nredir.redirect = copyparsetreeint(psh, src->nredir.redirect);
[3437]1865 break;
1866
1867 case NIF:
[3456]1868 ret = pstallocnode(psh, sizeof(src->nif));
[3437]1869 ret->nif.type = NIF;
[3457]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);
[3437]1873 break;
1874
1875 case NFOR:
[3456]1876 ret = pstallocnode(psh, sizeof(src->nfor));
[3437]1877 ret->nfor.type = NFOR;
[3457]1878 ret->nfor.args = copyparsetreeint(psh, src->nfor.args);
1879 ret->nfor.body = copyparsetreeint(psh, src->nfor.body);
[3456]1880 ret->nfor.var = pstsavestr(psh, src->nfor.var);
[3437]1881 break;
1882
1883 case NCASE:
[3456]1884 ret = pstallocnode(psh, sizeof(src->ncase));
[3437]1885 ret->ncase.type = NCASE;
[3457]1886 ret->ncase.expr = copyparsetreeint(psh, src->ncase.expr);
1887 ret->ncase.cases = copyparsetreeint(psh, src->ncase.cases);
[3437]1888 break;
1889
1890 case NCLIST:
[3456]1891 ret = pstallocnode(psh, sizeof(src->nclist));
[3437]1892 ret->nclist.type = NCLIST;
[3457]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);
[3437]1896 break;
1897
1898 case NDEFUN:
1899 case NARG:
[3456]1900 ret = pstallocnode(psh, sizeof(src->narg));
[3437]1901 ret->narg.type = type;
[3457]1902 ret->narg.next = copyparsetreeint(psh, src->narg.next);
[3456]1903 ret->narg.text = pstsavestr(psh, src->narg.text);
[3437]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:
[3456]1912 ret = pstallocnode(psh, sizeof(src->nfile));
[3437]1913 ret->nfile.type = type;
1914 ret->nfile.fd = src->nfile.fd;
[3457]1915 ret->nfile.next = copyparsetreeint(psh, src->nfile.next);
1916 ret->nfile.fname = copyparsetreeint(psh, src->nfile.fname);
[3437]1917 break;
1918
1919 case NTOFD:
1920 case NFROMFD:
[3456]1921 ret = pstallocnode(psh, sizeof(src->ndup));
[3437]1922 ret->ndup.type = type;
1923 ret->ndup.fd = src->ndup.fd;
[3457]1924 ret->ndup.next = copyparsetreeint(psh, src->ndup.next);
[3437]1925 ret->ndup.dupfd = src->ndup.dupfd;
[3457]1926 ret->ndup.vname = copyparsetreeint(psh, src->ndup.vname);
[3437]1927 break;
1928
1929 case NHERE:
1930 case NXHERE:
[3456]1931 ret = pstallocnode(psh, sizeof(src->nhere));
[3437]1932 ret->nhere.type = type;
1933 ret->nhere.fd = src->nhere.fd;
[3457]1934 ret->nhere.next = copyparsetreeint(psh, src->nhere.next);
1935 ret->nhere.doc = copyparsetreeint(psh, src->nhere.doc);
[3437]1936 break;
1937
1938 case NNOT:
[3456]1939 ret = pstallocnode(psh, sizeof(src->nnot));
[3437]1940 ret->nnot.type = NNOT;
[3457]1941 ret->nnot.com = copyparsetreeint(psh, src->nnot.com);
[3437]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
[3458]1954#endif
1955
[3457]1956union node *copyparsetree(shinstance *psh, union node *src)
1957{
1958#ifdef KASH_SEPARATE_PARSER_ALLOCATOR
[3458]1959 K_NOREF(psh);
1960 pstackretainpush(psh, src->pblock);
1961 return src;
1962#else
1963 return copyparsetreeint(psh, src);
[3457]1964#endif
1965}
1966
Note: See TracBrowser for help on using the repository browser.