expr: treat expressions as strs until evaluation - sbase - suckless unix tools | |
git clone git://git.suckless.org/sbase | |
Log | |
Files | |
Refs | |
README | |
LICENSE | |
--- | |
commit e50d533d598dbe284e225e2ee52ed5f76a6e6f6a | |
parent 7d5b113423ae72b2edbd8433871ad8a819bfadca | |
Author: Randy Palamar <[email protected]> | |
Date: Sun, 7 Jan 2024 11:02:17 -0700 | |
expr: treat expressions as strs until evaluation | |
Comparison operations (>, <, =, etc.) and matching operations must | |
operate originally provided string not one that has gone back and | |
forth through string formatting. This caused operations such as | |
the following to give incorrect results: | |
./expr 00003 : '.*' | |
Before: 1 | |
After: 5 | |
This commit fixes that issue. | |
Diffstat: | |
M expr.c | 73 +++++++++++------------------… | |
1 file changed, 25 insertions(+), 48 deletions(-) | |
--- | |
diff --git a/expr.c b/expr.c | |
@@ -18,11 +18,13 @@ struct val { | |
}; | |
static void | |
-enan(struct val *v) | |
+tonum(struct val *v) | |
{ | |
- if (!v->str) | |
- return; | |
- enprintf(2, "syntax error: expected integer, got %s\n", v->str); | |
+ const char *errstr; | |
+ long long d = strtonum(v->str, LLONG_MIN, LLONG_MAX, &errstr); | |
+ if (errstr) | |
+ enprintf(2, "error: expected integer, got %s\n", v->str); | |
+ v->num = d; | |
} | |
static void | |
@@ -37,16 +39,14 @@ static int | |
valcmp(struct val *a, struct val *b) | |
{ | |
int ret; | |
- char buf[BUFSIZ]; | |
- | |
- if (!a->str && !b->str) { | |
- ret = (a->num > b->num) - (a->num < b->num); | |
- } else if (a->str && !b->str) { | |
- snprintf(buf, sizeof(buf), "%lld", b->num); | |
- ret = strcmp(a->str, buf); | |
- } else if (!a->str && b->str) { | |
- snprintf(buf, sizeof(buf), "%lld", a->num); | |
- ret = strcmp(buf, b->str); | |
+ const char *err1, *err2; | |
+ long long d1, d2; | |
+ | |
+ d1 = strtonum(a->str, LLONG_MIN, LLONG_MAX, &err1); | |
+ d2 = strtonum(b->str, LLONG_MIN, LLONG_MAX, &err2); | |
+ | |
+ if (!err1 && !err2) { | |
+ ret = (d1 > d2) - (d1 < d2); | |
} else { | |
ret = strcmp(a->str, b->str); | |
} | |
@@ -61,24 +61,10 @@ match(struct val *vstr, struct val *vregx, struct val *ret) | |
regmatch_t matches[2]; | |
long long d; | |
size_t anchlen; | |
- char strbuf[BUFSIZ], regxbuf[BUFSIZ], | |
- *s, *p, *anchreg, *str, *regx; | |
+ char *s, *p, *anchreg; | |
+ char *str = vstr->str, *regx = vregx->str; | |
const char *errstr; | |
- if (!vstr->str) { | |
- snprintf(strbuf, sizeof(strbuf), "%lld", vstr->num); | |
- str = strbuf; | |
- } else { | |
- str = vstr->str; | |
- } | |
- | |
- if (!vregx->str) { | |
- snprintf(regxbuf, sizeof(regxbuf), "%lld", vregx->num); | |
- regx = regxbuf; | |
- } else { | |
- regx = vregx->str; | |
- } | |
- | |
/* anchored regex */ | |
anchlen = strlen(regx) + 1 + 1; | |
anchreg = emalloc(anchlen); | |
@@ -152,11 +138,11 @@ doop(int *ophead, int *opp, struct val *valhead, struct v… | |
case LE : ret.num = (valcmp(a, b) <= 0); break; | |
case NE : ret.num = (valcmp(a, b) != 0); break; | |
- case '+': enan(a); enan(b); ret.num = a->num + b->num; break; | |
- case '-': enan(a); enan(b); ret.num = a->num - b->num; break; | |
- case '*': enan(a); enan(b); ret.num = a->num * b->num; break; | |
- case '/': enan(a); enan(b); ezero(b); ret.num = a->num / b->num; break; | |
- case '%': enan(a); enan(b); ezero(b); ret.num = a->num % b->num; break; | |
+ case '+': tonum(a); tonum(b); ret.num = a->num + b->num; bre… | |
+ case '-': tonum(a); tonum(b); ret.num = a->num - b->num; bre… | |
+ case '*': tonum(a); tonum(b); ret.num = a->num * b->num; bre… | |
+ case '/': tonum(a); tonum(b); ezero(b); ret.num = a->num / b->num; bre… | |
+ case '%': tonum(a); tonum(b); ezero(b); ret.num = a->num % b->num; bre… | |
case ':': match(a, b, &ret); break; | |
} | |
@@ -167,25 +153,15 @@ doop(int *ophead, int *opp, struct val *valhead, struct v… | |
static int | |
lex(char *s, struct val *v) | |
{ | |
- long long d; | |
int type = VAL; | |
char *ops = "|&=><+-*/%():"; | |
- const char *errstr; | |
- | |
- d = strtonum(s, LLONG_MIN, LLONG_MAX, &errstr); | |
- if (!errstr) { | |
- /* integer */ | |
- v->num = d; | |
- } else if (s[0] && strchr(ops, s[0]) && !s[1]) { | |
+ if (s[0] && strchr(ops, s[0]) && !s[1]) { | |
/* one-char operand */ | |
type = s[0]; | |
} else if (s[0] && strchr("><!", s[0]) && s[1] == '=' && !s[2]) { | |
/* two-char operand */ | |
type = (s[0] == '>') ? GE : (s[0] == '<') ? LE : NE; | |
- } else { | |
- /* string */ | |
- v->str = s; | |
} | |
return type; | |
@@ -211,8 +187,9 @@ parse(char *expr[], int numexpr) | |
for (; *expr; expr++) { | |
switch ((type = lex(*expr, &v))) { | |
case VAL: | |
- valp->str = v.str; | |
- valp->num = v.num; | |
+ /* treatment of *expr is not known until | |
+ * doop(); treat as a string for now */ | |
+ valp->str = *expr; | |
valp++; | |
break; | |
case '(': |