summaryrefslogtreecommitdiffstats
path: root/bin/expr/expr.c
diff options
context:
space:
mode:
authorderaadt <deraadt@openbsd.org>1995-10-18 08:37:01 +0000
committerderaadt <deraadt@openbsd.org>1995-10-18 08:37:01 +0000
commitdf930be708d50e9715f173caa26ffe1b7599b157 (patch)
treeaa317e49e28cb999c9cf3db7f00c20903fe6010a /bin/expr/expr.c
downloadwireguard-openbsd-df930be708d50e9715f173caa26ffe1b7599b157.tar.xz
wireguard-openbsd-df930be708d50e9715f173caa26ffe1b7599b157.zip
initial import of NetBSD tree
Diffstat (limited to 'bin/expr/expr.c')
-rw-r--r--bin/expr/expr.c507
1 files changed, 507 insertions, 0 deletions
diff --git a/bin/expr/expr.c b/bin/expr/expr.c
new file mode 100644
index 00000000000..dcf570bfb23
--- /dev/null
+++ b/bin/expr/expr.c
@@ -0,0 +1,507 @@
+/* $NetBSD: expr.c,v 1.3 1995/04/28 23:27:15 jtc 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;
+
+ if (!setlocale(LC_ALL, "")) {
+ fprintf(stderr,
+ "setlocale failed, continuing with \"C\" locale.");
+ }
+ 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));
+}