VirtualBox

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

Last change on this file since 3140 was 3140, checked in by bird, 7 years ago

kmk: Merged in changes from GNU make 4.2.1 (2e55f5e4abdc0e38c1d64be703b446695e70b3b6 / https://git.savannah.gnu.org/git/make.git).

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

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