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