sh.func.c revision 194767
1167465Smp/* $Header: /p/tcsh/cvsroot/tcsh/sh.func.c,v 3.143 2006/08/24 20:56:31 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
35167465SmpRCSID("$tcsh: sh.func.c,v 3.143 2006/08/24 20:56:31 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
44167465Smp#if defined (NLS_CATALOGS) && defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
45145479Smp#include <langinfo.h>
46145479Smpstatic iconv_t catgets_iconv; /* Or (iconv_t)-1 */
47145479Smp#endif
48145479Smp
4959243Sobrien/*
5059243Sobrien * C shell
5159243Sobrien */
5259243Sobrien
53145479Smpextern int MapsAreInited;
54145479Smpextern int NLSMapsAreInited;
55145479Smpextern int GotTermCaps;
5659243Sobrien
5759243Sobrienstatic int zlast = -1;
5859243Sobrien
59167465Smpstatic	void	islogin		(void);
60167465Smpstatic	void	preread		(void);
61167465Smpstatic	void	doagain		(void);
62167465Smpstatic  const char *isrchx	(int);
63167465Smpstatic	void	search		(int, int, Char *);
64167465Smpstatic	int	getword		(struct Strbuf *);
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++;
52259243Sobrien    sp = cp = strip(*v);
52359243Sobrien    if (!letter(*sp))
52459243Sobrien	stderror(ERR_NAME | ERR_VARBEGIN);
52559243Sobrien    while (*cp && alnum(*cp))
52659243Sobrien	cp++;
52759243Sobrien    if (*cp)
52859243Sobrien	stderror(ERR_NAME | ERR_VARALNUM);
52959243Sobrien    cp = *v++;
53059243Sobrien    if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
53159243Sobrien	stderror(ERR_NAME | ERR_NOPAREN);
53259243Sobrien    v++;
533167465Smp    gflag = tglob(v);
53459243Sobrien    if (gflag) {
535167465Smp	v = globall(v, gflag);
536167465Smp	if (v == 0 && !noexec)
53759243Sobrien	    stderror(ERR_NAME | ERR_NOMATCH);
53859243Sobrien    }
53959243Sobrien    else {
540167465Smp	v = saveblk(v);
54159243Sobrien	trim(v);
54259243Sobrien    }
543167465Smp    nwp = xcalloc(1, sizeof *nwp);
54459243Sobrien    nwp->w_fe = nwp->w_fe0 = v;
54559243Sobrien    btell(&nwp->w_start);
54659243Sobrien    nwp->w_fename = Strsave(cp);
54759243Sobrien    nwp->w_next = whyles;
54869408Sache    nwp->w_end.type = TCSH_F_SEEK;
54959243Sobrien    whyles = nwp;
55059243Sobrien    /*
55159243Sobrien     * Pre-read the loop so as to be more comprehensible to a terminal user.
55259243Sobrien     */
55359243Sobrien    zlast = TC_FOREACH;
55459243Sobrien    if (intty)
55559243Sobrien	preread();
556167465Smp    if (!noexec)
557167465Smp	doagain();
55859243Sobrien}
55959243Sobrien
56059243Sobrien/*ARGSUSED*/
56159243Sobrienvoid
562167465Smpdowhile(Char **v, struct command *c)
56359243Sobrien{
564145479Smp    int status;
565145479Smp    int again = whyles != 0 &&
56659243Sobrien			  SEEKEQ(&whyles->w_start, &lineloc) &&
56759243Sobrien			  whyles->w_fename == 0;
56859243Sobrien
56959243Sobrien    USE(c);
57059243Sobrien    v++;
57159243Sobrien    /*
57259243Sobrien     * Implement prereading here also, taking care not to evaluate the
57359243Sobrien     * expression before the loop has been read up from a terminal.
57459243Sobrien     */
575167465Smp    if (noexec)
576167465Smp	status = 0;
577167465Smp    else if (intty && !again)
57859243Sobrien	status = !exp0(&v, 1);
57959243Sobrien    else
58059243Sobrien	status = !expr(&v);
581167465Smp    if (*v && !noexec)
58259243Sobrien	stderror(ERR_NAME | ERR_EXPRESSION);
58359243Sobrien    if (!again) {
584167465Smp	struct whyle *nwp = xcalloc(1, sizeof(*nwp));
58559243Sobrien
58659243Sobrien	nwp->w_start = lineloc;
58769408Sache	nwp->w_end.type = TCSH_F_SEEK;
58859243Sobrien	nwp->w_end.f_seek = 0;
58959243Sobrien	nwp->w_next = whyles;
59059243Sobrien	whyles = nwp;
59159243Sobrien	zlast = TC_WHILE;
59259243Sobrien	if (intty) {
59359243Sobrien	    /*
59459243Sobrien	     * The tty preread
59559243Sobrien	     */
59659243Sobrien	    preread();
59759243Sobrien	    doagain();
59859243Sobrien	    return;
59959243Sobrien	}
60059243Sobrien    }
60159243Sobrien    if (status)
60259243Sobrien	/* We ain't gonna loop no more, no more! */
60359243Sobrien	toend();
60459243Sobrien}
60559243Sobrien
60659243Sobrienstatic void
607167465Smppreread(void)
60859243Sobrien{
609167465Smp    int old_pintr_disabled;
610167465Smp
61169408Sache    whyles->w_end.type = TCSH_I_SEEK;
61259243Sobrien    if (setintr)
613167465Smp	pintr_push_enable(&old_pintr_disabled);
61459243Sobrien    search(TC_BREAK, 0, NULL);		/* read the expression in */
61559243Sobrien    if (setintr)
616167465Smp	cleanup_until(&old_pintr_disabled);
61759243Sobrien    btell(&whyles->w_end);
61859243Sobrien}
61959243Sobrien
62059243Sobrien/*ARGSUSED*/
62159243Sobrienvoid
622167465Smpdoend(Char **v, struct command *c)
62359243Sobrien{
62459243Sobrien    USE(v);
62559243Sobrien    USE(c);
62659243Sobrien    if (!whyles)
62759243Sobrien	stderror(ERR_NAME | ERR_NOTWHILE);
62859243Sobrien    btell(&whyles->w_end);
629167465Smp    if (!noexec)
630167465Smp	doagain();
63159243Sobrien}
63259243Sobrien
63359243Sobrien/*ARGSUSED*/
63459243Sobrienvoid
635167465Smpdocontin(Char **v, struct command *c)
63659243Sobrien{
63759243Sobrien    USE(v);
63859243Sobrien    USE(c);
63959243Sobrien    if (!whyles)
64059243Sobrien	stderror(ERR_NAME | ERR_NOTWHILE);
641167465Smp    if (!noexec)
642167465Smp	doagain();
64359243Sobrien}
64459243Sobrien
64559243Sobrienstatic void
646167465Smpdoagain(void)
64759243Sobrien{
64859243Sobrien    /* Repeating a while is simple */
64959243Sobrien    if (whyles->w_fename == 0) {
65059243Sobrien	bseek(&whyles->w_start);
65159243Sobrien	return;
65259243Sobrien    }
65359243Sobrien    /*
65459243Sobrien     * The foreach variable list actually has a spurious word ")" at the end of
65559243Sobrien     * the w_fe list.  Thus we are at the of the list if one word beyond this
65659243Sobrien     * is 0.
65759243Sobrien     */
65859243Sobrien    if (!whyles->w_fe[1]) {
65959243Sobrien	dobreak(NULL, NULL);
66059243Sobrien	return;
66159243Sobrien    }
662167465Smp    setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE);
66359243Sobrien    bseek(&whyles->w_start);
66459243Sobrien}
66559243Sobrien
66659243Sobrienvoid
667167465Smpdorepeat(Char **v, struct command *kp)
66859243Sobrien{
669100616Smp    int i = 1;
67059243Sobrien
671100616Smp    do {
672100616Smp	i *= getn(v[1]);
673100616Smp	lshift(v, 2);
674100616Smp    } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0);
675167465Smp    if (noexec)
676167465Smp	i = 1;
677100616Smp
678167465Smp    if (setintr) {
679167465Smp	pintr_disabled++;
680167465Smp	cleanup_push(&pintr_disabled, disabled_cleanup);
681167465Smp    }
68259243Sobrien    while (i > 0) {
683167465Smp	if (setintr && pintr_disabled == 1) {
684167465Smp	    cleanup_until(&pintr_disabled);
685167465Smp	    pintr_disabled++;
686167465Smp	    cleanup_push(&pintr_disabled, disabled_cleanup);
687167465Smp	}
68859243Sobrien	reexecute(kp);
68959243Sobrien	--i;
69059243Sobrien    }
691167465Smp    cleanup_until(&pintr_disabled);
69259243Sobrien    donefds();
69359243Sobrien}
69459243Sobrien
69559243Sobrien/*ARGSUSED*/
69659243Sobrienvoid
697167465Smpdoswbrk(Char **v, struct command *c)
69859243Sobrien{
69959243Sobrien    USE(v);
70059243Sobrien    USE(c);
701167465Smp    if (!noexec)
702167465Smp	search(TC_BRKSW, 0, NULL);
70359243Sobrien}
70459243Sobrien
70559243Sobrienint
706167465Smpsrchx(Char *cp)
70759243Sobrien{
70859243Sobrien    struct srch *sp, *sp1, *sp2;
70959243Sobrien    int i;
71059243Sobrien
71159243Sobrien    /*
71259243Sobrien     * Ignore keywords inside heredocs
71359243Sobrien     */
71459243Sobrien    if (inheredoc)
71559243Sobrien	return -1;
71659243Sobrien
71759243Sobrien    /*
71859243Sobrien     * Binary search Sp1 is the beginning of the current search range. Sp2 is
71959243Sobrien     * one past the end.
72059243Sobrien     */
72159243Sobrien    for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
72259243Sobrien	sp = sp1 + ((sp2 - sp1) >> 1);
72359243Sobrien	if ((i = *cp - *sp->s_name) == 0 &&
72459243Sobrien	    (i = Strcmp(cp, str2short(sp->s_name))) == 0)
72559243Sobrien	    return sp->s_value;
72659243Sobrien	if (i < 0)
72759243Sobrien	    sp2 = sp;
72859243Sobrien	else
72959243Sobrien	    sp1 = sp + 1;
73059243Sobrien    }
73159243Sobrien    return (-1);
73259243Sobrien}
73359243Sobrien
734145479Smpstatic const char *
735167465Smpisrchx(int n)
73659243Sobrien{
737145479Smp    struct srch *sp, *sp2;
73859243Sobrien
73959243Sobrien    for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++)
74059243Sobrien	if (sp->s_value == n)
74159243Sobrien	    return (sp->s_name);
74259243Sobrien    return ("");
74359243Sobrien}
74459243Sobrien
74559243Sobrien
746167465Smpstatic int Stype;
74759243Sobrienstatic Char *Sgoal;
74859243Sobrien
74959243Sobrienstatic void
750167465Smpsearch(int type, int level, Char *goal)
75159243Sobrien{
752167465Smp    struct Strbuf word = Strbuf_INIT;
753145479Smp    Char *cp;
754131962Smp    struct whyle *wp;
755131962Smp    int wlevel = 0;
75659243Sobrien
757167465Smp    Stype = type;
75859243Sobrien    Sgoal = goal;
75959243Sobrien    if (type == TC_GOTO) {
76059243Sobrien	struct Ain a;
76169408Sache	a.type = TCSH_F_SEEK;
76259243Sobrien	a.f_seek = 0;
76359243Sobrien	bseek(&a);
76459243Sobrien    }
765167465Smp    cleanup_push(&word, Strbuf_cleanup);
76659243Sobrien    do {
76769408Sache	if (intty && fseekp == feobp && aret == TCSH_F_SEEK)
76859243Sobrien	    printprompt(1, isrchx(type == TC_BREAK ? zlast : type));
76959243Sobrien	/* xprintf("? "), flush(); */
770167465Smp	(void) getword(&word);
771167465Smp	Strbuf_terminate(&word);
772167465Smp	switch (srchx(word.s)) {
77359243Sobrien
77459243Sobrien	case TC_ELSE:
77559243Sobrien	    if (level == 0 && type == TC_IF)
776167465Smp		goto end;
77759243Sobrien	    break;
77859243Sobrien
77959243Sobrien	case TC_IF:
780167465Smp	    while (getword(&word))
78159243Sobrien		continue;
78259243Sobrien	    if ((type == TC_IF || type == TC_ELSE) &&
783167465Smp		eq(word.s, STRthen))
78459243Sobrien		level++;
78559243Sobrien	    break;
78659243Sobrien
78759243Sobrien	case TC_ENDIF:
78859243Sobrien	    if (type == TC_IF || type == TC_ELSE)
78959243Sobrien		level--;
79059243Sobrien	    break;
79159243Sobrien
79259243Sobrien	case TC_FOREACH:
79359243Sobrien	case TC_WHILE:
794131962Smp	    wlevel++;
79559243Sobrien	    if (type == TC_BREAK)
79659243Sobrien		level++;
79759243Sobrien	    break;
79859243Sobrien
79959243Sobrien	case TC_END:
800131962Smp	    if (type == TC_BRKSW) {
801131962Smp		if (wlevel == 0) {
802131962Smp		    wp = whyles;
803131962Smp		    if (wp) {
804131962Smp			    whyles = wp->w_next;
805131962Smp			    wpfree(wp);
806131962Smp		    }
807131962Smp		}
808131962Smp	    }
80959243Sobrien	    if (type == TC_BREAK)
81059243Sobrien		level--;
811131962Smp	    wlevel--;
81259243Sobrien	    break;
81359243Sobrien
81459243Sobrien	case TC_SWITCH:
81559243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
81659243Sobrien		level++;
81759243Sobrien	    break;
81859243Sobrien
81959243Sobrien	case TC_ENDSW:
82059243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
82159243Sobrien		level--;
82259243Sobrien	    break;
82359243Sobrien
82459243Sobrien	case TC_LABEL:
825167465Smp	    if (type == TC_GOTO && getword(&word) && eq(word.s, goal))
82659243Sobrien		level = -1;
82759243Sobrien	    break;
82859243Sobrien
82959243Sobrien	default:
83059243Sobrien	    if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
83159243Sobrien		break;
832167465Smp	    if (word.len == 0 || word.s[word.len - 1] != ':')
83359243Sobrien		break;
834167465Smp	    word.s[--word.len] = 0;
835167465Smp	    if ((type == TC_GOTO && eq(word.s, goal)) ||
836167465Smp		(type == TC_SWITCH && eq(word.s, STRdefault)))
83759243Sobrien		level = -1;
83859243Sobrien	    break;
83959243Sobrien
84059243Sobrien	case TC_CASE:
84159243Sobrien	    if (type != TC_SWITCH || level != 0)
84259243Sobrien		break;
843167465Smp	    (void) getword(&word);
844167465Smp	    if (word.len != 0 && word.s[word.len - 1] == ':')
845167465Smp		word.s[--word.len] = 0;
846167465Smp	    cp = strip(Dfix1(word.s));
847167465Smp	    cleanup_push(cp, xfree);
84859243Sobrien	    if (Gmatch(goal, cp))
84959243Sobrien		level = -1;
850167465Smp	    cleanup_until(cp);
85159243Sobrien	    break;
85259243Sobrien
85359243Sobrien	case TC_DEFAULT:
85459243Sobrien	    if (type == TC_SWITCH && level == 0)
85559243Sobrien		level = -1;
85659243Sobrien	    break;
85759243Sobrien	}
85859243Sobrien	(void) getword(NULL);
85959243Sobrien    } while (level >= 0);
860167465Smp end:
861167465Smp    cleanup_until(&word);
86259243Sobrien}
86359243Sobrien
86459243Sobrienstatic int
865167465Smpgetword(struct Strbuf *wp)
86659243Sobrien{
86759243Sobrien    int found = 0, first;
868145479Smp    eChar c, d;
86959243Sobrien
870167465Smp    if (wp)
871167465Smp	wp->len = 0;
87259243Sobrien    c = readc(1);
87359243Sobrien    d = 0;
87459243Sobrien    do {
87559243Sobrien	while (c == ' ' || c == '\t')
87659243Sobrien	    c = readc(1);
87759243Sobrien	if (c == '#')
87859243Sobrien	    do
87959243Sobrien		c = readc(1);
880145479Smp	    while (c != CHAR_ERR && c != '\n');
881145479Smp	if (c == CHAR_ERR)
88259243Sobrien	    goto past;
88359243Sobrien	if (c == '\n') {
88459243Sobrien	    if (wp)
88559243Sobrien		break;
88659243Sobrien	    return (0);
88759243Sobrien	}
88859243Sobrien	unreadc(c);
88959243Sobrien	found = 1;
89059243Sobrien	first = 1;
89159243Sobrien	do {
89259243Sobrien	    c = readc(1);
89359243Sobrien	    if (c == '\\' && (c = readc(1)) == '\n')
89459243Sobrien		c = ' ';
89559243Sobrien	    if (c == '\'' || c == '"') {
89659243Sobrien		if (d == 0)
89759243Sobrien		    d = c;
89859243Sobrien		else if (d == c)
89959243Sobrien		    d = 0;
90059243Sobrien	    }
901145479Smp	    if (c == CHAR_ERR)
90259243Sobrien		goto past;
903167465Smp	    if (wp)
904167465Smp		Strbuf_append1(wp, (Char) c);
90559243Sobrien	    if (!first && !d && c == '(') {
906167465Smp		if (wp)
907167465Smp		    goto past_word_end;
90859243Sobrien		else
90959243Sobrien		    break;
91059243Sobrien	    }
91159243Sobrien	    first = 0;
91259243Sobrien	} while ((d || (c != ' ' && c != '\t')) && c != '\n');
91359243Sobrien    } while (wp == 0);
91459243Sobrien
915167465Smp past_word_end:
91659243Sobrien    unreadc(c);
917167465Smp    if (found) {
918167465Smp	wp->len--;
919167465Smp	Strbuf_terminate(wp);
920167465Smp    }
92159243Sobrien
92259243Sobrien    return (found);
92359243Sobrien
92459243Sobrienpast:
92559243Sobrien    switch (Stype) {
92659243Sobrien
92759243Sobrien    case TC_IF:
92859243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
92959243Sobrien	break;
93059243Sobrien
93159243Sobrien    case TC_ELSE:
93259243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
93359243Sobrien	break;
93459243Sobrien
93559243Sobrien    case TC_BRKSW:
93659243Sobrien    case TC_SWITCH:
93759243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
93859243Sobrien	break;
93959243Sobrien
94059243Sobrien    case TC_BREAK:
94159243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "end");
94259243Sobrien	break;
94359243Sobrien
94459243Sobrien    case TC_GOTO:
94559243Sobrien	setname(short2str(Sgoal));
94659243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "label");
94759243Sobrien	break;
94859243Sobrien
94959243Sobrien    default:
95059243Sobrien	break;
95159243Sobrien    }
95259243Sobrien    /* NOTREACHED */
95359243Sobrien    return (0);
95459243Sobrien}
95559243Sobrien
95659243Sobrienstatic void
957167465Smptoend(void)
95859243Sobrien{
95969408Sache    if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) {
96059243Sobrien	search(TC_BREAK, 0, NULL);
96159243Sobrien	btell(&whyles->w_end);
96259243Sobrien	whyles->w_end.f_seek--;
96359243Sobrien    }
96459243Sobrien    else {
96559243Sobrien	bseek(&whyles->w_end);
96659243Sobrien    }
96759243Sobrien    wfree();
96859243Sobrien}
96959243Sobrien
970131962Smpstatic void
971167465Smpwpfree(struct whyle *wp)
972131962Smp{
973131962Smp	if (wp->w_fe0)
974131962Smp	    blkfree(wp->w_fe0);
975167465Smp	xfree(wp->w_fename);
976167465Smp	xfree(wp);
977131962Smp}
978131962Smp
97959243Sobrienvoid
980167465Smpwfree(void)
98159243Sobrien{
98259243Sobrien    struct Ain    o;
98359243Sobrien    struct whyle *nwp;
98459243Sobrien#ifdef lint
98559243Sobrien    nwp = NULL;	/* sun lint is dumb! */
98659243Sobrien#endif
98759243Sobrien
98859243Sobrien#ifdef FDEBUG
989167465Smp    static const char foo[] = "IAFE";
99059243Sobrien#endif /* FDEBUG */
99159243Sobrien
99259243Sobrien    btell(&o);
99359243Sobrien
99459243Sobrien#ifdef FDEBUG
995167465Smp    xprintf("o->type %c o->a_seek %d o->f_seek %d\n",
99659243Sobrien	    foo[o.type + 1], o.a_seek, o.f_seek);
99759243Sobrien#endif /* FDEBUG */
99859243Sobrien
99959243Sobrien    for (; whyles; whyles = nwp) {
1000145479Smp	struct whyle *wp = whyles;
100159243Sobrien	nwp = wp->w_next;
100259243Sobrien
100359243Sobrien#ifdef FDEBUG
1004167465Smp	xprintf("start->type %c start->a_seek %d start->f_seek %d\n",
100559243Sobrien		foo[wp->w_start.type+1],
100659243Sobrien		wp->w_start.a_seek, wp->w_start.f_seek);
1007167465Smp	xprintf("end->type %c end->a_seek %d end->f_seek %d\n",
100859243Sobrien		foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
100959243Sobrien#endif /* FDEBUG */
101059243Sobrien
101159243Sobrien	/*
101259243Sobrien	 * XXX: We free loops that have different seek types.
101359243Sobrien	 */
101469408Sache	if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type &&
101559243Sobrien	    wp->w_start.type == o.type) {
101669408Sache	    if (wp->w_end.type == TCSH_F_SEEK) {
101759243Sobrien		if (o.f_seek >= wp->w_start.f_seek &&
101859243Sobrien		    (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
101959243Sobrien		    break;
102059243Sobrien	    }
102159243Sobrien	    else {
102259243Sobrien		if (o.a_seek >= wp->w_start.a_seek &&
102359243Sobrien		    (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
102459243Sobrien		    break;
102559243Sobrien	    }
102659243Sobrien	}
102759243Sobrien
1028131962Smp	wpfree(wp);
102959243Sobrien    }
103059243Sobrien}
103159243Sobrien
103259243Sobrien/*ARGSUSED*/
103359243Sobrienvoid
1034167465Smpdoecho(Char **v, struct command *c)
103559243Sobrien{
103659243Sobrien    USE(c);
103759243Sobrien    xecho(' ', v);
103859243Sobrien}
103959243Sobrien
104059243Sobrien/*ARGSUSED*/
104159243Sobrienvoid
1042167465Smpdoglob(Char **v, struct command *c)
104359243Sobrien{
104459243Sobrien    USE(c);
104559243Sobrien    xecho(0, v);
104659243Sobrien    flush();
104759243Sobrien}
104859243Sobrien
104959243Sobrienstatic void
1050167465Smpxecho(int sep, Char **v)
105159243Sobrien{
1052167465Smp    Char *cp, **globbed = NULL;
105359243Sobrien    int     nonl = 0;
105459243Sobrien    int	    echo_style = ECHO_STYLE;
105559243Sobrien    struct varent *vp;
105659243Sobrien
105759243Sobrien    if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
105859243Sobrien	vp->vec[0] != NULL) {
105959243Sobrien	if (Strcmp(vp->vec[0], STRbsd) == 0)
106059243Sobrien	    echo_style = BSD_ECHO;
106159243Sobrien	else if (Strcmp(vp->vec[0], STRsysv) == 0)
106259243Sobrien	    echo_style = SYSV_ECHO;
106359243Sobrien	else if (Strcmp(vp->vec[0], STRboth) == 0)
106459243Sobrien	    echo_style = BOTH_ECHO;
106559243Sobrien	else if (Strcmp(vp->vec[0], STRnone) == 0)
106659243Sobrien	    echo_style = NONE_ECHO;
106759243Sobrien    }
106859243Sobrien
106959243Sobrien    v++;
107059243Sobrien    if (*v == 0)
107183098Smp	goto done;
1072167465Smp    if (setintr) {
1073167465Smp	int old_pintr_disabled;
1074167465Smp	pintr_push_enable(&old_pintr_disabled);
1075167465Smp	v = glob_all_or_error(v);
1076167465Smp	cleanup_until(&old_pintr_disabled);
1077167465Smp    } else {
1078167465Smp	v = glob_all_or_error(v);
107959243Sobrien    }
1080167465Smp    globbed = v;
1081167465Smp    if (globbed != NULL)
1082167465Smp	cleanup_push(globbed, blk_cleanup);
108359243Sobrien
108459243Sobrien    if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
108559243Sobrien	nonl++, v++;
108659243Sobrien
108759243Sobrien    while ((cp = *v++) != 0) {
1088145479Smp	Char c;
108959243Sobrien
1090167465Smp	if (setintr) {
1091167465Smp	    int old_pintr_disabled;
1092167465Smp
1093167465Smp	    pintr_push_enable(&old_pintr_disabled);
1094167465Smp	    cleanup_until(&old_pintr_disabled);
1095167465Smp	}
109659243Sobrien	while ((c = *cp++) != 0) {
109759243Sobrien	    if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
109859243Sobrien		switch (c = *cp++) {
109959243Sobrien		case 'a':
110059243Sobrien		    c = '\a';
110159243Sobrien		    break;
110259243Sobrien		case 'b':
110359243Sobrien		    c = '\b';
110459243Sobrien		    break;
110559243Sobrien		case 'c':
110659243Sobrien		    nonl = 1;
110759243Sobrien		    goto done;
110859243Sobrien		case 'e':
110959243Sobrien#if 0			/* Windows does not understand \e */
111059243Sobrien		    c = '\e';
111159243Sobrien#else
1112167465Smp		    c = CTL_ESC('\033');
111359243Sobrien#endif
111459243Sobrien		    break;
111559243Sobrien		case 'f':
111659243Sobrien		    c = '\f';
111759243Sobrien		    break;
111859243Sobrien		case 'n':
111959243Sobrien		    c = '\n';
112059243Sobrien		    break;
112159243Sobrien		case 'r':
112259243Sobrien		    c = '\r';
112359243Sobrien		    break;
112459243Sobrien		case 't':
112559243Sobrien		    c = '\t';
112659243Sobrien		    break;
112759243Sobrien		case 'v':
112859243Sobrien		    c = '\v';
112959243Sobrien		    break;
113059243Sobrien		case '\\':
113159243Sobrien		    c = '\\';
113259243Sobrien		    break;
113359243Sobrien		case '0':
113459243Sobrien		    c = 0;
113559243Sobrien		    if (*cp >= '0' && *cp < '8')
113659243Sobrien			c = c * 8 + *cp++ - '0';
113759243Sobrien		    if (*cp >= '0' && *cp < '8')
113859243Sobrien			c = c * 8 + *cp++ - '0';
113959243Sobrien		    if (*cp >= '0' && *cp < '8')
114059243Sobrien			c = c * 8 + *cp++ - '0';
114159243Sobrien		    break;
114259243Sobrien		case '\0':
114359243Sobrien		    c = '\\';
114459243Sobrien		    cp--;
114559243Sobrien		    break;
114659243Sobrien		default:
114759243Sobrien		    xputchar('\\' | QUOTE);
114859243Sobrien		    break;
114959243Sobrien		}
115059243Sobrien	    }
1151145479Smp	    xputwchar(c | QUOTE);
115259243Sobrien
115359243Sobrien	}
115459243Sobrien	if (*v)
115559243Sobrien	    xputchar(sep | QUOTE);
115659243Sobrien    }
115759243Sobriendone:
115859243Sobrien    if (sep && nonl == 0)
115959243Sobrien	xputchar('\n');
116059243Sobrien    else
116159243Sobrien	flush();
1162167465Smp    if (globbed != NULL)
1163167465Smp	cleanup_until(globbed);
116459243Sobrien}
116559243Sobrien
116659243Sobrien/* check whether an environment variable should invoke 'set_locale()' */
1167145479Smpstatic int
1168167465Smpislocale_var(Char *var)
116959243Sobrien{
117059243Sobrien    static Char *locale_vars[] = {
1171131962Smp	STRLANG,	STRLC_ALL, 	STRLC_CTYPE,	STRLC_NUMERIC,
1172131962Smp	STRLC_TIME,	STRLC_COLLATE,	STRLC_MESSAGES,	STRLC_MONETARY, 0
117359243Sobrien    };
1174145479Smp    Char **v;
117559243Sobrien
117659243Sobrien    for (v = locale_vars; *v; ++v)
117759243Sobrien	if (eq(var, *v))
117859243Sobrien	    return 1;
117959243Sobrien    return 0;
118059243Sobrien}
118159243Sobrien
1182167465Smpstatic void
1183167465Smpxlate_cr_cleanup(void *dummy)
1184167465Smp{
1185167465Smp    USE(dummy);
1186167465Smp    xlate_cr = 0;
1187167465Smp}
1188167465Smp
118959243Sobrien/*ARGSUSED*/
119059243Sobrienvoid
1191167465Smpdoprintenv(Char **v, struct command *c)
119259243Sobrien{
119359243Sobrien    Char   *e;
119459243Sobrien
119559243Sobrien    USE(c);
119659243Sobrien    v++;
119759243Sobrien    if (*v == 0) {
1198145479Smp	Char **ep;
119959243Sobrien
120059243Sobrien	xlate_cr = 1;
1201167465Smp	cleanup_push(&xlate_cr, xlate_cr_cleanup);
1202167465Smp	for (ep = STR_environ; *ep; ep++) {
1203167465Smp	    if (setintr) {
1204167465Smp		int old_pintr_disabled;
1205167465Smp
1206167465Smp		pintr_push_enable(&old_pintr_disabled);
1207167465Smp		cleanup_until(&old_pintr_disabled);
1208167465Smp	    }
120959243Sobrien	    xprintf("%S\n", *ep);
1210167465Smp	}
1211167465Smp	cleanup_until(&xlate_cr);
121259243Sobrien    }
121359243Sobrien    else if ((e = tgetenv(*v)) != NULL) {
1214167465Smp	int old_output_raw;
1215167465Smp
1216167465Smp	old_output_raw = output_raw;
121759243Sobrien	output_raw = 1;
1218167465Smp	cleanup_push(&old_output_raw, output_raw_restore);
121959243Sobrien	xprintf("%S\n", e);
1220167465Smp	cleanup_until(&old_output_raw);
122159243Sobrien    }
122259243Sobrien    else
1223167465Smp	setcopy(STRstatus, STR1, VAR_READWRITE);
122459243Sobrien}
122559243Sobrien
122659243Sobrien/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
122759243Sobrien   (and anything else with a modern compiler) */
122859243Sobrien
122959243Sobrien/*ARGSUSED*/
123059243Sobrienvoid
1231167465Smpdosetenv(Char **v, struct command *c)
123259243Sobrien{
123359243Sobrien    Char   *vp, *lp;
123459243Sobrien
123559243Sobrien    USE(c);
123659243Sobrien    if (*++v == 0) {
123759243Sobrien	doprintenv(--v, 0);
123859243Sobrien	return;
123959243Sobrien    }
124059243Sobrien
124159243Sobrien    vp = *v++;
124259243Sobrien
1243100616Smp    lp = vp;
1244100616Smp
1245131962Smp    for (; *lp != '\0' ; lp++) {
1246131962Smp	if (*lp == '=')
1247131962Smp	    stderror(ERR_NAME | ERR_SYNTAX);
1248131962Smp    }
124959243Sobrien    if ((lp = *v++) == 0)
125059243Sobrien	lp = STRNULL;
125159243Sobrien
1252167465Smp    lp = globone(lp, G_APPEND);
1253167465Smp    cleanup_push(lp, xfree);
1254167465Smp    tsetenv(vp, lp);
125559243Sobrien    if (eq(vp, STRKPATH)) {
1256167465Smp        importpath(lp);
125759243Sobrien	dohash(NULL, NULL);
1258167465Smp	cleanup_until(lp);
125959243Sobrien	return;
126059243Sobrien    }
126159243Sobrien
126259243Sobrien#ifdef apollo
126359243Sobrien    if (eq(vp, STRSYSTYPE)) {
126459243Sobrien	dohash(NULL, NULL);
1265167465Smp	cleanup_until(lp);
126659243Sobrien	return;
126759243Sobrien    }
126859243Sobrien#endif /* apollo */
126959243Sobrien
127059243Sobrien    /* dspkanji/dspmbyte autosetting */
127159243Sobrien    /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
127259243Sobrien#if defined(DSPMBYTE)
127359243Sobrien    if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) {
127459243Sobrien	autoset_dspmbyte(lp);
127559243Sobrien    }
127659243Sobrien#endif
127759243Sobrien
127859243Sobrien    if (islocale_var(vp)) {
127959243Sobrien#ifdef NLS
128059243Sobrien	int     k;
128159243Sobrien
128259243Sobrien# ifdef SETLOCALEBUG
128359243Sobrien	dont_free = 1;
128459243Sobrien# endif /* SETLOCALEBUG */
128559243Sobrien	(void) setlocale(LC_ALL, "");
128659243Sobrien# ifdef LC_COLLATE
128759243Sobrien	(void) setlocale(LC_COLLATE, "");
128859243Sobrien# endif
1289145479Smp# ifdef LC_CTYPE
1290145479Smp	(void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1291145479Smp# endif /* LC_CTYPE */
129259243Sobrien# ifdef NLS_CATALOGS
129359243Sobrien#  ifdef LC_MESSAGES
129459243Sobrien	(void) setlocale(LC_MESSAGES, "");
129559243Sobrien#  endif /* LC_MESSAGES */
1296145479Smp	nlsclose();
129759243Sobrien	nlsinit();
129859243Sobrien# endif /* NLS_CATALOGS */
129959243Sobrien# ifdef SETLOCALEBUG
130059243Sobrien	dont_free = 0;
130159243Sobrien# endif /* SETLOCALEBUG */
130259243Sobrien# ifdef STRCOLLBUG
130359243Sobrien	fix_strcoll_bug();
130459243Sobrien# endif /* STRCOLLBUG */
130559243Sobrien	tw_cmd_free();	/* since the collation sequence has changed */
1306167465Smp	for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
130759243Sobrien	    continue;
1308145479Smp	AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
130959243Sobrien#else /* !NLS */
131059243Sobrien	AsciiOnly = 0;
131159243Sobrien#endif /* NLS */
131259243Sobrien	NLSMapsAreInited = 0;
131359243Sobrien	ed_Init();
131459243Sobrien	if (MapsAreInited && !NLSMapsAreInited)
131559243Sobrien	    ed_InitNLSMaps();
1316167465Smp	cleanup_until(lp);
131759243Sobrien	return;
131859243Sobrien    }
131959243Sobrien
1320100616Smp#ifdef NLS_CATALOGS
1321100616Smp    if (eq(vp, STRNLSPATH)) {
1322145479Smp	nlsclose();
1323100616Smp	nlsinit();
1324100616Smp    }
1325100616Smp#endif
1326100616Smp
132759243Sobrien    if (eq(vp, STRNOREBIND)) {
132859243Sobrien	NoNLSRebind = 1;
132959243Sobrien	MapsAreInited = 0;
133059243Sobrien	NLSMapsAreInited = 0;
133159243Sobrien	ed_InitMaps();
1332167465Smp	cleanup_until(lp);
133359243Sobrien	return;
133459243Sobrien    }
133569408Sache#ifdef WINNT_NATIVE
133659243Sobrien    if (eq(vp, STRtcshlang)) {
133759243Sobrien	nlsinit();
1338167465Smp	cleanup_until(lp);
133959243Sobrien	return;
134059243Sobrien    }
134169408Sache#endif /* WINNT_NATIVE */
134259243Sobrien    if (eq(vp, STRKTERM)) {
134359243Sobrien	char *t;
1344167465Smp
1345167465Smp	setv(STRterm, quote(lp), VAR_READWRITE);	/* lp memory used here */
1346167465Smp	cleanup_ignore(lp);
1347167465Smp	cleanup_until(lp);
134859243Sobrien	t = short2str(lp);
134959243Sobrien	if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) {
135059243Sobrien	    editing = 1;
135159243Sobrien	    noediting = 0;
1352167465Smp	    setNS(STRedit);
135359243Sobrien	}
135459243Sobrien	GotTermCaps = 0;
135559243Sobrien	ed_Init();
135659243Sobrien	return;
135759243Sobrien    }
135859243Sobrien
135959243Sobrien    if (eq(vp, STRKHOME)) {
1360167465Smp	Char *canon;
136159243Sobrien	/*
136259243Sobrien	 * convert to canonical pathname (possibly resolving symlinks)
136359243Sobrien	 */
1364167465Smp	canon = dcanon(lp, lp);
1365167465Smp	cleanup_ignore(lp);
1366167465Smp	cleanup_until(lp);
1367167465Smp	cleanup_push(canon, xfree);
1368167465Smp	setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */
1369167465Smp	cleanup_ignore(canon);
1370167465Smp	cleanup_until(canon);
137159243Sobrien
137259243Sobrien	/* fix directory stack for new tilde home */
137359243Sobrien	dtilde();
137459243Sobrien	return;
137559243Sobrien    }
137659243Sobrien
137759243Sobrien    if (eq(vp, STRKSHLVL)) {
1378167465Smp	setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */
1379167465Smp	cleanup_ignore(lp);
1380167465Smp	cleanup_until(lp);
138159243Sobrien	return;
138259243Sobrien    }
138359243Sobrien
138459243Sobrien    if (eq(vp, STRKUSER)) {
1385167465Smp	setv(STRuser, quote(lp), VAR_READWRITE);	/* lp memory used here */
1386167465Smp	cleanup_ignore(lp);
1387167465Smp	cleanup_until(lp);
138859243Sobrien	return;
138959243Sobrien    }
139059243Sobrien
139159243Sobrien    if (eq(vp, STRKGROUP)) {
1392167465Smp	setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */
1393167465Smp	cleanup_ignore(lp);
1394167465Smp	cleanup_until(lp);
139559243Sobrien	return;
139659243Sobrien    }
139759243Sobrien
139859243Sobrien#ifdef COLOR_LS_F
139959243Sobrien    if (eq(vp, STRLS_COLORS)) {
140059243Sobrien        parseLS_COLORS(lp);
1401167465Smp	cleanup_until(lp);
140259243Sobrien	return;
140359243Sobrien    }
140459243Sobrien#endif /* COLOR_LS_F */
140559243Sobrien
140659243Sobrien#ifdef SIG_WINDOW
140759243Sobrien    /*
140859243Sobrien     * Load/Update $LINES $COLUMNS
140959243Sobrien     */
141059243Sobrien    if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
141159243Sobrien	eq(vp, STRTERMCAP)) {
1412167465Smp	cleanup_until(lp);
141359243Sobrien	check_window_size(1);
141459243Sobrien	return;
141559243Sobrien    }
141659243Sobrien
141759243Sobrien    /*
141859243Sobrien     * Change the size to the one directed by $LINES and $COLUMNS
141959243Sobrien     */
142059243Sobrien    if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
142159243Sobrien#if 0
142259243Sobrien	GotTermCaps = 0;
142359243Sobrien#endif
1424167465Smp	cleanup_until(lp);
142559243Sobrien	ed_Init();
142659243Sobrien	return;
142759243Sobrien    }
142859243Sobrien#endif /* SIG_WINDOW */
1429167465Smp    cleanup_until(lp);
143059243Sobrien}
143159243Sobrien
143259243Sobrien/*ARGSUSED*/
143359243Sobrienvoid
1434167465Smpdounsetenv(Char **v, struct command *c)
143559243Sobrien{
1436167465Smp    Char  **ep, *p, *n, *name;
143759243Sobrien    int     i, maxi;
143859243Sobrien
143959243Sobrien    USE(c);
144059243Sobrien    /*
144159243Sobrien     * Find the longest environment variable
144259243Sobrien     */
144359243Sobrien    for (maxi = 0, ep = STR_environ; *ep; ep++) {
144459243Sobrien	for (i = 0, p = *ep; *p && *p != '='; p++, i++)
144559243Sobrien	    continue;
144659243Sobrien	if (i > maxi)
144759243Sobrien	    maxi = i;
144859243Sobrien    }
144959243Sobrien
1450167465Smp    name = xmalloc((maxi + 1) * sizeof(Char));
1451167465Smp    cleanup_push(name, xfree);
145259243Sobrien
145359243Sobrien    while (++v && *v)
145459243Sobrien	for (maxi = 1; maxi;)
145559243Sobrien	    for (maxi = 0, ep = STR_environ; *ep; ep++) {
145659243Sobrien		for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
145759243Sobrien		    continue;
145859243Sobrien		*n = '\0';
145959243Sobrien		if (!Gmatch(name, *v))
146059243Sobrien		    continue;
146159243Sobrien		maxi = 1;
146259243Sobrien
146359243Sobrien		/* Unset the name. This wasn't being done until
146459243Sobrien		 * later but most of the stuff following won't
146559243Sobrien		 * work (particularly the setlocale() and getenv()
146659243Sobrien		 * stuff) as intended until the name is actually
146759243Sobrien		 * removed. (sg)
146859243Sobrien		 */
146959243Sobrien		Unsetenv(name);
147059243Sobrien
147159243Sobrien		if (eq(name, STRNOREBIND)) {
147259243Sobrien		    NoNLSRebind = 0;
147359243Sobrien		    MapsAreInited = 0;
147459243Sobrien		    NLSMapsAreInited = 0;
147559243Sobrien		    ed_InitMaps();
147659243Sobrien		}
147759243Sobrien#ifdef apollo
147859243Sobrien		else if (eq(name, STRSYSTYPE))
147959243Sobrien		    dohash(NULL, NULL);
148059243Sobrien#endif /* apollo */
148159243Sobrien		else if (islocale_var(name)) {
148259243Sobrien#ifdef NLS
148359243Sobrien		    int     k;
148459243Sobrien
148559243Sobrien# ifdef SETLOCALEBUG
148659243Sobrien		    dont_free = 1;
148759243Sobrien# endif /* SETLOCALEBUG */
148859243Sobrien		    (void) setlocale(LC_ALL, "");
148959243Sobrien# ifdef LC_COLLATE
149059243Sobrien		    (void) setlocale(LC_COLLATE, "");
149159243Sobrien# endif
1492145479Smp# ifdef LC_CTYPE
1493167465Smp		    (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1494145479Smp# endif /* LC_CTYPE */
149559243Sobrien# ifdef NLS_CATALOGS
149659243Sobrien#  ifdef LC_MESSAGES
149759243Sobrien		    (void) setlocale(LC_MESSAGES, "");
149859243Sobrien#  endif /* LC_MESSAGES */
1499145479Smp		    nlsclose();
150059243Sobrien		    nlsinit();
150159243Sobrien# endif /* NLS_CATALOGS */
150259243Sobrien# ifdef SETLOCALEBUG
150359243Sobrien		    dont_free = 0;
150459243Sobrien# endif /* SETLOCALEBUG */
150559243Sobrien# ifdef STRCOLLBUG
150659243Sobrien		    fix_strcoll_bug();
150759243Sobrien# endif /* STRCOLLBUG */
150859243Sobrien		    tw_cmd_free();/* since the collation sequence has changed */
1509167465Smp		    for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
151059243Sobrien			continue;
1511145479Smp		    AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
151259243Sobrien#else /* !NLS */
151359243Sobrien		    AsciiOnly = getenv("LANG") == NULL &&
151459243Sobrien			getenv("LC_CTYPE") == NULL;
151559243Sobrien#endif /* NLS */
151659243Sobrien		    NLSMapsAreInited = 0;
151759243Sobrien		    ed_Init();
151859243Sobrien		    if (MapsAreInited && !NLSMapsAreInited)
151959243Sobrien			ed_InitNLSMaps();
152059243Sobrien
152159243Sobrien		}
152269408Sache#ifdef WINNT_NATIVE
152359243Sobrien		else if (eq(name,(STRtcshlang))) {
152459243Sobrien		    nls_dll_unload();
152559243Sobrien		    nlsinit();
152659243Sobrien		}
152769408Sache#endif /* WINNT_NATIVE */
152859243Sobrien#ifdef COLOR_LS_F
152959243Sobrien		else if (eq(name, STRLS_COLORS))
153059243Sobrien		    parseLS_COLORS(n);
153159243Sobrien#endif /* COLOR_LS_F */
1532100616Smp#ifdef NLS_CATALOGS
1533100616Smp		else if (eq(name, STRNLSPATH)) {
1534145479Smp		    nlsclose();
1535100616Smp		    nlsinit();
1536100616Smp		}
1537100616Smp#endif
153859243Sobrien		/*
153959243Sobrien		 * start again cause the environment changes
154059243Sobrien		 */
154159243Sobrien		break;
154259243Sobrien	    }
1543167465Smp    cleanup_until(name);
154459243Sobrien}
154559243Sobrien
154659243Sobrienvoid
1547167465Smptsetenv(const Char *name, const Char *val)
154859243Sobrien{
154959243Sobrien#ifdef SETENV_IN_LIB
155059243Sobrien/*
155159243Sobrien * XXX: This does not work right, since tcsh cannot track changes to
155259243Sobrien * the environment this way. (the builtin setenv without arguments does
155359243Sobrien * not print the right stuff neither does unsetenv). This was for Mach,
155459243Sobrien * it is not needed anymore.
155559243Sobrien */
155659243Sobrien#undef setenv
1557167465Smp    char   *cname;
155859243Sobrien
1559167465Smp    if (name == NULL)
156059243Sobrien	return;
1561167465Smp    cname = strsave(short2str(name));
1562167465Smp    setenv(cname, short2str(val), 1);
1563167465Smp    xfree(cname);
156459243Sobrien#else /* !SETENV_IN_LIB */
1565145479Smp    Char **ep = STR_environ;
1566145479Smp    const Char *ccp;
1567145479Smp    Char *cp, *dp;
156859243Sobrien    Char   *blk[2];
156959243Sobrien    Char  **oep = ep;
157059243Sobrien
157169408Sache#ifdef WINNT_NATIVE
1572167465Smp    nt_set_env(name,val);
157369408Sache#endif /* WINNT_NATIVE */
157459243Sobrien    for (; *ep; ep++) {
157569408Sache#ifdef WINNT_NATIVE
1576145479Smp	for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp);
1577145479Smp				ccp++, dp++)
157859243Sobrien#else
1579145479Smp	for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++)
158069408Sache#endif /* WINNT_NATIVE */
158159243Sobrien	    continue;
1582145479Smp	if (*ccp != 0 || *dp != '=')
158359243Sobrien	    continue;
158459243Sobrien	cp = Strspl(STRequal, val);
1585167465Smp	xfree(*ep);
158659243Sobrien	*ep = strip(Strspl(name, cp));
1587167465Smp	xfree(cp);
158859243Sobrien	blkfree((Char **) environ);
158959243Sobrien	environ = short2blk(STR_environ);
159059243Sobrien	return;
159159243Sobrien    }
159259243Sobrien    cp = Strspl(name, STRequal);
159359243Sobrien    blk[0] = strip(Strspl(cp, val));
1594167465Smp    xfree(cp);
159559243Sobrien    blk[1] = 0;
159659243Sobrien    STR_environ = blkspl(STR_environ, blk);
159759243Sobrien    blkfree((Char **) environ);
159859243Sobrien    environ = short2blk(STR_environ);
1599167465Smp    xfree(oep);
160059243Sobrien#endif /* SETENV_IN_LIB */
160159243Sobrien}
160259243Sobrien
160359243Sobrienvoid
1604167465SmpUnsetenv(Char *name)
160559243Sobrien{
1606145479Smp    Char **ep = STR_environ;
1607145479Smp    Char *cp, *dp;
160859243Sobrien    Char **oep = ep;
160959243Sobrien
161069408Sache#ifdef WINNT_NATIVE
161159243Sobrien	nt_set_env(name,NULL);
161269408Sache#endif /*WINNT_NATIVE */
161359243Sobrien    for (; *ep; ep++) {
161459243Sobrien	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
161559243Sobrien	    continue;
161659243Sobrien	if (*cp != 0 || *dp != '=')
161759243Sobrien	    continue;
161859243Sobrien	cp = *ep;
161959243Sobrien	*ep = 0;
162059243Sobrien	STR_environ = blkspl(STR_environ, ep + 1);
162159243Sobrien	blkfree((Char **) environ);
162259243Sobrien	environ = short2blk(STR_environ);
162359243Sobrien	*ep = cp;
1624167465Smp	xfree(cp);
1625167465Smp	xfree(oep);
162659243Sobrien	return;
162759243Sobrien    }
162859243Sobrien}
162959243Sobrien
163059243Sobrien/*ARGSUSED*/
163159243Sobrienvoid
1632167465Smpdoumask(Char **v, struct command *c)
163359243Sobrien{
1634145479Smp    Char *cp = v[1];
1635145479Smp    int i;
163659243Sobrien
163759243Sobrien    USE(c);
163859243Sobrien    if (cp == 0) {
163959415Sobrien	i = (int)umask(0);
164059243Sobrien	(void) umask(i);
164159243Sobrien	xprintf("%o\n", i);
164259243Sobrien	return;
164359243Sobrien    }
164459243Sobrien    i = 0;
164559243Sobrien    while (Isdigit(*cp) && *cp != '8' && *cp != '9')
164659243Sobrien	i = i * 8 + *cp++ - '0';
164759243Sobrien    if (*cp || i < 0 || i > 0777)
164859243Sobrien	stderror(ERR_NAME | ERR_MASK);
164959243Sobrien    (void) umask(i);
165059243Sobrien}
165159243Sobrien
165259243Sobrien#ifndef HAVENOLIMIT
165359243Sobrien# ifndef BSDLIMIT
165459243Sobrien   typedef long RLIM_TYPE;
1655145479Smp#  ifdef _OSD_POSIX /* BS2000 */
1656145479Smp#   include <ulimit.h>
1657145479Smp#  endif
165859243Sobrien#  ifndef RLIM_INFINITY
165959243Sobrien#   if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY)
166059243Sobrien    extern RLIM_TYPE ulimit();
166159243Sobrien#   endif /* ! _MINIX && !__clipper__ */
166259243Sobrien#   define RLIM_INFINITY 0x003fffff
166359243Sobrien#   define RLIMIT_FSIZE 1
166459243Sobrien#  endif /* RLIM_INFINITY */
166559243Sobrien#  ifdef aiws
166659243Sobrien#   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
166759243Sobrien#   define RLIMIT_DATA	3
166859243Sobrien#   define RLIMIT_STACK 1005
166959243Sobrien#  else /* aiws */
167059243Sobrien#   define toset(a) ((a) + 1)
167159243Sobrien#  endif /* aiws */
167259243Sobrien# else /* BSDLIMIT */
1673145479Smp#  if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__)
1674100616Smp    typedef rlim_t RLIM_TYPE;
167559243Sobrien#  else
167659243Sobrien#   if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3)
167759243Sobrien     typedef rlim_t RLIM_TYPE;
167859243Sobrien#   else
167959243Sobrien#    if defined(_SX)
168059243Sobrien      typedef long long RLIM_TYPE;
1681100616Smp#    else /* !_SX */
168259243Sobrien      typedef unsigned long RLIM_TYPE;
168359243Sobrien#    endif /* _SX */
168459243Sobrien#   endif /* SOLARIS2 || (sgi && SYSVREL > 3) */
168559243Sobrien#  endif /* BSD4_4 && !__386BSD__  */
168659243Sobrien# endif /* BSDLIMIT */
168759243Sobrien
1688131962Smp# if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT)
168959243Sobrien/* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
169059243Sobrien/* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
169159243Sobrien#  ifndef RLIMIT_CPU
169259243Sobrien#   define RLIMIT_CPU		0
169359243Sobrien#   define RLIMIT_FSIZE		1
169459243Sobrien#   define RLIMIT_DATA		2
169559243Sobrien#   define RLIMIT_STACK		3
169659243Sobrien#   define RLIMIT_CORE		4
169759243Sobrien#   define RLIMIT_RSS		5
169859243Sobrien#   define RLIMIT_NOFILE	6
169959243Sobrien#  endif /* RLIMIT_CPU */
170059243Sobrien#  ifndef RLIM_INFINITY
170159243Sobrien#   define RLIM_INFINITY	0x7fffffff
170259243Sobrien#  endif /* RLIM_INFINITY */
170359243Sobrien   /*
170459243Sobrien    * old versions of HP/UX counted limits in 512 bytes
170559243Sobrien    */
170659243Sobrien#  ifndef SIGRTMIN
170759243Sobrien#   define FILESIZE512
170859243Sobrien#  endif /* SIGRTMIN */
1709131962Smp# endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */
171059243Sobrien
171159243Sobrien# if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX)
171259243Sobrien/* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */
171359243Sobrien/* sh.h.  However, some SVR4 limits are defined in <sys/resource.h>.  Rather */
171459243Sobrien/* than include both and get warnings, we define the extra SVR4 limits here. */
171583098Smp/* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */
171683098Smp/* RLIMIT_VMEM based on it? */
171759243Sobrien#  ifndef RLIMIT_VMEM
171859243Sobrien#   define RLIMIT_VMEM	6
171959243Sobrien#  endif
172059243Sobrien#  ifndef RLIMIT_AS
172159243Sobrien#   define RLIMIT_AS	RLIMIT_VMEM
172259243Sobrien#  endif
172359243Sobrien# endif /* SYSVREL > 3 && BSDLIMIT */
172459243Sobrien
1725145479Smp# if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__)) && defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
172683098Smp#  define RLIMIT_VMEM	RLIMIT_AS
172783098Smp# endif
172883098Smp
172959243Sobrienstruct limits limits[] =
173059243Sobrien{
173159243Sobrien# ifdef RLIMIT_CPU
173259243Sobrien    { RLIMIT_CPU, 	"cputime",	1,	"seconds"	},
173359243Sobrien# endif /* RLIMIT_CPU */
173459243Sobrien
173559243Sobrien# ifdef RLIMIT_FSIZE
173659243Sobrien#  ifndef aiws
173759243Sobrien    { RLIMIT_FSIZE, 	"filesize",	1024,	"kbytes"	},
173859243Sobrien#  else
173959243Sobrien    { RLIMIT_FSIZE, 	"filesize",	512,	"blocks"	},
174059243Sobrien#  endif /* aiws */
174159243Sobrien# endif /* RLIMIT_FSIZE */
174259243Sobrien
174359243Sobrien# ifdef RLIMIT_DATA
174459243Sobrien    { RLIMIT_DATA, 	"datasize",	1024,	"kbytes"	},
174559243Sobrien# endif /* RLIMIT_DATA */
174659243Sobrien
174759243Sobrien# ifdef RLIMIT_STACK
174859243Sobrien#  ifndef aiws
174959243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024,	"kbytes"	},
175059243Sobrien#  else
175159243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024 * 1024,	"kbytes"},
175259243Sobrien#  endif /* aiws */
175359243Sobrien# endif /* RLIMIT_STACK */
175459243Sobrien
175559243Sobrien# ifdef RLIMIT_CORE
175659243Sobrien    { RLIMIT_CORE, 	"coredumpsize",	1024,	"kbytes"	},
175759243Sobrien# endif /* RLIMIT_CORE */
175859243Sobrien
175959243Sobrien# ifdef RLIMIT_RSS
176059243Sobrien    { RLIMIT_RSS, 	"memoryuse",	1024,	"kbytes"	},
176159243Sobrien# endif /* RLIMIT_RSS */
176259243Sobrien
176359243Sobrien# ifdef RLIMIT_UMEM
176459243Sobrien    { RLIMIT_UMEM, 	"memoryuse",	1024,	"kbytes"	},
176559243Sobrien# endif /* RLIMIT_UMEM */
176659243Sobrien
176759243Sobrien# ifdef RLIMIT_VMEM
176859243Sobrien    { RLIMIT_VMEM, 	"vmemoryuse",	1024,	"kbytes"	},
176959243Sobrien# endif /* RLIMIT_VMEM */
177059243Sobrien
1771145479Smp# if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */
1772145479Smp    { RLIMIT_HEAP,	"heapsize",	1024,	"kbytes"	},
1773145479Smp# endif /* RLIMIT_HEAP */
1774145479Smp
177559243Sobrien# ifdef RLIMIT_NOFILE
177659243Sobrien    { RLIMIT_NOFILE, 	"descriptors", 1,	""		},
177759243Sobrien# endif /* RLIMIT_NOFILE */
177859243Sobrien
177959243Sobrien# ifdef RLIMIT_CONCUR
178059243Sobrien    { RLIMIT_CONCUR, 	"concurrency", 1,	"thread(s)"	},
178159243Sobrien# endif /* RLIMIT_CONCUR */
178259243Sobrien
178359243Sobrien# ifdef RLIMIT_MEMLOCK
178459243Sobrien    { RLIMIT_MEMLOCK,	"memorylocked",	1024,	"kbytes"	},
178559243Sobrien# endif /* RLIMIT_MEMLOCK */
178659243Sobrien
178759243Sobrien# ifdef RLIMIT_NPROC
178859243Sobrien    { RLIMIT_NPROC,	"maxproc",	1,	""		},
178959243Sobrien# endif /* RLIMIT_NPROC */
179059243Sobrien
1791100616Smp# if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
179259243Sobrien    { RLIMIT_OFILE,	"openfiles",	1,	""		},
1793100616Smp# endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */
179459243Sobrien
1795100616Smp# ifdef RLIMIT_SBSIZE
1796100616Smp    { RLIMIT_SBSIZE,	"sbsize",	1,	""		},
1797100616Smp# endif /* RLIMIT_SBSIZE */
1798100616Smp
1799194767Skib# ifdef RLIMIT_SWAP
1800194767Skib    { RLIMIT_SWAP,	"swaplimit",	1024,	"kbytes"	},
1801194767Skib# endif /* RLIMIT_SWAP */
1802194767Skib
180359243Sobrien    { -1, 		NULL, 		0, 	NULL		}
180459243Sobrien};
180559243Sobrien
1806167465Smpstatic struct limits *findlim	(Char *);
1807167465Smpstatic RLIM_TYPE getval		(struct limits *, Char **);
1808167465Smpstatic void limtail		(Char *, const char *);
1809167465Smpstatic void plim		(struct limits *, int);
1810167465Smpstatic int setlim		(struct limits *, int, RLIM_TYPE);
181159243Sobrien
181259243Sobrien#ifdef convex
181359243Sobrienstatic  RLIM_TYPE
1814167465Smprestrict_limit(double value)
181559243Sobrien{
181659243Sobrien    /*
181759243Sobrien     * is f too large to cope with? return the maximum or minimum int
181859243Sobrien     */
181959243Sobrien    if (value > (double) INT_MAX)
182059243Sobrien	return (RLIM_TYPE) INT_MAX;
182159243Sobrien    else if (value < (double) INT_MIN)
182259243Sobrien	return (RLIM_TYPE) INT_MIN;
182359243Sobrien    else
182459243Sobrien	return (RLIM_TYPE) value;
182559243Sobrien}
182659243Sobrien#else /* !convex */
182759243Sobrien# define restrict_limit(x)	((RLIM_TYPE) (x))
182859243Sobrien#endif /* convex */
182959243Sobrien
183059243Sobrien
183159243Sobrienstatic struct limits *
1832167465Smpfindlim(Char *cp)
183359243Sobrien{
1834145479Smp    struct limits *lp, *res;
183559243Sobrien
1836167465Smp    res = NULL;
183759243Sobrien    for (lp = limits; lp->limconst >= 0; lp++)
183859243Sobrien	if (prefix(cp, str2short(lp->limname))) {
183959243Sobrien	    if (res)
184059243Sobrien		stderror(ERR_NAME | ERR_AMBIG);
184159243Sobrien	    res = lp;
184259243Sobrien	}
184359243Sobrien    if (res)
184459243Sobrien	return (res);
184559243Sobrien    stderror(ERR_NAME | ERR_LIMIT);
184659243Sobrien    /* NOTREACHED */
184759243Sobrien    return (0);
184859243Sobrien}
184959243Sobrien
185059243Sobrien/*ARGSUSED*/
185159243Sobrienvoid
1852167465Smpdolimit(Char **v, struct command *c)
185359243Sobrien{
1854145479Smp    struct limits *lp;
1855145479Smp    RLIM_TYPE limit;
185659243Sobrien    int    hard = 0;
185759243Sobrien
185859243Sobrien    USE(c);
185959243Sobrien    v++;
186059243Sobrien    if (*v && eq(*v, STRmh)) {
186159243Sobrien	hard = 1;
186259243Sobrien	v++;
186359243Sobrien    }
186459243Sobrien    if (*v == 0) {
186559243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
186659243Sobrien	    plim(lp, hard);
186759243Sobrien	return;
186859243Sobrien    }
186959243Sobrien    lp = findlim(v[0]);
187059243Sobrien    if (v[1] == 0) {
187159243Sobrien	plim(lp, hard);
187259243Sobrien	return;
187359243Sobrien    }
187459243Sobrien    limit = getval(lp, v + 1);
187559243Sobrien    if (setlim(lp, hard, limit) < 0)
187659243Sobrien	stderror(ERR_SILENT);
187759243Sobrien}
187859243Sobrien
187959243Sobrienstatic  RLIM_TYPE
1880167465Smpgetval(struct limits *lp, Char **v)
188159243Sobrien{
1882145479Smp    float f;
188359243Sobrien    Char   *cp = *v++;
188459243Sobrien
188559243Sobrien    f = atof(short2str(cp));
188659243Sobrien
188759243Sobrien# ifdef convex
188859243Sobrien    /*
188959243Sobrien     * is f too large to cope with. limit f to minint, maxint  - X-6768 by
189059243Sobrien     * strike
189159243Sobrien     */
189259243Sobrien    if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
189359243Sobrien	stderror(ERR_NAME | ERR_TOOLARGE);
189459243Sobrien    }
189559243Sobrien# endif /* convex */
189659243Sobrien
189759243Sobrien    while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
189859243Sobrien	cp++;
189959243Sobrien    if (*cp == 0) {
190059243Sobrien	if (*v == 0)
190169408Sache	    return restrict_limit((f * lp->limdiv) + 0.5);
190259243Sobrien	cp = *v;
190359243Sobrien    }
190459243Sobrien    switch (*cp) {
190559243Sobrien# ifdef RLIMIT_CPU
190659243Sobrien    case ':':
190759243Sobrien	if (lp->limconst != RLIMIT_CPU)
190859243Sobrien	    goto badscal;
190959243Sobrien	return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
191059243Sobrien    case 'h':
191159243Sobrien	if (lp->limconst != RLIMIT_CPU)
191259243Sobrien	    goto badscal;
191359243Sobrien	limtail(cp, "hours");
191459243Sobrien	f *= 3600.0;
191559243Sobrien	break;
191659243Sobrien    case 'm':
191759243Sobrien	if (lp->limconst == RLIMIT_CPU) {
191859243Sobrien	    limtail(cp, "minutes");
191959243Sobrien	    f *= 60.0;
192059243Sobrien	    break;
192159243Sobrien	}
192259243Sobrien	*cp = 'm';
192359243Sobrien	limtail(cp, "megabytes");
192459243Sobrien	f *= 1024.0 * 1024.0;
192559243Sobrien	break;
192659243Sobrien    case 's':
192759243Sobrien	if (lp->limconst != RLIMIT_CPU)
192859243Sobrien	    goto badscal;
192959243Sobrien	limtail(cp, "seconds");
193059243Sobrien	break;
193159243Sobrien# endif /* RLIMIT_CPU */
193259243Sobrien    case 'M':
193359243Sobrien# ifdef RLIMIT_CPU
193459243Sobrien	if (lp->limconst == RLIMIT_CPU)
193559243Sobrien	    goto badscal;
193659243Sobrien# endif /* RLIMIT_CPU */
193759243Sobrien	*cp = 'm';
193859243Sobrien	limtail(cp, "megabytes");
193959243Sobrien	f *= 1024.0 * 1024.0;
194059243Sobrien	break;
194159243Sobrien    case 'k':
194259243Sobrien# ifdef RLIMIT_CPU
194359243Sobrien	if (lp->limconst == RLIMIT_CPU)
194459243Sobrien	    goto badscal;
194559243Sobrien# endif /* RLIMIT_CPU */
194659243Sobrien	limtail(cp, "kbytes");
194759243Sobrien	f *= 1024.0;
194859243Sobrien	break;
194959243Sobrien    case 'b':
195059243Sobrien# ifdef RLIMIT_CPU
195159243Sobrien	if (lp->limconst == RLIMIT_CPU)
195259243Sobrien	    goto badscal;
195359243Sobrien# endif /* RLIMIT_CPU */
195459243Sobrien	limtail(cp, "blocks");
195559243Sobrien	f *= 512.0;
195659243Sobrien	break;
195759243Sobrien    case 'u':
195859243Sobrien	limtail(cp, "unlimited");
195959243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
196059243Sobrien    default:
196159243Sobrien# ifdef RLIMIT_CPU
196259243Sobrienbadscal:
196359243Sobrien# endif /* RLIMIT_CPU */
196459243Sobrien	stderror(ERR_NAME | ERR_SCALEF);
196559243Sobrien    }
196659243Sobrien# ifdef convex
196759243Sobrien    return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5));
196859243Sobrien# else
196959243Sobrien    f += 0.5;
197059243Sobrien    if (f > (float) RLIM_INFINITY)
197159243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
197259243Sobrien    else
197359243Sobrien	return ((RLIM_TYPE) f);
197459243Sobrien# endif /* convex */
197559243Sobrien}
197659243Sobrien
197759243Sobrienstatic void
1978167465Smplimtail(Char *cp, const char *str)
197959243Sobrien{
1980145479Smp    const char *sp;
198161515Sobrien
198261515Sobrien    sp = str;
1983145479Smp    while (*cp && *cp == (Char)*str)
198459243Sobrien	cp++, str++;
198559243Sobrien    if (*cp)
198661515Sobrien	stderror(ERR_BADSCALE, sp);
198759243Sobrien}
198859243Sobrien
198959243Sobrien
199059243Sobrien/*ARGSUSED*/
199159243Sobrienstatic void
1992167465Smpplim(struct limits *lp, int hard)
199359243Sobrien{
199459243Sobrien# ifdef BSDLIMIT
199559243Sobrien    struct rlimit rlim;
199659243Sobrien# endif /* BSDLIMIT */
199759243Sobrien    RLIM_TYPE limit;
1998145479Smp    int     xdiv = lp->limdiv;
199959243Sobrien
2000131962Smp    xprintf("%-13.13s", lp->limname);
200159243Sobrien
200259243Sobrien# ifndef BSDLIMIT
200359243Sobrien    limit = ulimit(lp->limconst, 0);
200459243Sobrien#  ifdef aiws
200559243Sobrien    if (lp->limconst == RLIMIT_DATA)
200659243Sobrien	limit -= 0x20000000;
200759243Sobrien#  endif /* aiws */
200859243Sobrien# else /* BSDLIMIT */
200959243Sobrien    (void) getrlimit(lp->limconst, &rlim);
201059243Sobrien    limit = hard ? rlim.rlim_max : rlim.rlim_cur;
201159243Sobrien# endif /* BSDLIMIT */
201259243Sobrien
201359243Sobrien# if !defined(BSDLIMIT) || defined(FILESIZE512)
201459243Sobrien    /*
201559243Sobrien     * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
201659243Sobrien     * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
201759243Sobrien     */
201859243Sobrien    if (lp->limconst == RLIMIT_FSIZE) {
201959243Sobrien	if (limit >= (RLIM_INFINITY / 512))
202059243Sobrien	    limit = RLIM_INFINITY;
202159243Sobrien	else
2022145479Smp	    xdiv = (xdiv == 1024 ? 2 : 1);
202359243Sobrien    }
202459243Sobrien# endif /* !BSDLIMIT || FILESIZE512 */
202559243Sobrien
202659243Sobrien    if (limit == RLIM_INFINITY)
202759243Sobrien	xprintf("unlimited");
202859243Sobrien    else
2029145479Smp# if defined(RLIMIT_CPU) && defined(_OSD_POSIX)
2030145479Smp    if (lp->limconst == RLIMIT_CPU &&
2031145479Smp        (unsigned long)limit >= 0x7ffffffdUL)
2032145479Smp	xprintf("unlimited");
2033145479Smp    else
2034145479Smp# endif
203559243Sobrien# ifdef RLIMIT_CPU
203659243Sobrien    if (lp->limconst == RLIMIT_CPU)
2037167465Smp	psecs(limit);
203859243Sobrien    else
203959243Sobrien# endif /* RLIMIT_CPU */
2040145479Smp	xprintf("%ld %s", (long) (limit / xdiv), lp->limscale);
204159243Sobrien    xputchar('\n');
204259243Sobrien}
204359243Sobrien
204459243Sobrien/*ARGSUSED*/
204559243Sobrienvoid
2046167465Smpdounlimit(Char **v, struct command *c)
204759243Sobrien{
2048145479Smp    struct limits *lp;
204959243Sobrien    int    lerr = 0;
205059243Sobrien    int    hard = 0;
205159243Sobrien    int	   force = 0;
205259243Sobrien
205359243Sobrien    USE(c);
205459243Sobrien    while (*++v && **v == '-') {
205559243Sobrien	Char   *vp = *v;
205659243Sobrien	while (*++vp)
205759243Sobrien	    switch (*vp) {
205859243Sobrien	    case 'f':
205959243Sobrien		force = 1;
206059243Sobrien		break;
206159243Sobrien	    case 'h':
206259243Sobrien		hard = 1;
206359243Sobrien		break;
206459243Sobrien	    default:
206559243Sobrien		stderror(ERR_ULIMUS);
206659243Sobrien		break;
206759243Sobrien	    }
206859243Sobrien    }
206959243Sobrien
207059243Sobrien    if (*v == 0) {
207159243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
207259243Sobrien	    if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
207359243Sobrien		lerr++;
207459243Sobrien	if (!force && lerr)
207559243Sobrien	    stderror(ERR_SILENT);
207659243Sobrien	return;
207759243Sobrien    }
207859243Sobrien    while (*v) {
207959243Sobrien	lp = findlim(*v++);
208059243Sobrien	if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force)
208159243Sobrien	    stderror(ERR_SILENT);
208259243Sobrien    }
208359243Sobrien}
208459243Sobrien
208559243Sobrienstatic int
2086167465Smpsetlim(struct limits *lp, int hard, RLIM_TYPE limit)
208759243Sobrien{
208859243Sobrien# ifdef BSDLIMIT
208959243Sobrien    struct rlimit rlim;
209059243Sobrien
209159243Sobrien    (void) getrlimit(lp->limconst, &rlim);
209259243Sobrien
209359243Sobrien#  ifdef FILESIZE512
209459243Sobrien    /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
209559243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
209659243Sobrien	limit /= 512;
209759243Sobrien#  endif /* FILESIZE512 */
209859243Sobrien    if (hard)
209959243Sobrien	rlim.rlim_max = limit;
210059243Sobrien    else if (limit == RLIM_INFINITY && euid != 0)
210159243Sobrien	rlim.rlim_cur = rlim.rlim_max;
210259243Sobrien    else
210359243Sobrien	rlim.rlim_cur = limit;
210459243Sobrien
2105100616Smp    if (rlim.rlim_cur > rlim.rlim_max)
2106100616Smp	rlim.rlim_max = rlim.rlim_cur;
2107100616Smp
210859243Sobrien    if (setrlimit(lp->limconst, &rlim) < 0) {
210959243Sobrien# else /* BSDLIMIT */
211059243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
211159243Sobrien	limit /= 512;
211259243Sobrien# ifdef aiws
211359243Sobrien    if (lp->limconst == RLIMIT_DATA)
211459243Sobrien	limit += 0x20000000;
211559243Sobrien# endif /* aiws */
211659243Sobrien    if (ulimit(toset(lp->limconst), limit) < 0) {
211759243Sobrien# endif /* BSDLIMIT */
2118145479Smp        int err;
2119145479Smp        char *op, *type;
2120145479Smp
2121145479Smp	err = errno;
2122145479Smp	op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") :
2123145479Smp		     	CGETS(15, 3, "set"));
2124167465Smp	cleanup_push(op, xfree);
2125145479Smp	type = strsave(hard ? CGETS(15, 4, " hard") : "");
2126167465Smp	cleanup_push(type, xfree);
2127100616Smp	xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname,
2128145479Smp	    lp->limname, op, type, strerror(err));
2129167465Smp	cleanup_until(op);
213059243Sobrien	return (-1);
213159243Sobrien    }
213259243Sobrien    return (0);
213359243Sobrien}
213459243Sobrien
213559243Sobrien#endif /* !HAVENOLIMIT */
213659243Sobrien
213759243Sobrien/*ARGSUSED*/
213859243Sobrienvoid
2139167465Smpdosuspend(Char **v, struct command *c)
214059243Sobrien{
214159243Sobrien#ifdef BSDJOBS
214259243Sobrien    int     ctpgrp;
2143167465Smp    struct sigaction old;
214459243Sobrien#endif /* BSDJOBS */
214559243Sobrien
214659243Sobrien    USE(c);
214759243Sobrien    USE(v);
214859243Sobrien
214959243Sobrien    if (loginsh)
215059243Sobrien	stderror(ERR_SUSPLOG);
215159243Sobrien    untty();
215259243Sobrien
215359243Sobrien#ifdef BSDJOBS
2154167465Smp    sigaction(SIGTSTP, NULL, &old);
2155167465Smp    signal(SIGTSTP, SIG_DFL);
215659243Sobrien    (void) kill(0, SIGTSTP);
215759243Sobrien    /* the shell stops here */
2158167465Smp    sigaction(SIGTSTP, &old, NULL);
215959243Sobrien#else /* !BSDJOBS */
216059243Sobrien    stderror(ERR_JOBCONTROL);
216159243Sobrien#endif /* BSDJOBS */
216259243Sobrien
216359243Sobrien#ifdef BSDJOBS
216459243Sobrien    if (tpgrp != -1) {
216559243Sobrienretry:
216659243Sobrien	ctpgrp = tcgetpgrp(FSHTTY);
2167131962Smp	if (ctpgrp == -1)
2168131962Smp	    stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno));
216959243Sobrien	if (ctpgrp != opgrp) {
2170167465Smp	    sigaction(SIGTTIN, NULL, &old);
2171167465Smp	    signal(SIGTTIN, SIG_DFL);
217259243Sobrien	    (void) kill(0, SIGTTIN);
2173167465Smp	    sigaction(SIGTTIN, &old, NULL);
217459243Sobrien	    goto retry;
217559243Sobrien	}
217659243Sobrien	(void) setpgid(0, shpgrp);
217759243Sobrien	(void) tcsetpgrp(FSHTTY, shpgrp);
217859243Sobrien    }
217959243Sobrien#endif /* BSDJOBS */
218059243Sobrien    (void) setdisc(FSHTTY);
218159243Sobrien}
218259243Sobrien
218359243Sobrien/* This is the dreaded EVAL built-in.
218459243Sobrien *   If you don't fiddle with file descriptors, and reset didfds,
218559243Sobrien *   this command will either ignore redirection inside or outside
218659243Sobrien *   its arguments, e.g. eval "date >x"  vs.  eval "date" >x
218759243Sobrien *   The stuff here seems to work, but I did it by trial and error rather
218859243Sobrien *   than really knowing what was going on.  If tpgrp is zero, we are
218959243Sobrien *   probably a background eval, e.g. "eval date &", and we want to
219059243Sobrien *   make sure that any processes we start stay in our pgrp.
219159243Sobrien *   This is also the case for "time eval date" -- stay in same pgrp.
219259243Sobrien *   Otherwise, under stty tostop, processes will stop in the wrong
219359243Sobrien *   pgrp, with no way for the shell to get them going again.  -IAN!
219459243Sobrien */
219559243Sobrien
2196167465Smpstruct doeval_state
219759243Sobrien{
2198167465Smp    Char **evalvec, *evalp;
2199167465Smp    int didfds;
220059243Sobrien#ifndef CLOSE_ON_EXEC
2201167465Smp    int didcch;
2202167465Smp#endif
2203167465Smp    int saveIN, saveOUT, saveDIAG;
2204167465Smp    int SHIN, SHOUT, SHDIAG;
2205167465Smp};
220659243Sobrien
2207167465Smpstatic void
2208167465Smpdoeval_cleanup(void *xstate)
2209167465Smp{
2210167465Smp    struct doeval_state *state;
2211167465Smp
2212167465Smp    state = xstate;
2213167465Smp    evalvec = state->evalvec;
2214167465Smp    evalp = state->evalp;
2215167465Smp    doneinp = 0;
221659243Sobrien#ifndef CLOSE_ON_EXEC
2217167465Smp    didcch = state->didcch;
221859243Sobrien#endif /* CLOSE_ON_EXEC */
2219167465Smp    didfds = state->didfds;
2220167465Smp    xclose(SHIN);
2221167465Smp    xclose(SHOUT);
2222167465Smp    xclose(SHDIAG);
2223167465Smp    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
2224167465Smp    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
2225167465Smp    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
2226167465Smp}
222759243Sobrien
2228167465Smp/*ARGSUSED*/
2229167465Smpvoid
2230167465Smpdoeval(Char **v, struct command *c)
2231167465Smp{
2232167465Smp    struct doeval_state state;
2233167465Smp    int gflag;
2234167465Smp    Char **gv;
223559243Sobrien
2236167465Smp    USE(c);
2237167465Smp    v++;
2238167465Smp    if (*v == 0)
223959243Sobrien	return;
2240167465Smp    gflag = tglob(v);
224159243Sobrien    if (gflag) {
2242167465Smp	gv = v = globall(v, gflag);
2243167465Smp	if (v == 0)
224459243Sobrien	    stderror(ERR_NOMATCH);
2245167465Smp	cleanup_push(gv, blk_cleanup);
2246167465Smp	v = copyblk(v);
224759243Sobrien    }
224859243Sobrien    else {
224959243Sobrien	gv = NULL;
2250167465Smp	v = copyblk(v);
2251167465Smp	trim(v);
225259243Sobrien    }
225359243Sobrien
2254167465Smp    state.evalvec = evalvec;
2255167465Smp    state.evalp = evalp;
2256167465Smp    state.didfds = didfds;
225759243Sobrien#ifndef CLOSE_ON_EXEC
2258167465Smp    state.didcch = didcch;
225959243Sobrien#endif /* CLOSE_ON_EXEC */
2260167465Smp    state.SHIN = SHIN;
2261167465Smp    state.SHOUT = SHOUT;
2262167465Smp    state.SHDIAG = SHDIAG;
226359243Sobrien
2264167465Smp    (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1);
2265167465Smp    (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1);
2266167465Smp    (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1);
2267167465Smp
2268167465Smp    cleanup_push(&state, doeval_cleanup);
2269167465Smp
2270167465Smp    evalvec = v;
2271167465Smp    evalp = 0;
2272167465Smp    (void)close_on_exec(SHIN = dcopy(0, -1), 1);
2273167465Smp    (void)close_on_exec(SHOUT = dcopy(1, -1), 1);
2274167465Smp    (void)close_on_exec(SHDIAG = dcopy(2, -1), 1);
227559243Sobrien#ifndef CLOSE_ON_EXEC
2276167465Smp    didcch = 0;
227759243Sobrien#endif /* CLOSE_ON_EXEC */
2278167465Smp    didfds = 0;
2279167465Smp    process(0);
228059243Sobrien
2281167465Smp    cleanup_until(&state);
2282167465Smp
228359243Sobrien    if (gv)
2284167465Smp	cleanup_until(gv);
228559243Sobrien}
228659243Sobrien
228759243Sobrien/*************************************************************************/
228859243Sobrien/* print list of builtin commands */
228959243Sobrien
2290167465Smpstatic void
2291167465Smplbuffed_cleanup (void *dummy)
2292167465Smp{
2293167465Smp    USE(dummy);
2294167465Smp    lbuffed = 1;
2295167465Smp}
2296167465Smp
229759243Sobrien/*ARGSUSED*/
229859243Sobrienvoid
2299167465Smpdobuiltins(Char **v, struct command *c)
230059243Sobrien{
230159243Sobrien    /* would use print_by_column() in tw.parse.c but that assumes
230259243Sobrien     * we have an array of Char * to pass.. (sg)
230359243Sobrien     */
2304167465Smp    const struct biltins *b;
2305145479Smp    int row, col, columns, rows;
230659243Sobrien    unsigned int w, maxwidth;
230759243Sobrien
230859243Sobrien    USE(c);
230959243Sobrien    USE(v);
231059243Sobrien    lbuffed = 0;		/* turn off line buffering */
2311167465Smp    cleanup_push(&lbuffed, lbuffed_cleanup);
231259243Sobrien
231359243Sobrien    /* find widest string */
231459243Sobrien    for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
231559243Sobrien	maxwidth = max(maxwidth, strlen(b->bname));
231659243Sobrien    ++maxwidth;					/* for space */
231759243Sobrien
231859243Sobrien    columns = (TermH + 1) / maxwidth;	/* PWP: terminal size change */
231959243Sobrien    if (!columns)
232059243Sobrien	columns = 1;
232159243Sobrien    rows = (nbfunc + (columns - 1)) / columns;
232259243Sobrien
232359243Sobrien    for (b = bfunc, row = 0; row < rows; row++) {
232459243Sobrien	for (col = 0; col < columns; col++) {
232559243Sobrien	    if (b < &bfunc[nbfunc]) {
232659243Sobrien		w = strlen(b->bname);
232759243Sobrien		xprintf("%s", b->bname);
232859243Sobrien		if (col < (columns - 1))	/* Not last column? */
232959243Sobrien		    for (; w < maxwidth; w++)
233059243Sobrien			xputchar(' ');
233159243Sobrien		++b;
233259243Sobrien	    }
233359243Sobrien	}
233459243Sobrien	if (row < (rows - 1)) {
233559243Sobrien	    if (Tty_raw_mode)
233659243Sobrien		xputchar('\r');
233759243Sobrien	    xputchar('\n');
233859243Sobrien	}
233959243Sobrien    }
234069408Sache#ifdef WINNT_NATIVE
234159243Sobrien    nt_print_builtins(maxwidth);
234259243Sobrien#else
234359243Sobrien    if (Tty_raw_mode)
234459243Sobrien	xputchar('\r');
234559243Sobrien    xputchar('\n');
234669408Sache#endif /* WINNT_NATIVE */
234759243Sobrien
2348167465Smp    cleanup_until(&lbuffed);		/* turn back on line buffering */
234959243Sobrien    flush();
235059243Sobrien}
235159243Sobrien
2352145479Smp#ifdef NLS_CATALOGS
2353145479Smpchar *
2354167465Smpxcatgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2355145479Smp{
2356167465Smp    char *res;
2357167465Smp
2358167465Smp    errno = 0;
2359167465Smp    while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) {
2360167465Smp	handle_pending_signals();
2361167465Smp	errno = 0;
2362167465Smp    }
2363167465Smp    return res;
2364167465Smp}
2365167465Smp
2366167465Smp
2367167465Smp# if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2368167465Smpchar *
2369167465Smpiconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2370167465Smp{
2371145479Smp    static char *buf = NULL;
2372145479Smp    static size_t buf_size = 0;
2373145479Smp
2374145479Smp    char *orig, *dest, *p;
2375167465Smp    ICONV_CONST char *src;
2376145479Smp    size_t src_size, dest_size;
2377145479Smp
2378167465Smp    orig = xcatgets(ctd, set_id, msg_id, s);
2379145479Smp    if (catgets_iconv == (iconv_t)-1 || orig == s)
2380145479Smp        return orig;
2381145479Smp    src = orig;
2382145479Smp    src_size = strlen(src) + 1;
2383145479Smp    if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL)
2384145479Smp	return orig;
2385145479Smp    dest = buf;
2386145479Smp    while (src_size != 0) {
2387145479Smp        dest_size = buf + buf_size - dest;
2388145479Smp	if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size)
2389145479Smp	    == (size_t)-1) {
2390145479Smp	    switch (errno) {
2391145479Smp	    case E2BIG:
2392145479Smp		if ((p = xrealloc(buf, buf_size * 2)) == NULL)
2393145479Smp		    return orig;
2394145479Smp		buf_size *= 2;
2395145479Smp		dest = p + (dest - buf);
2396145479Smp		buf = p;
2397145479Smp		break;
2398145479Smp
2399145479Smp	    case EILSEQ: case EINVAL: default:
2400145479Smp		return orig;
2401145479Smp	    }
2402145479Smp	}
2403145479Smp    }
2404145479Smp    return buf;
2405145479Smp}
2406167465Smp# endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2407167465Smp#endif /* NLS_CATALOGS */
2408145479Smp
240959243Sobrienvoid
2410167465Smpnlsinit(void)
241159243Sobrien{
241259243Sobrien#ifdef NLS_CATALOGS
2413167465Smp    static const char default_catalog[] = "tcsh";
241469408Sache
2415167465Smp    char *catalog = (char *)(intptr_t)default_catalog;
2416167465Smp
241769408Sache    if (adrof(STRcatalog) != NULL)
2418167465Smp	catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog)));
241969408Sache    catd = catopen(catalog, MCLoadBySet);
2420167465Smp    if (catalog != default_catalog)
2421167465Smp	xfree(catalog);
2422167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2423167465Smp    /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */
2424145479Smp    catgets_iconv = iconv_open (nl_langinfo (CODESET),
2425167465Smp				xcatgets(catd, 255, 1, "ASCII"));
2426167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
242769408Sache#endif /* NLS_CATALOGS */
242869408Sache#ifdef WINNT_NATIVE
242959243Sobrien    nls_dll_init();
243069408Sache#endif /* WINNT_NATIVE */
243159243Sobrien    errinit();		/* init the errorlist in correct locale */
243259243Sobrien    mesginit();		/* init the messages for signals */
243359243Sobrien    dateinit();		/* init the messages for dates */
243459243Sobrien    editinit();		/* init the editor messages */
243559243Sobrien    terminit();		/* init the termcap messages */
243659243Sobrien}
2437145479Smp
2438145479Smpvoid
2439167465Smpnlsclose(void)
2440145479Smp{
2441145479Smp#ifdef NLS_CATALOGS
2442167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2443145479Smp    if (catgets_iconv != (iconv_t)-1) {
2444145479Smp	iconv_close(catgets_iconv);
2445145479Smp	catgets_iconv = (iconv_t)-1;
2446145479Smp    }
2447167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2448167465Smp    if (catd != (nl_catd)-1) {
2449167465Smp	/*
2450167465Smp	 * catclose can call other functions which can call longjmp
2451167465Smp	 * making us re-enter this code. Prevent infinite recursion
2452167465Smp	 * by resetting catd. Problem reported and solved by:
2453167465Smp	 * Gerhard Niklasch
2454167465Smp	 */
2455167465Smp	nl_catd oldcatd = catd;
2456167465Smp	catd = (nl_catd)-1;
2457167465Smp	while (catclose(oldcatd) == -1 && errno == EINTR)
2458167465Smp	    handle_pending_signals();
2459167465Smp    }
2460145479Smp#endif /* NLS_CATALOGS */
2461145479Smp}
2462