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