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