blob: cbbd4cd0399649e12cb89889bebd6d34ab1b1d26 [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>.
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010 * Aug 2003 Vladimir Oleynik - reduced 464 bytes.
Eric Andersen1b355eb2000-09-05 17:37:48 +000011 *
Eric Andersenaff114c2004-04-14 17:51:38 +000012 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
Eric Andersen1b355eb2000-09-05 17:37:48 +000015 * (at your option) any later version.
16 *
Eric Andersenaff114c2004-04-14 17:51:38 +000017 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
Eric Andersen1b355eb2000-09-05 17:37:48 +000021 *
Eric Andersenaff114c2004-04-14 17:51:38 +000022 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Eric Andersen1b355eb2000-09-05 17:37:48 +000025 *
26 */
27
28/* This program evaluates expressions. Each token (operator, operand,
Eric Andersenaff114c2004-04-14 17:51:38 +000029 * parenthesis) of the expression must be a separate argument. The
Eric Andersen1b355eb2000-09-05 17:37:48 +000030 * parser used is a reasonably general one, though any incarnation of
31 * it is language-specific. It is especially nice for expressions.
32 *
33 * No parse tree is needed; a new node is evaluated immediately.
34 * One function can handle multiple operators all of equal precedence,
35 * provided they all associate ((x op x) op x). */
36
Mark Whitley827e45c2001-03-09 23:59:51 +000037/* no getopt needed */
38
Eric Andersen1b355eb2000-09-05 17:37:48 +000039#include <stdio.h>
Eric Andersened3ef502001-01-27 08:24:39 +000040#include <string.h>
41#include <stdlib.h>
42#include <regex.h>
Eric Andersen1b355eb2000-09-05 17:37:48 +000043#include <sys/types.h>
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000044#include <errno.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000045#include "busybox.h"
Eric Andersen1b355eb2000-09-05 17:37:48 +000046
Eric Andersen1b355eb2000-09-05 17:37:48 +000047
48/* The kinds of value we can have. */
49enum valtype {
50 integer,
51 string
52};
53typedef enum valtype TYPE;
54
55/* A value is.... */
56struct valinfo {
57 TYPE type; /* Which kind. */
58 union { /* The value itself. */
59 int i;
60 char *s;
61 } u;
62};
63typedef struct valinfo VALUE;
64
65/* The arguments given to the program, minus the program name. */
66static char **args;
67
68static VALUE *docolon (VALUE *sv, VALUE *pv);
69static VALUE *eval (void);
70static VALUE *int_value (int i);
71static VALUE *str_value (char *s);
72static int nextarg (char *str);
73static int null (VALUE *v);
74static int toarith (VALUE *v);
75static void freev (VALUE *v);
76static void tostring (VALUE *v);
77
78int expr_main (int argc, char **argv)
79{
80 VALUE *v;
81
82 if (argc == 1) {
Manuel Novoa III cad53642003-03-19 09:13:01 +000083 bb_error_msg_and_die("too few arguments");
Eric Andersen1b355eb2000-09-05 17:37:48 +000084 }
85
86 args = argv + 1;
87
88 v = eval ();
89 if (*args)
Manuel Novoa III cad53642003-03-19 09:13:01 +000090 bb_error_msg_and_die ("syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +000091
92 if (v->type == integer)
93 printf ("%d\n", v->u.i);
Matt Kraai59df6f72001-05-16 14:21:09 +000094 else
95 puts (v->u.s);
Eric Andersen1b355eb2000-09-05 17:37:48 +000096
97 exit (null (v));
98}
99
100/* Return a VALUE for I. */
101
102static VALUE *int_value (int i)
103{
104 VALUE *v;
105
106 v = xmalloc (sizeof(VALUE));
107 v->type = integer;
108 v->u.i = i;
109 return v;
110}
111
112/* Return a VALUE for S. */
113
114static VALUE *str_value (char *s)
115{
116 VALUE *v;
117
118 v = xmalloc (sizeof(VALUE));
119 v->type = string;
Manuel Novoa III 08386222004-02-01 07:34:28 +0000120 v->u.s = bb_xstrdup (s);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000121 return v;
122}
123
124/* Free VALUE V, including structure components. */
125
126static void freev (VALUE *v)
127{
128 if (v->type == string)
129 free (v->u.s);
130 free (v);
131}
132
133/* Return nonzero if V is a null-string or zero-number. */
134
135static int null (VALUE *v)
136{
137 switch (v->type) {
138 case integer:
139 return v->u.i == 0;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000140 default: /* string: */
Eric Andersen1b355eb2000-09-05 17:37:48 +0000141 return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000142 }
143}
144
145/* Coerce V to a string value (can't fail). */
146
147static void tostring (VALUE *v)
148{
Eric Andersen1b355eb2000-09-05 17:37:48 +0000149 if (v->type == integer) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000150 bb_xasprintf (&(v->u.s), "%d", v->u.i);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000151 v->type = string;
152 }
153}
154
155/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
156
157static int toarith (VALUE *v)
158{
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000159 if(v->type == string) {
Manuel Novoa III 70183852004-01-25 19:47:10 +0000160 int i;
161 char *e;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000162
Manuel Novoa III 70183852004-01-25 19:47:10 +0000163 /* Don't interpret the empty string as an integer. */
164 /* Currently does not worry about overflow or int/long differences. */
165 i = (int) strtol(v->u.s, &e, 10);
166 if ((v->u.s == e) || *e)
167 return 0;
168 free (v->u.s);
169 v->u.i = i;
170 v->type = integer;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000171 }
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000172 return 1;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000173}
174
175/* Return nonzero if the next token matches STR exactly.
176 STR must not be NULL. */
177
178static int
179nextarg (char *str)
180{
181 if (*args == NULL)
182 return 0;
183 return strcmp (*args, str) == 0;
184}
185
186/* The comparison operator handling functions. */
187
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000188static int cmp_common (VALUE *l, VALUE *r, int op)
189{
190 int cmpval;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000191
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000192 if (l->type == string || r->type == string) {
193 tostring (l);
194 tostring (r);
195 cmpval = strcmp (l->u.s, r->u.s);
196 }
197 else
198 cmpval = l->u.i - r->u.i;
199 switch(op) {
200 case '<':
201 return cmpval < 0;
202 case ('L'+'E'):
203 return cmpval <= 0;
204 case '=':
205 return cmpval == 0;
206 case '!':
207 return cmpval != 0;
208 case '>':
209 return cmpval > 0;
210 default: /* >= */
211 return cmpval >= 0;
212 }
213}
Eric Andersen1b355eb2000-09-05 17:37:48 +0000214
215/* The arithmetic operator handling functions. */
216
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000217static int arithmetic_common (VALUE *l, VALUE *r, int op)
218{
219 int li, ri;
220
221 if (!toarith (l) || !toarith (r))
222 bb_error_msg_and_die ("non-numeric argument");
223 li = l->u.i;
224 ri = r->u.i;
225 if((op == '/' || op == '%') && ri == 0)
226 bb_error_msg_and_die ( "division by zero");
227 switch(op) {
228 case '+':
229 return li + ri;
230 case '-':
231 return li - ri;
232 case '*':
233 return li * ri;
234 case '/':
235 return li / ri;
236 default:
237 return li % ri;
238 }
Eric Andersen1b355eb2000-09-05 17:37:48 +0000239}
240
Eric Andersen1b355eb2000-09-05 17:37:48 +0000241/* Do the : operator.
242 SV is the VALUE for the lhs (the string),
243 PV is the VALUE for the rhs (the pattern). */
244
245static VALUE *docolon (VALUE *sv, VALUE *pv)
246{
247 VALUE *v;
248 const char *errmsg;
249 struct re_pattern_buffer re_buffer;
250 struct re_registers re_regs;
251 int len;
252
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
263 len = strlen (pv->u.s);
264 memset (&re_buffer, 0, sizeof (re_buffer));
265 memset (&re_regs, 0, sizeof (re_regs));
266 re_buffer.allocated = 2 * len;
267 re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
268 re_buffer.translate = 0;
269 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
270 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
271 if (errmsg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000272 bb_error_msg_and_die("%s", errmsg);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000273 }
274
275 len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
276 if (len >= 0) {
277 /* Were \(...\) used? */
278 if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
279 sv->u.s[re_regs.end[1]] = '\0';
280 v = str_value (sv->u.s + re_regs.start[1]);
281 }
282 else
283 v = int_value (len);
284 }
285 else {
286 /* Match failed -- return the right kind of null. */
287 if (re_buffer.re_nsub > 0)
288 v = str_value ("");
289 else
290 v = int_value (0);
291 }
292 free (re_buffer.buffer);
293 return v;
294}
295
296/* Handle bare operands and ( expr ) syntax. */
297
298static VALUE *eval7 (void)
299{
300 VALUE *v;
301
302 if (!*args)
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
305 if (nextarg ("(")) {
306 args++;
307 v = eval ();
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 args++;
311 return v;
312 }
313
314 if (nextarg (")"))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000315 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000316
317 return str_value (*args++);
318}
319
320/* Handle match, substr, index, length, and quote keywords. */
321
322static VALUE *eval6 (void)
323{
324 VALUE *l, *r, *v, *i1, *i2;
325
326 if (nextarg ("quote")) {
327 args++;
328 if (!*args)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000329 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000330 return str_value (*args++);
331 }
332 else if (nextarg ("length")) {
333 args++;
334 r = eval6 ();
335 tostring (r);
336 v = int_value (strlen (r->u.s));
337 freev (r);
338 return v;
339 }
340 else if (nextarg ("match")) {
341 args++;
342 l = eval6 ();
343 r = eval6 ();
344 v = docolon (l, r);
345 freev (l);
346 freev (r);
347 return v;
348 }
349 else if (nextarg ("index")) {
350 args++;
351 l = eval6 ();
352 r = eval6 ();
353 tostring (l);
354 tostring (r);
355 v = int_value (strcspn (l->u.s, r->u.s) + 1);
356 if (v->u.i == (int) strlen (l->u.s) + 1)
357 v->u.i = 0;
358 freev (l);
359 freev (r);
360 return v;
361 }
362 else if (nextarg ("substr")) {
363 args++;
364 l = eval6 ();
365 i1 = eval6 ();
366 i2 = eval6 ();
367 tostring (l);
368 if (!toarith (i1) || !toarith (i2)
369 || i1->u.i > (int) strlen (l->u.s)
370 || i1->u.i <= 0 || i2->u.i <= 0)
371 v = str_value ("");
372 else {
373 v = xmalloc (sizeof(VALUE));
374 v->type = string;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000375 v->u.s = bb_xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000376 }
377 freev (l);
378 freev (i1);
379 freev (i2);
380 return v;
381 }
382 else
383 return eval7 ();
384}
385
386/* Handle : operator (pattern matching).
387 Calls docolon to do the real work. */
388
389static VALUE *eval5 (void)
390{
391 VALUE *l, *r, *v;
392
393 l = eval6 ();
394 while (nextarg (":")) {
395 args++;
396 r = eval6 ();
397 v = docolon (l, r);
398 freev (l);
399 freev (r);
400 l = v;
401 }
402 return l;
403}
404
405/* Handle *, /, % operators. */
406
407static VALUE *eval4 (void)
408{
409 VALUE *l, *r;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000410 int op, val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000411
412 l = eval5 ();
413 while (1) {
414 if (nextarg ("*"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000415 op = '*';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000416 else if (nextarg ("/"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000417 op = '/';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000418 else if (nextarg ("%"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000419 op = '%';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000420 else
421 return l;
422 args++;
423 r = eval5 ();
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000424 val = arithmetic_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000425 freev (l);
426 freev (r);
427 l = int_value (val);
428 }
429}
430
431/* Handle +, - operators. */
432
433static VALUE *eval3 (void)
434{
435 VALUE *l, *r;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000436 int op, val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000437
438 l = eval4 ();
439 while (1) {
440 if (nextarg ("+"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000441 op = '+';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000442 else if (nextarg ("-"))
Glenn L McGrath07f6b952003-09-08 23:19:12 +0000443 op = '-';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000444 else
445 return l;
446 args++;
447 r = eval4 ();
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000448 val = arithmetic_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000449 freev (l);
450 freev (r);
451 l = int_value (val);
452 }
453}
454
455/* Handle comparisons. */
456
457static VALUE *eval2 (void)
458{
459 VALUE *l, *r;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000460 int op, val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000461
462 l = eval3 ();
463 while (1) {
464 if (nextarg ("<"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000465 op = '<';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000466 else if (nextarg ("<="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000467 op = 'L'+'E';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000468 else if (nextarg ("=") || nextarg ("=="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000469 op = '=';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000470 else if (nextarg ("!="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000471 op = '!';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000472 else if (nextarg (">="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000473 op = 'G'+'E';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000474 else if (nextarg (">"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000475 op = '>';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000476 else
477 return l;
478 args++;
479 r = eval3 ();
480 toarith (l);
481 toarith (r);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000482 val = cmp_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000483 freev (l);
484 freev (r);
485 l = int_value (val);
486 }
487}
488
489/* Handle &. */
490
491static VALUE *eval1 (void)
492{
493 VALUE *l, *r;
494
495 l = eval2 ();
496 while (nextarg ("&")) {
497 args++;
498 r = eval2 ();
499 if (null (l) || null (r)) {
500 freev (l);
501 freev (r);
502 l = int_value (0);
503 }
504 else
505 freev (r);
506 }
507 return l;
508}
509
510/* Handle |. */
511
512static VALUE *eval (void)
513{
514 VALUE *l, *r;
515
516 l = eval1 ();
517 while (nextarg ("|")) {
518 args++;
519 r = eval1 ();
520 if (null (l)) {
521 freev (l);
522 l = r;
523 }
524 else
525 freev (r);
526 }
527 return l;
528}