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