source: trunk/src/kmk/kmkbuiltin/expr.c@ 1704

Last change on this file since 1704 was 1703, checked in by bird, 17 years ago

kmkbuiltin/expr.c: file revision 1.17 from OpenBSD

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 7.6 KB
Line 
1/* $OpenBSD: expr.c,v 1.17 2006/06/21 18:28:24 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
17struct val *make_int(int);
18struct val *make_str(char *);
19void free_value(struct val *);
20int is_integer(struct val *, int *);
21int to_integer(struct val *);
22void to_string(struct val *);
23int is_zero_or_null(struct val *);
24void nexttoken(int);
25__dead void error(void);
26struct val *eval6(void);
27struct val *eval5(void);
28struct val *eval4(void);
29struct val *eval3(void);
30struct val *eval2(void);
31struct val *eval1(void);
32struct val *eval0(void);
33
34enum token {
35 OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
36 NE, LE, GE, OPERAND, EOI
37};
38
39struct val {
40 enum {
41 integer,
42 string
43 } type;
44
45 union {
46 char *s;
47 int i;
48 } u;
49};
50
51enum token token;
52struct val *tokval;
53char **av;
54
55struct val *
56make_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
70struct val *
71make_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
84void
85free_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 */
94int
95is_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 */
136int
137to_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 */
156void
157to_string(struct val *vp)
158{
159 char *tmp;
160
161 if (vp->type == string)
162 return;
163
164 if (asprintf(&tmp, "%d", vp->u.i) == -1)
165 err(3, NULL);
166
167 vp->type = string;
168 vp->u.s = tmp;
169}
170
171int
172is_zero_or_null(struct val *vp)
173{
174 if (vp->type == integer) {
175 return (vp->u.i == 0);
176 } else {
177 return (*vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0));
178 }
179 /* NOTREACHED */
180}
181
182void
183nexttoken(int pat)
184{
185 char *p;
186
187 if ((p = *av) == NULL) {
188 token = EOI;
189 return;
190 }
191 av++;
192
193
194 if (pat == 0 && p[0] != '\0') {
195 if (p[1] == '\0') {
196 const char *x = "|&=<>+-*/%:()";
197 char *i; /* index */
198
199 if ((i = strchr(x, *p)) != NULL) {
200 token = i - x;
201 return;
202 }
203 } else if (p[1] == '=' && p[2] == '\0') {
204 switch (*p) {
205 case '<':
206 token = LE;
207 return;
208 case '>':
209 token = GE;
210 return;
211 case '!':
212 token = NE;
213 return;
214 }
215 }
216 }
217 tokval = make_str(p);
218 token = OPERAND;
219 return;
220}
221
222__dead void
223error(void)
224{
225 errx(2, "syntax error");
226 /* NOTREACHED */
227}
228
229struct val *
230eval6(void)
231{
232 struct val *v;
233
234 if (token == OPERAND) {
235 nexttoken(0);
236 return tokval;
237
238 } else if (token == RP) {
239 nexttoken(0);
240 v = eval0();
241
242 if (token != LP) {
243 error();
244 /* NOTREACHED */
245 }
246 nexttoken(0);
247 return v;
248 } else {
249 error();
250 }
251 /* NOTREACHED */
252}
253
254/* Parse and evaluate match (regex) expressions */
255struct val *
256eval5(void)
257{
258 regex_t rp;
259 regmatch_t rm[2];
260 char errbuf[256];
261 int eval;
262 struct val *l, *r;
263 struct val *v;
264
265 l = eval6();
266 while (token == MATCH) {
267 nexttoken(1);
268 r = eval6();
269
270 /* coerce to both arguments to strings */
271 to_string(l);
272 to_string(r);
273
274 /* compile regular expression */
275 if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
276 regerror(eval, &rp, errbuf, sizeof(errbuf));
277 errx(2, "%s", errbuf);
278 }
279
280 /* compare string against pattern -- remember that patterns
281 are anchored to the beginning of the line */
282 if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
283 if (rm[1].rm_so >= 0) {
284 *(l->u.s + rm[1].rm_eo) = '\0';
285 v = make_str(l->u.s + rm[1].rm_so);
286
287 } else {
288 v = make_int((int)(rm[0].rm_eo - rm[0].rm_so));
289 }
290 } else {
291 if (rp.re_nsub == 0) {
292 v = make_int(0);
293 } else {
294 v = make_str("");
295 }
296 }
297
298 /* free arguments and pattern buffer */
299 free_value(l);
300 free_value(r);
301 regfree(&rp);
302
303 l = v;
304 }
305
306 return l;
307}
308
309/* Parse and evaluate multiplication and division expressions */
310struct val *
311eval4(void)
312{
313 struct val *l, *r;
314 enum token op;
315
316 l = eval5();
317 while ((op = token) == MUL || op == DIV || op == MOD) {
318 nexttoken(0);
319 r = eval5();
320
321 if (!to_integer(l) || !to_integer(r)) {
322 errx(2, "non-numeric argument");
323 }
324
325 if (op == MUL) {
326 l->u.i *= r->u.i;
327 } else {
328 if (r->u.i == 0) {
329 errx(2, "division by zero");
330 }
331 if (op == DIV) {
332 l->u.i /= r->u.i;
333 } else {
334 l->u.i %= r->u.i;
335 }
336 }
337
338 free_value(r);
339 }
340
341 return l;
342}
343
344/* Parse and evaluate addition and subtraction expressions */
345struct val *
346eval3(void)
347{
348 struct val *l, *r;
349 enum token op;
350
351 l = eval4();
352 while ((op = token) == ADD || op == SUB) {
353 nexttoken(0);
354 r = eval4();
355
356 if (!to_integer(l) || !to_integer(r)) {
357 errx(2, "non-numeric argument");
358 }
359
360 if (op == ADD) {
361 l->u.i += r->u.i;
362 } else {
363 l->u.i -= r->u.i;
364 }
365
366 free_value(r);
367 }
368
369 return l;
370}
371
372/* Parse and evaluate comparison expressions */
373struct val *
374eval2(void)
375{
376 struct val *l, *r;
377 enum token op;
378 int v = 0, li, ri;
379
380 l = eval3();
381 while ((op = token) == EQ || op == NE || op == LT || op == GT ||
382 op == LE || op == GE) {
383 nexttoken(0);
384 r = eval3();
385
386 if (is_integer(l, &li) && is_integer(r, &ri)) {
387 switch (op) {
388 case GT:
389 v = (li > ri);
390 break;
391 case GE:
392 v = (li >= ri);
393 break;
394 case LT:
395 v = (li < ri);
396 break;
397 case LE:
398 v = (li <= ri);
399 break;
400 case EQ:
401 v = (li == ri);
402 break;
403 case NE:
404 v = (li != ri);
405 break;
406 default:
407 break;
408 }
409 } else {
410 to_string(l);
411 to_string(r);
412
413 switch (op) {
414 case GT:
415 v = (strcoll(l->u.s, r->u.s) > 0);
416 break;
417 case GE:
418 v = (strcoll(l->u.s, r->u.s) >= 0);
419 break;
420 case LT:
421 v = (strcoll(l->u.s, r->u.s) < 0);
422 break;
423 case LE:
424 v = (strcoll(l->u.s, r->u.s) <= 0);
425 break;
426 case EQ:
427 v = (strcoll(l->u.s, r->u.s) == 0);
428 break;
429 case NE:
430 v = (strcoll(l->u.s, r->u.s) != 0);
431 break;
432 default:
433 break;
434 }
435 }
436
437 free_value(l);
438 free_value(r);
439 l = make_int(v);
440 }
441
442 return l;
443}
444
445/* Parse and evaluate & expressions */
446struct val *
447eval1(void)
448{
449 struct val *l, *r;
450
451 l = eval2();
452 while (token == AND) {
453 nexttoken(0);
454 r = eval2();
455
456 if (is_zero_or_null(l) || is_zero_or_null(r)) {
457 free_value(l);
458 free_value(r);
459 l = make_int(0);
460 } else {
461 free_value(r);
462 }
463 }
464
465 return l;
466}
467
468/* Parse and evaluate | expressions */
469struct val *
470eval0(void)
471{
472 struct val *l, *r;
473
474 l = eval1();
475 while (token == OR) {
476 nexttoken(0);
477 r = eval1();
478
479 if (is_zero_or_null(l)) {
480 free_value(l);
481 l = r;
482 } else {
483 free_value(r);
484 }
485 }
486
487 return l;
488}
489
490
491int
492main(int argc, char *argv[])
493{
494 struct val *vp;
495
496 (void) setlocale(LC_ALL, "");
497
498 if (argc > 1 && !strcmp(argv[1], "--"))
499 argv++;
500
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}
Note: See TracBrowser for help on using the repository browser.