sync
[bitrig.git] / bin / expr / expr.c
blob085ed7114d38da4efbce7756c5f2ee5267a2fe58
1 /* $OpenBSD: expr.c,v 1.18 2013/03/28 08:40:31 nicm Exp $ */
2 /* $NetBSD: expr.c,v 1.3.6.1 1996/06/04 20:41:47 cgd Exp $ */
4 /*
5 * Written by J.T. Conklin <jtc@netbsd.org>.
6 * Public domain.
7 */
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>
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);
35 enum token {
36 OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
37 NE, LE, GE, OPERAND, EOI
40 struct val {
41 enum {
42 integer,
43 string
44 } type;
46 union {
47 char *s;
48 int i;
49 } u;
52 enum token token;
53 struct val *tokval;
54 char **av;
56 struct val *
57 make_int(int i)
59 struct val *vp;
61 vp = (struct val *) malloc(sizeof(*vp));
62 if (vp == NULL) {
63 err(3, NULL);
65 vp->type = integer;
66 vp->u.i = i;
67 return vp;
71 struct val *
72 make_str(char *s)
74 struct val *vp;
76 vp = (struct val *) malloc(sizeof(*vp));
77 if (vp == NULL || ((vp->u.s = strdup(s)) == NULL)) {
78 err(3, NULL);
80 vp->type = string;
81 return vp;
85 void
86 free_value(struct val *vp)
88 if (vp->type == string)
89 free(vp->u.s);
90 free(vp);
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)
98 char *s;
99 int neg;
100 int i;
102 if (vp->type == integer) {
103 *r = vp->u.i;
104 return 1;
108 * POSIX.2 defines an "integer" as an optional unary minus
109 * followed by digits.
111 s = vp->u.s;
112 i = 0;
114 neg = (*s == '-');
115 if (neg)
116 s++;
118 while (*s) {
119 if (!isdigit(*s))
120 return 0;
122 i *= 10;
123 i += *s - '0';
125 s++;
128 if (neg)
129 i *= -1;
131 *r = i;
132 return 1;
136 /* coerce to vp to an integer */
138 to_integer(struct val *vp)
140 int r;
142 if (vp->type == integer)
143 return 1;
145 if (is_integer(vp, &r)) {
146 free(vp->u.s);
147 vp->u.i = r;
148 vp->type = integer;
149 return 1;
152 return 0;
156 /* coerce to vp to an string */
157 void
158 to_string(struct val *vp)
160 char *tmp;
162 if (vp->type == string)
163 return;
165 if (asprintf(&tmp, "%d", vp->u.i) == -1)
166 err(3, NULL);
168 vp->type = string;
169 vp->u.s = tmp;
173 is_zero_or_null(struct val *vp)
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));
180 /* NOTREACHED */
183 void
184 nexttoken(int pat)
186 char *p;
188 if ((p = *av) == NULL) {
189 token = EOI;
190 return;
192 av++;
195 if (pat == 0 && p[0] != '\0') {
196 if (p[1] == '\0') {
197 const char *x = "|&=<>+-*/%:()";
198 char *i; /* index */
200 if ((i = strchr(x, *p)) != NULL) {
201 token = i - x;
202 return;
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;
218 tokval = make_str(p);
219 token = OPERAND;
220 return;
223 __dead void
224 error(void)
226 errx(2, "syntax error");
227 /* NOTREACHED */
230 struct val *
231 eval6(void)
233 struct val *v;
235 if (token == OPERAND) {
236 nexttoken(0);
237 return tokval;
239 } else if (token == RP) {
240 nexttoken(0);
241 v = eval0();
243 if (token != LP) {
244 error();
245 /* NOTREACHED */
247 nexttoken(0);
248 return v;
249 } else {
250 error();
252 /* NOTREACHED */
255 /* Parse and evaluate match (regex) expressions */
256 struct val *
257 eval5(void)
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;
266 l = eval6();
267 while (token == MATCH) {
268 nexttoken(1);
269 r = eval6();
271 /* coerce to both arguments to strings */
272 to_string(l);
273 to_string(r);
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);
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);
288 } else {
289 v = make_int((int)(rm[0].rm_eo - rm[0].rm_so));
291 } else {
292 if (rp.re_nsub == 0) {
293 v = make_int(0);
294 } else {
295 v = make_str("");
299 /* free arguments and pattern buffer */
300 free_value(l);
301 free_value(r);
302 regfree(&rp);
304 l = v;
307 return l;
310 /* Parse and evaluate multiplication and division expressions */
311 struct val *
312 eval4(void)
314 struct val *l, *r;
315 enum token op;
317 l = eval5();
318 while ((op = token) == MUL || op == DIV || op == MOD) {
319 nexttoken(0);
320 r = eval5();
322 if (!to_integer(l) || !to_integer(r)) {
323 errx(2, "non-numeric argument");
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");
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;
343 free_value(r);
346 return l;
349 /* Parse and evaluate addition and subtraction expressions */
350 struct val *
351 eval3(void)
353 struct val *l, *r;
354 enum token op;
356 l = eval4();
357 while ((op = token) == ADD || op == SUB) {
358 nexttoken(0);
359 r = eval4();
361 if (!to_integer(l) || !to_integer(r)) {
362 errx(2, "non-numeric argument");
365 if (op == ADD) {
366 l->u.i += r->u.i;
367 } else {
368 l->u.i -= r->u.i;
371 free_value(r);
374 return l;
377 /* Parse and evaluate comparison expressions */
378 struct val *
379 eval2(void)
381 struct val *l, *r;
382 enum token op;
383 int v = 0, li, ri;
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();
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;
414 } else {
415 to_string(l);
416 to_string(r);
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;
442 free_value(l);
443 free_value(r);
444 l = make_int(v);
447 return l;
450 /* Parse and evaluate & expressions */
451 struct val *
452 eval1(void)
454 struct val *l, *r;
456 l = eval2();
457 while (token == AND) {
458 nexttoken(0);
459 r = eval2();
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);
470 return l;
473 /* Parse and evaluate | expressions */
474 struct val *
475 eval0(void)
477 struct val *l, *r;
479 l = eval1();
480 while (token == OR) {
481 nexttoken(0);
482 r = eval1();
484 if (is_zero_or_null(l)) {
485 free_value(l);
486 l = r;
487 } else {
488 free_value(r);
492 return l;
497 main(int argc, char *argv[])
499 struct val *vp;
501 (void) setlocale(LC_ALL, "");
503 if (argc > 1 && !strcmp(argv[1], "--"))
504 argv++;
506 av = argv + 1;
508 nexttoken(0);
509 vp = eval0();
511 if (token != EOI) {
512 error();
513 /* NOTREACHED */
516 if (vp->type == integer)
517 printf("%d\n", vp->u.i);
518 else
519 printf("%s\n", vp->u.s);
521 exit(is_zero_or_null(vp));