diff options
Diffstat (limited to 'bin/expr')
| -rw-r--r-- | bin/expr/Makefile | 14 | ||||
| -rw-r--r-- | bin/expr/expr.1 | 132 | ||||
| -rw-r--r-- | bin/expr/expr.y | 533 |
3 files changed, 679 insertions, 0 deletions
diff --git a/bin/expr/Makefile b/bin/expr/Makefile new file mode 100644 index 000000000000..c2db2b639aad --- /dev/null +++ b/bin/expr/Makefile @@ -0,0 +1,14 @@ +# /b/source/CVS/src/bin/expr/Makefile,v 1.5 1993/06/14 19:56:06 jtc Exp + +PROG= expr +SRCS= expr.c +CLEANFILES+= expr.c y.tab.h +LDADD+= -lgnuregex +DPADD+= /usr/lib/libgnuregex.a + +expr.c: + ${YACC} -d ${.IMPSRC} + mv y.tab.c expr.c + +.include <bsd.prog.mk> + diff --git a/bin/expr/expr.1 b/bin/expr/expr.1 new file mode 100644 index 000000000000..8514d9dd64cc --- /dev/null +++ b/bin/expr/expr.1 @@ -0,0 +1,132 @@ +.\" -*- nroff -*- +.\" +.\" Copyright (c) 1993 Winning Strategies, Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Winning Strategies, Inc. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software withough specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $Id: expr.1,v 1.2 1993/10/04 22:07:27 jtc Exp $ +.\" +.Dd July 3, 1993 +.Dt EXPR 1 +.Os +.Sh NAME +.Nm expr +.Nd evaluate expression +.Sh SYNOPSIS +.Nm expr +.Ar expression +.Sh DESCRIPTION +The +.Nm expr +utility evaluates +.Ar expression +and writes the result on standard output. +.Pp +All operators are separate arguments to the +.Nm expr +utility. +Characters special to the command interpreter must be escaped. +.Pp +Operators are listed below in order of increasing precidence. +Operators with equal precidence are grouped within { } symbols. +.Bl -tag -width indent +.It Ar expr1 Li | Ar expr2 +Returns the evaluation of +.Ar expr1 +if it is neither an empty string nor zero; +otherwise, returns the evaluation of +.Ar expr2 . +.It Ar expr1 Li & Ar expr2 +Returns the evaluation of +.Ar expr1 +if neither expression evaluates to an empty string or zero; +otherwise, returns zero. +.It Ar expr1 Li "{=, >, >=, <, <=, !=}" Ar expr2 +Returns the results of integer comparision if both arguments are integers; +otherwise, returns the results of string comparison using the locale-specific +collation sequence. +The result of each comparison is 1 if the specified relation is true, +or 0 if the relation is false. +.It Ar expr1 Li "{+, -}" Ar expr2 +Returns the results of addition or subtraction of integer-valued arguments. +.It Ar expr1 Li "{*, /, %}" Ar expr2 +Returns the results of multiplication, integer division, or remainder of integer-valued arguments. +.It Ar expr1 Li : Ar expr2 +The +.Dq \: +operator matches +.Ar expr1 +against +.Ar expr2 , +which must be a regular expression. The regular expression is anchored +to the begining of the string with an implicit +.Dq ^ . +.Pp +If the match succeeds and the pattern contains at least one regular +expression subexpression +.Dq "\e(...\e)" , +the string corresponding to +.Dq "\e1" +is returned; +otherwise the matching operator returns the number of characters matched. +If the match fails and the pattern contains a regular expression subexpression +the null string is returned; +otherwise 0. +.El +.Pp +Parentheses are used for grouping in the usual manner. +.Sh EXAMPLES +.Bl -enum +.It +The following example adds one to the variable a. +.Dl a=`expr $a + 1` +.It +The following example returns the filename portion of a pathname stored +in variable a. The // characters act to eliminate ambiguity with the +division operator. +.Dl expr "//$a" Li : '.*/\e(.*\e)' +.It +The following example returns the number of characters in variable a. +.Dl expr $a Li : '.*' +.El +.Sh DIAGNOSTICS +The +.Nm expr +utility exits with one of the following values: +.Bl -tag -width Ds -compact +.It 0 +the expression is neither an empty string nor 0. +.It 1 +the expression is an empty string or 0. +.It 2 +the expression is invalid. +.El +.Sh STANDARDS +The +.Nm expr +utility conforms to +.St -p1003.2 . diff --git a/bin/expr/expr.y b/bin/expr/expr.y new file mode 100644 index 000000000000..57ad5022e496 --- /dev/null +++ b/bin/expr/expr.y @@ -0,0 +1,533 @@ +%{ +/* Written by Pace Willisson (pace@blitz.com) + * and placed in the public domain. + * + * Largely rewritten by J.T. Conklin (jtc@wimsey.com) + * + * $Id : /b/source/CVS/src/bin/expr/expr.y,v 1.11 1993/08/17 16:01:23 jtc Exp $ + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#include <err.h> + +enum valtype { + integer, string +} ; + +struct val { + enum valtype type; + union { + char *s; + int i; + } u; +} ; + +struct val *result; +struct val *op_or (); +struct val *op_and (); +struct val *op_eq (); +struct val *op_gt (); +struct val *op_lt (); +struct val *op_ge (); +struct val *op_le (); +struct val *op_ne (); +struct val *op_plus (); +struct val *op_minus (); +struct val *op_times (); +struct val *op_div (); +struct val *op_rem (); +struct val *op_colon (); + +char **av; +%} + +%union +{ + struct val *val; +} + +%left <val> '|' +%left <val> '&' +%left <val> '=' '>' '<' GE LE NE +%left <val> '+' '-' +%left <val> '*' '/' '%' +%left <val> ':' +%left UNARY + +%token <val> TOKEN +%type <val> start expr + +%% + +start: expr { result = $$; } + +expr: TOKEN + | '(' expr ')' { $$ = $2; } + | expr '|' expr { $$ = op_or ($1, $3); } + | expr '&' expr { $$ = op_and ($1, $3); } + | expr '=' expr { $$ = op_eq ($1, $3); } + | expr '>' expr { $$ = op_gt ($1, $3); } + | expr '<' expr { $$ = op_lt ($1, $3); } + | expr GE expr { $$ = op_ge ($1, $3); } + | expr LE expr { $$ = op_le ($1, $3); } + | expr NE expr { $$ = op_ne ($1, $3); } + | expr '+' expr { $$ = op_plus ($1, $3); } + | expr '-' expr { $$ = op_minus ($1, $3); } + | expr '*' expr { $$ = op_times ($1, $3); } + | expr '/' expr { $$ = op_div ($1, $3); } + | expr '%' expr { $$ = op_rem ($1, $3); } + | expr ':' expr { $$ = op_colon ($1, $3); } + ; + + +%% + +struct val * +make_integer (i) +int i; +{ + struct val *vp; + + vp = (struct val *) malloc (sizeof (*vp)); + if (vp == NULL) { + err (2, NULL); + } + + vp->type = integer; + vp->u.i = i; + return vp; +} + +struct val * +make_str (s) +char *s; +{ + struct val *vp; + + vp = (struct val *) malloc (sizeof (*vp)); + if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) { + err (2, NULL); + } + + vp->type = string; + return vp; +} + + +void +free_value (vp) +struct val *vp; +{ + if (vp->type == string) + free (vp->u.s); +} + + +int +to_integer (vp) +struct val *vp; +{ + char *s; + int neg; + int i; + + if (vp->type == integer) + return 1; + + s = vp->u.s; + i = 0; + + neg = (*s == '-'); + if (neg) + s++; + + for (;*s; s++) { + if (!isdigit (*s)) + return 0; + + i *= 10; + i += *s - '0'; + } + + free (vp->u.s); + if (neg) + i *= -1; + + vp->type = integer; + vp->u.i = i; + return 1; +} + +void +to_string (vp) +struct val *vp; +{ + char *tmp; + + if (vp->type == string) + return; + + tmp = malloc (25); + if (tmp == NULL) { + err (2, NULL); + } + + sprintf (tmp, "%d", vp->u.i); + vp->type = string; + vp->u.s = tmp; +} + + +int +isstring (vp) +struct val *vp; +{ + return (vp->type == string); +} + + +int +yylex () +{ + struct val *vp; + char *p; + + if (*av == NULL) + return (0); + + p = *av++; + + if (strlen (p) == 1) { + if (strchr ("|&=<>+-*/%:()", *p)) + return (*p); + } else if (strlen (p) == 2 && p[1] == '=') { + switch (*p) { + case '>': return (GE); + case '<': return (LE); + case '!': return (NE); + } + } + + yylval.val = make_str (p); + return (TOKEN); +} + +int +is_zero_or_null (vp) +struct val *vp; +{ + if (vp->type == integer) { + return (vp->u.i == 0); + } else { + return (*vp->u.s == 0 || (to_integer (vp) && vp->u.i == 0)); + } + /* NOTREACHED */ +} + +void +main (argc, argv) +int argc; +char **argv; +{ + setlocale (LC_ALL, ""); + + av = argv + 1; + + yyparse (); + + if (result->type == integer) + printf ("%d\n", result->u.i); + else + printf ("%s\n", result->u.s); + + exit (is_zero_or_null (result)); +} + +int +yyerror (s) +char *s; +{ + errx (2, "syntax error"); +} + + +struct val * +op_or (a, b) +struct val *a, *b; +{ + if (is_zero_or_null (a)) { + free_value (a); + return (b); + } else { + free_value (b); + return (a); + } +} + +struct val * +op_and (a, b) +struct val *a, *b; +{ + if (is_zero_or_null (a) || is_zero_or_null (b)) { + free_value (a); + free_value (b); + return (make_integer (0)); + } else { + free_value (b); + return (a); + } +} + +struct val * +op_eq (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer (strcoll (a->u.s, b->u.s) == 0); + } else { + r = make_integer (a->u.i == b->u.i); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_gt (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer (strcoll (a->u.s, b->u.s) > 0); + } else { + r= make_integer (a->u.i > b->u.i); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_lt (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer (strcoll (a->u.s, b->u.s) < 0); + } else { + r = make_integer (a->u.i < b->u.i); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_ge (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer (strcoll (a->u.s, b->u.s) >= 0); + } else { + r = make_integer (a->u.i >= b->u.i); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_le (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer (strcoll (a->u.s, b->u.s) <= 0); + } else { + r = make_integer (a->u.i <= b->u.i); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_ne (a, b) +struct val *a, *b; +{ + struct val *r; + + if (isstring (a) || isstring (b)) { + to_string (a); + to_string (b); + r = make_integer (strcoll (a->u.s, b->u.s) != 0); + } else { + r = make_integer (a->u.i != b->u.i); + } + + free_value (a); + free_value (b); + return r; +} + +struct val * +op_plus (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + r = make_integer (a->u.i + b->u.i); + free_value (a); + free_value (b); + return r; +} + +struct val * +op_minus (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + r = make_integer (a->u.i - b->u.i); + free_value (a); + free_value (b); + return r; +} + +struct val * +op_times (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + r = make_integer (a->u.i * b->u.i); + free_value (a); + free_value (b); + return (r); +} + +struct val * +op_div (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + if (b->u.i == 0) { + errx (2, "division by zero"); + } + + r = make_integer (a->u.i / b->u.i); + free_value (a); + free_value (b); + return r; +} + +struct val * +op_rem (a, b) +struct val *a, *b; +{ + struct val *r; + + if (!to_integer (a) || !to_integer (b)) { + errx (2, "non-numeric argument"); + } + + if (b->u.i == 0) { + errx (2, "division by zero"); + } + + r = make_integer (a->u.i % b->u.i); + free_value (a); + free_value (b); + return r; +} + +#include <regex.h> + +struct val * +op_colon (a, b) +struct val *a, *b; +{ + regex_t rp; + regmatch_t rm[2]; + char errbuf[256]; + int eval; + struct val *v; + + /* coerce to both arguments to strings */ + to_string(a); + to_string(b); + + /* compile regular expression */ + if ((eval = regcomp (&rp, b->u.s, 0)) != 0) { + regerror (eval, &rp, errbuf, sizeof(errbuf)); + errx (2, "%s", errbuf); + } + + /* compare string against pattern */ + /* remember that patterns are anchored to the beginning of the line */ + if (regexec(&rp, a->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) { + if (rm[1].rm_so >= 0) { + *(a->u.s + rm[1].rm_eo) = '\0'; + v = make_str (a->u.s + rm[1].rm_so); + + } else { + v = make_integer (rm[0].rm_eo - rm[0].rm_so); + } + } else { + if (rp.re_nsub == 0) { + v = make_integer (0); + } else { + v = make_str (""); + } + } + + /* free arguments and pattern buffer */ + free_value (a); + free_value (b); + regfree (&rp); + + return v; +} |
