1 /* $OpenBSD: c_ksh.c,v 1.34 2013/12/17 16:37:05 deraadt Exp $ */
2 /* $OpenBSD: c_sh.c,v 1.45 2014/08/27 08:26:04 jmc Exp $ */
3 /* $OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $ */
4 /* $OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $ */
5
6 /*-
7 * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
8 * 2010, 2011, 2012, 2013, 2014, 2015
9 * Thorsten Glaser <tg@mirbsd.org>
10 *
11 * Provided that these terms and disclaimer and all copyright notices
12 * are retained or reproduced in an accompanying document, permission
13 * is granted to deal in this work without restriction, including un-
14 * limited rights to use, publicly perform, distribute, sell, modify,
15 * merge, give away, or sublicence.
16 *
17 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
18 * the utmost extent permitted by applicable law, neither express nor
19 * implied; without malicious intent or gross negligence. In no event
20 * may a licensor, author or contributor be held liable for indirect,
21 * direct, other damage, loss, or other issues arising in any way out
22 * of dealing in the work, even if advised of the possibility of such
23 * damage or existence of a defect, except proven that it results out
24 * of said person's immediate fault when using the work as intended.
25 */
26
27 #include "sh.h"
28
29 #if HAVE_SELECT
30 #if HAVE_SYS_BSDTYPES_H
31 #include <sys/bsdtypes.h>
32 #endif
33 #if HAVE_SYS_SELECT_H
34 #include <sys/select.h>
35 #endif
36 #if HAVE_BSTRING_H
37 #include <bstring.h>
38 #endif
39 #endif
40
41 __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.259.2.5 2015/04/19 19:18:16 tg Exp $");
42
43 #if HAVE_KILLPG
44 /*
45 * use killpg if < -1 since -1 does special things
46 * for some non-killpg-endowed kills
47 */
48 #define mksh_kill(p,s) ((p) < -1 ? killpg(-(p), (s)) : kill((p), (s)))
49 #else
50 /* cross fingers and hope kill is killpg-endowed */
51 #define mksh_kill kill
52 #endif
53
54 /* XXX conditions correct? */
55 #if !defined(RLIM_INFINITY) && !defined(MKSH_NO_LIMITS)
56 #define MKSH_NO_LIMITS 1
57 #endif
58
59 #ifdef MKSH_NO_LIMITS
60 #define c_ulimit c_true
61 #endif
62
63 #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
64 static int c_suspend(const char **);
65 #endif
66
67 /* getn() that prints error */
68 static int
bi_getn(const char * as,int * ai)69 bi_getn(const char *as, int *ai)
70 {
71 int rv;
72
73 if (!(rv = getn(as, ai)))
74 bi_errorf("%s: %s", as, "bad number");
75 return (rv);
76 }
77
78 static int
c_true(const char ** wp MKSH_A_UNUSED)79 c_true(const char **wp MKSH_A_UNUSED)
80 {
81 return (0);
82 }
83
84 static int
c_false(const char ** wp MKSH_A_UNUSED)85 c_false(const char **wp MKSH_A_UNUSED)
86 {
87 return (1);
88 }
89
90 /*
91 * A leading = means assignments before command are kept.
92 * A leading * means a POSIX special builtin.
93 */
94 const struct builtin mkshbuiltins[] = {
95 {"*=.", c_dot},
96 {"*=:", c_true},
97 {"[", c_test},
98 /* no =: AT&T manual wrong */
99 {Talias, c_alias},
100 {"*=break", c_brkcont},
101 {Tgbuiltin, c_builtin},
102 {"cat", c_cat},
103 {"cd", c_cd},
104 /* dash compatibility hack */
105 {"chdir", c_cd},
106 {"command", c_command},
107 {"*=continue", c_brkcont},
108 {"echo", c_print},
109 {"*=eval", c_eval},
110 {"*=exec", c_exec},
111 {"*=exit", c_exitreturn},
112 {Tsgexport, c_typeset},
113 {"false", c_false},
114 {"fc", c_fc},
115 {"getopts", c_getopts},
116 {"=global", c_typeset},
117 {"jobs", c_jobs},
118 {"kill", c_kill},
119 {"let", c_let},
120 {"let]", c_let},
121 {"print", c_print},
122 {"pwd", c_pwd},
123 {"read", c_read},
124 {Tsgreadonly, c_typeset},
125 {"realpath", c_realpath},
126 {"rename", c_rename},
127 {"*=return", c_exitreturn},
128 {Tsgset, c_set},
129 {"*=shift", c_shift},
130 #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
131 {"suspend", c_suspend},
132 #endif
133 {"test", c_test},
134 {"*=times", c_times},
135 {"*=trap", c_trap},
136 {"true", c_true},
137 {T_typeset, c_typeset},
138 {"ulimit", c_ulimit},
139 {"umask", c_umask},
140 {Tunalias, c_unalias},
141 {Tsgunset, c_unset},
142 {"=wait", c_wait},
143 {"whence", c_whence},
144 #ifndef MKSH_UNEMPLOYED
145 {"bg", c_fgbg},
146 {"fg", c_fgbg},
147 #endif
148 #ifndef MKSH_NO_CMDLINE_EDITING
149 {"bind", c_bind},
150 #endif
151 #if HAVE_MKNOD
152 {"mknod", c_mknod},
153 #endif
154 #ifdef MKSH_PRINTF_BUILTIN
155 {"printf", c_printf},
156 #endif
157 #if HAVE_SELECT
158 {"sleep", c_sleep},
159 #endif
160 #ifdef __MirBSD__
161 /* alias to "true" for historical reasons */
162 {"domainname", c_true},
163 #endif
164 {NULL, (int (*)(const char **))NULL}
165 };
166
167 struct kill_info {
168 int num_width;
169 int name_width;
170 };
171
172 static const struct t_op {
173 char op_text[4];
174 Test_op op_num;
175 } u_ops[] = {
176 {"-a", TO_FILAXST },
177 {"-b", TO_FILBDEV },
178 {"-c", TO_FILCDEV },
179 {"-d", TO_FILID },
180 {"-e", TO_FILEXST },
181 {"-f", TO_FILREG },
182 {"-G", TO_FILGID },
183 {"-g", TO_FILSETG },
184 {"-h", TO_FILSYM },
185 {"-H", TO_FILCDF },
186 {"-k", TO_FILSTCK },
187 {"-L", TO_FILSYM },
188 {"-n", TO_STNZE },
189 {"-O", TO_FILUID },
190 {"-o", TO_OPTION },
191 {"-p", TO_FILFIFO },
192 {"-r", TO_FILRD },
193 {"-s", TO_FILGZ },
194 {"-S", TO_FILSOCK },
195 {"-t", TO_FILTT },
196 {"-u", TO_FILSETU },
197 {"-w", TO_FILWR },
198 {"-x", TO_FILEX },
199 {"-z", TO_STZER },
200 {"", TO_NONOP }
201 };
202 static const struct t_op b_ops[] = {
203 {"=", TO_STEQL },
204 {"==", TO_STEQL },
205 {"!=", TO_STNEQ },
206 {"<", TO_STLT },
207 {">", TO_STGT },
208 {"-eq", TO_INTEQ },
209 {"-ne", TO_INTNE },
210 {"-gt", TO_INTGT },
211 {"-ge", TO_INTGE },
212 {"-lt", TO_INTLT },
213 {"-le", TO_INTLE },
214 {"-ef", TO_FILEQ },
215 {"-nt", TO_FILNT },
216 {"-ot", TO_FILOT },
217 {"", TO_NONOP }
218 };
219
220 static int test_oexpr(Test_env *, bool);
221 static int test_aexpr(Test_env *, bool);
222 static int test_nexpr(Test_env *, bool);
223 static int test_primary(Test_env *, bool);
224 static Test_op ptest_isa(Test_env *, Test_meta);
225 static const char *ptest_getopnd(Test_env *, Test_op, bool);
226 static void ptest_error(Test_env *, int, const char *);
227 static char *kill_fmt_entry(char *, size_t, unsigned int, const void *);
228 static void p_time(struct shf *, bool, long, int, int,
229 const char *, const char *);
230
231 int
c_pwd(const char ** wp)232 c_pwd(const char **wp)
233 {
234 int optc;
235 bool physical = tobool(Flag(FPHYSICAL));
236 char *p, *allocd = NULL;
237
238 while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
239 switch (optc) {
240 case 'L':
241 physical = false;
242 break;
243 case 'P':
244 physical = true;
245 break;
246 case '?':
247 return (1);
248 }
249 wp += builtin_opt.optind;
250
251 if (wp[0]) {
252 bi_errorf("too many arguments");
253 return (1);
254 }
255 p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
256 current_wd) : NULL;
257 /* LINTED use of access */
258 if (p && access(p, R_OK) < 0)
259 p = NULL;
260 if (!p && !(p = allocd = ksh_get_wd())) {
261 bi_errorf("%s: %s", "can't determine current directory",
262 cstrerror(errno));
263 return (1);
264 }
265 shprintf("%s\n", p);
266 afree(allocd, ATEMP);
267 return (0);
268 }
269
270 static const char *s_ptr;
271 static int s_get(void);
272 static void s_put(int);
273
274 int
c_print(const char ** wp)275 c_print(const char **wp)
276 {
277 #define PO_NL BIT(0) /* print newline */
278 #define PO_EXPAND BIT(1) /* expand backslash sequences */
279 #define PO_PMINUSMINUS BIT(2) /* print a -- argument */
280 #define PO_HIST BIT(3) /* print to history instead of stdout */
281 #define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
282 int fd = 1, c;
283 int flags = PO_EXPAND | PO_NL;
284 const char *s, *emsg;
285 XString xs;
286 char *xp;
287
288 if (wp[0][0] == 'e') {
289 /* echo builtin */
290 wp++;
291 #ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
292 if (Flag(FSH)) {
293 /*
294 * MidnightBSD /bin/sh needs a BSD echo, that is,
295 * one that supports -e but does not enable it by
296 * default
297 */
298 flags = PO_NL;
299 }
300 #endif
301 if (Flag(FPOSIX) ||
302 #ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
303 Flag(FSH) ||
304 #endif
305 Flag(FAS_BUILTIN)) {
306 /* Debian Policy 10.4 compliant "echo" builtin */
307 if (*wp && !strcmp(*wp, "-n")) {
308 /* we recognise "-n" only as the first arg */
309 flags = 0;
310 wp++;
311 } else
312 /* otherwise, we print everything as-is */
313 flags = PO_NL;
314 } else {
315 int nflags = flags;
316
317 /**
318 * a compromise between sysV and BSD echo commands:
319 * escape sequences are enabled by default, and -n,
320 * -e and -E are recognised if they appear in argu-
321 * ments with no illegal options (ie, echo -nq will
322 * print -nq).
323 * Different from sysV echo since options are reco-
324 * gnised, different from BSD echo since escape se-
325 * quences are enabled by default.
326 */
327
328 while ((s = *wp) && *s == '-' && s[1]) {
329 while (*++s)
330 if (*s == 'n')
331 nflags &= ~PO_NL;
332 else if (*s == 'e')
333 nflags |= PO_EXPAND;
334 else if (*s == 'E')
335 nflags &= ~PO_EXPAND;
336 else
337 /*
338 * bad option: don't use
339 * nflags, print argument
340 */
341 break;
342
343 if (*s)
344 break;
345 wp++;
346 flags = nflags;
347 }
348 }
349 } else {
350 int optc;
351 const char *opts = "Rnprsu,";
352
353 while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
354 switch (optc) {
355 case 'R':
356 /* fake BSD echo command */
357 flags |= PO_PMINUSMINUS;
358 flags &= ~PO_EXPAND;
359 opts = "ne";
360 break;
361 case 'e':
362 flags |= PO_EXPAND;
363 break;
364 case 'n':
365 flags &= ~PO_NL;
366 break;
367 case 'p':
368 if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
369 bi_errorf("%s: %s", "-p", emsg);
370 return (1);
371 }
372 break;
373 case 'r':
374 flags &= ~PO_EXPAND;
375 break;
376 case 's':
377 flags |= PO_HIST;
378 break;
379 case 'u':
380 if (!*(s = builtin_opt.optarg))
381 fd = 0;
382 else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
383 bi_errorf("%s: %s: %s", "-u", s, emsg);
384 return (1);
385 }
386 break;
387 case '?':
388 return (1);
389 }
390
391 if (!(builtin_opt.info & GI_MINUSMINUS)) {
392 /* treat a lone - like -- */
393 if (wp[builtin_opt.optind] &&
394 ksh_isdash(wp[builtin_opt.optind]))
395 builtin_opt.optind++;
396 } else if (flags & PO_PMINUSMINUS)
397 builtin_opt.optind--;
398 wp += builtin_opt.optind;
399 }
400
401 Xinit(xs, xp, 128, ATEMP);
402
403 while (*wp != NULL) {
404 s = *wp;
405 while ((c = *s++) != '\0') {
406 Xcheck(xs, xp);
407 if ((flags & PO_EXPAND) && c == '\\') {
408 s_ptr = s;
409 c = unbksl(false, s_get, s_put);
410 s = s_ptr;
411 if (c == -1) {
412 /* rejected by generic function */
413 switch ((c = *s++)) {
414 case 'c':
415 flags &= ~PO_NL;
416 /* AT&T brain damage */
417 continue;
418 case '\0':
419 s--;
420 c = '\\';
421 break;
422 default:
423 Xput(xs, xp, '\\');
424 }
425 } else if ((unsigned int)c > 0xFF) {
426 /* generic function returned Unicode */
427 char ts[4];
428
429 ts[utf_wctomb(ts, c - 0x100)] = 0;
430 for (c = 0; ts[c]; ++c)
431 Xput(xs, xp, ts[c]);
432 continue;
433 }
434 }
435 Xput(xs, xp, c);
436 }
437 if (*++wp != NULL)
438 Xput(xs, xp, ' ');
439 }
440 if (flags & PO_NL)
441 Xput(xs, xp, '\n');
442
443 if (flags & PO_HIST) {
444 Xput(xs, xp, '\0');
445 histsave(&source->line, Xstring(xs, xp), true, false);
446 Xfree(xs, xp);
447 } else {
448 int len = Xlength(xs, xp);
449 int opipe = 0;
450
451 /*
452 * Ensure we aren't killed by a SIGPIPE while writing to
453 * a coprocess. AT&T ksh doesn't seem to do this (seems
454 * to just check that the co-process is alive which is
455 * not enough).
456 */
457 if (coproc.write >= 0 && coproc.write == fd) {
458 flags |= PO_COPROC;
459 opipe = block_pipe();
460 }
461 for (s = Xstring(xs, xp); len > 0; ) {
462 if ((c = write(fd, s, len)) < 0) {
463 if (flags & PO_COPROC)
464 restore_pipe(opipe);
465 if (errno == EINTR) {
466 /* allow user to ^C out */
467 intrcheck();
468 if (flags & PO_COPROC)
469 opipe = block_pipe();
470 continue;
471 }
472 return (1);
473 }
474 s += c;
475 len -= c;
476 }
477 if (flags & PO_COPROC)
478 restore_pipe(opipe);
479 }
480
481 return (0);
482 }
483
484 static int
s_get(void)485 s_get(void)
486 {
487 return (*s_ptr++);
488 }
489
490 static void
s_put(int c MKSH_A_UNUSED)491 s_put(int c MKSH_A_UNUSED)
492 {
493 --s_ptr;
494 }
495
496 int
c_whence(const char ** wp)497 c_whence(const char **wp)
498 {
499 struct tbl *tp;
500 const char *id;
501 bool pflag = false, vflag = false, Vflag = false;
502 int rv = 0, optc, fcflags;
503 bool iam_whence = wp[0][0] == 'w';
504 const char *opts = iam_whence ? "pv" : "pvV";
505
506 while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
507 switch (optc) {
508 case 'p':
509 pflag = true;
510 break;
511 case 'v':
512 vflag = true;
513 break;
514 case 'V':
515 Vflag = true;
516 break;
517 case '?':
518 return (1);
519 }
520 wp += builtin_opt.optind;
521
522 fcflags = FC_BI | FC_PATH | FC_FUNC;
523 if (!iam_whence) {
524 /* Note that -p on its own is deal with in comexec() */
525 if (pflag)
526 fcflags |= FC_DEFPATH;
527 /*
528 * Convert command options to whence options - note that
529 * command -pV uses a different path search than whence -v
530 * or whence -pv. This should be considered a feature.
531 */
532 vflag = Vflag;
533 }
534 if (pflag)
535 fcflags &= ~(FC_BI | FC_FUNC);
536
537 while ((vflag || rv == 0) && (id = *wp++) != NULL) {
538 uint32_t h = 0;
539
540 tp = NULL;
541 if ((iam_whence || vflag) && !pflag)
542 tp = ktsearch(&keywords, id, h = hash(id));
543 if (!tp && !pflag) {
544 tp = ktsearch(&aliases, id, h ? h : hash(id));
545 if (tp && !(tp->flag & ISSET))
546 tp = NULL;
547 }
548 if (!tp)
549 tp = findcom(id, fcflags);
550 if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
551 tp->type != CTALIAS))
552 shf_puts(id, shl_stdout);
553 if (vflag) {
554 switch (tp->type) {
555 case CKEYWD:
556 case CALIAS:
557 case CFUNC:
558 case CSHELL:
559 shf_puts(" is a", shl_stdout);
560 break;
561 }
562 switch (tp->type) {
563 case CKEYWD:
564 case CSHELL:
565 case CTALIAS:
566 case CEXEC:
567 shf_putc(' ', shl_stdout);
568 break;
569 }
570 }
571
572 switch (tp->type) {
573 case CKEYWD:
574 if (vflag)
575 shf_puts("reserved word", shl_stdout);
576 break;
577 case CALIAS:
578 if (vflag)
579 shprintf("n %s%s for ",
580 (tp->flag & EXPORT) ? "exported " : null,
581 Talias);
582 if (!iam_whence && !vflag)
583 shprintf("%s %s=", Talias, id);
584 print_value_quoted(shl_stdout, tp->val.s);
585 break;
586 case CFUNC:
587 if (vflag) {
588 if (tp->flag & EXPORT)
589 shf_puts("n exported", shl_stdout);
590 if (tp->flag & TRACE)
591 shf_puts(" traced", shl_stdout);
592 if (!(tp->flag & ISSET)) {
593 shf_puts(" undefined", shl_stdout);
594 if (tp->u.fpath)
595 shprintf(" (autoload from %s)",
596 tp->u.fpath);
597 }
598 shf_puts(T_function, shl_stdout);
599 }
600 break;
601 case CSHELL:
602 if (vflag) {
603 if (tp->flag & SPEC_BI)
604 shf_puts("special ", shl_stdout);
605 shprintf("%s %s", "shell", Tbuiltin);
606 }
607 break;
608 case CTALIAS:
609 case CEXEC:
610 if (tp->flag & ISSET) {
611 if (vflag) {
612 shf_puts("is ", shl_stdout);
613 if (tp->type == CTALIAS)
614 shprintf("a tracked %s%s for ",
615 (tp->flag & EXPORT) ?
616 "exported " : null,
617 Talias);
618 }
619 shf_puts(tp->val.s, shl_stdout);
620 } else {
621 if (vflag)
622 shf_puts("not found", shl_stdout);
623 rv = 1;
624 }
625 break;
626 default:
627 shf_puts(" is *GOK*", shl_stdout);
628 break;
629 }
630 if (vflag || !rv)
631 shf_putc('\n', shl_stdout);
632 }
633 return (rv);
634 }
635
636 /* Deal with command -vV - command -p dealt with in comexec() */
637 int
c_command(const char ** wp)638 c_command(const char **wp)
639 {
640 /*
641 * Let c_whence do the work. Note that c_command() must be
642 * a distinct function from c_whence() (tested in comexec()).
643 */
644 return (c_whence(wp));
645 }
646
647 /* typeset, global, export, and readonly */
648 static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool);
649 static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
650 bool);
651 int
c_typeset(const char ** wp)652 c_typeset(const char **wp)
653 {
654 struct tbl *vp, **p;
655 uint32_t fset = 0, fclr = 0, flag;
656 int thing = 0, field = 0, base = 0, i;
657 struct block *l;
658 const char *opts;
659 const char *fieldstr = NULL, *basestr = NULL;
660 bool localv = false, func = false, pflag = false, istset = true;
661 enum namerefflag new_refflag = SRF_NOP;
662
663 switch (**wp) {
664
665 /* export */
666 case 'e':
667 fset |= EXPORT;
668 istset = false;
669 break;
670
671 /* readonly */
672 case 'r':
673 fset |= RDONLY;
674 istset = false;
675 break;
676
677 /* set */
678 case 's':
679 /* called with 'typeset -' */
680 break;
681
682 /* typeset */
683 case 't':
684 localv = true;
685 break;
686 }
687
688 /* see comment below regarding possible opions */
689 opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
690
691 builtin_opt.flags |= GF_PLUSOPT;
692 /*
693 * AT&T ksh seems to have 0-9 as options which are multiplied
694 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
695 * sets right justify in a field of 12). This allows options
696 * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
697 * does not allow the number to be specified as a separate argument
698 * Here, the number must follow the RLZi option, but is optional
699 * (see the # kludge in ksh_getopt()).
700 */
701 while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
702 flag = 0;
703 switch (i) {
704 case 'L':
705 flag = LJUST;
706 fieldstr = builtin_opt.optarg;
707 break;
708 case 'R':
709 flag = RJUST;
710 fieldstr = builtin_opt.optarg;
711 break;
712 case 'U':
713 /*
714 * AT&T ksh uses u, but this conflicts with
715 * upper/lower case. If this option is changed,
716 * need to change the -U below as well
717 */
718 flag = INT_U;
719 break;
720 case 'Z':
721 flag = ZEROFIL;
722 fieldstr = builtin_opt.optarg;
723 break;
724 case 'a':
725 /*
726 * this is supposed to set (-a) or unset (+a) the
727 * indexed array attribute; it does nothing on an
728 * existing regular string or indexed array though
729 */
730 break;
731 case 'f':
732 func = true;
733 break;
734 case 'i':
735 flag = INTEGER;
736 basestr = builtin_opt.optarg;
737 break;
738 case 'l':
739 flag = LCASEV;
740 break;
741 case 'n':
742 new_refflag = (builtin_opt.info & GI_PLUS) ?
743 SRF_DISABLE : SRF_ENABLE;
744 break;
745 /* export, readonly: POSIX -p flag */
746 case 'p':
747 /* typeset: show values as well */
748 pflag = true;
749 if (istset)
750 continue;
751 break;
752 case 'r':
753 flag = RDONLY;
754 break;
755 case 't':
756 flag = TRACE;
757 break;
758 case 'u':
759 /* upper case / autoload */
760 flag = UCASEV_AL;
761 break;
762 case 'x':
763 flag = EXPORT;
764 break;
765 case '?':
766 return (1);
767 }
768 if (builtin_opt.info & GI_PLUS) {
769 fclr |= flag;
770 fset &= ~flag;
771 thing = '+';
772 } else {
773 fset |= flag;
774 fclr &= ~flag;
775 thing = '-';
776 }
777 }
778
779 if (fieldstr && !bi_getn(fieldstr, &field))
780 return (1);
781 if (basestr && (!getn(basestr, &base) || base < 1 || base > 36)) {
782 bi_errorf("%s: %s", "bad integer base", basestr);
783 return (1);
784 }
785
786 if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
787 (wp[builtin_opt.optind][0] == '-' ||
788 wp[builtin_opt.optind][0] == '+') &&
789 wp[builtin_opt.optind][1] == '\0') {
790 thing = wp[builtin_opt.optind][0];
791 builtin_opt.optind++;
792 }
793
794 if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
795 new_refflag != SRF_NOP)) {
796 bi_errorf("only -t, -u and -x options may be used with -f");
797 return (1);
798 }
799 if (wp[builtin_opt.optind]) {
800 /*
801 * Take care of exclusions.
802 * At this point, flags in fset are cleared in fclr and vice
803 * versa. This property should be preserved.
804 */
805 if (fset & LCASEV)
806 /* LCASEV has priority over UCASEV_AL */
807 fset &= ~UCASEV_AL;
808 if (fset & LJUST)
809 /* LJUST has priority over RJUST */
810 fset &= ~RJUST;
811 if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
812 /* -Z implies -ZR */
813 fset |= RJUST;
814 fclr &= ~RJUST;
815 }
816 /*
817 * Setting these attributes clears the others, unless they
818 * are also set in this command
819 */
820 if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
821 INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
822 fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
823 LCASEV | INTEGER | INT_U | INT_L);
824 }
825 if (new_refflag != SRF_NOP) {
826 fclr &= ~(ARRAY | ASSOC);
827 fset &= ~(ARRAY | ASSOC);
828 fclr |= EXPORT;
829 fset |= ASSOC;
830 if (new_refflag == SRF_DISABLE)
831 fclr |= ASSOC;
832 }
833
834 /* set variables and attributes */
835 if (wp[builtin_opt.optind] &&
836 /* not "typeset -p varname" */
837 !(!func && pflag && !(fset | fclr))) {
838 int rv = 0;
839 struct tbl *f;
840
841 if (localv && !func)
842 fset |= LOCAL;
843 for (i = builtin_opt.optind; wp[i]; i++) {
844 if (func) {
845 f = findfunc(wp[i], hash(wp[i]),
846 tobool(fset & UCASEV_AL));
847 if (!f) {
848 /* AT&T ksh does ++rv: bogus */
849 rv = 1;
850 continue;
851 }
852 if (fset | fclr) {
853 f->flag |= fset;
854 f->flag &= ~fclr;
855 } else {
856 fpFUNCTf(shl_stdout, 0,
857 tobool(f->flag & FKSH),
858 wp[i], f->val.t);
859 shf_putc('\n', shl_stdout);
860 }
861 } else if (!typeset(wp[i], fset, fclr, field, base)) {
862 bi_errorf("%s: %s", wp[i], "is not an identifier");
863 return (1);
864 }
865 }
866 return (rv);
867 }
868
869 /* list variables and attributes */
870
871 /* no difference at this point.. */
872 flag = fset | fclr;
873 if (func) {
874 for (l = e->loc; l; l = l->next) {
875 for (p = ktsort(&l->funs); (vp = *p++); ) {
876 if (flag && (vp->flag & flag) == 0)
877 continue;
878 if (thing == '-')
879 fpFUNCTf(shl_stdout, 0,
880 tobool(vp->flag & FKSH),
881 vp->name, vp->val.t);
882 else
883 shf_puts(vp->name, shl_stdout);
884 shf_putc('\n', shl_stdout);
885 }
886 }
887 } else if (wp[builtin_opt.optind]) {
888 for (i = builtin_opt.optind; wp[i]; i++) {
889 varsearch(e->loc, &vp, wp[i], hash(wp[i]));
890 c_typeset_vardump(vp, flag, thing, pflag, istset);
891 }
892 } else
893 c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
894 return (0);
895 }
896
897 static void
c_typeset_vardump_recursive(struct block * l,uint32_t flag,int thing,bool pflag,bool istset)898 c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
899 bool pflag, bool istset)
900 {
901 struct tbl **blockvars, *vp;
902
903 if (l->next)
904 c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
905 blockvars = ktsort(&l->vars);
906 while ((vp = *blockvars++))
907 c_typeset_vardump(vp, flag, thing, pflag, istset);
908 /*XXX doesn’t this leak? */
909 }
910
911 static void
c_typeset_vardump(struct tbl * vp,uint32_t flag,int thing,bool pflag,bool istset)912 c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
913 bool istset)
914 {
915 struct tbl *tvp;
916 int any_set = 0;
917 char *s;
918
919 if (!vp)
920 return;
921
922 /*
923 * See if the parameter is set (for arrays, if any
924 * element is set).
925 */
926 for (tvp = vp; tvp; tvp = tvp->u.array)
927 if (tvp->flag & ISSET) {
928 any_set = 1;
929 break;
930 }
931
932 /*
933 * Check attributes - note that all array elements
934 * have (should have?) the same attributes, so checking
935 * the first is sufficient.
936 *
937 * Report an unset param only if the user has
938 * explicitly given it some attribute (like export);
939 * otherwise, after "echo $FOO", we would report FOO...
940 */
941 if (!any_set && !(vp->flag & USERATTRIB))
942 return;
943 if (flag && (vp->flag & flag) == 0)
944 return;
945 if (!(vp->flag & ARRAY))
946 /* optimise later conditionals */
947 any_set = 0;
948 do {
949 /*
950 * Ignore array elements that aren't set unless there
951 * are no set elements, in which case the first is
952 * reported on
953 */
954 if (any_set && !(vp->flag & ISSET))
955 continue;
956 /* no arguments */
957 if (!thing && !flag) {
958 if (any_set == 1) {
959 shprintf("%s %s %s\n", Tset, "-A", vp->name);
960 any_set = 2;
961 }
962 /*
963 * AT&T ksh prints things like export, integer,
964 * leftadj, zerofill, etc., but POSIX says must
965 * be suitable for re-entry...
966 */
967 shprintf("%s %s", Ttypeset, "");
968 if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
969 shprintf("%s ", "-n");
970 if ((vp->flag & INTEGER))
971 shprintf("%s ", "-i");
972 if ((vp->flag & EXPORT))
973 shprintf("%s ", "-x");
974 if ((vp->flag & RDONLY))
975 shprintf("%s ", "-r");
976 if ((vp->flag & TRACE))
977 shprintf("%s ", "-t");
978 if ((vp->flag & LJUST))
979 shprintf("-L%d ", vp->u2.field);
980 if ((vp->flag & RJUST))
981 shprintf("-R%d ", vp->u2.field);
982 if ((vp->flag & ZEROFIL))
983 shprintf("%s ", "-Z");
984 if ((vp->flag & LCASEV))
985 shprintf("%s ", "-l");
986 if ((vp->flag & UCASEV_AL))
987 shprintf("%s ", "-u");
988 if ((vp->flag & INT_U))
989 shprintf("%s ", "-U");
990 } else if (pflag) {
991 shprintf("%s %s", istset ? Ttypeset :
992 (flag & EXPORT) ? Texport : Treadonly, "");
993 }
994 if (any_set)
995 shprintf("%s[%lu]", vp->name, arrayindex(vp));
996 else
997 shf_puts(vp->name, shl_stdout);
998 if ((!thing && !flag && pflag) ||
999 (thing == '-' && (vp->flag & ISSET))) {
1000 s = str_val(vp);
1001 shf_putc('=', shl_stdout);
1002 /* AT&T ksh can't have justified integers... */
1003 if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
1004 shf_puts(s, shl_stdout);
1005 else
1006 print_value_quoted(shl_stdout, s);
1007 }
1008 shf_putc('\n', shl_stdout);
1009
1010 /*
1011 * Only report first 'element' of an array with
1012 * no set elements.
1013 */
1014 if (!any_set)
1015 return;
1016 } while ((vp = vp->u.array));
1017 }
1018
1019 int
c_alias(const char ** wp)1020 c_alias(const char **wp)
1021 {
1022 struct table *t = &aliases;
1023 int rv = 0, prefix = 0;
1024 bool rflag = false, tflag, Uflag = false, pflag = false;
1025 uint32_t xflag = 0;
1026 int optc;
1027
1028 builtin_opt.flags |= GF_PLUSOPT;
1029 while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != -1) {
1030 prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
1031 switch (optc) {
1032 case 'd':
1033 #ifdef MKSH_NOPWNAM
1034 t = NULL; /* fix "alias -dt" */
1035 #else
1036 t = &homedirs;
1037 #endif
1038 break;
1039 case 'p':
1040 pflag = true;
1041 break;
1042 case 'r':
1043 rflag = true;
1044 break;
1045 case 't':
1046 t = &taliases;
1047 break;
1048 case 'U':
1049 /*
1050 * kludge for tracked alias initialization
1051 * (don't do a path search, just make an entry)
1052 */
1053 Uflag = true;
1054 break;
1055 case 'x':
1056 xflag = EXPORT;
1057 break;
1058 case '?':
1059 return (1);
1060 }
1061 }
1062 #ifdef MKSH_NOPWNAM
1063 if (t == NULL)
1064 return (0);
1065 #endif
1066 wp += builtin_opt.optind;
1067
1068 if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
1069 (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
1070 prefix = wp[0][0];
1071 wp++;
1072 }
1073
1074 tflag = t == &taliases;
1075
1076 /* "hash -r" means reset all the tracked aliases.. */
1077 if (rflag) {
1078 static const char *args[] = {
1079 Tunalias, "-ta", NULL
1080 };
1081
1082 if (!tflag || *wp) {
1083 shprintf("%s: -r flag can only be used with -t"
1084 " and without arguments\n", Talias);
1085 return (1);
1086 }
1087 ksh_getopt_reset(&builtin_opt, GF_ERROR);
1088 return (c_unalias(args));
1089 }
1090
1091 if (*wp == NULL) {
1092 struct tbl *ap, **p;
1093
1094 for (p = ktsort(t); (ap = *p++) != NULL; )
1095 if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
1096 if (pflag)
1097 shprintf("%s ", Talias);
1098 shf_puts(ap->name, shl_stdout);
1099 if (prefix != '+') {
1100 shf_putc('=', shl_stdout);
1101 print_value_quoted(shl_stdout, ap->val.s);
1102 }
1103 shf_putc('\n', shl_stdout);
1104 }
1105 }
1106
1107 for (; *wp != NULL; wp++) {
1108 const char *alias = *wp, *val, *newval;
1109 char *xalias = NULL;
1110 struct tbl *ap;
1111 uint32_t h;
1112
1113 if ((val = cstrchr(alias, '='))) {
1114 strndupx(xalias, alias, val++ - alias, ATEMP);
1115 alias = xalias;
1116 }
1117 h = hash(alias);
1118 if (val == NULL && !tflag && !xflag) {
1119 ap = ktsearch(t, alias, h);
1120 if (ap != NULL && (ap->flag&ISSET)) {
1121 if (pflag)
1122 shprintf("%s ", Talias);
1123 shf_puts(ap->name, shl_stdout);
1124 if (prefix != '+') {
1125 shf_putc('=', shl_stdout);
1126 print_value_quoted(shl_stdout, ap->val.s);
1127 }
1128 shf_putc('\n', shl_stdout);
1129 } else {
1130 shprintf("%s %s %s\n", alias, Talias,
1131 "not found");
1132 rv = 1;
1133 }
1134 continue;
1135 }
1136 ap = ktenter(t, alias, h);
1137 ap->type = tflag ? CTALIAS : CALIAS;
1138 /* Are we setting the value or just some flags? */
1139 if ((val && !tflag) || (!val && tflag && !Uflag)) {
1140 if (ap->flag&ALLOC) {
1141 ap->flag &= ~(ALLOC|ISSET);
1142 afree(ap->val.s, APERM);
1143 }
1144 /* ignore values for -t (AT&T ksh does this) */
1145 newval = tflag ?
1146 search_path(alias, path, X_OK, NULL) :
1147 val;
1148 if (newval) {
1149 strdupx(ap->val.s, newval, APERM);
1150 ap->flag |= ALLOC|ISSET;
1151 } else
1152 ap->flag &= ~ISSET;
1153 }
1154 ap->flag |= DEFINED;
1155 if (prefix == '+')
1156 ap->flag &= ~xflag;
1157 else
1158 ap->flag |= xflag;
1159 afree(xalias, ATEMP);
1160 }
1161
1162 return (rv);
1163 }
1164
1165 int
c_unalias(const char ** wp)1166 c_unalias(const char **wp)
1167 {
1168 struct table *t = &aliases;
1169 struct tbl *ap;
1170 int optc, rv = 0;
1171 bool all = false;
1172
1173 while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != -1)
1174 switch (optc) {
1175 case 'a':
1176 all = true;
1177 break;
1178 case 'd':
1179 #ifdef MKSH_NOPWNAM
1180 /* fix "unalias -dt" */
1181 t = NULL;
1182 #else
1183 t = &homedirs;
1184 #endif
1185 break;
1186 case 't':
1187 t = &taliases;
1188 break;
1189 case '?':
1190 return (1);
1191 }
1192 #ifdef MKSH_NOPWNAM
1193 if (t == NULL)
1194 return (0);
1195 #endif
1196 wp += builtin_opt.optind;
1197
1198 for (; *wp != NULL; wp++) {
1199 ap = ktsearch(t, *wp, hash(*wp));
1200 if (ap == NULL) {
1201 /* POSIX */
1202 rv = 1;
1203 continue;
1204 }
1205 if (ap->flag&ALLOC) {
1206 ap->flag &= ~(ALLOC|ISSET);
1207 afree(ap->val.s, APERM);
1208 }
1209 ap->flag &= ~(DEFINED|ISSET|EXPORT);
1210 }
1211
1212 if (all) {
1213 struct tstate ts;
1214
1215 for (ktwalk(&ts, t); (ap = ktnext(&ts)); ) {
1216 if (ap->flag&ALLOC) {
1217 ap->flag &= ~(ALLOC|ISSET);
1218 afree(ap->val.s, APERM);
1219 }
1220 ap->flag &= ~(DEFINED|ISSET|EXPORT);
1221 }
1222 }
1223
1224 return (rv);
1225 }
1226
1227 int
c_let(const char ** wp)1228 c_let(const char **wp)
1229 {
1230 int rv = 1;
1231 mksh_ari_t val;
1232
1233 if (wp[1] == NULL)
1234 /* AT&T ksh does this */
1235 bi_errorf("no arguments");
1236 else
1237 for (wp++; *wp; wp++)
1238 if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
1239 /* distinguish error from zero result */
1240 rv = 2;
1241 break;
1242 } else
1243 rv = val == 0;
1244 return (rv);
1245 }
1246
1247 int
c_jobs(const char ** wp)1248 c_jobs(const char **wp)
1249 {
1250 int optc, flag = 0, nflag = 0, rv = 0;
1251
1252 while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != -1)
1253 switch (optc) {
1254 case 'l':
1255 flag = 1;
1256 break;
1257 case 'p':
1258 flag = 2;
1259 break;
1260 case 'n':
1261 nflag = 1;
1262 break;
1263 case 'z':
1264 /* debugging: print zombies */
1265 nflag = -1;
1266 break;
1267 case '?':
1268 return (1);
1269 }
1270 wp += builtin_opt.optind;
1271 if (!*wp) {
1272 if (j_jobs(NULL, flag, nflag))
1273 rv = 1;
1274 } else {
1275 for (; *wp; wp++)
1276 if (j_jobs(*wp, flag, nflag))
1277 rv = 1;
1278 }
1279 return (rv);
1280 }
1281
1282 #ifndef MKSH_UNEMPLOYED
1283 int
c_fgbg(const char ** wp)1284 c_fgbg(const char **wp)
1285 {
1286 bool bg = strcmp(*wp, "bg") == 0;
1287 int rv = 0;
1288
1289 if (!Flag(FMONITOR)) {
1290 bi_errorf("job control not enabled");
1291 return (1);
1292 }
1293 if (ksh_getopt(wp, &builtin_opt, null) == '?')
1294 return (1);
1295 wp += builtin_opt.optind;
1296 if (*wp)
1297 for (; *wp; wp++)
1298 rv = j_resume(*wp, bg);
1299 else
1300 rv = j_resume("%%", bg);
1301 return (bg ? 0 : rv);
1302 }
1303 #endif
1304
1305 /* format a single kill item */
1306 static char *
kill_fmt_entry(char * buf,size_t buflen,unsigned int i,const void * arg)1307 kill_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
1308 {
1309 const struct kill_info *ki = (const struct kill_info *)arg;
1310
1311 i++;
1312 shf_snprintf(buf, buflen, "%*u %*s %s",
1313 ki->num_width, i,
1314 ki->name_width, sigtraps[i].name,
1315 sigtraps[i].mess);
1316 return (buf);
1317 }
1318
1319 int
c_kill(const char ** wp)1320 c_kill(const char **wp)
1321 {
1322 Trap *t = NULL;
1323 const char *p;
1324 bool lflag = false;
1325 int i, n, rv, sig;
1326
1327 /* assume old style options if -digits or -UPPERCASE */
1328 if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
1329 ksh_isupper(p[1]))) {
1330 if (!(t = gettrap(p + 1, false))) {
1331 bi_errorf("bad signal '%s'", p + 1);
1332 return (1);
1333 }
1334 i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
1335 } else {
1336 int optc;
1337
1338 while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != -1)
1339 switch (optc) {
1340 case 'l':
1341 lflag = true;
1342 break;
1343 case 's':
1344 if (!(t = gettrap(builtin_opt.optarg, true))) {
1345 bi_errorf("bad signal '%s'",
1346 builtin_opt.optarg);
1347 return (1);
1348 }
1349 break;
1350 case '?':
1351 return (1);
1352 }
1353 i = builtin_opt.optind;
1354 }
1355 if ((lflag && t) || (!wp[i] && !lflag)) {
1356 #ifndef MKSH_SMALL
1357 shf_puts("usage:\tkill [-s signame | -signum | -signame]"
1358 " { job | pid | pgrp } ...\n"
1359 "\tkill -l [exit_status ...]\n", shl_out);
1360 #endif
1361 bi_errorfz();
1362 return (1);
1363 }
1364
1365 if (lflag) {
1366 if (wp[i]) {
1367 for (; wp[i]; i++) {
1368 if (!bi_getn(wp[i], &n))
1369 return (1);
1370 #if (NSIG < 128)
1371 if (n > 128 && n < 128 + NSIG)
1372 n -= 128;
1373 #endif
1374 if (n > 0 && n < NSIG)
1375 shprintf("%s\n", sigtraps[n].name);
1376 else
1377 shprintf("%d\n", n);
1378 }
1379 } else {
1380 ssize_t w, mess_cols, mess_octs;
1381 int j;
1382 struct kill_info ki;
1383
1384 for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
1385 ki.num_width++;
1386 ki.name_width = mess_cols = mess_octs = 0;
1387 for (j = 0; j < NSIG; j++) {
1388 w = strlen(sigtraps[j].name);
1389 if (w > ki.name_width)
1390 ki.name_width = w;
1391 w = strlen(sigtraps[j].mess);
1392 if (w > mess_octs)
1393 mess_octs = w;
1394 w = utf_mbswidth(sigtraps[j].mess);
1395 if (w > mess_cols)
1396 mess_cols = w;
1397 }
1398
1399 print_columns(shl_stdout, (unsigned int)(NSIG - 1),
1400 kill_fmt_entry, (void *)&ki,
1401 ki.num_width + 1 + ki.name_width + 1 + mess_octs,
1402 ki.num_width + 1 + ki.name_width + 1 + mess_cols,
1403 true);
1404 }
1405 return (0);
1406 }
1407 rv = 0;
1408 sig = t ? t->signal : SIGTERM;
1409 for (; (p = wp[i]); i++) {
1410 if (*p == '%') {
1411 if (j_kill(p, sig))
1412 rv = 1;
1413 } else if (!getn(p, &n)) {
1414 bi_errorf("%s: %s", p,
1415 "arguments must be jobs or process IDs");
1416 rv = 1;
1417 } else {
1418 if (mksh_kill(n, sig) < 0) {
1419 bi_errorf("%s: %s", p, cstrerror(errno));
1420 rv = 1;
1421 }
1422 }
1423 }
1424 return (rv);
1425 }
1426
1427 void
getopts_reset(int val)1428 getopts_reset(int val)
1429 {
1430 if (val >= 1) {
1431 ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT);
1432 user_opt.optind = user_opt.uoptind = val;
1433 }
1434 }
1435
1436 int
c_getopts(const char ** wp)1437 c_getopts(const char **wp)
1438 {
1439 int argc, optc, rv;
1440 const char *opts, *var;
1441 char buf[3];
1442 struct tbl *vq, *voptarg;
1443
1444 if (ksh_getopt(wp, &builtin_opt, null) == '?')
1445 return (1);
1446 wp += builtin_opt.optind;
1447
1448 opts = *wp++;
1449 if (!opts) {
1450 bi_errorf("missing %s argument", "options");
1451 return (1);
1452 }
1453
1454 var = *wp++;
1455 if (!var) {
1456 bi_errorf("missing %s argument", "name");
1457 return (1);
1458 }
1459 if (!*var || *skip_varname(var, true)) {
1460 bi_errorf("%s: %s", var, "is not an identifier");
1461 return (1);
1462 }
1463
1464 if (e->loc->next == NULL) {
1465 internal_warningf("%s: %s", "c_getopts", "no argv");
1466 return (1);
1467 }
1468 /* Which arguments are we parsing... */
1469 if (*wp == NULL)
1470 wp = e->loc->next->argv;
1471 else
1472 *--wp = e->loc->next->argv[0];
1473
1474 /* Check that our saved state won't cause a core dump... */
1475 for (argc = 0; wp[argc]; argc++)
1476 ;
1477 if (user_opt.optind > argc ||
1478 (user_opt.p != 0 &&
1479 user_opt.p > strlen(wp[user_opt.optind - 1]))) {
1480 bi_errorf("arguments changed since last call");
1481 return (1);
1482 }
1483
1484 user_opt.optarg = NULL;
1485 optc = ksh_getopt(wp, &user_opt, opts);
1486
1487 if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
1488 buf[0] = '+';
1489 buf[1] = optc;
1490 buf[2] = '\0';
1491 } else {
1492 /*
1493 * POSIX says var is set to ? at end-of-options, AT&T ksh
1494 * sets it to null - we go with POSIX...
1495 */
1496 buf[0] = optc < 0 ? '?' : optc;
1497 buf[1] = '\0';
1498 }
1499
1500 /* AT&T ksh93 in fact does change OPTIND for unknown options too */
1501 user_opt.uoptind = user_opt.optind;
1502
1503 voptarg = global("OPTARG");
1504 /* AT&T ksh clears ro and int */
1505 voptarg->flag &= ~RDONLY;
1506 /* Paranoia: ensure no bizarre results. */
1507 if (voptarg->flag & INTEGER)
1508 typeset("OPTARG", 0, INTEGER, 0, 0);
1509 if (user_opt.optarg == NULL)
1510 unset(voptarg, 1);
1511 else
1512 /* This can't fail (have cleared readonly/integer) */
1513 setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
1514
1515 rv = 0;
1516
1517 vq = global(var);
1518 /* Error message already printed (integer, readonly) */
1519 if (!setstr(vq, buf, KSH_RETURN_ERROR))
1520 rv = 2;
1521 if (Flag(FEXPORT))
1522 typeset(var, EXPORT, 0, 0, 0);
1523
1524 return (optc < 0 ? 1 : rv);
1525 }
1526
1527 #ifndef MKSH_NO_CMDLINE_EDITING
1528 int
c_bind(const char ** wp)1529 c_bind(const char **wp)
1530 {
1531 int optc, rv = 0;
1532 #ifndef MKSH_SMALL
1533 bool macro = false;
1534 #endif
1535 bool list = false;
1536 const char *cp;
1537 char *up;
1538
1539 while ((optc = ksh_getopt(wp, &builtin_opt,
1540 #ifndef MKSH_SMALL
1541 "lm"
1542 #else
1543 "l"
1544 #endif
1545 )) != -1)
1546 switch (optc) {
1547 case 'l':
1548 list = true;
1549 break;
1550 #ifndef MKSH_SMALL
1551 case 'm':
1552 macro = true;
1553 break;
1554 #endif
1555 case '?':
1556 return (1);
1557 }
1558 wp += builtin_opt.optind;
1559
1560 if (*wp == NULL)
1561 /* list all */
1562 rv = x_bind(NULL, NULL,
1563 #ifndef MKSH_SMALL
1564 false,
1565 #endif
1566 list);
1567
1568 for (; *wp != NULL; wp++) {
1569 if ((cp = cstrchr(*wp, '=')) == NULL)
1570 up = NULL;
1571 else {
1572 strdupx(up, *wp, ATEMP);
1573 up[cp++ - *wp] = '\0';
1574 }
1575 if (x_bind(up ? up : *wp, cp,
1576 #ifndef MKSH_SMALL
1577 macro,
1578 #endif
1579 false))
1580 rv = 1;
1581 afree(up, ATEMP);
1582 }
1583
1584 return (rv);
1585 }
1586 #endif
1587
1588 int
c_shift(const char ** wp)1589 c_shift(const char **wp)
1590 {
1591 struct block *l = e->loc;
1592 int n;
1593 mksh_ari_t val;
1594 const char *arg;
1595
1596 if (ksh_getopt(wp, &builtin_opt, null) == '?')
1597 return (1);
1598 arg = wp[builtin_opt.optind];
1599
1600 if (!arg)
1601 n = 1;
1602 else if (!evaluate(arg, &val, KSH_RETURN_ERROR, false)) {
1603 /* error already printed */
1604 bi_errorfz();
1605 return (1);
1606 } else if (!(n = val)) {
1607 /* nothing to do */
1608 return (0);
1609 } else if (n < 0) {
1610 bi_errorf("%s: %s", arg, "bad number");
1611 return (1);
1612 }
1613 if (l->argc < n) {
1614 bi_errorf("nothing to shift");
1615 return (1);
1616 }
1617 l->argv[n] = l->argv[0];
1618 l->argv += n;
1619 l->argc -= n;
1620 return (0);
1621 }
1622
1623 int
c_umask(const char ** wp)1624 c_umask(const char **wp)
1625 {
1626 int i, optc;
1627 const char *cp;
1628 bool symbolic = false;
1629 mode_t old_umask;
1630
1631 while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1)
1632 switch (optc) {
1633 case 'S':
1634 symbolic = true;
1635 break;
1636 case '?':
1637 return (1);
1638 }
1639 cp = wp[builtin_opt.optind];
1640 if (cp == NULL) {
1641 old_umask = umask((mode_t)0);
1642 umask(old_umask);
1643 if (symbolic) {
1644 char buf[18], *p;
1645 int j;
1646
1647 old_umask = ~old_umask;
1648 p = buf;
1649 for (i = 0; i < 3; i++) {
1650 *p++ = "ugo"[i];
1651 *p++ = '=';
1652 for (j = 0; j < 3; j++)
1653 if (old_umask & (1 << (8 - (3*i + j))))
1654 *p++ = "rwx"[j];
1655 *p++ = ',';
1656 }
1657 p[-1] = '\0';
1658 shprintf("%s\n", buf);
1659 } else
1660 shprintf("%#3.3o\n", (unsigned int)old_umask);
1661 } else {
1662 mode_t new_umask;
1663
1664 if (ksh_isdigit(*cp)) {
1665 for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
1666 new_umask = new_umask * 8 + (*cp - '0');
1667 if (*cp) {
1668 bi_errorf("bad number");
1669 return (1);
1670 }
1671 } else {
1672 /* symbolic format */
1673 int positions, new_val;
1674 char op;
1675
1676 old_umask = umask((mode_t)0);
1677 /* in case of error */
1678 umask(old_umask);
1679 old_umask = ~old_umask;
1680 new_umask = old_umask;
1681 positions = 0;
1682 while (*cp) {
1683 while (*cp && vstrchr("augo", *cp))
1684 switch (*cp++) {
1685 case 'a':
1686 positions |= 0111;
1687 break;
1688 case 'u':
1689 positions |= 0100;
1690 break;
1691 case 'g':
1692 positions |= 0010;
1693 break;
1694 case 'o':
1695 positions |= 0001;
1696 break;
1697 }
1698 if (!positions)
1699 /* default is a */
1700 positions = 0111;
1701 if (!vstrchr("=+-", op = *cp))
1702 break;
1703 cp++;
1704 new_val = 0;
1705 while (*cp && vstrchr("rwxugoXs", *cp))
1706 switch (*cp++) {
1707 case 'r': new_val |= 04; break;
1708 case 'w': new_val |= 02; break;
1709 case 'x': new_val |= 01; break;
1710 case 'u':
1711 new_val |= old_umask >> 6;
1712 break;
1713 case 'g':
1714 new_val |= old_umask >> 3;
1715 break;
1716 case 'o':
1717 new_val |= old_umask >> 0;
1718 break;
1719 case 'X':
1720 if (old_umask & 0111)
1721 new_val |= 01;
1722 break;
1723 case 's':
1724 /* ignored */
1725 break;
1726 }
1727 new_val = (new_val & 07) * positions;
1728 switch (op) {
1729 case '-':
1730 new_umask &= ~new_val;
1731 break;
1732 case '=':
1733 new_umask = new_val |
1734 (new_umask & ~(positions * 07));
1735 break;
1736 case '+':
1737 new_umask |= new_val;
1738 }
1739 if (*cp == ',') {
1740 positions = 0;
1741 cp++;
1742 } else if (!vstrchr("=+-", *cp))
1743 break;
1744 }
1745 if (*cp) {
1746 bi_errorf("bad mask");
1747 return (1);
1748 }
1749 new_umask = ~new_umask;
1750 }
1751 umask(new_umask);
1752 }
1753 return (0);
1754 }
1755
1756 int
c_dot(const char ** wp)1757 c_dot(const char **wp)
1758 {
1759 const char *file, *cp, **argv;
1760 int argc, i, errcode;
1761
1762 if (ksh_getopt(wp, &builtin_opt, null) == '?')
1763 return (1);
1764
1765 if ((cp = wp[builtin_opt.optind]) == NULL) {
1766 bi_errorf("missing argument");
1767 return (1);
1768 }
1769 if ((file = search_path(cp, path, R_OK, &errcode)) == NULL) {
1770 bi_errorf("%s: %s", cp, cstrerror(errcode));
1771 return (1);
1772 }
1773
1774 /* Set positional parameters? */
1775 if (wp[builtin_opt.optind + 1]) {
1776 argv = wp + builtin_opt.optind;
1777 /* preserve $0 */
1778 argv[0] = e->loc->argv[0];
1779 for (argc = 0; argv[argc + 1]; argc++)
1780 ;
1781 } else {
1782 argc = 0;
1783 argv = NULL;
1784 }
1785 if ((i = include(file, argc, argv, false)) < 0) {
1786 /* should not happen */
1787 bi_errorf("%s: %s", cp, cstrerror(errno));
1788 return (1);
1789 }
1790 return (i);
1791 }
1792
1793 int
c_wait(const char ** wp)1794 c_wait(const char **wp)
1795 {
1796 int rv = 0, sig;
1797
1798 if (ksh_getopt(wp, &builtin_opt, null) == '?')
1799 return (1);
1800 wp += builtin_opt.optind;
1801 if (*wp == NULL) {
1802 while (waitfor(NULL, &sig) >= 0)
1803 ;
1804 rv = sig;
1805 } else {
1806 for (; *wp; wp++)
1807 rv = waitfor(*wp, &sig);
1808 if (rv < 0)
1809 /* magic exit code: bad job-id */
1810 rv = sig ? sig : 127;
1811 }
1812 return (rv);
1813 }
1814
1815 static char REPLY[] = "REPLY";
1816 int
c_read(const char ** wp)1817 c_read(const char **wp)
1818 {
1819 #define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS))
1820 int c, fd = 0, rv = 0;
1821 bool savehist = false, intoarray = false, aschars = false;
1822 bool rawmode = false, expanding = false;
1823 bool lastparmmode = false, lastparmused = false;
1824 enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
1825 char delim = '\n';
1826 size_t bytesleft = 128, bytesread;
1827 struct tbl *vp /* FU gcc */ = NULL, *vq;
1828 char *cp, *allocd = NULL, *xp;
1829 const char *ccp;
1830 XString xs;
1831 size_t xsave = 0;
1832 mksh_ttyst tios;
1833 bool restore_tios = false;
1834 #if HAVE_SELECT
1835 bool hastimeout = false;
1836 struct timeval tv, tvlim;
1837 #define c_read_opts "Aad:N:n:prst:u,"
1838 #else
1839 #define c_read_opts "Aad:N:n:prsu,"
1840 #endif
1841
1842 while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1)
1843 switch (c) {
1844 case 'a':
1845 aschars = true;
1846 /* FALLTHROUGH */
1847 case 'A':
1848 intoarray = true;
1849 break;
1850 case 'd':
1851 delim = builtin_opt.optarg[0];
1852 break;
1853 case 'N':
1854 case 'n':
1855 readmode = c == 'N' ? BYTES : UPTO;
1856 if (!bi_getn(builtin_opt.optarg, &c))
1857 return (2);
1858 if (c == -1) {
1859 readmode = readmode == BYTES ? READALL : UPTO;
1860 bytesleft = 1024;
1861 } else
1862 bytesleft = (unsigned int)c;
1863 break;
1864 case 'p':
1865 if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
1866 bi_errorf("%s: %s", "-p", ccp);
1867 return (2);
1868 }
1869 break;
1870 case 'r':
1871 rawmode = true;
1872 break;
1873 case 's':
1874 savehist = true;
1875 break;
1876 #if HAVE_SELECT
1877 case 't':
1878 if (parse_usec(builtin_opt.optarg, &tv)) {
1879 bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
1880 builtin_opt.optarg);
1881 return (2);
1882 }
1883 hastimeout = true;
1884 break;
1885 #endif
1886 case 'u':
1887 if (!builtin_opt.optarg[0])
1888 fd = 0;
1889 else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
1890 bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
1891 return (2);
1892 }
1893 break;
1894 case '?':
1895 return (2);
1896 }
1897 wp += builtin_opt.optind;
1898 if (*wp == NULL)
1899 *--wp = REPLY;
1900
1901 if (intoarray && wp[1] != NULL) {
1902 bi_errorf("too many arguments");
1903 return (2);
1904 }
1905
1906 if ((ccp = cstrchr(*wp, '?')) != NULL) {
1907 strdupx(allocd, *wp, ATEMP);
1908 allocd[ccp - *wp] = '\0';
1909 *wp = allocd;
1910 if (isatty(fd)) {
1911 /*
1912 * AT&T ksh says it prints prompt on fd if it's open
1913 * for writing and is a tty, but it doesn't do it
1914 * (it also doesn't check the interactive flag,
1915 * as is indicated in the Korn Shell book).
1916 */
1917 shf_puts(ccp + 1, shl_out);
1918 shf_flush(shl_out);
1919 }
1920 }
1921
1922 Xinit(xs, xp, bytesleft, ATEMP);
1923
1924 if (readmode == LINES)
1925 bytesleft = 1;
1926 else if (isatty(fd)) {
1927 x_mkraw(fd, &tios, true);
1928 restore_tios = true;
1929 }
1930
1931 #if HAVE_SELECT
1932 if (hastimeout) {
1933 mksh_TIME(tvlim);
1934 timeradd(&tvlim, &tv, &tvlim);
1935 }
1936 #endif
1937
1938 c_read_readloop:
1939 #if HAVE_SELECT
1940 if (hastimeout) {
1941 fd_set fdset;
1942
1943 FD_ZERO(&fdset);
1944 FD_SET((unsigned int)fd, &fdset);
1945 mksh_TIME(tv);
1946 timersub(&tvlim, &tv, &tv);
1947 if (tv.tv_sec < 0) {
1948 /* timeout expired globally */
1949 rv = 1;
1950 goto c_read_out;
1951 }
1952
1953 switch (select(fd + 1, &fdset, NULL, NULL, &tv)) {
1954 case 1:
1955 break;
1956 case 0:
1957 /* timeout expired for this call */
1958 rv = 1;
1959 goto c_read_out;
1960 default:
1961 bi_errorf("%s: %s", Tselect, cstrerror(errno));
1962 rv = 2;
1963 goto c_read_out;
1964 }
1965 }
1966 #endif
1967
1968 bytesread = blocking_read(fd, xp, bytesleft);
1969 if (bytesread == (size_t)-1) {
1970 /* interrupted */
1971 if (errno == EINTR && fatal_trap_check()) {
1972 /*
1973 * Was the offending signal one that would
1974 * normally kill a process? If so, pretend
1975 * the read was killed.
1976 */
1977 rv = 2;
1978 goto c_read_out;
1979 }
1980 /* just ignore the signal */
1981 goto c_read_readloop;
1982 }
1983
1984 switch (readmode) {
1985 case READALL:
1986 if (bytesread == 0) {
1987 /* end of file reached */
1988 rv = 1;
1989 goto c_read_readdone;
1990 }
1991 xp += bytesread;
1992 XcheckN(xs, xp, bytesleft);
1993 break;
1994
1995 case UPTO:
1996 if (bytesread == 0)
1997 /* end of file reached */
1998 rv = 1;
1999 xp += bytesread;
2000 goto c_read_readdone;
2001
2002 case BYTES:
2003 if (bytesread == 0) {
2004 /* end of file reached */
2005 rv = 1;
2006 xp = Xstring(xs, xp);
2007 goto c_read_readdone;
2008 }
2009 xp += bytesread;
2010 if ((bytesleft -= bytesread) == 0)
2011 goto c_read_readdone;
2012 break;
2013 case LINES:
2014 if (bytesread == 0) {
2015 /* end of file reached */
2016 rv = 1;
2017 goto c_read_readdone;
2018 }
2019 if ((c = *xp) == '\0' && !aschars && delim != '\0') {
2020 /* skip any read NULs unless delimiter */
2021 break;
2022 }
2023 if (expanding) {
2024 expanding = false;
2025 if (c == delim) {
2026 if (Flag(FTALKING_I) && isatty(fd)) {
2027 /*
2028 * set prompt in case this is
2029 * called from .profile or $ENV
2030 */
2031 set_prompt(PS2, NULL);
2032 pprompt(prompt, 0);
2033 }
2034 /* drop the backslash */
2035 --xp;
2036 /* and the delimiter */
2037 break;
2038 }
2039 } else if (c == delim) {
2040 goto c_read_readdone;
2041 } else if (!rawmode && c == '\\') {
2042 expanding = true;
2043 }
2044 Xcheck(xs, xp);
2045 ++xp;
2046 break;
2047 }
2048 goto c_read_readloop;
2049
2050 c_read_readdone:
2051 bytesread = Xlength(xs, xp);
2052 Xput(xs, xp, '\0');
2053
2054 /*-
2055 * state: we finished reading the input and NUL terminated it
2056 * Xstring(xs, xp) -> xp-1 = input string without trailing delim
2057 * rv = 1 if EOF, 0 otherwise (errors handled already)
2058 */
2059
2060 if (rv == 1) {
2061 /* clean up coprocess if needed, on EOF */
2062 coproc_read_close(fd);
2063 if (readmode == READALL)
2064 /* EOF is no error here */
2065 rv = 0;
2066 }
2067
2068 if (savehist)
2069 histsave(&source->line, Xstring(xs, xp), true, false);
2070
2071 ccp = cp = Xclose(xs, xp);
2072 expanding = false;
2073 XinitN(xs, 128, ATEMP);
2074 if (intoarray) {
2075 vp = global(*wp);
2076 if (vp->flag & RDONLY) {
2077 c_read_splitro:
2078 bi_errorf("read-only: %s", *wp);
2079 c_read_spliterr:
2080 rv = 2;
2081 afree(cp, ATEMP);
2082 goto c_read_out;
2083 }
2084 /* exporting an array is currently pointless */
2085 unset(vp, 1);
2086 /* counter for array index */
2087 c = 0;
2088 }
2089 if (!aschars) {
2090 /* skip initial IFS whitespace */
2091 while (bytesread && is_ifsws(*ccp)) {
2092 ++ccp;
2093 --bytesread;
2094 }
2095 /* trim trailing IFS whitespace */
2096 while (bytesread && is_ifsws(ccp[bytesread - 1])) {
2097 --bytesread;
2098 }
2099 }
2100 c_read_splitloop:
2101 xp = Xstring(xs, xp);
2102 /* generate next word */
2103 if (!bytesread) {
2104 /* no more input */
2105 if (intoarray)
2106 goto c_read_splitdone;
2107 /* zero out next parameters */
2108 goto c_read_gotword;
2109 }
2110 if (aschars) {
2111 Xput(xs, xp, '1');
2112 Xput(xs, xp, '#');
2113 bytesleft = utf_ptradj(ccp);
2114 while (bytesleft && bytesread) {
2115 *xp++ = *ccp++;
2116 --bytesleft;
2117 --bytesread;
2118 }
2119 if (xp[-1] == '\0') {
2120 xp[-1] = '0';
2121 xp[-3] = '2';
2122 }
2123 goto c_read_gotword;
2124 }
2125
2126 if (!intoarray && wp[1] == NULL)
2127 lastparmmode = true;
2128
2129 c_read_splitlast:
2130 /* copy until IFS character */
2131 while (bytesread) {
2132 char ch;
2133
2134 ch = *ccp;
2135 if (expanding) {
2136 expanding = false;
2137 goto c_read_splitcopy;
2138 } else if (ctype(ch, C_IFS)) {
2139 break;
2140 } else if (!rawmode && ch == '\\') {
2141 expanding = true;
2142 } else {
2143 c_read_splitcopy:
2144 Xcheck(xs, xp);
2145 Xput(xs, xp, ch);
2146 }
2147 ++ccp;
2148 --bytesread;
2149 }
2150 xsave = Xsavepos(xs, xp);
2151 /* copy word delimiter: IFSWS+IFS,IFSWS */
2152 expanding = false;
2153 while (bytesread) {
2154 char ch;
2155
2156 ch = *ccp;
2157 if (!ctype(ch, C_IFS))
2158 break;
2159 if (lastparmmode && !expanding && !rawmode && ch == '\\') {
2160 expanding = true;
2161 } else {
2162 Xcheck(xs, xp);
2163 Xput(xs, xp, ch);
2164 }
2165 ++ccp;
2166 --bytesread;
2167 if (expanding)
2168 continue;
2169 if (!ctype(ch, C_IFSWS))
2170 break;
2171 }
2172 while (bytesread && is_ifsws(*ccp)) {
2173 Xcheck(xs, xp);
2174 Xput(xs, xp, *ccp);
2175 ++ccp;
2176 --bytesread;
2177 }
2178 /* if no more parameters, rinse and repeat */
2179 if (lastparmmode && bytesread) {
2180 lastparmused = true;
2181 goto c_read_splitlast;
2182 }
2183 /* get rid of the delimiter unless we pack the rest */
2184 if (!lastparmused)
2185 xp = Xrestpos(xs, xp, xsave);
2186 c_read_gotword:
2187 Xput(xs, xp, '\0');
2188 if (intoarray) {
2189 vq = arraysearch(vp, c++);
2190 } else {
2191 vq = global(*wp);
2192 /* must be checked before exporting */
2193 if (vq->flag & RDONLY)
2194 goto c_read_splitro;
2195 if (Flag(FEXPORT))
2196 typeset(*wp, EXPORT, 0, 0, 0);
2197 }
2198 if (!setstr(vq, Xstring(xs, xp), KSH_RETURN_ERROR))
2199 goto c_read_spliterr;
2200 if (aschars) {
2201 setint_v(vq, vq, false);
2202 /* protect from UTFMODE changes */
2203 vq->type = 0;
2204 }
2205 if (intoarray || *++wp != NULL)
2206 goto c_read_splitloop;
2207
2208 c_read_splitdone:
2209 /* free up */
2210 afree(cp, ATEMP);
2211
2212 c_read_out:
2213 afree(allocd, ATEMP);
2214 Xfree(xs, xp);
2215 if (restore_tios)
2216 mksh_tcset(fd, &tios);
2217 return (rv);
2218 #undef is_ifsws
2219 }
2220
2221 int
c_eval(const char ** wp)2222 c_eval(const char **wp)
2223 {
2224 struct source *s, *saves = source;
2225 unsigned char savef;
2226 int rv;
2227
2228 if (ksh_getopt(wp, &builtin_opt, null) == '?')
2229 return (1);
2230 s = pushs(SWORDS, ATEMP);
2231 s->u.strv = wp + builtin_opt.optind;
2232
2233 /*-
2234 * The following code handles the case where the command is
2235 * empty due to failed command substitution, for example by
2236 * eval "$(false)"
2237 * This has historically returned 1 by AT&T ksh88. In this
2238 * case, shell() will not set or change exstat because the
2239 * compiled tree is empty, so it will use the value we pass
2240 * from subst_exstat, which is cleared in execute(), so it
2241 * should have been 0 if there were no substitutions.
2242 *
2243 * POSIX however says we don't do this, even though it is
2244 * traditionally done. AT&T ksh93 agrees with POSIX, so we
2245 * do. The following is an excerpt from SUSv4 [1003.2-2008]:
2246 *
2247 * 2.9.1: Simple Commands
2248 * ... If there is a command name, execution shall
2249 * continue as described in 2.9.1.1 [Command Search
2250 * and Execution]. If there is no command name, but
2251 * the command contained a command substitution, the
2252 * command shall complete with the exit status of the
2253 * last command substitution performed.
2254 * 2.9.1.1: Command Search and Execution
2255 * (1) a. If the command name matches the name of a
2256 * special built-in utility, that special built-in
2257 * utility shall be invoked.
2258 * 2.14.5: eval
2259 * If there are no arguments, or only null arguments,
2260 * eval shall return a zero exit status; ...
2261 */
2262 /* AT&T ksh88: use subst_exstat */
2263 /* exstat = subst_exstat; */
2264 /* SUSv4: OR with a high value never written otherwise */
2265 exstat |= 0x4000;
2266
2267 savef = Flag(FERREXIT);
2268 Flag(FERREXIT) |= 0x80;
2269 rv = shell(s, false);
2270 Flag(FERREXIT) = savef;
2271 source = saves;
2272 afree(s, ATEMP);
2273 if (exstat & 0x4000)
2274 /* detect old exstat, use 0 in that case */
2275 rv = 0;
2276 return (rv);
2277 }
2278
2279 int
c_trap(const char ** wp)2280 c_trap(const char **wp)
2281 {
2282 int i;
2283 const char *s;
2284 Trap *p;
2285
2286 if (ksh_getopt(wp, &builtin_opt, null) == '?')
2287 return (1);
2288 wp += builtin_opt.optind;
2289
2290 if (*wp == NULL) {
2291 for (p = sigtraps, i = NSIG + 1; --i >= 0; p++)
2292 if (p->trap != NULL) {
2293 shf_puts("trap -- ", shl_stdout);
2294 print_value_quoted(shl_stdout, p->trap);
2295 shprintf(" %s\n", p->name);
2296 }
2297 return (0);
2298 }
2299
2300 /*
2301 * Use case sensitive lookup for first arg so the
2302 * command 'exit' isn't confused with the pseudo-signal
2303 * 'EXIT'.
2304 */
2305 /* get command */
2306 s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL;
2307 if (s != NULL && s[0] == '-' && s[1] == '\0')
2308 s = NULL;
2309
2310 /* set/clear traps */
2311 i = 0;
2312 while (*wp != NULL)
2313 if ((p = gettrap(*wp++, true)) == NULL) {
2314 warningf(true, "%s: %s '%s'", builtin_argv0,
2315 "bad signal", wp[-1]);
2316 ++i;
2317 } else
2318 settrap(p, s);
2319 return (i);
2320 }
2321
2322 int
c_exitreturn(const char ** wp)2323 c_exitreturn(const char **wp)
2324 {
2325 int n, how = LEXIT;
2326 const char *arg;
2327
2328 if (ksh_getopt(wp, &builtin_opt, null) == '?')
2329 goto c_exitreturn_err;
2330 arg = wp[builtin_opt.optind];
2331
2332 if (arg)
2333 exstat = bi_getn(arg, &n) ? (n & 0xFF) : 1;
2334 else if (trap_exstat != -1)
2335 exstat = trap_exstat;
2336 if (wp[0][0] == 'r') {
2337 /* return */
2338 struct env *ep;
2339
2340 /*
2341 * need to tell if this is exit or return so trap exit will
2342 * work right (POSIX)
2343 */
2344 for (ep = e; ep; ep = ep->oenv)
2345 if (STOP_RETURN(ep->type)) {
2346 how = LRETURN;
2347 break;
2348 }
2349 }
2350
2351 if (how == LEXIT && !really_exit && j_stopped_running()) {
2352 really_exit = true;
2353 how = LSHELL;
2354 }
2355
2356 /* get rid of any i/o redirections */
2357 quitenv(NULL);
2358 unwind(how);
2359 /* NOTREACHED */
2360
2361 c_exitreturn_err:
2362 return (1);
2363 }
2364
2365 int
c_brkcont(const char ** wp)2366 c_brkcont(const char **wp)
2367 {
2368 unsigned int quit;
2369 int n;
2370 struct env *ep, *last_ep = NULL;
2371 const char *arg;
2372
2373 if (ksh_getopt(wp, &builtin_opt, null) == '?')
2374 goto c_brkcont_err;
2375 arg = wp[builtin_opt.optind];
2376
2377 if (!arg)
2378 n = 1;
2379 else if (!bi_getn(arg, &n))
2380 goto c_brkcont_err;
2381 if (n <= 0) {
2382 /* AT&T ksh does this for non-interactive shells only - weird */
2383 bi_errorf("%s: %s", arg, "bad value");
2384 goto c_brkcont_err;
2385 }
2386 quit = (unsigned int)n;
2387
2388 /* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
2389 for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
2390 if (ep->type == E_LOOP) {
2391 if (--quit == 0)
2392 break;
2393 ep->flags |= EF_BRKCONT_PASS;
2394 last_ep = ep;
2395 }
2396
2397 if (quit) {
2398 /*
2399 * AT&T ksh doesn't print a message - just does what it
2400 * can. We print a message 'cause it helps in debugging
2401 * scripts, but don't generate an error (ie, keep going).
2402 */
2403 if ((unsigned int)n == quit) {
2404 warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
2405 return (0);
2406 }
2407 /*
2408 * POSIX says if n is too big, the last enclosing loop
2409 * shall be used. Doesn't say to print an error but we
2410 * do anyway 'cause the user messed up.
2411 */
2412 if (last_ep)
2413 last_ep->flags &= ~EF_BRKCONT_PASS;
2414 warningf(true, "%s: can only %s %u level(s)",
2415 wp[0], wp[0], (unsigned int)n - quit);
2416 }
2417
2418 unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
2419 /* NOTREACHED */
2420
2421 c_brkcont_err:
2422 return (1);
2423 }
2424
2425 int
c_set(const char ** wp)2426 c_set(const char **wp)
2427 {
2428 int argi;
2429 bool setargs;
2430 struct block *l = e->loc;
2431 const char **owp;
2432
2433 if (wp[1] == NULL) {
2434 static const char *args[] = { Tset, "-", NULL };
2435 return (c_typeset(args));
2436 }
2437
2438 if ((argi = parse_args(wp, OF_SET, &setargs)) < 0)
2439 return (2);
2440 /* set $# and $* */
2441 if (setargs) {
2442 wp += argi - 1;
2443 owp = wp;
2444 /* save $0 */
2445 wp[0] = l->argv[0];
2446 while (*++wp != NULL)
2447 strdupx(*wp, *wp, &l->area);
2448 l->argc = wp - owp - 1;
2449 l->argv = alloc2(l->argc + 2, sizeof(char *), &l->area);
2450 for (wp = l->argv; (*wp++ = *owp++) != NULL; )
2451 ;
2452 }
2453 /*-
2454 * POSIX says set exit status is 0, but old scripts that use
2455 * getopt(1) use the construct
2456 * set -- $(getopt ab:c "$@")
2457 * which assumes the exit value set will be that of the $()
2458 * (subst_exstat is cleared in execute() so that it will be 0
2459 * if there are no command substitutions).
2460 */
2461 #ifdef MKSH_LEGACY_MODE
2462 /* traditional behaviour, unless set -o posix */
2463 return (Flag(FPOSIX) ? 0 : subst_exstat);
2464 #else
2465 /* conformant behaviour, unless set -o sh +o posix */
2466 return (Flag(FSH) && !Flag(FPOSIX) ? subst_exstat : 0);
2467 #endif
2468 }
2469
2470 int
c_unset(const char ** wp)2471 c_unset(const char **wp)
2472 {
2473 const char *id;
2474 int optc, rv = 0;
2475 bool unset_var = true;
2476
2477 while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1)
2478 switch (optc) {
2479 case 'f':
2480 unset_var = false;
2481 break;
2482 case 'v':
2483 unset_var = true;
2484 break;
2485 case '?':
2486 /*XXX not reached due to GF_ERROR */
2487 return (2);
2488 }
2489 wp += builtin_opt.optind;
2490 for (; (id = *wp) != NULL; wp++)
2491 if (unset_var) {
2492 /* unset variable */
2493 struct tbl *vp;
2494 char *cp = NULL;
2495 size_t n;
2496
2497 n = strlen(id);
2498 if (n > 3 && id[n-3] == '[' && id[n-2] == '*' &&
2499 id[n-1] == ']') {
2500 strndupx(cp, id, n - 3, ATEMP);
2501 id = cp;
2502 optc = 3;
2503 } else
2504 optc = vstrchr(id, '[') ? 0 : 1;
2505
2506 vp = global(id);
2507 afree(cp, ATEMP);
2508
2509 if ((vp->flag&RDONLY)) {
2510 warningf(true, "read-only: %s", vp->name);
2511 rv = 1;
2512 } else
2513 unset(vp, optc);
2514 } else
2515 /* unset function */
2516 define(id, NULL);
2517 return (rv);
2518 }
2519
2520 static void
p_time(struct shf * shf,bool posix,long tv_sec,int tv_usec,int width,const char * prefix,const char * suffix)2521 p_time(struct shf *shf, bool posix, long tv_sec, int tv_usec, int width,
2522 const char *prefix, const char *suffix)
2523 {
2524 tv_usec /= 10000;
2525 if (posix)
2526 shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width,
2527 tv_sec, tv_usec, suffix);
2528 else
2529 shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width,
2530 tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix);
2531 }
2532
2533 int
c_times(const char ** wp MKSH_A_UNUSED)2534 c_times(const char **wp MKSH_A_UNUSED)
2535 {
2536 struct rusage usage;
2537
2538 getrusage(RUSAGE_SELF, &usage);
2539 p_time(shl_stdout, false, usage.ru_utime.tv_sec,
2540 usage.ru_utime.tv_usec, 0, null, " ");
2541 p_time(shl_stdout, false, usage.ru_stime.tv_sec,
2542 usage.ru_stime.tv_usec, 0, null, "\n");
2543
2544 getrusage(RUSAGE_CHILDREN, &usage);
2545 p_time(shl_stdout, false, usage.ru_utime.tv_sec,
2546 usage.ru_utime.tv_usec, 0, null, " ");
2547 p_time(shl_stdout, false, usage.ru_stime.tv_sec,
2548 usage.ru_stime.tv_usec, 0, null, "\n");
2549
2550 return (0);
2551 }
2552
2553 /*
2554 * time pipeline (really a statement, not a built-in command)
2555 */
2556 int
timex(struct op * t,int f,volatile int * xerrok)2557 timex(struct op *t, int f, volatile int *xerrok)
2558 {
2559 #define TF_NOARGS BIT(0)
2560 #define TF_NOREAL BIT(1) /* don't report real time */
2561 #define TF_POSIX BIT(2) /* report in POSIX format */
2562 int rv = 0, tf = 0;
2563 struct rusage ru0, ru1, cru0, cru1;
2564 struct timeval usrtime, systime, tv0, tv1;
2565
2566 mksh_TIME(tv0);
2567 getrusage(RUSAGE_SELF, &ru0);
2568 getrusage(RUSAGE_CHILDREN, &cru0);
2569 if (t->left) {
2570 /*
2571 * Two ways of getting cpu usage of a command: just use t0
2572 * and t1 (which will get cpu usage from other jobs that
2573 * finish while we are executing t->left), or get the
2574 * cpu usage of t->left. AT&T ksh does the former, while
2575 * pdksh tries to do the later (the j_usrtime hack doesn't
2576 * really work as it only counts the last job).
2577 */
2578 timerclear(&j_usrtime);
2579 timerclear(&j_systime);
2580 rv = execute(t->left, f | XTIME, xerrok);
2581 if (t->left->type == TCOM)
2582 tf |= t->left->str[0];
2583 mksh_TIME(tv1);
2584 getrusage(RUSAGE_SELF, &ru1);
2585 getrusage(RUSAGE_CHILDREN, &cru1);
2586 } else
2587 tf = TF_NOARGS;
2588
2589 if (tf & TF_NOARGS) {
2590 /* ksh93 - report shell times (shell+kids) */
2591 tf |= TF_NOREAL;
2592 timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
2593 timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
2594 } else {
2595 timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime);
2596 timeradd(&usrtime, &j_usrtime, &usrtime);
2597 timersub(&ru1.ru_stime, &ru0.ru_stime, &systime);
2598 timeradd(&systime, &j_systime, &systime);
2599 }
2600
2601 if (!(tf & TF_NOREAL)) {
2602 timersub(&tv1, &tv0, &tv1);
2603 if (tf & TF_POSIX)
2604 p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
2605 5, "real ", "\n");
2606 else
2607 p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
2608 5, null, " real ");
2609 }
2610 if (tf & TF_POSIX)
2611 p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
2612 5, "user ", "\n");
2613 else
2614 p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
2615 5, null, " user ");
2616 if (tf & TF_POSIX)
2617 p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
2618 5, "sys ", "\n");
2619 else
2620 p_time(shl_out, false, systime.tv_sec, systime.tv_usec,
2621 5, null, " system\n");
2622 shf_flush(shl_out);
2623
2624 return (rv);
2625 }
2626
2627 void
timex_hook(struct op * t,char ** volatile * app)2628 timex_hook(struct op *t, char **volatile *app)
2629 {
2630 char **wp = *app;
2631 int optc, i, j;
2632 Getopt opt;
2633
2634 ksh_getopt_reset(&opt, 0);
2635 /* start at the start */
2636 opt.optind = 0;
2637 while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1)
2638 switch (optc) {
2639 case 'p':
2640 t->str[0] |= TF_POSIX;
2641 break;
2642 case '?':
2643 errorf("time: -%s %s", opt.optarg,
2644 "unknown option");
2645 case ':':
2646 errorf("time: -%s %s", opt.optarg,
2647 "requires an argument");
2648 }
2649 /* Copy command words down over options. */
2650 if (opt.optind != 0) {
2651 for (i = 0; i < opt.optind; i++)
2652 afree(wp[i], ATEMP);
2653 for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
2654 ;
2655 }
2656 if (!wp[0])
2657 t->str[0] |= TF_NOARGS;
2658 *app = wp;
2659 }
2660
2661 /* exec with no args - args case is taken care of in comexec() */
2662 int
c_exec(const char ** wp MKSH_A_UNUSED)2663 c_exec(const char **wp MKSH_A_UNUSED)
2664 {
2665 int i;
2666
2667 /* make sure redirects stay in place */
2668 if (e->savefd != NULL) {
2669 for (i = 0; i < NUFILE; i++) {
2670 if (e->savefd[i] > 0)
2671 close(e->savefd[i]);
2672 #ifndef MKSH_LEGACY_MODE
2673 /*
2674 * keep all file descriptors > 2 private for ksh,
2675 * but not for POSIX or legacy/kludge sh
2676 */
2677 if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
2678 e->savefd[i])
2679 fcntl(i, F_SETFD, FD_CLOEXEC);
2680 #endif
2681 }
2682 e->savefd = NULL;
2683 }
2684 return (0);
2685 }
2686
2687 #if HAVE_MKNOD
2688 int
c_mknod(const char ** wp)2689 c_mknod(const char **wp)
2690 {
2691 int argc, optc, rv = 0;
2692 bool ismkfifo = false;
2693 const char **argv;
2694 void *set = NULL;
2695 mode_t mode = 0, oldmode = 0;
2696
2697 while ((optc = ksh_getopt(wp, &builtin_opt, "m:")) != -1) {
2698 switch (optc) {
2699 case 'm':
2700 set = setmode(builtin_opt.optarg);
2701 if (set == NULL) {
2702 bi_errorf("invalid file mode");
2703 return (1);
2704 }
2705 mode = getmode(set, (mode_t)(DEFFILEMODE));
2706 free_ossetmode(set);
2707 break;
2708 default:
2709 goto c_mknod_usage;
2710 }
2711 }
2712 argv = &wp[builtin_opt.optind];
2713 if (argv[0] == NULL)
2714 goto c_mknod_usage;
2715 for (argc = 0; argv[argc]; argc++)
2716 ;
2717 if (argc == 2 && argv[1][0] == 'p')
2718 ismkfifo = true;
2719 else if (argc != 4 || (argv[1][0] != 'b' && argv[1][0] != 'c'))
2720 goto c_mknod_usage;
2721
2722 if (set != NULL)
2723 oldmode = umask((mode_t)0);
2724 else
2725 mode = DEFFILEMODE;
2726
2727 mode |= (argv[1][0] == 'b') ? S_IFBLK :
2728 (argv[1][0] == 'c') ? S_IFCHR : 0;
2729
2730 if (!ismkfifo) {
2731 unsigned long majnum, minnum;
2732 dev_t dv;
2733 char *c;
2734
2735 majnum = strtoul(argv[2], &c, 0);
2736 if ((c == argv[2]) || (*c != '\0')) {
2737 bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
2738 goto c_mknod_err;
2739 }
2740 minnum = strtoul(argv[3], &c, 0);
2741 if ((c == argv[3]) || (*c != '\0')) {
2742 bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
2743 goto c_mknod_err;
2744 }
2745 dv = makedev(majnum, minnum);
2746 if ((unsigned long)(major(dv)) != majnum) {
2747 bi_errorf("%s %s too large: %lu", "device", "major", majnum);
2748 goto c_mknod_err;
2749 }
2750 if ((unsigned long)(minor(dv)) != minnum) {
2751 bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
2752 goto c_mknod_err;
2753 }
2754 if (mknod(argv[0], mode, dv))
2755 goto c_mknod_failed;
2756 } else if (mkfifo(argv[0], mode)) {
2757 c_mknod_failed:
2758 bi_errorf("%s: %s", argv[0], cstrerror(errno));
2759 c_mknod_err:
2760 rv = 1;
2761 }
2762
2763 if (set)
2764 umask(oldmode);
2765 return (rv);
2766 c_mknod_usage:
2767 bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor");
2768 bi_errorf("%s: %s", "usage", "mknod [-m mode] name p");
2769 return (1);
2770 }
2771 #endif
2772
2773 /*-
2774 test(1) roughly accepts the following grammar:
2775 oexpr ::= aexpr | aexpr "-o" oexpr ;
2776 aexpr ::= nexpr | nexpr "-a" aexpr ;
2777 nexpr ::= primary | "!" nexpr ;
2778 primary ::= unary-operator operand
2779 | operand binary-operator operand
2780 | operand
2781 | "(" oexpr ")"
2782 ;
2783
2784 unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
2785 "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
2786 "-L"|"-h"|"-S"|"-H";
2787
2788 binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
2789 "-nt"|"-ot"|"-ef"|
2790 "<"|">" # rules used for [[ ... ]] expressions
2791 ;
2792 operand ::= <anything>
2793 */
2794
2795 /* POSIX says > 1 for errors */
2796 #define T_ERR_EXIT 2
2797
2798 int
c_test(const char ** wp)2799 c_test(const char **wp)
2800 {
2801 int argc, rv, invert = 0;
2802 Test_env te;
2803 Test_op op;
2804 const char *lhs, **swp;
2805
2806 te.flags = 0;
2807 te.isa = ptest_isa;
2808 te.getopnd = ptest_getopnd;
2809 te.eval = test_eval;
2810 te.error = ptest_error;
2811
2812 for (argc = 0; wp[argc]; argc++)
2813 ;
2814
2815 if (strcmp(wp[0], "[") == 0) {
2816 if (strcmp(wp[--argc], "]") != 0) {
2817 bi_errorf("missing ]");
2818 return (T_ERR_EXIT);
2819 }
2820 }
2821
2822 te.pos.wp = wp + 1;
2823 te.wp_end = wp + argc;
2824
2825 /*
2826 * Attempt to conform to POSIX special cases. This is pretty
2827 * dumb code straight-forward from the 2008 spec, but unless
2828 * the old pdksh code doesn't live from so many assumptions.
2829 * It does, though, inline some calls to '(*te.funcname)()'.
2830 */
2831 switch (argc - 1) {
2832 case 0:
2833 return (1);
2834 case 1:
2835 ptest_one:
2836 op = TO_STNZE;
2837 goto ptest_unary;
2838 case 2:
2839 ptest_two:
2840 if (ptest_isa(&te, TM_NOT)) {
2841 ++invert;
2842 goto ptest_one;
2843 }
2844 if ((op = ptest_isa(&te, TM_UNOP))) {
2845 ptest_unary:
2846 rv = test_eval(&te, op, *te.pos.wp++, NULL, true);
2847 ptest_out:
2848 return ((invert & 1) ? rv : !rv);
2849 }
2850 /* let the parser deal with anything else */
2851 break;
2852 case 3:
2853 ptest_three:
2854 swp = te.pos.wp;
2855 /* use inside knowledge of ptest_getopnd inlined below */
2856 lhs = *te.pos.wp++;
2857 if ((op = ptest_isa(&te, TM_BINOP))) {
2858 /* test lhs op rhs */
2859 rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
2860 goto ptest_out;
2861 }
2862 /* back up to lhs */
2863 te.pos.wp = swp;
2864 if (ptest_isa(&te, TM_NOT)) {
2865 ++invert;
2866 goto ptest_two;
2867 }
2868 if (ptest_isa(&te, TM_OPAREN)) {
2869 swp = te.pos.wp;
2870 /* skip operand, without evaluation */
2871 te.pos.wp++;
2872 /* check for closing parenthesis */
2873 op = ptest_isa(&te, TM_CPAREN);
2874 /* back up to operand */
2875 te.pos.wp = swp;
2876 /* if there was a closing paren, handle it */
2877 if (op)
2878 goto ptest_one;
2879 /* backing up is done before calling the parser */
2880 }
2881 /* let the parser deal with it */
2882 break;
2883 case 4:
2884 if (ptest_isa(&te, TM_NOT)) {
2885 ++invert;
2886 goto ptest_three;
2887 }
2888 if (ptest_isa(&te, TM_OPAREN)) {
2889 swp = te.pos.wp;
2890 /* skip two operands, without evaluation */
2891 te.pos.wp++;
2892 te.pos.wp++;
2893 /* check for closing parenthesis */
2894 op = ptest_isa(&te, TM_CPAREN);
2895 /* back up to first operand */
2896 te.pos.wp = swp;
2897 /* if there was a closing paren, handle it */
2898 if (op)
2899 goto ptest_two;
2900 /* backing up is done before calling the parser */
2901 }
2902 /* defer this to the parser */
2903 break;
2904 }
2905
2906 /* "The results are unspecified." */
2907 te.pos.wp = wp + 1;
2908 return (test_parse(&te));
2909 }
2910
2911 /*
2912 * Generic test routines.
2913 */
2914
2915 Test_op
test_isop(Test_meta meta,const char * s)2916 test_isop(Test_meta meta, const char *s)
2917 {
2918 char sc1;
2919 const struct t_op *tbl;
2920
2921 tbl = meta == TM_UNOP ? u_ops : b_ops;
2922 if (*s) {
2923 sc1 = s[1];
2924 for (; tbl->op_text[0]; tbl++)
2925 if (sc1 == tbl->op_text[1] && !strcmp(s, tbl->op_text))
2926 return (tbl->op_num);
2927 }
2928 return (TO_NONOP);
2929 }
2930
2931 int
test_eval(Test_env * te,Test_op op,const char * opnd1,const char * opnd2,bool do_eval)2932 test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
2933 bool do_eval)
2934 {
2935 int i, s;
2936 size_t k;
2937 struct stat b1, b2;
2938 mksh_ari_t v1, v2;
2939
2940 if (!do_eval)
2941 return (0);
2942
2943 #ifdef DEBUG
2944 switch (op) {
2945 /* Binary operators */
2946 case TO_STEQL:
2947 case TO_STNEQ:
2948 case TO_STLT:
2949 case TO_STGT:
2950 case TO_INTEQ:
2951 case TO_INTNE:
2952 case TO_INTGT:
2953 case TO_INTGE:
2954 case TO_INTLT:
2955 case TO_INTLE:
2956 case TO_FILEQ:
2957 case TO_FILNT:
2958 case TO_FILOT:
2959 /* consistency check, but does not happen in practice */
2960 if (!opnd2) {
2961 te->flags |= TEF_ERROR;
2962 return (1);
2963 }
2964 break;
2965 default:
2966 /* for completeness of switch */
2967 break;
2968 }
2969 #endif
2970
2971 switch (op) {
2972
2973 /*
2974 * Unary Operators
2975 */
2976
2977 /* -n */
2978 case TO_STNZE:
2979 return (*opnd1 != '\0');
2980
2981 /* -z */
2982 case TO_STZER:
2983 return (*opnd1 == '\0');
2984
2985 /* -o */
2986 case TO_OPTION:
2987 if ((i = *opnd1) == '!' || i == '?')
2988 opnd1++;
2989 if ((k = option(opnd1)) == (size_t)-1)
2990 return (0);
2991 return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k));
2992
2993 /* -r */
2994 case TO_FILRD:
2995 /* LINTED use of access */
2996 return (access(opnd1, R_OK) == 0);
2997
2998 /* -w */
2999 case TO_FILWR:
3000 /* LINTED use of access */
3001 return (access(opnd1, W_OK) == 0);
3002
3003 /* -x */
3004 case TO_FILEX:
3005 return (ksh_access(opnd1, X_OK) == 0);
3006
3007 /* -a */
3008 case TO_FILAXST:
3009 /* -e */
3010 case TO_FILEXST:
3011 return (stat(opnd1, &b1) == 0);
3012
3013 /* -r */
3014 case TO_FILREG:
3015 return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
3016
3017 /* -d */
3018 case TO_FILID:
3019 return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
3020
3021 /* -c */
3022 case TO_FILCDEV:
3023 return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
3024
3025 /* -b */
3026 case TO_FILBDEV:
3027 return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
3028
3029 /* -p */
3030 case TO_FILFIFO:
3031 return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
3032
3033 /* -h or -L */
3034 case TO_FILSYM:
3035 #ifdef MKSH__NO_SYMLINK
3036 return (0);
3037 #else
3038 return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
3039 #endif
3040
3041 /* -S */
3042 case TO_FILSOCK:
3043 return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
3044
3045 /* -H => HP context dependent files (directories) */
3046 case TO_FILCDF:
3047 #ifdef S_ISCDF
3048 {
3049 char *nv;
3050
3051 /*
3052 * Append a + to filename and check to see if result is
3053 * a setuid directory. CDF stuff in general is hookey,
3054 * since it breaks for, e.g., the following sequence:
3055 * echo hi >foo+; mkdir foo; echo bye >foo/default;
3056 * chmod u+s foo (foo+ refers to the file with hi in it,
3057 * there is no way to get at the file with bye in it;
3058 * please correct me if I'm wrong about this).
3059 */
3060
3061 nv = shf_smprintf("%s+", opnd1);
3062 i = (stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
3063 afree(nv, ATEMP);
3064 return (i);
3065 }
3066 #else
3067 return (0);
3068 #endif
3069
3070 /* -u */
3071 case TO_FILSETU:
3072 return (stat(opnd1, &b1) == 0 &&
3073 (b1.st_mode & S_ISUID) == S_ISUID);
3074
3075 /* -g */
3076 case TO_FILSETG:
3077 return (stat(opnd1, &b1) == 0 &&
3078 (b1.st_mode & S_ISGID) == S_ISGID);
3079
3080 /* -k */
3081 case TO_FILSTCK:
3082 #ifdef S_ISVTX
3083 return (stat(opnd1, &b1) == 0 &&
3084 (b1.st_mode & S_ISVTX) == S_ISVTX);
3085 #else
3086 return (0);
3087 #endif
3088
3089 /* -s */
3090 case TO_FILGZ:
3091 return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
3092
3093 /* -t */
3094 case TO_FILTT:
3095 if (opnd1 && !bi_getn(opnd1, &i)) {
3096 te->flags |= TEF_ERROR;
3097 i = 0;
3098 } else
3099 i = isatty(opnd1 ? i : 0);
3100 return (i);
3101
3102 /* -O */
3103 case TO_FILUID:
3104 return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
3105
3106 /* -G */
3107 case TO_FILGID:
3108 return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
3109
3110 /*
3111 * Binary Operators
3112 */
3113
3114 /* = */
3115 case TO_STEQL:
3116 if (te->flags & TEF_DBRACKET)
3117 return (gmatchx(opnd1, opnd2, false));
3118 return (strcmp(opnd1, opnd2) == 0);
3119
3120 /* != */
3121 case TO_STNEQ:
3122 if (te->flags & TEF_DBRACKET)
3123 return (!gmatchx(opnd1, opnd2, false));
3124 return (strcmp(opnd1, opnd2) != 0);
3125
3126 /* < */
3127 case TO_STLT:
3128 return (strcmp(opnd1, opnd2) < 0);
3129
3130 /* > */
3131 case TO_STGT:
3132 return (strcmp(opnd1, opnd2) > 0);
3133
3134 /* -eq */
3135 case TO_INTEQ:
3136 /* -ne */
3137 case TO_INTNE:
3138 /* -ge */
3139 case TO_INTGE:
3140 /* -gt */
3141 case TO_INTGT:
3142 /* -le */
3143 case TO_INTLE:
3144 /* -lt */
3145 case TO_INTLT:
3146 if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) ||
3147 !evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) {
3148 /* error already printed.. */
3149 te->flags |= TEF_ERROR;
3150 return (1);
3151 }
3152 switch (op) {
3153 case TO_INTEQ:
3154 return (v1 == v2);
3155 case TO_INTNE:
3156 return (v1 != v2);
3157 case TO_INTGE:
3158 return (v1 >= v2);
3159 case TO_INTGT:
3160 return (v1 > v2);
3161 case TO_INTLE:
3162 return (v1 <= v2);
3163 case TO_INTLT:
3164 return (v1 < v2);
3165 default:
3166 /* NOTREACHED */
3167 break;
3168 }
3169 /* NOTREACHED */
3170
3171 /* -nt */
3172 case TO_FILNT:
3173 /*
3174 * ksh88/ksh93 succeed if file2 can't be stated
3175 * (subtly different from 'does not exist').
3176 */
3177 return (stat(opnd1, &b1) == 0 &&
3178 (((s = stat(opnd2, &b2)) == 0 &&
3179 b1.st_mtime > b2.st_mtime) || s < 0));
3180
3181 /* -ot */
3182 case TO_FILOT:
3183 /*
3184 * ksh88/ksh93 succeed if file1 can't be stated
3185 * (subtly different from 'does not exist').
3186 */
3187 return (stat(opnd2, &b2) == 0 &&
3188 (((s = stat(opnd1, &b1)) == 0 &&
3189 b1.st_mtime < b2.st_mtime) || s < 0));
3190
3191 /* -ef */
3192 case TO_FILEQ:
3193 return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
3194 b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
3195
3196 /* all other cases */
3197 case TO_NONOP:
3198 case TO_NONNULL:
3199 /* throw the error */
3200 break;
3201 }
3202 (*te->error)(te, 0, "internal error: unknown op");
3203 return (1);
3204 }
3205
3206 int
test_parse(Test_env * te)3207 test_parse(Test_env *te)
3208 {
3209 int rv;
3210
3211 rv = test_oexpr(te, 1);
3212
3213 if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
3214 (*te->error)(te, 0, "unexpected operator/operand");
3215
3216 return ((te->flags & TEF_ERROR) ? T_ERR_EXIT : !rv);
3217 }
3218
3219 static int
test_oexpr(Test_env * te,bool do_eval)3220 test_oexpr(Test_env *te, bool do_eval)
3221 {
3222 int rv;
3223
3224 if ((rv = test_aexpr(te, do_eval)))
3225 do_eval = false;
3226 if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
3227 return (test_oexpr(te, do_eval) || rv);
3228 return (rv);
3229 }
3230
3231 static int
test_aexpr(Test_env * te,bool do_eval)3232 test_aexpr(Test_env *te, bool do_eval)
3233 {
3234 int rv;
3235
3236 if (!(rv = test_nexpr(te, do_eval)))
3237 do_eval = false;
3238 if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
3239 return (test_aexpr(te, do_eval) && rv);
3240 return (rv);
3241 }
3242
3243 static int
test_nexpr(Test_env * te,bool do_eval)3244 test_nexpr(Test_env *te, bool do_eval)
3245 {
3246 if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
3247 return (!test_nexpr(te, do_eval));
3248 return (test_primary(te, do_eval));
3249 }
3250
3251 static int
test_primary(Test_env * te,bool do_eval)3252 test_primary(Test_env *te, bool do_eval)
3253 {
3254 const char *opnd1, *opnd2;
3255 int rv;
3256 Test_op op;
3257
3258 if (te->flags & TEF_ERROR)
3259 return (0);
3260 if ((*te->isa)(te, TM_OPAREN)) {
3261 rv = test_oexpr(te, do_eval);
3262 if (te->flags & TEF_ERROR)
3263 return (0);
3264 if (!(*te->isa)(te, TM_CPAREN)) {
3265 (*te->error)(te, 0, "missing )");
3266 return (0);
3267 }
3268 return (rv);
3269 }
3270 /*
3271 * Binary should have precedence over unary in this case
3272 * so that something like test \( -f = -f \) is accepted
3273 */
3274 if ((te->flags & TEF_DBRACKET) || (&te->pos.wp[1] < te->wp_end &&
3275 !test_isop(TM_BINOP, te->pos.wp[1]))) {
3276 if ((op = (*te->isa)(te, TM_UNOP))) {
3277 /* unary expression */
3278 opnd1 = (*te->getopnd)(te, op, do_eval);
3279 if (!opnd1) {
3280 (*te->error)(te, -1, "missing argument");
3281 return (0);
3282 }
3283
3284 return ((*te->eval)(te, op, opnd1, NULL, do_eval));
3285 }
3286 }
3287 opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
3288 if (!opnd1) {
3289 (*te->error)(te, 0, "expression expected");
3290 return (0);
3291 }
3292 if ((op = (*te->isa)(te, TM_BINOP))) {
3293 /* binary expression */
3294 opnd2 = (*te->getopnd)(te, op, do_eval);
3295 if (!opnd2) {
3296 (*te->error)(te, -1, "missing second argument");
3297 return (0);
3298 }
3299
3300 return ((*te->eval)(te, op, opnd1, opnd2, do_eval));
3301 }
3302 return ((*te->eval)(te, TO_STNZE, opnd1, NULL, do_eval));
3303 }
3304
3305 /*
3306 * Plain test (test and [ .. ]) specific routines.
3307 */
3308
3309 /*
3310 * Test if the current token is a whatever. Accepts the current token if
3311 * it is. Returns 0 if it is not, non-zero if it is (in the case of
3312 * TM_UNOP and TM_BINOP, the returned value is a Test_op).
3313 */
3314 static Test_op
ptest_isa(Test_env * te,Test_meta meta)3315 ptest_isa(Test_env *te, Test_meta meta)
3316 {
3317 /* Order important - indexed by Test_meta values */
3318 static const char * const tokens[] = {
3319 "-o", "-a", "!", "(", ")"
3320 };
3321 Test_op rv;
3322
3323 if (te->pos.wp >= te->wp_end)
3324 return (meta == TM_END ? TO_NONNULL : TO_NONOP);
3325
3326 if (meta == TM_UNOP || meta == TM_BINOP)
3327 rv = test_isop(meta, *te->pos.wp);
3328 else if (meta == TM_END)
3329 rv = TO_NONOP;
3330 else
3331 rv = !strcmp(*te->pos.wp, tokens[(int)meta]) ?
3332 TO_NONNULL : TO_NONOP;
3333
3334 /* Accept the token? */
3335 if (rv != TO_NONOP)
3336 te->pos.wp++;
3337
3338 return (rv);
3339 }
3340
3341 static const char *
ptest_getopnd(Test_env * te,Test_op op,bool do_eval MKSH_A_UNUSED)3342 ptest_getopnd(Test_env *te, Test_op op, bool do_eval MKSH_A_UNUSED)
3343 {
3344 if (te->pos.wp >= te->wp_end)
3345 return (op == TO_FILTT ? "1" : NULL);
3346 return (*te->pos.wp++);
3347 }
3348
3349 static void
ptest_error(Test_env * te,int ofs,const char * msg)3350 ptest_error(Test_env *te, int ofs, const char *msg)
3351 {
3352 const char *op;
3353
3354 te->flags |= TEF_ERROR;
3355 if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
3356 bi_errorf("%s: %s", op, msg);
3357 else
3358 bi_errorf("%s", msg);
3359 }
3360
3361 #ifndef MKSH_NO_LIMITS
3362 #define SOFT 0x1
3363 #define HARD 0x2
3364
3365 /* Magic to divine the 'm' and 'v' limits */
3366
3367 #ifdef RLIMIT_AS
3368 #if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
3369 !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
3370 #define ULIMIT_V_IS_AS
3371 #elif defined(RLIMIT_VMEM)
3372 #if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
3373 #define ULIMIT_V_IS_AS
3374 #else
3375 #define ULIMIT_V_IS_VMEM
3376 #endif
3377 #endif
3378 #endif
3379
3380 #ifdef RLIMIT_RSS
3381 #ifdef ULIMIT_V_IS_VMEM
3382 #define ULIMIT_M_IS_RSS
3383 #elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
3384 #define ULIMIT_M_IS_VMEM
3385 #else
3386 #define ULIMIT_M_IS_RSS
3387 #endif
3388 #if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)
3389 #undef ULIMIT_M_IS_RSS
3390 #endif
3391 #endif
3392
3393 #if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
3394 #define ULIMIT_V_IS_VMEM
3395 #endif
3396
3397 #if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
3398 (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
3399 #define ULIMIT_M_IS_VMEM
3400 #endif
3401
3402 #if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
3403 (RLIMIT_VMEM == RLIMIT_AS)
3404 #undef ULIMIT_M_IS_VMEM
3405 #endif
3406
3407 #if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM)
3408 # error nonsensical m ulimit
3409 #endif
3410
3411 #if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS)
3412 # error nonsensical v ulimit
3413 #endif
3414
3415 #define RLIMITS_DEFNS
3416 #include "rlimits.gen"
3417
3418 static void print_ulimit(const struct limits *, int);
3419 static int set_ulimit(const struct limits *, const char *, int);
3420
3421 static const struct limits * const rlimits[] = {
3422 #define RLIMITS_ITEMS
3423 #include "rlimits.gen"
3424 };
3425
3426 static const char rlimits_opts[] =
3427 #define RLIMITS_OPTCS
3428 #include "rlimits.gen"
3429 ;
3430
3431 int
c_ulimit(const char ** wp)3432 c_ulimit(const char **wp)
3433 {
3434 size_t i = 0;
3435 int how = SOFT | HARD, optc, what = 'f';
3436 bool all = false;
3437
3438 while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1)
3439 switch (optc) {
3440 case 'H':
3441 how = HARD;
3442 break;
3443 case 'S':
3444 how = SOFT;
3445 break;
3446 case 'a':
3447 all = true;
3448 break;
3449 case '?':
3450 bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts);
3451 return (1);
3452 default:
3453 what = optc;
3454 }
3455
3456 while (i < NELEM(rlimits)) {
3457 if (rlimits[i]->optchar == what)
3458 goto found;
3459 ++i;
3460 }
3461 internal_warningf("ulimit: %c", what);
3462 return (1);
3463 found:
3464 if (wp[builtin_opt.optind]) {
3465 if (all || wp[builtin_opt.optind + 1]) {
3466 bi_errorf("too many arguments");
3467 return (1);
3468 }
3469 return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
3470 }
3471 if (!all)
3472 print_ulimit(rlimits[i], how);
3473 else for (i = 0; i < NELEM(rlimits); ++i) {
3474 shprintf("%-20s ", rlimits[i]->name);
3475 print_ulimit(rlimits[i], how);
3476 }
3477 return (0);
3478 }
3479
3480 static int
set_ulimit(const struct limits * l,const char * v,int how)3481 set_ulimit(const struct limits *l, const char *v, int how)
3482 {
3483 rlim_t val = (rlim_t)0;
3484 struct rlimit limit;
3485
3486 if (strcmp(v, "unlimited") == 0)
3487 val = (rlim_t)RLIM_INFINITY;
3488 else {
3489 mksh_uari_t rval;
3490
3491 if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
3492 return (1);
3493 /*
3494 * Avoid problems caused by typos that evaluate misses due
3495 * to evaluating unset parameters to 0...
3496 * If this causes problems, will have to add parameter to
3497 * evaluate() to control if unset params are 0 or an error.
3498 */
3499 if (!rval && !ksh_isdigit(v[0])) {
3500 bi_errorf("invalid %s limit: %s", l->name, v);
3501 return (1);
3502 }
3503 val = (rlim_t)((rlim_t)rval * l->factor);
3504 }
3505
3506 if (getrlimit(l->resource, &limit) < 0) {
3507 #ifndef MKSH_SMALL
3508 bi_errorf("limit %s could not be read, contact the mksh developers: %s",
3509 l->name, cstrerror(errno));
3510 #endif
3511 /* some can't be read */
3512 limit.rlim_cur = RLIM_INFINITY;
3513 limit.rlim_max = RLIM_INFINITY;
3514 }
3515 if (how & SOFT)
3516 limit.rlim_cur = val;
3517 if (how & HARD)
3518 limit.rlim_max = val;
3519 if (!setrlimit(l->resource, &limit))
3520 return (0);
3521 if (errno == EPERM)
3522 bi_errorf("%s exceeds allowable %s limit", v, l->name);
3523 else
3524 bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
3525 return (1);
3526 }
3527
3528 static void
print_ulimit(const struct limits * l,int how)3529 print_ulimit(const struct limits *l, int how)
3530 {
3531 rlim_t val = (rlim_t)0;
3532 struct rlimit limit;
3533
3534 if (getrlimit(l->resource, &limit)) {
3535 shf_puts("unknown\n", shl_stdout);
3536 return;
3537 }
3538 if (how & SOFT)
3539 val = limit.rlim_cur;
3540 else if (how & HARD)
3541 val = limit.rlim_max;
3542 if (val == (rlim_t)RLIM_INFINITY)
3543 shf_puts("unlimited\n", shl_stdout);
3544 else
3545 shprintf("%lu\n", (unsigned long)(val / l->factor));
3546 }
3547 #endif
3548
3549 int
c_rename(const char ** wp)3550 c_rename(const char **wp)
3551 {
3552 int rv = 1;
3553
3554 /* skip argv[0] */
3555 ++wp;
3556 if (wp[0] && !strcmp(wp[0], "--"))
3557 /* skip "--" (options separator) */
3558 ++wp;
3559
3560 /* check for exactly two arguments */
3561 if (wp[0] == NULL /* first argument */ ||
3562 wp[1] == NULL /* second argument */ ||
3563 wp[2] != NULL /* no further args please */)
3564 bi_errorf(Tsynerr);
3565 else if ((rv = rename(wp[0], wp[1])) != 0) {
3566 rv = errno;
3567 bi_errorf("%s: %s", "failed", cstrerror(rv));
3568 }
3569
3570 return (rv);
3571 }
3572
3573 int
c_realpath(const char ** wp)3574 c_realpath(const char **wp)
3575 {
3576 int rv = 1;
3577 char *buf;
3578
3579 /* skip argv[0] */
3580 ++wp;
3581 if (wp[0] && !strcmp(wp[0], "--"))
3582 /* skip "--" (options separator) */
3583 ++wp;
3584
3585 /* check for exactly one argument */
3586 if (wp[0] == NULL || wp[1] != NULL)
3587 bi_errorf(Tsynerr);
3588 else if ((buf = do_realpath(wp[0])) == NULL) {
3589 rv = errno;
3590 bi_errorf("%s: %s", wp[0], cstrerror(rv));
3591 if ((unsigned int)rv > 255)
3592 rv = 255;
3593 } else {
3594 shprintf("%s\n", buf);
3595 afree(buf, ATEMP);
3596 rv = 0;
3597 }
3598
3599 return (rv);
3600 }
3601
3602 int
c_cat(const char ** wp)3603 c_cat(const char **wp)
3604 {
3605 int fd = STDIN_FILENO, rv, eno;
3606 ssize_t n, w;
3607 const char *fn = "<stdin>";
3608 char *buf, *cp;
3609 #define MKSH_CAT_BUFSIZ 4096
3610
3611 /* parse options: POSIX demands we support "-u" as no-op */
3612 while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
3613 switch (rv) {
3614 case 'u':
3615 /* we already operate unbuffered */
3616 break;
3617 default:
3618 bi_errorf(Tsynerr);
3619 return (1);
3620 }
3621 }
3622 wp += builtin_opt.optind;
3623 rv = 0;
3624
3625 if ((buf = malloc_osfunc(MKSH_CAT_BUFSIZ)) == NULL) {
3626 bi_errorf(Toomem, (size_t)MKSH_CAT_BUFSIZ);
3627 return (1);
3628 }
3629
3630 do {
3631 if (*wp) {
3632 fn = *wp++;
3633 if (fn[0] == '-' && fn[1] == '\0')
3634 fd = STDIN_FILENO;
3635 else if ((fd = open(fn, O_RDONLY | O_BINARY)) < 0) {
3636 eno = errno;
3637 bi_errorf("%s: %s", fn, cstrerror(eno));
3638 rv = 1;
3639 continue;
3640 }
3641 }
3642 while (/* CONSTCOND */ 1) {
3643 n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
3644 eno = errno;
3645 /* give the user a chance to ^C out */
3646 intrcheck();
3647 if (n == -1) {
3648 if (eno == EINTR) {
3649 /* interrupted, try again */
3650 continue;
3651 }
3652 /* an error occured during reading */
3653 bi_errorf("%s: %s", fn, cstrerror(eno));
3654 rv = 1;
3655 break;
3656 } else if (n == 0)
3657 /* end of file reached */
3658 break;
3659 while (n) {
3660 w = write(STDOUT_FILENO, cp, n);
3661 eno = errno;
3662 /* give the user a chance to ^C out */
3663 intrcheck();
3664 if (w == -1) {
3665 if (eno == EINTR)
3666 /* interrupted, try again */
3667 continue;
3668 /* an error occured during writing */
3669 bi_errorf("%s: %s", "<stdout>",
3670 cstrerror(eno));
3671 rv = 1;
3672 if (fd != STDIN_FILENO)
3673 close(fd);
3674 goto out;
3675 }
3676 n -= w;
3677 cp += w;
3678 }
3679 }
3680 if (fd != STDIN_FILENO)
3681 close(fd);
3682 } while (*wp);
3683
3684 out:
3685 free_osfunc(buf);
3686 return (rv);
3687 }
3688
3689 #if HAVE_SELECT
3690 int
c_sleep(const char ** wp)3691 c_sleep(const char **wp)
3692 {
3693 struct timeval tv;
3694 int rv = 1;
3695
3696 /* skip argv[0] */
3697 ++wp;
3698 if (wp[0] && !strcmp(wp[0], "--"))
3699 /* skip "--" (options separator) */
3700 ++wp;
3701
3702 if (!wp[0] || wp[1])
3703 bi_errorf(Tsynerr);
3704 else if (parse_usec(wp[0], &tv))
3705 bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
3706 else {
3707 #ifndef MKSH_NOPROSPECTOFWORK
3708 sigset_t omask, bmask;
3709
3710 /* block a number of signals from interrupting us, though */
3711 (void)sigemptyset(&bmask);
3712 (void)sigaddset(&bmask, SIGPIPE);
3713 (void)sigaddset(&bmask, SIGCHLD);
3714 #ifdef SIGWINCH
3715 (void)sigaddset(&bmask, SIGWINCH);
3716 #endif
3717 #ifdef SIGINFO
3718 (void)sigaddset(&bmask, SIGINFO);
3719 #endif
3720 #ifdef SIGUSR1
3721 (void)sigaddset(&bmask, SIGUSR1);
3722 #endif
3723 #ifdef SIGUSR2
3724 (void)sigaddset(&bmask, SIGUSR2);
3725 #endif
3726 sigprocmask(SIG_BLOCK, &bmask, &omask);
3727 #endif
3728 if (select(1, NULL, NULL, NULL, &tv) == 0 || errno == EINTR)
3729 /*
3730 * strictly speaking only for SIGALRM, but the
3731 * execution may be interrupted by other signals
3732 */
3733 rv = 0;
3734 else
3735 bi_errorf("%s: %s", Tselect, cstrerror(errno));
3736 #ifndef MKSH_NOPROSPECTOFWORK
3737 /* this will re-schedule signal delivery */
3738 sigprocmask(SIG_SETMASK, &omask, NULL);
3739 #endif
3740 }
3741 return (rv);
3742 }
3743 #endif
3744
3745 #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
3746 static int
c_suspend(const char ** wp)3747 c_suspend(const char **wp)
3748 {
3749 if (wp[1] != NULL) {
3750 bi_errorf("too many arguments");
3751 return (1);
3752 }
3753 if (Flag(FLOGIN)) {
3754 /* Can't suspend an orphaned process group. */
3755 if (getpgid(kshppid) == getpgid(0) ||
3756 getsid(kshppid) != getsid(0)) {
3757 bi_errorf("can't suspend a login shell");
3758 return (1);
3759 }
3760 }
3761 j_suspend();
3762 return (0);
3763 }
3764 #endif
3765