OSDN Git Service

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