tc.func.c revision 180637
1139743Simp/* $Header: /p/tcsh/cvsroot/tcsh/tc.func.c,v 3.136 2006/09/01 12:51:35 christos Exp $ */
249267Snewton/*
349267Snewton * tc.func.c: New tcsh builtins.
449267Snewton */
549267Snewton/*-
649267Snewton * Copyright (c) 1980, 1991 The Regents of the University of California.
749267Snewton * All rights reserved.
849267Snewton *
949267Snewton * Redistribution and use in source and binary forms, with or without
1049267Snewton * modification, are permitted provided that the following conditions
1149267Snewton * are met:
1249267Snewton * 1. Redistributions of source code must retain the above copyright
1349267Snewton *    notice, this list of conditions and the following disclaimer.
1449267Snewton * 2. Redistributions in binary form must reproduce the above copyright
1549267Snewton *    notice, this list of conditions and the following disclaimer in the
1649267Snewton *    documentation and/or other materials provided with the distribution.
1749267Snewton * 3. Neither the name of the University nor the names of its contributors
1849267Snewton *    may be used to endorse or promote products derived from this software
1949267Snewton *    without specific prior written permission.
2049267Snewton *
2149267Snewton * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2249267Snewton * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2349267Snewton * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2449267Snewton * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2549267Snewton * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2649267Snewton * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2749267Snewton * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2849267Snewton * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2949267Snewton * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3049267Snewton * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31116174Sobrien * SUCH DAMAGE.
32116174Sobrien */
33116174Sobrien#include "sh.h"
3443412Snewton
3543412SnewtonRCSID("$tcsh: tc.func.c,v 3.136 2006/09/01 12:51:35 christos Exp $")
3643412Snewton
3743412Snewton#include "ed.h"
3843412Snewton#include "ed.defns.h"		/* for the function names */
3943412Snewton#include "tw.h"
4043412Snewton#include "tc.h"
4143412Snewton#ifdef WINNT_NATIVE
4248503Sgreen#include "nt.const.h"
4343412Snewton#else /* WINNT_NATIVE */
4443412Snewton#include <sys/wait.h>
4543412Snewton#endif /* WINNT_NATIVE */
4643412Snewton
47177785Skib#ifdef AFS
4894455Sjhb#include <afs/stds.h>
4943412Snewton#include <afs/kautils.h>
50141486Sjhblong ka_UserAuthenticateGeneral();
5194455Sjhb#endif /* AFS */
5243412Snewton
53141486Sjhb#ifdef TESLA
54141486Sjhbextern int do_logout;
5543412Snewton#endif /* TESLA */
5643412Snewtonextern time_t t_period;
5743412Snewtonextern int just_signaled;
5843412Snewtonstatic int precmd_active = 0;
5943412Snewtonstatic int jobcmd_active = 0; /* GrP */
6043412Snewtonstatic int postcmd_active = 0;
6143412Snewtonstatic int periodic_active = 0;
6265302Sobrienstatic int cwdcmd_active = 0;	/* PWP: for cwd_cmd */
6365302Sobrienstatic int beepcmd_active = 0;
6465302Sobrienstatic void (*alm_fun)(void) = NULL;
6565302Sobrien
66160558Sjhbstatic	void	 auto_logout	(void);
6765302Sobrienstatic	char	*xgetpass	(const char *);
6865302Sobrienstatic	void	 auto_lock	(void);
6965302Sobrien#ifdef BSDJOBS
7065302Sobrienstatic	void	 insert		(struct wordent *, int);
7165302Sobrienstatic	void	 insert_we	(struct wordent *, struct wordent *);
7243412Snewtonstatic	int	 inlist		(Char *, Char *);
7343412Snewton#endif /* BSDJOBS */
7443412Snewtonstatic	int	 tildecompare	(const void *, const void *);
7543412Snewtonstatic  Char    *gethomedir	(const Char *);
7643412Snewton#ifdef REMOTEHOST
7743412Snewtonstatic	void	 palarm		(int);
7843412Snewtonstatic	void	 getremotehost	(int);
7943412Snewton#endif /* REMOTEHOST */
8043412Snewton
8143412Snewton/*
8243412Snewton * Tops-C shell
8343412Snewton */
8443412Snewton
8543412Snewton/*
8643412Snewton * expand_lex: Take the given lex and return an expanded version of it.
8743412Snewton * First guy in lex list is ignored; last guy is ^J which we ignore.
8843412Snewton * Only take lex'es from position 'from' to position 'to' inclusive
8943412Snewton *
9043412Snewton * Note: csh sometimes sets bit 8 in characters which causes all kinds
9143412Snewton * of problems if we don't mask it here. Note: excl's in lexes have been
9243412Snewton * un-back-slashed and must be re-back-slashed
9343412Snewton *
9443412Snewton */
9543412Snewton/* PWP: this is a combination of the old sprlex() and the expand_lex from
9643412Snewton   the magic-space stuff */
9743412Snewton
9843412SnewtonChar   *
9943412Snewtonexpand_lex(const struct wordent *sp0, int from, int to)
10043412Snewton{
10143412Snewton    struct Strbuf buf = Strbuf_INIT;
10243412Snewton    const struct wordent *sp;
10343412Snewton    Char *s;
10443412Snewton    Char prev_c;
10543412Snewton    int i;
10643412Snewton
10743412Snewton    prev_c = '\0';
10843412Snewton
10943412Snewton    if (!sp0 || (sp = sp0->next) == sp0 || sp == (sp0 = sp0->prev))
11043412Snewton	return Strbuf_finish(&buf); /* null lex */
11143412Snewton
11243412Snewton    for (i = 0; ; i++) {
11343412Snewton	if ((i >= from) && (i <= to)) {	/* if in range */
11443412Snewton	    for (s = sp->word; *s; s++) {
11543412Snewton		/*
11643412Snewton		 * bugfix by Michael Bloom: anything but the current history
11743412Snewton		 * character {(PWP) and backslash} seem to be dealt with
11843412Snewton		 * elsewhere.
11943412Snewton		 */
12043412Snewton		if ((*s & QUOTE)
12143412Snewton		    && (((*s & TRIM) == HIST) ||
12243412Snewton			(((*s & TRIM) == '\'') && (prev_c != '\\')) ||
12343412Snewton			(((*s & TRIM) == '\"') && (prev_c != '\\')) ||
12443412Snewton			(((*s & TRIM) == '\\') && (prev_c != '\\')))) {
12543412Snewton		    Strbuf_append1(&buf, '\\');
12643412Snewton		}
12743412Snewton		Strbuf_append1(&buf, *s & TRIM);
12843412Snewton		prev_c = *s;
12943412Snewton	    }
13043412Snewton	    Strbuf_append1(&buf, ' ');
13143412Snewton	}
13243412Snewton	sp = sp->next;
13343412Snewton	if (sp == sp0)
13443412Snewton	    break;
13543412Snewton    }
13643412Snewton    if (buf.len != 0)
13743412Snewton	buf.len--;		/* get rid of trailing space */
13843412Snewton
13943412Snewton    return Strbuf_finish(&buf);
14043412Snewton}
14143412Snewton
14243412SnewtonChar   *
14343412Snewtonsprlex(const struct wordent *sp0)
14443412Snewton{
14543412Snewton    return expand_lex(sp0, 0, INT_MAX);
14643412Snewton}
14743412Snewton
14843412Snewton
14943412SnewtonChar *
15043412SnewtonItoa(int n, size_t min_digits, Char attributes)
15143412Snewton{
15243412Snewton    /*
15343412Snewton     * The array size here is derived from
15443412Snewton     *	log8(UINT_MAX)
15543412Snewton     * which is guaranteed to be enough for a decimal
15643412Snewton     * representation.  We add 1 because integer divide
15743412Snewton     * rounds down.
15843412Snewton     */
15943412Snewton#ifndef CHAR_BIT
16043412Snewton# define CHAR_BIT 8
16159342Sobrien#endif
16243412Snewton    Char buf[CHAR_BIT * sizeof(int) / 3 + 1], *res, *p, *s;
16343412Snewton    unsigned int un;	/* handle most negative # too */
16443412Snewton    int pad = (min_digits != 0);
16543412Snewton
16643412Snewton    if (sizeof(buf) - 1 < min_digits)
16743412Snewton	min_digits = sizeof(buf) - 1;
16843412Snewton
16943412Snewton    un = n;
17043412Snewton    if (n < 0)
17143412Snewton	un = -n;
17243412Snewton
17343412Snewton    p = buf;
174142500Ssam    do {
175142500Ssam	*p++ = un % 10 + '0';
17643412Snewton	un /= 10;
17743412Snewton    } while ((pad && --min_digits > 0) || un != 0);
178102808Sjake
17943412Snewton    res = xmalloc((p - buf + 2) * sizeof(*res));
18043412Snewton    s = res;
18143412Snewton    if (n < 0)
18243412Snewton	*s++ = '-';
18349267Snewton    while (p > buf)
18449267Snewton	*s++ = *--p | attributes;
185100384Speter
18668520Smarcel    *s = '\0';
187102808Sjake    return res;
188102808Sjake}
189102808Sjake
190102808Sjake
191102808Sjake/*ARGSUSED*/
192102808Sjakevoid
193102808Sjakedolist(Char **v, struct command *c)
194102808Sjake{
195120422Speter    Char **globbed;
196120422Speter    int     i, k;
19743412Snewton    struct stat st;
19843412Snewton
199141486Sjhb    USE(c);
200141486Sjhb    if (*++v == NULL) {
20143412Snewton	struct Strbuf word = Strbuf_INIT;
20299669Srobert
203100384Speter	Strbuf_terminate(&word);
20472999Sobrien	cleanup_push(&word, Strbuf_cleanup);
20559342Sobrien	(void) t_search(&word, LIST, TW_ZERO, 0, STRNULL, 0);
20643412Snewton	cleanup_until(&word);
207123742Speter	return;
208123742Speter    }
20943412Snewton    v = glob_all_or_error(v);
21043412Snewton    globbed = v;
21143412Snewton    cleanup_push(globbed, blk_cleanup);
21259342Sobrien    for (k = 0; v[k] != NULL && v[k][0] != '-'; k++)
21343412Snewton	continue;
214112470Sjhb    if (v[k]) {
21559342Sobrien	/*
21643412Snewton	 * We cannot process a flag therefore we let ls do it right.
217177127Sjeff	 */
218112470Sjhb	Char *lspath;
219112470Sjhb	struct command *t;
220140992Ssobomax	struct wordent cmd, *nextword, *lastword;
22143412Snewton	Char   *cp;
222112470Sjhb	struct varent *vp;
22343412Snewton
224112470Sjhb	if (setintr) {
22543412Snewton	    pintr_disabled++;
22643412Snewton	    cleanup_push(&pintr_disabled, disabled_cleanup);
22743412Snewton	}
22843412Snewton	if (seterr) {
22943412Snewton	    xfree(seterr);
23043412Snewton	    seterr = NULL;
23143412Snewton	}
23243412Snewton
23377183Srwatson	lspath = STRls;
23477183Srwatson	STRmCF[1] = 'C';
23577183Srwatson	STRmCF[3] = '\0';
23677183Srwatson	/* Look at listflags, to add -A to the flags, to get a path
23743412Snewton	   of ls if necessary */
23843412Snewton	if ((vp = adrof(STRlistflags)) != NULL && vp->vec != NULL &&
23943412Snewton	    vp->vec[0] != STRNULL) {
24043412Snewton	    if (vp->vec[1] != NULL && vp->vec[1][0] != '\0')
24143412Snewton		lspath = vp->vec[1];
24243412Snewton	    for (cp = vp->vec[0]; *cp; cp++)
243140992Ssobomax		switch (*cp) {
24443412Snewton		case 'x':
24543412Snewton		    STRmCF[1] = 'x';
24643412Snewton		    break;
24743412Snewton		case 'a':
24843412Snewton		    STRmCF[3] = 'a';
24972091Sasmodai		    break;
25043412Snewton		case 'A':
25143412Snewton		    STRmCF[3] = 'A';
25243412Snewton		    break;
25343412Snewton		default:
25443412Snewton		    break;
25543412Snewton		}
256141486Sjhb	}
257141486Sjhb
25843412Snewton	cmd.word = STRNULL;
25943412Snewton	lastword = &cmd;
260141486Sjhb	nextword = xcalloc(1, sizeof cmd);
261141486Sjhb	nextword->word = Strsave(lspath);
26243412Snewton	lastword->next = nextword;
26343412Snewton	nextword->prev = lastword;
26443412Snewton	lastword = nextword;
26543412Snewton	nextword = xcalloc(1, sizeof cmd);
26643412Snewton	nextword->word = Strsave(STRmCF);
26743412Snewton	lastword->next = nextword;
26843412Snewton	nextword->prev = lastword;
26943412Snewton#if defined(KANJI) && defined(SHORT_STRINGS) && defined(DSPMBYTE)
27043412Snewton	if (dspmbyte_ls) {
27143412Snewton	    lastword = nextword;
27243412Snewton	    nextword = xcalloc(1, sizeof cmd);
273160558Sjhb	    nextword->word = Strsave(STRmmliteral);
274160558Sjhb	    lastword->next = nextword;
27543412Snewton	    nextword->prev = lastword;
276160558Sjhb	}
277160558Sjhb#endif
278160558Sjhb#ifdef COLOR_LS_F
27943412Snewton	if (color_context_ls) {
280160558Sjhb	    lastword = nextword;
28143412Snewton	    nextword = xcalloc(1, sizeof cmd);
28243412Snewton	    nextword->word = Strsave(STRmmcolormauto);
28343597Snewton	    lastword->next = nextword;
284100384Speter	    nextword->prev = lastword;
28543597Snewton	}
286100384Speter#endif /* COLOR_LS_F */
28743412Snewton	lastword = nextword;
28843597Snewton	for (cp = *v; cp; cp = *++v) {
28943597Snewton	    nextword = xcalloc(1, sizeof cmd);
290160558Sjhb	    nextword->word = quote(Strsave(cp));
29143597Snewton	    lastword->next = nextword;
29243597Snewton	    nextword->prev = lastword;
293160558Sjhb	    lastword = nextword;
294160558Sjhb	}
295160558Sjhb	lastword->next = &cmd;
29643412Snewton	cmd.prev = lastword;
297160558Sjhb	cleanup_push(&cmd, lex_cleanup);
29843412Snewton
29943412Snewton	/* build a syntax tree for the command. */
300132199Sphk	t = syntax(cmd.next, &cmd, 0);
30143412Snewton	cleanup_push(t, syntax_cleanup);
30243412Snewton	if (seterr)
30343412Snewton	    stderror(ERR_OLD);
30443412Snewton	/* expand aliases like process() does */
30543412Snewton	/* alias(&cmd); */
30643412Snewton	/* execute the parse tree. */
30743412Snewton	execute(t, tpgrp > 0 ? tpgrp : -1, NULL, NULL, FALSE);
30843412Snewton	/* done. free the lex list and parse tree. */
30943412Snewton	cleanup_until(&cmd);
31043412Snewton	if (setintr)
31143412Snewton	    cleanup_until(&pintr_disabled);
31260060Sgreen    }
313    else {
314	Char   *dp, *tmp;
315	struct Strbuf buf = Strbuf_INIT;
316
317	cleanup_push(&buf, Strbuf_cleanup);
318	for (k = 0, i = 0; v[k] != NULL; k++) {
319	    tmp = dnormalize(v[k], symlinks == SYM_IGNORE);
320	    cleanup_push(tmp, xfree);
321	    dp = Strend(tmp) - 1;
322	    if (*dp == '/' && dp != tmp)
323#ifdef apollo
324		if (dp != &tmp[1])
325#endif /* apollo */
326		*dp = '\0';
327	    if (stat(short2str(tmp), &st) == -1) {
328		int err;
329
330		err = errno;
331		if (k != i) {
332		    if (i != 0)
333			xputchar('\n');
334		    print_by_column(STRNULL, &v[i], k - i, FALSE);
335		}
336		xprintf("%S: %s.\n", tmp, strerror(err));
337		i = k + 1;
338	    }
339	    else if (S_ISDIR(st.st_mode)) {
340		Char   *cp;
341
342		if (k != i) {
343		    if (i != 0)
344			xputchar('\n');
345		    print_by_column(STRNULL, &v[i], k - i, FALSE);
346		}
347		if (k != 0 && v[1] != NULL)
348		    xputchar('\n');
349		xprintf("%S:\n", tmp);
350		buf.len = 0;
351		for (cp = tmp; *cp; cp++)
352		    Strbuf_append1(&buf, (*cp | QUOTE));
353		Strbuf_terminate(&buf);
354		dp = &buf.s[buf.len - 1];
355		if (
356#ifdef WINNT_NATIVE
357		    (*dp != (Char) (':' | QUOTE)) &&
358#endif /* WINNT_NATIVE */
359		    (*dp != (Char) ('/' | QUOTE))) {
360		    Strbuf_append1(&buf, '/');
361		    Strbuf_terminate(&buf);
362		} else
363		    *dp &= TRIM;
364		(void) t_search(&buf, LIST, TW_ZERO, 0, STRNULL, 0);
365		i = k + 1;
366	    }
367	    cleanup_until(tmp);
368	}
369	cleanup_until(&buf);
370	if (k != i) {
371	    if (i != 0)
372		xputchar('\n');
373	    print_by_column(STRNULL, &v[i], k - i, FALSE);
374	}
375    }
376
377    cleanup_until(globbed);
378}
379
380extern int GotTermCaps;
381
382/*ARGSUSED*/
383void
384dotelltc(Char **v, struct command *c)
385{
386    USE(v);
387    USE(c);
388    if (!GotTermCaps)
389	GetTermCaps();
390    TellTC();
391}
392
393/*ARGSUSED*/
394void
395doechotc(Char **v, struct command *c)
396{
397    USE(c);
398    if (!GotTermCaps)
399	GetTermCaps();
400    EchoTC(++v);
401}
402
403/*ARGSUSED*/
404void
405dosettc(Char **v, struct command *c)
406{
407    char    *tv[2];
408
409    USE(c);
410    if (!GotTermCaps)
411	GetTermCaps();
412
413    tv[0] = strsave(short2str(v[1]));
414    cleanup_push(tv[0], xfree);
415    tv[1] = strsave(short2str(v[2]));
416    cleanup_push(tv[1], xfree);
417    SetTC(tv[0], tv[1]);
418    cleanup_until(tv[0]);
419}
420
421/* The dowhich() is by:
422 *  Andreas Luik <luik@isaak.isa.de>
423 *  I S A  GmbH - Informationssysteme fuer computerintegrierte Automatisierung
424 *  Azenberstr. 35
425 *  D-7000 Stuttgart 1
426 *  West-Germany
427 * Thanks!!
428 */
429int
430cmd_expand(Char *cmd, Char **str)
431{
432    struct wordent lexp[3];
433    struct varent *vp;
434    int rv = TRUE;
435
436    lexp[0].next = &lexp[1];
437    lexp[1].next = &lexp[2];
438    lexp[2].next = &lexp[0];
439
440    lexp[0].prev = &lexp[2];
441    lexp[1].prev = &lexp[0];
442    lexp[2].prev = &lexp[1];
443
444    lexp[0].word = STRNULL;
445    lexp[2].word = STRret;
446
447    if ((vp = adrof1(cmd, &aliases)) != NULL && vp->vec != NULL) {
448	if (str == NULL) {
449	    xprintf(CGETS(22, 1, "%S: \t aliased to "), cmd);
450	    blkpr(vp->vec);
451	    xputchar('\n');
452	}
453	else
454	    *str = blkexpand(vp->vec);
455    }
456    else {
457	lexp[1].word = cmd;
458	rv = tellmewhat(lexp, str);
459    }
460    return rv;
461}
462
463
464/*ARGSUSED*/
465void
466dowhich(Char **v, struct command *c)
467{
468    int rv = TRUE;
469    USE(c);
470
471    /*
472     * We don't want to glob dowhich args because we lose quoteing
473     * E.g. which \ls if ls is aliased will not work correctly if
474     * we glob here.
475     */
476
477    while (*++v)
478	rv &= cmd_expand(*v, NULL);
479
480    if (!rv)
481	setcopy(STRstatus, STR1, VAR_READWRITE);
482}
483
484/* PWP: a hack to start up your stopped editor on a single keystroke */
485/* jbs - fixed hack so it worked :-) 3/28/89 */
486
487struct process *
488find_stop_ed(void)
489{
490    struct process *pp, *retp;
491    const char *ep, *vp;
492    char *cp, *p;
493    size_t epl, vpl;
494    int pstatus;
495
496    if ((ep = getenv("EDITOR")) != NULL) {	/* if we have a value */
497	if ((p = strrchr(ep, '/')) != NULL) 	/* if it has a path */
498	    ep = p + 1;		/* then we want only the last part */
499    }
500    else
501	ep = "ed";
502
503    if ((vp = getenv("VISUAL")) != NULL) {	/* if we have a value */
504	if ((p = strrchr(vp, '/')) != NULL) 	/* and it has a path */
505	    vp = p + 1;		/* then we want only the last part */
506    }
507    else
508	vp = "vi";
509
510    for (vpl = 0; vp[vpl] && !isspace((unsigned char)vp[vpl]); vpl++)
511	continue;
512    for (epl = 0; ep[epl] && !isspace((unsigned char)ep[epl]); epl++)
513	continue;
514
515    if (pcurrent == NULL)	/* see if we have any jobs */
516	return NULL;		/* nope */
517
518    retp = NULL;
519    for (pp = proclist.p_next; pp; pp = pp->p_next)
520	if (pp->p_procid == pp->p_jobid) {
521
522	    /*
523	     * Only foreground an edit session if it is suspended.  Some GUI
524	     * editors have may be happily running in a separate window, no
525	     * point in foregrounding these if they're already running - webb
526	     */
527	    pstatus = (int) (pp->p_flags & PALLSTATES);
528	    if (pstatus != PINTERRUPTED && pstatus != PSTOPPED &&
529		pstatus != PSIGNALED)
530		continue;
531
532	    p = short2str(pp->p_command);
533	    /* get the first word */
534	    for (cp = p; *cp && !isspace((unsigned char) *cp); cp++)
535		continue;
536	    *cp = '\0';
537
538	    if ((cp = strrchr(p, '/')) != NULL)	/* and it has a path */
539		cp = cp + 1;		/* then we want only the last part */
540	    else
541		cp = p;			/* else we get all of it */
542
543	    /* if we find either in the current name, fg it */
544	    if (strncmp(ep, cp, epl) == 0 ||
545		strncmp(vp, cp, vpl) == 0) {
546
547		/*
548		 * If there is a choice, then choose the current process if
549		 * available, or the previous process otherwise, or else
550		 * anything will do - Robert Webb (robertw@mulga.cs.mu.oz.au).
551		 */
552		if (pp == pcurrent)
553		    return pp;
554		else if (retp == NULL || pp == pprevious)
555		    retp = pp;
556	    }
557	}
558
559    return retp;		/* Will be NULL if we didn't find a job */
560}
561
562void
563fg_proc_entry(struct process *pp)
564{
565    jmp_buf_t osetexit;
566    int    ohaderr;
567    Char    oGettingInput;
568    size_t omark;
569
570    getexit(osetexit);
571
572    pintr_disabled++;
573    oGettingInput = GettingInput;
574    GettingInput = 0;
575
576    ohaderr = haderr;		/* we need to ignore setting of haderr due to
577				 * process getting stopped by a signal */
578    omark = cleanup_push_mark();
579    if (setexit() == 0) {	/* come back here after pjwait */
580	pendjob();
581	(void) alarm(0);	/* No autologout */
582	alrmcatch_disabled = 1;
583	if (!pstart(pp, 1)) {
584	    pp->p_procid = 0;
585	    stderror(ERR_BADJOB, pp->p_command, strerror(errno));
586	}
587	pjwait(pp);
588    }
589    setalarm(1);		/* Autologout back on */
590    cleanup_pop_mark(omark);
591    resexit(osetexit);
592    haderr = ohaderr;
593    GettingInput = oGettingInput;
594
595    disabled_cleanup(&pintr_disabled);
596}
597
598static char *
599xgetpass(const char *prm)
600{
601    static struct strbuf pass; /* = strbuf_INIT; */
602    int fd;
603    sigset_t oset, set;
604    struct sigaction sa, osa;
605
606    sa.sa_handler = SIG_IGN;
607    sigemptyset(&sa.sa_mask);
608    sa.sa_flags = 0;
609    (void)sigaction(SIGINT, &sa, &osa);
610
611    sigemptyset(&set);
612    sigaddset(&set, SIGINT);
613    (void)sigprocmask(SIG_UNBLOCK, &set, &oset);
614
615    cleanup_push(&osa, sigint_cleanup);
616    cleanup_push(&oset, sigprocmask_cleanup);
617    (void) Rawmode();	/* Make sure, cause we want echo off */
618    fd = xopen("/dev/tty", O_RDWR|O_LARGEFILE);
619    if (fd == -1)
620	fd = SHIN;
621    else
622	cleanup_push(&fd, open_cleanup);
623
624    xprintf("%s", prm); flush();
625    pass.len = 0;
626    for (;;)  {
627	char c;
628
629	if (xread(fd, &c, 1) < 1 || c == '\n')
630	    break;
631	strbuf_append1(&pass, c);
632    }
633    strbuf_terminate(&pass);
634
635    cleanup_until(&osa);
636
637    return pass.s;
638}
639
640#ifndef NO_CRYPT
641#if !HAVE_DECL_CRYPT
642    extern char *crypt ();
643#endif
644#ifdef HAVE_CRYPT_H
645#include <crypt.h>
646#endif
647#endif
648
649/*
650 * Ask the user for his login password to continue working
651 * On systems that have a shadow password, this will only
652 * work for root, but what can we do?
653 *
654 * If we fail to get the password, then we log the user out
655 * immediately
656 */
657/*ARGSUSED*/
658static void
659auto_lock(void)
660{
661#ifndef NO_CRYPT
662
663    int i;
664    char *srpp = NULL;
665    struct passwd *pw;
666
667#undef XCRYPT
668
669#if defined(HAVE_AUTH_H) && defined(HAVE_GETAUTHUID)
670
671    struct authorization *apw;
672    extern char *crypt16 (const char *, const char *);
673
674# define XCRYPT(a, b) crypt16(a, b)
675
676    if ((pw = xgetpwuid(euid)) != NULL &&	/* effective user passwd  */
677        (apw = getauthuid(euid)) != NULL) 	/* enhanced ultrix passwd */
678	srpp = apw->a_password;
679
680#elif defined(HAVE_SHADOW_H)
681
682    struct spwd *spw;
683
684# define XCRYPT(a, b) crypt(a, b)
685
686    if ((pw = xgetpwuid(euid)) != NULL)	{	/* effective user passwd  */
687	errno = 0;
688	while ((spw = getspnam(pw->pw_name)) == NULL && errno == EINTR) {
689	    handle_pending_signals();
690	    errno = 0;
691	}
692	if (spw != NULL)			 /* shadowed passwd	  */
693	    srpp = spw->sp_pwdp;
694    }
695
696#else
697
698#define XCRYPT(a, b) crypt(a, b)
699
700#if !defined(__MVS__)
701    if ((pw = xgetpwuid(euid)) != NULL)	/* effective user passwd  */
702	srpp = pw->pw_passwd;
703#endif /* !MVS */
704
705#endif
706
707    if (srpp == NULL) {
708	auto_logout();
709	/*NOTREACHED*/
710	return;
711    }
712
713    setalarm(0);		/* Not for locking any more */
714    xputchar('\n');
715    for (i = 0; i < 5; i++) {
716	const char *crpp;
717	char *pp;
718#ifdef AFS
719	char *afsname;
720	Char *safs;
721
722	if ((safs = varval(STRafsuser)) != STRNULL)
723	    afsname = short2str(safs);
724	else
725	    if ((afsname = getenv("AFSUSER")) == NULL)
726	        afsname = pw->pw_name;
727#endif
728	pp = xgetpass("Password:");
729
730	crpp = XCRYPT(pp, srpp);
731	if ((strcmp(crpp, srpp) == 0)
732#ifdef AFS
733	    || (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION,
734					   afsname,     /* name */
735					   NULL,        /* instance */
736					   NULL,        /* realm */
737					   pp,          /* password */
738					   0,           /* lifetime */
739					   0, 0,         /* spare */
740					   NULL)        /* reason */
741	    == 0)
742#endif /* AFS */
743	    ) {
744	    (void) memset(pp, 0, strlen(pp));
745	    if (GettingInput && !just_signaled) {
746		(void) Rawmode();
747		ClearLines();
748		ClearDisp();
749		Refresh();
750	    }
751	    just_signaled = 0;
752	    return;
753	}
754	xprintf(CGETS(22, 2, "\nIncorrect passwd for %s\n"), pw->pw_name);
755    }
756#endif /* NO_CRYPT */
757    auto_logout();
758}
759
760
761static void
762auto_logout(void)
763{
764    xprintf("auto-logout\n");
765    /* Don't leave the tty in raw mode */
766    if (editing)
767	(void) Cookedmode();
768    xclose(SHIN);
769    setcopy(STRlogout, STRautomatic, VAR_READWRITE);
770    child = 1;
771#ifdef TESLA
772    do_logout = 1;
773#endif /* TESLA */
774    GettingInput = FALSE; /* make flush() work to write hist files. Huber*/
775    goodbye(NULL, NULL);
776}
777
778void
779alrmcatch(void)
780{
781    (*alm_fun)();
782    setalarm(1);
783}
784
785/*
786 * Karl Kleinpaste, 21oct1983.
787 * Added precmd(), which checks for the alias
788 * precmd in aliases.  If it's there, the alias
789 * is executed as a command.  This is done
790 * after mailchk() and just before print-
791 * ing the prompt.  Useful for things like printing
792 * one's current directory just before each command.
793 */
794void
795precmd(void)
796{
797    pintr_disabled++;
798    cleanup_push(&pintr_disabled, disabled_cleanup);
799    if (precmd_active) {	/* an error must have been caught */
800	aliasrun(2, STRunalias, STRprecmd);
801	xprintf(CGETS(22, 3, "Faulty alias 'precmd' removed.\n"));
802	goto leave;
803    }
804    precmd_active = 1;
805    if (!whyles && adrof1(STRprecmd, &aliases))
806	aliasrun(1, STRprecmd, NULL);
807leave:
808    precmd_active = 0;
809    cleanup_until(&pintr_disabled);
810}
811
812void
813postcmd(void)
814{
815    pintr_disabled++;
816    cleanup_push(&pintr_disabled, disabled_cleanup);
817    if (postcmd_active) {	/* an error must have been caught */
818	aliasrun(2, STRunalias, STRpostcmd);
819	xprintf(CGETS(22, 3, "Faulty alias 'postcmd' removed.\n"));
820	goto leave;
821    }
822    postcmd_active = 1;
823    if (!whyles && adrof1(STRpostcmd, &aliases))
824	aliasrun(1, STRpostcmd, NULL);
825leave:
826    postcmd_active = 0;
827    cleanup_until(&pintr_disabled);
828}
829
830/*
831 * Paul Placeway  11/24/87  Added cwd_cmd by hacking precmd() into
832 * submission...  Run every time $cwd is set (after it is set).  Useful
833 * for putting your machine and cwd (or anything else) in an xterm title
834 * space.
835 */
836void
837cwd_cmd(void)
838{
839    pintr_disabled++;
840    cleanup_push(&pintr_disabled, disabled_cleanup);
841    if (cwdcmd_active) {	/* an error must have been caught */
842	aliasrun(2, STRunalias, STRcwdcmd);
843	xprintf(CGETS(22, 4, "Faulty alias 'cwdcmd' removed.\n"));
844	goto leave;
845    }
846    cwdcmd_active = 1;
847    if (!whyles && adrof1(STRcwdcmd, &aliases))
848	aliasrun(1, STRcwdcmd, NULL);
849leave:
850    cwdcmd_active = 0;
851    cleanup_until(&pintr_disabled);
852}
853
854/*
855 * Joachim Hoenig  07/16/91  Added beep_cmd, run every time tcsh wishes
856 * to beep the terminal bell. Useful for playing nice sounds instead.
857 */
858void
859beep_cmd(void)
860{
861    pintr_disabled++;
862    cleanup_push(&pintr_disabled, disabled_cleanup);
863    if (beepcmd_active) {	/* an error must have been caught */
864	aliasrun(2, STRunalias, STRbeepcmd);
865	xprintf(CGETS(22, 5, "Faulty alias 'beepcmd' removed.\n"));
866    }
867    else {
868	beepcmd_active = 1;
869	if (!whyles && adrof1(STRbeepcmd, &aliases))
870	    aliasrun(1, STRbeepcmd, NULL);
871    }
872    beepcmd_active = 0;
873    cleanup_until(&pintr_disabled);
874}
875
876
877/*
878 * Karl Kleinpaste, 18 Jan 1984.
879 * Added period_cmd(), which executes the alias "periodic" every
880 * $tperiod minutes.  Useful for occasional checking of msgs and such.
881 */
882void
883period_cmd(void)
884{
885    Char *vp;
886    time_t  t, interval;
887
888    pintr_disabled++;
889    cleanup_push(&pintr_disabled, disabled_cleanup);
890    if (periodic_active) {	/* an error must have been caught */
891	aliasrun(2, STRunalias, STRperiodic);
892	xprintf(CGETS(22, 6, "Faulty alias 'periodic' removed.\n"));
893	goto leave;
894    }
895    periodic_active = 1;
896    if (!whyles && adrof1(STRperiodic, &aliases)) {
897	vp = varval(STRtperiod);
898	if (vp == STRNULL) {
899	    aliasrun(1, STRperiodic, NULL);
900	    goto leave;
901	}
902	interval = getn(vp);
903	(void) time(&t);
904	if (t - t_period >= interval * 60) {
905	    t_period = t;
906	    aliasrun(1, STRperiodic, NULL);
907	}
908    }
909leave:
910    periodic_active = 0;
911    cleanup_until(&pintr_disabled);
912}
913
914
915/*
916 * GrP Greg Parker May 2001
917 * Added job_cmd(), which is run every time a job is started or
918 * foregrounded. The command is passed a single argument, the string
919 * used to start the job originally. With precmd, useful for setting
920 * xterm titles.
921 * Cloned from cwd_cmd().
922 */
923void
924job_cmd(Char *args)
925{
926    pintr_disabled++;
927    cleanup_push(&pintr_disabled, disabled_cleanup);
928    if (jobcmd_active) {	/* an error must have been caught */
929	aliasrun(2, STRunalias, STRjobcmd);
930	xprintf(CGETS(22, 14, "Faulty alias 'jobcmd' removed.\n"));
931	goto leave;
932    }
933    jobcmd_active = 1;
934    if (!whyles && adrof1(STRjobcmd, &aliases)) {
935	struct process *pp = pcurrjob; /* put things back after the hook */
936	aliasrun(2, STRjobcmd, args);
937	pcurrjob = pp;
938    }
939leave:
940    jobcmd_active = 0;
941    cleanup_until(&pintr_disabled);
942}
943
944
945/*
946 * Karl Kleinpaste, 21oct1983.
947 * Set up a one-word alias command, for use for special things.
948 * This code is based on the mainline of process().
949 */
950void
951aliasrun(int cnt, Char *s1, Char *s2)
952{
953    struct wordent w, *new1, *new2;	/* for holding alias name */
954    struct command *t = NULL;
955    jmp_buf_t osetexit;
956    int status;
957    size_t omark;
958
959    getexit(osetexit);
960    if (seterr) {
961	xfree(seterr);
962	seterr = NULL;	/* don't repeatedly print err msg. */
963    }
964    w.word = STRNULL;
965    new1 = xcalloc(1, sizeof w);
966    new1->word = Strsave(s1);
967    if (cnt == 1) {
968	/* build a lex list with one word. */
969	w.next = w.prev = new1;
970	new1->next = new1->prev = &w;
971    }
972    else {
973	/* build a lex list with two words. */
974	new2 = xcalloc(1, sizeof w);
975	new2->word = Strsave(s2);
976	w.next = new2->prev = new1;
977	new1->next = w.prev = new2;
978	new1->prev = new2->next = &w;
979    }
980    cleanup_push(&w, lex_cleanup);
981
982    /* Save the old status */
983    status = getn(varval(STRstatus));
984
985    /* expand aliases like process() does. */
986    alias(&w);
987    /* build a syntax tree for the command. */
988    t = syntax(w.next, &w, 0);
989    cleanup_push(t, syntax_cleanup);
990    if (seterr)
991	stderror(ERR_OLD);
992
993    psavejob();
994    cleanup_push(&cnt, psavejob_cleanup); /* cnt is used only as a marker */
995
996    /* catch any errors here */
997    omark = cleanup_push_mark();
998    if (setexit() == 0)
999	/* execute the parse tree. */
1000	/*
1001	 * From: Michael Schroeder <mlschroe@immd4.informatik.uni-erlangen.de>
1002	 * was execute(t, tpgrp);
1003	 */
1004	execute(t, tpgrp > 0 ? tpgrp : -1, NULL, NULL, TRUE);
1005    /* reset the error catcher to the old place */
1006    cleanup_pop_mark(omark);
1007    resexit(osetexit);
1008    if (haderr) {
1009	haderr = 0;
1010	/*
1011	 * Either precmd, or cwdcmd, or periodic had an error. Call it again so
1012	 * that it is removed
1013	 */
1014	if (precmd_active)
1015	    precmd();
1016	if (postcmd_active)
1017	    postcmd();
1018#ifdef notdef
1019	/*
1020	 * XXX: On the other hand, just interrupting them causes an error too.
1021	 * So if we hit ^C in the middle of cwdcmd or periodic the alias gets
1022	 * removed. We don't want that. Note that we want to remove precmd
1023	 * though, cause that could lead into an infinite loop. This should be
1024	 * fixed correctly, but then haderr should give us the whole exit
1025	 * status not just true or false.
1026	 */
1027	else if (cwdcmd_active)
1028	    cwd_cmd();
1029	else if (beepcmd_active)
1030	    beep_cmd();
1031	else if (periodic_active)
1032	    period_cmd();
1033#endif /* notdef */
1034    }
1035    cleanup_until(&w);
1036    pendjob();
1037    /* Restore status */
1038    setv(STRstatus, putn(status), VAR_READWRITE);
1039}
1040
1041void
1042setalarm(int lck)
1043{
1044    struct varent *vp;
1045    Char   *cp;
1046    unsigned alrm_time = 0, logout_time, lock_time;
1047    time_t cl, nl, sched_dif;
1048
1049    if ((vp = adrof(STRautologout)) != NULL && vp->vec != NULL) {
1050	if ((cp = vp->vec[0]) != 0) {
1051	    if ((logout_time = (unsigned) atoi(short2str(cp)) * 60) > 0) {
1052#ifdef SOLARIS2
1053		/*
1054		 * Solaris alarm(2) uses a timer based in clock ticks
1055		 * internally so it multiplies our value with CLK_TCK...
1056		 * Of course that can overflow leading to unexpected
1057		 * results, so we clip it here. Grr. Where is that
1058		 * documented folks?
1059		 */
1060		if (logout_time >= 0x7fffffff / CLK_TCK)
1061			logout_time = 0x7fffffff / CLK_TCK;
1062#endif /* SOLARIS2 */
1063		alrm_time = logout_time;
1064		alm_fun = auto_logout;
1065	    }
1066	}
1067	if ((cp = vp->vec[1]) != 0) {
1068	    if ((lock_time = (unsigned) atoi(short2str(cp)) * 60) > 0) {
1069		if (lck) {
1070		    if (alrm_time == 0 || lock_time < alrm_time) {
1071			alrm_time = lock_time;
1072			alm_fun = auto_lock;
1073		    }
1074		}
1075		else /* lock_time always < alrm_time */
1076		    if (alrm_time)
1077			alrm_time -= lock_time;
1078	    }
1079	}
1080    }
1081    if ((nl = sched_next()) != -1) {
1082	(void) time(&cl);
1083	sched_dif = nl > cl ? nl - cl : 0;
1084	if ((alrm_time == 0) || ((unsigned) sched_dif < alrm_time)) {
1085	    alrm_time = ((unsigned) sched_dif) + 1;
1086	    alm_fun = sched_run;
1087	}
1088    }
1089    alrmcatch_disabled = 0;
1090    (void) alarm(alrm_time);	/* Autologout ON */
1091}
1092
1093#undef RMDEBUG			/* For now... */
1094
1095void
1096rmstar(struct wordent *cp)
1097{
1098    struct wordent *we, *args;
1099    struct wordent *tmp, *del;
1100
1101#ifdef RMDEBUG
1102    static Char STRrmdebug[] = {'r', 'm', 'd', 'e', 'b', 'u', 'g', '\0'};
1103    Char   *tag;
1104#endif /* RMDEBUG */
1105    Char   *charac;
1106    char    c;
1107    int     ask, doit, star = 0, silent = 0;
1108
1109    if (!adrof(STRrmstar))
1110	return;
1111#ifdef RMDEBUG
1112    tag = varval(STRrmdebug);
1113#endif /* RMDEBUG */
1114    we = cp->next;
1115    while (*we->word == ';' && we != cp)
1116	we = we->next;
1117    while (we != cp) {
1118#ifdef RMDEBUG
1119	if (*tag)
1120	    xprintf(CGETS(22, 7, "parsing command line\n"));
1121#endif /* RMDEBUG */
1122	if (!Strcmp(we->word, STRrm)) {
1123	    args = we->next;
1124	    ask = (*args->word != '-');
1125	    while (*args->word == '-' && !silent) {	/* check options */
1126		for (charac = (args->word + 1); *charac && !silent; charac++)
1127		    silent = (*charac == 'i' || *charac == 'f');
1128		args = args->next;
1129	    }
1130	    ask = (ask || (!ask && !silent));
1131	    if (ask) {
1132		for (; !star && *args->word != ';'
1133		     && args != cp; args = args->next)
1134		    if (!Strcmp(args->word, STRstar))
1135			star = 1;
1136		if (ask && star) {
1137		    xprintf(CGETS(22, 8,
1138			    "Do you really want to delete all files? [n/y] "));
1139		    flush();
1140		    (void) force_read(SHIN, &c, 1);
1141		    /*
1142		     * Perhaps we should use the yesexpr from the
1143		     * actual locale
1144		     */
1145		    doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL);
1146		    while (c != '\n' && force_read(SHIN, &c, 1) == 1)
1147			continue;
1148		    if (!doit) {
1149			/* remove the command instead */
1150#ifdef RMDEBUG
1151			if (*tag)
1152			    xprintf(CGETS(22, 9,
1153				    "skipping deletion of files!\n"));
1154#endif /* RMDEBUG */
1155			for (tmp = we;
1156			     *tmp->word != '\n' &&
1157			     *tmp->word != ';' && tmp != cp;) {
1158			    tmp->prev->next = tmp->next;
1159			    tmp->next->prev = tmp->prev;
1160			    xfree(tmp->word);
1161			    del = tmp;
1162			    tmp = tmp->next;
1163			    xfree(del);
1164			}
1165			if (*tmp->word == ';') {
1166			    tmp->prev->next = tmp->next;
1167			    tmp->next->prev = tmp->prev;
1168			    xfree(tmp->word);
1169			    del = tmp;
1170			    tmp = tmp->next;
1171			    xfree(del);
1172			}
1173			we = tmp;
1174			continue;
1175		    }
1176		}
1177	    }
1178	}
1179	for (we = we->next;
1180	     *we->word != ';' && we != cp;
1181	     we = we->next)
1182	    continue;
1183	if (*we->word == ';')
1184	    we = we->next;
1185    }
1186#ifdef RMDEBUG
1187    if (*tag) {
1188	xprintf(CGETS(22, 10, "command line now is:\n"));
1189	for (we = cp->next; we != cp; we = we->next)
1190	    xprintf("%S ", we->word);
1191    }
1192#endif /* RMDEBUG */
1193    return;
1194}
1195
1196#ifdef BSDJOBS
1197/* Check if command is in continue list
1198   and do a "aliasing" if it exists as a job in background */
1199
1200#undef CNDEBUG			/* For now */
1201void
1202continue_jobs(struct wordent *cp)
1203{
1204    struct wordent *we;
1205    struct process *pp, *np;
1206    Char   *cmd, *continue_list, *continue_args_list;
1207
1208#ifdef CNDEBUG
1209    Char   *tag;
1210    static Char STRcndebug[] =
1211    {'c', 'n', 'd', 'e', 'b', 'u', 'g', '\0'};
1212#endif /* CNDEBUG */
1213    int    in_cont_list, in_cont_arg_list;
1214
1215
1216#ifdef CNDEBUG
1217    tag = varval(STRcndebug);
1218#endif /* CNDEBUG */
1219    continue_list = varval(STRcontinue);
1220    continue_args_list = varval(STRcontinue_args);
1221    if (*continue_list == '\0' && *continue_args_list == '\0')
1222	return;
1223
1224    we = cp->next;
1225    while (*we->word == ';' && we != cp)
1226	we = we->next;
1227    while (we != cp) {
1228#ifdef CNDEBUG
1229	if (*tag)
1230	    xprintf(CGETS(22, 11, "parsing command line\n"));
1231#endif /* CNDEBUG */
1232	cmd = we->word;
1233	in_cont_list = inlist(continue_list, cmd);
1234	in_cont_arg_list = inlist(continue_args_list, cmd);
1235	if (in_cont_list || in_cont_arg_list) {
1236#ifdef CNDEBUG
1237	    if (*tag)
1238		xprintf(CGETS(22, 12, "in one of the lists\n"));
1239#endif /* CNDEBUG */
1240	    np = NULL;
1241	    for (pp = proclist.p_next; pp; pp = pp->p_next) {
1242		if (prefix(cmd, pp->p_command)) {
1243		    if (pp->p_index) {
1244			np = pp;
1245			break;
1246		    }
1247		}
1248	    }
1249	    if (np) {
1250		insert(we, in_cont_arg_list);
1251	    }
1252	}
1253	for (we = we->next;
1254	     *we->word != ';' && we != cp;
1255	     we = we->next)
1256	    continue;
1257	if (*we->word == ';')
1258	    we = we->next;
1259    }
1260#ifdef CNDEBUG
1261    if (*tag) {
1262	xprintf(CGETS(22, 13, "command line now is:\n"));
1263	for (we = cp->next; we != cp; we = we->next)
1264	    xprintf("%S ", we->word);
1265    }
1266#endif /* CNDEBUG */
1267    return;
1268}
1269
1270/* The actual "aliasing" of for backgrounds() is done here
1271   with the aid of insert_we().   */
1272static void
1273insert(struct wordent *pl, int file_args)
1274{
1275    struct wordent *now, *last;
1276    Char   *cmd, *bcmd, *cp1, *cp2;
1277    size_t cmd_len;
1278    Char   *upause = STRunderpause;
1279    size_t p_len = Strlen(upause);
1280
1281    cmd_len = Strlen(pl->word);
1282    cmd = xcalloc(1, (cmd_len + 1) * sizeof(Char));
1283    (void) Strcpy(cmd, pl->word);
1284/* Do insertions at beginning, first replace command word */
1285
1286    if (file_args) {
1287	now = pl;
1288	xfree(now->word);
1289	now->word = xcalloc(1, 5 * sizeof(Char));
1290	(void) Strcpy(now->word, STRecho);
1291
1292	now = xcalloc(1, sizeof(struct wordent));
1293	now->word = xcalloc(1, 6 * sizeof(Char));
1294	(void) Strcpy(now->word, STRbackqpwd);
1295	insert_we(now, pl);
1296
1297	for (last = now; *last->word != '\n' && *last->word != ';';
1298	     last = last->next)
1299	    continue;
1300
1301	now = xcalloc(1, sizeof(struct wordent));
1302	now->word = xcalloc(1, 2 * sizeof(Char));
1303	(void) Strcpy(now->word, STRgt);
1304	insert_we(now, last->prev);
1305
1306	now = xcalloc(1, sizeof(struct wordent));
1307	now->word = xcalloc(1, 2 * sizeof(Char));
1308	(void) Strcpy(now->word, STRbang);
1309	insert_we(now, last->prev);
1310
1311	now = xcalloc(1, sizeof(struct wordent));
1312	now->word = xcalloc(1, (cmd_len + p_len + 4) * sizeof(Char));
1313	cp1 = now->word;
1314	cp2 = cmd;
1315	*cp1++ = '~';
1316	*cp1++ = '/';
1317	*cp1++ = '.';
1318	while ((*cp1++ = *cp2++) != '\0')
1319	    continue;
1320	cp1--;
1321	cp2 = upause;
1322	while ((*cp1++ = *cp2++) != '\0')
1323	    continue;
1324	insert_we(now, last->prev);
1325
1326	now = xcalloc(1, sizeof(struct wordent));
1327	now->word = xcalloc(1, 2 * sizeof(Char));
1328	(void) Strcpy(now->word, STRsemi);
1329	insert_we(now, last->prev);
1330	bcmd = xcalloc(1, (cmd_len + 2) * sizeof(Char));
1331	*bcmd = '%';
1332	Strcpy(bcmd + 1, cmd);
1333	now = xcalloc(1, sizeof(struct wordent));
1334	now->word = bcmd;
1335	insert_we(now, last->prev);
1336    }
1337    else {
1338	struct wordent *del;
1339
1340	now = pl;
1341	xfree(now->word);
1342	now->word = xcalloc(1, (cmd_len + 2) * sizeof(Char));
1343	*now->word = '%';
1344	Strcpy(now->word + 1, cmd);
1345	for (now = now->next;
1346	     *now->word != '\n' && *now->word != ';' && now != pl;) {
1347	    now->prev->next = now->next;
1348	    now->next->prev = now->prev;
1349	    xfree(now->word);
1350	    del = now;
1351	    now = now->next;
1352	    xfree(del);
1353	}
1354    }
1355}
1356
1357static void
1358insert_we(struct wordent *new, struct wordent *where)
1359{
1360
1361    new->prev = where;
1362    new->next = where->next;
1363    where->next = new;
1364    new->next->prev = new;
1365}
1366
1367static int
1368inlist(Char *list, Char *name)
1369{
1370    Char *l, *n;
1371
1372    l = list;
1373    n = name;
1374
1375    while (*l && *n) {
1376	if (*l == *n) {
1377	    l++;
1378	    n++;
1379	    if (*n == '\0' && (*l == ' ' || *l == '\0'))
1380		return (1);
1381	    else
1382		continue;
1383	}
1384	else {
1385	    while (*l && *l != ' ')
1386		l++;		/* skip to blank */
1387	    while (*l && *l == ' ')
1388		l++;		/* and find first nonblank character */
1389	    n = name;
1390	}
1391    }
1392    return (0);
1393}
1394
1395#endif /* BSDJOBS */
1396
1397
1398/*
1399 * Implement a small cache for tilde names. This is used primarily
1400 * to expand tilde names to directories, but also
1401 * we can find users from their home directories for the tilde
1402 * prompt, on machines where yp lookup is slow this can be a big win...
1403 * As with any cache this can run out of sync, rehash can sync it again.
1404 */
1405static struct tildecache {
1406    Char   *user;
1407    Char   *home;
1408    size_t  hlen;
1409}      *tcache = NULL;
1410
1411#define TILINCR 10
1412size_t tlength = 0;
1413static size_t tsize = TILINCR;
1414
1415static int
1416tildecompare(const void *xp1, const void *xp2)
1417{
1418    const struct tildecache *p1, *p2;
1419
1420    p1 = xp1;
1421    p2 = xp2;
1422    return Strcmp(p1->user, p2->user);
1423}
1424
1425static Char *
1426gethomedir(const Char *us)
1427{
1428    struct passwd *pp;
1429#ifdef HESIOD
1430    char **res, **res1, *cp;
1431    Char *rp;
1432#endif /* HESIOD */
1433
1434    pp = xgetpwnam(short2str(us));
1435#ifdef YPBUGS
1436    fix_yp_bugs();
1437#endif /* YPBUGS */
1438    if (pp != NULL) {
1439#if 0
1440	/* Don't return if root */
1441	if (pp->pw_dir[0] == '/' && pp->pw_dir[1] == '\0')
1442	    return NULL;
1443	else
1444#endif
1445	    return Strsave(str2short(pp->pw_dir));
1446    }
1447#ifdef HESIOD
1448    res = hes_resolve(short2str(us), "filsys");
1449    rp = NULL;
1450    if (res != NULL) {
1451	if ((*res) != NULL) {
1452	    /*
1453	     * Look at the first token to determine how to interpret
1454	     * the rest of it.
1455	     * Yes, strtok is evil (it's not thread-safe), but it's also
1456	     * easy to use.
1457	     */
1458	    cp = strtok(*res, " ");
1459	    if (strcmp(cp, "AFS") == 0) {
1460		/* next token is AFS pathname.. */
1461		cp = strtok(NULL, " ");
1462		if (cp != NULL)
1463		    rp = Strsave(str2short(cp));
1464	    } else if (strcmp(cp, "NFS") == 0) {
1465		cp = NULL;
1466		if ((strtok(NULL, " ")) && /* skip remote pathname */
1467		    (strtok(NULL, " ")) && /* skip host */
1468		    (strtok(NULL, " ")) && /* skip mode */
1469		    (cp = strtok(NULL, " "))) {
1470		    rp = Strsave(str2short(cp));
1471		}
1472	    }
1473	}
1474	for (res1 = res; *res1; res1++)
1475	    free(*res1);
1476#if 0
1477	/* Don't return if root */
1478	if (rp != NULL && rp[0] == '/' && rp[1] == '\0') {
1479	    xfree(rp);
1480	    rp = NULL;
1481	}
1482#endif
1483	return rp;
1484    }
1485#endif /* HESIOD */
1486    return NULL;
1487}
1488
1489Char   *
1490gettilde(const Char *us)
1491{
1492    struct tildecache *bp1, *bp2, *bp;
1493    Char *hd;
1494
1495    /* Ignore NIS special names */
1496    if (*us == '+' || *us == '-')
1497	return NULL;
1498
1499    if (tcache == NULL)
1500	tcache = xmalloc(TILINCR * sizeof(struct tildecache));
1501    /*
1502     * Binary search
1503     */
1504    for (bp1 = tcache, bp2 = tcache + tlength; bp1 < bp2;) {
1505	int i;
1506
1507	bp = bp1 + ((bp2 - bp1) >> 1);
1508	if ((i = *us - *bp->user) == 0 && (i = Strcmp(us, bp->user)) == 0)
1509	    return (bp->home);
1510	if (i < 0)
1511	    bp2 = bp;
1512	else
1513	    bp1 = bp + 1;
1514    }
1515    /*
1516     * Not in the cache, try to get it from the passwd file
1517     */
1518    hd = gethomedir(us);
1519    if (hd == NULL)
1520	return NULL;
1521
1522    /*
1523     * Update the cache
1524     */
1525    tcache[tlength].user = Strsave(us);
1526    tcache[tlength].home = hd;
1527    tcache[tlength++].hlen = Strlen(hd);
1528
1529    qsort(tcache, tlength, sizeof(struct tildecache), tildecompare);
1530
1531    if (tlength == tsize) {
1532	tsize += TILINCR;
1533	tcache = xrealloc(tcache, tsize * sizeof(struct tildecache));
1534    }
1535    return (hd);
1536}
1537
1538/*
1539 * Return the username if the directory path passed contains a
1540 * user's home directory in the tilde cache, otherwise return NULL
1541 * hm points to the place where the path became different.
1542 * Special case: Our own home directory.
1543 * If we are passed a null pointer, then we flush the cache.
1544 */
1545Char   *
1546getusername(Char **hm)
1547{
1548    Char   *h, *p;
1549    size_t i, j;
1550
1551    if (hm == NULL) {
1552	for (i = 0; i < tlength; i++) {
1553	    xfree(tcache[i].home);
1554	    xfree(tcache[i].user);
1555	}
1556	xfree(tcache);
1557	tlength = 0;
1558	tsize = TILINCR;
1559	tcache = NULL;
1560	return NULL;
1561    }
1562    p = *hm;
1563    if (((h = varval(STRhome)) != STRNULL) &&
1564	(Strncmp(p, h, j = Strlen(h)) == 0) &&
1565	(p[j] == '/' || p[j] == '\0')) {
1566	*hm = &p[j];
1567	return STRNULL;
1568    }
1569    for (i = 0; i < tlength; i++)
1570	if ((Strncmp(p, tcache[i].home, (j = tcache[i].hlen)) == 0) &&
1571	    (p[j] == '/' || p[j] == '\0')) {
1572	    *hm = &p[j];
1573	    return tcache[i].user;
1574	}
1575    return NULL;
1576}
1577
1578
1579/*
1580 * set the shell-level var to 1 or apply change to it.
1581 */
1582void
1583shlvl(int val)
1584{
1585    char *cp;
1586
1587    if ((cp = getenv("SHLVL")) != NULL) {
1588
1589	if (loginsh)
1590	    val = 1;
1591	else
1592	    val += atoi(cp);
1593
1594	if (val <= 0) {
1595	    if (adrof(STRshlvl) != NULL)
1596		unsetv(STRshlvl);
1597	    Unsetenv(STRKSHLVL);
1598	}
1599	else {
1600	    Char *p;
1601
1602	    p = Itoa(val, 0, 0);
1603	    cleanup_push(p, xfree);
1604	    setv(STRshlvl, p, VAR_READWRITE);
1605	    cleanup_ignore(p);
1606	    cleanup_until(p);
1607	    tsetenv(STRKSHLVL, p);
1608	}
1609    }
1610    else {
1611	setcopy(STRshlvl, STR1, VAR_READWRITE);
1612	tsetenv(STRKSHLVL, STR1);
1613    }
1614}
1615
1616
1617/* fixio():
1618 *	Try to recover from a read error
1619 */
1620int
1621fixio(int fd, int e)
1622{
1623    switch (e) {
1624    case -1:	/* Make sure that the code is reachable */
1625
1626#ifdef EWOULDBLOCK
1627    case EWOULDBLOCK:
1628# define FDRETRY
1629#endif /* EWOULDBLOCK */
1630
1631#if defined(POSIX) && defined(EAGAIN)
1632# if !defined(EWOULDBLOCK) || EWOULDBLOCK != EAGAIN
1633    case EAGAIN:
1634#  define FDRETRY
1635# endif /* !EWOULDBLOCK || EWOULDBLOCK != EAGAIN */
1636#endif /* POSIX && EAGAIN */
1637
1638	e = 0;
1639#ifdef FDRETRY
1640# ifdef F_SETFL
1641/*
1642 * Great! we have on suns 3 flavors and 5 names...
1643 * I hope that will cover everything.
1644 * I added some more defines... many systems have different defines.
1645 * Rather than dealing with getting the right includes, we'll just
1646 * cover all the known possibilities here.  -- sterling@netcom.com
1647 */
1648#  ifndef O_NONBLOCK
1649#   define O_NONBLOCK 0
1650#  endif /* O_NONBLOCK */
1651#  ifndef O_NDELAY
1652#   define O_NDELAY 0
1653#  endif /* O_NDELAY */
1654#  ifndef FNBIO
1655#   define FNBIO 0
1656#  endif /* FNBIO */
1657#  ifndef _FNBIO
1658#   define _FNBIO 0
1659#  endif /* _FNBIO */
1660#  ifndef FNONBIO
1661#   define FNONBIO 0
1662#  endif /* FNONBIO */
1663#  ifndef FNONBLOCK
1664#   define FNONBLOCK 0
1665#  endif /* FNONBLOCK */
1666#  ifndef _FNONBLOCK
1667#   define _FNONBLOCK 0
1668#  endif /* _FNONBLOCK */
1669#  ifndef FNDELAY
1670#   define FNDELAY 0
1671#  endif /* FNDELAY */
1672#  ifndef _FNDELAY
1673#   define _FNDELAY 0
1674#  endif /* _FNDELAY */
1675#  ifndef FNDLEAY	/* Some linux versions have this typo */
1676#   define FNDLEAY 0
1677#  endif /* FNDLEAY */
1678	if ((e = fcntl(fd, F_GETFL, 0)) == -1)
1679	    return -1;
1680
1681	e &= ~(O_NDELAY|O_NONBLOCK|FNBIO|_FNBIO|FNONBIO|FNONBLOCK|_FNONBLOCK|
1682	       FNDELAY|_FNDELAY|FNDLEAY);	/* whew! */
1683	if (fcntl(fd, F_SETFL, e) == -1)
1684	    return -1;
1685	else
1686	    e = 1;
1687# endif /* F_SETFL */
1688
1689# ifdef FIONBIO
1690	e = 0;
1691	if (ioctl(fd, FIONBIO, (ioctl_t) &e) == -1)
1692	    return -1;
1693	else
1694	    e = 1;
1695# endif	/* FIONBIO */
1696
1697#endif /* FDRETRY */
1698	return e ? 0 : -1;
1699
1700    case EINTR:
1701	return 0;
1702
1703    default:
1704	return -1;
1705    }
1706}
1707
1708/* collate():
1709 *	String collation
1710 */
1711int
1712collate(const Char *a, const Char *b)
1713{
1714    int rv;
1715#ifdef SHORT_STRINGS
1716    /* This strips the quote bit as a side effect */
1717    char *sa = strsave(short2str(a));
1718    char *sb = strsave(short2str(b));
1719#else
1720    char *sa = strip(strsave(a));
1721    char *sb = strip(strsave(b));
1722#endif /* SHORT_STRINGS */
1723
1724#if defined(NLS) && defined(HAVE_STRCOLL)
1725    errno = 0;	/* strcoll sets errno, another brain-damage */
1726
1727    rv = strcoll(sa, sb);
1728
1729    /*
1730     * We should be checking for errno != 0, but some systems
1731     * forget to reset errno to 0. So we only check for the
1732     * only documented valid errno value for strcoll [EINVAL]
1733     */
1734    if (errno == EINVAL) {
1735	xfree(sa);
1736	xfree(sb);
1737	stderror(ERR_SYSTEM, "strcoll", strerror(errno));
1738    }
1739#else
1740    rv = strcmp(sa, sb);
1741#endif /* NLS && HAVE_STRCOLL */
1742
1743    xfree(sa);
1744    xfree(sb);
1745
1746    return rv;
1747}
1748
1749#ifdef HASHBANG
1750/*
1751 * From: peter@zeus.dialix.oz.au (Peter Wemm)
1752 * If exec() fails look first for a #! [word] [word] ....
1753 * If it is, splice the header into the argument list and retry.
1754 */
1755#define HACKBUFSZ 1024		/* Max chars in #! vector */
1756int
1757hashbang(int fd, Char ***vp)
1758{
1759    struct blk_buf sarg = BLK_BUF_INIT;
1760    char lbuf[HACKBUFSZ], *p, *ws;
1761#ifdef WINNT_NATIVE
1762    int fw = 0; 	/* found at least one word */
1763    int first_word = 1;
1764    char *real;
1765#endif /* WINNT_NATIVE */
1766
1767    if (xread(fd, lbuf, HACKBUFSZ) <= 0)
1768	return -1;
1769
1770    ws = 0;	/* word started = 0 */
1771
1772    for (p = lbuf; p < &lbuf[HACKBUFSZ]; ) {
1773	switch (*p) {
1774	case ' ':
1775	case '\t':
1776#ifdef WINNT_NATIVE
1777	case '\r':
1778#endif /* WINNT_NATIVE */
1779	    if (ws) {	/* a blank after a word.. save it */
1780		*p = '\0';
1781#ifdef WINNT_NATIVE
1782		if (first_word) {
1783		    real = hb_subst(ws);
1784		    if (real != NULL)
1785			ws = real;
1786		}
1787	    	fw = 1;
1788		first_word = 0;
1789#endif /* WINNT_NATIVE */
1790		bb_append(&sarg, SAVE(ws));
1791		ws = NULL;
1792	    }
1793	    p++;
1794	    continue;
1795
1796	case '\0':	/* Whoa!! what the hell happened */
1797	    goto err;
1798
1799	case '\n':	/* The end of the line. */
1800	    if (
1801#ifdef WINNT_NATIVE
1802		fw ||
1803#endif /* WINNT_NATIVE */
1804		ws) {	/* terminate the last word */
1805		*p = '\0';
1806#ifdef WINNT_NATIVE
1807		/* deal with the 1-word case */
1808		if (first_word) {
1809		    real = hb_subst(ws);
1810		    if (real != NULL)
1811			ws = real;
1812		}
1813#endif /* !WINNT_NATIVE */
1814		if (ws)
1815		    bb_append(&sarg, SAVE(ws));
1816	    }
1817	    if (sarg.len > 0) {
1818		*vp = bb_finish(&sarg);
1819		return 0;
1820	    }
1821	    else
1822		goto err;
1823
1824	default:
1825	    if (!ws)	/* Start a new word? */
1826		ws = p;
1827	    p++;
1828	    break;
1829	}
1830    }
1831 err:
1832    bb_cleanup(&sarg);
1833    return -1;
1834}
1835#endif /* HASHBANG */
1836
1837#ifdef REMOTEHOST
1838
1839static void
1840palarm(int snum)
1841{
1842    USE(snum);
1843    _exit(1);
1844}
1845
1846static void
1847getremotehost(int dest_fd)
1848{
1849    const char *host = NULL;
1850#ifdef INET6
1851    struct sockaddr_storage saddr;
1852    static char hbuf[NI_MAXHOST];
1853#else
1854    struct hostent* hp;
1855    struct sockaddr_in saddr;
1856#endif
1857    socklen_t len = sizeof(saddr);
1858
1859#ifdef INET6
1860    if (getpeername(SHIN, (struct sockaddr *) &saddr, &len) != -1 &&
1861	(saddr.ss_family == AF_INET6 || saddr.ss_family == AF_INET)) {
1862	int flag = NI_NUMERICHOST;
1863
1864#ifdef NI_WITHSCOPEID
1865	flag |= NI_WITHSCOPEID;
1866#endif
1867	getnameinfo((struct sockaddr *)&saddr, len, hbuf, sizeof(hbuf),
1868		    NULL, 0, flag);
1869	host = hbuf;
1870#else
1871    if (getpeername(SHIN, (struct sockaddr *) &saddr, &len) != -1 &&
1872	saddr.sin_family == AF_INET) {
1873#if 0
1874	if ((hp = gethostbyaddr((char *)&saddr.sin_addr, sizeof(struct in_addr),
1875				AF_INET)) != NULL)
1876	    host = hp->h_name;
1877	else
1878#endif
1879	    host = inet_ntoa(saddr.sin_addr);
1880#endif
1881    }
1882#ifdef HAVE_STRUCT_UTMP_UT_HOST
1883    else {
1884	char *ptr;
1885	char *name = utmphost();
1886	/* Avoid empty names and local X displays */
1887	if (name != NULL && *name != '\0' && *name != ':') {
1888	    struct in_addr addr;
1889	    char *sptr;
1890
1891	    /* Look for host:display.screen */
1892	    /*
1893	     * There is conflict with IPv6 address and X DISPLAY.  So,
1894	     * we assume there is no IPv6 address in utmp and don't
1895	     * touch here.
1896	     */
1897	    if ((sptr = strchr(name, ':')) != NULL)
1898		*sptr = '\0';
1899	    /* Leave IPv4 address as is */
1900	    /*
1901	     * we use inet_addr here, not inet_aton because many systems
1902	     * have not caught up yet.
1903	     */
1904	    addr.s_addr = inet_addr(name);
1905	    if (addr.s_addr != (unsigned int)~0)
1906		host = name;
1907	    else {
1908		if (sptr != name) {
1909#ifdef INET6
1910		    char *s, *domain;
1911		    char dbuf[MAXHOSTNAMELEN];
1912		    struct addrinfo hints, *res = NULL;
1913
1914		    memset(&hints, 0, sizeof(hints));
1915		    hints.ai_family = PF_UNSPEC;
1916		    hints.ai_socktype = SOCK_STREAM;
1917		    hints.ai_flags = AI_PASSIVE | AI_CANONNAME;
1918		    if (strlen(name) < utmphostsize())
1919		    {
1920			if (getaddrinfo(name, NULL, &hints, &res) != 0)
1921			    res = NULL;
1922		    } else if (gethostname(dbuf, sizeof(dbuf)) == 0 &&
1923			       (dbuf[sizeof(dbuf)-1] = '\0', /*FIXME: ugly*/
1924				(domain = strchr(dbuf, '.')) != NULL)) {
1925			for (s = strchr(name, '.');
1926			     s != NULL; s = strchr(s + 1, '.')) {
1927			    if (*(s + 1) != '\0' &&
1928				(ptr = strstr(domain, s)) != NULL) {
1929			        char *cbuf;
1930
1931				cbuf = strspl(name, ptr);
1932				if (getaddrinfo(cbuf, NULL, &hints, &res) != 0)
1933				    res = NULL;
1934				xfree(cbuf);
1935				break;
1936			    }
1937			}
1938		    }
1939		    if (res != NULL) {
1940			if (res->ai_canonname != NULL) {
1941			    strncpy(hbuf, res->ai_canonname, sizeof(hbuf));
1942			    host = hbuf;
1943			}
1944			freeaddrinfo(res);
1945		    }
1946#else
1947		    if ((hp = gethostbyname(name)) == NULL) {
1948			/* Try again eliminating the trailing domain */
1949			if ((ptr = strchr(name, '.')) != NULL) {
1950			    *ptr = '\0';
1951			    if ((hp = gethostbyname(name)) != NULL)
1952				host = hp->h_name;
1953			    *ptr = '.';
1954			}
1955		    }
1956		    else
1957			host = hp->h_name;
1958#endif
1959		}
1960	    }
1961	}
1962    }
1963#endif
1964
1965    if (host) {
1966	size_t left;
1967
1968	left = strlen(host);
1969	while (left != 0) {
1970	    ssize_t res;
1971
1972	    res = xwrite(dest_fd, host, left);
1973	    if (res < 0)
1974		_exit(1);
1975	    host += res;
1976	    left -= res;
1977	}
1978    }
1979    _exit(0);
1980}
1981
1982/*
1983 * From: <lesv@ppvku.ericsson.se> (Lennart Svensson)
1984 */
1985void
1986remotehost(void)
1987{
1988    struct sigaction sa;
1989    struct strbuf hostname = strbuf_INIT;
1990    int fds[2], wait_options, status;
1991    pid_t pid, wait_res;
1992
1993    sa.sa_handler = SIG_DFL; /* Make sure a zombie is created */
1994    sigemptyset(&sa.sa_mask);
1995    sa.sa_flags = 0;
1996    sigaction(SIGCHLD, &sa, NULL);
1997    mypipe(fds);
1998    pid = fork();
1999    if (pid == 0) {
2000	sigset_t set;
2001	xclose(fds[0]);
2002	/* Don't get stuck if the resolver does not work! */
2003	signal(SIGALRM, palarm);
2004	sigemptyset(&set);
2005	sigaddset(&set, SIGALRM);
2006	(void)sigprocmask(SIG_UNBLOCK, &set, NULL);
2007	(void)alarm(2);
2008	getremotehost(fds[1]);
2009	/*NOTREACHED*/
2010    }
2011    xclose(fds[1]);
2012    for (;;) {
2013	char buf[BUFSIZE];
2014	ssize_t res;
2015
2016	res = xread(fds[0], buf, sizeof(buf));
2017	if (res == -1) {
2018	    hostname.len = 0;
2019	    wait_options = WNOHANG;
2020	    goto done;
2021	}
2022	if (res == 0)
2023	    break;
2024	strbuf_appendn(&hostname, buf, res);
2025    }
2026    wait_options = 0;
2027 done:
2028    xclose(fds[0]);
2029    while ((wait_res = waitpid(pid, &status, wait_options)) == -1
2030	   && errno == EINTR)
2031	handle_pending_signals();
2032    cleanup_push(&hostname, strbuf_cleanup);
2033    if (wait_res == pid && WIFEXITED(status) && WEXITSTATUS(status) == 0) {
2034	strbuf_terminate(&hostname);
2035	tsetenv(STRREMOTEHOST, str2short(hostname.s));
2036    }
2037    cleanup_until(&hostname);
2038
2039#ifdef YPBUGS
2040    /* From: casper@fwi.uva.nl (Casper H.S. Dik), for Solaris 2.3 */
2041    fix_yp_bugs();
2042#endif /* YPBUGS */
2043
2044}
2045#endif /* REMOTEHOST */
2046
2047#ifndef WINNT_NATIVE
2048/*
2049 * indicate if a terminal type is defined in terminfo/termcap
2050 * (by default the current term type). This allows ppl to look
2051 * for a working term type automatically in their login scripts
2052 * when using a terminal known as different things on different
2053 * platforms
2054 */
2055void
2056dotermname(Char **v, struct command *c)
2057{
2058    char *termtype;
2059    /*
2060     * Maximum size of a termcap record. We make it twice as large.
2061     */
2062    char termcap_buffer[2048];
2063
2064    USE(c);
2065    /* try to find which entry we should be looking for */
2066    termtype = (v[1] == NULL ? getenv("TERM") : short2str(v[1]));
2067    if (termtype == NULL) {
2068	/* no luck - the user didn't provide one and none is
2069	 * specified in the environment
2070	 */
2071	setcopy(STRstatus, STR1, VAR_READWRITE);
2072	return;
2073    }
2074
2075    /*
2076     * we use the termcap function - if we are using terminfo we
2077     * will end up with it's compatibility function
2078     * terminfo/termcap will be initialized with the new
2079     * type but we don't care because tcsh has cached all the things
2080     * it needs.
2081     */
2082    if (tgetent(termcap_buffer, termtype) == 1) {
2083	xprintf("%s\n", termtype);
2084	setcopy(STRstatus, STR0, VAR_READWRITE);
2085    } else
2086	setcopy(STRstatus, STR1, VAR_READWRITE);
2087}
2088#endif /* WINNT_NATIVE */
2089