sh.func.c revision 232633
116715Ssherman/* $Header: /p/tcsh/cvsroot/tcsh/sh.func.c,v 3.162 2011/02/26 00:07:06 christos Exp $ */
216715Ssherman/*
316715Ssherman * sh.func.c: csh builtin functions
416715Ssherman */
516715Ssherman/*-
616715Ssherman * Copyright (c) 1980, 1991 The Regents of the University of California.
716715Ssherman * All rights reserved.
816715Ssherman *
916715Ssherman * Redistribution and use in source and binary forms, with or without
1016715Ssherman * modification, are permitted provided that the following conditions
1116715Ssherman * are met:
1216715Ssherman * 1. Redistributions of source code must retain the above copyright
1316715Ssherman *    notice, this list of conditions and the following disclaimer.
1416715Ssherman * 2. Redistributions in binary form must reproduce the above copyright
1516715Ssherman *    notice, this list of conditions and the following disclaimer in the
1616715Ssherman *    documentation and/or other materials provided with the distribution.
1716715Ssherman * 3. Neither the name of the University nor the names of its contributors
1816715Ssherman *    may be used to endorse or promote products derived from this software
1916715Ssherman *    without specific prior written permission.
2016715Ssherman *
2116715Ssherman * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2216715Ssherman * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2316715Ssherman * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2416715Ssherman * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2516715Ssherman * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2616715Ssherman * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2716715Ssherman * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2816715Ssherman * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2916715Ssherman * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3016715Ssherman * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3116715Ssherman * SUCH DAMAGE.
3216715Ssherman */
3316715Ssherman#include "sh.h"
3416715Ssherman
3516715SshermanRCSID("$tcsh: sh.func.c,v 3.162 2011/02/26 00:07:06 christos Exp $")
3616715Ssherman
3716715Ssherman#include "ed.h"
3816715Ssherman#include "tw.h"
3916715Ssherman#include "tc.h"
4016715Ssherman#ifdef WINNT_NATIVE
4116715Ssherman#include "nt.const.h"
4216715Ssherman#endif /* WINNT_NATIVE */
4316715Ssherman
4416715Ssherman#if defined (NLS_CATALOGS) && defined(HAVE_ICONV)
4516715Sshermanstatic iconv_t catgets_iconv; /* Or (iconv_t)-1 */
4616715Ssherman#endif
4716715Ssherman
4816715Ssherman/*
4916715Ssherman * C shell
5016715Ssherman */
5116715Ssherman
5216715Sshermanextern int MapsAreInited;
5316715Sshermanextern int NLSMapsAreInited;
5416715Sshermanextern int GotTermCaps;
5516715Ssherman
5616715Sshermanstatic int zlast = -1;
5716715Ssherman
5816715Sshermanstatic	void	islogin		(void);
5916715Sshermanstatic	void	preread		(void);
6016715Sshermanstatic	void	doagain		(void);
6116715Sshermanstatic  const char *isrchx	(int);
6216715Sshermanstatic	void	search		(int, int, Char *);
6316715Sshermanstatic	int	getword		(struct Strbuf *);
6416715Sshermanstatic	struct wordent	*histgetword	(struct wordent *);
6516715Sshermanstatic	void	toend		(void);
6616715Sshermanstatic	void	xecho		(int, Char **);
6716715Sshermanstatic	int	islocale_var	(Char *);
6816715Sshermanstatic	void	wpfree		(struct whyle *);
6916715Ssherman
7016715Sshermanconst struct biltins *
7116715Sshermanisbfunc(struct command *t)
7216715Ssherman{
7316715Ssherman    Char *cp = t->t_dcom[0];
7416715Ssherman    const struct biltins *bp, *bp1, *bp2;
7516715Ssherman    static struct biltins label = {"", dozip, 0, 0};
7616715Ssherman    static struct biltins foregnd = {"%job", dofg1, 0, 0};
7716715Ssherman    static struct biltins backgnd = {"%job &", dobg1, 0, 0};
7816715Ssherman
7916715Ssherman    /*
8016715Ssherman     * We never match a builtin that has quoted the first
8116715Ssherman     * character; this has been the traditional way to escape
8216715Ssherman     * builtin commands.
8316715Ssherman     */
8416715Ssherman    if (*cp & QUOTE)
8516715Ssherman	return NULL;
8616715Ssherman
8716715Ssherman    if (*cp != ':' && lastchr(cp) == ':') {
8816715Ssherman	label.bname = short2str(cp);
8916715Ssherman	return (&label);
9016715Ssherman    }
9116715Ssherman    if (*cp == '%') {
9216715Ssherman	if (t->t_dflg & F_AMPERSAND) {
9316715Ssherman	    t->t_dflg &= ~F_AMPERSAND;
9416715Ssherman	    backgnd.bname = short2str(cp);
9516715Ssherman	    return (&backgnd);
9616715Ssherman	}
9716715Ssherman	foregnd.bname = short2str(cp);
9816715Ssherman	return (&foregnd);
9916715Ssherman    }
10016715Ssherman#ifdef WARP
10116715Ssherman    /*
10216715Ssherman     * This is a perhaps kludgy way to determine if the warp builtin is to be
10316715Ssherman     * acknowledged or not.  If checkwarp() fails, then we are to assume that
10416715Ssherman     * the warp command is invalid, and carry on as we would handle any other
10516715Ssherman     * non-builtin command.         -- JDK 2/4/88
10616715Ssherman     */
10716715Ssherman    if (eq(STRwarp, cp) && !checkwarp()) {
10816715Ssherman	return (0);		/* this builtin disabled */
10916715Ssherman    }
11016715Ssherman#endif /* WARP */
11116715Ssherman    /*
11216715Ssherman     * Binary search Bp1 is the beginning of the current search range. Bp2 is
11316715Ssherman     * one past the end.
11416715Ssherman     */
11516715Ssherman    for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
11616715Ssherman	int i;
11716715Ssherman
11816715Ssherman	bp = bp1 + ((bp2 - bp1) >> 1);
11916715Ssherman	if ((i = ((char) *cp) - *bp->bname) == 0 &&
12016715Ssherman	    (i = StrQcmp(cp, str2short(bp->bname))) == 0)
12116715Ssherman	    return bp;
12216715Ssherman	if (i < 0)
12316715Ssherman	    bp2 = bp;
12416715Ssherman	else
12516715Ssherman	    bp1 = bp + 1;
12616715Ssherman    }
12716715Ssherman#ifdef WINNT_NATIVE
12816715Ssherman    return nt_check_additional_builtins(cp);
12916715Ssherman#endif /*WINNT_NATIVE*/
13016715Ssherman    return (0);
13116715Ssherman}
13216715Ssherman
13316715Sshermanvoid
13416715Sshermanfunc(struct command *t, const struct biltins *bp)
13516715Ssherman{
13616715Ssherman    int     i;
13716715Ssherman
13816715Ssherman    xechoit(t->t_dcom);
13916715Ssherman    setname(bp->bname);
14016715Ssherman    i = blklen(t->t_dcom) - 1;
14116715Ssherman    if (i < bp->minargs)
14216715Ssherman	stderror(ERR_NAME | ERR_TOOFEW);
14316715Ssherman    if (i > bp->maxargs)
14416715Ssherman	stderror(ERR_NAME | ERR_TOOMANY);
14516715Ssherman    (*bp->bfunct) (t->t_dcom, t);
14616715Ssherman}
14716715Ssherman
14816715Ssherman/*ARGSUSED*/
14916715Sshermanvoid
15016715Sshermandoonintr(Char **v, struct command *c)
15116715Ssherman{
15216715Ssherman    Char *cp;
15316715Ssherman    Char *vv = v[1];
15416715Ssherman
15516715Ssherman    USE(c);
15616715Ssherman    if (parintr.sa_handler == SIG_IGN)
15716715Ssherman	return;
15816715Ssherman    if (setintr && intty)
15916715Ssherman	stderror(ERR_NAME | ERR_TERMINAL);
16016715Ssherman    cp = gointr;
16116715Ssherman    gointr = 0;
16216715Ssherman    xfree(cp);
16316715Ssherman    if (vv == 0) {
16416715Ssherman	if (setintr)
16516715Ssherman	    sigset_interrupting(SIGINT, queue_pintr);
16616715Ssherman	else
16716715Ssherman	    (void) signal(SIGINT, SIG_DFL);
16816715Ssherman	gointr = 0;
16916715Ssherman    }
17016715Ssherman    else if (eq((vv = strip(vv)), STRminus)) {
17116715Ssherman	(void) signal(SIGINT, SIG_IGN);
17216715Ssherman	gointr = Strsave(STRminus);
17316715Ssherman    }
17416715Ssherman    else {
17516715Ssherman	gointr = Strsave(vv);
17616715Ssherman	sigset_interrupting(SIGINT, queue_pintr);
17716715Ssherman    }
17816715Ssherman}
17916715Ssherman
18016715Ssherman/*ARGSUSED*/
18116715Sshermanvoid
18216715Sshermandonohup(Char **v, struct command *c)
18316715Ssherman{
18416715Ssherman    USE(c);
18516715Ssherman    USE(v);
18616715Ssherman    if (intty)
18716715Ssherman	stderror(ERR_NAME | ERR_TERMINAL);
18816715Ssherman    if (setintr == 0) {
18916715Ssherman	(void) signal(SIGHUP, SIG_IGN);
19016715Ssherman	phup_disabled = 1;
19116715Ssherman#ifdef CC
19216715Ssherman	submit(getpid());
19316715Ssherman#endif /* CC */
19416715Ssherman    }
19516715Ssherman}
19616715Ssherman
19716715Ssherman/*ARGSUSED*/
19816715Sshermanvoid
19916715Sshermandohup(Char **v, struct command *c)
20016715Ssherman{
20116715Ssherman    USE(c);
20216715Ssherman    USE(v);
20316715Ssherman    if (intty)
20416715Ssherman	stderror(ERR_NAME | ERR_TERMINAL);
20516715Ssherman    if (setintr == 0)
20616715Ssherman	(void) signal(SIGHUP, SIG_DFL);
20716715Ssherman}
20816715Ssherman
20916715Ssherman
21016715Ssherman/*ARGSUSED*/
21116715Sshermanvoid
21216715Sshermandozip(Char **v, struct command *c)
21316715Ssherman{
21416715Ssherman    USE(c);
21516715Ssherman    USE(v);
21616715Ssherman}
21716715Ssherman
21816715Ssherman/*ARGSUSED*/
21916715Sshermanvoid
22016715Sshermandofiletest(Char **v, struct command *c)
22116715Ssherman{
22216715Ssherman    Char **globbed, **fileptr, *ftest, *res;
22316715Ssherman
22416715Ssherman    USE(c);
22516715Ssherman    if (*(ftest = *++v) != '-')
22616715Ssherman	stderror(ERR_NAME | ERR_FILEINQ);
22716715Ssherman    ++v;
22816715Ssherman
22916715Ssherman    v = glob_all_or_error(v);
23016715Ssherman    globbed = v;
23116715Ssherman    cleanup_push(globbed, blk_cleanup);
23216715Ssherman
23316715Ssherman    while (*(fileptr = v++) != '\0') {
23416715Ssherman	res = filetest(ftest, &fileptr, 0);
23516715Ssherman	cleanup_push(res, xfree);
23616715Ssherman	xprintf("%S", res);
23716715Ssherman	cleanup_until(res);
23816715Ssherman	if (*v)
23916715Ssherman	    xprintf(" ");
24016715Ssherman    }
24116715Ssherman    xprintf("\n");
24216715Ssherman
24316715Ssherman    cleanup_until(globbed);
24416715Ssherman}
24516715Ssherman
24616715Sshermanvoid
24716715Sshermanprvars(void)
24816715Ssherman{
24916715Ssherman    plist(&shvhed, VAR_ALL);
25016715Ssherman}
25116715Ssherman
25216715Ssherman/*ARGSUSED*/
25316715Sshermanvoid
25416715Sshermandoalias(Char **v, struct command *c)
25516715Ssherman{
25616715Ssherman    struct varent *vp;
25716715Ssherman    Char *p;
25816715Ssherman
25916715Ssherman    USE(c);
26016715Ssherman    v++;
26116715Ssherman    p = *v++;
26216715Ssherman    if (p == 0)
26316715Ssherman	plist(&aliases, VAR_ALL);
26416715Ssherman    else if (*v == 0) {
26516715Ssherman	vp = adrof1(strip(p), &aliases);
26616715Ssherman	if (vp && vp->vec)
26716715Ssherman	    blkpr(vp->vec), xputchar('\n');
26816715Ssherman    }
26916715Ssherman    else {
27016715Ssherman	if (eq(p, STRalias) || eq(p, STRunalias)) {
27116715Ssherman	    setname(short2str(p));
27216715Ssherman	    stderror(ERR_NAME | ERR_DANGER);
27316715Ssherman	}
27416715Ssherman	set1(strip(p), saveblk(v), &aliases, VAR_READWRITE);
27516715Ssherman	tw_cmd_free();
27616715Ssherman    }
27716715Ssherman}
27816715Ssherman
27916715Ssherman/*ARGSUSED*/
28016715Sshermanvoid
28116715Sshermanunalias(Char **v, struct command *c)
28216715Ssherman{
28316715Ssherman    USE(c);
28416715Ssherman    unset1(v, &aliases);
28516715Ssherman    tw_cmd_free();
28616715Ssherman}
28716715Ssherman
28816715Ssherman/*ARGSUSED*/
28916715Sshermanvoid
29016715Sshermandologout(Char **v, struct command *c)
29116715Ssherman{
29216715Ssherman    USE(c);
29316715Ssherman    USE(v);
29416715Ssherman    islogin();
29516715Ssherman    goodbye(NULL, NULL);
29616715Ssherman}
29716715Ssherman
29816715Ssherman/*ARGSUSED*/
29916715Sshermanvoid
30016715Sshermandologin(Char **v, struct command *c)
30116715Ssherman{
30216715Ssherman#ifdef WINNT_NATIVE
30316715Ssherman    USE(c);
30416715Ssherman    USE(v);
30516715Ssherman#else /* !WINNT_NATIVE */
30616715Ssherman    char **p = short2blk(v);
30716715Ssherman
30816715Ssherman    USE(c);
30916715Ssherman    cleanup_push((Char **)p, blk_cleanup);
31016715Ssherman    islogin();
31116715Ssherman    rechist(NULL, adrof(STRsavehist) != NULL);
31216715Ssherman    sigaction(SIGTERM, &parterm, NULL);
31316715Ssherman    (void) execv(_PATH_BIN_LOGIN, p);
31416715Ssherman    (void) execv(_PATH_USRBIN_LOGIN, p);
31516715Ssherman    cleanup_until((Char **)p);
31616715Ssherman    untty();
31716715Ssherman    xexit(1);
31816715Ssherman#endif /* !WINNT_NATIVE */
31916715Ssherman}
32016715Ssherman
32116715Ssherman
32216715Ssherman#ifdef NEWGRP
32316715Ssherman/*ARGSUSED*/
32416715Sshermanvoid
32516715Sshermandonewgrp(Char **v, struct command *c)
32616715Ssherman{
32716715Ssherman    char **p;
32816715Ssherman    if (chkstop == 0 && setintr)
32916715Ssherman	panystop(0);
33016715Ssherman    sigaction(SIGTERM, &parterm, NULL);
33116715Ssherman    p = short2blk(v);
33216715Ssherman    /*
33316715Ssherman     * From Beto Appleton (beto@aixwiz.austin.ibm.com)
33416715Ssherman     * Newgrp can take 2 arguments...
33516715Ssherman     */
33616715Ssherman    (void) execv(_PATH_BIN_NEWGRP, p);
33716715Ssherman    (void) execv(_PATH_USRBIN_NEWGRP, p);
33816715Ssherman    blkfree((Char **) p);
33916715Ssherman    untty();
34016715Ssherman    xexit(1);
34116715Ssherman}
34216715Ssherman#endif /* NEWGRP */
34316715Ssherman
34416715Sshermanstatic void
34516715Sshermanislogin(void)
34616715Ssherman{
34716715Ssherman    if (chkstop == 0 && setintr)
34816715Ssherman	panystop(0);
34916715Ssherman    if (loginsh)
35016715Ssherman	return;
35116715Ssherman    stderror(ERR_NOTLOGIN);
35216715Ssherman}
35316715Ssherman
35416715Sshermanvoid
35516715Sshermandoif(Char **v, struct command *kp)
35616715Ssherman{
35716715Ssherman    int i;
35816715Ssherman    Char **vv;
35916715Ssherman
36016715Ssherman    v++;
36116715Ssherman    i = noexec ? 1 : expr(&v);
36216715Ssherman    vv = v;
36316715Ssherman    if (*vv == NULL)
36416715Ssherman	stderror(ERR_NAME | ERR_EMPTYIF);
36516715Ssherman    if (eq(*vv, STRthen)) {
36616715Ssherman	if (*++vv)
36716715Ssherman	    stderror(ERR_NAME | ERR_IMPRTHEN);
36816715Ssherman	setname(short2str(STRthen));
36916715Ssherman	/*
37016715Ssherman	 * If expression was zero, then scan to else , otherwise just fall into
37116715Ssherman	 * following code.
37216715Ssherman	 */
37316715Ssherman	if (!i)
37416715Ssherman	    search(TC_IF, 0, NULL);
37516715Ssherman	return;
37616715Ssherman    }
37716715Ssherman    /*
37816715Ssherman     * Simple command attached to this if. Left shift the node in this tree,
37916715Ssherman     * munging it so we can reexecute it.
38016715Ssherman     */
38116715Ssherman    if (i) {
38216715Ssherman	lshift(kp->t_dcom, vv - kp->t_dcom);
38316715Ssherman	reexecute(kp);
38416715Ssherman	donefds();
38516715Ssherman    }
38616715Ssherman}
38716715Ssherman
38816715Ssherman/*
38916715Ssherman * Reexecute a command, being careful not
39016715Ssherman * to redo i/o redirection, which is already set up.
39116715Ssherman */
39216715Sshermanvoid
39316715Sshermanreexecute(struct command *kp)
39416715Ssherman{
39516715Ssherman    kp->t_dflg &= F_SAVE;
39616715Ssherman    kp->t_dflg |= F_REPEAT;
39716715Ssherman    /*
39816715Ssherman     * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
39916715Ssherman     * pgrp's as the jobs would then have no way to get the tty (we can't give
40016715Ssherman     * it to them, and our parent wouldn't know their pgrp, etc.
40116715Ssherman     */
40216715Ssherman    execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
40316715Ssherman}
40416715Ssherman
40516715Ssherman/*ARGSUSED*/
40616715Sshermanvoid
40716715Sshermandoelse (Char **v, struct command *c)
40816715Ssherman{
40916715Ssherman    USE(c);
41016715Ssherman    USE(v);
41116715Ssherman    if (!noexec)
41216715Ssherman	search(TC_ELSE, 0, NULL);
41316715Ssherman}
41416715Ssherman
41516715Ssherman/*ARGSUSED*/
41616715Sshermanvoid
41716715Sshermandogoto(Char **v, struct command *c)
41816715Ssherman{
41916715Ssherman    Char   *lp;
42016715Ssherman
42116715Ssherman    USE(c);
42216715Ssherman    lp = globone(v[1], G_ERROR);
42316715Ssherman    cleanup_push(lp, xfree);
42416715Ssherman    if (!noexec)
42516715Ssherman	gotolab(lp);
42616715Ssherman    cleanup_until(lp);
42716715Ssherman}
42816715Ssherman
42916715Sshermanvoid
43016715Sshermangotolab(Char *lab)
43116715Ssherman{
43216715Ssherman    struct whyle *wp;
43316715Ssherman    /*
43416715Ssherman     * While we still can, locate any unknown ends of existing loops. This
43516715Ssherman     * obscure code is the WORST result of the fact that we don't really parse.
43616715Ssherman     */
43716715Ssherman    zlast = TC_GOTO;
43816715Ssherman    for (wp = whyles; wp; wp = wp->w_next)
43916715Ssherman	if (wp->w_end.type == TCSH_F_SEEK && wp->w_end.f_seek == 0) {
44016715Ssherman	    search(TC_BREAK, 0, NULL);
44116715Ssherman	    btell(&wp->w_end);
44216715Ssherman	}
44316715Ssherman	else {
44416715Ssherman	    bseek(&wp->w_end);
44516715Ssherman	}
44616715Ssherman    search(TC_GOTO, 0, lab);
44716715Ssherman    /*
44816715Ssherman     * Eliminate loops which were exited.
44916715Ssherman     */
45016715Ssherman    wfree();
45116715Ssherman}
45216715Ssherman
45316715Ssherman/*ARGSUSED*/
45416715Sshermanvoid
45516715Sshermandoswitch(Char **v, struct command *c)
45616715Ssherman{
45716715Ssherman    Char *cp, *lp;
45816715Ssherman
45916715Ssherman    USE(c);
46016715Ssherman    v++;
46116715Ssherman    if (!*v || *(*v++) != '(')
46216715Ssherman	stderror(ERR_SYNTAX);
46316715Ssherman    cp = **v == ')' ? STRNULL : *v++;
46416715Ssherman    if (*(*v++) != ')')
46516715Ssherman	v--;
46616715Ssherman    if (*v)
46716715Ssherman	stderror(ERR_SYNTAX);
46816715Ssherman    lp = globone(cp, G_ERROR);
46916715Ssherman    cleanup_push(lp, xfree);
47016715Ssherman    if (!noexec)
47116715Ssherman	search(TC_SWITCH, 0, lp);
47216715Ssherman    cleanup_until(lp);
47316715Ssherman}
47416715Ssherman
47516715Ssherman/*ARGSUSED*/
47616715Sshermanvoid
47716715Sshermandobreak(Char **v, struct command *c)
47816715Ssherman{
47916715Ssherman    USE(v);
48016715Ssherman    USE(c);
48116715Ssherman    if (whyles == NULL)
48216715Ssherman	stderror(ERR_NAME | ERR_NOTWHILE);
48316715Ssherman    if (!noexec)
48416715Ssherman	toend();
48516715Ssherman}
48616715Ssherman
48716715Ssherman/*ARGSUSED*/
48816715Sshermanvoid
48916715Sshermandoexit(Char **v, struct command *c)
49016715Ssherman{
49116715Ssherman    USE(c);
49216715Ssherman
49316715Ssherman    if (chkstop == 0 && (intty || intact) && evalvec == 0)
49416715Ssherman	panystop(0);
49516715Ssherman    /*
49616715Ssherman     * Don't DEMAND parentheses here either.
49716715Ssherman     */
49816715Ssherman    v++;
49916715Ssherman    if (*v) {
50016715Ssherman	setv(STRstatus, putn(expr(&v)), VAR_READWRITE);
50116715Ssherman	if (*v)
50216715Ssherman	    stderror(ERR_NAME | ERR_EXPRESSION);
50316715Ssherman    }
50416715Ssherman    btoeof();
50516715Ssherman#if 0
50616715Ssherman    if (intty)
50716715Ssherman#endif
50816715Ssherman    /* Always close, why only on ttys? */
50916715Ssherman	xclose(SHIN);
51016715Ssherman}
51116715Ssherman
51216715Ssherman/*ARGSUSED*/
51316715Sshermanvoid
51416715Sshermandoforeach(Char **v, struct command *c)
51516715Ssherman{
51616715Ssherman    Char *cp, *sp;
51716715Ssherman    struct whyle *nwp;
51816715Ssherman    int gflag;
51916715Ssherman
52016715Ssherman    USE(c);
52116715Ssherman    v++;
52216715Ssherman    cp = sp = strip(*v);
52316715Ssherman    if (!letter(*cp))
52416715Ssherman	stderror(ERR_NAME | ERR_VARBEGIN);
52516715Ssherman    do {
52616715Ssherman	cp++;
52716715Ssherman    } while (alnum(*cp));
52816715Ssherman    if (*cp != '\0')
52916715Ssherman	stderror(ERR_NAME | ERR_VARALNUM);
53016715Ssherman    cp = *v++;
53116715Ssherman    if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
53216715Ssherman	stderror(ERR_NAME | ERR_NOPAREN);
53316715Ssherman    v++;
53416715Ssherman    gflag = tglob(v);
53516715Ssherman    if (gflag) {
53616715Ssherman	v = globall(v, gflag);
53716715Ssherman	if (v == 0 && !noexec)
53816715Ssherman	    stderror(ERR_NAME | ERR_NOMATCH);
53916715Ssherman    }
54016715Ssherman    else {
54116715Ssherman	v = saveblk(v);
54216715Ssherman	trim(v);
54316715Ssherman    }
54416715Ssherman    nwp = xcalloc(1, sizeof *nwp);
54516715Ssherman    nwp->w_fe = nwp->w_fe0 = v;
54616715Ssherman    btell(&nwp->w_start);
54716715Ssherman    nwp->w_fename = Strsave(cp);
54816715Ssherman    nwp->w_next = whyles;
54916715Ssherman    nwp->w_end.type = TCSH_F_SEEK;
55016715Ssherman    whyles = nwp;
55116715Ssherman    /*
55216715Ssherman     * Pre-read the loop so as to be more comprehensible to a terminal user.
55316715Ssherman     */
55416715Ssherman    zlast = TC_FOREACH;
55516715Ssherman    if (intty)
55616715Ssherman	preread();
55716715Ssherman    if (!noexec)
55816715Ssherman	doagain();
55916715Ssherman}
56016715Ssherman
56116715Ssherman/*ARGSUSED*/
56216715Sshermanvoid
56316715Sshermandowhile(Char **v, struct command *c)
56416715Ssherman{
56516715Ssherman    int status;
56616715Ssherman    int again = whyles != 0 &&
56716715Ssherman			  SEEKEQ(&whyles->w_start, &lineloc) &&
56816715Ssherman			  whyles->w_fename == 0;
56916715Ssherman
57016715Ssherman    USE(c);
57116715Ssherman    v++;
57216715Ssherman    /*
57316715Ssherman     * Implement prereading here also, taking care not to evaluate the
57416715Ssherman     * expression before the loop has been read up from a terminal.
57516715Ssherman     */
57616715Ssherman    if (noexec)
57716715Ssherman	status = 0;
57816715Ssherman    else if (intty && !again)
57916715Ssherman	status = !exp0(&v, 1);
58016715Ssherman    else
58116715Ssherman	status = !expr(&v);
58216715Ssherman    if (*v && !noexec)
58316715Ssherman	stderror(ERR_NAME | ERR_EXPRESSION);
58416715Ssherman    if (!again) {
58516715Ssherman	struct whyle *nwp = xcalloc(1, sizeof(*nwp));
58616715Ssherman
58716715Ssherman	nwp->w_start = lineloc;
58816715Ssherman	nwp->w_end.type = TCSH_F_SEEK;
58916715Ssherman	nwp->w_end.f_seek = 0;
59016715Ssherman	nwp->w_end.a_seek = 0;
59116715Ssherman	nwp->w_next = whyles;
59216715Ssherman	whyles = nwp;
59316715Ssherman	zlast = TC_WHILE;
59416715Ssherman	if (intty) {
59516715Ssherman	    /*
59616715Ssherman	     * The tty preread
59716715Ssherman	     */
59816715Ssherman	    preread();
59916715Ssherman	    doagain();
60016715Ssherman	    return;
60116715Ssherman	}
60216715Ssherman    }
60316715Ssherman    if (status)
60416715Ssherman	/* We ain't gonna loop no more, no more! */
60516715Ssherman	toend();
60616715Ssherman}
60716715Ssherman
60816715Sshermanstatic void
60916715Sshermanpreread(void)
61016715Ssherman{
61116715Ssherman    int old_pintr_disabled;
61216715Ssherman
61316715Ssherman    whyles->w_end.type = TCSH_I_SEEK;
61416715Ssherman    if (setintr)
61516715Ssherman	pintr_push_enable(&old_pintr_disabled);
61616715Ssherman    search(TC_BREAK, 0, NULL);		/* read the expression in */
61716715Ssherman    if (setintr)
61816715Ssherman	cleanup_until(&old_pintr_disabled);
61916715Ssherman    btell(&whyles->w_end);
62016715Ssherman}
62116715Ssherman
62216715Ssherman/*ARGSUSED*/
62316715Sshermanvoid
62416715Sshermandoend(Char **v, struct command *c)
62516715Ssherman{
62616715Ssherman    USE(v);
62716715Ssherman    USE(c);
62816715Ssherman    if (!whyles)
62916715Ssherman	stderror(ERR_NAME | ERR_NOTWHILE);
63016715Ssherman    btell(&whyles->w_end);
63116715Ssherman    if (!noexec)
63216715Ssherman	doagain();
63316715Ssherman}
63416715Ssherman
63516715Ssherman/*ARGSUSED*/
63616715Sshermanvoid
63716715Sshermandocontin(Char **v, struct command *c)
63816715Ssherman{
63916715Ssherman    USE(v);
64016715Ssherman    USE(c);
64116715Ssherman    if (!whyles)
64216715Ssherman	stderror(ERR_NAME | ERR_NOTWHILE);
64316715Ssherman    if (!noexec)
64416715Ssherman	doagain();
64516715Ssherman}
64616715Ssherman
64716715Sshermanstatic void
64816715Sshermandoagain(void)
64916715Ssherman{
65016715Ssherman    /* Repeating a while is simple */
65116715Ssherman    if (whyles->w_fename == 0) {
65216715Ssherman	bseek(&whyles->w_start);
65316715Ssherman	return;
65416715Ssherman    }
65516715Ssherman    /*
65616715Ssherman     * The foreach variable list actually has a spurious word ")" at the end of
65716715Ssherman     * the w_fe list.  Thus we are at the of the list if one word beyond this
65816715Ssherman     * is 0.
65916715Ssherman     */
66016715Ssherman    if (!whyles->w_fe[1]) {
66116715Ssherman	dobreak(NULL, NULL);
66216715Ssherman	return;
66316715Ssherman    }
66416715Ssherman    setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE);
66516715Ssherman    bseek(&whyles->w_start);
66616715Ssherman}
66716715Ssherman
66816715Sshermanvoid
66916715Sshermandorepeat(Char **v, struct command *kp)
67016715Ssherman{
67116715Ssherman    int i = 1;
67216715Ssherman
67316715Ssherman    do {
67416715Ssherman	i *= getn(v[1]);
67516715Ssherman	lshift(v, 2);
67616715Ssherman    } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0);
67716715Ssherman    if (noexec)
67816715Ssherman	i = 1;
67916715Ssherman
68016715Ssherman    if (setintr) {
68116715Ssherman	pintr_disabled++;
68216715Ssherman	cleanup_push(&pintr_disabled, disabled_cleanup);
68316715Ssherman    }
68416715Ssherman    while (i > 0) {
68516715Ssherman	if (setintr && pintr_disabled == 1) {
68616715Ssherman	    cleanup_until(&pintr_disabled);
68716715Ssherman	    pintr_disabled++;
68816715Ssherman	    cleanup_push(&pintr_disabled, disabled_cleanup);
68916715Ssherman	}
69016715Ssherman	reexecute(kp);
69116715Ssherman	--i;
69216715Ssherman    }
69316715Ssherman    if (setintr && pintr_disabled == 1)
69416715Ssherman        cleanup_until(&pintr_disabled);
69516715Ssherman    donefds();
69616715Ssherman}
69716715Ssherman
69816715Ssherman/*ARGSUSED*/
69916715Sshermanvoid
70016715Sshermandoswbrk(Char **v, struct command *c)
70116715Ssherman{
70216715Ssherman    USE(v);
70316715Ssherman    USE(c);
70416715Ssherman    if (!noexec)
70516715Ssherman	search(TC_BRKSW, 0, NULL);
70616715Ssherman}
70716715Ssherman
70816715Sshermanint
70916715Sshermansrchx(Char *cp)
71016715Ssherman{
71116715Ssherman    struct srch *sp, *sp1, *sp2;
71216715Ssherman    int i;
71316715Ssherman
71416715Ssherman    /*
71516715Ssherman     * Ignore keywords inside heredocs
71616715Ssherman     */
71716715Ssherman    if (inheredoc)
71816715Ssherman	return -1;
71916715Ssherman
72016715Ssherman    /*
72116715Ssherman     * Binary search Sp1 is the beginning of the current search range. Sp2 is
72216715Ssherman     * one past the end.
72316715Ssherman     */
72416715Ssherman    for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
72516715Ssherman	sp = sp1 + ((sp2 - sp1) >> 1);
72616715Ssherman	if ((i = *cp - *sp->s_name) == 0 &&
72716715Ssherman	    (i = Strcmp(cp, str2short(sp->s_name))) == 0)
72816715Ssherman	    return sp->s_value;
72916715Ssherman	if (i < 0)
73016715Ssherman	    sp2 = sp;
73116715Ssherman	else
73216715Ssherman	    sp1 = sp + 1;
73316715Ssherman    }
73416715Ssherman    return (-1);
73516715Ssherman}
73616715Ssherman
73716715Sshermanstatic const char *
73816715Sshermanisrchx(int n)
73916715Ssherman{
74016715Ssherman    struct srch *sp, *sp2;
74116715Ssherman
74216715Ssherman    for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++)
74316715Ssherman	if (sp->s_value == n)
74416715Ssherman	    return (sp->s_name);
74516715Ssherman    return ("");
74616715Ssherman}
74716715Ssherman
74816715Ssherman
74916715Sshermanstatic int Stype;
75016715Sshermanstatic Char *Sgoal;
75116715Ssherman
75216715Sshermanstatic void
75316715Sshermansearch(int type, int level, Char *goal)
75416715Ssherman{
75516715Ssherman    struct Strbuf word = Strbuf_INIT;
75616715Ssherman    Char *cp;
75716715Ssherman    struct whyle *wp;
75816715Ssherman    int wlevel = 0;
75916715Ssherman    struct wordent *histent = NULL, *ohistent = NULL;
76016715Ssherman
76116715Ssherman    Stype = type;
76216715Ssherman    Sgoal = goal;
76316715Ssherman    if (type == TC_GOTO) {
76416715Ssherman	struct Ain a;
76516715Ssherman	a.type = TCSH_F_SEEK;
76616715Ssherman	a.f_seek = 0;
76716715Ssherman	a.a_seek = 0;
76816715Ssherman	bseek(&a);
76916715Ssherman    }
77016715Ssherman    cleanup_push(&word, Strbuf_cleanup);
77116715Ssherman    do {
77216715Ssherman
77316715Ssherman	if (intty) {
77416715Ssherman	    histent = xmalloc(sizeof(*histent));
77516715Ssherman	    ohistent = xmalloc(sizeof(*histent));
77616715Ssherman	    ohistent->word = STRNULL;
77716715Ssherman	    ohistent->next = histent;
77816715Ssherman	    histent->prev = ohistent;
77916715Ssherman	}
78016715Ssherman
78116715Ssherman	if (intty && fseekp == feobp && aret == TCSH_F_SEEK)
78216715Ssherman	    printprompt(1, isrchx(type == TC_BREAK ? zlast : type));
78316715Ssherman	/* xprintf("? "), flush(); */
78416715Ssherman	(void) getword(&word);
78516715Ssherman	Strbuf_terminate(&word);
78616715Ssherman
78716715Ssherman	if (intty && Strlen(word.s) > 0) {
78816715Ssherman	    histent->word = Strsave(word.s);
78916715Ssherman	    histent->next = xmalloc(sizeof(*histent));
79016715Ssherman	    histent->next->prev = histent;
79116715Ssherman	    histent = histent->next;
79216715Ssherman	}
79316715Ssherman
79416715Ssherman	switch (srchx(word.s)) {
79516715Ssherman
79616715Ssherman	case TC_ELSE:
79716715Ssherman	    if (level == 0 && type == TC_IF)
79816715Ssherman		goto end;
79916715Ssherman	    break;
80016715Ssherman
80116715Ssherman	case TC_IF:
80216715Ssherman	    while (getword(&word))
80316715Ssherman		continue;
80416715Ssherman	    if ((type == TC_IF || type == TC_ELSE) &&
80516715Ssherman		eq(word.s, STRthen))
80616715Ssherman		level++;
80716715Ssherman	    break;
80816715Ssherman
80916715Ssherman	case TC_ENDIF:
81016715Ssherman	    if (type == TC_IF || type == TC_ELSE)
81116715Ssherman		level--;
81216715Ssherman	    break;
81316715Ssherman
81416715Ssherman	case TC_FOREACH:
81516715Ssherman	case TC_WHILE:
81616715Ssherman	    wlevel++;
81716715Ssherman	    if (type == TC_BREAK)
81816715Ssherman		level++;
81916715Ssherman	    break;
82016715Ssherman
82116715Ssherman	case TC_END:
82216715Ssherman	    if (type == TC_BRKSW) {
82316715Ssherman		if (wlevel == 0) {
82416715Ssherman		    wp = whyles;
82516715Ssherman		    if (wp) {
82616715Ssherman			    whyles = wp->w_next;
82716715Ssherman			    wpfree(wp);
82816715Ssherman		    }
82916715Ssherman		}
83016715Ssherman	    }
83116715Ssherman	    if (type == TC_BREAK)
83216715Ssherman		level--;
83316715Ssherman	    wlevel--;
83416715Ssherman	    break;
83516715Ssherman
83616715Ssherman	case TC_SWITCH:
83716715Ssherman	    if (type == TC_SWITCH || type == TC_BRKSW)
83816715Ssherman		level++;
83916715Ssherman	    break;
84016715Ssherman
84116715Ssherman	case TC_ENDSW:
84216715Ssherman	    if (type == TC_SWITCH || type == TC_BRKSW)
84316715Ssherman		level--;
84416715Ssherman	    break;
84516715Ssherman
84616715Ssherman	case TC_LABEL:
84716715Ssherman	    if (type == TC_GOTO && getword(&word) && eq(word.s, goal))
84816715Ssherman		level = -1;
84916715Ssherman	    break;
85016715Ssherman
85116715Ssherman	default:
85216715Ssherman	    if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
85316715Ssherman		break;
85416715Ssherman	    if (word.len == 0 || word.s[word.len - 1] != ':')
85516715Ssherman		break;
85616715Ssherman	    word.s[--word.len] = 0;
85716715Ssherman	    if ((type == TC_GOTO && eq(word.s, goal)) ||
85816715Ssherman		(type == TC_SWITCH && eq(word.s, STRdefault)))
85916715Ssherman		level = -1;
86016715Ssherman	    break;
86116715Ssherman
86216715Ssherman	case TC_CASE:
86316715Ssherman	    if (type != TC_SWITCH || level != 0)
86416715Ssherman		break;
86516715Ssherman	    (void) getword(&word);
86616715Ssherman	    if (word.len != 0 && word.s[word.len - 1] == ':')
86716715Ssherman		word.s[--word.len] = 0;
86816715Ssherman	    cp = strip(Dfix1(word.s));
86916715Ssherman	    cleanup_push(cp, xfree);
87016715Ssherman	    if (Gmatch(goal, cp))
87116715Ssherman		level = -1;
87216715Ssherman	    cleanup_until(cp);
87316715Ssherman	    break;
87416715Ssherman
87516715Ssherman	case TC_DEFAULT:
87616715Ssherman	    if (type == TC_SWITCH && level == 0)
87716715Ssherman		level = -1;
87816715Ssherman	    break;
87916715Ssherman	}
88016715Ssherman	if (intty) {
88116715Ssherman	    ohistent->prev = histgetword(histent);
88216715Ssherman	    ohistent->prev->next = ohistent;
88316715Ssherman	    savehist(ohistent, 0);
88416715Ssherman	    freelex(ohistent);
88516715Ssherman	    xfree(ohistent);
88616715Ssherman	} else
88716715Ssherman	    (void) getword(NULL);
88816715Ssherman    } while (level >= 0);
88916715Ssherman end:
89016715Ssherman    cleanup_until(&word);
89116715Ssherman}
89216715Ssherman
89316715Sshermanstatic struct wordent *
89416715Sshermanhistgetword(struct wordent *histent)
89516715Ssherman{
89616715Ssherman    int found = 0, first;
89716715Ssherman    eChar c, d;
89816715Ssherman    int e;
89916715Ssherman    struct Strbuf *tmp;
90016715Ssherman    tmp = xmalloc(sizeof(*tmp));
90116715Ssherman    tmp->size = 0;
90216715Ssherman    tmp->s = NULL;
90316715Ssherman    c = readc(1);
90416715Ssherman    d = 0;
90516715Ssherman    e = 0;
90616715Ssherman    for (;;) {
90716715Ssherman	tmp->len = 0;
90816715Ssherman	Strbuf_terminate (tmp);
90916715Ssherman	while (c == ' ' || c == '\t')
91016715Ssherman	    c = readc(1);
91116715Ssherman	if (c == '#')
91216715Ssherman	    do
91316715Ssherman		c = readc(1);
91416715Ssherman	    while (c != CHAR_ERR && c != '\n');
91516715Ssherman	if (c == CHAR_ERR)
91616715Ssherman	    goto past;
91716715Ssherman	if (c == '\n')
91816715Ssherman	    goto nl;
91916715Ssherman	unreadc(c);
92016715Ssherman	found = 1;
92116715Ssherman	first = 1;
92216715Ssherman	do {
92316715Ssherman	    e = (c == '\\');
92416715Ssherman	    c = readc(1);
92516715Ssherman	    if (c == '\\' && !e) {
92616715Ssherman		if ((c = readc(1)) == '\n') {
92716715Ssherman		    e = 1;
92816715Ssherman		    c = ' ';
92916715Ssherman		} else {
93016715Ssherman		    unreadc(c);
93116715Ssherman		    c = '\\';
93216715Ssherman		}
93316715Ssherman	    }
93416715Ssherman	    if ((c == '\'' || c == '"') && !e) {
93516715Ssherman		if (d == 0)
93616715Ssherman		    d = c;
93716715Ssherman		else if (d == c)
93816715Ssherman		    d = 0;
93916715Ssherman	    }
94016715Ssherman	    if (c == CHAR_ERR)
94116715Ssherman		goto past;
94216715Ssherman
94316715Ssherman	    Strbuf_append1(tmp, (Char) c);
94416715Ssherman
94516715Ssherman	    if (!first && !d && c == '(' && !e) {
94616715Ssherman		break;
94716715Ssherman	    }
94816715Ssherman	    first = 0;
94916715Ssherman	} while (d || e || (c != ' ' && c != '\t' && c != '\n'));
95016715Ssherman	tmp->len--;
95116715Ssherman	if (tmp->len) {
95216715Ssherman	    Strbuf_terminate(tmp);
95316715Ssherman	    histent->word = Strsave(tmp->s);
95416715Ssherman	    histent->next = xmalloc(sizeof (*histent));
95516715Ssherman	    histent->next->prev = histent;
95616715Ssherman	    histent = histent->next;
95716715Ssherman	}
95816715Ssherman	if (c == '\n') {
95916715Ssherman	nl:
96016715Ssherman	    tmp->len = 0;
96116715Ssherman	    Strbuf_append1(tmp, (Char) c);
96216715Ssherman	    Strbuf_terminate(tmp);
96316715Ssherman	    histent->word = Strsave(tmp->s);
96416715Ssherman	    return histent;
96516715Ssherman	}
96616715Ssherman    }
96716715Ssherman
96816715Sshermanpast:
96916715Ssherman    switch (Stype) {
97016715Ssherman
97116715Ssherman    case TC_IF:
97216715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
97316715Ssherman	break;
97416715Ssherman
97516715Ssherman    case TC_ELSE:
97616715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
97716715Ssherman	break;
97816715Ssherman
97916715Ssherman    case TC_BRKSW:
98016715Ssherman    case TC_SWITCH:
98116715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
98216715Ssherman	break;
98316715Ssherman
98416715Ssherman    case TC_BREAK:
98516715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "end");
98616715Ssherman	break;
98716715Ssherman
98816715Ssherman    case TC_GOTO:
98916715Ssherman	setname(short2str(Sgoal));
99016715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "label");
99116715Ssherman	break;
99216715Ssherman
99316715Ssherman    default:
99416715Ssherman	break;
99516715Ssherman    }
99616715Ssherman    /* NOTREACHED */
99716715Ssherman    return NULL;
99816715Ssherman}
99916715Ssherman
100016715Sshermanstatic int
100116715Sshermangetword(struct Strbuf *wp)
100216715Ssherman{
100316715Ssherman    int found = 0, first;
100416715Ssherman    eChar c, d;
100516715Ssherman
100616715Ssherman    if (wp)
100716715Ssherman	wp->len = 0;
100816715Ssherman    c = readc(1);
100916715Ssherman    d = 0;
101016715Ssherman    do {
101116715Ssherman	while (c == ' ' || c == '\t')
101216715Ssherman	    c = readc(1);
101316715Ssherman	if (c == '#')
101416715Ssherman	    do
101516715Ssherman		c = readc(1);
101616715Ssherman	    while (c != CHAR_ERR && c != '\n');
101716715Ssherman	if (c == CHAR_ERR)
101816715Ssherman	    goto past;
101916715Ssherman	if (c == '\n') {
102016715Ssherman	    if (wp)
102116715Ssherman		break;
102216715Ssherman	    return (0);
102316715Ssherman	}
102416715Ssherman	unreadc(c);
102516715Ssherman	found = 1;
102616715Ssherman	first = 1;
102716715Ssherman	do {
102816715Ssherman	    c = readc(1);
102916715Ssherman	    if (c == '\\' && (c = readc(1)) == '\n')
103016715Ssherman		c = ' ';
103116715Ssherman	    if (c == '\'' || c == '"') {
103216715Ssherman		if (d == 0)
103316715Ssherman		    d = c;
103416715Ssherman		else if (d == c)
103516715Ssherman		    d = 0;
103616715Ssherman	    }
103716715Ssherman	    if (c == CHAR_ERR)
103816715Ssherman		goto past;
103916715Ssherman	    if (wp)
104016715Ssherman		Strbuf_append1(wp, (Char) c);
104116715Ssherman	    if (!first && !d && c == '(') {
104216715Ssherman		if (wp)
104316715Ssherman		    goto past_word_end;
104416715Ssherman		else
104516715Ssherman		    break;
104616715Ssherman	    }
104716715Ssherman	    first = 0;
104816715Ssherman	} while ((d || (c != ' ' && c != '\t')) && c != '\n');
104916715Ssherman    } while (wp == 0);
105016715Ssherman
105116715Ssherman past_word_end:
105216715Ssherman    unreadc(c);
105316715Ssherman    if (found) {
105416715Ssherman	wp->len--;
105516715Ssherman	Strbuf_terminate(wp);
105616715Ssherman    }
105716715Ssherman
105816715Ssherman    return (found);
105916715Ssherman
106016715Sshermanpast:
106116715Ssherman    switch (Stype) {
106216715Ssherman
106316715Ssherman    case TC_IF:
106416715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
106516715Ssherman	break;
106616715Ssherman
106716715Ssherman    case TC_ELSE:
106816715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
106916715Ssherman	break;
107016715Ssherman
107116715Ssherman    case TC_BRKSW:
107216715Ssherman    case TC_SWITCH:
107316715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
107416715Ssherman	break;
107516715Ssherman
107616715Ssherman    case TC_BREAK:
107716715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "end");
107816715Ssherman	break;
107916715Ssherman
108016715Ssherman    case TC_GOTO:
108116715Ssherman	setname(short2str(Sgoal));
108216715Ssherman	stderror(ERR_NAME | ERR_NOTFOUND, "label");
108316715Ssherman	break;
108416715Ssherman
108516715Ssherman    default:
108616715Ssherman	break;
108716715Ssherman    }
108816715Ssherman    /* NOTREACHED */
108916715Ssherman    return (0);
109016715Ssherman}
109116715Ssherman
109216715Sshermanstatic void
109316715Sshermantoend(void)
109416715Ssherman{
109516715Ssherman    if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) {
109616715Ssherman	search(TC_BREAK, 0, NULL);
109716715Ssherman	btell(&whyles->w_end);
109816715Ssherman	whyles->w_end.f_seek--;
109916715Ssherman    }
110016715Ssherman    else {
110116715Ssherman	bseek(&whyles->w_end);
110216715Ssherman    }
110316715Ssherman    wfree();
110416715Ssherman}
110516715Ssherman
110616715Sshermanstatic void
110716715Sshermanwpfree(struct whyle *wp)
110816715Ssherman{
110916715Ssherman	if (wp->w_fe0)
111016715Ssherman	    blkfree(wp->w_fe0);
111116715Ssherman	xfree(wp->w_fename);
111216715Ssherman	xfree(wp);
111316715Ssherman}
111416715Ssherman
111516715Sshermanvoid
111616715Sshermanwfree(void)
111716715Ssherman{
111816715Ssherman    struct Ain    o;
111916715Ssherman    struct whyle *nwp;
112016715Ssherman#ifdef lint
112116715Ssherman    nwp = NULL;	/* sun lint is dumb! */
112216715Ssherman#endif
112316715Ssherman
112416715Ssherman#ifdef FDEBUG
112516715Ssherman    static const char foo[] = "IAFE";
112616715Ssherman#endif /* FDEBUG */
112716715Ssherman
112816715Ssherman    btell(&o);
112916715Ssherman
113016715Ssherman#ifdef FDEBUG
113116715Ssherman    xprintf("o->type %c o->a_seek %d o->f_seek %d\n",
113216715Ssherman	    foo[o.type + 1], o.a_seek, o.f_seek);
113316715Ssherman#endif /* FDEBUG */
113416715Ssherman
113516715Ssherman    for (; whyles; whyles = nwp) {
113616715Ssherman	struct whyle *wp = whyles;
113716715Ssherman	nwp = wp->w_next;
113816715Ssherman
113916715Ssherman#ifdef FDEBUG
114016715Ssherman	xprintf("start->type %c start->a_seek %d start->f_seek %d\n",
114116715Ssherman		foo[wp->w_start.type+1],
114216715Ssherman		wp->w_start.a_seek, wp->w_start.f_seek);
114316715Ssherman	xprintf("end->type %c end->a_seek %d end->f_seek %d\n",
114416715Ssherman		foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
114516715Ssherman#endif /* FDEBUG */
114616715Ssherman
114716715Ssherman	/*
114816715Ssherman	 * XXX: We free loops that have different seek types.
114916715Ssherman	 */
115016715Ssherman	if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type &&
115116715Ssherman	    wp->w_start.type == o.type) {
115216715Ssherman	    if (wp->w_end.type == TCSH_F_SEEK) {
115316715Ssherman		if (o.f_seek >= wp->w_start.f_seek &&
115416715Ssherman		    (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
115516715Ssherman		    break;
115616715Ssherman	    }
115716715Ssherman	    else {
115816715Ssherman		if (o.a_seek >= wp->w_start.a_seek &&
115916715Ssherman		    (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
116016715Ssherman		    break;
116116715Ssherman	    }
116216715Ssherman	}
116316715Ssherman
116416715Ssherman	wpfree(wp);
116516715Ssherman    }
116616715Ssherman}
116716715Ssherman
116816715Ssherman/*ARGSUSED*/
116916715Sshermanvoid
117016715Sshermandoecho(Char **v, struct command *c)
117116715Ssherman{
117216715Ssherman    USE(c);
117316715Ssherman    xecho(' ', v);
117416715Ssherman}
117516715Ssherman
117616715Ssherman/*ARGSUSED*/
117716715Sshermanvoid
117816715Sshermandoglob(Char **v, struct command *c)
117916715Ssherman{
118016715Ssherman    USE(c);
118116715Ssherman    xecho(0, v);
118216715Ssherman    flush();
118316715Ssherman}
118416715Ssherman
118516715Sshermanstatic void
118616715Sshermanxecho(int sep, Char **v)
118716715Ssherman{
118816715Ssherman    Char *cp, **globbed = NULL;
118916715Ssherman    int     nonl = 0;
119016715Ssherman    int	    echo_style = ECHO_STYLE;
119116715Ssherman    struct varent *vp;
119216715Ssherman
119316715Ssherman    if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
119416715Ssherman	vp->vec[0] != NULL) {
119516715Ssherman	if (Strcmp(vp->vec[0], STRbsd) == 0)
119616715Ssherman	    echo_style = BSD_ECHO;
119716715Ssherman	else if (Strcmp(vp->vec[0], STRsysv) == 0)
119816715Ssherman	    echo_style = SYSV_ECHO;
119916715Ssherman	else if (Strcmp(vp->vec[0], STRboth) == 0)
120016715Ssherman	    echo_style = BOTH_ECHO;
120116715Ssherman	else if (Strcmp(vp->vec[0], STRnone) == 0)
120216715Ssherman	    echo_style = NONE_ECHO;
120316715Ssherman    }
120416715Ssherman
120516715Ssherman    v++;
120616715Ssherman    if (*v == 0)
120716715Ssherman	goto done;
120816715Ssherman    if (setintr) {
120916715Ssherman	int old_pintr_disabled;
121016715Ssherman	pintr_push_enable(&old_pintr_disabled);
121116715Ssherman	v = glob_all_or_error(v);
121216715Ssherman	cleanup_until(&old_pintr_disabled);
121316715Ssherman    } else {
121416715Ssherman	v = glob_all_or_error(v);
121516715Ssherman    }
121616715Ssherman    globbed = v;
121716715Ssherman    if (globbed != NULL)
121816715Ssherman	cleanup_push(globbed, blk_cleanup);
121916715Ssherman
122016715Ssherman    if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
122116715Ssherman	nonl++, v++;
122216715Ssherman
122316715Ssherman    while ((cp = *v++) != 0) {
122416715Ssherman	Char c;
122516715Ssherman
122616715Ssherman	if (setintr) {
122716715Ssherman	    int old_pintr_disabled;
1228
1229	    pintr_push_enable(&old_pintr_disabled);
1230	    cleanup_until(&old_pintr_disabled);
1231	}
1232	while ((c = *cp++) != 0) {
1233	    if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
1234		switch (c = *cp++) {
1235		case 'a':
1236		    c = '\a';
1237		    break;
1238		case 'b':
1239		    c = '\b';
1240		    break;
1241		case 'c':
1242		    nonl = 1;
1243		    goto done;
1244		case 'e':
1245#if 0			/* Windows does not understand \e */
1246		    c = '\e';
1247#else
1248		    c = CTL_ESC('\033');
1249#endif
1250		    break;
1251		case 'f':
1252		    c = '\f';
1253		    break;
1254		case 'n':
1255		    c = '\n';
1256		    break;
1257		case 'r':
1258		    c = '\r';
1259		    break;
1260		case 't':
1261		    c = '\t';
1262		    break;
1263		case 'v':
1264		    c = '\v';
1265		    break;
1266		case '\\':
1267		    c = '\\';
1268		    break;
1269		case '0':
1270		    c = 0;
1271		    if (*cp >= '0' && *cp < '8')
1272			c = c * 8 + *cp++ - '0';
1273		    if (*cp >= '0' && *cp < '8')
1274			c = c * 8 + *cp++ - '0';
1275		    if (*cp >= '0' && *cp < '8')
1276			c = c * 8 + *cp++ - '0';
1277		    break;
1278		case '\0':
1279		    c = '\\';
1280		    cp--;
1281		    break;
1282		default:
1283		    xputchar('\\' | QUOTE);
1284		    break;
1285		}
1286	    }
1287	    xputwchar(c | QUOTE);
1288
1289	}
1290	if (*v)
1291	    xputchar(sep | QUOTE);
1292    }
1293done:
1294    if (sep && nonl == 0)
1295	xputchar('\n');
1296    else
1297	flush();
1298    if (globbed != NULL)
1299	cleanup_until(globbed);
1300}
1301
1302/* check whether an environment variable should invoke 'set_locale()' */
1303static int
1304islocale_var(Char *var)
1305{
1306    static Char *locale_vars[] = {
1307	STRLANG,	STRLC_ALL, 	STRLC_CTYPE,	STRLC_NUMERIC,
1308	STRLC_TIME,	STRLC_COLLATE,	STRLC_MESSAGES,	STRLC_MONETARY, 0
1309    };
1310    Char **v;
1311
1312    for (v = locale_vars; *v; ++v)
1313	if (eq(var, *v))
1314	    return 1;
1315    return 0;
1316}
1317
1318static void
1319xlate_cr_cleanup(void *dummy)
1320{
1321    USE(dummy);
1322    xlate_cr = 0;
1323}
1324
1325/*ARGSUSED*/
1326void
1327doprintenv(Char **v, struct command *c)
1328{
1329    Char   *e;
1330
1331    USE(c);
1332    v++;
1333    if (*v == 0) {
1334	Char **ep;
1335
1336	xlate_cr = 1;
1337	cleanup_push(&xlate_cr, xlate_cr_cleanup);
1338	for (ep = STR_environ; *ep; ep++) {
1339	    if (setintr) {
1340		int old_pintr_disabled;
1341
1342		pintr_push_enable(&old_pintr_disabled);
1343		cleanup_until(&old_pintr_disabled);
1344	    }
1345	    xprintf("%S\n", *ep);
1346	}
1347	cleanup_until(&xlate_cr);
1348    }
1349    else if ((e = tgetenv(*v)) != NULL) {
1350	int old_output_raw;
1351
1352	old_output_raw = output_raw;
1353	output_raw = 1;
1354	cleanup_push(&old_output_raw, output_raw_restore);
1355	xprintf("%S\n", e);
1356	cleanup_until(&old_output_raw);
1357    }
1358    else
1359	setcopy(STRstatus, STR1, VAR_READWRITE);
1360}
1361
1362/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
1363   (and anything else with a modern compiler) */
1364
1365/*ARGSUSED*/
1366void
1367dosetenv(Char **v, struct command *c)
1368{
1369    Char   *vp, *lp;
1370
1371    USE(c);
1372    if (*++v == 0) {
1373	doprintenv(--v, 0);
1374	return;
1375    }
1376
1377    vp = *v++;
1378    lp = vp;
1379
1380    if (!letter(*lp))
1381	stderror(ERR_NAME | ERR_VARBEGIN);
1382    do {
1383	lp++;
1384    } while (alnum(*lp));
1385    if (*lp != '\0')
1386	stderror(ERR_NAME | ERR_VARALNUM);
1387
1388    if ((lp = *v++) == 0)
1389	lp = STRNULL;
1390
1391    lp = globone(lp, G_APPEND);
1392    cleanup_push(lp, xfree);
1393    tsetenv(vp, lp);
1394    if (eq(vp, STRKPATH)) {
1395        importpath(lp);
1396	dohash(NULL, NULL);
1397	cleanup_until(lp);
1398	return;
1399    }
1400
1401#ifdef apollo
1402    if (eq(vp, STRSYSTYPE)) {
1403	dohash(NULL, NULL);
1404	cleanup_until(lp);
1405	return;
1406    }
1407#endif /* apollo */
1408
1409    /* dspkanji/dspmbyte autosetting */
1410    /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
1411#if defined(DSPMBYTE)
1412    if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) {
1413	autoset_dspmbyte(lp);
1414    }
1415#endif
1416
1417    if (islocale_var(vp)) {
1418#ifdef NLS
1419	int     k;
1420
1421# ifdef SETLOCALEBUG
1422	dont_free = 1;
1423# endif /* SETLOCALEBUG */
1424	(void) setlocale(LC_ALL, "");
1425# ifdef LC_COLLATE
1426	(void) setlocale(LC_COLLATE, "");
1427# endif
1428# ifdef LC_CTYPE
1429	(void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1430# endif /* LC_CTYPE */
1431# if defined(AUTOSET_KANJI)
1432        autoset_kanji();
1433# endif /* AUTOSET_KANJI */
1434# ifdef NLS_CATALOGS
1435#  ifdef LC_MESSAGES
1436	(void) setlocale(LC_MESSAGES, "");
1437#  endif /* LC_MESSAGES */
1438	nlsclose();
1439	nlsinit();
1440# endif /* NLS_CATALOGS */
1441# ifdef SETLOCALEBUG
1442	dont_free = 0;
1443# endif /* SETLOCALEBUG */
1444# ifdef STRCOLLBUG
1445	fix_strcoll_bug();
1446# endif /* STRCOLLBUG */
1447	tw_cmd_free();	/* since the collation sequence has changed */
1448	for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
1449	    continue;
1450	AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
1451#else /* !NLS */
1452	AsciiOnly = 0;
1453#endif /* NLS */
1454	NLSMapsAreInited = 0;
1455	ed_Init();
1456	if (MapsAreInited && !NLSMapsAreInited)
1457	    ed_InitNLSMaps();
1458	cleanup_until(lp);
1459	return;
1460    }
1461
1462#ifdef NLS_CATALOGS
1463    if (eq(vp, STRNLSPATH)) {
1464	nlsclose();
1465	nlsinit();
1466    }
1467#endif
1468
1469    if (eq(vp, STRNOREBIND)) {
1470	NoNLSRebind = 1;
1471	MapsAreInited = 0;
1472	NLSMapsAreInited = 0;
1473	ed_InitMaps();
1474	cleanup_until(lp);
1475	return;
1476    }
1477#ifdef WINNT_NATIVE
1478    if (eq(vp, STRtcshlang)) {
1479	nlsinit();
1480	cleanup_until(lp);
1481	return;
1482    }
1483#endif /* WINNT_NATIVE */
1484    if (eq(vp, STRKTERM)) {
1485	char *t;
1486
1487	setv(STRterm, quote(lp), VAR_READWRITE);	/* lp memory used here */
1488	cleanup_ignore(lp);
1489	cleanup_until(lp);
1490	t = short2str(lp);
1491	if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) {
1492	    editing = 1;
1493	    noediting = 0;
1494	    setNS(STRedit);
1495	}
1496	GotTermCaps = 0;
1497	ed_Init();
1498	return;
1499    }
1500
1501    if (eq(vp, STRKHOME)) {
1502	Char *canon;
1503	/*
1504	 * convert to canonical pathname (possibly resolving symlinks)
1505	 */
1506	canon = dcanon(lp, lp);
1507	cleanup_ignore(lp);
1508	cleanup_until(lp);
1509	cleanup_push(canon, xfree);
1510	setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */
1511	cleanup_ignore(canon);
1512	cleanup_until(canon);
1513
1514	/* fix directory stack for new tilde home */
1515	dtilde();
1516	return;
1517    }
1518
1519    if (eq(vp, STRKSHLVL)) {
1520	setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */
1521	cleanup_ignore(lp);
1522	cleanup_until(lp);
1523	return;
1524    }
1525
1526    if (eq(vp, STRKUSER)) {
1527	setv(STRuser, quote(lp), VAR_READWRITE);	/* lp memory used here */
1528	cleanup_ignore(lp);
1529	cleanup_until(lp);
1530	return;
1531    }
1532
1533    if (eq(vp, STRKGROUP)) {
1534	setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */
1535	cleanup_ignore(lp);
1536	cleanup_until(lp);
1537	return;
1538    }
1539
1540#ifdef COLOR_LS_F
1541    if (eq(vp, STRLS_COLORS)) {
1542        parseLS_COLORS(lp);
1543	cleanup_until(lp);
1544	return;
1545    }
1546#endif /* COLOR_LS_F */
1547
1548#ifdef SIG_WINDOW
1549    /*
1550     * Load/Update $LINES $COLUMNS
1551     */
1552    if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
1553	eq(vp, STRTERMCAP)) {
1554	cleanup_until(lp);
1555	check_window_size(1);
1556	return;
1557    }
1558
1559    /*
1560     * Change the size to the one directed by $LINES and $COLUMNS
1561     */
1562    if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
1563#if 0
1564	GotTermCaps = 0;
1565#endif
1566	cleanup_until(lp);
1567	ed_Init();
1568	return;
1569    }
1570#endif /* SIG_WINDOW */
1571    cleanup_until(lp);
1572}
1573
1574/*ARGSUSED*/
1575void
1576dounsetenv(Char **v, struct command *c)
1577{
1578    Char  **ep, *p, *n, *name;
1579    int     i, maxi;
1580
1581    USE(c);
1582    /*
1583     * Find the longest environment variable
1584     */
1585    for (maxi = 0, ep = STR_environ; *ep; ep++) {
1586	for (i = 0, p = *ep; *p && *p != '='; p++, i++)
1587	    continue;
1588	if (i > maxi)
1589	    maxi = i;
1590    }
1591
1592    name = xmalloc((maxi + 1) * sizeof(Char));
1593    cleanup_push(name, xfree);
1594
1595    while (++v && *v)
1596	for (maxi = 1; maxi;)
1597	    for (maxi = 0, ep = STR_environ; *ep; ep++) {
1598		for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
1599		    continue;
1600		*n = '\0';
1601		if (!Gmatch(name, *v))
1602		    continue;
1603		maxi = 1;
1604
1605		/* Unset the name. This wasn't being done until
1606		 * later but most of the stuff following won't
1607		 * work (particularly the setlocale() and getenv()
1608		 * stuff) as intended until the name is actually
1609		 * removed. (sg)
1610		 */
1611		Unsetenv(name);
1612
1613		if (eq(name, STRNOREBIND)) {
1614		    NoNLSRebind = 0;
1615		    MapsAreInited = 0;
1616		    NLSMapsAreInited = 0;
1617		    ed_InitMaps();
1618		}
1619#ifdef apollo
1620		else if (eq(name, STRSYSTYPE))
1621		    dohash(NULL, NULL);
1622#endif /* apollo */
1623		else if (islocale_var(name)) {
1624#ifdef NLS
1625		    int     k;
1626
1627# ifdef SETLOCALEBUG
1628		    dont_free = 1;
1629# endif /* SETLOCALEBUG */
1630		    (void) setlocale(LC_ALL, "");
1631# ifdef LC_COLLATE
1632		    (void) setlocale(LC_COLLATE, "");
1633# endif
1634# ifdef LC_CTYPE
1635		    (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1636# endif /* LC_CTYPE */
1637# ifdef NLS_CATALOGS
1638#  ifdef LC_MESSAGES
1639		    (void) setlocale(LC_MESSAGES, "");
1640#  endif /* LC_MESSAGES */
1641		    nlsclose();
1642		    nlsinit();
1643# endif /* NLS_CATALOGS */
1644# ifdef SETLOCALEBUG
1645		    dont_free = 0;
1646# endif /* SETLOCALEBUG */
1647# ifdef STRCOLLBUG
1648		    fix_strcoll_bug();
1649# endif /* STRCOLLBUG */
1650		    tw_cmd_free();/* since the collation sequence has changed */
1651		    for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
1652			continue;
1653		    AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
1654#else /* !NLS */
1655		    AsciiOnly = getenv("LANG") == NULL &&
1656			getenv("LC_CTYPE") == NULL;
1657#endif /* NLS */
1658		    NLSMapsAreInited = 0;
1659		    ed_Init();
1660		    if (MapsAreInited && !NLSMapsAreInited)
1661			ed_InitNLSMaps();
1662
1663		}
1664#ifdef WINNT_NATIVE
1665		else if (eq(name,(STRtcshlang))) {
1666		    nls_dll_unload();
1667		    nlsinit();
1668		}
1669#endif /* WINNT_NATIVE */
1670#ifdef COLOR_LS_F
1671		else if (eq(name, STRLS_COLORS))
1672		    parseLS_COLORS(n);
1673#endif /* COLOR_LS_F */
1674#ifdef NLS_CATALOGS
1675		else if (eq(name, STRNLSPATH)) {
1676		    nlsclose();
1677		    nlsinit();
1678		}
1679#endif
1680		/*
1681		 * start again cause the environment changes
1682		 */
1683		break;
1684	    }
1685    cleanup_until(name);
1686}
1687
1688void
1689tsetenv(const Char *name, const Char *val)
1690{
1691#ifdef SETENV_IN_LIB
1692/*
1693 * XXX: This does not work right, since tcsh cannot track changes to
1694 * the environment this way. (the builtin setenv without arguments does
1695 * not print the right stuff neither does unsetenv). This was for Mach,
1696 * it is not needed anymore.
1697 */
1698#undef setenv
1699    char   *cname;
1700
1701    if (name == NULL)
1702	return;
1703    cname = strsave(short2str(name));
1704    setenv(cname, short2str(val), 1);
1705    xfree(cname);
1706#else /* !SETENV_IN_LIB */
1707    Char **ep = STR_environ;
1708    const Char *ccp;
1709    Char *cp, *dp;
1710    Char   *blk[2];
1711    Char  **oep = ep;
1712
1713#ifdef WINNT_NATIVE
1714    nt_set_env(name,val);
1715#endif /* WINNT_NATIVE */
1716    for (; *ep; ep++) {
1717#ifdef WINNT_NATIVE
1718	for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp);
1719				ccp++, dp++)
1720#else
1721	for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++)
1722#endif /* WINNT_NATIVE */
1723	    continue;
1724	if (*ccp != 0 || *dp != '=')
1725	    continue;
1726	cp = Strspl(STRequal, val);
1727	xfree(*ep);
1728	*ep = strip(Strspl(name, cp));
1729	xfree(cp);
1730	blkfree((Char **) environ);
1731	environ = short2blk(STR_environ);
1732	return;
1733    }
1734    cp = Strspl(name, STRequal);
1735    blk[0] = strip(Strspl(cp, val));
1736    xfree(cp);
1737    blk[1] = 0;
1738    STR_environ = blkspl(STR_environ, blk);
1739    blkfree((Char **) environ);
1740    environ = short2blk(STR_environ);
1741    xfree(oep);
1742#endif /* SETENV_IN_LIB */
1743}
1744
1745void
1746Unsetenv(Char *name)
1747{
1748    Char **ep = STR_environ;
1749    Char *cp, *dp;
1750    Char **oep = ep;
1751
1752#ifdef WINNT_NATIVE
1753	nt_set_env(name,NULL);
1754#endif /*WINNT_NATIVE */
1755    for (; *ep; ep++) {
1756	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
1757	    continue;
1758	if (*cp != 0 || *dp != '=')
1759	    continue;
1760	cp = *ep;
1761	*ep = 0;
1762	STR_environ = blkspl(STR_environ, ep + 1);
1763	blkfree((Char **) environ);
1764	environ = short2blk(STR_environ);
1765	*ep = cp;
1766	xfree(cp);
1767	xfree(oep);
1768	return;
1769    }
1770}
1771
1772/*ARGSUSED*/
1773void
1774doumask(Char **v, struct command *c)
1775{
1776    Char *cp = v[1];
1777    int i;
1778
1779    USE(c);
1780    if (cp == 0) {
1781	i = (int)umask(0);
1782	(void) umask(i);
1783	xprintf("%o\n", i);
1784	return;
1785    }
1786    i = 0;
1787    while (Isdigit(*cp) && *cp != '8' && *cp != '9')
1788	i = i * 8 + *cp++ - '0';
1789    if (*cp || i < 0 || i > 0777)
1790	stderror(ERR_NAME | ERR_MASK);
1791    (void) umask(i);
1792}
1793
1794#ifndef HAVENOLIMIT
1795# ifndef BSDLIMIT
1796   typedef long RLIM_TYPE;
1797#  ifdef _OSD_POSIX /* BS2000 */
1798#   include <ulimit.h>
1799#  endif
1800#  ifndef RLIM_INFINITY
1801#   if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY)
1802    extern RLIM_TYPE ulimit();
1803#   endif /* ! _MINIX && !__clipper__ */
1804#   define RLIM_INFINITY 0x003fffff
1805#   define RLIMIT_FSIZE 1
1806#  endif /* RLIM_INFINITY */
1807#  ifdef aiws
1808#   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
1809#   define RLIMIT_DATA	3
1810#   define RLIMIT_STACK 1005
1811#  else /* aiws */
1812#   define toset(a) ((a) + 1)
1813#  endif /* aiws */
1814# else /* BSDLIMIT */
1815#  if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__)
1816    typedef rlim_t RLIM_TYPE;
1817#  else
1818#   if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3)
1819     typedef rlim_t RLIM_TYPE;
1820#   else
1821#    if defined(_SX)
1822      typedef long long RLIM_TYPE;
1823#    else /* !_SX */
1824      typedef unsigned long RLIM_TYPE;
1825#    endif /* _SX */
1826#   endif /* SOLARIS2 || (sgi && SYSVREL > 3) */
1827#  endif /* BSD4_4 && !__386BSD__  */
1828# endif /* BSDLIMIT */
1829
1830# if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT)
1831/* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
1832/* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
1833#  ifndef RLIMIT_CPU
1834#   define RLIMIT_CPU		0
1835#   define RLIMIT_FSIZE		1
1836#   define RLIMIT_DATA		2
1837#   define RLIMIT_STACK		3
1838#   define RLIMIT_CORE		4
1839#   define RLIMIT_RSS		5
1840#   define RLIMIT_NOFILE	6
1841#  endif /* RLIMIT_CPU */
1842#  ifndef RLIM_INFINITY
1843#   define RLIM_INFINITY	0x7fffffff
1844#  endif /* RLIM_INFINITY */
1845   /*
1846    * old versions of HP/UX counted limits in 512 bytes
1847    */
1848#  ifndef SIGRTMIN
1849#   define FILESIZE512
1850#  endif /* SIGRTMIN */
1851# endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */
1852
1853# if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX)
1854/* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */
1855/* sh.h.  However, some SVR4 limits are defined in <sys/resource.h>.  Rather */
1856/* than include both and get warnings, we define the extra SVR4 limits here. */
1857/* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */
1858/* RLIMIT_VMEM based on it? */
1859#  ifndef RLIMIT_VMEM
1860#   define RLIMIT_VMEM	6
1861#  endif
1862#  ifndef RLIMIT_AS
1863#   define RLIMIT_AS	RLIMIT_VMEM
1864#  endif
1865# endif /* SYSVREL > 3 && BSDLIMIT */
1866
1867# if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__))
1868#  if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
1869#   define RLIMIT_VMEM	RLIMIT_AS
1870#  endif
1871/*
1872 * Oh well, <asm-generic/resource.h> has it, but <bits/resource.h> does not
1873 * Linux headers: When the left hand does not know what the right hand does.
1874 */
1875#  if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME)
1876#   define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1)
1877#  endif
1878# endif
1879
1880struct limits limits[] =
1881{
1882# ifdef RLIMIT_CPU
1883    { RLIMIT_CPU, 	"cputime",	1,	"seconds"	},
1884# endif /* RLIMIT_CPU */
1885
1886# ifdef RLIMIT_FSIZE
1887#  ifndef aiws
1888    { RLIMIT_FSIZE, 	"filesize",	1024,	"kbytes"	},
1889#  else
1890    { RLIMIT_FSIZE, 	"filesize",	512,	"blocks"	},
1891#  endif /* aiws */
1892# endif /* RLIMIT_FSIZE */
1893
1894# ifdef RLIMIT_DATA
1895    { RLIMIT_DATA, 	"datasize",	1024,	"kbytes"	},
1896# endif /* RLIMIT_DATA */
1897
1898# ifdef RLIMIT_STACK
1899#  ifndef aiws
1900    { RLIMIT_STACK, 	"stacksize",	1024,	"kbytes"	},
1901#  else
1902    { RLIMIT_STACK, 	"stacksize",	1024 * 1024,	"kbytes"},
1903#  endif /* aiws */
1904# endif /* RLIMIT_STACK */
1905
1906# ifdef RLIMIT_CORE
1907    { RLIMIT_CORE, 	"coredumpsize",	1024,	"kbytes"	},
1908# endif /* RLIMIT_CORE */
1909
1910# ifdef RLIMIT_RSS
1911    { RLIMIT_RSS, 	"memoryuse",	1024,	"kbytes"	},
1912# endif /* RLIMIT_RSS */
1913
1914# ifdef RLIMIT_UMEM
1915    { RLIMIT_UMEM, 	"memoryuse",	1024,	"kbytes"	},
1916# endif /* RLIMIT_UMEM */
1917
1918# ifdef RLIMIT_VMEM
1919    { RLIMIT_VMEM, 	"vmemoryuse",	1024,	"kbytes"	},
1920# endif /* RLIMIT_VMEM */
1921
1922# if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */
1923    { RLIMIT_HEAP,	"heapsize",	1024,	"kbytes"	},
1924# endif /* RLIMIT_HEAP */
1925
1926# ifdef RLIMIT_NOFILE
1927    { RLIMIT_NOFILE, 	"descriptors", 1,	""		},
1928# endif /* RLIMIT_NOFILE */
1929
1930# ifdef RLIMIT_CONCUR
1931    { RLIMIT_CONCUR, 	"concurrency", 1,	"thread(s)"	},
1932# endif /* RLIMIT_CONCUR */
1933
1934# ifdef RLIMIT_MEMLOCK
1935    { RLIMIT_MEMLOCK,	"memorylocked",	1024,	"kbytes"	},
1936# endif /* RLIMIT_MEMLOCK */
1937
1938# ifdef RLIMIT_NPROC
1939    { RLIMIT_NPROC,	"maxproc",	1,	""		},
1940# endif /* RLIMIT_NPROC */
1941
1942# if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
1943    { RLIMIT_OFILE,	"openfiles",	1,	""		},
1944# endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */
1945
1946# ifdef RLIMIT_SBSIZE
1947    { RLIMIT_SBSIZE,	"sbsize",	1,	""		},
1948# endif /* RLIMIT_SBSIZE */
1949
1950# ifdef RLIMIT_SWAP
1951    { RLIMIT_SWAP,	"swapsize",	1024,	"kbytes"	},
1952# endif /* RLIMIT_SWAP */
1953
1954# ifdef RLIMIT_LOCKS
1955    { RLIMIT_LOCKS,	"maxlocks",	1,	""		},
1956# endif /* RLIMIT_LOCKS */
1957
1958# ifdef RLIMIT_SIGPENDING
1959    { RLIMIT_SIGPENDING,"maxsignal",	1,	""		},
1960# endif /* RLIMIT_SIGPENDING */
1961
1962# ifdef RLIMIT_MSGQUEUE
1963    { RLIMIT_MSGQUEUE,	"maxmessage",	1,	""		},
1964# endif /* RLIMIT_MSGQUEUE */
1965
1966# ifdef RLIMIT_NICE
1967    { RLIMIT_NICE,	"maxnice",	1,	""		},
1968# endif /* RLIMIT_NICE */
1969
1970# ifdef RLIMIT_RTPRIO
1971    { RLIMIT_RTPRIO,	"maxrtprio",	1,	""		},
1972# endif /* RLIMIT_RTPRIO */
1973
1974# ifdef RLIMIT_RTTIME
1975    { RLIMIT_RTTIME,	"maxrttime",	1,	"usec"		},
1976# endif /* RLIMIT_RTTIME */
1977
1978    { -1, 		NULL, 		0, 	NULL		}
1979};
1980
1981static struct limits *findlim	(Char *);
1982static RLIM_TYPE getval		(struct limits *, Char **);
1983static int strtail		(Char *, const char *);
1984static void limtail		(Char *, const char *);
1985static void limtail2		(Char *, const char *, const char *);
1986static void plim		(struct limits *, int);
1987static int setlim		(struct limits *, int, RLIM_TYPE);
1988
1989#ifdef convex
1990static  RLIM_TYPE
1991restrict_limit(double value)
1992{
1993    /*
1994     * is f too large to cope with? return the maximum or minimum int
1995     */
1996    if (value > (double) INT_MAX)
1997	return (RLIM_TYPE) INT_MAX;
1998    else if (value < (double) INT_MIN)
1999	return (RLIM_TYPE) INT_MIN;
2000    else
2001	return (RLIM_TYPE) value;
2002}
2003#else /* !convex */
2004# define restrict_limit(x)	((RLIM_TYPE) (x))
2005#endif /* convex */
2006
2007
2008static struct limits *
2009findlim(Char *cp)
2010{
2011    struct limits *lp, *res;
2012
2013    res = NULL;
2014    for (lp = limits; lp->limconst >= 0; lp++)
2015	if (prefix(cp, str2short(lp->limname))) {
2016	    if (res)
2017		stderror(ERR_NAME | ERR_AMBIG);
2018	    res = lp;
2019	}
2020    if (res)
2021	return (res);
2022    stderror(ERR_NAME | ERR_LIMIT);
2023    /* NOTREACHED */
2024    return (0);
2025}
2026
2027/*ARGSUSED*/
2028void
2029dolimit(Char **v, struct command *c)
2030{
2031    struct limits *lp;
2032    RLIM_TYPE limit;
2033    int    hard = 0;
2034
2035    USE(c);
2036    v++;
2037    if (*v && eq(*v, STRmh)) {
2038	hard = 1;
2039	v++;
2040    }
2041    if (*v == 0) {
2042	for (lp = limits; lp->limconst >= 0; lp++)
2043	    plim(lp, hard);
2044	return;
2045    }
2046    lp = findlim(v[0]);
2047    if (v[1] == 0) {
2048	plim(lp, hard);
2049	return;
2050    }
2051    limit = getval(lp, v + 1);
2052    if (setlim(lp, hard, limit) < 0)
2053	stderror(ERR_SILENT);
2054}
2055
2056static  RLIM_TYPE
2057getval(struct limits *lp, Char **v)
2058{
2059    float f;
2060    Char   *cp = *v++;
2061
2062    f = atof(short2str(cp));
2063
2064# ifdef convex
2065    /*
2066     * is f too large to cope with. limit f to minint, maxint  - X-6768 by
2067     * strike
2068     */
2069    if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
2070	stderror(ERR_NAME | ERR_TOOLARGE);
2071    }
2072# endif /* convex */
2073
2074    while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
2075	cp++;
2076    if (*cp == 0) {
2077	if (*v == 0)
2078	    return restrict_limit((f * lp->limdiv) + 0.5);
2079	cp = *v;
2080    }
2081    switch (*cp) {
2082# ifdef RLIMIT_CPU
2083    case ':':
2084	if (lp->limconst != RLIMIT_CPU)
2085	    goto badscal;
2086	return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
2087    case 'h':
2088	if (lp->limconst != RLIMIT_CPU)
2089	    goto badscal;
2090	limtail(cp, "hours");
2091	f *= 3600.0;
2092	break;
2093# endif /* RLIMIT_CPU */
2094    case 'm':
2095# ifdef RLIMIT_CPU
2096	if (lp->limconst == RLIMIT_CPU) {
2097	    limtail(cp, "minutes");
2098	    f *= 60.0;
2099	    break;
2100	}
2101# endif /* RLIMIT_CPU */
2102	limtail2(cp, "megabytes", "mbytes");
2103	f *= 1024.0 * 1024.0;
2104	break;
2105# ifdef RLIMIT_CPU
2106    case 's':
2107	if (lp->limconst != RLIMIT_CPU)
2108	    goto badscal;
2109	limtail(cp, "seconds");
2110	break;
2111# endif /* RLIMIT_CPU */
2112    case 'G':
2113	*cp = 'g';
2114	/*FALLTHROUGH*/
2115    case 'g':
2116# ifdef RLIMIT_CPU
2117	if (lp->limconst == RLIMIT_CPU)
2118	    goto badscal;
2119# endif /* RLIMIT_CPU */
2120	limtail2(cp, "gigabytes", "gbytes");
2121	f *= 1024.0 * 1024.0 * 1024.0;
2122	break;
2123    case 'M':
2124# ifdef RLIMIT_CPU
2125	if (lp->limconst == RLIMIT_CPU)
2126	    goto badscal;
2127# endif /* RLIMIT_CPU */
2128	*cp = 'm';
2129	limtail2(cp, "megabytes", "mbytes");
2130	f *= 1024.0 * 1024.0;
2131	break;
2132    case 'k':
2133# ifdef RLIMIT_CPU
2134	if (lp->limconst == RLIMIT_CPU)
2135	    goto badscal;
2136# endif /* RLIMIT_CPU */
2137	limtail2(cp, "kilobytes", "kbytes");
2138	f *= 1024.0;
2139	break;
2140    case 'b':
2141# ifdef RLIMIT_CPU
2142	if (lp->limconst == RLIMIT_CPU)
2143	    goto badscal;
2144# endif /* RLIMIT_CPU */
2145	limtail(cp, "blocks");
2146	f *= 512.0;
2147	break;
2148    case 'u':
2149	limtail(cp, "unlimited");
2150	return ((RLIM_TYPE) RLIM_INFINITY);
2151    default:
2152# ifdef RLIMIT_CPU
2153badscal:
2154# endif /* RLIMIT_CPU */
2155	stderror(ERR_NAME | ERR_SCALEF);
2156    }
2157# ifdef convex
2158    return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5));
2159# else
2160    f += 0.5;
2161    if (f > (float) ((RLIM_TYPE) RLIM_INFINITY))
2162	return ((RLIM_TYPE) RLIM_INFINITY);
2163    else
2164	return ((RLIM_TYPE) f);
2165# endif /* convex */
2166}
2167
2168static int
2169strtail(Char *cp, const char *str)
2170{
2171    while (*cp && *cp == (Char)*str)
2172	cp++, str++;
2173    return (*cp != '\0');
2174}
2175
2176static void
2177limtail(Char *cp, const char *str)
2178{
2179    if (strtail(cp, str))
2180	stderror(ERR_BADSCALE, str);
2181}
2182
2183static void
2184limtail2(Char *cp, const char *str1, const char *str2)
2185{
2186    if (strtail(cp, str1) && strtail(cp, str2))
2187	stderror(ERR_BADSCALE, str1);
2188}
2189
2190/*ARGSUSED*/
2191static void
2192plim(struct limits *lp, int hard)
2193{
2194# ifdef BSDLIMIT
2195    struct rlimit rlim;
2196# endif /* BSDLIMIT */
2197    RLIM_TYPE limit;
2198    int     xdiv = lp->limdiv;
2199
2200    xprintf("%-13.13s", lp->limname);
2201
2202# ifndef BSDLIMIT
2203    limit = ulimit(lp->limconst, 0);
2204#  ifdef aiws
2205    if (lp->limconst == RLIMIT_DATA)
2206	limit -= 0x20000000;
2207#  endif /* aiws */
2208# else /* BSDLIMIT */
2209    (void) getrlimit(lp->limconst, &rlim);
2210    limit = hard ? rlim.rlim_max : rlim.rlim_cur;
2211# endif /* BSDLIMIT */
2212
2213# if !defined(BSDLIMIT) || defined(FILESIZE512)
2214    /*
2215     * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
2216     * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
2217     */
2218    if (lp->limconst == RLIMIT_FSIZE) {
2219	if (limit >= (RLIM_INFINITY / 512))
2220	    limit = RLIM_INFINITY;
2221	else
2222	    xdiv = (xdiv == 1024 ? 2 : 1);
2223    }
2224# endif /* !BSDLIMIT || FILESIZE512 */
2225
2226    if (limit == RLIM_INFINITY)
2227	xprintf("unlimited");
2228    else
2229# if defined(RLIMIT_CPU) && defined(_OSD_POSIX)
2230    if (lp->limconst == RLIMIT_CPU &&
2231        (unsigned long)limit >= 0x7ffffffdUL)
2232	xprintf("unlimited");
2233    else
2234# endif
2235# ifdef RLIMIT_CPU
2236    if (lp->limconst == RLIMIT_CPU)
2237	psecs(limit);
2238    else
2239# endif /* RLIMIT_CPU */
2240	xprintf("%ld %s", (long) (limit / xdiv), lp->limscale);
2241    xputchar('\n');
2242}
2243
2244/*ARGSUSED*/
2245void
2246dounlimit(Char **v, struct command *c)
2247{
2248    struct limits *lp;
2249    int    lerr = 0;
2250    int    hard = 0;
2251    int	   force = 0;
2252
2253    USE(c);
2254    while (*++v && **v == '-') {
2255	Char   *vp = *v;
2256	while (*++vp)
2257	    switch (*vp) {
2258	    case 'f':
2259		force = 1;
2260		break;
2261	    case 'h':
2262		hard = 1;
2263		break;
2264	    default:
2265		stderror(ERR_ULIMUS);
2266		break;
2267	    }
2268    }
2269
2270    if (*v == 0) {
2271	for (lp = limits; lp->limconst >= 0; lp++)
2272	    if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
2273		lerr++;
2274	if (!force && lerr)
2275	    stderror(ERR_SILENT);
2276	return;
2277    }
2278    while (*v) {
2279	lp = findlim(*v++);
2280	if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force)
2281	    stderror(ERR_SILENT);
2282    }
2283}
2284
2285static int
2286setlim(struct limits *lp, int hard, RLIM_TYPE limit)
2287{
2288# ifdef BSDLIMIT
2289    struct rlimit rlim;
2290
2291    (void) getrlimit(lp->limconst, &rlim);
2292
2293#  ifdef FILESIZE512
2294    /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
2295    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
2296	limit /= 512;
2297#  endif /* FILESIZE512 */
2298    if (hard)
2299	rlim.rlim_max = limit;
2300    else if (limit == RLIM_INFINITY && euid != 0)
2301	rlim.rlim_cur = rlim.rlim_max;
2302    else
2303	rlim.rlim_cur = limit;
2304
2305    if (rlim.rlim_cur > rlim.rlim_max)
2306	rlim.rlim_max = rlim.rlim_cur;
2307
2308    if (setrlimit(lp->limconst, &rlim) < 0) {
2309# else /* BSDLIMIT */
2310    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
2311	limit /= 512;
2312# ifdef aiws
2313    if (lp->limconst == RLIMIT_DATA)
2314	limit += 0x20000000;
2315# endif /* aiws */
2316    if (ulimit(toset(lp->limconst), limit) < 0) {
2317# endif /* BSDLIMIT */
2318        int err;
2319        char *op, *type;
2320
2321	err = errno;
2322	op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") :
2323		     	CGETS(15, 3, "set"));
2324	cleanup_push(op, xfree);
2325	type = strsave(hard ? CGETS(15, 4, " hard") : "");
2326	cleanup_push(type, xfree);
2327	xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname,
2328	    lp->limname, op, type, strerror(err));
2329	cleanup_until(op);
2330	return (-1);
2331    }
2332    return (0);
2333}
2334
2335#endif /* !HAVENOLIMIT */
2336
2337/*ARGSUSED*/
2338void
2339dosuspend(Char **v, struct command *c)
2340{
2341#ifdef BSDJOBS
2342    struct sigaction old;
2343#endif /* BSDJOBS */
2344
2345    USE(c);
2346    USE(v);
2347
2348    if (loginsh)
2349	stderror(ERR_SUSPLOG);
2350    untty();
2351
2352#ifdef BSDJOBS
2353    sigaction(SIGTSTP, NULL, &old);
2354    signal(SIGTSTP, SIG_DFL);
2355    (void) kill(0, SIGTSTP);
2356    /* the shell stops here */
2357    sigaction(SIGTSTP, &old, NULL);
2358#else /* !BSDJOBS */
2359    stderror(ERR_JOBCONTROL);
2360#endif /* BSDJOBS */
2361
2362#ifdef BSDJOBS
2363    if (tpgrp != -1) {
2364	if (grabpgrp(FSHTTY, opgrp) == -1)
2365	    stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno));
2366	(void) setpgid(0, shpgrp);
2367	(void) tcsetpgrp(FSHTTY, shpgrp);
2368    }
2369#endif /* BSDJOBS */
2370    (void) setdisc(FSHTTY);
2371}
2372
2373/* This is the dreaded EVAL built-in.
2374 *   If you don't fiddle with file descriptors, and reset didfds,
2375 *   this command will either ignore redirection inside or outside
2376 *   its arguments, e.g. eval "date >x"  vs.  eval "date" >x
2377 *   The stuff here seems to work, but I did it by trial and error rather
2378 *   than really knowing what was going on.  If tpgrp is zero, we are
2379 *   probably a background eval, e.g. "eval date &", and we want to
2380 *   make sure that any processes we start stay in our pgrp.
2381 *   This is also the case for "time eval date" -- stay in same pgrp.
2382 *   Otherwise, under stty tostop, processes will stop in the wrong
2383 *   pgrp, with no way for the shell to get them going again.  -IAN!
2384 */
2385
2386struct doeval_state
2387{
2388    Char **evalvec, *evalp;
2389    int didfds;
2390#ifndef CLOSE_ON_EXEC
2391    int didcch;
2392#endif
2393    int saveIN, saveOUT, saveDIAG;
2394    int SHIN, SHOUT, SHDIAG;
2395};
2396
2397static void
2398doeval_cleanup(void *xstate)
2399{
2400    struct doeval_state *state;
2401
2402    state = xstate;
2403    evalvec = state->evalvec;
2404    evalp = state->evalp;
2405    doneinp = 0;
2406#ifndef CLOSE_ON_EXEC
2407    didcch = state->didcch;
2408#endif /* CLOSE_ON_EXEC */
2409    didfds = state->didfds;
2410    xclose(SHIN);
2411    xclose(SHOUT);
2412    xclose(SHDIAG);
2413    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
2414    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
2415    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
2416}
2417
2418static Char **Ggv;
2419/*ARGSUSED*/
2420void
2421doeval(Char **v, struct command *c)
2422{
2423    struct doeval_state state;
2424    int gflag, my_reenter;
2425    Char **gv;
2426    jmp_buf_t osetexit;
2427
2428    USE(c);
2429    v++;
2430    if (*v == 0)
2431	return;
2432    gflag = tglob(v);
2433    if (gflag) {
2434	gv = v = globall(v, gflag);
2435	if (v == 0)
2436	    stderror(ERR_NOMATCH);
2437	cleanup_push(gv, blk_cleanup);
2438	v = copyblk(v);
2439    }
2440    else {
2441	gv = NULL;
2442	v = copyblk(v);
2443	trim(v);
2444    }
2445
2446    Ggv = gv;
2447    state.evalvec = evalvec;
2448    state.evalp = evalp;
2449    state.didfds = didfds;
2450#ifndef CLOSE_ON_EXEC
2451    state.didcch = didcch;
2452#endif /* CLOSE_ON_EXEC */
2453    state.SHIN = SHIN;
2454    state.SHOUT = SHOUT;
2455    state.SHDIAG = SHDIAG;
2456
2457    (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1);
2458    (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1);
2459    (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1);
2460
2461    cleanup_push(&state, doeval_cleanup);
2462
2463    getexit(osetexit);
2464
2465    /* PWP: setjmp/longjmp bugfix for optimizing compilers */
2466#ifdef cray
2467    my_reenter = 1;             /* assume non-zero return val */
2468    if (setexit() == 0) {
2469	my_reenter = 0;         /* Oh well, we were wrong */
2470#else /* !cray */
2471    if ((my_reenter = setexit()) == 0) {
2472#endif /* cray */
2473	evalvec = v;
2474	evalp = 0;
2475	(void)close_on_exec(SHIN = dcopy(0, -1), 1);
2476	(void)close_on_exec(SHOUT = dcopy(1, -1), 1);
2477	(void)close_on_exec(SHDIAG = dcopy(2, -1), 1);
2478#ifndef CLOSE_ON_EXEC
2479	didcch = 0;
2480#endif /* CLOSE_ON_EXEC */
2481	didfds = 0;
2482	gv = Ggv;
2483	process(0);
2484	Ggv = gv;
2485    }
2486
2487    if (my_reenter == 0) {
2488	cleanup_until(&state);
2489	if (Ggv)
2490	    cleanup_until(Ggv);
2491    }
2492
2493    resexit(osetexit);
2494    if (my_reenter)
2495	stderror(ERR_SILENT);
2496}
2497
2498/*************************************************************************/
2499/* print list of builtin commands */
2500
2501static void
2502lbuffed_cleanup (void *dummy)
2503{
2504    USE(dummy);
2505    lbuffed = 1;
2506}
2507
2508/*ARGSUSED*/
2509void
2510dobuiltins(Char **v, struct command *c)
2511{
2512    /* would use print_by_column() in tw.parse.c but that assumes
2513     * we have an array of Char * to pass.. (sg)
2514     */
2515    const struct biltins *b;
2516    int row, col, columns, rows;
2517    unsigned int w, maxwidth;
2518
2519    USE(c);
2520    USE(v);
2521    lbuffed = 0;		/* turn off line buffering */
2522    cleanup_push(&lbuffed, lbuffed_cleanup);
2523
2524    /* find widest string */
2525    for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
2526	maxwidth = max(maxwidth, strlen(b->bname));
2527    ++maxwidth;					/* for space */
2528
2529    columns = (TermH + 1) / maxwidth;	/* PWP: terminal size change */
2530    if (!columns)
2531	columns = 1;
2532    rows = (nbfunc + (columns - 1)) / columns;
2533
2534    for (b = bfunc, row = 0; row < rows; row++) {
2535	for (col = 0; col < columns; col++) {
2536	    if (b < &bfunc[nbfunc]) {
2537		w = strlen(b->bname);
2538		xprintf("%s", b->bname);
2539		if (col < (columns - 1))	/* Not last column? */
2540		    for (; w < maxwidth; w++)
2541			xputchar(' ');
2542		++b;
2543	    }
2544	}
2545	if (row < (rows - 1)) {
2546	    if (Tty_raw_mode)
2547		xputchar('\r');
2548	    xputchar('\n');
2549	}
2550    }
2551#ifdef WINNT_NATIVE
2552    nt_print_builtins(maxwidth);
2553#else
2554    if (Tty_raw_mode)
2555	xputchar('\r');
2556    xputchar('\n');
2557#endif /* WINNT_NATIVE */
2558
2559    cleanup_until(&lbuffed);		/* turn back on line buffering */
2560    flush();
2561}
2562
2563#ifdef NLS_CATALOGS
2564char *
2565xcatgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2566{
2567    char *res;
2568
2569    errno = 0;
2570    while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) {
2571	handle_pending_signals();
2572	errno = 0;
2573    }
2574    return res;
2575}
2576
2577
2578# if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2579char *
2580iconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2581{
2582    static char *buf = NULL;
2583    static size_t buf_size = 0;
2584
2585    char *orig, *dest, *p;
2586    ICONV_CONST char *src;
2587    size_t src_size, dest_size;
2588
2589    orig = xcatgets(ctd, set_id, msg_id, s);
2590    if (catgets_iconv == (iconv_t)-1 || orig == s)
2591        return orig;
2592    src = orig;
2593    src_size = strlen(src) + 1;
2594    if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL)
2595	return orig;
2596    dest = buf;
2597    while (src_size != 0) {
2598        dest_size = buf + buf_size - dest;
2599	if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size)
2600	    == (size_t)-1) {
2601	    switch (errno) {
2602	    case E2BIG:
2603		if ((p = xrealloc(buf, buf_size * 2)) == NULL)
2604		    return orig;
2605		buf_size *= 2;
2606		dest = p + (dest - buf);
2607		buf = p;
2608		break;
2609
2610	    case EILSEQ: case EINVAL: default:
2611		return orig;
2612	    }
2613	}
2614    }
2615    return buf;
2616}
2617# endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2618#endif /* NLS_CATALOGS */
2619
2620void
2621nlsinit(void)
2622{
2623#ifdef NLS_CATALOGS
2624    static const char default_catalog[] = "tcsh";
2625
2626    char *catalog = (char *)(intptr_t)default_catalog;
2627
2628    if (adrof(STRcatalog) != NULL)
2629	catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog)));
2630#ifdef NL_CAT_LOCALE /* POSIX-compliant. */
2631    /*
2632     * Check if LC_MESSAGES is set in the environment and use it, if so.
2633     * If not, fall back to the setting of LANG.
2634     */
2635    catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0);
2636#else /* pre-POSIX */
2637# ifndef MCLoadBySet
2638#  define MCLoadBySet 0
2639#  endif
2640    catd = catopen(catalog, MCLoadBySet);
2641#endif
2642    if (catalog != default_catalog)
2643	xfree(catalog);
2644#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2645    /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */
2646    catgets_iconv = iconv_open (nl_langinfo (CODESET),
2647				xcatgets(catd, 255, 1, "UTF-8"));
2648#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2649#endif /* NLS_CATALOGS */
2650#ifdef WINNT_NATIVE
2651    nls_dll_init();
2652#endif /* WINNT_NATIVE */
2653    errinit();		/* init the errorlist in correct locale */
2654    mesginit();		/* init the messages for signals */
2655    dateinit();		/* init the messages for dates */
2656    editinit();		/* init the editor messages */
2657    terminit();		/* init the termcap messages */
2658}
2659
2660void
2661nlsclose(void)
2662{
2663#ifdef NLS_CATALOGS
2664#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2665    if (catgets_iconv != (iconv_t)-1) {
2666	iconv_close(catgets_iconv);
2667	catgets_iconv = (iconv_t)-1;
2668    }
2669#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2670    if (catd != (nl_catd)-1) {
2671	/*
2672	 * catclose can call other functions which can call longjmp
2673	 * making us re-enter this code. Prevent infinite recursion
2674	 * by resetting catd. Problem reported and solved by:
2675	 * Gerhard Niklasch
2676	 */
2677	nl_catd oldcatd = catd;
2678	catd = (nl_catd)-1;
2679	while (catclose(oldcatd) == -1 && errno == EINTR)
2680	    handle_pending_signals();
2681    }
2682#endif /* NLS_CATALOGS */
2683}
2684