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