blob: 3b454bd3a96be8d508df327965286665661ee263 [file] [log] [blame]
Eric Andersen1b355eb2000-09-05 17:37:48 +00001/* vi: set sw=4 ts=4: */
2/*
3 * Mini expr implementation for busybox
4 *
5 * based on GNU expr Mike Parker.
6 * Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
7 *
8 * Busybox modifications
9 * Copyright (c) 2000 Edward Betts <edward@debian.org>.
10 *
11 * this program is free software; you can redistribute it and/or modify
12 * it under the terms of the gnu general public license as published by
13 * the free software foundation; either version 2 of the license, or
14 * (at your option) any later version.
15 *
16 * this program is distributed in the hope that it will be useful,
17 * but without any warranty; without even the implied warranty of
18 * merchantability or fitness for a particular purpose. see the gnu
19 * general public license for more details.
20 *
21 * you should have received a copy of the gnu general public license
22 * along with this program; if not, write to the free software
23 * foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa
24 *
25 */
26
27/* This program evaluates expressions. Each token (operator, operand,
28 * parenthesis) of the expression must be a seperate argument. The
29 * parser used is a reasonably general one, though any incarnation of
30 * it is language-specific. It is especially nice for expressions.
31 *
32 * No parse tree is needed; a new node is evaluated immediately.
33 * One function can handle multiple operators all of equal precedence,
34 * provided they all associate ((x op x) op x). */
35
Eric Andersen1b355eb2000-09-05 17:37:48 +000036#include <stdio.h>
Eric Andersened3ef502001-01-27 08:24:39 +000037#include <string.h>
38#include <stdlib.h>
39#include <regex.h>
Eric Andersen1b355eb2000-09-05 17:37:48 +000040#include <sys/types.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000041#include "busybox.h"
Eric Andersen1b355eb2000-09-05 17:37:48 +000042
Eric Andersen1b355eb2000-09-05 17:37:48 +000043
44/* The kinds of value we can have. */
45enum valtype {
46 integer,
47 string
48};
49typedef enum valtype TYPE;
50
51/* A value is.... */
52struct valinfo {
53 TYPE type; /* Which kind. */
54 union { /* The value itself. */
55 int i;
56 char *s;
57 } u;
58};
59typedef struct valinfo VALUE;
60
61/* The arguments given to the program, minus the program name. */
62static char **args;
63
64static VALUE *docolon (VALUE *sv, VALUE *pv);
65static VALUE *eval (void);
66static VALUE *int_value (int i);
67static VALUE *str_value (char *s);
68static int nextarg (char *str);
69static int null (VALUE *v);
70static int toarith (VALUE *v);
71static void freev (VALUE *v);
72static void tostring (VALUE *v);
73
74int expr_main (int argc, char **argv)
75{
76 VALUE *v;
77
78 if (argc == 1) {
Matt Kraaidd19c692001-01-31 19:00:21 +000079 error_msg_and_die("too few arguments");
Eric Andersen1b355eb2000-09-05 17:37:48 +000080 }
81
82 args = argv + 1;
83
84 v = eval ();
85 if (*args)
Matt Kraaidd19c692001-01-31 19:00:21 +000086 error_msg_and_die ("syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +000087
88 if (v->type == integer)
89 printf ("%d\n", v->u.i);
90 else
91 printf ("%s\n", v->u.s);
92
93 exit (null (v));
94}
95
96/* Return a VALUE for I. */
97
98static VALUE *int_value (int i)
99{
100 VALUE *v;
101
102 v = xmalloc (sizeof(VALUE));
103 v->type = integer;
104 v->u.i = i;
105 return v;
106}
107
108/* Return a VALUE for S. */
109
110static VALUE *str_value (char *s)
111{
112 VALUE *v;
113
114 v = xmalloc (sizeof(VALUE));
115 v->type = string;
116 v->u.s = strdup (s);
117 return v;
118}
119
120/* Free VALUE V, including structure components. */
121
122static void freev (VALUE *v)
123{
124 if (v->type == string)
125 free (v->u.s);
126 free (v);
127}
128
129/* Return nonzero if V is a null-string or zero-number. */
130
131static int null (VALUE *v)
132{
133 switch (v->type) {
134 case integer:
135 return v->u.i == 0;
136 case string:
137 return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
138 default:
139 abort ();
140 }
141}
142
143/* Coerce V to a string value (can't fail). */
144
145static void tostring (VALUE *v)
146{
147 char *temp;
148
149 if (v->type == integer) {
150 temp = xmalloc (4 * (sizeof (int) / sizeof (char)));
151 sprintf (temp, "%d", v->u.i);
152 v->u.s = temp;
153 v->type = string;
154 }
155}
156
157/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
158
159static int toarith (VALUE *v)
160{
161 int i;
162
163 switch (v->type) {
164 case integer:
165 return 1;
166 case string:
167 i = 0;
168 /* Don't interpret the empty string as an integer. */
169 if (v->u.s == 0)
170 return 0;
171 i = atoi(v->u.s);
172 free (v->u.s);
173 v->u.i = i;
174 v->type = integer;
175 return 1;
176 default:
177 abort ();
178 }
179}
180
181/* Return nonzero if the next token matches STR exactly.
182 STR must not be NULL. */
183
184static int
185nextarg (char *str)
186{
187 if (*args == NULL)
188 return 0;
189 return strcmp (*args, str) == 0;
190}
191
192/* The comparison operator handling functions. */
193
194#define cmpf(name, rel) \
195static int name (l, r) VALUE *l; VALUE *r; \
196{ \
197 if (l->type == string || r->type == string) { \
198 tostring (l); \
199 tostring (r); \
200 return strcmp (l->u.s, r->u.s) rel 0; \
201 } \
202 else \
203 return l->u.i rel r->u.i; \
204}
205 cmpf (less_than, <)
206 cmpf (less_equal, <=)
207 cmpf (equal, ==)
208 cmpf (not_equal, !=)
209 cmpf (greater_equal, >=)
210 cmpf (greater_than, >)
211
212#undef cmpf
213
214/* The arithmetic operator handling functions. */
215
216#define arithf(name, op) \
217static \
218int name (l, r) VALUE *l; VALUE *r; \
219{ \
220 if (!toarith (l) || !toarith (r)) \
Matt Kraaidd19c692001-01-31 19:00:21 +0000221 error_msg_and_die ("non-numeric argument"); \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000222 return l->u.i op r->u.i; \
223}
224
225#define arithdivf(name, op) \
226int name (l, r) VALUE *l; VALUE *r; \
227{ \
228 if (!toarith (l) || !toarith (r)) \
Matt Kraaidd19c692001-01-31 19:00:21 +0000229 error_msg_and_die ( "non-numeric argument"); \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000230 if (r->u.i == 0) \
Matt Kraaidd19c692001-01-31 19:00:21 +0000231 error_msg_and_die ( "division by zero"); \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000232 return l->u.i op r->u.i; \
233}
234
235 arithf (plus, +)
236 arithf (minus, -)
237 arithf (multiply, *)
238 arithdivf (divide, /)
239 arithdivf (mod, %)
240
241#undef arithf
242#undef arithdivf
243
244/* Do the : operator.
245 SV is the VALUE for the lhs (the string),
246 PV is the VALUE for the rhs (the pattern). */
247
248static VALUE *docolon (VALUE *sv, VALUE *pv)
249{
250 VALUE *v;
251 const char *errmsg;
252 struct re_pattern_buffer re_buffer;
253 struct re_registers re_regs;
254 int len;
255
256 tostring (sv);
257 tostring (pv);
258
259 if (pv->u.s[0] == '^') {
260 fprintf (stderr, "\
261warning: unportable BRE: `%s': using `^' as the first character\n\
262of a basic regular expression is not portable; it is being ignored",
263 pv->u.s);
264 }
265
266 len = strlen (pv->u.s);
267 memset (&re_buffer, 0, sizeof (re_buffer));
268 memset (&re_regs, 0, sizeof (re_regs));
269 re_buffer.allocated = 2 * len;
270 re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
271 re_buffer.translate = 0;
272 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
273 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
274 if (errmsg) {
Matt Kraaidd19c692001-01-31 19:00:21 +0000275 error_msg_and_die("%s", errmsg);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000276 }
277
278 len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
279 if (len >= 0) {
280 /* Were \(...\) used? */
281 if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
282 sv->u.s[re_regs.end[1]] = '\0';
283 v = str_value (sv->u.s + re_regs.start[1]);
284 }
285 else
286 v = int_value (len);
287 }
288 else {
289 /* Match failed -- return the right kind of null. */
290 if (re_buffer.re_nsub > 0)
291 v = str_value ("");
292 else
293 v = int_value (0);
294 }
295 free (re_buffer.buffer);
296 return v;
297}
298
299/* Handle bare operands and ( expr ) syntax. */
300
301static VALUE *eval7 (void)
302{
303 VALUE *v;
304
305 if (!*args)
Matt Kraaidd19c692001-01-31 19:00:21 +0000306 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000307
308 if (nextarg ("(")) {
309 args++;
310 v = eval ();
311 if (!nextarg (")"))
Matt Kraaidd19c692001-01-31 19:00:21 +0000312 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000313 args++;
314 return v;
315 }
316
317 if (nextarg (")"))
Matt Kraaidd19c692001-01-31 19:00:21 +0000318 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000319
320 return str_value (*args++);
321}
322
323/* Handle match, substr, index, length, and quote keywords. */
324
325static VALUE *eval6 (void)
326{
327 VALUE *l, *r, *v, *i1, *i2;
328
329 if (nextarg ("quote")) {
330 args++;
331 if (!*args)
Matt Kraaidd19c692001-01-31 19:00:21 +0000332 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000333 return str_value (*args++);
334 }
335 else if (nextarg ("length")) {
336 args++;
337 r = eval6 ();
338 tostring (r);
339 v = int_value (strlen (r->u.s));
340 freev (r);
341 return v;
342 }
343 else if (nextarg ("match")) {
344 args++;
345 l = eval6 ();
346 r = eval6 ();
347 v = docolon (l, r);
348 freev (l);
349 freev (r);
350 return v;
351 }
352 else if (nextarg ("index")) {
353 args++;
354 l = eval6 ();
355 r = eval6 ();
356 tostring (l);
357 tostring (r);
358 v = int_value (strcspn (l->u.s, r->u.s) + 1);
359 if (v->u.i == (int) strlen (l->u.s) + 1)
360 v->u.i = 0;
361 freev (l);
362 freev (r);
363 return v;
364 }
365 else if (nextarg ("substr")) {
366 args++;
367 l = eval6 ();
368 i1 = eval6 ();
369 i2 = eval6 ();
370 tostring (l);
371 if (!toarith (i1) || !toarith (i2)
372 || i1->u.i > (int) strlen (l->u.s)
373 || i1->u.i <= 0 || i2->u.i <= 0)
374 v = str_value ("");
375 else {
376 v = xmalloc (sizeof(VALUE));
377 v->type = string;
378 v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
379 l->u.s + i1->u.i - 1, i2->u.i);
380 v->u.s[i2->u.i] = 0;
381 }
382 freev (l);
383 freev (i1);
384 freev (i2);
385 return v;
386 }
387 else
388 return eval7 ();
389}
390
391/* Handle : operator (pattern matching).
392 Calls docolon to do the real work. */
393
394static VALUE *eval5 (void)
395{
396 VALUE *l, *r, *v;
397
398 l = eval6 ();
399 while (nextarg (":")) {
400 args++;
401 r = eval6 ();
402 v = docolon (l, r);
403 freev (l);
404 freev (r);
405 l = v;
406 }
407 return l;
408}
409
410/* Handle *, /, % operators. */
411
412static VALUE *eval4 (void)
413{
414 VALUE *l, *r;
415 int (*fxn) (), val;
416
417 l = eval5 ();
418 while (1) {
419 if (nextarg ("*"))
420 fxn = multiply;
421 else if (nextarg ("/"))
422 fxn = divide;
423 else if (nextarg ("%"))
424 fxn = mod;
425 else
426 return l;
427 args++;
428 r = eval5 ();
429 val = (*fxn) (l, r);
430 freev (l);
431 freev (r);
432 l = int_value (val);
433 }
434}
435
436/* Handle +, - operators. */
437
438static VALUE *eval3 (void)
439{
440 VALUE *l, *r;
441 int (*fxn) (), val;
442
443 l = eval4 ();
444 while (1) {
445 if (nextarg ("+"))
446 fxn = plus;
447 else if (nextarg ("-"))
448 fxn = minus;
449 else
450 return l;
451 args++;
452 r = eval4 ();
453 val = (*fxn) (l, r);
454 freev (l);
455 freev (r);
456 l = int_value (val);
457 }
458}
459
460/* Handle comparisons. */
461
462static VALUE *eval2 (void)
463{
464 VALUE *l, *r;
465 int (*fxn) (), val;
466
467 l = eval3 ();
468 while (1) {
469 if (nextarg ("<"))
470 fxn = less_than;
471 else if (nextarg ("<="))
472 fxn = less_equal;
473 else if (nextarg ("=") || nextarg ("=="))
474 fxn = equal;
475 else if (nextarg ("!="))
476 fxn = not_equal;
477 else if (nextarg (">="))
478 fxn = greater_equal;
479 else if (nextarg (">"))
480 fxn = greater_than;
481 else
482 return l;
483 args++;
484 r = eval3 ();
485 toarith (l);
486 toarith (r);
487 val = (*fxn) (l, r);
488 freev (l);
489 freev (r);
490 l = int_value (val);
491 }
492}
493
494/* Handle &. */
495
496static VALUE *eval1 (void)
497{
498 VALUE *l, *r;
499
500 l = eval2 ();
501 while (nextarg ("&")) {
502 args++;
503 r = eval2 ();
504 if (null (l) || null (r)) {
505 freev (l);
506 freev (r);
507 l = int_value (0);
508 }
509 else
510 freev (r);
511 }
512 return l;
513}
514
515/* Handle |. */
516
517static VALUE *eval (void)
518{
519 VALUE *l, *r;
520
521 l = eval1 ();
522 while (nextarg ("|")) {
523 args++;
524 r = eval1 ();
525 if (null (l)) {
526 freev (l);
527 l = r;
528 }
529 else
530 freev (r);
531 }
532 return l;
533}