xref: /openbsd/bin/expr/expr.c (revision d51a2013)
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