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