diff options
author | Eric Andersen <andersen@codepoet.org> | 2000-09-05 17:37:48 +0000 |
---|---|---|
committer | Eric Andersen <andersen@codepoet.org> | 2000-09-05 17:37:48 +0000 |
commit | 1b355ebba68bdd567dd3961a18291dfd9532c2e8 (patch) | |
tree | c4c1cb4ed9cc025e05640e19c0b0a3f86080563a | |
parent | 43c8c38bbfe3d25e21f37d899184de52c6dc9c17 (diff) | |
download | busybox-w32-1b355ebba68bdd567dd3961a18291dfd9532c2e8.tar.gz busybox-w32-1b355ebba68bdd567dd3961a18291dfd9532c2e8.tar.bz2 busybox-w32-1b355ebba68bdd567dd3961a18291dfd9532c2e8.zip |
Added expr, from Edward Betts <edward@debian.org>, with some fixups
and docs added by me.
-Erik
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | TODO | 1 | ||||
-rw-r--r-- | applets/busybox.c | 3 | ||||
-rwxr-xr-x | applets/busybox.sh | 2 | ||||
-rw-r--r-- | applets/usage.c | 38 | ||||
-rw-r--r-- | busybox.c | 3 | ||||
-rw-r--r-- | busybox.def.h | 1 | ||||
-rwxr-xr-x | busybox.sh | 2 | ||||
-rw-r--r-- | coreutils/expr.c | 531 | ||||
-rw-r--r-- | coreutils/wc.c | 2 | ||||
-rw-r--r-- | docs/busybox.pod | 41 | ||||
-rw-r--r-- | docs/busybox.sgml | 52 | ||||
-rw-r--r-- | expr.c | 531 | ||||
-rw-r--r-- | internal.h | 3 | ||||
-rw-r--r-- | usage.c | 38 | ||||
-rw-r--r-- | wc.c | 2 |
16 files changed, 1247 insertions, 6 deletions
@@ -12,6 +12,9 @@ Erik Andersen <andersen@lineo.com>, <andersee@debian.org> | |||
12 | Tons of new stuff, major rewrite of most of the | 12 | Tons of new stuff, major rewrite of most of the |
13 | core apps, tons of new apps as noted in header files. | 13 | core apps, tons of new apps as noted in header files. |
14 | 14 | ||
15 | Edward Betts <edward@debian.org> | ||
16 | expr, hostid, logname, tty, wc, whoami, yes | ||
17 | |||
15 | John Beppu <beppu@lineo.com> | 18 | John Beppu <beppu@lineo.com> |
16 | du, head, nslookup, sort, tee, uniq | 19 | du, head, nslookup, sort, tee, uniq |
17 | 20 | ||
@@ -22,7 +22,6 @@ around to it some time. If you have any good ideas, please let me know. | |||
22 | * rdate | 22 | * rdate |
23 | * hwclock | 23 | * hwclock |
24 | * stty | 24 | * stty |
25 | * expr | ||
26 | * wget (or whatever I call it) | 25 | * wget (or whatever I call it) |
27 | * tftp | 26 | * tftp |
28 | * ftp | 27 | * ftp |
diff --git a/applets/busybox.c b/applets/busybox.c index 0ffa94e09..a7c5d37a0 100644 --- a/applets/busybox.c +++ b/applets/busybox.c | |||
@@ -76,6 +76,9 @@ const struct BB_applet applets[] = { | |||
76 | #ifdef BB_ECHO | 76 | #ifdef BB_ECHO |
77 | {"echo", echo_main, _BB_DIR_BIN, echo_usage}, | 77 | {"echo", echo_main, _BB_DIR_BIN, echo_usage}, |
78 | #endif | 78 | #endif |
79 | #ifdef BB_EXPR | ||
80 | {"expr", expr_main, _BB_DIR_USR_BIN, expr_usage}, | ||
81 | #endif | ||
79 | #ifdef BB_TRUE_FALSE | 82 | #ifdef BB_TRUE_FALSE |
80 | {"false", false_main, _BB_DIR_BIN, false_usage}, | 83 | {"false", false_main, _BB_DIR_BIN, false_usage}, |
81 | #endif | 84 | #endif |
diff --git a/applets/busybox.sh b/applets/busybox.sh index 5dd6e1c3d..2b4521065 100755 --- a/applets/busybox.sh +++ b/applets/busybox.sh | |||
@@ -5,6 +5,6 @@ | |||
5 | ls -1 ` \ | 5 | ls -1 ` \ |
6 | gcc -E -dM busybox.def.h | \ | 6 | gcc -E -dM busybox.def.h | \ |
7 | sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ | 7 | sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ |
8 | | tr [:upper:] [:lower:] | sort | 8 | | tr '[:upper:]' '[:lower:]' | sort |
9 | ` 2>/dev/null | sed -e 's/\.c$/\.o/g' | 9 | ` 2>/dev/null | sed -e 's/\.c$/\.o/g' |
10 | 10 | ||
diff --git a/applets/usage.c b/applets/usage.c index 91064f52c..05913f3e3 100644 --- a/applets/usage.c +++ b/applets/usage.c | |||
@@ -250,6 +250,44 @@ const char echo_usage[] = | |||
250 | ; | 250 | ; |
251 | #endif | 251 | #endif |
252 | 252 | ||
253 | #if defined BB_EXPR | ||
254 | const char expr_usage[] = | ||
255 | "expr EXPRESSION\n" | ||
256 | #ifndef BB_FEATURE_TRIVIAL_HELP | ||
257 | "\nPrints the value of EXPRESSION to standard output.\n\n" | ||
258 | "EXPRESSION may be:\n" | ||
259 | "ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" | ||
260 | "ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" | ||
261 | "ARG1 < ARG2 ARG1 is less than ARG2\n" | ||
262 | "ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" | ||
263 | "ARG1 = ARG2 ARG1 is equal to ARG2\n" | ||
264 | "ARG1 != ARG2 ARG1 is unequal to ARG2\n" | ||
265 | "ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" | ||
266 | "ARG1 > ARG2 ARG1 is greater than ARG2\n" | ||
267 | "ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n" | ||
268 | "ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n" | ||
269 | "ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n" | ||
270 | "ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n" | ||
271 | "ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n" | ||
272 | "STRING : REGEXP anchored pattern match of REGEXP in STRING\n" | ||
273 | "match STRING REGEXP same as STRING : REGEXP\n" | ||
274 | "substr STRING POS LENGTH substring of STRING, POS counted from 1\n" | ||
275 | "index STRING CHARS index in STRING where any CHARS is found, or 0\n" | ||
276 | "length STRING length of STRING\n" | ||
277 | "quote TOKEN interpret TOKEN as a string, even if it is a \n" | ||
278 | " keyword like `match' or an operator like `/'\n" | ||
279 | "( EXPRESSION ) value of EXPRESSION\n\n" | ||
280 | "Beware that many operators need to be escaped or quoted for shells.\n" | ||
281 | "Comparisons are arithmetic if both ARGs are numbers, else\n" | ||
282 | "lexicographical. Pattern matches return the string matched between \n" | ||
283 | "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" | ||
284 | "of characters matched or 0.\n" | ||
285 | |||
286 | #endif | ||
287 | ; | ||
288 | #endif | ||
289 | |||
290 | |||
253 | #if defined BB_TRUE_FALSE | 291 | #if defined BB_TRUE_FALSE |
254 | const char false_usage[] = | 292 | const char false_usage[] = |
255 | "false\n" | 293 | "false\n" |
@@ -76,6 +76,9 @@ const struct BB_applet applets[] = { | |||
76 | #ifdef BB_ECHO | 76 | #ifdef BB_ECHO |
77 | {"echo", echo_main, _BB_DIR_BIN, echo_usage}, | 77 | {"echo", echo_main, _BB_DIR_BIN, echo_usage}, |
78 | #endif | 78 | #endif |
79 | #ifdef BB_EXPR | ||
80 | {"expr", expr_main, _BB_DIR_USR_BIN, expr_usage}, | ||
81 | #endif | ||
79 | #ifdef BB_TRUE_FALSE | 82 | #ifdef BB_TRUE_FALSE |
80 | {"false", false_main, _BB_DIR_BIN, false_usage}, | 83 | {"false", false_main, _BB_DIR_BIN, false_usage}, |
81 | #endif | 84 | #endif |
diff --git a/busybox.def.h b/busybox.def.h index 3ad118dd6..f88851af5 100644 --- a/busybox.def.h +++ b/busybox.def.h | |||
@@ -27,6 +27,7 @@ | |||
27 | #define BB_DU | 27 | #define BB_DU |
28 | #define BB_DUMPKMAP | 28 | #define BB_DUMPKMAP |
29 | #define BB_ECHO | 29 | #define BB_ECHO |
30 | #define BB_EXPR | ||
30 | #define BB_FBSET | 31 | #define BB_FBSET |
31 | #define BB_FDFLUSH | 32 | #define BB_FDFLUSH |
32 | #define BB_FIND | 33 | #define BB_FIND |
diff --git a/busybox.sh b/busybox.sh index 5dd6e1c3d..2b4521065 100755 --- a/busybox.sh +++ b/busybox.sh | |||
@@ -5,6 +5,6 @@ | |||
5 | ls -1 ` \ | 5 | ls -1 ` \ |
6 | gcc -E -dM busybox.def.h | \ | 6 | gcc -E -dM busybox.def.h | \ |
7 | sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ | 7 | sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ |
8 | | tr [:upper:] [:lower:] | sort | 8 | | tr '[:upper:]' '[:lower:]' | sort |
9 | ` 2>/dev/null | sed -e 's/\.c$/\.o/g' | 9 | ` 2>/dev/null | sed -e 's/\.c$/\.o/g' |
10 | 10 | ||
diff --git a/coreutils/expr.c b/coreutils/expr.c new file mode 100644 index 000000000..9e3c04a11 --- /dev/null +++ b/coreutils/expr.c | |||
@@ -0,0 +1,531 @@ | |||
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 | * | ||
11 | * this program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the gnu general public license as published by | ||
13 | * the free software foundation; either version 2 of the license, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * this program is distributed in the hope that it will be useful, | ||
17 | * but without any warranty; without even the implied warranty of | ||
18 | * merchantability or fitness for a particular purpose. see the gnu | ||
19 | * general public license for more details. | ||
20 | * | ||
21 | * you should have received a copy of the gnu general public license | ||
22 | * along with this program; if not, write to the free software | ||
23 | * foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | /* This program evaluates expressions. Each token (operator, operand, | ||
28 | * parenthesis) of the expression must be a seperate argument. The | ||
29 | * parser used is a reasonably general one, though any incarnation of | ||
30 | * it is language-specific. It is especially nice for expressions. | ||
31 | * | ||
32 | * No parse tree is needed; a new node is evaluated immediately. | ||
33 | * One function can handle multiple operators all of equal precedence, | ||
34 | * provided they all associate ((x op x) op x). */ | ||
35 | |||
36 | #include "internal.h" | ||
37 | #include <stdio.h> | ||
38 | #include <sys/types.h> | ||
39 | |||
40 | #include <regex.h> | ||
41 | |||
42 | /* The kinds of value we can have. */ | ||
43 | enum valtype { | ||
44 | integer, | ||
45 | string | ||
46 | }; | ||
47 | typedef enum valtype TYPE; | ||
48 | |||
49 | /* A value is.... */ | ||
50 | struct valinfo { | ||
51 | TYPE type; /* Which kind. */ | ||
52 | union { /* The value itself. */ | ||
53 | int i; | ||
54 | char *s; | ||
55 | } u; | ||
56 | }; | ||
57 | typedef struct valinfo VALUE; | ||
58 | |||
59 | /* The arguments given to the program, minus the program name. */ | ||
60 | static char **args; | ||
61 | |||
62 | static VALUE *docolon (VALUE *sv, VALUE *pv); | ||
63 | static VALUE *eval (void); | ||
64 | static VALUE *int_value (int i); | ||
65 | static VALUE *str_value (char *s); | ||
66 | static int nextarg (char *str); | ||
67 | static int null (VALUE *v); | ||
68 | static int toarith (VALUE *v); | ||
69 | static void freev (VALUE *v); | ||
70 | static void tostring (VALUE *v); | ||
71 | |||
72 | int expr_main (int argc, char **argv) | ||
73 | { | ||
74 | VALUE *v; | ||
75 | |||
76 | if (argc == 1) { | ||
77 | fatalError("too few arguments\n"); | ||
78 | } | ||
79 | |||
80 | args = argv + 1; | ||
81 | |||
82 | v = eval (); | ||
83 | if (*args) | ||
84 | fatalError ("syntax error\n"); | ||
85 | |||
86 | if (v->type == integer) | ||
87 | printf ("%d\n", v->u.i); | ||
88 | else | ||
89 | printf ("%s\n", v->u.s); | ||
90 | |||
91 | exit (null (v)); | ||
92 | } | ||
93 | |||
94 | /* Return a VALUE for I. */ | ||
95 | |||
96 | static VALUE *int_value (int i) | ||
97 | { | ||
98 | VALUE *v; | ||
99 | |||
100 | v = xmalloc (sizeof(VALUE)); | ||
101 | v->type = integer; | ||
102 | v->u.i = i; | ||
103 | return v; | ||
104 | } | ||
105 | |||
106 | /* Return a VALUE for S. */ | ||
107 | |||
108 | static VALUE *str_value (char *s) | ||
109 | { | ||
110 | VALUE *v; | ||
111 | |||
112 | v = xmalloc (sizeof(VALUE)); | ||
113 | v->type = string; | ||
114 | v->u.s = strdup (s); | ||
115 | return v; | ||
116 | } | ||
117 | |||
118 | /* Free VALUE V, including structure components. */ | ||
119 | |||
120 | static void freev (VALUE *v) | ||
121 | { | ||
122 | if (v->type == string) | ||
123 | free (v->u.s); | ||
124 | free (v); | ||
125 | } | ||
126 | |||
127 | /* Return nonzero if V is a null-string or zero-number. */ | ||
128 | |||
129 | static int null (VALUE *v) | ||
130 | { | ||
131 | switch (v->type) { | ||
132 | case integer: | ||
133 | return v->u.i == 0; | ||
134 | case string: | ||
135 | return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0; | ||
136 | default: | ||
137 | abort (); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /* Coerce V to a string value (can't fail). */ | ||
142 | |||
143 | static void tostring (VALUE *v) | ||
144 | { | ||
145 | char *temp; | ||
146 | |||
147 | if (v->type == integer) { | ||
148 | temp = xmalloc (4 * (sizeof (int) / sizeof (char))); | ||
149 | sprintf (temp, "%d", v->u.i); | ||
150 | v->u.s = temp; | ||
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 | int i; | ||
160 | |||
161 | switch (v->type) { | ||
162 | case integer: | ||
163 | return 1; | ||
164 | case string: | ||
165 | i = 0; | ||
166 | /* Don't interpret the empty string as an integer. */ | ||
167 | if (v->u.s == 0) | ||
168 | return 0; | ||
169 | i = atoi(v->u.s); | ||
170 | free (v->u.s); | ||
171 | v->u.i = i; | ||
172 | v->type = integer; | ||
173 | return 1; | ||
174 | default: | ||
175 | abort (); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /* Return nonzero if the next token matches STR exactly. | ||
180 | STR must not be NULL. */ | ||
181 | |||
182 | static int | ||
183 | nextarg (char *str) | ||
184 | { | ||
185 | if (*args == NULL) | ||
186 | return 0; | ||
187 | return strcmp (*args, str) == 0; | ||
188 | } | ||
189 | |||
190 | /* The comparison operator handling functions. */ | ||
191 | |||
192 | #define cmpf(name, rel) \ | ||
193 | static int name (l, r) VALUE *l; VALUE *r; \ | ||
194 | { \ | ||
195 | if (l->type == string || r->type == string) { \ | ||
196 | tostring (l); \ | ||
197 | tostring (r); \ | ||
198 | return strcmp (l->u.s, r->u.s) rel 0; \ | ||
199 | } \ | ||
200 | else \ | ||
201 | return l->u.i rel r->u.i; \ | ||
202 | } | ||
203 | cmpf (less_than, <) | ||
204 | cmpf (less_equal, <=) | ||
205 | cmpf (equal, ==) | ||
206 | cmpf (not_equal, !=) | ||
207 | cmpf (greater_equal, >=) | ||
208 | cmpf (greater_than, >) | ||
209 | |||
210 | #undef cmpf | ||
211 | |||
212 | /* The arithmetic operator handling functions. */ | ||
213 | |||
214 | #define arithf(name, op) \ | ||
215 | static \ | ||
216 | int name (l, r) VALUE *l; VALUE *r; \ | ||
217 | { \ | ||
218 | if (!toarith (l) || !toarith (r)) \ | ||
219 | fatalError ("non-numeric argument\n"); \ | ||
220 | return l->u.i op r->u.i; \ | ||
221 | } | ||
222 | |||
223 | #define arithdivf(name, op) \ | ||
224 | int name (l, r) VALUE *l; VALUE *r; \ | ||
225 | { \ | ||
226 | if (!toarith (l) || !toarith (r)) \ | ||
227 | fatalError ( "non-numeric argument\n"); \ | ||
228 | if (r->u.i == 0) \ | ||
229 | fatalError ( "division by zero\n"); \ | ||
230 | return l->u.i op r->u.i; \ | ||
231 | } | ||
232 | |||
233 | arithf (plus, +) | ||
234 | arithf (minus, -) | ||
235 | arithf (multiply, *) | ||
236 | arithdivf (divide, /) | ||
237 | arithdivf (mod, %) | ||
238 | |||
239 | #undef arithf | ||
240 | #undef arithdivf | ||
241 | |||
242 | /* Do the : operator. | ||
243 | SV is the VALUE for the lhs (the string), | ||
244 | PV is the VALUE for the rhs (the pattern). */ | ||
245 | |||
246 | static VALUE *docolon (VALUE *sv, VALUE *pv) | ||
247 | { | ||
248 | VALUE *v; | ||
249 | const char *errmsg; | ||
250 | struct re_pattern_buffer re_buffer; | ||
251 | struct re_registers re_regs; | ||
252 | int len; | ||
253 | |||
254 | tostring (sv); | ||
255 | tostring (pv); | ||
256 | |||
257 | if (pv->u.s[0] == '^') { | ||
258 | fprintf (stderr, "\ | ||
259 | warning: unportable BRE: `%s': using `^' as the first character\n\ | ||
260 | of a basic regular expression is not portable; it is being ignored", | ||
261 | pv->u.s); | ||
262 | } | ||
263 | |||
264 | len = strlen (pv->u.s); | ||
265 | memset (&re_buffer, 0, sizeof (re_buffer)); | ||
266 | memset (&re_regs, 0, sizeof (re_regs)); | ||
267 | re_buffer.allocated = 2 * len; | ||
268 | re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated); | ||
269 | re_buffer.translate = 0; | ||
270 | re_syntax_options = RE_SYNTAX_POSIX_BASIC; | ||
271 | errmsg = re_compile_pattern (pv->u.s, len, &re_buffer); | ||
272 | if (errmsg) { | ||
273 | fatalError("%s\n", errmsg); | ||
274 | } | ||
275 | |||
276 | len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs); | ||
277 | if (len >= 0) { | ||
278 | /* Were \(...\) used? */ | ||
279 | if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */ | ||
280 | sv->u.s[re_regs.end[1]] = '\0'; | ||
281 | v = str_value (sv->u.s + re_regs.start[1]); | ||
282 | } | ||
283 | else | ||
284 | v = int_value (len); | ||
285 | } | ||
286 | else { | ||
287 | /* Match failed -- return the right kind of null. */ | ||
288 | if (re_buffer.re_nsub > 0) | ||
289 | v = str_value (""); | ||
290 | else | ||
291 | v = int_value (0); | ||
292 | } | ||
293 | free (re_buffer.buffer); | ||
294 | return v; | ||
295 | } | ||
296 | |||
297 | /* Handle bare operands and ( expr ) syntax. */ | ||
298 | |||
299 | static VALUE *eval7 (void) | ||
300 | { | ||
301 | VALUE *v; | ||
302 | |||
303 | if (!*args) | ||
304 | fatalError ( "syntax error\n"); | ||
305 | |||
306 | if (nextarg ("(")) { | ||
307 | args++; | ||
308 | v = eval (); | ||
309 | if (!nextarg (")")) | ||
310 | fatalError ( "syntax error\n"); | ||
311 | args++; | ||
312 | return v; | ||
313 | } | ||
314 | |||
315 | if (nextarg (")")) | ||
316 | fatalError ( "syntax error\n"); | ||
317 | |||
318 | return str_value (*args++); | ||
319 | } | ||
320 | |||
321 | /* Handle match, substr, index, length, and quote keywords. */ | ||
322 | |||
323 | static VALUE *eval6 (void) | ||
324 | { | ||
325 | VALUE *l, *r, *v, *i1, *i2; | ||
326 | |||
327 | if (nextarg ("quote")) { | ||
328 | args++; | ||
329 | if (!*args) | ||
330 | fatalError ( "syntax error\n"); | ||
331 | return str_value (*args++); | ||
332 | } | ||
333 | else if (nextarg ("length")) { | ||
334 | args++; | ||
335 | r = eval6 (); | ||
336 | tostring (r); | ||
337 | v = int_value (strlen (r->u.s)); | ||
338 | freev (r); | ||
339 | return v; | ||
340 | } | ||
341 | else if (nextarg ("match")) { | ||
342 | args++; | ||
343 | l = eval6 (); | ||
344 | r = eval6 (); | ||
345 | v = docolon (l, r); | ||
346 | freev (l); | ||
347 | freev (r); | ||
348 | return v; | ||
349 | } | ||
350 | else if (nextarg ("index")) { | ||
351 | args++; | ||
352 | l = eval6 (); | ||
353 | r = eval6 (); | ||
354 | tostring (l); | ||
355 | tostring (r); | ||
356 | v = int_value (strcspn (l->u.s, r->u.s) + 1); | ||
357 | if (v->u.i == (int) strlen (l->u.s) + 1) | ||
358 | v->u.i = 0; | ||
359 | freev (l); | ||
360 | freev (r); | ||
361 | return v; | ||
362 | } | ||
363 | else if (nextarg ("substr")) { | ||
364 | args++; | ||
365 | l = eval6 (); | ||
366 | i1 = eval6 (); | ||
367 | i2 = eval6 (); | ||
368 | tostring (l); | ||
369 | if (!toarith (i1) || !toarith (i2) | ||
370 | || i1->u.i > (int) strlen (l->u.s) | ||
371 | || i1->u.i <= 0 || i2->u.i <= 0) | ||
372 | v = str_value (""); | ||
373 | else { | ||
374 | v = xmalloc (sizeof(VALUE)); | ||
375 | v->type = string; | ||
376 | v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1), | ||
377 | l->u.s + i1->u.i - 1, i2->u.i); | ||
378 | v->u.s[i2->u.i] = 0; | ||
379 | } | ||
380 | freev (l); | ||
381 | freev (i1); | ||
382 | freev (i2); | ||
383 | return v; | ||
384 | } | ||
385 | else | ||
386 | return eval7 (); | ||
387 | } | ||
388 | |||
389 | /* Handle : operator (pattern matching). | ||
390 | Calls docolon to do the real work. */ | ||
391 | |||
392 | static VALUE *eval5 (void) | ||
393 | { | ||
394 | VALUE *l, *r, *v; | ||
395 | |||
396 | l = eval6 (); | ||
397 | while (nextarg (":")) { | ||
398 | args++; | ||
399 | r = eval6 (); | ||
400 | v = docolon (l, r); | ||
401 | freev (l); | ||
402 | freev (r); | ||
403 | l = v; | ||
404 | } | ||
405 | return l; | ||
406 | } | ||
407 | |||
408 | /* Handle *, /, % operators. */ | ||
409 | |||
410 | static VALUE *eval4 (void) | ||
411 | { | ||
412 | VALUE *l, *r; | ||
413 | int (*fxn) (), val; | ||
414 | |||
415 | l = eval5 (); | ||
416 | while (1) { | ||
417 | if (nextarg ("*")) | ||
418 | fxn = multiply; | ||
419 | else if (nextarg ("/")) | ||
420 | fxn = divide; | ||
421 | else if (nextarg ("%")) | ||
422 | fxn = mod; | ||
423 | else | ||
424 | return l; | ||
425 | args++; | ||
426 | r = eval5 (); | ||
427 | val = (*fxn) (l, r); | ||
428 | freev (l); | ||
429 | freev (r); | ||
430 | l = int_value (val); | ||
431 | } | ||
432 | } | ||
433 | |||
434 | /* Handle +, - operators. */ | ||
435 | |||
436 | static VALUE *eval3 (void) | ||
437 | { | ||
438 | VALUE *l, *r; | ||
439 | int (*fxn) (), val; | ||
440 | |||
441 | l = eval4 (); | ||
442 | while (1) { | ||
443 | if (nextarg ("+")) | ||
444 | fxn = plus; | ||
445 | else if (nextarg ("-")) | ||
446 | fxn = minus; | ||
447 | else | ||
448 | return l; | ||
449 | args++; | ||
450 | r = eval4 (); | ||
451 | val = (*fxn) (l, r); | ||
452 | freev (l); | ||
453 | freev (r); | ||
454 | l = int_value (val); | ||
455 | } | ||
456 | } | ||
457 | |||
458 | /* Handle comparisons. */ | ||
459 | |||
460 | static VALUE *eval2 (void) | ||
461 | { | ||
462 | VALUE *l, *r; | ||
463 | int (*fxn) (), val; | ||
464 | |||
465 | l = eval3 (); | ||
466 | while (1) { | ||
467 | if (nextarg ("<")) | ||
468 | fxn = less_than; | ||
469 | else if (nextarg ("<=")) | ||
470 | fxn = less_equal; | ||
471 | else if (nextarg ("=") || nextarg ("==")) | ||
472 | fxn = equal; | ||
473 | else if (nextarg ("!=")) | ||
474 | fxn = not_equal; | ||
475 | else if (nextarg (">=")) | ||
476 | fxn = greater_equal; | ||
477 | else if (nextarg (">")) | ||
478 | fxn = greater_than; | ||
479 | else | ||
480 | return l; | ||
481 | args++; | ||
482 | r = eval3 (); | ||
483 | toarith (l); | ||
484 | toarith (r); | ||
485 | val = (*fxn) (l, r); | ||
486 | freev (l); | ||
487 | freev (r); | ||
488 | l = int_value (val); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | /* Handle &. */ | ||
493 | |||
494 | static VALUE *eval1 (void) | ||
495 | { | ||
496 | VALUE *l, *r; | ||
497 | |||
498 | l = eval2 (); | ||
499 | while (nextarg ("&")) { | ||
500 | args++; | ||
501 | r = eval2 (); | ||
502 | if (null (l) || null (r)) { | ||
503 | freev (l); | ||
504 | freev (r); | ||
505 | l = int_value (0); | ||
506 | } | ||
507 | else | ||
508 | freev (r); | ||
509 | } | ||
510 | return l; | ||
511 | } | ||
512 | |||
513 | /* Handle |. */ | ||
514 | |||
515 | static VALUE *eval (void) | ||
516 | { | ||
517 | VALUE *l, *r; | ||
518 | |||
519 | l = eval1 (); | ||
520 | while (nextarg ("|")) { | ||
521 | args++; | ||
522 | r = eval1 (); | ||
523 | if (null (l)) { | ||
524 | freev (l); | ||
525 | l = r; | ||
526 | } | ||
527 | else | ||
528 | freev (r); | ||
529 | } | ||
530 | return l; | ||
531 | } | ||
diff --git a/coreutils/wc.c b/coreutils/wc.c index 02e2b2aa6..ca5b3680a 100644 --- a/coreutils/wc.c +++ b/coreutils/wc.c | |||
@@ -2,7 +2,7 @@ | |||
2 | /* | 2 | /* |
3 | * Mini wc implementation for busybox | 3 | * Mini wc implementation for busybox |
4 | * | 4 | * |
5 | * by Edward Betts <edward@debian.org> | 5 | * Copyright (C) 2000 Edward Betts <edward@debian.org> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by |
diff --git a/docs/busybox.pod b/docs/busybox.pod index accdaed23..3bedcefb7 100644 --- a/docs/busybox.pod +++ b/docs/busybox.pod | |||
@@ -497,6 +497,45 @@ Example: | |||
497 | 497 | ||
498 | ------------------------------- | 498 | ------------------------------- |
499 | 499 | ||
500 | =item echo | ||
501 | |||
502 | |||
503 | Usage: expr EXPRESSION | ||
504 | |||
505 | Prints the value of EXPRESSION to standard output. | ||
506 | |||
507 | EXPRESSION may be: | ||
508 | |||
509 | ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 | ||
510 | ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 | ||
511 | ARG1 < ARG2 ARG1 is less than ARG2 | ||
512 | ARG1 <= ARG2 ARG1 is less than or equal to ARG2 | ||
513 | ARG1 = ARG2 ARG1 is equal to ARG2 | ||
514 | ARG1 != ARG2 ARG1 is unequal to ARG2 | ||
515 | ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 | ||
516 | ARG1 > ARG2 ARG1 is greater than ARG2 | ||
517 | ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 | ||
518 | ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 | ||
519 | ARG1 * ARG2 arithmetic product of ARG1 and ARG2 | ||
520 | ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 | ||
521 | ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 | ||
522 | STRING : REGEXP anchored pattern match of REGEXP in STRING | ||
523 | match STRING REGEXP same as STRING : REGEXP | ||
524 | substr STRING POS LENGTH substring of STRING, POS counted from 1 | ||
525 | index STRING CHARS index in STRING where any CHARS is found, or 0 | ||
526 | length STRING length of STRING | ||
527 | quote TOKEN interpret TOKEN as a string, even if it is a | ||
528 | keyword like `match' or an operator like `/' | ||
529 | ( EXPRESSION ) value of EXPRESSION | ||
530 | |||
531 | Beware that many operators need to be escaped or quoted for shells. | ||
532 | Comparisons are arithmetic if both ARGs are numbers, else | ||
533 | lexicographical. Pattern matches return the string matched between | ||
534 | \( and \) or null; if \( and \) are not used, they return the number | ||
535 | of characters matched or 0. | ||
536 | |||
537 | ------------------------------- | ||
538 | |||
500 | =item false | 539 | =item false |
501 | 540 | ||
502 | Returns an exit code of FALSE (1) | 541 | Returns an exit code of FALSE (1) |
@@ -2112,4 +2151,4 @@ Enrique Zanardi <ezanardi@ull.es> | |||
2112 | 2151 | ||
2113 | =cut | 2152 | =cut |
2114 | 2153 | ||
2115 | # $Id: busybox.pod,v 1.65 2000/09/01 16:12:57 andersen Exp $ | 2154 | # $Id: busybox.pod,v 1.66 2000/09/05 17:37:48 andersen Exp $ |
diff --git a/docs/busybox.sgml b/docs/busybox.sgml index ec6f5041d..a25aa0189 100644 --- a/docs/busybox.sgml +++ b/docs/busybox.sgml | |||
@@ -879,6 +879,58 @@ | |||
879 | </para> | 879 | </para> |
880 | </sect1> | 880 | </sect1> |
881 | 881 | ||
882 | <sect1 id="expr"> | ||
883 | <title>expr</title> | ||
884 | |||
885 | <para> | ||
886 | Usage: expr EXPRESSION | ||
887 | </para> | ||
888 | |||
889 | <para> | ||
890 | Prints the value of EXPRESSION to standard output. | ||
891 | </para> | ||
892 | |||
893 | <para> | ||
894 | EXPRESSION may be: | ||
895 | </para> | ||
896 | |||
897 | <para> | ||
898 | <screen> | ||
899 | ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2 | ||
900 | ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0 | ||
901 | ARG1 < ARG2 ARG1 is less than ARG2 | ||
902 | ARG1 <= ARG2 ARG1 is less than or equal to ARG2 | ||
903 | ARG1 = ARG2 ARG1 is equal to ARG2 | ||
904 | ARG1 != ARG2 ARG1 is unequal to ARG2 | ||
905 | ARG1 >= ARG2 ARG1 is greater than or equal to ARG2 | ||
906 | ARG1 > ARG2 ARG1 is greater than ARG2 | ||
907 | ARG1 + ARG2 arithmetic sum of ARG1 and ARG2 | ||
908 | ARG1 - ARG2 arithmetic difference of ARG1 and ARG2 | ||
909 | ARG1 * ARG2 arithmetic product of ARG1 and ARG2 | ||
910 | ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2 | ||
911 | ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2 | ||
912 | STRING : REGEXP anchored pattern match of REGEXP in STRING | ||
913 | match STRING REGEXP same as STRING : REGEXP | ||
914 | substr STRING POS LENGTH substring of STRING, POS counted from 1 | ||
915 | index STRING CHARS index in STRING where any CHARS is found, or 0 | ||
916 | length STRING length of STRING | ||
917 | quote TOKEN interpret TOKEN as a string, even if it is a | ||
918 | keyword like `match' or an operator like `/' | ||
919 | ( EXPRESSION ) value of EXPRESSION | ||
920 | </screen> | ||
921 | </para> | ||
922 | |||
923 | <para> | ||
924 | Beware that many operators need to be escaped or quoted for shells. | ||
925 | Comparisons are arithmetic if both ARGs are numbers, else | ||
926 | lexicographical. Pattern matches return the string matched between | ||
927 | \( and \) or null; if \( and \) are not used, they return the number | ||
928 | of characters matched or 0. | ||
929 | </para> | ||
930 | |||
931 | </sect1> | ||
932 | |||
933 | |||
882 | <sect1 id="false"> | 934 | <sect1 id="false"> |
883 | <title>false</title> | 935 | <title>false</title> |
884 | 936 | ||
@@ -0,0 +1,531 @@ | |||
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 | * | ||
11 | * this program is free software; you can redistribute it and/or modify | ||
12 | * it under the terms of the gnu general public license as published by | ||
13 | * the free software foundation; either version 2 of the license, or | ||
14 | * (at your option) any later version. | ||
15 | * | ||
16 | * this program is distributed in the hope that it will be useful, | ||
17 | * but without any warranty; without even the implied warranty of | ||
18 | * merchantability or fitness for a particular purpose. see the gnu | ||
19 | * general public license for more details. | ||
20 | * | ||
21 | * you should have received a copy of the gnu general public license | ||
22 | * along with this program; if not, write to the free software | ||
23 | * foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | /* This program evaluates expressions. Each token (operator, operand, | ||
28 | * parenthesis) of the expression must be a seperate argument. The | ||
29 | * parser used is a reasonably general one, though any incarnation of | ||
30 | * it is language-specific. It is especially nice for expressions. | ||
31 | * | ||
32 | * No parse tree is needed; a new node is evaluated immediately. | ||
33 | * One function can handle multiple operators all of equal precedence, | ||
34 | * provided they all associate ((x op x) op x). */ | ||
35 | |||
36 | #include "internal.h" | ||
37 | #include <stdio.h> | ||
38 | #include <sys/types.h> | ||
39 | |||
40 | #include <regex.h> | ||
41 | |||
42 | /* The kinds of value we can have. */ | ||
43 | enum valtype { | ||
44 | integer, | ||
45 | string | ||
46 | }; | ||
47 | typedef enum valtype TYPE; | ||
48 | |||
49 | /* A value is.... */ | ||
50 | struct valinfo { | ||
51 | TYPE type; /* Which kind. */ | ||
52 | union { /* The value itself. */ | ||
53 | int i; | ||
54 | char *s; | ||
55 | } u; | ||
56 | }; | ||
57 | typedef struct valinfo VALUE; | ||
58 | |||
59 | /* The arguments given to the program, minus the program name. */ | ||
60 | static char **args; | ||
61 | |||
62 | static VALUE *docolon (VALUE *sv, VALUE *pv); | ||
63 | static VALUE *eval (void); | ||
64 | static VALUE *int_value (int i); | ||
65 | static VALUE *str_value (char *s); | ||
66 | static int nextarg (char *str); | ||
67 | static int null (VALUE *v); | ||
68 | static int toarith (VALUE *v); | ||
69 | static void freev (VALUE *v); | ||
70 | static void tostring (VALUE *v); | ||
71 | |||
72 | int expr_main (int argc, char **argv) | ||
73 | { | ||
74 | VALUE *v; | ||
75 | |||
76 | if (argc == 1) { | ||
77 | fatalError("too few arguments\n"); | ||
78 | } | ||
79 | |||
80 | args = argv + 1; | ||
81 | |||
82 | v = eval (); | ||
83 | if (*args) | ||
84 | fatalError ("syntax error\n"); | ||
85 | |||
86 | if (v->type == integer) | ||
87 | printf ("%d\n", v->u.i); | ||
88 | else | ||
89 | printf ("%s\n", v->u.s); | ||
90 | |||
91 | exit (null (v)); | ||
92 | } | ||
93 | |||
94 | /* Return a VALUE for I. */ | ||
95 | |||
96 | static VALUE *int_value (int i) | ||
97 | { | ||
98 | VALUE *v; | ||
99 | |||
100 | v = xmalloc (sizeof(VALUE)); | ||
101 | v->type = integer; | ||
102 | v->u.i = i; | ||
103 | return v; | ||
104 | } | ||
105 | |||
106 | /* Return a VALUE for S. */ | ||
107 | |||
108 | static VALUE *str_value (char *s) | ||
109 | { | ||
110 | VALUE *v; | ||
111 | |||
112 | v = xmalloc (sizeof(VALUE)); | ||
113 | v->type = string; | ||
114 | v->u.s = strdup (s); | ||
115 | return v; | ||
116 | } | ||
117 | |||
118 | /* Free VALUE V, including structure components. */ | ||
119 | |||
120 | static void freev (VALUE *v) | ||
121 | { | ||
122 | if (v->type == string) | ||
123 | free (v->u.s); | ||
124 | free (v); | ||
125 | } | ||
126 | |||
127 | /* Return nonzero if V is a null-string or zero-number. */ | ||
128 | |||
129 | static int null (VALUE *v) | ||
130 | { | ||
131 | switch (v->type) { | ||
132 | case integer: | ||
133 | return v->u.i == 0; | ||
134 | case string: | ||
135 | return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0; | ||
136 | default: | ||
137 | abort (); | ||
138 | } | ||
139 | } | ||
140 | |||
141 | /* Coerce V to a string value (can't fail). */ | ||
142 | |||
143 | static void tostring (VALUE *v) | ||
144 | { | ||
145 | char *temp; | ||
146 | |||
147 | if (v->type == integer) { | ||
148 | temp = xmalloc (4 * (sizeof (int) / sizeof (char))); | ||
149 | sprintf (temp, "%d", v->u.i); | ||
150 | v->u.s = temp; | ||
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 | int i; | ||
160 | |||
161 | switch (v->type) { | ||
162 | case integer: | ||
163 | return 1; | ||
164 | case string: | ||
165 | i = 0; | ||
166 | /* Don't interpret the empty string as an integer. */ | ||
167 | if (v->u.s == 0) | ||
168 | return 0; | ||
169 | i = atoi(v->u.s); | ||
170 | free (v->u.s); | ||
171 | v->u.i = i; | ||
172 | v->type = integer; | ||
173 | return 1; | ||
174 | default: | ||
175 | abort (); | ||
176 | } | ||
177 | } | ||
178 | |||
179 | /* Return nonzero if the next token matches STR exactly. | ||
180 | STR must not be NULL. */ | ||
181 | |||
182 | static int | ||
183 | nextarg (char *str) | ||
184 | { | ||
185 | if (*args == NULL) | ||
186 | return 0; | ||
187 | return strcmp (*args, str) == 0; | ||
188 | } | ||
189 | |||
190 | /* The comparison operator handling functions. */ | ||
191 | |||
192 | #define cmpf(name, rel) \ | ||
193 | static int name (l, r) VALUE *l; VALUE *r; \ | ||
194 | { \ | ||
195 | if (l->type == string || r->type == string) { \ | ||
196 | tostring (l); \ | ||
197 | tostring (r); \ | ||
198 | return strcmp (l->u.s, r->u.s) rel 0; \ | ||
199 | } \ | ||
200 | else \ | ||
201 | return l->u.i rel r->u.i; \ | ||
202 | } | ||
203 | cmpf (less_than, <) | ||
204 | cmpf (less_equal, <=) | ||
205 | cmpf (equal, ==) | ||
206 | cmpf (not_equal, !=) | ||
207 | cmpf (greater_equal, >=) | ||
208 | cmpf (greater_than, >) | ||
209 | |||
210 | #undef cmpf | ||
211 | |||
212 | /* The arithmetic operator handling functions. */ | ||
213 | |||
214 | #define arithf(name, op) \ | ||
215 | static \ | ||
216 | int name (l, r) VALUE *l; VALUE *r; \ | ||
217 | { \ | ||
218 | if (!toarith (l) || !toarith (r)) \ | ||
219 | fatalError ("non-numeric argument\n"); \ | ||
220 | return l->u.i op r->u.i; \ | ||
221 | } | ||
222 | |||
223 | #define arithdivf(name, op) \ | ||
224 | int name (l, r) VALUE *l; VALUE *r; \ | ||
225 | { \ | ||
226 | if (!toarith (l) || !toarith (r)) \ | ||
227 | fatalError ( "non-numeric argument\n"); \ | ||
228 | if (r->u.i == 0) \ | ||
229 | fatalError ( "division by zero\n"); \ | ||
230 | return l->u.i op r->u.i; \ | ||
231 | } | ||
232 | |||
233 | arithf (plus, +) | ||
234 | arithf (minus, -) | ||
235 | arithf (multiply, *) | ||
236 | arithdivf (divide, /) | ||
237 | arithdivf (mod, %) | ||
238 | |||
239 | #undef arithf | ||
240 | #undef arithdivf | ||
241 | |||
242 | /* Do the : operator. | ||
243 | SV is the VALUE for the lhs (the string), | ||
244 | PV is the VALUE for the rhs (the pattern). */ | ||
245 | |||
246 | static VALUE *docolon (VALUE *sv, VALUE *pv) | ||
247 | { | ||
248 | VALUE *v; | ||
249 | const char *errmsg; | ||
250 | struct re_pattern_buffer re_buffer; | ||
251 | struct re_registers re_regs; | ||
252 | int len; | ||
253 | |||
254 | tostring (sv); | ||
255 | tostring (pv); | ||
256 | |||
257 | if (pv->u.s[0] == '^') { | ||
258 | fprintf (stderr, "\ | ||
259 | warning: unportable BRE: `%s': using `^' as the first character\n\ | ||
260 | of a basic regular expression is not portable; it is being ignored", | ||
261 | pv->u.s); | ||
262 | } | ||
263 | |||
264 | len = strlen (pv->u.s); | ||
265 | memset (&re_buffer, 0, sizeof (re_buffer)); | ||
266 | memset (&re_regs, 0, sizeof (re_regs)); | ||
267 | re_buffer.allocated = 2 * len; | ||
268 | re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated); | ||
269 | re_buffer.translate = 0; | ||
270 | re_syntax_options = RE_SYNTAX_POSIX_BASIC; | ||
271 | errmsg = re_compile_pattern (pv->u.s, len, &re_buffer); | ||
272 | if (errmsg) { | ||
273 | fatalError("%s\n", errmsg); | ||
274 | } | ||
275 | |||
276 | len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs); | ||
277 | if (len >= 0) { | ||
278 | /* Were \(...\) used? */ | ||
279 | if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */ | ||
280 | sv->u.s[re_regs.end[1]] = '\0'; | ||
281 | v = str_value (sv->u.s + re_regs.start[1]); | ||
282 | } | ||
283 | else | ||
284 | v = int_value (len); | ||
285 | } | ||
286 | else { | ||
287 | /* Match failed -- return the right kind of null. */ | ||
288 | if (re_buffer.re_nsub > 0) | ||
289 | v = str_value (""); | ||
290 | else | ||
291 | v = int_value (0); | ||
292 | } | ||
293 | free (re_buffer.buffer); | ||
294 | return v; | ||
295 | } | ||
296 | |||
297 | /* Handle bare operands and ( expr ) syntax. */ | ||
298 | |||
299 | static VALUE *eval7 (void) | ||
300 | { | ||
301 | VALUE *v; | ||
302 | |||
303 | if (!*args) | ||
304 | fatalError ( "syntax error\n"); | ||
305 | |||
306 | if (nextarg ("(")) { | ||
307 | args++; | ||
308 | v = eval (); | ||
309 | if (!nextarg (")")) | ||
310 | fatalError ( "syntax error\n"); | ||
311 | args++; | ||
312 | return v; | ||
313 | } | ||
314 | |||
315 | if (nextarg (")")) | ||
316 | fatalError ( "syntax error\n"); | ||
317 | |||
318 | return str_value (*args++); | ||
319 | } | ||
320 | |||
321 | /* Handle match, substr, index, length, and quote keywords. */ | ||
322 | |||
323 | static VALUE *eval6 (void) | ||
324 | { | ||
325 | VALUE *l, *r, *v, *i1, *i2; | ||
326 | |||
327 | if (nextarg ("quote")) { | ||
328 | args++; | ||
329 | if (!*args) | ||
330 | fatalError ( "syntax error\n"); | ||
331 | return str_value (*args++); | ||
332 | } | ||
333 | else if (nextarg ("length")) { | ||
334 | args++; | ||
335 | r = eval6 (); | ||
336 | tostring (r); | ||
337 | v = int_value (strlen (r->u.s)); | ||
338 | freev (r); | ||
339 | return v; | ||
340 | } | ||
341 | else if (nextarg ("match")) { | ||
342 | args++; | ||
343 | l = eval6 (); | ||
344 | r = eval6 (); | ||
345 | v = docolon (l, r); | ||
346 | freev (l); | ||
347 | freev (r); | ||
348 | return v; | ||
349 | } | ||
350 | else if (nextarg ("index")) { | ||
351 | args++; | ||
352 | l = eval6 (); | ||
353 | r = eval6 (); | ||
354 | tostring (l); | ||
355 | tostring (r); | ||
356 | v = int_value (strcspn (l->u.s, r->u.s) + 1); | ||
357 | if (v->u.i == (int) strlen (l->u.s) + 1) | ||
358 | v->u.i = 0; | ||
359 | freev (l); | ||
360 | freev (r); | ||
361 | return v; | ||
362 | } | ||
363 | else if (nextarg ("substr")) { | ||
364 | args++; | ||
365 | l = eval6 (); | ||
366 | i1 = eval6 (); | ||
367 | i2 = eval6 (); | ||
368 | tostring (l); | ||
369 | if (!toarith (i1) || !toarith (i2) | ||
370 | || i1->u.i > (int) strlen (l->u.s) | ||
371 | || i1->u.i <= 0 || i2->u.i <= 0) | ||
372 | v = str_value (""); | ||
373 | else { | ||
374 | v = xmalloc (sizeof(VALUE)); | ||
375 | v->type = string; | ||
376 | v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1), | ||
377 | l->u.s + i1->u.i - 1, i2->u.i); | ||
378 | v->u.s[i2->u.i] = 0; | ||
379 | } | ||
380 | freev (l); | ||
381 | freev (i1); | ||
382 | freev (i2); | ||
383 | return v; | ||
384 | } | ||
385 | else | ||
386 | return eval7 (); | ||
387 | } | ||
388 | |||
389 | /* Handle : operator (pattern matching). | ||
390 | Calls docolon to do the real work. */ | ||
391 | |||
392 | static VALUE *eval5 (void) | ||
393 | { | ||
394 | VALUE *l, *r, *v; | ||
395 | |||
396 | l = eval6 (); | ||
397 | while (nextarg (":")) { | ||
398 | args++; | ||
399 | r = eval6 (); | ||
400 | v = docolon (l, r); | ||
401 | freev (l); | ||
402 | freev (r); | ||
403 | l = v; | ||
404 | } | ||
405 | return l; | ||
406 | } | ||
407 | |||
408 | /* Handle *, /, % operators. */ | ||
409 | |||
410 | static VALUE *eval4 (void) | ||
411 | { | ||
412 | VALUE *l, *r; | ||
413 | int (*fxn) (), val; | ||
414 | |||
415 | l = eval5 (); | ||
416 | while (1) { | ||
417 | if (nextarg ("*")) | ||
418 | fxn = multiply; | ||
419 | else if (nextarg ("/")) | ||
420 | fxn = divide; | ||
421 | else if (nextarg ("%")) | ||
422 | fxn = mod; | ||
423 | else | ||
424 | return l; | ||
425 | args++; | ||
426 | r = eval5 (); | ||
427 | val = (*fxn) (l, r); | ||
428 | freev (l); | ||
429 | freev (r); | ||
430 | l = int_value (val); | ||
431 | } | ||
432 | } | ||
433 | |||
434 | /* Handle +, - operators. */ | ||
435 | |||
436 | static VALUE *eval3 (void) | ||
437 | { | ||
438 | VALUE *l, *r; | ||
439 | int (*fxn) (), val; | ||
440 | |||
441 | l = eval4 (); | ||
442 | while (1) { | ||
443 | if (nextarg ("+")) | ||
444 | fxn = plus; | ||
445 | else if (nextarg ("-")) | ||
446 | fxn = minus; | ||
447 | else | ||
448 | return l; | ||
449 | args++; | ||
450 | r = eval4 (); | ||
451 | val = (*fxn) (l, r); | ||
452 | freev (l); | ||
453 | freev (r); | ||
454 | l = int_value (val); | ||
455 | } | ||
456 | } | ||
457 | |||
458 | /* Handle comparisons. */ | ||
459 | |||
460 | static VALUE *eval2 (void) | ||
461 | { | ||
462 | VALUE *l, *r; | ||
463 | int (*fxn) (), val; | ||
464 | |||
465 | l = eval3 (); | ||
466 | while (1) { | ||
467 | if (nextarg ("<")) | ||
468 | fxn = less_than; | ||
469 | else if (nextarg ("<=")) | ||
470 | fxn = less_equal; | ||
471 | else if (nextarg ("=") || nextarg ("==")) | ||
472 | fxn = equal; | ||
473 | else if (nextarg ("!=")) | ||
474 | fxn = not_equal; | ||
475 | else if (nextarg (">=")) | ||
476 | fxn = greater_equal; | ||
477 | else if (nextarg (">")) | ||
478 | fxn = greater_than; | ||
479 | else | ||
480 | return l; | ||
481 | args++; | ||
482 | r = eval3 (); | ||
483 | toarith (l); | ||
484 | toarith (r); | ||
485 | val = (*fxn) (l, r); | ||
486 | freev (l); | ||
487 | freev (r); | ||
488 | l = int_value (val); | ||
489 | } | ||
490 | } | ||
491 | |||
492 | /* Handle &. */ | ||
493 | |||
494 | static VALUE *eval1 (void) | ||
495 | { | ||
496 | VALUE *l, *r; | ||
497 | |||
498 | l = eval2 (); | ||
499 | while (nextarg ("&")) { | ||
500 | args++; | ||
501 | r = eval2 (); | ||
502 | if (null (l) || null (r)) { | ||
503 | freev (l); | ||
504 | freev (r); | ||
505 | l = int_value (0); | ||
506 | } | ||
507 | else | ||
508 | freev (r); | ||
509 | } | ||
510 | return l; | ||
511 | } | ||
512 | |||
513 | /* Handle |. */ | ||
514 | |||
515 | static VALUE *eval (void) | ||
516 | { | ||
517 | VALUE *l, *r; | ||
518 | |||
519 | l = eval1 (); | ||
520 | while (nextarg ("|")) { | ||
521 | args++; | ||
522 | r = eval1 (); | ||
523 | if (null (l)) { | ||
524 | freev (l); | ||
525 | l = r; | ||
526 | } | ||
527 | else | ||
528 | freev (r); | ||
529 | } | ||
530 | return l; | ||
531 | } | ||
diff --git a/internal.h b/internal.h index 33f101e09..b2034da5e 100644 --- a/internal.h +++ b/internal.h | |||
@@ -110,6 +110,7 @@ extern const struct BB_applet applets[]; | |||
110 | 110 | ||
111 | extern int ar_main(int argc, char **argv); | 111 | extern int ar_main(int argc, char **argv); |
112 | extern int basename_main(int argc, char **argv); | 112 | extern int basename_main(int argc, char **argv); |
113 | extern int bogomips_main(int argc, char **argv); | ||
113 | extern int busybox_main(int argc, char** argv); | 114 | extern int busybox_main(int argc, char** argv); |
114 | extern int cat_main(int argc, char** argv); | 115 | extern int cat_main(int argc, char** argv); |
115 | extern int chmod_chown_chgrp_main(int argc, char** argv); | 116 | extern int chmod_chown_chgrp_main(int argc, char** argv); |
@@ -129,6 +130,7 @@ extern int du_main(int argc, char** argv); | |||
129 | extern int dumpkmap_main(int argc, char** argv); | 130 | extern int dumpkmap_main(int argc, char** argv); |
130 | extern int dutmp_main(int argc, char** argv); | 131 | extern int dutmp_main(int argc, char** argv); |
131 | extern int echo_main(int argc, char** argv); | 132 | extern int echo_main(int argc, char** argv); |
133 | extern int expr_main(int argc, char** argv); | ||
132 | extern int false_main(int argc, char** argv); | 134 | extern int false_main(int argc, char** argv); |
133 | extern int fbset_main(int argc, char** argv); | 135 | extern int fbset_main(int argc, char** argv); |
134 | extern int fdisk_main(int argc, char** argv); | 136 | extern int fdisk_main(int argc, char** argv); |
@@ -240,6 +242,7 @@ extern const char du_usage[]; | |||
240 | extern const char dumpkmap_usage[]; | 242 | extern const char dumpkmap_usage[]; |
241 | extern const char dutmp_usage[]; | 243 | extern const char dutmp_usage[]; |
242 | extern const char echo_usage[]; | 244 | extern const char echo_usage[]; |
245 | extern const char expr_usage[]; | ||
243 | extern const char false_usage[]; | 246 | extern const char false_usage[]; |
244 | extern const char fdflush_usage[]; | 247 | extern const char fdflush_usage[]; |
245 | extern const char find_usage[]; | 248 | extern const char find_usage[]; |
@@ -250,6 +250,44 @@ const char echo_usage[] = | |||
250 | ; | 250 | ; |
251 | #endif | 251 | #endif |
252 | 252 | ||
253 | #if defined BB_EXPR | ||
254 | const char expr_usage[] = | ||
255 | "expr EXPRESSION\n" | ||
256 | #ifndef BB_FEATURE_TRIVIAL_HELP | ||
257 | "\nPrints the value of EXPRESSION to standard output.\n\n" | ||
258 | "EXPRESSION may be:\n" | ||
259 | "ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n" | ||
260 | "ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n" | ||
261 | "ARG1 < ARG2 ARG1 is less than ARG2\n" | ||
262 | "ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n" | ||
263 | "ARG1 = ARG2 ARG1 is equal to ARG2\n" | ||
264 | "ARG1 != ARG2 ARG1 is unequal to ARG2\n" | ||
265 | "ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n" | ||
266 | "ARG1 > ARG2 ARG1 is greater than ARG2\n" | ||
267 | "ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n" | ||
268 | "ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n" | ||
269 | "ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n" | ||
270 | "ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n" | ||
271 | "ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n" | ||
272 | "STRING : REGEXP anchored pattern match of REGEXP in STRING\n" | ||
273 | "match STRING REGEXP same as STRING : REGEXP\n" | ||
274 | "substr STRING POS LENGTH substring of STRING, POS counted from 1\n" | ||
275 | "index STRING CHARS index in STRING where any CHARS is found, or 0\n" | ||
276 | "length STRING length of STRING\n" | ||
277 | "quote TOKEN interpret TOKEN as a string, even if it is a \n" | ||
278 | " keyword like `match' or an operator like `/'\n" | ||
279 | "( EXPRESSION ) value of EXPRESSION\n\n" | ||
280 | "Beware that many operators need to be escaped or quoted for shells.\n" | ||
281 | "Comparisons are arithmetic if both ARGs are numbers, else\n" | ||
282 | "lexicographical. Pattern matches return the string matched between \n" | ||
283 | "\\( and \\) or null; if \\( and \\) are not used, they return the number \n" | ||
284 | "of characters matched or 0.\n" | ||
285 | |||
286 | #endif | ||
287 | ; | ||
288 | #endif | ||
289 | |||
290 | |||
253 | #if defined BB_TRUE_FALSE | 291 | #if defined BB_TRUE_FALSE |
254 | const char false_usage[] = | 292 | const char false_usage[] = |
255 | "false\n" | 293 | "false\n" |
@@ -2,7 +2,7 @@ | |||
2 | /* | 2 | /* |
3 | * Mini wc implementation for busybox | 3 | * Mini wc implementation for busybox |
4 | * | 4 | * |
5 | * by Edward Betts <edward@debian.org> | 5 | * Copyright (C) 2000 Edward Betts <edward@debian.org> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by |