VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/expr.c@ 1708

Last change on this file since 1708 was 1708, checked in by bird, 16 years ago

kmk_expr: no dead, please.

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette