159243Sobrien/*
259243Sobrien * sh.func.c: csh builtin functions
359243Sobrien */
459243Sobrien/*-
559243Sobrien * Copyright (c) 1980, 1991 The Regents of the University of California.
659243Sobrien * All rights reserved.
759243Sobrien *
859243Sobrien * Redistribution and use in source and binary forms, with or without
959243Sobrien * modification, are permitted provided that the following conditions
1059243Sobrien * are met:
1159243Sobrien * 1. Redistributions of source code must retain the above copyright
1259243Sobrien *    notice, this list of conditions and the following disclaimer.
1359243Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1459243Sobrien *    notice, this list of conditions and the following disclaimer in the
1559243Sobrien *    documentation and/or other materials provided with the distribution.
16100616Smp * 3. Neither the name of the University nor the names of its contributors
1759243Sobrien *    may be used to endorse or promote products derived from this software
1859243Sobrien *    without specific prior written permission.
1959243Sobrien *
2059243Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2159243Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2259243Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2359243Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2459243Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2559243Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2659243Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2759243Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2859243Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2959243Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3059243Sobrien * SUCH DAMAGE.
3159243Sobrien */
3259243Sobrien#include "sh.h"
3359243Sobrien#include "ed.h"
3459243Sobrien#include "tw.h"
3559243Sobrien#include "tc.h"
3669408Sache#ifdef WINNT_NATIVE
3759243Sobrien#include "nt.const.h"
3869408Sache#endif /* WINNT_NATIVE */
3959243Sobrien
40231990Smp#if defined (NLS_CATALOGS) && defined(HAVE_ICONV)
41145479Smpstatic iconv_t catgets_iconv; /* Or (iconv_t)-1 */
42145479Smp#endif
43145479Smp
4459243Sobrien/*
4559243Sobrien * C shell
4659243Sobrien */
4759243Sobrien
48145479Smpextern int MapsAreInited;
49145479Smpextern int NLSMapsAreInited;
50145479Smpextern int GotTermCaps;
5159243Sobrien
5259243Sobrienstatic int zlast = -1;
5359243Sobrien
54167465Smpstatic	void	islogin		(void);
55167465Smpstatic	void	preread		(void);
56167465Smpstatic	void	doagain		(void);
57167465Smpstatic  const char *isrchx	(int);
58167465Smpstatic	void	search		(int, int, Char *);
59167465Smpstatic	int	getword		(struct Strbuf *);
60195609Smpstatic	struct wordent	*histgetword	(struct wordent *);
61167465Smpstatic	void	toend		(void);
62167465Smpstatic	void	xecho		(int, Char **);
63167465Smpstatic	int	islocale_var	(Char *);
64167465Smpstatic	void	wpfree		(struct whyle *);
6559243Sobrien
66167465Smpconst struct biltins *
67167465Smpisbfunc(struct command *t)
6859243Sobrien{
69145479Smp    Char *cp = t->t_dcom[0];
70167465Smp    const struct biltins *bp, *bp1, *bp2;
7159243Sobrien    static struct biltins label = {"", dozip, 0, 0};
7259243Sobrien    static struct biltins foregnd = {"%job", dofg1, 0, 0};
7359243Sobrien    static struct biltins backgnd = {"%job &", dobg1, 0, 0};
7459243Sobrien
7559243Sobrien    /*
7659243Sobrien     * We never match a builtin that has quoted the first
7759243Sobrien     * character; this has been the traditional way to escape
7859243Sobrien     * builtin commands.
7959243Sobrien     */
8059243Sobrien    if (*cp & QUOTE)
8159243Sobrien	return NULL;
8259243Sobrien
8359243Sobrien    if (*cp != ':' && lastchr(cp) == ':') {
8459243Sobrien	label.bname = short2str(cp);
8559243Sobrien	return (&label);
8659243Sobrien    }
8759243Sobrien    if (*cp == '%') {
8859243Sobrien	if (t->t_dflg & F_AMPERSAND) {
8959243Sobrien	    t->t_dflg &= ~F_AMPERSAND;
9059243Sobrien	    backgnd.bname = short2str(cp);
9159243Sobrien	    return (&backgnd);
9259243Sobrien	}
9359243Sobrien	foregnd.bname = short2str(cp);
9459243Sobrien	return (&foregnd);
9559243Sobrien    }
9659243Sobrien#ifdef WARP
9759243Sobrien    /*
9859243Sobrien     * This is a perhaps kludgy way to determine if the warp builtin is to be
9959243Sobrien     * acknowledged or not.  If checkwarp() fails, then we are to assume that
10059243Sobrien     * the warp command is invalid, and carry on as we would handle any other
10159243Sobrien     * non-builtin command.         -- JDK 2/4/88
10259243Sobrien     */
10359243Sobrien    if (eq(STRwarp, cp) && !checkwarp()) {
10459243Sobrien	return (0);		/* this builtin disabled */
10559243Sobrien    }
10659243Sobrien#endif /* WARP */
10759243Sobrien    /*
10859243Sobrien     * Binary search Bp1 is the beginning of the current search range. Bp2 is
10959243Sobrien     * one past the end.
11059243Sobrien     */
11159243Sobrien    for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2;) {
11259243Sobrien	int i;
11359243Sobrien
11459243Sobrien	bp = bp1 + ((bp2 - bp1) >> 1);
11559243Sobrien	if ((i = ((char) *cp) - *bp->bname) == 0 &&
11659243Sobrien	    (i = StrQcmp(cp, str2short(bp->bname))) == 0)
11759243Sobrien	    return bp;
11859243Sobrien	if (i < 0)
11959243Sobrien	    bp2 = bp;
12059243Sobrien	else
12159243Sobrien	    bp1 = bp + 1;
12259243Sobrien    }
12369408Sache#ifdef WINNT_NATIVE
12459243Sobrien    return nt_check_additional_builtins(cp);
12569408Sache#endif /*WINNT_NATIVE*/
12659243Sobrien    return (0);
12759243Sobrien}
12859243Sobrien
12959243Sobrienvoid
130167465Smpfunc(struct command *t, const struct biltins *bp)
13159243Sobrien{
13259243Sobrien    int     i;
13359243Sobrien
13459243Sobrien    xechoit(t->t_dcom);
13559243Sobrien    setname(bp->bname);
13659243Sobrien    i = blklen(t->t_dcom) - 1;
13759243Sobrien    if (i < bp->minargs)
13859243Sobrien	stderror(ERR_NAME | ERR_TOOFEW);
13959243Sobrien    if (i > bp->maxargs)
14059243Sobrien	stderror(ERR_NAME | ERR_TOOMANY);
14159243Sobrien    (*bp->bfunct) (t->t_dcom, t);
14259243Sobrien}
14359243Sobrien
14459243Sobrien/*ARGSUSED*/
14559243Sobrienvoid
146167465Smpdoonintr(Char **v, struct command *c)
14759243Sobrien{
148145479Smp    Char *cp;
149145479Smp    Char *vv = v[1];
15059243Sobrien
15159243Sobrien    USE(c);
152167465Smp    if (parintr.sa_handler == SIG_IGN)
15359243Sobrien	return;
15459243Sobrien    if (setintr && intty)
15559243Sobrien	stderror(ERR_NAME | ERR_TERMINAL);
15659243Sobrien    cp = gointr;
15759243Sobrien    gointr = 0;
158167465Smp    xfree(cp);
15959243Sobrien    if (vv == 0) {
160167465Smp	if (setintr)
161167465Smp	    sigset_interrupting(SIGINT, queue_pintr);
162167465Smp	else
16359243Sobrien	    (void) signal(SIGINT, SIG_DFL);
16459243Sobrien	gointr = 0;
16559243Sobrien    }
16659243Sobrien    else if (eq((vv = strip(vv)), STRminus)) {
16759243Sobrien	(void) signal(SIGINT, SIG_IGN);
16859243Sobrien	gointr = Strsave(STRminus);
16959243Sobrien    }
17059243Sobrien    else {
17159243Sobrien	gointr = Strsave(vv);
172167465Smp	sigset_interrupting(SIGINT, queue_pintr);
17359243Sobrien    }
17459243Sobrien}
17559243Sobrien
17659243Sobrien/*ARGSUSED*/
17759243Sobrienvoid
178167465Smpdonohup(Char **v, struct command *c)
17959243Sobrien{
18059243Sobrien    USE(c);
18159243Sobrien    USE(v);
18259243Sobrien    if (intty)
18359243Sobrien	stderror(ERR_NAME | ERR_TERMINAL);
18459243Sobrien    if (setintr == 0) {
18559243Sobrien	(void) signal(SIGHUP, SIG_IGN);
186167465Smp	phup_disabled = 1;
18759243Sobrien#ifdef CC
18859243Sobrien	submit(getpid());
18959243Sobrien#endif /* CC */
19059243Sobrien    }
19159243Sobrien}
19259243Sobrien
19359243Sobrien/*ARGSUSED*/
19459243Sobrienvoid
195167465Smpdohup(Char **v, struct command *c)
19659243Sobrien{
19759243Sobrien    USE(c);
19859243Sobrien    USE(v);
19959243Sobrien    if (intty)
20059243Sobrien	stderror(ERR_NAME | ERR_TERMINAL);
20159243Sobrien    if (setintr == 0)
202354195Sbrooks    	sigset_interrupting(SIGHUP, SIG_DFL);
20359243Sobrien}
20459243Sobrien
20559243Sobrien
20659243Sobrien/*ARGSUSED*/
20759243Sobrienvoid
208167465Smpdozip(Char **v, struct command *c)
20959243Sobrien{
21059243Sobrien    USE(c);
21159243Sobrien    USE(v);
21259243Sobrien}
21359243Sobrien
21459243Sobrien/*ARGSUSED*/
21559243Sobrienvoid
216167465Smpdofiletest(Char **v, struct command *c)
21759243Sobrien{
218167465Smp    Char **globbed, **fileptr, *ftest, *res;
21959243Sobrien
220145479Smp    USE(c);
22159243Sobrien    if (*(ftest = *++v) != '-')
22259243Sobrien	stderror(ERR_NAME | ERR_FILEINQ);
22359243Sobrien    ++v;
22459243Sobrien
225167465Smp    v = glob_all_or_error(v);
226167465Smp    globbed = v;
227167465Smp    cleanup_push(globbed, blk_cleanup);
22859243Sobrien
229354195Sbrooks    while (*(fileptr = v++) != NULL) {
230167465Smp	res = filetest(ftest, &fileptr, 0);
231167465Smp	cleanup_push(res, xfree);
232167465Smp	xprintf("%S", res);
233167465Smp	cleanup_until(res);
23459243Sobrien	if (*v)
23559243Sobrien	    xprintf(" ");
23659243Sobrien    }
23759243Sobrien    xprintf("\n");
23859243Sobrien
239167465Smp    cleanup_until(globbed);
24059243Sobrien}
24159243Sobrien
24259243Sobrienvoid
243167465Smpprvars(void)
24459243Sobrien{
24559243Sobrien    plist(&shvhed, VAR_ALL);
24659243Sobrien}
24759243Sobrien
24859243Sobrien/*ARGSUSED*/
24959243Sobrienvoid
250167465Smpdoalias(Char **v, struct command *c)
25159243Sobrien{
252145479Smp    struct varent *vp;
253145479Smp    Char *p;
25459243Sobrien
25559243Sobrien    USE(c);
25659243Sobrien    v++;
25759243Sobrien    p = *v++;
25859243Sobrien    if (p == 0)
25959243Sobrien	plist(&aliases, VAR_ALL);
26059243Sobrien    else if (*v == 0) {
26159243Sobrien	vp = adrof1(strip(p), &aliases);
262100616Smp	if (vp && vp->vec)
26359243Sobrien	    blkpr(vp->vec), xputchar('\n');
26459243Sobrien    }
26559243Sobrien    else {
26659243Sobrien	if (eq(p, STRalias) || eq(p, STRunalias)) {
26759243Sobrien	    setname(short2str(p));
26859243Sobrien	    stderror(ERR_NAME | ERR_DANGER);
26959243Sobrien	}
27059243Sobrien	set1(strip(p), saveblk(v), &aliases, VAR_READWRITE);
27159243Sobrien	tw_cmd_free();
27259243Sobrien    }
27359243Sobrien}
27459243Sobrien
27559243Sobrien/*ARGSUSED*/
27659243Sobrienvoid
277167465Smpunalias(Char **v, struct command *c)
27859243Sobrien{
27959243Sobrien    USE(c);
28059243Sobrien    unset1(v, &aliases);
28159243Sobrien    tw_cmd_free();
28259243Sobrien}
28359243Sobrien
28459243Sobrien/*ARGSUSED*/
28559243Sobrienvoid
286167465Smpdologout(Char **v, struct command *c)
28759243Sobrien{
28859243Sobrien    USE(c);
28959243Sobrien    USE(v);
29059243Sobrien    islogin();
29159243Sobrien    goodbye(NULL, NULL);
29259243Sobrien}
29359243Sobrien
29459243Sobrien/*ARGSUSED*/
29559243Sobrienvoid
296167465Smpdologin(Char **v, struct command *c)
29759243Sobrien{
298131962Smp#ifdef WINNT_NATIVE
29959243Sobrien    USE(c);
30059243Sobrien    USE(v);
30169408Sache#else /* !WINNT_NATIVE */
302131962Smp    char **p = short2blk(v);
303167465Smp
304131962Smp    USE(c);
305167465Smp    cleanup_push((Char **)p, blk_cleanup);
30659243Sobrien    islogin();
30759243Sobrien    rechist(NULL, adrof(STRsavehist) != NULL);
308167465Smp    sigaction(SIGTERM, &parterm, NULL);
309131962Smp    (void) execv(_PATH_BIN_LOGIN, p);
310131962Smp    (void) execv(_PATH_USRBIN_LOGIN, p);
311167465Smp    cleanup_until((Char **)p);
31259243Sobrien    untty();
31359243Sobrien    xexit(1);
31469408Sache#endif /* !WINNT_NATIVE */
31559243Sobrien}
31659243Sobrien
31759243Sobrien
31859243Sobrien#ifdef NEWGRP
31959243Sobrien/*ARGSUSED*/
32059243Sobrienvoid
321167465Smpdonewgrp(Char **v, struct command *c)
32259243Sobrien{
32359243Sobrien    char **p;
32459243Sobrien    if (chkstop == 0 && setintr)
32559243Sobrien	panystop(0);
326167465Smp    sigaction(SIGTERM, &parterm, NULL);
32759243Sobrien    p = short2blk(v);
32859243Sobrien    /*
32959243Sobrien     * From Beto Appleton (beto@aixwiz.austin.ibm.com)
33059243Sobrien     * Newgrp can take 2 arguments...
33159243Sobrien     */
33259243Sobrien    (void) execv(_PATH_BIN_NEWGRP, p);
33359243Sobrien    (void) execv(_PATH_USRBIN_NEWGRP, p);
33459243Sobrien    blkfree((Char **) p);
33559243Sobrien    untty();
33659243Sobrien    xexit(1);
33759243Sobrien}
33859243Sobrien#endif /* NEWGRP */
33959243Sobrien
34059243Sobrienstatic void
341167465Smpislogin(void)
34259243Sobrien{
34359243Sobrien    if (chkstop == 0 && setintr)
34459243Sobrien	panystop(0);
34559243Sobrien    if (loginsh)
34659243Sobrien	return;
34759243Sobrien    stderror(ERR_NOTLOGIN);
34859243Sobrien}
34959243Sobrien
35059243Sobrienvoid
351167465Smpdoif(Char **v, struct command *kp)
35259243Sobrien{
353145479Smp    int i;
354145479Smp    Char **vv;
35559243Sobrien
35659243Sobrien    v++;
357167465Smp    i = noexec ? 1 : expr(&v);
35859243Sobrien    vv = v;
35959243Sobrien    if (*vv == NULL)
36059243Sobrien	stderror(ERR_NAME | ERR_EMPTYIF);
36159243Sobrien    if (eq(*vv, STRthen)) {
36259243Sobrien	if (*++vv)
36359243Sobrien	    stderror(ERR_NAME | ERR_IMPRTHEN);
36459243Sobrien	setname(short2str(STRthen));
36559243Sobrien	/*
36659243Sobrien	 * If expression was zero, then scan to else , otherwise just fall into
36759243Sobrien	 * following code.
36859243Sobrien	 */
36959243Sobrien	if (!i)
37059243Sobrien	    search(TC_IF, 0, NULL);
37159243Sobrien	return;
37259243Sobrien    }
37359243Sobrien    /*
37459243Sobrien     * Simple command attached to this if. Left shift the node in this tree,
37559243Sobrien     * munging it so we can reexecute it.
37659243Sobrien     */
37759243Sobrien    if (i) {
37859243Sobrien	lshift(kp->t_dcom, vv - kp->t_dcom);
37959243Sobrien	reexecute(kp);
38059243Sobrien	donefds();
38159243Sobrien    }
38259243Sobrien}
38359243Sobrien
38459243Sobrien/*
38559243Sobrien * Reexecute a command, being careful not
38659243Sobrien * to redo i/o redirection, which is already set up.
38759243Sobrien */
38859243Sobrienvoid
389167465Smpreexecute(struct command *kp)
39059243Sobrien{
39159243Sobrien    kp->t_dflg &= F_SAVE;
39259243Sobrien    kp->t_dflg |= F_REPEAT;
39359243Sobrien    /*
39459243Sobrien     * If tty is still ours to arbitrate, arbitrate it; otherwise dont even set
39559243Sobrien     * pgrp's as the jobs would then have no way to get the tty (we can't give
39659243Sobrien     * it to them, and our parent wouldn't know their pgrp, etc.
39759243Sobrien     */
398100616Smp    execute(kp, (tpgrp > 0 ? tpgrp : -1), NULL, NULL, TRUE);
39959243Sobrien}
40059243Sobrien
40159243Sobrien/*ARGSUSED*/
40259243Sobrienvoid
403167465Smpdoelse (Char **v, struct command *c)
40459243Sobrien{
40559243Sobrien    USE(c);
40659243Sobrien    USE(v);
407167465Smp    if (!noexec)
408167465Smp	search(TC_ELSE, 0, NULL);
40959243Sobrien}
41059243Sobrien
41159243Sobrien/*ARGSUSED*/
41259243Sobrienvoid
413167465Smpdogoto(Char **v, struct command *c)
41459243Sobrien{
41559243Sobrien    Char   *lp;
41659243Sobrien
41759243Sobrien    USE(c);
418167465Smp    lp = globone(v[1], G_ERROR);
419167465Smp    cleanup_push(lp, xfree);
420167465Smp    if (!noexec)
421167465Smp	gotolab(lp);
422167465Smp    cleanup_until(lp);
42359243Sobrien}
42459243Sobrien
42559243Sobrienvoid
426167465Smpgotolab(Char *lab)
42759243Sobrien{
428145479Smp    struct whyle *wp;
42959243Sobrien    /*
43059243Sobrien     * While we still can, locate any unknown ends of existing loops. This
43159243Sobrien     * obscure code is the WORST result of the fact that we don't really parse.
43259243Sobrien     */
43359243Sobrien    zlast = TC_GOTO;
43459243Sobrien    for (wp = whyles; wp; wp = wp->w_next)
43569408Sache	if (wp->w_end.type == TCSH_F_SEEK && wp->w_end.f_seek == 0) {
43659243Sobrien	    search(TC_BREAK, 0, NULL);
43759243Sobrien	    btell(&wp->w_end);
43859243Sobrien	}
43959243Sobrien	else {
44059243Sobrien	    bseek(&wp->w_end);
44159243Sobrien	}
44259243Sobrien    search(TC_GOTO, 0, lab);
44359243Sobrien    /*
44459243Sobrien     * Eliminate loops which were exited.
44559243Sobrien     */
44659243Sobrien    wfree();
44759243Sobrien}
44859243Sobrien
44959243Sobrien/*ARGSUSED*/
45059243Sobrienvoid
451167465Smpdoswitch(Char **v, struct command *c)
45259243Sobrien{
453145479Smp    Char *cp, *lp;
45459243Sobrien
45559243Sobrien    USE(c);
45659243Sobrien    v++;
45759243Sobrien    if (!*v || *(*v++) != '(')
45859243Sobrien	stderror(ERR_SYNTAX);
45959243Sobrien    cp = **v == ')' ? STRNULL : *v++;
46059243Sobrien    if (*(*v++) != ')')
46159243Sobrien	v--;
46259243Sobrien    if (*v)
46359243Sobrien	stderror(ERR_SYNTAX);
464167465Smp    lp = globone(cp, G_ERROR);
465167465Smp    cleanup_push(lp, xfree);
466167465Smp    if (!noexec)
467167465Smp	search(TC_SWITCH, 0, lp);
468167465Smp    cleanup_until(lp);
46959243Sobrien}
47059243Sobrien
47159243Sobrien/*ARGSUSED*/
47259243Sobrienvoid
473167465Smpdobreak(Char **v, struct command *c)
47459243Sobrien{
47559243Sobrien    USE(v);
47659243Sobrien    USE(c);
477167465Smp    if (whyles == NULL)
478167465Smp	stderror(ERR_NAME | ERR_NOTWHILE);
479167465Smp    if (!noexec)
48059243Sobrien	toend();
48159243Sobrien}
48259243Sobrien
48359243Sobrien/*ARGSUSED*/
48459243Sobrienvoid
485167465Smpdoexit(Char **v, struct command *c)
48659243Sobrien{
48759243Sobrien    USE(c);
48859243Sobrien
48959243Sobrien    if (chkstop == 0 && (intty || intact) && evalvec == 0)
49059243Sobrien	panystop(0);
49159243Sobrien    /*
49259243Sobrien     * Don't DEMAND parentheses here either.
49359243Sobrien     */
49459243Sobrien    v++;
49559243Sobrien    if (*v) {
496167465Smp	setv(STRstatus, putn(expr(&v)), VAR_READWRITE);
49759243Sobrien	if (*v)
49859243Sobrien	    stderror(ERR_NAME | ERR_EXPRESSION);
49959243Sobrien    }
50059243Sobrien    btoeof();
50159243Sobrien#if 0
50259243Sobrien    if (intty)
50359243Sobrien#endif
50459243Sobrien    /* Always close, why only on ttys? */
505167465Smp	xclose(SHIN);
50659243Sobrien}
50759243Sobrien
50859243Sobrien/*ARGSUSED*/
50959243Sobrienvoid
510167465Smpdoforeach(Char **v, struct command *c)
51159243Sobrien{
512145479Smp    Char *cp, *sp;
513145479Smp    struct whyle *nwp;
514167465Smp    int gflag;
51559243Sobrien
51659243Sobrien    USE(c);
51759243Sobrien    v++;
518231990Smp    cp = sp = strip(*v);
519231990Smp    if (!letter(*cp))
52059243Sobrien	stderror(ERR_NAME | ERR_VARBEGIN);
521231990Smp    do {
52259243Sobrien	cp++;
523231990Smp    } while (alnum(*cp));
524231990Smp    if (*cp != '\0')
52559243Sobrien	stderror(ERR_NAME | ERR_VARALNUM);
52659243Sobrien    cp = *v++;
52759243Sobrien    if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')')
52859243Sobrien	stderror(ERR_NAME | ERR_NOPAREN);
52959243Sobrien    v++;
530167465Smp    gflag = tglob(v);
53159243Sobrien    if (gflag) {
532167465Smp	v = globall(v, gflag);
533167465Smp	if (v == 0 && !noexec)
53459243Sobrien	    stderror(ERR_NAME | ERR_NOMATCH);
53559243Sobrien    }
53659243Sobrien    else {
537167465Smp	v = saveblk(v);
53859243Sobrien	trim(v);
53959243Sobrien    }
540167465Smp    nwp = xcalloc(1, sizeof *nwp);
54159243Sobrien    nwp->w_fe = nwp->w_fe0 = v;
54259243Sobrien    btell(&nwp->w_start);
54359243Sobrien    nwp->w_fename = Strsave(cp);
54459243Sobrien    nwp->w_next = whyles;
54569408Sache    nwp->w_end.type = TCSH_F_SEEK;
54659243Sobrien    whyles = nwp;
54759243Sobrien    /*
54859243Sobrien     * Pre-read the loop so as to be more comprehensible to a terminal user.
54959243Sobrien     */
55059243Sobrien    zlast = TC_FOREACH;
55159243Sobrien    if (intty)
55259243Sobrien	preread();
553167465Smp    if (!noexec)
554167465Smp	doagain();
55559243Sobrien}
55659243Sobrien
55759243Sobrien/*ARGSUSED*/
55859243Sobrienvoid
559167465Smpdowhile(Char **v, struct command *c)
56059243Sobrien{
561145479Smp    int status;
562145479Smp    int again = whyles != 0 &&
56359243Sobrien			  SEEKEQ(&whyles->w_start, &lineloc) &&
56459243Sobrien			  whyles->w_fename == 0;
56559243Sobrien
56659243Sobrien    USE(c);
56759243Sobrien    v++;
56859243Sobrien    /*
56959243Sobrien     * Implement prereading here also, taking care not to evaluate the
57059243Sobrien     * expression before the loop has been read up from a terminal.
57159243Sobrien     */
572167465Smp    if (noexec)
573167465Smp	status = 0;
574167465Smp    else if (intty && !again)
57559243Sobrien	status = !exp0(&v, 1);
57659243Sobrien    else
57759243Sobrien	status = !expr(&v);
578167465Smp    if (*v && !noexec)
57959243Sobrien	stderror(ERR_NAME | ERR_EXPRESSION);
58059243Sobrien    if (!again) {
581167465Smp	struct whyle *nwp = xcalloc(1, sizeof(*nwp));
58259243Sobrien
58359243Sobrien	nwp->w_start = lineloc;
58469408Sache	nwp->w_end.type = TCSH_F_SEEK;
58559243Sobrien	nwp->w_end.f_seek = 0;
586231990Smp	nwp->w_end.a_seek = 0;
58759243Sobrien	nwp->w_next = whyles;
58859243Sobrien	whyles = nwp;
58959243Sobrien	zlast = TC_WHILE;
59059243Sobrien	if (intty) {
59159243Sobrien	    /*
59259243Sobrien	     * The tty preread
59359243Sobrien	     */
59459243Sobrien	    preread();
59559243Sobrien	    doagain();
59659243Sobrien	    return;
59759243Sobrien	}
59859243Sobrien    }
59959243Sobrien    if (status)
60059243Sobrien	/* We ain't gonna loop no more, no more! */
60159243Sobrien	toend();
60259243Sobrien}
60359243Sobrien
60459243Sobrienstatic void
605167465Smppreread(void)
60659243Sobrien{
607167465Smp    int old_pintr_disabled;
608167465Smp
60969408Sache    whyles->w_end.type = TCSH_I_SEEK;
61059243Sobrien    if (setintr)
611167465Smp	pintr_push_enable(&old_pintr_disabled);
61259243Sobrien    search(TC_BREAK, 0, NULL);		/* read the expression in */
61359243Sobrien    if (setintr)
614167465Smp	cleanup_until(&old_pintr_disabled);
61559243Sobrien    btell(&whyles->w_end);
61659243Sobrien}
61759243Sobrien
61859243Sobrien/*ARGSUSED*/
61959243Sobrienvoid
620167465Smpdoend(Char **v, struct command *c)
62159243Sobrien{
62259243Sobrien    USE(v);
62359243Sobrien    USE(c);
62459243Sobrien    if (!whyles)
62559243Sobrien	stderror(ERR_NAME | ERR_NOTWHILE);
62659243Sobrien    btell(&whyles->w_end);
627167465Smp    if (!noexec)
628167465Smp	doagain();
62959243Sobrien}
63059243Sobrien
63159243Sobrien/*ARGSUSED*/
63259243Sobrienvoid
633167465Smpdocontin(Char **v, struct command *c)
63459243Sobrien{
63559243Sobrien    USE(v);
63659243Sobrien    USE(c);
63759243Sobrien    if (!whyles)
63859243Sobrien	stderror(ERR_NAME | ERR_NOTWHILE);
639167465Smp    if (!noexec)
640167465Smp	doagain();
64159243Sobrien}
64259243Sobrien
64359243Sobrienstatic void
644167465Smpdoagain(void)
64559243Sobrien{
64659243Sobrien    /* Repeating a while is simple */
64759243Sobrien    if (whyles->w_fename == 0) {
64859243Sobrien	bseek(&whyles->w_start);
64959243Sobrien	return;
65059243Sobrien    }
65159243Sobrien    /*
65259243Sobrien     * The foreach variable list actually has a spurious word ")" at the end of
65359243Sobrien     * the w_fe list.  Thus we are at the of the list if one word beyond this
65459243Sobrien     * is 0.
65559243Sobrien     */
65659243Sobrien    if (!whyles->w_fe[1]) {
65759243Sobrien	dobreak(NULL, NULL);
65859243Sobrien	return;
65959243Sobrien    }
660167465Smp    setv(whyles->w_fename, quote(Strsave(*whyles->w_fe++)), VAR_READWRITE);
66159243Sobrien    bseek(&whyles->w_start);
66259243Sobrien}
66359243Sobrien
66459243Sobrienvoid
665167465Smpdorepeat(Char **v, struct command *kp)
66659243Sobrien{
667100616Smp    int i = 1;
66859243Sobrien
669100616Smp    do {
670100616Smp	i *= getn(v[1]);
671100616Smp	lshift(v, 2);
672100616Smp    } while (v[0] != NULL && Strcmp(v[0], STRrepeat) == 0);
673167465Smp    if (noexec)
674167465Smp	i = 1;
675100616Smp
676167465Smp    if (setintr) {
677167465Smp	pintr_disabled++;
678167465Smp	cleanup_push(&pintr_disabled, disabled_cleanup);
679167465Smp    }
68059243Sobrien    while (i > 0) {
681167465Smp	if (setintr && pintr_disabled == 1) {
682167465Smp	    cleanup_until(&pintr_disabled);
683167465Smp	    pintr_disabled++;
684167465Smp	    cleanup_push(&pintr_disabled, disabled_cleanup);
685167465Smp	}
68659243Sobrien	reexecute(kp);
68759243Sobrien	--i;
68859243Sobrien    }
689195609Smp    if (setintr && pintr_disabled == 1)
690195609Smp        cleanup_until(&pintr_disabled);
69159243Sobrien    donefds();
69259243Sobrien}
69359243Sobrien
69459243Sobrien/*ARGSUSED*/
69559243Sobrienvoid
696167465Smpdoswbrk(Char **v, struct command *c)
69759243Sobrien{
69859243Sobrien    USE(v);
69959243Sobrien    USE(c);
700167465Smp    if (!noexec)
701167465Smp	search(TC_BRKSW, 0, NULL);
70259243Sobrien}
70359243Sobrien
70459243Sobrienint
705167465Smpsrchx(Char *cp)
70659243Sobrien{
70759243Sobrien    struct srch *sp, *sp1, *sp2;
70859243Sobrien    int i;
70959243Sobrien
71059243Sobrien    /*
71159243Sobrien     * Ignore keywords inside heredocs
71259243Sobrien     */
71359243Sobrien    if (inheredoc)
71459243Sobrien	return -1;
71559243Sobrien
71659243Sobrien    /*
71759243Sobrien     * Binary search Sp1 is the beginning of the current search range. Sp2 is
71859243Sobrien     * one past the end.
71959243Sobrien     */
72059243Sobrien    for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2;) {
72159243Sobrien	sp = sp1 + ((sp2 - sp1) >> 1);
72259243Sobrien	if ((i = *cp - *sp->s_name) == 0 &&
72359243Sobrien	    (i = Strcmp(cp, str2short(sp->s_name))) == 0)
72459243Sobrien	    return sp->s_value;
72559243Sobrien	if (i < 0)
72659243Sobrien	    sp2 = sp;
72759243Sobrien	else
72859243Sobrien	    sp1 = sp + 1;
72959243Sobrien    }
73059243Sobrien    return (-1);
73159243Sobrien}
73259243Sobrien
733145479Smpstatic const char *
734167465Smpisrchx(int n)
73559243Sobrien{
736145479Smp    struct srch *sp, *sp2;
73759243Sobrien
73859243Sobrien    for (sp = srchn, sp2 = srchn + nsrchn; sp < sp2; sp++)
73959243Sobrien	if (sp->s_value == n)
74059243Sobrien	    return (sp->s_name);
74159243Sobrien    return ("");
74259243Sobrien}
74359243Sobrien
74459243Sobrien
745167465Smpstatic int Stype;
74659243Sobrienstatic Char *Sgoal;
74759243Sobrien
74859243Sobrienstatic void
749167465Smpsearch(int type, int level, Char *goal)
75059243Sobrien{
751167465Smp    struct Strbuf word = Strbuf_INIT;
752145479Smp    Char *cp;
753131962Smp    struct whyle *wp;
754131962Smp    int wlevel = 0;
755195609Smp    struct wordent *histent = NULL, *ohistent = NULL;
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;
763231990Smp	a.a_seek = 0;
76459243Sobrien	bseek(&a);
76559243Sobrien    }
766167465Smp    cleanup_push(&word, Strbuf_cleanup);
76759243Sobrien    do {
768195609Smp
769195609Smp	if (intty) {
770195609Smp	    histent = xmalloc(sizeof(*histent));
771195609Smp	    ohistent = xmalloc(sizeof(*histent));
772195609Smp	    ohistent->word = STRNULL;
773195609Smp	    ohistent->next = histent;
774195609Smp	    histent->prev = ohistent;
775195609Smp	}
776195609Smp
77769408Sache	if (intty && fseekp == feobp && aret == TCSH_F_SEEK)
77859243Sobrien	    printprompt(1, isrchx(type == TC_BREAK ? zlast : type));
77959243Sobrien	/* xprintf("? "), flush(); */
780167465Smp	(void) getword(&word);
781167465Smp	Strbuf_terminate(&word);
782195609Smp
783195609Smp	if (intty && Strlen(word.s) > 0) {
784195609Smp	    histent->word = Strsave(word.s);
785195609Smp	    histent->next = xmalloc(sizeof(*histent));
786195609Smp	    histent->next->prev = histent;
787195609Smp	    histent = histent->next;
788195609Smp	}
789195609Smp
790167465Smp	switch (srchx(word.s)) {
79159243Sobrien
79259243Sobrien	case TC_ELSE:
79359243Sobrien	    if (level == 0 && type == TC_IF)
794167465Smp		goto end;
79559243Sobrien	    break;
79659243Sobrien
79759243Sobrien	case TC_IF:
798316957Sdchagin	    while (getword(&word)) {
799316957Sdchagin		if (intty) {
800316957Sdchagin		    histent->word = Strsave(word.s);
801316957Sdchagin		    histent->next = xmalloc(sizeof(*histent));
802316957Sdchagin		    histent->next->prev = histent;
803316957Sdchagin		    histent = histent->next;
804316957Sdchagin		}
80559243Sobrien		continue;
806316957Sdchagin	    }
807316957Sdchagin
80859243Sobrien	    if ((type == TC_IF || type == TC_ELSE) &&
809167465Smp		eq(word.s, STRthen))
81059243Sobrien		level++;
81159243Sobrien	    break;
81259243Sobrien
81359243Sobrien	case TC_ENDIF:
81459243Sobrien	    if (type == TC_IF || type == TC_ELSE)
81559243Sobrien		level--;
81659243Sobrien	    break;
81759243Sobrien
81859243Sobrien	case TC_FOREACH:
81959243Sobrien	case TC_WHILE:
820131962Smp	    wlevel++;
82159243Sobrien	    if (type == TC_BREAK)
82259243Sobrien		level++;
82359243Sobrien	    break;
82459243Sobrien
82559243Sobrien	case TC_END:
826131962Smp	    if (type == TC_BRKSW) {
827131962Smp		if (wlevel == 0) {
828131962Smp		    wp = whyles;
829131962Smp		    if (wp) {
830131962Smp			    whyles = wp->w_next;
831131962Smp			    wpfree(wp);
832131962Smp		    }
833131962Smp		}
834131962Smp	    }
83559243Sobrien	    if (type == TC_BREAK)
83659243Sobrien		level--;
837131962Smp	    wlevel--;
83859243Sobrien	    break;
83959243Sobrien
84059243Sobrien	case TC_SWITCH:
84159243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
84259243Sobrien		level++;
84359243Sobrien	    break;
84459243Sobrien
84559243Sobrien	case TC_ENDSW:
84659243Sobrien	    if (type == TC_SWITCH || type == TC_BRKSW)
84759243Sobrien		level--;
84859243Sobrien	    break;
84959243Sobrien
85059243Sobrien	case TC_LABEL:
851167465Smp	    if (type == TC_GOTO && getword(&word) && eq(word.s, goal))
85259243Sobrien		level = -1;
85359243Sobrien	    break;
85459243Sobrien
85559243Sobrien	default:
85659243Sobrien	    if (type != TC_GOTO && (type != TC_SWITCH || level != 0))
85759243Sobrien		break;
858167465Smp	    if (word.len == 0 || word.s[word.len - 1] != ':')
85959243Sobrien		break;
860167465Smp	    word.s[--word.len] = 0;
861167465Smp	    if ((type == TC_GOTO && eq(word.s, goal)) ||
862167465Smp		(type == TC_SWITCH && eq(word.s, STRdefault)))
86359243Sobrien		level = -1;
86459243Sobrien	    break;
86559243Sobrien
86659243Sobrien	case TC_CASE:
86759243Sobrien	    if (type != TC_SWITCH || level != 0)
86859243Sobrien		break;
869167465Smp	    (void) getword(&word);
870167465Smp	    if (word.len != 0 && word.s[word.len - 1] == ':')
871167465Smp		word.s[--word.len] = 0;
872167465Smp	    cp = strip(Dfix1(word.s));
873167465Smp	    cleanup_push(cp, xfree);
87459243Sobrien	    if (Gmatch(goal, cp))
87559243Sobrien		level = -1;
876167465Smp	    cleanup_until(cp);
87759243Sobrien	    break;
87859243Sobrien
87959243Sobrien	case TC_DEFAULT:
88059243Sobrien	    if (type == TC_SWITCH && level == 0)
88159243Sobrien		level = -1;
88259243Sobrien	    break;
88359243Sobrien	}
884195609Smp	if (intty) {
885195609Smp	    ohistent->prev = histgetword(histent);
886195609Smp	    ohistent->prev->next = ohistent;
887195609Smp	    savehist(ohistent, 0);
888195609Smp	    freelex(ohistent);
889195609Smp	    xfree(ohistent);
890195609Smp	} else
891195609Smp	    (void) getword(NULL);
89259243Sobrien    } while (level >= 0);
893167465Smp end:
894167465Smp    cleanup_until(&word);
89559243Sobrien}
89659243Sobrien
897195609Smpstatic struct wordent *
898195609Smphistgetword(struct wordent *histent)
899195609Smp{
900316957Sdchagin    int first;
901195609Smp    eChar c, d;
902195609Smp    int e;
903195609Smp    struct Strbuf *tmp;
904195609Smp    tmp = xmalloc(sizeof(*tmp));
905195609Smp    tmp->size = 0;
906195609Smp    tmp->s = NULL;
907195609Smp    c = readc(1);
908195609Smp    d = 0;
909195609Smp    e = 0;
910195609Smp    for (;;) {
911195609Smp	tmp->len = 0;
912195609Smp	Strbuf_terminate (tmp);
913195609Smp	while (c == ' ' || c == '\t')
914195609Smp	    c = readc(1);
915195609Smp	if (c == '#')
916195609Smp	    do
917195609Smp		c = readc(1);
918195609Smp	    while (c != CHAR_ERR && c != '\n');
919195609Smp	if (c == CHAR_ERR)
920195609Smp	    goto past;
921195609Smp	if (c == '\n')
922195609Smp	    goto nl;
923195609Smp	unreadc(c);
924195609Smp	first = 1;
925195609Smp	do {
926195609Smp	    e = (c == '\\');
927195609Smp	    c = readc(1);
928195609Smp	    if (c == '\\' && !e) {
929195609Smp		if ((c = readc(1)) == '\n') {
930195609Smp		    e = 1;
931195609Smp		    c = ' ';
932195609Smp		} else {
933195609Smp		    unreadc(c);
934195609Smp		    c = '\\';
935195609Smp		}
936195609Smp	    }
937195609Smp	    if ((c == '\'' || c == '"') && !e) {
938195609Smp		if (d == 0)
939195609Smp		    d = c;
940195609Smp		else if (d == c)
941195609Smp		    d = 0;
942195609Smp	    }
943195609Smp	    if (c == CHAR_ERR)
944195609Smp		goto past;
945195609Smp
946195609Smp	    Strbuf_append1(tmp, (Char) c);
947195609Smp
948195609Smp	    if (!first && !d && c == '(' && !e) {
949195609Smp		break;
950195609Smp	    }
951195609Smp	    first = 0;
952195609Smp	} while (d || e || (c != ' ' && c != '\t' && c != '\n'));
953195609Smp	tmp->len--;
954195609Smp	if (tmp->len) {
955195609Smp	    Strbuf_terminate(tmp);
956195609Smp	    histent->word = Strsave(tmp->s);
957195609Smp	    histent->next = xmalloc(sizeof (*histent));
958195609Smp	    histent->next->prev = histent;
959195609Smp	    histent = histent->next;
960195609Smp	}
961195609Smp	if (c == '\n') {
962195609Smp	nl:
963195609Smp	    tmp->len = 0;
964195609Smp	    Strbuf_append1(tmp, (Char) c);
965195609Smp	    Strbuf_terminate(tmp);
966195609Smp	    histent->word = Strsave(tmp->s);
967195609Smp	    return histent;
968195609Smp	}
969195609Smp    }
970195609Smp
971195609Smppast:
972195609Smp    switch (Stype) {
973195609Smp
974195609Smp    case TC_IF:
975195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
976195609Smp	break;
977195609Smp
978195609Smp    case TC_ELSE:
979195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
980195609Smp	break;
981195609Smp
982195609Smp    case TC_BRKSW:
983195609Smp    case TC_SWITCH:
984195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
985195609Smp	break;
986195609Smp
987195609Smp    case TC_BREAK:
988195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "end");
989195609Smp	break;
990195609Smp
991195609Smp    case TC_GOTO:
992195609Smp	setname(short2str(Sgoal));
993195609Smp	stderror(ERR_NAME | ERR_NOTFOUND, "label");
994195609Smp	break;
995195609Smp
996195609Smp    default:
997195609Smp	break;
998195609Smp    }
999195609Smp    /* NOTREACHED */
1000195609Smp    return NULL;
1001195609Smp}
1002195609Smp
100359243Sobrienstatic int
1004167465Smpgetword(struct Strbuf *wp)
100559243Sobrien{
100659243Sobrien    int found = 0, first;
1007145479Smp    eChar c, d;
100859243Sobrien
1009167465Smp    if (wp)
1010167465Smp	wp->len = 0;
101159243Sobrien    c = readc(1);
101259243Sobrien    d = 0;
101359243Sobrien    do {
101459243Sobrien	while (c == ' ' || c == '\t')
101559243Sobrien	    c = readc(1);
101659243Sobrien	if (c == '#')
101759243Sobrien	    do
101859243Sobrien		c = readc(1);
1019145479Smp	    while (c != CHAR_ERR && c != '\n');
1020145479Smp	if (c == CHAR_ERR)
102159243Sobrien	    goto past;
102259243Sobrien	if (c == '\n') {
102359243Sobrien	    if (wp)
102459243Sobrien		break;
102559243Sobrien	    return (0);
102659243Sobrien	}
102759243Sobrien	unreadc(c);
102859243Sobrien	found = 1;
102959243Sobrien	first = 1;
103059243Sobrien	do {
103159243Sobrien	    c = readc(1);
103259243Sobrien	    if (c == '\\' && (c = readc(1)) == '\n')
103359243Sobrien		c = ' ';
103459243Sobrien	    if (c == '\'' || c == '"') {
103559243Sobrien		if (d == 0)
103659243Sobrien		    d = c;
103759243Sobrien		else if (d == c)
103859243Sobrien		    d = 0;
103959243Sobrien	    }
1040145479Smp	    if (c == CHAR_ERR)
104159243Sobrien		goto past;
1042167465Smp	    if (wp)
1043167465Smp		Strbuf_append1(wp, (Char) c);
1044316957Sdchagin	    if (!d && c == ')') {
1045316957Sdchagin		if (!first && wp) {
1046316957Sdchagin		    goto past_word_end;
1047316957Sdchagin		} else {
1048316957Sdchagin		    if (wp) {
1049316957Sdchagin			wp->len = 1;
1050316957Sdchagin			Strbuf_terminate(wp);
1051316957Sdchagin		    }
1052316957Sdchagin		    return found;
1053316957Sdchagin		}
1054316957Sdchagin	    }
105559243Sobrien	    if (!first && !d && c == '(') {
1056167465Smp		if (wp)
1057167465Smp		    goto past_word_end;
105859243Sobrien		else
105959243Sobrien		    break;
106059243Sobrien	    }
106159243Sobrien	    first = 0;
106259243Sobrien	} while ((d || (c != ' ' && c != '\t')) && c != '\n');
106359243Sobrien    } while (wp == 0);
106459243Sobrien
1065167465Smp past_word_end:
106659243Sobrien    unreadc(c);
1067167465Smp    if (found) {
1068167465Smp	wp->len--;
1069167465Smp	Strbuf_terminate(wp);
1070167465Smp    }
107159243Sobrien
107259243Sobrien    return (found);
107359243Sobrien
107459243Sobrienpast:
107559243Sobrien    switch (Stype) {
107659243Sobrien
107759243Sobrien    case TC_IF:
107859243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "then/endif");
107959243Sobrien	break;
108059243Sobrien
108159243Sobrien    case TC_ELSE:
108259243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endif");
108359243Sobrien	break;
108459243Sobrien
108559243Sobrien    case TC_BRKSW:
108659243Sobrien    case TC_SWITCH:
108759243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "endsw");
108859243Sobrien	break;
108959243Sobrien
109059243Sobrien    case TC_BREAK:
109159243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "end");
109259243Sobrien	break;
109359243Sobrien
109459243Sobrien    case TC_GOTO:
109559243Sobrien	setname(short2str(Sgoal));
109659243Sobrien	stderror(ERR_NAME | ERR_NOTFOUND, "label");
109759243Sobrien	break;
109859243Sobrien
109959243Sobrien    default:
110059243Sobrien	break;
110159243Sobrien    }
110259243Sobrien    /* NOTREACHED */
110359243Sobrien    return (0);
110459243Sobrien}
110559243Sobrien
110659243Sobrienstatic void
1107167465Smptoend(void)
110859243Sobrien{
110969408Sache    if (whyles->w_end.type == TCSH_F_SEEK && whyles->w_end.f_seek == 0) {
111059243Sobrien	search(TC_BREAK, 0, NULL);
111159243Sobrien	btell(&whyles->w_end);
111259243Sobrien	whyles->w_end.f_seek--;
111359243Sobrien    }
111459243Sobrien    else {
111559243Sobrien	bseek(&whyles->w_end);
111659243Sobrien    }
111759243Sobrien    wfree();
111859243Sobrien}
111959243Sobrien
1120131962Smpstatic void
1121167465Smpwpfree(struct whyle *wp)
1122131962Smp{
1123131962Smp	if (wp->w_fe0)
1124131962Smp	    blkfree(wp->w_fe0);
1125167465Smp	xfree(wp->w_fename);
1126167465Smp	xfree(wp);
1127131962Smp}
1128131962Smp
112959243Sobrienvoid
1130167465Smpwfree(void)
113159243Sobrien{
113259243Sobrien    struct Ain    o;
113359243Sobrien    struct whyle *nwp;
113459243Sobrien#ifdef lint
113559243Sobrien    nwp = NULL;	/* sun lint is dumb! */
113659243Sobrien#endif
113759243Sobrien
113859243Sobrien#ifdef FDEBUG
1139167465Smp    static const char foo[] = "IAFE";
114059243Sobrien#endif /* FDEBUG */
114159243Sobrien
114259243Sobrien    btell(&o);
114359243Sobrien
114459243Sobrien#ifdef FDEBUG
1145167465Smp    xprintf("o->type %c o->a_seek %d o->f_seek %d\n",
114659243Sobrien	    foo[o.type + 1], o.a_seek, o.f_seek);
114759243Sobrien#endif /* FDEBUG */
114859243Sobrien
114959243Sobrien    for (; whyles; whyles = nwp) {
1150145479Smp	struct whyle *wp = whyles;
115159243Sobrien	nwp = wp->w_next;
115259243Sobrien
115359243Sobrien#ifdef FDEBUG
1154167465Smp	xprintf("start->type %c start->a_seek %d start->f_seek %d\n",
115559243Sobrien		foo[wp->w_start.type+1],
115659243Sobrien		wp->w_start.a_seek, wp->w_start.f_seek);
1157167465Smp	xprintf("end->type %c end->a_seek %d end->f_seek %d\n",
115859243Sobrien		foo[wp->w_end.type + 1], wp->w_end.a_seek, wp->w_end.f_seek);
115959243Sobrien#endif /* FDEBUG */
116059243Sobrien
116159243Sobrien	/*
116259243Sobrien	 * XXX: We free loops that have different seek types.
116359243Sobrien	 */
116469408Sache	if (wp->w_end.type != TCSH_I_SEEK && wp->w_start.type == wp->w_end.type &&
116559243Sobrien	    wp->w_start.type == o.type) {
116669408Sache	    if (wp->w_end.type == TCSH_F_SEEK) {
116759243Sobrien		if (o.f_seek >= wp->w_start.f_seek &&
116859243Sobrien		    (wp->w_end.f_seek == 0 || o.f_seek < wp->w_end.f_seek))
116959243Sobrien		    break;
117059243Sobrien	    }
117159243Sobrien	    else {
117259243Sobrien		if (o.a_seek >= wp->w_start.a_seek &&
117359243Sobrien		    (wp->w_end.a_seek == 0 || o.a_seek < wp->w_end.a_seek))
117459243Sobrien		    break;
117559243Sobrien	    }
117659243Sobrien	}
117759243Sobrien
1178131962Smp	wpfree(wp);
117959243Sobrien    }
118059243Sobrien}
118159243Sobrien
118259243Sobrien/*ARGSUSED*/
118359243Sobrienvoid
1184167465Smpdoecho(Char **v, struct command *c)
118559243Sobrien{
118659243Sobrien    USE(c);
118759243Sobrien    xecho(' ', v);
118859243Sobrien}
118959243Sobrien
119059243Sobrien/*ARGSUSED*/
119159243Sobrienvoid
1192167465Smpdoglob(Char **v, struct command *c)
119359243Sobrien{
119459243Sobrien    USE(c);
119559243Sobrien    xecho(0, v);
119659243Sobrien    flush();
119759243Sobrien}
119859243Sobrien
119959243Sobrienstatic void
1200167465Smpxecho(int sep, Char **v)
120159243Sobrien{
1202167465Smp    Char *cp, **globbed = NULL;
120359243Sobrien    int     nonl = 0;
120459243Sobrien    int	    echo_style = ECHO_STYLE;
120559243Sobrien    struct varent *vp;
120659243Sobrien
120759243Sobrien    if ((vp = adrof(STRecho_style)) != NULL && vp->vec != NULL &&
120859243Sobrien	vp->vec[0] != NULL) {
120959243Sobrien	if (Strcmp(vp->vec[0], STRbsd) == 0)
121059243Sobrien	    echo_style = BSD_ECHO;
121159243Sobrien	else if (Strcmp(vp->vec[0], STRsysv) == 0)
121259243Sobrien	    echo_style = SYSV_ECHO;
121359243Sobrien	else if (Strcmp(vp->vec[0], STRboth) == 0)
121459243Sobrien	    echo_style = BOTH_ECHO;
121559243Sobrien	else if (Strcmp(vp->vec[0], STRnone) == 0)
121659243Sobrien	    echo_style = NONE_ECHO;
121759243Sobrien    }
121859243Sobrien
121959243Sobrien    v++;
122059243Sobrien    if (*v == 0)
122183098Smp	goto done;
1222167465Smp    if (setintr) {
1223167465Smp	int old_pintr_disabled;
1224167465Smp	pintr_push_enable(&old_pintr_disabled);
1225167465Smp	v = glob_all_or_error(v);
1226167465Smp	cleanup_until(&old_pintr_disabled);
1227167465Smp    } else {
1228167465Smp	v = glob_all_or_error(v);
122959243Sobrien    }
1230167465Smp    globbed = v;
1231167465Smp    if (globbed != NULL)
1232167465Smp	cleanup_push(globbed, blk_cleanup);
123359243Sobrien
123459243Sobrien    if ((echo_style & BSD_ECHO) != 0 && sep == ' ' && *v && eq(*v, STRmn))
123559243Sobrien	nonl++, v++;
123659243Sobrien
123759243Sobrien    while ((cp = *v++) != 0) {
1238145479Smp	Char c;
123959243Sobrien
1240167465Smp	if (setintr) {
1241167465Smp	    int old_pintr_disabled;
1242167465Smp
1243167465Smp	    pintr_push_enable(&old_pintr_disabled);
1244167465Smp	    cleanup_until(&old_pintr_disabled);
1245167465Smp	}
124659243Sobrien	while ((c = *cp++) != 0) {
124759243Sobrien	    if ((echo_style & SYSV_ECHO) != 0 && c == '\\') {
124859243Sobrien		switch (c = *cp++) {
124959243Sobrien		case 'a':
125059243Sobrien		    c = '\a';
125159243Sobrien		    break;
125259243Sobrien		case 'b':
125359243Sobrien		    c = '\b';
125459243Sobrien		    break;
125559243Sobrien		case 'c':
125659243Sobrien		    nonl = 1;
125759243Sobrien		    goto done;
125859243Sobrien		case 'e':
125959243Sobrien#if 0			/* Windows does not understand \e */
126059243Sobrien		    c = '\e';
126159243Sobrien#else
1262167465Smp		    c = CTL_ESC('\033');
126359243Sobrien#endif
126459243Sobrien		    break;
126559243Sobrien		case 'f':
126659243Sobrien		    c = '\f';
126759243Sobrien		    break;
126859243Sobrien		case 'n':
126959243Sobrien		    c = '\n';
127059243Sobrien		    break;
127159243Sobrien		case 'r':
127259243Sobrien		    c = '\r';
127359243Sobrien		    break;
127459243Sobrien		case 't':
127559243Sobrien		    c = '\t';
127659243Sobrien		    break;
127759243Sobrien		case 'v':
127859243Sobrien		    c = '\v';
127959243Sobrien		    break;
128059243Sobrien		case '\\':
128159243Sobrien		    c = '\\';
128259243Sobrien		    break;
128359243Sobrien		case '0':
128459243Sobrien		    c = 0;
128559243Sobrien		    if (*cp >= '0' && *cp < '8')
128659243Sobrien			c = c * 8 + *cp++ - '0';
128759243Sobrien		    if (*cp >= '0' && *cp < '8')
128859243Sobrien			c = c * 8 + *cp++ - '0';
128959243Sobrien		    if (*cp >= '0' && *cp < '8')
129059243Sobrien			c = c * 8 + *cp++ - '0';
129159243Sobrien		    break;
129259243Sobrien		case '\0':
129359243Sobrien		    c = '\\';
129459243Sobrien		    cp--;
129559243Sobrien		    break;
129659243Sobrien		default:
129759243Sobrien		    xputchar('\\' | QUOTE);
129859243Sobrien		    break;
129959243Sobrien		}
130059243Sobrien	    }
1301145479Smp	    xputwchar(c | QUOTE);
130259243Sobrien
130359243Sobrien	}
130459243Sobrien	if (*v)
130559243Sobrien	    xputchar(sep | QUOTE);
130659243Sobrien    }
130759243Sobriendone:
130859243Sobrien    if (sep && nonl == 0)
130959243Sobrien	xputchar('\n');
131059243Sobrien    else
131159243Sobrien	flush();
1312167465Smp    if (globbed != NULL)
1313167465Smp	cleanup_until(globbed);
131459243Sobrien}
131559243Sobrien
131659243Sobrien/* check whether an environment variable should invoke 'set_locale()' */
1317145479Smpstatic int
1318167465Smpislocale_var(Char *var)
131959243Sobrien{
132059243Sobrien    static Char *locale_vars[] = {
1321131962Smp	STRLANG,	STRLC_ALL, 	STRLC_CTYPE,	STRLC_NUMERIC,
1322131962Smp	STRLC_TIME,	STRLC_COLLATE,	STRLC_MESSAGES,	STRLC_MONETARY, 0
132359243Sobrien    };
1324145479Smp    Char **v;
132559243Sobrien
132659243Sobrien    for (v = locale_vars; *v; ++v)
132759243Sobrien	if (eq(var, *v))
132859243Sobrien	    return 1;
132959243Sobrien    return 0;
133059243Sobrien}
133159243Sobrien
1332167465Smpstatic void
1333167465Smpxlate_cr_cleanup(void *dummy)
1334167465Smp{
1335167465Smp    USE(dummy);
1336167465Smp    xlate_cr = 0;
1337167465Smp}
1338167465Smp
133959243Sobrien/*ARGSUSED*/
134059243Sobrienvoid
1341167465Smpdoprintenv(Char **v, struct command *c)
134259243Sobrien{
134359243Sobrien    Char   *e;
134459243Sobrien
134559243Sobrien    USE(c);
134659243Sobrien    v++;
134759243Sobrien    if (*v == 0) {
1348145479Smp	Char **ep;
134959243Sobrien
135059243Sobrien	xlate_cr = 1;
1351167465Smp	cleanup_push(&xlate_cr, xlate_cr_cleanup);
1352167465Smp	for (ep = STR_environ; *ep; ep++) {
1353167465Smp	    if (setintr) {
1354167465Smp		int old_pintr_disabled;
1355167465Smp
1356167465Smp		pintr_push_enable(&old_pintr_disabled);
1357167465Smp		cleanup_until(&old_pintr_disabled);
1358167465Smp	    }
135959243Sobrien	    xprintf("%S\n", *ep);
1360167465Smp	}
1361167465Smp	cleanup_until(&xlate_cr);
136259243Sobrien    }
136359243Sobrien    else if ((e = tgetenv(*v)) != NULL) {
1364167465Smp	int old_output_raw;
1365167465Smp
1366167465Smp	old_output_raw = output_raw;
136759243Sobrien	output_raw = 1;
1368167465Smp	cleanup_push(&old_output_raw, output_raw_restore);
136959243Sobrien	xprintf("%S\n", e);
1370167465Smp	cleanup_until(&old_output_raw);
137159243Sobrien    }
137259243Sobrien    else
1373167465Smp	setcopy(STRstatus, STR1, VAR_READWRITE);
137459243Sobrien}
137559243Sobrien
137659243Sobrien/* from "Karl Berry." <karl%mote.umb.edu@relay.cs.net> -- for NeXT things
137759243Sobrien   (and anything else with a modern compiler) */
137859243Sobrien
137959243Sobrien/*ARGSUSED*/
138059243Sobrienvoid
1381167465Smpdosetenv(Char **v, struct command *c)
138259243Sobrien{
138359243Sobrien    Char   *vp, *lp;
138459243Sobrien
138559243Sobrien    USE(c);
138659243Sobrien    if (*++v == 0) {
138759243Sobrien	doprintenv(--v, 0);
138859243Sobrien	return;
138959243Sobrien    }
139059243Sobrien
139159243Sobrien    vp = *v++;
1392231990Smp    lp = vp;
139359243Sobrien
1394231990Smp    if (!letter(*lp))
1395231990Smp	stderror(ERR_NAME | ERR_VARBEGIN);
1396231990Smp    do {
1397231990Smp	lp++;
1398316957Sdchagin    } while (alnum(*lp) || *lp == '.');
1399231990Smp    if (*lp != '\0')
1400231990Smp	stderror(ERR_NAME | ERR_VARALNUM);
1401231990Smp
140259243Sobrien    if ((lp = *v++) == 0)
140359243Sobrien	lp = STRNULL;
140459243Sobrien
1405167465Smp    lp = globone(lp, G_APPEND);
1406167465Smp    cleanup_push(lp, xfree);
1407167465Smp    tsetenv(vp, lp);
140859243Sobrien    if (eq(vp, STRKPATH)) {
1409167465Smp        importpath(lp);
141059243Sobrien	dohash(NULL, NULL);
1411167465Smp	cleanup_until(lp);
141259243Sobrien	return;
141359243Sobrien    }
141459243Sobrien
141559243Sobrien#ifdef apollo
141659243Sobrien    if (eq(vp, STRSYSTYPE)) {
141759243Sobrien	dohash(NULL, NULL);
1418167465Smp	cleanup_until(lp);
141959243Sobrien	return;
142059243Sobrien    }
142159243Sobrien#endif /* apollo */
142259243Sobrien
142359243Sobrien    /* dspkanji/dspmbyte autosetting */
142459243Sobrien    /* PATCH IDEA FROM Issei.Suzuki VERY THANKS */
142559243Sobrien#if defined(DSPMBYTE)
142659243Sobrien    if(eq(vp, STRLANG) && !adrof(CHECK_MBYTEVAR)) {
142759243Sobrien	autoset_dspmbyte(lp);
142859243Sobrien    }
142959243Sobrien#endif
143059243Sobrien
143159243Sobrien    if (islocale_var(vp)) {
143259243Sobrien#ifdef NLS
143359243Sobrien	int     k;
143459243Sobrien
143559243Sobrien# ifdef SETLOCALEBUG
143659243Sobrien	dont_free = 1;
143759243Sobrien# endif /* SETLOCALEBUG */
143859243Sobrien	(void) setlocale(LC_ALL, "");
143959243Sobrien# ifdef LC_COLLATE
144059243Sobrien	(void) setlocale(LC_COLLATE, "");
144159243Sobrien# endif
1442145479Smp# ifdef LC_CTYPE
1443145479Smp	(void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1444145479Smp# endif /* LC_CTYPE */
1445231990Smp# if defined(AUTOSET_KANJI)
1446231990Smp        autoset_kanji();
1447231990Smp# endif /* AUTOSET_KANJI */
144859243Sobrien# ifdef NLS_CATALOGS
144959243Sobrien#  ifdef LC_MESSAGES
145059243Sobrien	(void) setlocale(LC_MESSAGES, "");
145159243Sobrien#  endif /* LC_MESSAGES */
1452145479Smp	nlsclose();
145359243Sobrien	nlsinit();
145459243Sobrien# endif /* NLS_CATALOGS */
145559243Sobrien# ifdef SETLOCALEBUG
145659243Sobrien	dont_free = 0;
145759243Sobrien# endif /* SETLOCALEBUG */
145859243Sobrien# ifdef STRCOLLBUG
145959243Sobrien	fix_strcoll_bug();
146059243Sobrien# endif /* STRCOLLBUG */
146159243Sobrien	tw_cmd_free();	/* since the collation sequence has changed */
1462167465Smp	for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
146359243Sobrien	    continue;
1464145479Smp	AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
146559243Sobrien#else /* !NLS */
146659243Sobrien	AsciiOnly = 0;
146759243Sobrien#endif /* NLS */
146859243Sobrien	NLSMapsAreInited = 0;
146959243Sobrien	ed_Init();
147059243Sobrien	if (MapsAreInited && !NLSMapsAreInited)
147159243Sobrien	    ed_InitNLSMaps();
1472167465Smp	cleanup_until(lp);
147359243Sobrien	return;
147459243Sobrien    }
147559243Sobrien
1476100616Smp#ifdef NLS_CATALOGS
1477100616Smp    if (eq(vp, STRNLSPATH)) {
1478145479Smp	nlsclose();
1479100616Smp	nlsinit();
1480100616Smp    }
1481100616Smp#endif
1482100616Smp
148359243Sobrien    if (eq(vp, STRNOREBIND)) {
148459243Sobrien	NoNLSRebind = 1;
148559243Sobrien	MapsAreInited = 0;
148659243Sobrien	NLSMapsAreInited = 0;
148759243Sobrien	ed_InitMaps();
1488167465Smp	cleanup_until(lp);
148959243Sobrien	return;
149059243Sobrien    }
149169408Sache#ifdef WINNT_NATIVE
149259243Sobrien    if (eq(vp, STRtcshlang)) {
149359243Sobrien	nlsinit();
1494167465Smp	cleanup_until(lp);
149559243Sobrien	return;
149659243Sobrien    }
149769408Sache#endif /* WINNT_NATIVE */
149859243Sobrien    if (eq(vp, STRKTERM)) {
149959243Sobrien	char *t;
1500167465Smp
1501167465Smp	setv(STRterm, quote(lp), VAR_READWRITE);	/* lp memory used here */
1502167465Smp	cleanup_ignore(lp);
1503167465Smp	cleanup_until(lp);
150459243Sobrien	t = short2str(lp);
150559243Sobrien	if (noediting && strcmp(t, "unknown") != 0 && strcmp(t,"dumb") != 0) {
150659243Sobrien	    editing = 1;
150759243Sobrien	    noediting = 0;
1508167465Smp	    setNS(STRedit);
150959243Sobrien	}
151059243Sobrien	GotTermCaps = 0;
151159243Sobrien	ed_Init();
151259243Sobrien	return;
151359243Sobrien    }
151459243Sobrien
151559243Sobrien    if (eq(vp, STRKHOME)) {
1516167465Smp	Char *canon;
151759243Sobrien	/*
151859243Sobrien	 * convert to canonical pathname (possibly resolving symlinks)
151959243Sobrien	 */
1520167465Smp	canon = dcanon(lp, lp);
1521167465Smp	cleanup_ignore(lp);
1522167465Smp	cleanup_until(lp);
1523167465Smp	cleanup_push(canon, xfree);
1524167465Smp	setv(STRhome, quote(canon), VAR_READWRITE); /* lp memory used here */
1525167465Smp	cleanup_ignore(canon);
1526167465Smp	cleanup_until(canon);
152759243Sobrien
152859243Sobrien	/* fix directory stack for new tilde home */
152959243Sobrien	dtilde();
153059243Sobrien	return;
153159243Sobrien    }
153259243Sobrien
153359243Sobrien    if (eq(vp, STRKSHLVL)) {
1534167465Smp	setv(STRshlvl, quote(lp), VAR_READWRITE); /* lp memory used here */
1535167465Smp	cleanup_ignore(lp);
1536167465Smp	cleanup_until(lp);
153759243Sobrien	return;
153859243Sobrien    }
153959243Sobrien
154059243Sobrien    if (eq(vp, STRKUSER)) {
1541167465Smp	setv(STRuser, quote(lp), VAR_READWRITE);	/* lp memory used here */
1542167465Smp	cleanup_ignore(lp);
1543167465Smp	cleanup_until(lp);
154459243Sobrien	return;
154559243Sobrien    }
154659243Sobrien
154759243Sobrien    if (eq(vp, STRKGROUP)) {
1548167465Smp	setv(STRgroup, quote(lp), VAR_READWRITE); /* lp memory used here */
1549167465Smp	cleanup_ignore(lp);
1550167465Smp	cleanup_until(lp);
155159243Sobrien	return;
155259243Sobrien    }
155359243Sobrien
155459243Sobrien#ifdef COLOR_LS_F
155559243Sobrien    if (eq(vp, STRLS_COLORS)) {
155659243Sobrien        parseLS_COLORS(lp);
1557167465Smp	cleanup_until(lp);
155859243Sobrien	return;
155959243Sobrien    }
1560316957Sdchagin    if (eq(vp, STRLSCOLORS)) {
1561316957Sdchagin        parseLSCOLORS(lp);
1562316957Sdchagin	cleanup_until(lp);
1563316957Sdchagin	return;
1564316957Sdchagin    }
156559243Sobrien#endif /* COLOR_LS_F */
156659243Sobrien
156759243Sobrien#ifdef SIG_WINDOW
156859243Sobrien    /*
156959243Sobrien     * Load/Update $LINES $COLUMNS
157059243Sobrien     */
157159243Sobrien    if ((eq(lp, STRNULL) && (eq(vp, STRLINES) || eq(vp, STRCOLUMNS))) ||
157259243Sobrien	eq(vp, STRTERMCAP)) {
1573167465Smp	cleanup_until(lp);
157459243Sobrien	check_window_size(1);
157559243Sobrien	return;
157659243Sobrien    }
157759243Sobrien
157859243Sobrien    /*
157959243Sobrien     * Change the size to the one directed by $LINES and $COLUMNS
158059243Sobrien     */
158159243Sobrien    if (eq(vp, STRLINES) || eq(vp, STRCOLUMNS)) {
158259243Sobrien#if 0
158359243Sobrien	GotTermCaps = 0;
158459243Sobrien#endif
1585167465Smp	cleanup_until(lp);
158659243Sobrien	ed_Init();
158759243Sobrien	return;
158859243Sobrien    }
158959243Sobrien#endif /* SIG_WINDOW */
1590167465Smp    cleanup_until(lp);
159159243Sobrien}
159259243Sobrien
159359243Sobrien/*ARGSUSED*/
159459243Sobrienvoid
1595167465Smpdounsetenv(Char **v, struct command *c)
159659243Sobrien{
1597167465Smp    Char  **ep, *p, *n, *name;
159859243Sobrien    int     i, maxi;
159959243Sobrien
160059243Sobrien    USE(c);
160159243Sobrien    /*
160259243Sobrien     * Find the longest environment variable
160359243Sobrien     */
160459243Sobrien    for (maxi = 0, ep = STR_environ; *ep; ep++) {
160559243Sobrien	for (i = 0, p = *ep; *p && *p != '='; p++, i++)
160659243Sobrien	    continue;
160759243Sobrien	if (i > maxi)
160859243Sobrien	    maxi = i;
160959243Sobrien    }
161059243Sobrien
1611167465Smp    name = xmalloc((maxi + 1) * sizeof(Char));
1612167465Smp    cleanup_push(name, xfree);
161359243Sobrien
161459243Sobrien    while (++v && *v)
161559243Sobrien	for (maxi = 1; maxi;)
161659243Sobrien	    for (maxi = 0, ep = STR_environ; *ep; ep++) {
161759243Sobrien		for (n = name, p = *ep; *p && *p != '='; *n++ = *p++)
161859243Sobrien		    continue;
161959243Sobrien		*n = '\0';
162059243Sobrien		if (!Gmatch(name, *v))
162159243Sobrien		    continue;
162259243Sobrien		maxi = 1;
162359243Sobrien
162459243Sobrien		/* Unset the name. This wasn't being done until
162559243Sobrien		 * later but most of the stuff following won't
162659243Sobrien		 * work (particularly the setlocale() and getenv()
162759243Sobrien		 * stuff) as intended until the name is actually
162859243Sobrien		 * removed. (sg)
162959243Sobrien		 */
163059243Sobrien		Unsetenv(name);
163159243Sobrien
163259243Sobrien		if (eq(name, STRNOREBIND)) {
163359243Sobrien		    NoNLSRebind = 0;
163459243Sobrien		    MapsAreInited = 0;
163559243Sobrien		    NLSMapsAreInited = 0;
163659243Sobrien		    ed_InitMaps();
163759243Sobrien		}
163859243Sobrien#ifdef apollo
163959243Sobrien		else if (eq(name, STRSYSTYPE))
164059243Sobrien		    dohash(NULL, NULL);
164159243Sobrien#endif /* apollo */
164259243Sobrien		else if (islocale_var(name)) {
164359243Sobrien#ifdef NLS
164459243Sobrien		    int     k;
164559243Sobrien
164659243Sobrien# ifdef SETLOCALEBUG
164759243Sobrien		    dont_free = 1;
164859243Sobrien# endif /* SETLOCALEBUG */
164959243Sobrien		    (void) setlocale(LC_ALL, "");
165059243Sobrien# ifdef LC_COLLATE
165159243Sobrien		    (void) setlocale(LC_COLLATE, "");
165259243Sobrien# endif
1653145479Smp# ifdef LC_CTYPE
1654167465Smp		    (void) setlocale(LC_CTYPE, ""); /* for iscntrl */
1655145479Smp# endif /* LC_CTYPE */
165659243Sobrien# ifdef NLS_CATALOGS
165759243Sobrien#  ifdef LC_MESSAGES
165859243Sobrien		    (void) setlocale(LC_MESSAGES, "");
165959243Sobrien#  endif /* LC_MESSAGES */
1660145479Smp		    nlsclose();
166159243Sobrien		    nlsinit();
166259243Sobrien# endif /* NLS_CATALOGS */
166359243Sobrien# ifdef SETLOCALEBUG
166459243Sobrien		    dont_free = 0;
166559243Sobrien# endif /* SETLOCALEBUG */
166659243Sobrien# ifdef STRCOLLBUG
166759243Sobrien		    fix_strcoll_bug();
166859243Sobrien# endif /* STRCOLLBUG */
166959243Sobrien		    tw_cmd_free();/* since the collation sequence has changed */
1670167465Smp		    for (k = 0200; k <= 0377 && !Isprint(CTL_ESC(k)); k++)
167159243Sobrien			continue;
1672145479Smp		    AsciiOnly = MB_CUR_MAX == 1 && k > 0377;
167359243Sobrien#else /* !NLS */
167459243Sobrien		    AsciiOnly = getenv("LANG") == NULL &&
167559243Sobrien			getenv("LC_CTYPE") == NULL;
167659243Sobrien#endif /* NLS */
167759243Sobrien		    NLSMapsAreInited = 0;
167859243Sobrien		    ed_Init();
167959243Sobrien		    if (MapsAreInited && !NLSMapsAreInited)
168059243Sobrien			ed_InitNLSMaps();
168159243Sobrien
168259243Sobrien		}
168369408Sache#ifdef WINNT_NATIVE
168459243Sobrien		else if (eq(name,(STRtcshlang))) {
168559243Sobrien		    nls_dll_unload();
168659243Sobrien		    nlsinit();
168759243Sobrien		}
168869408Sache#endif /* WINNT_NATIVE */
168959243Sobrien#ifdef COLOR_LS_F
169059243Sobrien		else if (eq(name, STRLS_COLORS))
169159243Sobrien		    parseLS_COLORS(n);
1692316957Sdchagin		else if (eq(name, STRLSCOLORS))
1693316957Sdchagin		    parseLSCOLORS(n);
169459243Sobrien#endif /* COLOR_LS_F */
1695100616Smp#ifdef NLS_CATALOGS
1696100616Smp		else if (eq(name, STRNLSPATH)) {
1697145479Smp		    nlsclose();
1698100616Smp		    nlsinit();
1699100616Smp		}
1700100616Smp#endif
170159243Sobrien		/*
170259243Sobrien		 * start again cause the environment changes
170359243Sobrien		 */
170459243Sobrien		break;
170559243Sobrien	    }
1706167465Smp    cleanup_until(name);
170759243Sobrien}
170859243Sobrien
170959243Sobrienvoid
1710167465Smptsetenv(const Char *name, const Char *val)
171159243Sobrien{
171259243Sobrien#ifdef SETENV_IN_LIB
171359243Sobrien/*
171459243Sobrien * XXX: This does not work right, since tcsh cannot track changes to
171559243Sobrien * the environment this way. (the builtin setenv without arguments does
171659243Sobrien * not print the right stuff neither does unsetenv). This was for Mach,
171759243Sobrien * it is not needed anymore.
171859243Sobrien */
171959243Sobrien#undef setenv
1720167465Smp    char   *cname;
172159243Sobrien
1722167465Smp    if (name == NULL)
172359243Sobrien	return;
1724167465Smp    cname = strsave(short2str(name));
1725167465Smp    setenv(cname, short2str(val), 1);
1726167465Smp    xfree(cname);
172759243Sobrien#else /* !SETENV_IN_LIB */
1728145479Smp    Char **ep = STR_environ;
1729145479Smp    const Char *ccp;
1730145479Smp    Char *cp, *dp;
173159243Sobrien    Char   *blk[2];
173259243Sobrien    Char  **oep = ep;
173359243Sobrien
173469408Sache#ifdef WINNT_NATIVE
1735167465Smp    nt_set_env(name,val);
173669408Sache#endif /* WINNT_NATIVE */
173759243Sobrien    for (; *ep; ep++) {
173869408Sache#ifdef WINNT_NATIVE
1739145479Smp	for (ccp = name, dp = *ep; *ccp && Tolower(*ccp & TRIM) == Tolower(*dp);
1740145479Smp				ccp++, dp++)
174159243Sobrien#else
1742145479Smp	for (ccp = name, dp = *ep; *ccp && (*ccp & TRIM) == *dp; ccp++, dp++)
174369408Sache#endif /* WINNT_NATIVE */
174459243Sobrien	    continue;
1745145479Smp	if (*ccp != 0 || *dp != '=')
174659243Sobrien	    continue;
174759243Sobrien	cp = Strspl(STRequal, val);
1748167465Smp	xfree(*ep);
174959243Sobrien	*ep = strip(Strspl(name, cp));
1750167465Smp	xfree(cp);
175159243Sobrien	blkfree((Char **) environ);
175259243Sobrien	environ = short2blk(STR_environ);
175359243Sobrien	return;
175459243Sobrien    }
175559243Sobrien    cp = Strspl(name, STRequal);
175659243Sobrien    blk[0] = strip(Strspl(cp, val));
1757167465Smp    xfree(cp);
175859243Sobrien    blk[1] = 0;
175959243Sobrien    STR_environ = blkspl(STR_environ, blk);
176059243Sobrien    blkfree((Char **) environ);
176159243Sobrien    environ = short2blk(STR_environ);
1762167465Smp    xfree(oep);
176359243Sobrien#endif /* SETENV_IN_LIB */
176459243Sobrien}
176559243Sobrien
176659243Sobrienvoid
1767167465SmpUnsetenv(Char *name)
176859243Sobrien{
1769145479Smp    Char **ep = STR_environ;
1770145479Smp    Char *cp, *dp;
177159243Sobrien    Char **oep = ep;
177259243Sobrien
177369408Sache#ifdef WINNT_NATIVE
177459243Sobrien	nt_set_env(name,NULL);
177569408Sache#endif /*WINNT_NATIVE */
177659243Sobrien    for (; *ep; ep++) {
177759243Sobrien	for (cp = name, dp = *ep; *cp && *cp == *dp; cp++, dp++)
177859243Sobrien	    continue;
177959243Sobrien	if (*cp != 0 || *dp != '=')
178059243Sobrien	    continue;
178159243Sobrien	cp = *ep;
178259243Sobrien	*ep = 0;
178359243Sobrien	STR_environ = blkspl(STR_environ, ep + 1);
178459243Sobrien	blkfree((Char **) environ);
178559243Sobrien	environ = short2blk(STR_environ);
178659243Sobrien	*ep = cp;
1787167465Smp	xfree(cp);
1788167465Smp	xfree(oep);
178959243Sobrien	return;
179059243Sobrien    }
179159243Sobrien}
179259243Sobrien
179359243Sobrien/*ARGSUSED*/
179459243Sobrienvoid
1795167465Smpdoumask(Char **v, struct command *c)
179659243Sobrien{
1797145479Smp    Char *cp = v[1];
1798145479Smp    int i;
179959243Sobrien
180059243Sobrien    USE(c);
180159243Sobrien    if (cp == 0) {
180259415Sobrien	i = (int)umask(0);
180359243Sobrien	(void) umask(i);
180459243Sobrien	xprintf("%o\n", i);
180559243Sobrien	return;
180659243Sobrien    }
180759243Sobrien    i = 0;
180859243Sobrien    while (Isdigit(*cp) && *cp != '8' && *cp != '9')
180959243Sobrien	i = i * 8 + *cp++ - '0';
181059243Sobrien    if (*cp || i < 0 || i > 0777)
181159243Sobrien	stderror(ERR_NAME | ERR_MASK);
181259243Sobrien    (void) umask(i);
181359243Sobrien}
181459243Sobrien
181559243Sobrien#ifndef HAVENOLIMIT
181659243Sobrien# ifndef BSDLIMIT
181759243Sobrien   typedef long RLIM_TYPE;
1818145479Smp#  ifdef _OSD_POSIX /* BS2000 */
1819145479Smp#   include <ulimit.h>
1820145479Smp#  endif
182159243Sobrien#  ifndef RLIM_INFINITY
182259243Sobrien#   if !defined(_MINIX) && !defined(__clipper__) && !defined(_CRAY)
182359243Sobrien    extern RLIM_TYPE ulimit();
182459243Sobrien#   endif /* ! _MINIX && !__clipper__ */
182559243Sobrien#   define RLIM_INFINITY 0x003fffff
182659243Sobrien#   define RLIMIT_FSIZE 1
182759243Sobrien#  endif /* RLIM_INFINITY */
182859243Sobrien#  ifdef aiws
182959243Sobrien#   define toset(a) (((a) == 3) ? 1004 : (a) + 1)
183059243Sobrien#   define RLIMIT_DATA	3
183159243Sobrien#   define RLIMIT_STACK 1005
183259243Sobrien#  else /* aiws */
183359243Sobrien#   define toset(a) ((a) + 1)
183459243Sobrien#  endif /* aiws */
183559243Sobrien# else /* BSDLIMIT */
1836145479Smp#  if (defined(BSD4_4) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || (HPUXVERSION >= 1100)) && !defined(__386BSD__)
1837100616Smp    typedef rlim_t RLIM_TYPE;
183859243Sobrien#  else
183959243Sobrien#   if defined(SOLARIS2) || (defined(sgi) && SYSVREL > 3)
184059243Sobrien     typedef rlim_t RLIM_TYPE;
184159243Sobrien#   else
184259243Sobrien#    if defined(_SX)
184359243Sobrien      typedef long long RLIM_TYPE;
1844100616Smp#    else /* !_SX */
184559243Sobrien      typedef unsigned long RLIM_TYPE;
184659243Sobrien#    endif /* _SX */
184759243Sobrien#   endif /* SOLARIS2 || (sgi && SYSVREL > 3) */
184859243Sobrien#  endif /* BSD4_4 && !__386BSD__  */
184959243Sobrien# endif /* BSDLIMIT */
185059243Sobrien
1851131962Smp# if (HPUXVERSION > 700) && (HPUXVERSION < 1100) && defined(BSDLIMIT)
185259243Sobrien/* Yes hpux8.0 has limits but <sys/resource.h> does not make them public */
185359243Sobrien/* Yes, we could have defined _KERNEL, and -I/etc/conf/h, but is that better? */
185459243Sobrien#  ifndef RLIMIT_CPU
185559243Sobrien#   define RLIMIT_CPU		0
185659243Sobrien#   define RLIMIT_FSIZE		1
185759243Sobrien#   define RLIMIT_DATA		2
185859243Sobrien#   define RLIMIT_STACK		3
185959243Sobrien#   define RLIMIT_CORE		4
186059243Sobrien#   define RLIMIT_RSS		5
186159243Sobrien#   define RLIMIT_NOFILE	6
186259243Sobrien#  endif /* RLIMIT_CPU */
186359243Sobrien#  ifndef RLIM_INFINITY
186459243Sobrien#   define RLIM_INFINITY	0x7fffffff
186559243Sobrien#  endif /* RLIM_INFINITY */
186659243Sobrien   /*
186759243Sobrien    * old versions of HP/UX counted limits in 512 bytes
186859243Sobrien    */
186959243Sobrien#  ifndef SIGRTMIN
187059243Sobrien#   define FILESIZE512
187159243Sobrien#  endif /* SIGRTMIN */
1872131962Smp# endif /* (HPUXVERSION > 700) && (HPUXVERSION < 1100) && BSDLIMIT */
187359243Sobrien
187459243Sobrien# if SYSVREL > 3 && defined(BSDLIMIT) && !defined(_SX)
187559243Sobrien/* In order to use rusage, we included "/usr/ucbinclude/sys/resource.h" in */
187659243Sobrien/* sh.h.  However, some SVR4 limits are defined in <sys/resource.h>.  Rather */
187759243Sobrien/* than include both and get warnings, we define the extra SVR4 limits here. */
187883098Smp/* XXX: I don't understand if RLIMIT_AS is defined, why don't we define */
187983098Smp/* RLIMIT_VMEM based on it? */
188059243Sobrien#  ifndef RLIMIT_VMEM
188159243Sobrien#   define RLIMIT_VMEM	6
188259243Sobrien#  endif
188359243Sobrien#  ifndef RLIMIT_AS
188459243Sobrien#   define RLIMIT_AS	RLIMIT_VMEM
188559243Sobrien#  endif
188659243Sobrien# endif /* SYSVREL > 3 && BSDLIMIT */
188759243Sobrien
1888231990Smp# if (defined(__linux__) || defined(__GNU__) || defined(__GLIBC__))
1889231990Smp#  if defined(RLIMIT_AS) && !defined(RLIMIT_VMEM)
1890231990Smp#   define RLIMIT_VMEM	RLIMIT_AS
1891231990Smp#  endif
1892231990Smp/*
1893231990Smp * Oh well, <asm-generic/resource.h> has it, but <bits/resource.h> does not
1894231990Smp * Linux headers: When the left hand does not know what the right hand does.
1895231990Smp */
1896231990Smp#  if defined(RLIMIT_RTPRIO) && !defined(RLIMIT_RTTIME)
1897231990Smp#   define RLIMIT_RTTIME (RLIMIT_RTPRIO + 1)
1898231990Smp#  endif
189983098Smp# endif
190083098Smp
190159243Sobrienstruct limits limits[] =
190259243Sobrien{
190359243Sobrien# ifdef RLIMIT_CPU
190459243Sobrien    { RLIMIT_CPU, 	"cputime",	1,	"seconds"	},
190559243Sobrien# endif /* RLIMIT_CPU */
190659243Sobrien
190759243Sobrien# ifdef RLIMIT_FSIZE
190859243Sobrien#  ifndef aiws
190959243Sobrien    { RLIMIT_FSIZE, 	"filesize",	1024,	"kbytes"	},
191059243Sobrien#  else
191159243Sobrien    { RLIMIT_FSIZE, 	"filesize",	512,	"blocks"	},
191259243Sobrien#  endif /* aiws */
191359243Sobrien# endif /* RLIMIT_FSIZE */
191459243Sobrien
191559243Sobrien# ifdef RLIMIT_DATA
191659243Sobrien    { RLIMIT_DATA, 	"datasize",	1024,	"kbytes"	},
191759243Sobrien# endif /* RLIMIT_DATA */
191859243Sobrien
191959243Sobrien# ifdef RLIMIT_STACK
192059243Sobrien#  ifndef aiws
192159243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024,	"kbytes"	},
192259243Sobrien#  else
192359243Sobrien    { RLIMIT_STACK, 	"stacksize",	1024 * 1024,	"kbytes"},
192459243Sobrien#  endif /* aiws */
192559243Sobrien# endif /* RLIMIT_STACK */
192659243Sobrien
192759243Sobrien# ifdef RLIMIT_CORE
192859243Sobrien    { RLIMIT_CORE, 	"coredumpsize",	1024,	"kbytes"	},
192959243Sobrien# endif /* RLIMIT_CORE */
193059243Sobrien
193159243Sobrien# ifdef RLIMIT_RSS
193259243Sobrien    { RLIMIT_RSS, 	"memoryuse",	1024,	"kbytes"	},
193359243Sobrien# endif /* RLIMIT_RSS */
193459243Sobrien
193559243Sobrien# ifdef RLIMIT_UMEM
193659243Sobrien    { RLIMIT_UMEM, 	"memoryuse",	1024,	"kbytes"	},
193759243Sobrien# endif /* RLIMIT_UMEM */
193859243Sobrien
193959243Sobrien# ifdef RLIMIT_VMEM
194059243Sobrien    { RLIMIT_VMEM, 	"vmemoryuse",	1024,	"kbytes"	},
194159243Sobrien# endif /* RLIMIT_VMEM */
194259243Sobrien
1943145479Smp# if defined(RLIMIT_HEAP) /* found on BS2000/OSD systems */
1944145479Smp    { RLIMIT_HEAP,	"heapsize",	1024,	"kbytes"	},
1945145479Smp# endif /* RLIMIT_HEAP */
1946145479Smp
194759243Sobrien# ifdef RLIMIT_NOFILE
194859243Sobrien    { RLIMIT_NOFILE, 	"descriptors", 1,	""		},
194959243Sobrien# endif /* RLIMIT_NOFILE */
195059243Sobrien
1951316957Sdchagin# ifdef RLIMIT_NPTS
1952316957Sdchagin    { RLIMIT_NPTS,	"pseudoterminals", 1,	""		},
1953316957Sdchagin# endif /* RLIMIT_NPTS */
1954316957Sdchagin
1955316957Sdchagin# ifdef RLIMIT_KQUEUES
1956316957Sdchagin    { RLIMIT_KQUEUES,	"kqueues",	1,	""		},
1957316957Sdchagin# endif /* RLIMIT_KQUEUES */
1958316957Sdchagin
195959243Sobrien# ifdef RLIMIT_CONCUR
196059243Sobrien    { RLIMIT_CONCUR, 	"concurrency", 1,	"thread(s)"	},
196159243Sobrien# endif /* RLIMIT_CONCUR */
196259243Sobrien
196359243Sobrien# ifdef RLIMIT_MEMLOCK
196459243Sobrien    { RLIMIT_MEMLOCK,	"memorylocked",	1024,	"kbytes"	},
196559243Sobrien# endif /* RLIMIT_MEMLOCK */
196659243Sobrien
196759243Sobrien# ifdef RLIMIT_NPROC
196859243Sobrien    { RLIMIT_NPROC,	"maxproc",	1,	""		},
196959243Sobrien# endif /* RLIMIT_NPROC */
197059243Sobrien
1971316957Sdchagin# ifdef RLIMIT_NTHR
1972316957Sdchagin    { RLIMIT_NTHR,	"maxthread",	1,	""		},
1973316957Sdchagin# endif /* RLIMIT_NTHR */
1974316957Sdchagin
1975100616Smp# if defined(RLIMIT_OFILE) && !defined(RLIMIT_NOFILE)
197659243Sobrien    { RLIMIT_OFILE,	"openfiles",	1,	""		},
1977100616Smp# endif /* RLIMIT_OFILE && !defined(RLIMIT_NOFILE) */
197859243Sobrien
1979100616Smp# ifdef RLIMIT_SBSIZE
1980100616Smp    { RLIMIT_SBSIZE,	"sbsize",	1,	""		},
1981100616Smp# endif /* RLIMIT_SBSIZE */
1982100616Smp
1983195609Smp# ifdef RLIMIT_SWAP
1984195609Smp    { RLIMIT_SWAP,	"swapsize",	1024,	"kbytes"	},
1985195609Smp# endif /* RLIMIT_SWAP */
1986194767Skib
1987231990Smp# ifdef RLIMIT_LOCKS
1988231990Smp    { RLIMIT_LOCKS,	"maxlocks",	1,	""		},
1989231990Smp# endif /* RLIMIT_LOCKS */
1990231990Smp
1991316957Sdchagin# ifdef RLIMIT_POSIXLOCKS
1992316957Sdchagin    { RLIMIT_POSIXLOCKS,"posixlocks",	1,	""		},
1993316957Sdchagin# endif /* RLIMIT_POSIXLOCKS */
1994316957Sdchagin
1995231990Smp# ifdef RLIMIT_SIGPENDING
1996231990Smp    { RLIMIT_SIGPENDING,"maxsignal",	1,	""		},
1997231990Smp# endif /* RLIMIT_SIGPENDING */
1998231990Smp
1999231990Smp# ifdef RLIMIT_MSGQUEUE
2000231990Smp    { RLIMIT_MSGQUEUE,	"maxmessage",	1,	""		},
2001231990Smp# endif /* RLIMIT_MSGQUEUE */
2002231990Smp
2003231990Smp# ifdef RLIMIT_NICE
2004231990Smp    { RLIMIT_NICE,	"maxnice",	1,	""		},
2005231990Smp# endif /* RLIMIT_NICE */
2006231990Smp
2007231990Smp# ifdef RLIMIT_RTPRIO
2008231990Smp    { RLIMIT_RTPRIO,	"maxrtprio",	1,	""		},
2009231990Smp# endif /* RLIMIT_RTPRIO */
2010231990Smp
2011231990Smp# ifdef RLIMIT_RTTIME
2012231990Smp    { RLIMIT_RTTIME,	"maxrttime",	1,	"usec"		},
2013231990Smp# endif /* RLIMIT_RTTIME */
2014231990Smp
201559243Sobrien    { -1, 		NULL, 		0, 	NULL		}
201659243Sobrien};
201759243Sobrien
2018167465Smpstatic struct limits *findlim	(Char *);
2019167465Smpstatic RLIM_TYPE getval		(struct limits *, Char **);
2020231990Smpstatic int strtail		(Char *, const char *);
2021167465Smpstatic void limtail		(Char *, const char *);
2022231990Smpstatic void limtail2		(Char *, const char *, const char *);
2023167465Smpstatic void plim		(struct limits *, int);
2024167465Smpstatic int setlim		(struct limits *, int, RLIM_TYPE);
202559243Sobrien
202659243Sobrien#ifdef convex
202759243Sobrienstatic  RLIM_TYPE
2028167465Smprestrict_limit(double value)
202959243Sobrien{
203059243Sobrien    /*
203159243Sobrien     * is f too large to cope with? return the maximum or minimum int
203259243Sobrien     */
203359243Sobrien    if (value > (double) INT_MAX)
203459243Sobrien	return (RLIM_TYPE) INT_MAX;
203559243Sobrien    else if (value < (double) INT_MIN)
203659243Sobrien	return (RLIM_TYPE) INT_MIN;
203759243Sobrien    else
203859243Sobrien	return (RLIM_TYPE) value;
203959243Sobrien}
204059243Sobrien#else /* !convex */
204159243Sobrien# define restrict_limit(x)	((RLIM_TYPE) (x))
204259243Sobrien#endif /* convex */
204359243Sobrien
204459243Sobrien
204559243Sobrienstatic struct limits *
2046167465Smpfindlim(Char *cp)
204759243Sobrien{
2048145479Smp    struct limits *lp, *res;
204959243Sobrien
2050167465Smp    res = NULL;
205159243Sobrien    for (lp = limits; lp->limconst >= 0; lp++)
205259243Sobrien	if (prefix(cp, str2short(lp->limname))) {
205359243Sobrien	    if (res)
205459243Sobrien		stderror(ERR_NAME | ERR_AMBIG);
205559243Sobrien	    res = lp;
205659243Sobrien	}
205759243Sobrien    if (res)
205859243Sobrien	return (res);
205959243Sobrien    stderror(ERR_NAME | ERR_LIMIT);
206059243Sobrien    /* NOTREACHED */
206159243Sobrien    return (0);
206259243Sobrien}
206359243Sobrien
206459243Sobrien/*ARGSUSED*/
206559243Sobrienvoid
2066167465Smpdolimit(Char **v, struct command *c)
206759243Sobrien{
2068145479Smp    struct limits *lp;
2069145479Smp    RLIM_TYPE limit;
207059243Sobrien    int    hard = 0;
207159243Sobrien
207259243Sobrien    USE(c);
207359243Sobrien    v++;
207459243Sobrien    if (*v && eq(*v, STRmh)) {
207559243Sobrien	hard = 1;
207659243Sobrien	v++;
207759243Sobrien    }
207859243Sobrien    if (*v == 0) {
207959243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
208059243Sobrien	    plim(lp, hard);
208159243Sobrien	return;
208259243Sobrien    }
208359243Sobrien    lp = findlim(v[0]);
208459243Sobrien    if (v[1] == 0) {
208559243Sobrien	plim(lp, hard);
208659243Sobrien	return;
208759243Sobrien    }
208859243Sobrien    limit = getval(lp, v + 1);
208959243Sobrien    if (setlim(lp, hard, limit) < 0)
209059243Sobrien	stderror(ERR_SILENT);
209159243Sobrien}
209259243Sobrien
209359243Sobrienstatic  RLIM_TYPE
2094167465Smpgetval(struct limits *lp, Char **v)
209559243Sobrien{
2096145479Smp    float f;
209759243Sobrien    Char   *cp = *v++;
209859243Sobrien
209959243Sobrien    f = atof(short2str(cp));
210059243Sobrien
210159243Sobrien# ifdef convex
210259243Sobrien    /*
210359243Sobrien     * is f too large to cope with. limit f to minint, maxint  - X-6768 by
210459243Sobrien     * strike
210559243Sobrien     */
210659243Sobrien    if ((f < (double) INT_MIN) || (f > (double) INT_MAX)) {
210759243Sobrien	stderror(ERR_NAME | ERR_TOOLARGE);
210859243Sobrien    }
210959243Sobrien# endif /* convex */
211059243Sobrien
211159243Sobrien    while (Isdigit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E')
211259243Sobrien	cp++;
211359243Sobrien    if (*cp == 0) {
211459243Sobrien	if (*v == 0)
211569408Sache	    return restrict_limit((f * lp->limdiv) + 0.5);
211659243Sobrien	cp = *v;
211759243Sobrien    }
211859243Sobrien    switch (*cp) {
211959243Sobrien# ifdef RLIMIT_CPU
212059243Sobrien    case ':':
212159243Sobrien	if (lp->limconst != RLIMIT_CPU)
212259243Sobrien	    goto badscal;
212359243Sobrien	return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f * 60.0 + atof(short2str(cp + 1))));
212459243Sobrien    case 'h':
212559243Sobrien	if (lp->limconst != RLIMIT_CPU)
212659243Sobrien	    goto badscal;
212759243Sobrien	limtail(cp, "hours");
212859243Sobrien	f *= 3600.0;
212959243Sobrien	break;
2130231990Smp# endif /* RLIMIT_CPU */
213159243Sobrien    case 'm':
2132231990Smp# ifdef RLIMIT_CPU
213359243Sobrien	if (lp->limconst == RLIMIT_CPU) {
213459243Sobrien	    limtail(cp, "minutes");
213559243Sobrien	    f *= 60.0;
213659243Sobrien	    break;
213759243Sobrien	}
2138231990Smp# endif /* RLIMIT_CPU */
2139231990Smp	limtail2(cp, "megabytes", "mbytes");
214059243Sobrien	f *= 1024.0 * 1024.0;
214159243Sobrien	break;
2142231990Smp# ifdef RLIMIT_CPU
214359243Sobrien    case 's':
214459243Sobrien	if (lp->limconst != RLIMIT_CPU)
214559243Sobrien	    goto badscal;
214659243Sobrien	limtail(cp, "seconds");
214759243Sobrien	break;
214859243Sobrien# endif /* RLIMIT_CPU */
2149231990Smp    case 'G':
2150231990Smp	*cp = 'g';
2151231990Smp	/*FALLTHROUGH*/
2152231990Smp    case 'g':
2153231990Smp# ifdef RLIMIT_CPU
2154231990Smp	if (lp->limconst == RLIMIT_CPU)
2155231990Smp	    goto badscal;
2156231990Smp# endif /* RLIMIT_CPU */
2157231990Smp	limtail2(cp, "gigabytes", "gbytes");
2158231990Smp	f *= 1024.0 * 1024.0 * 1024.0;
2159231990Smp	break;
216059243Sobrien    case 'M':
216159243Sobrien# ifdef RLIMIT_CPU
216259243Sobrien	if (lp->limconst == RLIMIT_CPU)
216359243Sobrien	    goto badscal;
216459243Sobrien# endif /* RLIMIT_CPU */
216559243Sobrien	*cp = 'm';
2166231990Smp	limtail2(cp, "megabytes", "mbytes");
216759243Sobrien	f *= 1024.0 * 1024.0;
216859243Sobrien	break;
216959243Sobrien    case 'k':
217059243Sobrien# ifdef RLIMIT_CPU
217159243Sobrien	if (lp->limconst == RLIMIT_CPU)
217259243Sobrien	    goto badscal;
217359243Sobrien# endif /* RLIMIT_CPU */
2174231990Smp	limtail2(cp, "kilobytes", "kbytes");
217559243Sobrien	f *= 1024.0;
217659243Sobrien	break;
217759243Sobrien    case 'b':
217859243Sobrien# ifdef RLIMIT_CPU
217959243Sobrien	if (lp->limconst == RLIMIT_CPU)
218059243Sobrien	    goto badscal;
218159243Sobrien# endif /* RLIMIT_CPU */
218259243Sobrien	limtail(cp, "blocks");
218359243Sobrien	f *= 512.0;
218459243Sobrien	break;
218559243Sobrien    case 'u':
218659243Sobrien	limtail(cp, "unlimited");
218759243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
218859243Sobrien    default:
218959243Sobrien# ifdef RLIMIT_CPU
219059243Sobrienbadscal:
219159243Sobrien# endif /* RLIMIT_CPU */
219259243Sobrien	stderror(ERR_NAME | ERR_SCALEF);
219359243Sobrien    }
219459243Sobrien# ifdef convex
219559243Sobrien    return f == 0.0 ? (RLIM_TYPE) 0 : restrict_limit((f + 0.5));
219659243Sobrien# else
219759243Sobrien    f += 0.5;
2198231990Smp    if (f > (float) ((RLIM_TYPE) RLIM_INFINITY))
219959243Sobrien	return ((RLIM_TYPE) RLIM_INFINITY);
220059243Sobrien    else
220159243Sobrien	return ((RLIM_TYPE) f);
220259243Sobrien# endif /* convex */
220359243Sobrien}
220459243Sobrien
2205231990Smpstatic int
2206231990Smpstrtail(Char *cp, const char *str)
2207231990Smp{
2208231990Smp    while (*cp && *cp == (Char)*str)
2209231990Smp	cp++, str++;
2210231990Smp    return (*cp != '\0');
2211231990Smp}
2212231990Smp
221359243Sobrienstatic void
2214167465Smplimtail(Char *cp, const char *str)
221559243Sobrien{
2216231990Smp    if (strtail(cp, str))
2217231990Smp	stderror(ERR_BADSCALE, str);
2218231990Smp}
221961515Sobrien
2220231990Smpstatic void
2221231990Smplimtail2(Char *cp, const char *str1, const char *str2)
2222231990Smp{
2223231990Smp    if (strtail(cp, str1) && strtail(cp, str2))
2224231990Smp	stderror(ERR_BADSCALE, str1);
222559243Sobrien}
222659243Sobrien
222759243Sobrien/*ARGSUSED*/
222859243Sobrienstatic void
2229167465Smpplim(struct limits *lp, int hard)
223059243Sobrien{
223159243Sobrien# ifdef BSDLIMIT
223259243Sobrien    struct rlimit rlim;
223359243Sobrien# endif /* BSDLIMIT */
223459243Sobrien    RLIM_TYPE limit;
2235145479Smp    int     xdiv = lp->limdiv;
223659243Sobrien
2237131962Smp    xprintf("%-13.13s", lp->limname);
223859243Sobrien
223959243Sobrien# ifndef BSDLIMIT
224059243Sobrien    limit = ulimit(lp->limconst, 0);
224159243Sobrien#  ifdef aiws
224259243Sobrien    if (lp->limconst == RLIMIT_DATA)
224359243Sobrien	limit -= 0x20000000;
224459243Sobrien#  endif /* aiws */
224559243Sobrien# else /* BSDLIMIT */
224659243Sobrien    (void) getrlimit(lp->limconst, &rlim);
224759243Sobrien    limit = hard ? rlim.rlim_max : rlim.rlim_cur;
224859243Sobrien# endif /* BSDLIMIT */
224959243Sobrien
225059243Sobrien# if !defined(BSDLIMIT) || defined(FILESIZE512)
225159243Sobrien    /*
225259243Sobrien     * Christos: filesize comes in 512 blocks. we divide by 2 to get 1024
225359243Sobrien     * blocks. Note we cannot pre-multiply cause we might overflow (A/UX)
225459243Sobrien     */
225559243Sobrien    if (lp->limconst == RLIMIT_FSIZE) {
225659243Sobrien	if (limit >= (RLIM_INFINITY / 512))
225759243Sobrien	    limit = RLIM_INFINITY;
225859243Sobrien	else
2259145479Smp	    xdiv = (xdiv == 1024 ? 2 : 1);
226059243Sobrien    }
226159243Sobrien# endif /* !BSDLIMIT || FILESIZE512 */
226259243Sobrien
226359243Sobrien    if (limit == RLIM_INFINITY)
226459243Sobrien	xprintf("unlimited");
226559243Sobrien    else
2266145479Smp# if defined(RLIMIT_CPU) && defined(_OSD_POSIX)
2267145479Smp    if (lp->limconst == RLIMIT_CPU &&
2268145479Smp        (unsigned long)limit >= 0x7ffffffdUL)
2269145479Smp	xprintf("unlimited");
2270145479Smp    else
2271145479Smp# endif
227259243Sobrien# ifdef RLIMIT_CPU
227359243Sobrien    if (lp->limconst == RLIMIT_CPU)
2274167465Smp	psecs(limit);
227559243Sobrien    else
227659243Sobrien# endif /* RLIMIT_CPU */
2277145479Smp	xprintf("%ld %s", (long) (limit / xdiv), lp->limscale);
227859243Sobrien    xputchar('\n');
227959243Sobrien}
228059243Sobrien
228159243Sobrien/*ARGSUSED*/
228259243Sobrienvoid
2283167465Smpdounlimit(Char **v, struct command *c)
228459243Sobrien{
2285145479Smp    struct limits *lp;
228659243Sobrien    int    lerr = 0;
228759243Sobrien    int    hard = 0;
228859243Sobrien    int	   force = 0;
228959243Sobrien
229059243Sobrien    USE(c);
229159243Sobrien    while (*++v && **v == '-') {
229259243Sobrien	Char   *vp = *v;
229359243Sobrien	while (*++vp)
229459243Sobrien	    switch (*vp) {
229559243Sobrien	    case 'f':
229659243Sobrien		force = 1;
229759243Sobrien		break;
229859243Sobrien	    case 'h':
229959243Sobrien		hard = 1;
230059243Sobrien		break;
230159243Sobrien	    default:
230259243Sobrien		stderror(ERR_ULIMUS);
230359243Sobrien		break;
230459243Sobrien	    }
230559243Sobrien    }
230659243Sobrien
230759243Sobrien    if (*v == 0) {
230859243Sobrien	for (lp = limits; lp->limconst >= 0; lp++)
230959243Sobrien	    if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0)
231059243Sobrien		lerr++;
231159243Sobrien	if (!force && lerr)
231259243Sobrien	    stderror(ERR_SILENT);
231359243Sobrien	return;
231459243Sobrien    }
231559243Sobrien    while (*v) {
231659243Sobrien	lp = findlim(*v++);
231759243Sobrien	if (setlim(lp, hard, (RLIM_TYPE) RLIM_INFINITY) < 0 && !force)
231859243Sobrien	    stderror(ERR_SILENT);
231959243Sobrien    }
232059243Sobrien}
232159243Sobrien
232259243Sobrienstatic int
2323167465Smpsetlim(struct limits *lp, int hard, RLIM_TYPE limit)
232459243Sobrien{
232559243Sobrien# ifdef BSDLIMIT
232659243Sobrien    struct rlimit rlim;
232759243Sobrien
232859243Sobrien    (void) getrlimit(lp->limconst, &rlim);
232959243Sobrien
233059243Sobrien#  ifdef FILESIZE512
233159243Sobrien    /* Even though hpux has setrlimit(), it expects fsize in 512 byte blocks */
233259243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
233359243Sobrien	limit /= 512;
233459243Sobrien#  endif /* FILESIZE512 */
233559243Sobrien    if (hard)
233659243Sobrien	rlim.rlim_max = limit;
233759243Sobrien    else if (limit == RLIM_INFINITY && euid != 0)
233859243Sobrien	rlim.rlim_cur = rlim.rlim_max;
233959243Sobrien    else
234059243Sobrien	rlim.rlim_cur = limit;
234159243Sobrien
2342100616Smp    if (rlim.rlim_cur > rlim.rlim_max)
2343100616Smp	rlim.rlim_max = rlim.rlim_cur;
2344100616Smp
234559243Sobrien    if (setrlimit(lp->limconst, &rlim) < 0) {
234659243Sobrien# else /* BSDLIMIT */
234759243Sobrien    if (limit != RLIM_INFINITY && lp->limconst == RLIMIT_FSIZE)
234859243Sobrien	limit /= 512;
234959243Sobrien# ifdef aiws
235059243Sobrien    if (lp->limconst == RLIMIT_DATA)
235159243Sobrien	limit += 0x20000000;
235259243Sobrien# endif /* aiws */
235359243Sobrien    if (ulimit(toset(lp->limconst), limit) < 0) {
235459243Sobrien# endif /* BSDLIMIT */
2355145479Smp        int err;
2356145479Smp        char *op, *type;
2357145479Smp
2358145479Smp	err = errno;
2359145479Smp	op = strsave(limit == RLIM_INFINITY ? CGETS(15, 2, "remove") :
2360145479Smp		     	CGETS(15, 3, "set"));
2361167465Smp	cleanup_push(op, xfree);
2362145479Smp	type = strsave(hard ? CGETS(15, 4, " hard") : "");
2363167465Smp	cleanup_push(type, xfree);
2364100616Smp	xprintf(CGETS(15, 1, "%s: %s: Can't %s%s limit (%s)\n"), bname,
2365145479Smp	    lp->limname, op, type, strerror(err));
2366167465Smp	cleanup_until(op);
236759243Sobrien	return (-1);
236859243Sobrien    }
236959243Sobrien    return (0);
237059243Sobrien}
237159243Sobrien
237259243Sobrien#endif /* !HAVENOLIMIT */
237359243Sobrien
237459243Sobrien/*ARGSUSED*/
237559243Sobrienvoid
2376167465Smpdosuspend(Char **v, struct command *c)
237759243Sobrien{
237859243Sobrien#ifdef BSDJOBS
2379167465Smp    struct sigaction old;
238059243Sobrien#endif /* BSDJOBS */
2381195609Smp
238259243Sobrien    USE(c);
238359243Sobrien    USE(v);
238459243Sobrien
238559243Sobrien    if (loginsh)
238659243Sobrien	stderror(ERR_SUSPLOG);
238759243Sobrien    untty();
238859243Sobrien
238959243Sobrien#ifdef BSDJOBS
2390167465Smp    sigaction(SIGTSTP, NULL, &old);
2391167465Smp    signal(SIGTSTP, SIG_DFL);
239259243Sobrien    (void) kill(0, SIGTSTP);
239359243Sobrien    /* the shell stops here */
2394167465Smp    sigaction(SIGTSTP, &old, NULL);
239559243Sobrien#else /* !BSDJOBS */
239659243Sobrien    stderror(ERR_JOBCONTROL);
239759243Sobrien#endif /* BSDJOBS */
239859243Sobrien
239959243Sobrien#ifdef BSDJOBS
240059243Sobrien    if (tpgrp != -1) {
2401195609Smp	if (grabpgrp(FSHTTY, opgrp) == -1)
2402131962Smp	    stderror(ERR_SYSTEM, "tcgetpgrp", strerror(errno));
240359243Sobrien	(void) setpgid(0, shpgrp);
240459243Sobrien	(void) tcsetpgrp(FSHTTY, shpgrp);
240559243Sobrien    }
240659243Sobrien#endif /* BSDJOBS */
240759243Sobrien    (void) setdisc(FSHTTY);
240859243Sobrien}
240959243Sobrien
241059243Sobrien/* This is the dreaded EVAL built-in.
241159243Sobrien *   If you don't fiddle with file descriptors, and reset didfds,
241259243Sobrien *   this command will either ignore redirection inside or outside
241359243Sobrien *   its arguments, e.g. eval "date >x"  vs.  eval "date" >x
241459243Sobrien *   The stuff here seems to work, but I did it by trial and error rather
241559243Sobrien *   than really knowing what was going on.  If tpgrp is zero, we are
241659243Sobrien *   probably a background eval, e.g. "eval date &", and we want to
241759243Sobrien *   make sure that any processes we start stay in our pgrp.
241859243Sobrien *   This is also the case for "time eval date" -- stay in same pgrp.
241959243Sobrien *   Otherwise, under stty tostop, processes will stop in the wrong
242059243Sobrien *   pgrp, with no way for the shell to get them going again.  -IAN!
242159243Sobrien */
242259243Sobrien
2423167465Smpstruct doeval_state
242459243Sobrien{
2425167465Smp    Char **evalvec, *evalp;
2426167465Smp    int didfds;
242759243Sobrien#ifndef CLOSE_ON_EXEC
2428167465Smp    int didcch;
2429167465Smp#endif
2430167465Smp    int saveIN, saveOUT, saveDIAG;
2431167465Smp    int SHIN, SHOUT, SHDIAG;
2432167465Smp};
243359243Sobrien
2434167465Smpstatic void
2435167465Smpdoeval_cleanup(void *xstate)
2436167465Smp{
2437167465Smp    struct doeval_state *state;
2438167465Smp
2439167465Smp    state = xstate;
2440167465Smp    evalvec = state->evalvec;
2441167465Smp    evalp = state->evalp;
2442167465Smp    doneinp = 0;
244359243Sobrien#ifndef CLOSE_ON_EXEC
2444167465Smp    didcch = state->didcch;
244559243Sobrien#endif /* CLOSE_ON_EXEC */
2446167465Smp    didfds = state->didfds;
2447316957Sdchagin    if (state->saveIN != SHIN)
2448316957Sdchagin	xclose(SHIN);
2449316957Sdchagin    if (state->saveOUT != SHOUT)
2450316957Sdchagin	xclose(SHOUT);
2451316957Sdchagin    if (state->saveDIAG != SHDIAG)
2452316957Sdchagin	xclose(SHDIAG);
2453167465Smp    close_on_exec(SHIN = dmove(state->saveIN, state->SHIN), 1);
2454167465Smp    close_on_exec(SHOUT = dmove(state->saveOUT, state->SHOUT), 1);
2455167465Smp    close_on_exec(SHDIAG = dmove(state->saveDIAG, state->SHDIAG), 1);
2456316957Sdchagin    if (didfds) {
2457316957Sdchagin	close_on_exec(dcopy(SHIN, 0), 1);
2458316957Sdchagin	close_on_exec(dcopy(SHOUT, 1), 1);
2459316957Sdchagin	close_on_exec(dcopy(SHDIAG, 2), 1);
2460316957Sdchagin    }
2461167465Smp}
246259243Sobrien
2463195609Smpstatic Char **Ggv;
2464167465Smp/*ARGSUSED*/
2465167465Smpvoid
2466167465Smpdoeval(Char **v, struct command *c)
2467167465Smp{
2468167465Smp    struct doeval_state state;
2469195609Smp    int gflag, my_reenter;
2470167465Smp    Char **gv;
2471195609Smp    jmp_buf_t osetexit;
247259243Sobrien
2473167465Smp    USE(c);
2474167465Smp    v++;
2475167465Smp    if (*v == 0)
247659243Sobrien	return;
2477167465Smp    gflag = tglob(v);
247859243Sobrien    if (gflag) {
2479167465Smp	gv = v = globall(v, gflag);
2480167465Smp	if (v == 0)
248159243Sobrien	    stderror(ERR_NOMATCH);
2482167465Smp	cleanup_push(gv, blk_cleanup);
2483167465Smp	v = copyblk(v);
248459243Sobrien    }
248559243Sobrien    else {
248659243Sobrien	gv = NULL;
2487167465Smp	v = copyblk(v);
2488167465Smp	trim(v);
248959243Sobrien    }
249059243Sobrien
2491195609Smp    Ggv = gv;
2492167465Smp    state.evalvec = evalvec;
2493167465Smp    state.evalp = evalp;
2494167465Smp    state.didfds = didfds;
249559243Sobrien#ifndef CLOSE_ON_EXEC
2496167465Smp    state.didcch = didcch;
249759243Sobrien#endif /* CLOSE_ON_EXEC */
2498167465Smp    state.SHIN = SHIN;
2499167465Smp    state.SHOUT = SHOUT;
2500167465Smp    state.SHDIAG = SHDIAG;
250159243Sobrien
2502167465Smp    (void)close_on_exec(state.saveIN = dcopy(SHIN, -1), 1);
2503167465Smp    (void)close_on_exec(state.saveOUT = dcopy(SHOUT, -1), 1);
2504167465Smp    (void)close_on_exec(state.saveDIAG = dcopy(SHDIAG, -1), 1);
2505167465Smp
2506167465Smp    cleanup_push(&state, doeval_cleanup);
2507167465Smp
2508195609Smp    getexit(osetexit);
2509195609Smp
2510195609Smp    /* PWP: setjmp/longjmp bugfix for optimizing compilers */
2511195609Smp#ifdef cray
2512195609Smp    my_reenter = 1;             /* assume non-zero return val */
2513195609Smp    if (setexit() == 0) {
2514195609Smp	my_reenter = 0;         /* Oh well, we were wrong */
2515195609Smp#else /* !cray */
2516195609Smp    if ((my_reenter = setexit()) == 0) {
2517195609Smp#endif /* cray */
2518195609Smp	evalvec = v;
2519195609Smp	evalp = 0;
2520195609Smp	(void)close_on_exec(SHIN = dcopy(0, -1), 1);
2521195609Smp	(void)close_on_exec(SHOUT = dcopy(1, -1), 1);
2522195609Smp	(void)close_on_exec(SHDIAG = dcopy(2, -1), 1);
252359243Sobrien#ifndef CLOSE_ON_EXEC
2524195609Smp	didcch = 0;
252559243Sobrien#endif /* CLOSE_ON_EXEC */
2526195609Smp	didfds = 0;
2527195609Smp	gv = Ggv;
2528195609Smp	process(0);
2529195609Smp	Ggv = gv;
2530195609Smp    }
253159243Sobrien
2532195609Smp    if (my_reenter == 0) {
2533195609Smp	cleanup_until(&state);
2534195609Smp	if (Ggv)
2535195609Smp	    cleanup_until(Ggv);
2536195609Smp    }
2537167465Smp
2538195609Smp    resexit(osetexit);
2539195609Smp    if (my_reenter)
2540195609Smp	stderror(ERR_SILENT);
254159243Sobrien}
254259243Sobrien
254359243Sobrien/*************************************************************************/
254459243Sobrien/* print list of builtin commands */
254559243Sobrien
2546167465Smpstatic void
2547167465Smplbuffed_cleanup (void *dummy)
2548167465Smp{
2549167465Smp    USE(dummy);
2550167465Smp    lbuffed = 1;
2551167465Smp}
2552167465Smp
255359243Sobrien/*ARGSUSED*/
255459243Sobrienvoid
2555167465Smpdobuiltins(Char **v, struct command *c)
255659243Sobrien{
255759243Sobrien    /* would use print_by_column() in tw.parse.c but that assumes
255859243Sobrien     * we have an array of Char * to pass.. (sg)
255959243Sobrien     */
2560167465Smp    const struct biltins *b;
2561145479Smp    int row, col, columns, rows;
256259243Sobrien    unsigned int w, maxwidth;
256359243Sobrien
256459243Sobrien    USE(c);
256559243Sobrien    USE(v);
256659243Sobrien    lbuffed = 0;		/* turn off line buffering */
2567167465Smp    cleanup_push(&lbuffed, lbuffed_cleanup);
256859243Sobrien
256959243Sobrien    /* find widest string */
257059243Sobrien    for (maxwidth = 0, b = bfunc; b < &bfunc[nbfunc]; ++b)
257159243Sobrien	maxwidth = max(maxwidth, strlen(b->bname));
257259243Sobrien    ++maxwidth;					/* for space */
257359243Sobrien
257459243Sobrien    columns = (TermH + 1) / maxwidth;	/* PWP: terminal size change */
257559243Sobrien    if (!columns)
257659243Sobrien	columns = 1;
257759243Sobrien    rows = (nbfunc + (columns - 1)) / columns;
257859243Sobrien
257959243Sobrien    for (b = bfunc, row = 0; row < rows; row++) {
258059243Sobrien	for (col = 0; col < columns; col++) {
258159243Sobrien	    if (b < &bfunc[nbfunc]) {
258259243Sobrien		w = strlen(b->bname);
258359243Sobrien		xprintf("%s", b->bname);
258459243Sobrien		if (col < (columns - 1))	/* Not last column? */
258559243Sobrien		    for (; w < maxwidth; w++)
258659243Sobrien			xputchar(' ');
258759243Sobrien		++b;
258859243Sobrien	    }
258959243Sobrien	}
259059243Sobrien	if (row < (rows - 1)) {
259159243Sobrien	    if (Tty_raw_mode)
259259243Sobrien		xputchar('\r');
259359243Sobrien	    xputchar('\n');
259459243Sobrien	}
259559243Sobrien    }
259669408Sache#ifdef WINNT_NATIVE
259759243Sobrien    nt_print_builtins(maxwidth);
259859243Sobrien#else
259959243Sobrien    if (Tty_raw_mode)
260059243Sobrien	xputchar('\r');
260159243Sobrien    xputchar('\n');
260269408Sache#endif /* WINNT_NATIVE */
260359243Sobrien
2604167465Smp    cleanup_until(&lbuffed);		/* turn back on line buffering */
260559243Sobrien    flush();
260659243Sobrien}
260759243Sobrien
2608145479Smp#ifdef NLS_CATALOGS
2609145479Smpchar *
2610167465Smpxcatgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2611145479Smp{
2612167465Smp    char *res;
2613167465Smp
2614167465Smp    errno = 0;
2615167465Smp    while ((res = catgets(ctd, set_id, msg_id, s)) == s && errno == EINTR) {
2616167465Smp	handle_pending_signals();
2617167465Smp	errno = 0;
2618167465Smp    }
2619167465Smp    return res;
2620167465Smp}
2621167465Smp
2622167465Smp
2623167465Smp# if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2624167465Smpchar *
2625167465Smpiconv_catgets(nl_catd ctd, int set_id, int msg_id, const char *s)
2626167465Smp{
2627145479Smp    static char *buf = NULL;
2628145479Smp    static size_t buf_size = 0;
2629145479Smp
2630145479Smp    char *orig, *dest, *p;
2631167465Smp    ICONV_CONST char *src;
2632145479Smp    size_t src_size, dest_size;
2633145479Smp
2634167465Smp    orig = xcatgets(ctd, set_id, msg_id, s);
2635145479Smp    if (catgets_iconv == (iconv_t)-1 || orig == s)
2636145479Smp        return orig;
2637145479Smp    src = orig;
2638145479Smp    src_size = strlen(src) + 1;
2639145479Smp    if (buf == NULL && (buf = xmalloc(buf_size = src_size + 32)) == NULL)
2640145479Smp	return orig;
2641145479Smp    dest = buf;
2642145479Smp    while (src_size != 0) {
2643145479Smp        dest_size = buf + buf_size - dest;
2644145479Smp	if (iconv(catgets_iconv, &src, &src_size, &dest, &dest_size)
2645145479Smp	    == (size_t)-1) {
2646145479Smp	    switch (errno) {
2647145479Smp	    case E2BIG:
2648145479Smp		if ((p = xrealloc(buf, buf_size * 2)) == NULL)
2649145479Smp		    return orig;
2650145479Smp		buf_size *= 2;
2651145479Smp		dest = p + (dest - buf);
2652145479Smp		buf = p;
2653145479Smp		break;
2654145479Smp
2655145479Smp	    case EILSEQ: case EINVAL: default:
2656145479Smp		return orig;
2657145479Smp	    }
2658145479Smp	}
2659145479Smp    }
2660145479Smp    return buf;
2661145479Smp}
2662167465Smp# endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2663167465Smp#endif /* NLS_CATALOGS */
2664145479Smp
266559243Sobrienvoid
2666167465Smpnlsinit(void)
266759243Sobrien{
266859243Sobrien#ifdef NLS_CATALOGS
2669167465Smp    static const char default_catalog[] = "tcsh";
267069408Sache
2671167465Smp    char *catalog = (char *)(intptr_t)default_catalog;
2672167465Smp
267369408Sache    if (adrof(STRcatalog) != NULL)
2674167465Smp	catalog = xasprintf("tcsh.%s", short2str(varval(STRcatalog)));
2675231990Smp#ifdef NL_CAT_LOCALE /* POSIX-compliant. */
2676231990Smp    /*
2677231990Smp     * Check if LC_MESSAGES is set in the environment and use it, if so.
2678231990Smp     * If not, fall back to the setting of LANG.
2679231990Smp     */
2680231990Smp    catd = catopen(catalog, tgetenv(STRLC_MESSAGES) ? NL_CAT_LOCALE : 0);
2681231990Smp#else /* pre-POSIX */
2682231990Smp# ifndef MCLoadBySet
2683231990Smp#  define MCLoadBySet 0
2684231990Smp#  endif
268569408Sache    catd = catopen(catalog, MCLoadBySet);
2686231990Smp#endif
2687167465Smp    if (catalog != default_catalog)
2688167465Smp	xfree(catalog);
2689167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2690167465Smp    /* xcatgets (), not CGETS, the charset name should be in ASCII anyway. */
2691145479Smp    catgets_iconv = iconv_open (nl_langinfo (CODESET),
2692231990Smp				xcatgets(catd, 255, 1, "UTF-8"));
2693167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
269469408Sache#endif /* NLS_CATALOGS */
269569408Sache#ifdef WINNT_NATIVE
269659243Sobrien    nls_dll_init();
269769408Sache#endif /* WINNT_NATIVE */
269859243Sobrien    errinit();		/* init the errorlist in correct locale */
269959243Sobrien    mesginit();		/* init the messages for signals */
270059243Sobrien    dateinit();		/* init the messages for dates */
270159243Sobrien    editinit();		/* init the editor messages */
270259243Sobrien    terminit();		/* init the termcap messages */
270359243Sobrien}
2704145479Smp
2705145479Smpvoid
2706167465Smpnlsclose(void)
2707145479Smp{
2708145479Smp#ifdef NLS_CATALOGS
2709167465Smp#if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO)
2710145479Smp    if (catgets_iconv != (iconv_t)-1) {
2711145479Smp	iconv_close(catgets_iconv);
2712145479Smp	catgets_iconv = (iconv_t)-1;
2713145479Smp    }
2714167465Smp#endif /* HAVE_ICONV && HAVE_NL_LANGINFO */
2715167465Smp    if (catd != (nl_catd)-1) {
2716167465Smp	/*
2717167465Smp	 * catclose can call other functions which can call longjmp
2718167465Smp	 * making us re-enter this code. Prevent infinite recursion
2719167465Smp	 * by resetting catd. Problem reported and solved by:
2720167465Smp	 * Gerhard Niklasch
2721167465Smp	 */
2722167465Smp	nl_catd oldcatd = catd;
2723167465Smp	catd = (nl_catd)-1;
2724167465Smp	while (catclose(oldcatd) == -1 && errno == EINTR)
2725167465Smp	    handle_pending_signals();
2726167465Smp    }
2727145479Smp#endif /* NLS_CATALOGS */
2728145479Smp}
2729316957Sdchagin
2730316957Sdchaginint
2731316957SdchagingetYN(const char *prompt)
2732316957Sdchagin{
2733316957Sdchagin    int doit;
2734316957Sdchagin    char c;
2735316957Sdchagin
2736316957Sdchagin    xprintf("%s", prompt);
2737316957Sdchagin    flush();
2738316957Sdchagin    (void) force_read(SHIN, &c, sizeof(c));
2739316957Sdchagin    /*
2740316957Sdchagin     * Perhaps we should use the yesexpr from the
2741316957Sdchagin     * actual locale
2742316957Sdchagin     */
2743316957Sdchagin    doit = (strchr(CGETS(22, 14, "Yy"), c) != NULL);
2744316957Sdchagin    while (c != '\n' && force_read(SHIN, &c, sizeof(c)) == sizeof(c))
2745316957Sdchagin	continue;
2746316957Sdchagin    return doit;
2747316957Sdchagin}
2748