*BSD News Article 11832


Return to BSD News archive

Received: by minnie.vk1xwt.ampr.org with NNTP
	id AA2312 ; Mon, 01 Mar 93 10:53:35 EST
Path: sserve!manuel.anu.edu.au!munnari.oz.au!news.Hawaii.Edu!ames!olivea!apple!conklin
From: conklin@kaleida.com (J.T. Conklin)
Newsgroups: comp.os.386bsd.bugs
Subject: /bin/expr totally broken
Message-ID: <CONKLIN.93Feb26113140@talisman.kaleida.com>
Date: 26 Feb 93 19:31:40 GMT
Sender: usenet@Apple.COM
Reply-To: conklin@kaleida.com
Organization: Kaleida Labs, Inc., Mountain View, CA
Lines: 624

Here is the results of my fixes to /bin/expr.  It fixes parenthesized
expressions, devision by zero, and string expressions being coerced
into intergers too soon.

I've allready sent this to the Jolitz's and Jordan, but I thought I'd
share it everyone since I've seen several complaints about expr.

I'm enclosing this as a new file, since the diffs are larger than the
program itself.  It replaces /usr/src/bin/expr/expr.y

Regression Tests:
	$ expr \( 2 + 3 \) \* 5
	25
	$ expr '0000001' : '.*\(.....\)$'
	00001
	$ expr 0123
	0123
	$ expr 0123 = 123
	1
	$ expr 1 / 0
	expr: devision by zero
	$ expr 1 % 0
	expr: devision by zero

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	expr.y
#
echo x - expr.y
sed 's/^X//' >expr.y << 'END-of-expr.y'
X%{
X/* Written by Pace Willisson (pace@blitz.com) 
X * and placed in the public domain
X */
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <ctype.h>
X
Xenum valtype {
X	integer, string
X} ;
X    
Xstruct val {
X	enum valtype type;
X	union {
X		char *s;
X		int   i;
X	} u;
X};
X
Xstruct val *result;
Xstruct val *op_or ();
Xstruct val *op_and ();
Xstruct val *op_eq ();
Xstruct val *op_gt ();
Xstruct val *op_lt ();
Xstruct val *op_ge ();
Xstruct val *op_le ();
Xstruct val *op_ne ();
Xstruct val *op_plus ();
Xstruct val *op_minus ();
Xstruct val *op_times ();
Xstruct val *op_div ();
Xstruct val *op_rem ();
Xstruct val *op_colon ();
X
Xchar **av;
X%}
X
X%union
X{
X	struct val *val;
X}
X
X%left <val> '|'
X%left <val> '&'
X%left <val> '=' '>' '<' GE LE NE
X%left <val> '+' '-'
X%left <val> '*' '/' '%'
X%left <val> ':'
X%left UNARY
X
X%token <val> TOKEN
X%type <val> start expr
X
X%%
X
Xstart: expr { result = $$; }
X
Xexpr:	TOKEN
X	| '(' expr ')' { $$ = $2; }
X	| expr '|' expr { $$ = op_or ($1, $3); }
X	| expr '&' expr { $$ = op_and ($1, $3); }
X	| expr '=' expr { $$ = op_eq ($1, $3); }
X	| expr '>' expr { $$ = op_gt ($1, $3); }
X	| expr '<' expr { $$ = op_lt ($1, $3); }
X	| expr GE expr  { $$ = op_ge ($1, $3); }
X	| expr LE expr  { $$ = op_le ($1, $3); }
X	| expr NE expr  { $$ = op_ne ($1, $3); }
X	| expr '+' expr { $$ = op_plus ($1, $3); }
X	| expr '-' expr { $$ = op_minus ($1, $3); }
X	| expr '*' expr { $$ = op_times ($1, $3); }
X	| expr '/' expr { $$ = op_div ($1, $3); }
X	| expr '%' expr { $$ = op_rem ($1, $3); }
X	| expr ':' expr { $$ = op_colon ($1, $3); }
X	| '-' expr %prec UNARY { $$ = op_minus (NULL, $2); }
X	;
X
X
X%%
X
Xstruct val *
Xmake_integer (i)
Xint i;
X{
X	struct val *vp;
X
X	vp = malloc (sizeof (*vp));
X	if (vp == NULL) {
X		fprintf (stderr, "expr: out of memory\n");
X		exit (2);
X	}
X
X	vp->type = integer;
X	vp->u.i  = i;
X	return vp; 
X}
X
Xstruct val *
Xmake_str (s)
Xchar *s;
X{
X	struct val *vp;
X
X	vp = malloc (sizeof (*vp));
X	if (vp == NULL || ((vp->u.s = strdup (s)) == NULL)) {
X		fprintf (stderr, "expr: out of memory\n");
X		exit (2);
X	}
X
X	vp->type = string;
X	return vp;
X}
X
X
Xvoid
Xfree_value (vp)
Xstruct val *vp;
X{
X	if (vp->type == string)
X		free (vp->u.s);	
X}
X
X
Xint
Xto_integer (vp)
Xstruct val *vp;
X{
X	char *s;
X	int neg;
X	int i;
X
X	if (vp->type == integer)
X		return 1;
X
X	s = vp->u.s;
X	i = 0;
X      
X	neg = (*s == '-');
X	if (neg)
X		s++;
X      
X	for (;*s; s++) {
X		if (!isdigit (*s)) 
X			return 0;
X      
X		i *= 10;
X		i += *s - '0';
X	}
X
X	free (vp->u.s);
X	if (neg) 
X		i *= -1;
X  
X	vp->type = integer;
X	vp->u.i  = i;
X	return 1;
X}
X
Xvoid
Xto_string (vp)
Xstruct val *vp;
X{
X	char *tmp;
X
X	if (vp->type == string)
X		return;
X
X	tmp = malloc (25);
X	if (tmp == NULL) {
X		fprintf (stderr, "expr: out of memory\n");
X		exit (2);
X	}
X
X	sprintf (tmp, "%d", vp->u.i);
X	vp->type = string;
X	vp->u.s  = tmp;
X}
X
X
Xint
Xisstring (vp)
Xstruct val *vp;
X{
X	return (vp->type == string);
X}
X
X
Xint
Xyylex ()
X{
X	struct val *vp;
X	char *p;
X
X	if (*av == NULL)
X		return (0);
X
X	p = *av++;
X
X	if (strlen (p) == 1) {
X		if (strchr ("|&=<>+-*/%:()", *p))
X			return (*p);
X	} else if (strlen (p) == 2 && p[1] == '=') {
X		switch (*p) {
X		case '>': return (GE);
X		case '<': return (LE);
X		case '!': return (NE);
X		}
X	}
X
X	yylval.val = make_str (p);
X	return (TOKEN);
X}
X
Xint
Xis_zero_or_null (vp)
Xstruct val *vp;
X{
X	if (vp->type = integer) {
X		if (vp->u.i == 0)
X			return (1);
X	} else {
X		if (*vp->u.s == 0)
X			return (1);
X	}
X
X	return (0);
X}
X
Xvoid
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X	av = argv + 1;
X
X	yyparse ();
X
X	if (result->type == integer)
X		printf ("%d\n", result->u.i);
X	else
X		printf ("%s\n", result->u.s);
X
X	if (is_zero_or_null (result))
X		exit (1);
X	else
X		exit (0);
X}
X
Xint
Xyyerror (s)
Xchar *s;
X{
X	fprintf (stderr, "expr: syntax error\n");
X	exit (2);
X}
X
X
Xstruct val *
Xop_or (a, b)
Xstruct val *a, *b;
X{
X	if (is_zero_or_null (a)) {
X		free_value (a);
X		return (b);
X	} else {
X		free_value (b);
X		return (a);
X	}
X}
X		
Xstruct val *
Xop_and (a, b)
Xstruct val *a, *b;
X{
X	if (is_zero_or_null (a) || is_zero_or_null (b)) {
X		free_value (a);
X		free_value (b);
X		return (make_integer (0));
X	} else {
X		free_value (b);
X		return (a);
X	}
X}
X
Xstruct val *
Xop_eq (a, b)
Xstruct val *a, *b;
X{
X	struct val *r; 
X
X	/* attempt to coerce both arguments to integers */
X	(void) to_integer (a);
X	(void) to_integer (b);
X
X	/* But if either one of them really is a string, do 
X	   a string comparison */
X	if (isstring (a) || isstring (b)) {
X		to_string (a);
X		to_string (b);	
X		r = make_integer (strcmp (a->u.s, b->u.s) == 0);
X	} else {
X		r = make_integer (a->u.i == b->u.i);
X	}
X
X	free_value (a);
X	free_value (b);
X	return r;
X}
X
Xstruct val *
Xop_gt (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	/* attempt to coerce both arguments to integers */
X	(void) to_integer (a);
X	(void) to_integer (b);
X
X	/* But if either one of them really is a string, do 
X	   a string comparison */
X	if (isstring (a) || isstring (b)) {
X		to_string (a);
X		to_string (b);
X		r = make_integer (strcmp (a->u.s, b->u.s) > 0);
X	} else {
X		r= make_integer (a->u.i > b->u.i);
X	}
X
X	free_value (a);
X	free_value (b);
X	return r;
X}
X
Xstruct val *
Xop_lt (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	/* attempt to coerce both arguments to integers */
X	(void) to_integer (a);
X	(void) to_integer (b);
X
X	/* But if either one of them really is a string, do 
X	   a string comparison */
X	if (isstring (a) || isstring (b)) {
X		to_string (a);
X		to_string (b);
X		r = make_integer (strcmp (a->u.s, b->u.s) < 0);
X	} else {
X		r = make_integer (a->u.i < b->u.i);
X	}
X
X	free_value (a);
X	free_value (b);
X	return r;
X}
X
Xstruct val *
Xop_ge (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	/* attempt to coerce both arguments to integers */
X	(void) to_integer (a);
X	(void) to_integer (b);
X
X	/* But if either one of them really is a string, do 
X	   a string comparison */
X	if (isstring (a) || isstring (b)) {
X		to_string (a);
X		to_string (b);
X		r = make_integer (strcmp (a->u.s, b->u.s) >= 0);
X	} else {
X		r = make_integer (a->u.i >= b->u.i);
X	}
X
X	free_value (a);
X	free_value (b);
X	return r;
X}
X
Xstruct val *
Xop_le (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	/* attempt to coerce both arguments to integers */
X	(void) to_integer (a);
X	(void) to_integer (b);
X
X	/* But if either one of them really is a string, do 
X	   a string comparison */
X	if (isstring (a) || isstring (b)) {
X		to_string (a);
X		to_string (b);
X		r = make_integer (strcmp (a->u.s, b->u.s) <= 0);
X	} else {
X		r = make_integer (a->u.i <= b->u.i);
X	}
X
X	free_value (a);
X	free_value (b);
X	return r;
X}
X
Xstruct val *
Xop_ne (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	/* attempt to coerce both arguments to integers */
X	(void) to_integer (a);
X	(void) to_integer (b);
X
X	/* But if either one of them really is a string, do 
X	   a string comparison */
X	if (isstring (a) || isstring (b)) {
X		to_string (a);
X		to_string (b);
X		r = make_integer (strcmp (a->u.s, b->u.s) != 0);
X	} else {
X		r = make_integer (a->u.i != b->u.i);
X	}
X
X	free_value (a);
X	free_value (b);
X	return r;
X}
X
Xstruct val *
Xop_plus (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	if (!to_integer (a) || !to_integer (b)) {
X		fprintf (stderr, "expr: non-numeric argument\n");
X		exit (2);
X	}
X
X	r = make_integer (a->u.i + b->u.i);
X	free_value (a);
X	free_value (b);
X	return r;
X}
X	
Xstruct val *
Xop_minus (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	if (!to_integer (a) || !to_integer (b)) {
X		fprintf (stderr, "expr: non-numeric argument\n");
X		exit (2);
X	}
X
X	r = make_integer (a->u.i - b->u.i);
X	free_value (a);
X	free_value (b);
X	return r;
X}
X	
Xstruct val *
Xop_times (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	if (!to_integer (a) || !to_integer (b)) {
X		fprintf (stderr, "expr: non-numeric argument\n");
X		exit (2);
X	}
X
X	r = make_integer (a->u.i * b->u.i);
X	free_value (a);
X	free_value (b);
X	return (r);
X}
X	
Xstruct val *
Xop_div (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	if (!to_integer (a) || !to_integer (b)) {
X		fprintf (stderr, "expr: non-numeric argument\n");
X		exit (2);
X	}
X
X	if (b->u.i == 0) {
X		fprintf (stderr, "expr: division by zero\n");
X		exit (2);
X	}
X
X	r = make_integer (a->u.i / b->u.i);
X	free_value (a);
X	free_value (b);
X	return r;
X}
X	
Xstruct val *
Xop_rem (a, b)
Xstruct val *a, *b;
X{
X	struct val *r;
X
X	if (!to_integer (a) || !to_integer (b)) {
X		fprintf (stderr, "expr: non-numeric argument\n");
X		exit (2);
X	}
X
X	if (b->u.i == 0) {
X		fprintf (stderr, "expr: division by zero\n");
X		exit (2);
X	}
X
X	r = make_integer (a->u.i % b->u.i);
X	free_value (a);
X	free_value (b);
X	return r;
X}
X	
X#include <regexp.h>
X
Xstruct val *
Xop_colon (a, b)
Xstruct val *a, *b;
X{
X	regexp *rp;
X	char *newexp;
X	char *p;
X	char *q;
X
X	newexp = malloc (3 * strlen (b->u.s));
X	p = b->u.s;
X	q = newexp;
X
X	*q++ = '^';
X	while (*p) {
X		if (*p == '\\') {
X			p++;
X			if (*p == '(' || *p == ')') {
X				*q++ = *p++;
X			} else {
X				*q++ = '\\';
X				*q++ = *p++;
X			}
X		} else if (*p == '(' || *p == ')') {
X			*q++ = '\\';
X			*q++ = *p++;
X		} else {
X			*q++ = *p++;
X		}
X	}
X	*q = 0;
X				
X	if ((rp = regcomp (newexp)) == NULL)
X		yyerror ("invalid regular expression");
X
X	if (regexec (rp, a->u.s)) {
X		if (rp->startp[1]) {
X			rp->endp[1][0] = 0;
X			return (make_str (rp->startp[1]));
X		} else {
X			return (make_integer (rp->endp[0] - rp->startp[0]));
X		}
X	} else {
X		return (make_integer (0));
X	}
X}
END-of-expr.y
exit


--
J.T. Conklin <jtc@wimsey.com>    | Your source for floppy distributions
    Winning Strategies, Inc.     |    of the 386BSD OS and binaries
    61 Crestwood Drive #18       | Send e-mail for complete product list
    Daly City, CA 94015          +---------------------------------------