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