Added expr, from Edward Betts <edward@debian.org>, with some fixups

and docs added by me.
 -Erik
This commit is contained in:
Eric Andersen 2000-09-05 17:37:48 +00:00
parent 43c8c38bbf
commit 1b355ebba6
16 changed files with 1247 additions and 6 deletions

View File

@ -12,6 +12,9 @@ Erik Andersen <andersen@lineo.com>, <andersee@debian.org>
Tons of new stuff, major rewrite of most of the Tons of new stuff, major rewrite of most of the
core apps, tons of new apps as noted in header files. core apps, tons of new apps as noted in header files.
Edward Betts <edward@debian.org>
expr, hostid, logname, tty, wc, whoami, yes
John Beppu <beppu@lineo.com> John Beppu <beppu@lineo.com>
du, head, nslookup, sort, tee, uniq du, head, nslookup, sort, tee, uniq

1
TODO
View File

@ -22,7 +22,6 @@ around to it some time. If you have any good ideas, please let me know.
* rdate * rdate
* hwclock * hwclock
* stty * stty
* expr
* wget (or whatever I call it) * wget (or whatever I call it)
* tftp * tftp
* ftp * ftp

View File

@ -76,6 +76,9 @@ const struct BB_applet applets[] = {
#ifdef BB_ECHO #ifdef BB_ECHO
{"echo", echo_main, _BB_DIR_BIN, echo_usage}, {"echo", echo_main, _BB_DIR_BIN, echo_usage},
#endif #endif
#ifdef BB_EXPR
{"expr", expr_main, _BB_DIR_USR_BIN, expr_usage},
#endif
#ifdef BB_TRUE_FALSE #ifdef BB_TRUE_FALSE
{"false", false_main, _BB_DIR_BIN, false_usage}, {"false", false_main, _BB_DIR_BIN, false_usage},
#endif #endif

View File

@ -5,6 +5,6 @@
ls -1 ` \ ls -1 ` \
gcc -E -dM busybox.def.h | \ gcc -E -dM busybox.def.h | \
sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \
| tr [:upper:] [:lower:] | sort | tr '[:upper:]' '[:lower:]' | sort
` 2>/dev/null | sed -e 's/\.c$/\.o/g' ` 2>/dev/null | sed -e 's/\.c$/\.o/g'

View File

@ -250,6 +250,44 @@ const char echo_usage[] =
; ;
#endif #endif
#if defined BB_EXPR
const char expr_usage[] =
"expr EXPRESSION\n"
#ifndef BB_FEATURE_TRIVIAL_HELP
"\nPrints the value of EXPRESSION to standard output.\n\n"
"EXPRESSION may be:\n"
"ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n"
"ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n"
"ARG1 < ARG2 ARG1 is less than ARG2\n"
"ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n"
"ARG1 = ARG2 ARG1 is equal to ARG2\n"
"ARG1 != ARG2 ARG1 is unequal to ARG2\n"
"ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n"
"ARG1 > ARG2 ARG1 is greater than ARG2\n"
"ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n"
"ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n"
"ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n"
"ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n"
"ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n"
"STRING : REGEXP anchored pattern match of REGEXP in STRING\n"
"match STRING REGEXP same as STRING : REGEXP\n"
"substr STRING POS LENGTH substring of STRING, POS counted from 1\n"
"index STRING CHARS index in STRING where any CHARS is found, or 0\n"
"length STRING length of STRING\n"
"quote TOKEN interpret TOKEN as a string, even if it is a \n"
" keyword like `match' or an operator like `/'\n"
"( EXPRESSION ) value of EXPRESSION\n\n"
"Beware that many operators need to be escaped or quoted for shells.\n"
"Comparisons are arithmetic if both ARGs are numbers, else\n"
"lexicographical. Pattern matches return the string matched between \n"
"\\( and \\) or null; if \\( and \\) are not used, they return the number \n"
"of characters matched or 0.\n"
#endif
;
#endif
#if defined BB_TRUE_FALSE #if defined BB_TRUE_FALSE
const char false_usage[] = const char false_usage[] =
"false\n" "false\n"

View File

@ -76,6 +76,9 @@ const struct BB_applet applets[] = {
#ifdef BB_ECHO #ifdef BB_ECHO
{"echo", echo_main, _BB_DIR_BIN, echo_usage}, {"echo", echo_main, _BB_DIR_BIN, echo_usage},
#endif #endif
#ifdef BB_EXPR
{"expr", expr_main, _BB_DIR_USR_BIN, expr_usage},
#endif
#ifdef BB_TRUE_FALSE #ifdef BB_TRUE_FALSE
{"false", false_main, _BB_DIR_BIN, false_usage}, {"false", false_main, _BB_DIR_BIN, false_usage},
#endif #endif

View File

@ -27,6 +27,7 @@
#define BB_DU #define BB_DU
#define BB_DUMPKMAP #define BB_DUMPKMAP
#define BB_ECHO #define BB_ECHO
#define BB_EXPR
#define BB_FBSET #define BB_FBSET
#define BB_FDFLUSH #define BB_FDFLUSH
#define BB_FIND #define BB_FIND

View File

@ -5,6 +5,6 @@
ls -1 ` \ ls -1 ` \
gcc -E -dM busybox.def.h | \ gcc -E -dM busybox.def.h | \
sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \ sed -n -e '/^.*BB_FEATURE.*$/d;s/^#define.*\<BB_\(.*\)\>/\1.c/gp;' \
| tr [:upper:] [:lower:] | sort | tr '[:upper:]' '[:lower:]' | sort
` 2>/dev/null | sed -e 's/\.c$/\.o/g' ` 2>/dev/null | sed -e 's/\.c$/\.o/g'

531
coreutils/expr.c Normal file
View File

@ -0,0 +1,531 @@
/* vi: set sw=4 ts=4: */
/*
* Mini expr implementation for busybox
*
* based on GNU expr Mike Parker.
* Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
*
* Busybox modifications
* Copyright (c) 2000 Edward Betts <edward@debian.org>.
*
* this program is free software; you can redistribute it and/or modify
* it under the terms of the gnu general public license as published by
* the free software foundation; either version 2 of the license, or
* (at your option) any later version.
*
* this program is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. see the gnu
* general public license for more details.
*
* you should have received a copy of the gnu general public license
* along with this program; if not, write to the free software
* foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa
*
*/
/* This program evaluates expressions. Each token (operator, operand,
* parenthesis) of the expression must be a seperate argument. The
* parser used is a reasonably general one, though any incarnation of
* it is language-specific. It is especially nice for expressions.
*
* No parse tree is needed; a new node is evaluated immediately.
* One function can handle multiple operators all of equal precedence,
* provided they all associate ((x op x) op x). */
#include "internal.h"
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
/* The kinds of value we can have. */
enum valtype {
integer,
string
};
typedef enum valtype TYPE;
/* A value is.... */
struct valinfo {
TYPE type; /* Which kind. */
union { /* The value itself. */
int i;
char *s;
} u;
};
typedef struct valinfo VALUE;
/* The arguments given to the program, minus the program name. */
static char **args;
static VALUE *docolon (VALUE *sv, VALUE *pv);
static VALUE *eval (void);
static VALUE *int_value (int i);
static VALUE *str_value (char *s);
static int nextarg (char *str);
static int null (VALUE *v);
static int toarith (VALUE *v);
static void freev (VALUE *v);
static void tostring (VALUE *v);
int expr_main (int argc, char **argv)
{
VALUE *v;
if (argc == 1) {
fatalError("too few arguments\n");
}
args = argv + 1;
v = eval ();
if (*args)
fatalError ("syntax error\n");
if (v->type == integer)
printf ("%d\n", v->u.i);
else
printf ("%s\n", v->u.s);
exit (null (v));
}
/* Return a VALUE for I. */
static VALUE *int_value (int i)
{
VALUE *v;
v = xmalloc (sizeof(VALUE));
v->type = integer;
v->u.i = i;
return v;
}
/* Return a VALUE for S. */
static VALUE *str_value (char *s)
{
VALUE *v;
v = xmalloc (sizeof(VALUE));
v->type = string;
v->u.s = strdup (s);
return v;
}
/* Free VALUE V, including structure components. */
static void freev (VALUE *v)
{
if (v->type == string)
free (v->u.s);
free (v);
}
/* Return nonzero if V is a null-string or zero-number. */
static int null (VALUE *v)
{
switch (v->type) {
case integer:
return v->u.i == 0;
case string:
return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
default:
abort ();
}
}
/* Coerce V to a string value (can't fail). */
static void tostring (VALUE *v)
{
char *temp;
if (v->type == integer) {
temp = xmalloc (4 * (sizeof (int) / sizeof (char)));
sprintf (temp, "%d", v->u.i);
v->u.s = temp;
v->type = string;
}
}
/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
static int toarith (VALUE *v)
{
int i;
switch (v->type) {
case integer:
return 1;
case string:
i = 0;
/* Don't interpret the empty string as an integer. */
if (v->u.s == 0)
return 0;
i = atoi(v->u.s);
free (v->u.s);
v->u.i = i;
v->type = integer;
return 1;
default:
abort ();
}
}
/* Return nonzero if the next token matches STR exactly.
STR must not be NULL. */
static int
nextarg (char *str)
{
if (*args == NULL)
return 0;
return strcmp (*args, str) == 0;
}
/* The comparison operator handling functions. */
#define cmpf(name, rel) \
static int name (l, r) VALUE *l; VALUE *r; \
{ \
if (l->type == string || r->type == string) { \
tostring (l); \
tostring (r); \
return strcmp (l->u.s, r->u.s) rel 0; \
} \
else \
return l->u.i rel r->u.i; \
}
cmpf (less_than, <)
cmpf (less_equal, <=)
cmpf (equal, ==)
cmpf (not_equal, !=)
cmpf (greater_equal, >=)
cmpf (greater_than, >)
#undef cmpf
/* The arithmetic operator handling functions. */
#define arithf(name, op) \
static \
int name (l, r) VALUE *l; VALUE *r; \
{ \
if (!toarith (l) || !toarith (r)) \
fatalError ("non-numeric argument\n"); \
return l->u.i op r->u.i; \
}
#define arithdivf(name, op) \
int name (l, r) VALUE *l; VALUE *r; \
{ \
if (!toarith (l) || !toarith (r)) \
fatalError ( "non-numeric argument\n"); \
if (r->u.i == 0) \
fatalError ( "division by zero\n"); \
return l->u.i op r->u.i; \
}
arithf (plus, +)
arithf (minus, -)
arithf (multiply, *)
arithdivf (divide, /)
arithdivf (mod, %)
#undef arithf
#undef arithdivf
/* Do the : operator.
SV is the VALUE for the lhs (the string),
PV is the VALUE for the rhs (the pattern). */
static VALUE *docolon (VALUE *sv, VALUE *pv)
{
VALUE *v;
const char *errmsg;
struct re_pattern_buffer re_buffer;
struct re_registers re_regs;
int len;
tostring (sv);
tostring (pv);
if (pv->u.s[0] == '^') {
fprintf (stderr, "\
warning: unportable BRE: `%s': using `^' as the first character\n\
of a basic regular expression is not portable; it is being ignored",
pv->u.s);
}
len = strlen (pv->u.s);
memset (&re_buffer, 0, sizeof (re_buffer));
memset (&re_regs, 0, sizeof (re_regs));
re_buffer.allocated = 2 * len;
re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
re_buffer.translate = 0;
re_syntax_options = RE_SYNTAX_POSIX_BASIC;
errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
if (errmsg) {
fatalError("%s\n", errmsg);
}
len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
if (len >= 0) {
/* Were \(...\) used? */
if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
sv->u.s[re_regs.end[1]] = '\0';
v = str_value (sv->u.s + re_regs.start[1]);
}
else
v = int_value (len);
}
else {
/* Match failed -- return the right kind of null. */
if (re_buffer.re_nsub > 0)
v = str_value ("");
else
v = int_value (0);
}
free (re_buffer.buffer);
return v;
}
/* Handle bare operands and ( expr ) syntax. */
static VALUE *eval7 (void)
{
VALUE *v;
if (!*args)
fatalError ( "syntax error\n");
if (nextarg ("(")) {
args++;
v = eval ();
if (!nextarg (")"))
fatalError ( "syntax error\n");
args++;
return v;
}
if (nextarg (")"))
fatalError ( "syntax error\n");
return str_value (*args++);
}
/* Handle match, substr, index, length, and quote keywords. */
static VALUE *eval6 (void)
{
VALUE *l, *r, *v, *i1, *i2;
if (nextarg ("quote")) {
args++;
if (!*args)
fatalError ( "syntax error\n");
return str_value (*args++);
}
else if (nextarg ("length")) {
args++;
r = eval6 ();
tostring (r);
v = int_value (strlen (r->u.s));
freev (r);
return v;
}
else if (nextarg ("match")) {
args++;
l = eval6 ();
r = eval6 ();
v = docolon (l, r);
freev (l);
freev (r);
return v;
}
else if (nextarg ("index")) {
args++;
l = eval6 ();
r = eval6 ();
tostring (l);
tostring (r);
v = int_value (strcspn (l->u.s, r->u.s) + 1);
if (v->u.i == (int) strlen (l->u.s) + 1)
v->u.i = 0;
freev (l);
freev (r);
return v;
}
else if (nextarg ("substr")) {
args++;
l = eval6 ();
i1 = eval6 ();
i2 = eval6 ();
tostring (l);
if (!toarith (i1) || !toarith (i2)
|| i1->u.i > (int) strlen (l->u.s)
|| i1->u.i <= 0 || i2->u.i <= 0)
v = str_value ("");
else {
v = xmalloc (sizeof(VALUE));
v->type = string;
v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
l->u.s + i1->u.i - 1, i2->u.i);
v->u.s[i2->u.i] = 0;
}
freev (l);
freev (i1);
freev (i2);
return v;
}
else
return eval7 ();
}
/* Handle : operator (pattern matching).
Calls docolon to do the real work. */
static VALUE *eval5 (void)
{
VALUE *l, *r, *v;
l = eval6 ();
while (nextarg (":")) {
args++;
r = eval6 ();
v = docolon (l, r);
freev (l);
freev (r);
l = v;
}
return l;
}
/* Handle *, /, % operators. */
static VALUE *eval4 (void)
{
VALUE *l, *r;
int (*fxn) (), val;
l = eval5 ();
while (1) {
if (nextarg ("*"))
fxn = multiply;
else if (nextarg ("/"))
fxn = divide;
else if (nextarg ("%"))
fxn = mod;
else
return l;
args++;
r = eval5 ();
val = (*fxn) (l, r);
freev (l);
freev (r);
l = int_value (val);
}
}
/* Handle +, - operators. */
static VALUE *eval3 (void)
{
VALUE *l, *r;
int (*fxn) (), val;
l = eval4 ();
while (1) {
if (nextarg ("+"))
fxn = plus;
else if (nextarg ("-"))
fxn = minus;
else
return l;
args++;
r = eval4 ();
val = (*fxn) (l, r);
freev (l);
freev (r);
l = int_value (val);
}
}
/* Handle comparisons. */
static VALUE *eval2 (void)
{
VALUE *l, *r;
int (*fxn) (), val;
l = eval3 ();
while (1) {
if (nextarg ("<"))
fxn = less_than;
else if (nextarg ("<="))
fxn = less_equal;
else if (nextarg ("=") || nextarg ("=="))
fxn = equal;
else if (nextarg ("!="))
fxn = not_equal;
else if (nextarg (">="))
fxn = greater_equal;
else if (nextarg (">"))
fxn = greater_than;
else
return l;
args++;
r = eval3 ();
toarith (l);
toarith (r);
val = (*fxn) (l, r);
freev (l);
freev (r);
l = int_value (val);
}
}
/* Handle &. */
static VALUE *eval1 (void)
{
VALUE *l, *r;
l = eval2 ();
while (nextarg ("&")) {
args++;
r = eval2 ();
if (null (l) || null (r)) {
freev (l);
freev (r);
l = int_value (0);
}
else
freev (r);
}
return l;
}
/* Handle |. */
static VALUE *eval (void)
{
VALUE *l, *r;
l = eval1 ();
while (nextarg ("|")) {
args++;
r = eval1 ();
if (null (l)) {
freev (l);
l = r;
}
else
freev (r);
}
return l;
}

View File

@ -2,7 +2,7 @@
/* /*
* Mini wc implementation for busybox * Mini wc implementation for busybox
* *
* by Edward Betts <edward@debian.org> * Copyright (C) 2000 Edward Betts <edward@debian.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by

View File

@ -497,6 +497,45 @@ Example:
------------------------------- -------------------------------
=item echo
Usage: expr EXPRESSION
Prints the value of EXPRESSION to standard output.
EXPRESSION may be:
ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2
ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0
ARG1 < ARG2 ARG1 is less than ARG2
ARG1 <= ARG2 ARG1 is less than or equal to ARG2
ARG1 = ARG2 ARG1 is equal to ARG2
ARG1 != ARG2 ARG1 is unequal to ARG2
ARG1 >= ARG2 ARG1 is greater than or equal to ARG2
ARG1 > ARG2 ARG1 is greater than ARG2
ARG1 + ARG2 arithmetic sum of ARG1 and ARG2
ARG1 - ARG2 arithmetic difference of ARG1 and ARG2
ARG1 * ARG2 arithmetic product of ARG1 and ARG2
ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2
ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2
STRING : REGEXP anchored pattern match of REGEXP in STRING
match STRING REGEXP same as STRING : REGEXP
substr STRING POS LENGTH substring of STRING, POS counted from 1
index STRING CHARS index in STRING where any CHARS is found, or 0
length STRING length of STRING
quote TOKEN interpret TOKEN as a string, even if it is a
keyword like `match' or an operator like `/'
( EXPRESSION ) value of EXPRESSION
Beware that many operators need to be escaped or quoted for shells.
Comparisons are arithmetic if both ARGs are numbers, else
lexicographical. Pattern matches return the string matched between
\( and \) or null; if \( and \) are not used, they return the number
of characters matched or 0.
-------------------------------
=item false =item false
Returns an exit code of FALSE (1) Returns an exit code of FALSE (1)
@ -2112,4 +2151,4 @@ Enrique Zanardi <ezanardi@ull.es>
=cut =cut
# $Id: busybox.pod,v 1.65 2000/09/01 16:12:57 andersen Exp $ # $Id: busybox.pod,v 1.66 2000/09/05 17:37:48 andersen Exp $

View File

@ -879,6 +879,58 @@
</para> </para>
</sect1> </sect1>
<sect1 id="expr">
<title>expr</title>
<para>
Usage: expr EXPRESSION
</para>
<para>
Prints the value of EXPRESSION to standard output.
</para>
<para>
EXPRESSION may be:
</para>
<para>
<screen>
ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2
ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0
ARG1 &lt ARG2 ARG1 is less than ARG2
ARG1 &lt= ARG2 ARG1 is less than or equal to ARG2
ARG1 = ARG2 ARG1 is equal to ARG2
ARG1 != ARG2 ARG1 is unequal to ARG2
ARG1 &gt= ARG2 ARG1 is greater than or equal to ARG2
ARG1 &gt ARG2 ARG1 is greater than ARG2
ARG1 + ARG2 arithmetic sum of ARG1 and ARG2
ARG1 - ARG2 arithmetic difference of ARG1 and ARG2
ARG1 * ARG2 arithmetic product of ARG1 and ARG2
ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2
ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2
STRING : REGEXP anchored pattern match of REGEXP in STRING
match STRING REGEXP same as STRING : REGEXP
substr STRING POS LENGTH substring of STRING, POS counted from 1
index STRING CHARS index in STRING where any CHARS is found, or 0
length STRING length of STRING
quote TOKEN interpret TOKEN as a string, even if it is a
keyword like `match' or an operator like `/'
( EXPRESSION ) value of EXPRESSION
</screen>
</para>
<para>
Beware that many operators need to be escaped or quoted for shells.
Comparisons are arithmetic if both ARGs are numbers, else
lexicographical. Pattern matches return the string matched between
\( and \) or null; if \( and \) are not used, they return the number
of characters matched or 0.
</para>
</sect1>
<sect1 id="false"> <sect1 id="false">
<title>false</title> <title>false</title>

531
expr.c Normal file
View File

@ -0,0 +1,531 @@
/* vi: set sw=4 ts=4: */
/*
* Mini expr implementation for busybox
*
* based on GNU expr Mike Parker.
* Copyright (C) 86, 1991-1997, 1999 Free Software Foundation, Inc.
*
* Busybox modifications
* Copyright (c) 2000 Edward Betts <edward@debian.org>.
*
* this program is free software; you can redistribute it and/or modify
* it under the terms of the gnu general public license as published by
* the free software foundation; either version 2 of the license, or
* (at your option) any later version.
*
* this program is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* merchantability or fitness for a particular purpose. see the gnu
* general public license for more details.
*
* you should have received a copy of the gnu general public license
* along with this program; if not, write to the free software
* foundation, inc., 59 temple place, suite 330, boston, ma 02111-1307 usa
*
*/
/* This program evaluates expressions. Each token (operator, operand,
* parenthesis) of the expression must be a seperate argument. The
* parser used is a reasonably general one, though any incarnation of
* it is language-specific. It is especially nice for expressions.
*
* No parse tree is needed; a new node is evaluated immediately.
* One function can handle multiple operators all of equal precedence,
* provided they all associate ((x op x) op x). */
#include "internal.h"
#include <stdio.h>
#include <sys/types.h>
#include <regex.h>
/* The kinds of value we can have. */
enum valtype {
integer,
string
};
typedef enum valtype TYPE;
/* A value is.... */
struct valinfo {
TYPE type; /* Which kind. */
union { /* The value itself. */
int i;
char *s;
} u;
};
typedef struct valinfo VALUE;
/* The arguments given to the program, minus the program name. */
static char **args;
static VALUE *docolon (VALUE *sv, VALUE *pv);
static VALUE *eval (void);
static VALUE *int_value (int i);
static VALUE *str_value (char *s);
static int nextarg (char *str);
static int null (VALUE *v);
static int toarith (VALUE *v);
static void freev (VALUE *v);
static void tostring (VALUE *v);
int expr_main (int argc, char **argv)
{
VALUE *v;
if (argc == 1) {
fatalError("too few arguments\n");
}
args = argv + 1;
v = eval ();
if (*args)
fatalError ("syntax error\n");
if (v->type == integer)
printf ("%d\n", v->u.i);
else
printf ("%s\n", v->u.s);
exit (null (v));
}
/* Return a VALUE for I. */
static VALUE *int_value (int i)
{
VALUE *v;
v = xmalloc (sizeof(VALUE));
v->type = integer;
v->u.i = i;
return v;
}
/* Return a VALUE for S. */
static VALUE *str_value (char *s)
{
VALUE *v;
v = xmalloc (sizeof(VALUE));
v->type = string;
v->u.s = strdup (s);
return v;
}
/* Free VALUE V, including structure components. */
static void freev (VALUE *v)
{
if (v->type == string)
free (v->u.s);
free (v);
}
/* Return nonzero if V is a null-string or zero-number. */
static int null (VALUE *v)
{
switch (v->type) {
case integer:
return v->u.i == 0;
case string:
return v->u.s[0] == '\0' || strcmp (v->u.s, "0") == 0;
default:
abort ();
}
}
/* Coerce V to a string value (can't fail). */
static void tostring (VALUE *v)
{
char *temp;
if (v->type == integer) {
temp = xmalloc (4 * (sizeof (int) / sizeof (char)));
sprintf (temp, "%d", v->u.i);
v->u.s = temp;
v->type = string;
}
}
/* Coerce V to an integer value. Return 1 on success, 0 on failure. */
static int toarith (VALUE *v)
{
int i;
switch (v->type) {
case integer:
return 1;
case string:
i = 0;
/* Don't interpret the empty string as an integer. */
if (v->u.s == 0)
return 0;
i = atoi(v->u.s);
free (v->u.s);
v->u.i = i;
v->type = integer;
return 1;
default:
abort ();
}
}
/* Return nonzero if the next token matches STR exactly.
STR must not be NULL. */
static int
nextarg (char *str)
{
if (*args == NULL)
return 0;
return strcmp (*args, str) == 0;
}
/* The comparison operator handling functions. */
#define cmpf(name, rel) \
static int name (l, r) VALUE *l; VALUE *r; \
{ \
if (l->type == string || r->type == string) { \
tostring (l); \
tostring (r); \
return strcmp (l->u.s, r->u.s) rel 0; \
} \
else \
return l->u.i rel r->u.i; \
}
cmpf (less_than, <)
cmpf (less_equal, <=)
cmpf (equal, ==)
cmpf (not_equal, !=)
cmpf (greater_equal, >=)
cmpf (greater_than, >)
#undef cmpf
/* The arithmetic operator handling functions. */
#define arithf(name, op) \
static \
int name (l, r) VALUE *l; VALUE *r; \
{ \
if (!toarith (l) || !toarith (r)) \
fatalError ("non-numeric argument\n"); \
return l->u.i op r->u.i; \
}
#define arithdivf(name, op) \
int name (l, r) VALUE *l; VALUE *r; \
{ \
if (!toarith (l) || !toarith (r)) \
fatalError ( "non-numeric argument\n"); \
if (r->u.i == 0) \
fatalError ( "division by zero\n"); \
return l->u.i op r->u.i; \
}
arithf (plus, +)
arithf (minus, -)
arithf (multiply, *)
arithdivf (divide, /)
arithdivf (mod, %)
#undef arithf
#undef arithdivf
/* Do the : operator.
SV is the VALUE for the lhs (the string),
PV is the VALUE for the rhs (the pattern). */
static VALUE *docolon (VALUE *sv, VALUE *pv)
{
VALUE *v;
const char *errmsg;
struct re_pattern_buffer re_buffer;
struct re_registers re_regs;
int len;
tostring (sv);
tostring (pv);
if (pv->u.s[0] == '^') {
fprintf (stderr, "\
warning: unportable BRE: `%s': using `^' as the first character\n\
of a basic regular expression is not portable; it is being ignored",
pv->u.s);
}
len = strlen (pv->u.s);
memset (&re_buffer, 0, sizeof (re_buffer));
memset (&re_regs, 0, sizeof (re_regs));
re_buffer.allocated = 2 * len;
re_buffer.buffer = (unsigned char *) xmalloc (re_buffer.allocated);
re_buffer.translate = 0;
re_syntax_options = RE_SYNTAX_POSIX_BASIC;
errmsg = re_compile_pattern (pv->u.s, len, &re_buffer);
if (errmsg) {
fatalError("%s\n", errmsg);
}
len = re_match (&re_buffer, sv->u.s, strlen (sv->u.s), 0, &re_regs);
if (len >= 0) {
/* Were \(...\) used? */
if (re_buffer.re_nsub > 0) { /* was (re_regs.start[1] >= 0) */
sv->u.s[re_regs.end[1]] = '\0';
v = str_value (sv->u.s + re_regs.start[1]);
}
else
v = int_value (len);
}
else {
/* Match failed -- return the right kind of null. */
if (re_buffer.re_nsub > 0)
v = str_value ("");
else
v = int_value (0);
}
free (re_buffer.buffer);
return v;
}
/* Handle bare operands and ( expr ) syntax. */
static VALUE *eval7 (void)
{
VALUE *v;
if (!*args)
fatalError ( "syntax error\n");
if (nextarg ("(")) {
args++;
v = eval ();
if (!nextarg (")"))
fatalError ( "syntax error\n");
args++;
return v;
}
if (nextarg (")"))
fatalError ( "syntax error\n");
return str_value (*args++);
}
/* Handle match, substr, index, length, and quote keywords. */
static VALUE *eval6 (void)
{
VALUE *l, *r, *v, *i1, *i2;
if (nextarg ("quote")) {
args++;
if (!*args)
fatalError ( "syntax error\n");
return str_value (*args++);
}
else if (nextarg ("length")) {
args++;
r = eval6 ();
tostring (r);
v = int_value (strlen (r->u.s));
freev (r);
return v;
}
else if (nextarg ("match")) {
args++;
l = eval6 ();
r = eval6 ();
v = docolon (l, r);
freev (l);
freev (r);
return v;
}
else if (nextarg ("index")) {
args++;
l = eval6 ();
r = eval6 ();
tostring (l);
tostring (r);
v = int_value (strcspn (l->u.s, r->u.s) + 1);
if (v->u.i == (int) strlen (l->u.s) + 1)
v->u.i = 0;
freev (l);
freev (r);
return v;
}
else if (nextarg ("substr")) {
args++;
l = eval6 ();
i1 = eval6 ();
i2 = eval6 ();
tostring (l);
if (!toarith (i1) || !toarith (i2)
|| i1->u.i > (int) strlen (l->u.s)
|| i1->u.i <= 0 || i2->u.i <= 0)
v = str_value ("");
else {
v = xmalloc (sizeof(VALUE));
v->type = string;
v->u.s = strncpy ((char *) xmalloc (i2->u.i + 1),
l->u.s + i1->u.i - 1, i2->u.i);
v->u.s[i2->u.i] = 0;
}
freev (l);
freev (i1);
freev (i2);
return v;
}
else
return eval7 ();
}
/* Handle : operator (pattern matching).
Calls docolon to do the real work. */
static VALUE *eval5 (void)
{
VALUE *l, *r, *v;
l = eval6 ();
while (nextarg (":")) {
args++;
r = eval6 ();
v = docolon (l, r);
freev (l);
freev (r);
l = v;
}
return l;
}
/* Handle *, /, % operators. */
static VALUE *eval4 (void)
{
VALUE *l, *r;
int (*fxn) (), val;
l = eval5 ();
while (1) {
if (nextarg ("*"))
fxn = multiply;
else if (nextarg ("/"))
fxn = divide;
else if (nextarg ("%"))
fxn = mod;
else
return l;
args++;
r = eval5 ();
val = (*fxn) (l, r);
freev (l);
freev (r);
l = int_value (val);
}
}
/* Handle +, - operators. */
static VALUE *eval3 (void)
{
VALUE *l, *r;
int (*fxn) (), val;
l = eval4 ();
while (1) {
if (nextarg ("+"))
fxn = plus;
else if (nextarg ("-"))
fxn = minus;
else
return l;
args++;
r = eval4 ();
val = (*fxn) (l, r);
freev (l);
freev (r);
l = int_value (val);
}
}
/* Handle comparisons. */
static VALUE *eval2 (void)
{
VALUE *l, *r;
int (*fxn) (), val;
l = eval3 ();
while (1) {
if (nextarg ("<"))
fxn = less_than;
else if (nextarg ("<="))
fxn = less_equal;
else if (nextarg ("=") || nextarg ("=="))
fxn = equal;
else if (nextarg ("!="))
fxn = not_equal;
else if (nextarg (">="))
fxn = greater_equal;
else if (nextarg (">"))
fxn = greater_than;
else
return l;
args++;
r = eval3 ();
toarith (l);
toarith (r);
val = (*fxn) (l, r);
freev (l);
freev (r);
l = int_value (val);
}
}
/* Handle &. */
static VALUE *eval1 (void)
{
VALUE *l, *r;
l = eval2 ();
while (nextarg ("&")) {
args++;
r = eval2 ();
if (null (l) || null (r)) {
freev (l);
freev (r);
l = int_value (0);
}
else
freev (r);
}
return l;
}
/* Handle |. */
static VALUE *eval (void)
{
VALUE *l, *r;
l = eval1 ();
while (nextarg ("|")) {
args++;
r = eval1 ();
if (null (l)) {
freev (l);
l = r;
}
else
freev (r);
}
return l;
}

View File

@ -110,6 +110,7 @@ extern const struct BB_applet applets[];
extern int ar_main(int argc, char **argv); extern int ar_main(int argc, char **argv);
extern int basename_main(int argc, char **argv); extern int basename_main(int argc, char **argv);
extern int bogomips_main(int argc, char **argv);
extern int busybox_main(int argc, char** argv); extern int busybox_main(int argc, char** argv);
extern int cat_main(int argc, char** argv); extern int cat_main(int argc, char** argv);
extern int chmod_chown_chgrp_main(int argc, char** argv); extern int chmod_chown_chgrp_main(int argc, char** argv);
@ -129,6 +130,7 @@ extern int du_main(int argc, char** argv);
extern int dumpkmap_main(int argc, char** argv); extern int dumpkmap_main(int argc, char** argv);
extern int dutmp_main(int argc, char** argv); extern int dutmp_main(int argc, char** argv);
extern int echo_main(int argc, char** argv); extern int echo_main(int argc, char** argv);
extern int expr_main(int argc, char** argv);
extern int false_main(int argc, char** argv); extern int false_main(int argc, char** argv);
extern int fbset_main(int argc, char** argv); extern int fbset_main(int argc, char** argv);
extern int fdisk_main(int argc, char** argv); extern int fdisk_main(int argc, char** argv);
@ -240,6 +242,7 @@ extern const char du_usage[];
extern const char dumpkmap_usage[]; extern const char dumpkmap_usage[];
extern const char dutmp_usage[]; extern const char dutmp_usage[];
extern const char echo_usage[]; extern const char echo_usage[];
extern const char expr_usage[];
extern const char false_usage[]; extern const char false_usage[];
extern const char fdflush_usage[]; extern const char fdflush_usage[];
extern const char find_usage[]; extern const char find_usage[];

38
usage.c
View File

@ -250,6 +250,44 @@ const char echo_usage[] =
; ;
#endif #endif
#if defined BB_EXPR
const char expr_usage[] =
"expr EXPRESSION\n"
#ifndef BB_FEATURE_TRIVIAL_HELP
"\nPrints the value of EXPRESSION to standard output.\n\n"
"EXPRESSION may be:\n"
"ARG1 | ARG2 ARG1 if it is neither null nor 0, otherwise ARG2\n"
"ARG1 & ARG2 ARG1 if neither argument is null or 0, otherwise 0\n"
"ARG1 < ARG2 ARG1 is less than ARG2\n"
"ARG1 <= ARG2 ARG1 is less than or equal to ARG2\n"
"ARG1 = ARG2 ARG1 is equal to ARG2\n"
"ARG1 != ARG2 ARG1 is unequal to ARG2\n"
"ARG1 >= ARG2 ARG1 is greater than or equal to ARG2\n"
"ARG1 > ARG2 ARG1 is greater than ARG2\n"
"ARG1 + ARG2 arithmetic sum of ARG1 and ARG2\n"
"ARG1 - ARG2 arithmetic difference of ARG1 and ARG2\n"
"ARG1 * ARG2 arithmetic product of ARG1 and ARG2\n"
"ARG1 / ARG2 arithmetic quotient of ARG1 divided by ARG2\n"
"ARG1 % ARG2 arithmetic remainder of ARG1 divided by ARG2\n"
"STRING : REGEXP anchored pattern match of REGEXP in STRING\n"
"match STRING REGEXP same as STRING : REGEXP\n"
"substr STRING POS LENGTH substring of STRING, POS counted from 1\n"
"index STRING CHARS index in STRING where any CHARS is found, or 0\n"
"length STRING length of STRING\n"
"quote TOKEN interpret TOKEN as a string, even if it is a \n"
" keyword like `match' or an operator like `/'\n"
"( EXPRESSION ) value of EXPRESSION\n\n"
"Beware that many operators need to be escaped or quoted for shells.\n"
"Comparisons are arithmetic if both ARGs are numbers, else\n"
"lexicographical. Pattern matches return the string matched between \n"
"\\( and \\) or null; if \\( and \\) are not used, they return the number \n"
"of characters matched or 0.\n"
#endif
;
#endif
#if defined BB_TRUE_FALSE #if defined BB_TRUE_FALSE
const char false_usage[] = const char false_usage[] =
"false\n" "false\n"

2
wc.c
View File

@ -2,7 +2,7 @@
/* /*
* Mini wc implementation for busybox * Mini wc implementation for busybox
* *
* by Edward Betts <edward@debian.org> * Copyright (C) 2000 Edward Betts <edward@debian.org>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by