blob: 55ae3a969eda90a743feb7180aa75cc3b8bfcbf0 [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
Mark Whitley827e45c2001-03-09 23:59:51 +000036/* no getopt needed */
37
Eric Andersen1b355eb2000-09-05 17:37:48 +000038#include <stdio.h>
Eric Andersened3ef502001-01-27 08:24:39 +000039#include <string.h>
40#include <stdlib.h>
41#include <regex.h>
Eric Andersen1b355eb2000-09-05 17:37:48 +000042#include <sys/types.h>
Eric Andersencbe31da2001-02-20 06:14:08 +000043#include "busybox.h"
Eric Andersen1b355eb2000-09-05 17:37:48 +000044
Eric Andersen1b355eb2000-09-05 17:37:48 +000045
46/* The kinds of value we can have. */
47enum valtype {
48 integer,
49 string
50};
51typedef enum valtype TYPE;
52
53/* A value is.... */
54struct valinfo {
55 TYPE type; /* Which kind. */
56 union { /* The value itself. */
57 int i;
58 char *s;
59 } u;
60};
61typedef struct valinfo VALUE;
62
63/* The arguments given to the program, minus the program name. */
64static char **args;
65
66static VALUE *docolon (VALUE *sv, VALUE *pv);
67static VALUE *eval (void);
68static VALUE *int_value (int i);
69static VALUE *str_value (char *s);
70static int nextarg (char *str);
71static int null (VALUE *v);
72static int toarith (VALUE *v);
73static void freev (VALUE *v);
74static void tostring (VALUE *v);
75
76int expr_main (int argc, char **argv)
77{
78 VALUE *v;
79
80 if (argc == 1) {
Matt Kraaidd19c692001-01-31 19:00:21 +000081 error_msg_and_die("too few arguments");
Eric Andersen1b355eb2000-09-05 17:37:48 +000082 }
83
84 args = argv + 1;
85
86 v = eval ();
87 if (*args)
Matt Kraaidd19c692001-01-31 19:00:21 +000088 error_msg_and_die ("syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +000089
90 if (v->type == integer)
91 printf ("%d\n", v->u.i);
Matt Kraai59df6f72001-05-16 14:21:09 +000092 else
93 puts (v->u.s);
Eric Andersen1b355eb2000-09-05 17:37:48 +000094
95 exit (null (v));
96}
97
98/* Return a VALUE for I. */
99
100static VALUE *int_value (int i)
101{
102 VALUE *v;
103
104 v = xmalloc (sizeof(VALUE));
105 v->type = integer;
106 v->u.i = i;
107 return v;
108}
109
110/* Return a VALUE for S. */
111
112static VALUE *str_value (char *s)
113{
114 VALUE *v;
115
116 v = xmalloc (sizeof(VALUE));
117 v->type = string;
118 v->u.s = strdup (s);
119 return v;
120}
121
122/* Free VALUE V, including structure components. */
123
124static void freev (VALUE *v)
125{
126 if (v->type == string)
127 free (v->u.s);
128 free (v);
129}
130
131/* Return nonzero if V is a null-string or zero-number. */
132
133static int null (VALUE *v)
134{
135 switch (v->type) {
136 case integer:
137 return v->u.i == 0;
138 case string:
139 return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
140 default:
141 abort ();
142 }
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) {
Robert Griebld378c312002-07-19 00:05:54 +0000150 bb_asprintf (&(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{
159 int i;
160
161 switch (v->type) {
162 case integer:
163 return 1;
164 case string:
165 i = 0;
166 /* Don't interpret the empty string as an integer. */
167 if (v->u.s == 0)
168 return 0;
169 i = atoi(v->u.s);
170 free (v->u.s);
171 v->u.i = i;
172 v->type = integer;
173 return 1;
174 default:
175 abort ();
176 }
177}
178
179/* Return nonzero if the next token matches STR exactly.
180 STR must not be NULL. */
181
182static int
183nextarg (char *str)
184{
185 if (*args == NULL)
186 return 0;
187 return strcmp (*args, str) == 0;
188}
189
190/* The comparison operator handling functions. */
191
192#define cmpf(name, rel) \
Eric Anderseneaecbf32001-10-31 10:41:31 +0000193static int name (VALUE *l, VALUE *r) \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000194{ \
195 if (l->type == string || r->type == string) { \
196 tostring (l); \
197 tostring (r); \
198 return strcmp (l->u.s, r->u.s) rel 0; \
199 } \
200 else \
201 return l->u.i rel r->u.i; \
202}
203 cmpf (less_than, <)
204 cmpf (less_equal, <=)
205 cmpf (equal, ==)
206 cmpf (not_equal, !=)
207 cmpf (greater_equal, >=)
208 cmpf (greater_than, >)
209
210#undef cmpf
211
212/* The arithmetic operator handling functions. */
213
214#define arithf(name, op) \
215static \
Eric Anderseneaecbf32001-10-31 10:41:31 +0000216int name (VALUE *l, VALUE *r) \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000217{ \
218 if (!toarith (l) || !toarith (r)) \
Matt Kraaidd19c692001-01-31 19:00:21 +0000219 error_msg_and_die ("non-numeric argument"); \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000220 return l->u.i op r->u.i; \
221}
222
223#define arithdivf(name, op) \
Eric Anderseneaecbf32001-10-31 10:41:31 +0000224static int name (VALUE *l, VALUE *r) \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000225{ \
226 if (!toarith (l) || !toarith (r)) \
Matt Kraaidd19c692001-01-31 19:00:21 +0000227 error_msg_and_die ( "non-numeric argument"); \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000228 if (r->u.i == 0) \
Matt Kraaidd19c692001-01-31 19:00:21 +0000229 error_msg_and_die ( "division by zero"); \
Eric Andersen1b355eb2000-09-05 17:37:48 +0000230 return l->u.i op r->u.i; \
231}
232
233 arithf (plus, +)
234 arithf (minus, -)
235 arithf (multiply, *)
236 arithdivf (divide, /)
237 arithdivf (mod, %)
238
239#undef arithf
240#undef arithdivf
241
242/* 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;
249 const char *errmsg;
250 struct re_pattern_buffer re_buffer;
251 struct re_registers re_regs;
252 int len;
253
254 tostring (sv);
255 tostring (pv);
256
257 if (pv->u.s[0] == '^') {
258 fprintf (stderr, "\
259warning: unportable BRE: `%s': using `^' as the first character\n\
260of a basic regular expression is not portable; it is being ignored",
261 pv->u.s);
262 }
263
264 len = strlen (pv->u.s);
265 memset (&re_buffer, 0, sizeof (re_buffer));
266 memset (&re_regs, 0, sizeof (re_regs));
267 re_buffer.allocated = 2 * len;
268 re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
269 re_buffer.translate = 0;
270 re_syntax_options = RE_SYNTAX_POSIX_BASIC;
271 errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
272 if (errmsg) {
Matt Kraaidd19c692001-01-31 19:00:21 +0000273 error_msg_and_die("%s", errmsg);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000274 }
275
276 len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
277 if (len >= 0) {
278 /* Were \(...\) used? */
279 if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
280 sv->u.s[re_regs.end[1]] = '\0';
281 v = str_value (sv->u.s + re_regs.start[1]);
282 }
283 else
284 v = int_value (len);
285 }
286 else {
287 /* Match failed -- return the right kind of null. */
288 if (re_buffer.re_nsub > 0)
289 v = str_value ("");
290 else
291 v = int_value (0);
292 }
293 free (re_buffer.buffer);
294 return v;
295}
296
297/* Handle bare operands and ( expr ) syntax. */
298
299static VALUE *eval7 (void)
300{
301 VALUE *v;
302
303 if (!*args)
Matt Kraaidd19c692001-01-31 19:00:21 +0000304 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000305
306 if (nextarg ("(")) {
307 args++;
308 v = eval ();
309 if (!nextarg (")"))
Matt Kraaidd19c692001-01-31 19:00:21 +0000310 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000311 args++;
312 return v;
313 }
314
315 if (nextarg (")"))
Matt Kraaidd19c692001-01-31 19:00:21 +0000316 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000317
318 return str_value (*args++);
319}
320
321/* Handle match, substr, index, length, and quote keywords. */
322
323static VALUE *eval6 (void)
324{
325 VALUE *l, *r, *v, *i1, *i2;
326
327 if (nextarg ("quote")) {
328 args++;
329 if (!*args)
Matt Kraaidd19c692001-01-31 19:00:21 +0000330 error_msg_and_die ( "syntax error");
Eric Andersen1b355eb2000-09-05 17:37:48 +0000331 return str_value (*args++);
332 }
333 else if (nextarg ("length")) {
334 args++;
335 r = eval6 ();
336 tostring (r);
337 v = int_value (strlen (r->u.s));
338 freev (r);
339 return v;
340 }
341 else if (nextarg ("match")) {
342 args++;
343 l = eval6 ();
344 r = eval6 ();
345 v = docolon (l, r);
346 freev (l);
347 freev (r);
348 return v;
349 }
350 else if (nextarg ("index")) {
351 args++;
352 l = eval6 ();
353 r = eval6 ();
354 tostring (l);
355 tostring (r);
356 v = int_value (strcspn (l->u.s, r->u.s) + 1);
357 if (v->u.i == (int) strlen (l->u.s) + 1)
358 v->u.i = 0;
359 freev (l);
360 freev (r);
361 return v;
362 }
363 else if (nextarg ("substr")) {
364 args++;
365 l = eval6 ();
366 i1 = eval6 ();
367 i2 = eval6 ();
368 tostring (l);
369 if (!toarith (i1) || !toarith (i2)
370 || i1->u.i > (int) strlen (l->u.s)
371 || i1->u.i <= 0 || i2->u.i <= 0)
372 v = str_value ("");
373 else {
374 v = xmalloc (sizeof(VALUE));
375 v->type = string;
Robert Griebld378c312002-07-19 00:05:54 +0000376 v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
Eric Andersen1b355eb2000-09-05 17:37:48 +0000377 }
378 freev (l);
379 freev (i1);
380 freev (i2);
381 return v;
382 }
383 else
384 return eval7 ();
385}
386
387/* Handle : operator (pattern matching).
388 Calls docolon to do the real work. */
389
390static VALUE *eval5 (void)
391{
392 VALUE *l, *r, *v;
393
394 l = eval6 ();
395 while (nextarg (":")) {
396 args++;
397 r = eval6 ();
398 v = docolon (l, r);
399 freev (l);
400 freev (r);
401 l = v;
402 }
403 return l;
404}
405
406/* Handle *, /, % operators. */
407
408static VALUE *eval4 (void)
409{
410 VALUE *l, *r;
Eric Anderseneaecbf32001-10-31 10:41:31 +0000411 int (*fxn) (VALUE *, VALUE *), val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000412
413 l = eval5 ();
414 while (1) {
415 if (nextarg ("*"))
416 fxn = multiply;
417 else if (nextarg ("/"))
418 fxn = divide;
419 else if (nextarg ("%"))
420 fxn = mod;
421 else
422 return l;
423 args++;
424 r = eval5 ();
425 val = (*fxn) (l, r);
426 freev (l);
427 freev (r);
428 l = int_value (val);
429 }
430}
431
432/* Handle +, - operators. */
433
434static VALUE *eval3 (void)
435{
436 VALUE *l, *r;
Eric Anderseneaecbf32001-10-31 10:41:31 +0000437 int (*fxn) (VALUE *, VALUE *), val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000438
439 l = eval4 ();
440 while (1) {
441 if (nextarg ("+"))
442 fxn = plus;
443 else if (nextarg ("-"))
444 fxn = minus;
445 else
446 return l;
447 args++;
448 r = eval4 ();
449 val = (*fxn) (l, r);
450 freev (l);
451 freev (r);
452 l = int_value (val);
453 }
454}
455
456/* Handle comparisons. */
457
458static VALUE *eval2 (void)
459{
460 VALUE *l, *r;
Eric Anderseneaecbf32001-10-31 10:41:31 +0000461 int (*fxn) (VALUE *, VALUE *), val;
Eric Andersen1b355eb2000-09-05 17:37:48 +0000462
463 l = eval3 ();
464 while (1) {
465 if (nextarg ("<"))
466 fxn = less_than;
467 else if (nextarg ("<="))
468 fxn = less_equal;
469 else if (nextarg ("=") || nextarg ("=="))
470 fxn = equal;
471 else if (nextarg ("!="))
472 fxn = not_equal;
473 else if (nextarg (">="))
474 fxn = greater_equal;
475 else if (nextarg (">"))
476 fxn = greater_than;
477 else
478 return l;
479 args++;
480 r = eval3 ();
481 toarith (l);
482 toarith (r);
483 val = (*fxn) (l, r);
484 freev (l);
485 freev (r);
486 l = int_value (val);
487 }
488}
489
490/* Handle &. */
491
492static VALUE *eval1 (void)
493{
494 VALUE *l, *r;
495
496 l = eval2 ();
497 while (nextarg ("&")) {
498 args++;
499 r = eval2 ();
500 if (null (l) || null (r)) {
501 freev (l);
502 freev (r);
503 l = int_value (0);
504 }
505 else
506 freev (r);
507 }
508 return l;
509}
510
511/* Handle |. */
512
513static VALUE *eval (void)
514{
515 VALUE *l, *r;
516
517 l = eval1 ();
518 while (nextarg ("|")) {
519 args++;
520 r = eval1 ();
521 if (null (l)) {
522 freev (l);
523 l = r;
524 }
525 else
526 freev (r);
527 }
528 return l;
529}