1 |
|
|
/* $OpenBSD: c_sh.c,v 1.59 2016/03/04 15:11:06 deraadt Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* built-in Bourne commands |
5 |
|
|
*/ |
6 |
|
|
|
7 |
|
|
#include <sys/resource.h> |
8 |
|
|
#include <sys/stat.h> |
9 |
|
|
#include <sys/time.h> |
10 |
|
|
|
11 |
|
|
#include <ctype.h> |
12 |
|
|
#include <errno.h> |
13 |
|
|
#include <fcntl.h> |
14 |
|
|
#include <stdio.h> |
15 |
|
|
#include <stdlib.h> |
16 |
|
|
#include <string.h> |
17 |
|
|
#include <unistd.h> |
18 |
|
|
|
19 |
|
|
#include "sh.h" |
20 |
|
|
|
21 |
|
|
static void p_time(struct shf *, int, struct timeval *, int, char *, char *); |
22 |
|
|
|
23 |
|
|
/* :, false and true */ |
24 |
|
|
int |
25 |
|
|
c_label(char **wp) |
26 |
|
1280 |
{ |
27 |
|
1280 |
return wp[0][0] == 'f' ? 1 : 0; |
28 |
|
|
} |
29 |
|
|
|
30 |
|
|
int |
31 |
|
|
c_shift(char **wp) |
32 |
|
134 |
{ |
33 |
|
134 |
struct block *l = genv->loc; |
34 |
|
|
int n; |
35 |
|
|
long val; |
36 |
|
|
char *arg; |
37 |
|
|
|
38 |
✗✓ |
134 |
if (ksh_getopt(wp, &builtin_opt, null) == '?') |
39 |
|
|
return 1; |
40 |
|
134 |
arg = wp[builtin_opt.optind]; |
41 |
|
|
|
42 |
✗✓ |
134 |
if (arg) { |
43 |
|
|
evaluate(arg, &val, KSH_UNWIND_ERROR, false); |
44 |
|
|
n = val; |
45 |
|
|
} else |
46 |
|
134 |
n = 1; |
47 |
✗✓ |
134 |
if (n < 0) { |
48 |
|
|
bi_errorf("%s: bad number", arg); |
49 |
|
|
return (1); |
50 |
|
|
} |
51 |
✗✓ |
134 |
if (l->argc < n) { |
52 |
|
|
bi_errorf("nothing to shift"); |
53 |
|
|
return (1); |
54 |
|
|
} |
55 |
|
134 |
l->argv[n] = l->argv[0]; |
56 |
|
134 |
l->argv += n; |
57 |
|
134 |
l->argc -= n; |
58 |
|
134 |
return 0; |
59 |
|
|
} |
60 |
|
|
|
61 |
|
|
int |
62 |
|
|
c_umask(char **wp) |
63 |
|
14 |
{ |
64 |
|
|
int i; |
65 |
|
|
char *cp; |
66 |
|
14 |
int symbolic = 0; |
67 |
|
|
mode_t old_umask; |
68 |
|
|
int optc; |
69 |
|
|
|
70 |
✗✓ |
28 |
while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1) |
71 |
|
|
switch (optc) { |
72 |
|
|
case 'S': |
73 |
|
|
symbolic = 1; |
74 |
|
|
break; |
75 |
|
|
case '?': |
76 |
|
|
return 1; |
77 |
|
|
} |
78 |
|
14 |
cp = wp[builtin_opt.optind]; |
79 |
✓✓ |
14 |
if (cp == NULL) { |
80 |
|
2 |
old_umask = umask(0); |
81 |
|
2 |
umask(old_umask); |
82 |
✗✓ |
2 |
if (symbolic) { |
83 |
|
|
char buf[18]; |
84 |
|
|
int j; |
85 |
|
|
|
86 |
|
|
old_umask = ~old_umask; |
87 |
|
|
cp = buf; |
88 |
|
|
for (i = 0; i < 3; i++) { |
89 |
|
|
*cp++ = "ugo"[i]; |
90 |
|
|
*cp++ = '='; |
91 |
|
|
for (j = 0; j < 3; j++) |
92 |
|
|
if (old_umask & (1 << (8 - (3*i + j)))) |
93 |
|
|
*cp++ = "rwx"[j]; |
94 |
|
|
*cp++ = ','; |
95 |
|
|
} |
96 |
|
|
cp[-1] = '\0'; |
97 |
|
|
shprintf("%s\n", buf); |
98 |
|
|
} else |
99 |
|
2 |
shprintf("%#3.3o\n", old_umask); |
100 |
|
|
} else { |
101 |
|
|
mode_t new_umask; |
102 |
|
|
|
103 |
✓✗ |
12 |
if (digit(*cp)) { |
104 |
✓✓ |
48 |
for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++) |
105 |
|
36 |
new_umask = new_umask * 8 + (*cp - '0'); |
106 |
✗✓ |
12 |
if (*cp) { |
107 |
|
|
bi_errorf("bad number"); |
108 |
|
|
return 1; |
109 |
|
|
} |
110 |
|
|
} else { |
111 |
|
|
/* symbolic format */ |
112 |
|
|
int positions, new_val; |
113 |
|
|
char op; |
114 |
|
|
|
115 |
|
|
old_umask = umask(0); |
116 |
|
|
umask(old_umask); /* in case of error */ |
117 |
|
|
old_umask = ~old_umask; |
118 |
|
|
new_umask = old_umask; |
119 |
|
|
positions = 0; |
120 |
|
|
while (*cp) { |
121 |
|
|
while (*cp && strchr("augo", *cp)) |
122 |
|
|
switch (*cp++) { |
123 |
|
|
case 'a': |
124 |
|
|
positions |= 0111; |
125 |
|
|
break; |
126 |
|
|
case 'u': |
127 |
|
|
positions |= 0100; |
128 |
|
|
break; |
129 |
|
|
case 'g': |
130 |
|
|
positions |= 0010; |
131 |
|
|
break; |
132 |
|
|
case 'o': |
133 |
|
|
positions |= 0001; |
134 |
|
|
break; |
135 |
|
|
} |
136 |
|
|
if (!positions) |
137 |
|
|
positions = 0111; /* default is a */ |
138 |
|
|
if (!strchr("=+-", op = *cp)) |
139 |
|
|
break; |
140 |
|
|
cp++; |
141 |
|
|
new_val = 0; |
142 |
|
|
while (*cp && strchr("rwxugoXs", *cp)) |
143 |
|
|
switch (*cp++) { |
144 |
|
|
case 'r': new_val |= 04; break; |
145 |
|
|
case 'w': new_val |= 02; break; |
146 |
|
|
case 'x': new_val |= 01; break; |
147 |
|
|
case 'u': new_val |= old_umask >> 6; |
148 |
|
|
break; |
149 |
|
|
case 'g': new_val |= old_umask >> 3; |
150 |
|
|
break; |
151 |
|
|
case 'o': new_val |= old_umask >> 0; |
152 |
|
|
break; |
153 |
|
|
case 'X': if (old_umask & 0111) |
154 |
|
|
new_val |= 01; |
155 |
|
|
break; |
156 |
|
|
case 's': /* ignored */ |
157 |
|
|
break; |
158 |
|
|
} |
159 |
|
|
new_val = (new_val & 07) * positions; |
160 |
|
|
switch (op) { |
161 |
|
|
case '-': |
162 |
|
|
new_umask &= ~new_val; |
163 |
|
|
break; |
164 |
|
|
case '=': |
165 |
|
|
new_umask = new_val | |
166 |
|
|
(new_umask & ~(positions * 07)); |
167 |
|
|
break; |
168 |
|
|
case '+': |
169 |
|
|
new_umask |= new_val; |
170 |
|
|
} |
171 |
|
|
if (*cp == ',') { |
172 |
|
|
positions = 0; |
173 |
|
|
cp++; |
174 |
|
|
} else if (!strchr("=+-", *cp)) |
175 |
|
|
break; |
176 |
|
|
} |
177 |
|
|
if (*cp) { |
178 |
|
|
bi_errorf("bad mask"); |
179 |
|
|
return 1; |
180 |
|
|
} |
181 |
|
|
new_umask = ~new_umask; |
182 |
|
|
} |
183 |
|
12 |
umask(new_umask); |
184 |
|
|
} |
185 |
|
14 |
return 0; |
186 |
|
|
} |
187 |
|
|
|
188 |
|
|
int |
189 |
|
|
c_dot(char **wp) |
190 |
|
14 |
{ |
191 |
|
|
char *file, *cp; |
192 |
|
|
char **argv; |
193 |
|
|
int argc; |
194 |
|
|
int i; |
195 |
|
|
int err; |
196 |
|
|
|
197 |
✗✓ |
14 |
if (ksh_getopt(wp, &builtin_opt, null) == '?') |
198 |
|
|
return 1; |
199 |
|
|
|
200 |
✗✓ |
14 |
if ((cp = wp[builtin_opt.optind]) == NULL) |
201 |
|
|
return 0; |
202 |
|
14 |
file = search(cp, path, R_OK, &err); |
203 |
✓✓ |
14 |
if (file == NULL) { |
204 |
✗✓ |
4 |
bi_errorf("%s: %s", cp, err ? strerror(err) : "not found"); |
205 |
|
|
return 1; |
206 |
|
|
} |
207 |
|
|
|
208 |
|
|
/* Set positional parameters? */ |
209 |
✗✓ |
10 |
if (wp[builtin_opt.optind + 1]) { |
210 |
|
|
argv = wp + builtin_opt.optind; |
211 |
|
|
argv[0] = genv->loc->argv[0]; /* preserve $0 */ |
212 |
|
|
for (argc = 0; argv[argc + 1]; argc++) |
213 |
|
|
; |
214 |
|
|
} else { |
215 |
|
10 |
argc = 0; |
216 |
|
10 |
argv = NULL; |
217 |
|
|
} |
218 |
|
10 |
i = include(file, argc, argv, 0); |
219 |
✗✓ |
10 |
if (i < 0) { /* should not happen */ |
220 |
|
|
bi_errorf("%s: %s", cp, strerror(errno)); |
221 |
|
|
return 1; |
222 |
|
|
} |
223 |
|
10 |
return i; |
224 |
|
|
} |
225 |
|
|
|
226 |
|
|
int |
227 |
|
|
c_wait(char **wp) |
228 |
|
|
{ |
229 |
|
|
int rv = 0; |
230 |
|
|
int sig; |
231 |
|
|
|
232 |
|
|
if (ksh_getopt(wp, &builtin_opt, null) == '?') |
233 |
|
|
return 1; |
234 |
|
|
wp += builtin_opt.optind; |
235 |
|
|
if (*wp == NULL) { |
236 |
|
|
while (waitfor(NULL, &sig) >= 0) |
237 |
|
|
; |
238 |
|
|
rv = sig; |
239 |
|
|
} else { |
240 |
|
|
for (; *wp; wp++) |
241 |
|
|
rv = waitfor(*wp, &sig); |
242 |
|
|
if (rv < 0) |
243 |
|
|
rv = sig ? sig : 127; /* magic exit code: bad job-id */ |
244 |
|
|
} |
245 |
|
|
return rv; |
246 |
|
|
} |
247 |
|
|
|
248 |
|
|
int |
249 |
|
|
c_read(char **wp) |
250 |
|
50 |
{ |
251 |
|
50 |
int c = 0; |
252 |
|
50 |
int expand = 1, history = 0; |
253 |
|
|
int expanding; |
254 |
|
50 |
int ecode = 0; |
255 |
|
|
char *cp; |
256 |
|
50 |
int fd = 0; |
257 |
|
|
struct shf *shf; |
258 |
|
|
int optc; |
259 |
|
|
const char *emsg; |
260 |
|
|
XString cs, xs; |
261 |
|
|
struct tbl *vp; |
262 |
|
50 |
char *xp = NULL; |
263 |
|
|
|
264 |
✗✓ |
100 |
while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != -1) |
265 |
|
|
switch (optc) { |
266 |
|
|
case 'p': |
267 |
|
|
if ((fd = coproc_getfd(R_OK, &emsg)) < 0) { |
268 |
|
|
bi_errorf("-p: %s", emsg); |
269 |
|
|
return 1; |
270 |
|
|
} |
271 |
|
|
break; |
272 |
|
|
case 'r': |
273 |
|
|
expand = 0; |
274 |
|
|
break; |
275 |
|
|
case 's': |
276 |
|
|
history = 1; |
277 |
|
|
break; |
278 |
|
|
case 'u': |
279 |
|
|
if (!*(cp = builtin_opt.optarg)) |
280 |
|
|
fd = 0; |
281 |
|
|
else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) { |
282 |
|
|
bi_errorf("-u: %s: %s", cp, emsg); |
283 |
|
|
return 1; |
284 |
|
|
} |
285 |
|
|
break; |
286 |
|
|
case '?': |
287 |
|
|
return 1; |
288 |
|
|
} |
289 |
|
50 |
wp += builtin_opt.optind; |
290 |
|
|
|
291 |
✓✓ |
50 |
if (*wp == NULL) |
292 |
|
2 |
*--wp = "REPLY"; |
293 |
|
|
|
294 |
|
|
/* Since we can't necessarily seek backwards on non-regular files, |
295 |
|
|
* don't buffer them so we can't read too much. |
296 |
|
|
*/ |
297 |
|
50 |
shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare); |
298 |
|
|
|
299 |
✗✓ |
50 |
if ((cp = strchr(*wp, '?')) != NULL) { |
300 |
|
|
*cp = 0; |
301 |
|
|
if (isatty(fd)) { |
302 |
|
|
/* at&t ksh says it prints prompt on fd if it's open |
303 |
|
|
* for writing and is a tty, but it doesn't do it |
304 |
|
|
* (it also doesn't check the interactive flag, |
305 |
|
|
* as is indicated in the Kornshell book). |
306 |
|
|
*/ |
307 |
|
|
shellf("%s", cp+1); |
308 |
|
|
} |
309 |
|
|
} |
310 |
|
|
|
311 |
|
|
/* If we are reading from the co-process for the first time, |
312 |
|
|
* make sure the other side of the pipe is closed first. This allows |
313 |
|
|
* the detection of eof. |
314 |
|
|
* |
315 |
|
|
* This is not compatible with at&t ksh... the fd is kept so another |
316 |
|
|
* coproc can be started with same output, however, this means eof |
317 |
|
|
* can't be detected... This is why it is closed here. |
318 |
|
|
* If this call is removed, remove the eof check below, too. |
319 |
|
|
* coproc_readw_close(fd); |
320 |
|
|
*/ |
321 |
|
|
|
322 |
✗✓ |
50 |
if (history) |
323 |
|
|
Xinit(xs, xp, 128, ATEMP); |
324 |
|
50 |
expanding = 0; |
325 |
|
50 |
Xinit(cs, cp, 128, ATEMP); |
326 |
✓✓ |
136 |
for (; *wp != NULL; wp++) { |
327 |
|
86 |
for (cp = Xstring(cs, cp); ; ) { |
328 |
✓✓ |
996 |
if (c == '\n' || c == EOF) |
329 |
|
8 |
break; |
330 |
|
|
while (1) { |
331 |
✓✓ |
988 |
c = shf_getc(shf); |
332 |
✗✓ |
988 |
if (c == '\0') |
333 |
|
|
continue; |
334 |
✓✓✗✓ ✗✗ |
988 |
if (c == EOF && shf_error(shf) && |
335 |
|
|
shf->errno_ == EINTR) { |
336 |
|
|
/* Was the offending signal one that |
337 |
|
|
* would normally kill a process? |
338 |
|
|
* If so, pretend the read was killed. |
339 |
|
|
*/ |
340 |
|
|
ecode = fatal_trap_check(); |
341 |
|
|
|
342 |
|
|
/* non fatal (eg, CHLD), carry on */ |
343 |
|
|
if (!ecode) { |
344 |
|
|
shf_clearerr(shf); |
345 |
|
|
continue; |
346 |
|
|
} |
347 |
|
|
} |
348 |
|
|
break; |
349 |
|
|
} |
350 |
✗✓ |
988 |
if (history) { |
351 |
|
|
Xcheck(xs, xp); |
352 |
|
|
Xput(xs, xp, c); |
353 |
|
|
} |
354 |
✓✓ |
988 |
Xcheck(cs, cp); |
355 |
✗✓ |
988 |
if (expanding) { |
356 |
|
|
expanding = 0; |
357 |
|
|
if (c == '\n') { |
358 |
|
|
c = 0; |
359 |
|
|
if (Flag(FTALKING_I) && isatty(fd)) { |
360 |
|
|
/* set prompt in case this is |
361 |
|
|
* called from .profile or $ENV |
362 |
|
|
*/ |
363 |
|
|
set_prompt(PS2, NULL); |
364 |
|
|
pprompt(prompt, 0); |
365 |
|
|
} |
366 |
|
|
} else if (c != EOF) |
367 |
|
|
Xput(cs, cp, c); |
368 |
|
|
continue; |
369 |
|
|
} |
370 |
✗✓ |
988 |
if (expand && c == '\\') { |
371 |
|
|
expanding = 1; |
372 |
|
|
continue; |
373 |
|
|
} |
374 |
✓✓ |
988 |
if (c == '\n' || c == EOF) |
375 |
|
50 |
break; |
376 |
✓✓ |
938 |
if (ctype(c, C_IFS)) { |
377 |
✗✓✗✗
|
32 |
if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS)) |
378 |
|
|
continue; |
379 |
✓✓ |
32 |
if (wp[1]) |
380 |
|
28 |
break; |
381 |
|
|
} |
382 |
|
910 |
Xput(cs, cp, c); |
383 |
|
|
} |
384 |
|
|
/* strip trailing IFS white space from last variable */ |
385 |
✓✓ |
86 |
if (!wp[1]) |
386 |
✓✓✓✓
|
52 |
while (Xlength(cs, cp) && ctype(cp[-1], C_IFS) && |
387 |
|
|
ctype(cp[-1], C_IFSWS)) |
388 |
|
2 |
cp--; |
389 |
|
86 |
Xput(cs, cp, '\0'); |
390 |
|
86 |
vp = global(*wp); |
391 |
|
|
/* Must be done before setting export. */ |
392 |
✗✓ |
86 |
if (vp->flag & RDONLY) { |
393 |
|
|
shf_flush(shf); |
394 |
|
|
bi_errorf("%s is read only", *wp); |
395 |
|
|
return 1; |
396 |
|
|
} |
397 |
✗✓ |
86 |
if (Flag(FEXPORT)) |
398 |
|
|
typeset(*wp, EXPORT, 0, 0, 0); |
399 |
✗✓ |
86 |
if (!setstr(vp, Xstring(cs, cp), KSH_RETURN_ERROR)) { |
400 |
|
|
shf_flush(shf); |
401 |
|
|
return 1; |
402 |
|
|
} |
403 |
|
|
} |
404 |
|
|
|
405 |
|
50 |
shf_flush(shf); |
406 |
✗✓ |
50 |
if (history) { |
407 |
|
|
Xput(xs, xp, '\0'); |
408 |
|
|
source->line++; |
409 |
|
|
histsave(source->line, Xstring(xs, xp), 1); |
410 |
|
|
Xfree(xs, xp); |
411 |
|
|
} |
412 |
|
|
/* if this is the co-process fd, close the file descriptor |
413 |
|
|
* (can get eof if and only if all processes are have died, ie, |
414 |
|
|
* coproc.njobs is 0 and the pipe is closed). |
415 |
|
|
*/ |
416 |
✓✓ |
50 |
if (c == EOF && !ecode) |
417 |
|
10 |
coproc_read_close(fd); |
418 |
|
|
|
419 |
✓✗ |
50 |
return ecode ? ecode : c == EOF; |
420 |
|
|
} |
421 |
|
|
|
422 |
|
|
int |
423 |
|
|
c_eval(char **wp) |
424 |
|
8246 |
{ |
425 |
|
|
struct source *s; |
426 |
|
|
int rv; |
427 |
|
|
|
428 |
✗✓ |
8246 |
if (ksh_getopt(wp, &builtin_opt, null) == '?') |
429 |
|
|
return 1; |
430 |
|
8246 |
s = pushs(SWORDS, ATEMP); |
431 |
|
8246 |
s->u.strv = wp + builtin_opt.optind; |
432 |
✓✓ |
8246 |
if (!Flag(FPOSIX)) { |
433 |
|
|
/* |
434 |
|
|
* Handle case where the command is empty due to failed |
435 |
|
|
* command substitution, eg, eval "$(false)". |
436 |
|
|
* In this case, shell() will not set/change exstat (because |
437 |
|
|
* compiled tree is empty), so will use this value. |
438 |
|
|
* subst_exstat is cleared in execute(), so should be 0 if |
439 |
|
|
* there were no substitutions. |
440 |
|
|
* |
441 |
|
|
* A strict reading of POSIX says we don't do this (though |
442 |
|
|
* it is traditionally done). [from 1003.2-1992] |
443 |
|
|
* 3.9.1: Simple Commands |
444 |
|
|
* ... If there is a command name, execution shall |
445 |
|
|
* continue as described in 3.9.1.1. If there |
446 |
|
|
* is no command name, but the command contained a command |
447 |
|
|
* substitution, the command shall complete with the exit |
448 |
|
|
* status of the last command substitution |
449 |
|
|
* 3.9.1.1: Command Search and Execution |
450 |
|
|
* ...(1)...(a) If the command name matches the name of |
451 |
|
|
* a special built-in utility, that special built-in |
452 |
|
|
* utility shall be invoked. |
453 |
|
|
* 3.14.5: Eval |
454 |
|
|
* ... If there are no arguments, or only null arguments, |
455 |
|
|
* eval shall return an exit status of zero. |
456 |
|
|
*/ |
457 |
|
3682 |
exstat = subst_exstat; |
458 |
|
|
} |
459 |
|
|
|
460 |
|
8246 |
rv = shell(s, false); |
461 |
|
8245 |
afree(s, ATEMP); |
462 |
|
8245 |
return (rv); |
463 |
|
|
} |
464 |
|
|
|
465 |
|
|
int |
466 |
|
|
c_trap(char **wp) |
467 |
|
31 |
{ |
468 |
|
|
int i; |
469 |
|
|
char *s; |
470 |
|
|
Trap *p; |
471 |
|
|
|
472 |
✗✓ |
31 |
if (ksh_getopt(wp, &builtin_opt, null) == '?') |
473 |
|
|
return 1; |
474 |
|
31 |
wp += builtin_opt.optind; |
475 |
|
|
|
476 |
✗✓ |
31 |
if (*wp == NULL) { |
477 |
|
|
for (p = sigtraps, i = NSIG+1; --i >= 0; p++) { |
478 |
|
|
if (p->trap != NULL) { |
479 |
|
|
shprintf("trap -- "); |
480 |
|
|
print_value_quoted(p->trap); |
481 |
|
|
shprintf(" %s\n", p->name); |
482 |
|
|
} |
483 |
|
|
} |
484 |
|
|
return 0; |
485 |
|
|
} |
486 |
|
|
|
487 |
|
|
/* |
488 |
|
|
* Use case sensitive lookup for first arg so the |
489 |
|
|
* command 'exit' isn't confused with the pseudo-signal |
490 |
|
|
* 'EXIT'. |
491 |
|
|
*/ |
492 |
✓✗ |
31 |
s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL; /* get command */ |
493 |
✓✗✗✓ ✗✗ |
31 |
if (s != NULL && s[0] == '-' && s[1] == '\0') |
494 |
|
|
s = NULL; |
495 |
|
|
|
496 |
|
|
/* set/clear traps */ |
497 |
✓✓ |
74 |
while (*wp != NULL) { |
498 |
|
43 |
p = gettrap(*wp++, true); |
499 |
✗✓ |
43 |
if (p == NULL) { |
500 |
|
|
bi_errorf("bad signal %s", wp[-1]); |
501 |
|
|
return 1; |
502 |
|
|
} |
503 |
|
43 |
settrap(p, s); |
504 |
|
|
} |
505 |
|
31 |
return 0; |
506 |
|
|
} |
507 |
|
|
|
508 |
|
|
int |
509 |
|
|
c_exitreturn(char **wp) |
510 |
|
398 |
{ |
511 |
|
398 |
int how = LEXIT; |
512 |
|
|
int n; |
513 |
|
|
char *arg; |
514 |
|
|
|
515 |
✗✓ |
398 |
if (ksh_getopt(wp, &builtin_opt, null) == '?') |
516 |
|
|
return 1; |
517 |
|
398 |
arg = wp[builtin_opt.optind]; |
518 |
|
|
|
519 |
✓✓ |
398 |
if (arg) { |
520 |
✗✓ |
388 |
if (!getn(arg, &n)) { |
521 |
|
|
exstat = 1; |
522 |
|
|
warningf(true, "%s: bad number", arg); |
523 |
|
|
} else |
524 |
|
388 |
exstat = n; |
525 |
|
|
} |
526 |
✓✓ |
398 |
if (wp[0][0] == 'r') { /* return */ |
527 |
|
|
struct env *ep; |
528 |
|
|
|
529 |
|
|
/* need to tell if this is exit or return so trap exit will |
530 |
|
|
* work right (POSIX) |
531 |
|
|
*/ |
532 |
✓✗ |
596 |
for (ep = genv; ep; ep = ep->oenv) |
533 |
✓✓ |
596 |
if (STOP_RETURN(ep->type)) { |
534 |
|
196 |
how = LRETURN; |
535 |
|
196 |
break; |
536 |
|
|
} |
537 |
|
|
} |
538 |
|
|
|
539 |
✓✓✓✗ ✗✓ |
398 |
if (how == LEXIT && !really_exit && j_stopped_running()) { |
540 |
|
|
really_exit = 1; |
541 |
|
|
how = LSHELL; |
542 |
|
|
} |
543 |
|
|
|
544 |
|
398 |
quitenv(NULL); /* get rid of any i/o redirections */ |
545 |
|
398 |
unwind(how); |
546 |
|
|
/* NOTREACHED */ |
547 |
|
|
return 0; |
548 |
|
|
} |
549 |
|
|
|
550 |
|
|
int |
551 |
|
|
c_brkcont(char **wp) |
552 |
|
196 |
{ |
553 |
|
|
int n, quit; |
554 |
|
196 |
struct env *ep, *last_ep = NULL; |
555 |
|
|
char *arg; |
556 |
|
|
|
557 |
✗✓ |
196 |
if (ksh_getopt(wp, &builtin_opt, null) == '?') |
558 |
|
|
return 1; |
559 |
|
196 |
arg = wp[builtin_opt.optind]; |
560 |
|
|
|
561 |
✓✓ |
196 |
if (!arg) |
562 |
|
161 |
n = 1; |
563 |
✗✓ |
35 |
else if (!bi_getn(arg, &n)) |
564 |
|
|
return 1; |
565 |
|
192 |
quit = n; |
566 |
✗✓ |
192 |
if (quit <= 0) { |
567 |
|
|
/* at&t ksh does this for non-interactive shells only - weird */ |
568 |
|
|
bi_errorf("%s: bad value", arg); |
569 |
|
|
return 1; |
570 |
|
|
} |
571 |
|
|
|
572 |
|
|
/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */ |
573 |
✓✗✓✓
|
777 |
for (ep = genv; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv) |
574 |
✓✓ |
765 |
if (ep->type == E_LOOP) { |
575 |
✓✓ |
205 |
if (--quit == 0) |
576 |
|
180 |
break; |
577 |
|
25 |
ep->flags |= EF_BRKCONT_PASS; |
578 |
|
25 |
last_ep = ep; |
579 |
|
|
} |
580 |
|
|
|
581 |
✓✓ |
192 |
if (quit) { |
582 |
|
|
/* at&t ksh doesn't print a message - just does what it |
583 |
|
|
* can. We print a message 'cause it helps in debugging |
584 |
|
|
* scripts, but don't generate an error (ie, keep going). |
585 |
|
|
*/ |
586 |
✓✓ |
12 |
if (n == quit) { |
587 |
|
4 |
warningf(true, "%s: cannot %s", wp[0], wp[0]); |
588 |
|
4 |
return 0; |
589 |
|
|
} |
590 |
|
|
/* POSIX says if n is too big, the last enclosing loop |
591 |
|
|
* shall be used. Doesn't say to print an error but we |
592 |
|
|
* do anyway 'cause the user messed up. |
593 |
|
|
*/ |
594 |
✓✗ |
8 |
if (last_ep) |
595 |
|
8 |
last_ep->flags &= ~EF_BRKCONT_PASS; |
596 |
|
8 |
warningf(true, "%s: can only %s %d level(s)", |
597 |
|
|
wp[0], wp[0], n - quit); |
598 |
|
|
} |
599 |
|
|
|
600 |
✓✓ |
188 |
unwind(*wp[0] == 'b' ? LBREAK : LCONTIN); |
601 |
|
|
/* NOTREACHED */ |
602 |
|
|
} |
603 |
|
|
|
604 |
|
|
int |
605 |
|
|
c_set(char **wp) |
606 |
|
1551 |
{ |
607 |
|
|
int argi, setargs; |
608 |
|
1551 |
struct block *l = genv->loc; |
609 |
|
1551 |
char **owp = wp; |
610 |
|
|
|
611 |
✓✓ |
1551 |
if (wp[1] == NULL) { |
612 |
|
|
static const char *const args [] = { "set", "-", NULL }; |
613 |
|
18 |
return c_typeset((char **) args); |
614 |
|
|
} |
615 |
|
|
|
616 |
|
1533 |
argi = parse_args(wp, OF_SET, &setargs); |
617 |
✗✓ |
1533 |
if (argi < 0) |
618 |
|
|
return 1; |
619 |
|
|
/* set $# and $* */ |
620 |
✓✓ |
1533 |
if (setargs) { |
621 |
|
118 |
owp = wp += argi - 1; |
622 |
|
118 |
wp[0] = l->argv[0]; /* save $0 */ |
623 |
✓✓ |
538 |
while (*++wp != NULL) |
624 |
|
302 |
*wp = str_save(*wp, &l->area); |
625 |
|
118 |
l->argc = wp - owp - 1; |
626 |
|
118 |
l->argv = areallocarray(NULL, l->argc+2, sizeof(char *), &l->area); |
627 |
✓✓ |
118 |
for (wp = l->argv; (*wp++ = *owp++) != NULL; ) |
628 |
|
|
; |
629 |
|
|
} |
630 |
|
|
/* POSIX says set exit status is 0, but old scripts that use |
631 |
|
|
* getopt(1), use the construct: set -- `getopt ab:c "$@"` |
632 |
|
|
* which assumes the exit value set will be that of the `` |
633 |
|
|
* (subst_exstat is cleared in execute() so that it will be 0 |
634 |
|
|
* if there are no command substitutions). |
635 |
|
|
*/ |
636 |
✓✓ |
1533 |
return Flag(FPOSIX) ? 0 : subst_exstat; |
637 |
|
|
} |
638 |
|
|
|
639 |
|
|
int |
640 |
|
|
c_unset(char **wp) |
641 |
|
250 |
{ |
642 |
|
|
char *id; |
643 |
|
250 |
int optc, unset_var = 1; |
644 |
|
|
|
645 |
✓✓ |
502 |
while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1) |
646 |
✓✗✗✗
|
2 |
switch (optc) { |
647 |
|
|
case 'f': |
648 |
|
2 |
unset_var = 0; |
649 |
|
2 |
break; |
650 |
|
|
case 'v': |
651 |
|
|
unset_var = 1; |
652 |
|
|
break; |
653 |
|
|
case '?': |
654 |
|
|
return 1; |
655 |
|
|
} |
656 |
|
250 |
wp += builtin_opt.optind; |
657 |
✓✓ |
507 |
for (; (id = *wp) != NULL; wp++) |
658 |
✓✓ |
257 |
if (unset_var) { /* unset variable */ |
659 |
|
255 |
struct tbl *vp = global(id); |
660 |
|
|
|
661 |
✗✓ |
255 |
if ((vp->flag&RDONLY)) { |
662 |
|
|
bi_errorf("%s is read only", vp->name); |
663 |
|
|
return 1; |
664 |
|
|
} |
665 |
|
255 |
unset(vp, strchr(id, '[') ? 1 : 0); |
666 |
|
|
} else { /* unset function */ |
667 |
|
2 |
define(id, NULL); |
668 |
|
|
} |
669 |
|
250 |
return 0; |
670 |
|
|
} |
671 |
|
|
|
672 |
|
|
static void |
673 |
|
|
p_time(struct shf *shf, int posix, struct timeval *tv, int width, char *prefix, |
674 |
|
|
char *suffix) |
675 |
|
6 |
{ |
676 |
✗✓ |
6 |
if (posix) |
677 |
|
|
shf_fprintf(shf, "%s%*lld.%02ld%s", prefix ? prefix : "", |
678 |
|
|
width, (long long)tv->tv_sec, tv->tv_usec / 10000, suffix); |
679 |
|
|
else |
680 |
✗✓ |
6 |
shf_fprintf(shf, "%s%*lldm%02lld.%02lds%s", prefix ? prefix : "", |
681 |
|
|
width, (long long)tv->tv_sec / 60, |
682 |
|
|
(long long)tv->tv_sec % 60, |
683 |
|
|
tv->tv_usec / 10000, suffix); |
684 |
|
6 |
} |
685 |
|
|
|
686 |
|
|
int |
687 |
|
|
c_times(char **wp) |
688 |
|
|
{ |
689 |
|
|
struct rusage usage; |
690 |
|
|
|
691 |
|
|
(void) getrusage(RUSAGE_SELF, &usage); |
692 |
|
|
p_time(shl_stdout, 0, &usage.ru_utime, 0, NULL, " "); |
693 |
|
|
p_time(shl_stdout, 0, &usage.ru_stime, 0, NULL, "\n"); |
694 |
|
|
|
695 |
|
|
(void) getrusage(RUSAGE_CHILDREN, &usage); |
696 |
|
|
p_time(shl_stdout, 0, &usage.ru_utime, 0, NULL, " "); |
697 |
|
|
p_time(shl_stdout, 0, &usage.ru_stime, 0, NULL, "\n"); |
698 |
|
|
|
699 |
|
|
return 0; |
700 |
|
|
} |
701 |
|
|
|
702 |
|
|
/* |
703 |
|
|
* time pipeline (really a statement, not a built-in command) |
704 |
|
|
*/ |
705 |
|
|
int |
706 |
|
|
timex(struct op *t, int f, volatile int *xerrok) |
707 |
|
2 |
{ |
708 |
|
|
#define TF_NOARGS BIT(0) |
709 |
|
|
#define TF_NOREAL BIT(1) /* don't report real time */ |
710 |
|
|
#define TF_POSIX BIT(2) /* report in posix format */ |
711 |
|
2 |
int rv = 0; |
712 |
|
|
struct rusage ru0, ru1, cru0, cru1; |
713 |
|
|
struct timeval usrtime, systime, tv0, tv1; |
714 |
|
2 |
int tf = 0; |
715 |
|
|
extern struct timeval j_usrtime, j_systime; /* computed by j_wait */ |
716 |
|
|
|
717 |
|
2 |
gettimeofday(&tv0, NULL); |
718 |
|
2 |
getrusage(RUSAGE_SELF, &ru0); |
719 |
|
2 |
getrusage(RUSAGE_CHILDREN, &cru0); |
720 |
✓✗ |
2 |
if (t->left) { |
721 |
|
|
/* |
722 |
|
|
* Two ways of getting cpu usage of a command: just use t0 |
723 |
|
|
* and t1 (which will get cpu usage from other jobs that |
724 |
|
|
* finish while we are executing t->left), or get the |
725 |
|
|
* cpu usage of t->left. at&t ksh does the former, while |
726 |
|
|
* pdksh tries to do the later (the j_usrtime hack doesn't |
727 |
|
|
* really work as it only counts the last job). |
728 |
|
|
*/ |
729 |
|
2 |
timerclear(&j_usrtime); |
730 |
|
2 |
timerclear(&j_systime); |
731 |
|
2 |
rv = execute(t->left, f | XTIME, xerrok); |
732 |
✓✗ |
2 |
if (t->left->type == TCOM) |
733 |
|
2 |
tf |= t->left->str[0]; |
734 |
|
2 |
gettimeofday(&tv1, NULL); |
735 |
|
2 |
getrusage(RUSAGE_SELF, &ru1); |
736 |
|
2 |
getrusage(RUSAGE_CHILDREN, &cru1); |
737 |
|
|
} else |
738 |
|
|
tf = TF_NOARGS; |
739 |
|
|
|
740 |
✗✓ |
2 |
if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */ |
741 |
|
|
tf |= TF_NOREAL; |
742 |
|
|
timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime); |
743 |
|
|
timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime); |
744 |
|
|
} else { |
745 |
✗✓ |
2 |
timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime); |
746 |
✗✓ |
2 |
timeradd(&usrtime, &j_usrtime, &usrtime); |
747 |
✗✓ |
2 |
timersub(&ru1.ru_stime, &ru0.ru_stime, &systime); |
748 |
✗✓ |
2 |
timeradd(&systime, &j_systime, &systime); |
749 |
|
|
} |
750 |
|
|
|
751 |
✓✗ |
2 |
if (!(tf & TF_NOREAL)) { |
752 |
✗✓ |
2 |
timersub(&tv1, &tv0, &tv1); |
753 |
✗✓ |
2 |
if (tf & TF_POSIX) |
754 |
|
|
p_time(shl_out, 1, &tv1, 5, "real ", "\n"); |
755 |
|
|
else |
756 |
|
2 |
p_time(shl_out, 0, &tv1, 5, NULL, " real "); |
757 |
|
|
} |
758 |
✗✓ |
2 |
if (tf & TF_POSIX) |
759 |
|
|
p_time(shl_out, 1, &usrtime, 5, "user ", "\n"); |
760 |
|
|
else |
761 |
|
2 |
p_time(shl_out, 0, &usrtime, 5, NULL, " user "); |
762 |
✗✓ |
2 |
if (tf & TF_POSIX) |
763 |
|
|
p_time(shl_out, 1, &systime, 5, "sys ", "\n"); |
764 |
|
|
else |
765 |
|
2 |
p_time(shl_out, 0, &systime, 5, NULL, " system\n"); |
766 |
|
2 |
shf_flush(shl_out); |
767 |
|
|
|
768 |
|
2 |
return rv; |
769 |
|
|
} |
770 |
|
|
|
771 |
|
|
void |
772 |
|
|
timex_hook(struct op *t, char **volatile *app) |
773 |
|
2 |
{ |
774 |
|
2 |
char **wp = *app; |
775 |
|
|
int optc; |
776 |
|
|
int i, j; |
777 |
|
|
Getopt opt; |
778 |
|
|
|
779 |
|
2 |
ksh_getopt_reset(&opt, 0); |
780 |
|
2 |
opt.optind = 0; /* start at the start */ |
781 |
✗✓ |
4 |
while ((optc = ksh_getopt(wp, &opt, ":p")) != -1) |
782 |
|
|
switch (optc) { |
783 |
|
|
case 'p': |
784 |
|
|
t->str[0] |= TF_POSIX; |
785 |
|
|
break; |
786 |
|
|
case '?': |
787 |
|
|
errorf("time: -%s unknown option", opt.optarg); |
788 |
|
|
case ':': |
789 |
|
|
errorf("time: -%s requires an argument", |
790 |
|
|
opt.optarg); |
791 |
|
|
} |
792 |
|
|
/* Copy command words down over options. */ |
793 |
✗✓ |
2 |
if (opt.optind != 0) { |
794 |
|
|
for (i = 0; i < opt.optind; i++) |
795 |
|
|
afree(wp[i], ATEMP); |
796 |
|
|
for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++) |
797 |
|
|
; |
798 |
|
|
} |
799 |
✗✓ |
2 |
if (!wp[0]) |
800 |
|
|
t->str[0] |= TF_NOARGS; |
801 |
|
2 |
*app = wp; |
802 |
|
2 |
} |
803 |
|
|
|
804 |
|
|
/* exec with no args - args case is taken care of in comexec() */ |
805 |
|
|
int |
806 |
|
|
c_exec(char **wp) |
807 |
|
15 |
{ |
808 |
|
|
int i; |
809 |
|
|
|
810 |
|
|
/* make sure redirects stay in place */ |
811 |
✓✗ |
15 |
if (genv->savefd != NULL) { |
812 |
✓✓ |
495 |
for (i = 0; i < NUFILE; i++) { |
813 |
✓✓ |
480 |
if (genv->savefd[i] > 0) |
814 |
|
12 |
close(genv->savefd[i]); |
815 |
|
|
/* |
816 |
|
|
* For ksh keep anything > 2 private, |
817 |
|
|
* for sh, let them be (POSIX says what |
818 |
|
|
* happens is unspecified and the bourne shell |
819 |
|
|
* keeps them open). |
820 |
|
|
*/ |
821 |
✓✓✗✓
|
480 |
if (!Flag(FSH) && i > 2 && genv->savefd[i]) |
822 |
|
|
fcntl(i, F_SETFD, FD_CLOEXEC); |
823 |
|
|
} |
824 |
|
15 |
genv->savefd = NULL; |
825 |
|
|
} |
826 |
|
15 |
return 0; |
827 |
|
|
} |
828 |
|
|
|
829 |
|
|
static int |
830 |
|
|
c_suspend(char **wp) |
831 |
|
|
{ |
832 |
|
|
if (wp[1] != NULL) { |
833 |
|
|
bi_errorf("too many arguments"); |
834 |
|
|
return 1; |
835 |
|
|
} |
836 |
|
|
if (Flag(FLOGIN)) { |
837 |
|
|
/* Can't suspend an orphaned process group. */ |
838 |
|
|
pid_t parent = getppid(); |
839 |
|
|
if (getpgid(parent) == getpgid(0) || |
840 |
|
|
getsid(parent) != getsid(0)) { |
841 |
|
|
bi_errorf("can't suspend a login shell"); |
842 |
|
|
return 1; |
843 |
|
|
} |
844 |
|
|
} |
845 |
|
|
j_suspend(); |
846 |
|
|
return 0; |
847 |
|
|
} |
848 |
|
|
|
849 |
|
|
/* dummy function, special case in comexec() */ |
850 |
|
|
int |
851 |
|
|
c_builtin(char **wp) |
852 |
|
|
{ |
853 |
|
|
return 0; |
854 |
|
|
} |
855 |
|
|
|
856 |
|
|
extern int c_test(char **wp); /* in c_test.c */ |
857 |
|
|
extern int c_ulimit(char **wp); /* in c_ulimit.c */ |
858 |
|
|
|
859 |
|
|
/* A leading = means assignments before command are kept; |
860 |
|
|
* a leading * means a POSIX special builtin; |
861 |
|
|
* a leading + means a POSIX regular builtin |
862 |
|
|
* (* and + should not be combined). |
863 |
|
|
*/ |
864 |
|
|
const struct builtin shbuiltins [] = { |
865 |
|
|
{"*=.", c_dot}, |
866 |
|
|
{"*=:", c_label}, |
867 |
|
|
{"[", c_test}, |
868 |
|
|
{"*=break", c_brkcont}, |
869 |
|
|
{"=builtin", c_builtin}, |
870 |
|
|
{"*=continue", c_brkcont}, |
871 |
|
|
{"*=eval", c_eval}, |
872 |
|
|
{"*=exec", c_exec}, |
873 |
|
|
{"*=exit", c_exitreturn}, |
874 |
|
|
{"+false", c_label}, |
875 |
|
|
{"*=return", c_exitreturn}, |
876 |
|
|
{"*=set", c_set}, |
877 |
|
|
{"*=shift", c_shift}, |
878 |
|
|
{"*=times", c_times}, |
879 |
|
|
{"*=trap", c_trap}, |
880 |
|
|
{"+=wait", c_wait}, |
881 |
|
|
{"+read", c_read}, |
882 |
|
|
{"test", c_test}, |
883 |
|
|
{"+true", c_label}, |
884 |
|
|
{"ulimit", c_ulimit}, |
885 |
|
|
{"+umask", c_umask}, |
886 |
|
|
{"*=unset", c_unset}, |
887 |
|
|
{"suspend", c_suspend}, |
888 |
|
|
{NULL, NULL} |
889 |
|
|
}; |