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
19struct val	*make_int(int64_t);
20struct val	*make_str(char *);
21void		 free_value(struct val *);
22int		 is_integer(struct val *, int64_t *, const char **);
23int		 to_integer(struct val *, const char **);
24void		 to_string(struct val *);
25int		 is_zero_or_null(struct val *);
26void		 nexttoken(int);
27__dead void	 error(void);
28struct val	*eval6(void);
29struct val	*eval5(void);
30struct val	*eval4(void);
31struct val	*eval3(void);
32struct val	*eval2(void);
33struct val	*eval1(void);
34struct val	*eval0(void);
35
36enum token {
37	OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
38	NE, LE, GE, OPERAND, EOI
39};
40
41struct val {
42	enum {
43		integer,
44		string
45	} type;
46
47	union {
48		char	       *s;
49		int64_t		i;
50	} u;
51};
52
53enum token	token;
54struct val     *tokval;
55char	      **av;
56
57struct val *
58make_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
72struct val *
73make_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
86void
87free_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 */
96int
97is_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 */
121int
122to_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 */
144void
145to_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
159int
160is_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
168void
169nexttoken(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
209error(void)
210{
211	errx(2, "syntax error");
212}
213
214struct val *
215eval6(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 */
234struct val *
235eval5(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 */
289struct val *
290eval4(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 */
336struct val *
337eval3(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 */
375struct val *
376eval2(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 */
448struct val *
449eval1(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 */
471struct val *
472eval0(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
493int
494main(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