source: MondoRescue/branches/3.3/mindi-busybox/coreutils/expr.c@ 3906

Last change on this file since 3906 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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