expr: small code shrink
[oweals/busybox.git] / 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 * const keywords[] = {
281                 "quote", "length", "match", "index", "substr", NULL
282         };
283
284         VALUE *r, *i1, *i2;
285         VALUE *l = l; /* silence gcc */
286         VALUE *v = v; /* silence gcc */
287         int key = *G.args ? index_in_str_array(keywords, *G.args) + 1 : 0;
288
289         if (key == 0) /* not a keyword */
290                 return eval7();
291         G.args++; /* We have a valid token, so get the next argument.  */
292         if (key == 1) { /* quote */
293                 if (!*G.args)
294                         bb_error_msg_and_die("syntax error");
295                 return str_value(*G.args++);
296         }
297         if (key == 2) { /* length */
298                 r = eval6();
299                 tostring(r);
300                 v = int_value(strlen(r->u.s));
301                 freev(r);
302         } else
303                 l = eval6();
304
305         if (key == 3) { /* match */
306                 r = eval6();
307                 v = docolon(l, r);
308                 freev(l);
309                 freev(r);
310         }
311         if (key == 4) { /* index */
312                 r = eval6();
313                 tostring(l);
314                 tostring(r);
315                 v = int_value(strcspn(l->u.s, r->u.s) + 1);
316                 if (v->u.i == (arith_t) strlen(l->u.s) + 1)
317                         v->u.i = 0;
318                 freev(l);
319                 freev(r);
320         }
321         if (key == 5) { /* substr */
322                 i1 = eval6();
323                 i2 = eval6();
324                 tostring(l);
325                 if (!toarith(i1) || !toarith(i2)
326                  || i1->u.i > (arith_t) strlen(l->u.s)
327                  || i1->u.i <= 0 || i2->u.i <= 0)
328                         v = str_value("");
329                 else {
330                         v = xmalloc(sizeof(VALUE));
331                         v->type = string;
332                         v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
333                 }
334                 freev(l);
335                 freev(i1);
336                 freev(i2);
337         }
338         return v;
339
340 }
341
342 /* Handle : operator (pattern matching).
343    Calls docolon to do the real work.  */
344
345 static VALUE *eval5(void)
346 {
347         VALUE *l, *r, *v;
348
349         l = eval6();
350         while (nextarg(":")) {
351                 G.args++;
352                 r = eval6();
353                 v = docolon(l, r);
354                 freev(l);
355                 freev(r);
356                 l = v;
357         }
358         return l;
359 }
360
361 /* Handle *, /, % operators.  */
362
363 static VALUE *eval4(void)
364 {
365         VALUE *l, *r;
366         int op;
367         arith_t val;
368
369         l = eval5();
370         while (1) {
371                 if (nextarg("*"))
372                         op = '*';
373                 else if (nextarg("/"))
374                         op = '/';
375                 else if (nextarg("%"))
376                         op = '%';
377                 else
378                         return l;
379                 G.args++;
380                 r = eval5();
381                 val = arithmetic_common(l, r, op);
382                 freev(l);
383                 freev(r);
384                 l = int_value(val);
385         }
386 }
387
388 /* Handle +, - operators.  */
389
390 static VALUE *eval3(void)
391 {
392         VALUE *l, *r;
393         int op;
394         arith_t val;
395
396         l = eval4();
397         while (1) {
398                 if (nextarg("+"))
399                         op = '+';
400                 else if (nextarg("-"))
401                         op = '-';
402                 else
403                         return l;
404                 G.args++;
405                 r = eval4();
406                 val = arithmetic_common(l, r, op);
407                 freev(l);
408                 freev(r);
409                 l = int_value(val);
410         }
411 }
412
413 /* Handle comparisons.  */
414
415 static VALUE *eval2(void)
416 {
417         VALUE *l, *r;
418         int op;
419         arith_t val;
420
421         l = eval3();
422         while (1) {
423                 if (nextarg("<"))
424                         op = '<';
425                 else if (nextarg("<="))
426                         op = 'L' + 'E';
427                 else if (nextarg("=") || nextarg("=="))
428                         op = '=';
429                 else if (nextarg("!="))
430                         op = '!';
431                 else if (nextarg(">="))
432                         op = 'G' + 'E';
433                 else if (nextarg(">"))
434                         op = '>';
435                 else
436                         return l;
437                 G.args++;
438                 r = eval3();
439                 toarith(l);
440                 toarith(r);
441                 val = cmp_common(l, r, op);
442                 freev(l);
443                 freev(r);
444                 l = int_value(val);
445         }
446 }
447
448 /* Handle &.  */
449
450 static VALUE *eval1(void)
451 {
452         VALUE *l, *r;
453
454         l = eval2();
455         while (nextarg("&")) {
456                 G.args++;
457                 r = eval2();
458                 if (null(l) || null(r)) {
459                         freev(l);
460                         freev(r);
461                         l = int_value(0);
462                 } else
463                         freev(r);
464         }
465         return l;
466 }
467
468 /* Handle |.  */
469
470 static VALUE *eval(void)
471 {
472         VALUE *l, *r;
473
474         l = eval1();
475         while (nextarg("|")) {
476                 G.args++;
477                 r = eval1();
478                 if (null(l)) {
479                         freev(l);
480                         l = r;
481                 } else
482                         freev(r);
483         }
484         return l;
485 }
486
487 int expr_main(int argc, char **argv);
488 int expr_main(int argc, char **argv)
489 {
490         VALUE *v;
491
492         if (argc == 1) {
493                 bb_error_msg_and_die("too few arguments");
494         }
495
496         G.args = argv + 1;
497
498         v = eval();
499         if (*G.args)
500                 bb_error_msg_and_die("syntax error");
501
502         if (v->type == integer)
503                 printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
504         else
505                 puts(v->u.s);
506
507         fflush_stdout_and_exit(null(v));
508 }
509
510