VirtualBox

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

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

kmk: make sure alloca.h gets included (Solaris again).

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