1316958Sdchagin/* $Header: /p/tcsh/cvsroot/tcsh/sh.func.c,v 3.176 2016/10/18 17:26:42 christos Exp $ */
259243Sobrien/*
359243Sobrien * sh.func.c: csh builtin functions
459243Sobrien */
559243Sobrien/*-
659243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
759243Sobrien * All rights reserved.
859243Sobrien *
959243Sobrien * Redistribution and use in source and binary forms, with or without
1059243Sobrien * modification, are permitted provided that the following conditions
1159243Sobrien * are met:
1259243Sobrien * 1. Redistributions of source code must retain the above copyright
1359243Sobrien *    notice, this list of conditions and the following disclaimer.
1459243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1559243Sobrien *    notice, this list of conditions and the following disclaimer in the
1659243Sobrien *    documentation and/or other materials provided with the distribution.
17100616Smp * 3. Neither the name of the University nor the names of its contributors
1859243Sobrien *    may be used to endorse or promote products derived from this software
1959243Sobrien *    without specific prior written permission.
2059243Sobrien *
2159243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2259243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2359243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2459243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2559243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2659243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2759243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2859243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2959243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3059243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3159243Sobrien * SUCH DAMAGE.
3259243Sobrien */
3359243Sobrien#include "sh.h"
3459243Sobrien
35316958SdchaginRCSID("$tcsh: sh.func.c,v 3.176 2016/10/18 17:26:42 christos Exp $")
3659243Sobrien
3759243Sobrien#include "ed.h"
3859243Sobrien#include "tw.h"
3959243Sobrien#include "tc.h"
4069408Sache#ifdef WINNT_NATIVE
4159243Sobrien#include "nt.const.h"
4269408Sache#endif /* WINNT_NATIVE */
4359243Sobrien
44231990Smp#if defined (NLS_CATALOGS) && defined(HAVE_ICONV)
45145479Smpstatic iconv_t catgets_iconv; /* Or (iconv_t)-1 */
46145479Smp#endif
47145479Smp
4859243Sobrien/*
4959243Sobrien * C shell
5059243Sobrien */
5159243Sobrien
52145479Smpextern int MapsAreInited;
53145479Smpextern int NLSMapsAreInited;
54145479Smpextern int GotTermCaps;
5559243Sobrien
5659243Sobrienstatic int zlast = -1;
5759243Sobrien
58167465Smpstatic	void	islogin		(void);
59167465Smpstatic	void	preread		(void);
60167465Smpstatic	void	doagain		(void);
61167465Smpstatic  const char *isrchx	(int);
62167465Smpstatic	void	search		(int, int, Char *);
63167465Smpstatic	int	getword		(struct Strbuf *);
64195609Smpstatic	struct wordent	*histgetword	(struct wordent *);
65167465Smpstatic	void	toend		(void);
66167465Smpstatic	void	xecho		(int, Char **);
67167465Smpstatic	int	islocale_var	(Char *);
68167465Smpstatic	void	wpfree		(struct whyle *);
6959243Sobrien
70167465Smpconst struct biltins *
71167465Smpisbfunc(struct command *t)
7259243Sobrien{
73145479Smp    Char *cp = t->t_dcom[0];
74167465Smp    const struct biltins *bp, *bp1, *bp2;
7559243Sobrien    static struct biltins label = {"", dozip, 0, 0};
7659243Sobrien    static struct biltins foregnd = {"%job", dofg1, 0, 0};
7759243Sobrien    static struct biltins backgnd = {"%job &", dobg1, 0, 0};
7859243Sobrien
7959243Sobrien    /*
8059243Sobrien     * We never match a builtin that has quoted the first
8159243Sobrien     * character; this has been the traditional way to escape
8259243Sobrien     * builtin commands.
8359243Sobrien     */
8459243Sobrien    if (*cp & QUOTE)
8559243Sobrien	return NULL;
8659243Sobrien
8759243Sobrien    if (*cp != ':' && lastchr(cp) == ':') {
8859243Sobrien	label.bname = short2str(cp);
8959243Sobrien	return (&label);
9059243Sobrien    }
9159243Sobrien    if (*cp == '%') {
9259243Sobrien	if (t->t_dflg & F_AMPERSAND) {
9359243Sobrien	    t->t_dflg &= ~F_AMPERSAND;
9459243Sobrien	    backgnd.bname = short2str(cp);
9559243Sobrien	    return (&backgnd);
9659243Sobrien	}
9759243Sobrien	foregnd.bname = short2str(cp);
9859243Sobrien	return (&foregnd);
9959243Sobrien    }
10059243Sobrien#ifdef WARP
10159243Sobrien    /*
10259243Sobrien     * This is a perhaps kludgy way to determine if the warp builtin is to be
10359243Sobrien     * acknowledged or not.  If checkwarp() fails, then we are to assume that
10459243Sobrien     * the warp command is invalid, and carry on as we would handle any other
10559243Sobrien     * non-builtin command.         -- JDK 2/4/88
10659243Sobrien     */
10759243Sobrien    if (eq(STRwarp, cp) && !checkwarp()) {
10859243Sobrien	return (0);		/* this builtin disabled */
10959243Sobrien    }
11059243Sobrien#endif /* WARP */
11159243Sobrien    /*
11259243Sobrien     * Binary search Bp1 is the beginning of the current search range. Bp2 is
11359243Sobrien     * one past the end.
11459243Sobrien     */
11559243Sobrien    for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
11659243Sobrien	int i;
11759243Sobrien
11859243Sobrien	bp = bp1 + ((bp2 - bp1) >> 1);
11959243Sobrien	if ((i = ((char) *cp) - *bp->bname) == 0 &&
12059243Sobrien	    (i = StrQcmp(cp, str2short(bp->bname))) == 0)
12159243Sobrien	    return bp;
12259243Sobrien	if (i < 0)
12359243Sobrien	    bp2 = bp;
12459243Sobrien	else
12559243Sobrien	    bp1 = bp + 1;
12659243Sobrien    }
12769408Sache#ifdef WINNT_NATIVE
12859243Sobrien    return nt_check_additional_builtins(cp);
12969408Sache#endif /*WINNT_NATIVE*/
13059243Sobrien    return (0);
13159243Sobrien}
13259243Sobrien
13359243Sobrienvoid
134167465Smpfunc(struct command *t, const struct biltins *bp)
13559243Sobrien{
13659243Sobrien    int     i;
13759243Sobrien
13859243Sobrien    xechoit(t->t_dcom);
13959243Sobrien    setname(bp->bname);
14059243Sobrien    i = blklen(t->t_dcom) - 1;
14159243Sobrien    if (i < bp->minargs)
14259243Sobrien	stderror(ERR_NAME | ERR_TOOFEW);
14359243Sobrien    if (i > bp->maxargs)
14459243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
14559243Sobrien    (*bp->bfunct) (t->t_dcom, t);
14659243Sobrien}
14759243Sobrien
14859243Sobrien/*ARGSUSED*/
14959243Sobrienvoid
150167465Smpdoonintr(Char **v, struct command *c)
15159243Sobrien{
152145479Smp    Char *cp;
153145479Smp    Char *vv = v[1];
15459243Sobrien
15559243Sobrien    USE(c);
156167465Smp    if (parintr.sa_handler == SIG_IGN)
15759243Sobrien	return;
15859243Sobrien    if (setintr && intty)
15959243Sobrien	stderror(ERR_NAME | ERR_TERMINAL);
16059243Sobrien    cp = gointr;
16159243Sobrien    gointr = 0;
162167465Smp    xfree(cp);
16359243Sobrien    if (vv == 0) {
164167465Smp	if (setintr)
165167465Smp	    sigset_interrupting(SIGINT, queue_pintr);
166167465Smp	else
16759243Sobrien	    (void) signal(SIGINT, SIG_DFL);
16859243Sobrien	gointr = 0;
16959243Sobrien    }
17059243Sobrien    else if (eq((vv = strip(vv)), STRminus)) {
17159243Sobrien	(void) signal(SIGINT, SIG_IGN);
17259243Sobrien	gointr = Strsave(STRminus);
17359243Sobrien    }
17459243Sobrien    else {
17559243Sobrien	gointr = Strsave(vv);
176167465Smp	sigset_interrupting(SIGINT, queue_pintr);
17759243Sobrien    }
17859243Sobrien}
17959243Sobrien
18059243Sobrien/*ARGSUSED*/
18159243Sobrienvoid
182167465Smpdonohup(Char **v, struct command *c)
18359243Sobrien{
18459243Sobrien    USE(c);
18559243Sobrien    USE(v);
18659243Sobrien    if (intty)
18759243Sobrien	stderror(ERR_NAME | ERR_TERMINAL);
18859243Sobrien    if (setintr == 0) {
18959243Sobrien	(void) signal(SIGHUP, SIG_IGN);
190167465Smp	phup_disabled = 1;
19159243Sobrien#ifdef CC
19259243Sobrien	submit(getpid());
19359243Sobrien#endif /* CC */
19459243Sobrien    }
19559243Sobrien}
19659243Sobrien
19759243Sobrien/*ARGSUSED*/
19859243Sobrienvoid
199167465Smpdohup(Char **v, struct command *c)
20059243Sobrien{
20159243Sobrien    USE(c);
20259243Sobrien    USE(v);
20359243Sobrien    if (intty)
20459243Sobrien	stderror(ERR_NAME | ERR_TERMINAL);
20559243Sobrien    if (setintr == 0)
20659243Sobrien	(void) signal(SIGHUP, SIG_DFL);
20759243Sobrien}
20859243Sobrien
20959243Sobrien
21059243Sobrien/*ARGSUSED*/
21159243Sobrienvoid
212167465Smpdozip(Char **v, struct command *c)
21359243Sobrien{
21459243Sobrien    USE(c);
21559243Sobrien    USE(v);
21659243Sobrien}
21759243Sobrien
21859243Sobrien/*ARGSUSED*/
21959243Sobrienvoid
220167465Smpdofiletest(Char **v, struct command *c)
22159243Sobrien{
222167465Smp    Char **globbed, **fileptr, *ftest, *res;
22359243Sobrien
224145479Smp    USE(c);
22559243Sobrien    if (*(ftest = *++v) != '-')
22659243Sobrien	stderror(ERR_NAME | ERR_FILEINQ);
22759243Sobrien    ++v;
22859243Sobrien
229167465Smp    v = glob_all_or_error(v);
230167465Smp    globbed = v;
231167465Smp    cleanup_push(globbed, blk_cleanup);
23259243Sobrien
23359243Sobrien    while (*(fileptr = v++) != '\0') {
234167465Smp	res = filetest(ftest, &fileptr, 0);
235167465Smp	cleanup_push(res, xfree);
236167465Smp	xprintf("%S", res);
237167465Smp	cleanup_until(res);
23859243Sobrien	if (*v)
23959243Sobrien	    xprintf(" ");
24059243Sobrien    }
24159243Sobrien    xprintf("\n");
24259243Sobrien
243167465Smp    cleanup_until(globbed);
24459243Sobrien}
24559243Sobrien
24659243Sobrienvoid
247167465Smpprvars(void)
24859243Sobrien{
24959243Sobrien    plist(&shvhed, VAR_ALL);
25059243Sobrien}
25159243Sobrien
25259243Sobrien/*ARGSUSED*/
25359243Sobrienvoid
254167465Smpdoalias(Char **v, struct command *c)
25559243Sobrien{
256145479Smp    struct varent *vp;
257145479Smp    Char *p;
25859243Sobrien
25959243Sobrien    USE(c);
26059243Sobrien    v++;
26159243Sobrien    p = *v++;
26259243Sobrien    if (p == 0)
26359243Sobrien	plist(&aliases, VAR_ALL);
26459243Sobrien    else if (*v == 0) {
26559243Sobrien	vp = adrof1(strip(p), &aliases);
266100616Smp	if (vp && vp->vec)
26759243Sobrien	    blkpr(vp->vec), xputchar('\n');
26859243Sobrien    }
26959243Sobrien    else {
27059243Sobrien	if (eq(p, STRalias) || eq(p, STRunalias)) {
27159243Sobrien	    setname(short2str(p));
27259243Sobrien	    stderror(ERR_NAME | ERR_DANGER);
27359243Sobrien	}
27459243Sobrien	set1(strip(p), saveblk(v), &aliases, VAR_READWRITE);
27559243Sobrien	tw_cmd_free();
27659243Sobrien    }
27759243Sobrien}
27859243Sobrien
27959243Sobrien/*ARGSUSED*/
28059243Sobrienvoid
281167465Smpunalias(Char **v, struct command *c)
28259243Sobrien{
28359243Sobrien    USE(c);
28459243Sobrien    unset1(v, &aliases);
28559243Sobrien    tw_cmd_free();
28659243Sobrien}
28759243Sobrien
28859243Sobrien/*ARGSUSED*/
28959243Sobrienvoid
290167465Smpdologout(Char **v, struct command *c)
29159243Sobrien{
29259243Sobrien    USE(c);
29359243Sobrien    USE(v);
29459243Sobrien    islogin();
29559243Sobrien    goodbye(NULL, NULL);
29659243Sobrien}
29759243Sobrien
29859243Sobrien/*ARGSUSED*/
29959243Sobrienvoid
300167465Smpdologin(Char **v, struct command *c)
30159243Sobrien{
302131962Smp#ifdef WINNT_NATIVE
30359243Sobrien    USE(c);
30459243Sobrien    USE(v);
30569408Sache#else /* !WINNT_NATIVE */
306131962Smp    char **p = short2blk(v);
307167465Smp
308131962Smp    USE(c);
309167465Smp    cleanup_push((Char **)p, blk_cleanup);
31059243Sobrien    islogin();
31159243Sobrien    rechist(NULL, adrof(STRsavehist) != NULL);
312167465Smp    sigaction(SIGTERM, &parterm, NULL);
313131962Smp    (void) execv(_PATH_BIN_LOGIN, p);
314131962Smp    (void) execv(_PATH_USRBIN_LOGIN, p);
315167465Smp    cleanup_until((Char **)p);
31659243Sobrien    untty();
31759243Sobrien    xexit(1);
31869408Sache#endif /* !WINNT_NATIVE */
31959243Sobrien}
32059243Sobrien
32159243Sobrien
32259243Sobrien#ifdef NEWGRP
32359243Sobrien/*ARGSUSED*/
32459243Sobrienvoid
325167465Smpdonewgrp(Char **v, struct command *c)
32659243Sobrien{
32759243Sobrien    char **p;
32859243Sobrien    if (chkstop == 0 && setintr)
32959243Sobrien	panystop(0);
330167465Smp    sigaction(SIGTERM, &parterm, NULL);
33159243Sobrien    p = short2blk(v);
33259243Sobrien    /*
33359243Sobrien     * From Beto Appleton (beto@aixwiz.austin.ibm.com)
33459243Sobrien     * Newgrp can take 2 arguments...
33559243Sobrien     */
33659243Sobrien    (void) execv(_PATH_BIN_NEWGRP, p);
33759243Sobrien    (void) execv(_PATH_USRBIN_NEWGRP, p);
33859243Sobrien    blkfree((Char **) p);
33959243Sobrien    untty();
34059243Sobrien    xexit(1);
34159243Sobrien}
34259243Sobrien#endif /* NEWGRP */
34359243Sobrien
34459243Sobrienstatic void
345167465Smpislogin(void)
34659243Sobrien{
34759243Sobrien    if (chkstop == 0 && setintr)
34859243Sobrien	panystop(0);
34959243Sobrien    if (loginsh)
35059243Sobrien	return;
35159243Sobrien    stderror(ERR_NOTLOGIN);
35259243Sobrien}
35359243Sobrien
35459243Sobrienvoid
355167465Smpdoif(Char **v, struct command *kp)
35659243Sobrien{
357145479Smp    int i;
358145479Smp    Char **vv;
35959243Sobrien
36059243Sobrien    v++;
361167465Smp    i = noexec ? 1 : expr(&v);
36259243Sobrien    vv = v;
36359243Sobrien    if (*vv == NULL)
36459243Sobrien	stderror(ERR_NAME | ERR_EMPTYIF);
36559243Sobrien    if (eq(*vv, STRthen)) {
36659243Sobrien	if (*++vv)
36759243Sobrien	    stderror(ERR_NAME | ERR_IMPRTHEN);
36859243Sobrien	setname(short2str(STRthen));
36959243Sobrien	/*
37059243Sobrien	 * If expression was zero, then scan to else , otherwise just fall into
37159243Sobrien	 * following code.
37259243Sobrien	 */
37359243Sobrien	if (!i)
37459243Sobrien	    search(TC_IF, 0, NULL);
37559243Sobrien	return;
37659243Sobrien    }
37759243Sobrien    /*
37859243Sobrien     * Simple command attached to this if. Left shift the node in this tree,
37959243Sobrien     * munging it so we can reexecute it.
38059243Sobrien     */
38159243Sobrien    if (i) {
38259243Sobrien	lshift(kp->t_dcom, vv - kp->t_dcom);
38359243Sobrien	reexecute(kp);
38459243Sobrien	donefds();
38559243Sobrien    }
38659243Sobrien}
38759243Sobrien
38859243Sobrien/*
38959243Sobrien * Reexecute a command, being careful not
39059243Sobrien * to redo i/o redirection, which is already set up.
39159243Sobrien */
39259243Sobrienvoid
393167465Smpreexecute(struct command *kp)
39459243Sobrien{
39559243Sobrien    kp->t_dflg &= F_SAVE;
39659243Sobrien    kp->t_dflg |= F_REPEAT;
39759243Sobrien    /*
39859243Sobrien     * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
39959243Sobrien     * pgrp's as the jobs would then have no way to get the tty (we can't give
40059243Sobrien     * it to them, and our parent wouldn't know their pgrp, etc.
40159243Sobrien     */
402100616Smp    execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
40359243Sobrien}
40459243Sobrien
40559243Sobrien/*ARGSUSED*/
40659243Sobrienvoid
407167465Smpdoelse (Char **v, struct command *c)
40859243Sobrien{
40959243Sobrien    USE(c);
41059243Sobrien    USE(v);
411167465Smp    if (!noexec)
412167465Smp	search(TC_ELSE, 0, NULL);
41359243Sobrien}
41459243Sobrien
41559243Sobrien/*ARGSUSED*/
41659243Sobrienvoid
417167465Smpdogoto(Char **v, struct command *c)
41859243Sobrien{
41959243Sobrien    Char   *lp;
42059243Sobrien
42159243Sobrien    USE(c);
422167465Smp    lp = globone(v[1], G_ERROR);
423167465Smp    cleanup_push(lp, xfree);
424167465Smp    if (!noexec)
425167465Smp	gotolab(lp);
426167465Smp    cleanup_until(lp);
42759243Sobrien}
42859243Sobrien
42959243Sobrienvoid
430167465Smpgotolab(Char *lab)
43159243Sobrien{
432145479Smp    struct whyle *wp;
43359243Sobrien    /*
43459243Sobrien     * While we still can, locate any unknown ends of existing loops. This
43559243Sobrien     * obscure code is the WORST result of the fact that we don't really parse.
43659243Sobrien     */
43759243Sobrien    zlast = TC_GOTO;
43859243Sobrien    for (wp = whyles; wp; wp = wp->w_next)
43969408Sache	if (wp->w_end.type == TCSH_F_SEEK && wp->w_end.f_seek == 0) {
44059243Sobrien	    search(TC_BREAK, 0, NULL);
44159243Sobrien	    btell(&wp->w_end);
44259243Sobrien	}
44359243Sobrien	else {
44459243Sobrien	    bseek(&wp->w_end);
44559243Sobrien	}
44659243Sobrien    search(TC_GOTO, 0, lab);
44759243Sobrien    /*
44859243Sobrien     * Eliminate loops which were exited.
44959243Sobrien     */
45059243Sobrien    wfree();
45159243Sobrien}
45259243Sobrien
45359243Sobrien/*ARGSUSED*/
45459243Sobrienvoid
455167465Smpdoswitch(Char **v, struct command *c)
45659243Sobrien{
457145479Smp    Char *cp, *lp;
45859243Sobrien
45959243Sobrien    USE(c);
46059243Sobrien    v++;
46159243Sobrien    if (!*v || *(*v++) != '(')
46259243Sobrien	stderror(ERR_SYNTAX);
46359243Sobrien    cp = **v == ')' ? STRNULL : *v++;
46459243Sobrien    if (*(*v++) != ')')
46559243Sobrien	v--;
46659243Sobrien    if (*v)
46759243Sobrien	stderror(ERR_SYNTAX);
468167465Smp    lp = globone(cp, G_ERROR);
469167465Smp    cleanup_push(lp, xfree);
470167465Smp    if (!noexec)
471167465Smp	search(TC_SWITCH, 0, lp);
472167465Smp    cleanup_until(lp);
47359243Sobrien}
47459243Sobrien
47559243Sobrien/*ARGSUSED*/
47659243Sobrienvoid
477167465Smpdobreak(Char **v, struct command *c)
47859243Sobrien{
47959243Sobrien    USE(v);
48059243Sobrien    USE(c);
481167465Smp    if (whyles == NULL)
482167465Smp	stderror(ERR_NAME | ERR_NOTWHILE);
483167465Smp    if (!noexec)
48459243Sobrien	toend();
48559243Sobrien}
48659243Sobrien
48759243Sobrien/*ARGSUSED*/
48859243Sobrienvoid
489167465Smpdoexit(Char **v, struct command *c)
49059243Sobrien{
49159243Sobrien    USE(c);
49259243Sobrien
49359243Sobrien    if (chkstop == 0 && (intty || intact) && evalvec == 0)
49459243Sobrien	panystop(0);
49559243Sobrien    /*
49659243Sobrien     * Don't DEMAND parentheses here either.
49759243Sobrien     */
49859243Sobrien    v++;
49959243Sobrien    if (*v) {
500167465Smp	setv(STRstatus, putn(expr(&v)), VAR_READWRITE);
50159243Sobrien	if (*v)
50259243Sobrien	    stderror(ERR_NAME | ERR_EXPRESSION);
50359243Sobrien    }
50459243Sobrien    btoeof();
50559243Sobrien#if 0
50659243Sobrien    if (intty)
50759243Sobrien#endif
50859243Sobrien    /* Always close, why only on ttys? */
509167465Smp	xclose(SHIN);
51059243Sobrien}
51159243Sobrien
51259243Sobrien/*ARGSUSED*/
51359243Sobrienvoid
514167465Smpdoforeach(Char **v, struct command *c)
51559243Sobrien{
516145479Smp    Char *cp, *sp;
517145479Smp    struct whyle *nwp;
518167465Smp    int gflag;
51959243Sobrien
52059243Sobrien    USE(c);
52159243Sobrien    v++;
522231990Smp    cp = sp = strip(*v);
523231990Smp    if (!letter(*cp))
52459243Sobrien	stderror(ERR_NAME | ERR_VARBEGIN);
525231990Smp    do {
52659243Sobrien	cp++;
527231990Smp    } while (alnum(*cp));
528231990Smp    if (*cp != '\0')
52959243Sobrien	stderror(ERR_NAME | ERR_VARALNUM);
53059243Sobrien    cp = *v++;
53159243Sobrien    if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
53259243Sobrien	stderror(ERR_NAME | ERR_NOPAREN);
53359243Sobrien    v++;
534167465Smp    gflag = tglob(v);
53559243Sobrien    if (gflag) {
536167465Smp	v = globall(v, gflag);
537167465Smp	if (v == 0 && !noexec)
53859243Sobrien	    stderror(ERR_NAME | ERR_NOMATCH);
53959243Sobrien    }
54059243Sobrien    else {
541167465Smp	v = saveblk(v);
54259243Sobrien	trim(v);
54359243Sobrien    }
544167465Smp    nwp = xcalloc(1, sizeof *nwp);
54559243Sobrien    nwp->w_fe = nwp->w_fe0 = v;
54659243Sobrien    btell(&nwp->w_start);
54759243Sobrien    nwp->w_fename = Strsave(cp);
54859243Sobrien    nwp->w_next = whyles;
54969408Sache    nwp->w_end.type = TCSH_F_SEEK;
55059243Sobrien    whyles = nwp;
55159243Sobrien    /*
55259243Sobrien     * Pre-read the loop so as to be more comprehensible to a terminal user.
55359243Sobrien     */
55459243Sobrien    zlast = TC_FOREACH;
55559243Sobrien    if (intty)
55659243Sobrien	preread();
557167465Smp    if (!noexec)
558167465Smp	doagain();
55959243Sobrien}
56059243Sobrien
56159243Sobrien/*ARGSUSED*/
56259243Sobrienvoid
563167465Smpdowhile(Char **v, struct command *c)
56459243Sobrien{
565145479Smp    int status;
566145479Smp    int again = whyles != 0 &&
56759243Sobrien			  SEEKEQ(&whyles->w_start, &lineloc) &&
56859243Sobrien			  whyles->w_fename == 0;
56959243Sobrien
57059243Sobrien    USE(c);
57159243Sobrien    v++;
57259243Sobrien    /*
57359243Sobrien     * Implement prereading here also, taking care not to evaluate the
57459243Sobrien     * expression before the loop has been read up from a terminal.
57559243Sobrien     */
576167465Smp    if (noexec)
577167465Smp	status = 0;
578167465Smp    else if (intty && !again)
57959243Sobrien	status = !exp0(&v, 1);
58059243Sobrien    else
58159243Sobrien	status = !expr(&v);
582167465Smp    if (*v && !noexec)
58359243Sobrien	stderror(ERR_NAME | ERR_EXPRESSION);
58459243Sobrien    if (!again) {
585167465Smp	struct whyle *nwp = xcalloc(1, sizeof(*nwp));
58659243Sobrien
58759243Sobrien	nwp->w_start = lineloc;
58869408Sache	nwp->w_end.type = TCSH_F_SEEK;
58959243Sobrien	nwp->w_end.f_seek = 0;
590231990Smp	nwp->w_end.a_seek = 0;
59159243Sobrien	nwp->w_next = whyles;
59259243Sobrien	whyles = nwp;
59359243Sobrien	zlast = TC_WHILE;
59459243Sobrien	if (intty) {
59559243Sobrien	    /*
59659243Sobrien	     * The tty preread
59759243Sobrien	     */
59859243Sobrien	    preread();
59959243Sobrien	    doagain();
60059243Sobrien	    return;
60159243Sobrien	}
60259243Sobrien    }
60359243Sobrien    if (status)
60459243Sobrien	/* We ain't gonna loop no more, no more! */
60559243Sobrien	toend();
60659243Sobrien}
60759243Sobrien
60859243Sobrienstatic void
609167465Smppreread(void)
61059243Sobrien{
611167465Smp    int old_pintr_disabled;
612167465Smp
61369408Sache    whyles->w_end.type = TCSH_I_SEEK;
61459243Sobrien    if (setintr)
615167465Smp	pintr_push_enable(&old_pintr_disabled);
61659243Sobrien    search(TC_BREAK, 0, NULL);		/* read the expression in */
61759243Sobrien    if (setintr)
618167465Smp	cleanup_until(&old_pintr_disabled);
61959243Sobrien    btell(&whyles->w_end);
62059243Sobrien}
62159243Sobrien
62259243Sobrien/*ARGSUSED*/
62359243Sobrienvoid
624167465Smpdoend(Char **v, struct command *c)
62559243Sobrien{
62659243Sobrien    USE(v);
62759243Sobrien    USE(c);
62859243Sobrien    if (!whyles)
62959243Sobrien	stderror(ERR_NAME | ERR_NOTWHILE);
63059243Sobrien    btell(&whyles->w_end);
631167465Smp    if (!noexec)
632167465Smp	doagain();
63359243Sobrien}
63459243Sobrien
63559243Sobrien/*ARGSUSED*/
63659243Sobrienvoid
637167465Smpdocontin(Char **v, struct command *c)
63859243Sobrien{
63959243Sobrien    USE(v);
64059243Sobrien    USE(c);
64159243Sobrien    if (!whyles)
64259243Sobrien	stderror(ERR_NAME | ERR_NOTWHILE);
643167465Smp    if (!noexec)
644167465Smp	doagain();
64559243Sobrien}
64659243Sobrien
64759243Sobrienstatic void
648167465Smpdoagain(void)
64959243Sobrien{
65059243Sobrien    /* Repeating a while is simple */
65159243Sobrien    if (whyles->w_fename == 0) {
65259243Sobrien	bseek(&whyles->w_start);
65359243Sobrien	return;
65459243Sobrien    }
65559243Sobrien    /*
65659243Sobrien     * The foreach variable list actually has a spurious word ")" at the end of
65759243Sobrien     * the w_fe list.  Thus we are at the of the list if one word beyond this
65859243Sobrien     * is 0.
65959243Sobrien     */
66059243Sobrien    if (!whyles->w_fe[1]) {
66159243Sobrien	dobreak(NULL, NULL);
66259243Sobrien	return;
66359243Sobrien    }
664167465Smp    setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE);
66559243Sobrien    bseek(&whyles->w_start);
66659243Sobrien}
66759243Sobrien
66859243Sobrienvoid
669167465Smpdorepeat(Char **v, struct command *kp)
67059243Sobrien{
671100616Smp    int i = 1;
67259243Sobrien
673100616Smp    do {
674100616Smp	i *= getn(v[1]);
675100616Smp	lshift(v, 2);
676100616Smp    } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0);
677167465Smp    if (noexec)
678167465Smp	i = 1;
679100616Smp
680167465Smp    if (setintr) {
681167465Smp	pintr_disabled++;
682167465Smp	cleanup_push(&pintr_disabled, disabled_cleanup);
683167465Smp    }
68459243Sobrien    while (i > 0) {
685167465Smp	if (setintr && pintr_disabled == 1) {
686167465Smp	    cleanup_until(&pintr_disabled);
687167465Smp	    pintr_disabled++;
688167465Smp	    cleanup_push(&pintr_disabled, disabled_cleanup);
689167465Smp	}
69059243Sobrien	reexecute(kp);
69159243Sobrien	--i;
69259243Sobrien    }
693195609Smp    if (setintr && pintr_disabled == 1)
694195609Smp        cleanup_until(&pintr_disabled);
69559243Sobrien    donefds();
69659243Sobrien}
69759243Sobrien
69859243Sobrien/*ARGSUSED*/
69959243Sobrienvoid
700167465Smpdoswbrk(Char **v, struct command *c)
70159243Sobrien{
70259243Sobrien    USE(v);
70359243Sobrien    USE(c);
704167465Smp    if (!noexec)
705167465Smp	search(TC_BRKSW, 0, NULL);
70659243Sobrien}
70759243Sobrien
70859243Sobrienint
709167465Smpsrchx(Char *cp)
71059243Sobrien{
71159243Sobrien    struct srch *sp, *sp1, *sp2;
71259243Sobrien    int i;
71359243Sobrien
71459243Sobrien    /*
71559243Sobrien     * Ignore keywords inside heredocs
71659243Sobrien     */
71759243Sobrien    if (inheredoc)
71859243Sobrien	return -1;
71959243Sobrien
72059243Sobrien    /*
72159243Sobrien     * Binary search Sp1 is the beginning of the current search range. Sp2 is
72259243Sobrien     * one past the end.
72359243Sobrien     */
72459243Sobrien    for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
72559243Sobrien	sp = sp1 + ((sp2 - sp1) >> 1);
72659243Sobrien	if ((i = *cp - *sp->s_name) == 0 &&
72759243Sobrien	    (i = Strcmp(cp, str2short(sp->s_name))) == 0)
72859243Sobrien	    return sp->s_value;
72959243Sobrien	if (i < 0)
73059243Sobrien	    sp2 = sp;
73159243Sobrien	else
73259243Sobrien	    sp1 = sp + 1;
73359243Sobrien    }
73459243Sobrien    return (-1);
73559243Sobrien}
73659243Sobrien
737145479Smpstatic const char *
738167465Smpisrchx(int n)
73959243Sobrien{
740145479Smp    struct srch *sp, *sp2;
74159243Sobrien
74259243Sobrien    for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++)
74359243Sobrien	if (sp->s_value == n)
74459243Sobrien	    return (sp->s_name);
74559243Sobrien    return ("");
74659243Sobrien}
74759243Sobrien
74859243Sobrien
749167465Smpstatic int Stype;
75059243Sobrienstatic Char *Sgoal;
75159243Sobrien
75259243Sobrienstatic void
753167465Smpsearch(int type, int level, Char *goal)
75459243Sobrien{
755167465Smp    struct Strbuf word = Strbuf_INIT;
756145479Smp    Char *cp;
757131962Smp    struct whyle *wp;
758131962Smp    int wlevel = 0;
759195609Smp    struct wordent *histent = NULL, *ohistent = NULL;
76059243Sobrien
761167465Smp    Stype = type;
76259243Sobrien    Sgoal = goal;
76359243Sobrien    if (type == TC_GOTO) {
76459243Sobrien	struct Ain a;
76569408Sache	a.type = TCSH_F_SEEK;
76659243Sobrien	a.f_seek = 0;
767231990Smp	a.a_seek = 0;
76859243Sobrien	bseek(&a);
76959243Sobrien    }
770167465Smp    cleanup_push(&word, Strbuf_cleanup);
77159243Sobrien    do {
772195609Smp
773195609Smp	if (intty) {
774195609Smp	    histent = xmalloc(sizeof(*histent));
775195609Smp	    ohistent = xmalloc(sizeof(*histent));
776195609Smp	    ohistent->word = STRNULL;
777195609Smp	    ohistent->next = histent;
778195609Smp	    histent->prev = ohistent;
779195609Smp	}
780195609Smp
78169408Sache	if (intty && fseekp == feobp && aret == TCSH_F_SEEK)
78259243Sobrien	    printprompt(1, isrchx(type == TC_BREAK ? zlast : type));
78359243Sobrien	/* xprintf("? "), flush(); */
784167465Smp	(void) getword(&word);
785167465Smp	Strbuf_terminate(&word);
786195609Smp
787195609Smp	if (intty && Strlen(word.s) > 0) {
788195609Smp	    histent->word = Strsave(word.s);
789195609Smp	    histent->next = xmalloc(sizeof(*histent));
790195609Smp	    histent->next->prev = histent;
791195609Smp	    histent = histent->next;
792195609Smp	}
793195609Smp
794167465Smp	switch (srchx(word.s)) {
79559243Sobrien
79659243Sobrien	case TC_ELSE:
79759243Sobrien	    if (level == 0 && type == TC_IF)
798167465Smp		goto end;
79959243Sobrien	    break;
80059243Sobrien
80159243Sobrien	case TC_IF:
802316958Sdchagin	    while (getword(&word)) {
803316958Sdchagin		if (intty) {
804316958Sdchagin		    histent->word = Strsave(word.s);
805316958Sdchagin		    histent->next = xmalloc(sizeof(*histent));
806316958Sdchagin		    histent->next->prev = histent;
807316958Sdchagin		    histent = histent->next;
808316958Sdchagin		}
80959243Sobrien		continue;
810316958Sdchagin	    }
811316958Sdchagin
81259243Sobrien	    if ((type == TC_IF || type == TC_ELSE) &&
813167465Smp		eq(word.s, STRthen))
81459243Sobrien		level++;
81559243Sobrien	    break;
81659243Sobrien
81759243Sobrien	case TC_ENDIF:
81859243Sobrien	    if (type == TC_IF || type == TC_ELSE)
81959243Sobrien		level--;
82059243Sobrien	    break;
82159243Sobrien
82259243Sobrien	case TC_FOREACH:
82359243Sobrien	case TC_WHILE:
824131962Smp	    wlevel++;
82559243Sobrien	    if (type == TC_BREAK)
82659243Sobrien		level++;
82759243Sobrien	    break;
82859243Sobrien
82959243Sobrien	case TC_END:
830131962Smp	    if (type == TC_BRKSW) {
831131962Smp		if (wlevel == 0) {
832131962Smp		    wp = whyles;
833131962Smp		    if (wp) {
834131962Smp			    whyles = wp->w_next;
835131962Smp			    wpfree(wp);
836131962Smp		    }
837131962Smp		}
838131962Smp	    }
83959243Sobrien	    if (type == TC_BREAK)
84059243Sobrien		level--;
841131962Smp	    wlevel--;
84259243Sobrien	    break;
84359243Sobrien
84459243Sobrien	case TC_SWITCH:
84559243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
84659243Sobrien		level++;
84759243Sobrien	    break;
84859243Sobrien
84959243Sobrien	case TC_ENDSW:
85059243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
85159243Sobrien		level--;
85259243Sobrien	    break;
85359243Sobrien
85459243Sobrien	case TC_LABEL:
855167465Smp	    if (type == TC_GOTO && getword(&word) && eq(word.s, goal))
85659243Sobrien		level = -1;
85759243Sobrien	    break;
85859243Sobrien
85959243Sobrien	default:
86059243Sobrien	    if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
86159243Sobrien		break;
862167465Smp	    if (word.len == 0 || word.s[word.len - 1] != ':')
86359243Sobrien		break;
864167465Smp	    word.s[--word.len] = 0;
865167465Smp	    if ((type == TC_GOTO && eq(word.s, goal)) ||
866167465Smp		(type == TC_SWITCH && eq(word.s, STRdefault)))
86759243Sobrien		level = -1;
86859243Sobrien	    break;
86959243Sobrien
87059243Sobrien	case TC_CASE:
87159243Sobrien	    if (type != TC_SWITCH || level != 0)
87259243Sobrien		break;
873167465Smp	    (void) getword(&word);
874167465Smp	    if (word.len != 0 && word.s[word.len - 1] == ':')
875167465Smp		word.s[--word.len] = 0;
876167465Smp	    cp = strip(Dfix1(word.s));
877167465Smp	    cleanup_push(cp, xfree);
87859243Sobrien	    if (Gmatch(goal, cp))
87959243Sobrien		level = -1;
880167465Smp	    cleanup_until(cp);
88159243Sobrien	    break;
88259243Sobrien
88359243Sobrien	case TC_DEFAULT:
88459243Sobrien	    if (type == TC_SWITCH && level == 0)
88559243Sobrien		level = -1;
88659243Sobrien	    break;
88759243Sobrien	}
888195609Smp	if (intty) {
889195609Smp	    ohistent->prev = histgetword(histent);
890195609Smp	    ohistent->prev->next = ohistent;
891195609Smp	    savehist(ohistent, 0);
892195609Smp	    freelex(ohistent);
893195609Smp	    xfree(ohistent);
894195609Smp	} else
895195609Smp	    (void) getword(NULL);
89659243Sobrien    } while (level >= 0);
897167465Smp end:
898167465Smp    cleanup_until(&word);
89959243Sobrien}
90059243Sobrien
901195609Smpstatic struct wordent *
902195609Smphistgetword(struct wordent *histent)
903195609Smp{
904316958Sdchagin    int first;
905195609Smp    eChar c, d;
906195609Smp    int e;
907195609Smp    struct Strbuf *tmp;
908195609Smp    tmp = xmalloc(sizeof(*tmp));
909195609Smp    tmp->size = 0;
910195609Smp    tmp->s = NULL;
911195609Smp    c = readc(1);
912195609Smp    d = 0;
913195609Smp    e = 0;
914195609Smp    for (;;) {
915195609Smp	tmp->len = 0;
916195609Smp	Strbuf_terminate (tmp);
917195609Smp	while (c == ' ' || c == '\t')
918195609Smp	    c = readc(1);
919195609Smp	if (c == '#')
920195609Smp	    do
921195609Smp		c = readc(1);
922195609Smp	    while (c != CHAR_ERR && c != '\n');
923195609Smp	if (c == CHAR_ERR)
924195609Smp	    goto past;
925195609Smp	if (c == '\n')
926195609Smp	    goto nl;
927195609Smp	unreadc(c);
928195609Smp	first = 1;
929195609Smp	do {
930195609Smp	    e = (c == '\\');
931195609Smp	    c = readc(1);
932195609Smp	    if (c == '\\' && !e) {
933195609Smp		if ((c = readc(1)) == '\n') {
934195609Smp		    e = 1;
935195609Smp		    c = ' ';
936195609Smp		} else {
937195609Smp		    unreadc(c);
938195609Smp		    c = '\\';
939195609Smp		}
940195609Smp	    }
941195609Smp	    if ((c == '\'' || c == '"') && !e) {
942195609Smp		if (d == 0)
943195609Smp		    d = c;
944195609Smp		else if (d == c)
945195609Smp		    d = 0;
946195609Smp	    }
947195609Smp	    if (c == CHAR_ERR)
948195609Smp		goto past;
949195609Smp
950195609Smp	    Strbuf_append1(tmp, (Char) c);
951195609Smp
952195609Smp	    if (!first && !d && c == '(' && !e) {
953195609Smp		break;
954195609Smp	    }
955195609Smp	    first = 0;
956195609Smp	} while (d || e || (c != ' ' && c != '\t' && c != '\n'));
957195609Smp	tmp->len--;
958195609Smp	if (tmp->len) {
959195609Smp	    Strbuf_terminate(tmp);
960195609Smp	    histent->word = Strsave(tmp->s);
961195609Smp	    histent->next = xmalloc(sizeof (*histent));
962195609Smp	    histent->next->prev = histent;
963195609Smp	    histent = histent->next;
964195609Smp	}
965195609Smp	if (c == '\n') {
966195609Smp	nl:
967195609Smp	    tmp->len = 0;
968195609Smp	    Strbuf_append1(tmp, (Char) c);
969195609Smp	    Strbuf_terminate(tmp);
970195609Smp	    histent->word = Strsave(tmp->s);
971195609Smp	    return histent;
972195609Smp	}
973195609Smp    }
974195609Smp
975195609Smppast:
976195609Smp    switch (Stype) {
977195609Smp
978195609Smp    case TC_IF:
979195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
980195609Smp	break;
981195609Smp
982195609Smp    case TC_ELSE:
983195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
984195609Smp	break;
985195609Smp
986195609Smp    case TC_BRKSW:
987195609Smp    case TC_SWITCH:
988195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
989195609Smp	break;
990195609Smp
991195609Smp    case TC_BREAK:
992195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "end");
993195609Smp	break;
994195609Smp
995195609Smp    case TC_GOTO:
996195609Smp	setname(short2str(Sgoal));
997195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "label");
998195609Smp	break;
999195609Smp
1000195609Smp    default:
1001195609Smp	break;
1002195609Smp    }
1003195609Smp    /* NOTREACHED */
1004195609Smp    return NULL;
1005195609Smp}
1006195609Smp
100759243Sobrienstatic int
1008167465Smpgetword(struct Strbuf *wp)
100959243Sobrien{
101059243Sobrien    int found = 0, first;
1011145479Smp    eChar c, d;
101259243Sobrien
1013167465Smp    if (wp)
1014167465Smp	wp->len = 0;
101559243Sobrien    c = readc(1);
101659243Sobrien    d = 0;
101759243Sobrien    do {
101859243Sobrien	while (c == ' ' || c == '\t')
101959243Sobrien	    c = readc(1);
102059243Sobrien	if (c == '#')
102159243Sobrien	    do
102259243Sobrien		c = readc(1);
1023145479Smp	    while (c != CHAR_ERR && c != '\n');
1024145479Smp	if (c == CHAR_ERR)
102559243Sobrien	    goto past;
102659243Sobrien	if (c == '\n') {
102759243Sobrien	    if (wp)
102859243Sobrien		break;
102959243Sobrien	    return (0);
103059243Sobrien	}
103159243Sobrien	unreadc(c);
103259243Sobrien	found = 1;
103359243Sobrien	first = 1;
103459243Sobrien	do {
103559243Sobrien	    c = readc(1);
103659243Sobrien	    if (c == '\\' && (c = readc(1)) == '\n')
103759243Sobrien		c = ' ';
103859243Sobrien	    if (c == '\'' || c == '"') {
103959243Sobrien		if (d == 0)
104059243Sobrien		    d = c;
104159243Sobrien		else if (d == c)
104259243Sobrien		    d = 0;
104359243Sobrien	    }
1044145479Smp	    if (c == CHAR_ERR)
104559243Sobrien		goto past;
1046167465Smp	    if (wp)
1047167465Smp		Strbuf_append1(wp, (Char) c);
1048316958Sdchagin	    if (!d && c == ')') {
1049316958Sdchagin		if (!first && wp) {
1050316958Sdchagin		    goto past_word_end;
1051316958Sdchagin		} else {
1052316958Sdchagin		    if (wp) {
1053316958Sdchagin			wp->len = 1;
1054316958Sdchagin			Strbuf_terminate(wp);
1055316958Sdchagin		    }
1056316958Sdchagin		    return found;
1057316958Sdchagin		}
1058316958Sdchagin	    }
105959243Sobrien	    if (!first && !d && c == '(') {
1060167465Smp		if (wp)
1061167465Smp		    goto past_word_end;
106259243Sobrien		else
106359243Sobrien		    break;
106459243Sobrien	    }
106559243Sobrien	    first = 0;
106659243Sobrien	} while ((d || (c != ' ' && c != '\t')) && c != '\n');
106759243Sobrien    } while (wp == 0);
106859243Sobrien
1069167465Smp past_word_end:
107059243Sobrien    unreadc(c);
1071167465Smp    if (found) {
1072167465Smp	wp->len--;
1073167465Smp	Strbuf_terminate(wp);
1074167465Smp    }
107559243Sobrien
107659243Sobrien    return (found);
107759243Sobrien
107859243Sobrienpast:
107959243Sobrien    switch (Stype) {
108059243Sobrien
108159243Sobrien    case TC_IF:
108259243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
108359243Sobrien	break;
108459243Sobrien
108559243Sobrien    case TC_ELSE:
108659243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
108759243Sobrien	break;
108859243Sobrien
108959243Sobrien    case TC_BRKSW:
109059243Sobrien    case TC_SWITCH:
109159243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
109259243Sobrien	break;
109359243Sobrien
109459243Sobrien    case TC_BREAK:
109559243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "end");
109659243Sobrien	break;
109759243Sobrien
109859243Sobrien    case TC_GOTO:
109959243Sobrien	setname(short2str(Sgoal));
110059243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "label");
110159243Sobrien	break;
110259243Sobrien
110359243Sobrien    default:
110459243Sobrien	break;
110559243Sobrien    }
110659243Sobrien    /* NOTREACHED */
110759243Sobrien    return (0);
110859243Sobrien}
110959243Sobrien
111059243Sobrienstatic void
1111167465Smptoend(void)
111259243Sobrien{
111369408Sache    if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) {
111459243Sobrien	search(TC_BREAK, 0, NULL);
111559243Sobrien	btell(&whyles->w_end);
111659243Sobrien	whyles->w_end.f_seek--;
111759243Sobrien    }
111859243Sobrien    else {
111959243Sobrien	bseek(&whyles->w_end);
112059243Sobrien    }
112159243Sobrien    wfree();
112259243Sobrien}
112359243Sobrien
1124131962Smpstatic void
1125167465Smpwpfree(struct whyle *wp)
1126131962Smp{
1127131962Smp	if (wp->w_fe0)
1128131962Smp	    blkfree(wp->w_fe0);
1129167465Smp	xfree(wp->w_fename);
1130167465Smp	xfree(wp);
1131131962Smp}
1132131962Smp
113359243Sobrienvoid
1134167465Smpwfree(void)
113559243Sobrien{
113659243Sobrien    struct Ain    o;
113759243Sobrien    struct whyle *nwp;
113859243Sobrien#ifdef lint
113959243Sobrien    nwp = NULL;	/* sun lint is dumb! */
114059243Sobrien#endif
114159243Sobrien
114259243Sobrien#ifdef FDEBUG
1143167465Smp    static const char foo[] = "IAFE";
114459243Sobrien#endif /* FDEBUG */
114559243Sobrien
114659243Sobrien    btell(&o);
114759243Sobrien
114859243Sobrien#ifdef FDEBUG
1149167465Smp    xprintf("o->type %c o->a_seek %d o->f_seek %d\n",
115059243Sobrien	    foo[o.type + 1], o.a_seek, o.f_seek);
115159243Sobrien#endif /* FDEBUG */
115259243Sobrien
115359243Sobrien    for (; whyles; whyles = nwp) {
1154145479Smp	struct whyle *wp = whyles;
115559243Sobrien	nwp = wp->w_next;
115659243Sobrien
115759243Sobrien#ifdef FDEBUG
1158167465Smp	xprintf("start->type %c start->a_seek %d start->f_seek %d\n",
115959243Sobrien		foo[wp->w_start.type+1],
116059243Sobrien		wp->w_start.a_seek, wp->w_start.f_seek);
1161167465Smp	xprintf("end->type %c end->a_seek %d end->f_seek %d\n",
116259243Sobrien		foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
116359243Sobrien#endif /* FDEBUG */
116459243Sobrien
116559243Sobrien	/*
116659243Sobrien	 * XXX: We free loops that have different seek types.
116759243Sobrien	 */
116869408Sache	if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type &&
116959243Sobrien	    wp->w_start.type == o.type) {
117069408Sache	    if (wp->w_end.type == TCSH_F_SEEK) {
117159243Sobrien		if (o.f_seek >= wp->w_start.f_seek &&
117259243Sobrien		    (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
117359243Sobrien		    break;
117459243Sobrien	    }
117559243Sobrien	    else {
117659243Sobrien		if (o.a_seek >= wp->w_start.a_seek &&
117759243Sobrien		    (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
117859243Sobrien		    break;
117959243Sobrien	    }
118059243Sobrien	}
118159243Sobrien
1182131962Smp	wpfree(wp);
118359243Sobrien    }
118459243Sobrien}
118559243Sobrien
118659243Sobrien/*ARGSUSED*/
118759243Sobrienvoid
1188167465Smpdoecho(Char **v, struct command *c)
118959243Sobrien{
119059243Sobrien    USE(c);
119159243Sobrien    xecho(' ', v);
119259243Sobrien}
119359243Sobrien
119459243Sobrien/*ARGSUSED*/
119559243Sobrienvoid
1196167465Smpdoglob(Char **v, struct command *c)
119759243Sobrien{
119859243Sobrien    USE(c);
119959243Sobrien    xecho(0, v);
120059243Sobrien    flush();
120159243Sobrien}
120259243Sobrien
120359243Sobrienstatic void
1204167465Smpxecho(int sep, Char **v)
120559243Sobrien{
1206167465Smp    Char *cp, **globbed = NULL;
120759243Sobrien    int     nonl = 0;
120859243Sobrien    int	    echo_style = ECHO_STYLE;
120959243Sobrien    struct varent *vp;
121059243Sobrien
121159243Sobrien    if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
121259243Sobrien	vp->vec[0] != NULL) {
121359243Sobrien	if (Strcmp(vp->vec[0], STRbsd) == 0)
121459243Sobrien	    echo_style = BSD_ECHO;
121559243Sobrien	else if (Strcmp(vp->vec[0], STRsysv) == 0)
121659243Sobrien	    echo_style = SYSV_ECHO;
121759243Sobrien	else if (Strcmp(vp->vec[0], STRboth) == 0)
121859243Sobrien	    echo_style = BOTH_ECHO;
121959243Sobrien	else if (Strcmp(vp->vec[0], STRnone) == 0)
122059243Sobrien	    echo_style = NONE_ECHO;
122159243Sobrien    }
122259243Sobrien
122359243Sobrien    v++;
122459243Sobrien    if (*v == 0)
122583098Smp	goto done;
1226167465Smp    if (setintr) {
1227167465Smp	int old_pintr_disabled;
1228167465Smp	pintr_push_enable(&old_pintr_disabled);
1229167465Smp	v = glob_all_or_error(v);
1230167465Smp	cleanup_until(&old_pintr_disabled);
1231167465Smp    } else {
1232167465Smp	v = glob_all_or_error(v);
123359243Sobrien    }
1234167465Smp    globbed = v;
1235167465Smp    if (globbed != NULL)
1236167465Smp	cleanup_push(globbed, blk_cleanup);
123759243Sobrien
123859243Sobrien    if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
123959243Sobrien	nonl++, v++;
124059243Sobrien
124159243Sobrien    while ((cp = *v++) != 0) {
1242145479Smp	Char c;
124359243Sobrien
1244167465Smp	if (setintr) {
1245167465Smp	    int old_pintr_disabled;
1246167465Smp
1247167465Smp	    pintr_push_enable(&old_pintr_disabled);
1248167465Smp	    cleanup_until(&old_pintr_disabled);
1249167465Smp	}
125059243Sobrien	while ((c = *cp++) != 0) {
125159243Sobrien	    if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
125259243Sobrien		switch (c = *cp++) {
125359243Sobrien		case 'a':
125459243Sobrien		    c = '\a';
125559243Sobrien		    break;
125659243Sobrien		case 'b':
125759243Sobrien		    c = '\b';
125859243Sobrien		    break;
125959243Sobrien		case 'c':
126059243Sobrien		    nonl = 1;
126159243Sobrien		    goto done;
126259243Sobrien		case 'e':
126359243Sobrien#if 0			/* Windows does not understand \e */
126459243Sobrien		    c = '\e';
126559243Sobrien#else
1266167465Smp		    c = CTL_ESC('\033');
126759243Sobrien#endif
126859243Sobrien		    break;
126959243Sobrien		case 'f':
127059243Sobrien		    c = '\f';
127159243Sobrien		    break;
127259243Sobrien		case 'n':
127359243Sobrien		    c = '\n';
127459243Sobrien		    break;
127559243Sobrien		case 'r':
127659243Sobrien		    c = '\r';
127759243Sobrien		    break;
127859243Sobrien		case 't':
127959243Sobrien		    c = '\t';
128059243Sobrien		    break;
128159243Sobrien		case 'v':
128259243Sobrien		    c = '\v';
128359243Sobrien		    break;
128459243Sobrien		case '\\':
128559243Sobrien		    c = '\\';
128659243Sobrien		    break;
128759243Sobrien		case '0':
128859243Sobrien		    c = 0;
128959243Sobrien		    if (*cp >= '0' && *cp < '8')
129059243Sobrien			c = c * 8 + *cp++ - '0';
129159243Sobrien		    if (*cp >= '0' && *cp < '8')
129259243Sobrien			c = c * 8 + *cp++ - '0';
129359243Sobrien		    if (*cp >= '0' && *cp < '8')
129459243Sobrien			c = c * 8 + *cp++ - '0';
129559243Sobrien		    break;
129659243Sobrien		case '\0':
129759243Sobrien		    c = '\\';
129859243Sobrien		    cp--;
129959243Sobrien		    break;
130059243Sobrien		default:
130159243Sobrien		    xputchar('\\' | QUOTE);
130259243Sobrien		    break;
130359243Sobrien		}
130459243Sobrien	    }
1305145479Smp	    xputwchar(c | QUOTE);
130659243Sobrien
130759243Sobrien	}
130859243Sobrien	if (*v)
130959243Sobrien	    xputchar(sep | QUOTE);
131059243Sobrien    }
131159243Sobriendone:
131259243Sobrien    if (sep && nonl == 0)
131359243Sobrien	xputchar('\n');
131459243Sobrien    else
131559243Sobrien	flush();
1316167465Smp    if (globbed != NULL)
1317167465Smp	cleanup_until(globbed);
131859243Sobrien}
131959243Sobrien
132059243Sobrien/* check whether an environment variable should invoke 'set_locale()' */
1321145479Smpstatic int
1322167465Smpislocale_var(Char *var)
132359243Sobrien{
132459243Sobrien    static Char *locale_vars[] = {
1325131962Smp	STRLANG,	STRLC_ALL, 	STRLC_CTYPE,	STRLC_NUMERIC,
1326131962Smp	STRLC_TIME,	STRLC_COLLATE,	STRLC_MESSAGES,	STRLC_MONETARY, 0
132759243Sobrien    };
1328145479Smp    Char **v;
132959243Sobrien
133059243Sobrien    for (v = locale_vars; *v; ++v)
133159243Sobrien	if (eq(var, *v))
133259243Sobrien	    return 1;
133359243Sobrien    return 0;
133459243Sobrien}
133559243Sobrien
1336167465Smpstatic void
1337167465Smpxlate_cr_cleanup(void *dummy)
1338167465Smp{
1339167465Smp    USE(dummy);
1340167465Smp    xlate_cr = 0;
1341167465Smp}
1342167465Smp
134359243Sobrien/*ARGSUSED*/
134459243Sobrienvoid
1345167465Smpdoprintenv(Char **v, struct command *c)
134659243Sobrien{
134759243Sobrien    Char   *e;
134859243Sobrien
134959243Sobrien    USE(c);
135059243Sobrien    v++;
135159243Sobrien    if (*v == 0) {
1352145479Smp	Char **ep;
135359243Sobrien
135459243Sobrien	xlate_cr = 1;
1355167465Smp	cleanup_push(&xlate_cr, xlate_cr_cleanup);
1356167465Smp	for (ep = STR_environ; *ep; ep++) {
1357167465Smp	    if (setintr) {
1358167465Smp		int old_pintr_disabled;
1359167465Smp
1360167465Smp		pintr_push_enable(&old_pintr_disabled);
1361167465Smp		cleanup_until(&old_pintr_disabled);
1362167465Smp	    }
136359243Sobrien	    xprintf("%S\n", *ep);
1364167465Smp	}
1365167465Smp	cleanup_until(&xlate_cr);
136659243Sobrien    }
136759243Sobrien    else if ((e = tgetenv(*v)) != NULL) {
1368167465Smp	int old_output_raw;
1369167465Smp
1370167465Smp	old_output_raw = output_raw;
137159243Sobrien	output_raw = 1;
1372167465Smp	cleanup_push(&old_output_raw, output_raw_restore);
137359243Sobrien	xprintf("%S\n", e);
1374167465Smp	cleanup_until(&old_output_raw);
137559243Sobrien    }
137659243Sobrien    else
1377167465Smp	setcopy(STRstatus, STR1, VAR_READWRITE);
137859243Sobrien}
137959243Sobrien
138059243Sobrien/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
138159243Sobrien   (and anything else with a modern compiler) */
138259243Sobrien
138359243Sobrien/*ARGSUSED*/
138459243Sobrienvoid
1385167465Smpdosetenv(Char **v, struct command *c)
138659243Sobrien{
138759243Sobrien    Char   *vp, *lp;
138859243Sobrien
138959243Sobrien    USE(c);
139059243Sobrien    if (*++v == 0) {
139159243Sobrien	doprintenv(--v, 0);
139259243Sobrien	return;
139359243Sobrien    }
139459243Sobrien
139559243Sobrien    vp = *v++;
1396231990Smp    lp = vp;
139759243Sobrien
1398231990Smp    if (!letter(*lp))
1399231990Smp	stderror(ERR_NAME | ERR_VARBEGIN);
1400231990Smp    do {
1401231990Smp	lp++;
1402316958Sdchagin    } while (alnum(*lp) || *lp == '.');
1403231990Smp    if (*lp != '\0')
1404231990Smp	stderror(ERR_NAME | ERR_VARALNUM);
1405231990Smp
140659243Sobrien    if ((lp = *v++) == 0)
140759243Sobrien	lp = STRNULL;
140859243Sobrien
1409167465Smp    lp = globone(lp, G_APPEND);
1410167465Smp    cleanup_push(lp, xfree);
1411167465Smp    tsetenv(vp, lp);
141259243Sobrien    if (eq(vp, STRKPATH)) {
1413167465Smp        importpath(lp);
141459243Sobrien	dohash(NULL, NULL);
1415167465Smp	cleanup_until(lp);
141659243Sobrien	return;
141759243Sobrien    }
141859243Sobrien
141959243Sobrien#ifdef apollo
142059243Sobrien    if (eq(vp, STRSYSTYPE)) {
142159243Sobrien	dohash(NULL, NULL);
1422167465Smp	cleanup_until(lp);
142359243Sobrien	return;
142459243Sobrien    }
142559243Sobrien#endif /* apollo */
142659243Sobrien
142759243Sobrien    /* dspkanji/dspmbyte autosetting */
142859243Sobrien    /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
142959243Sobrien#if defined(DSPMBYTE)
143059243Sobrien    if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) {
143159243Sobrien	autoset_dspmbyte(lp);
143259243Sobrien    }
143359243Sobrien#endif
143459243Sobrien
143559243Sobrien    if (islocale_var(vp)) {
143659243Sobrien#ifdef NLS
143759243Sobrien	int     k;
143859243Sobrien
143959243Sobrien# ifdef SETLOCALEBUG
144059243Sobrien	dont_free = 1;
144159243Sobrien# endif /* SETLOCALEBUG */
144259243Sobrien	(void) setlocale(LC_ALL, "");
144359243Sobrien# ifdef LC_COLLATE
144459243Sobrien	(void) setlocale(LC_COLLATE, "");
144559243Sobrien# endif
1446145479Smp# ifdef LC_CTYPE
1447145479Smp	(void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1448145479Smp# endif /* LC_CTYPE */
1449231990Smp# if defined(AUTOSET_KANJI)
1450231990Smp        autoset_kanji();
1451231990Smp# endif /* AUTOSET_KANJI */
145259243Sobrien# ifdef NLS_CATALOGS
145359243Sobrien#  ifdef LC_MESSAGES
145459243Sobrien	(void) setlocale(LC_MESSAGES, "");
145559243Sobrien#  endif /* LC_MESSAGES */
1456145479Smp	nlsclose();
145759243Sobrien	nlsinit();
145859243Sobrien# endif /* NLS_CATALOGS */
145959243Sobrien# ifdef SETLOCALEBUG
146059243Sobrien	dont_free = 0;
146159243Sobrien# endif /* SETLOCALEBUG */
146259243Sobrien# ifdef STRCOLLBUG
146359243Sobrien	fix_strcoll_bug();
146459243Sobrien# endif /* STRCOLLBUG */
146559243Sobrien	tw_cmd_free();	/* since the collation sequence has changed */
1466167465Smp	for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
146759243Sobrien	    continue;
1468145479Smp	AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
146959243Sobrien#else /* !NLS */
147059243Sobrien	AsciiOnly = 0;
147159243Sobrien#endif /* NLS */
147259243Sobrien	NLSMapsAreInited = 0;
147359243Sobrien	ed_Init();
147459243Sobrien	if (MapsAreInited && !NLSMapsAreInited)
147559243Sobrien	    ed_InitNLSMaps();
1476167465Smp	cleanup_until(lp);
147759243Sobrien	return;
147859243Sobrien    }
147959243Sobrien
1480100616Smp#ifdef NLS_CATALOGS
1481100616Smp    if (eq(vp, STRNLSPATH)) {
1482145479Smp	nlsclose();
1483100616Smp	nlsinit();
1484100616Smp    }
1485100616Smp#endif
1486100616Smp
148759243Sobrien    if (eq(vp, STRNOREBIND)) {
148859243Sobrien	NoNLSRebind = 1;
148959243Sobrien	MapsAreInited = 0;
149059243Sobrien	NLSMapsAreInited = 0;
149159243Sobrien	ed_InitMaps();
1492167465Smp	cleanup_until(lp);
149359243Sobrien	return;
149459243Sobrien    }
149569408Sache#ifdef WINNT_NATIVE
149659243Sobrien    if (eq(vp, STRtcshlang)) {
149759243Sobrien	nlsinit();
1498167465Smp	cleanup_until(lp);
149959243Sobrien	return;
150059243Sobrien    }
150169408Sache#endif /* WINNT_NATIVE */
150259243Sobrien    if (eq(vp, STRKTERM)) {
150359243Sobrien	char *t;
1504167465Smp
1505167465Smp	setv(STRterm, quote(lp), VAR_READWRITE);	/* lp memory used here */
1506167465Smp	cleanup_ignore(lp);
1507167465Smp	cleanup_until(lp);
150859243Sobrien	t = short2str(lp);
150959243Sobrien	if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) {
151059243Sobrien	    editing = 1;
151159243Sobrien	    noediting = 0;
1512167465Smp	    setNS(STRedit);
151359243Sobrien	}
151459243Sobrien	GotTermCaps = 0;
151559243Sobrien	ed_Init();
151659243Sobrien	return;
151759243Sobrien    }
151859243Sobrien
151959243Sobrien    if (eq(vp, STRKHOME)) {
1520167465Smp	Char *canon;
152159243Sobrien	/*
152259243Sobrien	 * convert to canonical pathname (possibly resolving symlinks)
152359243Sobrien	 */
1524167465Smp	canon = dcanon(lp, lp);
1525167465Smp	cleanup_ignore(lp);
1526167465Smp	cleanup_until(lp);
1527167465Smp	cleanup_push(canon, xfree);
1528167465Smp	setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */
1529167465Smp	cleanup_ignore(canon);
1530167465Smp	cleanup_until(canon);
153159243Sobrien
153259243Sobrien	/* fix directory stack for new tilde home */
153359243Sobrien	dtilde();
153459243Sobrien	return;
153559243Sobrien    }
153659243Sobrien
153759243Sobrien    if (eq(vp, STRKSHLVL)) {
1538167465Smp	setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */
1539167465Smp	cleanup_ignore(lp);
1540167465Smp	cleanup_until(lp);
154159243Sobrien	return;
154259243Sobrien    }
154359243Sobrien
154459243Sobrien    if (eq(vp, STRKUSER)) {
1545167465Smp	setv(STRuser, quote(lp), VAR_READWRITE);	/* lp memory used here */
1546167465Smp	cleanup_ignore(lp);
1547167465Smp	cleanup_until(lp);
154859243Sobrien	return;
154959243Sobrien    }
155059243Sobrien
155159243Sobrien    if (eq(vp, STRKGROUP)) {
1552167465Smp	setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */
1553167465Smp	cleanup_ignore(lp);
1554167465Smp	cleanup_until(lp);
155559243Sobrien	return;
155659243Sobrien    }
155759243Sobrien
155859243Sobrien#ifdef COLOR_LS_F
155959243Sobrien    if (eq(vp, STRLS_COLORS)) {
156059243Sobrien        parseLS_COLORS(lp);
1561167465Smp	cleanup_until(lp);
156259243Sobrien	return;
156359243Sobrien    }
1564316958Sdchagin    if (eq(vp, STRLSCOLORS)) {
1565316958Sdchagin        parseLSCOLORS(lp);
1566316958Sdchagin	cleanup_until(lp);
1567316958Sdchagin	return;
1568316958Sdchagin    }
156959243Sobrien#endif /* COLOR_LS_F */
157059243Sobrien
157159243Sobrien#ifdef SIG_WINDOW
157259243Sobrien    /*
157359243Sobrien     * Load/Update $LINES $COLUMNS
157459243Sobrien     */
157559243Sobrien    if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
157659243Sobrien	eq(vp, STRTERMCAP)) {
1577167465Smp	cleanup_until(lp);
157859243Sobrien	check_window_size(1);
157959243Sobrien	return;
158059243Sobrien    }
158159243Sobrien
158259243Sobrien    /*
158359243Sobrien     * Change the size to the one directed by $LINES and $COLUMNS
158459243Sobrien     */
158559243Sobrien    if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
158659243Sobrien#if 0
158759243Sobrien	GotTermCaps = 0;
158859243Sobrien#endif
1589167465Smp	cleanup_until(lp);
159059243Sobrien	ed_Init();
159159243Sobrien	return;
159259243Sobrien    }
159359243Sobrien#endif /* SIG_WINDOW */
1594167465Smp    cleanup_until(lp);
159559243Sobrien}
159659243Sobrien
159759243Sobrien/*ARGSUSED*/
159859243Sobrienvoid
1599167465Smpdounsetenv(Char **v, struct command *c)
160059243Sobrien{
1601167465Smp    Char  **ep, *p, *n, *name;
160259243Sobrien    int     i, maxi;
160359243Sobrien
160459243Sobrien    USE(c);
160559243Sobrien    /*
160659243Sobrien     * Find the longest environment variable
160759243Sobrien     */
160859243Sobrien    for (maxi = 0, ep = STR_environ; *ep; ep++) {
160959243Sobrien	for (i = 0, p = *ep; *p && *p != '='; p++, i++)
161059243Sobrien	    continue;
161159243Sobrien	if (i > maxi)
161259243Sobrien	    maxi = i;
161359243Sobrien    }
161459243Sobrien
1615167465Smp    name = xmalloc((maxi + 1) * sizeof(Char));
1616167465Smp    cleanup_push(name, xfree);
161759243Sobrien
161859243Sobrien    while (++v && *v)
161959243Sobrien	for (maxi = 1; maxi;)
162059243Sobrien	    for (maxi = 0, ep = STR_environ; *ep; ep++) {
162159243Sobrien		for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
162259243Sobrien		    continue;
162359243Sobrien		*n = '\0';
162459243Sobrien		if (!Gmatch(name, *v))
162559243Sobrien		    continue;
162659243Sobrien		maxi = 1;
162759243Sobrien
162859243Sobrien		/* Unset the name. This wasn't being done until
162959243Sobrien		 * later but most of the stuff following won't
163059243Sobrien		 * work (particularly the setlocale() and getenv()
163159243Sobrien		 * stuff) as intended until the name is actually
163259243Sobrien		 * removed. (sg)
163359243Sobrien		 */
163459243Sobrien		Unsetenv(name);
163559243Sobrien
163659243Sobrien		if (eq(name, STRNOREBIND)) {
163759243Sobrien		    NoNLSRebind = 0;
163859243Sobrien		    MapsAreInited = 0;
163959243Sobrien		    NLSMapsAreInited = 0;
164059243Sobrien		    ed_InitMaps();
164159243Sobrien		}
164259243Sobrien#ifdef apollo
164359243Sobrien		else if (eq(name, STRSYSTYPE))
164459243Sobrien		    dohash(NULL, NULL);
164559243Sobrien#endif /* apollo */
164659243Sobrien		else if (islocale_var(name)) {
164759243Sobrien#ifdef NLS
164859243Sobrien		    int     k;
164959243Sobrien
165059243Sobrien# ifdef SETLOCALEBUG
165159243Sobrien		    dont_free = 1;
165259243Sobrien# endif /* SETLOCALEBUG */
165359243Sobrien		    (void) setlocale(LC_ALL, "");
165459243Sobrien# ifdef LC_COLLATE
165559243Sobrien		    (void) setlocale(LC_COLLATE, "");
165659243Sobrien# endif
1657145479Smp# ifdef LC_CTYPE
1658167465Smp		    (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1659145479Smp# endif /* LC_CTYPE */
166059243Sobrien# ifdef NLS_CATALOGS
166159243Sobrien#  ifdef LC_MESSAGES
166259243Sobrien		    (void) setlocale(LC_MESSAGES, "");
166359243Sobrien#  endif /* LC_MESSAGES */
1664145479Smp		    nlsclose();
166559243Sobrien		    nlsinit();
166659243Sobrien# endif /* NLS_CATALOGS */
166759243Sobrien# ifdef SETLOCALEBUG
166859243Sobrien		    dont_free = 0;
166959243Sobrien# endif /* SETLOCALEBUG */
167059243Sobrien# ifdef STRCOLLBUG
167159243Sobrien		    fix_strcoll_bug();
167259243Sobrien# endif /* STRCOLLBUG */
167359243Sobrien		    tw_cmd_free();/* since the collation sequence has changed */
1674167465Smp		    for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
167559243Sobrien			continue;
1676145479Smp		    AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
167759243Sobrien#else /* !NLS */
167859243Sobrien		    AsciiOnly = getenv("LANG") == NULL &&
167959243Sobrien			getenv("LC_CTYPE") == NULL;
168059243Sobrien#endif /* NLS */
168159243Sobrien		    NLSMapsAreInited = 0;
168259243Sobrien		    ed_Init();
168359243Sobrien		    if (MapsAreInited && !NLSMapsAreInited)
168459243Sobrien			ed_InitNLSMaps();
168559243Sobrien
168659243Sobrien		}
168769408Sache#ifdef WINNT_NATIVE
168859243Sobrien		else if (eq(name,(STRtcshlang))) {
168959243Sobrien		    nls_dll_unload();
169059243Sobrien		    nlsinit();
169159243Sobrien		}
169269408Sache#endif /* WINNT_NATIVE */
169359243Sobrien#ifdef COLOR_LS_F
169459243Sobrien		else if (eq(name, STRLS_COLORS))
169559243Sobrien		    parseLS_COLORS(n);
1696316958Sdchagin		else if (eq(name, STRLSCOLORS))
1697316958Sdchagin		    parseLSCOLORS(n);
169859243Sobrien#endif /* COLOR_LS_F */
1699100616Smp#ifdef NLS_CATALOGS
1700100616Smp		else if (eq(name, STRNLSPATH)) {
1701145479Smp		    nlsclose();
1702100616Smp		    nlsinit();
1703100616Smp		}
1704100616Smp#endif
170559243Sobrien		/*
170659243Sobrien		 * start again cause the environment changes
170759243Sobrien		 */
170859243Sobrien		break;
170959243Sobrien	    }
1710167465Smp    cleanup_until(name);
171159243Sobrien}
171259243Sobrien
171359243Sobrienvoid
1714167465Smptsetenv(const Char *name, const Char *val)
171559243Sobrien{
171659243Sobrien#ifdef SETENV_IN_LIB
171759243Sobrien/*
171859243Sobrien * XXX: This does not work right, since tcsh cannot track changes to
171959243Sobrien * the environment this way. (the builtin setenv without arguments does
172059243Sobrien * not print the right stuff neither does unsetenv). This was for Mach,
172159243Sobrien * it is not needed anymore.
172259243Sobrien */
172359243Sobrien#undef setenv
1724167465Smp    char   *cname;
172559243Sobrien
1726167465Smp    if (name == NULL)
172759243Sobrien	return;
1728167465Smp    cname = strsave(short2str(name));
1729167465Smp    setenv(cname, short2str(val), 1);
1730167465Smp    xfree(cname);
173159243Sobrien#else /* !SETENV_IN_LIB */
1732145479Smp    Char **ep = STR_environ;
1733145479Smp    const Char *ccp;
1734145479Smp    Char *cp, *dp;
173559243Sobrien    Char   *blk[2];
173659243Sobrien    Char  **oep = ep;
173759243Sobrien
173869408Sache#ifdef WINNT_NATIVE
1739167465Smp    nt_set_env(name,val);
174069408Sache#endif /* WINNT_NATIVE */
174159243Sobrien    for (; *ep; ep++) {
174269408Sache#ifdef WINNT_NATIVE
1743145479Smp	for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp);
1744145479Smp				ccp++, dp++)
174559243Sobrien#else
1746145479Smp	for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++)
174769408Sache#endif /* WINNT_NATIVE */
174859243Sobrien	    continue;
1749145479Smp	if (*ccp != 0 || *dp != '=')
175059243Sobrien	    continue;
175159243Sobrien	cp = Strspl(STRequal, val);
1752167465Smp	xfree(*ep);
175359243Sobrien	*ep = strip(Strspl(name, cp));
1754167465Smp	xfree(cp);
175559243Sobrien	blkfree((Char **) environ);
175659243Sobrien	environ = short2blk(STR_environ);
175759243Sobrien	return;
175859243Sobrien    }
175959243Sobrien    cp = Strspl(name, STRequal);
176059243Sobrien    blk[0] = strip(Strspl(cp, val));
1761167465Smp    xfree(cp);
176259243Sobrien    blk[1] = 0;
176359243Sobrien    STR_environ = blkspl(STR_environ, blk);
176459243Sobrien    blkfree((Char **) environ);
176559243Sobrien    environ = short2blk(STR_environ);
1766167465Smp    xfree(oep);
176759243Sobrien#endif /* SETENV_IN_LIB */
176859243Sobrien}
176959243Sobrien
177059243Sobrienvoid
1771167465SmpUnsetenv(Char *name)
177259243Sobrien{
1773145479Smp    Char **ep = STR_environ;
1774145479Smp    Char *cp, *dp;
177559243Sobrien    Char **oep = ep;
177659243Sobrien
177769408Sache#ifdef WINNT_NATIVE
177859243Sobrien	nt_set_env(name,NULL);
177969408Sache#endif /*WINNT_NATIVE */
178059243Sobrien    for (; *ep; ep++) {
178159243Sobrien	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
178259243Sobrien	    continue;
178359243Sobrien	if (*cp != 0 || *dp != '=')
178459243Sobrien	    continue;
178559243Sobrien	cp = *ep;
178659243Sobrien	*ep = 0;
178759243Sobrien	STR_environ = blkspl(STR_environ, ep + 1);
178859243Sobrien	blkfree((Char **) environ);
178959243Sobrien	environ = short2blk(STR_environ);
179059243Sobrien	*ep = cp;
1791167465Smp	xfree(cp);
1792167465Smp	xfree(oep);
179359243Sobrien	return;
179459243Sobrien    }
179559243Sobrien}
179659243Sobrien
179759243Sobrien/*ARGSUSED*/
179859243Sobrienvoid
1799167465Smpdoumask(Char **v, struct command *c)
180059243Sobrien{
1801145479Smp    Char *cp = v[1];
1802145479Smp    int i;
180359243Sobrien
180459243Sobrien    USE(c);
180559243Sobrien    if (cp == 0) {
180659415Sobrien	i = (int)umask(0);
180759243Sobrien	(void) umask(i);
180859243Sobrien	xprintf("%o\n", i);
180959243Sobrien	return;
181059243Sobrien    }
181159243Sobrien    i = 0;
181259243Sobrien    while (Isdigit(*cp) && *cp != '8' && *cp != '9')
181359243Sobrien	i = i * 8 + *cp++ - '0';
181459243Sobrien    if (*cp || i < 0 || i > 0777)
181559243Sobrien	stderror(ERR_NAME | ERR_MASK);
181659243Sobrien    (void) umask(i);
181759243Sobrien}
181859243Sobrien
181959243Sobrien#ifndef HAVENOLIMIT
182059243Sobrien# ifndef BSDLIMIT
182159243Sobrien   typedef long RLIM_TYPE;
1822145479Smp#  ifdef _OSD_POSIX /* BS2000 */
1823145479Smp#   include <ulimit.h>
1824145479Smp#  endif
182559243Sobrien#  ifndef RLIM_INFINITY
182659243Sobrien#   if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY)
182759243Sobrien    extern RLIM_TYPE ulimit();
182859243Sobrien#   endif /* ! _MINIX && !__clipper__ */
182959243Sobrien#   define RLIM_INFINITY 0x003fffff
183059243Sobrien#   define RLIMIT_FSIZE 1
183159243Sobrien#  endif /* RLIM_INFINITY */
183259243Sobrien#  ifdef aiws
183359243Sobrien#   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
183459243Sobrien#   define RLIMIT_DATA	3
183559243Sobrien#   define RLIMIT_STACK 1005
183659243Sobrien#  else /* aiws */
183759243Sobrien#   define toset(a) ((a) + 1)
183859243Sobrien#  endif /* aiws */
183959243Sobrien# else /* BSDLIMIT */
1840145479Smp#  if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__)
1841100616Smp    typedef rlim_t RLIM_TYPE;
184259243Sobrien#  else
184359243Sobrien#   if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3)
184459243Sobrien     typedef rlim_t RLIM_TYPE;
184559243Sobrien#   else
184659243Sobrien#    if defined(_SX)
184759243Sobrien      typedef long long RLIM_TYPE;
1848100616Smp#    else /* !_SX */
184959243Sobrien      typedef unsigned long RLIM_TYPE;
185059243Sobrien#    endif /* _SX */
185159243Sobrien#   endif /* SOLARIS2 || (sgi && SYSVREL > 3) */
185259243Sobrien#  endif /* BSD4_4 && !__386BSD__  */
185359243Sobrien# endif /* BSDLIMIT */
185459243Sobrien
1855131962Smp# if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT)
185659243Sobrien/* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
185759243Sobrien/* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
185859243Sobrien#  ifndef RLIMIT_CPU
185959243Sobrien#   define RLIMIT_CPU		0
186059243Sobrien#   define RLIMIT_FSIZE		1
186159243Sobrien#   define RLIMIT_DATA		2
186259243Sobrien#   define RLIMIT_STACK		3
186359243Sobrien#   define RLIMIT_CORE		4
186459243Sobrien#   define RLIMIT_RSS		5
186559243Sobrien#   define RLIMIT_NOFILE	6
186659243Sobrien#  endif /* RLIMIT_CPU */
186759243Sobrien#  ifndef RLIM_INFINITY
186859243Sobrien#   define RLIM_INFINITY	0x7fffffff
186959243Sobrien#  endif /* RLIM_INFINITY */
187059243Sobrien   /*
187159243Sobrien    * old versions of HP/UX counted limits in 512 bytes
187259243Sobrien    */
187359243Sobrien#  ifndef SIGRTMIN
187459243Sobrien#   define FILESIZE512
187559243Sobrien#  endif /* SIGRTMIN */
1876131962Smp# endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */
187759243Sobrien
187859243Sobrien# if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX)
187959243Sobrien/* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */
188059243Sobrien/* sh.h.  However, some SVR4 limits are defined in <sys/resource.h>.  Rather */
188159243Sobrien/* than include both and get warnings, we define the extra SVR4 limits here. */
188283098Smp/* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */
188383098Smp/* RLIMIT_VMEM based on it? */
188459243Sobrien#  ifndef RLIMIT_VMEM
188559243Sobrien#   define RLIMIT_VMEM	6
188659243Sobrien#  endif
188759243Sobrien#  ifndef RLIMIT_AS
188859243Sobrien#   define RLIMIT_AS	RLIMIT_VMEM
188959243Sobrien#  endif
189059243Sobrien# endif /* SYSVREL > 3 && BSDLIMIT */
189159243Sobrien
1892231990Smp# if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__))
1893231990Smp#  if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
1894231990Smp#   define RLIMIT_VMEM	RLIMIT_AS
1895231990Smp#  endif
1896231990Smp/*
1897231990Smp * Oh well, <asm-generic/resource.h> has it, but <bits/resource.h> does not
1898231990Smp * Linux headers: When the left hand does not know what the right hand does.
1899231990Smp */
1900231990Smp#  if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME)
1901231990Smp#   define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1)
1902231990Smp#  endif
190383098Smp# endif
190483098Smp
190559243Sobrienstruct limits limits[] =
190659243Sobrien{
190759243Sobrien# ifdef RLIMIT_CPU
190859243Sobrien    { RLIMIT_CPU, 	"cputime",	1,	"seconds"	},
190959243Sobrien# endif /* RLIMIT_CPU */
191059243Sobrien
191159243Sobrien# ifdef RLIMIT_FSIZE
191259243Sobrien#  ifndef aiws
191359243Sobrien    { RLIMIT_FSIZE, 	"filesize",	1024,	"kbytes"	},
191459243Sobrien#  else
191559243Sobrien    { RLIMIT_FSIZE, 	"filesize",	512,	"blocks"	},
191659243Sobrien#  endif /* aiws */
191759243Sobrien# endif /* RLIMIT_FSIZE */
191859243Sobrien
191959243Sobrien# ifdef RLIMIT_DATA
192059243Sobrien    { RLIMIT_DATA, 	"datasize",	1024,	"kbytes"	},
192159243Sobrien# endif /* RLIMIT_DATA */
192259243Sobrien
192359243Sobrien# ifdef RLIMIT_STACK
192459243Sobrien#  ifndef aiws
192559243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024,	"kbytes"	},
192659243Sobrien#  else
192759243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024 * 1024,	"kbytes"},
192859243Sobrien#  endif /* aiws */
192959243Sobrien# endif /* RLIMIT_STACK */
193059243Sobrien
193159243Sobrien# ifdef RLIMIT_CORE
193259243Sobrien    { RLIMIT_CORE, 	"coredumpsize",	1024,	"kbytes"	},
193359243Sobrien# endif /* RLIMIT_CORE */
193459243Sobrien
193559243Sobrien# ifdef RLIMIT_RSS
193659243Sobrien    { RLIMIT_RSS, 	"memoryuse",	1024,	"kbytes"	},
193759243Sobrien# endif /* RLIMIT_RSS */
193859243Sobrien
193959243Sobrien# ifdef RLIMIT_UMEM
194059243Sobrien    { RLIMIT_UMEM, 	"memoryuse",	1024,	"kbytes"	},
194159243Sobrien# endif /* RLIMIT_UMEM */
194259243Sobrien
194359243Sobrien# ifdef RLIMIT_VMEM
194459243Sobrien    { RLIMIT_VMEM, 	"vmemoryuse",	1024,	"kbytes"	},
194559243Sobrien# endif /* RLIMIT_VMEM */
194659243Sobrien
1947145479Smp# if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */
1948145479Smp    { RLIMIT_HEAP,	"heapsize",	1024,	"kbytes"	},
1949145479Smp# endif /* RLIMIT_HEAP */
1950145479Smp
195159243Sobrien# ifdef RLIMIT_NOFILE
195259243Sobrien    { RLIMIT_NOFILE, 	"descriptors", 1,	""		},
195359243Sobrien# endif /* RLIMIT_NOFILE */
195459243Sobrien
1955316958Sdchagin# ifdef RLIMIT_NPTS
1956316958Sdchagin    { RLIMIT_NPTS,	"pseudoterminals", 1,	""		},
1957316958Sdchagin# endif /* RLIMIT_NPTS */
1958316958Sdchagin
1959316958Sdchagin# ifdef RLIMIT_KQUEUES
1960316958Sdchagin    { RLIMIT_KQUEUES,	"kqueues",	1,	""		},
1961316958Sdchagin# endif /* RLIMIT_KQUEUES */
1962316958Sdchagin
196359243Sobrien# ifdef RLIMIT_CONCUR
196459243Sobrien    { RLIMIT_CONCUR, 	"concurrency", 1,	"thread(s)"	},
196559243Sobrien# endif /* RLIMIT_CONCUR */
196659243Sobrien
196759243Sobrien# ifdef RLIMIT_MEMLOCK
196859243Sobrien    { RLIMIT_MEMLOCK,	"memorylocked",	1024,	"kbytes"	},
196959243Sobrien# endif /* RLIMIT_MEMLOCK */
197059243Sobrien
197159243Sobrien# ifdef RLIMIT_NPROC
197259243Sobrien    { RLIMIT_NPROC,	"maxproc",	1,	""		},
197359243Sobrien# endif /* RLIMIT_NPROC */
197459243Sobrien
1975316958Sdchagin# ifdef RLIMIT_NTHR
1976316958Sdchagin    { RLIMIT_NTHR,	"maxthread",	1,	""		},
1977316958Sdchagin# endif /* RLIMIT_NTHR */
1978316958Sdchagin
1979100616Smp# if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
198059243Sobrien    { RLIMIT_OFILE,	"openfiles",	1,	""		},
1981100616Smp# endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */
198259243Sobrien
1983100616Smp# ifdef RLIMIT_SBSIZE
1984100616Smp    { RLIMIT_SBSIZE,	"sbsize",	1,	""		},
1985100616Smp# endif /* RLIMIT_SBSIZE */
1986100616Smp
1987195609Smp# ifdef RLIMIT_SWAP
1988195609Smp    { RLIMIT_SWAP,	"swapsize",	1024,	"kbytes"	},
1989195609Smp# endif /* RLIMIT_SWAP */
1990194767Skib
1991231990Smp# ifdef RLIMIT_LOCKS
1992231990Smp    { RLIMIT_LOCKS,	"maxlocks",	1,	""		},
1993231990Smp# endif /* RLIMIT_LOCKS */
1994231990Smp
1995316958Sdchagin# ifdef RLIMIT_POSIXLOCKS
1996316958Sdchagin    { RLIMIT_POSIXLOCKS,"posixlocks",	1,	""		},
1997316958Sdchagin# endif /* RLIMIT_POSIXLOCKS */
1998316958Sdchagin
1999231990Smp# ifdef RLIMIT_SIGPENDING
2000231990Smp    { RLIMIT_SIGPENDING,"maxsignal",	1,	""		},
2001231990Smp# endif /* RLIMIT_SIGPENDING */
2002231990Smp
2003231990Smp# ifdef RLIMIT_MSGQUEUE
2004231990Smp    { RLIMIT_MSGQUEUE,	"maxmessage",	1,	""		},
2005231990Smp# endif /* RLIMIT_MSGQUEUE */
2006231990Smp
2007231990Smp# ifdef RLIMIT_NICE
2008231990Smp    { RLIMIT_NICE,	"maxnice",	1,	""		},
2009231990Smp# endif /* RLIMIT_NICE */
2010231990Smp
2011231990Smp# ifdef RLIMIT_RTPRIO
2012231990Smp    { RLIMIT_RTPRIO,	"maxrtprio",	1,	""		},
2013231990Smp# endif /* RLIMIT_RTPRIO */
2014231990Smp
2015231990Smp# ifdef RLIMIT_RTTIME
2016231990Smp    { RLIMIT_RTTIME,	"maxrttime",	1,	"usec"		},
2017231990Smp# endif /* RLIMIT_RTTIME */
2018231990Smp
201959243Sobrien    { -1, 		NULL, 		0, 	NULL		}
202059243Sobrien};
202159243Sobrien
2022167465Smpstatic struct limits *findlim	(Char *);
2023167465Smpstatic RLIM_TYPE getval		(struct limits *, Char **);
2024231990Smpstatic int strtail		(Char *, const char *);
2025167465Smpstatic void limtail		(Char *, const char *);
2026231990Smpstatic void limtail2		(Char *, const char *, const char *);
2027167465Smpstatic void plim		(struct limits *, int);
2028167465Smpstatic int setlim		(struct limits *, int, RLIM_TYPE);
202959243Sobrien
203059243Sobrien#ifdef convex
203159243Sobrienstatic  RLIM_TYPE
2032167465Smprestrict_limit(double value)
203359243Sobrien{
203459243Sobrien    /*
203559243Sobrien     * is f too large to cope with? return the maximum or minimum int
203659243Sobrien     */
203759243Sobrien    if (value > (double) INT_MAX)
203859243Sobrien	return (RLIM_TYPE) INT_MAX;
203959243Sobrien    else if (value < (double) INT_MIN)
204059243Sobrien	return (RLIM_TYPE) INT_MIN;
204159243Sobrien    else
204259243Sobrien	return (RLIM_TYPE) value;
204359243Sobrien}
204459243Sobrien#else /* !convex */
204559243Sobrien# define restrict_limit(x)	((RLIM_TYPE) (x))
204659243Sobrien#endif /* convex */
204759243Sobrien
204859243Sobrien
204959243Sobrienstatic struct limits *
2050167465Smpfindlim(Char *cp)
205159243Sobrien{
2052145479Smp    struct limits *lp, *res;
205359243Sobrien
2054167465Smp    res = NULL;
205559243Sobrien    for (lp = limits; lp->limconst >= 0; lp++)
205659243Sobrien	if (prefix(cp, str2short(lp->limname))) {
205759243Sobrien	    if (res)
205859243Sobrien		stderror(ERR_NAME | ERR_AMBIG);
205959243Sobrien	    res = lp;
206059243Sobrien	}
206159243Sobrien    if (res)
206259243Sobrien	return (res);
206359243Sobrien    stderror(ERR_NAME | ERR_LIMIT);
206459243Sobrien    /* NOTREACHED */
206559243Sobrien    return (0);
206659243Sobrien}
206759243Sobrien
206859243Sobrien/*ARGSUSED*/
206959243Sobrienvoid
2070167465Smpdolimit(Char **v, struct command *c)
207159243Sobrien{
2072145479Smp    struct limits *lp;
2073145479Smp    RLIM_TYPE limit;
207459243Sobrien    int    hard = 0;
207559243Sobrien
207659243Sobrien    USE(c);
207759243Sobrien    v++;
207859243Sobrien    if (*v && eq(*v, STRmh)) {
207959243Sobrien	hard = 1;
208059243Sobrien	v++;
208159243Sobrien    }
208259243Sobrien    if (*v == 0) {
208359243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
208459243Sobrien	    plim(lp, hard);
208559243Sobrien	return;
208659243Sobrien    }
208759243Sobrien    lp = findlim(v[0]);
208859243Sobrien    if (v[1] == 0) {
208959243Sobrien	plim(lp, hard);
209059243Sobrien	return;
209159243Sobrien    }
209259243Sobrien    limit = getval(lp, v + 1);
209359243Sobrien    if (setlim(lp, hard, limit) < 0)
209459243Sobrien	stderror(ERR_SILENT);
209559243Sobrien}
209659243Sobrien
209759243Sobrienstatic  RLIM_TYPE
2098167465Smpgetval(struct limits *lp, Char **v)
209959243Sobrien{
2100145479Smp    float f;
210159243Sobrien    Char   *cp = *v++;
210259243Sobrien
210359243Sobrien    f = atof(short2str(cp));
210459243Sobrien
210559243Sobrien# ifdef convex
210659243Sobrien    /*
210759243Sobrien     * is f too large to cope with. limit f to minint, maxint  - X-6768 by
210859243Sobrien     * strike
210959243Sobrien     */
211059243Sobrien    if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
211159243Sobrien	stderror(ERR_NAME | ERR_TOOLARGE);
211259243Sobrien    }
211359243Sobrien# endif /* convex */
211459243Sobrien
211559243Sobrien    while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
211659243Sobrien	cp++;
211759243Sobrien    if (*cp == 0) {
211859243Sobrien	if (*v == 0)
211969408Sache	    return restrict_limit((f * lp->limdiv) + 0.5);
212059243Sobrien	cp = *v;
212159243Sobrien    }
212259243Sobrien    switch (*cp) {
212359243Sobrien# ifdef RLIMIT_CPU
212459243Sobrien    case ':':
212559243Sobrien	if (lp->limconst != RLIMIT_CPU)
212659243Sobrien	    goto badscal;
212759243Sobrien	return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
212859243Sobrien    case 'h':
212959243Sobrien	if (lp->limconst != RLIMIT_CPU)
213059243Sobrien	    goto badscal;
213159243Sobrien	limtail(cp, "hours");
213259243Sobrien	f *= 3600.0;
213359243Sobrien	break;
2134231990Smp# endif /* RLIMIT_CPU */
213559243Sobrien    case 'm':
2136231990Smp# ifdef RLIMIT_CPU
213759243Sobrien	if (lp->limconst == RLIMIT_CPU) {
213859243Sobrien	    limtail(cp, "minutes");
213959243Sobrien	    f *= 60.0;
214059243Sobrien	    break;
214159243Sobrien	}
2142231990Smp# endif /* RLIMIT_CPU */
2143231990Smp	limtail2(cp, "megabytes", "mbytes");
214459243Sobrien	f *= 1024.0 * 1024.0;
214559243Sobrien	break;
2146231990Smp# ifdef RLIMIT_CPU
214759243Sobrien    case 's':
214859243Sobrien	if (lp->limconst != RLIMIT_CPU)
214959243Sobrien	    goto badscal;
215059243Sobrien	limtail(cp, "seconds");
215159243Sobrien	break;
215259243Sobrien# endif /* RLIMIT_CPU */
2153231990Smp    case 'G':
2154231990Smp	*cp = 'g';
2155231990Smp	/*FALLTHROUGH*/
2156231990Smp    case 'g':
2157231990Smp# ifdef RLIMIT_CPU
2158231990Smp	if (lp->limconst == RLIMIT_CPU)
2159231990Smp	    goto badscal;
2160231990Smp# endif /* RLIMIT_CPU */
2161231990Smp	limtail2(cp, "gigabytes", "gbytes");
2162231990Smp	f *= 1024.0 * 1024.0 * 1024.0;
2163231990Smp	break;
216459243Sobrien    case 'M':
216559243Sobrien# ifdef RLIMIT_CPU
216659243Sobrien	if (lp->limconst == RLIMIT_CPU)
216759243Sobrien	    goto badscal;
216859243Sobrien# endif /* RLIMIT_CPU */
216959243Sobrien	*cp = 'm';
2170231990Smp	limtail2(cp, "megabytes", "mbytes");
217159243Sobrien	f *= 1024.0 * 1024.0;
217259243Sobrien	break;
217359243Sobrien    case 'k':
217459243Sobrien# ifdef RLIMIT_CPU
217559243Sobrien	if (lp->limconst == RLIMIT_CPU)
217659243Sobrien	    goto badscal;
217759243Sobrien# endif /* RLIMIT_CPU */
2178231990Smp	limtail2(cp, "kilobytes", "kbytes");
217959243Sobrien	f *= 1024.0;
218059243Sobrien	break;
218159243Sobrien    case 'b':
218259243Sobrien# ifdef RLIMIT_CPU
218359243Sobrien	if (lp->limconst == RLIMIT_CPU)
218459243Sobrien	    goto badscal;
218559243Sobrien# endif /* RLIMIT_CPU */
218659243Sobrien	limtail(cp, "blocks");
218759243Sobrien	f *= 512.0;
218859243Sobrien	break;
218959243Sobrien    case 'u':
219059243Sobrien	limtail(cp, "unlimited");
219159243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
219259243Sobrien    default:
219359243Sobrien# ifdef RLIMIT_CPU
219459243Sobrienbadscal:
219559243Sobrien# endif /* RLIMIT_CPU */
219659243Sobrien	stderror(ERR_NAME | ERR_SCALEF);
219759243Sobrien    }
219859243Sobrien# ifdef convex
219959243Sobrien    return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5));
220059243Sobrien# else
220159243Sobrien    f += 0.5;
2202231990Smp    if (f > (float) ((RLIM_TYPE) RLIM_INFINITY))
220359243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
220459243Sobrien    else
220559243Sobrien	return ((RLIM_TYPE) f);
220659243Sobrien# endif /* convex */
220759243Sobrien}
220859243Sobrien
2209231990Smpstatic int
2210231990Smpstrtail(Char *cp, const char *str)
2211231990Smp{
2212231990Smp    while (*cp && *cp == (Char)*str)
2213231990Smp	cp++, str++;
2214231990Smp    return (*cp != '\0');
2215231990Smp}
2216231990Smp
221759243Sobrienstatic void
2218167465Smplimtail(Char *cp, const char *str)
221959243Sobrien{
2220231990Smp    if (strtail(cp, str))
2221231990Smp	stderror(ERR_BADSCALE, str);
2222231990Smp}
222361515Sobrien
2224231990Smpstatic void
2225231990Smplimtail2(Char *cp, const char *str1, const char *str2)
2226231990Smp{
2227231990Smp    if (strtail(cp, str1) && strtail(cp, str2))
2228231990Smp	stderror(ERR_BADSCALE, str1);
222959243Sobrien}
223059243Sobrien
223159243Sobrien/*ARGSUSED*/
223259243Sobrienstatic void
2233167465Smpplim(struct limits *lp, int hard)
223459243Sobrien{
223559243Sobrien# ifdef BSDLIMIT
223659243Sobrien    struct rlimit rlim;
223759243Sobrien# endif /* BSDLIMIT */
223859243Sobrien    RLIM_TYPE limit;
2239145479Smp    int     xdiv = lp->limdiv;
224059243Sobrien
2241131962Smp    xprintf("%-13.13s", lp->limname);
224259243Sobrien
224359243Sobrien# ifndef BSDLIMIT
224459243Sobrien    limit = ulimit(lp->limconst, 0);
224559243Sobrien#  ifdef aiws
224659243Sobrien    if (lp->limconst == RLIMIT_DATA)
224759243Sobrien	limit -= 0x20000000;
224859243Sobrien#  endif /* aiws */
224959243Sobrien# else /* BSDLIMIT */
225059243Sobrien    (void) getrlimit(lp->limconst, &rlim);
225159243Sobrien    limit = hard ? rlim.rlim_max : rlim.rlim_cur;
225259243Sobrien# endif /* BSDLIMIT */
225359243Sobrien
225459243Sobrien# if !defined(BSDLIMIT) || defined(FILESIZE512)
225559243Sobrien    /*
225659243Sobrien     * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
225759243Sobrien     * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
225859243Sobrien     */
225959243Sobrien    if (lp->limconst == RLIMIT_FSIZE) {
226059243Sobrien	if (limit >= (RLIM_INFINITY / 512))
226159243Sobrien	    limit = RLIM_INFINITY;
226259243Sobrien	else
2263145479Smp	    xdiv = (xdiv == 1024 ? 2 : 1);
226459243Sobrien    }
226559243Sobrien# endif /* !BSDLIMIT || FILESIZE512 */
226659243Sobrien
226759243Sobrien    if (limit == RLIM_INFINITY)
226859243Sobrien	xprintf("unlimited");
226959243Sobrien    else
2270145479Smp# if defined(RLIMIT_CPU) && defined(_OSD_POSIX)
2271145479Smp    if (lp->limconst == RLIMIT_CPU &&
2272145479Smp        (unsigned long)limit >= 0x7ffffffdUL)
2273145479Smp	xprintf("unlimited");
2274145479Smp    else
2275145479Smp# endif
227659243Sobrien# ifdef RLIMIT_CPU
227759243Sobrien    if (lp->limconst == RLIMIT_CPU)
2278167465Smp	psecs(limit);
227959243Sobrien    else
228059243Sobrien# endif /* RLIMIT_CPU */
2281145479Smp	xprintf("%ld %s", (long) (limit / xdiv), lp->limscale);
228259243Sobrien    xputchar('\n');
228359243Sobrien}
228459243Sobrien
228559243Sobrien/*ARGSUSED*/
228659243Sobrienvoid
2287167465Smpdounlimit(Char **v, struct command *c)
228859243Sobrien{
2289145479Smp    struct limits *lp;
229059243Sobrien    int    lerr = 0;
229159243Sobrien    int    hard = 0;
229259243Sobrien    int	   force = 0;
229359243Sobrien
229459243Sobrien    USE(c);
229559243Sobrien    while (*++v && **v == '-') {
229659243Sobrien	Char   *vp = *v;
229759243Sobrien	while (*++vp)
229859243Sobrien	    switch (*vp) {
229959243Sobrien	    case 'f':
230059243Sobrien		force = 1;
230159243Sobrien		break;
230259243Sobrien	    case 'h':
230359243Sobrien		hard = 1;
230459243Sobrien		break;
230559243Sobrien	    default:
230659243Sobrien		stderror(ERR_ULIMUS);
230759243Sobrien		break;
230859243Sobrien	    }
230959243Sobrien    }
231059243Sobrien
231159243Sobrien    if (*v == 0) {
231259243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
231359243Sobrien	    if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
231459243Sobrien		lerr++;
231559243Sobrien	if (!force && lerr)
231659243Sobrien	    stderror(ERR_SILENT);
231759243Sobrien	return;
231859243Sobrien    }
231959243Sobrien    while (*v) {
232059243Sobrien	lp = findlim(*v++);
232159243Sobrien	if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force)
232259243Sobrien	    stderror(ERR_SILENT);
232359243Sobrien    }
232459243Sobrien}
232559243Sobrien
232659243Sobrienstatic int
2327167465Smpsetlim(struct limits *lp, int hard, RLIM_TYPE limit)
232859243Sobrien{
232959243Sobrien# ifdef BSDLIMIT
233059243Sobrien    struct rlimit rlim;
233159243Sobrien
233259243Sobrien    (void) getrlimit(lp->limconst, &rlim);
233359243Sobrien
233459243Sobrien#  ifdef FILESIZE512
233559243Sobrien    /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
233659243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
233759243Sobrien	limit /= 512;
233859243Sobrien#  endif /* FILESIZE512 */
233959243Sobrien    if (hard)
234059243Sobrien	rlim.rlim_max = limit;
234159243Sobrien    else if (limit == RLIM_INFINITY && euid != 0)
234259243Sobrien	rlim.rlim_cur = rlim.rlim_max;
234359243Sobrien    else
234459243Sobrien	rlim.rlim_cur = limit;
234559243Sobrien
2346100616Smp    if (rlim.rlim_cur > rlim.rlim_max)
2347100616Smp	rlim.rlim_max = rlim.rlim_cur;
2348100616Smp
234959243Sobrien    if (setrlimit(lp->limconst, &rlim) < 0) {
235059243Sobrien# else /* BSDLIMIT */
235159243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
235259243Sobrien	limit /= 512;
235359243Sobrien# ifdef aiws
235459243Sobrien    if (lp->limconst == RLIMIT_DATA)
235559243Sobrien	limit += 0x20000000;
235659243Sobrien# endif /* aiws */
235759243Sobrien    if (ulimit(toset(lp->limconst), limit) < 0) {
235859243Sobrien# endif /* BSDLIMIT */
2359145479Smp        int err;
2360145479Smp        char *op, *type;
2361145479Smp
2362145479Smp	err = errno;
2363145479Smp	op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") :
2364145479Smp		     	CGETS(15, 3, "set"));
2365167465Smp	cleanup_push(op, xfree);
2366145479Smp	type = strsave(hard ? CGETS(15, 4, " hard") : "");
2367167465Smp	cleanup_push(type, xfree);
2368100616Smp	xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname,
2369145479Smp	    lp->limname, op, type, strerror(err));
2370167465Smp	cleanup_until(op);
237159243Sobrien	return (-1);
237259243Sobrien    }
237359243Sobrien    return (0);
237459243Sobrien}
237559243Sobrien
237659243Sobrien#endif /* !HAVENOLIMIT */
237759243Sobrien
237859243Sobrien/*ARGSUSED*/
237959243Sobrienvoid
2380167465Smpdosuspend(Char **v, struct command *c)
238159243Sobrien{
238259243Sobrien#ifdef BSDJOBS
2383167465Smp    struct sigaction old;
238459243Sobrien#endif /* BSDJOBS */
2385195609Smp
238659243Sobrien    USE(c);
238759243Sobrien    USE(v);
238859243Sobrien
238959243Sobrien    if (loginsh)
239059243Sobrien	stderror(ERR_SUSPLOG);
239159243Sobrien    untty();
239259243Sobrien
239359243Sobrien#ifdef BSDJOBS
2394167465Smp    sigaction(SIGTSTP, NULL, &old);
2395167465Smp    signal(SIGTSTP, SIG_DFL);
239659243Sobrien    (void) kill(0, SIGTSTP);
239759243Sobrien    /* the shell stops here */
2398167465Smp    sigaction(SIGTSTP, &old, NULL);
239959243Sobrien#else /* !BSDJOBS */
240059243Sobrien    stderror(ERR_JOBCONTROL);
240159243Sobrien#endif /* BSDJOBS */
240259243Sobrien
240359243Sobrien#ifdef BSDJOBS
240459243Sobrien    if (tpgrp != -1) {
2405195609Smp	if (grabpgrp(FSHTTY, opgrp) == -1)
2406131962Smp	    stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno));
240759243Sobrien	(void) setpgid(0, shpgrp);
240859243Sobrien	(void) tcsetpgrp(FSHTTY, shpgrp);
240959243Sobrien    }
241059243Sobrien#endif /* BSDJOBS */
241159243Sobrien    (void) setdisc(FSHTTY);
241259243Sobrien}
241359243Sobrien
241459243Sobrien/* This is the dreaded EVAL built-in.
241559243Sobrien *   If you don't fiddle with file descriptors, and reset didfds,
241659243Sobrien *   this command will either ignore redirection inside or outside
241759243Sobrien *   its arguments, e.g. eval "date >x"  vs.  eval "date" >x
241859243Sobrien *   The stuff here seems to work, but I did it by trial and error rather
241959243Sobrien *   than really knowing what was going on.  If tpgrp is zero, we are
242059243Sobrien *   probably a background eval, e.g. "eval date &", and we want to
242159243Sobrien *   make sure that any processes we start stay in our pgrp.
242259243Sobrien *   This is also the case for "time eval date" -- stay in same pgrp.
242359243Sobrien *   Otherwise, under stty tostop, processes will stop in the wrong
242459243Sobrien *   pgrp, with no way for the shell to get them going again.  -IAN!
242559243Sobrien */
242659243Sobrien
2427167465Smpstruct doeval_state
242859243Sobrien{
2429167465Smp    Char **evalvec, *evalp;
2430167465Smp    int didfds;
243159243Sobrien#ifndef CLOSE_ON_EXEC
2432167465Smp    int didcch;
2433167465Smp#endif
2434167465Smp    int saveIN, saveOUT, saveDIAG;
2435167465Smp    int SHIN, SHOUT, SHDIAG;
2436167465Smp};
243759243Sobrien
2438167465Smpstatic void
2439167465Smpdoeval_cleanup(void *xstate)
2440167465Smp{
2441167465Smp    struct doeval_state *state;
2442167465Smp
2443167465Smp    state = xstate;
2444167465Smp    evalvec = state->evalvec;
2445167465Smp    evalp = state->evalp;
2446167465Smp    doneinp = 0;
244759243Sobrien#ifndef CLOSE_ON_EXEC
2448167465Smp    didcch = state->didcch;
244959243Sobrien#endif /* CLOSE_ON_EXEC */
2450167465Smp    didfds = state->didfds;
2451316958Sdchagin    if (state->saveIN != SHIN)
2452316958Sdchagin	xclose(SHIN);
2453316958Sdchagin    if (state->saveOUT != SHOUT)
2454316958Sdchagin	xclose(SHOUT);
2455316958Sdchagin    if (state->saveDIAG != SHDIAG)
2456316958Sdchagin	xclose(SHDIAG);
2457167465Smp    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
2458167465Smp    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
2459167465Smp    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
2460316958Sdchagin    if (didfds) {
2461316958Sdchagin	close_on_exec(dcopy(SHIN, 0), 1);
2462316958Sdchagin	close_on_exec(dcopy(SHOUT, 1), 1);
2463316958Sdchagin	close_on_exec(dcopy(SHDIAG, 2), 1);
2464316958Sdchagin    }
2465167465Smp}
246659243Sobrien
2467195609Smpstatic Char **Ggv;
2468167465Smp/*ARGSUSED*/
2469167465Smpvoid
2470167465Smpdoeval(Char **v, struct command *c)
2471167465Smp{
2472167465Smp    struct doeval_state state;
2473195609Smp    int gflag, my_reenter;
2474167465Smp    Char **gv;
2475195609Smp    jmp_buf_t osetexit;
247659243Sobrien
2477167465Smp    USE(c);
2478167465Smp    v++;
2479167465Smp    if (*v == 0)
248059243Sobrien	return;
2481167465Smp    gflag = tglob(v);
248259243Sobrien    if (gflag) {
2483167465Smp	gv = v = globall(v, gflag);
2484167465Smp	if (v == 0)
248559243Sobrien	    stderror(ERR_NOMATCH);
2486167465Smp	cleanup_push(gv, blk_cleanup);
2487167465Smp	v = copyblk(v);
248859243Sobrien    }
248959243Sobrien    else {
249059243Sobrien	gv = NULL;
2491167465Smp	v = copyblk(v);
2492167465Smp	trim(v);
249359243Sobrien    }
249459243Sobrien
2495195609Smp    Ggv = gv;
2496167465Smp    state.evalvec = evalvec;
2497167465Smp    state.evalp = evalp;
2498167465Smp    state.didfds = didfds;
249959243Sobrien#ifndef CLOSE_ON_EXEC
2500167465Smp    state.didcch = didcch;
250159243Sobrien#endif /* CLOSE_ON_EXEC */
2502167465Smp    state.SHIN = SHIN;
2503167465Smp    state.SHOUT = SHOUT;
2504167465Smp    state.SHDIAG = SHDIAG;
250559243Sobrien
2506167465Smp    (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1);
2507167465Smp    (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1);
2508167465Smp    (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1);
2509167465Smp
2510167465Smp    cleanup_push(&state, doeval_cleanup);
2511167465Smp
2512195609Smp    getexit(osetexit);
2513195609Smp
2514195609Smp    /* PWP: setjmp/longjmp bugfix for optimizing compilers */
2515195609Smp#ifdef cray
2516195609Smp    my_reenter = 1;             /* assume non-zero return val */
2517195609Smp    if (setexit() == 0) {
2518195609Smp	my_reenter = 0;         /* Oh well, we were wrong */
2519195609Smp#else /* !cray */
2520195609Smp    if ((my_reenter = setexit()) == 0) {
2521195609Smp#endif /* cray */
2522195609Smp	evalvec = v;
2523195609Smp	evalp = 0;
2524195609Smp	(void)close_on_exec(SHIN = dcopy(0, -1), 1);
2525195609Smp	(void)close_on_exec(SHOUT = dcopy(1, -1), 1);
2526195609Smp	(void)close_on_exec(SHDIAG = dcopy(2, -1), 1);
252759243Sobrien#ifndef CLOSE_ON_EXEC
2528195609Smp	didcch = 0;
252959243Sobrien#endif /* CLOSE_ON_EXEC */
2530195609Smp	didfds = 0;
2531195609Smp	gv = Ggv;
2532195609Smp	process(0);
2533195609Smp	Ggv = gv;
2534195609Smp    }
253559243Sobrien
2536195609Smp    if (my_reenter == 0) {
2537195609Smp	cleanup_until(&state);
2538195609Smp	if (Ggv)
2539195609Smp	    cleanup_until(Ggv);
2540195609Smp    }
2541167465Smp
2542195609Smp    resexit(osetexit);
2543195609Smp    if (my_reenter)
2544195609Smp	stderror(ERR_SILENT);
254559243Sobrien}
254659243Sobrien
254759243Sobrien/*************************************************************************/
254859243Sobrien/* print list of builtin commands */
254959243Sobrien
2550167465Smpstatic void
2551167465Smplbuffed_cleanup (void *dummy)
2552167465Smp{
2553167465Smp    USE(dummy);
2554167465Smp    lbuffed = 1;
2555167465Smp}
2556167465Smp
255759243Sobrien/*ARGSUSED*/
255859243Sobrienvoid
2559167465Smpdobuiltins(Char **v, struct command *c)
256059243Sobrien{
256159243Sobrien    /* would use print_by_column() in tw.parse.c but that assumes
256259243Sobrien     * we have an array of Char * to pass.. (sg)
256359243Sobrien     */
2564167465Smp    const struct biltins *b;
2565145479Smp    int row, col, columns, rows;
256659243Sobrien    unsigned int w, maxwidth;
256759243Sobrien
256859243Sobrien    USE(c);
256959243Sobrien    USE(v);
257059243Sobrien    lbuffed = 0;		/* turn off line buffering */
2571167465Smp    cleanup_push(&lbuffed, lbuffed_cleanup);
257259243Sobrien
257359243Sobrien    /* find widest string */
257459243Sobrien    for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
257559243Sobrien	maxwidth = max(maxwidth, strlen(b->bname));
257659243Sobrien    ++maxwidth;					/* for space */
257759243Sobrien
257859243Sobrien    columns = (TermH + 1) / maxwidth;	/* PWP: terminal size change */
257959243Sobrien    if (!columns)
258059243Sobrien	columns = 1;
258159243Sobrien    rows = (nbfunc + (columns - 1)) / columns;
258259243Sobrien
258359243Sobrien    for (b = bfunc, row = 0; row < rows; row++) {
258459243Sobrien	for (col = 0; col < columns; col++) {
258559243Sobrien	    if (b < &bfunc[nbfunc]) {
258659243Sobrien		w = strlen(b->bname);
258759243Sobrien		xprintf("%s", b->bname);
258859243Sobrien		if (col < (columns - 1))	/* Not last column? */
258959243Sobrien		    for (; w < maxwidth; w++)
259059243Sobrien			xputchar(' ');
259159243Sobrien		++b;
259259243Sobrien	    }
259359243Sobrien	}
259459243Sobrien	if (row < (rows - 1)) {
259559243Sobrien	    if (Tty_raw_mode)
259659243Sobrien		xputchar('\r');
259759243Sobrien	    xputchar('\n');
259859243Sobrien	}
259959243Sobrien    }
260069408Sache#ifdef WINNT_NATIVE
260159243Sobrien    nt_print_builtins(maxwidth);
260259243Sobrien#else
260359243Sobrien    if (Tty_raw_mode)
260459243Sobrien	xputchar('\r');
260559243Sobrien    xputchar('\n');
260669408Sache#endif /* WINNT_NATIVE */
260759243Sobrien
2608167465Smp    cleanup_until(&lbuffed);		/* turn back on line buffering */
260959243Sobrien    flush();
261059243Sobrien}
261159243Sobrien
2612145479Smp#ifdef NLS_CATALOGS
2613145479Smpchar *
2614167465Smpxcatgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2615145479Smp{
2616167465Smp    char *res;
2617167465Smp
2618167465Smp    errno = 0;
2619167465Smp    while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) {
2620167465Smp	handle_pending_signals();
2621167465Smp	errno = 0;
2622167465Smp    }
2623167465Smp    return res;
2624167465Smp}
2625167465Smp
2626167465Smp
2627167465Smp# if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2628167465Smpchar *
2629167465Smpiconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2630167465Smp{
2631145479Smp    static char *buf = NULL;
2632145479Smp    static size_t buf_size = 0;
2633145479Smp
2634145479Smp    char *orig, *dest, *p;
2635167465Smp    ICONV_CONST char *src;
2636145479Smp    size_t src_size, dest_size;
2637145479Smp
2638167465Smp    orig = xcatgets(ctd, set_id, msg_id, s);
2639145479Smp    if (catgets_iconv == (iconv_t)-1 || orig == s)
2640145479Smp        return orig;
2641145479Smp    src = orig;
2642145479Smp    src_size = strlen(src) + 1;
2643145479Smp    if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL)
2644145479Smp	return orig;
2645145479Smp    dest = buf;
2646145479Smp    while (src_size != 0) {
2647145479Smp        dest_size = buf + buf_size - dest;
2648145479Smp	if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size)
2649145479Smp	    == (size_t)-1) {
2650145479Smp	    switch (errno) {
2651145479Smp	    case E2BIG:
2652145479Smp		if ((p = xrealloc(buf, buf_size * 2)) == NULL)
2653145479Smp		    return orig;
2654145479Smp		buf_size *= 2;
2655145479Smp		dest = p + (dest - buf);
2656145479Smp		buf = p;
2657145479Smp		break;
2658145479Smp
2659145479Smp	    case EILSEQ: case EINVAL: default:
2660145479Smp		return orig;
2661145479Smp	    }
2662145479Smp	}
2663145479Smp    }
2664145479Smp    return buf;
2665145479Smp}
2666167465Smp# endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2667167465Smp#endif /* NLS_CATALOGS */
2668145479Smp
266959243Sobrienvoid
2670167465Smpnlsinit(void)
267159243Sobrien{
267259243Sobrien#ifdef NLS_CATALOGS
2673167465Smp    static const char default_catalog[] = "tcsh";
267469408Sache
2675167465Smp    char *catalog = (char *)(intptr_t)default_catalog;
2676167465Smp
267769408Sache    if (adrof(STRcatalog) != NULL)
2678167465Smp	catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog)));
2679231990Smp#ifdef NL_CAT_LOCALE /* POSIX-compliant. */
2680231990Smp    /*
2681231990Smp     * Check if LC_MESSAGES is set in the environment and use it, if so.
2682231990Smp     * If not, fall back to the setting of LANG.
2683231990Smp     */
2684231990Smp    catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0);
2685231990Smp#else /* pre-POSIX */
2686231990Smp# ifndef MCLoadBySet
2687231990Smp#  define MCLoadBySet 0
2688231990Smp#  endif
268969408Sache    catd = catopen(catalog, MCLoadBySet);
2690231990Smp#endif
2691167465Smp    if (catalog != default_catalog)
2692167465Smp	xfree(catalog);
2693167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2694167465Smp    /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */
2695145479Smp    catgets_iconv = iconv_open (nl_langinfo (CODESET),
2696231990Smp				xcatgets(catd, 255, 1, "UTF-8"));
2697167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
269869408Sache#endif /* NLS_CATALOGS */
269969408Sache#ifdef WINNT_NATIVE
270059243Sobrien    nls_dll_init();
270169408Sache#endif /* WINNT_NATIVE */
270259243Sobrien    errinit();		/* init the errorlist in correct locale */
270359243Sobrien    mesginit();		/* init the messages for signals */
270459243Sobrien    dateinit();		/* init the messages for dates */
270559243Sobrien    editinit();		/* init the editor messages */
270659243Sobrien    terminit();		/* init the termcap messages */
270759243Sobrien}
2708145479Smp
2709145479Smpvoid
2710167465Smpnlsclose(void)
2711145479Smp{
2712145479Smp#ifdef NLS_CATALOGS
2713167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2714145479Smp    if (catgets_iconv != (iconv_t)-1) {
2715145479Smp	iconv_close(catgets_iconv);
2716145479Smp	catgets_iconv = (iconv_t)-1;
2717145479Smp    }
2718167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2719167465Smp    if (catd != (nl_catd)-1) {
2720167465Smp	/*
2721167465Smp	 * catclose can call other functions which can call longjmp
2722167465Smp	 * making us re-enter this code. Prevent infinite recursion
2723167465Smp	 * by resetting catd. Problem reported and solved by:
2724167465Smp	 * Gerhard Niklasch
2725167465Smp	 */
2726167465Smp	nl_catd oldcatd = catd;
2727167465Smp	catd = (nl_catd)-1;
2728167465Smp	while (catclose(oldcatd) == -1 && errno == EINTR)
2729167465Smp	    handle_pending_signals();
2730167465Smp    }
2731145479Smp#endif /* NLS_CATALOGS */
2732145479Smp}
2733316958Sdchagin
2734316958Sdchaginint
2735316958SdchagingetYN(const char *prompt)
2736316958Sdchagin{
2737316958Sdchagin    int doit;
2738316958Sdchagin    char c;
2739316958Sdchagin
2740316958Sdchagin    xprintf("%s", prompt);
2741316958Sdchagin    flush();
2742316958Sdchagin    (void) force_read(SHIN, &c, sizeof(c));
2743316958Sdchagin    /*
2744316958Sdchagin     * Perhaps we should use the yesexpr from the
2745316958Sdchagin     * actual locale
2746316958Sdchagin     */
2747316958Sdchagin    doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL);
2748316958Sdchagin    while (c != '\n' && force_read(SHIN, &c, sizeof(c)) == sizeof(c))
2749316958Sdchagin	continue;
2750316958Sdchagin    return doit;
2751316958Sdchagin}
2752