1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80#include "libbb.h"
81#include "common_bufsiz.h"
82#include "xregex.h"
83
84#if ENABLE_EXPR_MATH_SUPPORT_64
85typedef int64_t arith_t;
86
87#define PF_REZ "ll"
88#define PF_REZ_TYPE (long long)
89#define STRTOL(s, e, b) strtoll(s, e, b)
90#else
91typedef long arith_t;
92
93#define PF_REZ "l"
94#define PF_REZ_TYPE (long)
95#define STRTOL(s, e, b) strtol(s, e, b)
96#endif
97
98
99
100
101enum {
102 INTEGER,
103 STRING
104};
105
106
107struct valinfo {
108 smallint type;
109 union {
110 arith_t i;
111 char *s;
112 } u;
113};
114typedef struct valinfo VALUE;
115
116
117struct globals {
118 char **args;
119} FIX_ALIASING;
120#define G (*(struct globals*)bb_common_bufsiz1)
121#define INIT_G() do { \
122 setup_common_bufsiz(); \
123 \
124} while (0)
125
126
127static VALUE *eval(void);
128
129
130
131
132static VALUE *int_value(arith_t i)
133{
134 VALUE *v;
135
136 v = xzalloc(sizeof(VALUE));
137 if (INTEGER)
138 v->type = INTEGER;
139 v->u.i = i;
140 return v;
141}
142
143
144
145static VALUE *str_value(const char *s)
146{
147 VALUE *v;
148
149 v = xzalloc(sizeof(VALUE));
150 if (STRING)
151 v->type = STRING;
152 v->u.s = xstrdup(s);
153 return v;
154}
155
156
157
158static void freev(VALUE *v)
159{
160 if (v->type == STRING)
161 free(v->u.s);
162 free(v);
163}
164
165
166
167static int null(VALUE *v)
168{
169 if (v->type == INTEGER)
170 return v->u.i == 0;
171
172 return v->u.s[0] == '\0' || LONE_CHAR(v->u.s, '0');
173}
174
175
176
177static void tostring(VALUE *v)
178{
179 if (v->type == INTEGER) {
180 v->u.s = xasprintf("%" PF_REZ "d", PF_REZ_TYPE v->u.i);
181 v->type = STRING;
182 }
183}
184
185
186
187static bool toarith(VALUE *v)
188{
189 if (v->type == STRING) {
190 arith_t i;
191 char *e;
192
193
194
195 i = STRTOL(v->u.s, &e, 10);
196 if ((v->u.s == e) || *e)
197 return 0;
198 free(v->u.s);
199 v->u.i = i;
200 v->type = INTEGER;
201 }
202 return 1;
203}
204
205
206
207
208static int nextarg(const char *str)
209{
210 if (*G.args == NULL || strcmp(*G.args, str) != 0)
211 return 0;
212 return (unsigned char)str[0] + (unsigned char)str[1];
213}
214
215
216
217static int cmp_common(VALUE *l, VALUE *r, int op)
218{
219 arith_t ll, rr;
220
221 ll = l->u.i;
222 rr = r->u.i;
223 if (l->type == STRING || r->type == STRING) {
224 tostring(l);
225 tostring(r);
226 ll = strcmp(l->u.s, r->u.s);
227 rr = 0;
228 }
229
230
231 if (op == '<')
232 return ll < rr;
233 if (op == ('<' + '='))
234 return ll <= rr;
235 if (op == '=' || (op == '=' + '='))
236 return ll == rr;
237 if (op == '!' + '=')
238 return ll != rr;
239 if (op == '>')
240 return ll > rr;
241
242 return ll >= rr;
243}
244
245
246
247static arith_t arithmetic_common(VALUE *l, VALUE *r, int op)
248{
249 arith_t li, ri;
250
251 if (!toarith(l) || !toarith(r))
252 bb_simple_error_msg_and_die("non-numeric argument");
253 li = l->u.i;
254 ri = r->u.i;
255 if (op == '+')
256 return li + ri;
257 if (op == '-')
258 return li - ri;
259 if (op == '*')
260 return li * ri;
261 if (ri == 0)
262 bb_simple_error_msg_and_die("division by zero");
263 if (op == '/')
264 return li / ri;
265 return li % ri;
266}
267
268
269
270
271
272static VALUE *docolon(VALUE *sv, VALUE *pv)
273{
274 enum { NMATCH = 2 };
275 VALUE *v;
276 regex_t re_buffer;
277 regmatch_t re_regs[NMATCH];
278
279 tostring(sv);
280 tostring(pv);
281
282 if (pv->u.s[0] == '^') {
283 bb_error_msg(
284"warning: '%s': using '^' as the first character\n"
285"of a basic regular expression is not portable; it is ignored", pv->u.s);
286 }
287
288 memset(&re_buffer, 0, sizeof(re_buffer));
289 memset(re_regs, 0, sizeof(re_regs));
290 xregcomp(&re_buffer, pv->u.s, 0);
291
292
293
294 if (regexec(&re_buffer, sv->u.s, NMATCH, re_regs, 0) != REG_NOMATCH
295 && re_regs[0].rm_so == 0
296 ) {
297
298 if (re_buffer.re_nsub > 0 && re_regs[1].rm_so >= 0) {
299 sv->u.s[re_regs[1].rm_eo] = '\0';
300 v = str_value(sv->u.s + re_regs[1].rm_so);
301 } else {
302 v = int_value(re_regs[0].rm_eo);
303 }
304 } else {
305
306 if (re_buffer.re_nsub > 0)
307 v = str_value("");
308 else
309 v = int_value(0);
310 }
311 regfree(&re_buffer);
312 return v;
313}
314
315
316
317static VALUE *eval7(void)
318{
319 VALUE *v;
320
321 if (!*G.args)
322 bb_simple_error_msg_and_die("syntax error");
323
324 if (nextarg("(")) {
325 G.args++;
326 v = eval();
327 if (!nextarg(")"))
328 bb_simple_error_msg_and_die("syntax error");
329 G.args++;
330 return v;
331 }
332
333 if (nextarg(")"))
334 bb_simple_error_msg_and_die("syntax error");
335
336 return str_value(*G.args++);
337}
338
339
340
341static VALUE *eval6(void)
342{
343 static const char keywords[] ALIGN1 =
344 "quote\0""length\0""match\0""index\0""substr\0";
345
346 VALUE *r, *i1, *i2;
347 VALUE *l = l;
348 VALUE *v = v;
349 int key = *G.args ? index_in_strings(keywords, *G.args) + 1 : 0;
350
351 if (key == 0)
352 return eval7();
353 G.args++;
354 if (key == 1) {
355 if (!*G.args)
356 bb_simple_error_msg_and_die("syntax error");
357 return str_value(*G.args++);
358 }
359 if (key == 2) {
360 r = eval6();
361 tostring(r);
362 v = int_value(strlen(r->u.s));
363 freev(r);
364 } else
365 l = eval6();
366
367 if (key == 3) {
368 r = eval6();
369 v = docolon(l, r);
370 freev(l);
371 freev(r);
372 }
373 if (key == 4) {
374 r = eval6();
375 tostring(l);
376 tostring(r);
377 v = int_value(strcspn(l->u.s, r->u.s) + 1);
378 if (v->u.i == (arith_t) strlen(l->u.s) + 1)
379 v->u.i = 0;
380 freev(l);
381 freev(r);
382 }
383 if (key == 5) {
384 i1 = eval6();
385 i2 = eval6();
386 tostring(l);
387 if (!toarith(i1) || !toarith(i2)
388 || i1->u.i > (arith_t) strlen(l->u.s)
389 || i1->u.i <= 0 || i2->u.i <= 0)
390 v = str_value("");
391 else {
392 v = xmalloc(sizeof(VALUE));
393 v->type = STRING;
394 v->u.s = xstrndup(l->u.s + i1->u.i - 1, i2->u.i);
395 }
396 freev(l);
397 freev(i1);
398 freev(i2);
399 }
400 return v;
401}
402
403
404
405
406static VALUE *eval5(void)
407{
408 VALUE *l, *r, *v;
409
410 l = eval6();
411 while (nextarg(":")) {
412 G.args++;
413 r = eval6();
414 v = docolon(l, r);
415 freev(l);
416 freev(r);
417 l = v;
418 }
419 return l;
420}
421
422
423
424static VALUE *eval4(void)
425{
426 VALUE *l, *r;
427 int op;
428 arith_t val;
429
430 l = eval5();
431 while (1) {
432 op = nextarg("*");
433 if (!op) { op = nextarg("/");
434 if (!op) { op = nextarg("%");
435 if (!op) return l;
436 }}
437 G.args++;
438 r = eval5();
439 val = arithmetic_common(l, r, op);
440 freev(l);
441 freev(r);
442 l = int_value(val);
443 }
444}
445
446
447
448static VALUE *eval3(void)
449{
450 VALUE *l, *r;
451 int op;
452 arith_t val;
453
454 l = eval4();
455 while (1) {
456 op = nextarg("+");
457 if (!op) {
458 op = nextarg("-");
459 if (!op) return l;
460 }
461 G.args++;
462 r = eval4();
463 val = arithmetic_common(l, r, op);
464 freev(l);
465 freev(r);
466 l = int_value(val);
467 }
468}
469
470
471
472static VALUE *eval2(void)
473{
474 VALUE *l, *r;
475 int op;
476 arith_t val;
477
478 l = eval3();
479 while (1) {
480 op = nextarg("<");
481 if (!op) { op = nextarg("<=");
482 if (!op) { op = nextarg("=");
483 if (!op) { op = nextarg("==");
484 if (!op) { op = nextarg("!=");
485 if (!op) { op = nextarg(">=");
486 if (!op) { op = nextarg(">");
487 if (!op) return l;
488 }}}}}}
489 G.args++;
490 r = eval3();
491 toarith(l);
492 toarith(r);
493 val = cmp_common(l, r, op);
494 freev(l);
495 freev(r);
496 l = int_value(val);
497 }
498}
499
500
501
502static VALUE *eval1(void)
503{
504 VALUE *l, *r;
505
506 l = eval2();
507 while (nextarg("&")) {
508 G.args++;
509 r = eval2();
510 if (null(l) || null(r)) {
511 freev(l);
512 freev(r);
513 l = int_value(0);
514 } else
515 freev(r);
516 }
517 return l;
518}
519
520
521
522static VALUE *eval(void)
523{
524 VALUE *l, *r;
525
526 l = eval1();
527 while (nextarg("|")) {
528 G.args++;
529 r = eval1();
530 if (null(l)) {
531 freev(l);
532 l = r;
533 } else
534 freev(r);
535 }
536 return l;
537}
538
539int expr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
540int expr_main(int argc UNUSED_PARAM, char **argv)
541{
542 VALUE *v;
543
544 INIT_G();
545
546 xfunc_error_retval = 2;
547 G.args = argv + 1;
548 if (*G.args == NULL) {
549 bb_simple_error_msg_and_die("too few arguments");
550 }
551 v = eval();
552 if (*G.args)
553 bb_simple_error_msg_and_die("syntax error");
554 if (v->type == INTEGER)
555 printf("%" PF_REZ "d\n", PF_REZ_TYPE v->u.i);
556 else
557 puts(v->u.s);
558 fflush_stdout_and_exit(null(v));
559}
560