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