Subversion Repositories HelenOS

Rev

Rev 2714 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed

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