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