blob: f58f4b06293989c97dbee8e883db1399e54c5a68 [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>.
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010 * Aug 2003 Vladimir Oleynik - reduced 464 bytes.
Eric Andersen1b355eb2000-09-05 17:37:48 +000011 *
12 * 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
15 * (at your option) any later version.
16 *
17 * 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.
21 *
22 * 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
25 *
26 */
27
28/* This program evaluates expressions. Each token (operator, operand,
29 * parenthesis) of the expression must be a seperate argument. The
30 * 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;
120 v->u.s = strdup (s);
121 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) {
Eric Andersen1b355eb2000-09-05 17:37:48 +0000160 int i;
161
Eric Andersen1b355eb2000-09-05 17:37:48 +0000162 /* Don't interpret the empty string as an integer. */
163 if (v->u.s == 0)
164 return 0;
165 i = atoi(v->u.s);
166 free (v->u.s);
167 v->u.i = i;
168 v->type = integer;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000169 }
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000170 return 1;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000171}
172
173/* Return nonzero if the next token matches STR exactly.
174 STR must not be NULL. */
175
176static int
177nextarg (char *str)
178{
179 if (*args == NULL)
180 return 0;
181 return strcmp (*args, str) == 0;
182}
183
184/* The comparison operator handling functions. */
185
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000186static int cmp_common (VALUE *l, VALUE *r, int op)
187{
188 int cmpval;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000189
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000190 if (l->type == string || r->type == string) {
191 tostring (l);
192 tostring (r);
193 cmpval = strcmp (l->u.s, r->u.s);
194 }
195 else
196 cmpval = l->u.i - r->u.i;
197 switch(op) {
198 case '<':
199 return cmpval < 0;
200 case ('L'+'E'):
201 return cmpval <= 0;
202 case '=':
203 return cmpval == 0;
204 case '!':
205 return cmpval != 0;
206 case '>':
207 return cmpval > 0;
208 default: /* >= */
209 return cmpval >= 0;
210 }
211}
Eric Andersen1b355eb2000-09-05 17:37:48 +0000212
213/* The arithmetic operator handling functions. */
214
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000215static int arithmetic_common (VALUE *l, VALUE *r, int op)
216{
217 int li, ri;
218
219 if (!toarith (l) || !toarith (r))
220 bb_error_msg_and_die ("non-numeric argument");
221 li = l->u.i;
222 ri = r->u.i;
223 if((op == '/' || op == '%') && ri == 0)
224 bb_error_msg_and_die ( "division by zero");
225 switch(op) {
226 case '+':
227 return li + ri;
228 case '-':
229 return li - ri;
230 case '*':
231 return li * ri;
232 case '/':
233 return li / ri;
234 default:
235 return li % ri;
236 }
Eric Andersen1b355eb2000-09-05 17:37:48 +0000237}
238
Eric Andersen1b355eb2000-09-05 17:37:48 +0000239/* Do the : operator.
240 SV is the VALUE for the lhs (the string),
241 PV is the VALUE for the rhs (the pattern). */
242
243static VALUE *docolon (VALUE *sv, VALUE *pv)
244{
245 VALUE *v;
246 const char *errmsg;
247 struct re_pattern_buffer re_buffer;
248 struct re_registers re_regs;
249 int len;
250
251 tostring (sv);
252 tostring (pv);
253
254 if (pv->u.s[0] == '^') {
255 fprintf (stderr, "\
256warning: unportable BRE: `%s': using `^' as the first character\n\
257of a basic regular expression is not portable; it is being ignored",
258 pv->u.s);
259 }
260
261 len = strlen (pv->u.s);
262 memset (&re_buffer, 0, sizeof (re_buffer));
263 memset (&re_regs, 0, sizeof (re_regs));
264 re_buffer.allocated = 2 * len;
265 re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
266 re_buffer.translate = 0;
267 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
268 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
269 if (errmsg) {
Manuel Novoa III cad53642003-03-19 09:13:01 +0000270 bb_error_msg_and_die("%s", errmsg);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000271 }
272
273 len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
274 if (len >= 0) {
275 /* Were \(...\) used? */
276 if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
277 sv->u.s[re_regs.end[1]] = '\0';
278 v = str_value (sv->u.s + re_regs.start[1]);
279 }
280 else
281 v = int_value (len);
282 }
283 else {
284 /* Match failed -- return the right kind of null. */
285 if (re_buffer.re_nsub > 0)
286 v = str_value ("");
287 else
288 v = int_value (0);
289 }
290 free (re_buffer.buffer);
291 return v;
292}
293
294/* Handle bare operands and ( expr ) syntax. */
295
296static VALUE *eval7 (void)
297{
298 VALUE *v;
299
300 if (!*args)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000301 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000302
303 if (nextarg ("(")) {
304 args++;
305 v = eval ();
306 if (!nextarg (")"))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000307 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000308 args++;
309 return v;
310 }
311
312 if (nextarg (")"))
Manuel Novoa III cad53642003-03-19 09:13:01 +0000313 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000314
315 return str_value (*args++);
316}
317
318/* Handle match, substr, index, length, and quote keywords. */
319
320static VALUE *eval6 (void)
321{
322 VALUE *l, *r, *v, *i1, *i2;
323
324 if (nextarg ("quote")) {
325 args++;
326 if (!*args)
Manuel Novoa III cad53642003-03-19 09:13:01 +0000327 bb_error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000328 return str_value (*args++);
329 }
330 else if (nextarg ("length")) {
331 args++;
332 r = eval6 ();
333 tostring (r);
334 v = int_value (strlen (r->u.s));
335 freev (r);
336 return v;
337 }
338 else if (nextarg ("match")) {
339 args++;
340 l = eval6 ();
341 r = eval6 ();
342 v = docolon (l, r);
343 freev (l);
344 freev (r);
345 return v;
346 }
347 else if (nextarg ("index")) {
348 args++;
349 l = eval6 ();
350 r = eval6 ();
351 tostring (l);
352 tostring (r);
353 v = int_value (strcspn (l->u.s, r->u.s) + 1);
354 if (v->u.i == (int) strlen (l->u.s) + 1)
355 v->u.i = 0;
356 freev (l);
357 freev (r);
358 return v;
359 }
360 else if (nextarg ("substr")) {
361 args++;
362 l = eval6 ();
363 i1 = eval6 ();
364 i2 = eval6 ();
365 tostring (l);
366 if (!toarith (i1) || !toarith (i2)
367 || i1->u.i > (int) strlen (l->u.s)
368 || i1->u.i <= 0 || i2->u.i <= 0)
369 v = str_value ("");
370 else {
371 v = xmalloc (sizeof(VALUE));
372 v->type = string;
Manuel Novoa III cad53642003-03-19 09:13:01 +0000373 v->u.s = bb_xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000374 }
375 freev (l);
376 freev (i1);
377 freev (i2);
378 return v;
379 }
380 else
381 return eval7 ();
382}
383
384/* Handle : operator (pattern matching).
385 Calls docolon to do the real work. */
386
387static VALUE *eval5 (void)
388{
389 VALUE *l, *r, *v;
390
391 l = eval6 ();
392 while (nextarg (":")) {
393 args++;
394 r = eval6 ();
395 v = docolon (l, r);
396 freev (l);
397 freev (r);
398 l = v;
399 }
400 return l;
401}
402
403/* Handle *, /, % operators. */
404
405static VALUE *eval4 (void)
406{
407 VALUE *l, *r;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000408 int op, val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000409
410 l = eval5 ();
411 while (1) {
412 if (nextarg ("*"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000413 op = '*';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000414 else 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
419 return l;
420 args++;
421 r = eval5 ();
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000422 val = arithmetic_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000423 freev (l);
424 freev (r);
425 l = int_value (val);
426 }
427}
428
429/* Handle +, - operators. */
430
431static VALUE *eval3 (void)
432{
433 VALUE *l, *r;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000434 int op, val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000435
436 l = eval4 ();
437 while (1) {
438 if (nextarg ("+"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000439 op = '+';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000440 else if (nextarg ("-"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000441 op = '+';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000442 else
443 return l;
444 args++;
445 r = eval4 ();
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000446 val = arithmetic_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000447 freev (l);
448 freev (r);
449 l = int_value (val);
450 }
451}
452
453/* Handle comparisons. */
454
455static VALUE *eval2 (void)
456{
457 VALUE *l, *r;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000458 int op, val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000459
460 l = eval3 ();
461 while (1) {
462 if (nextarg ("<"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000463 op = '<';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000464 else if (nextarg ("<="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000465 op = 'L'+'E';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000466 else if (nextarg ("=") || nextarg ("=="))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000467 op = '=';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000468 else if (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 = 'G'+'E';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000472 else if (nextarg (">"))
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000473 op = '>';
Eric Andersen1b355eb2000-09-05 17:37:48 +0000474 else
475 return l;
476 args++;
477 r = eval3 ();
478 toarith (l);
479 toarith (r);
Glenn L McGrath7b8765c2003-08-29 07:29:30 +0000480 val = cmp_common (l, r, op);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000481 freev (l);
482 freev (r);
483 l = int_value (val);
484 }
485}
486
487/* Handle &. */
488
489static VALUE *eval1 (void)
490{
491 VALUE *l, *r;
492
493 l = eval2 ();
494 while (nextarg ("&")) {
495 args++;
496 r = eval2 ();
497 if (null (l) || null (r)) {
498 freev (l);
499 freev (r);
500 l = int_value (0);
501 }
502 else
503 freev (r);
504 }
505 return l;
506}
507
508/* Handle |. */
509
510static VALUE *eval (void)
511{
512 VALUE *l, *r;
513
514 l = eval1 ();
515 while (nextarg ("|")) {
516 args++;
517 r = eval1 ();
518 if (null (l)) {
519 freev (l);
520 l = r;
521 }
522 else
523 freev (r);
524 }
525 return l;
526}