xref: /openbsd-src/bin/ksh/expr.c (revision 0ac57757bcdd34e94e3e2d3b05ea4601a6b10e8a)
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