1 /* $OpenBSD: expr.c,v 1.28 2022/01/28 05:15:05 guenther Exp $ */
2 /* $NetBSD: expr.c,v 1.3.6.1 1996/06/04 20:41:47 cgd Exp $ */
3
4 /*
5 * Written by J.T. Conklin <jtc@netbsd.org>.
6 * Public domain.
7 */
8
9 #include <stdio.h>
10 #include <stdint.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <limits.h>
14 #include <ctype.h>
15 #include <unistd.h>
16 #include <regex.h>
17 #include <err.h>
18
19 struct val *make_int(int64_t);
20 struct val *make_str(char *);
21 void free_value(struct val *);
22 int is_integer(struct val *, int64_t *, const char **);
23 int to_integer(struct val *, const char **);
24 void to_string(struct val *);
25 int is_zero_or_null(struct val *);
26 void nexttoken(int);
27 __dead void error(void);
28 struct val *eval6(void);
29 struct val *eval5(void);
30 struct val *eval4(void);
31 struct val *eval3(void);
32 struct val *eval2(void);
33 struct val *eval1(void);
34 struct val *eval0(void);
35
36 enum token {
37 OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
38 NE, LE, GE, OPERAND, EOI
39 };
40
41 struct val {
42 enum {
43 integer,
44 string
45 } type;
46
47 union {
48 char *s;
49 int64_t i;
50 } u;
51 };
52
53 enum token token;
54 struct val *tokval;
55 char **av;
56
57 struct val *
make_int(int64_t i)58 make_int(int64_t i)
59 {
60 struct val *vp;
61
62 vp = malloc(sizeof(*vp));
63 if (vp == NULL) {
64 err(3, NULL);
65 }
66 vp->type = integer;
67 vp->u.i = i;
68 return vp;
69 }
70
71
72 struct val *
make_str(char * s)73 make_str(char *s)
74 {
75 struct val *vp;
76
77 vp = malloc(sizeof(*vp));
78 if (vp == NULL || ((vp->u.s = strdup(s)) == NULL)) {
79 err(3, NULL);
80 }
81 vp->type = string;
82 return vp;
83 }
84
85
86 void
free_value(struct val * vp)87 free_value(struct val *vp)
88 {
89 if (vp->type == string)
90 free(vp->u.s);
91 free(vp);
92 }
93
94
95 /* determine if vp is an integer; if so, return its value in *r */
96 int
is_integer(struct val * vp,int64_t * r,const char ** errstr)97 is_integer(struct val *vp, int64_t *r, const char **errstr)
98 {
99 const char *errstrp;
100
101 if (errstr == NULL)
102 errstr = &errstrp;
103 *errstr = NULL;
104
105 if (vp->type == integer) {
106 *r = vp->u.i;
107 return 1;
108 }
109
110 /*
111 * POSIX.2 defines an "integer" as an optional unary minus
112 * followed by digits. Other representations are unspecified,
113 * which means that strtonum(3) is a viable option here.
114 */
115 *r = strtonum(vp->u.s, INT64_MIN, INT64_MAX, errstr);
116 return *errstr == NULL;
117 }
118
119
120 /* coerce to vp to an integer */
121 int
to_integer(struct val * vp,const char ** errstr)122 to_integer(struct val *vp, const char **errstr)
123 {
124 int64_t r;
125
126 if (errstr != NULL)
127 *errstr = NULL;
128
129 if (vp->type == integer)
130 return 1;
131
132 if (is_integer(vp, &r, errstr)) {
133 free(vp->u.s);
134 vp->u.i = r;
135 vp->type = integer;
136 return 1;
137 }
138
139 return 0;
140 }
141
142
143 /* coerce to vp to an string */
144 void
to_string(struct val * vp)145 to_string(struct val *vp)
146 {
147 char *tmp;
148
149 if (vp->type == string)
150 return;
151
152 if (asprintf(&tmp, "%lld", vp->u.i) == -1)
153 err(3, NULL);
154
155 vp->type = string;
156 vp->u.s = tmp;
157 }
158
159 int
is_zero_or_null(struct val * vp)160 is_zero_or_null(struct val *vp)
161 {
162 if (vp->type == integer)
163 return vp->u.i == 0;
164 else
165 return *vp->u.s == 0 || (to_integer(vp, NULL) && vp->u.i == 0);
166 }
167
168 void
nexttoken(int pat)169 nexttoken(int pat)
170 {
171 char *p;
172
173 if ((p = *av) == NULL) {
174 token = EOI;
175 return;
176 }
177 av++;
178
179
180 if (pat == 0 && p[0] != '\0') {
181 if (p[1] == '\0') {
182 const char *x = "|&=<>+-*/%:()";
183 char *i; /* index */
184
185 if ((i = strchr(x, *p)) != NULL) {
186 token = i - x;
187 return;
188 }
189 } else if (p[1] == '=' && p[2] == '\0') {
190 switch (*p) {
191 case '<':
192 token = LE;
193 return;
194 case '>':
195 token = GE;
196 return;
197 case '!':
198 token = NE;
199 return;
200 }
201 }
202 }
203 tokval = make_str(p);
204 token = OPERAND;
205 return;
206 }
207
208 __dead void
error(void)209 error(void)
210 {
211 errx(2, "syntax error");
212 }
213
214 struct val *
eval6(void)215 eval6(void)
216 {
217 struct val *v;
218
219 if (token == OPERAND) {
220 nexttoken(0);
221 return tokval;
222 } else if (token == RP) {
223 nexttoken(0);
224 v = eval0();
225 if (token != LP)
226 error();
227 nexttoken(0);
228 return v;
229 } else
230 error();
231 }
232
233 /* Parse and evaluate match (regex) expressions */
234 struct val *
eval5(void)235 eval5(void)
236 {
237 regex_t rp;
238 regmatch_t rm[2];
239 char errbuf[256];
240 int eval;
241 struct val *l, *r;
242 struct val *v;
243
244 l = eval6();
245 while (token == MATCH) {
246 nexttoken(1);
247 r = eval6();
248
249 /* coerce to both arguments to strings */
250 to_string(l);
251 to_string(r);
252
253 /* compile regular expression */
254 if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
255 regerror(eval, &rp, errbuf, sizeof(errbuf));
256 errx(2, "%s", errbuf);
257 }
258
259 /* compare string against pattern -- remember that patterns
260 are anchored to the beginning of the line */
261 if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
262 if (rm[1].rm_so >= 0) {
263 *(l->u.s + rm[1].rm_eo) = '\0';
264 v = make_str(l->u.s + rm[1].rm_so);
265
266 } else {
267 v = make_int(rm[0].rm_eo - rm[0].rm_so);
268 }
269 } else {
270 if (rp.re_nsub == 0) {
271 v = make_int(0);
272 } else {
273 v = make_str("");
274 }
275 }
276
277 /* free arguments and pattern buffer */
278 free_value(l);
279 free_value(r);
280 regfree(&rp);
281
282 l = v;
283 }
284
285 return l;
286 }
287
288 /* Parse and evaluate multiplication and division expressions */
289 struct val *
eval4(void)290 eval4(void)
291 {
292 const char *errstr;
293 struct val *l, *r;
294 enum token op;
295 volatile int64_t res;
296
297 l = eval5();
298 while ((op = token) == MUL || op == DIV || op == MOD) {
299 nexttoken(0);
300 r = eval5();
301
302 if (!to_integer(l, &errstr))
303 errx(2, "number \"%s\" is %s", l->u.s, errstr);
304 if (!to_integer(r, &errstr))
305 errx(2, "number \"%s\" is %s", r->u.s, errstr);
306
307 if (op == MUL) {
308 res = l->u.i * r->u.i;
309 if (r->u.i != 0 && l->u.i != res / r->u.i)
310 errx(3, "overflow");
311 l->u.i = res;
312 } else {
313 if (r->u.i == 0) {
314 errx(2, "division by zero");
315 }
316 if (op == DIV) {
317 if (l->u.i != INT64_MIN || r->u.i != -1)
318 l->u.i /= r->u.i;
319 else
320 errx(3, "overflow");
321 } else {
322 if (l->u.i != INT64_MIN || r->u.i != -1)
323 l->u.i %= r->u.i;
324 else
325 l->u.i = 0;
326 }
327 }
328
329 free_value(r);
330 }
331
332 return l;
333 }
334
335 /* Parse and evaluate addition and subtraction expressions */
336 struct val *
eval3(void)337 eval3(void)
338 {
339 const char *errstr;
340 struct val *l, *r;
341 enum token op;
342 volatile int64_t res;
343
344 l = eval4();
345 while ((op = token) == ADD || op == SUB) {
346 nexttoken(0);
347 r = eval4();
348
349 if (!to_integer(l, &errstr))
350 errx(2, "number \"%s\" is %s", l->u.s, errstr);
351 if (!to_integer(r, &errstr))
352 errx(2, "number \"%s\" is %s", r->u.s, errstr);
353
354 if (op == ADD) {
355 res = l->u.i + r->u.i;
356 if ((l->u.i > 0 && r->u.i > 0 && res <= 0) ||
357 (l->u.i < 0 && r->u.i < 0 && res >= 0))
358 errx(3, "overflow");
359 l->u.i = res;
360 } else {
361 res = l->u.i - r->u.i;
362 if ((l->u.i >= 0 && r->u.i < 0 && res <= 0) ||
363 (l->u.i < 0 && r->u.i > 0 && res >= 0))
364 errx(3, "overflow");
365 l->u.i = res;
366 }
367
368 free_value(r);
369 }
370
371 return l;
372 }
373
374 /* Parse and evaluate comparison expressions */
375 struct val *
eval2(void)376 eval2(void)
377 {
378 struct val *l, *r;
379 enum token op;
380 int64_t v = 0, li, ri;
381
382 l = eval3();
383 while ((op = token) == EQ || op == NE || op == LT || op == GT ||
384 op == LE || op == GE) {
385 nexttoken(0);
386 r = eval3();
387
388 if (is_integer(l, &li, NULL) && is_integer(r, &ri, NULL)) {
389 switch (op) {
390 case GT:
391 v = (li > ri);
392 break;
393 case GE:
394 v = (li >= ri);
395 break;
396 case LT:
397 v = (li < ri);
398 break;
399 case LE:
400 v = (li <= ri);
401 break;
402 case EQ:
403 v = (li == ri);
404 break;
405 case NE:
406 v = (li != ri);
407 break;
408 default:
409 break;
410 }
411 } else {
412 to_string(l);
413 to_string(r);
414
415 switch (op) {
416 case GT:
417 v = (strcoll(l->u.s, r->u.s) > 0);
418 break;
419 case GE:
420 v = (strcoll(l->u.s, r->u.s) >= 0);
421 break;
422 case LT:
423 v = (strcoll(l->u.s, r->u.s) < 0);
424 break;
425 case LE:
426 v = (strcoll(l->u.s, r->u.s) <= 0);
427 break;
428 case EQ:
429 v = (strcoll(l->u.s, r->u.s) == 0);
430 break;
431 case NE:
432 v = (strcoll(l->u.s, r->u.s) != 0);
433 break;
434 default:
435 break;
436 }
437 }
438
439 free_value(l);
440 free_value(r);
441 l = make_int(v);
442 }
443
444 return l;
445 }
446
447 /* Parse and evaluate & expressions */
448 struct val *
eval1(void)449 eval1(void)
450 {
451 struct val *l, *r;
452
453 l = eval2();
454 while (token == AND) {
455 nexttoken(0);
456 r = eval2();
457
458 if (is_zero_or_null(l) || is_zero_or_null(r)) {
459 free_value(l);
460 free_value(r);
461 l = make_int(0);
462 } else {
463 free_value(r);
464 }
465 }
466
467 return l;
468 }
469
470 /* Parse and evaluate | expressions */
471 struct val *
eval0(void)472 eval0(void)
473 {
474 struct val *l, *r;
475
476 l = eval1();
477 while (token == OR) {
478 nexttoken(0);
479 r = eval1();
480
481 if (is_zero_or_null(l)) {
482 free_value(l);
483 l = r;
484 } else {
485 free_value(r);
486 }
487 }
488
489 return l;
490 }
491
492
493 int
main(int argc,char * argv[])494 main(int argc, char *argv[])
495 {
496 struct val *vp;
497
498 if (pledge("stdio", NULL) == -1)
499 err(2, "pledge");
500
501 if (argc > 1 && !strcmp(argv[1], "--"))
502 argv++;
503
504 av = argv + 1;
505
506 nexttoken(0);
507 vp = eval0();
508
509 if (token != EOI)
510 error();
511
512 if (vp->type == integer)
513 printf("%lld\n", vp->u.i);
514 else
515 printf("%s\n", vp->u.s);
516
517 return is_zero_or_null(vp);
518 }
519