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