GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/expr/expr.c Lines: 120 219 54.8 %
Date: 2017-11-07 Branches: 56 136 41.2 %

Line Branch Exec Source
1
/*	$OpenBSD: expr.c,v 1.26 2016/10/19 18:20:25 schwarze Exp $	*/
2
/*	$NetBSD: expr.c,v 1.3.6.1 1996/06/04 20:41:47 cgd Exp $	*/
3
4
/*
5
 * Written by J.T. Conklin <jtc@netbsd.org>.
6
 * Public domain.
7
 */
8
9
#include <stdio.h>
10
#include <stdint.h>
11
#include <stdlib.h>
12
#include <string.h>
13
#include <limits.h>
14
#include <ctype.h>
15
#include <unistd.h>
16
#include <regex.h>
17
#include <err.h>
18
19
struct val	*make_int(int64_t);
20
struct val	*make_str(char *);
21
void		 free_value(struct val *);
22
int		 is_integer(struct val *, int64_t *);
23
int		 to_integer(struct val *);
24
void		 to_string(struct val *);
25
int		 is_zero_or_null(struct val *);
26
void		 nexttoken(int);
27
__dead void	 error(void);
28
struct val	*eval6(void);
29
struct val	*eval5(void);
30
struct val	*eval4(void);
31
struct val	*eval3(void);
32
struct val	*eval2(void);
33
struct val	*eval1(void);
34
struct val	*eval0(void);
35
36
enum token {
37
	OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
38
	NE, LE, GE, OPERAND, EOI
39
};
40
41
struct val {
42
	enum {
43
		integer,
44
		string
45
	} type;
46
47
	union {
48
		char	       *s;
49
		int64_t		i;
50
	} u;
51
};
52
53
enum token	token;
54
struct val     *tokval;
55
char	      **av;
56
57
struct val *
58
make_int(int64_t i)
59
{
60
	struct val     *vp;
61
62
94
	vp = malloc(sizeof(*vp));
63
47
	if (vp == NULL) {
64
		err(3, NULL);
65
	}
66
47
	vp->type = integer;
67
47
	vp->u.i = i;
68
47
	return vp;
69
}
70
71
72
struct val *
73
make_str(char *s)
74
{
75
	struct val     *vp;
76
77
4958
	vp = malloc(sizeof(*vp));
78

4958
	if (vp == NULL || ((vp->u.s = strdup(s)) == NULL)) {
79
		err(3, NULL);
80
	}
81
2479
	vp->type = string;
82
2479
	return vp;
83
}
84
85
86
void
87
free_value(struct val *vp)
88
{
89
2750
	if (vp->type == string)
90
448
		free(vp->u.s);
91
1375
	free(vp);
92
1375
}
93
94
95
/* determine if vp is an integer; if so, return it's value in *r */
96
int
97
is_integer(struct val *vp, int64_t *r)
98
{
99
	char	       *s;
100
	int		neg;
101
	int64_t		i;
102
103
4062
	if (vp->type == integer) {
104
		*r = vp->u.i;
105
		return 1;
106
	}
107
108
	/*
109
	 * POSIX.2 defines an "integer" as an optional unary minus
110
	 * followed by digits.
111
	 */
112
2031
	s = vp->u.s;
113
	i = 0;
114
115
2031
	neg = (*s == '-');
116
2031
	if (neg)
117
		s++;
118
119
7639
	while (*s) {
120
2941
		if (!isdigit((unsigned char)*s))
121
137
			return 0;
122
123
2804
		i *= 10;
124
2804
		i += *s - '0';
125
126
2804
		s++;
127
	}
128
129
1894
	if (neg)
130
		i *= -1;
131
132
1894
	*r = i;
133
1894
	return 1;
134
2031
}
135
136
137
/* coerce to vp to an integer */
138
int
139
to_integer(struct val *vp)
140
{
141
4062
	int64_t		r;
142
143
2031
	if (vp->type == integer)
144
		return 1;
145
146
2031
	if (is_integer(vp, &r)) {
147
1894
		free(vp->u.s);
148
1894
		vp->u.i = r;
149
1894
		vp->type = integer;
150
1894
		return 1;
151
	}
152
153
137
	return 0;
154
2031
}
155
156
157
/* coerce to vp to an string */
158
void
159
to_string(struct val *vp)
160
{
161
896
	char	       *tmp;
162
163
448
	if (vp->type == string)
164
448
		return;
165
166
	if (asprintf(&tmp, "%lld", vp->u.i) == -1)
167
		err(3, NULL);
168
169
	vp->type = string;
170
	vp->u.s = tmp;
171
448
}
172
173
int
174
is_zero_or_null(struct val *vp)
175
{
176
2302
	if (vp->type == integer)
177
974
		return vp->u.i == 0;
178
	else
179

571
		return *vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0);
180
1151
}
181
182
void
183
nexttoken(int pat)
184
{
185
	char	       *p;
186
187
9208
	if ((p = *av) == NULL) {
188
1151
		token = EOI;
189
1151
		return;
190
	}
191
3453
	av++;
192
193
194

6682
	if (pat == 0 && p[0] != '\0') {
195
3229
		if (p[1] == '\0') {
196
			const char     *x = "|&=<>+-*/%:()";
197
			char	       *i;	/* index */
198
199
2234
			if ((i = strchr(x, *p)) != NULL) {
200
1151
				token = i - x;
201
1151
				return;
202
			}
203

2078
		} else if (p[1] == '=' && p[2] == '\0') {
204
			switch (*p) {
205
			case '<':
206
				token = LE;
207
				return;
208
			case '>':
209
				token = GE;
210
				return;
211
			case '!':
212
				token = NE;
213
				return;
214
			}
215
		}
216
	}
217
2302
	tokval = make_str(p);
218
2302
	token = OPERAND;
219
2302
	return;
220
4604
}
221
222
__dead void
223
error(void)
224
{
225
	errx(2, "syntax error");
226
}
227
228
struct val *
229
eval6(void)
230
{
231
	struct val     *v;
232
233
4604
	if (token == OPERAND) {
234
2302
		nexttoken(0);
235
2302
		return tokval;
236
	} else if (token == RP) {
237
		nexttoken(0);
238
		v = eval0();
239
		if (token != LP)
240
			error();
241
		nexttoken(0);
242
		return v;
243
	} else
244
		error();
245
2302
}
246
247
/* Parse and evaluate match (regex) expressions */
248
struct val *
249
eval5(void)
250
{
251
4156
	regex_t		rp;
252
2078
	regmatch_t	rm[2];
253
2078
	char		errbuf[256];
254
	int		eval;
255
	struct val     *l, *r;
256
	struct val     *v;
257
258
2078
	l = eval6();
259
4604
	while (token == MATCH) {
260
224
		nexttoken(1);
261
224
		r = eval6();
262
263
		/* coerce to both arguments to strings */
264
224
		to_string(l);
265
224
		to_string(r);
266
267
		/* compile regular expression */
268
224
		if ((eval = regcomp(&rp, r->u.s, 0)) != 0) {
269
			regerror(eval, &rp, errbuf, sizeof(errbuf));
270
			errx(2, "%s", errbuf);
271
		}
272
273
		/* compare string against pattern --  remember that patterns
274
		   are anchored to the beginning of the line */
275

401
		if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
276
177
			if (rm[1].rm_so >= 0) {
277
177
				*(l->u.s + rm[1].rm_eo) = '\0';
278
177
				v = make_str(l->u.s + rm[1].rm_so);
279
280
177
			} else {
281
				v = make_int(rm[0].rm_eo - rm[0].rm_so);
282
			}
283
		} else {
284
47
			if (rp.re_nsub == 0) {
285
47
				v = make_int(0);
286
47
			} else {
287
				v = make_str("");
288
			}
289
		}
290
291
		/* free arguments and pattern buffer */
292
224
		free_value(l);
293
224
		free_value(r);
294
224
		regfree(&rp);
295
296
		l = v;
297
	}
298
299
2078
	return l;
300
2078
}
301
302
/* Parse and evaluate multiplication and division expressions */
303
struct val *
304
eval4(void)
305
{
306
	struct val     *l, *r;
307
	enum token	op;
308
309
4156
	l = eval5();
310
4156
	while ((op = token) == MUL || op == DIV || op == MOD) {
311
		nexttoken(0);
312
		r = eval5();
313
314
		if (!to_integer(l) || !to_integer(r)) {
315
			errx(2, "non-numeric argument");
316
		}
317
318
		if (op == MUL) {
319
			l->u.i *= r->u.i;
320
		} else {
321
			if (r->u.i == 0) {
322
				errx(2, "division by zero");
323
			}
324
			if (op == DIV) {
325
				if (l->u.i != INT64_MIN || r->u.i != -1)
326
					l->u.i /= r->u.i;
327
			} else {
328
				if (l->u.i != INT64_MIN || r->u.i != -1)
329
					l->u.i %= r->u.i;
330
				else
331
					l->u.i = 0;
332
			}
333
		}
334
335
		free_value(r);
336
	}
337
338
2078
	return l;
339
}
340
341
/* Parse and evaluate addition and subtraction expressions */
342
struct val *
343
eval3(void)
344
{
345
	struct val     *l, *r;
346
	enum token	op;
347
348
2302
	l = eval4();
349
4156
	while ((op = token) == ADD || op == SUB) {
350
927
		nexttoken(0);
351
927
		r = eval4();
352
353

1854
		if (!to_integer(l) || !to_integer(r)) {
354
			errx(2, "non-numeric argument");
355
		}
356
357
927
		if (op == ADD) {
358
927
			l->u.i += r->u.i;
359
		} else {
360
927
			l->u.i -= r->u.i;
361
		}
362
363
927
		free_value(r);
364
	}
365
366
1151
	return l;
367
}
368
369
/* Parse and evaluate comparison expressions */
370
struct val *
371
eval2(void)
372
{
373
	struct val     *l, *r;
374
	enum token	op;
375
2302
	int64_t		v = 0, li, ri;
376
377
1151
	l = eval3();
378
4604
	while ((op = token) == EQ || op == NE || op == LT || op == GT ||
379
2302
	    op == LE || op == GE) {
380
		nexttoken(0);
381
		r = eval3();
382
383
		if (is_integer(l, &li) && is_integer(r, &ri)) {
384
			switch (op) {
385
			case GT:
386
				v = (li >  ri);
387
				break;
388
			case GE:
389
				v = (li >= ri);
390
				break;
391
			case LT:
392
				v = (li <  ri);
393
				break;
394
			case LE:
395
				v = (li <= ri);
396
				break;
397
			case EQ:
398
				v = (li == ri);
399
				break;
400
			case NE:
401
				v = (li != ri);
402
				break;
403
			default:
404
				break;
405
			}
406
		} else {
407
			to_string(l);
408
			to_string(r);
409
410
			switch (op) {
411
			case GT:
412
				v = (strcoll(l->u.s, r->u.s) > 0);
413
				break;
414
			case GE:
415
				v = (strcoll(l->u.s, r->u.s) >= 0);
416
				break;
417
			case LT:
418
				v = (strcoll(l->u.s, r->u.s) < 0);
419
				break;
420
			case LE:
421
				v = (strcoll(l->u.s, r->u.s) <= 0);
422
				break;
423
			case EQ:
424
				v = (strcoll(l->u.s, r->u.s) == 0);
425
				break;
426
			case NE:
427
				v = (strcoll(l->u.s, r->u.s) != 0);
428
				break;
429
			default:
430
				break;
431
			}
432
		}
433
434
		free_value(l);
435
		free_value(r);
436
		l = make_int(v);
437
	}
438
439
1151
	return l;
440
1151
}
441
442
/* Parse and evaluate & expressions */
443
struct val *
444
eval1(void)
445
{
446
	struct val     *l, *r;
447
448
2302
	l = eval2();
449
2302
	while (token == AND) {
450
		nexttoken(0);
451
		r = eval2();
452
453
		if (is_zero_or_null(l) || is_zero_or_null(r)) {
454
			free_value(l);
455
			free_value(r);
456
			l = make_int(0);
457
		} else {
458
			free_value(r);
459
		}
460
	}
461
462
1151
	return l;
463
}
464
465
/* Parse and evaluate | expressions */
466
struct val *
467
eval0(void)
468
{
469
	struct val     *l, *r;
470
471
2302
	l = eval1();
472
2302
	while (token == OR) {
473
		nexttoken(0);
474
		r = eval1();
475
476
		if (is_zero_or_null(l)) {
477
			free_value(l);
478
			l = r;
479
		} else {
480
			free_value(r);
481
		}
482
	}
483
484
1151
	return l;
485
}
486
487
488
int
489
main(int argc, char *argv[])
490
{
491
	struct val     *vp;
492
493
2302
	if (pledge("stdio flock rpath cpath wpath", NULL) == -1)
494
		err(2, "pledge");
495
496

2302
	if (argc > 1 && !strcmp(argv[1], "--"))
497
		argv++;
498
499
1151
	av = argv + 1;
500
501
1151
	nexttoken(0);
502
1151
	vp = eval0();
503
504
1151
	if (token != EOI)
505
		error();
506
507
1151
	if (vp->type == integer)
508
974
		printf("%lld\n", vp->u.i);
509
	else
510
177
		printf("%s\n", vp->u.s);
511
512
1151
	return is_zero_or_null(vp);
513
}