root/usr/src/cmd/csh/sh.func.c
/*
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*      Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T     */
/*        All Rights Reserved   */

/*
 * Copyright (c) 1980 Regents of the University of California.
 * All rights reserved.  The Berkeley Software License Agreement
 * specifies the terms and conditions for redistribution.
 */

#include "sh.h"
#include <locale.h>     /* For LC_ALL */
#include "sh.tconst.h"
#include <sys/types.h>
#include <stdlib.h>

/*
 * N.B.: Some of the limits change from SunOS 4.x to SunOS 5.0.  In
 * particular, RLIMIT_RSS is gone and RLIMIT_VMEM is new.  Beware of consusing
 * the keywords that the command prints for these two.  The old one was
 * "memoryuse" and the new one is "memorysize".  Note also that a given limit
 * doesn't necessarily appear in the same position in the two releases.
 */
struct limits {
        int     limconst;
        tchar *limname;
        int     limdiv;
        tchar *limscale;
} limits[] = {
        RLIMIT_CPU,     S_cputime,      /* "cputime" */
                1,      S_seconds,      /* "seconds" */
        RLIMIT_FSIZE,   S_filesize,     /* "filesize" */
                1024,   S_kbytes,       /* "kbytes" */
        RLIMIT_DATA,    S_datasize,     /* "datasize" */
                1024,   S_kbytes,       /* "kbytes" */
        RLIMIT_STACK,   S_stacksize,    /* "stacksize" */
                1024,   S_kbytes,       /* "kbytes" */
        RLIMIT_CORE,    S_coredumpsize, /* "coredumpsize" */
                1024,   S_kbytes,       /* "kbytes" */
        RLIMIT_NOFILE,  S_descriptors,  /* "descriptors" */
                1,      S_,             /* "" */
        RLIMIT_VMEM,    S_memorysize,   /* "memorysize" */
                1024,   S_kbytes,       /* "kbytes" */
        -1,             0,
};

struct Bin      B;
struct whyle    *whyles;
bool            chkstop;
bool            doneinp;
bool            intty;
bool            setintr;
int             shpgrp;
int             opgrp;
off_t           lineloc;
tchar           *evalp;
tchar           **evalvec;
tchar           *gointr;


static int getval(struct limits *lp, tchar **v, rlim_t *);
void islogin(void);
int dolabel(void);
void reexecute(struct command *kp);
void preread_(void);
void doagain(void);
void toend(void);
void wfree(void);
void echo(tchar sep, tchar **v);
void local_setenv(tchar *name, tchar *val);
void local_unsetenv(tchar *name);
void limtail(tchar *cp, tchar *str0);
void plim(struct limits *lp, tchar hard);
void search();

#define BUFSZ   1028

/*
 * C shell
 */

struct biltins *
isbfunc(struct command *t)
{
        tchar *cp = t->t_dcom[0];
        struct biltins *bp, *bp1, *bp2;
        int dofg1(), dobg1();

        static struct biltins label = { S_, dolabel, 0, 0 };
        static struct biltins foregnd = { S_Pjob, dofg1, 0, 0 };
        static struct biltins backgnd = { S_PjobAND, dobg1, 0, 0 };
#ifdef TRACE
        tprintf("TRACE- isbfunc()\n");
#endif
        if (lastchr(cp) == ':') {
                label.bname = cp;
                return (&label);
        }
        if (*cp == '%') {
                if (t->t_dflg & FAND) {
                        t->t_dflg &= ~FAND;
                        backgnd.bname = cp;
                        return (&backgnd);
                }
                foregnd.bname = cp;
                return (&foregnd);
        }
        /*
         * Binary search
         * Bp1 is the beginning of the current search range.
         * Bp2 is one past the end.
         */
        for (bp1 = bfunc, bp2 = bfunc + nbfunc; bp1 < bp2; ) {
                int i;

                bp = bp1 + (bp2 - bp1 >> 1);
                if ((i = *cp - *bp->bname) == 0 &&
                    (i = strcmp_(cp, bp->bname)) == 0) {
                        return (bp);
                }
                if (i < 0) {
                        bp2 = bp;
                } else {
                        bp1 = bp + 1;
                }
        }
        return (0);
}

void
func(struct command *t, struct biltins *bp)
{
        int i;

#ifdef TRACE
        tprintf("TRACE- func()\n");
#endif
        xechoit(t->t_dcom);
        setname(bp->bname);
        i = blklen(t->t_dcom) - 1;
        if (i < bp->minargs) {
                bferr("Too few arguments");
        }
        if (i > bp->maxargs) {
                bferr("Too many arguments");
        }
        (*bp->bfunct)(t->t_dcom, t);
}

int
dolabel(void)
{
#ifdef TRACE
        tprintf("TRACE- dolabel()\n");
#endif
        return (0);
}

void
doonintr(tchar **v)
{
        tchar *cp;
        tchar *vv = v[1];

#ifdef TRACE
        tprintf("TRACE- doonintr()\n");
#endif
        if (parintr == SIG_IGN) {
                return;
        }
        if (setintr && intty) {
                bferr("Can't from terminal");
        }
        cp = gointr, gointr = 0, xfree(cp);
        if (vv == 0) {
                if (setintr) {
                        (void) sigblock(sigmask(SIGINT));
                } else {
                        (void) signal(SIGINT, SIG_DFL);
                }
                gointr = 0;
        } else if (eq((vv = strip(vv)), S_MINUS)) {
                (void) signal(SIGINT, SIG_IGN);
                gointr = S_MINUS;
        } else {
                gointr = savestr(vv);
                (void) signal(SIGINT, pintr);
        }
}

void
donohup(void)
{

#ifdef TRACE
        tprintf("TRACE- donohup()\n");
#endif
        if (intty) {
                bferr("Can't from terminal");
        }
        if (setintr == 0) {
                (void) signal(SIGHUP, SIG_IGN);
#ifdef CC
                submit(getpid());
#endif
        }
}

void
dozip(void)
{
        ;
}

void
prvars(void)
{
#ifdef TRACE
        tprintf("TRACE- prvars()\n");
#endif

        plist(&shvhed);
}

void
doalias(tchar **v)
{
        struct varent *vp;
        tchar *p;

#ifdef TRACE
        tprintf("TRACE- doalias()\n");
#endif
        v++;
        p = *v++;
        if (p == 0) {
                plist(&aliases);
        } else if (*v == 0) {
                vp = adrof1(strip(p), &aliases);
                if (vp) {
                        blkpr(vp->vec), printf("\n");
                }
        } else {
                if (eq(p, S_alias) ||
                    eq(p, S_unalias)) {
                        setname(p);
                        bferr("Too dangerous to alias that");
                }
                set1(strip(p), saveblk(v), &aliases);
        }
}

void
unalias(tchar **v)
{

#ifdef TRACE
        tprintf("TRACE- unalias()\n");
#endif
        unset1(v, &aliases);
}

void
dologout(void)
{

#ifdef TRACE
        tprintf("TRACE- dologout()\n");
#endif
        islogin();
        goodbye();
}

void
dologin(tchar **v)
{

        char *v_;       /* work */
#ifdef TRACE
        tprintf("TRACE- dologin()\n");
#endif
        islogin();
        rechist();
        (void) signal(SIGTERM, parterm);
        if (v[1] != NULL) {
                v_ = tstostr(NULL, v[1]);       /* No need to free */
        } else {
                v_ = 0;
        }
        execl("/bin/login", "login", v_, 0);
        untty();
        exit(1);
}

#ifdef NEWGRP
void
donewgrp(tchar **v)
{

        char *v_;       /* work */
#ifdef TRACE
        tprintf("TRACE- donewgrp()\n");
#endif
        if (chkstop == 0 && setintr) {
                panystop(0);
        }
        (void) signal(SIGTERM, parterm);

        if (v[1] != NULL) {
                v_ = tstostr(NOSTR, v[1]);      /* No need to free */
        } else {
                v_ = 0;
        }
        execl("/bin/newgrp", "newgrp", v_, 0);
        execl("/usr/bin/newgrp", "newgrp", v_, 0);
        untty();
        exit(1);
}
#endif

void
islogin(void)
{

#ifdef TRACE
        tprintf("TRACE- islogin()\n");
#endif
        if (chkstop == 0 && setintr) {
                panystop(0);
        }
        if (loginsh) {
                return;
        }
        error("Not login shell");
}

void
doif(tchar **v, struct command *kp)
{
        int i;
        tchar **vv;

#ifdef TRACE
        tprintf("TRACE- doif()\n");
#endif
        v++;
        i = exp(&v);
        vv = v;
        if (*vv == NOSTR) {
                bferr("Empty if");
        }
        if (eq(*vv, S_then)) {
                if (*++vv) {
                        bferr("Improper then");
                }
                setname(S_then);
                /*
                 * If expression was zero, then scan to else,
                 * otherwise just fall into following code.
                 */
                if (!i) {
                        search(ZIF, 0);
                }
                return;
        }
        /*
         * Simple command attached to this if.
         * Left shift the node in this tree, munging it
         * so we can reexecute it.
         */
        if (i) {
                lshift(kp->t_dcom, vv - kp->t_dcom);
                reexecute(kp);
                donefds();
        }
}

/*
 * Reexecute a command, being careful not
 * to redo i/o redirection, which is already set up.
 */
void
reexecute(struct command *kp)
{

#ifdef TRACE
        tprintf("TRACE- reexecute()\n");
#endif
        kp->t_dflg &= FSAVE;
        kp->t_dflg |= FREDO;
        /*
         * If tty is still ours to arbitrate, arbitrate it;
         * otherwise dont even set pgrp's as the jobs would
         * then have no way to get the tty (we can't give it
         * to them, and our parent wouldn't know their pgrp, etc.
         */
        execute(kp, tpgrp > 0 ? tpgrp : -1);
}

void
doelse(void)
{

#ifdef TRACE
        tprintf("TRACE- doelse()\n");
#endif
        search(ZELSE, 0);
}

void
dogoto(tchar **v)
{
        struct whyle *wp;
        tchar *lp;
#ifdef TRACE
        tprintf("TRACE- dogoto()\n");
#endif

        /*
         * While we still can, locate any unknown ends of existing loops.
         * This obscure code is the WORST result of the fact that we
         * don't really parse.
         */
        for (wp = whyles; wp; wp = wp->w_next) {
                if (wp->w_end == 0) {
                        search(ZBREAK, 0);
                        wp->w_end = btell();
                } else {
                        bseek(wp->w_end);
                }
        }
        search(ZGOTO, 0, lp = globone(v[1]));
        xfree(lp);
        /*
         * Eliminate loops which were exited.
         */
        wfree();
}

void
doswitch(tchar **v)
{
        tchar *cp, *lp;

#ifdef TRACE
        tprintf("TRACE- doswitch()\n");
#endif
        v++;
        if (!*v || *(*v++) != '(') {
                goto syntax;
        }
        cp = **v == ')' ? S_ : *v++;
        if (*(*v++) != ')') {
                v--;
        }
        if (*v) {
syntax:
                error("Syntax error");
        }
        search(ZSWITCH, 0, lp = globone(cp));
        xfree(lp);
}

void
dobreak(void)
{

#ifdef TRACE
        tprintf("TRACE- dobreak()\n");
#endif
        if (whyles) {
                toend();
        } else {
                bferr("Not in while/foreach");
        }
}

void
doexit(tchar **v)
{

#ifdef TRACE
        tprintf("TRACE- doexit()\n");
#endif
        if (chkstop == 0) {
                panystop(0);
        }
        /*
         * Don't DEMAND parentheses here either.
         */
        v++;
        if (*v) {
                set(S_status, putn(exp(&v)));
                if (*v) {
                        bferr("Expression syntax");
                }
        }
        btoeof();
        if (intty) {
                (void) close(SHIN);
                unsetfd(SHIN);
        }
}

void
doforeach(tchar **v)
{
        tchar *cp;
        struct whyle *nwp;

#ifdef TRACE
        tprintf("TRACE- doforeach()\n");
#endif
        v++;
        cp = strip(*v);
        while (*cp && alnum(*cp)) {
                cp++;
        }
        if (*cp || strlen_(*v) >= MAX_VAR_LEN || !letter(**v)) {
                bferr("Invalid variable");
        }
        cp = *v++;
        if (v[0][0] != '(' || v[blklen(v) - 1][0] != ')') {
                bferr("Words not ()'ed");
        }
        v++;
        gflag = 0, tglob(v);
        v = glob(v);
        if (v == 0) {
                bferr("No match");
        }
        nwp = (struct whyle *)xcalloc(1, sizeof (*nwp));
        nwp->w_fe = nwp->w_fe0 = v; gargv = 0;
        nwp->w_start = btell();
        nwp->w_fename = savestr(cp);
        nwp->w_next = whyles;
        whyles = nwp;
        /*
         * Pre-read the loop so as to be more
         * comprehensible to a terminal user.
         */
        if (intty) {
                preread_();
        }
        doagain();
}

void
dowhile(tchar **v)
{
        int status;
        bool again = whyles != 0 && whyles->w_start == lineloc &&
            whyles->w_fename == 0;

#ifdef TRACE
        tprintf("TRACE- dowhile()\n");
#endif
        v++;
        /*
         * Implement prereading here also, taking care not to
         * evaluate the expression before the loop has been read up
         * from a terminal.
         */
        if (intty && !again) {
                status = !exp0(&v, 1);
        } else {
                status = !exp(&v);
        }
        if (*v) {
                bferr("Expression syntax");
        }
        if (!again) {
                struct whyle *nwp = (struct whyle *)xcalloc(1, sizeof (*nwp));

                nwp->w_start = lineloc;
                nwp->w_end = 0;
                nwp->w_next = whyles;
                whyles = nwp;
                if (intty) {
                        /*
                         * The tty preread
                         */
                        preread_();
                        doagain();
                        return;
                }
        }
        if (status) {
                /* We ain't gonna loop no more, no more! */
                toend();
        }
}

void
preread_(void)
{
#ifdef TRACE
        tprintf("TRACE- preread()\n");
#endif

        whyles->w_end = -1;
        if (setintr) {
                (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
        }
        search(ZBREAK, 0);
        if (setintr) {
                (void) sigblock(sigmask(SIGINT));
        }
        whyles->w_end = btell();
}

void
doend(void)
{

#ifdef TRACE
        tprintf("TRACE- doend()\n");
#endif
        if (!whyles) {
                bferr("Not in while/foreach");
        }
        whyles->w_end = btell();
        doagain();
}

void
docontin(void)
{
#ifdef TRACE
        tprintf("TRACE- docontin()\n");
#endif

        if (!whyles) {
                bferr("Not in while/foreach");
        }
        doagain();
}

void
doagain(void)
{

#ifdef TRACE
        tprintf("TRACE- doagain()\n");
#endif
        /* Repeating a while is simple */
        if (whyles->w_fename == 0) {
                bseek(whyles->w_start);
                return;
        }
        /*
         * The foreach variable list actually has a spurious word
         * ")" at the end of the w_fe list.  Thus we are at the
         * of the list if one word beyond this is 0.
         */
        if (!whyles->w_fe[1]) {
                dobreak();
                return;
        }
        set(whyles->w_fename, savestr(*whyles->w_fe++));
        bseek(whyles->w_start);
}

void
dorepeat(tchar **v, struct command *kp)
{
        int i, omask;

#ifdef TRACE
        tprintf("TRACE- dorepeat()\n");
#endif
        i = getn(v[1]);
        if (setintr) {
                omask = sigblock(sigmask(SIGINT)) & ~sigmask(SIGINT);
        }
        lshift(v, 2);
        while (i > 0) {
                if (setintr) {
                        (void) sigsetmask(omask);
                }
                reexecute(kp);
                --i;
        }
        donefds();
        if (setintr) {
                (void) sigsetmask(omask);
        }
}

void
doswbrk(void)
{

#ifdef TRACE
        tprintf("TRACE- doswbrk()\n");
#endif
        search(ZBRKSW, 0);
}

int
srchx(tchar *cp)
{
        struct srch *sp, *sp1, *sp2;
        int i;

#ifdef TRACE
        tprintf("TRACE- srchx()\n");
#endif
        /*
         * Binary search
         * Sp1 is the beginning of the current search range.
         * Sp2 is one past the end.
         */
        for (sp1 = srchn, sp2 = srchn + nsrchn; sp1 < sp2; ) {
                sp = sp1 + (sp2 - sp1 >> 1);
                if ((i = *cp - *sp->s_name) == 0 &&
                    (i = strcmp_(cp, sp->s_name)) == 0) {
                        return (sp->s_value);
                }
                if (i < 0) {
                        sp2 = sp;
                } else {
                        sp1 = sp + 1;
                }
        }
        return (-1);
}

tchar Stype;
tchar *Sgoal;

/*VARARGS2*/
void
search(type, level, goal)
        int type; int level; tchar *goal;
{
        tchar wordbuf[BUFSIZ];
        tchar *aword = wordbuf;
        tchar *cp;

#ifdef TRACE
        tprintf("TRACE- search()\n");
#endif
        Stype = type; Sgoal = goal;
        if (type == ZGOTO) {
                bseek((off_t)0);
        }
        do {
                if (intty && fseekp == feobp) {
                        printf("? "), flush();
                }
                aword[0] = 0;
                (void) getword(aword);

                switch (srchx(aword)) {

                case ZELSE:
                        if (level == 0 && type == ZIF) {
                                return;
                        }
                        break;

                case ZIF:
                        while (getword(aword)) {
                                continue;
                        }
                        if ((type == ZIF || type == ZELSE) &&
                            eq(aword, S_then)) {
                                level++;
                        }
                        break;

                case ZENDIF:
                        if (type == ZIF || type == ZELSE) {
                                level--;
                        }
                        break;

                case ZFOREACH:
                case ZWHILE:
                        if (type == ZBREAK) {
                                level++;
                        }
                        break;

                case ZEND:
                        if (type == ZBREAK) {
                                level--;
                        }
                        break;

                case ZSWITCH:
                        if (type == ZSWITCH || type == ZBRKSW) {
                                level++;
                        }
                        break;

                case ZENDSW:
                        if (type == ZSWITCH || type == ZBRKSW) {
                                level--;
                        }
                        break;

                case ZLABEL:
                        if (type == ZGOTO && getword(aword) &&
                            eq(aword, goal)) {
                                level = -1;
                        }
                        break;

                default:
                        if (type != ZGOTO && (type != ZSWITCH || level != 0)) {
                                break;
                        }
                        if (lastchr(aword) != ':') {
                                break;
                        }
                        aword[strlen_(aword) - 1] = 0;
                        if (type == ZGOTO && eq(aword, goal) ||
                            type == ZSWITCH && eq(aword, S_default)) {
                                level = -1;
                        }
                        break;

                case ZCASE:
                        if (type != ZSWITCH || level != 0) {
                                break;
                        }
                        (void) getword(aword);
                        if (lastchr(aword) == ':') {
                                aword[strlen_(aword) - 1] = 0;
                        }
                        cp = strip(Dfix1(aword));
                        if (Gmatch(goal, cp)) {
                                level = -1;
                        }
                        xfree(cp);
                        break;

                case ZDEFAULT:
                        if (type == ZSWITCH && level == 0) {
                                level = -1;
                        }
                        break;
                }
                (void) getword(NOSTR);
        } while (level >= 0);
}

int
getword(tchar *wp)
{
        int found = 0;
        int c, d;
#ifdef TRACE
        tprintf("TRACE- getword()\n");
#endif

        c = readc(1);
        d = 0;
        do {
                while (issp(c)) {
                        c = readc(1);
                }
                if (c == '#') {
                        do {
                                c = readc(1);
                        } while (c >= 0 && c != '\n');
                }
                if (c < 0) {
                        goto past;
                }
                if (c == '\n') {
                        if (wp) {
                                break;
                        }
                        return (0);
                }

                /* ( and ) form separate words */
                if (c == '(' || c == ')') {
                        return (1);
                }

                unreadc(c);
                found = 1;
                do {
                        c = readc(1);
                        if (c == '\\' && (c = readc(1)) == '\n') {
                                c = ' ';
                        }
                        if (c == '\'' || c == '"') {
                                if (d == 0) {
                                        d = c;
                                } else if (d == c) {
                                        d = 0;
                                }
                        }
                        if (c < 0) {
                                goto past;
                        }
                        if (wp) {
                                *wp++ = c;
                        }
                } while ((d || !issp(c) && c != '(' && c != ')') && c != '\n');
        } while (wp == 0);
        unreadc(c);
        if (found) {
                *--wp = 0;
        }
        return (found);

past:
        switch (Stype) {

        case ZIF:
                bferr("then/endif not found");

        case ZELSE:
                bferr("endif not found");

        case ZBRKSW:
        case ZSWITCH:
                bferr("endsw not found");

        case ZBREAK:
                bferr("end not found");

        case ZGOTO:
                setname(Sgoal);
                bferr("label not found");
        }
        /*NOTREACHED*/

        return (0);
}

void
toend(void)
{

#ifdef TRACE
        tprintf("TRACE- toend()\n");
#endif
        if (whyles->w_end == 0) {
                search(ZBREAK, 0);
                whyles->w_end = btell() - 1;
        } else {
                bseek(whyles->w_end);
        }
        wfree();
}

void
wfree(void)
{
        long o = btell();

#ifdef TRACE
        tprintf("TRACE- wfree()\n");
#endif
        while (whyles) {
                struct whyle *wp = whyles;
                struct whyle *nwp = wp->w_next;

                if (o >= wp->w_start && (wp->w_end == 0 || o < wp->w_end)) {
                        break;
                }
                if (wp->w_fe0) {
                        blkfree(wp->w_fe0);
                }
                if (wp->w_fename) {
                        xfree(wp->w_fename);
                }
                xfree((char *)wp);
                whyles = nwp;
        }
}

void
doecho(tchar **v)
{

#ifdef TRACE
        tprintf("TRACE- doecho()\n");
#endif
        echo(' ', v);
}

void
doglob(tchar **v)
{

#ifdef TRACE
        tprintf("TRACE- doglob()\n");
#endif
        echo(0, v);
        flush();
}

void
echo(tchar sep, tchar **v)
{
        tchar *cp;
        int nonl = 0;

#ifdef TRACE
        tprintf("TRACE- echo()\n");
#endif
        if (setintr) {
                (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
        }
        v++;
        if (*v == 0) {
                /*
                 * echo command needs to have newline when there are no
                 * flags or arguments.  glob should have no newline.  If
                 * the separator is a blank, we are doing an echo.  If the
                 * separator is zero, we are globbing.
                 */
                if (sep == (tchar)' ')
                        Putchar('\n');
                return;
        }
        gflag = 0, tglob(v);
        if (gflag) {
                v = glob(v);
                if (v == 0) {
                        bferr("No match");
                }
        }
        /* check for -n arg, NOTE: it might be quoted */
        if (sep == ' ' && *v && strlen_(*v) == 2 &&
            ((**v&TRIM) == '-' && (*(*v + 1) & TRIM) == 'n' &&
            (*(*v+2)&TRIM) == 0)) {
                nonl++, v++;
        }
        while (cp = *v++) {
                int c;

                while (c = *cp++) {
                        Putchar(c | QUOTE);
                }
                if (*v) {
                        Putchar(sep | QUOTE);
                }
        }
        if (sep && nonl == 0) {
                Putchar('\n');
        } else {
                flush();
        }
        if (setintr) {
                (void) sigblock(sigmask(SIGINT));
        }
        if (gargv) {
                blkfree(gargv), gargv = 0;
        }
}

extern char **environ;

/*
 * Check if the environment variable vp affects this csh's behavior
 * and therefore we should call setlocale() or not.
 * This function has two side effects when it returns 1:
 *      variable islocalevar_catnum is set to the LC_xxx value.
 *      variable islocalevar_catname is set to the string "LC_xxx"
 */
static int      islocalevar_catnum;
static char     *islocalevar_catname;

static
bool
islocalevar(tchar *vp)
{
        static struct lcinfo {
                tchar * evname; /* The name of the env. var. */
        } categories_we_care[] = {
            S_LANG, S_LC_ALL, S_LC_CTYPE, S_LC_MESSAGES,
            NOSTR               /* assumption: LC_xxx >= 0 */
        };
        struct lcinfo *p = categories_we_care;

        do {
                if (strcmp_(vp, p->evname) == 0) {
                        return (1);
                }
        } while (((++p)->evname) != NOSTR);
        return (0);
}

void
dosetenv(tchar **v)
{
        tchar *vp, *lp;

#ifdef TRACE
        tprintf("TRACE- dosetenv()\n");
#endif
        v++;
        if ((vp = *v++) == 0) {
                char **ep;

                if (setintr) {
                        (void) sigsetmask(sigblock(0) & ~ sigmask(SIGINT));
                }
                for (ep = environ; *ep; ep++) {
                        printf("%s\n", *ep);
                }
                return;
        }

        if ((lp = *v++) == 0) {
                lp = S_;        /* "" */
        }
        local_setenv(vp, lp = globone(lp));
        if (eq(vp, S_PATH)) {
                importpath(lp);
                dohash(xhash);
        } else if (islocalevar(vp)) {
                if (!setlocale(LC_ALL, "")) {
                        error("Locale could not be set properly");
                }
        }

        xfree(lp);
}

void
dounsetenv(tchar **v)
{
#ifdef TRACE
        tprintf("TRACE- dounsetenv()\n");
#endif
        v++;
        do {
                local_unsetenv(*v);
                if (islocalevar(*v++)) {
                        setlocale(LC_ALL, "");  /* Hope no error! */
                }
        } while (*v);
}

void
local_setenv(tchar *name, tchar *val)
{
        char **ep = environ;
        tchar *cp;
        char *dp;
        tchar *ep_;     /* temporary */
        char *blk[2], **oep = ep;

#ifdef TRACE
        /* tprintf("TRACE- local_setenv(%t, %t)\n", name, val); */
        /* printf("IN local_setenv args = (%t)\n", val); */
#endif
        for (; *ep; ep++) {
#ifdef MBCHAR
                for (cp = name, dp = *ep; *cp && *dp; cp++) {
                        /*
                         * This loop compares two chars in different
                         * representations, EUC (as char *) and wchar_t
                         * (in tchar), and ends when they are different.
                         */
                        wchar_t dwc;
                        int     n;

                        n = mbtowc(&dwc, dp, MB_CUR_MAX);
                        if (n <= 0) {
                                break; /* Illegal multibyte. */
                        }
                        dp += n; /* Advance to next multibyte char. */
                        if (dwc == (wchar_t)(*cp & TRIM)) {
                                continue;
                        } else  {
                                break;
                        }
                }
#else /* !MBCHAR */
                for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) {
                        continue;
                }
#endif /* !MBCHAR */
                if (*cp != 0 || *dp != '=') {
                        continue;
                }
                cp = strspl(S_EQ, val);
                xfree(*ep);
                ep_ = strspl(name, cp);         /* ep_ is xalloc'ed */
                xfree(cp);
                /*
                 * Trimming is not needed here.
                 * trim();
                 */
                *ep = tstostr(NULL, ep_);
                xfree(ep_);                     /* because temp.  use */
                return;
        }
        ep_ = strspl(name, S_EQ);               /* ep_ is xalloc'ed */
        blk[0] = tstostr(NULL, ep_);
        blk[1] = 0;
        xfree(ep_);
        environ = (char **)blkspl_((char **)environ, blk);
        xfree((void *)oep);
        local_setenv(name, val);
}

void
local_unsetenv(tchar *name)
{
        char **ep = environ;
        tchar *cp;
        char *dp;
        char **oep = ep;
        char *cp_;      /* tmp use */
        static int cnt = 0;     /* delete counter */

#ifdef TRACE
        tprintf("TRACE- local_unsetenv()\n");
#endif
        for (; *ep; ep++) {
#ifdef MBCHAR
                for (cp = name, dp = *ep; *cp && *dp; cp++) {
                        /*
                         * This loop compares two chars in different
                         * representations, EUC (as char *) and wchar_t
                         * (in tchar), and ends when they are different.
                         */
                        wchar_t dwc;
                        int     n;

                        n = mbtowc(&dwc, dp, MB_CUR_MAX);
                        if (n <= 0) {
                                break; /* Illegal multibyte. */
                        }
                        dp += n; /* Advance to next multibyte char. */
                        if (dwc == (wchar_t)(*cp & TRIM)) {
                                continue;
                        } else {
                                break;
                        }
                }
#else /* !MBCHAR */
                for (cp = name, dp = *ep; *cp && (char)*cp == *dp; cp++, dp++) {
                        continue;
                }
#endif /* !MBCHAR */
                if (*cp != 0 || *dp != '=') {
                        continue;
                }
                cp_ = *ep;
                *ep = 0;
                environ = (char **)blkspl_((char **)environ, ep+1);
                *ep = cp_;
                xfree(cp_);
                xfree((void *)oep);
                return;
        }
}

void
doumask(tchar **v)
{
        tchar *cp = v[1];
        int i;

#ifdef TRACE
        tprintf("TRACE- dounmask()\n");
#endif
        if (cp == 0) {
                i = umask(0);
                (void) umask(i);
                printf("%o\n", i);
                return;
        }
        i = 0;
        while (digit(*cp) && *cp != '8' && *cp != '9') {
                i = i * 8 + *cp++ - '0';
        }
        if (*cp || i < 0 || i > 0777) {
                bferr("Improper mask");
        }
        (void) umask(i);
}


struct limits *
findlim(tchar *cp)
{
        struct limits *lp, *res;

#ifdef TRACE
        tprintf("TRACE- findlim()\n");
#endif
        res = 0;
        for (lp = limits; lp->limconst >= 0; lp++) {
                if (prefix(cp, lp->limname)) {
                        if (res) {
                                bferr("Ambiguous");
                        }
                        res = lp;
                }
        }
        if (res) {
                return (res);
        }
        bferr("No such limit");
        /*NOTREACHED*/
}

void
dolimit(tchar **v)
{
        struct limits *lp;
        rlim_t limit;
        tchar hard = 0;

#ifdef TRACE
        tprintf("TRACE- dolimit()\n");
#endif
        v++;
        if (*v && eq(*v, S_h)) {
                hard = 1;
                v++;
        }
        if (*v == 0) {
                for (lp = limits; lp->limconst >= 0; lp++) {
                        plim(lp, hard);
                }
                return;
        }
        lp = findlim(v[0]);
        if (v[1] == 0) {
                plim(lp,  hard);
                return;
        }
        switch (getval(lp, v+1, &limit)) {
        case 0:
                error("Value specified for limit is too large");
                return;
        case (-1):
                error("Numeric conversion failed");
                return;
        default:
                if (setlim(lp, hard, limit) < 0) {
                        error(NOSTR);
                }
        }
}

static int
getval(struct limits *lp, tchar **v, rlim_t *retval)
{
        rlim_t value, tmp, tmp2;
        tchar *cp = *v++;
        char chbuf[BUFSIZ * MB_LEN_MAX];

#ifdef TRACE
        tprintf("TRACE- getval()\n");
#endif

        tstostr(chbuf, cp);
        errno = 0;
        value = strtoull(chbuf, NULL, 0);
/*
 * we must accept zero, but the conversion can fail and give us
 * zero as well...try to deal with it as gracefully as possible
 * by checking for EINVAL
 */
        if (value == 0 && errno == EINVAL)
                return (-1);

        while (digit(*cp) || *cp == '.' || *cp == 'e' || *cp == 'E') {
                cp++;
        }
        if (*cp == 0) {
                if (*v == 0) {
                        tmp = value * (rlim_t)lp->limdiv;
                        /* Check for overflow */
                        if (tmp >= value) {
                                *retval = tmp;
                                return (1);
                        } else {
                                return (0);
                        }
                }
                cp = *v;
        }
        switch (*cp) {

        case ':':
                if (lp->limconst != RLIMIT_CPU) {
                        goto badscal;
                }
                tstostr(chbuf, cp + 1);
                tmp = strtoull(chbuf, NULL, 0);
                tmp2 = value * 60 + tmp;
                if (tmp2 >= value) {
                        *retval = tmp2;
                        return (1);
                } else {
                        return (0);
                }

        case 'h':
                if (lp->limconst != RLIMIT_CPU) {
                        goto badscal;
                }
                limtail(cp, S_hours);
                tmp = value * 3600;
                if (tmp < value) {
                        return (0);
                }
                value = tmp;
                break;

        case 'm':
                if (lp->limconst == RLIMIT_CPU) {
                        limtail(cp, S_minutes);
                        tmp = value * 60;
                        if (tmp < value) {
                                return (0);
                        }
                        value = tmp;
                        break;
                }
        case 'M':
                if (lp->limconst == RLIMIT_CPU) {
                        goto badscal;
                }
                *cp = 'm';
                limtail(cp, S_megabytes);
                tmp = value * 1024 * 1024;
                if (tmp < value) {
                        return (0);
                }
                value = tmp;
                break;

        case 's':
                if (lp->limconst != RLIMIT_CPU) {
                        goto badscal;
                }
                limtail(cp, S_seconds);
                break;

        case 'k':
                if (lp->limconst == RLIMIT_CPU) {
                        goto badscal;
                }
                limtail(cp, S_kbytes);
                tmp = value * 1024;
                if (tmp < value) {
                        return (0);
                }
                value = tmp;
                break;

        case 'u':
                limtail(cp, S_unlimited);
                *retval = RLIM_INFINITY;
                return (1);

        default:
badscal:
                bferr("Improper or unknown scale factor");
        }
        *retval = value;
        return (1);
}

void
limtail(tchar *cp, tchar *str0)
{
        tchar *str = str0;
#ifdef TRACE
        tprintf("TRACE- limtail()\n");
#endif

        while (*cp && *cp == *str) {
                cp++, str++;
        }
        if (*cp) {
                error("Bad scaling; did you mean ``%t''?", str0);
        }
}

void
plim(struct limits *lp, tchar hard)
{
        struct rlimit rlim;
        char buf[BUFSZ];
        char *pbuf;
        rlim_t limit;

#ifdef TRACE
        tprintf("TRACE- plim()\n");
#endif
        printf("%t \t", lp->limname);
        (void) getrlimit(lp->limconst, &rlim);
        limit = hard ? rlim.rlim_max : rlim.rlim_cur;
        if (limit == RLIM_INFINITY) {
                printf("unlimited");
        } else if (lp->limconst == RLIMIT_CPU) {
                psecs_ull(limit);
        } else {
                buf[BUFSZ - 1] = '\0';
                pbuf = ulltostr((limit / lp->limdiv), &buf[BUFSZ - 1]);
                printf("%s %t", pbuf, lp->limscale);
        }
        printf("\n");
}

void
dounlimit(tchar **v)
{
        struct limits *lp;
        int err = 0;
        tchar hard = 0;
#ifdef TRACE
        tprintf("TRACE- dounlimit()\n");
#endif

        v++;
        if (*v && eq(*v, S_h)) {
                hard = 1;
                v++;
        }
        if (*v == 0) {
                for (lp = limits; lp->limconst >= 0; lp++) {
                        if (setlim(lp, hard, RLIM_INFINITY) < 0) {
                                err++;
                        }
                }
                if (err) {
                        error(NULL);
                }
                return;
        }
        while (*v) {
                lp = findlim(*v++);
                if (setlim(lp, hard, RLIM_INFINITY) < 0) {
                        error(NULL);
                }
        }
}

int
setlim(struct limits *lp, tchar hard, rlim_t limit)
{
        struct rlimit rlim;

#ifdef TRACE
        tprintf("TRACE- setlim()\n");
#endif
        (void) getrlimit(lp->limconst, &rlim);
        if (hard) {
                rlim.rlim_max = limit;
        } else if (limit == RLIM_INFINITY && geteuid() != 0) {
                rlim.rlim_cur = rlim.rlim_max;
        } else {
                rlim.rlim_cur = limit;
        }
        if (setrlimit(lp->limconst, &rlim) < 0) {
                printf("%t: %t: Can't %s%s limit\n", bname, lp->limname,
                    limit == RLIM_INFINITY ? "remove" : "set",
                    hard ? " hard" : "");
                return (-1);
        }
        return (0);
}

void
dosuspend()
{
        int ctpgrp;
        void (*old)();

#ifdef TRACE
        tprintf("TRACE- dosuspend()\n");
#endif
        if (loginsh) {
                error("Can't suspend a login shell (yet)");
        }
        if (getpid() == getsid(0)) {
                error("Can't suspend this shell");
        }
        untty();
        old = (void (*)())signal(SIGTSTP, SIG_DFL);
        (void) kill(0, SIGTSTP);
        /* the shell stops here */
        (void) signal(SIGTSTP, old);
        if (tpgrp != -1) {
retry:
                (void) ioctl(FSHTTY, TIOCGPGRP,  (char *)&ctpgrp);
                if (ctpgrp != opgrp) {
                        old = (void (*)())signal(SIGTTIN, SIG_DFL);
                        (void) kill(0, SIGTTIN);
                        (void) signal(SIGTTIN, old);
                        goto retry;
                }
                (void) setpgid(0, shpgrp);
                (void) ioctl(FSHTTY, TIOCSPGRP, (char *)&shpgrp);
        }
}

void
doeval(tchar **v)
{
        tchar **oevalvec = evalvec;
        tchar *oevalp = evalp;
        jmp_buf osetexit;
        int reenter;
        tchar **gv = 0;

#ifdef TRACE
        tprintf("TRACE- doeval()\n");
#endif
        v++;
        if (*v == 0) {
                return;
        }
        gflag = 0, tglob(v);
        if (gflag) {
                gv = v = glob(v);
                gargv = 0;
                if (v == 0) {
                        error("No match");
                }
                v = copyblk(v);
        } else {
                trim(v);
        }
        getexit(osetexit);
        reenter = 0;
        setexit();
        reenter++;
        if (reenter == 1) {
                evalvec = v;
                evalp = 0;
                process(0);
        }
        evalvec = oevalvec;
        evalp = oevalp;
        doneinp = 0;
        if (gv) {
                blkfree(gv);
        }
        resexit(osetexit);
        if (reenter >= 2) {
                error(NULL);
        }
}