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