root/usr/src/cmd/csh/sh.exp.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 "sh.tconst.h"

/*
 * C shell
 */

#define IGNORE  1       /* in ignore, it means to ignore value, just parse */
#define NOGLOB  2       /* in ignore, it means not to globone */

#define ADDOP   1
#define MULOP   2
#define EQOP    4
#define RELOP   8
#define RESTOP  16
#define ANYOP   31

#define EQEQ    1
#define GTR     2
#define LSS     4
#define NOTEQ   6
#define EQMATCH 7
#define NOTEQMATCH 8

int     uid;

int     exp0(tchar ***, bool);
int     exp1(tchar ***, bool);
int     exp2(tchar ***, bool);
int     exp2a(tchar ***, bool);
int     exp2b(tchar ***, bool);
int     exp2c(tchar ***, bool);
tchar   *exp3(tchar ***, bool);
tchar   *exp3a(tchar ***, bool);
tchar   *exp4(tchar ***, bool);
tchar   *exp5(tchar ***, bool);
tchar   *exp6(tchar ***, bool);
void    evalav(tchar **);

/*
 * Determine if file given by name is accessible with permissions
 * given by mode.
 *
 * Borrowed from the Bourne sh, and modified a bit
 *
 * If the requested access  is  permitted,  a  value  of  0  is
 * returned.  Otherwise, a value of -1 is returned and errno is
 * set to indicate the error
 */

int
chk_access(tchar *path, mode_t mode)
{
        static int flag;
        static uid_t euid;
        struct stat statb;
        mode_t ftype;
        unsigned char name[MAXPATHLEN*MB_LEN_MAX]; /* General use buffer. */

        /* convert tchar * to char * */
        tstostr((char *)name, path);

        if (flag == 0) {
                euid = geteuid();
                flag = 1;
        }
        if (stat((char *)name, &statb) == 0) {
                ftype = statb.st_mode & S_IFMT;
                if (access((char *)name, 010|(mode>>6)) == 0) {
                        if (euid == 0) {
                                if (ftype != S_IFREG || mode != S_IEXEC)
                                        return (0);
                                /* root can execute file as long as it has execute
                                permission for someone */
                                if (statb.st_mode & (S_IEXEC|(S_IEXEC>>3)|(S_IEXEC>>6)))
                                        return (0);
                                return (-1);
                        }
                        return (0);
                }
        }
        return (-1);
}

int
exp(tchar ***vp)
{
#ifdef TRACE
        tprintf("TRACE- exp()\n");
#endif

        return (exp0(vp, 0));
}

int
exp0(tchar ***vp, bool ignore)
{
        int p1 = exp1(vp, ignore);
#ifdef TRACE
        tprintf("TRACE- exp0()\n");
#endif

#ifdef EDEBUG
        etraci("exp0 p1", p1, vp);
#endif
        if (**vp && eq(**vp, S_BARBAR /* "||" */)) {
                int p2;

                (*vp)++;
                p2 = exp0(vp, (ignore&IGNORE) || p1);
#ifdef EDEBUG
                etraci("exp0 p2", p2, vp);
#endif
                return (p1 || p2);
        }
        return (p1);
}

int
exp1(tchar ***vp, bool ignore)
{
        int p1 = exp2(vp, ignore);

#ifdef TRACE
        tprintf("TRACE- exp1()\n");
#endif
#ifdef EDEBUG
        etraci("exp1 p1", p1, vp);
#endif
        if (**vp && eq(**vp, S_ANDAND /* "&&" */)) {
                int p2;

                (*vp)++;
                p2 = exp1(vp, (ignore&IGNORE) || !p1);
#ifdef EDEBUG
                etraci("exp1 p2", p2, vp);
#endif
                return (p1 && p2);
        }
        return (p1);
}

int
exp2(tchar ***vp, bool ignore)
{
        int p1 = exp2a(vp, ignore);

#ifdef TRACE
        tprintf("TRACE- exp2()\n");
#endif
#ifdef EDEBUG
        etraci("exp3 p1", p1, vp);
#endif
        if (**vp && eq(**vp, S_BAR /* "|" */)) {
                int p2;

                (*vp)++;
                p2 = exp2(vp, ignore);
#ifdef EDEBUG
                etraci("exp3 p2", p2, vp);
#endif
                return (p1 | p2);
        }
        return (p1);
}

int
exp2a(tchar ***vp, bool ignore)
{
        int p1 = exp2b(vp, ignore);

#ifdef TRACE
        tprintf("TRACE- exp2a()\n");
#endif
#ifdef EDEBUG
        etraci("exp2a p1", p1, vp);
#endif
        if (**vp && eq(**vp, S_HAT /* "^" */)) {
                int p2;

                (*vp)++;
                p2 = exp2a(vp, ignore);
#ifdef EDEBUG
                etraci("exp2a p2", p2, vp);
#endif
                return (p1 ^ p2);
        }
        return (p1);
}

int
exp2b(tchar ***vp, bool ignore)
{
        int p1 = exp2c(vp, ignore);

#ifdef TRACE
        tprintf("TRACE- exp2b()\n");
#endif
#ifdef EDEBUG
        etraci("exp2b p1", p1, vp);
#endif
        if (**vp && eq(**vp, S_AND /* "&" */)) {
                int p2;

                (*vp)++;
                p2 = exp2b(vp, ignore);
#ifdef EDEBUG
                etraci("exp2b p2", p2, vp);
#endif
                return (p1 & p2);
        }
        return (p1);
}

int
exp2c(tchar ***vp, bool ignore)
{
        tchar *p1 = exp3(vp, ignore);
        tchar *p2;
        int i;

#ifdef TRACE
        tprintf("TRACE- exp2c()\n");
#endif
#ifdef EDEBUG
        etracc("exp2c p1", p1, vp);
#endif
        if (i = isa(**vp, EQOP)) {
                (*vp)++;
                if (i == EQMATCH || i == NOTEQMATCH)
                        ignore |= NOGLOB;
                p2 = exp3(vp, ignore);
#ifdef EDEBUG
                etracc("exp2c p2", p2, vp);
#endif
                if (!(ignore&IGNORE)) switch (i) {

                case EQEQ:
                        i = eq(p1, p2);
                        break;

                case NOTEQ:
                        i = !eq(p1, p2);
                        break;

                case EQMATCH:
                        i = Gmatch(p1, p2);
                        break;

                case NOTEQMATCH:
                        i = !Gmatch(p1, p2);
                        break;
                }
                xfree(p1), xfree(p2);
                return (i);
        }
        i = egetn(p1);
        xfree(p1);
        return (i);
}

tchar *
exp3(tchar ***vp, bool ignore)
{
        tchar *p1, *p2;
        int i;

#ifdef TRACE
        tprintf("TRACE- exp3()\n");
#endif
        p1 = exp3a(vp, ignore);
#ifdef EDEBUG
        etracc("exp3 p1", p1, vp);
#endif
        if (i = isa(**vp, RELOP)) {
                (*vp)++;
                if (**vp && eq(**vp, S_EQ /* "=" */))
                        i |= 1, (*vp)++;
                p2 = exp3(vp, ignore);
#ifdef EDEBUG
                etracc("exp3 p2", p2, vp);
#endif
                if (!(ignore&IGNORE)) switch (i) {

                case GTR:
                        i = egetn(p1) > egetn(p2);
                        break;

                case GTR|1:
                        i = egetn(p1) >= egetn(p2);
                        break;

                case LSS:
                        i = egetn(p1) < egetn(p2);
                        break;

                case LSS|1:
                        i = egetn(p1) <= egetn(p2);
                        break;
                }
                xfree(p1), xfree(p2);
                return (putn(i));
        }
        return (p1);
}

tchar *
exp3a(tchar ***vp, bool ignore)
{
        tchar *p1, *p2, *op;
        int i;

#ifdef TRACE
        tprintf("TRACE- exp3a()\n");
#endif
        p1 = exp4(vp, ignore);
#ifdef EDEBUG
        etracc("exp3a p1", p1, vp);
#endif
        op = **vp;
        /* if (op && any(op[0], "<>") && op[0] == op[1]) { */
        if (op && (op[0] == '<' || op[0] == '>') && op[0] == op[1]) {
                (*vp)++;
                p2 = exp3a(vp, ignore);
#ifdef EDEBUG
                etracc("exp3a p2", p2, vp);
#endif
                if (op[0] == '<')
                        i = egetn(p1) << egetn(p2);
                else
                        i = egetn(p1) >> egetn(p2);
                xfree(p1), xfree(p2);
                return (putn(i));
        }
        return (p1);
}

tchar *
exp4(tchar ***vp, bool ignore)
{
        tchar *p1, *p2;
        int i = 0;

#ifdef TRACE
        tprintf("TRACE- exp4()\n");
#endif
        p1 = exp5(vp, ignore);
#ifdef EDEBUG
        etracc("exp4 p1", p1, vp);
#endif
        if (isa(**vp, ADDOP)) {
                tchar *op = *(*vp)++;

                p2 = exp4(vp, ignore);
#ifdef EDEBUG
                etracc("exp4 p2", p2, vp);
#endif
                if (!(ignore&IGNORE)) switch (op[0]) {

                case '+':
                        i = egetn(p1) + egetn(p2);
                        break;

                case '-':
                        i = egetn(p1) - egetn(p2);
                        break;
                }
                xfree(p1), xfree(p2);
                return (putn(i));
        }
        return (p1);
}

tchar *
exp5(tchar ***vp, bool ignore)
{
        tchar *p1, *p2;
        int i = 0;

#ifdef TRACE
        tprintf("TRACE- exp5()\n");
#endif
        p1 = exp6(vp, ignore);
#ifdef EDEBUG
        etracc("exp5 p1", p1, vp);
#endif
        if (isa(**vp, MULOP)) {
                tchar *op = *(*vp)++;

                p2 = exp5(vp, ignore);
#ifdef EDEBUG
                etracc("exp5 p2", p2, vp);
#endif
                if (!(ignore&IGNORE)) switch (op[0]) {

                case '*':
                        i = egetn(p1) * egetn(p2);
                        break;

                case '/':
                        i = egetn(p2);
                        if (i == 0)
                                error("Divide by 0");
                        i = egetn(p1) / i;
                        break;

                case '%':
                        i = egetn(p2);
                        if (i == 0)
                                error("Mod by 0");
                        i = egetn(p1) % i;
                        break;
                }
                xfree(p1), xfree(p2);
                return (putn(i));
        }
        return (p1);
}

tchar *
exp6(tchar ***vp, bool ignore)
{
        int ccode, i;
        tchar *cp, *dp, *ep;

#ifdef TRACE
        tprintf("TRACE- exp6()\n");
#endif
        if (**vp == 0)
                bferr("Expression syntax");
        if (eq(**vp, S_EXAS /* "!" */)) {
                (*vp)++;
                cp = exp6(vp, ignore);
#ifdef EDEBUG
                etracc("exp6 ! cp", cp, vp);
#endif
                i = egetn(cp);
                xfree(cp);
                return (putn(!i));
        }
        if (eq(**vp, S_TIL /* "~" */)) {
                (*vp)++;
                cp = exp6(vp, ignore);
#ifdef EDEBUG
                etracc("exp6 ~ cp", cp, vp);
#endif
                i = egetn(cp);
                xfree(cp);
                return (putn(~i));
        }
        if (eq(**vp, S_LPAR /* "(" */)) {
                (*vp)++;
                ccode = exp0(vp, ignore);
#ifdef EDEBUG
                etraci("exp6 () ccode", ccode, vp);
#endif
                if (*vp == 0 || **vp == 0 || ***vp != ')')
                        bferr("Expression syntax");
                (*vp)++;
                return (putn(ccode));
        }
        if (eq(**vp, S_LBRA /* "{" */)) {
                tchar **v;
                struct command faket;
                tchar *fakecom[2];

                faket.t_dtyp = TCOM;
                faket.t_dflg = 0;
                faket.t_dcar = faket.t_dcdr = faket.t_dspr = (struct command *)0;
                faket.t_dcom = fakecom;
                fakecom[0] = S_BRAPPPBRA /* "{ ... }" */;
                fakecom[1] = NOSTR;
                (*vp)++;
                v = *vp;
                for (;;) {
                        if (!**vp)
                                bferr("Missing }");
                        if (eq(*(*vp)++, S_RBRA /* "}" */))
                                break;
                }
                if (ignore&IGNORE)
                        return (S_ /* "" */);
                psavejob();
                if (pfork(&faket, -1) == 0) {
                        *--(*vp) = 0;
                        evalav(v);
                        exitstat();
                }
                pwait();
                prestjob();
#ifdef EDEBUG
                etraci("exp6 {} status", egetn(value("status")), vp);
#endif
                return (putn(egetn(value(S_status /* "status" */)) == 0));
        }
        if (isa(**vp, ANYOP))
                return (S_ /* "" */);
        cp = *(*vp)++;
        if (*cp == '-' && any(cp[1], S_erwxfdzo /* "erwxfdzo" */)) {
                struct stat stb;

                if (cp[2] != '\0')
                        bferr("Malformed file inquiry");

                /*
                 * Detect missing file names by checking for operator
                 * in the file name position.  However, if an operator
                 * name appears there, we must make sure that there's
                 * no file by that name (e.g., "/") before announcing
                 * an error.  Even this check isn't quite right, since
                 * it doesn't take globbing into account.
                 */
                if ((**vp == NULL) || isa(**vp, ANYOP) && stat_(**vp, &stb))
                        bferr("Missing file name");
                dp = *(*vp)++;

                if (ignore&IGNORE)
                        return (S_ /* "" */);
                ep = globone(dp);
                switch (cp[1]) {

                case 'r':
                        i = !chk_access(ep, S_IREAD);
                        break;

                case 'w':
                        i = !chk_access(ep, S_IWRITE);
                        break;

                case 'x':
                        i = !chk_access(ep, S_IEXEC);
                        break;

                default:
                        if (stat_(ep, &stb)) {
                                xfree(ep);
                                return (S_0 /* "0" */);
                        }
                        switch (cp[1]) {

                        case 'f':
                                i = (stb.st_mode & S_IFMT) == S_IFREG;
                                break;

                        case 'd':
                                i = (stb.st_mode & S_IFMT) == S_IFDIR;
                                break;

                        case 'z':
                                i = stb.st_size == 0;
                                break;

                        case 'e':
                                i = 1;
                                break;

                        case 'o':
                                i = stb.st_uid == uid;
                                break;
                        }
                }
#ifdef EDEBUG
                etraci("exp6 -? i", i, vp);
#endif
                xfree(ep);
                return (putn(i));
        }
#ifdef EDEBUG
        etracc("exp6 default", cp, vp);
#endif
        return (ignore&NOGLOB ? savestr(cp) : globone(cp));
}

void
evalav(tchar **v)
{
        struct wordent paraml;
        struct wordent *hp = &paraml;
        struct command *t;
        struct wordent *wdp = hp;

#ifdef TRACE
        tprintf("TRACE- evalav()\n");
#endif
        set(S_status /* "status" */, S_0 /* "0" */);
        hp->prev = hp->next = hp;
        hp->word = S_ /* "" */;
        while (*v) {
                struct wordent *new = (struct wordent *)xcalloc(1, sizeof *wdp);

                new->prev = wdp;
                new->next = hp;
                wdp->next = new;
                wdp = new;
                wdp->word = savestr(*v++);
        }
        hp->prev = wdp;
        alias(&paraml);
        t = syntax(paraml.next, &paraml, 0);
        if (err_msg)
                error("%s", gettext(err_msg));
        execute(t, -1);
        freelex(&paraml), freesyn(t);
}

int
isa(tchar *cp, int what)
{

#ifdef TRACE
        tprintf("TRACE- isa()\n");
#endif
        if (cp == 0)
                return ((what & RESTOP) != 0);
        if (cp[1] == 0) {
                if (what & ADDOP && (*cp == '+' || *cp == '-'))
                        return (1);
                if (what & MULOP && (*cp == '*' || *cp == '/' || *cp == '%'))
                        return (1);
                if (what & RESTOP && (*cp == '(' || *cp == ')' || *cp == '!' ||
                                        *cp == '~' || *cp == '^' || *cp == '"'))
                        return (1);
        } else if (cp[2] == 0) {
                if (what & RESTOP) {
                        if (cp[0] == '|' && cp[1] == '&')
                                return (1);
                        if (cp[0] == '<' && cp[1] == '<')
                                return (1);
                        if (cp[0] == '>' && cp[1] == '>')
                                return (1);
                }
                if (what & EQOP) {
                        if (cp[0] == '=') {
                                if (cp[1] == '=')
                                        return (EQEQ);
                                if (cp[1] == '~')
                                        return (EQMATCH);
                        } else if (cp[0] == '!') {
                                if (cp[1] == '=')
                                        return (NOTEQ);
                                if (cp[1] == '~')
                                        return (NOTEQMATCH);
                        }
                }
        }
        if (what & RELOP) {
                if (*cp == '<')
                        return (LSS);
                if (*cp == '>')
                        return (GTR);
        }
        return (0);
}

int
egetn(tchar *cp)
{

#ifdef TRACE
        tprintf("TRACE- egetn()\n");
#endif
        if (*cp && *cp != '-' && !digit(*cp))
                bferr("Expression syntax");
        return (getn(cp));
}

/* Phew! */

#ifdef EDEBUG
void
etraci(tchar *str, int i, tchar ***vp)
{

        printf("%s=%d\t", str, i);
        blkpr(*vp);
        printf("\n");
}

void
etracc(tchar *str, tchar *cp, tchar ***vp)
{

        printf("%s=%s\t", str, cp);
        blkpr(*vp);
        printf("\n");
}
#endif