| GCC Code Coverage Report | |||||||||||||||||||||
        
  | 
    |||||||||||||||||||||
| Line | Branch | Exec | Source | 
1  | 
    /* $OpenBSD: exec.c,v 1.68 2016/12/11 17:49:19 millert Exp $ */  | 
    ||
2  | 
    |||
3  | 
    /*  | 
    ||
4  | 
    * execute command tree  | 
    ||
5  | 
    */  | 
    ||
6  | 
    |||
7  | 
    #include <sys/stat.h>  | 
    ||
8  | 
    |||
9  | 
    #include <ctype.h>  | 
    ||
10  | 
    #include <errno.h>  | 
    ||
11  | 
    #include <fcntl.h>  | 
    ||
12  | 
    #include <paths.h>  | 
    ||
13  | 
    #include <stdio.h>  | 
    ||
14  | 
    #include <stdlib.h>  | 
    ||
15  | 
    #include <string.h>  | 
    ||
16  | 
    #include <unistd.h>  | 
    ||
17  | 
    |||
18  | 
    #include "sh.h"  | 
    ||
19  | 
    #include "c_test.h"  | 
    ||
20  | 
    |||
21  | 
    /* Does ps4 get parameter substitutions done? */  | 
    ||
22  | 
    # define PS4_SUBSTITUTE(s) substitute((s), 0)  | 
    ||
23  | 
    |||
24  | 
    static int comexec(struct op *, struct tbl *volatile, char **,  | 
    ||
25  | 
    int volatile, volatile int *);  | 
    ||
26  | 
    static void scriptexec(struct op *, char **);  | 
    ||
27  | 
    static int call_builtin(struct tbl *, char **);  | 
    ||
28  | 
    static int iosetup(struct ioword *, struct tbl *);  | 
    ||
29  | 
    static int herein(const char *, int);  | 
    ||
30  | 
    static char *do_selectargs(char **, bool);  | 
    ||
31  | 
    static int dbteste_isa(Test_env *, Test_meta);  | 
    ||
32  | 
    static const char *dbteste_getopnd(Test_env *, Test_op, int);  | 
    ||
33  | 
    static int dbteste_eval(Test_env *, Test_op, const char *, const char *,  | 
    ||
34  | 
    int);  | 
    ||
35  | 
    static void dbteste_error(Test_env *, int, const char *);  | 
    ||
36  | 
    |||
37  | 
    |||
38  | 
    /*  | 
    ||
39  | 
    * execute command tree  | 
    ||
40  | 
    */  | 
    ||
41  | 
    int  | 
    ||
42  | 
    execute(struct op *volatile t,  | 
    ||
43  | 
    volatile int flags, /* if XEXEC don't fork */  | 
    ||
44  | 
    volatile int *xerrok) /* inform recursive callers in -e mode that  | 
    ||
45  | 
    * short-circuit && or || shouldn't be treated  | 
    ||
46  | 
    * as an error */  | 
    ||
47  | 
    { | 
    ||
48  | 
    10761622  | 
    int i, dummy = 0, save_xerrok = 0;  | 
    |
49  | 
    10761622  | 
    volatile int rv = 0;  | 
    |
50  | 
    10761622  | 
    int pv[2];  | 
    |
51  | 
    10761622  | 
    char ** volatile ap;  | 
    |
52  | 
    char *s, *cp;  | 
    ||
53  | 
    struct ioword **iowp;  | 
    ||
54  | 
    struct tbl *tp = NULL;  | 
    ||
55  | 
    |||
56  | 
    ✓✓ | 10761622  | 
    if (t == NULL)  | 
    
57  | 
    721022  | 
    return 0;  | 
    |
58  | 
    |||
59  | 
    /* Caller doesn't care if XERROK should propagate. */  | 
    ||
60  | 
    ✓✓ | 10040600  | 
    if (xerrok == NULL)  | 
    
61  | 
    7208042  | 
    xerrok = &dummy;  | 
    |
62  | 
    |||
63  | 
    /* Is this the end of a pipeline? If so, we want to evaluate the  | 
    ||
64  | 
    * command arguments  | 
    ||
65  | 
    bool eval_done = false;  | 
    ||
66  | 
    	if ((flags&XFORK) && !(flags&XEXEC) && (flags&XPCLOSE)) { | 
    ||
67  | 
    eval_done = true;  | 
    ||
68  | 
    tp = eval_execute_args(t, &ap);  | 
    ||
69  | 
    }  | 
    ||
70  | 
    */  | 
    ||
71  | 
    ✓✓✓✓ ✓✓  | 
    12783285  | 
    if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)  | 
    
72  | 
    491505  | 
    return exchild(t, flags & ~XTIME, xerrok, -1); /* run in sub-process */  | 
    |
73  | 
    |||
74  | 
    9549095  | 
    newenv(E_EXEC);  | 
    |
75  | 
    ✓✓ | 9549095  | 
    if (trap)  | 
    
76  | 
    1513913  | 
    runtraps(0);  | 
    |
77  | 
    |||
78  | 
    ✓✓ | 9549080  | 
    	if (t->type == TCOM) { | 
    
79  | 
    /* Clear subst_exstat before argument expansion. Used by  | 
    ||
80  | 
    * null commands (see comexec() and c_eval()) and by c_set().  | 
    ||
81  | 
    */  | 
    ||
82  | 
    5746434  | 
    subst_exstat = 0;  | 
    |
83  | 
    |||
84  | 
    5746434  | 
    current_lineno = t->lineno; /* for $LINENO */  | 
    |
85  | 
    |||
86  | 
    /* POSIX says expand command words first, then redirections,  | 
    ||
87  | 
    * and assignments last..  | 
    ||
88  | 
    */  | 
    ||
89  | 
    5746434  | 
    ap = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE);  | 
    |
90  | 
    ✓✓ | 5746434  | 
    if (flags & XTIME)  | 
    
91  | 
    /* Allow option parsing (bizarre, but POSIX) */  | 
    ||
92  | 
    1358  | 
    timex_hook(t, &ap);  | 
    |
93  | 
    ✓✓✓✗ | 
    5769002  | 
    		if (Flag(FXTRACE) && ap[0]) { | 
    
94  | 
    22568  | 
    shf_fprintf(shl_out, "%s",  | 
    |
95  | 
    22568  | 
    				PS4_SUBSTITUTE(str_val(global("PS4")))); | 
    |
96  | 
    ✓✓ | 141232  | 
    for (i = 0; ap[i]; i++)  | 
    
97  | 
    96096  | 
    shf_fprintf(shl_out, "%s%s", ap[i],  | 
    |
98  | 
    48048  | 
    ap[i + 1] ? " " : "\n");  | 
    |
99  | 
    22568  | 
    shf_flush(shl_out);  | 
    |
100  | 
    22568  | 
    }  | 
    |
101  | 
    ✓✓ | 5746434  | 
    if (ap[0])  | 
    
102  | 
    3069498  | 
    tp = findcom(ap[0], FC_BI|FC_FUNC);  | 
    |
103  | 
    }  | 
    ||
104  | 
    9548525  | 
    flags &= ~XTIME;  | 
    |
105  | 
    |||
106  | 
    ✓✓✓✓ ✗✓  | 
    27492757  | 
    	if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) { | 
    
107  | 
    1078167  | 
    genv->savefd = areallocarray(NULL, NUFILE, sizeof(short), ATEMP);  | 
    |
108  | 
    /* initialize to not redirected */  | 
    ||
109  | 
    1078167  | 
    memset(genv->savefd, 0, NUFILE * sizeof(short));  | 
    |
110  | 
    1078167  | 
    }  | 
    |
111  | 
    |||
112  | 
    /* do redirection, to be restored in quitenv() */  | 
    ||
113  | 
    ✓✓ | 9548525  | 
    if (t->ioact != NULL)  | 
    
114  | 
    ✓✓ | 383934  | 
    		for (iowp = t->ioact; *iowp != NULL; iowp++) { | 
    
115  | 
    ✗✓ | 117316  | 
    			if (iosetup(*iowp, tp) < 0) { | 
    
116  | 
    exstat = rv = 1;  | 
    ||
117  | 
    /* Redirection failures for special commands  | 
    ||
118  | 
    * cause (non-interactive) shell to exit.  | 
    ||
119  | 
    */  | 
    ||
120  | 
    if (tp && tp->type == CSHELL &&  | 
    ||
121  | 
    (tp->flag & SPEC_BI))  | 
    ||
122  | 
    errorf(NULL);  | 
    ||
123  | 
    /* Deal with FERREXIT, quitenv(), etc. */  | 
    ||
124  | 
    goto Break;  | 
    ||
125  | 
    }  | 
    ||
126  | 
    }  | 
    ||
127  | 
    |||
128  | 
    ✓✓✓✓ ✗✓✗✓ ✓✓✗✓ ✗✓✗✓ ✓✓✓✓ ✗✓  | 
    18671622  | 
    	switch (t->type) { | 
    
129  | 
    case TCOM:  | 
    ||
130  | 
    5746428  | 
    rv = comexec(t, tp, ap, flags, xerrok);  | 
    |
131  | 
    5746428  | 
    break;  | 
    |
132  | 
    |||
133  | 
    case TPAREN:  | 
    ||
134  | 
    1670  | 
    rv = execute(t->left, flags|XFORK, xerrok);  | 
    |
135  | 
    1670  | 
    break;  | 
    |
136  | 
    |||
137  | 
    case TPIPE:  | 
    ||
138  | 
    1003516  | 
    flags |= XFORK;  | 
    |
139  | 
    1003516  | 
    flags &= ~XEXEC;  | 
    |
140  | 
    1003516  | 
    genv->savefd[0] = savefd(0);  | 
    |
141  | 
    1003516  | 
    genv->savefd[1] = savefd(1);  | 
    |
142  | 
    ✓✓ | 4795978  | 
    		while (t->type == TPIPE) { | 
    
143  | 
    1409192  | 
    openpipe(pv);  | 
    |
144  | 
    1409192  | 
    (void) ksh_dup2(pv[1], 1, false); /* stdout of curr */  | 
    |
145  | 
    /* Let exchild() close pv[0] in child  | 
    ||
146  | 
    * (if this isn't done, commands like  | 
    ||
147  | 
    * (: ; cat /etc/termcap) | sleep 1  | 
    ||
148  | 
    * will hang forever).  | 
    ||
149  | 
    */  | 
    ||
150  | 
    1409192  | 
    exchild(t->left, flags|XPIPEO|XCCLOSE, NULL, pv[0]);  | 
    |
151  | 
    1409192  | 
    (void) ksh_dup2(pv[0], 0, false); /* stdin of next */  | 
    |
152  | 
    1409192  | 
    closepipe(pv);  | 
    |
153  | 
    1409192  | 
    flags |= XPIPEI;  | 
    |
154  | 
    1409192  | 
    t = t->right;  | 
    |
155  | 
    }  | 
    ||
156  | 
    974012  | 
    restfd(1, genv->savefd[1]); /* stdout of last */  | 
    |
157  | 
    974012  | 
    genv->savefd[1] = 0; /* no need to re-restore this */  | 
    |
158  | 
    /* Let exchild() close 0 in parent, after fork, before wait */  | 
    ||
159  | 
    974012  | 
    i = exchild(t, flags|XPCLOSE, xerrok, 0);  | 
    |
160  | 
    ✓✓✓✓ | 
    1948018  | 
    if (!(flags&XBGND) && !(flags&XXCOM))  | 
    
161  | 
    98750  | 
    rv = i;  | 
    |
162  | 
    break;  | 
    ||
163  | 
    |||
164  | 
    case TLIST:  | 
    ||
165  | 
    ✓✓ | 10097175  | 
    		while (t->type == TLIST) { | 
    
166  | 
    4236829  | 
    execute(t->left, flags & XERROK, NULL);  | 
    |
167  | 
    4236829  | 
    t = t->right;  | 
    |
168  | 
    }  | 
    ||
169  | 
    779930  | 
    rv = execute(t, flags & XERROK, xerrok);  | 
    |
170  | 
    779930  | 
    break;  | 
    |
171  | 
    |||
172  | 
    case TCOPROC:  | 
    ||
173  | 
    	    { | 
    ||
174  | 
    sigset_t omask;  | 
    ||
175  | 
    |||
176  | 
    /* Block sigchild as we are using things changed in the  | 
    ||
177  | 
    * signal handler  | 
    ||
178  | 
    */  | 
    ||
179  | 
    sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);  | 
    ||
180  | 
    genv->type = E_ERRH;  | 
    ||
181  | 
    i = sigsetjmp(genv->jbuf, 0);  | 
    ||
182  | 
    		if (i) { | 
    ||
183  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
184  | 
    quitenv(NULL);  | 
    ||
185  | 
    unwind(i);  | 
    ||
186  | 
    /* NOTREACHED */  | 
    ||
187  | 
    }  | 
    ||
188  | 
    /* Already have a (live) co-process? */  | 
    ||
189  | 
    if (coproc.job && coproc.write >= 0)  | 
    ||
190  | 
    			errorf("coprocess already exists"); | 
    ||
191  | 
    |||
192  | 
    /* Can we re-use the existing co-process pipe? */  | 
    ||
193  | 
    coproc_cleanup(true);  | 
    ||
194  | 
    |||
195  | 
    /* do this before opening pipes, in case these fail */  | 
    ||
196  | 
    genv->savefd[0] = savefd(0);  | 
    ||
197  | 
    genv->savefd[1] = savefd(1);  | 
    ||
198  | 
    |||
199  | 
    openpipe(pv);  | 
    ||
200  | 
    		if (pv[0] != 0) { | 
    ||
201  | 
    ksh_dup2(pv[0], 0, false);  | 
    ||
202  | 
    close(pv[0]);  | 
    ||
203  | 
    }  | 
    ||
204  | 
    coproc.write = pv[1];  | 
    ||
205  | 
    coproc.job = NULL;  | 
    ||
206  | 
    |||
207  | 
    if (coproc.readw >= 0)  | 
    ||
208  | 
    ksh_dup2(coproc.readw, 1, false);  | 
    ||
209  | 
    		else { | 
    ||
210  | 
    openpipe(pv);  | 
    ||
211  | 
    coproc.read = pv[0];  | 
    ||
212  | 
    ksh_dup2(pv[1], 1, false);  | 
    ||
213  | 
    coproc.readw = pv[1]; /* closed before first read */  | 
    ||
214  | 
    coproc.njobs = 0;  | 
    ||
215  | 
    /* create new coprocess id */  | 
    ||
216  | 
    ++coproc.id;  | 
    ||
217  | 
    }  | 
    ||
218  | 
    sigprocmask(SIG_SETMASK, &omask, NULL);  | 
    ||
219  | 
    genv->type = E_EXEC; /* no more need for error handler */  | 
    ||
220  | 
    |||
221  | 
    /* exchild() closes coproc.* in child after fork,  | 
    ||
222  | 
    * will also increment coproc.njobs when the  | 
    ||
223  | 
    * job is actually created.  | 
    ||
224  | 
    */  | 
    ||
225  | 
    flags &= ~XEXEC;  | 
    ||
226  | 
    exchild(t->left, flags|XBGND|XFORK|XCOPROC|XCCLOSE,  | 
    ||
227  | 
    NULL, coproc.readw);  | 
    ||
228  | 
    break;  | 
    ||
229  | 
    }  | 
    ||
230  | 
    |||
231  | 
    case TASYNC:  | 
    ||
232  | 
    /* XXX non-optimal, I think - "(foo &)", forks for (),  | 
    ||
233  | 
    * forks again for async... parent should optimize  | 
    ||
234  | 
    * this to "foo &"...  | 
    ||
235  | 
    */  | 
    ||
236  | 
    775  | 
    rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok);  | 
    |
237  | 
    775  | 
    break;  | 
    |
238  | 
    |||
239  | 
    case TOR:  | 
    ||
240  | 
    case TAND:  | 
    ||
241  | 
    444954  | 
    rv = execute(t->left, XERROK, xerrok);  | 
    |
242  | 
    ✓✓ | 444954  | 
    if ((rv == 0) == (t->type == TAND))  | 
    
243  | 
    185584  | 
    rv = execute(t->right, flags & XERROK, xerrok);  | 
    |
244  | 
    		else { | 
    ||
245  | 
    259370  | 
    flags |= XERROK;  | 
    |
246  | 
    259370  | 
    *xerrok = 1;  | 
    |
247  | 
    }  | 
    ||
248  | 
    break;  | 
    ||
249  | 
    |||
250  | 
    case TBANG:  | 
    ||
251  | 
    643  | 
    rv = !execute(t->right, XERROK, xerrok);  | 
    |
252  | 
    643  | 
    flags |= XERROK;  | 
    |
253  | 
    643  | 
    *xerrok = 1;  | 
    |
254  | 
    643  | 
    break;  | 
    |
255  | 
    |||
256  | 
    case TDBRACKET:  | 
    ||
257  | 
    	    { | 
    ||
258  | 
    13234  | 
    Test_env te;  | 
    |
259  | 
    |||
260  | 
    13234  | 
    te.flags = TEF_DBRACKET;  | 
    |
261  | 
    13234  | 
    te.pos.wp = t->args;  | 
    |
262  | 
    13234  | 
    te.isa = dbteste_isa;  | 
    |
263  | 
    13234  | 
    te.getopnd = dbteste_getopnd;  | 
    |
264  | 
    13234  | 
    te.eval = dbteste_eval;  | 
    |
265  | 
    13234  | 
    te.error = dbteste_error;  | 
    |
266  | 
    |||
267  | 
    13234  | 
    rv = test_parse(&te);  | 
    |
268  | 
    break;  | 
    ||
269  | 
    13234  | 
    }  | 
    |
270  | 
    |||
271  | 
    case TFOR:  | 
    ||
272  | 
    case TSELECT:  | 
    ||
273  | 
    	    { | 
    ||
274  | 
    32190  | 
    volatile bool is_first = true;  | 
    |
275  | 
    ✓✓ | 96564  | 
    ap = (t->vars != NULL) ? eval(t->vars, DOBLANK|DOGLOB|DOTILDE) :  | 
    
276  | 
    308  | 
    genv->loc->argv + 1;  | 
    |
277  | 
    32184  | 
    genv->type = E_LOOP;  | 
    |
278  | 
    32368  | 
    		while (1) { | 
    |
279  | 
    32758  | 
    i = sigsetjmp(genv->jbuf, 0);  | 
    |
280  | 
    ✓✓ | 32758  | 
    if (!i)  | 
    
281  | 
    break;  | 
    ||
282  | 
    ✓✓ | 390  | 
    if ((genv->flags&EF_BRKCONT_PASS) ||  | 
    
283  | 
    ✓✓ | 366  | 
    			    (i != LBREAK && i != LCONTIN)) { | 
    
284  | 
    quitenv(NULL);  | 
    ||
285  | 
    unwind(i);  | 
    ||
286  | 
    ✓✓ | 308  | 
    			} else if (i == LBREAK) { | 
    
287  | 
    124  | 
    rv = 0;  | 
    |
288  | 
    124  | 
    goto Break;  | 
    |
289  | 
    }  | 
    ||
290  | 
    }  | 
    ||
291  | 
    32368  | 
    rv = 0; /* in case of a continue */  | 
    |
292  | 
    ✓✗ | 32368  | 
    		if (t->type == TFOR) { | 
    
293  | 
    32368  | 
    save_xerrok = *xerrok;  | 
    |
294  | 
    ✓✓ | 443657  | 
    			while (*ap != NULL) { | 
    
295  | 
    190405  | 
    setstr(global(t->str), *ap++, KSH_UNWIND_ERROR);  | 
    |
296  | 
    /* undo xerrok in all iterations except the  | 
    ||
297  | 
    * last */  | 
    ||
298  | 
    190405  | 
    *xerrok = save_xerrok;  | 
    |
299  | 
    190405  | 
    rv = execute(t->left, flags & XERROK, xerrok);  | 
    |
300  | 
    }  | 
    ||
301  | 
    /* ripple xerrok set at final iteration */  | 
    ||
302  | 
    		} else { /* TSELECT */ | 
    ||
303  | 
    			for (;;) { | 
    ||
304  | 
    				if (!(cp = do_selectargs(ap, is_first))) { | 
    ||
305  | 
    rv = 1;  | 
    ||
306  | 
    break;  | 
    ||
307  | 
    }  | 
    ||
308  | 
    is_first = false;  | 
    ||
309  | 
    setstr(global(t->str), cp, KSH_UNWIND_ERROR);  | 
    ||
310  | 
    rv = execute(t->left, flags & XERROK, xerrok);  | 
    ||
311  | 
    }  | 
    ||
312  | 
    }  | 
    ||
313  | 
    ✗✓✓ | 229490  | 
    }  | 
    
314  | 
    break;  | 
    ||
315  | 
    |||
316  | 
    case TWHILE:  | 
    ||
317  | 
    case TUNTIL:  | 
    ||
318  | 
    61475  | 
    genv->type = E_LOOP;  | 
    |
319  | 
    62601  | 
    		while (1) { | 
    |
320  | 
    63763  | 
    i = sigsetjmp(genv->jbuf, 0);  | 
    |
321  | 
    ✓✓ | 63763  | 
    if (!i)  | 
    
322  | 
    break;  | 
    ||
323  | 
    ✓✗ | 1162  | 
    if ((genv->flags&EF_BRKCONT_PASS) ||  | 
    
324  | 
    ✓✓ | 1162  | 
    			    (i != LBREAK && i != LCONTIN)) { | 
    
325  | 
    quitenv(NULL);  | 
    ||
326  | 
    unwind(i);  | 
    ||
327  | 
    ✓✓ | 1132  | 
    			} else if (i == LBREAK) { | 
    
328  | 
    6  | 
    rv = 0;  | 
    |
329  | 
    6  | 
    goto Break;  | 
    |
330  | 
    }  | 
    ||
331  | 
    }  | 
    ||
332  | 
    62601  | 
    rv = 0; /* in case of a continue */  | 
    |
333  | 
    ✓✓ | 327694  | 
    while ((execute(t->left, XERROK, NULL) == 0) == (t->type == TWHILE))  | 
    
334  | 
    102574  | 
    rv = execute(t->right, flags & XERROK, xerrok);  | 
    |
335  | 
    break;  | 
    ||
336  | 
    |||
337  | 
    case TIF:  | 
    ||
338  | 
    case TELIF:  | 
    ||
339  | 
    ✓✗ | 882844  | 
    if (t->right == NULL)  | 
    
340  | 
    break; /* should be error */  | 
    ||
341  | 
    ✓✓ | 2635544  | 
    rv = execute(t->left, XERROK, NULL) == 0 ?  | 
    
342  | 
    141750  | 
    execute(t->right->left, flags & XERROK, xerrok) :  | 
    |
343  | 
    740821  | 
    execute(t->right->right, flags & XERROK, xerrok);  | 
    |
344  | 
    870402  | 
    break;  | 
    |
345  | 
    |||
346  | 
    case TCASE:  | 
    ||
347  | 
    15455  | 
    cp = evalstr(t->str, DOTILDE);  | 
    |
348  | 
    ✓✓✓✗ | 
    69553  | 
    		for (t = t->left; t != NULL && t->type == TPAT; t = t->right) { | 
    
349  | 
    ✓✓ | 66072  | 
    			for (ap = t->vars; *ap; ap++) { | 
    
350  | 
    ✓✗✓✓ | 
    50242  | 
    if ((s = evalstr(*ap, DOTILDE|DOPAT)) &&  | 
    
351  | 
    25121  | 
    gmatch(cp, s, false))  | 
    |
352  | 
    goto Found;  | 
    ||
353  | 
    }  | 
    ||
354  | 
    }  | 
    ||
355  | 
    break;  | 
    ||
356  | 
    Found:  | 
    ||
357  | 
    14898  | 
    rv = execute(t->left, flags & XERROK, xerrok);  | 
    |
358  | 
    14898  | 
    break;  | 
    |
359  | 
    |||
360  | 
    case TBRACE:  | 
    ||
361  | 
    474009  | 
    rv = execute(t->left, flags & XERROK, xerrok);  | 
    |
362  | 
    474009  | 
    break;  | 
    |
363  | 
    |||
364  | 
    case TFUNCT:  | 
    ||
365  | 
    26168  | 
    rv = define(t->str, t);  | 
    |
366  | 
    26168  | 
    break;  | 
    |
367  | 
    |||
368  | 
    case TTIME:  | 
    ||
369  | 
    /* Clear XEXEC so nested execute() call doesn't exit  | 
    ||
370  | 
    * (allows "ls -l | time grep foo").  | 
    ||
371  | 
    */  | 
    ||
372  | 
    1358  | 
    rv = timex(t, flags & ~XEXEC, xerrok);  | 
    |
373  | 
    1358  | 
    break;  | 
    |
374  | 
    |||
375  | 
    case TEXEC: /* an eval'd TCOM */  | 
    ||
376  | 
    s = t->args[0];  | 
    ||
377  | 
    ap = makenv();  | 
    ||
378  | 
    restoresigs();  | 
    ||
379  | 
    cleanup_proc_env();  | 
    ||
380  | 
    execve(t->str, t->args, ap);  | 
    ||
381  | 
    if (errno == ENOEXEC)  | 
    ||
382  | 
    scriptexec(t, ap);  | 
    ||
383  | 
    else  | 
    ||
384  | 
    			errorf("%s: %s", s, strerror(errno)); | 
    ||
385  | 
    }  | 
    ||
386  | 
    Break:  | 
    ||
387  | 
    9222609  | 
    exstat = rv;  | 
    |
388  | 
    |||
389  | 
    9222609  | 
    quitenv(NULL); /* restores IO */  | 
    |
390  | 
    ✓✓ | 9222609  | 
    if ((flags&XEXEC))  | 
    
391  | 
    unwind(LEXIT); /* exit child */  | 
    ||
392  | 
    ✓✓✓✓ ✓✓  | 
    10542633  | 
    	if (rv != 0 && !(flags & XERROK) && !*xerrok) { | 
    
393  | 
    11121  | 
    trapsig(SIGERR_);  | 
    |
394  | 
    ✓✓ | 11121  | 
    if (Flag(FERREXIT))  | 
    
395  | 
    unwind(LERROR);  | 
    ||
396  | 
    }  | 
    ||
397  | 
    9219657  | 
    return rv;  | 
    |
398  | 
    10424867  | 
    }  | 
    |
399  | 
    |||
400  | 
    /*  | 
    ||
401  | 
    * execute simple command  | 
    ||
402  | 
    */  | 
    ||
403  | 
    |||
404  | 
    static int  | 
    ||
405  | 
    comexec(struct op *t, struct tbl *volatile tp, char **ap, volatile int flags,  | 
    ||
406  | 
    volatile int *xerrok)  | 
    ||
407  | 
    { | 
    ||
408  | 
    int i;  | 
    ||
409  | 
    5746428  | 
    volatile int rv = 0;  | 
    |
410  | 
    char *cp;  | 
    ||
411  | 
    char **lastp;  | 
    ||
412  | 
    static struct op texec; /* Must be static (XXX but why?) */  | 
    ||
413  | 
    int type_flags;  | 
    ||
414  | 
    int keepasn_ok;  | 
    ||
415  | 
    int fcflags = FC_BI|FC_FUNC|FC_PATH;  | 
    ||
416  | 
    int bourne_function_call = 0;  | 
    ||
417  | 
    |||
418  | 
    /* snag the last argument for $_ XXX not the same as at&t ksh,  | 
    ||
419  | 
    * which only seems to set $_ after a newline (but not in  | 
    ||
420  | 
    * functions/dot scripts, but in interactive and script) -  | 
    ||
421  | 
    * perhaps save last arg here and set it in shell()?.  | 
    ||
422  | 
    */  | 
    ||
423  | 
    ✓✓✓✓ ✓✓  | 
    5774289  | 
    	if (!Flag(FSH) && Flag(FTALKING) && *(lastp = ap)) { | 
    
424  | 
    ✓✓ | 4832  | 
    while (*++lastp)  | 
    
425  | 
    ;  | 
    ||
426  | 
    /* setstr() can't fail here */  | 
    ||
427  | 
    860  | 
    		setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp, | 
    |
428  | 
    KSH_RETURN_ERROR);  | 
    ||
429  | 
    860  | 
    }  | 
    |
430  | 
    |||
431  | 
    /* Deal with the shell builtins builtin, exec and command since  | 
    ||
432  | 
    * they can be followed by other commands. This must be done before  | 
    ||
433  | 
    * we know if we should create a local block, which must be done  | 
    ||
434  | 
    * before we can do a path search (in case the assignments change  | 
    ||
435  | 
    * PATH).  | 
    ||
436  | 
    * Odd cases:  | 
    ||
437  | 
    * FOO=bar exec > /dev/null FOO is kept but not exported  | 
    ||
438  | 
    * FOO=bar exec foobar FOO is exported  | 
    ||
439  | 
    * FOO=bar command exec > /dev/null FOO is neither kept nor exported  | 
    ||
440  | 
    * FOO=bar command FOO is neither kept nor exported  | 
    ||
441  | 
    * PATH=... foobar use new PATH in foobar search  | 
    ||
442  | 
    */  | 
    ||
443  | 
    keepasn_ok = 1;  | 
    ||
444  | 
    ✓✓✓✓ | 
    14425889  | 
    	while (tp && tp->type == CSHELL) { | 
    
445  | 
    fcflags = FC_BI|FC_FUNC|FC_PATH;/* undo effects of command */  | 
    ||
446  | 
    ✗✓ | 2459025  | 
    		if (tp->val.f == c_builtin) { | 
    
447  | 
    			if ((cp = *++ap) == NULL) { | 
    ||
448  | 
    tp = NULL;  | 
    ||
449  | 
    break;  | 
    ||
450  | 
    }  | 
    ||
451  | 
    tp = findcom(cp, FC_BI);  | 
    ||
452  | 
    if (tp == NULL)  | 
    ||
453  | 
    				errorf("builtin: %s: not a builtin", cp); | 
    ||
454  | 
    continue;  | 
    ||
455  | 
    ✓✓ | 2459025  | 
    		} else if (tp->val.f == c_exec) { | 
    
456  | 
    ✓✓ | 36388  | 
    if (ap[1] == NULL)  | 
    
457  | 
    break;  | 
    ||
458  | 
    6  | 
    ap++;  | 
    |
459  | 
    6  | 
    flags |= XEXEC;  | 
    |
460  | 
    ✓✓ | 2422643  | 
    		} else if (tp->val.f == c_command) { | 
    
461  | 
    int optc, saw_p = 0;  | 
    ||
462  | 
    |||
463  | 
    /* Ugly dealing with options in two places (here and  | 
    ||
464  | 
    * in c_command(), but such is life)  | 
    ||
465  | 
    */  | 
    ||
466  | 
    144  | 
    ksh_getopt_reset(&builtin_opt, 0);  | 
    |
467  | 
    ✓✓ | 432  | 
    while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p')  | 
    
468  | 
    saw_p = 1;  | 
    ||
469  | 
    ✓✗ | 144  | 
    if (optc != EOF)  | 
    
470  | 
    144  | 
    break; /* command -vV or something */  | 
    |
471  | 
    /* don't look for functions */  | 
    ||
472  | 
    fcflags = FC_BI|FC_PATH;  | 
    ||
473  | 
    			if (saw_p) { | 
    ||
474  | 
    				if (Flag(FRESTRICTED)) { | 
    ||
475  | 
    warningf(true,  | 
    ||
476  | 
    "command -p: restricted");  | 
    ||
477  | 
    rv = 1;  | 
    ||
478  | 
    goto Leave;  | 
    ||
479  | 
    }  | 
    ||
480  | 
    fcflags |= FC_DEFPATH;  | 
    ||
481  | 
    }  | 
    ||
482  | 
    ap += builtin_opt.optind;  | 
    ||
483  | 
    /* POSIX says special builtins lose their status  | 
    ||
484  | 
    * if accessed using command.  | 
    ||
485  | 
    */  | 
    ||
486  | 
    keepasn_ok = 0;  | 
    ||
487  | 
    			if (!ap[0]) { | 
    ||
488  | 
    /* ensure command with no args exits with 0 */  | 
    ||
489  | 
    subst_exstat = 0;  | 
    ||
490  | 
    break;  | 
    ||
491  | 
    }  | 
    ||
492  | 
    } else  | 
    ||
493  | 
    break;  | 
    ||
494  | 
    6  | 
    tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));  | 
    |
495  | 
    }  | 
    ||
496  | 
    ✓✗✓✓ ✓✓✓✓  | 
    17495363  | 
    if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))  | 
    
497  | 
    3208770  | 
    type_flags = 0;  | 
    |
498  | 
    	else { | 
    ||
499  | 
    /* create new variable/function block */  | 
    ||
500  | 
    2537658  | 
    newblock();  | 
    |
501  | 
    /* ksh functions don't keep assignments, POSIX functions do. */  | 
    ||
502  | 
    ✓✗✓✓ ✓✓✓✓  | 
    7950493  | 
    if (keepasn_ok && tp && tp->type == CFUNC &&  | 
    
503  | 
    473996  | 
    		    !(tp->flag & FKSH)) { | 
    |
504  | 
    bourne_function_call = 1;  | 
    ||
505  | 
    type_flags = 0;  | 
    ||
506  | 
    471556  | 
    } else  | 
    |
507  | 
    type_flags = LOCAL|LOCAL_COPY|EXPORT;  | 
    ||
508  | 
    }  | 
    ||
509  | 
    ✗✓ | 5746428  | 
    if (Flag(FEXPORT))  | 
    
510  | 
    type_flags |= EXPORT;  | 
    ||
511  | 
    ✓✓ | 16843198  | 
    	for (i = 0; t->vars[i]; i++) { | 
    
512  | 
    2675195  | 
    cp = evalstr(t->vars[i], DOASNTILDE);  | 
    |
513  | 
    ✗✓ | 2675195  | 
    		if (Flag(FXTRACE)) { | 
    
514  | 
    if (i == 0)  | 
    ||
515  | 
    shf_fprintf(shl_out, "%s",  | 
    ||
516  | 
    				    PS4_SUBSTITUTE(str_val(global("PS4")))); | 
    ||
517  | 
    shf_fprintf(shl_out, "%s%s", cp,  | 
    ||
518  | 
    t->vars[i + 1] ? " " : "\n");  | 
    ||
519  | 
    if (!t->vars[i + 1])  | 
    ||
520  | 
    shf_flush(shl_out);  | 
    ||
521  | 
    }  | 
    ||
522  | 
    2675171  | 
    typeset(cp, type_flags, 0, 0, 0);  | 
    |
523  | 
    ✓✓✓✗ | 
    2675183  | 
    if (bourne_function_call && !(type_flags & EXPORT))  | 
    
524  | 
    12  | 
    typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0);  | 
    |
525  | 
    }  | 
    ||
526  | 
    |||
527  | 
    ✓✓ | 5735838  | 
    	if ((cp = *ap) == NULL) { | 
    
528  | 
    2666352  | 
    rv = subst_exstat;  | 
    |
529  | 
    2666352  | 
    goto Leave;  | 
    |
530  | 
    ✓✓ | 3069486  | 
    	} else if (!tp) { | 
    
531  | 
    ✗✓✗✗ | 
    136471  | 
    		if (Flag(FRESTRICTED) && strchr(cp, '/')) { | 
    
532  | 
    warningf(true, "%s: restricted", cp);  | 
    ||
533  | 
    rv = 1;  | 
    ||
534  | 
    goto Leave;  | 
    ||
535  | 
    }  | 
    ||
536  | 
    136471  | 
    tp = findcom(cp, fcflags);  | 
    |
537  | 
    136471  | 
    }  | 
    |
538  | 
    |||
539  | 
    ✓✓✗✓ ✓  | 
    8140172  | 
    	switch (tp->type) { | 
    
540  | 
    case CSHELL: /* shell built-in */  | 
    ||
541  | 
    2459019  | 
    rv = call_builtin(tp, ap);  | 
    |
542  | 
    2459019  | 
    break;  | 
    |
543  | 
    |||
544  | 
    case CFUNC: /* function call */  | 
    ||
545  | 
    	    { | 
    ||
546  | 
    473996  | 
    volatile int old_xflag, old_inuse;  | 
    |
547  | 
    473996  | 
    const char *volatile old_kshname;  | 
    |
548  | 
    |||
549  | 
    ✗✓ | 473996  | 
    		if (!(tp->flag & ISSET)) { | 
    
550  | 
    struct tbl *ftp;  | 
    ||
551  | 
    |||
552  | 
    			if (!tp->u.fpath) { | 
    ||
553  | 
    				if (tp->u2.errno_) { | 
    ||
554  | 
    warningf(true,  | 
    ||
555  | 
    "%s: can't find function "  | 
    ||
556  | 
    "definition file - %s",  | 
    ||
557  | 
    cp, strerror(tp->u2.errno_));  | 
    ||
558  | 
    rv = 126;  | 
    ||
559  | 
    				} else { | 
    ||
560  | 
    warningf(true,  | 
    ||
561  | 
    "%s: can't find function "  | 
    ||
562  | 
    "definition file", cp);  | 
    ||
563  | 
    rv = 127;  | 
    ||
564  | 
    }  | 
    ||
565  | 
    break;  | 
    ||
566  | 
    }  | 
    ||
567  | 
    			if (include(tp->u.fpath, 0, NULL, 0) < 0) { | 
    ||
568  | 
    warningf(true,  | 
    ||
569  | 
    "%s: can't open function definition file %s - %s",  | 
    ||
570  | 
    cp, tp->u.fpath, strerror(errno));  | 
    ||
571  | 
    rv = 127;  | 
    ||
572  | 
    break;  | 
    ||
573  | 
    }  | 
    ||
574  | 
    if (!(ftp = findfunc(cp, hash(cp), false)) ||  | 
    ||
575  | 
    			    !(ftp->flag & ISSET)) { | 
    ||
576  | 
    warningf(true,  | 
    ||
577  | 
    "%s: function not defined by %s",  | 
    ||
578  | 
    cp, tp->u.fpath);  | 
    ||
579  | 
    rv = 127;  | 
    ||
580  | 
    break;  | 
    ||
581  | 
    }  | 
    ||
582  | 
    tp = ftp;  | 
    ||
583  | 
    }  | 
    ||
584  | 
    |||
585  | 
    /* ksh functions set $0 to function name, POSIX functions leave  | 
    ||
586  | 
    * $0 unchanged.  | 
    ||
587  | 
    */  | 
    ||
588  | 
    473996  | 
    old_kshname = kshname;  | 
    |
589  | 
    ✓✓ | 473996  | 
    if (tp->flag & FKSH)  | 
    
590  | 
    2440  | 
    kshname = ap[0];  | 
    |
591  | 
    else  | 
    ||
592  | 
    471556  | 
    ap[0] = (char *) kshname;  | 
    |
593  | 
    473996  | 
    genv->loc->argv = ap;  | 
    |
594  | 
    ✓✓ | 5128030  | 
    for (i = 0; *ap++ != NULL; i++)  | 
    
595  | 
    ;  | 
    ||
596  | 
    473996  | 
    genv->loc->argc = i - 1;  | 
    |
597  | 
    /* ksh-style functions handle getopts sanely,  | 
    ||
598  | 
    * bourne/posix functions are insane...  | 
    ||
599  | 
    */  | 
    ||
600  | 
    ✓✓ | 473996  | 
    		if (tp->flag & FKSH) { | 
    
601  | 
    2440  | 
    genv->loc->flags |= BF_DOGETOPTS;  | 
    |
602  | 
    2440  | 
    genv->loc->getopts_state = user_opt;  | 
    |
603  | 
    2440  | 
    getopts_reset(1);  | 
    |
604  | 
    2440  | 
    }  | 
    |
605  | 
    |||
606  | 
    518310  | 
    old_xflag = Flag(FXTRACE);  | 
    |
607  | 
    518310  | 
    Flag(FXTRACE) = tp->flag & TRACE ? true : false;  | 
    |
608  | 
    |||
609  | 
    518310  | 
    old_inuse = tp->flag & FINUSE;  | 
    |
610  | 
    518310  | 
    tp->flag |= FINUSE;  | 
    |
611  | 
    |||
612  | 
    518310  | 
    genv->type = E_FUNC;  | 
    |
613  | 
    518310  | 
    i = sigsetjmp(genv->jbuf, 0);  | 
    |
614  | 
    ✓✓ | 518310  | 
    		if (i == 0) { | 
    
615  | 
    /* seems odd to pass XERROK here, but at&t ksh does */  | 
    ||
616  | 
    473996  | 
    exstat = execute(tp->val.t, flags & XERROK, xerrok);  | 
    |
617  | 
    i = LRETURN;  | 
    ||
618  | 
    473996  | 
    }  | 
    |
619  | 
    458027  | 
    kshname = old_kshname;  | 
    |
620  | 
    458027  | 
    Flag(FXTRACE) = old_xflag;  | 
    |
621  | 
    458027  | 
    tp->flag = (tp->flag & ~FINUSE) | old_inuse;  | 
    |
622  | 
    /* Were we deleted while executing? If so, free the execution  | 
    ||
623  | 
    * tree. todo: Unfortunately, the table entry is never re-used  | 
    ||
624  | 
    * until the lookup table is expanded.  | 
    ||
625  | 
    */  | 
    ||
626  | 
    ✗✓ | 458027  | 
    		if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) { | 
    
627  | 
    			if (tp->flag & ALLOC) { | 
    ||
628  | 
    tp->flag &= ~ALLOC;  | 
    ||
629  | 
    tfree(tp->val.t, tp->areap);  | 
    ||
630  | 
    }  | 
    ||
631  | 
    tp->flag = 0;  | 
    ||
632  | 
    }  | 
    ||
633  | 
    ✗✓✗✗ ✗✓✗  | 
    458027  | 
    		switch (i) { | 
    
634  | 
    case LRETURN:  | 
    ||
635  | 
    case LERROR:  | 
    ||
636  | 
    457761  | 
    rv = exstat;  | 
    |
637  | 
    457761  | 
    break;  | 
    |
638  | 
    case LINTR:  | 
    ||
639  | 
    case LEXIT:  | 
    ||
640  | 
    case LLEAVE:  | 
    ||
641  | 
    case LSHELL:  | 
    ||
642  | 
    quitenv(NULL);  | 
    ||
643  | 
    unwind(i);  | 
    ||
644  | 
    /* NOTREACHED */  | 
    ||
645  | 
    default:  | 
    ||
646  | 
    quitenv(NULL);  | 
    ||
647  | 
    internal_errorf(1, "CFUNC %d", i);  | 
    ||
648  | 
    }  | 
    ||
649  | 
    457761  | 
    break;  | 
    |
650  | 
    457761  | 
    }  | 
    |
651  | 
    |||
652  | 
    case CEXEC: /* executable command */  | 
    ||
653  | 
    case CTALIAS: /* tracked alias */  | 
    ||
654  | 
    ✓✓ | 136471  | 
    		if (!(tp->flag&ISSET)) { | 
    
655  | 
    /* errno_ will be set if the named command was found  | 
    ||
656  | 
    * but could not be executed (permissions, no execute  | 
    ||
657  | 
    * bit, directory, etc). Print out a (hopefully)  | 
    ||
658  | 
    * useful error message and set the exit status to 126.  | 
    ||
659  | 
    */  | 
    ||
660  | 
    ✗✓ | 63  | 
    			if (tp->u2.errno_) { | 
    
661  | 
    warningf(true, "%s: cannot execute - %s", cp,  | 
    ||
662  | 
    strerror(tp->u2.errno_));  | 
    ||
663  | 
    rv = 126; /* POSIX */  | 
    ||
664  | 
    			} else { | 
    ||
665  | 
    63  | 
    warningf(true, "%s: not found", cp);  | 
    |
666  | 
    63  | 
    rv = 127;  | 
    |
667  | 
    }  | 
    ||
668  | 
    break;  | 
    ||
669  | 
    }  | 
    ||
670  | 
    |||
671  | 
    ✓✓ | 136408  | 
    		if (!Flag(FSH)) { | 
    
672  | 
    /* set $_ to program's full path */  | 
    ||
673  | 
    /* setstr() can't fail here */  | 
    ||
674  | 
    962  | 
    			setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0), | 
    |
675  | 
    481  | 
    tp->val.s, KSH_RETURN_ERROR);  | 
    |
676  | 
    481  | 
    }  | 
    |
677  | 
    |||
678  | 
    ✗✓ | 136408  | 
    		if (flags&XEXEC) { | 
    
679  | 
    j_exit();  | 
    ||
680  | 
    			if (!(flags&XBGND) || Flag(FMONITOR)) { | 
    ||
681  | 
    setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);  | 
    ||
682  | 
    setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);  | 
    ||
683  | 
    }  | 
    ||
684  | 
    }  | 
    ||
685  | 
    |||
686  | 
    /* to fork we set up a TEXEC node and call execute */  | 
    ||
687  | 
    136408  | 
    texec.type = TEXEC;  | 
    |
688  | 
    136408  | 
    texec.left = t; /* for tprint */  | 
    |
689  | 
    136408  | 
    texec.str = tp->val.s;  | 
    |
690  | 
    136408  | 
    texec.args = ap;  | 
    |
691  | 
    136408  | 
    rv = exchild(&texec, flags, xerrok, -1);  | 
    |
692  | 
    136408  | 
    break;  | 
    |
693  | 
    }  | 
    ||
694  | 
    Leave:  | 
    ||
695  | 
    ✓✓ | 5673241  | 
    	if (flags & XEXEC) { | 
    
696  | 
    exstat = rv;  | 
    ||
697  | 
    unwind(LLEAVE);  | 
    ||
698  | 
    }  | 
    ||
699  | 
    5641204  | 
    return rv;  | 
    |
700  | 
    5641204  | 
    }  | 
    |
701  | 
    |||
702  | 
    static void  | 
    ||
703  | 
    scriptexec(struct op *tp, char **ap)  | 
    ||
704  | 
    { | 
    ||
705  | 
    char *shell;  | 
    ||
706  | 
    |||
707  | 
    	shell = str_val(global("EXECSHELL")); | 
    ||
708  | 
    if (shell && *shell)  | 
    ||
709  | 
    shell = search(shell, path, X_OK, NULL);  | 
    ||
710  | 
    if (!shell || !*shell)  | 
    ||
711  | 
    shell = _PATH_BSHELL;  | 
    ||
712  | 
    |||
713  | 
    *tp->args-- = tp->str;  | 
    ||
714  | 
    *tp->args = shell;  | 
    ||
715  | 
    |||
716  | 
    execve(tp->args[0], tp->args, ap);  | 
    ||
717  | 
    |||
718  | 
    /* report both the program that was run and the bogus shell */  | 
    ||
719  | 
    	errorf("%s: %s: %s", tp->str, shell, strerror(errno)); | 
    ||
720  | 
    }  | 
    ||
721  | 
    |||
722  | 
    int  | 
    ||
723  | 
    shcomexec(char **wp)  | 
    ||
724  | 
    { | 
    ||
725  | 
    struct tbl *tp;  | 
    ||
726  | 
    |||
727  | 
    1467990  | 
    tp = ktsearch(&builtins, *wp, hash(*wp));  | 
    |
728  | 
    ✗✓ | 733995  | 
    if (tp == NULL)  | 
    
729  | 
    internal_errorf(1, "shcomexec: %s", *wp);  | 
    ||
730  | 
    733995  | 
    return call_builtin(tp, wp);  | 
    |
731  | 
    }  | 
    ||
732  | 
    |||
733  | 
    /*  | 
    ||
734  | 
    * Search function tables for a function. If create set, a table entry  | 
    ||
735  | 
    * is created if none is found.  | 
    ||
736  | 
    */  | 
    ||
737  | 
    struct tbl *  | 
    ||
738  | 
    findfunc(const char *name, unsigned int h, int create)  | 
    ||
739  | 
    { | 
    ||
740  | 
    struct block *l;  | 
    ||
741  | 
    struct tbl *tp = NULL;  | 
    ||
742  | 
    |||
743  | 
    ✓✓ | 19236833  | 
    	for (l = genv->loc; l; l = l->next) { | 
    
744  | 
    6121360  | 
    tp = ktsearch(&l->funs, name, h);  | 
    |
745  | 
    ✓✓ | 6121360  | 
    if (tp)  | 
    
746  | 
    break;  | 
    ||
747  | 
    ✓✓ | 5647302  | 
    		if (!l->next && create) { | 
    
748  | 
    26156  | 
    tp = ktenter(&l->funs, name, h);  | 
    |
749  | 
    26156  | 
    tp->flag = DEFINED;  | 
    |
750  | 
    26156  | 
    tp->type = CFUNC;  | 
    |
751  | 
    26156  | 
    tp->val.t = NULL;  | 
    |
752  | 
    26156  | 
    break;  | 
    |
753  | 
    }  | 
    ||
754  | 
    }  | 
    ||
755  | 
    2664847  | 
    return tp;  | 
    |
756  | 
    }  | 
    ||
757  | 
    |||
758  | 
    /*  | 
    ||
759  | 
    * define function. Returns 1 if function is being undefined (t == 0) and  | 
    ||
760  | 
    * function did not exist, returns 0 otherwise.  | 
    ||
761  | 
    */  | 
    ||
762  | 
    int  | 
    ||
763  | 
    define(const char *name, struct op *t)  | 
    ||
764  | 
    { | 
    ||
765  | 
    struct tbl *tp;  | 
    ||
766  | 
    int was_set = 0;  | 
    ||
767  | 
    |||
768  | 
    52348  | 
    	while (1) { | 
    |
769  | 
    26174  | 
    tp = findfunc(name, hash(name), true);  | 
    |
770  | 
    |||
771  | 
    ✓✓ | 26174  | 
    if (tp->flag & ISSET)  | 
    
772  | 
    18  | 
    was_set = 1;  | 
    |
773  | 
    /* If this function is currently being executed, we zap this  | 
    ||
774  | 
    * table entry so findfunc() won't see it  | 
    ||
775  | 
    */  | 
    ||
776  | 
    ✗✓ | 26174  | 
    		if (tp->flag & FINUSE) { | 
    
777  | 
    tp->name[0] = '\0';  | 
    ||
778  | 
    tp->flag &= ~DEFINED; /* ensure it won't be found */  | 
    ||
779  | 
    tp->flag |= FDELETE;  | 
    ||
780  | 
    } else  | 
    ||
781  | 
    break;  | 
    ||
782  | 
    }  | 
    ||
783  | 
    |||
784  | 
    ✓✓ | 26174  | 
    	if (tp->flag & ALLOC) { | 
    
785  | 
    18  | 
    tp->flag &= ~(ISSET|ALLOC);  | 
    |
786  | 
    18  | 
    tfree(tp->val.t, tp->areap);  | 
    |
787  | 
    18  | 
    }  | 
    |
788  | 
    |||
789  | 
    ✓✓ | 26174  | 
    	if (t == NULL) {		/* undefine */ | 
    
790  | 
    6  | 
    ktdelete(tp);  | 
    |
791  | 
    6  | 
    return was_set ? 0 : 1;  | 
    |
792  | 
    }  | 
    ||
793  | 
    |||
794  | 
    26168  | 
    tp->val.t = tcopy(t->left, tp->areap);  | 
    |
795  | 
    26168  | 
    tp->flag |= (ISSET|ALLOC);  | 
    |
796  | 
    ✓✓ | 26168  | 
    if (t->u.ksh_func)  | 
    
797  | 
    60  | 
    tp->flag |= FKSH;  | 
    |
798  | 
    |||
799  | 
    26168  | 
    return 0;  | 
    |
800  | 
    26174  | 
    }  | 
    |
801  | 
    |||
802  | 
    /*  | 
    ||
803  | 
    * add builtin  | 
    ||
804  | 
    */  | 
    ||
805  | 
    void  | 
    ||
806  | 
    builtin(const char *name, int (*func) (char **))  | 
    ||
807  | 
    { | 
    ||
808  | 
    struct tbl *tp;  | 
    ||
809  | 
    int flag;  | 
    ||
810  | 
    |||
811  | 
    /* see if any flags should be set for this builtin */  | 
    ||
812  | 
    13828716  | 
    	for (flag = 0; ; name++) { | 
    |
813  | 
    ✓✓ | 9428670  | 
    if (*name == '=') /* command does variable assignment */  | 
    
814  | 
    1885734  | 
    flag |= KEEPASN;  | 
    |
815  | 
    ✓✓ | 7542936  | 
    else if (*name == '*') /* POSIX special builtin */  | 
    
816  | 
    1571445  | 
    flag |= SPEC_BI;  | 
    |
817  | 
    ✓✓ | 5971491  | 
    else if (*name == '+') /* POSIX regular builtin */  | 
    
818  | 
    1571445  | 
    flag |= REG_BI;  | 
    |
819  | 
    else  | 
    ||
820  | 
    break;  | 
    ||
821  | 
    }  | 
    ||
822  | 
    |||
823  | 
    4400046  | 
    tp = ktenter(&builtins, name, hash(name));  | 
    |
824  | 
    4400046  | 
    tp->flag = DEFINED | flag;  | 
    |
825  | 
    4400046  | 
    tp->type = CSHELL;  | 
    |
826  | 
    4400046  | 
    tp->val.f = func;  | 
    |
827  | 
    4400046  | 
    }  | 
    |
828  | 
    |||
829  | 
    /*  | 
    ||
830  | 
    * find command  | 
    ||
831  | 
    * either function, hashed command, or built-in (in that order)  | 
    ||
832  | 
    */  | 
    ||
833  | 
    struct tbl *  | 
    ||
834  | 
    findcom(const char *name, int flags)  | 
    ||
835  | 
    { | 
    ||
836  | 
    static struct tbl temp;  | 
    ||
837  | 
    6412468  | 
    unsigned int h = hash(name);  | 
    |
838  | 
    struct tbl *tp = NULL, *tbi;  | 
    ||
839  | 
    3206234  | 
    int insert = Flag(FTRACKALL); /* insert if not found */  | 
    |
840  | 
    char *fpath; /* for function autoloading */  | 
    ||
841  | 
    char *npath;  | 
    ||
842  | 
    |||
843  | 
    ✓✓ | 3206234  | 
    	if (strchr(name, '/') != NULL) { | 
    
844  | 
    insert = 0;  | 
    ||
845  | 
    /* prevent FPATH search below */  | 
    ||
846  | 
    140962  | 
    flags &= ~FC_FUNC;  | 
    |
847  | 
    140962  | 
    goto Search;  | 
    |
848  | 
    }  | 
    ||
849  | 
    ✓✓ | 9195744  | 
    tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL;  | 
    
850  | 
    /* POSIX says special builtins first, then functions, then  | 
    ||
851  | 
    * POSIX regular builtins, then search path...  | 
    ||
852  | 
    */  | 
    ||
853  | 
    ✓✓✓✓ | 
    5524417  | 
    if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI))  | 
    
854  | 
    426527  | 
    tp = tbi;  | 
    |
855  | 
    ✓✓✓✓ | 
    5704017  | 
    	if (!tp && (flags & FC_FUNC)) { | 
    
856  | 
    2638673  | 
    tp = findfunc(name, h, false);  | 
    |
857  | 
    ✓✓✗✓ | 
    3112713  | 
    		if (tp && !(tp->flag & ISSET)) { | 
    
858  | 
    			if ((fpath = str_val(global("FPATH"))) == null) { | 
    ||
859  | 
    tp->u.fpath = NULL;  | 
    ||
860  | 
    tp->u2.errno_ = 0;  | 
    ||
861  | 
    } else  | 
    ||
862  | 
    tp->u.fpath = search(name, fpath, R_OK,  | 
    ||
863  | 
    &tp->u2.errno_);  | 
    ||
864  | 
    }  | 
    ||
865  | 
    }  | 
    ||
866  | 
    ✓✓✓✓ ✓✓  | 
    7262547  | 
    if (!tp && (flags & FC_REGBI) && tbi && (tbi->flag & REG_BI))  | 
    
867  | 
    30101  | 
    tp = tbi;  | 
    |
868  | 
    /* todo: posix says non-special/non-regular builtins must  | 
    ||
869  | 
    * be triggered by some user-controllable means like a  | 
    ||
870  | 
    * special directory in PATH. Requires modifications to  | 
    ||
871  | 
    * the search() function. Tracked aliases should be  | 
    ||
872  | 
    * modified to allow tracking of builtin commands.  | 
    ||
873  | 
    * This should be under control of the FPOSIX flag.  | 
    ||
874  | 
    * If this is changed, also change c_whence...  | 
    ||
875  | 
    */  | 
    ||
876  | 
    ✓✓✓✓ | 
    5199876  | 
    if (!tp && (flags & FC_UNREGBI) && tbi)  | 
    
877  | 
    2002469  | 
    tp = tbi;  | 
    |
878  | 
    ✓✓✓✓ ✓✓  | 
    3263525  | 
    	if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) { | 
    
879  | 
    66106  | 
    tp = ktsearch(&taliases, name, h);  | 
    |
880  | 
    ✓✓✓✓ ✗✓  | 
    127906  | 
    		if (tp && (tp->flag & ISSET) && access(tp->val.s, X_OK) != 0) { | 
    
881  | 
    			if (tp->flag & ALLOC) { | 
    ||
882  | 
    tp->flag &= ~ALLOC;  | 
    ||
883  | 
    afree(tp->val.s, APERM);  | 
    ||
884  | 
    }  | 
    ||
885  | 
    tp->flag &= ~ISSET;  | 
    ||
886  | 
    }  | 
    ||
887  | 
    }  | 
    ||
888  | 
    |||
889  | 
    Search:  | 
    ||
890  | 
    ✓✓✓✓ ✓✓✓✓  | 
    6498292  | 
    if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) &&  | 
    
891  | 
    260505  | 
    	    (flags & FC_PATH)) { | 
    |
892  | 
    ✓✓ | 124022  | 
    		if (!tp) { | 
    
893  | 
    ✓✓✓✓ | 
    104312  | 
    			if (insert && !(flags & FC_DEFPATH)) { | 
    
894  | 
    16894  | 
    tp = ktenter(&taliases, name, h);  | 
    |
895  | 
    tp->type = CTALIAS;  | 
    ||
896  | 
    16894  | 
    			} else { | 
    |
897  | 
    tp = &temp;  | 
    ||
898  | 
    tp->type = CEXEC;  | 
    ||
899  | 
    }  | 
    ||
900  | 
    87406  | 
    tp->flag = DEFINED; /* make ~ISSET */  | 
    |
901  | 
    87406  | 
    }  | 
    |
902  | 
    248044  | 
    npath = search(name, flags & FC_DEFPATH ? def_path : path,  | 
    |
903  | 
    124022  | 
    X_OK, &tp->u2.errno_);  | 
    |
904  | 
    ✓✓ | 124022  | 
    		if (npath) { | 
    
905  | 
    ✓✓ | 123877  | 
    			if (tp == &temp) { | 
    
906  | 
    70470  | 
    tp->val.s = npath;  | 
    |
907  | 
    70470  | 
    			} else { | 
    |
908  | 
    53407  | 
    tp->val.s = str_save(npath, APERM);  | 
    |
909  | 
    ✓✗ | 53407  | 
    if (npath != name)  | 
    
910  | 
    53407  | 
    afree(npath, ATEMP);  | 
    |
911  | 
    }  | 
    ||
912  | 
    123877  | 
    tp->flag |= ISSET|ALLOC;  | 
    |
913  | 
    ✓✓✗✗ | 
    124022  | 
    } else if ((flags & FC_FUNC) &&  | 
    
914  | 
    ✗✓ | 55  | 
    		    (fpath = str_val(global("FPATH"))) != null && | 
    
915  | 
    (npath = search(name, fpath, R_OK,  | 
    ||
916  | 
    		    &tp->u2.errno_)) != NULL) { | 
    ||
917  | 
    /* An undocumented feature of at&t ksh is that it  | 
    ||
918  | 
    * searches FPATH if a command is not found, even  | 
    ||
919  | 
    * if the command hasn't been set up as an autoloaded  | 
    ||
920  | 
    * function (ie, no typeset -uf).  | 
    ||
921  | 
    */  | 
    ||
922  | 
    tp = &temp;  | 
    ||
923  | 
    tp->type = CFUNC;  | 
    ||
924  | 
    tp->flag = DEFINED; /* make ~ISSET */  | 
    ||
925  | 
    tp->u.fpath = npath;  | 
    ||
926  | 
    }  | 
    ||
927  | 
    }  | 
    ||
928  | 
    3206234  | 
    return tp;  | 
    |
929  | 
    }  | 
    ||
930  | 
    |||
931  | 
    /*  | 
    ||
932  | 
    * flush executable commands with relative paths  | 
    ||
933  | 
    */  | 
    ||
934  | 
    void  | 
    ||
935  | 
    flushcom(int all) /* just relative or all */  | 
    ||
936  | 
    { | 
    ||
937  | 
    struct tbl *tp;  | 
    ||
938  | 
    465698  | 
    struct tstate ts;  | 
    |
939  | 
    |||
940  | 
    ✓✓ | 1305528  | 
    for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; )  | 
    
941  | 
    ✓✓✓✓ ✗✓  | 
    420209  | 
    		if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) { | 
    
942  | 
    ✓✗ | 32  | 
    			if (tp->flag&ALLOC) { | 
    
943  | 
    32  | 
    tp->flag &= ~(ALLOC|ISSET);  | 
    |
944  | 
    32  | 
    afree(tp->val.s, APERM);  | 
    |
945  | 
    32  | 
    }  | 
    |
946  | 
    32  | 
    tp->flag &= ~ISSET;  | 
    |
947  | 
    32  | 
    }  | 
    |
948  | 
    232849  | 
    }  | 
    |
949  | 
    |||
950  | 
    /* Check if path is something we want to find. Returns -1 for failure. */  | 
    ||
951  | 
    int  | 
    ||
952  | 
    search_access(const char *path, int mode,  | 
    ||
953  | 
    int *errnop) /* set if candidate found, but not suitable */  | 
    ||
954  | 
    { | 
    ||
955  | 
    int ret, err = 0;  | 
    ||
956  | 
    545672  | 
    struct stat statb;  | 
    |
957  | 
    |||
958  | 
    ✓✓ | 272836  | 
    if (stat(path, &statb) < 0)  | 
    
959  | 
    147570  | 
    return -1;  | 
    |
960  | 
    125266  | 
    ret = access(path, mode);  | 
    |
961  | 
    ✗✓ | 125266  | 
    if (ret < 0)  | 
    
962  | 
    err = errno; /* File exists, but we can't access it */  | 
    ||
963  | 
    ✓✓✓✗ ✗✓  | 
    373026  | 
    else if (mode == X_OK && (!S_ISREG(statb.st_mode) ||  | 
    
964  | 
    123880  | 
    	    !(statb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)))) { | 
    |
965  | 
    /* This 'cause access() says root can execute everything */  | 
    ||
966  | 
    ret = -1;  | 
    ||
967  | 
    err = S_ISDIR(statb.st_mode) ? EISDIR : EACCES;  | 
    ||
968  | 
    }  | 
    ||
969  | 
    ✗✓✗✗ | 
    125266  | 
    if (err && errnop && !*errnop)  | 
    
970  | 
    *errnop = err;  | 
    ||
971  | 
    125266  | 
    return ret;  | 
    |
972  | 
    272836  | 
    }  | 
    |
973  | 
    |||
974  | 
    /*  | 
    ||
975  | 
    * search for command with PATH  | 
    ||
976  | 
    */  | 
    ||
977  | 
    char *  | 
    ||
978  | 
    search(const char *name, const char *path,  | 
    ||
979  | 
    int mode, /* R_OK or X_OK */  | 
    ||
980  | 
    int *errnop) /* set if candidate found, but not suitable */  | 
    ||
981  | 
    { | 
    ||
982  | 
    const char *sp, *p;  | 
    ||
983  | 
    char *xp;  | 
    ||
984  | 
    250840  | 
    XString xs;  | 
    |
985  | 
    int namelen;  | 
    ||
986  | 
    |||
987  | 
    ✓✗ | 125420  | 
    if (errnop)  | 
    
988  | 
    125420  | 
    *errnop = 0;  | 
    |
989  | 
    ✓✓ | 125420  | 
    	if (strchr(name, '/')) { | 
    
990  | 
    ✓✓ | 71894  | 
    if (search_access(name, mode, errnop) == 0)  | 
    
991  | 
    71840  | 
    return (char *) name;  | 
    |
992  | 
    54  | 
    return NULL;  | 
    |
993  | 
    }  | 
    ||
994  | 
    |||
995  | 
    53526  | 
    namelen = strlen(name) + 1;  | 
    |
996  | 
    53526  | 
    Xinit(xs, xp, 128, ATEMP);  | 
    |
997  | 
    |||
998  | 
    sp = path;  | 
    ||
999  | 
    ✓✓ | 402084  | 
    	while (sp != NULL) { | 
    
1000  | 
    xp = Xstring(xs, xp);  | 
    ||
1001  | 
    ✓✓ | 200939  | 
    if (!(p = strchr(sp, ':')))  | 
    
1002  | 
    216  | 
    p = sp + strlen(sp);  | 
    |
1003  | 
    ✓✗ | 200939  | 
    		if (p != sp) { | 
    
1004  | 
    ✗✓ | 200939  | 
    XcheckN(xs, xp, p - sp);  | 
    
1005  | 
    200939  | 
    memcpy(xp, sp, p - sp);  | 
    |
1006  | 
    200939  | 
    xp += p - sp;  | 
    |
1007  | 
    200939  | 
    *xp++ = '/';  | 
    |
1008  | 
    200939  | 
    }  | 
    |
1009  | 
    sp = p;  | 
    ||
1010  | 
    ✗✓ | 200939  | 
    XcheckN(xs, xp, namelen);  | 
    
1011  | 
    200939  | 
    memcpy(xp, name, namelen);  | 
    |
1012  | 
    ✓✓ | 200939  | 
    if (search_access(Xstring(xs, xp), mode, errnop) == 0)  | 
    
1013  | 
    53423  | 
    return Xclose(xs, xp + namelen);  | 
    |
1014  | 
    147516  | 
    if (*sp++ == '\0')  | 
    |
1015  | 
    sp = NULL;  | 
    ||
1016  | 
    }  | 
    ||
1017  | 
    103  | 
    Xfree(xs, xp);  | 
    |
1018  | 
    103  | 
    return NULL;  | 
    |
1019  | 
    125420  | 
    }  | 
    |
1020  | 
    |||
1021  | 
    static int  | 
    ||
1022  | 
    call_builtin(struct tbl *tp, char **wp)  | 
    ||
1023  | 
    { | 
    ||
1024  | 
    int rv;  | 
    ||
1025  | 
    |||
1026  | 
    6386028  | 
    builtin_argv0 = wp[0];  | 
    |
1027  | 
    3193014  | 
    builtin_flag = tp->flag;  | 
    |
1028  | 
    3193014  | 
    shf_reopen(1, SHF_WR, shl_stdout);  | 
    |
1029  | 
    3193014  | 
    shl_stdout_ok = 1;  | 
    |
1030  | 
    3193014  | 
    ksh_getopt_reset(&builtin_opt, GF_ERROR);  | 
    |
1031  | 
    3193014  | 
    rv = (*tp->val.f)(wp);  | 
    |
1032  | 
    3193014  | 
    shf_flush(shl_stdout);  | 
    |
1033  | 
    3193014  | 
    shl_stdout_ok = 0;  | 
    |
1034  | 
    3193014  | 
    builtin_flag = 0;  | 
    |
1035  | 
    3193014  | 
    builtin_argv0 = NULL;  | 
    |
1036  | 
    3193014  | 
    return rv;  | 
    |
1037  | 
    }  | 
    ||
1038  | 
    |||
1039  | 
    /*  | 
    ||
1040  | 
    * set up redirection, saving old fd's in e->savefd  | 
    ||
1041  | 
    */  | 
    ||
1042  | 
    static int  | 
    ||
1043  | 
    iosetup(struct ioword *iop, struct tbl *tp)  | 
    ||
1044  | 
    { | 
    ||
1045  | 
    int u = -1;  | 
    ||
1046  | 
    234644  | 
    char *cp = iop->name;  | 
    |
1047  | 
    117322  | 
    int iotype = iop->flag & IOTYPE;  | 
    |
1048  | 
    int do_open = 1, do_close = 0, flags = 0;  | 
    ||
1049  | 
    117322  | 
    struct ioword iotmp;  | 
    |
1050  | 
    117322  | 
    struct stat statb;  | 
    |
1051  | 
    |||
1052  | 
    ✓✓ | 117322  | 
    if (iotype != IOHERE)  | 
    
1053  | 
    115244  | 
    cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));  | 
    |
1054  | 
    |||
1055  | 
    /* Used for tracing and error messages to print expanded cp */  | 
    ||
1056  | 
    117322  | 
    iotmp = *iop;  | 
    |
1057  | 
    117322  | 
    iotmp.name = (iotype == IOHERE) ? NULL : cp;  | 
    |
1058  | 
    117322  | 
    iotmp.flag |= IONAMEXP;  | 
    |
1059  | 
    |||
1060  | 
    ✗✓ | 117322  | 
    if (Flag(FXTRACE))  | 
    
1061  | 
    		shellf("%s%s\n", | 
    ||
1062  | 
    		    PS4_SUBSTITUTE(str_val(global("PS4"))), | 
    ||
1063  | 
    snptreef(NULL, 32, "%R", &iotmp));  | 
    ||
1064  | 
    |||
1065  | 
    ✓✓✓✗ ✓✓✓  | 
    234630  | 
    	switch (iotype) { | 
    
1066  | 
    case IOREAD:  | 
    ||
1067  | 
    flags = O_RDONLY;  | 
    ||
1068  | 
    8554  | 
    break;  | 
    |
1069  | 
    |||
1070  | 
    case IOCAT:  | 
    ||
1071  | 
    flags = O_WRONLY | O_APPEND | O_CREAT;  | 
    ||
1072  | 
    51424  | 
    break;  | 
    |
1073  | 
    |||
1074  | 
    case IOWRITE:  | 
    ||
1075  | 
    flags = O_WRONLY | O_CREAT | O_TRUNC;  | 
    ||
1076  | 
    /* The stat() is here to allow redirections to  | 
    ||
1077  | 
    * things like /dev/null without error.  | 
    ||
1078  | 
    */  | 
    ||
1079  | 
    ✗✓✗✗ ✗✗  | 
    14713  | 
    if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&  | 
    
1080  | 
    (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))  | 
    ||
1081  | 
    flags |= O_EXCL;  | 
    ||
1082  | 
    break;  | 
    ||
1083  | 
    |||
1084  | 
    case IORDWR:  | 
    ||
1085  | 
    flags = O_RDWR | O_CREAT;  | 
    ||
1086  | 
    break;  | 
    ||
1087  | 
    |||
1088  | 
    case IOHERE:  | 
    ||
1089  | 
    do_open = 0;  | 
    ||
1090  | 
    /* herein() returns -2 if error has been printed */  | 
    ||
1091  | 
    2078  | 
    u = herein(iop->heredoc, iop->flag & IOEVAL);  | 
    |
1092  | 
    /* cp may have wrong name */  | 
    ||
1093  | 
    2078  | 
    break;  | 
    |
1094  | 
    |||
1095  | 
    case IODUP:  | 
    ||
1096  | 
    	    { | 
    ||
1097  | 
    40553  | 
    const char *emsg;  | 
    |
1098  | 
    |||
1099  | 
    do_open = 0;  | 
    ||
1100  | 
    ✗✓✗✗ | 
    40553  | 
    		if (*cp == '-' && !cp[1]) { | 
    
1101  | 
    u = 1009; /* prevent error return below */  | 
    ||
1102  | 
    do_close = 1;  | 
    ||
1103  | 
    ✗✓ | 81106  | 
    } else if ((u = check_fd(cp,  | 
    
1104  | 
    40553  | 
    X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),  | 
    |
1105  | 
    40553  | 
    		    &emsg)) < 0) { | 
    |
1106  | 
    warningf(true, "%s: %s",  | 
    ||
1107  | 
    snptreef(NULL, 32, "%R", &iotmp), emsg);  | 
    ||
1108  | 
    return -1;  | 
    ||
1109  | 
    }  | 
    ||
1110  | 
    ✓✓ | 40553  | 
    if (u == iop->unit)  | 
    
1111  | 
    8  | 
    return 0; /* "dup from" == "dup to" */  | 
    |
1112  | 
    40545  | 
    break;  | 
    |
1113  | 
    ✓✓ | 40553  | 
    }  | 
    
1114  | 
    }  | 
    ||
1115  | 
    |||
1116  | 
    ✓✓ | 117308  | 
    	if (do_open) { | 
    
1117  | 
    ✗✓✗✗ | 
    74691  | 
    		if (Flag(FRESTRICTED) && (flags & O_CREAT)) { | 
    
1118  | 
    warningf(true, "%s: restricted", cp);  | 
    ||
1119  | 
    return -1;  | 
    ||
1120  | 
    }  | 
    ||
1121  | 
    74691  | 
    u = open(cp, flags, 0666);  | 
    |
1122  | 
    74691  | 
    }  | 
    |
1123  | 
    ✗✓ | 117308  | 
    	if (u < 0) { | 
    
1124  | 
    /* herein() may already have printed message */  | 
    ||
1125  | 
    if (u == -1)  | 
    ||
1126  | 
    warningf(true, "cannot %s %s: %s",  | 
    ||
1127  | 
    iotype == IODUP ? "dup" :  | 
    ||
1128  | 
    (iotype == IOREAD || iotype == IOHERE) ?  | 
    ||
1129  | 
    "open" : "create", cp, strerror(errno));  | 
    ||
1130  | 
    return -1;  | 
    ||
1131  | 
    }  | 
    ||
1132  | 
    /* Do not save if it has already been redirected (i.e. "cat >x >y"). */  | 
    ||
1133  | 
    ✓✓ | 117308  | 
    	if (genv->savefd[iop->unit] == 0) { | 
    
1134  | 
    /* If these are the same, it means unit was previously closed */  | 
    ||
1135  | 
    ✓✓ | 117302  | 
    if (u == iop->unit)  | 
    
1136  | 
    1  | 
    genv->savefd[iop->unit] = -1;  | 
    |
1137  | 
    else  | 
    ||
1138  | 
    /* c_exec() assumes e->savefd[fd] set for any  | 
    ||
1139  | 
    * redirections. Ask savefd() not to close iop->unit;  | 
    ||
1140  | 
    * this allows error messages to be seen if iop->unit  | 
    ||
1141  | 
    * is 2; also means we can't lose the fd (eg, both  | 
    ||
1142  | 
    * dup2 below and dup2 in restfd() failing).  | 
    ||
1143  | 
    */  | 
    ||
1144  | 
    117301  | 
    genv->savefd[iop->unit] = savefd(iop->unit);  | 
    |
1145  | 
    117302  | 
    }  | 
    |
1146  | 
    |||
1147  | 
    ✗✓ | 117308  | 
    if (do_close)  | 
    
1148  | 
    close(iop->unit);  | 
    ||
1149  | 
    ✓✓ | 117308  | 
    	else if (u != iop->unit) { | 
    
1150  | 
    ✗✓ | 117307  | 
    		if (ksh_dup2(u, iop->unit, true) < 0) { | 
    
1151  | 
    warningf(true,  | 
    ||
1152  | 
    "could not finish (dup) redirection %s: %s",  | 
    ||
1153  | 
    snptreef(NULL, 32, "%R", &iotmp),  | 
    ||
1154  | 
    strerror(errno));  | 
    ||
1155  | 
    if (iotype != IODUP)  | 
    ||
1156  | 
    close(u);  | 
    ||
1157  | 
    return -1;  | 
    ||
1158  | 
    }  | 
    ||
1159  | 
    ✓✓ | 117307  | 
    if (iotype != IODUP)  | 
    
1160  | 
    76762  | 
    close(u);  | 
    |
1161  | 
    /* Touching any co-process fd in an empty exec  | 
    ||
1162  | 
    * causes the shell to close its copies  | 
    ||
1163  | 
    */  | 
    ||
1164  | 
    ✓✓✓✓ ✓✓  | 
    120443  | 
    		else if (tp && tp->type == CSHELL && tp->val.f == c_exec) { | 
    
1165  | 
    ✗✓ | 39298  | 
    if (iop->flag & IORDUP) /* possible exec <&p */  | 
    
1166  | 
    coproc_read_close(u);  | 
    ||
1167  | 
    else /* possible exec >&p */  | 
    ||
1168  | 
    39298  | 
    coproc_write_close(u);  | 
    |
1169  | 
    }  | 
    ||
1170  | 
    }  | 
    ||
1171  | 
    ✓✓ | 117308  | 
    if (u == 2) /* Clear any write errors */  | 
    
1172  | 
    1456  | 
    shf_reopen(2, SHF_WR, shl_out);  | 
    |
1173  | 
    117308  | 
    return 0;  | 
    |
1174  | 
    117316  | 
    }  | 
    |
1175  | 
    |||
1176  | 
    /*  | 
    ||
1177  | 
    * open here document temp file.  | 
    ||
1178  | 
    * if unquoted here, expand here temp file into second temp file.  | 
    ||
1179  | 
    */  | 
    ||
1180  | 
    static int  | 
    ||
1181  | 
    herein(const char *content, int sub)  | 
    ||
1182  | 
    { | 
    ||
1183  | 
    4156  | 
    volatile int fd = -1;  | 
    |
1184  | 
    2078  | 
    struct source *s, *volatile osource;  | 
    |
1185  | 
    2078  | 
    struct shf *volatile shf;  | 
    |
1186  | 
    struct temp *h;  | 
    ||
1187  | 
    int i;  | 
    ||
1188  | 
    |||
1189  | 
    /* ksh -c 'cat << EOF' can cause this... */  | 
    ||
1190  | 
    ✗✓ | 2078  | 
    	if (content == NULL) { | 
    
1191  | 
    warningf(true, "here document missing");  | 
    ||
1192  | 
    return -2; /* special to iosetup(): don't print error */  | 
    ||
1193  | 
    }  | 
    ||
1194  | 
    |||
1195  | 
    /* Create temp file to hold content (done before newenv so temp  | 
    ||
1196  | 
    * doesn't get removed too soon).  | 
    ||
1197  | 
    */  | 
    ||
1198  | 
    2078  | 
    h = maketemp(ATEMP, TT_HEREDOC_EXP, &genv->temps);  | 
    |
1199  | 
    ✓✗✗✓ | 
    4156  | 
    	if (!(shf = h->shf) || (fd = open(h->name, O_RDONLY, 0)) < 0) { | 
    
1200  | 
    warningf(true, "can't %s temporary file %s: %s",  | 
    ||
1201  | 
    !shf ? "create" : "open",  | 
    ||
1202  | 
    h->name, strerror(errno));  | 
    ||
1203  | 
    if (shf)  | 
    ||
1204  | 
    shf_close(shf);  | 
    ||
1205  | 
    return -2 /* special to iosetup(): don't print error */;  | 
    ||
1206  | 
    }  | 
    ||
1207  | 
    |||
1208  | 
    2084  | 
    osource = source;  | 
    |
1209  | 
    2084  | 
    newenv(E_ERRH);  | 
    |
1210  | 
    2084  | 
    i = sigsetjmp(genv->jbuf, 0);  | 
    |
1211  | 
    ✓✓ | 2084  | 
    	if (i) { | 
    
1212  | 
    6  | 
    source = osource;  | 
    |
1213  | 
    6  | 
    quitenv(shf);  | 
    |
1214  | 
    6  | 
    close(fd);  | 
    |
1215  | 
    6  | 
    return -2; /* special to iosetup(): don't print error */  | 
    |
1216  | 
    }  | 
    ||
1217  | 
    ✓✓ | 2078  | 
    	if (sub) { | 
    
1218  | 
    /* Do substitutions on the content of heredoc */  | 
    ||
1219  | 
    1996  | 
    s = pushs(SSTRING, ATEMP);  | 
    |
1220  | 
    1996  | 
    s->start = s->str = content;  | 
    |
1221  | 
    1996  | 
    source = s;  | 
    |
1222  | 
    ✗✓ | 1996  | 
    if (yylex(ONEWORD|HEREDOC) != LWORD)  | 
    
1223  | 
    internal_errorf(1, "herein: yylex");  | 
    ||
1224  | 
    1996  | 
    source = osource;  | 
    |
1225  | 
    1996  | 
    shf_puts(evalstr(yylval.cp, 0), shf);  | 
    |
1226  | 
    1996  | 
    } else  | 
    |
1227  | 
    82  | 
    shf_puts(content, shf);  | 
    |
1228  | 
    |||
1229  | 
    2072  | 
    quitenv(NULL);  | 
    |
1230  | 
    |||
1231  | 
    ✗✓ | 2072  | 
    	if (shf_close(shf) == EOF) { | 
    
1232  | 
    close(fd);  | 
    ||
1233  | 
    warningf(true, "error writing %s: %s", h->name,  | 
    ||
1234  | 
    strerror(errno));  | 
    ||
1235  | 
    return -2; /* special to iosetup(): don't print error */  | 
    ||
1236  | 
    }  | 
    ||
1237  | 
    |||
1238  | 
    2072  | 
    return fd;  | 
    |
1239  | 
    2072  | 
    }  | 
    |
1240  | 
    |||
1241  | 
    #ifdef EDIT  | 
    ||
1242  | 
    /*  | 
    ||
1243  | 
    * ksh special - the select command processing section  | 
    ||
1244  | 
    * print the args in column form - assuming that we can  | 
    ||
1245  | 
    */  | 
    ||
1246  | 
    static char *  | 
    ||
1247  | 
    do_selectargs(char **ap, bool print_menu)  | 
    ||
1248  | 
    { | 
    ||
1249  | 
    	static const char *const read_args[] = { | 
    ||
1250  | 
    "read", "-r", "REPLY", NULL  | 
    ||
1251  | 
    };  | 
    ||
1252  | 
    const char *errstr;  | 
    ||
1253  | 
    char *s;  | 
    ||
1254  | 
    int i, argct;  | 
    ||
1255  | 
    |||
1256  | 
    for (argct = 0; ap[argct]; argct++)  | 
    ||
1257  | 
    ;  | 
    ||
1258  | 
    	while (1) { | 
    ||
1259  | 
    /* Menu is printed if  | 
    ||
1260  | 
    * - this is the first time around the select loop  | 
    ||
1261  | 
    * - the user enters a blank line  | 
    ||
1262  | 
    * - the REPLY parameter is empty  | 
    ||
1263  | 
    */  | 
    ||
1264  | 
    		if (print_menu || !*str_val(global("REPLY"))) | 
    ||
1265  | 
    pr_menu(ap);  | 
    ||
1266  | 
    		shellf("%s", str_val(global("PS3"))); | 
    ||
1267  | 
    		if (call_builtin(findcom("read", FC_BI), (char **) read_args)) | 
    ||
1268  | 
    return NULL;  | 
    ||
1269  | 
    		s = str_val(global("REPLY")); | 
    ||
1270  | 
    		if (*s) { | 
    ||
1271  | 
    i = strtonum(s, 1, argct, &errstr);  | 
    ||
1272  | 
    if (errstr)  | 
    ||
1273  | 
    return null;  | 
    ||
1274  | 
    return ap[i - 1];  | 
    ||
1275  | 
    }  | 
    ||
1276  | 
    print_menu = 1;  | 
    ||
1277  | 
    }  | 
    ||
1278  | 
    }  | 
    ||
1279  | 
    |||
1280  | 
    struct select_menu_info { | 
    ||
1281  | 
    char *const *args;  | 
    ||
1282  | 
    int arg_width;  | 
    ||
1283  | 
    int num_width;  | 
    ||
1284  | 
    };  | 
    ||
1285  | 
    |||
1286  | 
    static char *select_fmt_entry(void *arg, int i, char *buf, int buflen);  | 
    ||
1287  | 
    |||
1288  | 
    /* format a single select menu item */  | 
    ||
1289  | 
    static char *  | 
    ||
1290  | 
    select_fmt_entry(void *arg, int i, char *buf, int buflen)  | 
    ||
1291  | 
    { | 
    ||
1292  | 
    struct select_menu_info *smi = (struct select_menu_info *) arg;  | 
    ||
1293  | 
    |||
1294  | 
    shf_snprintf(buf, buflen, "%*d) %s",  | 
    ||
1295  | 
    smi->num_width, i + 1, smi->args[i]);  | 
    ||
1296  | 
    return buf;  | 
    ||
1297  | 
    }  | 
    ||
1298  | 
    |||
1299  | 
    /*  | 
    ||
1300  | 
    * print a select style menu  | 
    ||
1301  | 
    */  | 
    ||
1302  | 
    int  | 
    ||
1303  | 
    pr_menu(char *const *ap)  | 
    ||
1304  | 
    { | 
    ||
1305  | 
    struct select_menu_info smi;  | 
    ||
1306  | 
    char *const *pp;  | 
    ||
1307  | 
    int nwidth, dwidth;  | 
    ||
1308  | 
    int i, n;  | 
    ||
1309  | 
    |||
1310  | 
    /* Width/column calculations were done once and saved, but this  | 
    ||
1311  | 
    * means select can't be used recursively so we re-calculate each  | 
    ||
1312  | 
    * time (could save in a structure that is returned, but its probably  | 
    ||
1313  | 
    * not worth the bother).  | 
    ||
1314  | 
    */  | 
    ||
1315  | 
    |||
1316  | 
    /*  | 
    ||
1317  | 
    * get dimensions of the list  | 
    ||
1318  | 
    */  | 
    ||
1319  | 
    	for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) { | 
    ||
1320  | 
    i = strlen(*pp);  | 
    ||
1321  | 
    nwidth = (i > nwidth) ? i : nwidth;  | 
    ||
1322  | 
    }  | 
    ||
1323  | 
    /*  | 
    ||
1324  | 
    * we will print an index of the form  | 
    ||
1325  | 
    * %d)  | 
    ||
1326  | 
    * in front of each entry  | 
    ||
1327  | 
    * get the max width of this  | 
    ||
1328  | 
    */  | 
    ||
1329  | 
    for (i = n, dwidth = 1; i >= 10; i /= 10)  | 
    ||
1330  | 
    dwidth++;  | 
    ||
1331  | 
    |||
1332  | 
    smi.args = ap;  | 
    ||
1333  | 
    smi.arg_width = nwidth;  | 
    ||
1334  | 
    smi.num_width = dwidth;  | 
    ||
1335  | 
    print_columns(shl_out, n, select_fmt_entry, (void *) &smi,  | 
    ||
1336  | 
    dwidth + nwidth + 2, 1);  | 
    ||
1337  | 
    |||
1338  | 
    return n;  | 
    ||
1339  | 
    }  | 
    ||
1340  | 
    |||
1341  | 
    /* XXX: horrible kludge to fit within the framework */  | 
    ||
1342  | 
    |||
1343  | 
    static char *plain_fmt_entry(void *arg, int i, char *buf, int buflen);  | 
    ||
1344  | 
    |||
1345  | 
    static char *  | 
    ||
1346  | 
    plain_fmt_entry(void *arg, int i, char *buf, int buflen)  | 
    ||
1347  | 
    { | 
    ||
1348  | 
    shf_snprintf(buf, buflen, "%s", ((char *const *)arg)[i]);  | 
    ||
1349  | 
    return buf;  | 
    ||
1350  | 
    }  | 
    ||
1351  | 
    |||
1352  | 
    int  | 
    ||
1353  | 
    pr_list(char *const *ap)  | 
    ||
1354  | 
    { | 
    ||
1355  | 
    char *const *pp;  | 
    ||
1356  | 
    int nwidth;  | 
    ||
1357  | 
    int i, n;  | 
    ||
1358  | 
    |||
1359  | 
    	for (n = 0, nwidth = 0, pp = ap; *pp; n++, pp++) { | 
    ||
1360  | 
    i = strlen(*pp);  | 
    ||
1361  | 
    nwidth = (i > nwidth) ? i : nwidth;  | 
    ||
1362  | 
    }  | 
    ||
1363  | 
    print_columns(shl_out, n, plain_fmt_entry, (void *) ap, nwidth + 1, 0);  | 
    ||
1364  | 
    |||
1365  | 
    return n;  | 
    ||
1366  | 
    }  | 
    ||
1367  | 
    #endif /* EDIT */  | 
    ||
1368  | 
    |||
1369  | 
    /*  | 
    ||
1370  | 
    * [[ ... ]] evaluation routines  | 
    ||
1371  | 
    */  | 
    ||
1372  | 
    |||
1373  | 
    extern const char *const dbtest_tokens[];  | 
    ||
1374  | 
    extern const char db_close[];  | 
    ||
1375  | 
    |||
1376  | 
    /* Test if the current token is a whatever. Accepts the current token if  | 
    ||
1377  | 
    * it is. Returns 0 if it is not, non-zero if it is (in the case of  | 
    ||
1378  | 
    * TM_UNOP and TM_BINOP, the returned value is a Test_op).  | 
    ||
1379  | 
    */  | 
    ||
1380  | 
    static int  | 
    ||
1381  | 
    dbteste_isa(Test_env *te, Test_meta meta)  | 
    ||
1382  | 
    { | 
    ||
1383  | 
    int ret = 0;  | 
    ||
1384  | 
    int uqword;  | 
    ||
1385  | 
    char *p;  | 
    ||
1386  | 
    |||
1387  | 
    ✓✓ | 185080  | 
    if (!*te->pos.wp)  | 
    
1388  | 
    39702  | 
    return meta == TM_END;  | 
    |
1389  | 
    |||
1390  | 
    /* unquoted word? */  | 
    ||
1391  | 
    ✓✓ | 159396  | 
    for (p = *te->pos.wp; *p == CHAR; p += 2)  | 
    
1392  | 
    ;  | 
    ||
1393  | 
    52838  | 
    uqword = *p == EOS;  | 
    |
1394  | 
    |||
1395  | 
    ✓✓ | 52838  | 
    	if (meta == TM_UNOP || meta == TM_BINOP) { | 
    
1396  | 
    ✓✓ | 26370  | 
    		if (uqword) { | 
    
1397  | 
    13234  | 
    char buf[8]; /* longer than the longest operator */  | 
    |
1398  | 
    13234  | 
    char *q = buf;  | 
    |
1399  | 
    ✓✓ | 79404  | 
    for (p = *te->pos.wp;  | 
    
1400  | 
    66170  | 
    *p == CHAR && q < &buf[sizeof(buf) - 1]; p += 2)  | 
    |
1401  | 
    26468  | 
    *q++ = p[1];  | 
    |
1402  | 
    13234  | 
    *q = '\0';  | 
    |
1403  | 
    13234  | 
    ret = (int) test_isop(te, meta, buf);  | 
    |
1404  | 
    13234  | 
    }  | 
    |
1405  | 
    ✗✓ | 26468  | 
    } else if (meta == TM_END)  | 
    
1406  | 
    ret = 0;  | 
    ||
1407  | 
    else  | 
    ||
1408  | 
    ✓✓ | 52936  | 
    ret = uqword &&  | 
    
1409  | 
    196  | 
    strcmp(*te->pos.wp, dbtest_tokens[(int) meta]) == 0;  | 
    |
1410  | 
    |||
1411  | 
    /* Accept the token? */  | 
    ||
1412  | 
    ✓✓ | 52838  | 
    if (ret)  | 
    
1413  | 
    13234  | 
    te->pos.wp++;  | 
    |
1414  | 
    |||
1415  | 
    52838  | 
    return ret;  | 
    |
1416  | 
    92540  | 
    }  | 
    |
1417  | 
    |||
1418  | 
    static const char *  | 
    ||
1419  | 
    dbteste_getopnd(Test_env *te, Test_op op, int do_eval)  | 
    ||
1420  | 
    { | 
    ||
1421  | 
    52740  | 
    char *s = *te->pos.wp;  | 
    |
1422  | 
    |||
1423  | 
    ✗✓ | 26370  | 
    if (!s)  | 
    
1424  | 
    return NULL;  | 
    ||
1425  | 
    |||
1426  | 
    26370  | 
    te->pos.wp++;  | 
    |
1427  | 
    |||
1428  | 
    ✗✓ | 26370  | 
    if (!do_eval)  | 
    
1429  | 
    return null;  | 
    ||
1430  | 
    |||
1431  | 
    ✓✓ | 26370  | 
    if (op == TO_STEQL || op == TO_STNEQ)  | 
    
1432  | 
    13136  | 
    s = evalstr(s, DOTILDE | DOPAT);  | 
    |
1433  | 
    else  | 
    ||
1434  | 
    13234  | 
    s = evalstr(s, DOTILDE);  | 
    |
1435  | 
    |||
1436  | 
    26370  | 
    return s;  | 
    |
1437  | 
    26370  | 
    }  | 
    |
1438  | 
    |||
1439  | 
    static int  | 
    ||
1440  | 
    dbteste_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,  | 
    ||
1441  | 
    int do_eval)  | 
    ||
1442  | 
    { | 
    ||
1443  | 
    26468  | 
    return test_eval(te, op, opnd1, opnd2, do_eval);  | 
    |
1444  | 
    }  | 
    ||
1445  | 
    |||
1446  | 
    static void  | 
    ||
1447  | 
    dbteste_error(Test_env *te, int offset, const char *msg)  | 
    ||
1448  | 
    { | 
    ||
1449  | 
    te->flags |= TEF_ERROR;  | 
    ||
1450  | 
    internal_errorf(0, "dbteste_error: %s (offset %d)", msg, offset);  | 
    ||
1451  | 
    }  | 
    
| Generated by: GCOVR (Version 3.3) |