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