1/* See LICENSE file for copyright and license details. */
2
3
4#include <limits.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8
9#include "utf.h"
10#include "util.h"
11
12/* tokens, one-character operators represent themselves */
13enum {
14 VAL = CHAR_MAX + 1, GE, LE, NE
15};
16
17struct val {
18 char *str;
19 long long num;
20};
21
22static void
23tonum(struct val *v)
24{
25 const char *errstr;
26 long long d;
27
28 /* check if val is the result of an earlier calculation */
29 if (!v->str)
30 return;
31
32 d = strtonum(v->str, LLONG_MIN, LLONG_MAX, &errstr);
33 if (errstr)
34 enprintf(2, "error: expected integer, got %s\n", v->str);
35 v->num = d;
36}
37
38static void
39ezero(struct val *v)
40{
41 if (v->num != 0)
42 return;
43 enprintf(2, "division by zero\n");
44}
45
46static int
47valcmp(struct val *a, struct val *b)
48{
49 int ret;
50 const char *err1, *err2;
51 long long d1, d2;
52
53 d1 = strtonum(a->str, LLONG_MIN, LLONG_MAX, &err1);
54 d2 = strtonum(b->str, LLONG_MIN, LLONG_MAX, &err2);
55
56 if (!err1 && !err2) {
57 ret = (d1 > d2) - (d1 < d2);
58 } else {
59 ret = strcmp(a->str, b->str);
60 }
61
62 return ret;
63}
64
65static void
66match(struct val *vstr, struct val *vregx, struct val *ret)
67{
68 regex_t re;
69 regmatch_t matches[2];
70 size_t anchlen;
71 char *s, *p, *anchreg;
72 char *str = vstr->str, *regx = vregx->str;
73
74 /* anchored regex */
75 anchlen = strlen(regx) + 1 + 1;
76 anchreg = emalloc(anchlen);
77 estrlcpy(anchreg, "^", anchlen);
78 estrlcat(anchreg, regx, anchlen);
79 enregcomp(3, &re, anchreg, 0);
80 free(anchreg);
81
82 if (regexec(&re, str, 2, matches, 0)) {
83 regfree(&re);
84 ret->str = re.re_nsub ? "" : NULL;
85 return;
86 } else if (re.re_nsub) {
87 regfree(&re);
88
89 s = str + matches[1].rm_so;
90 p = str + matches[1].rm_eo;
91 *p = '\0';
92 ret->str = enstrdup(3, s);
93 return;
94 } else {
95 regfree(&re);
96 str += matches[0].rm_so;
97 ret->num = utfnlen(str, matches[0].rm_eo - matches[0].rm_so);
98 return;
99 }
100}
101
102static void
103doop(int *ophead, int *opp, struct val *valhead, struct val *valp)
104{
105 struct val ret = { .str = NULL, .num = 0 }, *a, *b;
106 int op;
107
108 (void)ophead;
109
110 /* an operation "a op b" needs an operator and two values */
111 if (opp[-1] == '(')
112 enprintf(2, "syntax error: extra (\n");
113 if (valp - valhead < 2)
114 enprintf(2, "syntax error: missing expression or extra operator\n");
115
116 a = valp - 2;
117 b = valp - 1;
118 op = opp[-1];
119
120 switch (op) {
121 case '|':
122 if ( a->str && *a->str) ret.str = a->str;
123 else if (!a->str && a->num) ret.num = a->num;
124 else if ( b->str && *b->str) ret.str = b->str;
125 else ret.num = b->num;
126 break;
127 case '&':
128 if (((a->str && *a->str) || a->num) &&
129 ((b->str && *b->str) || b->num)) {
130 ret.str = a->str;
131 ret.num = a->num;
132 }
133 break;
134
135 case '=': ret.num = (valcmp(a, b) == 0); break;
136 case '>': ret.num = (valcmp(a, b) > 0); break;
137 case GE : ret.num = (valcmp(a, b) >= 0); break;
138 case '<': ret.num = (valcmp(a, b) < 0); break;
139 case LE : ret.num = (valcmp(a, b) <= 0); break;
140 case NE : ret.num = (valcmp(a, b) != 0); break;
141
142 case '+': tonum(a); tonum(b); ret.num = a->num + b->num; break;
143 case '-': tonum(a); tonum(b); ret.num = a->num - b->num; break;
144 case '*': tonum(a); tonum(b); ret.num = a->num * b->num; break;
145 case '/': tonum(a); tonum(b); ezero(b); ret.num = a->num / b->num; break;
146 case '%': tonum(a); tonum(b); ezero(b); ret.num = a->num % b->num; break;
147
148 case ':': match(a, b, &ret); break;
149 }
150
151 valp[-2] = ret;
152}
153
154static int
155lex(char *s, struct val *v)
156{
157 int type = VAL;
158 char *ops = "|&=><+-*/%():";
159
160 (void)v;
161
162 if (s[0] && strchr(ops, s[0]) && !s[1]) {
163 /* one-char operand */
164 type = s[0];
165 } else if (s[0] && strchr("><!", s[0]) && s[1] == '=' && !s[2]) {
166 /* two-char operand */
167 type = (s[0] == '>') ? GE : (s[0] == '<') ? LE : NE;
168 }
169
170 return type;
171}
172
173static int
174parse(char *expr[], int numexpr)
175{
176 struct val *valhead, *valp, v = { .str = NULL, .num = 0 };
177 int *ophead, *opp, type, lasttype = 0;
178 char prec[] = {
179 [ 0 ] = 0, [VAL] = 0, ['('] = 0, [')'] = 0,
180 ['|'] = 1,
181 ['&'] = 2,
182 ['='] = 3, ['>'] = 3, [GE] = 3, ['<'] = 3, [LE] = 3, [NE] = 3,
183 ['+'] = 4, ['-'] = 4,
184 ['*'] = 5, ['/'] = 5, ['%'] = 5,
185 [':'] = 6,
186 };
187
188 valp = valhead = enreallocarray(3, NULL, numexpr, sizeof(*valp));
189 opp = ophead = enreallocarray(3, NULL, numexpr, sizeof(*opp));
190 for (; *expr; expr++) {
191 switch ((type = lex(*expr, &v))) {
192 case VAL:
193 /* treatment of *expr is not known until
194 * doop(); treat as a string for now */
195 valp->str = *expr;
196 valp++;
197 break;
198 case '(':
199 *opp++ = type;
200 break;
201 case ')':
202 if (lasttype == '(')
203 enprintf(2, "syntax error: empty ( )\n");
204 while (opp > ophead && opp[-1] != '(')
205 doop(ophead, opp--, valhead, valp--);
206 if (opp == ophead)
207 enprintf(2, "syntax error: extra )\n");
208 opp--;
209 break;
210 default: /* operator */
211 if (prec[lasttype])
212 enprintf(2, "syntax error: extra operator\n");
213 while (opp > ophead && prec[opp[-1]] >= prec[type])
214 doop(ophead, opp--, valhead, valp--);
215 *opp++ = type;
216 break;
217 }
218 lasttype = type;
219 v.str = NULL;
220 v.num = 0;
221 }
222 while (opp > ophead)
223 doop(ophead, opp--, valhead, valp--);
224 if (valp == valhead)
225 enprintf(2, "syntax error: missing expression\n");
226 if (--valp > valhead)
227 enprintf(2, "syntax error: extra expression\n");
228
229 if (valp->str)
230 puts(valp->str);
231 else
232 printf("%lld\n", valp->num);
233
234 return (valp->str && *valp->str) || valp->num;
235}
236
237// ?man expr: evaluate expression
238// ?man evaluate a command line expression and print the result
239int
240main(int argc, char *argv[])
241{
242 int ret;
243
244 argv0 = *argv, argv0 ? (argc--, argv++) : (void *)0;
245
246 ret = !parse(argv, argc);
247
248 if (fshut(stdout, "<stdout>"))
249 ret = 3;
250
251 return ret;
252}