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