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