xref: /openbsd-src/bin/expr/expr.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*	$OpenBSD: expr.c,v 1.26 2016/10/19 18:20:25 schwarze 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 *);
23 int		 to_integer(struct val *);
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 *
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 *
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
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 it's value in *r */
96 int
97 is_integer(struct val *vp, int64_t *r)
98 {
99 	char	       *s;
100 	int		neg;
101 	int64_t		i;
102 
103 	if (vp->type == integer) {
104 		*r = vp->u.i;
105 		return 1;
106 	}
107 
108 	/*
109 	 * POSIX.2 defines an "integer" as an optional unary minus
110 	 * followed by digits.
111 	 */
112 	s = vp->u.s;
113 	i = 0;
114 
115 	neg = (*s == '-');
116 	if (neg)
117 		s++;
118 
119 	while (*s) {
120 		if (!isdigit((unsigned char)*s))
121 			return 0;
122 
123 		i *= 10;
124 		i += *s - '0';
125 
126 		s++;
127 	}
128 
129 	if (neg)
130 		i *= -1;
131 
132 	*r = i;
133 	return 1;
134 }
135 
136 
137 /* coerce to vp to an integer */
138 int
139 to_integer(struct val *vp)
140 {
141 	int64_t		r;
142 
143 	if (vp->type == integer)
144 		return 1;
145 
146 	if (is_integer(vp, &r)) {
147 		free(vp->u.s);
148 		vp->u.i = r;
149 		vp->type = integer;
150 		return 1;
151 	}
152 
153 	return 0;
154 }
155 
156 
157 /* coerce to vp to an string */
158 void
159 to_string(struct val *vp)
160 {
161 	char	       *tmp;
162 
163 	if (vp->type == string)
164 		return;
165 
166 	if (asprintf(&tmp, "%lld", vp->u.i) == -1)
167 		err(3, NULL);
168 
169 	vp->type = string;
170 	vp->u.s = tmp;
171 }
172 
173 int
174 is_zero_or_null(struct val *vp)
175 {
176 	if (vp->type == integer)
177 		return vp->u.i == 0;
178 	else
179 		return *vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0);
180 }
181 
182 void
183 nexttoken(int pat)
184 {
185 	char	       *p;
186 
187 	if ((p = *av) == NULL) {
188 		token = EOI;
189 		return;
190 	}
191 	av++;
192 
193 
194 	if (pat == 0 && p[0] != '\0') {
195 		if (p[1] == '\0') {
196 			const char     *x = "|&=<>+-*/%:()";
197 			char	       *i;	/* index */
198 
199 			if ((i = strchr(x, *p)) != NULL) {
200 				token = i - x;
201 				return;
202 			}
203 		} else if (p[1] == '=' && p[2] == '\0') {
204 			switch (*p) {
205 			case '<':
206 				token = LE;
207 				return;
208 			case '>':
209 				token = GE;
210 				return;
211 			case '!':
212 				token = NE;
213 				return;
214 			}
215 		}
216 	}
217 	tokval = make_str(p);
218 	token = OPERAND;
219 	return;
220 }
221 
222 __dead void
223 error(void)
224 {
225 	errx(2, "syntax error");
226 }
227 
228 struct val *
229 eval6(void)
230 {
231 	struct val     *v;
232 
233 	if (token == OPERAND) {
234 		nexttoken(0);
235 		return tokval;
236 	} else if (token == RP) {
237 		nexttoken(0);
238 		v = eval0();
239 		if (token != LP)
240 			error();
241 		nexttoken(0);
242 		return v;
243 	} else
244 		error();
245 }
246 
247 /* Parse and evaluate match (regex) expressions */
248 struct val *
249 eval5(void)
250 {
251 	regex_t		rp;
252 	regmatch_t	rm[2];
253 	char		errbuf[256];
254 	int		eval;
255 	struct val     *l, *r;
256 	struct val     *v;
257 
258 	l = eval6();
259 	while (token == MATCH) {
260 		nexttoken(1);
261 		r = eval6();
262 
263 		/* coerce to both arguments to strings */
264 		to_string(l);
265 		to_string(r);
266 
267 		/* compile regular expression */
268 		if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
269 			regerror(eval, &rp, errbuf, sizeof(errbuf));
270 			errx(2, "%s", errbuf);
271 		}
272 
273 		/* compare string against pattern --  remember that patterns
274 		   are anchored to the beginning of the line */
275 		if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
276 			if (rm[1].rm_so >= 0) {
277 				*(l->u.s + rm[1].rm_eo) = '\0';
278 				v = make_str(l->u.s + rm[1].rm_so);
279 
280 			} else {
281 				v = make_int(rm[0].rm_eo - rm[0].rm_so);
282 			}
283 		} else {
284 			if (rp.re_nsub == 0) {
285 				v = make_int(0);
286 			} else {
287 				v = make_str("");
288 			}
289 		}
290 
291 		/* free arguments and pattern buffer */
292 		free_value(l);
293 		free_value(r);
294 		regfree(&rp);
295 
296 		l = v;
297 	}
298 
299 	return l;
300 }
301 
302 /* Parse and evaluate multiplication and division expressions */
303 struct val *
304 eval4(void)
305 {
306 	struct val     *l, *r;
307 	enum token	op;
308 
309 	l = eval5();
310 	while ((op = token) == MUL || op == DIV || op == MOD) {
311 		nexttoken(0);
312 		r = eval5();
313 
314 		if (!to_integer(l) || !to_integer(r)) {
315 			errx(2, "non-numeric argument");
316 		}
317 
318 		if (op == MUL) {
319 			l->u.i *= r->u.i;
320 		} else {
321 			if (r->u.i == 0) {
322 				errx(2, "division by zero");
323 			}
324 			if (op == DIV) {
325 				if (l->u.i != INT64_MIN || r->u.i != -1)
326 					l->u.i /= r->u.i;
327 			} else {
328 				if (l->u.i != INT64_MIN || r->u.i != -1)
329 					l->u.i %= r->u.i;
330 				else
331 					l->u.i = 0;
332 			}
333 		}
334 
335 		free_value(r);
336 	}
337 
338 	return l;
339 }
340 
341 /* Parse and evaluate addition and subtraction expressions */
342 struct val *
343 eval3(void)
344 {
345 	struct val     *l, *r;
346 	enum token	op;
347 
348 	l = eval4();
349 	while ((op = token) == ADD || op == SUB) {
350 		nexttoken(0);
351 		r = eval4();
352 
353 		if (!to_integer(l) || !to_integer(r)) {
354 			errx(2, "non-numeric argument");
355 		}
356 
357 		if (op == ADD) {
358 			l->u.i += r->u.i;
359 		} else {
360 			l->u.i -= r->u.i;
361 		}
362 
363 		free_value(r);
364 	}
365 
366 	return l;
367 }
368 
369 /* Parse and evaluate comparison expressions */
370 struct val *
371 eval2(void)
372 {
373 	struct val     *l, *r;
374 	enum token	op;
375 	int64_t		v = 0, li, ri;
376 
377 	l = eval3();
378 	while ((op = token) == EQ || op == NE || op == LT || op == GT ||
379 	    op == LE || op == GE) {
380 		nexttoken(0);
381 		r = eval3();
382 
383 		if (is_integer(l, &li) && is_integer(r, &ri)) {
384 			switch (op) {
385 			case GT:
386 				v = (li >  ri);
387 				break;
388 			case GE:
389 				v = (li >= ri);
390 				break;
391 			case LT:
392 				v = (li <  ri);
393 				break;
394 			case LE:
395 				v = (li <= ri);
396 				break;
397 			case EQ:
398 				v = (li == ri);
399 				break;
400 			case NE:
401 				v = (li != ri);
402 				break;
403 			default:
404 				break;
405 			}
406 		} else {
407 			to_string(l);
408 			to_string(r);
409 
410 			switch (op) {
411 			case GT:
412 				v = (strcoll(l->u.s, r->u.s) > 0);
413 				break;
414 			case GE:
415 				v = (strcoll(l->u.s, r->u.s) >= 0);
416 				break;
417 			case LT:
418 				v = (strcoll(l->u.s, r->u.s) < 0);
419 				break;
420 			case LE:
421 				v = (strcoll(l->u.s, r->u.s) <= 0);
422 				break;
423 			case EQ:
424 				v = (strcoll(l->u.s, r->u.s) == 0);
425 				break;
426 			case NE:
427 				v = (strcoll(l->u.s, r->u.s) != 0);
428 				break;
429 			default:
430 				break;
431 			}
432 		}
433 
434 		free_value(l);
435 		free_value(r);
436 		l = make_int(v);
437 	}
438 
439 	return l;
440 }
441 
442 /* Parse and evaluate & expressions */
443 struct val *
444 eval1(void)
445 {
446 	struct val     *l, *r;
447 
448 	l = eval2();
449 	while (token == AND) {
450 		nexttoken(0);
451 		r = eval2();
452 
453 		if (is_zero_or_null(l) || is_zero_or_null(r)) {
454 			free_value(l);
455 			free_value(r);
456 			l = make_int(0);
457 		} else {
458 			free_value(r);
459 		}
460 	}
461 
462 	return l;
463 }
464 
465 /* Parse and evaluate | expressions */
466 struct val *
467 eval0(void)
468 {
469 	struct val     *l, *r;
470 
471 	l = eval1();
472 	while (token == OR) {
473 		nexttoken(0);
474 		r = eval1();
475 
476 		if (is_zero_or_null(l)) {
477 			free_value(l);
478 			l = r;
479 		} else {
480 			free_value(r);
481 		}
482 	}
483 
484 	return l;
485 }
486 
487 
488 int
489 main(int argc, char *argv[])
490 {
491 	struct val     *vp;
492 
493 	if (pledge("stdio", NULL) == -1)
494 		err(2, "pledge");
495 
496 	if (argc > 1 && !strcmp(argv[1], "--"))
497 		argv++;
498 
499 	av = argv + 1;
500 
501 	nexttoken(0);
502 	vp = eval0();
503 
504 	if (token != EOI)
505 		error();
506 
507 	if (vp->type == integer)
508 		printf("%lld\n", vp->u.i);
509 	else
510 		printf("%s\n", vp->u.s);
511 
512 	return is_zero_or_null(vp);
513 }
514