1 /* 2 * tc.func.c: New tcsh builtins. 3 */ 4 /*- 5 * Copyright (c) 1980, 1991 The Regents of the University of California. 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 #include "sh.h" 33 #include "ed.h" 34 #include "ed.defns.h" /* for the function names */ 35 #include "tw.h" 36 #include "tc.h" 37 #ifdef WINNT_NATIVE 38 #include "nt.const.h" 39 #else /* WINNT_NATIVE */ 40 #include <sys/wait.h> 41 #endif /* WINNT_NATIVE */ 42 43 #ifdef AFS 44 #include <afs/stds.h> 45 #include <afs/kautils.h> 46 long ka_UserAuthenticateGeneral(); 47 #endif /* AFS */ 48 49 #ifdef TESLA 50 extern int do_logout; 51 #endif /* TESLA */ 52 extern time_t t_period; 53 extern int just_signaled; 54 static int precmd_active = 0; 55 static int jobcmd_active = 0; /* GrP */ 56 int postcmd_active = 0; 57 static int periodic_active = 0; 58 static int cwdcmd_active = 0; /* PWP: for cwd_cmd */ 59 static int beepcmd_active = 0; 60 static void (*alm_fun)(void) = NULL; 61 62 static void auto_logout (void); 63 static char *xgetpass (const char *); 64 static void auto_lock (void); 65 #ifdef BSDJOBS 66 static void insert (struct wordent *, int); 67 static void insert_we (struct wordent *, struct wordent *); 68 static int inlist (Char *, Char *); 69 #endif /* BSDJOBS */ 70 static int tildecompare (const void *, const void *); 71 static Char *gethomedir (const Char *); 72 #ifdef REMOTEHOST 73 static void palarm (int); 74 static void getremotehost (int); 75 #endif /* REMOTEHOST */ 76 77 /* 78 * Tops-C shell 79 */ 80 81 /* 82 * expand_lex: Take the given lex and return an expanded version of it. 83 * First guy in lex list is ignored; last guy is ^J which we ignore. 84 * Only take lex'es from position 'from' to position 'to' inclusive 85 * 86 * Note: csh sometimes sets bit 8 in characters which causes all kinds 87 * of problems if we don't mask it here. Note: excl's in lexes have been 88 * un-back-slashed and must be re-back-slashed 89 * 90 */ 91 /* PWP: this is a combination of the old sprlex() and the expand_lex from 92 the magic-space stuff */ 93 94 Char * 95 expand_lex(const struct wordent *sp0, int from, int to) 96 { 97 struct Strbuf buf = Strbuf_INIT; 98 const struct wordent *sp; 99 Char *s; 100 Char prev_c; 101 int i, dolsq = 0; 102 103 prev_c = '\0'; 104 105 if (!sp0 || (sp = sp0->next) == sp0 || sp == (sp0 = sp0->prev)) 106 return Strbuf_finish(&buf); /* null lex */ 107 108 for (i = 0; ; i++) { 109 if ((i >= from) && (i <= to)) { /* if in range */ 110 for (s = sp->word; *s; s++) { 111 /* 112 * bugfix by Michael Bloom: anything but the current history 113 * character {(PWP) and backslash} seem to be dealt with 114 * elsewhere. 115 */ 116 if (*s & QUOTE) { 117 if ((*s & TRIM) == HIST && HIST != '\0') 118 Strbuf_append1(&buf, '\\'); 119 else 120 switch (*s & TRIM) { 121 case '\'': 122 if (prev_c == '\\') break; 123 if (dolsq) { 124 dolsq = 0; 125 break; 126 } 127 if (prev_c == '$') { 128 dolsq++; 129 break; 130 } 131 Strbuf_append1(&buf, '\\'); 132 break; 133 case '\"': 134 if (prev_c == '\\') break; 135 if (dolsq) break; 136 Strbuf_append1(&buf, '\\'); 137 break; 138 } 139 } 140 #if INVALID_BYTE != 0 141 if ((*s & INVALID_BYTE) != INVALID_BYTE) /* *s < INVALID_BYTE */ 142 Strbuf_append1(&buf, *s & TRIM); 143 else 144 Strbuf_append1(&buf, *s); 145 #else 146 Strbuf_append1(&buf, *s & TRIM); 147 #endif 148 prev_c = *s; 149 } 150 Strbuf_append1(&buf, ' '); 151 } 152 sp = sp->next; 153 if (sp == sp0) 154 break; 155 } 156 if (buf.len != 0) 157 buf.len--; /* get rid of trailing space */ 158 159 return Strbuf_finish(&buf); 160 } 161 162 Char * 163 sprlex(const struct wordent *sp0) 164 { 165 return expand_lex(sp0, 0, INT_MAX); 166 } 167 168 169 Char * 170 Itoa(int n, size_t min_digits, Char attributes) 171 { 172 /* 173 * The array size here is derived from 174 * log8(UINT_MAX) 175 * which is guaranteed to be enough for a decimal 176 * representation. We add 1 because integer divide 177 * rounds down. 178 */ 179 #ifndef CHAR_BIT 180 # define CHAR_BIT 8 181 #endif 182 Char buf[CHAR_BIT * sizeof(int) / 3 + 1], *res, *p, *s; 183 unsigned int un; /* handle most negative # too */ 184 int pad = (min_digits != 0); 185 186 if (sizeof(buf) - 1 < min_digits) 187 min_digits = sizeof(buf) - 1; 188 189 un = n; 190 if (n < 0) 191 un = -n; 192 193 p = buf; 194 do { 195 *p++ = un % 10 + '0'; 196 un /= 10; 197 } while ((pad && (ssize_t)--min_digits > 0) || un != 0); 198 199 res = xmalloc((p - buf + 2) * sizeof(*res)); 200 s = res; 201 if (n < 0) 202 *s++ = '-'; 203 while (p > buf) 204 *s++ = *--p | attributes; 205 206 *s = '\0'; 207 return res; 208 } 209 210 211 /*ARGSUSED*/ 212 void 213 dolist(Char **v, struct command *c) 214 { 215 Char **globbed; 216 int i, k, ret = 0; 217 struct stat st; 218 219 USE(c); 220 if (*++v == NULL) { 221 struct Strbuf word = Strbuf_INIT; 222 223 Strbuf_terminate(&word); 224 cleanup_push(&word, Strbuf_cleanup); 225 (void) t_search(&word, LIST, TW_ZERO, 0, STRNULL, 0); 226 cleanup_until(&word); 227 return; 228 } 229 v = glob_all_or_error(v); 230 globbed = v; 231 cleanup_push(globbed, blk_cleanup); 232 for (k = 0; v[k] != NULL && v[k][0] != '-'; k++) 233 continue; 234 if (v[k]) { 235 /* 236 * We cannot process a flag therefore we let ls do it right. 237 */ 238 Char *lspath; 239 struct command *t; 240 struct wordent cmd, *nextword, *lastword; 241 Char *cp; 242 struct varent *vp; 243 244 if (setintr) { 245 pintr_disabled++; 246 cleanup_push(&pintr_disabled, disabled_cleanup); 247 } 248 if (seterr) { 249 xfree(seterr); 250 seterr = NULL; 251 } 252 253 lspath = STRls; 254 STRmCF[1] = 'C'; 255 STRmCF[3] = '\0'; 256 /* Look at listflags, to add -A to the flags, to get a path 257 of ls if necessary */ 258 if ((vp = adrof(STRlistflags)) != NULL && vp->vec != NULL && 259 vp->vec[0] != STRNULL) { 260 if (vp->vec[1] != NULL && vp->vec[1][0] != '\0') 261 lspath = vp->vec[1]; 262 for (cp = vp->vec[0]; *cp; cp++) 263 switch (*cp) { 264 case 'x': 265 STRmCF[1] = 'x'; 266 break; 267 case 'a': 268 STRmCF[3] = 'a'; 269 break; 270 case 'A': 271 STRmCF[3] = 'A'; 272 break; 273 default: 274 break; 275 } 276 } 277 278 cmd.word = STRNULL; 279 lastword = &cmd; 280 nextword = xcalloc(1, sizeof cmd); 281 nextword->word = Strsave(lspath); 282 lastword->next = nextword; 283 nextword->prev = lastword; 284 lastword = nextword; 285 nextword = xcalloc(1, sizeof cmd); 286 nextword->word = Strsave(STRmCF); 287 lastword->next = nextword; 288 nextword->prev = lastword; 289 #if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE) 290 if (dspmbyte_ls) { 291 lastword = nextword; 292 nextword = xcalloc(1, sizeof cmd); 293 nextword->word = Strsave(STRmmliteral); 294 lastword->next = nextword; 295 nextword->prev = lastword; 296 } 297 #endif 298 #ifdef COLOR_LS_F 299 if (color_context_ls) { 300 lastword = nextword; 301 nextword = xcalloc(1, sizeof cmd); 302 nextword->word = Strsave(STRmmcolormauto); 303 lastword->next = nextword; 304 nextword->prev = lastword; 305 } 306 #endif /* COLOR_LS_F */ 307 lastword = nextword; 308 for (cp = *v; cp; cp = *++v) { 309 nextword = xcalloc(1, sizeof cmd); 310 nextword->word = quote(Strsave(cp)); 311 lastword->next = nextword; 312 nextword->prev = lastword; 313 lastword = nextword; 314 } 315 lastword->next = &cmd; 316 cmd.prev = lastword; 317 cleanup_push(&cmd, lex_cleanup); 318 319 /* build a syntax tree for the command. */ 320 t = syntax(cmd.next, &cmd, 0); 321 cleanup_push(t, syntax_cleanup); 322 if (seterr) 323 stderror(ERR_OLD); 324 /* expand aliases like process() does */ 325 /* alias(&cmd); */ 326 /* execute the parse tree. */ 327 execute(t, tpgrp > 0 ? tpgrp : -1, NULL, NULL, FALSE); 328 /* done. free the lex list and parse tree. */ 329 cleanup_until(&cmd); 330 if (setintr) 331 cleanup_until(&pintr_disabled); 332 } 333 else { 334 Char *dp, *tmp; 335 struct Strbuf buf = Strbuf_INIT; 336 337 cleanup_push(&buf, Strbuf_cleanup); 338 for (k = 0, i = 0; v[k] != NULL; k++) { 339 tmp = dnormalize(v[k], symlinks == SYM_IGNORE); 340 cleanup_push(tmp, xfree); 341 dp = Strend(tmp) - 1; 342 if (*dp == '/' && dp != tmp) 343 #ifdef apollo 344 if (dp != &tmp[1]) 345 #endif /* apollo */ 346 *dp = '\0'; 347 if (stat(short2str(tmp), &st) == -1) { 348 int err; 349 350 err = errno; 351 if (k != i) { 352 if (i != 0) 353 xputchar('\n'); 354 print_by_column(STRNULL, &v[i], k - i, FALSE); 355 } 356 haderr = 1; 357 xprintf("%" TCSH_S ": %s.\n", tmp, strerror(err)); 358 haderr = 0; 359 i = k + 1; 360 ret = 1; 361 } 362 else if (S_ISDIR(st.st_mode)) { 363 Char *cp; 364 365 if (k != i) { 366 if (i != 0) 367 xputchar('\n'); 368 print_by_column(STRNULL, &v[i], k - i, FALSE); 369 } 370 if (k != 0 && v[1] != NULL) 371 xputchar('\n'); 372 xprintf("%" TCSH_S ":\n", tmp); 373 buf.len = 0; 374 for (cp = tmp; *cp; cp++) 375 Strbuf_append1(&buf, (*cp | QUOTE)); 376 Strbuf_terminate(&buf); 377 dp = &buf.s[buf.len - 1]; 378 if ( 379 #ifdef WINNT_NATIVE 380 (*dp != (Char) (':' | QUOTE)) && 381 #endif /* WINNT_NATIVE */ 382 (*dp != (Char) ('/' | QUOTE))) { 383 Strbuf_append1(&buf, '/'); 384 Strbuf_terminate(&buf); 385 } else 386 *dp &= TRIM; 387 (void) t_search(&buf, LIST, TW_ZERO, 0, STRNULL, 0); 388 i = k + 1; 389 } 390 cleanup_until(tmp); 391 } 392 cleanup_until(&buf); 393 if (k != i) { 394 if (i != 0) 395 xputchar('\n'); 396 print_by_column(STRNULL, &v[i], k - i, FALSE); 397 } 398 if (ret) 399 stderror(ERR_SILENT); 400 } 401 402 cleanup_until(globbed); 403 } 404 405 extern int GotTermCaps; 406 407 /*ARGSUSED*/ 408 void 409 dotelltc(Char **v, struct command *c) 410 { 411 USE(v); 412 USE(c); 413 if (!GotTermCaps) 414 GetTermCaps(); 415 TellTC(); 416 } 417 418 /*ARGSUSED*/ 419 void 420 doechotc(Char **v, struct command *c) 421 { 422 USE(c); 423 if (!GotTermCaps) 424 GetTermCaps(); 425 EchoTC(++v); 426 } 427 428 /*ARGSUSED*/ 429 void 430 dosettc(Char **v, struct command *c) 431 { 432 char *tv[2]; 433 434 USE(c); 435 if (!GotTermCaps) 436 GetTermCaps(); 437 438 tv[0] = strsave(short2str(v[1])); 439 cleanup_push(tv[0], xfree); 440 tv[1] = strsave(short2str(v[2])); 441 cleanup_push(tv[1], xfree); 442 SetTC(tv[0], tv[1]); 443 cleanup_until(tv[0]); 444 } 445 446 /* The dowhich() is by: 447 * Andreas Luik <luik@isaak.isa.de> 448 * I S A GmbH - Informationssysteme fuer computerintegrierte Automatisierung 449 * Azenberstr. 35 450 * D-7000 Stuttgart 1 451 * West-Germany 452 * Thanks!! 453 */ 454 int 455 cmd_expand(Char *cmd, Char **str) 456 { 457 struct wordent lexp[3]; 458 struct varent *vp; 459 int rv = TRUE; 460 461 lexp[0].next = &lexp[1]; 462 lexp[1].next = &lexp[2]; 463 lexp[2].next = &lexp[0]; 464 465 lexp[0].prev = &lexp[2]; 466 lexp[1].prev = &lexp[0]; 467 lexp[2].prev = &lexp[1]; 468 469 lexp[0].word = STRNULL; 470 lexp[2].word = STRret; 471 472 if ((vp = adrof1(cmd, &aliases)) != NULL && vp->vec != NULL) { 473 if (str == NULL) { 474 xprintf(CGETS(22, 1, "%" TCSH_S ": \t aliased to "), cmd); 475 blkpr(vp->vec); 476 xputchar('\n'); 477 } 478 else 479 *str = blkexpand(vp->vec); 480 } 481 else { 482 lexp[1].word = cmd; 483 rv = tellmewhat(lexp, str); 484 } 485 return rv; 486 } 487 488 489 /*ARGSUSED*/ 490 void 491 dowhich(Char **v, struct command *c) 492 { 493 int rv = TRUE; 494 USE(c); 495 496 /* 497 * We don't want to glob dowhich args because we lose quoteing 498 * E.g. which \ls if ls is aliased will not work correctly if 499 * we glob here. 500 */ 501 502 while (*++v) 503 rv &= cmd_expand(*v, NULL); 504 505 if (!rv) 506 setstatus(1); 507 } 508 509 static int 510 findvv(Char **vv, const char *cp) 511 { 512 for (; vv && *vv; vv++) { 513 size_t i; 514 for (i = 0; (*vv)[i] && (*vv)[i] == cp[i]; i++) 515 continue; 516 if ((*vv)[i] == '\0' && cp[i] == '\0') 517 return 1; 518 } 519 return 0; 520 } 521 522 /* PWP: a hack to start up your stopped editor on a single keystroke */ 523 /* jbs - fixed hack so it worked :-) 3/28/89 */ 524 525 struct process * 526 find_stop_ed(void) 527 { 528 struct process *pp, *retp; 529 const char *ep = NULL, *vp = NULL; 530 char *cp, *p; 531 size_t epl = 0, vpl = 0; 532 int pstatus; 533 struct varent *varp; 534 Char **vv; 535 536 if (pcurrent == NULL) /* see if we have any jobs */ 537 return NULL; /* nope */ 538 539 if ((varp = adrof(STReditors)) != NULL) 540 vv = varp->vec; 541 else 542 vv = NULL; 543 544 if (! vv) { 545 if ((ep = getenv("EDITOR")) != NULL) { /* if we have a value */ 546 if ((p = strrchr(ep, '/')) != NULL) /* if it has a path */ 547 ep = p + 1; /* then we want only the last part */ 548 } 549 else 550 ep = "ed"; 551 552 if ((vp = getenv("VISUAL")) != NULL) { /* if we have a value */ 553 if ((p = strrchr(vp, '/')) != NULL) /* and it has a path */ 554 vp = p + 1; /* then we want only the last part */ 555 } 556 else 557 vp = "vi"; 558 559 for (vpl = 0; vp[vpl] && !isspace((unsigned char)vp[vpl]); vpl++) 560 continue; 561 for (epl = 0; ep[epl] && !isspace((unsigned char)ep[epl]); epl++) 562 continue; 563 } 564 565 retp = NULL; 566 for (pp = proclist.p_next; pp; pp = pp->p_next) 567 if (pp->p_procid == pp->p_jobid) { 568 569 /* 570 * Only foreground an edit session if it is suspended. Some GUI 571 * editors have may be happily running in a separate window, no 572 * point in foregrounding these if they're already running - webb 573 */ 574 pstatus = (int) (pp->p_flags & PALLSTATES); 575 if (pstatus != PINTERRUPTED && pstatus != PSTOPPED && 576 pstatus != PSIGNALED) 577 continue; 578 579 p = short2str(pp->p_command); 580 /* get the first word */ 581 for (cp = p; *cp && !isspace((unsigned char) *cp); cp++) 582 continue; 583 *cp = '\0'; 584 585 if ((cp = strrchr(p, '/')) != NULL) /* and it has a path */ 586 cp = cp + 1; /* then we want only the last part */ 587 else 588 cp = p; /* else we get all of it */ 589 590 /* 591 * If we find the current name in the $editors array (if set) 592 * or as $EDITOR or $VISUAL (if $editors not set), fg it. 593 */ 594 if ((vv && findvv(vv, cp)) || 595 (epl && strncmp(ep, cp, epl) == 0 && cp[epl] == '\0') || 596 (vpl && strncmp(vp, cp, vpl) == 0 && cp[vpl] == '\0')) { 597 /* 598 * If there is a choice, then choose the current process if 599 * available, or the previous process otherwise, or else 600 * anything will do - Robert Webb (robertw@mulga.cs.mu.oz.au). 601 */ 602 if (pp == pcurrent) 603 return pp; 604 else if (retp == NULL || pp == pprevious) 605 retp = pp; 606 } 607 } 608 609 return retp; /* Will be NULL if we didn't find a job */ 610 } 611 612 void 613 fg_proc_entry(struct process *pp) 614 { 615 jmp_buf_t osetexit; 616 int ohaderr; 617 Char oGettingInput; 618 size_t omark; 619 620 getexit(osetexit); 621 622 pintr_disabled++; 623 oGettingInput = GettingInput; 624 GettingInput = 0; 625 626 ohaderr = haderr; /* we need to ignore setting of haderr due to 627 * process getting stopped by a signal */ 628 omark = cleanup_push_mark(); 629 if (setexit() == 0) { /* come back here after pjwait */ 630 pendjob(); 631 (void) alarm(0); /* No autologout */ 632 alrmcatch_disabled = 1; 633 if (!pstart(pp, 1)) { 634 pp->p_procid = 0; 635 stderror(ERR_BADJOB, pp->p_command, strerror(errno)); 636 } 637 pjwait(pp); 638 } 639 setalarm(1); /* Autologout back on */ 640 cleanup_pop_mark(omark); 641 resexit(osetexit); 642 haderr = ohaderr; 643 GettingInput = oGettingInput; 644 645 disabled_cleanup(&pintr_disabled); 646 } 647 648 static char * 649 xgetpass(const char *prm) 650 { 651 static struct strbuf pass; /* = strbuf_INIT; */ 652 int fd; 653 sigset_t oset, set; 654 struct sigaction sa, osa; 655 656 sa.sa_handler = SIG_IGN; 657 sigemptyset(&sa.sa_mask); 658 sa.sa_flags = 0; 659 (void)sigaction(SIGINT, &sa, &osa); 660 661 sigemptyset(&set); 662 sigaddset(&set, SIGINT); 663 (void)sigprocmask(SIG_UNBLOCK, &set, &oset); 664 665 cleanup_push(&osa, sigint_cleanup); 666 cleanup_push(&oset, sigprocmask_cleanup); 667 (void) Rawmode(); /* Make sure, cause we want echo off */ 668 fd = xopen("/dev/tty", O_RDWR|O_LARGEFILE); 669 if (fd == -1) 670 fd = SHIN; 671 else 672 cleanup_push(&fd, open_cleanup); 673 674 xprintf("%s", prm); flush(); 675 pass.len = 0; 676 for (;;) { 677 char c; 678 679 if (xread(fd, &c, 1) < 1 || c == '\n') 680 break; 681 strbuf_append1(&pass, c); 682 } 683 strbuf_terminate(&pass); 684 685 cleanup_until(&osa); 686 687 return pass.s; 688 } 689 690 #ifndef NO_CRYPT 691 #if !HAVE_DECL_CRYPT 692 extern char *crypt (); 693 #endif 694 #ifdef HAVE_CRYPT_H 695 #include <crypt.h> 696 #endif 697 #endif 698 699 /* 700 * Ask the user for his login password to continue working 701 * On systems that have a shadow password, this will only 702 * work for root, but what can we do? 703 * 704 * If we fail to get the password, then we log the user out 705 * immediately 706 */ 707 /*ARGSUSED*/ 708 static void 709 auto_lock(void) 710 { 711 #ifndef NO_CRYPT 712 713 int i; 714 char *srpp = NULL; 715 struct passwd *pw; 716 717 #undef XCRYPT 718 719 #if defined(HAVE_AUTH_H) && defined(HAVE_GETAUTHUID) 720 721 struct authorization *apw; 722 extern char *crypt16 (const char *, const char *); 723 724 # define XCRYPT(pw, a, b) crypt16(a, b) 725 726 if ((pw = xgetpwuid(euid)) != NULL && /* effective user passwd */ 727 (apw = getauthuid(euid)) != NULL) /* enhanced ultrix passwd */ 728 srpp = apw->a_password; 729 730 #elif defined(HAVE_SHADOW_H) 731 732 struct spwd *spw; 733 734 # define XCRYPT(pw, a, b) crypt(a, b) 735 736 if ((pw = xgetpwuid(euid)) != NULL) { /* effective user passwd */ 737 errno = 0; 738 while ((spw = getspnam(pw->pw_name)) == NULL && errno == EINTR) { 739 handle_pending_signals(); 740 errno = 0; 741 } 742 if (spw != NULL) /* shadowed passwd */ 743 srpp = spw->sp_pwdp; 744 } 745 746 #else 747 748 749 #ifdef __CYGWIN__ 750 # define XCRYPT(pw, a, b) cygwin_xcrypt(pw, a, b) 751 #else 752 # define XCRYPT(pw, a, b) crypt(a, b) 753 #endif 754 755 #if !defined(__MVS__) 756 if ((pw = xgetpwuid(euid)) != NULL) /* effective user passwd */ 757 srpp = pw->pw_passwd; 758 #endif /* !MVS */ 759 760 #endif 761 762 if (srpp == NULL) { 763 auto_logout(); 764 /*NOTREACHED*/ 765 return; 766 } 767 768 setalarm(0); /* Not for locking any more */ 769 xputchar('\n'); 770 for (i = 0; i < 5; i++) { 771 const char *crpp; 772 char *pp; 773 #ifdef AFS 774 char *afsname; 775 Char *safs; 776 777 if ((safs = varval(STRafsuser)) != STRNULL) 778 afsname = short2str(safs); 779 else 780 if ((afsname = getenv("AFSUSER")) == NULL) 781 afsname = pw->pw_name; 782 #endif 783 pp = xgetpass("Password:"); 784 785 crpp = XCRYPT(pw, pp, srpp); 786 if ((crpp && strcmp(crpp, srpp) == 0) 787 #ifdef AFS 788 || (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION, 789 afsname, /* name */ 790 NULL, /* instance */ 791 NULL, /* realm */ 792 pp, /* password */ 793 0, /* lifetime */ 794 0, 0, /* spare */ 795 NULL) /* reason */ 796 == 0) 797 #endif /* AFS */ 798 ) { 799 (void) memset(pp, 0, strlen(pp)); 800 if (GettingInput && !just_signaled) { 801 (void) Rawmode(); 802 ClearLines(); 803 ClearDisp(); 804 Refresh(); 805 } 806 just_signaled = 0; 807 return; 808 } 809 xprintf(CGETS(22, 2, "\nIncorrect passwd for %s\n"), pw->pw_name); 810 } 811 #endif /* NO_CRYPT */ 812 auto_logout(); 813 } 814 815 816 static void 817 auto_logout(void) 818 { 819 xprintf("auto-logout\n"); 820 /* Don't leave the tty in raw mode */ 821 if (editing) 822 (void) Cookedmode(); 823 xclose(SHIN); 824 setcopy(STRlogout, STRautomatic, VAR_READWRITE); 825 child = 1; 826 #ifdef TESLA 827 do_logout = 1; 828 #endif /* TESLA */ 829 GettingInput = FALSE; /* make flush() work to write hist files. Huber*/ 830 goodbye(NULL, NULL); 831 } 832 833 void 834 alrmcatch(void) 835 { 836 (*alm_fun)(); 837 setalarm(1); 838 } 839 840 /* 841 * Karl Kleinpaste, 21oct1983. 842 * Added precmd(), which checks for the alias 843 * precmd in aliases. If it's there, the alias 844 * is executed as a command. This is done 845 * after mailchk() and just before print- 846 * ing the prompt. Useful for things like printing 847 * one's current directory just before each command. 848 */ 849 void 850 precmd(void) 851 { 852 pintr_disabled++; 853 cleanup_push(&pintr_disabled, disabled_cleanup); 854 if (precmd_active) { /* an error must have been caught */ 855 aliasrun(2, STRunalias, STRprecmd); 856 xprintf("%s", CGETS(22, 3, "Faulty alias 'precmd' removed.\n")); 857 goto leave; 858 } 859 precmd_active = 1; 860 if (!whyles && adrof1(STRprecmd, &aliases)) 861 aliasrun(1, STRprecmd, NULL); 862 leave: 863 precmd_active = 0; 864 cleanup_until(&pintr_disabled); 865 } 866 867 void 868 postcmd(void) 869 { 870 pintr_disabled++; 871 cleanup_push(&pintr_disabled, disabled_cleanup); 872 if (postcmd_active) { /* an error must have been caught */ 873 aliasrun(2, STRunalias, STRpostcmd); 874 xprintf("%s", CGETS(22, 3, "Faulty alias 'postcmd' removed.\n")); 875 goto leave; 876 } 877 postcmd_active = 1; 878 if (!whyles && adrof1(STRpostcmd, &aliases)) 879 aliasrun(1, STRpostcmd, NULL); 880 leave: 881 postcmd_active = 0; 882 cleanup_until(&pintr_disabled); 883 } 884 885 /* 886 * Paul Placeway 11/24/87 Added cwd_cmd by hacking precmd() into 887 * submission... Run every time $cwd is set (after it is set). Useful 888 * for putting your machine and cwd (or anything else) in an xterm title 889 * space. 890 */ 891 void 892 cwd_cmd(void) 893 { 894 pintr_disabled++; 895 cleanup_push(&pintr_disabled, disabled_cleanup); 896 if (cwdcmd_active) { /* an error must have been caught */ 897 aliasrun(2, STRunalias, STRcwdcmd); 898 xprintf("%s", CGETS(22, 4, "Faulty alias 'cwdcmd' removed.\n")); 899 goto leave; 900 } 901 cwdcmd_active = 1; 902 if (!whyles && adrof1(STRcwdcmd, &aliases)) 903 aliasrun(1, STRcwdcmd, NULL); 904 leave: 905 cwdcmd_active = 0; 906 cleanup_until(&pintr_disabled); 907 } 908 909 /* 910 * Joachim Hoenig 07/16/91 Added beep_cmd, run every time tcsh wishes 911 * to beep the terminal bell. Useful for playing nice sounds instead. 912 */ 913 void 914 beep_cmd(void) 915 { 916 pintr_disabled++; 917 cleanup_push(&pintr_disabled, disabled_cleanup); 918 if (beepcmd_active) { /* an error must have been caught */ 919 aliasrun(2, STRunalias, STRbeepcmd); 920 xprintf("%s", CGETS(22, 5, "Faulty alias 'beepcmd' removed.\n")); 921 goto leave; 922 } 923 beepcmd_active = 1; 924 if (!whyles && adrof1(STRbeepcmd, &aliases)) 925 aliasrun(1, STRbeepcmd, NULL); 926 leave: 927 beepcmd_active = 0; 928 cleanup_until(&pintr_disabled); 929 } 930 931 932 /* 933 * Karl Kleinpaste, 18 Jan 1984. 934 * Added period_cmd(), which executes the alias "periodic" every 935 * $tperiod minutes. Useful for occasional checking of msgs and such. 936 */ 937 void 938 period_cmd(void) 939 { 940 Char *vp; 941 time_t t, interval; 942 943 if (whyles) 944 return; 945 pintr_disabled++; 946 cleanup_push(&pintr_disabled, disabled_cleanup); 947 if (periodic_active) { /* an error must have been caught */ 948 aliasrun(2, STRunalias, STRperiodic); 949 xprintf("%s", CGETS(22, 6, "Faulty alias 'periodic' removed.\n")); 950 goto leave; 951 } 952 periodic_active = 1; 953 if (!whyles && adrof1(STRperiodic, &aliases)) { 954 vp = varval(STRtperiod); 955 if (vp == STRNULL) { 956 aliasrun(1, STRperiodic, NULL); 957 goto leave; 958 } 959 interval = getn(vp); 960 (void) time(&t); 961 if (t - t_period >= interval * 60) { 962 t_period = t; 963 aliasrun(1, STRperiodic, NULL); 964 } 965 } 966 leave: 967 periodic_active = 0; 968 cleanup_until(&pintr_disabled); 969 } 970 971 972 /* 973 * GrP Greg Parker May 2001 974 * Added job_cmd(), which is run every time a job is started or 975 * foregrounded. The command is passed a single argument, the string 976 * used to start the job originally. With precmd, useful for setting 977 * xterm titles. 978 * Cloned from cwd_cmd(). 979 */ 980 void 981 job_cmd(Char *args) 982 { 983 if (whyles) 984 return; 985 pintr_disabled++; 986 cleanup_push(&pintr_disabled, disabled_cleanup); 987 if (jobcmd_active) { /* an error must have been caught */ 988 aliasrun(2, STRunalias, STRjobcmd); 989 xprintf("%s", CGETS(22, 14, "Faulty alias 'jobcmd' removed.\n")); 990 goto leave; 991 } 992 jobcmd_active = 1; 993 if (!whyles && adrof1(STRjobcmd, &aliases)) { 994 struct process *pp = pcurrjob; /* put things back after the hook */ 995 aliasrun(2, STRjobcmd, args); 996 pcurrjob = pp; 997 } 998 leave: 999 jobcmd_active = 0; 1000 cleanup_until(&pintr_disabled); 1001 } 1002 1003 1004 /* 1005 * Karl Kleinpaste, 21oct1983. 1006 * Set up a one-word alias command, for use for special things. 1007 * This code is based on the mainline of process(). 1008 */ 1009 void 1010 aliasrun(int cnt, Char *s1, Char *s2) 1011 { 1012 struct wordent w, *new1, *new2; /* for holding alias name */ 1013 struct command *t = NULL; 1014 jmp_buf_t osetexit; 1015 int status; 1016 size_t omark; 1017 1018 getexit(osetexit); 1019 if (seterr) { 1020 cleanhist(); 1021 xfree(seterr); 1022 seterr = NULL; /* don't repeatedly print err msg. */ 1023 } 1024 w.word = STRNULL; 1025 new1 = xcalloc(1, sizeof w); 1026 new1->word = Strsave(s1); 1027 if (cnt == 1) { 1028 /* build a lex list with one word. */ 1029 w.next = w.prev = new1; 1030 new1->next = new1->prev = &w; 1031 } 1032 else { 1033 /* build a lex list with two words. */ 1034 new2 = xcalloc(1, sizeof w); 1035 new2->word = Strsave(s2); 1036 w.next = new2->prev = new1; 1037 new1->next = w.prev = new2; 1038 new1->prev = new2->next = &w; 1039 } 1040 cleanup_push(&w, lex_cleanup); 1041 1042 /* Save the old status */ 1043 status = getstatus(); 1044 1045 /* expand aliases like process() does. */ 1046 alias(&w); 1047 /* build a syntax tree for the command. */ 1048 t = syntax(w.next, &w, 0); 1049 cleanup_push(t, syntax_cleanup); 1050 if (seterr) 1051 stderror(ERR_OLD); 1052 1053 psavejob(); 1054 cleanup_push(&cnt, psavejob_cleanup); /* cnt is used only as a marker */ 1055 1056 /* catch any errors here */ 1057 omark = cleanup_push_mark(); 1058 if (setexit() == 0) 1059 /* execute the parse tree. */ 1060 /* 1061 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de> 1062 * was execute(t, tpgrp); 1063 */ 1064 execute(t, tpgrp > 0 ? tpgrp : -1, NULL, NULL, TRUE); 1065 /* reset the error catcher to the old place */ 1066 cleanup_pop_mark(omark); 1067 resexit(osetexit); 1068 if (haderr) { 1069 haderr = 0; 1070 /* 1071 * Either precmd, or cwdcmd, or periodic had an error. Call it again so 1072 * that it is removed 1073 */ 1074 if (precmd_active) 1075 precmd(); 1076 if (postcmd_active) 1077 postcmd(); 1078 #ifdef notdef 1079 /* 1080 * XXX: On the other hand, just interrupting them causes an error too. 1081 * So if we hit ^C in the middle of cwdcmd or periodic the alias gets 1082 * removed. We don't want that. Note that we want to remove precmd 1083 * though, cause that could lead into an infinite loop. This should be 1084 * fixed correctly, but then haderr should give us the whole exit 1085 * status not just true or false. 1086 */ 1087 else if (cwdcmd_active) 1088 cwd_cmd(); 1089 else if (beepcmd_active) 1090 beep_cmd(); 1091 else if (periodic_active) 1092 period_cmd(); 1093 #endif /* notdef */ 1094 } 1095 cleanup_until(&w); 1096 pendjob(); 1097 cleanhist(); 1098 /* Restore status */ 1099 setstrstatus(putn((tcsh_number_t)status)); 1100 } 1101 1102 void 1103 setalarm(int lck) 1104 { 1105 struct varent *vp; 1106 Char *cp; 1107 unsigned alrm_time = 0, logout_time, lock_time; 1108 time_t cl, nl, sched_dif; 1109 1110 if ((vp = adrof(STRautologout)) != NULL && vp->vec != NULL) { 1111 if ((cp = vp->vec[0]) != 0) { 1112 if ((logout_time = (unsigned) atoi(short2str(cp)) * 60) > 0) { 1113 #ifdef SOLARIS2 1114 /* 1115 * Solaris alarm(2) uses a timer based in clock ticks 1116 * internally so it multiplies our value with CLK_TCK... 1117 * Of course that can overflow leading to unexpected 1118 * results, so we clip it here. Grr. Where is that 1119 * documented folks? 1120 */ 1121 if (logout_time >= 0x7fffffff / CLK_TCK) 1122 logout_time = 0x7fffffff / CLK_TCK; 1123 #endif /* SOLARIS2 */ 1124 alrm_time = logout_time; 1125 alm_fun = auto_logout; 1126 } 1127 } 1128 if ((cp = vp->vec[1]) != 0) { 1129 if ((lock_time = (unsigned) atoi(short2str(cp)) * 60) > 0) { 1130 if (lck) { 1131 if (alrm_time == 0 || lock_time < alrm_time) { 1132 alrm_time = lock_time; 1133 alm_fun = auto_lock; 1134 } 1135 } 1136 else /* lock_time always < alrm_time */ 1137 if (alrm_time) 1138 alrm_time -= lock_time; 1139 } 1140 } 1141 } 1142 if ((nl = sched_next()) != -1) { 1143 (void) time(&cl); 1144 sched_dif = nl > cl ? nl - cl : 0; 1145 if ((alrm_time == 0) || ((unsigned) sched_dif < alrm_time)) { 1146 alrm_time = ((unsigned) sched_dif) + 1; 1147 alm_fun = sched_run; 1148 } 1149 } 1150 alrmcatch_disabled = 0; 1151 (void) alarm(alrm_time); /* Autologout ON */ 1152 } 1153 1154 #undef RMDEBUG /* For now... */ 1155 1156 void 1157 rmstar(struct wordent *cp) 1158 { 1159 struct wordent *we, *args; 1160 struct wordent *tmp, *del; 1161 1162 #ifdef RMDEBUG 1163 static Char STRrmdebug[] = {'r', 'm', 'd', 'e', 'b', 'u', 'g', '\0'}; 1164 Char *tag; 1165 #endif /* RMDEBUG */ 1166 Char *charac; 1167 int ask, doit, star = 0, silent = 0, opintr_disabled; 1168 1169 if (!adrof(STRrmstar)) 1170 return; 1171 #ifdef RMDEBUG 1172 tag = varval(STRrmdebug); 1173 #endif /* RMDEBUG */ 1174 we = cp->next; 1175 while (*we->word == ';' && we != cp) 1176 we = we->next; 1177 opintr_disabled = pintr_disabled; 1178 pintr_disabled = 0; 1179 while (we != cp) { 1180 Char *cmd = we->word; 1181 if (cmd[0] == STRQNULL[0]) 1182 cmd++; 1183 #ifdef RMDEBUG 1184 if (*tag) 1185 xprintf(CGETS(22, 7, "parsing command line [%" TCSH_S "]\n"), cmd); 1186 #endif /* RMDEBUG */ 1187 if (!StrQcmp(cmd, STRrm)) { 1188 args = we->next; 1189 ask = (*args->word != '-'); 1190 while (*args->word == '-' && !silent) { /* check options */ 1191 for (charac = (args->word + 1); *charac && !silent; charac++) 1192 silent = (*charac == 'i' || *charac == 'f'); 1193 args = args->next; 1194 } 1195 ask = (ask || (!ask && !silent)); 1196 if (ask) { 1197 for (; !star && *args->word != ';' 1198 && args != cp; args = args->next) 1199 if (!Strcmp(args->word, STRstar)) 1200 star = 1; 1201 if (ask && star) { 1202 doit = getYN(CGETS(22, 8, 1203 "Do you really want to delete all files? [N/y] ")); 1204 if (!doit) { 1205 /* remove the command instead */ 1206 #ifdef RMDEBUG 1207 if (*tag) 1208 xprintf(CGETS(22, 9, 1209 "skipping deletion of files!\n")); 1210 #endif /* RMDEBUG */ 1211 for (tmp = we; 1212 *tmp->word != '\n' && 1213 *tmp->word != ';' && tmp != cp;) { 1214 tmp->prev->next = tmp->next; 1215 tmp->next->prev = tmp->prev; 1216 xfree(tmp->word); 1217 del = tmp; 1218 tmp = tmp->next; 1219 xfree(del); 1220 } 1221 if (*tmp->word == ';') { 1222 tmp->prev->next = tmp->next; 1223 tmp->next->prev = tmp->prev; 1224 xfree(tmp->word); 1225 del = tmp; 1226 tmp = tmp->next; 1227 xfree(del); 1228 } 1229 we = tmp; 1230 continue; 1231 } 1232 } 1233 } 1234 } 1235 for (we = we->next; 1236 *we->word != ';' && we != cp; 1237 we = we->next) 1238 continue; 1239 if (*we->word == ';') 1240 we = we->next; 1241 } 1242 #ifdef RMDEBUG 1243 if (*tag) { 1244 xprintf(CGETS(22, 10, "command line now is:\n")); 1245 for (we = cp->next; we != cp; we = we->next) 1246 xprintf("[%" TCSH_S "] ", we->word); 1247 } 1248 #endif /* RMDEBUG */ 1249 pintr_disabled = opintr_disabled; 1250 return; 1251 } 1252 1253 #ifdef BSDJOBS 1254 /* Check if command is in continue list 1255 and do a "aliasing" if it exists as a job in background */ 1256 1257 #undef CNDEBUG /* For now */ 1258 void 1259 continue_jobs(struct wordent *cp) 1260 { 1261 struct wordent *we; 1262 struct process *pp, *np; 1263 Char *cmd, *continue_list, *continue_args_list; 1264 1265 #ifdef CNDEBUG 1266 Char *tag; 1267 static Char STRcndebug[] = 1268 {'c', 'n', 'd', 'e', 'b', 'u', 'g', '\0'}; 1269 #endif /* CNDEBUG */ 1270 int in_cont_list, in_cont_arg_list; 1271 1272 1273 #ifdef CNDEBUG 1274 tag = varval(STRcndebug); 1275 #endif /* CNDEBUG */ 1276 continue_list = varval(STRcontinue); 1277 continue_args_list = varval(STRcontinue_args); 1278 if (*continue_list == '\0' && *continue_args_list == '\0') 1279 return; 1280 1281 we = cp->next; 1282 while (*we->word == ';' && we != cp) 1283 we = we->next; 1284 while (we != cp) { 1285 #ifdef CNDEBUG 1286 if (*tag) 1287 xprintf(CGETS(22, 11, "parsing command line\n")); 1288 #endif /* CNDEBUG */ 1289 cmd = we->word; 1290 in_cont_list = inlist(continue_list, cmd); 1291 in_cont_arg_list = inlist(continue_args_list, cmd); 1292 if (in_cont_list || in_cont_arg_list) { 1293 #ifdef CNDEBUG 1294 if (*tag) 1295 xprintf(CGETS(22, 12, "in one of the lists\n")); 1296 #endif /* CNDEBUG */ 1297 np = NULL; 1298 for (pp = proclist.p_next; pp; pp = pp->p_next) { 1299 if (prefix(cmd, pp->p_command)) { 1300 if (pp->p_index) { 1301 np = pp; 1302 break; 1303 } 1304 } 1305 } 1306 if (np) { 1307 insert(we, in_cont_arg_list); 1308 } 1309 } 1310 for (we = we->next; 1311 *we->word != ';' && we != cp; 1312 we = we->next) 1313 continue; 1314 if (*we->word == ';') 1315 we = we->next; 1316 } 1317 #ifdef CNDEBUG 1318 if (*tag) { 1319 xprintf(CGETS(22, 13, "command line now is:\n")); 1320 for (we = cp->next; we != cp; we = we->next) 1321 xprintf("%" TCSH_S " ", we->word); 1322 } 1323 #endif /* CNDEBUG */ 1324 return; 1325 } 1326 1327 /* The actual "aliasing" of for backgrounds() is done here 1328 with the aid of insert_we(). */ 1329 static void 1330 insert(struct wordent *pl, int file_args) 1331 { 1332 struct wordent *now, *last; 1333 Char *cmd, *bcmd, *cp1, *cp2; 1334 size_t cmd_len; 1335 Char *upause = STRunderpause; 1336 size_t p_len = Strlen(upause); 1337 1338 cmd_len = Strlen(pl->word); 1339 cmd = xcalloc(1, (cmd_len + 1) * sizeof(Char)); 1340 (void) Strcpy(cmd, pl->word); 1341 /* Do insertions at beginning, first replace command word */ 1342 1343 if (file_args) { 1344 now = pl; 1345 xfree(now->word); 1346 now->word = xcalloc(1, 5 * sizeof(Char)); 1347 (void) Strcpy(now->word, STRecho); 1348 1349 now = xcalloc(1, sizeof(struct wordent)); 1350 now->word = xcalloc(1, 6 * sizeof(Char)); 1351 (void) Strcpy(now->word, STRbackqpwd); 1352 insert_we(now, pl); 1353 1354 for (last = now; *last->word != '\n' && *last->word != ';'; 1355 last = last->next) 1356 continue; 1357 1358 now = xcalloc(1, sizeof(struct wordent)); 1359 now->word = xcalloc(1, 2 * sizeof(Char)); 1360 (void) Strcpy(now->word, STRgt); 1361 insert_we(now, last->prev); 1362 1363 now = xcalloc(1, sizeof(struct wordent)); 1364 now->word = xcalloc(1, 2 * sizeof(Char)); 1365 (void) Strcpy(now->word, STRbang); 1366 insert_we(now, last->prev); 1367 1368 now = xcalloc(1, sizeof(struct wordent)); 1369 now->word = xcalloc(1, (cmd_len + p_len + 4) * sizeof(Char)); 1370 cp1 = now->word; 1371 cp2 = cmd; 1372 *cp1++ = '~'; 1373 *cp1++ = '/'; 1374 *cp1++ = '.'; 1375 while ((*cp1++ = *cp2++) != '\0') 1376 continue; 1377 cp1--; 1378 cp2 = upause; 1379 while ((*cp1++ = *cp2++) != '\0') 1380 continue; 1381 insert_we(now, last->prev); 1382 1383 now = xcalloc(1, sizeof(struct wordent)); 1384 now->word = xcalloc(1, 2 * sizeof(Char)); 1385 (void) Strcpy(now->word, STRsemi); 1386 insert_we(now, last->prev); 1387 bcmd = xcalloc(1, (cmd_len + 2) * sizeof(Char)); 1388 *bcmd = '%'; 1389 Strcpy(bcmd + 1, cmd); 1390 now = xcalloc(1, sizeof(struct wordent)); 1391 now->word = bcmd; 1392 insert_we(now, last->prev); 1393 } 1394 else { 1395 struct wordent *del; 1396 1397 now = pl; 1398 xfree(now->word); 1399 now->word = xcalloc(1, (cmd_len + 2) * sizeof(Char)); 1400 *now->word = '%'; 1401 Strcpy(now->word + 1, cmd); 1402 for (now = now->next; 1403 *now->word != '\n' && *now->word != ';' && now != pl;) { 1404 now->prev->next = now->next; 1405 now->next->prev = now->prev; 1406 xfree(now->word); 1407 del = now; 1408 now = now->next; 1409 xfree(del); 1410 } 1411 } 1412 } 1413 1414 static void 1415 insert_we(struct wordent *new, struct wordent *where) 1416 { 1417 1418 new->prev = where; 1419 new->next = where->next; 1420 where->next = new; 1421 new->next->prev = new; 1422 } 1423 1424 static int 1425 inlist(Char *list, Char *name) 1426 { 1427 Char *l, *n; 1428 1429 l = list; 1430 n = name; 1431 1432 while (*l && *n) { 1433 if (*l == *n) { 1434 l++; 1435 n++; 1436 if (*n == '\0' && (*l == ' ' || *l == '\0')) 1437 return (1); 1438 else 1439 continue; 1440 } 1441 else { 1442 while (*l && *l != ' ') 1443 l++; /* skip to blank */ 1444 while (*l && *l == ' ') 1445 l++; /* and find first nonblank character */ 1446 n = name; 1447 } 1448 } 1449 return (0); 1450 } 1451 1452 #endif /* BSDJOBS */ 1453 1454 1455 /* 1456 * Implement a small cache for tilde names. This is used primarily 1457 * to expand tilde names to directories, but also 1458 * we can find users from their home directories for the tilde 1459 * prompt, on machines where yp lookup is slow this can be a big win... 1460 * As with any cache this can run out of sync, rehash can sync it again. 1461 */ 1462 static struct tildecache { 1463 Char *user; 1464 Char *home; 1465 size_t hlen; 1466 } *tcache = NULL; 1467 1468 #define TILINCR 10 1469 size_t tlength = 0; 1470 static size_t tsize = TILINCR; 1471 1472 static int 1473 tildecompare(const void *xp1, const void *xp2) 1474 { 1475 const struct tildecache *p1, *p2; 1476 1477 p1 = xp1; 1478 p2 = xp2; 1479 return Strcmp(p1->user, p2->user); 1480 } 1481 1482 static Char * 1483 gethomedir(const Char *us) 1484 { 1485 struct passwd *pp; 1486 #ifdef HESIOD 1487 char **res, **res1, *cp; 1488 Char *rp; 1489 #endif /* HESIOD */ 1490 1491 pp = xgetpwnam(short2str(us)); 1492 #ifdef YPBUGS 1493 fix_yp_bugs(); 1494 #endif /* YPBUGS */ 1495 if (pp != NULL) { 1496 #if 0 1497 /* Don't return if root */ 1498 if (pp->pw_dir[0] == '/' && pp->pw_dir[1] == '\0') 1499 return NULL; 1500 else 1501 #endif 1502 return Strsave(str2short(pp->pw_dir)); 1503 } 1504 #ifdef HESIOD 1505 res = hes_resolve(short2str(us), "filsys"); 1506 rp = NULL; 1507 if (res != NULL) { 1508 if ((*res) != NULL) { 1509 /* 1510 * Look at the first token to determine how to interpret 1511 * the rest of it. 1512 * Yes, strtok is evil (it's not thread-safe), but it's also 1513 * easy to use. 1514 */ 1515 cp = strtok(*res, " "); 1516 if (strcmp(cp, "AFS") == 0) { 1517 /* next token is AFS pathname.. */ 1518 cp = strtok(NULL, " "); 1519 if (cp != NULL) 1520 rp = Strsave(str2short(cp)); 1521 } else if (strcmp(cp, "NFS") == 0) { 1522 cp = NULL; 1523 if ((strtok(NULL, " ")) && /* skip remote pathname */ 1524 (strtok(NULL, " ")) && /* skip host */ 1525 (strtok(NULL, " ")) && /* skip mode */ 1526 (cp = strtok(NULL, " "))) { 1527 rp = Strsave(str2short(cp)); 1528 } 1529 } 1530 } 1531 for (res1 = res; *res1; res1++) 1532 free(*res1); 1533 #if 0 1534 /* Don't return if root */ 1535 if (rp != NULL && rp[0] == '/' && rp[1] == '\0') { 1536 xfree(rp); 1537 rp = NULL; 1538 } 1539 #endif 1540 return rp; 1541 } 1542 #endif /* HESIOD */ 1543 return NULL; 1544 } 1545 1546 Char * 1547 gettilde(const Char *us) 1548 { 1549 struct tildecache *bp1, *bp2, *bp; 1550 Char *hd; 1551 1552 /* Ignore NIS special names */ 1553 if (*us == '+' || *us == '-') 1554 return NULL; 1555 1556 if (tcache == NULL) 1557 tcache = xmalloc(TILINCR * sizeof(struct tildecache)); 1558 /* 1559 * Binary search 1560 */ 1561 for (bp1 = tcache, bp2 = tcache + tlength; bp1 < bp2;) { 1562 int i; 1563 1564 bp = bp1 + ((bp2 - bp1) >> 1); 1565 if ((i = *us - *bp->user) == 0 && (i = Strcmp(us, bp->user)) == 0) 1566 return (bp->home); 1567 if (i < 0) 1568 bp2 = bp; 1569 else 1570 bp1 = bp + 1; 1571 } 1572 /* 1573 * Not in the cache, try to get it from the passwd file 1574 */ 1575 hd = gethomedir(us); 1576 if (hd == NULL) 1577 return NULL; 1578 1579 /* 1580 * Update the cache 1581 */ 1582 tcache[tlength].user = Strsave(us); 1583 tcache[tlength].home = hd; 1584 tcache[tlength++].hlen = Strlen(hd); 1585 1586 qsort(tcache, tlength, sizeof(struct tildecache), tildecompare); 1587 1588 if (tlength == tsize) { 1589 tsize += TILINCR; 1590 tcache = xrealloc(tcache, tsize * sizeof(struct tildecache)); 1591 } 1592 return (hd); 1593 } 1594 1595 /* 1596 * Return the username if the directory path passed contains a 1597 * user's home directory in the tilde cache, otherwise return NULL 1598 * hm points to the place where the path became different. 1599 * Special case: Our own home directory. 1600 * If we are passed a null pointer, then we flush the cache. 1601 */ 1602 Char * 1603 getusername(Char **hm) 1604 { 1605 Char *h, *p; 1606 size_t i, j; 1607 1608 if (hm == NULL) { 1609 for (i = 0; i < tlength; i++) { 1610 xfree(tcache[i].home); 1611 xfree(tcache[i].user); 1612 } 1613 xfree(tcache); 1614 tlength = 0; 1615 tsize = TILINCR; 1616 tcache = NULL; 1617 return NULL; 1618 } 1619 p = *hm; 1620 if (((h = varval(STRhome)) != STRNULL) && 1621 (Strncmp(p, h, j = Strlen(h)) == 0) && 1622 (p[j] == '/' || p[j] == '\0')) { 1623 *hm = &p[j]; 1624 return STRNULL; 1625 } 1626 for (i = 0; i < tlength; i++) 1627 if ((Strncmp(p, tcache[i].home, (j = tcache[i].hlen)) == 0) && 1628 (p[j] == '/' || p[j] == '\0')) { 1629 *hm = &p[j]; 1630 return tcache[i].user; 1631 } 1632 return NULL; 1633 } 1634 1635 1636 /* 1637 * set the shell-level var to 1 or apply change to it. 1638 */ 1639 void 1640 shlvl(int val) 1641 { 1642 char *cp; 1643 1644 if ((cp = getenv("SHLVL")) != NULL) { 1645 1646 if (loginsh) 1647 val = 1; 1648 else 1649 val += atoi(cp); 1650 1651 if (val <= 0) { 1652 if (adrof(STRshlvl) != NULL) 1653 unsetv(STRshlvl); 1654 Unsetenv(STRKSHLVL); 1655 } 1656 else { 1657 Char *p; 1658 1659 p = Itoa(val, 0, 0); 1660 cleanup_push(p, xfree); 1661 setv(STRshlvl, p, VAR_READWRITE); 1662 cleanup_ignore(p); 1663 cleanup_until(p); 1664 tsetenv(STRKSHLVL, p); 1665 } 1666 } 1667 else { 1668 setcopy(STRshlvl, STR1, VAR_READWRITE); 1669 tsetenv(STRKSHLVL, STR1); 1670 } 1671 } 1672 1673 1674 /* fixio(): 1675 * Try to recover from a read error 1676 */ 1677 int 1678 fixio(int fd, int e) 1679 { 1680 switch (e) { 1681 case -1: /* Make sure that the code is reachable */ 1682 1683 #ifdef EWOULDBLOCK 1684 case EWOULDBLOCK: 1685 # define FDRETRY 1686 #endif /* EWOULDBLOCK */ 1687 1688 #if defined(POSIX) && defined(EAGAIN) 1689 # if !defined(EWOULDBLOCK) || EWOULDBLOCK != EAGAIN 1690 case EAGAIN: 1691 # define FDRETRY 1692 # endif /* !EWOULDBLOCK || EWOULDBLOCK != EAGAIN */ 1693 #endif /* POSIX && EAGAIN */ 1694 1695 e = -1; 1696 #ifdef FDRETRY 1697 # ifdef F_SETFL 1698 /* 1699 * Great! we have on suns 3 flavors and 5 names... 1700 * I hope that will cover everything. 1701 * I added some more defines... many systems have different defines. 1702 * Rather than dealing with getting the right includes, we'll just 1703 * cover all the known possibilities here. -- sterling@netcom.com 1704 */ 1705 # ifndef O_NONBLOCK 1706 # define O_NONBLOCK 0 1707 # endif /* O_NONBLOCK */ 1708 # ifndef O_NDELAY 1709 # define O_NDELAY 0 1710 # endif /* O_NDELAY */ 1711 # ifndef FNBIO 1712 # define FNBIO 0 1713 # endif /* FNBIO */ 1714 # ifndef _FNBIO 1715 # define _FNBIO 0 1716 # endif /* _FNBIO */ 1717 # ifndef FNONBIO 1718 # define FNONBIO 0 1719 # endif /* FNONBIO */ 1720 # ifndef FNONBLOCK 1721 # define FNONBLOCK 0 1722 # endif /* FNONBLOCK */ 1723 # ifndef _FNONBLOCK 1724 # define _FNONBLOCK 0 1725 # endif /* _FNONBLOCK */ 1726 # ifndef FNDELAY 1727 # define FNDELAY 0 1728 # endif /* FNDELAY */ 1729 # ifndef _FNDELAY 1730 # define _FNDELAY 0 1731 # endif /* _FNDELAY */ 1732 # ifndef FNDLEAY /* Some linux versions have this typo */ 1733 # define FNDLEAY 0 1734 # endif /* FNDLEAY */ 1735 if ((e = fcntl(fd, F_GETFL, 0)) == -1) 1736 return -1; 1737 1738 e &= ~(O_NDELAY|O_NONBLOCK|FNBIO|_FNBIO|FNONBIO|FNONBLOCK|_FNONBLOCK| 1739 FNDELAY|_FNDELAY|FNDLEAY); /* whew! */ 1740 if (fcntl(fd, F_SETFL, e) == -1) 1741 return -1; 1742 else 1743 e = 0; 1744 # endif /* F_SETFL */ 1745 1746 # ifdef FIONBIO 1747 e = 0; 1748 if (ioctl(fd, FIONBIO, (ioctl_t) &e) == -1) 1749 return -1; 1750 # endif /* FIONBIO */ 1751 1752 #endif /* FDRETRY */ 1753 return e; 1754 1755 case EINTR: 1756 return 0; 1757 1758 default: 1759 return -1; 1760 } 1761 } 1762 1763 /* collate(): 1764 * String collation 1765 */ 1766 int 1767 collate(const Char *a, const Char *b) 1768 { 1769 int rv; 1770 #ifdef SHORT_STRINGS 1771 /* This strips the quote bit as a side effect */ 1772 char *sa = strsave(short2str(a)); 1773 char *sb = strsave(short2str(b)); 1774 #else 1775 char *sa = strip(strsave(a)); 1776 char *sb = strip(strsave(b)); 1777 #endif /* SHORT_STRINGS */ 1778 1779 #if defined(NLS) && defined(HAVE_STRCOLL) 1780 errno = 0; /* strcoll sets errno, another brain-damage */ 1781 1782 rv = strcoll(sa, sb); 1783 1784 /* 1785 * We should be checking for errno != 0, but some systems 1786 * forget to reset errno to 0. So we only check for the 1787 * only documented valid errno value for strcoll [EINVAL] 1788 */ 1789 if (errno == EINVAL) { 1790 xfree(sa); 1791 xfree(sb); 1792 stderror(ERR_SYSTEM, "strcoll", strerror(errno)); 1793 } 1794 #else 1795 rv = strcmp(sa, sb); 1796 #endif /* NLS && HAVE_STRCOLL */ 1797 1798 xfree(sa); 1799 xfree(sb); 1800 1801 return rv; 1802 } 1803 1804 #ifdef HASHBANG 1805 /* 1806 * From: peter@zeus.dialix.oz.au (Peter Wemm) 1807 * If exec() fails look first for a #! [word] [word] .... 1808 * If it is, splice the header into the argument list and retry. 1809 */ 1810 #define HACKBUFSZ 1024 /* Max chars in #! vector */ 1811 int 1812 hashbang(int fd, Char ***vp) 1813 { 1814 struct blk_buf sarg = BLK_BUF_INIT; 1815 char lbuf[HACKBUFSZ], *p, *ws; 1816 #ifdef WINNT_NATIVE 1817 int fw = 0; /* found at least one word */ 1818 int first_word = 1; 1819 char *real; 1820 #endif /* WINNT_NATIVE */ 1821 1822 if (xread(fd, lbuf, HACKBUFSZ) <= 0) 1823 return -1; 1824 1825 ws = 0; /* word started = 0 */ 1826 1827 for (p = lbuf; p < &lbuf[HACKBUFSZ]; ) { 1828 switch (*p) { 1829 case ' ': 1830 case '\t': 1831 #if defined(WINNT_NATIVE) || defined (__CYGWIN__) 1832 case '\r': 1833 #endif /* WINNT_NATIVE || __CYGWIN__ */ 1834 if (ws) { /* a blank after a word.. save it */ 1835 *p = '\0'; 1836 #ifdef WINNT_NATIVE 1837 if (first_word) { 1838 real = hb_subst(ws); 1839 if (real != NULL) 1840 ws = real; 1841 } 1842 fw = 1; 1843 first_word = 0; 1844 #endif /* WINNT_NATIVE */ 1845 bb_append(&sarg, SAVE(ws)); 1846 ws = NULL; 1847 } 1848 p++; 1849 continue; 1850 1851 case '\0': /* Whoa!! what the hell happened */ 1852 goto err; 1853 1854 case '\n': /* The end of the line. */ 1855 if ( 1856 #ifdef WINNT_NATIVE 1857 fw || 1858 #endif /* WINNT_NATIVE */ 1859 ws) { /* terminate the last word */ 1860 *p = '\0'; 1861 #ifdef WINNT_NATIVE 1862 /* deal with the 1-word case */ 1863 if (first_word) { 1864 real = hb_subst(ws); 1865 if (real != NULL) 1866 ws = real; 1867 } 1868 #endif /* !WINNT_NATIVE */ 1869 if (ws) 1870 bb_append(&sarg, SAVE(ws)); 1871 } 1872 if (sarg.len > 0) { 1873 *vp = bb_finish(&sarg); 1874 return 0; 1875 } 1876 else 1877 goto err; 1878 1879 default: 1880 if (!ws) /* Start a new word? */ 1881 ws = p; 1882 p++; 1883 break; 1884 } 1885 } 1886 err: 1887 bb_cleanup(&sarg); 1888 return -1; 1889 } 1890 #endif /* HASHBANG */ 1891 1892 #ifdef REMOTEHOST 1893 1894 static void 1895 palarm(int snum) 1896 { 1897 USE(snum); 1898 _exit(1); 1899 } 1900 1901 static void 1902 getremotehost(int dest_fd) 1903 { 1904 const char *host = NULL; 1905 #ifdef INET6 1906 struct sockaddr_storage saddr; 1907 static char hbuf[NI_MAXHOST]; 1908 #else 1909 struct hostent* hp; 1910 struct sockaddr_in saddr; 1911 #endif 1912 socklen_t len = sizeof(saddr); 1913 1914 #ifdef INET6 1915 if (getpeername(SHIN, (struct sockaddr *) &saddr, &len) != -1 && 1916 (saddr.ss_family == AF_INET6 || saddr.ss_family == AF_INET)) { 1917 int flag = NI_NUMERICHOST; 1918 1919 #ifdef NI_WITHSCOPEID 1920 flag |= NI_WITHSCOPEID; 1921 #endif 1922 getnameinfo((struct sockaddr *)&saddr, len, hbuf, sizeof(hbuf), 1923 NULL, 0, flag); 1924 host = hbuf; 1925 #else 1926 if (getpeername(SHIN, (struct sockaddr *) &saddr, &len) != -1 && 1927 saddr.sin_family == AF_INET) { 1928 #if 0 1929 if ((hp = gethostbyaddr((char *)&saddr.sin_addr, sizeof(struct in_addr), 1930 AF_INET)) != NULL) 1931 host = hp->h_name; 1932 else 1933 #endif 1934 host = inet_ntoa(saddr.sin_addr); 1935 #endif 1936 } 1937 #ifdef HAVE_STRUCT_UTMP_UT_HOST 1938 else { 1939 char *ptr; 1940 char *name = utmphost(); 1941 /* Avoid empty names and local X displays */ 1942 if (name != NULL && *name != '\0' && *name != ':') { 1943 struct in_addr addr; 1944 char *sptr; 1945 1946 /* Look for host:display.screen */ 1947 /* 1948 * There is conflict with IPv6 address and X DISPLAY. So, 1949 * we assume there is no IPv6 address in utmp and don't 1950 * touch here. 1951 */ 1952 if ((sptr = strchr(name, ':')) != NULL) 1953 *sptr = '\0'; 1954 /* Leave IPv4 address as is */ 1955 /* 1956 * we use inet_addr here, not inet_aton because many systems 1957 * have not caught up yet. 1958 */ 1959 addr.s_addr = inet_addr(name); 1960 if (addr.s_addr != (unsigned int)~0) 1961 host = name; 1962 else { 1963 if (sptr != name) { 1964 #ifdef INET6 1965 char *s, *domain; 1966 char dbuf[MAXHOSTNAMELEN]; 1967 struct addrinfo hints, *res = NULL; 1968 1969 memset(&hints, 0, sizeof(hints)); 1970 hints.ai_family = PF_UNSPEC; 1971 hints.ai_socktype = SOCK_STREAM; 1972 hints.ai_flags = AI_PASSIVE | AI_CANONNAME; 1973 if (strlen(name) < utmphostsize()) 1974 { 1975 if (getaddrinfo(name, NULL, &hints, &res) != 0) 1976 res = NULL; 1977 } else if (gethostname(dbuf, sizeof(dbuf)) == 0 && 1978 (dbuf[sizeof(dbuf)-1] = '\0', /*FIXME: ugly*/ 1979 (domain = strchr(dbuf, '.')) != NULL)) { 1980 for (s = strchr(name, '.'); 1981 s != NULL; s = strchr(s + 1, '.')) { 1982 if (*(s + 1) != '\0' && 1983 (ptr = strstr(domain, s)) != NULL) { 1984 char *cbuf; 1985 1986 cbuf = strspl(name, ptr + strlen(s)); 1987 if (getaddrinfo(cbuf, NULL, &hints, &res) != 0) 1988 res = NULL; 1989 xfree(cbuf); 1990 break; 1991 } 1992 } 1993 } 1994 if (res != NULL) { 1995 if (res->ai_canonname != NULL) { 1996 strncpy(hbuf, res->ai_canonname, sizeof(hbuf)); 1997 hbuf[sizeof(hbuf) - 1] = '\0'; 1998 host = hbuf; 1999 } 2000 freeaddrinfo(res); 2001 } 2002 #else 2003 if ((hp = gethostbyname(name)) == NULL) { 2004 /* Try again eliminating the trailing domain */ 2005 if ((ptr = strchr(name, '.')) != NULL) { 2006 *ptr = '\0'; 2007 if ((hp = gethostbyname(name)) != NULL) 2008 host = hp->h_name; 2009 *ptr = '.'; 2010 } 2011 } 2012 else 2013 host = hp->h_name; 2014 #endif 2015 } 2016 } 2017 } 2018 } 2019 #endif 2020 2021 if (host) { 2022 size_t left; 2023 2024 left = strlen(host); 2025 while (left != 0) { 2026 ssize_t res; 2027 2028 res = xwrite(dest_fd, host, left); 2029 if (res < 0) 2030 _exit(1); 2031 host += res; 2032 left -= res; 2033 } 2034 } 2035 _exit(0); 2036 } 2037 2038 /* 2039 * From: <lesv@ppvku.ericsson.se> (Lennart Svensson) 2040 */ 2041 void 2042 remotehost(void) 2043 { 2044 struct sigaction sa; 2045 struct strbuf hostname = strbuf_INIT; 2046 int fds[2], wait_options, status; 2047 pid_t pid, wait_res; 2048 2049 sa.sa_handler = SIG_DFL; /* Make sure a zombie is created */ 2050 sigemptyset(&sa.sa_mask); 2051 sa.sa_flags = 0; 2052 sigaction(SIGCHLD, &sa, NULL); 2053 mypipe(fds); 2054 pid = fork(); 2055 if (pid == 0) { 2056 sigset_t set; 2057 xclose(fds[0]); 2058 /* Don't get stuck if the resolver does not work! */ 2059 signal(SIGALRM, palarm); 2060 sigemptyset(&set); 2061 sigaddset(&set, SIGALRM); 2062 (void)sigprocmask(SIG_UNBLOCK, &set, NULL); 2063 (void)alarm(2); 2064 getremotehost(fds[1]); 2065 /*NOTREACHED*/ 2066 } 2067 xclose(fds[1]); 2068 for (;;) { 2069 char buf[BUFSIZE]; 2070 ssize_t res; 2071 2072 res = xread(fds[0], buf, sizeof(buf)); 2073 if (res == -1) { 2074 hostname.len = 0; 2075 wait_options = WNOHANG; 2076 goto done; 2077 } 2078 if (res == 0) 2079 break; 2080 strbuf_appendn(&hostname, buf, res); 2081 } 2082 wait_options = 0; 2083 done: 2084 cleanup_push(&hostname, strbuf_cleanup); 2085 xclose(fds[0]); 2086 while ((wait_res = waitpid(pid, &status, wait_options)) == -1 2087 && errno == EINTR) 2088 handle_pending_signals(); 2089 if (hostname.len > 0 && wait_res == pid && WIFEXITED(status) 2090 && WEXITSTATUS(status) == 0) { 2091 strbuf_terminate(&hostname); 2092 tsetenv(STRREMOTEHOST, str2short(hostname.s)); 2093 } 2094 cleanup_until(&hostname); 2095 2096 #ifdef YPBUGS 2097 /* From: casper@fwi.uva.nl (Casper H.S. Dik), for Solaris 2.3 */ 2098 fix_yp_bugs(); 2099 #endif /* YPBUGS */ 2100 2101 } 2102 #endif /* REMOTEHOST */ 2103 2104 #ifndef WINNT_NATIVE 2105 /* 2106 * indicate if a terminal type is defined in terminfo/termcap 2107 * (by default the current term type). This allows ppl to look 2108 * for a working term type automatically in their login scripts 2109 * when using a terminal known as different things on different 2110 * platforms 2111 */ 2112 void 2113 dotermname(Char **v, struct command *c) 2114 { 2115 char *termtype; 2116 /* 2117 * Maximum size of a termcap record. We make it twice as large. 2118 */ 2119 char termcap_buffer[2048]; 2120 2121 USE(c); 2122 /* try to find which entry we should be looking for */ 2123 termtype = (v[1] == NULL ? getenv("TERM") : short2str(v[1])); 2124 if (termtype == NULL) { 2125 /* no luck - the user didn't provide one and none is 2126 * specified in the environment 2127 */ 2128 setstatus(1); 2129 return; 2130 } 2131 2132 /* 2133 * we use the termcap function - if we are using terminfo we 2134 * will end up with it's compatibility function 2135 * terminfo/termcap will be initialized with the new 2136 * type but we don't care because tcsh has cached all the things 2137 * it needs. 2138 */ 2139 if (tgetent(termcap_buffer, termtype) == 1) { 2140 xprintf("%s\n", termtype); 2141 setstatus(0); 2142 } else 2143 setstatus(1); 2144 } 2145 #endif /* WINNT_NATIVE */ 2146