diff options
Diffstat (limited to 'busybox/coreutils/expr.c')
-rw-r--r-- | busybox/coreutils/expr.c | 528 |
1 files changed, 528 insertions, 0 deletions
diff --git a/busybox/coreutils/expr.c b/busybox/coreutils/expr.c new file mode 100644 index 000000000..cbbd4cd03 --- /dev/null +++ b/busybox/coreutils/expr.c | |||
@@ -0,0 +1,528 @@ | |||
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 | * Aug 2003 Vladimir Oleynik - reduced 464 bytes. | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | * | ||
17 | * This program is distributed in the hope that it will be useful, | ||
18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
20 | * General Public License for more details. | ||
21 | * | ||
22 | * You should have received a copy of the GNU General Public License | ||
23 | * along with this program; if not, write to the Free Software | ||
24 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
25 | * | ||
26 | */ | ||
27 | |||
28 | /* This program evaluates expressions. Each token (operator, operand, | ||
29 | * parenthesis) of the expression must be a separate argument. The | ||
30 | * parser used is a reasonably general one, though any incarnation of | ||
31 | * it is language-specific. It is especially nice for expressions. | ||
32 | * | ||
33 | * No parse tree is needed; a new node is evaluated immediately. | ||
34 | * One function can handle multiple operators all of equal precedence, | ||
35 | * provided they all associate ((x op x) op x). */ | ||
36 | |||
37 | /* no getopt needed */ | ||
38 | |||
39 | #include <stdio.h> | ||
40 | #include <string.h> | ||
41 | #include <stdlib.h> | ||
42 | #include <regex.h> | ||
43 | #include <sys/types.h> | ||
44 | #include <errno.h> | ||
45 | #include "busybox.h" | ||
46 | |||
47 | |||
48 | /* The kinds of value we can have. */ | ||
49 | enum valtype { | ||
50 | integer, | ||
51 | string | ||
52 | }; | ||
53 | typedef enum valtype TYPE; | ||
54 | |||
55 | /* A value is.... */ | ||
56 | struct valinfo { | ||
57 | TYPE type; /* Which kind. */ | ||
58 | union { /* The value itself. */ | ||
59 | int i; | ||
60 | char *s; | ||
61 | } u; | ||
62 | }; | ||
63 | typedef struct valinfo VALUE; | ||
64 | |||
65 | /* The arguments given to the program, minus the program name. */ | ||
66 | static char **args; | ||
67 | |||
68 | static VALUE *docolon (VALUE *sv, VALUE *pv); | ||
69 | static VALUE *eval (void); | ||
70 | static VALUE *int_value (int i); | ||
71 | static VALUE *str_value (char *s); | ||
72 | static int nextarg (char *str); | ||
73 | static int null (VALUE *v); | ||
74 | static int toarith (VALUE *v); | ||
75 | static void freev (VALUE *v); | ||
76 | static void tostring (VALUE *v); | ||
77 | |||
78 | int expr_main (int argc, char **argv) | ||
79 | { | ||
80 | VALUE *v; | ||
81 | |||
82 | if (argc == 1) { | ||
83 | bb_error_msg_and_die("too few arguments"); | ||
84 | } | ||
85 | |||
86 | args = argv + 1; | ||
87 | |||
88 | v = eval (); | ||
89 | if (*args) | ||
90 | bb_error_msg_and_die ("syntax error"); | ||
91 | |||
92 | if (v->type == integer) | ||
93 | printf ("%d\n", v->u.i); | ||
94 | else | ||
95 | puts (v->u.s); | ||
96 | |||
97 | exit (null (v)); | ||
98 | } | ||
99 | |||
100 | /* Return a VALUE for I. */ | ||
101 | |||
102 | static VALUE *int_value (int i) | ||
103 | { | ||
104 | VALUE *v; | ||
105 | |||
106 | v = xmalloc (sizeof(VALUE)); | ||
107 | v->type = integer; | ||
108 | v->u.i = i; | ||
109 | return v; | ||
110 | } | ||
111 | |||
112 | /* Return a VALUE for S. */ | ||
113 | |||
114 | static VALUE *str_value (char *s) | ||
115 | { | ||
116 | VALUE *v; | ||
117 | |||
118 | v = xmalloc (sizeof(VALUE)); | ||
119 | v->type = string; | ||
120 | v->u.s = bb_xstrdup (s); | ||
121 | return v; | ||
122 | } | ||
123 | |||
124 | /* Free VALUE V, including structure components. */ | ||
125 | |||
126 | static void freev (VALUE *v) | ||
127 | { | ||
128 | if (v->type == string) | ||
129 | free (v->u.s); | ||
130 | free (v); | ||
131 | } | ||
132 | |||
133 | /* Return nonzero if V is a null-string or zero-number. */ | ||
134 | |||
135 | static int null (VALUE *v) | ||
136 | { | ||
137 | switch (v->type) { | ||
138 | case integer: | ||
139 | return v->u.i == 0; | ||
140 | default: /* string: */ | ||
141 | return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0; | ||
142 | } | ||
143 | } | ||
144 | |||
145 | /* Coerce V to a string value (can't fail). */ | ||
146 | |||
147 | static void tostring (VALUE *v) | ||
148 | { | ||
149 | if (v->type == integer) { | ||
150 | bb_xasprintf (&(v->u.s), "%d", v->u.i); | ||
151 | v->type = string; | ||
152 | } | ||
153 | } | ||
154 | |||
155 | /* Coerce V to an integer value. Return 1 on success, 0 on failure. */ | ||
156 | |||
157 | static int toarith (VALUE *v) | ||
158 | { | ||
159 | if(v->type == string) { | ||
160 | int i; | ||
161 | char *e; | ||
162 | |||
163 | /* Don't interpret the empty string as an integer. */ | ||
164 | /* Currently does not worry about overflow or int/long differences. */ | ||
165 | i = (int) strtol(v->u.s, &e, 10); | ||
166 | if ((v->u.s == e) || *e) | ||
167 | return 0; | ||
168 | free (v->u.s); | ||
169 | v->u.i = i; | ||
170 | v->type = integer; | ||
171 | } | ||
172 | return 1; | ||
173 | } | ||
174 | |||
175 | /* Return nonzero if the next token matches STR exactly. | ||
176 | STR must not be NULL. */ | ||
177 | |||
178 | static int | ||
179 | nextarg (char *str) | ||
180 | { | ||
181 | if (*args == NULL) | ||
182 | return 0; | ||
183 | return strcmp (*args, str) == 0; | ||
184 | } | ||
185 | |||
186 | /* The comparison operator handling functions. */ | ||
187 | |||
188 | static int cmp_common (VALUE *l, VALUE *r, int op) | ||
189 | { | ||
190 | int cmpval; | ||
191 | |||
192 | if (l->type == string || r->type == string) { | ||
193 | tostring (l); | ||
194 | tostring (r); | ||
195 | cmpval = strcmp (l->u.s, r->u.s); | ||
196 | } | ||
197 | else | ||
198 | cmpval = l->u.i - r->u.i; | ||
199 | switch(op) { | ||
200 | case '<': | ||
201 | return cmpval < 0; | ||
202 | case ('L'+'E'): | ||
203 | return cmpval <= 0; | ||
204 | case '=': | ||
205 | return cmpval == 0; | ||
206 | case '!': | ||
207 | return cmpval != 0; | ||
208 | case '>': | ||
209 | return cmpval > 0; | ||
210 | default: /* >= */ | ||
211 | return cmpval >= 0; | ||
212 | } | ||
213 | } | ||
214 | |||
215 | /* The arithmetic operator handling functions. */ | ||
216 | |||
217 | static int arithmetic_common (VALUE *l, VALUE *r, int op) | ||
218 | { | ||
219 | int li, ri; | ||
220 | |||
221 | if (!toarith (l) || !toarith (r)) | ||
222 | bb_error_msg_and_die ("non-numeric argument"); | ||
223 | li = l->u.i; | ||
224 | ri = r->u.i; | ||
225 | if((op == '/' || op == '%') && ri == 0) | ||
226 | bb_error_msg_and_die ( "division by zero"); | ||
227 | switch(op) { | ||
228 | case '+': | ||
229 | return li + ri; | ||
230 | case '-': | ||
231 | return li - ri; | ||
232 | case '*': | ||
233 | return li * ri; | ||
234 | case '/': | ||
235 | return li / ri; | ||
236 | default: | ||
237 | return li % ri; | ||
238 | } | ||
239 | } | ||
240 | |||
241 | /* Do the : operator. | ||
242 | SV is the VALUE for the lhs (the string), | ||
243 | PV is the VALUE for the rhs (the pattern). */ | ||
244 | |||
245 | static VALUE *docolon (VALUE *sv, VALUE *pv) | ||
246 | { | ||
247 | VALUE *v; | ||
248 | const char *errmsg; | ||
249 | struct re_pattern_buffer re_buffer; | ||
250 | struct re_registers re_regs; | ||
251 | int len; | ||
252 | |||
253 | tostring (sv); | ||
254 | tostring (pv); | ||
255 | |||
256 | if (pv->u.s[0] == '^') { | ||
257 | fprintf (stderr, "\ | ||
258 | warning: unportable BRE: `%s': using `^' as the first character\n\ | ||
259 | of a basic regular expression is not portable; it is being ignored", | ||
260 | pv->u.s); | ||
261 | } | ||
262 | |||
263 | len = strlen (pv->u.s); | ||
264 | memset (&re_buffer, 0, sizeof (re_buffer)); | ||
265 | memset (&re_regs, 0, sizeof (re_regs)); | ||
266 | re_buffer.allocated = 2 * len; | ||
267 | re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated); | ||
268 | re_buffer.translate = 0; | ||
269 | re_syntax_options = RE_SYNTAX_POSIX_BASIC; | ||
270 | errmsg = re_compile_pattern (pv->u.s, len, &re_buffer); | ||
271 | if (errmsg) { | ||
272 | bb_error_msg_and_die("%s", errmsg); | ||
273 | } | ||
274 | |||
275 | len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs); | ||
276 | if (len >= 0) { | ||
277 | /* Were \(...\) used? */ | ||
278 | if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */ | ||
279 | sv->u.s[re_regs.end[1]] = '\0'; | ||
280 | v = str_value (sv->u.s + re_regs.start[1]); | ||
281 | } | ||
282 | else | ||
283 | v = int_value (len); | ||
284 | } | ||
285 | else { | ||
286 | /* Match failed -- return the right kind of null. */ | ||
287 | if (re_buffer.re_nsub > 0) | ||
288 | v = str_value (""); | ||
289 | else | ||
290 | v = int_value (0); | ||
291 | } | ||
292 | free (re_buffer.buffer); | ||
293 | return v; | ||
294 | } | ||
295 | |||
296 | /* Handle bare operands and ( expr ) syntax. */ | ||
297 | |||
298 | static VALUE *eval7 (void) | ||
299 | { | ||
300 | VALUE *v; | ||
301 | |||
302 | if (!*args) | ||
303 | bb_error_msg_and_die ( "syntax error"); | ||
304 | |||
305 | if (nextarg ("(")) { | ||
306 | args++; | ||
307 | v = eval (); | ||
308 | if (!nextarg (")")) | ||
309 | bb_error_msg_and_die ( "syntax error"); | ||
310 | args++; | ||
311 | return v; | ||
312 | } | ||
313 | |||
314 | if (nextarg (")")) | ||
315 | bb_error_msg_and_die ( "syntax error"); | ||
316 | |||
317 | return str_value (*args++); | ||
318 | } | ||
319 | |||
320 | /* Handle match, substr, index, length, and quote keywords. */ | ||
321 | |||
322 | static VALUE *eval6 (void) | ||
323 | { | ||
324 | VALUE *l, *r, *v, *i1, *i2; | ||
325 | |||
326 | if (nextarg ("quote")) { | ||
327 | args++; | ||
328 | if (!*args) | ||
329 | bb_error_msg_and_die ( "syntax error"); | ||
330 | return str_value (*args++); | ||
331 | } | ||
332 | else if (nextarg ("length")) { | ||
333 | args++; | ||
334 | r = eval6 (); | ||
335 | tostring (r); | ||
336 | v = int_value (strlen (r->u.s)); | ||
337 | freev (r); | ||
338 | return v; | ||
339 | } | ||
340 | else if (nextarg ("match")) { | ||
341 | args++; | ||
342 | l = eval6 (); | ||
343 | r = eval6 (); | ||
344 | v = docolon (l, r); | ||
345 | freev (l); | ||
346 | freev (r); | ||
347 | return v; | ||
348 | } | ||
349 | else if (nextarg ("index")) { | ||
350 | args++; | ||
351 | l = eval6 (); | ||
352 | r = eval6 (); | ||
353 | tostring (l); | ||
354 | tostring (r); | ||
355 | v = int_value (strcspn (l->u.s, r->u.s) + 1); | ||
356 | if (v->u.i == (int) strlen (l->u.s) + 1) | ||
357 | v->u.i = 0; | ||
358 | freev (l); | ||
359 | freev (r); | ||
360 | return v; | ||
361 | } | ||
362 | else if (nextarg ("substr")) { | ||
363 | args++; | ||
364 | l = eval6 (); | ||
365 | i1 = eval6 (); | ||
366 | i2 = eval6 (); | ||
367 | tostring (l); | ||
368 | if (!toarith (i1) || !toarith (i2) | ||
369 | || i1->u.i > (int) strlen (l->u.s) | ||
370 | || i1->u.i <= 0 || i2->u.i <= 0) | ||
371 | v = str_value (""); | ||
372 | else { | ||
373 | v = xmalloc (sizeof(VALUE)); | ||
374 | v->type = string; | ||
375 | v->u.s = bb_xstrndup(l->u.s + i1->u.i - 1, i2->u.i); | ||
376 | } | ||
377 | freev (l); | ||
378 | freev (i1); | ||
379 | freev (i2); | ||
380 | return v; | ||
381 | } | ||
382 | else | ||
383 | return eval7 (); | ||
384 | } | ||
385 | |||
386 | /* Handle : operator (pattern matching). | ||
387 | Calls docolon to do the real work. */ | ||
388 | |||
389 | static VALUE *eval5 (void) | ||
390 | { | ||
391 | VALUE *l, *r, *v; | ||
392 | |||
393 | l = eval6 (); | ||
394 | while (nextarg (":")) { | ||
395 | args++; | ||
396 | r = eval6 (); | ||
397 | v = docolon (l, r); | ||
398 | freev (l); | ||
399 | freev (r); | ||
400 | l = v; | ||
401 | } | ||
402 | return l; | ||
403 | } | ||
404 | |||
405 | /* Handle *, /, % operators. */ | ||
406 | |||
407 | static VALUE *eval4 (void) | ||
408 | { | ||
409 | VALUE *l, *r; | ||
410 | int op, val; | ||
411 | |||
412 | l = eval5 (); | ||
413 | while (1) { | ||
414 | if (nextarg ("*")) | ||
415 | op = '*'; | ||
416 | else if (nextarg ("/")) | ||
417 | op = '/'; | ||
418 | else if (nextarg ("%")) | ||
419 | op = '%'; | ||
420 | else | ||
421 | return l; | ||
422 | args++; | ||
423 | r = eval5 (); | ||
424 | val = arithmetic_common (l, r, op); | ||
425 | freev (l); | ||
426 | freev (r); | ||
427 | l = int_value (val); | ||
428 | } | ||
429 | } | ||
430 | |||
431 | /* Handle +, - operators. */ | ||
432 | |||
433 | static VALUE *eval3 (void) | ||
434 | { | ||
435 | VALUE *l, *r; | ||
436 | int op, val; | ||
437 | |||
438 | l = eval4 (); | ||
439 | while (1) { | ||
440 | if (nextarg ("+")) | ||
441 | op = '+'; | ||
442 | else if (nextarg ("-")) | ||
443 | op = '-'; | ||
444 | else | ||
445 | return l; | ||
446 | args++; | ||
447 | r = eval4 (); | ||
448 | val = arithmetic_common (l, r, op); | ||
449 | freev (l); | ||
450 | freev (r); | ||
451 | l = int_value (val); | ||
452 | } | ||
453 | } | ||
454 | |||
455 | /* Handle comparisons. */ | ||
456 | |||
457 | static VALUE *eval2 (void) | ||
458 | { | ||
459 | VALUE *l, *r; | ||
460 | int op, val; | ||
461 | |||
462 | l = eval3 (); | ||
463 | while (1) { | ||
464 | if (nextarg ("<")) | ||
465 | op = '<'; | ||
466 | else if (nextarg ("<=")) | ||
467 | op = 'L'+'E'; | ||
468 | else if (nextarg ("=") || nextarg ("==")) | ||
469 | op = '='; | ||
470 | else if (nextarg ("!=")) | ||
471 | op = '!'; | ||
472 | else if (nextarg (">=")) | ||
473 | op = 'G'+'E'; | ||
474 | else if (nextarg (">")) | ||
475 | op = '>'; | ||
476 | else | ||
477 | return l; | ||
478 | args++; | ||
479 | r = eval3 (); | ||
480 | toarith (l); | ||
481 | toarith (r); | ||
482 | val = cmp_common (l, r, op); | ||
483 | freev (l); | ||
484 | freev (r); | ||
485 | l = int_value (val); | ||
486 | } | ||
487 | } | ||
488 | |||
489 | /* Handle &. */ | ||
490 | |||
491 | static VALUE *eval1 (void) | ||
492 | { | ||
493 | VALUE *l, *r; | ||
494 | |||
495 | l = eval2 (); | ||
496 | while (nextarg ("&")) { | ||
497 | args++; | ||
498 | r = eval2 (); | ||
499 | if (null (l) || null (r)) { | ||
500 | freev (l); | ||
501 | freev (r); | ||
502 | l = int_value (0); | ||
503 | } | ||
504 | else | ||
505 | freev (r); | ||
506 | } | ||
507 | return l; | ||
508 | } | ||
509 | |||
510 | /* Handle |. */ | ||
511 | |||
512 | static VALUE *eval (void) | ||
513 | { | ||
514 | VALUE *l, *r; | ||
515 | |||
516 | l = eval1 (); | ||
517 | while (nextarg ("|")) { | ||
518 | args++; | ||
519 | r = eval1 (); | ||
520 | if (null (l)) { | ||
521 | freev (l); | ||
522 | l = r; | ||
523 | } | ||
524 | else | ||
525 | freev (r); | ||
526 | } | ||
527 | return l; | ||
528 | } | ||