GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/grep/util.c Lines: 228 275 82.9 %
Date: 2017-11-13 Branches: 210 307 68.4 %

Line Branch Exec Source
1
/*	$OpenBSD: util.c,v 1.57 2017/04/03 16:18:35 tedu Exp $	*/
2
3
/*-
4
 * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
5
 * All rights reserved.
6
 *
7
 * Redistribution and use in source and binary forms, with or without
8
 * modification, are permitted provided that the following conditions
9
 * are met:
10
 * 1. Redistributions of source code must retain the above copyright
11
 *    notice, this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright
13
 *    notice, this list of conditions and the following disclaimer in the
14
 *    documentation and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26
 * SUCH DAMAGE.
27
 */
28
29
#include <sys/types.h>
30
#include <sys/stat.h>
31
32
#include <ctype.h>
33
#include <err.h>
34
#include <errno.h>
35
#include <fts.h>
36
#include <regex.h>
37
#include <stdbool.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
#include <unistd.h>
42
#include <zlib.h>
43
44
#include "grep.h"
45
46
/*
47
 * Process a file line by line...
48
 */
49
50
static int	linesqueued;
51
static int	procline(str_t *l, int);
52
static int	grep_search(fastgrep_t *, char *, size_t, regmatch_t *pmatch, int);
53
#ifndef SMALL
54
static bool	grep_cmp(const char *, const char *, size_t);
55
static void	grep_revstr(unsigned char *, int);
56
#endif
57
58
int
59
grep_tree(char **argv)
60
{
61
	FTS	*fts;
62
	FTSENT	*p;
63
	int	c, fts_flags;
64
65
	c = 0;
66
67
	fts_flags = FTS_PHYSICAL | FTS_NOSTAT | FTS_NOCHDIR;
68
69
	if (!(fts = fts_open(argv, fts_flags, NULL)))
70
		err(2, NULL);
71
	while ((p = fts_read(fts)) != NULL) {
72
		switch (p->fts_info) {
73
		case FTS_DNR:
74
			break;
75
		case FTS_ERR:
76
			file_err = 1;
77
			if(!sflag)
78
				warnc(p->fts_errno, "%s", p->fts_path);
79
			break;
80
		case FTS_DP:
81
			break;
82
		default:
83
			c += procfile(p->fts_path);
84
			break;
85
		}
86
	}
87
	if (errno)
88
		err(2, "fts_read");
89
	fts_close(fts);
90
	return c;
91
}
92
93
int
94
procfile(char *fn)
95
{
96
2394
	str_t ln;
97
	file_t *f;
98
	int c, t, z, nottext;
99
100
1197
	if (fn == NULL) {
101
		fn = "(standard input)";
102
955
		f = grep_fdopen(STDIN_FILENO, "r");
103
955
	} else {
104
242
		f = grep_open(fn, "r");
105
	}
106
1197
	if (f == NULL) {
107
5
		file_err = 1;
108
5
		if (!sflag)
109
			warn("%s", fn);
110
5
		return 0;
111
	}
112
113
1192
	nottext = grep_bin_file(f);
114
1192
	if (nottext && binbehave == BIN_FILE_SKIP) {
115
		grep_close(f);
116
		return 0;
117
	}
118
119
1192
	ln.file = fn;
120
1192
	ln.line_no = 0;
121
1192
	ln.len = 0;
122
1192
	linesqueued = 0;
123
1192
	tail = 0;
124
1192
	ln.off = -1;
125
126
1192
	if (Bflag > 0)
127
5
		initqueue();
128

1296948
	for (c = 0;  c == 0 || !(lflag || qflag); ) {
129
439447
		ln.off += ln.len + 1;
130
439447
		if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL)
131
			break;
132

876777
		if (ln.len > 0 && ln.dat[ln.len - 1] == '\n')
133
425787
			--ln.len;
134
438754
		ln.line_no++;
135
136
438754
		z = tail;
137
138
438754
		if ((t = procline(&ln, nottext)) == 0 && Bflag > 0 && z == 0) {
139
15
			enqueue(&ln);
140
15
			linesqueued++;
141
15
		}
142
438754
		c += t;
143
	}
144
1192
	if (Bflag > 0)
145
5
		clearqueue();
146
1192
	grep_close(f);
147
148
1192
	if (cflag) {
149
		if (!hflag)
150
			printf("%s:", ln.file);
151
		printf("%u\n", c);
152
	}
153
1192
	if (lflag && c != 0)
154
10
		printf("%s\n", fn);
155
1192
	if (Lflag && c == 0)
156
		printf("%s\n", fn);
157
3576
	if (c && !cflag && !lflag && !Lflag &&
158
2384
	    binbehave == BIN_FILE_BIN && nottext && !qflag)
159
		printf("Binary file %s matches\n", fn);
160
161
1192
	return c;
162
1197
}
163
164
165
/*
166
 * Process an individual line in a file. Return non-zero if it matches.
167
 */
168
169
#define isword(x) (isalnum((unsigned char)x) || (x) == '_')
170
171
static int
172
procline(str_t *l, int nottext)
173
{
174
877508
	regmatch_t	pmatch = { 0 };
175
	int		c, i, r;
176
	regoff_t	offset;
177
178
	/* size_t will be converted to regoff_t. ssize_t is guaranteed to fit
179
	 * into regoff_t */
180
438754
	if (l->len > SSIZE_MAX) {
181
		errx(2, "Line is too big to process");
182
	}
183
184
	c = 0;
185
	i = 0;
186
438754
	if (matchall) {
187
		c = 1;
188
25
		goto print;
189
	}
190
191
1750900
	for (i = 0; i < patterns; i++) {
192
438729
		offset = 0;
193
redo:
194
438774
		if (fg_pattern[i].pattern) {
195
			int flags = 0;
196
421668
			if (offset)
197
				flags |= REG_NOTBOL;
198
843336
			r = grep_search(&fg_pattern[i], l->dat + offset,
199
421668
			    l->len - offset, &pmatch, flags);
200
421668
			pmatch.rm_so += offset;
201
421668
			pmatch.rm_eo += offset;
202
421668
		} else {
203
17106
			int flags = eflags;
204
17106
			if (offset)
205
45
				flags |= REG_NOTBOL;
206
17106
			pmatch.rm_so = offset;
207
17106
			pmatch.rm_eo = l->len;
208
17106
			r = regexec(&r_pattern[i], l->dat, 1, &pmatch, flags);
209
		}
210
438774
		if (r == 0 && xflag) {
211

70
			if (pmatch.rm_so != 0 || pmatch.rm_eo != l->len)
212
35
				r = REG_NOMATCH;
213
		}
214
438774
		if (r == 0) {
215
			c = 1;
216

2103
			if (oflag && pmatch.rm_so != pmatch.rm_eo)
217
45
				goto print;
218
			break;
219
		}
220
	}
221
877448
	if (oflag)
222
10
		return c;
223
print:
224
438789
	if (vflag)
225
408070
		c = !c;
226
227
438789
	if (c && binbehave == BIN_FILE_BIN && nottext)
228
		return c; /* Binary file */
229
230
438789
	if ((tail > 0 || c) && !cflag && !qflag) {
231
408923
		if (c) {
232

817505
			if (first > 0 && tail == 0 && (Bflag < linesqueued) &&
233
			    (Aflag || Bflag))
234
				printf("--\n");
235
408918
			first = 1;
236
408918
			tail = Aflag;
237
408918
			if (Bflag > 0)
238
5
				printqueue();
239
408918
			linesqueued = 0;
240
408918
			printline(l, ':', oflag ? &pmatch : NULL);
241
408918
		} else {
242
5
			printline(l, '-', oflag ? &pmatch : NULL);
243
5
			tail--;
244
		}
245
	}
246
438789
	if (oflag && !matchall) {
247
45
		offset = pmatch.rm_eo;
248
45
		goto redo;
249
	}
250
438744
	return c;
251
438754
}
252
253
#ifndef SMALL
254
void
255
fgrepcomp(fastgrep_t *fg, const unsigned char *pattern)
256
{
257
	int i;
258
259
	/* Initialize. */
260
224
	fg->patternLen = strlen(pattern);
261
112
	fg->bol = 0;
262
112
	fg->eol = 0;
263
112
	fg->wmatch = wflag;
264
112
	fg->reversedSearch = 0;
265
266
	/*
267
	 * Make a copy and upper case it for later if in -i mode,
268
	 * else just copy the pointer.
269
	 */
270
112
	if (iflag) {
271
		fg->pattern = grep_malloc(fg->patternLen + 1);
272
		for (i = 0; i < fg->patternLen; i++)
273
			fg->pattern[i] = toupper(pattern[i]);
274
		fg->pattern[fg->patternLen] = '\0';
275
	} else
276
112
		fg->pattern = (unsigned char *)pattern;	/* really const */
277
278
	/* Preprocess pattern. */
279
57568
	for (i = 0; i <= UCHAR_MAX; i++)
280
28672
		fg->qsBc[i] = fg->patternLen;
281
1058
	for (i = 1; i < fg->patternLen; i++) {
282
417
		fg->qsBc[fg->pattern[i]] = fg->patternLen - i;
283
		/*
284
		 * If case is ignored, make the jump apply to both upper and
285
		 * lower cased characters.  As the pattern is stored in upper
286
		 * case, apply the same to the lower case equivalents.
287
		 */
288
417
		if (iflag)
289
			fg->qsBc[tolower(fg->pattern[i])] = fg->patternLen - i;
290
	}
291
112
}
292
#endif
293
294
/*
295
 * Returns: -1 on failure, 0 on success
296
 */
297
int
298
fastcomp(fastgrep_t *fg, const char *pattern)
299
{
300
#ifdef SMALL
301
	return -1;
302
#else
303
	int i;
304
	int bol = 0;
305
	int eol = 0;
306
	int shiftPatternLen;
307
	int hasDot = 0;
308
	int firstHalfDot = -1;
309
	int firstLastHalfDot = -1;
310
	int lastHalfDot = 0;
311
312
	/* Initialize. */
313
2024
	fg->patternLen = strlen(pattern);
314
1012
	fg->bol = 0;
315
1012
	fg->eol = 0;
316
1012
	fg->wmatch = 0;
317
1012
	fg->reversedSearch = 0;
318
319
	/* Remove end-of-line character ('$'). */
320

2019
	if (fg->patternLen > 0 && pattern[fg->patternLen - 1] == '$') {
321
		eol++;
322
24
		fg->eol = 1;
323
24
		fg->patternLen--;
324
24
	}
325
326
	/* Remove beginning-of-line character ('^'). */
327
1012
	if (pattern[0] == '^') {
328
		bol++;
329
118
		fg->bol = 1;
330
118
		fg->patternLen--;
331
118
	}
332
333
	/* Remove enclosing [[:<:]] and [[:>:]] (word match). */
334
1012
	if (wflag) {
335
		/* basic re's use \( \), extended re's ( ) */
336
158
		int extra = Eflag ? 1 : 2;
337
158
		fg->patternLen -= 14 + 2 * extra;
338
158
		fg->wmatch = 7 + extra;
339

1087
	} else if (fg->patternLen >= 14 &&
340
96
	    strncmp(pattern + fg->bol, "[[:<:]]", 7) == 0 &&
341
75
	    strncmp(pattern + fg->bol + fg->patternLen - 7, "[[:>:]]", 7) == 0) {
342
75
		fg->patternLen -= 14;
343
75
		fg->wmatch = 7;
344
75
	}
345
346
	/*
347
	 * Copy pattern minus '^' and '$' characters as well as word
348
	 * match character classes at the beginning and ending of the
349
	 * string respectively.
350
	 */
351
1012
	fg->pattern = grep_malloc(fg->patternLen + 1);
352
1012
	memcpy(fg->pattern, pattern + bol + fg->wmatch, fg->patternLen);
353
1012
	fg->pattern[fg->patternLen] = '\0';
354
355
	/* Look for ways to cheat...er...avoid the full regex engine. */
356
13450
	for (i = 0; i < fg->patternLen; i++)
357
	{
358



5964
		switch (fg->pattern[i]) {
359
		case '.':
360
			hasDot = i;
361
55
			if (i < fg->patternLen / 2) {
362
30
				if (firstHalfDot < 0)
363
					/* Closest dot to the beginning */
364
30
					firstHalfDot = i;
365
			} else {
366
				/* Closest dot to the end of the pattern. */
367
				lastHalfDot = i;
368
25
				if (firstLastHalfDot < 0)
369
25
					firstLastHalfDot = i;
370
			}
371
			break;
372
		case '(': case ')':
373
		case '{': case '}':
374
			/* Special in BRE if preceded by '\\' */
375
		case '?':
376
		case '+':
377
		case '|':
378
			/* Not special in BRE. */
379
119
			if (!Eflag)
380
				goto nonspecial;
381
		case '\\':
382
		case '*':
383
		case '[': case ']':
384
			/* Free memory and let others know this is empty. */
385
132
			free(fg->pattern);
386
132
			fg->pattern = NULL;
387
132
			return (-1);
388
		default:
389
nonspecial:
390
5658
			if (iflag)
391
91
				fg->pattern[i] = toupper(fg->pattern[i]);
392
			break;
393
		}
394
	}
395
396
	/*
397
	 * Determine if a reverse search would be faster based on the placement
398
	 * of the dots.
399
	 */
400

1765
	if ((!(lflag || cflag || oflag)) && ((!(bol || eol)) &&
401
895
	    ((lastHalfDot) && ((firstHalfDot < 0) ||
402
5
	    ((fg->patternLen - (lastHalfDot + 1)) < firstHalfDot))))) {
403
15
		fg->reversedSearch = 1;
404
15
		hasDot = fg->patternLen - (firstHalfDot < 0 ?
405
15
		    firstLastHalfDot : firstHalfDot) - 1;
406
15
		grep_revstr(fg->pattern, fg->patternLen);
407
15
	}
408
409
	/*
410
	 * Normal Quick Search would require a shift based on the position the
411
	 * next character after the comparison is within the pattern.  With
412
	 * wildcards, the position of the last dot effects the maximum shift
413
	 * distance.
414
	 * The closer to the end the wild card is the slower the search.  A
415
	 * reverse version of this algorithm would be useful for wildcards near
416
	 * the end of the string.
417
	 *
418
	 * Examples:
419
	 * Pattern	Max shift
420
	 * -------	---------
421
	 * this		5
422
	 * .his		4
423
	 * t.is		3
424
	 * th.s		2
425
	 * thi.		1
426
	 */
427
428
	/* Adjust the shift based on location of the last dot ('.'). */
429
880
	shiftPatternLen = fg->patternLen - hasDot;
430
431
	/* Preprocess pattern. */
432
452320
	for (i = 0; i <= UCHAR_MAX; i++)
433
225280
		fg->qsBc[i] = shiftPatternLen;
434
10140
	for (i = hasDot + 1; i < fg->patternLen; i++) {
435
4190
		fg->qsBc[fg->pattern[i]] = fg->patternLen - i;
436
		/*
437
		 * If case is ignored, make the jump apply to both upper and
438
		 * lower cased characters.  As the pattern is stored in upper
439
		 * case, apply the same to the lower case equivalents.
440
		 */
441
4190
		if (iflag)
442
78
			fg->qsBc[tolower(fg->pattern[i])] = fg->patternLen - i;
443
	}
444
445
	/*
446
	 * Put pattern back to normal after pre-processing to allow for easy
447
	 * comparisons later.
448
	 */
449
880
	if (fg->reversedSearch)
450
15
		grep_revstr(fg->pattern, fg->patternLen);
451
452
880
	return (0);
453
#endif
454
1012
}
455
456
/*
457
 * Word boundaries using regular expressions are defined as the point
458
 * of transition from a non-word char to a word char, or vice versa.
459
 * This means that grep -w +a and grep -w a+ never match anything,
460
 * because they lack a starting or ending transition, but grep -w a+b
461
 * does match a line containing a+b.
462
 */
463
#define wmatch(d, l, s, e)	\
464
	((s == 0 || !isword(d[s-1])) && (e == l || !isword(d[e])) && \
465
	  e > s && isword(d[s]) && isword(d[e-1]))
466
467
static int
468
grep_search(fastgrep_t *fg, char *data, size_t dataLen, regmatch_t *pmatch,
469
    int flags)
470
{
471
#ifdef SMALL
472
	return 0;
473
#else
474
	regoff_t j;
475
	int rtrnVal = REG_NOMATCH;
476
477
843336
	pmatch->rm_so = -1;
478
421668
	pmatch->rm_eo = -1;
479
480
	/* No point in going farther if we do not have enough data. */
481
421668
	if (dataLen < fg->patternLen)
482
777
		return (rtrnVal);
483
484
	/* Only try once at the beginning or ending of the line. */
485

836487
	if (fg->bol || fg->eol) {
486

10615
		if (fg->bol && (flags & REG_NOTBOL))
487
			return 0;
488
		/* Simple text comparison. */
489
		/* Verify data is >= pattern length before searching on it. */
490
5320
		if (dataLen >= fg->patternLen) {
491
			/* Determine where in data to start search at. */
492
5320
			if (fg->eol)
493
30
				j = dataLen - fg->patternLen;
494
			else
495
				j = 0;
496

10620
			if (!((fg->bol && fg->eol) && (dataLen != fg->patternLen)))
497
10640
				if (grep_cmp(fg->pattern, data + j,
498
5320
				    fg->patternLen)) {
499
212
					pmatch->rm_so = j;
500
212
					pmatch->rm_eo = j + fg->patternLen;
501






262
					if (!fg->wmatch || wmatch(data, dataLen,
502
					    pmatch->rm_so, pmatch->rm_eo))
503
212
						rtrnVal = 0;
504
				}
505
		}
506
415571
	} else if (fg->reversedSearch) {
507
		/* Quick Search algorithm. */
508
		j = dataLen;
509
165
		do {
510
790
			if (grep_cmp(fg->pattern, data + j - fg->patternLen,
511
			    fg->patternLen)) {
512
140
				pmatch->rm_so = j - fg->patternLen;
513
140
				pmatch->rm_eo = j;
514






225
				if (!fg->wmatch || wmatch(data, dataLen,
515
				    pmatch->rm_so, pmatch->rm_eo)) {
516
					rtrnVal = 0;
517
135
					break;
518
				}
519
			}
520
			/* Shift if within bounds, otherwise, we are done. */
521
655
			if (j == fg->patternLen)
522
				break;
523
635
			j -= fg->qsBc[(unsigned char)data[j - fg->patternLen - 1]];
524
635
		} while (j >= fg->patternLen);
525
	} else {
526
		/* Quick Search algorithm. */
527
		j = 0;
528
415406
		do {
529
3360422
			if (grep_cmp(fg->pattern, data + j, fg->patternLen)) {
530
1564
				pmatch->rm_so = j;
531
1564
				pmatch->rm_eo = j + fg->patternLen;
532

3128
				if (fg->patternLen == 0 || !fg->wmatch ||
533





1291
				    wmatch(data, dataLen, pmatch->rm_so,
534
				    pmatch->rm_eo)) {
535
					rtrnVal = 0;
536
1409
					break;
537
				}
538
			}
539
540
			/* Shift if within bounds, otherwise, we are done. */
541
3359013
			if (j + fg->patternLen == dataLen)
542
				break;
543
			else
544
2957588
				j += fg->qsBc[(unsigned char)data[j + fg->patternLen]];
545
2957588
		} while (j <= (dataLen - fg->patternLen));
546
	}
547
548
420891
	return (rtrnVal);
549
#endif
550
421668
}
551
552
553
void *
554
grep_malloc(size_t size)
555
{
556
	void	*ptr;
557
558
7200
	if ((ptr = malloc(size)) == NULL)
559
		err(2, "malloc");
560
3600
	return ptr;
561
}
562
563
void *
564
grep_calloc(size_t nmemb, size_t size)
565
{
566
	void	*ptr;
567
568
4516
	if ((ptr = calloc(nmemb, size)) == NULL)
569
		err(2, "calloc");
570
2258
	return ptr;
571
}
572
573
void *
574
grep_realloc(void *ptr, size_t size)
575
{
576
	if ((ptr = realloc(ptr, size)) == NULL)
577
		err(2, "realloc");
578
	return ptr;
579
}
580
581
void *
582
grep_reallocarray(void *ptr, size_t nmemb, size_t size)
583
{
584
2312
	if ((ptr = reallocarray(ptr, nmemb, size)) == NULL)
585
		err(2, "reallocarray");
586
1156
	return ptr;
587
}
588
589
#ifndef SMALL
590
/*
591
 * Returns:	true on success, false on failure
592
 */
593
static bool
594
grep_cmp(const char *pattern, const char *data, size_t len)
595
{
596
	size_t i;
597
598
10148414
	for (i = 0; i < len; i++) {
599

10276546
		if (((pattern[i] == data[i]) || (!Fflag && pattern[i] == '.'))
600

6887161
		    || (iflag && pattern[i] == toupper((unsigned char)data[i])))
601
			continue;
602
3364616
		return false;
603
	}
604
605
1916
	return true;
606
3366532
}
607
608
static void
609
grep_revstr(unsigned char *str, int len)
610
{
611
	int i;
612
	char c;
613
614
230
	for (i = 0; i < len / 2; i++) {
615
70
		c = str[i];
616
70
		str[i] = str[len - i - 1];
617
70
		str[len - i - 1] = c;
618
	}
619
30
}
620
#endif
621
622
void
623
printline(str_t *line, int sep, regmatch_t *pmatch)
624
{
625
	int n;
626
627
	n = 0;
628
817856
	if (!hflag) {
629
94
		fputs(line->file, stdout);
630
		++n;
631
94
	}
632
408928
	if (nflag) {
633
		if (n)
634
			putchar(sep);
635
		printf("%lld", line->line_no);
636
		++n;
637
	}
638
408928
	if (bflag) {
639
		if (n)
640
			putchar(sep);
641
		printf("%lld", (long long)line->off);
642
		++n;
643
	}
644
408928
	if (n)
645
188
		putchar(sep);
646
408928
	if (pmatch)
647
90
		fwrite(line->dat + pmatch->rm_so,
648
45
		    pmatch->rm_eo - pmatch->rm_so, 1, stdout);
649
	else
650
408883
		fwrite(line->dat, line->len, 1, stdout);
651
817856
	putchar('\n');
652
408928
}