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