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