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