aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Andersen <andersen@codepoet.org>2000-09-05 17:37:48 +0000
committerEric Andersen <andersen@codepoet.org>2000-09-05 17:37:48 +0000
commit1b355ebba68bdd567dd3961a18291dfd9532c2e8 (patch)
treec4c1cb4ed9cc025e05640e19c0b0a3f86080563a
parent43c8c38bbfe3d25e21f37d899184de52c6dc9c17 (diff)
downloadbusybox-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--AUTHORS3
-rw-r--r--TODO1
-rw-r--r--applets/busybox.c3
-rwxr-xr-xapplets/busybox.sh2
-rw-r--r--applets/usage.c38
-rw-r--r--busybox.c3
-rw-r--r--busybox.def.h1
-rwxr-xr-xbusybox.sh2
-rw-r--r--coreutils/expr.c531
-rw-r--r--coreutils/wc.c2
-rw-r--r--docs/busybox.pod41
-rw-r--r--docs/busybox.sgml52
-rw-r--r--expr.c531
-rw-r--r--internal.h3
-rw-r--r--usage.c38
-rw-r--r--wc.c2
16 files changed, 1247 insertions, 6 deletions
diff --git a/AUTHORS b/AUTHORS
index 64114a5fc..918a0015b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -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
15Edward Betts <edward@debian.org>
16 expr, hostid, logname, tty, wc, whoami, yes
17
15John Beppu <beppu@lineo.com> 18John Beppu <beppu@lineo.com>
16 du, head, nslookup, sort, tee, uniq 19 du, head, nslookup, sort, tee, uniq
17 20
diff --git a/TODO b/TODO
index d34572311..b8a25019f 100644
--- a/TODO
+++ b/TODO
@@ -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 @@
5ls -1 ` \ 5ls -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
254const 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
254const char false_usage[] = 292const char false_usage[] =
255 "false\n" 293 "false\n"
diff --git a/busybox.c b/busybox.c
index 0ffa94e09..a7c5d37a0 100644
--- a/busybox.c
+++ b/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/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 @@
5ls -1 ` \ 5ls -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. */
43enum valtype {
44 integer,
45 string
46};
47typedef enum valtype TYPE;
48
49/* A value is.... */
50struct valinfo {
51 TYPE type; /* Which kind. */
52 union { /* The value itself. */
53 int i;
54 char *s;
55 } u;
56};
57typedef struct valinfo VALUE;
58
59/* The arguments given to the program, minus the program name. */
60static char **args;
61
62static VALUE *docolon (VALUE *sv, VALUE *pv);
63static VALUE *eval (void);
64static VALUE *int_value (int i);
65static VALUE *str_value (char *s);
66static int nextarg (char *str);
67static int null (VALUE *v);
68static int toarith (VALUE *v);
69static void freev (VALUE *v);
70static void tostring (VALUE *v);
71
72int 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
96static 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
108static 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
120static 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
129static 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
143static 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
157static 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
182static int
183nextarg (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) \
193static 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) \
215static \
216int 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) \
224int 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
246static 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, "\
259warning: unportable BRE: `%s': using `^' as the first character\n\
260of 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
299static 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
323static 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
392static 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
410static 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
436static 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
460static 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
494static 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
515static 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
503Usage: expr EXPRESSION
504
505Prints the value of EXPRESSION to standard output.
506
507EXPRESSION 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
531Beware that many operators need to be escaped or quoted for shells.
532Comparisons are arithmetic if both ARGs are numbers, else
533lexicographical. Pattern matches return the string matched between
534\( and \) or null; if \( and \) are not used, they return the number
535of characters matched or 0.
536
537-------------------------------
538
500=item false 539=item false
501 540
502Returns an exit code of FALSE (1) 541Returns 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 &lt ARG2 ARG1 is less than ARG2
902 ARG1 &lt= 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 &gt= ARG2 ARG1 is greater than or equal to ARG2
906 ARG1 &gt 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
diff --git a/expr.c b/expr.c
new file mode 100644
index 000000000..9e3c04a11
--- /dev/null
+++ b/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. */
43enum valtype {
44 integer,
45 string
46};
47typedef enum valtype TYPE;
48
49/* A value is.... */
50struct valinfo {
51 TYPE type; /* Which kind. */
52 union { /* The value itself. */
53 int i;
54 char *s;
55 } u;
56};
57typedef struct valinfo VALUE;
58
59/* The arguments given to the program, minus the program name. */
60static char **args;
61
62static VALUE *docolon (VALUE *sv, VALUE *pv);
63static VALUE *eval (void);
64static VALUE *int_value (int i);
65static VALUE *str_value (char *s);
66static int nextarg (char *str);
67static int null (VALUE *v);
68static int toarith (VALUE *v);
69static void freev (VALUE *v);
70static void tostring (VALUE *v);
71
72int 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
96static 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
108static 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
120static 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
129static 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
143static 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
157static 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
182static int
183nextarg (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) \
193static 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) \
215static \
216int 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) \
224int 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
246static 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, "\
259warning: unportable BRE: `%s': using `^' as the first character\n\
260of 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
299static 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
323static 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
392static 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
410static 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
436static 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
460static 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
494static 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
515static 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
111extern int ar_main(int argc, char **argv); 111extern int ar_main(int argc, char **argv);
112extern int basename_main(int argc, char **argv); 112extern int basename_main(int argc, char **argv);
113extern int bogomips_main(int argc, char **argv);
113extern int busybox_main(int argc, char** argv); 114extern int busybox_main(int argc, char** argv);
114extern int cat_main(int argc, char** argv); 115extern int cat_main(int argc, char** argv);
115extern int chmod_chown_chgrp_main(int argc, char** argv); 116extern int chmod_chown_chgrp_main(int argc, char** argv);
@@ -129,6 +130,7 @@ extern int du_main(int argc, char** argv);
129extern int dumpkmap_main(int argc, char** argv); 130extern int dumpkmap_main(int argc, char** argv);
130extern int dutmp_main(int argc, char** argv); 131extern int dutmp_main(int argc, char** argv);
131extern int echo_main(int argc, char** argv); 132extern int echo_main(int argc, char** argv);
133extern int expr_main(int argc, char** argv);
132extern int false_main(int argc, char** argv); 134extern int false_main(int argc, char** argv);
133extern int fbset_main(int argc, char** argv); 135extern int fbset_main(int argc, char** argv);
134extern int fdisk_main(int argc, char** argv); 136extern int fdisk_main(int argc, char** argv);
@@ -240,6 +242,7 @@ extern const char du_usage[];
240extern const char dumpkmap_usage[]; 242extern const char dumpkmap_usage[];
241extern const char dutmp_usage[]; 243extern const char dutmp_usage[];
242extern const char echo_usage[]; 244extern const char echo_usage[];
245extern const char expr_usage[];
243extern const char false_usage[]; 246extern const char false_usage[];
244extern const char fdflush_usage[]; 247extern const char fdflush_usage[];
245extern const char find_usage[]; 248extern const char find_usage[];
diff --git a/usage.c b/usage.c
index 91064f52c..05913f3e3 100644
--- a/usage.c
+++ b/usage.c
@@ -250,6 +250,44 @@ const char echo_usage[] =
250 ; 250 ;
251#endif 251#endif
252 252
253#if defined BB_EXPR
254const 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
254const char false_usage[] = 292const char false_usage[] =
255 "false\n" 293 "false\n"
diff --git a/wc.c b/wc.c
index 02e2b2aa6..ca5b3680a 100644
--- a/wc.c
+++ b/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