OSDN Git Service

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