1 /*        $NetBSD: expr.c,v 1.13 2022/07/03 06:30:31 kre Exp $        */
2 
3 /*
4  * Korn expression evaluation
5  */
6 /*
7  * todo: better error handling: if in builtin, should be builtin error, etc.
8  */
9 #include <sys/cdefs.h>
10 
11 #ifndef lint
12 __RCSID("$NetBSD: expr.c,v 1.13 2022/07/03 06:30:31 kre Exp $");
13 #endif
14 
15 
16 #include "sh.h"
17 #include <ctype.h>
18 #include <stdbool.h>
19 
20 /* The order of these enums is constrained by the order of opinfo[] */
21 enum token {
22           /* some (long) unary operators */
23           O_PLUSPLUS = 0, O_MINUSMINUS,
24           /* binary operators */
25           O_EQ, O_NE,
26           /* assignments are assumed to be in range O_ASN .. O_BORASN */
27           O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
28                  O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
29           O_LSHIFT, O_RSHIFT,
30           O_LE, O_GE, O_LT, O_GT,
31           O_LAND,
32           O_LOR,
33           O_TIMES, O_DIV, O_MOD,
34           O_PLUS, O_MINUS,
35           O_BAND,
36           O_BXOR,
37           O_BOR,
38           O_TERN,
39           O_COMMA,
40           /* things after this aren't used as binary operators */
41           /* unary that are not also binaries */
42           O_BNOT, O_LNOT,
43           /* misc */
44           OPEN_PAREN, CLOSE_PAREN, CTERN,
45           /* things that don't appear in the opinfo[] table */
46           VAR, LIT, END, BAD
47     };
48 #define IS_BINOP(op) (((int)op) >= (int)O_EQ && ((int)op) <= (int)O_COMMA)
49 #define IS_ASSIGNOP(op)       ((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
50 
51 enum prec {
52           P_PRIMARY = 0,                /* VAR, LIT, (), ~ ! - + */
53           P_MULT,                       /* * / % */
54           P_ADD,                        /* + - */
55           P_SHIFT,            /* << >> */
56           P_RELATION,                   /* < <= > >= */
57           P_EQUALITY,                   /* == != */
58           P_BAND,                       /* & */
59           P_BXOR,                       /* ^ */
60           P_BOR,                        /* | */
61           P_LAND,                       /* && */
62           P_LOR,                        /* || */
63           P_TERN,                       /* ?: */
64           P_ASSIGN,           /* = *= /= %= += -= <<= >>= &= ^= |= */
65           P_COMMA                       /* , */
66     };
67 #define MAX_PREC    P_COMMA
68 
69 struct opinfo {
70           char                name[4];
71           int                 len;      /* name length */
72           enum prec prec;     /* precedence: lower is higher */
73 };
74 
75 /* Tokens in this table must be ordered so the longest are first
76  * (eg, += before +).  If you change something, change the order
77  * of enum token too.
78  */
79 static const struct opinfo opinfo[] = {
80                     { "++",    2, P_PRIMARY },    /* before + */
81                     { "--",    2, P_PRIMARY },    /* before - */
82                     { "==",    2, P_EQUALITY },   /* before = */
83                     { "!=",    2, P_EQUALITY },   /* before ! */
84                     { "=",     1, P_ASSIGN },               /* keep assigns in a block */
85                     { "*=",    2, P_ASSIGN },
86                     { "/=",    2, P_ASSIGN },
87                     { "%=",    2, P_ASSIGN },
88                     { "+=",    2, P_ASSIGN },
89                     { "-=",    2, P_ASSIGN },
90                     { "<<=", 3, P_ASSIGN },
91                     { ">>=", 3, P_ASSIGN },
92                     { "&=",    2, P_ASSIGN },
93                     { "^=",    2, P_ASSIGN },
94                     { "|=",    2, P_ASSIGN },
95                     { "<<",    2, P_SHIFT },
96                     { ">>",    2, P_SHIFT },
97                     { "<=",    2, P_RELATION },
98                     { ">=",    2, P_RELATION },
99                     { "<",     1, P_RELATION },
100                     { ">",     1, P_RELATION },
101                     { "&&",    2, P_LAND },
102                     { "||",    2, P_LOR },
103                     { "*",     1, P_MULT },
104                     { "/",     1, P_MULT },
105                     { "%",     1, P_MULT },
106                     { "+",     1, P_ADD },
107                     { "-",     1, P_ADD },
108                     { "&",     1, P_BAND },
109                     { "^",     1, P_BXOR },
110                     { "|",     1, P_BOR },
111                     { "?",     1, P_TERN },
112                     { ",",     1, P_COMMA },
113                     { "~",     1, P_PRIMARY },
114                     { "!",     1, P_PRIMARY },
115                     { "(",     1, P_PRIMARY },
116                     { ")",     1, P_PRIMARY },
117                     { ":",     1, P_PRIMARY },
118                     { "",      0, P_PRIMARY } /* end of table */
119               };
120 
121 
122 typedef struct expr_state Expr_state;
123 struct expr_state {
124           const char *expression;                 /* expression being evaluated */
125           const char *tokp;             /* lexical position */
126           enum token  tok;              /* token from token() */
127           int           noassign;                 /* don't do assigns (for ?:,&&,||) */
128           struct tbl *val;              /* value from token() */
129           struct tbl *evaling;                    /* variable that is being recursively
130                                                    * expanded (EXPRINEVAL flag set)
131                                                    */
132 };
133 
134 enum error_type { ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
135                       ET_LVALUE, ET_RDONLY, ET_STR };
136 
137 static void        evalerr  ARGS((Expr_state *es, enum error_type type,
138                                           const char *str)) GCC_FUNC_ATTR(noreturn);
139 static struct tbl *evalexpr ARGS((Expr_state *es, enum prec prec));
140 static void        token    ARGS((Expr_state *es));
141 static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool);
142 static void            assign_check ARGS((Expr_state *es, enum token op,
143                                               struct tbl *vasn));
144 static struct tbl *tempvar  ARGS((void));
145 static struct tbl *intvar   ARGS((Expr_state *es, struct tbl *vp));
146 
147 /*
148  * parse and evaluate expression
149  */
150 int
evaluate(expr,rval,error_ok)151 evaluate(expr, rval, error_ok)
152           const char *expr;
153           long *rval;
154           int error_ok;
155 {
156           struct tbl v;
157           int ret;
158 
159           v.flag = DEFINED|INTEGER;
160           v.type = 0;
161           ret = v_evaluate(&v, expr, error_ok);
162           *rval = v.val.i;
163           return ret;
164 }
165 
166 /*
167  * parse and evaluate expression, storing result in vp.
168  */
169 int
v_evaluate(vp,expr,error_ok)170 v_evaluate(vp, expr, error_ok)
171           struct tbl *vp;
172           const char *expr;
173           volatile int error_ok;
174 {
175           struct tbl *v;
176           Expr_state curstate;
177           Expr_state * const es = &curstate;
178           int i;
179 
180           /* save state to allow recursive calls */
181           curstate.expression = curstate.tokp = expr;
182           curstate.noassign = 0;
183           curstate.evaling = (struct tbl *) 0;
184 
185           newenv(E_ERRH);
186           i = ksh_sigsetjmp(e->jbuf, 0);
187           if (i) {
188                     /* Clear EXPRINEVAL in of any variables we were playing with */
189                     if (curstate.evaling)
190                               curstate.evaling->flag &= ~EXPRINEVAL;
191                     quitenv();
192                     if (i == LAEXPR) {
193                               if (error_ok == KSH_RETURN_ERROR)
194                                         return 0;
195                               errorf("%s", null);
196                     }
197                     unwind(i);
198                     /*NOTREACHED*/
199           }
200 
201           token(es);
202 #if 1 /* ifdef-out to disallow empty expressions to be treated as 0 */
203           if (es->tok == END) {
204                     es->tok = LIT;
205                     es->val = tempvar();
206           }
207 #endif /* 0 */
208           v = intvar(es, evalexpr(es, MAX_PREC));
209 
210           if (es->tok != END)
211                     evalerr(es, ET_UNEXPECTED, (char *) 0);
212 
213           if (vp->flag & INTEGER)
214                     setint_v(vp, v);
215           else
216                     /* can fail if readonly */
217                     setstr(vp, str_val(v), error_ok);
218 
219           quitenv();
220 
221           return 1;
222 }
223 
224 static void
evalerr(es,type,str)225 evalerr(es, type, str)
226           Expr_state *es;
227           enum error_type type;
228           const char *str;
229 {
230           char tbuf[2];
231           const char *s;
232 
233           switch (type) {
234           case ET_UNEXPECTED:
235                     switch (es->tok) {
236                     case VAR:
237                               s = es->val->name;
238                               break;
239                     case LIT:
240                               s = str_val(es->val);
241                               break;
242                     case END:
243                               s = "end of expression";
244                               break;
245                     case BAD:
246                               tbuf[0] = *es->tokp;
247                               tbuf[1] = '\0';
248                               s = tbuf;
249                               break;
250                     default:
251                               s = opinfo[(int)es->tok].name;
252                     }
253                     warningf(true, "%s: unexpected `%s'", es->expression, s);
254                     break;
255 
256           case ET_BADLIT:
257                     warningf(true, "%s: bad number `%s'", es->expression, str);
258                     break;
259 
260           case ET_RECURSIVE:
261                     warningf(true, "%s: expression recurses on parameter `%s'",
262                               es->expression, str);
263                     break;
264 
265           case ET_LVALUE:
266                     warningf(true, "%s: %s requires lvalue",
267                               es->expression, str);
268                     break;
269 
270           case ET_RDONLY:
271                     warningf(true, "%s: %s applied to read only variable",
272                               es->expression, str);
273                     break;
274 
275           default: /* keep gcc happy */
276           case ET_STR:
277                     warningf(true, "%s: %s", es->expression, str);
278                     break;
279           }
280           unwind(LAEXPR);
281 }
282 
283 static struct tbl *
evalexpr(es,prec)284 evalexpr(es, prec)
285           Expr_state *es;
286           enum prec prec;
287 {
288           struct tbl *vl, UNINITIALIZED(*vr), *vasn;
289           enum token op;
290           long UNINITIALIZED(res);
291 
292           if (prec == P_PRIMARY) {
293                     op = es->tok;
294                     if (op == O_BNOT || op == O_LNOT || op == O_MINUS
295                         || op == O_PLUS)
296                     {
297                               token(es);
298                               vl = intvar(es, evalexpr(es, P_PRIMARY));
299                               if (op == O_BNOT)
300                                         vl->val.i = ~vl->val.i;
301                               else if (op == O_LNOT)
302                                         vl->val.i = !vl->val.i;
303                               else if (op == O_MINUS)
304                                         vl->val.i = -vl->val.i;
305                               /* op == O_PLUS is a no-op */
306                     } else if (op == OPEN_PAREN) {
307                               token(es);
308                               vl = evalexpr(es, MAX_PREC);
309                               if (es->tok != CLOSE_PAREN)
310                                         evalerr(es, ET_STR, "missing )");
311                               token(es);
312                     } else if (op == O_PLUSPLUS || op == O_MINUSMINUS) {
313                               token(es);
314                               if (es->tok != VAR)
315                                         evalerr(es, ET_LVALUE, opinfo[(int) op].name);
316                               vl = do_ppmm(es, op, es->val, true);
317                               token(es);
318                     } else if (op == VAR || op == LIT) {
319                               vl = es->val;
320                               token(es);
321                     } else {
322                               evalerr(es, ET_UNEXPECTED, (char *) 0);
323                               /*NOTREACHED*/
324                     }
325                     if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
326                               vl = do_ppmm(es, es->tok, vl, false);
327                               token(es);
328                     }
329                     return vl;
330           }
331           vl = evalexpr(es, ((int) prec) - 1);
332           for (op = es->tok; IS_BINOP(op) && opinfo[(int) op].prec == prec;
333                     op = es->tok)
334           {
335                     token(es);
336                     vasn = vl;
337                     if (op != O_ASN) /* vl may not have a value yet */
338                               vl = intvar(es, vl);
339                     if (IS_ASSIGNOP(op)) {
340                               assign_check(es, op, vasn);
341                               vr = intvar(es, evalexpr(es, P_ASSIGN));
342                     } else if (op != O_TERN && op != O_LAND && op != O_LOR)
343                               vr = intvar(es, evalexpr(es, ((int) prec) - 1));
344                     if ((op == O_DIV || op == O_MOD || op == O_DIVASN
345                          || op == O_MODASN) && vr->val.i == 0)
346                     {
347                               if (es->noassign)
348                                         vr->val.i = 1;
349                               else
350                                         evalerr(es, ET_STR, "zero divisor");
351                     }
352                     switch ((int) op) {
353                     case O_TIMES:
354                     case O_TIMESASN:
355                               res = vl->val.i * vr->val.i;
356                               break;
357                     case O_DIV:
358                     case O_DIVASN:
359                               res = vl->val.i / vr->val.i;
360                               break;
361                     case O_MOD:
362                     case O_MODASN:
363                               res = vl->val.i % vr->val.i;
364                               break;
365                     case O_PLUS:
366                     case O_PLUSASN:
367                               res = vl->val.i + vr->val.i;
368                               break;
369                     case O_MINUS:
370                     case O_MINUSASN:
371                               res = vl->val.i - vr->val.i;
372                               break;
373                     case O_LSHIFT:
374                     case O_LSHIFTASN:
375                               res = vl->val.i << vr->val.i;
376                               break;
377                     case O_RSHIFT:
378                     case O_RSHIFTASN:
379                               res = vl->val.i >> vr->val.i;
380                               break;
381                     case O_LT:
382                               res = vl->val.i < vr->val.i;
383                               break;
384                     case O_LE:
385                               res = vl->val.i <= vr->val.i;
386                               break;
387                     case O_GT:
388                               res = vl->val.i > vr->val.i;
389                               break;
390                     case O_GE:
391                               res = vl->val.i >= vr->val.i;
392                               break;
393                     case O_EQ:
394                               res = vl->val.i == vr->val.i;
395                               break;
396                     case O_NE:
397                               res = vl->val.i != vr->val.i;
398                               break;
399                     case O_BAND:
400                     case O_BANDASN:
401                               res = vl->val.i & vr->val.i;
402                               break;
403                     case O_BXOR:
404                     case O_BXORASN:
405                               res = vl->val.i ^ vr->val.i;
406                               break;
407                     case O_BOR:
408                     case O_BORASN:
409                               res = vl->val.i | vr->val.i;
410                               break;
411                     case O_LAND:
412                               if (!vl->val.i)
413                                         es->noassign++;
414                               vr = intvar(es, evalexpr(es, ((int) prec) - 1));
415                               res = vl->val.i && vr->val.i;
416                               if (!vl->val.i)
417                                         es->noassign--;
418                               break;
419                     case O_LOR:
420                               if (vl->val.i)
421                                         es->noassign++;
422                               vr = intvar(es, evalexpr(es, ((int) prec) - 1));
423                               res = vl->val.i || vr->val.i;
424                               if (vl->val.i)
425                                         es->noassign--;
426                               break;
427                     case O_TERN:
428                               {
429                                         int ex = vl->val.i != 0;
430                                         if (!ex)
431                                                   es->noassign++;
432                                         vl = evalexpr(es, MAX_PREC);
433                                         if (!ex)
434                                                   es->noassign--;
435                                         if (es->tok != CTERN)
436                                                   evalerr(es, ET_STR, "missing :");
437                                         token(es);
438                                         if (ex)
439                                                   es->noassign++;
440                                         vr = evalexpr(es, P_TERN);
441                                         if (ex)
442                                                   es->noassign--;
443                                         vl = ex ? vl : vr;
444                               }
445                               break;
446                     case O_ASN:
447                               res = vr->val.i;
448                               break;
449                     case O_COMMA:
450                               res = vr->val.i;
451                               break;
452                     }
453                     if (IS_ASSIGNOP(op)) {
454                               vr->val.i = res;
455                               if (vasn->flag & INTEGER)
456                                         setint_v(vasn, vr);
457                               else
458                                         setint(vasn, res);
459                               vl = vr;
460                     } else if (op != O_TERN)
461                               vl->val.i = res;
462           }
463           return vl;
464 }
465 
466 static void
token(es)467 token(es)
468           Expr_state *es;
469 {
470           const char *cp;
471           int c;
472           char *tvar;
473 
474           /* skip white space */
475           for (cp = es->tokp; (c = *cp), isspace((unsigned char)c); cp++)
476                     ;
477           es->tokp = cp;
478 
479           if (c == '\0')
480                     es->tok = END;
481           else if (letter(c)) {
482                     for (; letnum(c); c = *cp)
483                               cp++;
484                     if (c == '[') {
485                               int len;
486 
487                               len = array_ref_len(cp);
488                               if (len == 0)
489                                         evalerr(es, ET_STR, "missing ]");
490                               cp += len;
491                     }
492 #ifdef KSH
493                     else if (c == '(' /*)*/ ) {
494                         /* todo: add math functions (all take single argument):
495                          * abs acos asin atan cos cosh exp int log sin sinh sqrt
496                          * tan tanh
497                          */
498                         ;
499                     }
500 #endif /* KSH */
501                     if (es->noassign) {
502                               es->val = tempvar();
503                               es->val->flag |= EXPRLVALUE;
504                     } else {
505                               tvar = str_nsave(es->tokp, cp - es->tokp, ATEMP);
506                               es->val = global(tvar);
507                               afree(tvar, ATEMP);
508                     }
509                     es->tok = VAR;
510           } else if (digit(c)) {
511                     for (; c != '_' && (letnum(c) || c == '#'); c = *cp++)
512                               ;
513                     tvar = str_nsave(es->tokp, --cp - es->tokp, ATEMP);
514                     es->val = tempvar();
515                     es->val->flag &= ~INTEGER;
516                     es->val->type = 0;
517                     es->val->val.s = tvar;
518                     if (setint_v(es->val, es->val) == NULL)
519                               evalerr(es, ET_BADLIT, tvar);
520                     afree(tvar, ATEMP);
521                     es->tok = LIT;
522           } else {
523                     int i, n0;
524 
525                     for (i = 0; (n0 = opinfo[i].name[0]); i++)
526                               if (c == n0
527                                   && strncmp(cp, opinfo[i].name, opinfo[i].len) == 0)
528                               {
529                                         es->tok = (enum token) i;
530                                         cp += opinfo[i].len;
531                                         break;
532                               }
533                     if (!n0)
534                               es->tok = BAD;
535           }
536           es->tokp = cp;
537 }
538 
539 /* Do a ++ or -- operation */
540 static struct tbl *
do_ppmm(Expr_state * es,enum token op,struct tbl * vasn,bool is_prefix)541 do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
542 {
543           struct tbl *vl;
544           int oval;
545 
546           assign_check(es, op, vasn);
547 
548           vl = intvar(es, vasn);
549           oval = op == O_PLUSPLUS ? vl->val.i++ : vl->val.i--;
550           if (vasn->flag & INTEGER)
551                     setint_v(vasn, vl);
552           else
553                     setint(vasn, vl->val.i);
554           if (!is_prefix)               /* undo the inc/dec */
555                     vl->val.i = oval;
556 
557           return vl;
558 }
559 
560 static void
assign_check(es,op,vasn)561 assign_check(es, op, vasn)
562           Expr_state *es;
563           enum token op;
564           struct tbl *vasn;
565 {
566           if (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE))
567                     evalerr(es, ET_LVALUE, opinfo[(int) op].name);
568           else if (vasn->flag & RDONLY)
569                     evalerr(es, ET_RDONLY, opinfo[(int) op].name);
570 }
571 
572 static struct tbl *
tempvar()573 tempvar()
574 {
575           struct tbl *vp;
576 
577           vp = (struct tbl*) alloc(sizeof(struct tbl), ATEMP);
578           vp->flag = ISSET|INTEGER;
579           vp->type = 0;
580           vp->areap = ATEMP;
581           vp->val.i = 0;
582           vp->name[0] = '\0';
583           return vp;
584 }
585 
586 /* cast (string) variable to temporary integer variable */
587 static struct tbl *
intvar(es,vp)588 intvar(es, vp)
589           Expr_state *es;
590           struct tbl *vp;
591 {
592           struct tbl *vq;
593 
594           /* try to avoid replacing a temp var with another temp var */
595           if (vp->name[0] == '\0'
596               && (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
597                     return vp;
598 
599           vq = tempvar();
600           if (setint_v(vq, vp) == NULL) {
601                     if (vp->flag & EXPRINEVAL)
602                               evalerr(es, ET_RECURSIVE, vp->name);
603                     es->evaling = vp;
604                     vp->flag |= EXPRINEVAL;
605                     v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR);
606                     vp->flag &= ~EXPRINEVAL;
607                     es->evaling = (struct tbl *) 0;
608           }
609           return vq;
610 }
611