source: trunk/src/kmk/kmkbuiltin/expr.c

Last change on this file was 3192, checked in by bird, 7 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
RevLine 
[1703]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
[2113]9#include "config.h"
[1703]10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <locale.h>
14#include <ctype.h>
[1707]15#ifdef KMK_WITH_REGEX
[1703]16#include <regex.h>
[1739]17#endif
[1707]18#include <setjmp.h>
19#include <assert.h>
[2121]20#ifdef HAVE_ALLOCA_H
21# include <alloca.h>
22#endif
[1707]23#include "err.h"
24#include "kmkbuiltin.h"
[1703]25
[3192]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 *);
[1707]31static int is_integer(struct val *, int *);
[3192]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);
[1703]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
[3192]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;
[1703]70
[1707]71
[3192]72static void expr_mem_record_alloc(PEXPRINSTANCE pThis, void *ptr)
[1707]73{
[3192]74 if (!(pThis->num_recorded_allocations & 31)) {
75 void *newtab = realloc(pThis->recorded_allocations, (pThis->num_recorded_allocations + 33) * sizeof(void *));
[1707]76 if (!newtab)
[3192]77 longjmp(pThis->g_expr_jmp, err(pThis->pCtx, 3, NULL));
78 pThis->recorded_allocations = (void **)newtab;
[1707]79 }
[3192]80 pThis->recorded_allocations[pThis->num_recorded_allocations++] = ptr;
[1707]81}
82
83
[3192]84static void expr_mem_record_free(PEXPRINSTANCE pThis, void *ptr)
[1707]85{
[3192]86 int i = pThis->num_recorded_allocations;
[1707]87 while (i-- > 0)
[3192]88 if (pThis->recorded_allocations[i] == ptr) {
89 pThis->num_recorded_allocations--;
90 pThis->recorded_allocations[i] = pThis->recorded_allocations[pThis->num_recorded_allocations];
[1707]91 return;
92 }
93 assert(i >= 0);
94}
95
[3192]96static void expr_mem_init(PEXPRINSTANCE pThis)
[1707]97{
[3192]98 pThis->num_recorded_allocations = 0;
99 pThis->recorded_allocations = NULL;
[1707]100}
101
[3192]102static void expr_mem_cleanup(PEXPRINSTANCE pThis)
[1707]103{
[3192]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;
[1707]109 }
110}
111
112
113static struct val *
[3192]114make_int(PEXPRINSTANCE pThis, int i)
[1703]115{
116 struct val *vp;
117
118 vp = (struct val *) malloc(sizeof(*vp));
[1707]119 if (vp == NULL)
[3192]120 longjmp(pThis->g_expr_jmp, err(pThis->pCtx, 3, NULL));
121 expr_mem_record_alloc(pThis, vp);
[1703]122 vp->type = integer;
123 vp->u.i = i;
124 return vp;
125}
126
127
[1707]128static struct val *
[3192]129make_str(PEXPRINSTANCE pThis, char *s)
[1703]130{
131 struct val *vp;
132
133 vp = (struct val *) malloc(sizeof(*vp));
[1707]134 if (vp == NULL || ((vp->u.s = strdup(s)) == NULL))
[3192]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);
[1703]138 vp->type = string;
139 return vp;
140}
141
142
[1707]143static void
[3192]144free_value(PEXPRINSTANCE pThis, struct val *vp)
[1703]145{
[1707]146 if (vp->type == string) {
[3192]147 expr_mem_record_free(pThis, vp->u.s);
[1703]148 free(vp->u.s);
[1707]149 }
[1703]150 free(vp);
[3192]151 expr_mem_record_free(pThis, vp);
[1703]152}
153
154
155/* determine if vp is an integer; if so, return it's value in *r */
[1707]156static int
[1703]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 */
[1707]198static int
[3192]199to_integer(PEXPRINSTANCE pThis, struct val *vp)
[1703]200{
201 int r;
202
203 if (vp->type == integer)
204 return 1;
205
206 if (is_integer(vp, &r)) {
[3192]207 expr_mem_record_free(pThis, vp->u.s);
[1703]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 */
[1707]219static void
[3192]220to_string(PEXPRINSTANCE pThis, struct val *vp)
[1703]221{
222 char *tmp;
223
224 if (vp->type == string)
225 return;
226
227 if (asprintf(&tmp, "%d", vp->u.i) == -1)
[3192]228 longjmp(pThis->g_expr_jmp, err(pThis->pCtx, 3, NULL));
229 expr_mem_record_alloc(pThis, tmp);
[1703]230
231 vp->type = string;
232 vp->u.s = tmp;
233}
234
[1707]235static int
[3192]236is_zero_or_null(PEXPRINSTANCE pThis, struct val *vp)
[1703]237{
238 if (vp->type == integer) {
239 return (vp->u.i == 0);
240 } else {
[3192]241 return (*vp->u.s == 0 || (to_integer(pThis, vp) && vp->u.i == 0));
[1703]242 }
243 /* NOTREACHED */
244}
245
[1707]246static void
[3192]247nexttoken(PEXPRINSTANCE pThis, int pat)
[1703]248{
249 char *p;
250
[3192]251 if ((p = *pThis->av) == NULL) {
252 pThis->token = EOI;
[1703]253 return;
254 }
[3192]255 pThis->av++;
[1703]256
[1739]257
[1703]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) {
[3192]264 pThis->token = i - x;
[1703]265 return;
266 }
267 } else if (p[1] == '=' && p[2] == '\0') {
268 switch (*p) {
269 case '<':
[3192]270 pThis->token = LE;
[1703]271 return;
272 case '>':
[3192]273 pThis->token = GE;
[1703]274 return;
275 case '!':
[3192]276 pThis->token = NE;
[1703]277 return;
278 }
279 }
280 }
[3192]281 pThis->tokval = make_str(pThis, p);
282 pThis->token = OPERAND;
[1703]283 return;
284}
285
[2591]286#ifdef __GNUC__
287__attribute__((noreturn))
288#endif
[3140]289#ifdef _MSC_VER
290__declspec(noreturn)
291#endif
[1707]292static void
[3192]293error(PEXPRINSTANCE pThis)
[1703]294{
[3192]295 longjmp(pThis->g_expr_jmp, errx(pThis->pCtx, 2, "syntax error"));
[1703]296 /* NOTREACHED */
297}
298
[1707]299static struct val *
[3192]300eval6(PEXPRINSTANCE pThis)
[1703]301{
302 struct val *v;
303
[3192]304 if (pThis->token == OPERAND) {
305 nexttoken(pThis, 0);
306 return pThis->tokval;
[1703]307
[3192]308 } else if (pThis->token == RP) {
309 nexttoken(pThis, 0);
310 v = eval0(pThis);
[1703]311
[3192]312 if (pThis->token != LP) {
313 error(pThis);
[1703]314 /* NOTREACHED */
315 }
[3192]316 nexttoken(pThis, 0);
[1703]317 return v;
318 } else {
[3192]319 error(pThis);
[1703]320 }
321 /* NOTREACHED */
322}
323
324/* Parse and evaluate match (regex) expressions */
[1707]325static struct val *
[3192]326eval5(PEXPRINSTANCE pThis)
[1703]327{
[1707]328#ifdef KMK_WITH_REGEX
[1703]329 regex_t rp;
330 regmatch_t rm[2];
331 char errbuf[256];
332 int eval;
[1739]333 struct val *r;
[1703]334 struct val *v;
[1739]335#endif
336 struct val *l;
[1703]337
[3192]338 l = eval6(pThis);
339 while (pThis->token == MATCH) {
[1739]340#ifdef KMK_WITH_REGEX
[3192]341 nexttoken(pThis, 1);
342 r = eval6(pThis);
[1703]343
344 /* coerce to both arguments to strings */
[3192]345 to_string(pThis, l);
346 to_string(pThis, r);
[1703]347
348 /* compile regular expression */
349 if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
350 regerror(eval, &rp, errbuf, sizeof(errbuf));
[3192]351 longjmp(pThis->g_expr_jmp, errx(pThis->pCtx, 2, "%s", errbuf));
[1703]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';
[3192]359 v = make_str(pThis, l->u.s + rm[1].rm_so);
[1703]360
361 } else {
[3192]362 v = make_int(pThis, (int)(rm[0].rm_eo - rm[0].rm_so));
[1703]363 }
364 } else {
365 if (rp.re_nsub == 0) {
[3192]366 v = make_int(pThis, 0);
[1703]367 } else {
[3192]368 v = make_str(pThis, "");
[1703]369 }
370 }
371
372 /* free arguments and pattern buffer */
[3192]373 free_value(pThis, l);
374 free_value(pThis, r);
[1703]375 regfree(&rp);
376
377 l = v;
[1739]378#else
[3192]379 longjmp(pThis->g_expr_jmp, errx(pThis->pCtx, 2, "regex not supported, sorry."));
[1739]380#endif
[1703]381 }
382
383 return l;
384}
385
386/* Parse and evaluate multiplication and division expressions */
[1707]387static struct val *
[3192]388eval4(PEXPRINSTANCE pThis)
[1703]389{
390 struct val *l, *r;
391 enum token op;
392
[3192]393 l = eval5(pThis);
394 while ((op = pThis->token) == MUL || op == DIV || op == MOD) {
395 nexttoken(pThis, 0);
396 r = eval5(pThis);
[1703]397
[3192]398 if (!to_integer(pThis, l) || !to_integer(pThis, r)) {
399 longjmp(pThis->g_expr_jmp, errx(pThis->pCtx, 2, "non-numeric argument"));
[1703]400 }
401
402 if (op == MUL) {
403 l->u.i *= r->u.i;
404 } else {
405 if (r->u.i == 0) {
[3192]406 longjmp(pThis->g_expr_jmp, errx(pThis->pCtx, 2, "division by zero"));
[1703]407 }
408 if (op == DIV) {
409 l->u.i /= r->u.i;
410 } else {
411 l->u.i %= r->u.i;
412 }
413 }
414
[3192]415 free_value(pThis, r);
[1703]416 }
417
418 return l;
419}
420
421/* Parse and evaluate addition and subtraction expressions */
[1707]422static struct val *
[3192]423eval3(PEXPRINSTANCE pThis)
[1703]424{
425 struct val *l, *r;
426 enum token op;
427
[3192]428 l = eval4(pThis);
429 while ((op = pThis->token) == ADD || op == SUB) {
430 nexttoken(pThis, 0);
431 r = eval4(pThis);
[1703]432
[3192]433 if (!to_integer(pThis, l) || !to_integer(pThis, r)) {
434 longjmp(pThis->g_expr_jmp, errx(pThis->pCtx, 2, "non-numeric argument"));
[1703]435 }
436
437 if (op == ADD) {
438 l->u.i += r->u.i;
439 } else {
440 l->u.i -= r->u.i;
441 }
442
[3192]443 free_value(pThis, r);
[1703]444 }
445
446 return l;
447}
448
449/* Parse and evaluate comparison expressions */
[1707]450static struct val *
[3192]451eval2(PEXPRINSTANCE pThis)
[1703]452{
453 struct val *l, *r;
454 enum token op;
455 int v = 0, li, ri;
456
[3192]457 l = eval3(pThis);
458 while ((op = pThis->token) == EQ || op == NE || op == LT || op == GT ||
[1703]459 op == LE || op == GE) {
[3192]460 nexttoken(pThis, 0);
461 r = eval3(pThis);
[1703]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 {
[3192]487 to_string(pThis, l);
488 to_string(pThis, r);
[1703]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
[3192]514 free_value(pThis, l);
515 free_value(pThis, r);
516 l = make_int(pThis, v);
[1703]517 }
518
519 return l;
520}
521
522/* Parse and evaluate & expressions */
[1707]523static struct val *
[3192]524eval1(PEXPRINSTANCE pThis)
[1703]525{
526 struct val *l, *r;
527
[3192]528 l = eval2(pThis);
529 while (pThis->token == AND) {
530 nexttoken(pThis, 0);
531 r = eval2(pThis);
[1703]532
[3192]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);
[1703]537 } else {
[3192]538 free_value(pThis, r);
[1703]539 }
540 }
541
542 return l;
543}
544
545/* Parse and evaluate | expressions */
[1707]546static struct val *
[3192]547eval0(PEXPRINSTANCE pThis)
[1703]548{
549 struct val *l, *r;
550
[3192]551 l = eval1(pThis);
552 while (pThis->token == OR) {
553 nexttoken(pThis, 0);
554 r = eval1(pThis);
[1703]555
[3192]556 if (is_zero_or_null(pThis, l)) {
557 free_value(pThis, l);
[1703]558 l = r;
559 } else {
[3192]560 free_value(pThis, r);
[1703]561 }
562 }
563
564 return l;
565}
566
567
568int
[3192]569kmk_builtin_expr(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
[1703]570{
[3192]571 EXPRINSTANCE This;
572 struct val *vp;
[1707]573 int rval;
[1703]574
575 if (argc > 1 && !strcmp(argv[1], "--"))
576 argv++;
577
[3192]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);
[1703]584
[3192]585 rval = setjmp(This.g_expr_jmp);
[1707]586 if (!rval) {
[3192]587 nexttoken(&This, 0);
588 vp = eval0(&This);
[1739]589
[3192]590 if (This.token != EOI) {
591 error(&This);
[1707]592 /* NOTREACHED */
593 }
[1739]594
[1707]595 if (vp->type == integer)
[3192]596 kmk_builtin_ctx_printf(pCtx, 0, "%d\n", vp->u.i);
[1707]597 else
[3192]598 kmk_builtin_ctx_printf(pCtx, 0, "%s\n", vp->u.s);
[1739]599
[3192]600 rval = is_zero_or_null(&This, vp);
[1739]601 }
[1707]602 /* else: longjmp */
[1703]603
[1707]604 /* cleanup */
[3192]605 expr_mem_cleanup(&This);
[1707]606 return rval;
[1703]607}
[3192]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.