NetBSD/bin/expr/expr.c
cgd ad8b41fa2a don't spew an error message if setlocale(LC_ALL, ""); fails. This is for
consistency with other programs, and because currently it can never
actually succeeed if LANG is set to something other than "C".  Sent
in by me in PR 2486, approved by jtc).
1996-06-04 20:41:15 +00:00

505 lines
7.2 KiB
C

/* $NetBSD: expr.c,v 1.4 1996/06/04 20:41:15 cgd Exp $ */
/*
* Written by J.T. Conklin <jtc@netbsd.org>.
* Public domain.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <locale.h>
#include <ctype.h>
#include <regex.h>
#include <err.h>
enum token {
OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
NE, LE, GE, OPERAND, EOI
};
struct val {
enum {
integer,
string
} type;
union {
char *s;
int i;
} u;
};
enum token token;
struct val *tokval;
char **av;
struct val *
make_int(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);
free(vp);
}
/* determine if vp is an integer; if so, return it's value in *r */
int
is_integer(vp, r)
struct val *vp;
int *r;
{
char *s;
int neg;
int i;
if (vp->type == integer) {
*r = vp->u.i;
return 1;
}
/*
* POSIX.2 defines an "integer" as an optional unary minus
* followed by digits.
*/
s = vp->u.s;
i = 0;
neg = (*s == '-');
if (neg)
s++;
while (*s) {
if (!isdigit(*s))
return 0;
i *= 10;
i += *s - '0';
s++;
}
if (neg)
i *= -1;
*r = i;
return 1;
}
/* coerce to vp to an integer */
int
to_integer(vp)
struct val *vp;
{
int r;
if (vp->type == integer)
return 1;
if (is_integer(vp, &r)) {
free(vp->u.s);
vp->u.i = r;
vp->type = integer;
return 1;
}
return 0;
}
/* coerce to vp to an string */
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
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
nexttoken()
{
char *p;
if ((p = *av) == NULL) {
token = EOI;
return;
}
av++;
if (p[0] != '\0') {
if (p[1] == '\0') {
const char *x = "|&=<>+-*/%:()";
char *i; /* index */
if ((i = strchr(x, *p)) != NULL) {
token = i - x;
return;
}
} else if (p[1] == '=' && p[2] == '\0') {
switch (*p) {
case '<':
token = LE;
return;
case '>':
token = GE;
return;
case '!':
token = NE;
return;
}
}
}
tokval = make_str(p);
token = OPERAND;
return;
}
void
error()
{
errx(2, "syntax error");
/* NOTREACHED */
}
struct val *
eval6()
{
struct val *eval0 __P((void));
struct val *v;
if (token == OPERAND) {
nexttoken();
return tokval;
} else if (token == RP) {
nexttoken();
v = eval0();
if (token != LP) {
error();
/* NOTREACHED */
}
nexttoken();
return v;
} else {
error();
}
/* NOTREACHED */
}
/* Parse and evaluate match (regex) expressions */
struct val *
eval5()
{
regex_t rp;
regmatch_t rm[2];
char errbuf[256];
int eval;
struct val *l, *r;
struct val *v;
l = eval6();
while (token == MATCH) {
nexttoken();
r = eval6();
/* coerce to both arguments to strings */
to_string(l);
to_string(r);
/* compile regular expression */
if ((eval = regcomp(&rp, r->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, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
if (rm[1].rm_so >= 0) {
*(l->u.s + rm[1].rm_eo) = '\0';
v = make_str(l->u.s + rm[1].rm_so);
} else {
v = make_int((int)(rm[0].rm_eo - rm[0].rm_so));
}
} else {
if (rp.re_nsub == 0) {
v = make_int(0);
} else {
v = make_str("");
}
}
/* free arguments and pattern buffer */
free_value(l);
free_value(r);
regfree(&rp);
l = v;
}
return l;
}
/* Parse and evaluate multiplication and division expressions */
struct val *
eval4()
{
struct val *l, *r;
enum token op;
l = eval5();
while ((op = token) == MUL || op == DIV || op == MOD) {
nexttoken();
r = eval5();
if (!to_integer(l) || !to_integer(r)) {
errx(2, "non-numeric argument");
}
if (op == MUL) {
l->u.i *= r->u.i;
} else {
if (r->u.i == 0) {
errx(2, "division by zero");
}
if (op == DIV) {
l->u.i /= r->u.i;
} else {
l->u.i %= r->u.i;
}
}
free_value(r);
}
return l;
}
/* Parse and evaluate addition and subtraction expressions */
struct val *
eval3()
{
struct val *l, *r;
enum token op;
l = eval4();
while ((op = token) == ADD || op == SUB) {
nexttoken();
r = eval4();
if (!to_integer(l) || !to_integer(r)) {
errx(2, "non-numeric argument");
}
if (op == ADD) {
l->u.i += r->u.i;
} else {
l->u.i -= r->u.i;
}
free_value(r);
}
return l;
}
/* Parse and evaluate comparison expressions */
struct val *
eval2()
{
struct val *l, *r;
enum token op;
int v, li, ri;
l = eval3();
while ((op = token) == EQ || op == NE || op == LT || op == GT || op == LE || op == GE) {
nexttoken();
r = eval3();
if (is_integer(l, &li) && is_integer(r, &ri)) {
switch (op) {
case GT:
v = (li > ri);
break;
case GE:
v = (li >= ri);
break;
case LT:
v = (li < ri);
break;
case LE:
v = (li <= ri);
break;
case EQ:
v = (li == ri);
break;
case NE:
v = (li != ri);
break;
}
} else {
to_string(l);
to_string(r);
switch (op) {
case GT:
v = (strcoll(l->u.s, r->u.s) > 0);
break;
case GE:
v = (strcoll(l->u.s, r->u.s) >= 0);
break;
case LT:
v = (strcoll(l->u.s, r->u.s) < 0);
break;
case LE:
v = (strcoll(l->u.s, r->u.s) <= 0);
break;
case EQ:
v = (strcoll(l->u.s, r->u.s) == 0);
break;
case NE:
v = (strcoll(l->u.s, r->u.s) != 0);
break;
}
}
free_value(l);
free_value(r);
l = make_int(v);
}
return l;
}
/* Parse and evaluate & expressions */
struct val *
eval1()
{
struct val *l, *r;
l = eval2();
while (token == AND) {
nexttoken();
r = eval2();
if (is_zero_or_null(l) || is_zero_or_null(r)) {
free_value(l);
free_value(r);
l = make_int(0);
} else {
free_value(r);
}
}
return l;
}
/* Parse and evaluate | expressions */
struct val *
eval0()
{
struct val *l, *r;
l = eval1();
while (token == OR) {
nexttoken();
r = eval1();
if (is_zero_or_null(l)) {
free_value(l);
l = r;
} else {
free_value(r);
}
}
return l;
}
int
main(argc, argv)
int argc;
char **argv;
{
struct val *vp;
(void) setlocale(LC_ALL, "");
av = argv + 1;
nexttoken();
vp = eval0();
if (token != EOI) {
error();
/* NOTREACHED */
}
if (vp->type == integer)
printf("%d\n", vp->u.i);
else
printf("%s\n", vp->u.s);
exit(is_zero_or_null(vp));
}