Contents of /trunk/mkinitrd-magellan/busybox/coreutils/expr.c
Parent Directory | Revision Log
Revision 532 -
(show annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 9560 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 9 months ago) by niro
File MIME type: text/plain
File size: 9560 byte(s)
-import if magellan mkinitrd; it is a fork of redhats mkinitrd-5.0.8 with all magellan patches and features; deprecates magellan-src/mkinitrd
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 "busybox.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 | static char **args; |
66 | |
67 | static VALUE *docolon(VALUE * sv, VALUE * pv); |
68 | static VALUE *eval(void); |
69 | static VALUE *int_value(arith_t i); |
70 | static VALUE *str_value(char *s); |
71 | static int nextarg(char *str); |
72 | static int null(VALUE * v); |
73 | static int toarith(VALUE * v); |
74 | static void freev(VALUE * v); |
75 | static void tostring(VALUE * v); |
76 | |
77 | int expr_main(int argc, char **argv) |
78 | { |
79 | VALUE *v; |
80 | |
81 | if (argc == 1) { |
82 | bb_error_msg_and_die("too few arguments"); |
83 | } |
84 | |
85 | args = argv + 1; |
86 | |
87 | v = eval(); |
88 | if (*args) |
89 | bb_error_msg_and_die("syntax error"); |
90 | |
91 | if (v->type == integer) |
92 | printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i); |
93 | else |
94 | puts(v->u.s); |
95 | |
96 | fflush_stdout_and_exit(null(v)); |
97 | } |
98 | |
99 | /* Return a VALUE for I. */ |
100 | |
101 | static VALUE *int_value(arith_t i) |
102 | { |
103 | VALUE *v; |
104 | |
105 | v = xmalloc(sizeof(VALUE)); |
106 | v->type = integer; |
107 | v->u.i = i; |
108 | return v; |
109 | } |
110 | |
111 | /* Return a VALUE for S. */ |
112 | |
113 | static VALUE *str_value(char *s) |
114 | { |
115 | VALUE *v; |
116 | |
117 | v = xmalloc(sizeof(VALUE)); |
118 | v->type = string; |
119 | v->u.s = xstrdup(s); |
120 | return v; |
121 | } |
122 | |
123 | /* Free VALUE V, including structure components. */ |
124 | |
125 | static void freev(VALUE * v) |
126 | { |
127 | if (v->type == string) |
128 | free(v->u.s); |
129 | free(v); |
130 | } |
131 | |
132 | /* Return nonzero if V is a null-string or zero-number. */ |
133 | |
134 | static int null(VALUE * v) |
135 | { |
136 | if (v->type == integer) |
137 | return v->u.i == 0; |
138 | else /* string: */ |
139 | return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0'); |
140 | } |
141 | |
142 | /* Coerce V to a string value (can't fail). */ |
143 | |
144 | static void tostring(VALUE * v) |
145 | { |
146 | if (v->type == integer) { |
147 | v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i); |
148 | v->type = string; |
149 | } |
150 | } |
151 | |
152 | /* Coerce V to an integer value. Return 1 on success, 0 on failure. */ |
153 | |
154 | static int toarith(VALUE * v) |
155 | { |
156 | if (v->type == string) { |
157 | arith_t i; |
158 | char *e; |
159 | |
160 | /* Don't interpret the empty string as an integer. */ |
161 | /* Currently does not worry about overflow or int/long differences. */ |
162 | i = STRTOL(v->u.s, &e, 10); |
163 | if ((v->u.s == e) || *e) |
164 | return 0; |
165 | free(v->u.s); |
166 | v->u.i = i; |
167 | v->type = integer; |
168 | } |
169 | return 1; |
170 | } |
171 | |
172 | /* Return nonzero if the next token matches STR exactly. |
173 | STR must not be NULL. */ |
174 | |
175 | static int nextarg(char *str) |
176 | { |
177 | if (*args == NULL) |
178 | return 0; |
179 | return strcmp(*args, str) == 0; |
180 | } |
181 | |
182 | /* The comparison operator handling functions. */ |
183 | |
184 | static int cmp_common(VALUE * l, VALUE * r, int op) |
185 | { |
186 | int cmpval; |
187 | |
188 | if (l->type == string || r->type == string) { |
189 | tostring(l); |
190 | tostring(r); |
191 | cmpval = strcmp(l->u.s, r->u.s); |
192 | } else |
193 | cmpval = l->u.i - r->u.i; |
194 | if (op == '<') |
195 | return cmpval < 0; |
196 | else if (op == ('L' + 'E')) |
197 | return cmpval <= 0; |
198 | else if (op == '=') |
199 | return cmpval == 0; |
200 | else if (op == '!') |
201 | return cmpval != 0; |
202 | else if (op == '>') |
203 | return cmpval > 0; |
204 | else /* >= */ |
205 | return cmpval >= 0; |
206 | } |
207 | |
208 | /* The arithmetic operator handling functions. */ |
209 | |
210 | static arith_t arithmetic_common(VALUE * l, VALUE * r, int op) |
211 | { |
212 | arith_t li, ri; |
213 | |
214 | if (!toarith(l) || !toarith(r)) |
215 | bb_error_msg_and_die("non-numeric argument"); |
216 | li = l->u.i; |
217 | ri = r->u.i; |
218 | if ((op == '/' || op == '%') && ri == 0) |
219 | bb_error_msg_and_die("division by zero"); |
220 | if (op == '+') |
221 | return li + ri; |
222 | else if (op == '-') |
223 | return li - ri; |
224 | else if (op == '*') |
225 | return li * ri; |
226 | else if (op == '/') |
227 | return li / ri; |
228 | else |
229 | return li % ri; |
230 | } |
231 | |
232 | /* Do the : operator. |
233 | SV is the VALUE for the lhs (the string), |
234 | PV is the VALUE for the rhs (the pattern). */ |
235 | |
236 | static VALUE *docolon(VALUE * sv, VALUE * pv) |
237 | { |
238 | VALUE *v; |
239 | regex_t re_buffer; |
240 | const int NMATCH = 2; |
241 | regmatch_t re_regs[NMATCH]; |
242 | |
243 | tostring(sv); |
244 | tostring(pv); |
245 | |
246 | if (pv->u.s[0] == '^') { |
247 | fprintf(stderr, "\ |
248 | warning: unportable BRE: `%s': using `^' as the first character\n\ |
249 | of a basic regular expression is not portable; it is being ignored", pv->u.s); |
250 | } |
251 | |
252 | memset(&re_buffer, 0, sizeof(re_buffer)); |
253 | memset(re_regs, 0, sizeof(*re_regs)); |
254 | if (regcomp(&re_buffer, pv->u.s, 0) != 0) |
255 | bb_error_msg_and_die("invalid regular expression"); |
256 | |
257 | /* expr uses an anchored pattern match, so check that there was a |
258 | * match and that the match starts at offset 0. */ |
259 | if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH && |
260 | re_regs[0].rm_so == 0) { |
261 | /* Were \(...\) used? */ |
262 | if (re_buffer.re_nsub > 0) { |
263 | sv->u.s[re_regs[1].rm_eo] = '\0'; |
264 | v = str_value(sv->u.s + re_regs[1].rm_so); |
265 | } else |
266 | v = int_value(re_regs[0].rm_eo); |
267 | } else { |
268 | /* Match failed -- return the right kind of null. */ |
269 | if (re_buffer.re_nsub > 0) |
270 | v = str_value(""); |
271 | else |
272 | v = int_value(0); |
273 | } |
274 | return v; |
275 | } |
276 | |
277 | /* Handle bare operands and ( expr ) syntax. */ |
278 | |
279 | static VALUE *eval7(void) |
280 | { |
281 | VALUE *v; |
282 | |
283 | if (!*args) |
284 | bb_error_msg_and_die("syntax error"); |
285 | |
286 | if (nextarg("(")) { |
287 | args++; |
288 | v = eval(); |
289 | if (!nextarg(")")) |
290 | bb_error_msg_and_die("syntax error"); |
291 | args++; |
292 | return v; |
293 | } |
294 | |
295 | if (nextarg(")")) |
296 | bb_error_msg_and_die("syntax error"); |
297 | |
298 | return str_value(*args++); |
299 | } |
300 | |
301 | /* Handle match, substr, index, length, and quote keywords. */ |
302 | |
303 | static VALUE *eval6(void) |
304 | { |
305 | VALUE *l, *r, *v, *i1, *i2; |
306 | |
307 | if (nextarg("quote")) { |
308 | args++; |
309 | if (!*args) |
310 | bb_error_msg_and_die("syntax error"); |
311 | return str_value(*args++); |
312 | } else if (nextarg("length")) { |
313 | args++; |
314 | r = eval6(); |
315 | tostring(r); |
316 | v = int_value(strlen(r->u.s)); |
317 | freev(r); |
318 | return v; |
319 | } else if (nextarg("match")) { |
320 | args++; |
321 | l = eval6(); |
322 | r = eval6(); |
323 | v = docolon(l, r); |
324 | freev(l); |
325 | freev(r); |
326 | return v; |
327 | } else if (nextarg("index")) { |
328 | args++; |
329 | l = eval6(); |
330 | r = eval6(); |
331 | tostring(l); |
332 | tostring(r); |
333 | v = int_value(strcspn(l->u.s, r->u.s) + 1); |
334 | if (v->u.i == (arith_t) strlen(l->u.s) + 1) |
335 | v->u.i = 0; |
336 | freev(l); |
337 | freev(r); |
338 | return v; |
339 | } else if (nextarg("substr")) { |
340 | args++; |
341 | l = eval6(); |
342 | i1 = eval6(); |
343 | i2 = eval6(); |
344 | tostring(l); |
345 | if (!toarith(i1) || !toarith(i2) |
346 | || i1->u.i > (arith_t) strlen(l->u.s) |
347 | || i1->u.i <= 0 || i2->u.i <= 0) |
348 | v = str_value(""); |
349 | else { |
350 | v = xmalloc(sizeof(VALUE)); |
351 | v->type = string; |
352 | v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i); |
353 | } |
354 | freev(l); |
355 | freev(i1); |
356 | freev(i2); |
357 | return v; |
358 | } else |
359 | return eval7(); |
360 | } |
361 | |
362 | /* Handle : operator (pattern matching). |
363 | Calls docolon to do the real work. */ |
364 | |
365 | static VALUE *eval5(void) |
366 | { |
367 | VALUE *l, *r, *v; |
368 | |
369 | l = eval6(); |
370 | while (nextarg(":")) { |
371 | args++; |
372 | r = eval6(); |
373 | v = docolon(l, r); |
374 | freev(l); |
375 | freev(r); |
376 | l = v; |
377 | } |
378 | return l; |
379 | } |
380 | |
381 | /* Handle *, /, % operators. */ |
382 | |
383 | static VALUE *eval4(void) |
384 | { |
385 | VALUE *l, *r; |
386 | int op; |
387 | arith_t val; |
388 | |
389 | l = eval5(); |
390 | while (1) { |
391 | if (nextarg("*")) |
392 | op = '*'; |
393 | else if (nextarg("/")) |
394 | op = '/'; |
395 | else if (nextarg("%")) |
396 | op = '%'; |
397 | else |
398 | return l; |
399 | args++; |
400 | r = eval5(); |
401 | val = arithmetic_common(l, r, op); |
402 | freev(l); |
403 | freev(r); |
404 | l = int_value(val); |
405 | } |
406 | } |
407 | |
408 | /* Handle +, - operators. */ |
409 | |
410 | static VALUE *eval3(void) |
411 | { |
412 | VALUE *l, *r; |
413 | int op; |
414 | arith_t val; |
415 | |
416 | l = eval4(); |
417 | while (1) { |
418 | if (nextarg("+")) |
419 | op = '+'; |
420 | else if (nextarg("-")) |
421 | op = '-'; |
422 | else |
423 | return l; |
424 | args++; |
425 | r = eval4(); |
426 | val = arithmetic_common(l, r, op); |
427 | freev(l); |
428 | freev(r); |
429 | l = int_value(val); |
430 | } |
431 | } |
432 | |
433 | /* Handle comparisons. */ |
434 | |
435 | static VALUE *eval2(void) |
436 | { |
437 | VALUE *l, *r; |
438 | int op; |
439 | arith_t val; |
440 | |
441 | l = eval3(); |
442 | while (1) { |
443 | if (nextarg("<")) |
444 | op = '<'; |
445 | else if (nextarg("<=")) |
446 | op = 'L' + 'E'; |
447 | else if (nextarg("=") || nextarg("==")) |
448 | op = '='; |
449 | else if (nextarg("!=")) |
450 | op = '!'; |
451 | else if (nextarg(">=")) |
452 | op = 'G' + 'E'; |
453 | else if (nextarg(">")) |
454 | op = '>'; |
455 | else |
456 | return l; |
457 | args++; |
458 | r = eval3(); |
459 | toarith(l); |
460 | toarith(r); |
461 | val = cmp_common(l, r, op); |
462 | freev(l); |
463 | freev(r); |
464 | l = int_value(val); |
465 | } |
466 | } |
467 | |
468 | /* Handle &. */ |
469 | |
470 | static VALUE *eval1(void) |
471 | { |
472 | VALUE *l, *r; |
473 | |
474 | l = eval2(); |
475 | while (nextarg("&")) { |
476 | args++; |
477 | r = eval2(); |
478 | if (null(l) || null(r)) { |
479 | freev(l); |
480 | freev(r); |
481 | l = int_value(0); |
482 | } else |
483 | freev(r); |
484 | } |
485 | return l; |
486 | } |
487 | |
488 | /* Handle |. */ |
489 | |
490 | static VALUE *eval(void) |
491 | { |
492 | VALUE *l, *r; |
493 | |
494 | l = eval1(); |
495 | while (nextarg("|")) { |
496 | args++; |
497 | r = eval1(); |
498 | if (null(l)) { |
499 | freev(l); |
500 | l = r; |
501 | } else |
502 | freev(r); |
503 | } |
504 | return l; |
505 | } |