GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/expr/expr.c Lines: 90 218 41.3 %
Date: 2017-11-13 Branches: 38 136 27.9 %

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
	vp = malloc(sizeof(*vp));
63
	if (vp == NULL) {
64
		err(3, NULL);
65
	}
66
	vp->type = integer;
67
	vp->u.i = i;
68
	return vp;
69
}
70
71
72
struct val *
73
make_str(char *s)
74
{
75
	struct val     *vp;
76
77
1940
	vp = malloc(sizeof(*vp));
78

1940
	if (vp == NULL || ((vp->u.s = strdup(s)) == NULL)) {
79
		err(3, NULL);
80
	}
81
970
	vp->type = string;
82
970
	return vp;
83
}
84
85
86
void
87
free_value(struct val *vp)
88
{
89
970
	if (vp->type == string)
90
		free(vp->u.s);
91
485
	free(vp);
92
485
}
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
1940
	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
970
	s = vp->u.s;
113
	i = 0;
114
115
970
	neg = (*s == '-');
116
970
	if (neg)
117
		s++;
118
119
4750
	while (*s) {
120
1405
		if (!isdigit((unsigned char)*s))
121
			return 0;
122
123
1405
		i *= 10;
124
1405
		i += *s - '0';
125
126
1405
		s++;
127
	}
128
129
970
	if (neg)
130
		i *= -1;
131
132
970
	*r = i;
133
970
	return 1;
134
970
}
135
136
137
/* coerce to vp to an integer */
138
int
139
to_integer(struct val *vp)
140
{
141
1940
	int64_t		r;
142
143
970
	if (vp->type == integer)
144
		return 1;
145
146
970
	if (is_integer(vp, &r)) {
147
970
		free(vp->u.s);
148
970
		vp->u.i = r;
149
970
		vp->type = integer;
150
970
		return 1;
151
	}
152
153
	return 0;
154
970
}
155
156
157
/* coerce to vp to an string */
158
void
159
to_string(struct val *vp)
160
{
161
	char	       *tmp;
162
163
	if (vp->type == string)
164
		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
}
172
173
int
174
is_zero_or_null(struct val *vp)
175
{
176
970
	if (vp->type == integer)
177
485
		return vp->u.i == 0;
178
	else
179
		return *vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0);
180
485
}
181
182
void
183
nexttoken(int pat)
184
{
185
	char	       *p;
186
187
3880
	if ((p = *av) == NULL) {
188
485
		token = EOI;
189
485
		return;
190
	}
191
1455
	av++;
192
193
194

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

970
		} 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
970
	tokval = make_str(p);
218
970
	token = OPERAND;
219
970
	return;
220
1940
}
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
1940
	if (token == OPERAND) {
234
970
		nexttoken(0);
235
970
		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
970
}
246
247
/* Parse and evaluate match (regex) expressions */
248
struct val *
249
eval5(void)
250
{
251
1940
	regex_t		rp;
252
970
	regmatch_t	rm[2];
253
970
	char		errbuf[256];
254
	int		eval;
255
	struct val     *l, *r;
256
	struct val     *v;
257
258
970
	l = eval6();
259
1940
	while (token == MATCH) {
260
		nexttoken(1);
261
		r = eval6();
262
263
		/* coerce to both arguments to strings */
264
		to_string(l);
265
		to_string(r);
266
267
		/* compile regular expression */
268
		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
		if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
276
			if (rm[1].rm_so >= 0) {
277
				*(l->u.s + rm[1].rm_eo) = '\0';
278
				v = make_str(l->u.s + rm[1].rm_so);
279
280
			} else {
281
				v = make_int(rm[0].rm_eo - rm[0].rm_so);
282
			}
283
		} else {
284
			if (rp.re_nsub == 0) {
285
				v = make_int(0);
286
			} else {
287
				v = make_str("");
288
			}
289
		}
290
291
		/* free arguments and pattern buffer */
292
		free_value(l);
293
		free_value(r);
294
		regfree(&rp);
295
296
		l = v;
297
	}
298
299
970
	return l;
300
970
}
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
1940
	l = eval5();
310
1940
	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
970
	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
970
	l = eval4();
349
1940
	while ((op = token) == ADD || op == SUB) {
350
485
		nexttoken(0);
351
485
		r = eval4();
352
353

970
		if (!to_integer(l) || !to_integer(r)) {
354
			errx(2, "non-numeric argument");
355
		}
356
357
485
		if (op == ADD) {
358
485
			l->u.i += r->u.i;
359
		} else {
360
485
			l->u.i -= r->u.i;
361
		}
362
363
485
		free_value(r);
364
	}
365
366
485
	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
970
	int64_t		v = 0, li, ri;
376
377
485
	l = eval3();
378
1940
	while ((op = token) == EQ || op == NE || op == LT || op == GT ||
379
970
	    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
485
	return l;
440
485
}
441
442
/* Parse and evaluate & expressions */
443
struct val *
444
eval1(void)
445
{
446
	struct val     *l, *r;
447
448
970
	l = eval2();
449
970
	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
485
	return l;
463
}
464
465
/* Parse and evaluate | expressions */
466
struct val *
467
eval0(void)
468
{
469
	struct val     *l, *r;
470
471
970
	l = eval1();
472
970
	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
485
	return l;
485
}
486
487
488
int
489
main(int argc, char *argv[])
490
{
491
	struct val     *vp;
492
493
970
	if (pledge("stdio flock rpath cpath wpath", NULL) == -1)
494
		err(2, "pledge");
495
496

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