1*0ac57757Sschwarze /* $OpenBSD: expr.c,v 1.34 2019/02/20 23:59:17 schwarze Exp $ */
27cb960a2Sdownsj
37cb960a2Sdownsj /*
47cb960a2Sdownsj * Korn expression evaluation
57cb960a2Sdownsj */
67cb960a2Sdownsj /*
77cb960a2Sdownsj * todo: better error handling: if in builtin, should be builtin error, etc.
87cb960a2Sdownsj */
97cb960a2Sdownsj
107cb960a2Sdownsj #include <ctype.h>
11dbfe7957Smmcc #include <limits.h>
1256018212Smmcc #include <string.h>
137cb960a2Sdownsj
14b608f594Smmcc #include "sh.h"
157cb960a2Sdownsj
167cb960a2Sdownsj /* The order of these enums is constrained by the order of opinfo[] */
177cb960a2Sdownsj enum token {
18dcacb757Sdownsj /* some (long) unary operators */
19dcacb757Sdownsj O_PLUSPLUS = 0, O_MINUSMINUS,
207cb960a2Sdownsj /* binary operators */
21dcacb757Sdownsj O_EQ, O_NE,
227cb960a2Sdownsj /* assignments are assumed to be in range O_ASN .. O_BORASN */
237cb960a2Sdownsj O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
247cb960a2Sdownsj O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
257cb960a2Sdownsj O_LSHIFT, O_RSHIFT,
267cb960a2Sdownsj O_LE, O_GE, O_LT, O_GT,
277cb960a2Sdownsj O_LAND,
287cb960a2Sdownsj O_LOR,
297cb960a2Sdownsj O_TIMES, O_DIV, O_MOD,
307cb960a2Sdownsj O_PLUS, O_MINUS,
317cb960a2Sdownsj O_BAND,
327cb960a2Sdownsj O_BXOR,
337cb960a2Sdownsj O_BOR,
347cb960a2Sdownsj O_TERN,
35dcacb757Sdownsj O_COMMA,
367cb960a2Sdownsj /* things after this aren't used as binary operators */
377cb960a2Sdownsj /* unary that are not also binaries */
387cb960a2Sdownsj O_BNOT, O_LNOT,
397cb960a2Sdownsj /* misc */
407cb960a2Sdownsj OPEN_PAREN, CLOSE_PAREN, CTERN,
417cb960a2Sdownsj /* things that don't appear in the opinfo[] table */
427cb960a2Sdownsj VAR, LIT, END, BAD
437cb960a2Sdownsj };
44f00c5086Smillert #define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
457cb960a2Sdownsj #define IS_ASSIGNOP(op) ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
467cb960a2Sdownsj
477cb960a2Sdownsj enum prec {
487cb960a2Sdownsj P_PRIMARY = 0, /* VAR, LIT, (), ~ ! - + */
497cb960a2Sdownsj P_MULT, /* * / % */
507cb960a2Sdownsj P_ADD, /* + - */
517cb960a2Sdownsj P_SHIFT, /* << >> */
527cb960a2Sdownsj P_RELATION, /* < <= > >= */
537cb960a2Sdownsj P_EQUALITY, /* == != */
547cb960a2Sdownsj P_BAND, /* & */
557cb960a2Sdownsj P_BXOR, /* ^ */
567cb960a2Sdownsj P_BOR, /* | */
577cb960a2Sdownsj P_LAND, /* && */
587cb960a2Sdownsj P_LOR, /* || */
597cb960a2Sdownsj P_TERN, /* ?: */
60dcacb757Sdownsj P_ASSIGN, /* = *= /= %= += -= <<= >>= &= ^= |= */
61dcacb757Sdownsj P_COMMA /* , */
627cb960a2Sdownsj };
63dcacb757Sdownsj #define MAX_PREC P_COMMA
647cb960a2Sdownsj
657cb960a2Sdownsj struct opinfo {
667cb960a2Sdownsj char name[4];
677cb960a2Sdownsj int len; /* name length */
6812e7fb2dSjmc enum prec prec; /* precedence: lower is higher */
697cb960a2Sdownsj };
707cb960a2Sdownsj
717cb960a2Sdownsj /* Tokens in this table must be ordered so the longest are first
727cb960a2Sdownsj * (eg, += before +). If you change something, change the order
737cb960a2Sdownsj * of enum token too.
747cb960a2Sdownsj */
757cb960a2Sdownsj static const struct opinfo opinfo[] = {
76dcacb757Sdownsj { "++", 2, P_PRIMARY }, /* before + */
77dcacb757Sdownsj { "--", 2, P_PRIMARY }, /* before - */
787cb960a2Sdownsj { "==", 2, P_EQUALITY }, /* before = */
797cb960a2Sdownsj { "!=", 2, P_EQUALITY }, /* before ! */
807cb960a2Sdownsj { "=", 1, P_ASSIGN }, /* keep assigns in a block */
817cb960a2Sdownsj { "*=", 2, P_ASSIGN },
827cb960a2Sdownsj { "/=", 2, P_ASSIGN },
837cb960a2Sdownsj { "%=", 2, P_ASSIGN },
847cb960a2Sdownsj { "+=", 2, P_ASSIGN },
857cb960a2Sdownsj { "-=", 2, P_ASSIGN },
867cb960a2Sdownsj { "<<=", 3, P_ASSIGN },
877cb960a2Sdownsj { ">>=", 3, P_ASSIGN },
887cb960a2Sdownsj { "&=", 2, P_ASSIGN },
897cb960a2Sdownsj { "^=", 2, P_ASSIGN },
907cb960a2Sdownsj { "|=", 2, P_ASSIGN },
917cb960a2Sdownsj { "<<", 2, P_SHIFT },
927cb960a2Sdownsj { ">>", 2, P_SHIFT },
937cb960a2Sdownsj { "<=", 2, P_RELATION },
947cb960a2Sdownsj { ">=", 2, P_RELATION },
957cb960a2Sdownsj { "<", 1, P_RELATION },
967cb960a2Sdownsj { ">", 1, P_RELATION },
977cb960a2Sdownsj { "&&", 2, P_LAND },
987cb960a2Sdownsj { "||", 2, P_LOR },
997cb960a2Sdownsj { "*", 1, P_MULT },
1007cb960a2Sdownsj { "/", 1, P_MULT },
1017cb960a2Sdownsj { "%", 1, P_MULT },
1027cb960a2Sdownsj { "+", 1, P_ADD },
1037cb960a2Sdownsj { "-", 1, P_ADD },
1047cb960a2Sdownsj { "&", 1, P_BAND },
1057cb960a2Sdownsj { "^", 1, P_BXOR },
1067cb960a2Sdownsj { "|", 1, P_BOR },
1077cb960a2Sdownsj { "?", 1, P_TERN },
108dcacb757Sdownsj { ",", 1, P_COMMA },
1097cb960a2Sdownsj { "~", 1, P_PRIMARY },
1107cb960a2Sdownsj { "!", 1, P_PRIMARY },
1117cb960a2Sdownsj { "(", 1, P_PRIMARY },
1127cb960a2Sdownsj { ")", 1, P_PRIMARY },
1137cb960a2Sdownsj { ":", 1, P_PRIMARY },
1147cb960a2Sdownsj { "", 0, P_PRIMARY } /* end of table */
1157cb960a2Sdownsj };
1167cb960a2Sdownsj
1177cb960a2Sdownsj
1187cb960a2Sdownsj typedef struct expr_state Expr_state;
1197cb960a2Sdownsj struct expr_state {
1207cb960a2Sdownsj const char *expression; /* expression being evaluated */
1217cb960a2Sdownsj const char *tokp; /* lexical position */
1227cb960a2Sdownsj enum token tok; /* token from token() */
123dcacb757Sdownsj int noassign; /* don't do assigns (for ?:,&&,||) */
1244095d876Sotto bool arith; /* true if evaluating an $(())
1254095d876Sotto * expression
1264095d876Sotto */
1277cb960a2Sdownsj struct tbl *val; /* value from token() */
128dcacb757Sdownsj struct tbl *evaling; /* variable that is being recursively
129dcacb757Sdownsj * expanded (EXPRINEVAL flag set)
130dcacb757Sdownsj */
1317cb960a2Sdownsj };
1327cb960a2Sdownsj
1337a8124d8Sderaadt enum error_type {
1347a8124d8Sderaadt ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
1357a8124d8Sderaadt ET_LVALUE, ET_RDONLY, ET_STR
1367a8124d8Sderaadt };
1377cb960a2Sdownsj
138c5d5393cSotto static void evalerr(Expr_state *, enum error_type, const char *)
139c5d5393cSotto __attribute__((__noreturn__));
140c5d5393cSotto static struct tbl *evalexpr(Expr_state *, enum prec);
141c5d5393cSotto static void token(Expr_state *);
1420e7d3a01Smillert static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool);
143c5d5393cSotto static void assign_check(Expr_state *, enum token, struct tbl *);
14469b9f96bSmillert static struct tbl *tempvar(void);
145c5d5393cSotto static struct tbl *intvar(Expr_state *, struct tbl *);
1467cb960a2Sdownsj
1477cb960a2Sdownsj /*
148060cee32Sjmc * parse and evaluate expression
1497cb960a2Sdownsj */
1507cb960a2Sdownsj int
evaluate(const char * expr,int64_t * rval,int error_ok,bool arith)151517d3880Stobias evaluate(const char *expr, int64_t *rval, int error_ok, bool arith)
1527cb960a2Sdownsj {
1537cb960a2Sdownsj struct tbl v;
1547cb960a2Sdownsj int ret;
1557cb960a2Sdownsj
1567cb960a2Sdownsj v.flag = DEFINED|INTEGER;
1577cb960a2Sdownsj v.type = 0;
1584095d876Sotto ret = v_evaluate(&v, expr, error_ok, arith);
1597cb960a2Sdownsj *rval = v.val.i;
1607cb960a2Sdownsj return ret;
1617cb960a2Sdownsj }
1627cb960a2Sdownsj
1637cb960a2Sdownsj /*
164060cee32Sjmc * parse and evaluate expression, storing result in vp.
1657cb960a2Sdownsj */
1667cb960a2Sdownsj int
v_evaluate(struct tbl * vp,const char * expr,volatile int error_ok,bool arith)1674095d876Sotto v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
1684095d876Sotto bool arith)
1697cb960a2Sdownsj {
1707cb960a2Sdownsj struct tbl *v;
1717cb960a2Sdownsj Expr_state curstate;
17276d6241fSmillert Expr_state * const es = &curstate;
173*0ac57757Sschwarze int save_disable_subst;
1747cb960a2Sdownsj int i;
1757cb960a2Sdownsj
1767cb960a2Sdownsj /* save state to allow recursive calls */
1777cb960a2Sdownsj curstate.expression = curstate.tokp = expr;
1787cb960a2Sdownsj curstate.noassign = 0;
1794095d876Sotto curstate.arith = arith;
180f7654a50Snicm curstate.evaling = NULL;
181f7654a50Snicm curstate.val = NULL;
1827cb960a2Sdownsj
1837cb960a2Sdownsj newenv(E_ERRH);
184*0ac57757Sschwarze save_disable_subst = disable_subst;
1855ae5b57eStedu i = sigsetjmp(genv->jbuf, 0);
1867cb960a2Sdownsj if (i) {
187*0ac57757Sschwarze disable_subst = save_disable_subst;
188dcacb757Sdownsj /* Clear EXPRINEVAL in of any variables we were playing with */
189dcacb757Sdownsj if (curstate.evaling)
190dcacb757Sdownsj curstate.evaling->flag &= ~EXPRINEVAL;
1910ee3f80eSotto quitenv(NULL);
1927cb960a2Sdownsj if (i == LAEXPR) {
193f00c5086Smillert if (error_ok == KSH_RETURN_ERROR)
1947cb960a2Sdownsj return 0;
19563ca93eaSmillert errorf(NULL);
1967cb960a2Sdownsj }
1977cb960a2Sdownsj unwind(i);
1987cb960a2Sdownsj /* NOTREACHED */
1997cb960a2Sdownsj }
2007cb960a2Sdownsj
20176d6241fSmillert token(es);
2027cb960a2Sdownsj #if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */
2037cb960a2Sdownsj if (es->tok == END) {
2047cb960a2Sdownsj es->tok = LIT;
2057cb960a2Sdownsj es->val = tempvar();
2067cb960a2Sdownsj }
2077cb960a2Sdownsj #endif /* 0 */
20876d6241fSmillert v = intvar(es, evalexpr(es, MAX_PREC));
2097cb960a2Sdownsj
2107cb960a2Sdownsj if (es->tok != END)
211355ffa75Stedu evalerr(es, ET_UNEXPECTED, NULL);
2127cb960a2Sdownsj
2137cb960a2Sdownsj if (vp->flag & INTEGER)
2144095d876Sotto setint_v(vp, v, es->arith);
2157cb960a2Sdownsj else
216060cee32Sjmc /* can fail if readonly */
217f00c5086Smillert setstr(vp, str_val(v), error_ok);
2187cb960a2Sdownsj
2190ee3f80eSotto quitenv(NULL);
2207cb960a2Sdownsj
2217cb960a2Sdownsj return 1;
2227cb960a2Sdownsj }
2237cb960a2Sdownsj
2247cb960a2Sdownsj static void
evalerr(Expr_state * es,enum error_type type,const char * str)225c5d5393cSotto evalerr(Expr_state *es, enum error_type type, const char *str)
2267cb960a2Sdownsj {
2277cb960a2Sdownsj char tbuf[2];
2287cb960a2Sdownsj const char *s;
2297cb960a2Sdownsj
2304095d876Sotto es->arith = false;
2317cb960a2Sdownsj switch (type) {
2327cb960a2Sdownsj case ET_UNEXPECTED:
2337cb960a2Sdownsj switch (es->tok) {
2347cb960a2Sdownsj case VAR:
2357cb960a2Sdownsj s = es->val->name;
2367cb960a2Sdownsj break;
2377cb960a2Sdownsj case LIT:
2387cb960a2Sdownsj s = str_val(es->val);
2397cb960a2Sdownsj break;
2407cb960a2Sdownsj case END:
2417cb960a2Sdownsj s = "end of expression";
2427cb960a2Sdownsj break;
2437cb960a2Sdownsj case BAD:
2447cb960a2Sdownsj tbuf[0] = *es->tokp;
2457cb960a2Sdownsj tbuf[1] = '\0';
2467cb960a2Sdownsj s = tbuf;
2477cb960a2Sdownsj break;
2487cb960a2Sdownsj default:
2497cb960a2Sdownsj s = opinfo[(int)es->tok].name;
2507cb960a2Sdownsj }
2510e7d3a01Smillert warningf(true, "%s: unexpected `%s'", es->expression, s);
2527cb960a2Sdownsj break;
2537cb960a2Sdownsj
2547cb960a2Sdownsj case ET_BADLIT:
2550e7d3a01Smillert warningf(true, "%s: bad number `%s'", es->expression, str);
2567cb960a2Sdownsj break;
2577cb960a2Sdownsj
258dcacb757Sdownsj case ET_RECURSIVE:
2590e7d3a01Smillert warningf(true, "%s: expression recurses on parameter `%s'",
260dcacb757Sdownsj es->expression, str);
261dcacb757Sdownsj break;
262dcacb757Sdownsj
263dcacb757Sdownsj case ET_LVALUE:
2640e7d3a01Smillert warningf(true, "%s: %s requires lvalue",
265dcacb757Sdownsj es->expression, str);
266dcacb757Sdownsj break;
267dcacb757Sdownsj
268dcacb757Sdownsj case ET_RDONLY:
2690e7d3a01Smillert warningf(true, "%s: %s applied to read only variable",
2707cb960a2Sdownsj es->expression, str);
2717cb960a2Sdownsj break;
2727cb960a2Sdownsj
2737cb960a2Sdownsj default: /* keep gcc happy */
2747cb960a2Sdownsj case ET_STR:
2750e7d3a01Smillert warningf(true, "%s: %s", es->expression, str);
2767cb960a2Sdownsj break;
2777cb960a2Sdownsj }
2787cb960a2Sdownsj unwind(LAEXPR);
2797cb960a2Sdownsj }
2807cb960a2Sdownsj
2817cb960a2Sdownsj static struct tbl *
evalexpr(Expr_state * es,enum prec prec)282c5d5393cSotto evalexpr(Expr_state *es, enum prec prec)
2837cb960a2Sdownsj {
28469b9f96bSmillert struct tbl *vl, *vr = NULL, *vasn;
28576d6241fSmillert enum token op;
286517d3880Stobias int64_t res = 0;
2877cb960a2Sdownsj
2887cb960a2Sdownsj if (prec == P_PRIMARY) {
2897cb960a2Sdownsj op = es->tok;
2907a8124d8Sderaadt if (op == O_BNOT || op == O_LNOT || op == O_MINUS ||
2917a8124d8Sderaadt op == O_PLUS) {
29276d6241fSmillert token(es);
29376d6241fSmillert vl = intvar(es, evalexpr(es, P_PRIMARY));
2947cb960a2Sdownsj if (op == O_BNOT)
2957cb960a2Sdownsj vl->val.i = ~vl->val.i;
2967cb960a2Sdownsj else if (op == O_LNOT)
2977cb960a2Sdownsj vl->val.i = !vl->val.i;
2987cb960a2Sdownsj else if (op == O_MINUS)
2997cb960a2Sdownsj vl->val.i = -vl->val.i;
3007cb960a2Sdownsj /* op == O_PLUS is a no-op */
3017cb960a2Sdownsj } else if (op == OPEN_PAREN) {
30276d6241fSmillert token(es);
30376d6241fSmillert vl = evalexpr(es, MAX_PREC);
3047cb960a2Sdownsj if (es->tok != CLOSE_PAREN)
30576d6241fSmillert evalerr(es, ET_STR, "missing )");
30676d6241fSmillert token(es);
307dcacb757Sdownsj } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
30876d6241fSmillert token(es);
3090e7d3a01Smillert vl = do_ppmm(es, op, es->val, true);
31076d6241fSmillert token(es);
3117cb960a2Sdownsj } else if (op == VAR || op == LIT) {
3127cb960a2Sdownsj vl = es->val;
31376d6241fSmillert token(es);
3147cb960a2Sdownsj } else {
315355ffa75Stedu evalerr(es, ET_UNEXPECTED, NULL);
3167cb960a2Sdownsj /* NOTREACHED */
3177cb960a2Sdownsj }
318dcacb757Sdownsj if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
3190e7d3a01Smillert vl = do_ppmm(es, es->tok, vl, false);
32076d6241fSmillert token(es);
321dcacb757Sdownsj }
3227cb960a2Sdownsj return vl;
3237cb960a2Sdownsj }
32476d6241fSmillert vl = evalexpr(es, ((int) prec) - 1);
325dcacb757Sdownsj for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec;
3267a8124d8Sderaadt op = es->tok) {
32776d6241fSmillert token(es);
3287cb960a2Sdownsj vasn = vl;
3297cb960a2Sdownsj if (op != O_ASN) /* vl may not have a value yet */
33076d6241fSmillert vl = intvar(es, vl);
3317cb960a2Sdownsj if (IS_ASSIGNOP(op)) {
33276d6241fSmillert assign_check(es, op, vasn);
33376d6241fSmillert vr = intvar(es, evalexpr(es, P_ASSIGN));
3347cb960a2Sdownsj } else if (op != O_TERN && op != O_LAND && op != O_LOR)
33576d6241fSmillert vr = intvar(es, evalexpr(es, ((int) prec) - 1));
3367a8124d8Sderaadt if ((op == O_DIV || op == O_MOD || op == O_DIVASN ||
3377a8124d8Sderaadt op == O_MODASN) && vr->val.i == 0) {
3387cb960a2Sdownsj if (es->noassign)
3397cb960a2Sdownsj vr->val.i = 1;
3407cb960a2Sdownsj else
34176d6241fSmillert evalerr(es, ET_STR, "zero divisor");
3427cb960a2Sdownsj }
3437cb960a2Sdownsj switch ((int) op) {
3447cb960a2Sdownsj case O_TIMES:
3457cb960a2Sdownsj case O_TIMESASN:
3467cb960a2Sdownsj res = vl->val.i * vr->val.i;
3477cb960a2Sdownsj break;
3487cb960a2Sdownsj case O_DIV:
3497cb960a2Sdownsj case O_DIVASN:
350c2dfab29Snicm if (vl->val.i == LONG_MIN && vr->val.i == -1)
351c2dfab29Snicm res = LONG_MIN;
352c2dfab29Snicm else
3537cb960a2Sdownsj res = vl->val.i / vr->val.i;
3547cb960a2Sdownsj break;
3557cb960a2Sdownsj case O_MOD:
3567cb960a2Sdownsj case O_MODASN:
357c2dfab29Snicm if (vl->val.i == LONG_MIN && vr->val.i == -1)
358c2dfab29Snicm res = 0;
359c2dfab29Snicm else
3607cb960a2Sdownsj res = vl->val.i % vr->val.i;
3617cb960a2Sdownsj break;
3627cb960a2Sdownsj case O_PLUS:
3637cb960a2Sdownsj case O_PLUSASN:
3647cb960a2Sdownsj res = vl->val.i + vr->val.i;
3657cb960a2Sdownsj break;
3667cb960a2Sdownsj case O_MINUS:
3677cb960a2Sdownsj case O_MINUSASN:
3687cb960a2Sdownsj res = vl->val.i - vr->val.i;
3697cb960a2Sdownsj break;
3707cb960a2Sdownsj case O_LSHIFT:
3717cb960a2Sdownsj case O_LSHIFTASN:
3727cb960a2Sdownsj res = vl->val.i << vr->val.i;
3737cb960a2Sdownsj break;
3747cb960a2Sdownsj case O_RSHIFT:
3757cb960a2Sdownsj case O_RSHIFTASN:
3767cb960a2Sdownsj res = vl->val.i >> vr->val.i;
3777cb960a2Sdownsj break;
3787cb960a2Sdownsj case O_LT:
3797cb960a2Sdownsj res = vl->val.i < vr->val.i;
3807cb960a2Sdownsj break;
3817cb960a2Sdownsj case O_LE:
3827cb960a2Sdownsj res = vl->val.i <= vr->val.i;
3837cb960a2Sdownsj break;
3847cb960a2Sdownsj case O_GT:
3857cb960a2Sdownsj res = vl->val.i > vr->val.i;
3867cb960a2Sdownsj break;
3877cb960a2Sdownsj case O_GE:
3887cb960a2Sdownsj res = vl->val.i >= vr->val.i;
3897cb960a2Sdownsj break;
3907cb960a2Sdownsj case O_EQ:
3917cb960a2Sdownsj res = vl->val.i == vr->val.i;
3927cb960a2Sdownsj break;
3937cb960a2Sdownsj case O_NE:
3947cb960a2Sdownsj res = vl->val.i != vr->val.i;
3957cb960a2Sdownsj break;
3967cb960a2Sdownsj case O_BAND:
3977cb960a2Sdownsj case O_BANDASN:
3987cb960a2Sdownsj res = vl->val.i & vr->val.i;
3997cb960a2Sdownsj break;
4007cb960a2Sdownsj case O_BXOR:
4017cb960a2Sdownsj case O_BXORASN:
4027cb960a2Sdownsj res = vl->val.i ^ vr->val.i;
4037cb960a2Sdownsj break;
4047cb960a2Sdownsj case O_BOR:
4057cb960a2Sdownsj case O_BORASN:
4067cb960a2Sdownsj res = vl->val.i | vr->val.i;
4077cb960a2Sdownsj break;
4087cb960a2Sdownsj case O_LAND:
4097cb960a2Sdownsj if (!vl->val.i)
4107cb960a2Sdownsj es->noassign++;
41176d6241fSmillert vr = intvar(es, evalexpr(es, ((int) prec) - 1));
4127cb960a2Sdownsj res = vl->val.i && vr->val.i;
4137cb960a2Sdownsj if (!vl->val.i)
4147cb960a2Sdownsj es->noassign--;
4157cb960a2Sdownsj break;
4167cb960a2Sdownsj case O_LOR:
4177cb960a2Sdownsj if (vl->val.i)
4187cb960a2Sdownsj es->noassign++;
41976d6241fSmillert vr = intvar(es, evalexpr(es, ((int) prec) - 1));
4207cb960a2Sdownsj res = vl->val.i || vr->val.i;
4217cb960a2Sdownsj if (vl->val.i)
4227cb960a2Sdownsj es->noassign--;
4237cb960a2Sdownsj break;
4247cb960a2Sdownsj case O_TERN:
4257cb960a2Sdownsj {
4267cb960a2Sdownsj int e = vl->val.i != 0;
4277a8124d8Sderaadt
4287cb960a2Sdownsj if (!e)
4297cb960a2Sdownsj es->noassign++;
43076d6241fSmillert vl = evalexpr(es, MAX_PREC);
4317cb960a2Sdownsj if (!e)
4327cb960a2Sdownsj es->noassign--;
4337cb960a2Sdownsj if (es->tok != CTERN)
43476d6241fSmillert evalerr(es, ET_STR, "missing :");
43576d6241fSmillert token(es);
4367cb960a2Sdownsj if (e)
4377cb960a2Sdownsj es->noassign++;
43876d6241fSmillert vr = evalexpr(es, P_TERN);
4397cb960a2Sdownsj if (e)
4407cb960a2Sdownsj es->noassign--;
4417cb960a2Sdownsj vl = e ? vl : vr;
4427cb960a2Sdownsj }
4437cb960a2Sdownsj break;
4447cb960a2Sdownsj case O_ASN:
4457cb960a2Sdownsj res = vr->val.i;
4467cb960a2Sdownsj break;
447dcacb757Sdownsj case O_COMMA:
448dcacb757Sdownsj res = vr->val.i;
449dcacb757Sdownsj break;
4507cb960a2Sdownsj }
4517cb960a2Sdownsj if (IS_ASSIGNOP(op)) {
4527cb960a2Sdownsj vr->val.i = res;
4537cb960a2Sdownsj if (vasn->flag & INTEGER)
4544095d876Sotto setint_v(vasn, vr, es->arith);
4557cb960a2Sdownsj else
4567cb960a2Sdownsj setint(vasn, res);
4577cb960a2Sdownsj vl = vr;
4587cb960a2Sdownsj } else if (op != O_TERN)
4597cb960a2Sdownsj vl->val.i = res;
4607cb960a2Sdownsj }
4617cb960a2Sdownsj return vl;
4627cb960a2Sdownsj }
4637cb960a2Sdownsj
4647cb960a2Sdownsj static void
token(Expr_state * es)465c5d5393cSotto token(Expr_state *es)
4667cb960a2Sdownsj {
46776d6241fSmillert const char *cp;
46876d6241fSmillert int c;
4697cb960a2Sdownsj char *tvar;
4707cb960a2Sdownsj
4717cb960a2Sdownsj /* skip white space */
472e569fc7cSderaadt for (cp = es->tokp; (c = *cp), isspace((unsigned char)c); cp++)
4737cb960a2Sdownsj ;
4747cb960a2Sdownsj es->tokp = cp;
4757cb960a2Sdownsj
4767cb960a2Sdownsj if (c == '\0')
4777cb960a2Sdownsj es->tok = END;
4787cb960a2Sdownsj else if (letter(c)) {
479dcacb757Sdownsj for (; letnum(c); c = *cp)
480dcacb757Sdownsj cp++;
4817cb960a2Sdownsj if (c == '[') {
4827cb960a2Sdownsj int len;
4837cb960a2Sdownsj
484dcacb757Sdownsj len = array_ref_len(cp);
4857cb960a2Sdownsj if (len == 0)
48676d6241fSmillert evalerr(es, ET_STR, "missing ]");
4877cb960a2Sdownsj cp += len;
48894e42df6Smillert } else if (c == '(' /*)*/ ) {
489f00c5086Smillert /* todo: add math functions (all take single argument):
490f00c5086Smillert * abs acos asin atan cos cosh exp int log sin sinh sqrt
491f00c5086Smillert * tan tanh
492f00c5086Smillert */
493f00c5086Smillert ;
494f00c5086Smillert }
495dcacb757Sdownsj if (es->noassign) {
4967cb960a2Sdownsj es->val = tempvar();
497dcacb757Sdownsj es->val->flag |= EXPRLVALUE;
498dcacb757Sdownsj } else {
499dcacb757Sdownsj tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP);
5007cb960a2Sdownsj es->val = global(tvar);
5017cb960a2Sdownsj afree(tvar, ATEMP);
5027cb960a2Sdownsj }
5037cb960a2Sdownsj es->tok = VAR;
5047cb960a2Sdownsj } else if (digit(c)) {
5057cb960a2Sdownsj for (; c != '_' && (letnum(c) || c == '#'); c = *cp++)
5067cb960a2Sdownsj ;
5077cb960a2Sdownsj tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP);
5087cb960a2Sdownsj es->val = tempvar();
5097cb960a2Sdownsj es->val->flag &= ~INTEGER;
5107cb960a2Sdownsj es->val->type = 0;
5117cb960a2Sdownsj es->val->val.s = tvar;
5124095d876Sotto if (setint_v(es->val, es->val, es->arith) == NULL)
51376d6241fSmillert evalerr(es, ET_BADLIT, tvar);
5147cb960a2Sdownsj afree(tvar, ATEMP);
5157cb960a2Sdownsj es->tok = LIT;
5167cb960a2Sdownsj } else {
5177cb960a2Sdownsj int i, n0;
5187cb960a2Sdownsj
5197cb960a2Sdownsj for (i = 0; (n0 = opinfo[i].name[0]); i++)
5207a8124d8Sderaadt if (c == n0 &&
5217a8124d8Sderaadt strncmp(cp, opinfo[i].name, opinfo[i].len) == 0) {
5227cb960a2Sdownsj es->tok = (enum token) i;
5237cb960a2Sdownsj cp += opinfo[i].len;
5247cb960a2Sdownsj break;
5257cb960a2Sdownsj }
5267cb960a2Sdownsj if (!n0)
5277cb960a2Sdownsj es->tok = BAD;
5287cb960a2Sdownsj }
5297cb960a2Sdownsj es->tokp = cp;
5307cb960a2Sdownsj }
5317cb960a2Sdownsj
532dcacb757Sdownsj /* Do a ++ or -- operation */
533dcacb757Sdownsj static struct tbl *
do_ppmm(Expr_state * es,enum token op,struct tbl * vasn,bool is_prefix)5340e7d3a01Smillert do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
535dcacb757Sdownsj {
536dcacb757Sdownsj struct tbl *vl;
537dcacb757Sdownsj int oval;
538dcacb757Sdownsj
53976d6241fSmillert assign_check(es, op, vasn);
540dcacb757Sdownsj
54176d6241fSmillert vl = intvar(es, vasn);
542dcacb757Sdownsj oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--;
543dcacb757Sdownsj if (vasn->flag & INTEGER)
5444095d876Sotto setint_v(vasn, vl, es->arith);
545dcacb757Sdownsj else
546dcacb757Sdownsj setint(vasn, vl->val.i);
547dcacb757Sdownsj if (!is_prefix) /* undo the inc/dec */
548dcacb757Sdownsj vl->val.i = oval;
549dcacb757Sdownsj
550dcacb757Sdownsj return vl;
551dcacb757Sdownsj }
552dcacb757Sdownsj
553dcacb757Sdownsj static void
assign_check(Expr_state * es,enum token op,struct tbl * vasn)554c5d5393cSotto assign_check(Expr_state *es, enum token op, struct tbl *vasn)
555dcacb757Sdownsj {
556c63563cdSotto if (es->tok == END || vasn == NULL ||
557881b8d6dSderaadt (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
558f00c5086Smillert evalerr(es, ET_LVALUE, opinfo[(int) op].name);
559dcacb757Sdownsj else if (vasn->flag & RDONLY)
560f00c5086Smillert evalerr(es, ET_RDONLY, opinfo[(int) op].name);
561dcacb757Sdownsj }
562dcacb757Sdownsj
5637cb960a2Sdownsj static struct tbl *
tempvar(void)564c5d5393cSotto tempvar(void)
5657cb960a2Sdownsj {
5667894b443Smillert struct tbl *vp;
5677cb960a2Sdownsj
5688c046d24Snicm vp = alloc(sizeof(struct tbl), ATEMP);
5697cb960a2Sdownsj vp->flag = ISSET|INTEGER;
5707cb960a2Sdownsj vp->type = 0;
5717cb960a2Sdownsj vp->areap = ATEMP;
5727cb960a2Sdownsj vp->val.i = 0;
5737cb960a2Sdownsj vp->name[0] = '\0';
5747cb960a2Sdownsj return vp;
5757cb960a2Sdownsj }
5767cb960a2Sdownsj
5777cb960a2Sdownsj /* cast (string) variable to temporary integer variable */
5787cb960a2Sdownsj static struct tbl *
intvar(Expr_state * es,struct tbl * vp)579c5d5393cSotto intvar(Expr_state *es, struct tbl *vp)
5807cb960a2Sdownsj {
58176d6241fSmillert struct tbl *vq;
5827cb960a2Sdownsj
5837cb960a2Sdownsj /* try to avoid replacing a temp var with another temp var */
5847a8124d8Sderaadt if (vp->name[0] == '\0' &&
5857a8124d8Sderaadt (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
5867cb960a2Sdownsj return vp;
5877cb960a2Sdownsj
5887cb960a2Sdownsj vq = tempvar();
5894095d876Sotto if (setint_v(vq, vp, es->arith) == NULL) {
590dcacb757Sdownsj if (vp->flag & EXPRINEVAL)
59176d6241fSmillert evalerr(es, ET_RECURSIVE, vp->name);
592dcacb757Sdownsj es->evaling = vp;
593dcacb757Sdownsj vp->flag |= EXPRINEVAL;
594*0ac57757Sschwarze disable_subst++;
5954095d876Sotto v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR, es->arith);
596*0ac57757Sschwarze disable_subst--;
597dcacb757Sdownsj vp->flag &= ~EXPRINEVAL;
598f7654a50Snicm es->evaling = NULL;
5997cb960a2Sdownsj }
6007cb960a2Sdownsj return vq;
6017cb960a2Sdownsj }
602