VirtualBox

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

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

kmk_expr: Fixed regex disabling.

  • 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 <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 *r;
321 struct val *v;
322#endif
323 struct val *l;
324
325 l = eval6();
326 while (token == MATCH) {
327#ifdef KMK_WITH_REGEX
328 nexttoken(1);
329 r = eval6();
330
331 /* coerce to both arguments to strings */
332 to_string(l);
333 to_string(r);
334
335 /* compile regular expression */
336 if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
337 regerror(eval, &rp, errbuf, sizeof(errbuf));
338 longjmp(g_expr_jmp, errx(2, "%s", errbuf));
339 }
340
341 /* compare string against pattern -- remember that patterns
342 are anchored to the beginning of the line */
343 if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
344 if (rm[1].rm_so >= 0) {
345 *(l->u.s + rm[1].rm_eo) = '\0';
346 v = make_str(l->u.s + rm[1].rm_so);
347
348 } else {
349 v = make_int((int)(rm[0].rm_eo - rm[0].rm_so));
350 }
351 } else {
352 if (rp.re_nsub == 0) {
353 v = make_int(0);
354 } else {
355 v = make_str("");
356 }
357 }
358
359 /* free arguments and pattern buffer */
360 free_value(l);
361 free_value(r);
362 regfree(&rp);
363
364 l = v;
365#else
366 longjmp(g_expr_jmp, errx(2, "regex not supported, sorry."));
367#endif
368 }
369
370 return l;
371}
372
373/* Parse and evaluate multiplication and division expressions */
374static struct val *
375eval4(void)
376{
377 struct val *l, *r;
378 enum token op;
379
380 l = eval5();
381 while ((op = token) == MUL || op == DIV || op == MOD) {
382 nexttoken(0);
383 r = eval5();
384
385 if (!to_integer(l) || !to_integer(r)) {
386 longjmp(g_expr_jmp, errx(2, "non-numeric argument"));
387 }
388
389 if (op == MUL) {
390 l->u.i *= r->u.i;
391 } else {
392 if (r->u.i == 0) {
393 longjmp(g_expr_jmp, errx(2, "division by zero"));
394 }
395 if (op == DIV) {
396 l->u.i /= r->u.i;
397 } else {
398 l->u.i %= r->u.i;
399 }
400 }
401
402 free_value(r);
403 }
404
405 return l;
406}
407
408/* Parse and evaluate addition and subtraction expressions */
409static struct val *
410eval3(void)
411{
412 struct val *l, *r;
413 enum token op;
414
415 l = eval4();
416 while ((op = token) == ADD || op == SUB) {
417 nexttoken(0);
418 r = eval4();
419
420 if (!to_integer(l) || !to_integer(r)) {
421 longjmp(g_expr_jmp, errx(2, "non-numeric argument"));
422 }
423
424 if (op == ADD) {
425 l->u.i += r->u.i;
426 } else {
427 l->u.i -= r->u.i;
428 }
429
430 free_value(r);
431 }
432
433 return l;
434}
435
436/* Parse and evaluate comparison expressions */
437static struct val *
438eval2(void)
439{
440 struct val *l, *r;
441 enum token op;
442 int v = 0, li, ri;
443
444 l = eval3();
445 while ((op = token) == EQ || op == NE || op == LT || op == GT ||
446 op == LE || op == GE) {
447 nexttoken(0);
448 r = eval3();
449
450 if (is_integer(l, &li) && is_integer(r, &ri)) {
451 switch (op) {
452 case GT:
453 v = (li > ri);
454 break;
455 case GE:
456 v = (li >= ri);
457 break;
458 case LT:
459 v = (li < ri);
460 break;
461 case LE:
462 v = (li <= ri);
463 break;
464 case EQ:
465 v = (li == ri);
466 break;
467 case NE:
468 v = (li != ri);
469 break;
470 default:
471 break;
472 }
473 } else {
474 to_string(l);
475 to_string(r);
476
477 switch (op) {
478 case GT:
479 v = (strcoll(l->u.s, r->u.s) > 0);
480 break;
481 case GE:
482 v = (strcoll(l->u.s, r->u.s) >= 0);
483 break;
484 case LT:
485 v = (strcoll(l->u.s, r->u.s) < 0);
486 break;
487 case LE:
488 v = (strcoll(l->u.s, r->u.s) <= 0);
489 break;
490 case EQ:
491 v = (strcoll(l->u.s, r->u.s) == 0);
492 break;
493 case NE:
494 v = (strcoll(l->u.s, r->u.s) != 0);
495 break;
496 default:
497 break;
498 }
499 }
500
501 free_value(l);
502 free_value(r);
503 l = make_int(v);
504 }
505
506 return l;
507}
508
509/* Parse and evaluate & expressions */
510static struct val *
511eval1(void)
512{
513 struct val *l, *r;
514
515 l = eval2();
516 while (token == AND) {
517 nexttoken(0);
518 r = eval2();
519
520 if (is_zero_or_null(l) || is_zero_or_null(r)) {
521 free_value(l);
522 free_value(r);
523 l = make_int(0);
524 } else {
525 free_value(r);
526 }
527 }
528
529 return l;
530}
531
532/* Parse and evaluate | expressions */
533static struct val *
534eval0(void)
535{
536 struct val *l, *r;
537
538 l = eval1();
539 while (token == OR) {
540 nexttoken(0);
541 r = eval1();
542
543 if (is_zero_or_null(l)) {
544 free_value(l);
545 l = r;
546 } else {
547 free_value(r);
548 }
549 }
550
551 return l;
552}
553
554
555int
556kmk_builtin_expr(int argc, char *argv[], char **envp)
557{
558 struct val *vp;
559 int rval;
560
561 /* re-init globals */
562 token = 0;
563 tokval = 0;
564 av = 0;
565 expr_mem_init();
566
567#ifdef kmk_builtin_expr /* kmk already does this. */
568 (void) setlocale(LC_ALL, "");
569#endif
570
571 if (argc > 1 && !strcmp(argv[1], "--"))
572 argv++;
573
574 av = argv + 1;
575
576 rval = setjmp(g_expr_jmp);
577 if (!rval) {
578 nexttoken(0);
579 vp = eval0();
580
581 if (token != EOI) {
582 error();
583 /* NOTREACHED */
584 }
585
586 if (vp->type == integer)
587 printf("%d\n", vp->u.i);
588 else
589 printf("%s\n", vp->u.s);
590
591 rval = is_zero_or_null(vp);
592 }
593 /* else: longjmp */
594
595 /* cleanup */
596 expr_mem_cleanup();
597 return rval;
598}
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