Subversion Repositories HelenOS

Rev

Details | Last modification | View Log | RSS feed

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