blob: 7251960392aa540803227238c4a697067e4800c0 [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 *
Eric Andersenc7bda1c2004-03-15 08:29:22 +00008 * Busybox modifications
Eric Andersen1b355eb2000-09-05 17:37:48 +00009 * Copyright (c) 2000 Edward Betts <edward@debian.org>.
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +000010 * Copyright (C) 2003-2005 Vladimir Oleynik <dzo@simtreas.ru>
11 * - reduced 464 bytes.
12 * - 64 math support
Eric Andersen1b355eb2000-09-05 17:37:48 +000013 *
"Robert P. J. Day"801ab142006-07-12 07:56:04 +000014 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
Eric Andersen1b355eb2000-09-05 17:37:48 +000015 */
16
17/* This program evaluates expressions. Each token (operator, operand,
Eric Andersenaff114c2004-04-14 17:51:38 +000018 * parenthesis) of the expression must be a separate argument. The
Eric Andersen1b355eb2000-09-05 17:37:48 +000019 * parser used is a reasonably general one, though any incarnation of
20 * it is language-specific. It is especially nice for expressions.
21 *
22 * No parse tree is needed; a new node is evaluated immediately.
23 * One function can handle multiple operators all of equal precedence,
24 * provided they all associate ((x op x) op x). */
25
Mark Whitley827e45c2001-03-09 23:59:51 +000026/* no getopt needed */
27
Eric Andersen1b355eb2000-09-05 17:37:48 +000028#include <stdio.h>
Eric Andersened3ef502001-01-27 08:24:39 +000029#include <string.h>
30#include <stdlib.h>
31#include <regex.h>
Eric Andersen1b355eb2000-09-05 17:37:48 +000032#include <sys/types.h>
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000033#include <errno.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000034#include "busybox.h"
Eric Andersen1b355eb2000-09-05 17:37:48 +000035
Eric Andersen1b355eb2000-09-05 17:37:48 +000036
37/* The kinds of value we can have. */
38enum valtype {
39 integer,
40 string
41};
42typedef enum valtype TYPE;
43
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +000044#if ENABLE_EXPR_MATH_SUPPORT_64
45typedef int64_t arith_t;
46#define PF_REZ "ll"
Eric Andersen5e678872006-01-30 19:48:23 +000047#define PF_REZ_TYPE (long long)
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +000048#define STRTOL(s, e, b) strtoll(s, e, b)
49#else
50typedef long arith_t;
51#define PF_REZ "l"
Eric Andersen5e678872006-01-30 19:48:23 +000052#define PF_REZ_TYPE (long)
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +000053#define STRTOL(s, e, b) strtol(s, e, b)
54#endif
55
Eric Andersen1b355eb2000-09-05 17:37:48 +000056/* A value is.... */
57struct valinfo {
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +000058 TYPE type; /* Which kind. */
59 union { /* The value itself. */
60 arith_t i;
Eric Andersen1b355eb2000-09-05 17:37:48 +000061 char *s;
62 } u;
63};
64typedef struct valinfo VALUE;
65
66/* The arguments given to the program, minus the program name. */
67static char **args;
68
69static VALUE *docolon (VALUE *sv, VALUE *pv);
70static VALUE *eval (void);
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +000071static VALUE *int_value (arith_t i);
Eric Andersen1b355eb2000-09-05 17:37:48 +000072static VALUE *str_value (char *s);
73static int nextarg (char *str);
74static int null (VALUE *v);
75static int toarith (VALUE *v);
76static void freev (VALUE *v);
77static void tostring (VALUE *v);
78
79int expr_main (int argc, char **argv)
80{
81 VALUE *v;
82
83 if (argc == 1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000084 bb_error_msg_and_die("too few arguments");
Eric Andersen1b355eb2000-09-05 17:37:48 +000085 }
86
87 args = argv + 1;
88
89 v = eval ();
90 if (*args)
Manuel Novoa III cad53642003-03-19 09:13:01 +000091 bb_error_msg_and_die ("syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +000092
93 if (v->type == integer)
Eric Andersen5e678872006-01-30 19:48:23 +000094 printf ("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
Matt Kraai59df6f72001-05-16 14:21:09 +000095 else
96 puts (v->u.s);
Eric Andersen1b355eb2000-09-05 17:37:48 +000097
98 exit (null (v));
99}
100
101/* Return a VALUE for I. */
102
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000103static VALUE *int_value (arith_t i)
Eric Andersen1b355eb2000-09-05 17:37:48 +0000104{
105 VALUE *v;
106
107 v = xmalloc (sizeof(VALUE));
108 v->type = integer;
109 v->u.i = i;
110 return v;
111}
112
113/* Return a VALUE for S. */
114
115static VALUE *str_value (char *s)
116{
117 VALUE *v;
118
119 v = xmalloc (sizeof(VALUE));
120 v->type = string;
Manuel Novoa III 08386222004-02-01 07:34:28 +0000121 v->u.s = bb_xstrdup (s);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000122 return v;
123}
124
125/* Free VALUE V, including structure components. */
126
127static void freev (VALUE *v)
128{
129 if (v->type == string)
130 free (v->u.s);
131 free (v);
132}
133
134/* Return nonzero if V is a null-string or zero-number. */
135
136static int null (VALUE *v)
137{
138 switch (v->type) {
139 case integer:
140 return v->u.i == 0;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000141 default: /* string: */
Eric Andersen1b355eb2000-09-05 17:37:48 +0000142 return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000143 }
144}
145
146/* Coerce V to a string value (can't fail). */
147
148static void tostring (VALUE *v)
149{
Eric Andersen1b355eb2000-09-05 17:37:48 +0000150 if (v->type == integer) {
Eric Andersen5e678872006-01-30 19:48:23 +0000151 v->u.s = bb_xasprintf ("%" PF_REZ "d", PF_REZ_TYPE v->u.i);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000152 v->type = string;
153 }
154}
155
156/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
157
158static int toarith (VALUE *v)
159{
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000160 if(v->type == string) {
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000161 arith_t i;
Manuel Novoa III 70183852004-01-25 19:47:10 +0000162 char *e;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000163
Manuel Novoa III 70183852004-01-25 19:47:10 +0000164 /* Don't interpret the empty string as an integer. */
165 /* Currently does not worry about overflow or int/long differences. */
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000166 i = STRTOL(v->u.s, &e, 10);
Manuel Novoa III 70183852004-01-25 19:47:10 +0000167 if ((v->u.s == e) || *e)
168 return 0;
169 free (v->u.s);
170 v->u.i = i;
171 v->type = integer;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000172 }
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000173 return 1;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000174}
175
176/* Return nonzero if the next token matches STR exactly.
177 STR must not be NULL. */
178
179static int
180nextarg (char *str)
181{
182 if (*args == NULL)
183 return 0;
184 return strcmp (*args, str) == 0;
185}
186
187/* The comparison operator handling functions. */
188
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000189static int cmp_common (VALUE *l, VALUE *r, int op)
190{
191 int cmpval;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000192
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000193 if (l->type == string || r->type == string) {
194 tostring (l);
195 tostring (r);
196 cmpval = strcmp (l->u.s, r->u.s);
197 }
198 else
199 cmpval = l->u.i - r->u.i;
200 switch(op) {
201 case '<':
202 return cmpval < 0;
203 case ('L'+'E'):
204 return cmpval <= 0;
205 case '=':
206 return cmpval == 0;
207 case '!':
208 return cmpval != 0;
209 case '>':
210 return cmpval > 0;
211 default: /* >= */
212 return cmpval >= 0;
213 }
214}
Eric Andersen1b355eb2000-09-05 17:37:48 +0000215
216/* The arithmetic operator handling functions. */
217
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000218static arith_t arithmetic_common (VALUE *l, VALUE *r, int op)
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000219{
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000220 arith_t li, ri;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000221
222 if (!toarith (l) || !toarith (r))
223 bb_error_msg_and_die ("non-numeric argument");
224 li = l->u.i;
225 ri = r->u.i;
226 if((op == '/' || op == '%') && ri == 0)
227 bb_error_msg_and_die ( "division by zero");
228 switch(op) {
229 case '+':
230 return li + ri;
231 case '-':
232 return li - ri;
233 case '*':
234 return li * ri;
235 case '/':
236 return li / ri;
237 default:
238 return li % ri;
239 }
Eric Andersen1b355eb2000-09-05 17:37:48 +0000240}
241
Eric Andersen1b355eb2000-09-05 17:37:48 +0000242/* Do the : operator.
243 SV is the VALUE for the lhs (the string),
244 PV is the VALUE for the rhs (the pattern). */
245
246static VALUE *docolon (VALUE *sv, VALUE *pv)
247{
248 VALUE *v;
Rob Landley540d3f62005-05-09 21:42:42 +0000249 regex_t re_buffer;
250 const int NMATCH = 2;
251 regmatch_t re_regs[NMATCH];
Eric Andersen1b355eb2000-09-05 17:37:48 +0000252
253 tostring (sv);
254 tostring (pv);
255
256 if (pv->u.s[0] == '^') {
257 fprintf (stderr, "\
258warning: unportable BRE: `%s': using `^' as the first character\n\
259of a basic regular expression is not portable; it is being ignored",
260 pv->u.s);
261 }
262
Eric Andersen1b355eb2000-09-05 17:37:48 +0000263 memset (&re_buffer, 0, sizeof (re_buffer));
Rob Landley540d3f62005-05-09 21:42:42 +0000264 memset (re_regs, 0, sizeof (*re_regs));
265 if( regcomp (&re_buffer, pv->u.s, 0) != 0 )
266 bb_error_msg_and_die("Invalid regular expression");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000267
Rob Landley540d3f62005-05-09 21:42:42 +0000268 /* expr uses an anchored pattern match, so check that there was a
269 * match and that the match starts at offset 0. */
270 if (regexec (&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH &&
271 re_regs[0].rm_so == 0) {
Eric Andersen1b355eb2000-09-05 17:37:48 +0000272 /* Were \(...\) used? */
Rob Landley540d3f62005-05-09 21:42:42 +0000273 if (re_buffer.re_nsub > 0) {
274 sv->u.s[re_regs[1].rm_eo] = '\0';
275 v = str_value (sv->u.s + re_regs[1].rm_so);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000276 }
277 else
Rob Landley540d3f62005-05-09 21:42:42 +0000278 v = int_value (re_regs[0].rm_eo);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000279 }
280 else {
281 /* Match failed -- return the right kind of null. */
282 if (re_buffer.re_nsub > 0)
283 v = str_value ("");
284 else
285 v = int_value (0);
286 }
Eric Andersen1b355eb2000-09-05 17:37:48 +0000287 return v;
288}
289
290/* Handle bare operands and ( expr ) syntax. */
291
292static VALUE *eval7 (void)
293{
294 VALUE *v;
295
296 if (!*args)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000297 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000298
299 if (nextarg ("(")) {
300 args++;
301 v = eval ();
302 if (!nextarg (")"))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000303 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000304 args++;
305 return v;
306 }
307
308 if (nextarg (")"))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000309 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000310
311 return str_value (*args++);
312}
313
314/* Handle match, substr, index, length, and quote keywords. */
315
316static VALUE *eval6 (void)
317{
318 VALUE *l, *r, *v, *i1, *i2;
319
320 if (nextarg ("quote")) {
321 args++;
322 if (!*args)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000323 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000324 return str_value (*args++);
325 }
326 else if (nextarg ("length")) {
327 args++;
328 r = eval6 ();
329 tostring (r);
330 v = int_value (strlen (r->u.s));
331 freev (r);
332 return v;
333 }
334 else if (nextarg ("match")) {
335 args++;
336 l = eval6 ();
337 r = eval6 ();
338 v = docolon (l, r);
339 freev (l);
340 freev (r);
341 return v;
342 }
343 else if (nextarg ("index")) {
344 args++;
345 l = eval6 ();
346 r = eval6 ();
347 tostring (l);
348 tostring (r);
349 v = int_value (strcspn (l->u.s, r->u.s) + 1);
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000350 if (v->u.i == (arith_t) strlen (l->u.s) + 1)
Eric Andersen1b355eb2000-09-05 17:37:48 +0000351 v->u.i = 0;
352 freev (l);
353 freev (r);
354 return v;
355 }
356 else if (nextarg ("substr")) {
357 args++;
358 l = eval6 ();
359 i1 = eval6 ();
360 i2 = eval6 ();
361 tostring (l);
362 if (!toarith (i1) || !toarith (i2)
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000363 || i1->u.i > (arith_t) strlen (l->u.s)
Eric Andersen1b355eb2000-09-05 17:37:48 +0000364 || i1->u.i <= 0 || i2->u.i <= 0)
365 v = str_value ("");
366 else {
367 v = xmalloc (sizeof(VALUE));
368 v->type = string;
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000369 v->u.s = bb_xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000370 }
371 freev (l);
372 freev (i1);
373 freev (i2);
374 return v;
375 }
376 else
377 return eval7 ();
378}
379
380/* Handle : operator (pattern matching).
381 Calls docolon to do the real work. */
382
383static VALUE *eval5 (void)
384{
385 VALUE *l, *r, *v;
386
387 l = eval6 ();
388 while (nextarg (":")) {
389 args++;
390 r = eval6 ();
391 v = docolon (l, r);
392 freev (l);
393 freev (r);
394 l = v;
395 }
396 return l;
397}
398
399/* Handle *, /, % operators. */
400
401static VALUE *eval4 (void)
402{
403 VALUE *l, *r;
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000404 int op;
405 arith_t val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000406
407 l = eval5 ();
408 while (1) {
409 if (nextarg ("*"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000410 op = '*';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000411 else if (nextarg ("/"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000412 op = '/';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000413 else if (nextarg ("%"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000414 op = '%';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000415 else
416 return l;
417 args++;
418 r = eval5 ();
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000419 val = arithmetic_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000420 freev (l);
421 freev (r);
422 l = int_value (val);
423 }
424}
425
426/* Handle +, - operators. */
427
428static VALUE *eval3 (void)
429{
430 VALUE *l, *r;
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000431 int op;
432 arith_t val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000433
434 l = eval4 ();
435 while (1) {
436 if (nextarg ("+"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000437 op = '+';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000438 else if (nextarg ("-"))
Glenn L McGrath07f6b952003-09-08 23:19:12 +0000439 op = '-';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000440 else
441 return l;
442 args++;
443 r = eval4 ();
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000444 val = arithmetic_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000445 freev (l);
446 freev (r);
447 l = int_value (val);
448 }
449}
450
451/* Handle comparisons. */
452
453static VALUE *eval2 (void)
454{
455 VALUE *l, *r;
"Vladimir N. Oleynik"8aa9e572006-01-25 13:56:03 +0000456 int op;
457 arith_t val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000458
459 l = eval3 ();
460 while (1) {
461 if (nextarg ("<"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000462 op = '<';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000463 else if (nextarg ("<="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000464 op = 'L'+'E';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000465 else if (nextarg ("=") || nextarg ("=="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000466 op = '=';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000467 else if (nextarg ("!="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000468 op = '!';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000469 else if (nextarg (">="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000470 op = 'G'+'E';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000471 else if (nextarg (">"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000472 op = '>';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000473 else
474 return l;
475 args++;
476 r = eval3 ();
477 toarith (l);
478 toarith (r);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000479 val = cmp_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000480 freev (l);
481 freev (r);
482 l = int_value (val);
483 }
484}
485
486/* Handle &. */
487
488static VALUE *eval1 (void)
489{
490 VALUE *l, *r;
491
492 l = eval2 ();
493 while (nextarg ("&")) {
494 args++;
495 r = eval2 ();
496 if (null (l) || null (r)) {
497 freev (l);
498 freev (r);
499 l = int_value (0);
500 }
501 else
502 freev (r);
503 }
504 return l;
505}
506
507/* Handle |. */
508
509static VALUE *eval (void)
510{
511 VALUE *l, *r;
512
513 l = eval1 ();
514 while (nextarg ("|")) {
515 args++;
516 r = eval1 ();
517 if (null (l)) {
518 freev (l);
519 l = r;
520 }
521 else
522 freev (r);
523 }
524 return l;
525}