Initial public busybox upstream commit
[busybox4maemo] / coreutils / expr.c
1 /* 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  * Copyright (C) 2003-2005  Vladimir Oleynik <dzo@simtreas.ru>
11  *  - reduced 464 bytes.
12  *  - 64 math support
13  *
14  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
15  */
16
17 /* This program evaluates expressions.  Each token (operator, operand,
18  * parenthesis) of the expression must be a separate argument.  The
19  * 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
26 /* no getopt needed */
27
28 #include "libbb.h"
29 #include "xregex.h"
30
31 /* The kinds of value we can have.  */
32 enum valtype {
33         integer,
34         string
35 };
36 typedef enum valtype TYPE;
37
38 #if ENABLE_EXPR_MATH_SUPPORT_64
39 typedef int64_t arith_t;
40
41 #define PF_REZ      "ll"
42 #define PF_REZ_TYPE (long long)
43 #define STRTOL(s, e, b) strtoll(s, e, b)
44 #else
45 typedef long arith_t;
46
47 #define PF_REZ      "l"
48 #define PF_REZ_TYPE (long)
49 #define STRTOL(s, e, b) strtol(s, e, b)
50 #endif
51
52 /* TODO: use bb_strtol[l]? It's easier to check for errors... */
53
54 /* A value is.... */
55 struct valinfo {
56         TYPE type;                      /* Which kind. */
57         union {                         /* The value itself. */
58                 arith_t i;
59                 char *s;
60         } u;
61 };
62 typedef struct valinfo VALUE;
63
64 /* The arguments given to the program, minus the program name.  */
65 struct globals {
66         char **args;
67 };
68 #define G (*(struct globals*)&bb_common_bufsiz1)
69
70 /* forward declarations */
71 static VALUE *eval(void);
72
73
74 /* Return a VALUE for I.  */
75
76 static VALUE *int_value(arith_t i)
77 {
78         VALUE *v;
79
80         v = xmalloc(sizeof(VALUE));
81         v->type = integer;
82         v->u.i = i;
83         return v;
84 }
85
86 /* Return a VALUE for S.  */
87
88 static VALUE *str_value(const char *s)
89 {
90         VALUE *v;
91
92         v = xmalloc(sizeof(VALUE));
93         v->type = string;
94         v->u.s = xstrdup(s);
95         return v;
96 }
97
98 /* Free VALUE V, including structure components.  */
99
100 static void freev(VALUE * v)
101 {
102         if (v->type == string)
103                 free(v->u.s);
104         free(v);
105 }
106
107 /* Return nonzero if V is a null-string or zero-number.  */
108
109 static int null(VALUE * v)
110 {
111         if (v->type == integer)
112                 return v->u.i == 0;
113         /* string: */
114         return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0');
115 }
116
117 /* Coerce V to a string value (can't fail).  */
118
119 static void tostring(VALUE * v)
120 {
121         if (v->type == integer) {
122                 v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i);
123                 v->type = string;
124         }
125 }
126
127 /* Coerce V to an integer value.  Return 1 on success, 0 on failure.  */
128
129 static bool toarith(VALUE * v)
130 {
131         if (v->type == string) {
132                 arith_t i;
133                 char *e;
134
135                 /* Don't interpret the empty string as an integer.  */
136                 /* Currently does not worry about overflow or int/long differences. */
137                 i = STRTOL(v->u.s, &e, 10);
138                 if ((v->u.s == e) || *e)
139                         return 0;
140                 free(v->u.s);
141                 v->u.i = i;
142                 v->type = integer;
143         }
144         return 1;
145 }
146
147 /* Return nonzero if the next token matches STR exactly.
148    STR must not be NULL.  */
149
150 static bool nextarg(const char *str)
151 {
152         if (*G.args == NULL)
153                 return 0;
154         return strcmp(*G.args, str) == 0;
155 }
156
157 /* The comparison operator handling functions.  */
158
159 static int cmp_common(VALUE * l, VALUE * r, int op)
160 {
161         int cmpval;
162
163         if (l->type == string || r->type == string) {
164                 tostring(l);
165                 tostring(r);
166                 cmpval = strcmp(l->u.s, r->u.s);
167         } else
168                 cmpval = l->u.i - r->u.i;
169         if (op == '<')
170                 return cmpval < 0;
171         if (op == ('L' + 'E'))
172                 return cmpval <= 0;
173         if (op == '=')
174                 return cmpval == 0;
175         if (op == '!')
176                 return cmpval != 0;
177         if (op == '>')
178                 return cmpval > 0;
179         /* >= */
180         return cmpval >= 0;
181 }
182
183 /* The arithmetic operator handling functions.  */
184
185 static arith_t arithmetic_common(VALUE * l, VALUE * r, int op)
186 {
187         arith_t li, ri;
188
189         if (!toarith(l) || !toarith(r))
190                 bb_error_msg_and_die("non-numeric argument");
191         li = l->u.i;
192         ri = r->u.i;
193         if ((op == '/' || op == '%') && ri == 0)
194                 bb_error_msg_and_die("division by zero");
195         if (op == '+')
196                 return li + ri;
197         else if (op == '-')
198                 return li - ri;
199         else if (op == '*')
200                 return li * ri;
201         else if (op == '/')
202                 return li / ri;
203         else
204                 return li % ri;
205 }
206
207 /* Do the : operator.
208    SV is the VALUE for the lhs (the string),
209    PV is the VALUE for the rhs (the pattern).  */
210
211 static VALUE *docolon(VALUE * sv, VALUE * pv)
212 {
213         VALUE *v;
214         regex_t re_buffer;
215         const int NMATCH = 2;
216         regmatch_t re_regs[NMATCH];
217
218         tostring(sv);
219         tostring(pv);
220
221         if (pv->u.s[0] == '^') {
222                 bb_error_msg("\
223 warning: unportable BRE: `%s': using `^' as the first character\n\
224 of a basic regular expression is not portable; it is being ignored", pv->u.s);
225         }
226
227         memset(&re_buffer, 0, sizeof(re_buffer));
228         memset(re_regs, 0, sizeof(*re_regs));
229         xregcomp(&re_buffer, pv->u.s, 0);
230
231         /* expr uses an anchored pattern match, so check that there was a
232          * match and that the match starts at offset 0. */
233         if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH &&
234                 re_regs[0].rm_so == 0) {
235                 /* Were \(...\) used? */
236                 if (re_buffer.re_nsub > 0) {
237                         sv->u.s[re_regs[1].rm_eo] = '\0';
238                         v = str_value(sv->u.s + re_regs[1].rm_so);
239                 } else
240                         v = int_value(re_regs[0].rm_eo);
241         } else {
242                 /* Match failed -- return the right kind of null.  */
243                 if (re_buffer.re_nsub > 0)
244                         v = str_value("");
245                 else
246                         v = int_value(0);
247         }
248 //FIXME: sounds like here is a bit missing: regfree(&re_buffer);
249         return v;
250 }
251
252 /* Handle bare operands and ( expr ) syntax.  */
253
254 static VALUE *eval7(void)
255 {
256         VALUE *v;
257
258         if (!*G.args)
259                 bb_error_msg_and_die("syntax error");
260
261         if (nextarg("(")) {
262                 G.args++;
263                 v = eval();
264                 if (!nextarg(")"))
265                         bb_error_msg_and_die("syntax error");
266                 G.args++;
267                 return v;
268         }
269
270         if (nextarg(")"))
271                 bb_error_msg_and_die("syntax error");
272
273         return str_value(*G.args++);
274 }
275
276 /* Handle match, substr, index, length, and quote keywords.  */
277
278 static VALUE *eval6(void)
279 {
280         static const char keywords[] ALIGN1 =
281                 "quote\0""length\0""match\0""index\0""substr\0";
282
283         VALUE *r, *i1, *i2;
284         VALUE *l = l; /* silence gcc */
285         VALUE *v = v; /* silence gcc */
286         int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0;
287
288         if (key == 0) /* not a keyword */
289                 return eval7();
290         G.args++; /* We have a valid token, so get the next argument.  */
291         if (key == 1) { /* quote */
292                 if (!*G.args)
293                         bb_error_msg_and_die("syntax error");
294                 return str_value(*G.args++);
295         }
296         if (key == 2) { /* length */
297                 r = eval6();
298                 tostring(r);
299                 v = int_value(strlen(r->u.s));
300                 freev(r);
301         } else
302                 l = eval6();
303
304         if (key == 3) { /* match */
305                 r = eval6();
306                 v = docolon(l, r);
307                 freev(l);
308                 freev(r);
309         }
310         if (key == 4) { /* index */
311                 r = eval6();
312                 tostring(l);
313                 tostring(r);
314                 v = int_value(strcspn(l->u.s, r->u.s) + 1);
315                 if (v->u.i == (arith_t) strlen(l->u.s) + 1)
316                         v->u.i = 0;
317                 freev(l);
318                 freev(r);
319         }
320         if (key == 5) { /* substr */
321                 i1 = eval6();
322                 i2 = eval6();
323                 tostring(l);
324                 if (!toarith(i1) || !toarith(i2)
325                  || i1->u.i > (arith_t) strlen(l->u.s)
326                  || i1->u.i <= 0 || i2->u.i <= 0)
327                         v = str_value("");
328                 else {
329                         v = xmalloc(sizeof(VALUE));
330                         v->type = string;
331                         v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
332                 }
333                 freev(l);
334                 freev(i1);
335                 freev(i2);
336         }
337         return v;
338
339 }
340
341 /* Handle : operator (pattern matching).
342    Calls docolon to do the real work.  */
343
344 static VALUE *eval5(void)
345 {
346         VALUE *l, *r, *v;
347
348         l = eval6();
349         while (nextarg(":")) {
350                 G.args++;
351                 r = eval6();
352                 v = docolon(l, r);
353                 freev(l);
354                 freev(r);
355                 l = v;
356         }
357         return l;
358 }
359
360 /* Handle *, /, % operators.  */
361
362 static VALUE *eval4(void)
363 {
364         VALUE *l, *r;
365         int op;
366         arith_t val;
367
368         l = eval5();
369         while (1) {
370                 if (nextarg("*"))
371                         op = '*';
372                 else if (nextarg("/"))
373                         op = '/';
374                 else if (nextarg("%"))
375                         op = '%';
376                 else
377                         return l;
378                 G.args++;
379                 r = eval5();
380                 val = arithmetic_common(l, r, op);
381                 freev(l);
382                 freev(r);
383                 l = int_value(val);
384         }
385 }
386
387 /* Handle +, - operators.  */
388
389 static VALUE *eval3(void)
390 {
391         VALUE *l, *r;
392         int op;
393         arith_t val;
394
395         l = eval4();
396         while (1) {
397                 if (nextarg("+"))
398                         op = '+';
399                 else if (nextarg("-"))
400                         op = '-';
401                 else
402                         return l;
403                 G.args++;
404                 r = eval4();
405                 val = arithmetic_common(l, r, op);
406                 freev(l);
407                 freev(r);
408                 l = int_value(val);
409         }
410 }
411
412 /* Handle comparisons.  */
413
414 static VALUE *eval2(void)
415 {
416         VALUE *l, *r;
417         int op;
418         arith_t val;
419
420         l = eval3();
421         while (1) {
422                 if (nextarg("<"))
423                         op = '<';
424                 else if (nextarg("<="))
425                         op = 'L' + 'E';
426                 else if (nextarg("=") || nextarg("=="))
427                         op = '=';
428                 else if (nextarg("!="))
429                         op = '!';
430                 else if (nextarg(">="))
431                         op = 'G' + 'E';
432                 else if (nextarg(">"))
433                         op = '>';
434                 else
435                         return l;
436                 G.args++;
437                 r = eval3();
438                 toarith(l);
439                 toarith(r);
440                 val = cmp_common(l, r, op);
441                 freev(l);
442                 freev(r);
443                 l = int_value(val);
444         }
445 }
446
447 /* Handle &.  */
448
449 static VALUE *eval1(void)
450 {
451         VALUE *l, *r;
452
453         l = eval2();
454         while (nextarg("&")) {
455                 G.args++;
456                 r = eval2();
457                 if (null(l) || null(r)) {
458                         freev(l);
459                         freev(r);
460                         l = int_value(0);
461                 } else
462                         freev(r);
463         }
464         return l;
465 }
466
467 /* Handle |.  */
468
469 static VALUE *eval(void)
470 {
471         VALUE *l, *r;
472
473         l = eval1();
474         while (nextarg("|")) {
475                 G.args++;
476                 r = eval1();
477                 if (null(l)) {
478                         freev(l);
479                         l = r;
480                 } else
481                         freev(r);
482         }
483         return l;
484 }
485
486 int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
487 int expr_main(int argc, char **argv)
488 {
489         VALUE *v;
490
491         if (argc == 1) {
492                 bb_error_msg_and_die("too few arguments");
493         }
494
495         G.args = argv + 1;
496
497         v = eval();
498         if (*G.args)
499                 bb_error_msg_and_die("syntax error");
500
501         if (v->type == integer)
502                 printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
503         else
504                 puts(v->u.s);
505
506         fflush_stdout_and_exit(null(v));
507 }