1232633Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.func.c,v 3.162 2011/02/26 00:07:06 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
35232633SmpRCSID("$tcsh: sh.func.c,v 3.162 2011/02/26 00:07:06 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
44232633Smp#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++;
522232633Smp    cp = sp = strip(*v);
523232633Smp    if (!letter(*cp))
52459243Sobrien	stderror(ERR_NAME | ERR_VARBEGIN);
525232633Smp    do {
52659243Sobrien	cp++;
527232633Smp    } while (alnum(*cp));
528232633Smp    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;
590232633Smp	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;
767232633Smp	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:
802167465Smp	    while (getword(&word))
80359243Sobrien		continue;
80459243Sobrien	    if ((type == TC_IF || type == TC_ELSE) &&
805167465Smp		eq(word.s, STRthen))
80659243Sobrien		level++;
80759243Sobrien	    break;
80859243Sobrien
80959243Sobrien	case TC_ENDIF:
81059243Sobrien	    if (type == TC_IF || type == TC_ELSE)
81159243Sobrien		level--;
81259243Sobrien	    break;
81359243Sobrien
81459243Sobrien	case TC_FOREACH:
81559243Sobrien	case TC_WHILE:
816131962Smp	    wlevel++;
81759243Sobrien	    if (type == TC_BREAK)
81859243Sobrien		level++;
81959243Sobrien	    break;
82059243Sobrien
82159243Sobrien	case TC_END:
822131962Smp	    if (type == TC_BRKSW) {
823131962Smp		if (wlevel == 0) {
824131962Smp		    wp = whyles;
825131962Smp		    if (wp) {
826131962Smp			    whyles = wp->w_next;
827131962Smp			    wpfree(wp);
828131962Smp		    }
829131962Smp		}
830131962Smp	    }
83159243Sobrien	    if (type == TC_BREAK)
83259243Sobrien		level--;
833131962Smp	    wlevel--;
83459243Sobrien	    break;
83559243Sobrien
83659243Sobrien	case TC_SWITCH:
83759243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
83859243Sobrien		level++;
83959243Sobrien	    break;
84059243Sobrien
84159243Sobrien	case TC_ENDSW:
84259243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
84359243Sobrien		level--;
84459243Sobrien	    break;
84559243Sobrien
84659243Sobrien	case TC_LABEL:
847167465Smp	    if (type == TC_GOTO && getword(&word) && eq(word.s, goal))
84859243Sobrien		level = -1;
84959243Sobrien	    break;
85059243Sobrien
85159243Sobrien	default:
85259243Sobrien	    if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
85359243Sobrien		break;
854167465Smp	    if (word.len == 0 || word.s[word.len - 1] != ':')
85559243Sobrien		break;
856167465Smp	    word.s[--word.len] = 0;
857167465Smp	    if ((type == TC_GOTO && eq(word.s, goal)) ||
858167465Smp		(type == TC_SWITCH && eq(word.s, STRdefault)))
85959243Sobrien		level = -1;
86059243Sobrien	    break;
86159243Sobrien
86259243Sobrien	case TC_CASE:
86359243Sobrien	    if (type != TC_SWITCH || level != 0)
86459243Sobrien		break;
865167465Smp	    (void) getword(&word);
866167465Smp	    if (word.len != 0 && word.s[word.len - 1] == ':')
867167465Smp		word.s[--word.len] = 0;
868167465Smp	    cp = strip(Dfix1(word.s));
869167465Smp	    cleanup_push(cp, xfree);
87059243Sobrien	    if (Gmatch(goal, cp))
87159243Sobrien		level = -1;
872167465Smp	    cleanup_until(cp);
87359243Sobrien	    break;
87459243Sobrien
87559243Sobrien	case TC_DEFAULT:
87659243Sobrien	    if (type == TC_SWITCH && level == 0)
87759243Sobrien		level = -1;
87859243Sobrien	    break;
87959243Sobrien	}
880195609Smp	if (intty) {
881195609Smp	    ohistent->prev = histgetword(histent);
882195609Smp	    ohistent->prev->next = ohistent;
883195609Smp	    savehist(ohistent, 0);
884195609Smp	    freelex(ohistent);
885195609Smp	    xfree(ohistent);
886195609Smp	} else
887195609Smp	    (void) getword(NULL);
88859243Sobrien    } while (level >= 0);
889167465Smp end:
890167465Smp    cleanup_until(&word);
89159243Sobrien}
89259243Sobrien
893195609Smpstatic struct wordent *
894195609Smphistgetword(struct wordent *histent)
895195609Smp{
896195609Smp    int found = 0, first;
897195609Smp    eChar c, d;
898195609Smp    int e;
899195609Smp    struct Strbuf *tmp;
900195609Smp    tmp = xmalloc(sizeof(*tmp));
901195609Smp    tmp->size = 0;
902195609Smp    tmp->s = NULL;
903195609Smp    c = readc(1);
904195609Smp    d = 0;
905195609Smp    e = 0;
906195609Smp    for (;;) {
907195609Smp	tmp->len = 0;
908195609Smp	Strbuf_terminate (tmp);
909195609Smp	while (c == ' ' || c == '\t')
910195609Smp	    c = readc(1);
911195609Smp	if (c == '#')
912195609Smp	    do
913195609Smp		c = readc(1);
914195609Smp	    while (c != CHAR_ERR && c != '\n');
915195609Smp	if (c == CHAR_ERR)
916195609Smp	    goto past;
917195609Smp	if (c == '\n')
918195609Smp	    goto nl;
919195609Smp	unreadc(c);
920195609Smp	found = 1;
921195609Smp	first = 1;
922195609Smp	do {
923195609Smp	    e = (c == '\\');
924195609Smp	    c = readc(1);
925195609Smp	    if (c == '\\' && !e) {
926195609Smp		if ((c = readc(1)) == '\n') {
927195609Smp		    e = 1;
928195609Smp		    c = ' ';
929195609Smp		} else {
930195609Smp		    unreadc(c);
931195609Smp		    c = '\\';
932195609Smp		}
933195609Smp	    }
934195609Smp	    if ((c == '\'' || c == '"') && !e) {
935195609Smp		if (d == 0)
936195609Smp		    d = c;
937195609Smp		else if (d == c)
938195609Smp		    d = 0;
939195609Smp	    }
940195609Smp	    if (c == CHAR_ERR)
941195609Smp		goto past;
942195609Smp
943195609Smp	    Strbuf_append1(tmp, (Char) c);
944195609Smp
945195609Smp	    if (!first && !d && c == '(' && !e) {
946195609Smp		break;
947195609Smp	    }
948195609Smp	    first = 0;
949195609Smp	} while (d || e || (c != ' ' && c != '\t' && c != '\n'));
950195609Smp	tmp->len--;
951195609Smp	if (tmp->len) {
952195609Smp	    Strbuf_terminate(tmp);
953195609Smp	    histent->word = Strsave(tmp->s);
954195609Smp	    histent->next = xmalloc(sizeof (*histent));
955195609Smp	    histent->next->prev = histent;
956195609Smp	    histent = histent->next;
957195609Smp	}
958195609Smp	if (c == '\n') {
959195609Smp	nl:
960195609Smp	    tmp->len = 0;
961195609Smp	    Strbuf_append1(tmp, (Char) c);
962195609Smp	    Strbuf_terminate(tmp);
963195609Smp	    histent->word = Strsave(tmp->s);
964195609Smp	    return histent;
965195609Smp	}
966195609Smp    }
967195609Smp
968195609Smppast:
969195609Smp    switch (Stype) {
970195609Smp
971195609Smp    case TC_IF:
972195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
973195609Smp	break;
974195609Smp
975195609Smp    case TC_ELSE:
976195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
977195609Smp	break;
978195609Smp
979195609Smp    case TC_BRKSW:
980195609Smp    case TC_SWITCH:
981195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
982195609Smp	break;
983195609Smp
984195609Smp    case TC_BREAK:
985195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "end");
986195609Smp	break;
987195609Smp
988195609Smp    case TC_GOTO:
989195609Smp	setname(short2str(Sgoal));
990195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "label");
991195609Smp	break;
992195609Smp
993195609Smp    default:
994195609Smp	break;
995195609Smp    }
996195609Smp    /* NOTREACHED */
997195609Smp    return NULL;
998195609Smp}
999195609Smp
100059243Sobrienstatic int
1001167465Smpgetword(struct Strbuf *wp)
100259243Sobrien{
100359243Sobrien    int found = 0, first;
1004145479Smp    eChar c, d;
100559243Sobrien
1006167465Smp    if (wp)
1007167465Smp	wp->len = 0;
100859243Sobrien    c = readc(1);
100959243Sobrien    d = 0;
101059243Sobrien    do {
101159243Sobrien	while (c == ' ' || c == '\t')
101259243Sobrien	    c = readc(1);
101359243Sobrien	if (c == '#')
101459243Sobrien	    do
101559243Sobrien		c = readc(1);
1016145479Smp	    while (c != CHAR_ERR && c != '\n');
1017145479Smp	if (c == CHAR_ERR)
101859243Sobrien	    goto past;
101959243Sobrien	if (c == '\n') {
102059243Sobrien	    if (wp)
102159243Sobrien		break;
102259243Sobrien	    return (0);
102359243Sobrien	}
102459243Sobrien	unreadc(c);
102559243Sobrien	found = 1;
102659243Sobrien	first = 1;
102759243Sobrien	do {
102859243Sobrien	    c = readc(1);
102959243Sobrien	    if (c == '\\' && (c = readc(1)) == '\n')
103059243Sobrien		c = ' ';
103159243Sobrien	    if (c == '\'' || c == '"') {
103259243Sobrien		if (d == 0)
103359243Sobrien		    d = c;
103459243Sobrien		else if (d == c)
103559243Sobrien		    d = 0;
103659243Sobrien	    }
1037145479Smp	    if (c == CHAR_ERR)
103859243Sobrien		goto past;
1039167465Smp	    if (wp)
1040167465Smp		Strbuf_append1(wp, (Char) c);
104159243Sobrien	    if (!first && !d && c == '(') {
1042167465Smp		if (wp)
1043167465Smp		    goto past_word_end;
104459243Sobrien		else
104559243Sobrien		    break;
104659243Sobrien	    }
104759243Sobrien	    first = 0;
104859243Sobrien	} while ((d || (c != ' ' && c != '\t')) && c != '\n');
104959243Sobrien    } while (wp == 0);
105059243Sobrien
1051167465Smp past_word_end:
105259243Sobrien    unreadc(c);
1053167465Smp    if (found) {
1054167465Smp	wp->len--;
1055167465Smp	Strbuf_terminate(wp);
1056167465Smp    }
105759243Sobrien
105859243Sobrien    return (found);
105959243Sobrien
106059243Sobrienpast:
106159243Sobrien    switch (Stype) {
106259243Sobrien
106359243Sobrien    case TC_IF:
106459243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
106559243Sobrien	break;
106659243Sobrien
106759243Sobrien    case TC_ELSE:
106859243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
106959243Sobrien	break;
107059243Sobrien
107159243Sobrien    case TC_BRKSW:
107259243Sobrien    case TC_SWITCH:
107359243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
107459243Sobrien	break;
107559243Sobrien
107659243Sobrien    case TC_BREAK:
107759243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "end");
107859243Sobrien	break;
107959243Sobrien
108059243Sobrien    case TC_GOTO:
108159243Sobrien	setname(short2str(Sgoal));
108259243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "label");
108359243Sobrien	break;
108459243Sobrien
108559243Sobrien    default:
108659243Sobrien	break;
108759243Sobrien    }
108859243Sobrien    /* NOTREACHED */
108959243Sobrien    return (0);
109059243Sobrien}
109159243Sobrien
109259243Sobrienstatic void
1093167465Smptoend(void)
109459243Sobrien{
109569408Sache    if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) {
109659243Sobrien	search(TC_BREAK, 0, NULL);
109759243Sobrien	btell(&whyles->w_end);
109859243Sobrien	whyles->w_end.f_seek--;
109959243Sobrien    }
110059243Sobrien    else {
110159243Sobrien	bseek(&whyles->w_end);
110259243Sobrien    }
110359243Sobrien    wfree();
110459243Sobrien}
110559243Sobrien
1106131962Smpstatic void
1107167465Smpwpfree(struct whyle *wp)
1108131962Smp{
1109131962Smp	if (wp->w_fe0)
1110131962Smp	    blkfree(wp->w_fe0);
1111167465Smp	xfree(wp->w_fename);
1112167465Smp	xfree(wp);
1113131962Smp}
1114131962Smp
111559243Sobrienvoid
1116167465Smpwfree(void)
111759243Sobrien{
111859243Sobrien    struct Ain    o;
111959243Sobrien    struct whyle *nwp;
112059243Sobrien#ifdef lint
112159243Sobrien    nwp = NULL;	/* sun lint is dumb! */
112259243Sobrien#endif
112359243Sobrien
112459243Sobrien#ifdef FDEBUG
1125167465Smp    static const char foo[] = "IAFE";
112659243Sobrien#endif /* FDEBUG */
112759243Sobrien
112859243Sobrien    btell(&o);
112959243Sobrien
113059243Sobrien#ifdef FDEBUG
1131167465Smp    xprintf("o->type %c o->a_seek %d o->f_seek %d\n",
113259243Sobrien	    foo[o.type + 1], o.a_seek, o.f_seek);
113359243Sobrien#endif /* FDEBUG */
113459243Sobrien
113559243Sobrien    for (; whyles; whyles = nwp) {
1136145479Smp	struct whyle *wp = whyles;
113759243Sobrien	nwp = wp->w_next;
113859243Sobrien
113959243Sobrien#ifdef FDEBUG
1140167465Smp	xprintf("start->type %c start->a_seek %d start->f_seek %d\n",
114159243Sobrien		foo[wp->w_start.type+1],
114259243Sobrien		wp->w_start.a_seek, wp->w_start.f_seek);
1143167465Smp	xprintf("end->type %c end->a_seek %d end->f_seek %d\n",
114459243Sobrien		foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
114559243Sobrien#endif /* FDEBUG */
114659243Sobrien
114759243Sobrien	/*
114859243Sobrien	 * XXX: We free loops that have different seek types.
114959243Sobrien	 */
115069408Sache	if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type &&
115159243Sobrien	    wp->w_start.type == o.type) {
115269408Sache	    if (wp->w_end.type == TCSH_F_SEEK) {
115359243Sobrien		if (o.f_seek >= wp->w_start.f_seek &&
115459243Sobrien		    (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
115559243Sobrien		    break;
115659243Sobrien	    }
115759243Sobrien	    else {
115859243Sobrien		if (o.a_seek >= wp->w_start.a_seek &&
115959243Sobrien		    (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
116059243Sobrien		    break;
116159243Sobrien	    }
116259243Sobrien	}
116359243Sobrien
1164131962Smp	wpfree(wp);
116559243Sobrien    }
116659243Sobrien}
116759243Sobrien
116859243Sobrien/*ARGSUSED*/
116959243Sobrienvoid
1170167465Smpdoecho(Char **v, struct command *c)
117159243Sobrien{
117259243Sobrien    USE(c);
117359243Sobrien    xecho(' ', v);
117459243Sobrien}
117559243Sobrien
117659243Sobrien/*ARGSUSED*/
117759243Sobrienvoid
1178167465Smpdoglob(Char **v, struct command *c)
117959243Sobrien{
118059243Sobrien    USE(c);
118159243Sobrien    xecho(0, v);
118259243Sobrien    flush();
118359243Sobrien}
118459243Sobrien
118559243Sobrienstatic void
1186167465Smpxecho(int sep, Char **v)
118759243Sobrien{
1188167465Smp    Char *cp, **globbed = NULL;
118959243Sobrien    int     nonl = 0;
119059243Sobrien    int	    echo_style = ECHO_STYLE;
119159243Sobrien    struct varent *vp;
119259243Sobrien
119359243Sobrien    if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
119459243Sobrien	vp->vec[0] != NULL) {
119559243Sobrien	if (Strcmp(vp->vec[0], STRbsd) == 0)
119659243Sobrien	    echo_style = BSD_ECHO;
119759243Sobrien	else if (Strcmp(vp->vec[0], STRsysv) == 0)
119859243Sobrien	    echo_style = SYSV_ECHO;
119959243Sobrien	else if (Strcmp(vp->vec[0], STRboth) == 0)
120059243Sobrien	    echo_style = BOTH_ECHO;
120159243Sobrien	else if (Strcmp(vp->vec[0], STRnone) == 0)
120259243Sobrien	    echo_style = NONE_ECHO;
120359243Sobrien    }
120459243Sobrien
120559243Sobrien    v++;
120659243Sobrien    if (*v == 0)
120783098Smp	goto done;
1208167465Smp    if (setintr) {
1209167465Smp	int old_pintr_disabled;
1210167465Smp	pintr_push_enable(&old_pintr_disabled);
1211167465Smp	v = glob_all_or_error(v);
1212167465Smp	cleanup_until(&old_pintr_disabled);
1213167465Smp    } else {
1214167465Smp	v = glob_all_or_error(v);
121559243Sobrien    }
1216167465Smp    globbed = v;
1217167465Smp    if (globbed != NULL)
1218167465Smp	cleanup_push(globbed, blk_cleanup);
121959243Sobrien
122059243Sobrien    if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
122159243Sobrien	nonl++, v++;
122259243Sobrien
122359243Sobrien    while ((cp = *v++) != 0) {
1224145479Smp	Char c;
122559243Sobrien
1226167465Smp	if (setintr) {
1227167465Smp	    int old_pintr_disabled;
1228167465Smp
1229167465Smp	    pintr_push_enable(&old_pintr_disabled);
1230167465Smp	    cleanup_until(&old_pintr_disabled);
1231167465Smp	}
123259243Sobrien	while ((c = *cp++) != 0) {
123359243Sobrien	    if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
123459243Sobrien		switch (c = *cp++) {
123559243Sobrien		case 'a':
123659243Sobrien		    c = '\a';
123759243Sobrien		    break;
123859243Sobrien		case 'b':
123959243Sobrien		    c = '\b';
124059243Sobrien		    break;
124159243Sobrien		case 'c':
124259243Sobrien		    nonl = 1;
124359243Sobrien		    goto done;
124459243Sobrien		case 'e':
124559243Sobrien#if 0			/* Windows does not understand \e */
124659243Sobrien		    c = '\e';
124759243Sobrien#else
1248167465Smp		    c = CTL_ESC('\033');
124959243Sobrien#endif
125059243Sobrien		    break;
125159243Sobrien		case 'f':
125259243Sobrien		    c = '\f';
125359243Sobrien		    break;
125459243Sobrien		case 'n':
125559243Sobrien		    c = '\n';
125659243Sobrien		    break;
125759243Sobrien		case 'r':
125859243Sobrien		    c = '\r';
125959243Sobrien		    break;
126059243Sobrien		case 't':
126159243Sobrien		    c = '\t';
126259243Sobrien		    break;
126359243Sobrien		case 'v':
126459243Sobrien		    c = '\v';
126559243Sobrien		    break;
126659243Sobrien		case '\\':
126759243Sobrien		    c = '\\';
126859243Sobrien		    break;
126959243Sobrien		case '0':
127059243Sobrien		    c = 0;
127159243Sobrien		    if (*cp >= '0' && *cp < '8')
127259243Sobrien			c = c * 8 + *cp++ - '0';
127359243Sobrien		    if (*cp >= '0' && *cp < '8')
127459243Sobrien			c = c * 8 + *cp++ - '0';
127559243Sobrien		    if (*cp >= '0' && *cp < '8')
127659243Sobrien			c = c * 8 + *cp++ - '0';
127759243Sobrien		    break;
127859243Sobrien		case '\0':
127959243Sobrien		    c = '\\';
128059243Sobrien		    cp--;
128159243Sobrien		    break;
128259243Sobrien		default:
128359243Sobrien		    xputchar('\\' | QUOTE);
128459243Sobrien		    break;
128559243Sobrien		}
128659243Sobrien	    }
1287145479Smp	    xputwchar(c | QUOTE);
128859243Sobrien
128959243Sobrien	}
129059243Sobrien	if (*v)
129159243Sobrien	    xputchar(sep | QUOTE);
129259243Sobrien    }
129359243Sobriendone:
129459243Sobrien    if (sep && nonl == 0)
129559243Sobrien	xputchar('\n');
129659243Sobrien    else
129759243Sobrien	flush();
1298167465Smp    if (globbed != NULL)
1299167465Smp	cleanup_until(globbed);
130059243Sobrien}
130159243Sobrien
130259243Sobrien/* check whether an environment variable should invoke 'set_locale()' */
1303145479Smpstatic int
1304167465Smpislocale_var(Char *var)
130559243Sobrien{
130659243Sobrien    static Char *locale_vars[] = {
1307131962Smp	STRLANG,	STRLC_ALL, 	STRLC_CTYPE,	STRLC_NUMERIC,
1308131962Smp	STRLC_TIME,	STRLC_COLLATE,	STRLC_MESSAGES,	STRLC_MONETARY, 0
130959243Sobrien    };
1310145479Smp    Char **v;
131159243Sobrien
131259243Sobrien    for (v = locale_vars; *v; ++v)
131359243Sobrien	if (eq(var, *v))
131459243Sobrien	    return 1;
131559243Sobrien    return 0;
131659243Sobrien}
131759243Sobrien
1318167465Smpstatic void
1319167465Smpxlate_cr_cleanup(void *dummy)
1320167465Smp{
1321167465Smp    USE(dummy);
1322167465Smp    xlate_cr = 0;
1323167465Smp}
1324167465Smp
132559243Sobrien/*ARGSUSED*/
132659243Sobrienvoid
1327167465Smpdoprintenv(Char **v, struct command *c)
132859243Sobrien{
132959243Sobrien    Char   *e;
133059243Sobrien
133159243Sobrien    USE(c);
133259243Sobrien    v++;
133359243Sobrien    if (*v == 0) {
1334145479Smp	Char **ep;
133559243Sobrien
133659243Sobrien	xlate_cr = 1;
1337167465Smp	cleanup_push(&xlate_cr, xlate_cr_cleanup);
1338167465Smp	for (ep = STR_environ; *ep; ep++) {
1339167465Smp	    if (setintr) {
1340167465Smp		int old_pintr_disabled;
1341167465Smp
1342167465Smp		pintr_push_enable(&old_pintr_disabled);
1343167465Smp		cleanup_until(&old_pintr_disabled);
1344167465Smp	    }
134559243Sobrien	    xprintf("%S\n", *ep);
1346167465Smp	}
1347167465Smp	cleanup_until(&xlate_cr);
134859243Sobrien    }
134959243Sobrien    else if ((e = tgetenv(*v)) != NULL) {
1350167465Smp	int old_output_raw;
1351167465Smp
1352167465Smp	old_output_raw = output_raw;
135359243Sobrien	output_raw = 1;
1354167465Smp	cleanup_push(&old_output_raw, output_raw_restore);
135559243Sobrien	xprintf("%S\n", e);
1356167465Smp	cleanup_until(&old_output_raw);
135759243Sobrien    }
135859243Sobrien    else
1359167465Smp	setcopy(STRstatus, STR1, VAR_READWRITE);
136059243Sobrien}
136159243Sobrien
136259243Sobrien/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
136359243Sobrien   (and anything else with a modern compiler) */
136459243Sobrien
136559243Sobrien/*ARGSUSED*/
136659243Sobrienvoid
1367167465Smpdosetenv(Char **v, struct command *c)
136859243Sobrien{
136959243Sobrien    Char   *vp, *lp;
137059243Sobrien
137159243Sobrien    USE(c);
137259243Sobrien    if (*++v == 0) {
137359243Sobrien	doprintenv(--v, 0);
137459243Sobrien	return;
137559243Sobrien    }
137659243Sobrien
137759243Sobrien    vp = *v++;
1378232633Smp    lp = vp;
137959243Sobrien
1380232633Smp    if (!letter(*lp))
1381232633Smp	stderror(ERR_NAME | ERR_VARBEGIN);
1382232633Smp    do {
1383232633Smp	lp++;
1384232633Smp    } while (alnum(*lp));
1385232633Smp    if (*lp != '\0')
1386232633Smp	stderror(ERR_NAME | ERR_VARALNUM);
1387232633Smp
138859243Sobrien    if ((lp = *v++) == 0)
138959243Sobrien	lp = STRNULL;
139059243Sobrien
1391167465Smp    lp = globone(lp, G_APPEND);
1392167465Smp    cleanup_push(lp, xfree);
1393167465Smp    tsetenv(vp, lp);
139459243Sobrien    if (eq(vp, STRKPATH)) {
1395167465Smp        importpath(lp);
139659243Sobrien	dohash(NULL, NULL);
1397167465Smp	cleanup_until(lp);
139859243Sobrien	return;
139959243Sobrien    }
140059243Sobrien
140159243Sobrien#ifdef apollo
140259243Sobrien    if (eq(vp, STRSYSTYPE)) {
140359243Sobrien	dohash(NULL, NULL);
1404167465Smp	cleanup_until(lp);
140559243Sobrien	return;
140659243Sobrien    }
140759243Sobrien#endif /* apollo */
140859243Sobrien
140959243Sobrien    /* dspkanji/dspmbyte autosetting */
141059243Sobrien    /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
141159243Sobrien#if defined(DSPMBYTE)
141259243Sobrien    if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) {
141359243Sobrien	autoset_dspmbyte(lp);
141459243Sobrien    }
141559243Sobrien#endif
141659243Sobrien
141759243Sobrien    if (islocale_var(vp)) {
141859243Sobrien#ifdef NLS
141959243Sobrien	int     k;
142059243Sobrien
142159243Sobrien# ifdef SETLOCALEBUG
142259243Sobrien	dont_free = 1;
142359243Sobrien# endif /* SETLOCALEBUG */
142459243Sobrien	(void) setlocale(LC_ALL, "");
142559243Sobrien# ifdef LC_COLLATE
142659243Sobrien	(void) setlocale(LC_COLLATE, "");
142759243Sobrien# endif
1428145479Smp# ifdef LC_CTYPE
1429145479Smp	(void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1430145479Smp# endif /* LC_CTYPE */
1431232633Smp# if defined(AUTOSET_KANJI)
1432232633Smp        autoset_kanji();
1433232633Smp# endif /* AUTOSET_KANJI */
143459243Sobrien# ifdef NLS_CATALOGS
143559243Sobrien#  ifdef LC_MESSAGES
143659243Sobrien	(void) setlocale(LC_MESSAGES, "");
143759243Sobrien#  endif /* LC_MESSAGES */
1438145479Smp	nlsclose();
143959243Sobrien	nlsinit();
144059243Sobrien# endif /* NLS_CATALOGS */
144159243Sobrien# ifdef SETLOCALEBUG
144259243Sobrien	dont_free = 0;
144359243Sobrien# endif /* SETLOCALEBUG */
144459243Sobrien# ifdef STRCOLLBUG
144559243Sobrien	fix_strcoll_bug();
144659243Sobrien# endif /* STRCOLLBUG */
144759243Sobrien	tw_cmd_free();	/* since the collation sequence has changed */
1448167465Smp	for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
144959243Sobrien	    continue;
1450145479Smp	AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
145159243Sobrien#else /* !NLS */
145259243Sobrien	AsciiOnly = 0;
145359243Sobrien#endif /* NLS */
145459243Sobrien	NLSMapsAreInited = 0;
145559243Sobrien	ed_Init();
145659243Sobrien	if (MapsAreInited && !NLSMapsAreInited)
145759243Sobrien	    ed_InitNLSMaps();
1458167465Smp	cleanup_until(lp);
145959243Sobrien	return;
146059243Sobrien    }
146159243Sobrien
1462100616Smp#ifdef NLS_CATALOGS
1463100616Smp    if (eq(vp, STRNLSPATH)) {
1464145479Smp	nlsclose();
1465100616Smp	nlsinit();
1466100616Smp    }
1467100616Smp#endif
1468100616Smp
146959243Sobrien    if (eq(vp, STRNOREBIND)) {
147059243Sobrien	NoNLSRebind = 1;
147159243Sobrien	MapsAreInited = 0;
147259243Sobrien	NLSMapsAreInited = 0;
147359243Sobrien	ed_InitMaps();
1474167465Smp	cleanup_until(lp);
147559243Sobrien	return;
147659243Sobrien    }
147769408Sache#ifdef WINNT_NATIVE
147859243Sobrien    if (eq(vp, STRtcshlang)) {
147959243Sobrien	nlsinit();
1480167465Smp	cleanup_until(lp);
148159243Sobrien	return;
148259243Sobrien    }
148369408Sache#endif /* WINNT_NATIVE */
148459243Sobrien    if (eq(vp, STRKTERM)) {
148559243Sobrien	char *t;
1486167465Smp
1487167465Smp	setv(STRterm, quote(lp), VAR_READWRITE);	/* lp memory used here */
1488167465Smp	cleanup_ignore(lp);
1489167465Smp	cleanup_until(lp);
149059243Sobrien	t = short2str(lp);
149159243Sobrien	if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) {
149259243Sobrien	    editing = 1;
149359243Sobrien	    noediting = 0;
1494167465Smp	    setNS(STRedit);
149559243Sobrien	}
149659243Sobrien	GotTermCaps = 0;
149759243Sobrien	ed_Init();
149859243Sobrien	return;
149959243Sobrien    }
150059243Sobrien
150159243Sobrien    if (eq(vp, STRKHOME)) {
1502167465Smp	Char *canon;
150359243Sobrien	/*
150459243Sobrien	 * convert to canonical pathname (possibly resolving symlinks)
150559243Sobrien	 */
1506167465Smp	canon = dcanon(lp, lp);
1507167465Smp	cleanup_ignore(lp);
1508167465Smp	cleanup_until(lp);
1509167465Smp	cleanup_push(canon, xfree);
1510167465Smp	setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */
1511167465Smp	cleanup_ignore(canon);
1512167465Smp	cleanup_until(canon);
151359243Sobrien
151459243Sobrien	/* fix directory stack for new tilde home */
151559243Sobrien	dtilde();
151659243Sobrien	return;
151759243Sobrien    }
151859243Sobrien
151959243Sobrien    if (eq(vp, STRKSHLVL)) {
1520167465Smp	setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */
1521167465Smp	cleanup_ignore(lp);
1522167465Smp	cleanup_until(lp);
152359243Sobrien	return;
152459243Sobrien    }
152559243Sobrien
152659243Sobrien    if (eq(vp, STRKUSER)) {
1527167465Smp	setv(STRuser, quote(lp), VAR_READWRITE);	/* lp memory used here */
1528167465Smp	cleanup_ignore(lp);
1529167465Smp	cleanup_until(lp);
153059243Sobrien	return;
153159243Sobrien    }
153259243Sobrien
153359243Sobrien    if (eq(vp, STRKGROUP)) {
1534167465Smp	setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */
1535167465Smp	cleanup_ignore(lp);
1536167465Smp	cleanup_until(lp);
153759243Sobrien	return;
153859243Sobrien    }
153959243Sobrien
154059243Sobrien#ifdef COLOR_LS_F
154159243Sobrien    if (eq(vp, STRLS_COLORS)) {
154259243Sobrien        parseLS_COLORS(lp);
1543167465Smp	cleanup_until(lp);
154459243Sobrien	return;
154559243Sobrien    }
154659243Sobrien#endif /* COLOR_LS_F */
154759243Sobrien
154859243Sobrien#ifdef SIG_WINDOW
154959243Sobrien    /*
155059243Sobrien     * Load/Update $LINES $COLUMNS
155159243Sobrien     */
155259243Sobrien    if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
155359243Sobrien	eq(vp, STRTERMCAP)) {
1554167465Smp	cleanup_until(lp);
155559243Sobrien	check_window_size(1);
155659243Sobrien	return;
155759243Sobrien    }
155859243Sobrien
155959243Sobrien    /*
156059243Sobrien     * Change the size to the one directed by $LINES and $COLUMNS
156159243Sobrien     */
156259243Sobrien    if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
156359243Sobrien#if 0
156459243Sobrien	GotTermCaps = 0;
156559243Sobrien#endif
1566167465Smp	cleanup_until(lp);
156759243Sobrien	ed_Init();
156859243Sobrien	return;
156959243Sobrien    }
157059243Sobrien#endif /* SIG_WINDOW */
1571167465Smp    cleanup_until(lp);
157259243Sobrien}
157359243Sobrien
157459243Sobrien/*ARGSUSED*/
157559243Sobrienvoid
1576167465Smpdounsetenv(Char **v, struct command *c)
157759243Sobrien{
1578167465Smp    Char  **ep, *p, *n, *name;
157959243Sobrien    int     i, maxi;
158059243Sobrien
158159243Sobrien    USE(c);
158259243Sobrien    /*
158359243Sobrien     * Find the longest environment variable
158459243Sobrien     */
158559243Sobrien    for (maxi = 0, ep = STR_environ; *ep; ep++) {
158659243Sobrien	for (i = 0, p = *ep; *p && *p != '='; p++, i++)
158759243Sobrien	    continue;
158859243Sobrien	if (i > maxi)
158959243Sobrien	    maxi = i;
159059243Sobrien    }
159159243Sobrien
1592167465Smp    name = xmalloc((maxi + 1) * sizeof(Char));
1593167465Smp    cleanup_push(name, xfree);
159459243Sobrien
159559243Sobrien    while (++v && *v)
159659243Sobrien	for (maxi = 1; maxi;)
159759243Sobrien	    for (maxi = 0, ep = STR_environ; *ep; ep++) {
159859243Sobrien		for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
159959243Sobrien		    continue;
160059243Sobrien		*n = '\0';
160159243Sobrien		if (!Gmatch(name, *v))
160259243Sobrien		    continue;
160359243Sobrien		maxi = 1;
160459243Sobrien
160559243Sobrien		/* Unset the name. This wasn't being done until
160659243Sobrien		 * later but most of the stuff following won't
160759243Sobrien		 * work (particularly the setlocale() and getenv()
160859243Sobrien		 * stuff) as intended until the name is actually
160959243Sobrien		 * removed. (sg)
161059243Sobrien		 */
161159243Sobrien		Unsetenv(name);
161259243Sobrien
161359243Sobrien		if (eq(name, STRNOREBIND)) {
161459243Sobrien		    NoNLSRebind = 0;
161559243Sobrien		    MapsAreInited = 0;
161659243Sobrien		    NLSMapsAreInited = 0;
161759243Sobrien		    ed_InitMaps();
161859243Sobrien		}
161959243Sobrien#ifdef apollo
162059243Sobrien		else if (eq(name, STRSYSTYPE))
162159243Sobrien		    dohash(NULL, NULL);
162259243Sobrien#endif /* apollo */
162359243Sobrien		else if (islocale_var(name)) {
162459243Sobrien#ifdef NLS
162559243Sobrien		    int     k;
162659243Sobrien
162759243Sobrien# ifdef SETLOCALEBUG
162859243Sobrien		    dont_free = 1;
162959243Sobrien# endif /* SETLOCALEBUG */
163059243Sobrien		    (void) setlocale(LC_ALL, "");
163159243Sobrien# ifdef LC_COLLATE
163259243Sobrien		    (void) setlocale(LC_COLLATE, "");
163359243Sobrien# endif
1634145479Smp# ifdef LC_CTYPE
1635167465Smp		    (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1636145479Smp# endif /* LC_CTYPE */
163759243Sobrien# ifdef NLS_CATALOGS
163859243Sobrien#  ifdef LC_MESSAGES
163959243Sobrien		    (void) setlocale(LC_MESSAGES, "");
164059243Sobrien#  endif /* LC_MESSAGES */
1641145479Smp		    nlsclose();
164259243Sobrien		    nlsinit();
164359243Sobrien# endif /* NLS_CATALOGS */
164459243Sobrien# ifdef SETLOCALEBUG
164559243Sobrien		    dont_free = 0;
164659243Sobrien# endif /* SETLOCALEBUG */
164759243Sobrien# ifdef STRCOLLBUG
164859243Sobrien		    fix_strcoll_bug();
164959243Sobrien# endif /* STRCOLLBUG */
165059243Sobrien		    tw_cmd_free();/* since the collation sequence has changed */
1651167465Smp		    for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
165259243Sobrien			continue;
1653145479Smp		    AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
165459243Sobrien#else /* !NLS */
165559243Sobrien		    AsciiOnly = getenv("LANG") == NULL &&
165659243Sobrien			getenv("LC_CTYPE") == NULL;
165759243Sobrien#endif /* NLS */
165859243Sobrien		    NLSMapsAreInited = 0;
165959243Sobrien		    ed_Init();
166059243Sobrien		    if (MapsAreInited && !NLSMapsAreInited)
166159243Sobrien			ed_InitNLSMaps();
166259243Sobrien
166359243Sobrien		}
166469408Sache#ifdef WINNT_NATIVE
166559243Sobrien		else if (eq(name,(STRtcshlang))) {
166659243Sobrien		    nls_dll_unload();
166759243Sobrien		    nlsinit();
166859243Sobrien		}
166969408Sache#endif /* WINNT_NATIVE */
167059243Sobrien#ifdef COLOR_LS_F
167159243Sobrien		else if (eq(name, STRLS_COLORS))
167259243Sobrien		    parseLS_COLORS(n);
167359243Sobrien#endif /* COLOR_LS_F */
1674100616Smp#ifdef NLS_CATALOGS
1675100616Smp		else if (eq(name, STRNLSPATH)) {
1676145479Smp		    nlsclose();
1677100616Smp		    nlsinit();
1678100616Smp		}
1679100616Smp#endif
168059243Sobrien		/*
168159243Sobrien		 * start again cause the environment changes
168259243Sobrien		 */
168359243Sobrien		break;
168459243Sobrien	    }
1685167465Smp    cleanup_until(name);
168659243Sobrien}
168759243Sobrien
168859243Sobrienvoid
1689167465Smptsetenv(const Char *name, const Char *val)
169059243Sobrien{
169159243Sobrien#ifdef SETENV_IN_LIB
169259243Sobrien/*
169359243Sobrien * XXX: This does not work right, since tcsh cannot track changes to
169459243Sobrien * the environment this way. (the builtin setenv without arguments does
169559243Sobrien * not print the right stuff neither does unsetenv). This was for Mach,
169659243Sobrien * it is not needed anymore.
169759243Sobrien */
169859243Sobrien#undef setenv
1699167465Smp    char   *cname;
170059243Sobrien
1701167465Smp    if (name == NULL)
170259243Sobrien	return;
1703167465Smp    cname = strsave(short2str(name));
1704167465Smp    setenv(cname, short2str(val), 1);
1705167465Smp    xfree(cname);
170659243Sobrien#else /* !SETENV_IN_LIB */
1707145479Smp    Char **ep = STR_environ;
1708145479Smp    const Char *ccp;
1709145479Smp    Char *cp, *dp;
171059243Sobrien    Char   *blk[2];
171159243Sobrien    Char  **oep = ep;
171259243Sobrien
171369408Sache#ifdef WINNT_NATIVE
1714167465Smp    nt_set_env(name,val);
171569408Sache#endif /* WINNT_NATIVE */
171659243Sobrien    for (; *ep; ep++) {
171769408Sache#ifdef WINNT_NATIVE
1718145479Smp	for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp);
1719145479Smp				ccp++, dp++)
172059243Sobrien#else
1721145479Smp	for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++)
172269408Sache#endif /* WINNT_NATIVE */
172359243Sobrien	    continue;
1724145479Smp	if (*ccp != 0 || *dp != '=')
172559243Sobrien	    continue;
172659243Sobrien	cp = Strspl(STRequal, val);
1727167465Smp	xfree(*ep);
172859243Sobrien	*ep = strip(Strspl(name, cp));
1729167465Smp	xfree(cp);
173059243Sobrien	blkfree((Char **) environ);
173159243Sobrien	environ = short2blk(STR_environ);
173259243Sobrien	return;
173359243Sobrien    }
173459243Sobrien    cp = Strspl(name, STRequal);
173559243Sobrien    blk[0] = strip(Strspl(cp, val));
1736167465Smp    xfree(cp);
173759243Sobrien    blk[1] = 0;
173859243Sobrien    STR_environ = blkspl(STR_environ, blk);
173959243Sobrien    blkfree((Char **) environ);
174059243Sobrien    environ = short2blk(STR_environ);
1741167465Smp    xfree(oep);
174259243Sobrien#endif /* SETENV_IN_LIB */
174359243Sobrien}
174459243Sobrien
174559243Sobrienvoid
1746167465SmpUnsetenv(Char *name)
174759243Sobrien{
1748145479Smp    Char **ep = STR_environ;
1749145479Smp    Char *cp, *dp;
175059243Sobrien    Char **oep = ep;
175159243Sobrien
175269408Sache#ifdef WINNT_NATIVE
175359243Sobrien	nt_set_env(name,NULL);
175469408Sache#endif /*WINNT_NATIVE */
175559243Sobrien    for (; *ep; ep++) {
175659243Sobrien	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
175759243Sobrien	    continue;
175859243Sobrien	if (*cp != 0 || *dp != '=')
175959243Sobrien	    continue;
176059243Sobrien	cp = *ep;
176159243Sobrien	*ep = 0;
176259243Sobrien	STR_environ = blkspl(STR_environ, ep + 1);
176359243Sobrien	blkfree((Char **) environ);
176459243Sobrien	environ = short2blk(STR_environ);
176559243Sobrien	*ep = cp;
1766167465Smp	xfree(cp);
1767167465Smp	xfree(oep);
176859243Sobrien	return;
176959243Sobrien    }
177059243Sobrien}
177159243Sobrien
177259243Sobrien/*ARGSUSED*/
177359243Sobrienvoid
1774167465Smpdoumask(Char **v, struct command *c)
177559243Sobrien{
1776145479Smp    Char *cp = v[1];
1777145479Smp    int i;
177859243Sobrien
177959243Sobrien    USE(c);
178059243Sobrien    if (cp == 0) {
178159415Sobrien	i = (int)umask(0);
178259243Sobrien	(void) umask(i);
178359243Sobrien	xprintf("%o\n", i);
178459243Sobrien	return;
178559243Sobrien    }
178659243Sobrien    i = 0;
178759243Sobrien    while (Isdigit(*cp) && *cp != '8' && *cp != '9')
178859243Sobrien	i = i * 8 + *cp++ - '0';
178959243Sobrien    if (*cp || i < 0 || i > 0777)
179059243Sobrien	stderror(ERR_NAME | ERR_MASK);
179159243Sobrien    (void) umask(i);
179259243Sobrien}
179359243Sobrien
179459243Sobrien#ifndef HAVENOLIMIT
179559243Sobrien# ifndef BSDLIMIT
179659243Sobrien   typedef long RLIM_TYPE;
1797145479Smp#  ifdef _OSD_POSIX /* BS2000 */
1798145479Smp#   include <ulimit.h>
1799145479Smp#  endif
180059243Sobrien#  ifndef RLIM_INFINITY
180159243Sobrien#   if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY)
180259243Sobrien    extern RLIM_TYPE ulimit();
180359243Sobrien#   endif /* ! _MINIX && !__clipper__ */
180459243Sobrien#   define RLIM_INFINITY 0x003fffff
180559243Sobrien#   define RLIMIT_FSIZE 1
180659243Sobrien#  endif /* RLIM_INFINITY */
180759243Sobrien#  ifdef aiws
180859243Sobrien#   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
180959243Sobrien#   define RLIMIT_DATA	3
181059243Sobrien#   define RLIMIT_STACK 1005
181159243Sobrien#  else /* aiws */
181259243Sobrien#   define toset(a) ((a) + 1)
181359243Sobrien#  endif /* aiws */
181459243Sobrien# else /* BSDLIMIT */
1815145479Smp#  if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__)
1816100616Smp    typedef rlim_t RLIM_TYPE;
181759243Sobrien#  else
181859243Sobrien#   if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3)
181959243Sobrien     typedef rlim_t RLIM_TYPE;
182059243Sobrien#   else
182159243Sobrien#    if defined(_SX)
182259243Sobrien      typedef long long RLIM_TYPE;
1823100616Smp#    else /* !_SX */
182459243Sobrien      typedef unsigned long RLIM_TYPE;
182559243Sobrien#    endif /* _SX */
182659243Sobrien#   endif /* SOLARIS2 || (sgi && SYSVREL > 3) */
182759243Sobrien#  endif /* BSD4_4 && !__386BSD__  */
182859243Sobrien# endif /* BSDLIMIT */
182959243Sobrien
1830131962Smp# if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT)
183159243Sobrien/* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
183259243Sobrien/* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
183359243Sobrien#  ifndef RLIMIT_CPU
183459243Sobrien#   define RLIMIT_CPU		0
183559243Sobrien#   define RLIMIT_FSIZE		1
183659243Sobrien#   define RLIMIT_DATA		2
183759243Sobrien#   define RLIMIT_STACK		3
183859243Sobrien#   define RLIMIT_CORE		4
183959243Sobrien#   define RLIMIT_RSS		5
184059243Sobrien#   define RLIMIT_NOFILE	6
184159243Sobrien#  endif /* RLIMIT_CPU */
184259243Sobrien#  ifndef RLIM_INFINITY
184359243Sobrien#   define RLIM_INFINITY	0x7fffffff
184459243Sobrien#  endif /* RLIM_INFINITY */
184559243Sobrien   /*
184659243Sobrien    * old versions of HP/UX counted limits in 512 bytes
184759243Sobrien    */
184859243Sobrien#  ifndef SIGRTMIN
184959243Sobrien#   define FILESIZE512
185059243Sobrien#  endif /* SIGRTMIN */
1851131962Smp# endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */
185259243Sobrien
185359243Sobrien# if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX)
185459243Sobrien/* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */
185559243Sobrien/* sh.h.  However, some SVR4 limits are defined in <sys/resource.h>.  Rather */
185659243Sobrien/* than include both and get warnings, we define the extra SVR4 limits here. */
185783098Smp/* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */
185883098Smp/* RLIMIT_VMEM based on it? */
185959243Sobrien#  ifndef RLIMIT_VMEM
186059243Sobrien#   define RLIMIT_VMEM	6
186159243Sobrien#  endif
186259243Sobrien#  ifndef RLIMIT_AS
186359243Sobrien#   define RLIMIT_AS	RLIMIT_VMEM
186459243Sobrien#  endif
186559243Sobrien# endif /* SYSVREL > 3 && BSDLIMIT */
186659243Sobrien
1867232633Smp# if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__))
1868232633Smp#  if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
1869232633Smp#   define RLIMIT_VMEM	RLIMIT_AS
1870232633Smp#  endif
1871232633Smp/*
1872232633Smp * Oh well, <asm-generic/resource.h> has it, but <bits/resource.h> does not
1873232633Smp * Linux headers: When the left hand does not know what the right hand does.
1874232633Smp */
1875232633Smp#  if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME)
1876232633Smp#   define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1)
1877232633Smp#  endif
187883098Smp# endif
187983098Smp
188059243Sobrienstruct limits limits[] =
188159243Sobrien{
188259243Sobrien# ifdef RLIMIT_CPU
188359243Sobrien    { RLIMIT_CPU, 	"cputime",	1,	"seconds"	},
188459243Sobrien# endif /* RLIMIT_CPU */
188559243Sobrien
188659243Sobrien# ifdef RLIMIT_FSIZE
188759243Sobrien#  ifndef aiws
188859243Sobrien    { RLIMIT_FSIZE, 	"filesize",	1024,	"kbytes"	},
188959243Sobrien#  else
189059243Sobrien    { RLIMIT_FSIZE, 	"filesize",	512,	"blocks"	},
189159243Sobrien#  endif /* aiws */
189259243Sobrien# endif /* RLIMIT_FSIZE */
189359243Sobrien
189459243Sobrien# ifdef RLIMIT_DATA
189559243Sobrien    { RLIMIT_DATA, 	"datasize",	1024,	"kbytes"	},
189659243Sobrien# endif /* RLIMIT_DATA */
189759243Sobrien
189859243Sobrien# ifdef RLIMIT_STACK
189959243Sobrien#  ifndef aiws
190059243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024,	"kbytes"	},
190159243Sobrien#  else
190259243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024 * 1024,	"kbytes"},
190359243Sobrien#  endif /* aiws */
190459243Sobrien# endif /* RLIMIT_STACK */
190559243Sobrien
190659243Sobrien# ifdef RLIMIT_CORE
190759243Sobrien    { RLIMIT_CORE, 	"coredumpsize",	1024,	"kbytes"	},
190859243Sobrien# endif /* RLIMIT_CORE */
190959243Sobrien
191059243Sobrien# ifdef RLIMIT_RSS
191159243Sobrien    { RLIMIT_RSS, 	"memoryuse",	1024,	"kbytes"	},
191259243Sobrien# endif /* RLIMIT_RSS */
191359243Sobrien
191459243Sobrien# ifdef RLIMIT_UMEM
191559243Sobrien    { RLIMIT_UMEM, 	"memoryuse",	1024,	"kbytes"	},
191659243Sobrien# endif /* RLIMIT_UMEM */
191759243Sobrien
191859243Sobrien# ifdef RLIMIT_VMEM
191959243Sobrien    { RLIMIT_VMEM, 	"vmemoryuse",	1024,	"kbytes"	},
192059243Sobrien# endif /* RLIMIT_VMEM */
192159243Sobrien
1922145479Smp# if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */
1923145479Smp    { RLIMIT_HEAP,	"heapsize",	1024,	"kbytes"	},
1924145479Smp# endif /* RLIMIT_HEAP */
1925145479Smp
192659243Sobrien# ifdef RLIMIT_NOFILE
192759243Sobrien    { RLIMIT_NOFILE, 	"descriptors", 1,	""		},
192859243Sobrien# endif /* RLIMIT_NOFILE */
192959243Sobrien
193059243Sobrien# ifdef RLIMIT_CONCUR
193159243Sobrien    { RLIMIT_CONCUR, 	"concurrency", 1,	"thread(s)"	},
193259243Sobrien# endif /* RLIMIT_CONCUR */
193359243Sobrien
193459243Sobrien# ifdef RLIMIT_MEMLOCK
193559243Sobrien    { RLIMIT_MEMLOCK,	"memorylocked",	1024,	"kbytes"	},
193659243Sobrien# endif /* RLIMIT_MEMLOCK */
193759243Sobrien
193859243Sobrien# ifdef RLIMIT_NPROC
193959243Sobrien    { RLIMIT_NPROC,	"maxproc",	1,	""		},
194059243Sobrien# endif /* RLIMIT_NPROC */
194159243Sobrien
1942100616Smp# if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
194359243Sobrien    { RLIMIT_OFILE,	"openfiles",	1,	""		},
1944100616Smp# endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */
194559243Sobrien
1946100616Smp# ifdef RLIMIT_SBSIZE
1947100616Smp    { RLIMIT_SBSIZE,	"sbsize",	1,	""		},
1948100616Smp# endif /* RLIMIT_SBSIZE */
1949100616Smp
1950195609Smp# ifdef RLIMIT_SWAP
1951195609Smp    { RLIMIT_SWAP,	"swapsize",	1024,	"kbytes"	},
1952195609Smp# endif /* RLIMIT_SWAP */
1953194767Skib
1954232633Smp# ifdef RLIMIT_LOCKS
1955232633Smp    { RLIMIT_LOCKS,	"maxlocks",	1,	""		},
1956232633Smp# endif /* RLIMIT_LOCKS */
1957232633Smp
1958232633Smp# ifdef RLIMIT_SIGPENDING
1959232633Smp    { RLIMIT_SIGPENDING,"maxsignal",	1,	""		},
1960232633Smp# endif /* RLIMIT_SIGPENDING */
1961232633Smp
1962232633Smp# ifdef RLIMIT_MSGQUEUE
1963232633Smp    { RLIMIT_MSGQUEUE,	"maxmessage",	1,	""		},
1964232633Smp# endif /* RLIMIT_MSGQUEUE */
1965232633Smp
1966232633Smp# ifdef RLIMIT_NICE
1967232633Smp    { RLIMIT_NICE,	"maxnice",	1,	""		},
1968232633Smp# endif /* RLIMIT_NICE */
1969232633Smp
1970232633Smp# ifdef RLIMIT_RTPRIO
1971232633Smp    { RLIMIT_RTPRIO,	"maxrtprio",	1,	""		},
1972232633Smp# endif /* RLIMIT_RTPRIO */
1973232633Smp
1974232633Smp# ifdef RLIMIT_RTTIME
1975232633Smp    { RLIMIT_RTTIME,	"maxrttime",	1,	"usec"		},
1976232633Smp# endif /* RLIMIT_RTTIME */
1977232633Smp
197859243Sobrien    { -1, 		NULL, 		0, 	NULL		}
197959243Sobrien};
198059243Sobrien
1981167465Smpstatic struct limits *findlim	(Char *);
1982167465Smpstatic RLIM_TYPE getval		(struct limits *, Char **);
1983232633Smpstatic int strtail		(Char *, const char *);
1984167465Smpstatic void limtail		(Char *, const char *);
1985232633Smpstatic void limtail2		(Char *, const char *, const char *);
1986167465Smpstatic void plim		(struct limits *, int);
1987167465Smpstatic int setlim		(struct limits *, int, RLIM_TYPE);
198859243Sobrien
198959243Sobrien#ifdef convex
199059243Sobrienstatic  RLIM_TYPE
1991167465Smprestrict_limit(double value)
199259243Sobrien{
199359243Sobrien    /*
199459243Sobrien     * is f too large to cope with? return the maximum or minimum int
199559243Sobrien     */
199659243Sobrien    if (value > (double) INT_MAX)
199759243Sobrien	return (RLIM_TYPE) INT_MAX;
199859243Sobrien    else if (value < (double) INT_MIN)
199959243Sobrien	return (RLIM_TYPE) INT_MIN;
200059243Sobrien    else
200159243Sobrien	return (RLIM_TYPE) value;
200259243Sobrien}
200359243Sobrien#else /* !convex */
200459243Sobrien# define restrict_limit(x)	((RLIM_TYPE) (x))
200559243Sobrien#endif /* convex */
200659243Sobrien
200759243Sobrien
200859243Sobrienstatic struct limits *
2009167465Smpfindlim(Char *cp)
201059243Sobrien{
2011145479Smp    struct limits *lp, *res;
201259243Sobrien
2013167465Smp    res = NULL;
201459243Sobrien    for (lp = limits; lp->limconst >= 0; lp++)
201559243Sobrien	if (prefix(cp, str2short(lp->limname))) {
201659243Sobrien	    if (res)
201759243Sobrien		stderror(ERR_NAME | ERR_AMBIG);
201859243Sobrien	    res = lp;
201959243Sobrien	}
202059243Sobrien    if (res)
202159243Sobrien	return (res);
202259243Sobrien    stderror(ERR_NAME | ERR_LIMIT);
202359243Sobrien    /* NOTREACHED */
202459243Sobrien    return (0);
202559243Sobrien}
202659243Sobrien
202759243Sobrien/*ARGSUSED*/
202859243Sobrienvoid
2029167465Smpdolimit(Char **v, struct command *c)
203059243Sobrien{
2031145479Smp    struct limits *lp;
2032145479Smp    RLIM_TYPE limit;
203359243Sobrien    int    hard = 0;
203459243Sobrien
203559243Sobrien    USE(c);
203659243Sobrien    v++;
203759243Sobrien    if (*v && eq(*v, STRmh)) {
203859243Sobrien	hard = 1;
203959243Sobrien	v++;
204059243Sobrien    }
204159243Sobrien    if (*v == 0) {
204259243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
204359243Sobrien	    plim(lp, hard);
204459243Sobrien	return;
204559243Sobrien    }
204659243Sobrien    lp = findlim(v[0]);
204759243Sobrien    if (v[1] == 0) {
204859243Sobrien	plim(lp, hard);
204959243Sobrien	return;
205059243Sobrien    }
205159243Sobrien    limit = getval(lp, v + 1);
205259243Sobrien    if (setlim(lp, hard, limit) < 0)
205359243Sobrien	stderror(ERR_SILENT);
205459243Sobrien}
205559243Sobrien
205659243Sobrienstatic  RLIM_TYPE
2057167465Smpgetval(struct limits *lp, Char **v)
205859243Sobrien{
2059145479Smp    float f;
206059243Sobrien    Char   *cp = *v++;
206159243Sobrien
206259243Sobrien    f = atof(short2str(cp));
206359243Sobrien
206459243Sobrien# ifdef convex
206559243Sobrien    /*
206659243Sobrien     * is f too large to cope with. limit f to minint, maxint  - X-6768 by
206759243Sobrien     * strike
206859243Sobrien     */
206959243Sobrien    if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
207059243Sobrien	stderror(ERR_NAME | ERR_TOOLARGE);
207159243Sobrien    }
207259243Sobrien# endif /* convex */
207359243Sobrien
207459243Sobrien    while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
207559243Sobrien	cp++;
207659243Sobrien    if (*cp == 0) {
207759243Sobrien	if (*v == 0)
207869408Sache	    return restrict_limit((f * lp->limdiv) + 0.5);
207959243Sobrien	cp = *v;
208059243Sobrien    }
208159243Sobrien    switch (*cp) {
208259243Sobrien# ifdef RLIMIT_CPU
208359243Sobrien    case ':':
208459243Sobrien	if (lp->limconst != RLIMIT_CPU)
208559243Sobrien	    goto badscal;
208659243Sobrien	return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
208759243Sobrien    case 'h':
208859243Sobrien	if (lp->limconst != RLIMIT_CPU)
208959243Sobrien	    goto badscal;
209059243Sobrien	limtail(cp, "hours");
209159243Sobrien	f *= 3600.0;
209259243Sobrien	break;
2093232633Smp# endif /* RLIMIT_CPU */
209459243Sobrien    case 'm':
2095232633Smp# ifdef RLIMIT_CPU
209659243Sobrien	if (lp->limconst == RLIMIT_CPU) {
209759243Sobrien	    limtail(cp, "minutes");
209859243Sobrien	    f *= 60.0;
209959243Sobrien	    break;
210059243Sobrien	}
2101232633Smp# endif /* RLIMIT_CPU */
2102232633Smp	limtail2(cp, "megabytes", "mbytes");
210359243Sobrien	f *= 1024.0 * 1024.0;
210459243Sobrien	break;
2105232633Smp# ifdef RLIMIT_CPU
210659243Sobrien    case 's':
210759243Sobrien	if (lp->limconst != RLIMIT_CPU)
210859243Sobrien	    goto badscal;
210959243Sobrien	limtail(cp, "seconds");
211059243Sobrien	break;
211159243Sobrien# endif /* RLIMIT_CPU */
2112232633Smp    case 'G':
2113232633Smp	*cp = 'g';
2114232633Smp	/*FALLTHROUGH*/
2115232633Smp    case 'g':
2116232633Smp# ifdef RLIMIT_CPU
2117232633Smp	if (lp->limconst == RLIMIT_CPU)
2118232633Smp	    goto badscal;
2119232633Smp# endif /* RLIMIT_CPU */
2120232633Smp	limtail2(cp, "gigabytes", "gbytes");
2121232633Smp	f *= 1024.0 * 1024.0 * 1024.0;
2122232633Smp	break;
212359243Sobrien    case 'M':
212459243Sobrien# ifdef RLIMIT_CPU
212559243Sobrien	if (lp->limconst == RLIMIT_CPU)
212659243Sobrien	    goto badscal;
212759243Sobrien# endif /* RLIMIT_CPU */
212859243Sobrien	*cp = 'm';
2129232633Smp	limtail2(cp, "megabytes", "mbytes");
213059243Sobrien	f *= 1024.0 * 1024.0;
213159243Sobrien	break;
213259243Sobrien    case 'k':
213359243Sobrien# ifdef RLIMIT_CPU
213459243Sobrien	if (lp->limconst == RLIMIT_CPU)
213559243Sobrien	    goto badscal;
213659243Sobrien# endif /* RLIMIT_CPU */
2137232633Smp	limtail2(cp, "kilobytes", "kbytes");
213859243Sobrien	f *= 1024.0;
213959243Sobrien	break;
214059243Sobrien    case 'b':
214159243Sobrien# ifdef RLIMIT_CPU
214259243Sobrien	if (lp->limconst == RLIMIT_CPU)
214359243Sobrien	    goto badscal;
214459243Sobrien# endif /* RLIMIT_CPU */
214559243Sobrien	limtail(cp, "blocks");
214659243Sobrien	f *= 512.0;
214759243Sobrien	break;
214859243Sobrien    case 'u':
214959243Sobrien	limtail(cp, "unlimited");
215059243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
215159243Sobrien    default:
215259243Sobrien# ifdef RLIMIT_CPU
215359243Sobrienbadscal:
215459243Sobrien# endif /* RLIMIT_CPU */
215559243Sobrien	stderror(ERR_NAME | ERR_SCALEF);
215659243Sobrien    }
215759243Sobrien# ifdef convex
215859243Sobrien    return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5));
215959243Sobrien# else
216059243Sobrien    f += 0.5;
2161232633Smp    if (f > (float) ((RLIM_TYPE) RLIM_INFINITY))
216259243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
216359243Sobrien    else
216459243Sobrien	return ((RLIM_TYPE) f);
216559243Sobrien# endif /* convex */
216659243Sobrien}
216759243Sobrien
2168232633Smpstatic int
2169232633Smpstrtail(Char *cp, const char *str)
2170232633Smp{
2171232633Smp    while (*cp && *cp == (Char)*str)
2172232633Smp	cp++, str++;
2173232633Smp    return (*cp != '\0');
2174232633Smp}
2175232633Smp
217659243Sobrienstatic void
2177167465Smplimtail(Char *cp, const char *str)
217859243Sobrien{
2179232633Smp    if (strtail(cp, str))
2180232633Smp	stderror(ERR_BADSCALE, str);
2181232633Smp}
218261515Sobrien
2183232633Smpstatic void
2184232633Smplimtail2(Char *cp, const char *str1, const char *str2)
2185232633Smp{
2186232633Smp    if (strtail(cp, str1) && strtail(cp, str2))
2187232633Smp	stderror(ERR_BADSCALE, str1);
218859243Sobrien}
218959243Sobrien
219059243Sobrien/*ARGSUSED*/
219159243Sobrienstatic void
2192167465Smpplim(struct limits *lp, int hard)
219359243Sobrien{
219459243Sobrien# ifdef BSDLIMIT
219559243Sobrien    struct rlimit rlim;
219659243Sobrien# endif /* BSDLIMIT */
219759243Sobrien    RLIM_TYPE limit;
2198145479Smp    int     xdiv = lp->limdiv;
219959243Sobrien
2200131962Smp    xprintf("%-13.13s", lp->limname);
220159243Sobrien
220259243Sobrien# ifndef BSDLIMIT
220359243Sobrien    limit = ulimit(lp->limconst, 0);
220459243Sobrien#  ifdef aiws
220559243Sobrien    if (lp->limconst == RLIMIT_DATA)
220659243Sobrien	limit -= 0x20000000;
220759243Sobrien#  endif /* aiws */
220859243Sobrien# else /* BSDLIMIT */
220959243Sobrien    (void) getrlimit(lp->limconst, &rlim);
221059243Sobrien    limit = hard ? rlim.rlim_max : rlim.rlim_cur;
221159243Sobrien# endif /* BSDLIMIT */
221259243Sobrien
221359243Sobrien# if !defined(BSDLIMIT) || defined(FILESIZE512)
221459243Sobrien    /*
221559243Sobrien     * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
221659243Sobrien     * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
221759243Sobrien     */
221859243Sobrien    if (lp->limconst == RLIMIT_FSIZE) {
221959243Sobrien	if (limit >= (RLIM_INFINITY / 512))
222059243Sobrien	    limit = RLIM_INFINITY;
222159243Sobrien	else
2222145479Smp	    xdiv = (xdiv == 1024 ? 2 : 1);
222359243Sobrien    }
222459243Sobrien# endif /* !BSDLIMIT || FILESIZE512 */
222559243Sobrien
222659243Sobrien    if (limit == RLIM_INFINITY)
222759243Sobrien	xprintf("unlimited");
222859243Sobrien    else
2229145479Smp# if defined(RLIMIT_CPU) && defined(_OSD_POSIX)
2230145479Smp    if (lp->limconst == RLIMIT_CPU &&
2231145479Smp        (unsigned long)limit >= 0x7ffffffdUL)
2232145479Smp	xprintf("unlimited");
2233145479Smp    else
2234145479Smp# endif
223559243Sobrien# ifdef RLIMIT_CPU
223659243Sobrien    if (lp->limconst == RLIMIT_CPU)
2237167465Smp	psecs(limit);
223859243Sobrien    else
223959243Sobrien# endif /* RLIMIT_CPU */
2240145479Smp	xprintf("%ld %s", (long) (limit / xdiv), lp->limscale);
224159243Sobrien    xputchar('\n');
224259243Sobrien}
224359243Sobrien
224459243Sobrien/*ARGSUSED*/
224559243Sobrienvoid
2246167465Smpdounlimit(Char **v, struct command *c)
224759243Sobrien{
2248145479Smp    struct limits *lp;
224959243Sobrien    int    lerr = 0;
225059243Sobrien    int    hard = 0;
225159243Sobrien    int	   force = 0;
225259243Sobrien
225359243Sobrien    USE(c);
225459243Sobrien    while (*++v && **v == '-') {
225559243Sobrien	Char   *vp = *v;
225659243Sobrien	while (*++vp)
225759243Sobrien	    switch (*vp) {
225859243Sobrien	    case 'f':
225959243Sobrien		force = 1;
226059243Sobrien		break;
226159243Sobrien	    case 'h':
226259243Sobrien		hard = 1;
226359243Sobrien		break;
226459243Sobrien	    default:
226559243Sobrien		stderror(ERR_ULIMUS);
226659243Sobrien		break;
226759243Sobrien	    }
226859243Sobrien    }
226959243Sobrien
227059243Sobrien    if (*v == 0) {
227159243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
227259243Sobrien	    if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
227359243Sobrien		lerr++;
227459243Sobrien	if (!force && lerr)
227559243Sobrien	    stderror(ERR_SILENT);
227659243Sobrien	return;
227759243Sobrien    }
227859243Sobrien    while (*v) {
227959243Sobrien	lp = findlim(*v++);
228059243Sobrien	if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force)
228159243Sobrien	    stderror(ERR_SILENT);
228259243Sobrien    }
228359243Sobrien}
228459243Sobrien
228559243Sobrienstatic int
2286167465Smpsetlim(struct limits *lp, int hard, RLIM_TYPE limit)
228759243Sobrien{
228859243Sobrien# ifdef BSDLIMIT
228959243Sobrien    struct rlimit rlim;
229059243Sobrien
229159243Sobrien    (void) getrlimit(lp->limconst, &rlim);
229259243Sobrien
229359243Sobrien#  ifdef FILESIZE512
229459243Sobrien    /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
229559243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
229659243Sobrien	limit /= 512;
229759243Sobrien#  endif /* FILESIZE512 */
229859243Sobrien    if (hard)
229959243Sobrien	rlim.rlim_max = limit;
230059243Sobrien    else if (limit == RLIM_INFINITY && euid != 0)
230159243Sobrien	rlim.rlim_cur = rlim.rlim_max;
230259243Sobrien    else
230359243Sobrien	rlim.rlim_cur = limit;
230459243Sobrien
2305100616Smp    if (rlim.rlim_cur > rlim.rlim_max)
2306100616Smp	rlim.rlim_max = rlim.rlim_cur;
2307100616Smp
230859243Sobrien    if (setrlimit(lp->limconst, &rlim) < 0) {
230959243Sobrien# else /* BSDLIMIT */
231059243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
231159243Sobrien	limit /= 512;
231259243Sobrien# ifdef aiws
231359243Sobrien    if (lp->limconst == RLIMIT_DATA)
231459243Sobrien	limit += 0x20000000;
231559243Sobrien# endif /* aiws */
231659243Sobrien    if (ulimit(toset(lp->limconst), limit) < 0) {
231759243Sobrien# endif /* BSDLIMIT */
2318145479Smp        int err;
2319145479Smp        char *op, *type;
2320145479Smp
2321145479Smp	err = errno;
2322145479Smp	op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") :
2323145479Smp		     	CGETS(15, 3, "set"));
2324167465Smp	cleanup_push(op, xfree);
2325145479Smp	type = strsave(hard ? CGETS(15, 4, " hard") : "");
2326167465Smp	cleanup_push(type, xfree);
2327100616Smp	xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname,
2328145479Smp	    lp->limname, op, type, strerror(err));
2329167465Smp	cleanup_until(op);
233059243Sobrien	return (-1);
233159243Sobrien    }
233259243Sobrien    return (0);
233359243Sobrien}
233459243Sobrien
233559243Sobrien#endif /* !HAVENOLIMIT */
233659243Sobrien
233759243Sobrien/*ARGSUSED*/
233859243Sobrienvoid
2339167465Smpdosuspend(Char **v, struct command *c)
234059243Sobrien{
234159243Sobrien#ifdef BSDJOBS
2342167465Smp    struct sigaction old;
234359243Sobrien#endif /* BSDJOBS */
2344195609Smp
234559243Sobrien    USE(c);
234659243Sobrien    USE(v);
234759243Sobrien
234859243Sobrien    if (loginsh)
234959243Sobrien	stderror(ERR_SUSPLOG);
235059243Sobrien    untty();
235159243Sobrien
235259243Sobrien#ifdef BSDJOBS
2353167465Smp    sigaction(SIGTSTP, NULL, &old);
2354167465Smp    signal(SIGTSTP, SIG_DFL);
235559243Sobrien    (void) kill(0, SIGTSTP);
235659243Sobrien    /* the shell stops here */
2357167465Smp    sigaction(SIGTSTP, &old, NULL);
235859243Sobrien#else /* !BSDJOBS */
235959243Sobrien    stderror(ERR_JOBCONTROL);
236059243Sobrien#endif /* BSDJOBS */
236159243Sobrien
236259243Sobrien#ifdef BSDJOBS
236359243Sobrien    if (tpgrp != -1) {
2364195609Smp	if (grabpgrp(FSHTTY, opgrp) == -1)
2365131962Smp	    stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno));
236659243Sobrien	(void) setpgid(0, shpgrp);
236759243Sobrien	(void) tcsetpgrp(FSHTTY, shpgrp);
236859243Sobrien    }
236959243Sobrien#endif /* BSDJOBS */
237059243Sobrien    (void) setdisc(FSHTTY);
237159243Sobrien}
237259243Sobrien
237359243Sobrien/* This is the dreaded EVAL built-in.
237459243Sobrien *   If you don't fiddle with file descriptors, and reset didfds,
237559243Sobrien *   this command will either ignore redirection inside or outside
237659243Sobrien *   its arguments, e.g. eval "date >x"  vs.  eval "date" >x
237759243Sobrien *   The stuff here seems to work, but I did it by trial and error rather
237859243Sobrien *   than really knowing what was going on.  If tpgrp is zero, we are
237959243Sobrien *   probably a background eval, e.g. "eval date &", and we want to
238059243Sobrien *   make sure that any processes we start stay in our pgrp.
238159243Sobrien *   This is also the case for "time eval date" -- stay in same pgrp.
238259243Sobrien *   Otherwise, under stty tostop, processes will stop in the wrong
238359243Sobrien *   pgrp, with no way for the shell to get them going again.  -IAN!
238459243Sobrien */
238559243Sobrien
2386167465Smpstruct doeval_state
238759243Sobrien{
2388167465Smp    Char **evalvec, *evalp;
2389167465Smp    int didfds;
239059243Sobrien#ifndef CLOSE_ON_EXEC
2391167465Smp    int didcch;
2392167465Smp#endif
2393167465Smp    int saveIN, saveOUT, saveDIAG;
2394167465Smp    int SHIN, SHOUT, SHDIAG;
2395167465Smp};
239659243Sobrien
2397167465Smpstatic void
2398167465Smpdoeval_cleanup(void *xstate)
2399167465Smp{
2400167465Smp    struct doeval_state *state;
2401167465Smp
2402167465Smp    state = xstate;
2403167465Smp    evalvec = state->evalvec;
2404167465Smp    evalp = state->evalp;
2405167465Smp    doneinp = 0;
240659243Sobrien#ifndef CLOSE_ON_EXEC
2407167465Smp    didcch = state->didcch;
240859243Sobrien#endif /* CLOSE_ON_EXEC */
2409167465Smp    didfds = state->didfds;
2410167465Smp    xclose(SHIN);
2411167465Smp    xclose(SHOUT);
2412167465Smp    xclose(SHDIAG);
2413167465Smp    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
2414167465Smp    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
2415167465Smp    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
2416167465Smp}
241759243Sobrien
2418195609Smpstatic Char **Ggv;
2419167465Smp/*ARGSUSED*/
2420167465Smpvoid
2421167465Smpdoeval(Char **v, struct command *c)
2422167465Smp{
2423167465Smp    struct doeval_state state;
2424195609Smp    int gflag, my_reenter;
2425167465Smp    Char **gv;
2426195609Smp    jmp_buf_t osetexit;
242759243Sobrien
2428167465Smp    USE(c);
2429167465Smp    v++;
2430167465Smp    if (*v == 0)
243159243Sobrien	return;
2432167465Smp    gflag = tglob(v);
243359243Sobrien    if (gflag) {
2434167465Smp	gv = v = globall(v, gflag);
2435167465Smp	if (v == 0)
243659243Sobrien	    stderror(ERR_NOMATCH);
2437167465Smp	cleanup_push(gv, blk_cleanup);
2438167465Smp	v = copyblk(v);
243959243Sobrien    }
244059243Sobrien    else {
244159243Sobrien	gv = NULL;
2442167465Smp	v = copyblk(v);
2443167465Smp	trim(v);
244459243Sobrien    }
244559243Sobrien
2446195609Smp    Ggv = gv;
2447167465Smp    state.evalvec = evalvec;
2448167465Smp    state.evalp = evalp;
2449167465Smp    state.didfds = didfds;
245059243Sobrien#ifndef CLOSE_ON_EXEC
2451167465Smp    state.didcch = didcch;
245259243Sobrien#endif /* CLOSE_ON_EXEC */
2453167465Smp    state.SHIN = SHIN;
2454167465Smp    state.SHOUT = SHOUT;
2455167465Smp    state.SHDIAG = SHDIAG;
245659243Sobrien
2457167465Smp    (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1);
2458167465Smp    (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1);
2459167465Smp    (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1);
2460167465Smp
2461167465Smp    cleanup_push(&state, doeval_cleanup);
2462167465Smp
2463195609Smp    getexit(osetexit);
2464195609Smp
2465195609Smp    /* PWP: setjmp/longjmp bugfix for optimizing compilers */
2466195609Smp#ifdef cray
2467195609Smp    my_reenter = 1;             /* assume non-zero return val */
2468195609Smp    if (setexit() == 0) {
2469195609Smp	my_reenter = 0;         /* Oh well, we were wrong */
2470195609Smp#else /* !cray */
2471195609Smp    if ((my_reenter = setexit()) == 0) {
2472195609Smp#endif /* cray */
2473195609Smp	evalvec = v;
2474195609Smp	evalp = 0;
2475195609Smp	(void)close_on_exec(SHIN = dcopy(0, -1), 1);
2476195609Smp	(void)close_on_exec(SHOUT = dcopy(1, -1), 1);
2477195609Smp	(void)close_on_exec(SHDIAG = dcopy(2, -1), 1);
247859243Sobrien#ifndef CLOSE_ON_EXEC
2479195609Smp	didcch = 0;
248059243Sobrien#endif /* CLOSE_ON_EXEC */
2481195609Smp	didfds = 0;
2482195609Smp	gv = Ggv;
2483195609Smp	process(0);
2484195609Smp	Ggv = gv;
2485195609Smp    }
248659243Sobrien
2487195609Smp    if (my_reenter == 0) {
2488195609Smp	cleanup_until(&state);
2489195609Smp	if (Ggv)
2490195609Smp	    cleanup_until(Ggv);
2491195609Smp    }
2492167465Smp
2493195609Smp    resexit(osetexit);
2494195609Smp    if (my_reenter)
2495195609Smp	stderror(ERR_SILENT);
249659243Sobrien}
249759243Sobrien
249859243Sobrien/*************************************************************************/
249959243Sobrien/* print list of builtin commands */
250059243Sobrien
2501167465Smpstatic void
2502167465Smplbuffed_cleanup (void *dummy)
2503167465Smp{
2504167465Smp    USE(dummy);
2505167465Smp    lbuffed = 1;
2506167465Smp}
2507167465Smp
250859243Sobrien/*ARGSUSED*/
250959243Sobrienvoid
2510167465Smpdobuiltins(Char **v, struct command *c)
251159243Sobrien{
251259243Sobrien    /* would use print_by_column() in tw.parse.c but that assumes
251359243Sobrien     * we have an array of Char * to pass.. (sg)
251459243Sobrien     */
2515167465Smp    const struct biltins *b;
2516145479Smp    int row, col, columns, rows;
251759243Sobrien    unsigned int w, maxwidth;
251859243Sobrien
251959243Sobrien    USE(c);
252059243Sobrien    USE(v);
252159243Sobrien    lbuffed = 0;		/* turn off line buffering */
2522167465Smp    cleanup_push(&lbuffed, lbuffed_cleanup);
252359243Sobrien
252459243Sobrien    /* find widest string */
252559243Sobrien    for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
252659243Sobrien	maxwidth = max(maxwidth, strlen(b->bname));
252759243Sobrien    ++maxwidth;					/* for space */
252859243Sobrien
252959243Sobrien    columns = (TermH + 1) / maxwidth;	/* PWP: terminal size change */
253059243Sobrien    if (!columns)
253159243Sobrien	columns = 1;
253259243Sobrien    rows = (nbfunc + (columns - 1)) / columns;
253359243Sobrien
253459243Sobrien    for (b = bfunc, row = 0; row < rows; row++) {
253559243Sobrien	for (col = 0; col < columns; col++) {
253659243Sobrien	    if (b < &bfunc[nbfunc]) {
253759243Sobrien		w = strlen(b->bname);
253859243Sobrien		xprintf("%s", b->bname);
253959243Sobrien		if (col < (columns - 1))	/* Not last column? */
254059243Sobrien		    for (; w < maxwidth; w++)
254159243Sobrien			xputchar(' ');
254259243Sobrien		++b;
254359243Sobrien	    }
254459243Sobrien	}
254559243Sobrien	if (row < (rows - 1)) {
254659243Sobrien	    if (Tty_raw_mode)
254759243Sobrien		xputchar('\r');
254859243Sobrien	    xputchar('\n');
254959243Sobrien	}
255059243Sobrien    }
255169408Sache#ifdef WINNT_NATIVE
255259243Sobrien    nt_print_builtins(maxwidth);
255359243Sobrien#else
255459243Sobrien    if (Tty_raw_mode)
255559243Sobrien	xputchar('\r');
255659243Sobrien    xputchar('\n');
255769408Sache#endif /* WINNT_NATIVE */
255859243Sobrien
2559167465Smp    cleanup_until(&lbuffed);		/* turn back on line buffering */
256059243Sobrien    flush();
256159243Sobrien}
256259243Sobrien
2563145479Smp#ifdef NLS_CATALOGS
2564145479Smpchar *
2565167465Smpxcatgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2566145479Smp{
2567167465Smp    char *res;
2568167465Smp
2569167465Smp    errno = 0;
2570167465Smp    while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) {
2571167465Smp	handle_pending_signals();
2572167465Smp	errno = 0;
2573167465Smp    }
2574167465Smp    return res;
2575167465Smp}
2576167465Smp
2577167465Smp
2578167465Smp# if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2579167465Smpchar *
2580167465Smpiconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2581167465Smp{
2582145479Smp    static char *buf = NULL;
2583145479Smp    static size_t buf_size = 0;
2584145479Smp
2585145479Smp    char *orig, *dest, *p;
2586167465Smp    ICONV_CONST char *src;
2587145479Smp    size_t src_size, dest_size;
2588145479Smp
2589167465Smp    orig = xcatgets(ctd, set_id, msg_id, s);
2590145479Smp    if (catgets_iconv == (iconv_t)-1 || orig == s)
2591145479Smp        return orig;
2592145479Smp    src = orig;
2593145479Smp    src_size = strlen(src) + 1;
2594145479Smp    if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL)
2595145479Smp	return orig;
2596145479Smp    dest = buf;
2597145479Smp    while (src_size != 0) {
2598145479Smp        dest_size = buf + buf_size - dest;
2599145479Smp	if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size)
2600145479Smp	    == (size_t)-1) {
2601145479Smp	    switch (errno) {
2602145479Smp	    case E2BIG:
2603145479Smp		if ((p = xrealloc(buf, buf_size * 2)) == NULL)
2604145479Smp		    return orig;
2605145479Smp		buf_size *= 2;
2606145479Smp		dest = p + (dest - buf);
2607145479Smp		buf = p;
2608145479Smp		break;
2609145479Smp
2610145479Smp	    case EILSEQ: case EINVAL: default:
2611145479Smp		return orig;
2612145479Smp	    }
2613145479Smp	}
2614145479Smp    }
2615145479Smp    return buf;
2616145479Smp}
2617167465Smp# endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2618167465Smp#endif /* NLS_CATALOGS */
2619145479Smp
262059243Sobrienvoid
2621167465Smpnlsinit(void)
262259243Sobrien{
262359243Sobrien#ifdef NLS_CATALOGS
2624167465Smp    static const char default_catalog[] = "tcsh";
262569408Sache
2626167465Smp    char *catalog = (char *)(intptr_t)default_catalog;
2627167465Smp
262869408Sache    if (adrof(STRcatalog) != NULL)
2629167465Smp	catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog)));
2630232633Smp#ifdef NL_CAT_LOCALE /* POSIX-compliant. */
2631232633Smp    /*
2632232633Smp     * Check if LC_MESSAGES is set in the environment and use it, if so.
2633232633Smp     * If not, fall back to the setting of LANG.
2634232633Smp     */
2635232633Smp    catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0);
2636232633Smp#else /* pre-POSIX */
2637232633Smp# ifndef MCLoadBySet
2638232633Smp#  define MCLoadBySet 0
2639232633Smp#  endif
264069408Sache    catd = catopen(catalog, MCLoadBySet);
2641232633Smp#endif
2642167465Smp    if (catalog != default_catalog)
2643167465Smp	xfree(catalog);
2644167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2645167465Smp    /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */
2646145479Smp    catgets_iconv = iconv_open (nl_langinfo (CODESET),
2647232633Smp				xcatgets(catd, 255, 1, "UTF-8"));
2648167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
264969408Sache#endif /* NLS_CATALOGS */
265069408Sache#ifdef WINNT_NATIVE
265159243Sobrien    nls_dll_init();
265269408Sache#endif /* WINNT_NATIVE */
265359243Sobrien    errinit();		/* init the errorlist in correct locale */
265459243Sobrien    mesginit();		/* init the messages for signals */
265559243Sobrien    dateinit();		/* init the messages for dates */
265659243Sobrien    editinit();		/* init the editor messages */
265759243Sobrien    terminit();		/* init the termcap messages */
265859243Sobrien}
2659145479Smp
2660145479Smpvoid
2661167465Smpnlsclose(void)
2662145479Smp{
2663145479Smp#ifdef NLS_CATALOGS
2664167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2665145479Smp    if (catgets_iconv != (iconv_t)-1) {
2666145479Smp	iconv_close(catgets_iconv);
2667145479Smp	catgets_iconv = (iconv_t)-1;
2668145479Smp    }
2669167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2670167465Smp    if (catd != (nl_catd)-1) {
2671167465Smp	/*
2672167465Smp	 * catclose can call other functions which can call longjmp
2673167465Smp	 * making us re-enter this code. Prevent infinite recursion
2674167465Smp	 * by resetting catd. Problem reported and solved by:
2675167465Smp	 * Gerhard Niklasch
2676167465Smp	 */
2677167465Smp	nl_catd oldcatd = catd;
2678167465Smp	catd = (nl_catd)-1;
2679167465Smp	while (catclose(oldcatd) == -1 && errno == EINTR)
2680167465Smp	    handle_pending_signals();
2681167465Smp    }
2682145479Smp#endif /* NLS_CATALOGS */
2683145479Smp}
2684