OSDN Git Service

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