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 +---------------------------------------