VirtualBox

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

Last change on this file since 3387 was 3192, checked in by bird, 6 years ago

kmkbuiltin: funnel output thru output.c (usually via err.c).

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

© 2023 Oracle
ContactPrivacy policyTerms of Use