Annotation of /trunk/mkinitrd-magellan/busybox/coreutils/expr.c
Parent Directory | Revision Log
Revision 532 -
(hide annotations)
(download)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 months ago) by niro
File MIME type: text/plain
File size: 9560 byte(s)
Sat Sep 1 22:45:15 2007 UTC (16 years, 8 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 | niro | 532 | /* 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 | } |