1 /* $NetBSD: c_ksh.c,v 1.30 2021/09/16 19:43:33 christos Exp $ */ 2 3 /* 4 * built-in Korn commands: c_* 5 */ 6 #include <sys/cdefs.h> 7 8 #ifndef lint 9 __RCSID("$NetBSD: c_ksh.c,v 1.30 2021/09/16 19:43:33 christos Exp $"); 10 #endif 11 12 #include <sys/stat.h> 13 #include <ctype.h> 14 15 #include "sh.h" 16 17 int 18 c_cd(wp) 19 char **wp; 20 { 21 int optc; 22 int physical = Flag(FPHYSICAL); 23 int cdnode; /* was a node from cdpath added in? */ 24 int printpath = 0; /* print where we cd'd? */ 25 int rval; 26 struct tbl *pwd_s, *oldpwd_s; 27 XString xs; 28 char *xp; 29 char *dir, *try, *pwd; 30 int phys_path; 31 char *cdpath; 32 char *fdir = NULL; 33 34 while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF) 35 switch (optc) { 36 case 'L': 37 physical = 0; 38 break; 39 case 'P': 40 physical = 1; 41 break; 42 case '?': 43 return 1; 44 } 45 wp += builtin_opt.optind; 46 47 if (Flag(FRESTRICTED)) { 48 bi_errorf("restricted shell - can't cd"); 49 return 1; 50 } 51 52 pwd_s = global("PWD"); 53 oldpwd_s = global("OLDPWD"); 54 55 if (!wp[0]) { 56 /* No arguments - go home */ 57 if ((dir = str_val(global("HOME"))) == null) { 58 bi_errorf("no home directory (HOME not set)"); 59 return 1; 60 } 61 } else if (!wp[1]) { 62 /* One argument: - or dir */ 63 dir = wp[0]; 64 if (strcmp(dir, "-") == 0) { 65 dir = str_val(oldpwd_s); 66 if (dir == null) { 67 bi_errorf("no OLDPWD"); 68 return 1; 69 } 70 printpath++; 71 } 72 } else if (!wp[2]) { 73 /* Two arguments - substitute arg1 in PWD for arg2 */ 74 int ilen, olen, nlen, elen; 75 char *cp; 76 77 if (!current_wd[0]) { 78 bi_errorf("don't know current directory"); 79 return 1; 80 } 81 /* substitute arg1 for arg2 in current path. 82 * if the first substitution fails because the cd fails 83 * we could try to find another substitution. For now 84 * we don't 85 */ 86 if ((cp = strstr(current_wd, wp[0])) == (char *) 0) { 87 bi_errorf("bad substitution"); 88 return 1; 89 } 90 ilen = cp - current_wd; 91 olen = strlen(wp[0]); 92 nlen = strlen(wp[1]); 93 elen = strlen(current_wd + ilen + olen) + 1; 94 fdir = dir = alloc(ilen + nlen + elen, ATEMP); 95 memcpy(dir, current_wd, ilen); 96 memcpy(dir + ilen, wp[1], nlen); 97 memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen); 98 printpath++; 99 } else { 100 bi_errorf("too many arguments"); 101 return 1; 102 } 103 104 Xinit(xs, xp, PATH, ATEMP); 105 /* xp will have a bogus value after make_path() - set it to 0 106 * so that if it's used, it will cause a dump 107 */ 108 xp = (char *) 0; 109 110 cdpath = str_val(global("CDPATH")); 111 do { 112 cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path); 113 #ifdef S_ISLNK 114 if (physical) 115 rval = chdir(try = Xstring(xs, xp) + phys_path); 116 else 117 #endif /* S_ISLNK */ 118 { 119 simplify_path(Xstring(xs, xp)); 120 rval = chdir(try = Xstring(xs, xp)); 121 } 122 } while (rval < 0 && cdpath != (char *) 0); 123 124 if (rval < 0) { 125 if (cdnode) 126 bi_errorf("%s: bad directory", dir); 127 else 128 bi_errorf("%s - %s", try, strerror(errno)); 129 if (fdir) 130 afree(fdir, ATEMP); 131 return 1; 132 } 133 134 /* Clear out tracked aliases with relative paths */ 135 flushcom(0); 136 137 /* Set OLDPWD (note: unsetting OLDPWD does not disable this 138 * setting in at&t ksh) 139 */ 140 if (current_wd[0]) 141 /* Ignore failure (happens if readonly or integer) */ 142 setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR); 143 144 if (!ISABSPATH(Xstring(xs, xp))) { 145 pwd = (char *) 0; 146 } else 147 #ifdef S_ISLNK 148 if (!physical || !(pwd = get_phys_path(Xstring(xs, xp)))) 149 #endif /* S_ISLNK */ 150 pwd = Xstring(xs, xp); 151 152 /* Set PWD */ 153 if (pwd) { 154 char *ptmp = pwd; 155 set_current_wd(ptmp); 156 /* Ignore failure (happens if readonly or integer) */ 157 setstr(pwd_s, ptmp, KSH_RETURN_ERROR); 158 } else { 159 set_current_wd(null); 160 pwd = Xstring(xs, xp); 161 /* XXX unset $PWD? */ 162 } 163 if (printpath || cdnode) 164 shprintf("%s\n", pwd); 165 166 if (fdir) 167 afree(fdir, ATEMP); 168 169 return 0; 170 } 171 172 int 173 c_pwd(wp) 174 char **wp; 175 { 176 int optc; 177 int physical = Flag(FPHYSICAL); 178 char *p, *freep = NULL; 179 180 while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != EOF) 181 switch (optc) { 182 case 'L': 183 physical = 0; 184 break; 185 case 'P': 186 physical = 1; 187 break; 188 case '?': 189 return 1; 190 } 191 wp += builtin_opt.optind; 192 193 if (wp[0]) { 194 bi_errorf("too many arguments"); 195 return 1; 196 } 197 #ifdef S_ISLNK 198 p = current_wd[0] ? (physical ? get_phys_path(current_wd) : current_wd) 199 : (char *) 0; 200 #else /* S_ISLNK */ 201 p = current_wd[0] ? current_wd : (char *) 0; 202 #endif /* S_ISLNK */ 203 if (p && eaccess(p, R_OK) < 0) 204 p = (char *) 0; 205 if (!p) { 206 freep = p = ksh_get_wd((char *) 0, 0); 207 if (!p) { 208 bi_errorf("can't get current directory - %s", 209 strerror(errno)); 210 return 1; 211 } 212 } 213 shprintf("%s\n", p); 214 if (freep) 215 afree(freep, ATEMP); 216 return 0; 217 } 218 219 int 220 c_print(wp) 221 char **wp; 222 { 223 #define PO_NL BIT(0) /* print newline */ 224 #define PO_EXPAND BIT(1) /* expand backslash sequences */ 225 #define PO_PMINUSMINUS BIT(2) /* print a -- argument */ 226 #define PO_HIST BIT(3) /* print to history instead of stdout */ 227 #define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */ 228 int fd = 1; 229 int flags = PO_EXPAND|PO_NL; 230 char *s; 231 const char *emsg; 232 XString xs; 233 char *xp; 234 235 if (wp[0][0] == 'e') { /* echo command */ 236 int nflags = flags; 237 238 /* A compromise between sysV and BSD echo commands: 239 * escape sequences are enabled by default, and 240 * -n, -e and -E are recognized if they appear 241 * in arguments with no illegal options (ie, echo -nq 242 * will print -nq). 243 * Different from sysV echo since options are recognized, 244 * different from BSD echo since escape sequences are enabled 245 * by default. 246 */ 247 wp += 1; 248 while ((s = *wp) && *s == '-' && s[1]) { 249 while (*++s) 250 if (*s == 'n') 251 nflags &= ~PO_NL; 252 else if (*s == 'e') 253 nflags |= PO_EXPAND; 254 else if (*s == 'E') 255 nflags &= ~PO_EXPAND; 256 else 257 /* bad option: don't use nflags, print 258 * argument 259 */ 260 break; 261 if (*s) 262 break; 263 wp++; 264 flags = nflags; 265 } 266 } else { 267 int optc; 268 const char *options = "Rnprsu,"; 269 while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) 270 switch (optc) { 271 case 'R': /* fake BSD echo command */ 272 flags |= PO_PMINUSMINUS; 273 flags &= ~PO_EXPAND; 274 options = "ne"; 275 break; 276 case 'e': 277 flags |= PO_EXPAND; 278 break; 279 case 'n': 280 flags &= ~PO_NL; 281 break; 282 #ifdef KSH 283 case 'p': 284 if ((fd = coproc_getfd(W_OK, &emsg)) < 0) { 285 bi_errorf("-p: %s", emsg); 286 return 1; 287 } 288 break; 289 #endif /* KSH */ 290 case 'r': 291 flags &= ~PO_EXPAND; 292 break; 293 case 's': 294 flags |= PO_HIST; 295 break; 296 case 'u': 297 if (!*(s = builtin_opt.optarg)) 298 fd = 0; 299 else if ((fd = check_fd(s, W_OK, &emsg)) < 0) { 300 bi_errorf("-u: %s: %s", s, emsg); 301 return 1; 302 } 303 break; 304 case '?': 305 return 1; 306 } 307 if (!(builtin_opt.info & GI_MINUSMINUS)) { 308 /* treat a lone - like -- */ 309 if (wp[builtin_opt.optind] 310 && strcmp(wp[builtin_opt.optind], "-") == 0) 311 builtin_opt.optind++; 312 } else if (flags & PO_PMINUSMINUS) 313 builtin_opt.optind--; 314 wp += builtin_opt.optind; 315 } 316 317 Xinit(xs, xp, 128, ATEMP); 318 319 while (*wp != NULL) { 320 int c; 321 s = *wp; 322 while ((c = *s++) != '\0') { 323 Xcheck(xs, xp); 324 if ((flags & PO_EXPAND) && c == '\\') { 325 int i; 326 327 switch ((c = *s++)) { 328 /* Oddly enough, \007 seems more portable than 329 * \a (due to old pcc's, 330 * etc.). 331 */ 332 case 'a': c = '\007'; break; 333 case 'b': c = '\b'; break; 334 case 'c': flags &= ~PO_NL; 335 continue; /* AT&T brain damage */ 336 case 'f': c = '\f'; break; 337 case 'n': c = '\n'; break; 338 case 'r': c = '\r'; break; 339 case 't': c = '\t'; break; 340 case 'v': c = 0x0B; break; 341 case '0': 342 /* Look for an octal number: can have 343 * three digits (not counting the 344 * leading 0). Truly burnt. 345 */ 346 c = 0; 347 for (i = 0; i < 3; i++) { 348 if (*s >= '0' && *s <= '7') 349 c = c*8 + *s++ - '0'; 350 else 351 break; 352 } 353 break; 354 case '\0': s--; c = '\\'; break; 355 case '\\': break; 356 default: 357 Xput(xs, xp, '\\'); 358 } 359 } 360 Xput(xs, xp, c); 361 } 362 if (*++wp != NULL) 363 Xput(xs, xp, ' '); 364 } 365 if (flags & PO_NL) 366 Xput(xs, xp, '\n'); 367 368 if (flags & PO_HIST) { 369 Xput(xs, xp, '\0'); 370 source->line++; 371 histsave(source->line, Xstring(xs, xp), 1); 372 Xfree(xs, xp); 373 } else { 374 int n, len = Xlength(xs, xp); 375 int UNINITIALIZED(opipe); 376 #ifdef KSH 377 378 /* Ensure we aren't killed by a SIGPIPE while writing to 379 * a coprocess. at&t ksh doesn't seem to do this (seems 380 * to just check that the co-process is alive, which is 381 * not enough). 382 */ 383 if (coproc.write >= 0 && coproc.write == fd) { 384 flags |= PO_COPROC; 385 opipe = block_pipe(); 386 } 387 #endif /* KSH */ 388 for (s = Xstring(xs, xp); len > 0; ) { 389 n = write(fd, s, len); 390 if (n < 0) { 391 #ifdef KSH 392 if (flags & PO_COPROC) 393 restore_pipe(opipe); 394 #endif /* KSH */ 395 if (errno == EINTR) { 396 /* allow user to ^C out */ 397 intrcheck(); 398 #ifdef KSH 399 if (flags & PO_COPROC) 400 opipe = block_pipe(); 401 #endif /* KSH */ 402 continue; 403 } 404 #ifdef KSH 405 /* This doesn't really make sense - could 406 * break scripts (print -p generates 407 * error message). 408 *if (errno == EPIPE) 409 * coproc_write_close(fd); 410 */ 411 #endif /* KSH */ 412 return 1; 413 } 414 s += n; 415 len -= n; 416 } 417 #ifdef KSH 418 if (flags & PO_COPROC) 419 restore_pipe(opipe); 420 #endif /* KSH */ 421 } 422 423 return 0; 424 } 425 426 int 427 c_whence(wp) 428 char **wp; 429 { 430 struct tbl *tp; 431 char *id; 432 int pflag = 0, vflag = 0, Vflag = 0; 433 int ret = 0; 434 int optc; 435 int iam_whence = wp[0][0] == 'w'; 436 int fcflags; 437 const char *options = iam_whence ? "pv" : "pvV"; 438 439 while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) 440 switch (optc) { 441 case 'p': 442 pflag = 1; 443 break; 444 case 'v': 445 vflag = 1; 446 break; 447 case 'V': 448 Vflag = 1; 449 break; 450 case '?': 451 return 1; 452 } 453 wp += builtin_opt.optind; 454 455 456 fcflags = FC_BI | FC_PATH | FC_FUNC; 457 if (!iam_whence) { 458 /* Note that -p on its own is deal with in comexec() */ 459 if (pflag) 460 fcflags |= FC_DEFPATH; 461 /* Convert command options to whence options - note that 462 * command -pV uses a different path search than whence -v 463 * or whence -pv. This should be considered a feature. 464 */ 465 vflag = Vflag; 466 } 467 if (pflag) 468 fcflags &= ~(FC_BI | FC_FUNC); 469 470 while ((vflag || ret == 0) && (id = *wp++) != NULL) { 471 tp = NULL; 472 if ((iam_whence || vflag) && !pflag) 473 tp = mytsearch(&keywords, id, hash(id)); 474 if (!tp && !pflag) { 475 tp = mytsearch(&aliases, id, hash(id)); 476 if (tp && !(tp->flag & ISSET)) 477 tp = NULL; 478 } 479 if (!tp) 480 tp = findcom(id, fcflags); 481 if (vflag || (tp->type != CALIAS && tp->type != CEXEC 482 && tp->type != CTALIAS)) 483 shprintf("%s", id); 484 switch (tp->type) { 485 case CKEYWD: 486 if (vflag) 487 shprintf(" is a reserved word"); 488 break; 489 case CALIAS: 490 if (vflag) 491 shprintf(" is an %salias for ", 492 (tp->flag & EXPORT) ? "exported " 493 : null); 494 if (!iam_whence && !vflag) 495 shprintf("alias %s=", id); 496 print_value_quoted(tp->val.s); 497 break; 498 case CFUNC: 499 if (vflag) { 500 shprintf(" is a"); 501 if (tp->flag & EXPORT) 502 shprintf("n exported"); 503 if (tp->flag & TRACE) 504 shprintf(" traced"); 505 if (!(tp->flag & ISSET)) { 506 shprintf(" undefined"); 507 if (tp->u.fpath) 508 shprintf(" (autoload from %s)", 509 tp->u.fpath); 510 } 511 shprintf(" function"); 512 } 513 break; 514 case CSHELL: 515 if (vflag) 516 shprintf(" is a%s shell builtin", 517 (tp->flag & SPEC_BI) ? " special" : null); 518 break; 519 case CTALIAS: 520 case CEXEC: 521 if (tp->flag & ISSET) { 522 if (vflag) { 523 shprintf(" is "); 524 if (tp->type == CTALIAS) 525 shprintf( 526 "a tracked %salias for ", 527 (tp->flag & EXPORT) ? 528 "exported " 529 : null); 530 } 531 shprintf("%s", tp->val.s); 532 } else { 533 if (vflag) 534 shprintf(" not found"); 535 ret = 1; 536 } 537 break; 538 default: 539 shprintf("%s is *GOK*", id); 540 break; 541 } 542 if (vflag || !ret) 543 shprintf("%s", newline); 544 } 545 return ret; 546 } 547 548 /* Deal with command -vV - command -p dealt with in comexec() */ 549 int 550 c_command(wp) 551 char **wp; 552 { 553 /* Let c_whence do the work. Note that c_command() must be 554 * a distinct function from c_whence() (tested in comexec()). 555 */ 556 return c_whence(wp); 557 } 558 559 /* typeset, export, and readonly */ 560 int 561 c_typeset(wp) 562 char **wp; 563 { 564 struct block *l = e->loc; 565 struct tbl *vp, **p; 566 Tflag fset = 0, fclr = 0; 567 int thing = 0, func = 0, localv = 0; 568 const char *options = "L#R#UZ#fi#lprtux"; /* see comment below */ 569 char *fieldstr, *basestr; 570 int field, base; 571 int optc; 572 Tflag flag; 573 int pflag = 0; 574 575 switch (**wp) { 576 case 'e': /* export */ 577 fset |= EXPORT; 578 options = "p"; 579 break; 580 case 'r': /* readonly */ 581 fset |= RDONLY; 582 options = "p"; 583 break; 584 case 's': /* set */ 585 /* called with 'typeset -' */ 586 break; 587 case 't': /* typeset */ 588 localv = 1; 589 break; 590 } 591 592 fieldstr = basestr = (char *) 0; 593 builtin_opt.flags |= GF_PLUSOPT; 594 /* at&t ksh seems to have 0-9 as options, which are multiplied 595 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2 596 * sets right justify in a field of 12). This allows options 597 * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and 598 * does not allow the number to be specified as a separate argument 599 * Here, the number must follow the RLZi option, but is optional 600 * (see the # kludge in ksh_getopt()). 601 */ 602 while ((optc = ksh_getopt(wp, &builtin_opt, options)) != EOF) { 603 flag = 0; 604 switch (optc) { 605 case 'L': 606 flag = LJUST; 607 fieldstr = builtin_opt.optarg; 608 break; 609 case 'R': 610 flag = RJUST; 611 fieldstr = builtin_opt.optarg; 612 break; 613 case 'U': 614 /* at&t ksh uses u, but this conflicts with 615 * upper/lower case. If this option is changed, 616 * need to change the -U below as well 617 */ 618 flag = INT_U; 619 break; 620 case 'Z': 621 flag = ZEROFIL; 622 fieldstr = builtin_opt.optarg; 623 break; 624 case 'f': 625 func = 1; 626 break; 627 case 'i': 628 flag = INTEGER; 629 basestr = builtin_opt.optarg; 630 break; 631 case 'l': 632 flag = LCASEV; 633 break; 634 case 'p': /* posix export/readonly -p flag. 635 * typeset -p is the same as typeset (in pdksh); 636 * here for compatibility with ksh93. 637 */ 638 pflag = 1; 639 break; 640 case 'r': 641 flag = RDONLY; 642 break; 643 case 't': 644 flag = TRACE; 645 break; 646 case 'u': 647 flag = UCASEV_AL; /* upper case / autoload */ 648 break; 649 case 'x': 650 flag = EXPORT; 651 break; 652 case '?': 653 return 1; 654 } 655 if (builtin_opt.info & GI_PLUS) { 656 fclr |= flag; 657 fset &= ~flag; 658 thing = '+'; 659 } else { 660 fset |= flag; 661 fclr &= ~flag; 662 thing = '-'; 663 } 664 } 665 666 field = 0; 667 if (fieldstr && !bi_getn(fieldstr, &field)) 668 return 1; 669 base = 0; 670 if (basestr && !bi_getn(basestr, &base)) 671 return 1; 672 673 if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] 674 && (wp[builtin_opt.optind][0] == '-' 675 || wp[builtin_opt.optind][0] == '+') 676 && wp[builtin_opt.optind][1] == '\0') 677 { 678 thing = wp[builtin_opt.optind][0]; 679 builtin_opt.optind++; 680 } 681 682 if (func && ((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT))) { 683 bi_errorf("only -t, -u and -x options may be used with -f"); 684 return 1; 685 } 686 if (wp[builtin_opt.optind]) { 687 /* Take care of exclusions. 688 * At this point, flags in fset are cleared in fclr and vise 689 * versa. This property should be preserved. 690 */ 691 if (fset & LCASEV) /* LCASEV has priority over UCASEV_AL */ 692 fset &= ~UCASEV_AL; 693 if (fset & LJUST) /* LJUST has priority over RJUST */ 694 fset &= ~RJUST; 695 if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) { /* -Z implies -ZR */ 696 fset |= RJUST; 697 fclr &= ~RJUST; 698 } 699 /* Setting these attributes clears the others, unless they 700 * are also set in this command 701 */ 702 if (fset & (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER 703 |INT_U|INT_L)) 704 fclr |= ~fset & 705 (LJUST|RJUST|ZEROFIL|UCASEV_AL|LCASEV|INTEGER 706 |INT_U|INT_L); 707 } 708 709 /* set variables and attributes */ 710 if (wp[builtin_opt.optind]) { 711 int i; 712 int rval = 0; 713 struct tbl *f; 714 715 if (localv && !func) 716 fset |= LOCAL; 717 for (i = builtin_opt.optind; wp[i]; i++) { 718 if (func) { 719 f = findfunc(wp[i], hash(wp[i]), 720 (fset&UCASEV_AL) ? true : false); 721 if (!f) { 722 /* at&t ksh does ++rval: bogus */ 723 rval = 1; 724 continue; 725 } 726 if (fset | fclr) { 727 f->flag |= fset; 728 f->flag &= ~fclr; 729 } else 730 fptreef(shl_stdout, 0, 731 f->flag & FKSH ? 732 "function %s %T\n" 733 : "%s() %T\n" 734 , 735 wp[i], f->val.t); 736 } else if (!typeset(wp[i], fset, fclr, field, base)) { 737 bi_errorf("%s: not identifier", wp[i]); 738 return 1; 739 } 740 } 741 return rval; 742 } 743 744 /* list variables and attributes */ 745 flag = fset | fclr; /* no difference at this point.. */ 746 if (func) { 747 for (l = e->loc; l; l = l->next) { 748 for (p = tsort(&l->funs); (vp = *p++); ) { 749 if (flag && (vp->flag & flag) == 0) 750 continue; 751 if (thing == '-') 752 fptreef(shl_stdout, 0, vp->flag & FKSH ? 753 "function %s %T\n" 754 : "%s() %T\n", 755 vp->name, vp->val.t); 756 else 757 shprintf("%s\n", vp->name); 758 } 759 } 760 } else { 761 for (l = e->loc; l; l = l->next) { 762 for (p = tsort(&l->vars); (vp = *p++); ) { 763 struct tbl *tvp; 764 int any_set = 0; 765 /* 766 * See if the parameter is set (for arrays, if any 767 * element is set). 768 */ 769 for (tvp = vp; tvp; tvp = tvp->u.array) 770 if (tvp->flag & ISSET) { 771 any_set = 1; 772 break; 773 } 774 /* 775 * Check attributes - note that all array elements 776 * have (should have?) the same attributes, so checking 777 * the first is sufficient. 778 * 779 * Report an unset param only if the user has 780 * explicitly given it some attribute (like export); 781 * otherwise, after "echo $FOO", we would report FOO... 782 */ 783 if (!any_set && !(vp->flag & USERATTRIB)) 784 continue; 785 if (flag && (vp->flag & flag) == 0) 786 continue; 787 for (; vp; vp = vp->u.array) { 788 /* Ignore array elements that aren't set unless there 789 * are no set elements, in which case the first is 790 * reported on 791 */ 792 if ((vp->flag&ARRAY) && any_set && !(vp->flag & ISSET)) 793 continue; 794 /* no arguments */ 795 if (thing == 0 && flag == 0) { 796 /* at&t ksh prints things like export, integer, 797 * leftadj, zerofill, etc., but POSIX says must 798 * be suitable for re-entry... 799 */ 800 shprintf("typeset "); 801 if ((vp->flag&INTEGER)) 802 shprintf("-i "); 803 if ((vp->flag&EXPORT)) 804 shprintf("-x "); 805 if ((vp->flag&RDONLY)) 806 shprintf("-r "); 807 if ((vp->flag&TRACE)) 808 shprintf("-t "); 809 if ((vp->flag&LJUST)) 810 shprintf("-L%d ", vp->u2.field); 811 if ((vp->flag&RJUST)) 812 shprintf("-R%d ", vp->u2.field); 813 if ((vp->flag&ZEROFIL)) 814 shprintf("-Z "); 815 if ((vp->flag&LCASEV)) 816 shprintf("-l "); 817 if ((vp->flag&UCASEV_AL)) 818 shprintf("-u "); 819 if ((vp->flag&INT_U)) 820 shprintf("-U "); 821 shprintf("%s\n", vp->name); 822 if (vp->flag&ARRAY) 823 break; 824 } else { 825 if (pflag) 826 shprintf("%s ", 827 (flag & EXPORT) ? "export" : "readonly"); 828 if ((vp->flag&ARRAY) && any_set) 829 shprintf("%s[%d]", vp->name, vp->index); 830 else 831 shprintf("%s", vp->name); 832 if (thing == '-' && (vp->flag&ISSET)) { 833 char *s = str_val(vp); 834 835 shprintf("="); 836 /* at&t ksh can't have justified integers.. */ 837 if ((vp->flag & (INTEGER|LJUST|RJUST)) 838 == INTEGER) 839 shprintf("%s", s); 840 else 841 print_value_quoted(s); 842 } 843 shprintf("%s", newline); 844 } 845 /* Only report first `element' of an array with 846 * no set elements. 847 */ 848 if (!any_set) 849 break; 850 } 851 } 852 } 853 } 854 return 0; 855 } 856 857 int 858 c_alias(wp) 859 char **wp; 860 { 861 struct table *t = &aliases; 862 int rv = 0, rflag = 0, tflag, Uflag = 0, pflag = 0; 863 int prefix = 0; 864 Tflag xflag = 0; 865 int optc; 866 867 builtin_opt.flags |= GF_PLUSOPT; 868 while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != EOF) { 869 prefix = builtin_opt.info & GI_PLUS ? '+' : '-'; 870 switch (optc) { 871 case 'd': 872 t = &homedirs; 873 break; 874 case 'p': 875 pflag = 1; 876 break; 877 case 'r': 878 rflag = 1; 879 break; 880 case 't': 881 t = &taliases; 882 break; 883 case 'U': /* kludge for tracked alias initialization 884 * (don't do a path search, just make an entry) 885 */ 886 Uflag = 1; 887 break; 888 case 'x': 889 xflag = EXPORT; 890 break; 891 case '?': 892 return 1; 893 } 894 } 895 wp += builtin_opt.optind; 896 897 if (!(builtin_opt.info & GI_MINUSMINUS) && *wp 898 && (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') 899 { 900 prefix = wp[0][0]; 901 wp++; 902 } 903 904 tflag = t == &taliases; 905 906 /* "hash -r" means reset all the tracked aliases.. */ 907 if (rflag) { 908 static const char *const args[] = { 909 "unalias", "-ta", (const char *) 0 910 }; 911 912 if (!tflag || *wp) { 913 shprintf( 914 "alias: -r flag can only be used with -t and without arguments\n"); 915 return 1; 916 } 917 ksh_getopt_reset(&builtin_opt, GF_ERROR); 918 return c_unalias((char **)__UNCONST(args)); 919 } 920 921 922 if (*wp == NULL) { 923 struct tbl *ap, **p; 924 925 for (p = tsort(t); (ap = *p++) != NULL; ) 926 if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) { 927 if (pflag) 928 shf_puts("alias ", shl_stdout); 929 shf_puts(ap->name, shl_stdout); 930 if (prefix != '+') { 931 shf_putc('=', shl_stdout); 932 print_value_quoted(ap->val.s); 933 } 934 shprintf("%s", newline); 935 } 936 } 937 938 for (; *wp != NULL; wp++) { 939 char *alias = *wp; 940 char *val = strchr(alias, '='); 941 char *newval; 942 struct tbl *ap; 943 int h; 944 945 if (val) 946 alias = str_nsave(alias, val++ - alias, ATEMP); 947 h = hash(alias); 948 if (val == NULL && !tflag && !xflag) { 949 ap = mytsearch(t, alias, h); 950 if (ap != NULL && (ap->flag&ISSET)) { 951 if (pflag) 952 shf_puts("alias ", shl_stdout); 953 shf_puts(ap->name, shl_stdout); 954 if (prefix != '+') { 955 shf_putc('=', shl_stdout); 956 print_value_quoted(ap->val.s); 957 } 958 shprintf("%s", newline); 959 } else { 960 shprintf("%s alias not found\n", alias); 961 rv = 1; 962 } 963 continue; 964 } 965 ap = tenter(t, alias, h); 966 ap->type = tflag ? CTALIAS : CALIAS; 967 /* Are we setting the value or just some flags? */ 968 if ((val && !tflag) || (!val && tflag && !Uflag)) { 969 if (ap->flag&ALLOC) { 970 ap->flag &= ~(ALLOC|ISSET); 971 afree((void*)ap->val.s, APERM); 972 } 973 /* ignore values for -t (at&t ksh does this) */ 974 newval = tflag ? search(alias, path, X_OK, (int *) 0) 975 : val; 976 if (newval) { 977 ap->val.s = str_save(newval, APERM); 978 ap->flag |= ALLOC|ISSET; 979 } else 980 ap->flag &= ~ISSET; 981 } 982 ap->flag |= DEFINED; 983 if (prefix == '+') 984 ap->flag &= ~xflag; 985 else 986 ap->flag |= xflag; 987 if (val) 988 afree(alias, ATEMP); 989 } 990 991 return rv; 992 } 993 994 int 995 c_unalias(wp) 996 char **wp; 997 { 998 struct table *t = &aliases; 999 struct tbl *ap; 1000 int rv = 0, all = 0; 1001 int optc; 1002 1003 while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != EOF) 1004 switch (optc) { 1005 case 'a': 1006 all = 1; 1007 break; 1008 case 'd': 1009 t = &homedirs; 1010 break; 1011 case 't': 1012 t = &taliases; 1013 break; 1014 case '?': 1015 return 1; 1016 } 1017 wp += builtin_opt.optind; 1018 1019 for (; *wp != NULL; wp++) { 1020 ap = mytsearch(t, *wp, hash(*wp)); 1021 if (ap == NULL) { 1022 rv = 1; /* POSIX */ 1023 continue; 1024 } 1025 if (ap->flag&ALLOC) { 1026 ap->flag &= ~(ALLOC|ISSET); 1027 afree((void*)ap->val.s, APERM); 1028 } 1029 ap->flag &= ~(DEFINED|ISSET|EXPORT); 1030 } 1031 1032 if (all) { 1033 struct tstate ts; 1034 1035 for (ksh_twalk(&ts, t); (ap = tnext(&ts)); ) { 1036 if (ap->flag&ALLOC) { 1037 ap->flag &= ~(ALLOC|ISSET); 1038 afree((void*)ap->val.s, APERM); 1039 } 1040 ap->flag &= ~(DEFINED|ISSET|EXPORT); 1041 } 1042 } 1043 1044 return rv; 1045 } 1046 1047 #ifdef KSH 1048 int 1049 c_let(wp) 1050 char **wp; 1051 { 1052 int rv = 1; 1053 long val; 1054 1055 if (wp[1] == (char *) 0) /* at&t ksh does this */ 1056 bi_errorf("no arguments"); 1057 else 1058 for (wp++; *wp; wp++) 1059 if (!evaluate(*wp, &val, KSH_RETURN_ERROR)) { 1060 rv = 2; /* distinguish error from zero result */ 1061 break; 1062 } else 1063 rv = val == 0; 1064 return rv; 1065 } 1066 #endif /* KSH */ 1067 1068 int 1069 c_jobs(wp) 1070 char **wp; 1071 { 1072 int optc; 1073 int flag = 0; 1074 int nflag = 0; 1075 int Zflag = 0; 1076 int rv = 0; 1077 1078 while ((optc = ksh_getopt(wp, &builtin_opt, "lpnzZ")) != EOF) 1079 switch (optc) { 1080 case 'l': 1081 flag = 1; 1082 break; 1083 case 'p': 1084 flag = 2; 1085 break; 1086 case 'n': 1087 nflag = 1; 1088 break; 1089 case 'z': /* debugging: print zombies */ 1090 nflag = -1; 1091 break; 1092 case 'Z': 1093 Zflag = 1; 1094 break; 1095 case '?': 1096 return 1; 1097 } 1098 wp += builtin_opt.optind; 1099 if (Zflag) { 1100 if (*wp && **wp) { 1101 setproctitle("%s", *wp); 1102 } else { 1103 setproctitle(NULL); 1104 } 1105 return 0; 1106 } 1107 if (!*wp) { 1108 if (j_jobs((char *) 0, flag, nflag)) 1109 rv = 1; 1110 } else { 1111 for (; *wp; wp++) 1112 if (j_jobs(*wp, flag, nflag)) 1113 rv = 1; 1114 } 1115 return rv; 1116 } 1117 1118 #ifdef JOBS 1119 int 1120 c_fgbg(wp) 1121 char **wp; 1122 { 1123 int bg = strcmp(*wp, "bg") == 0; 1124 int UNINITIALIZED(rv); 1125 1126 if (!Flag(FMONITOR)) { 1127 bi_errorf("job control not enabled"); 1128 return 1; 1129 } 1130 if (ksh_getopt(wp, &builtin_opt, null) == '?') 1131 return 1; 1132 wp += builtin_opt.optind; 1133 if (*wp) 1134 for (; *wp; wp++) 1135 rv = j_resume(*wp, bg); 1136 else 1137 rv = j_resume("%%", bg); 1138 /* POSIX says fg shall return 0 (unless an error occurs). 1139 * at&t ksh returns the exit value of the job... 1140 */ 1141 return (bg || Flag(FPOSIX)) ? 0 : rv; 1142 } 1143 #endif 1144 1145 struct kill_info { 1146 int num_width; 1147 int name_width; 1148 }; 1149 static char *kill_fmt_entry ARGS((void *arg, int i, char *buf, int buflen)); 1150 1151 /* format a single kill item */ 1152 static char * 1153 kill_fmt_entry(arg, i, buf, buflen) 1154 void *arg; 1155 int i; 1156 char *buf; 1157 int buflen; 1158 { 1159 struct kill_info *ki = (struct kill_info *) arg; 1160 1161 i++; 1162 if (sigtraps[i].name) 1163 shf_snprintf(buf, buflen, "%*d %*s %s", 1164 ki->num_width, i, 1165 ki->name_width, sigtraps[i].name, 1166 sigtraps[i].mess); 1167 else 1168 shf_snprintf(buf, buflen, "%*d %*d %s", 1169 ki->num_width, i, 1170 ki->name_width, sigtraps[i].signal, 1171 sigtraps[i].mess); 1172 return buf; 1173 } 1174 1175 1176 int 1177 c_kill(wp) 1178 char **wp; 1179 { 1180 Trap *t = (Trap *) 0; 1181 char *p; 1182 int lflag = 0; 1183 int i, n, rv, sig; 1184 1185 /* assume old style options if -digits or -UPPERCASE */ 1186 if ((p = wp[1]) && *p == '-' 1187 && (digit(p[1]) || isupper((unsigned char)p[1]))) { 1188 if (!(t = gettrap(p + 1, true))) { 1189 bi_errorf("bad signal `%s'", p + 1); 1190 return 1; 1191 } 1192 i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2; 1193 } else { 1194 int optc; 1195 1196 while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != EOF) 1197 switch (optc) { 1198 case 'l': 1199 lflag = 1; 1200 break; 1201 case 's': 1202 if (!(t = gettrap(builtin_opt.optarg, true))) { 1203 bi_errorf("bad signal `%s'", 1204 builtin_opt.optarg); 1205 return 1; 1206 } 1207 break; 1208 case '?': 1209 return 1; 1210 } 1211 i = builtin_opt.optind; 1212 } 1213 if ((lflag && t) || (!wp[i] && !lflag)) { 1214 shf_fprintf(shl_out, 1215 "usage: kill [ -s signame | -signum | -signame ] {pid|job}...\n\ 1216 kill -l [exit_status]\n" 1217 ); 1218 bi_errorf("%s", null); 1219 return 1; 1220 } 1221 1222 if (lflag) { 1223 if (wp[i]) { 1224 for (; wp[i]; i++) { 1225 if (!bi_getn(wp[i], &n)) 1226 return 1; 1227 if (n > 128 && n < 128 + SIGNALS) 1228 n -= 128; 1229 if (n > 0 && n < SIGNALS && sigtraps[n].name) 1230 shprintf("%s\n", sigtraps[n].name); 1231 else 1232 shprintf("%d\n", n); 1233 } 1234 } else if (Flag(FPOSIX)) { 1235 p = null; 1236 for (i = 1; i < SIGNALS; i++, p = space) 1237 if (sigtraps[i].name) 1238 shprintf("%s%s", p, sigtraps[i].name); 1239 shprintf("%s", newline); 1240 } else { 1241 int w, si; 1242 int mess_width; 1243 struct kill_info ki; 1244 1245 for (si = SIGNALS, ki.num_width = 1; si >= 10; si /= 10) 1246 ki.num_width++; 1247 ki.name_width = mess_width = 0; 1248 for (si = 0; si < SIGNALS; si++) { 1249 w = sigtraps[si].name ? 1250 (int)strlen(sigtraps[si].name) : 1251 ki.num_width; 1252 if (w > ki.name_width) 1253 ki.name_width = w; 1254 w = strlen(sigtraps[si].mess); 1255 if (w > mess_width) 1256 mess_width = w; 1257 } 1258 1259 print_columns(shl_stdout, SIGNALS - 1, 1260 kill_fmt_entry, (void *) &ki, 1261 ki.num_width + ki.name_width + mess_width + 3, 1); 1262 } 1263 return 0; 1264 } 1265 rv = 0; 1266 sig = t ? t->signal : SIGTERM; 1267 for (; (p = wp[i]); i++) { 1268 if (*p == '%') { 1269 if (j_kill(p, sig)) 1270 rv = 1; 1271 } else if (!getn(p, &n)) { 1272 bi_errorf("%s: arguments must be jobs or process IDs", 1273 p); 1274 rv = 1; 1275 } else { 1276 /* use killpg if < -1 since -1 does special things for 1277 * some non-killpg-endowed kills 1278 */ 1279 if ((n < -1 ? killpg(-n, sig) : kill(n, sig)) < 0) { 1280 bi_errorf("%s: %s", p, strerror(errno)); 1281 rv = 1; 1282 } 1283 } 1284 } 1285 return rv; 1286 } 1287 1288 void 1289 getopts_reset(val) 1290 int val; 1291 { 1292 if (val >= 1) { 1293 ksh_getopt_reset(&user_opt, 1294 GF_NONAME | (Flag(FPOSIX) ? 0 : GF_PLUSOPT)); 1295 user_opt.optind = user_opt.uoptind = val; 1296 } 1297 } 1298 1299 int 1300 c_getopts(wp) 1301 char **wp; 1302 { 1303 int argc; 1304 const char *options; 1305 const char *var; 1306 int optc; 1307 int ret; 1308 char buf[3]; 1309 struct tbl *vq, *voptarg; 1310 1311 if (ksh_getopt(wp, &builtin_opt, null) == '?') 1312 return 1; 1313 wp += builtin_opt.optind; 1314 1315 options = *wp++; 1316 if (!options) { 1317 bi_errorf("missing options argument"); 1318 return 1; 1319 } 1320 1321 var = *wp++; 1322 if (!var) { 1323 bi_errorf("missing name argument"); 1324 return 1; 1325 } 1326 if (!*var || *skip_varname(var, true)) { 1327 bi_errorf("%s: is not an identifier", var); 1328 return 1; 1329 } 1330 1331 if (e->loc->next == (struct block *) 0) { 1332 internal_errorf(0, "c_getopts: no argv"); 1333 return 1; 1334 } 1335 /* Which arguments are we parsing... */ 1336 if (*wp == (char *) 0) 1337 wp = e->loc->next->argv; 1338 else 1339 *--wp = e->loc->next->argv[0]; 1340 1341 /* Check that our saved state won't cause a core dump... */ 1342 for (argc = 0; wp[argc]; argc++) 1343 ; 1344 if (user_opt.optind > argc 1345 || (user_opt.p != 0 1346 && user_opt.p > strlen(wp[user_opt.optind - 1]))) 1347 { 1348 bi_errorf("arguments changed since last call"); 1349 return 1; 1350 } 1351 1352 user_opt.optarg = (char *) 0; 1353 optc = ksh_getopt(wp, &user_opt, options); 1354 1355 if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) { 1356 buf[0] = '+'; 1357 buf[1] = optc; 1358 buf[2] = '\0'; 1359 } else { 1360 /* POSIX says var is set to ? at end-of-options, at&t ksh 1361 * sets it to null - we go with POSIX... 1362 */ 1363 buf[0] = optc < 0 ? '?' : optc; 1364 buf[1] = '\0'; 1365 } 1366 1367 /* at&t ksh does not change OPTIND if it was an unknown option. 1368 * Scripts counting on this are prone to break... (ie, don't count 1369 * on this staying). 1370 */ 1371 if (optc != '?') { 1372 user_opt.uoptind = user_opt.optind; 1373 } 1374 1375 voptarg = global("OPTARG"); 1376 voptarg->flag &= ~RDONLY; /* at&t ksh clears ro and int */ 1377 /* Paranoia: ensure no bizarre results. */ 1378 if (voptarg->flag & INTEGER) 1379 typeset("OPTARG", 0, INTEGER, 0, 0); 1380 if (user_opt.optarg == (char *) 0) 1381 unset(voptarg, 0); 1382 else 1383 /* This can't fail (have cleared readonly/integer) */ 1384 setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR); 1385 1386 ret = 0; 1387 1388 vq = global(var); 1389 /* Error message already printed (integer, readonly) */ 1390 if (!setstr(vq, buf, KSH_RETURN_ERROR)) 1391 ret = 1; 1392 if (Flag(FEXPORT)) 1393 typeset(var, EXPORT, 0, 0, 0); 1394 1395 return optc < 0 ? 1 : ret; 1396 } 1397 1398 #ifdef EMACS 1399 int 1400 c_bind(wp) 1401 char **wp; 1402 { 1403 int rv = 0, macro = 0, list = 0; 1404 char *cp; 1405 int optc; 1406 1407 while ((optc = ksh_getopt(wp, &builtin_opt, "lm")) != EOF) 1408 switch (optc) { 1409 case 'l': 1410 list = 1; 1411 break; 1412 case 'm': 1413 macro = 1; 1414 break; 1415 case '?': 1416 return 1; 1417 } 1418 wp += builtin_opt.optind; 1419 1420 if (*wp == NULL) /* list all */ 1421 rv = x_bind(NULL, NULL, 0, list); 1422 1423 for (; *wp != NULL; wp++) { 1424 cp = strchr(*wp, '='); 1425 if (cp != NULL) 1426 *cp++ = '\0'; 1427 if (x_bind(*wp, cp, macro, 0)) 1428 rv = 1; 1429 } 1430 1431 return rv; 1432 } 1433 #endif 1434 1435 /* A leading = means assignments before command are kept; 1436 * a leading * means a POSIX special builtin; 1437 * a leading + means a POSIX regular builtin 1438 * (* and + should not be combined). 1439 */ 1440 const struct builtin kshbuiltins [] = { 1441 {"+alias", c_alias}, /* no =: at&t manual wrong */ 1442 {"+cd", c_cd}, 1443 {"+command", c_command}, 1444 {"echo", c_print}, 1445 {"*=export", c_typeset}, 1446 #ifdef HISTORY 1447 {"+fc", c_fc}, 1448 #endif /* HISTORY */ 1449 {"+getopts", c_getopts}, 1450 {"+jobs", c_jobs}, 1451 {"+kill", c_kill}, 1452 #ifdef KSH 1453 {"let", c_let}, 1454 #endif /* KSH */ 1455 {"print", c_print}, 1456 {"pwd", c_pwd}, 1457 {"*=readonly", c_typeset}, 1458 {"=typeset", c_typeset}, 1459 {"+unalias", c_unalias}, 1460 {"whence", c_whence}, 1461 #ifdef JOBS 1462 {"+bg", c_fgbg}, 1463 {"+fg", c_fgbg}, 1464 #endif 1465 #ifdef EMACS 1466 {"bind", c_bind}, 1467 #endif 1468 {NULL, NULL} 1469 }; 1470