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