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