| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: syn.c,v 1.38 2015/12/30 09:07:00 tedu Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*  | 
    ||
4  | 
    * shell parser (C version)  | 
    ||
5  | 
    */  | 
    ||
6  | 
    |||
7  | 
    #include <string.h>  | 
    ||
8  | 
    |||
9  | 
    #include "sh.h"  | 
    ||
10  | 
    #include "c_test.h"  | 
    ||
11  | 
    |||
12  | 
    struct nesting_state { | 
    ||
13  | 
    int start_token; /* token than began nesting (eg, FOR) */  | 
    ||
14  | 
    int start_line; /* line nesting began on */  | 
    ||
15  | 
    };  | 
    ||
16  | 
    |||
17  | 
    static void yyparse(void);  | 
    ||
18  | 
    static struct op *pipeline(int);  | 
    ||
19  | 
    static struct op *andor(void);  | 
    ||
20  | 
    static struct op *c_list(int);  | 
    ||
21  | 
    static struct ioword *synio(int);  | 
    ||
22  | 
    static void musthave(int, int);  | 
    ||
23  | 
    static struct op *nested(int, int, int);  | 
    ||
24  | 
    static struct op *get_command(int);  | 
    ||
25  | 
    static struct op *dogroup(void);  | 
    ||
26  | 
    static struct op *thenpart(void);  | 
    ||
27  | 
    static struct op *elsepart(void);  | 
    ||
28  | 
    static struct op *caselist(void);  | 
    ||
29  | 
    static struct op *casepart(int);  | 
    ||
30  | 
    static struct op *function_body(char *, int);  | 
    ||
31  | 
    static char ** wordlist(void);  | 
    ||
32  | 
    static struct op *block(int, struct op *, struct op *, char **);  | 
    ||
33  | 
    static struct op *newtp(int);  | 
    ||
34  | 
    static void syntaxerr(const char *) __attribute__((__noreturn__));  | 
    ||
35  | 
    static void nesting_push(struct nesting_state *, int);  | 
    ||
36  | 
    static void nesting_pop(struct nesting_state *);  | 
    ||
37  | 
    static int assign_command(char *);  | 
    ||
38  | 
    static int inalias(struct source *);  | 
    ||
39  | 
    static int dbtestp_isa(Test_env *, Test_meta);  | 
    ||
40  | 
    static const char *dbtestp_getopnd(Test_env *, Test_op, int);  | 
    ||
41  | 
    static int dbtestp_eval(Test_env *, Test_op, const char *, const char *,  | 
    ||
42  | 
    int);  | 
    ||
43  | 
    static void dbtestp_error(Test_env *, int, const char *);  | 
    ||
44  | 
    |||
45  | 
    static struct op *outtree; /* yyparse output */  | 
    ||
46  | 
    |||
47  | 
    static struct nesting_state nesting; /* \n changed to ; */  | 
    ||
48  | 
    |||
49  | 
    static int reject; /* token(cf) gets symbol again */  | 
    ||
50  | 
    static int symbol; /* yylex value */  | 
    ||
51  | 
    |||
52  | 
    #define token(cf) \  | 
    ||
53  | 
    ((reject) ? (reject = false, symbol) : (symbol = yylex(cf)))  | 
    ||
54  | 
    #define tpeek(cf) \  | 
    ||
55  | 
    ((reject) ? (symbol) : (reject = true, symbol = yylex(cf)))  | 
    ||
56  | 
    |||
57  | 
    static void  | 
    ||
58  | 
    yyparse(void)  | 
    ||
59  | 
    { | 
    ||
60  | 
    int c;  | 
    ||
61  | 
    |||
62  | 
    5044063  | 
    reject = false;  | 
    |
63  | 
    |||
64  | 
    2521701  | 
    outtree = c_list(source->type == SSTRING);  | 
    |
65  | 
    ✓✗ | 7565103  | 
    c = tpeek(0);  | 
    
66  | 
    ✓✓ | 2521701  | 
    if (c == 0 && !outtree)  | 
    
67  | 
    189156  | 
    outtree = newtp(TEOF);  | 
    |
68  | 
    ✓✓ | 2332545  | 
    else if (c != '\n' && c != 0)  | 
    
69  | 
    syntaxerr(NULL);  | 
    ||
70  | 
    2521695  | 
    }  | 
    |
71  | 
    |||
72  | 
    static struct op *  | 
    ||
73  | 
    pipeline(int cf)  | 
    ||
74  | 
    { | 
    ||
75  | 
    struct op *t, *p, *tl = NULL;  | 
    ||
76  | 
    |||
77  | 
    7654903  | 
    t = get_command(cf);  | 
    |
78  | 
    ✓✓ | 3827121  | 
    	if (t != NULL) { | 
    
79  | 
    ✓✗✓✓ | 
    18154136  | 
    		while (token(0) == '|') { | 
    
80  | 
    ✗✓ | 1644250  | 
    if ((p = get_command(CONTIN)) == NULL)  | 
    
81  | 
    syntaxerr(NULL);  | 
    ||
82  | 
    ✓✓ | 1644250  | 
    if (tl == NULL)  | 
    
83  | 
    1236559  | 
    t = tl = block(TPIPE, t, p, NULL);  | 
    |
84  | 
    else  | 
    ||
85  | 
    407691  | 
    tl = tl->right = block(TPIPE, tl->right, p, NULL);  | 
    |
86  | 
    }  | 
    ||
87  | 
    2894284  | 
    reject = true;  | 
    |
88  | 
    2894284  | 
    }  | 
    |
89  | 
    3827121  | 
    return (t);  | 
    |
90  | 
    }  | 
    ||
91  | 
    |||
92  | 
    static struct op *  | 
    ||
93  | 
    andor(void)  | 
    ||
94  | 
    { | 
    ||
95  | 
    struct op *t, *p;  | 
    ||
96  | 
    int c;  | 
    ||
97  | 
    |||
98  | 
    7518447  | 
    t = pipeline(0);  | 
    |
99  | 
    ✓✓ | 3758893  | 
    	if (t != NULL) { | 
    
100  | 
    ✓✗✓✓ | 
    11569132  | 
    		while ((c = token(0)) == LOGAND || c == LOGOR) { | 
    
101  | 
    ✗✓ | 66227  | 
    if ((p = pipeline(CONTIN)) == NULL)  | 
    
102  | 
    syntaxerr(NULL);  | 
    ||
103  | 
    66227  | 
    t = block(c == LOGAND? TAND: TOR, t, p, NULL);  | 
    |
104  | 
    }  | 
    ||
105  | 
    2826056  | 
    reject = true;  | 
    |
106  | 
    2826056  | 
    }  | 
    |
107  | 
    3758893  | 
    return (t);  | 
    |
108  | 
    }  | 
    ||
109  | 
    |||
110  | 
    static struct op *  | 
    ||
111  | 
    c_list(int multi)  | 
    ||
112  | 
    { | 
    ||
113  | 
    struct op *t = NULL, *p, *tl = NULL;  | 
    ||
114  | 
    int c;  | 
    ||
115  | 
    int have_sep;  | 
    ||
116  | 
    |||
117  | 
    6337696  | 
    	while (1) { | 
    |
118  | 
    3758893  | 
    p = andor();  | 
    |
119  | 
    /* Token has always been read/rejected at this point, so  | 
    ||
120  | 
    * we don't worry about what flags to pass token()  | 
    ||
121  | 
    */  | 
    ||
122  | 
    ✓✗ | 11276679  | 
    c = token(0);  | 
    
123  | 
    have_sep = 1;  | 
    ||
124  | 
    ✓✓✓✓ ✓✓  | 
    6044569  | 
    		if (c == '\n' && (multi || inalias(source))) { | 
    
125  | 
    ✓✓ | 538704  | 
    if (!p) /* ignore blank lines */  | 
    
126  | 
    125209  | 
    continue;  | 
    |
127  | 
    ✓✓ | 3220189  | 
    } else if (!p)  | 
    
128  | 
    break;  | 
    ||
129  | 
    ✓✓ | 2412561  | 
    else if (c == '&' || c == COPROC)  | 
    
130  | 
    490  | 
    p = block(c == '&' ? TASYNC : TCOPROC,  | 
    |
131  | 
    p, NULL, NULL);  | 
    ||
132  | 
    ✓✓ | 2412071  | 
    else if (c != ';')  | 
    
133  | 
    1895062  | 
    have_sep = 0;  | 
    |
134  | 
    ✓✓ | 2826056  | 
    if (!t)  | 
    
135  | 
    2070024  | 
    t = p;  | 
    |
136  | 
    ✓✓ | 756032  | 
    else if (!tl)  | 
    
137  | 
    380064  | 
    t = tl = block(TLIST, t, p, NULL);  | 
    |
138  | 
    else  | 
    ||
139  | 
    375968  | 
    tl = tl->right = block(TLIST, tl->right, p, NULL);  | 
    |
140  | 
    ✓✓ | 2826056  | 
    if (!have_sep)  | 
    
141  | 
    break;  | 
    ||
142  | 
    }  | 
    ||
143  | 
    2702690  | 
    reject = true;  | 
    |
144  | 
    2702690  | 
    return t;  | 
    |
145  | 
    }  | 
    ||
146  | 
    |||
147  | 
    static struct ioword *  | 
    ||
148  | 
    synio(int cf)  | 
    ||
149  | 
    { | 
    ||
150  | 
    struct ioword *iop;  | 
    ||
151  | 
    int ishere;  | 
    ||
152  | 
    |||
153  | 
    ✓✓✓✓ | 
    19418300  | 
    if (tpeek(cf) != REDIR)  | 
    
154  | 
    4564696  | 
    return NULL;  | 
    |
155  | 
    289879  | 
    reject = false;  | 
    |
156  | 
    289879  | 
    iop = yylval.iop;  | 
    |
157  | 
    289879  | 
    ishere = (iop->flag&IOTYPE) == IOHERE;  | 
    |
158  | 
    289879  | 
    musthave(LWORD, ishere ? HEREDELIM : 0);  | 
    |
159  | 
    ✓✓ | 289879  | 
    	if (ishere) { | 
    
160  | 
    2278  | 
    iop->delim = yylval.cp;  | 
    |
161  | 
    ✓✓ | 2278  | 
    if (*ident != 0) /* unquoted */  | 
    
162  | 
    2178  | 
    iop->flag |= IOEVAL;  | 
    |
163  | 
    ✗✓ | 2278  | 
    if (herep >= &heres[HERES])  | 
    
164  | 
    			yyerror("too many <<'s\n"); | 
    ||
165  | 
    2278  | 
    *herep++ = iop;  | 
    |
166  | 
    2278  | 
    } else  | 
    |
167  | 
    287601  | 
    iop->name = yylval.cp;  | 
    |
168  | 
    289879  | 
    return iop;  | 
    |
169  | 
    4854575  | 
    }  | 
    |
170  | 
    |||
171  | 
    static void  | 
    ||
172  | 
    musthave(int c, int cf)  | 
    ||
173  | 
    { | 
    ||
174  | 
    ✓✓✗✓ | 
    1914568  | 
    if ((token(cf)) != c)  | 
    
175  | 
    syntaxerr(NULL);  | 
    ||
176  | 
    478642  | 
    }  | 
    |
177  | 
    |||
178  | 
    static struct op *  | 
    ||
179  | 
    nested(int type, int smark, int emark)  | 
    ||
180  | 
    { | 
    ||
181  | 
    struct op *t;  | 
    ||
182  | 
    61746  | 
    struct nesting_state old_nesting;  | 
    |
183  | 
    |||
184  | 
    30873  | 
    nesting_push(&old_nesting, smark);  | 
    |
185  | 
    30873  | 
    t = c_list(true);  | 
    |
186  | 
    30873  | 
    musthave(emark, KEYWORD|ALIAS);  | 
    |
187  | 
    30873  | 
    nesting_pop(&old_nesting);  | 
    |
188  | 
    61746  | 
    return (block(type, t, NULL, NULL));  | 
    |
189  | 
    30873  | 
    }  | 
    |
190  | 
    |||
191  | 
    static struct op *  | 
    ||
192  | 
    get_command(int cf)  | 
    ||
193  | 
    { | 
    ||
194  | 
    struct op *t;  | 
    ||
195  | 
    int c, iopn = 0, syniocf;  | 
    ||
196  | 
    struct ioword *iop, **iops;  | 
    ||
197  | 
    10996388  | 
    XPtrV args, vars;  | 
    |
198  | 
    5498194  | 
    struct nesting_state old_nesting;  | 
    |
199  | 
    |||
200  | 
    5498194  | 
    iops = areallocarray(NULL, NUFILE + 1,  | 
    |
201  | 
    5498194  | 
    sizeof(struct ioword *), ATEMP);  | 
    |
202  | 
    5498194  | 
    XPinit(args, 16);  | 
    |
203  | 
    5498194  | 
    XPinit(vars, 16);  | 
    |
204  | 
    |||
205  | 
    syniocf = KEYWORD|ALIAS;  | 
    ||
206  | 
    ✓✓✓✗ ✓✓✓✓ ✓✗✓✗ ✓✓✓✓ ✓✓  | 
    16493927  | 
    	switch (c = token(cf|KEYWORD|ALIAS|VARASN)) { | 
    
207  | 
    default:  | 
    ||
208  | 
    932837  | 
    reject = true;  | 
    |
209  | 
    932837  | 
    afree(iops, ATEMP);  | 
    |
210  | 
    932837  | 
    XPfree(args);  | 
    |
211  | 
    932837  | 
    XPfree(vars);  | 
    |
212  | 
    932837  | 
    return NULL; /* empty line */  | 
    |
213  | 
    |||
214  | 
    case LWORD:  | 
    ||
215  | 
    case REDIR:  | 
    ||
216  | 
    4468205  | 
    reject = true;  | 
    |
217  | 
    syniocf &= ~(KEYWORD|ALIAS);  | 
    ||
218  | 
    4468205  | 
    t = newtp(TCOM);  | 
    |
219  | 
    4468205  | 
    t->lineno = source->line;  | 
    |
220  | 
    17097340  | 
    		while (1) { | 
    |
221  | 
    34194680  | 
    cf = (t->u.evalflags ? ARRAYVAR : 0) |  | 
    |
222  | 
    17097340  | 
    (XPsize(args) == 0 ? ALIAS|VARASN : CMDWORD);  | 
    |
223  | 
    ✓✓✓✓ ✓✓  | 
    51298911  | 
    			switch (tpeek(cf)) { | 
    
224  | 
    case REDIR:  | 
    ||
225  | 
    ✗✓ | 289042  | 
    if (iopn >= NUFILE)  | 
    
226  | 
    					yyerror("too many redirections\n"); | 
    ||
227  | 
    289042  | 
    iops[iopn++] = synio(cf);  | 
    |
228  | 
    289042  | 
    break;  | 
    |
229  | 
    |||
230  | 
    case LWORD:  | 
    ||
231  | 
    12340093  | 
    reject = false;  | 
    |
232  | 
    /* the iopn == 0 and XPsize(vars) == 0 are  | 
    ||
233  | 
    * dubious but at&t ksh acts this way  | 
    ||
234  | 
    */  | 
    ||
235  | 
    ✓✓✓✓ ✓✓  | 
    29146914  | 
    if (iopn == 0 && XPsize(vars) == 0 &&  | 
    
236  | 
    ✓✓ | 12174856  | 
    XPsize(args) == 0 &&  | 
    
237  | 
    4468187  | 
    assign_command(ident))  | 
    |
238  | 
    113693  | 
    t->u.evalflags = DOVACHECK;  | 
    |
239  | 
    ✓✓✗✓ ✓✓  | 
    24680186  | 
    if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&  | 
    
240  | 
    4551500  | 
    is_wdvarassign(yylval.cp))  | 
    |
241  | 
    ✗✓ | 413138  | 
    XPput(vars, yylval.cp);  | 
    
242  | 
    else  | 
    ||
243  | 
    ✓✓ | 24279791  | 
    XPput(args, yylval.cp);  | 
    
244  | 
    break;  | 
    ||
245  | 
    |||
246  | 
    			case '(': | 
    ||
247  | 
    /* Check for "> foo (echo hi)", which at&t ksh  | 
    ||
248  | 
    * allows (not POSIX, but not disallowed)  | 
    ||
249  | 
    */  | 
    ||
250  | 
    26108  | 
    afree(t, ATEMP);  | 
    |
251  | 
    ✓✓✓✗ | 
    26114  | 
    				if (XPsize(args) == 0 && XPsize(vars) == 0) { | 
    
252  | 
    6  | 
    reject = false;  | 
    |
253  | 
    goto Subshell;  | 
    ||
254  | 
    }  | 
    ||
255  | 
    /* Must be a function */  | 
    ||
256  | 
    ✓✗✓✗ ✗✓  | 
    78306  | 
    if (iopn != 0 || XPsize(args) != 1 ||  | 
    
257  | 
    26102  | 
    XPsize(vars) != 0)  | 
    |
258  | 
    syntaxerr(NULL);  | 
    ||
259  | 
    26102  | 
    reject = false;  | 
    |
260  | 
    /*(*/  | 
    ||
261  | 
    26102  | 
    				musthave(')', 0); | 
    |
262  | 
    26102  | 
    t = function_body(XPptrv(args)[0], false);  | 
    |
263  | 
    26102  | 
    goto Leave;  | 
    |
264  | 
    |||
265  | 
    default:  | 
    ||
266  | 
    goto Leave;  | 
    ||
267  | 
    }  | 
    ||
268  | 
    }  | 
    ||
269  | 
    Leave:  | 
    ||
270  | 
    6  | 
    break;  | 
    |
271  | 
    |||
272  | 
    Subshell:  | 
    ||
273  | 
    	case '(': | 
    ||
274  | 
    4565  | 
    		t = nested(TPAREN, '(', ')'); | 
    |
275  | 
    4565  | 
    break;  | 
    |
276  | 
    |||
277  | 
    	case '{': /*}*/ | 
    ||
278  | 
    26308  | 
    		t = nested(TBRACE, '{', '}'); | 
    |
279  | 
    26308  | 
    break;  | 
    |
280  | 
    |||
281  | 
    case MDPAREN:  | 
    ||
282  | 
    	  { | 
    ||
283  | 
    		static const char let_cmd[] = { | 
    ||
284  | 
    CHAR, 'l', CHAR, 'e',  | 
    ||
285  | 
    CHAR, 't', EOS  | 
    ||
286  | 
    };  | 
    ||
287  | 
    /* Leave KEYWORD in syniocf (allow if (( 1 )) then ...) */  | 
    ||
288  | 
    12  | 
    t = newtp(TCOM);  | 
    |
289  | 
    12  | 
    t->lineno = source->line;  | 
    |
290  | 
    12  | 
    reject = false;  | 
    |
291  | 
    ✗✓ | 24  | 
    XPput(args, wdcopy(let_cmd, ATEMP));  | 
    
292  | 
    12  | 
    musthave(LWORD,LETEXPR);  | 
    |
293  | 
    ✗✓ | 24  | 
    XPput(args, yylval.cp);  | 
    
294  | 
    12  | 
    break;  | 
    |
295  | 
    }  | 
    ||
296  | 
    |||
297  | 
    case DBRACKET: /* [[ .. ]] */  | 
    ||
298  | 
    /* Leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */  | 
    ||
299  | 
    330  | 
    t = newtp(TDBRACKET);  | 
    |
300  | 
    330  | 
    reject = false;  | 
    |
301  | 
    		{ | 
    ||
302  | 
    330  | 
    Test_env te;  | 
    |
303  | 
    |||
304  | 
    330  | 
    te.flags = TEF_DBRACKET;  | 
    |
305  | 
    330  | 
    te.pos.av = &args;  | 
    |
306  | 
    330  | 
    te.isa = dbtestp_isa;  | 
    |
307  | 
    330  | 
    te.getopnd = dbtestp_getopnd;  | 
    |
308  | 
    330  | 
    te.eval = dbtestp_eval;  | 
    |
309  | 
    330  | 
    te.error = dbtestp_error;  | 
    |
310  | 
    |||
311  | 
    330  | 
    test_parse(&te);  | 
    |
312  | 
    330  | 
    }  | 
    |
313  | 
    330  | 
    break;  | 
    |
314  | 
    |||
315  | 
    case FOR:  | 
    ||
316  | 
    case SELECT:  | 
    ||
317  | 
    5818  | 
    t = newtp((c == FOR) ? TFOR : TSELECT);  | 
    |
318  | 
    5818  | 
    musthave(LWORD, ARRAYVAR);  | 
    |
319  | 
    ✗✓ | 5818  | 
    if (!is_wdvarname(yylval.cp, true))  | 
    
320  | 
    			yyerror("%s: bad identifier\n", | 
    ||
321  | 
    c == FOR ? "for" : "select");  | 
    ||
322  | 
    5818  | 
    t->str = str_save(ident, ATEMP);  | 
    |
323  | 
    5818  | 
    nesting_push(&old_nesting, c);  | 
    |
324  | 
    5818  | 
    t->vars = wordlist();  | 
    |
325  | 
    5818  | 
    t->left = dogroup();  | 
    |
326  | 
    5818  | 
    nesting_pop(&old_nesting);  | 
    |
327  | 
    5818  | 
    break;  | 
    |
328  | 
    |||
329  | 
    case WHILE:  | 
    ||
330  | 
    case UNTIL:  | 
    ||
331  | 
    4089  | 
    nesting_push(&old_nesting, c);  | 
    |
332  | 
    4089  | 
    t = newtp((c == WHILE) ? TWHILE : TUNTIL);  | 
    |
333  | 
    4089  | 
    t->left = c_list(true);  | 
    |
334  | 
    4089  | 
    t->right = dogroup();  | 
    |
335  | 
    4089  | 
    nesting_pop(&old_nesting);  | 
    |
336  | 
    4089  | 
    break;  | 
    |
337  | 
    |||
338  | 
    case CASE:  | 
    ||
339  | 
    981  | 
    t = newtp(TCASE);  | 
    |
340  | 
    981  | 
    musthave(LWORD, 0);  | 
    |
341  | 
    981  | 
    t->str = yylval.cp;  | 
    |
342  | 
    981  | 
    nesting_push(&old_nesting, c);  | 
    |
343  | 
    981  | 
    t->left = caselist();  | 
    |
344  | 
    981  | 
    nesting_pop(&old_nesting);  | 
    |
345  | 
    981  | 
    break;  | 
    |
346  | 
    |||
347  | 
    case IF:  | 
    ||
348  | 
    52339  | 
    nesting_push(&old_nesting, c);  | 
    |
349  | 
    52339  | 
    t = newtp(TIF);  | 
    |
350  | 
    52339  | 
    t->left = c_list(true);  | 
    |
351  | 
    52339  | 
    t->right = thenpart();  | 
    |
352  | 
    52339  | 
    musthave(FI, KEYWORD|ALIAS);  | 
    |
353  | 
    52339  | 
    nesting_pop(&old_nesting);  | 
    |
354  | 
    52339  | 
    break;  | 
    |
355  | 
    |||
356  | 
    case BANG:  | 
    ||
357  | 
    syniocf &= ~(KEYWORD|ALIAS);  | 
    ||
358  | 
    643  | 
    t = pipeline(0);  | 
    |
359  | 
    ✗✓ | 643  | 
    if (t == NULL)  | 
    
360  | 
    syntaxerr(NULL);  | 
    ||
361  | 
    643  | 
    t = block(TBANG, NULL, t, NULL);  | 
    |
362  | 
    643  | 
    break;  | 
    |
363  | 
    |||
364  | 
    case TIME:  | 
    ||
365  | 
    syniocf &= ~(KEYWORD|ALIAS);  | 
    ||
366  | 
    1358  | 
    t = pipeline(0);  | 
    |
367  | 
    ✓✗ | 1358  | 
    		if (t) { | 
    
368  | 
    1358  | 
    t->str = alloc(2, ATEMP);  | 
    |
369  | 
    1358  | 
    t->str[0] = '\0'; /* TF_* flags */  | 
    |
370  | 
    1358  | 
    t->str[1] = '\0';  | 
    |
371  | 
    1358  | 
    }  | 
    |
372  | 
    1358  | 
    t = block(TTIME, t, NULL, NULL);  | 
    |
373  | 
    1358  | 
    break;  | 
    |
374  | 
    |||
375  | 
    case FUNCTION:  | 
    ||
376  | 
    60  | 
    musthave(LWORD, 0);  | 
    |
377  | 
    60  | 
    t = function_body(yylval.cp, true);  | 
    |
378  | 
    60  | 
    break;  | 
    |
379  | 
    }  | 
    ||
380  | 
    |||
381  | 
    ✓✓ | 9131066  | 
    	while ((iop = synio(syniocf)) != NULL) { | 
    
382  | 
    ✗✓ | 837  | 
    if (iopn >= NUFILE)  | 
    
383  | 
    			yyerror("too many redirections\n"); | 
    ||
384  | 
    837  | 
    iops[iopn++] = iop;  | 
    |
385  | 
    }  | 
    ||
386  | 
    |||
387  | 
    ✓✓ | 4564696  | 
    	if (iopn == 0) { | 
    
388  | 
    4351972  | 
    afree(iops, ATEMP);  | 
    |
389  | 
    t->ioact = NULL;  | 
    ||
390  | 
    4351972  | 
    	} else { | 
    |
391  | 
    212724  | 
    iops[iopn++] = NULL;  | 
    |
392  | 
    425448  | 
    iops = areallocarray(iops, iopn,  | 
    |
393  | 
    212724  | 
    sizeof(struct ioword *), ATEMP);  | 
    |
394  | 
    t->ioact = iops;  | 
    ||
395  | 
    }  | 
    ||
396  | 
    |||
397  | 
    ✓✓✓✓ | 
    4687289  | 
    	if (t->type == TCOM || t->type == TDBRACKET) { | 
    
398  | 
    ✓✓ | 8885158  | 
    XPput(args, NULL);  | 
    
399  | 
    4442433  | 
    t->args = (char **) XPclose(args);  | 
    |
400  | 
    ✗✓ | 8884866  | 
    XPput(vars, NULL);  | 
    
401  | 
    4442433  | 
    t->vars = (char **) XPclose(vars);  | 
    |
402  | 
    4442433  | 
    	} else { | 
    |
403  | 
    122263  | 
    XPfree(args);  | 
    |
404  | 
    122263  | 
    XPfree(vars);  | 
    |
405  | 
    }  | 
    ||
406  | 
    |||
407  | 
    4564696  | 
    return t;  | 
    |
408  | 
    5497533  | 
    }  | 
    |
409  | 
    |||
410  | 
    static struct op *  | 
    ||
411  | 
    dogroup(void)  | 
    ||
412  | 
    { | 
    ||
413  | 
    int c;  | 
    ||
414  | 
    struct op *list;  | 
    ||
415  | 
    |||
416  | 
    ✓✓ | 39628  | 
    c = token(CONTIN|KEYWORD|ALIAS);  | 
    
417  | 
    	/* A {...} can be used instead of do...done for for/select loops | 
    ||
418  | 
    * but not for while/until loops - we don't need to check if it  | 
    ||
419  | 
    * is a while loop because it would have been parsed as part of  | 
    ||
420  | 
    * the conditional command list...  | 
    ||
421  | 
    */  | 
    ||
422  | 
    ✓✗ | 9907  | 
    if (c == DO)  | 
    
423  | 
    9907  | 
    c = DONE;  | 
    |
424  | 
    	else if (c == '{') | 
    ||
425  | 
    c = '}';  | 
    ||
426  | 
    else  | 
    ||
427  | 
    syntaxerr(NULL);  | 
    ||
428  | 
    9907  | 
    list = c_list(true);  | 
    |
429  | 
    9907  | 
    musthave(c, KEYWORD|ALIAS);  | 
    |
430  | 
    9907  | 
    return list;  | 
    |
431  | 
    }  | 
    ||
432  | 
    |||
433  | 
    static struct op *  | 
    ||
434  | 
    thenpart(void)  | 
    ||
435  | 
    { | 
    ||
436  | 
    struct op *t;  | 
    ||
437  | 
    |||
438  | 
    104898  | 
    musthave(THEN, KEYWORD|ALIAS);  | 
    |
439  | 
    52449  | 
    t = newtp(0);  | 
    |
440  | 
    52449  | 
    t->left = c_list(true);  | 
    |
441  | 
    ✗✓ | 52449  | 
    if (t->left == NULL)  | 
    
442  | 
    syntaxerr(NULL);  | 
    ||
443  | 
    52449  | 
    t->right = elsepart();  | 
    |
444  | 
    52449  | 
    return (t);  | 
    |
445  | 
    }  | 
    ||
446  | 
    |||
447  | 
    static struct op *  | 
    ||
448  | 
    elsepart(void)  | 
    ||
449  | 
    { | 
    ||
450  | 
    struct op *t;  | 
    ||
451  | 
    |||
452  | 
    ✓✗✓✓ ✓  | 
    209796  | 
    	switch (token(KEYWORD|ALIAS|VARASN)) { | 
    
453  | 
    case ELSE:  | 
    ||
454  | 
    ✗✓ | 28381  | 
    if ((t = c_list(true)) == NULL)  | 
    
455  | 
    syntaxerr(NULL);  | 
    ||
456  | 
    28381  | 
    return (t);  | 
    |
457  | 
    |||
458  | 
    case ELIF:  | 
    ||
459  | 
    110  | 
    t = newtp(TELIF);  | 
    |
460  | 
    110  | 
    t->left = c_list(true);  | 
    |
461  | 
    110  | 
    t->right = thenpart();  | 
    |
462  | 
    110  | 
    return (t);  | 
    |
463  | 
    |||
464  | 
    default:  | 
    ||
465  | 
    23958  | 
    reject = true;  | 
    |
466  | 
    }  | 
    ||
467  | 
    23958  | 
    return NULL;  | 
    |
468  | 
    52449  | 
    }  | 
    |
469  | 
    |||
470  | 
    static struct op *  | 
    ||
471  | 
    caselist(void)  | 
    ||
472  | 
    { | 
    ||
473  | 
    struct op *t, *tl;  | 
    ||
474  | 
    int c;  | 
    ||
475  | 
    |||
476  | 
    ✗✓ | 3924  | 
    c = token(CONTIN|KEYWORD|ALIAS);  | 
    
477  | 
    	/* A {...} can be used instead of in...esac for case statements */ | 
    ||
478  | 
    ✓✗ | 981  | 
    if (c == IN)  | 
    
479  | 
    981  | 
    c = ESAC;  | 
    |
480  | 
    	else if (c == '{') | 
    ||
481  | 
    c = '}';  | 
    ||
482  | 
    else  | 
    ||
483  | 
    syntaxerr(NULL);  | 
    ||
484  | 
    t = tl = NULL;  | 
    ||
485  | 
    ✓✓✓✓ | 
    15288  | 
    	while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) { /* no ALIAS here */ | 
    
486  | 
    2841  | 
    struct op *tc = casepart(c);  | 
    |
487  | 
    ✓✓ | 2841  | 
    if (tl == NULL)  | 
    
488  | 
    981  | 
    t = tl = tc, tl->right = NULL;  | 
    |
489  | 
    else  | 
    ||
490  | 
    1860  | 
    tl->right = tc, tl = tc;  | 
    |
491  | 
    }  | 
    ||
492  | 
    981  | 
    musthave(c, KEYWORD|ALIAS);  | 
    |
493  | 
    981  | 
    return (t);  | 
    |
494  | 
    }  | 
    ||
495  | 
    |||
496  | 
    static struct op *  | 
    ||
497  | 
    casepart(int endtok)  | 
    ||
498  | 
    { | 
    ||
499  | 
    struct op *t;  | 
    ||
500  | 
    int c;  | 
    ||
501  | 
    XPtrV ptns;  | 
    ||
502  | 
    |||
503  | 
    5682  | 
    XPinit(ptns, 16);  | 
    |
504  | 
    2841  | 
    t = newtp(TPAT);  | 
    |
505  | 
    ✓✗ | 8523  | 
    c = token(CONTIN|KEYWORD); /* no ALIAS here */  | 
    
506  | 
    ✓✓ | 2841  | 
    	if (c != '(') | 
    
507  | 
    2799  | 
    reject = true;  | 
    |
508  | 
    2841  | 
    	do { | 
    |
509  | 
    3619  | 
    musthave(LWORD, 0);  | 
    |
510  | 
    ✗✓ | 7238  | 
    XPput(ptns, yylval.cp);  | 
    
511  | 
    ✗✓✓✓ | 
    10857  | 
    } while ((c = token(0)) == '|');  | 
    
512  | 
    2841  | 
    reject = true;  | 
    |
513  | 
    ✗✓ | 5682  | 
    XPput(ptns, NULL);  | 
    
514  | 
    2841  | 
    t->vars = (char **) XPclose(ptns);  | 
    |
515  | 
    2841  | 
    	musthave(')', 0); | 
    |
516  | 
    |||
517  | 
    2841  | 
    t->left = c_list(true);  | 
    |
518  | 
    /* Note: Posix requires the ;; */  | 
    ||
519  | 
    ✓✗✓✓ | 
    8523  | 
    if ((tpeek(CONTIN|KEYWORD|ALIAS)) != endtok)  | 
    
520  | 
    2721  | 
    musthave(BREAK, CONTIN|KEYWORD|ALIAS);  | 
    |
521  | 
    2841  | 
    return (t);  | 
    |
522  | 
    }  | 
    ||
523  | 
    |||
524  | 
    static struct op *  | 
    ||
525  | 
    function_body(char *name,  | 
    ||
526  | 
        int ksh_func)		/* function foo { ... } vs foo() { .. } */ | 
    ||
527  | 
    { | 
    ||
528  | 
    char *sname, *p;  | 
    ||
529  | 
    struct op *t;  | 
    ||
530  | 
    int old_func_parse;  | 
    ||
531  | 
    |||
532  | 
    52324  | 
    sname = wdstrip(name);  | 
    |
533  | 
    /* Check for valid characters in name. posix and ksh93 say only  | 
    ||
534  | 
    * allow [a-zA-Z_0-9] but this allows more as old pdksh's have  | 
    ||
535  | 
    * allowed more (the following were never allowed:  | 
    ||
536  | 
    * nul space nl tab $ ' " \ ` ( ) & | ; = < >  | 
    ||
537  | 
    * C_QUOTE covers all but = and adds # [ ? *)  | 
    ||
538  | 
    */  | 
    ||
539  | 
    ✓✓ | 398182  | 
    for (p = sname; *p; p++)  | 
    
540  | 
    ✓✗✗✓ | 
    345858  | 
    if (ctype(*p, C_QUOTE) || *p == '=')  | 
    
541  | 
    			yyerror("%s: invalid function name\n", sname); | 
    ||
542  | 
    |||
543  | 
    26162  | 
    t = newtp(TFUNCT);  | 
    |
544  | 
    26162  | 
    t->str = sname;  | 
    |
545  | 
    26162  | 
    t->u.ksh_func = ksh_func;  | 
    |
546  | 
    26162  | 
    t->lineno = source->line;  | 
    |
547  | 
    |||
548  | 
    /* Note that POSIX allows only compound statements after foo(), sh and  | 
    ||
549  | 
    * at&t ksh allow any command, go with the later since it shouldn't  | 
    ||
550  | 
    * break anything. However, for function foo, at&t ksh only accepts  | 
    ||
551  | 
    * an open-brace.  | 
    ||
552  | 
    */  | 
    ||
553  | 
    ✓✓ | 26162  | 
    	if (ksh_func) { | 
    
554  | 
    60  | 
    		musthave('{', CONTIN|KEYWORD|ALIAS); /* } */ | 
    |
555  | 
    60  | 
    reject = true;  | 
    |
556  | 
    60  | 
    }  | 
    |
557  | 
    |||
558  | 
    26162  | 
    old_func_parse = genv->flags & EF_FUNC_PARSE;  | 
    |
559  | 
    26162  | 
    genv->flags |= EF_FUNC_PARSE;  | 
    |
560  | 
    ✗✓ | 26162  | 
    	if ((t->left = get_command(CONTIN)) == NULL) { | 
    
561  | 
    /*  | 
    ||
562  | 
    * Probably something like foo() followed by eof or ;.  | 
    ||
563  | 
    * This is accepted by sh and ksh88.  | 
    ||
564  | 
    * To make "typeset -f foo" work reliably (so its output can  | 
    ||
565  | 
    * be used as input), we pretend there is a colon here.  | 
    ||
566  | 
    */  | 
    ||
567  | 
    t->left = newtp(TCOM);  | 
    ||
568  | 
    t->left->args = areallocarray(NULL, 2, sizeof(char *), ATEMP);  | 
    ||
569  | 
    t->left->args[0] = alloc(3, ATEMP);  | 
    ||
570  | 
    t->left->args[0][0] = CHAR;  | 
    ||
571  | 
    t->left->args[0][1] = ':';  | 
    ||
572  | 
    t->left->args[0][2] = EOS;  | 
    ||
573  | 
    t->left->args[1] = NULL;  | 
    ||
574  | 
    t->left->vars = alloc(sizeof(char *), ATEMP);  | 
    ||
575  | 
    t->left->vars[0] = NULL;  | 
    ||
576  | 
    t->left->lineno = 1;  | 
    ||
577  | 
    }  | 
    ||
578  | 
    ✓✓ | 26162  | 
    if (!old_func_parse)  | 
    
579  | 
    26156  | 
    genv->flags &= ~EF_FUNC_PARSE;  | 
    |
580  | 
    |||
581  | 
    26162  | 
    return t;  | 
    |
582  | 
    }  | 
    ||
583  | 
    |||
584  | 
    static char **  | 
    ||
585  | 
    wordlist(void)  | 
    ||
586  | 
    { | 
    ||
587  | 
    int c;  | 
    ||
588  | 
    XPtrV args;  | 
    ||
589  | 
    |||
590  | 
    11636  | 
    XPinit(args, 16);  | 
    |
591  | 
    /* Posix does not do alias expansion here... */  | 
    ||
592  | 
    ✗✓✓✓ | 
    17454  | 
    	if ((c = token(CONTIN|KEYWORD|ALIAS)) != IN) { | 
    
593  | 
    ✗✓ | 108  | 
    if (c != ';') /* non-POSIX, but at&t ksh accepts a ; here */  | 
    
594  | 
    reject = true;  | 
    ||
595  | 
    108  | 
    return NULL;  | 
    |
596  | 
    }  | 
    ||
597  | 
    ✗✓✓✓ | 
    107580  | 
    while ((c = token(0)) == LWORD)  | 
    
598  | 
    ✓✓ | 42533  | 
    XPput(args, yylval.cp);  | 
    
599  | 
    ✗✓ | 5710  | 
    if (c != '\n' && c != ';')  | 
    
600  | 
    syntaxerr(NULL);  | 
    ||
601  | 
    ✓✓ | 11427  | 
    XPput(args, NULL);  | 
    
602  | 
    5710  | 
    return (char **) XPclose(args);  | 
    |
603  | 
    5818  | 
    }  | 
    |
604  | 
    |||
605  | 
    /*  | 
    ||
606  | 
    * supporting functions  | 
    ||
607  | 
    */  | 
    ||
608  | 
    |||
609  | 
    static struct op *  | 
    ||
610  | 
    block(int type, struct op *t1, struct op *t2, char **wp)  | 
    ||
611  | 
    { | 
    ||
612  | 
    struct op *t;  | 
    ||
613  | 
    |||
614  | 
    4999746  | 
    t = newtp(type);  | 
    |
615  | 
    2499873  | 
    t->left = t1;  | 
    |
616  | 
    2499873  | 
    t->right = t2;  | 
    |
617  | 
    2499873  | 
    t->vars = wp;  | 
    |
618  | 
    2499873  | 
    return (t);  | 
    |
619  | 
    }  | 
    ||
620  | 
    |||
621  | 
    const	struct tokeninfo { | 
    ||
622  | 
    const char *name;  | 
    ||
623  | 
    short val;  | 
    ||
624  | 
    short reserved;  | 
    ||
625  | 
    } tokentab[] = { | 
    ||
626  | 
    /* Reserved words */  | 
    ||
627  | 
    	{ "if",		IF,	true }, | 
    ||
628  | 
    	{ "then",	THEN,	true }, | 
    ||
629  | 
    	{ "else",	ELSE,	true }, | 
    ||
630  | 
    	{ "elif",	ELIF,	true }, | 
    ||
631  | 
    	{ "fi",		FI,	true }, | 
    ||
632  | 
    	{ "case",	CASE,	true }, | 
    ||
633  | 
    	{ "esac",	ESAC,	true }, | 
    ||
634  | 
    	{ "for",	FOR,	true }, | 
    ||
635  | 
    	{ "select",	SELECT,	true }, | 
    ||
636  | 
    	{ "while",	WHILE,	true }, | 
    ||
637  | 
    	{ "until",	UNTIL,	true }, | 
    ||
638  | 
    	{ "do",		DO,	true }, | 
    ||
639  | 
    	{ "done",	DONE,	true }, | 
    ||
640  | 
    	{ "in",		IN,	true }, | 
    ||
641  | 
    	{ "function",	FUNCTION, true }, | 
    ||
642  | 
    	{ "time",	TIME,	true }, | 
    ||
643  | 
    	{ "{",		'{',	true }, | 
    ||
644  | 
    	{ "}",		'}',	true }, | 
    ||
645  | 
    	{ "!",		BANG,	true }, | 
    ||
646  | 
    	{ "[[",		DBRACKET, true }, | 
    ||
647  | 
    /* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */  | 
    ||
648  | 
    	{ "&&",		LOGAND,	false }, | 
    ||
649  | 
    	{ "||",		LOGOR,	false }, | 
    ||
650  | 
    	{ ";;",		BREAK,	false }, | 
    ||
651  | 
    	{ "((",		MDPAREN, false }, | 
    ||
652  | 
    	{ "|&",		COPROC,	false }, | 
    ||
653  | 
    /* and some special cases... */  | 
    ||
654  | 
    	{ "newline",	'\n',	false }, | 
    ||
655  | 
    	{ 0 } | 
    ||
656  | 
    };  | 
    ||
657  | 
    |||
658  | 
    void  | 
    ||
659  | 
    initkeywords(void)  | 
    ||
660  | 
    { | 
    ||
661  | 
    struct tokeninfo const *tt;  | 
    ||
662  | 
    struct tbl *p;  | 
    ||
663  | 
    |||
664  | 
    209526  | 
    ktinit(&keywords, APERM, 32); /* must be 2^n (currently 20 keywords) */  | 
    |
665  | 
    ✓✓ | 5657202  | 
    	for (tt = tokentab; tt->name; tt++) { | 
    
666  | 
    ✓✓ | 2723838  | 
    		if (tt->reserved) { | 
    
667  | 
    2095260  | 
    p = ktenter(&keywords, tt->name, hash(tt->name));  | 
    |
668  | 
    2095260  | 
    p->flag |= DEFINED|ISSET;  | 
    |
669  | 
    2095260  | 
    p->type = CKEYWD;  | 
    |
670  | 
    2095260  | 
    p->val.i = tt->val;  | 
    |
671  | 
    2095260  | 
    }  | 
    |
672  | 
    }  | 
    ||
673  | 
    104763  | 
    }  | 
    |
674  | 
    |||
675  | 
    static void  | 
    ||
676  | 
    syntaxerr(const char *what)  | 
    ||
677  | 
    { | 
    ||
678  | 
    12  | 
    char redir[6]; /* 2<<- is the longest redirection, I think */  | 
    |
679  | 
    const char *s;  | 
    ||
680  | 
    struct tokeninfo const *tt;  | 
    ||
681  | 
    int c;  | 
    ||
682  | 
    |||
683  | 
    ✓✗ | 6  | 
    if (!what)  | 
    
684  | 
    6  | 
    what = "unexpected";  | 
    |
685  | 
    6  | 
    reject = true;  | 
    |
686  | 
    ✓✗ | 18  | 
    c = token(0);  | 
    
687  | 
    Again:  | 
    ||
688  | 
    ✗✗✗✓ | 
    6  | 
    	switch (c) { | 
    
689  | 
    case 0:  | 
    ||
690  | 
    		if (nesting.start_token) { | 
    ||
691  | 
    c = nesting.start_token;  | 
    ||
692  | 
    source->errline = nesting.start_line;  | 
    ||
693  | 
    what = "unmatched";  | 
    ||
694  | 
    goto Again;  | 
    ||
695  | 
    }  | 
    ||
696  | 
    /* don't quote the EOF */  | 
    ||
697  | 
    		yyerror("syntax error: unexpected EOF\n"); | 
    ||
698  | 
    /* NOTREACHED */  | 
    ||
699  | 
    |||
700  | 
    case LWORD:  | 
    ||
701  | 
    s = snptreef(NULL, 32, "%S", yylval.cp);  | 
    ||
702  | 
    break;  | 
    ||
703  | 
    |||
704  | 
    case REDIR:  | 
    ||
705  | 
    s = snptreef(redir, sizeof(redir), "%R", yylval.iop);  | 
    ||
706  | 
    break;  | 
    ||
707  | 
    |||
708  | 
    default:  | 
    ||
709  | 
    ✓✓ | 324  | 
    for (tt = tokentab; tt->name; tt++)  | 
    
710  | 
    ✓✗ | 156  | 
    if (tt->val == c)  | 
    
711  | 
    break;  | 
    ||
712  | 
    ✗✓ | 6  | 
    if (tt->name)  | 
    
713  | 
    s = tt->name;  | 
    ||
714  | 
    		else { | 
    ||
715  | 
    ✓✗ | 6  | 
    			if (c > 0 && c < 256) { | 
    
716  | 
    6  | 
    redir[0] = c;  | 
    |
717  | 
    6  | 
    redir[1] = '\0';  | 
    |
718  | 
    6  | 
    } else  | 
    |
719  | 
    shf_snprintf(redir, sizeof(redir),  | 
    ||
720  | 
    "?%d", c);  | 
    ||
721  | 
    6  | 
    s = redir;  | 
    |
722  | 
    }  | 
    ||
723  | 
    }  | 
    ||
724  | 
    	yyerror("syntax error: `%s' %s\n", s, what); | 
    ||
725  | 
    }  | 
    ||
726  | 
    |||
727  | 
    static void  | 
    ||
728  | 
    nesting_push(struct nesting_state *save, int tok)  | 
    ||
729  | 
    { | 
    ||
730  | 
    188200  | 
    *save = nesting;  | 
    |
731  | 
    94100  | 
    nesting.start_token = tok;  | 
    |
732  | 
    94100  | 
    nesting.start_line = source->line;  | 
    |
733  | 
    94100  | 
    }  | 
    |
734  | 
    |||
735  | 
    static void  | 
    ||
736  | 
    nesting_pop(struct nesting_state *saved)  | 
    ||
737  | 
    { | 
    ||
738  | 
    188200  | 
    nesting = *saved;  | 
    |
739  | 
    94100  | 
    }  | 
    |
740  | 
    |||
741  | 
    static struct op *  | 
    ||
742  | 
    newtp(int type)  | 
    ||
743  | 
    { | 
    ||
744  | 
    struct op *t;  | 
    ||
745  | 
    |||
746  | 
    14604730  | 
    t = alloc(sizeof(*t), ATEMP);  | 
    |
747  | 
    7302365  | 
    t->type = type;  | 
    |
748  | 
    7302365  | 
    t->u.evalflags = 0;  | 
    |
749  | 
    7302365  | 
    t->args = t->vars = NULL;  | 
    |
750  | 
    7302365  | 
    t->ioact = NULL;  | 
    |
751  | 
    7302365  | 
    t->left = t->right = NULL;  | 
    |
752  | 
    7302365  | 
    t->str = NULL;  | 
    |
753  | 
    7302365  | 
    return (t);  | 
    |
754  | 
    }  | 
    ||
755  | 
    |||
756  | 
    struct op *  | 
    ||
757  | 
    compile(Source *s)  | 
    ||
758  | 
    { | 
    ||
759  | 
    5044724  | 
    nesting.start_token = 0;  | 
    |
760  | 
    2522362  | 
    nesting.start_line = 0;  | 
    |
761  | 
    2522362  | 
    herep = heres;  | 
    |
762  | 
    2522362  | 
    source = s;  | 
    |
763  | 
    2522362  | 
    yyparse();  | 
    |
764  | 
    2522362  | 
    return outtree;  | 
    |
765  | 
    }  | 
    ||
766  | 
    |||
767  | 
    /* This kludge exists to take care of sh/at&t ksh oddity in which  | 
    ||
768  | 
    * the arguments of alias/export/readonly/typeset have no field  | 
    ||
769  | 
    * splitting, file globbing, or (normal) tilde expansion done.  | 
    ||
770  | 
    * at&t ksh seems to do something similar to this since  | 
    ||
771  | 
    * $ touch a=a; typeset a=[ab]; echo "$a"  | 
    ||
772  | 
    * a=[ab]  | 
    ||
773  | 
    * $ x=typeset; $x a=[ab]; echo "$a"  | 
    ||
774  | 
    * a=a  | 
    ||
775  | 
    * $  | 
    ||
776  | 
    */  | 
    ||
777  | 
    static int  | 
    ||
778  | 
    assign_command(char *s)  | 
    ||
779  | 
    { | 
    ||
780  | 
    ✓✓✓✓ | 
    13404549  | 
    if (Flag(FPOSIX) || !*s)  | 
    
781  | 
    313334  | 
    return 0;  | 
    |
782  | 
    ✓✓ | 8309706  | 
    return (strcmp(s, "alias") == 0) ||  | 
    
783  | 
    ✓✓ | 4154679  | 
    (strcmp(s, "export") == 0) ||  | 
    
784  | 
    ✓✓ | 12447983  | 
    (strcmp(s, "readonly") == 0) ||  | 
    
785  | 
    4146523  | 
    (strcmp(s, "typeset") == 0);  | 
    |
786  | 
    4468187  | 
    }  | 
    |
787  | 
    |||
788  | 
    /* Check if we are in the middle of reading an alias */  | 
    ||
789  | 
    static int  | 
    ||
790  | 
    inalias(struct source *s)  | 
    ||
791  | 
    { | 
    ||
792  | 
    ✓✗✓✓ | 
    3494322  | 
    for (; s && s->type == SALIAS; s = s->next)  | 
    
793  | 
    ✓✓ | 150  | 
    if (!(s->flags & SF_ALIASEND))  | 
    
794  | 
    72  | 
    return 1;  | 
    |
795  | 
    873450  | 
    return 0;  | 
    |
796  | 
    873522  | 
    }  | 
    |
797  | 
    |||
798  | 
    |||
799  | 
    /* Order important - indexed by Test_meta values  | 
    ||
800  | 
    * Note that ||, &&, ( and ) can't appear in as unquoted strings  | 
    ||
801  | 
    * in normal shell input, so these can be interpreted unambiguously  | 
    ||
802  | 
    * in the evaluation pass.  | 
    ||
803  | 
    */  | 
    ||
804  | 
    static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS }; | 
    ||
805  | 
    static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS }; | 
    ||
806  | 
    static const char dbtest_not[] = { CHAR, '!', EOS }; | 
    ||
807  | 
    static const char dbtest_oparen[] = { CHAR, '(', EOS }; | 
    ||
808  | 
    static const char dbtest_cparen[] = { CHAR, ')', EOS }; | 
    ||
809  | 
    const char *const dbtest_tokens[] = { | 
    ||
810  | 
    dbtest_or, dbtest_and, dbtest_not,  | 
    ||
811  | 
    dbtest_oparen, dbtest_cparen  | 
    ||
812  | 
    };  | 
    ||
813  | 
    const char db_close[] = { CHAR, ']', CHAR, ']', EOS }; | 
    ||
814  | 
    const char db_lthan[] = { CHAR, '<', EOS }; | 
    ||
815  | 
    const char db_gthan[] = { CHAR, '>', EOS }; | 
    ||
816  | 
    |||
817  | 
    /* Test if the current token is a whatever. Accepts the current token if  | 
    ||
818  | 
    * it is. Returns 0 if it is not, non-zero if it is (in the case of  | 
    ||
819  | 
    * TM_UNOP and TM_BINOP, the returned value is a Test_op).  | 
    ||
820  | 
    */  | 
    ||
821  | 
    static int  | 
    ||
822  | 
    dbtestp_isa(Test_env *te, Test_meta meta)  | 
    ||
823  | 
    { | 
    ||
824  | 
    ✓✓ | 9072  | 
    int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));  | 
    
825  | 
    int uqword = 0;  | 
    ||
826  | 
    char *save = NULL;  | 
    ||
827  | 
    int ret = 0;  | 
    ||
828  | 
    |||
829  | 
    /* unquoted word? */  | 
    ||
830  | 
    2268  | 
    uqword = c == LWORD && *ident;  | 
    |
831  | 
    |||
832  | 
    ✓✓ | 2268  | 
    if (meta == TM_OR)  | 
    
833  | 
    330  | 
    ret = c == LOGOR;  | 
    |
834  | 
    ✓✓ | 1938  | 
    else if (meta == TM_AND)  | 
    
835  | 
    330  | 
    ret = c == LOGAND;  | 
    |
836  | 
    ✓✓ | 1608  | 
    else if (meta == TM_NOT)  | 
    
837  | 
    ✓✓ | 702  | 
    ret = uqword && strcmp(yylval.cp, dbtest_tokens[(int) TM_NOT]) == 0;  | 
    
838  | 
    ✓✓ | 1278  | 
    else if (meta == TM_OPAREN)  | 
    
839  | 
    330  | 
    		ret = c == '(' /*)*/; | 
    |
840  | 
    ✗✓ | 948  | 
    else if (meta == TM_CPAREN)  | 
    
841  | 
    ret = c == /*(*/ ')';  | 
    ||
842  | 
    ✓✓ | 948  | 
    	else if (meta == TM_UNOP || meta == TM_BINOP) { | 
    
843  | 
    ✗✓✗✗ | 
    618  | 
    if (meta == TM_BINOP && c == REDIR &&  | 
    
844  | 
    		    (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) { | 
    ||
845  | 
    ret = 1;  | 
    ||
846  | 
    save = wdcopy(yylval.iop->flag == IOREAD ?  | 
    ||
847  | 
    db_lthan : db_gthan, ATEMP);  | 
    ||
848  | 
    ✓✓✓✗ | 
    948  | 
    } else if (uqword && (ret = (int) test_isop(te, meta, ident)))  | 
    
849  | 
    330  | 
    save = yylval.cp;  | 
    |
850  | 
    } else /* meta == TM_END */  | 
    ||
851  | 
    ✓✗ | 990  | 
    ret = uqword && strcmp(yylval.cp, db_close) == 0;  | 
    
852  | 
    ✓✓ | 2268  | 
    	if (ret) { | 
    
853  | 
    660  | 
    reject = false;  | 
    |
854  | 
    ✓✓ | 660  | 
    		if (meta != TM_END) { | 
    
855  | 
    ✗✓ | 330  | 
    if (!save)  | 
    
856  | 
    save = wdcopy(dbtest_tokens[(int) meta], ATEMP);  | 
    ||
857  | 
    ✗✓ | 660  | 
    XPput(*te->pos.av, save);  | 
    
858  | 
    330  | 
    }  | 
    |
859  | 
    }  | 
    ||
860  | 
    2268  | 
    return ret;  | 
    |
861  | 
    }  | 
    ||
862  | 
    |||
863  | 
    static const char *  | 
    ||
864  | 
    dbtestp_getopnd(Test_env *te, Test_op op, int do_eval)  | 
    ||
865  | 
    { | 
    ||
866  | 
    ✓✓ | 2472  | 
    int c = tpeek(ARRAYVAR);  | 
    
867  | 
    |||
868  | 
    ✗✓ | 618  | 
    if (c != LWORD)  | 
    
869  | 
    return NULL;  | 
    ||
870  | 
    |||
871  | 
    618  | 
    reject = false;  | 
    |
872  | 
    ✗✓ | 1236  | 
    XPput(*te->pos.av, yylval.cp);  | 
    
873  | 
    |||
874  | 
    618  | 
    return null;  | 
    |
875  | 
    618  | 
    }  | 
    |
876  | 
    |||
877  | 
    static int  | 
    ||
878  | 
    dbtestp_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,  | 
    ||
879  | 
    int do_eval)  | 
    ||
880  | 
    { | 
    ||
881  | 
    660  | 
    return 1;  | 
    |
882  | 
    }  | 
    ||
883  | 
    |||
884  | 
    static void  | 
    ||
885  | 
    dbtestp_error(Test_env *te, int offset, const char *msg)  | 
    ||
886  | 
    { | 
    ||
887  | 
    te->flags |= TEF_ERROR;  | 
    ||
888  | 
    |||
889  | 
    	if (offset < 0) { | 
    ||
890  | 
    reject = true;  | 
    ||
891  | 
    /* Kludgy to say the least... */  | 
    ||
892  | 
    symbol = LWORD;  | 
    ||
893  | 
    yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) +  | 
    ||
894  | 
    offset);  | 
    ||
895  | 
    }  | 
    ||
896  | 
    syntaxerr(msg);  | 
    ||
897  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |