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