GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: bin/expr/expr.c Lines: 128 219 58.4 %
Date: 2016-12-06 Branches: 64 148 43.2 %

Line Branch Exec Source
1
/*	$OpenBSD: expr.c,v 1.25 2016/01/07 21:17:05 tedu 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 <locale.h>
15
#include <ctype.h>
16
#include <unistd.h>
17
#include <regex.h>
18
#include <err.h>
19
20
struct val	*make_int(int64_t);
21
struct val	*make_str(char *);
22
void		 free_value(struct val *);
23
int		 is_integer(struct val *, int64_t *);
24
int		 to_integer(struct val *);
25
void		 to_string(struct val *);
26
int		 is_zero_or_null(struct val *);
27
void		 nexttoken(int);
28
__dead void	 error(void);
29
struct val	*eval6(void);
30
struct val	*eval5(void);
31
struct val	*eval4(void);
32
struct val	*eval3(void);
33
struct val	*eval2(void);
34
struct val	*eval1(void);
35
struct val	*eval0(void);
36
37
enum token {
38
	OR, AND, EQ, LT, GT, ADD, SUB, MUL, DIV, MOD, MATCH, RP, LP,
39
	NE, LE, GE, OPERAND, EOI
40
};
41
42
struct val {
43
	enum {
44
		integer,
45
		string
46
	} type;
47
48
	union {
49
		char	       *s;
50
		int64_t		i;
51
	} u;
52
};
53
54
enum token	token;
55
struct val     *tokval;
56
char	      **av;
57
58
struct val *
59
make_int(int64_t i)
60
16
{
61
	struct val     *vp;
62
63
16
	vp = malloc(sizeof(*vp));
64
16
	if (vp == NULL) {
65
		err(3, NULL);
66
	}
67
16
	vp->type = integer;
68
16
	vp->u.i = i;
69
16
	return vp;
70
}
71
72
73
struct val *
74
make_str(char *s)
75
518
{
76
	struct val     *vp;
77
78
518
	vp = malloc(sizeof(*vp));
79

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

76
		return (*vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0));
181
	}
182
	/* NOTREACHED */
183
}
184
185
void
186
nexttoken(int pat)
187
896
{
188
	char	       *p;
189
190
896
	if ((p = *av) == NULL) {
191
209
		token = EOI;
192
209
		return;
193
	}
194
687
	av++;
195
196
197

687
	if (pat == 0 && p[0] != '\0') {
198
601
		if (p[1] == '\0') {
199
419
			const char     *x = "|&=<>+-*/%:()";
200
			char	       *i;	/* index */
201
202
419
			if ((i = strchr(x, *p)) != NULL) {
203
239
				token = i - x;
204
239
				return;
205
			}
206

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

86
		if (regexec(&rp, l->u.s, 2, rm, 0) == 0 && rm[0].rm_so == 0) {
286
62
			if (rm[1].rm_so >= 0) {
287
62
				*(l->u.s + rm[1].rm_eo) = '\0';
288
62
				v = make_str(l->u.s + rm[1].rm_so);
289
290
			} else {
291
				v = make_int(rm[0].rm_eo - rm[0].rm_so);
292
			}
293
		} else {
294
24
			if (rp.re_nsub == 0) {
295
16
				v = make_int(0);
296
			} else {
297
8
				v = make_str("");
298
			}
299
		}
300
301
		/* free arguments and pattern buffer */
302
86
		free_value(l);
303
86
		free_value(r);
304
86
		regfree(&rp);
305
306
86
		l = v;
307
	}
308
309
362
	return l;
310
}
311
312
/* Parse and evaluate multiplication and division expressions */
313
struct val *
314
eval4(void)
315
362
{
316
	struct val     *l, *r;
317
	enum token	op;
318
319
362
	l = eval5();
320

724
	while ((op = token) == MUL || op == DIV || op == MOD) {
321
		nexttoken(0);
322
		r = eval5();
323
324
		if (!to_integer(l) || !to_integer(r)) {
325
			errx(2, "non-numeric argument");
326
		}
327
328
		if (op == MUL) {
329
			l->u.i *= r->u.i;
330
		} else {
331
			if (r->u.i == 0) {
332
				errx(2, "division by zero");
333
			}
334
			if (op == DIV) {
335
				if (l->u.i != INT64_MIN || r->u.i != -1)
336
					l->u.i /= r->u.i;
337
			} else {
338
				if (l->u.i != INT64_MIN || r->u.i != -1)
339
					l->u.i %= r->u.i;
340
				else
341
					l->u.i = 0;
342
			}
343
		}
344
345
		free_value(r);
346
	}
347
348
362
	return l;
349
}
350
351
/* Parse and evaluate addition and subtraction expressions */
352
struct val *
353
eval3(void)
354
227
{
355
	struct val     *l, *r;
356
	enum token	op;
357
358
227
	l = eval4();
359

589
	while ((op = token) == ADD || op == SUB) {
360
135
		nexttoken(0);
361
135
		r = eval4();
362
363

135
		if (!to_integer(l) || !to_integer(r)) {
364
			errx(2, "non-numeric argument");
365
		}
366
367
135
		if (op == ADD) {
368
135
			l->u.i += r->u.i;
369
		} else {
370
			l->u.i -= r->u.i;
371
		}
372
373
135
		free_value(r);
374
	}
375
376
227
	return l;
377
}
378
379
/* Parse and evaluate comparison expressions */
380
struct val *
381
eval2(void)
382
227
{
383
	struct val     *l, *r;
384
	enum token	op;
385
227
	int64_t		v = 0, li, ri;
386
387
227
	l = eval3();
388


454
	while ((op = token) == EQ || op == NE || op == LT || op == GT ||
389
	    op == LE || op == GE) {
390
		nexttoken(0);
391
		r = eval3();
392
393
		if (is_integer(l, &li) && is_integer(r, &ri)) {
394
			switch (op) {
395
			case GT:
396
				v = (li >  ri);
397
				break;
398
			case GE:
399
				v = (li >= ri);
400
				break;
401
			case LT:
402
				v = (li <  ri);
403
				break;
404
			case LE:
405
				v = (li <= ri);
406
				break;
407
			case EQ:
408
				v = (li == ri);
409
				break;
410
			case NE:
411
				v = (li != ri);
412
				break;
413
			default:
414
				break;
415
			}
416
		} else {
417
			to_string(l);
418
			to_string(r);
419
420
			switch (op) {
421
			case GT:
422
				v = (strcoll(l->u.s, r->u.s) > 0);
423
				break;
424
			case GE:
425
				v = (strcoll(l->u.s, r->u.s) >= 0);
426
				break;
427
			case LT:
428
				v = (strcoll(l->u.s, r->u.s) < 0);
429
				break;
430
			case LE:
431
				v = (strcoll(l->u.s, r->u.s) <= 0);
432
				break;
433
			case EQ:
434
				v = (strcoll(l->u.s, r->u.s) == 0);
435
				break;
436
			case NE:
437
				v = (strcoll(l->u.s, r->u.s) != 0);
438
				break;
439
			default:
440
				break;
441
			}
442
		}
443
444
		free_value(l);
445
		free_value(r);
446
		l = make_int(v);
447
	}
448
449
227
	return l;
450
}
451
452
/* Parse and evaluate & expressions */
453
struct val *
454
eval1(void)
455
227
{
456
	struct val     *l, *r;
457
458
227
	l = eval2();
459
454
	while (token == AND) {
460
		nexttoken(0);
461
		r = eval2();
462
463
		if (is_zero_or_null(l) || is_zero_or_null(r)) {
464
			free_value(l);
465
			free_value(r);
466
			l = make_int(0);
467
		} else {
468
			free_value(r);
469
		}
470
	}
471
472
227
	return l;
473
}
474
475
/* Parse and evaluate | expressions */
476
struct val *
477
eval0(void)
478
209
{
479
	struct val     *l, *r;
480
481
209
	l = eval1();
482
436
	while (token == OR) {
483
18
		nexttoken(0);
484
18
		r = eval1();
485
486
18
		if (is_zero_or_null(l)) {
487
			free_value(l);
488
			l = r;
489
		} else {
490
18
			free_value(r);
491
		}
492
	}
493
494
209
	return l;
495
}
496
497
498
int
499
main(int argc, char *argv[])
500
209
{
501
	struct val     *vp;
502
503
209
	(void) setlocale(LC_ALL, "");
504
505
209
	if (pledge("stdio wpath cpath rpath", NULL) == -1)
506
		err(2, "pledge");
507
508

209
	if (argc > 1 && !strcmp(argv[1], "--"))
509
		argv++;
510
511
209
	av = argv + 1;
512
513
209
	nexttoken(0);
514
209
	vp = eval0();
515
516
209
	if (token != EOI) {
517
		error();
518
		/* NOTREACHED */
519
	}
520
521
209
	if (vp->type == integer)
522
151
		printf("%lld\n", vp->u.i);
523
	else
524
58
		printf("%s\n", vp->u.s);
525
526
209
	exit(is_zero_or_null(vp));
527
}