summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorElliott Hughes2018-07-26 16:43:31 -0500
committerElliott Hughes2018-07-26 16:52:45 -0500
commitf9408d5cdff0bf6b0cce47847ac6780fb235449b (patch)
tree1652b150fa8891631457d1cd4126259936a6fc67 /toolbox
parent46f281edf5e78a51c5c1765460cddcf805e88d48 (diff)
downloadplatform-system-core-f9408d5cdff0bf6b0cce47847ac6780fb235449b.tar.gz
platform-system-core-f9408d5cdff0bf6b0cce47847ac6780fb235449b.tar.xz
platform-system-core-f9408d5cdff0bf6b0cce47847ac6780fb235449b.zip
Revert "Switch to PCRE grep."
This reverts commit 90a018a87a605de6ad3550861143f1a5f7917570. Not a clean revert because things have changed slightly, and I'm sticking with the egrep/fgrep shell scripts for now. Bug: http://b/111849261 Bug: https://bugs.exim.org/show_bug.cgi?id=2294 Test: manual Change-Id: I72ae637c84f0eb1c2b5291db73ebff1628d54110
Diffstat (limited to 'toolbox')
-rw-r--r--toolbox/Android.bp17
-rw-r--r--toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c336
-rw-r--r--toolbox/upstream-netbsd/usr.bin/grep/file.c271
-rw-r--r--toolbox/upstream-netbsd/usr.bin/grep/grep.c707
-rw-r--r--toolbox/upstream-netbsd/usr.bin/grep/grep.h162
-rw-r--r--toolbox/upstream-netbsd/usr.bin/grep/queue.c116
-rw-r--r--toolbox/upstream-netbsd/usr.bin/grep/util.c499
7 files changed, 2108 insertions, 0 deletions
diff --git a/toolbox/Android.bp b/toolbox/Android.bp
index fc5170537..e75e4afcc 100644
--- a/toolbox/Android.bp
+++ b/toolbox/Android.bp
@@ -60,3 +60,20 @@ cc_binary {
60 defaults: ["toolbox_defaults"], 60 defaults: ["toolbox_defaults"],
61 srcs: ["r.c"], 61 srcs: ["r.c"],
62} 62}
63
64// We build BSD grep separately (but see http://b/111849261).
65cc_binary {
66 name: "grep",
67 defaults: ["toolbox_defaults"],
68 srcs: [
69 "upstream-netbsd/usr.bin/grep/fastgrep.c",
70 "upstream-netbsd/usr.bin/grep/file.c",
71 "upstream-netbsd/usr.bin/grep/grep.c",
72 "upstream-netbsd/usr.bin/grep/queue.c",
73 "upstream-netbsd/usr.bin/grep/util.c",
74 ],
75
76 sanitize: {
77 integer_overflow: false,
78 },
79}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
new file mode 100644
index 000000000..2fcd864e2
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/fastgrep.c
@@ -0,0 +1,336 @@
1/* $OpenBSD: util.c,v 1.36 2007/10/02 17:59:18 otto Exp $ */
2/* $FreeBSD: head/usr.bin/grep/fastgrep.c 211496 2010-08-19 09:28:59Z des $ */
3
4/*-
5 * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
6 * Copyright (C) 2008 Gabor Kovesdan <gabor@FreeBSD.org>
7 * All rights reserved.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 * SUCH DAMAGE.
29 */
30
31/*
32 * XXX: This file is a speed up for grep to cover the defects of the
33 * regex library. These optimizations should practically be implemented
34 * there keeping this code clean. This is a future TODO, but for the
35 * meantime, we need to use this workaround.
36 */
37
38#if HAVE_NBTOOL_CONFIG_H
39#include "nbtool_config.h"
40#endif
41
42#include <sys/cdefs.h>
43__RCSID("$NetBSD: fastgrep.c,v 1.5 2011/04/18 03:27:40 joerg Exp $");
44
45#include <limits.h>
46#include <stdbool.h>
47#include <stdlib.h>
48#include <string.h>
49#include <wchar.h>
50#include <wctype.h>
51
52#include "grep.h"
53
54static inline int grep_cmp(const unsigned char *, const unsigned char *, size_t);
55static inline void grep_revstr(unsigned char *, int);
56
57void
58fgrepcomp(fastgrep_t *fg, const char *pat)
59{
60 unsigned int i;
61
62 /* Initialize. */
63 fg->len = strlen(pat);
64 fg->bol = false;
65 fg->eol = false;
66 fg->reversed = false;
67
68 fg->pattern = (unsigned char *)grep_strdup(pat);
69
70 /* Preprocess pattern. */
71 for (i = 0; i <= UCHAR_MAX; i++)
72 fg->qsBc[i] = fg->len;
73 for (i = 1; i < fg->len; i++)
74 fg->qsBc[fg->pattern[i]] = fg->len - i;
75}
76
77/*
78 * Returns: -1 on failure, 0 on success
79 */
80int
81fastcomp(fastgrep_t *fg, const char *pat)
82{
83 unsigned int i;
84 int firstHalfDot = -1;
85 int firstLastHalfDot = -1;
86 int hasDot = 0;
87 int lastHalfDot = 0;
88 int shiftPatternLen;
89
90 /* Initialize. */
91 fg->len = strlen(pat);
92 fg->bol = false;
93 fg->eol = false;
94 fg->reversed = false;
95 fg->word = wflag;
96
97 /* Remove end-of-line character ('$'). */
98 if (fg->len > 0 && pat[fg->len - 1] == '$') {
99 fg->eol = true;
100 fg->len--;
101 }
102
103 /* Remove beginning-of-line character ('^'). */
104 if (pat[0] == '^') {
105 fg->bol = true;
106 fg->len--;
107 pat++;
108 }
109
110 if (fg->len >= 14 &&
111 memcmp(pat, "[[:<:]]", 7) == 0 &&
112 memcmp(pat + fg->len - 7, "[[:>:]]", 7) == 0) {
113 fg->len -= 14;
114 pat += 7;
115 /* Word boundary is handled separately in util.c */
116 fg->word = true;
117 }
118
119 /*
120 * pat has been adjusted earlier to not include '^', '$' or
121 * the word match character classes at the beginning and ending
122 * of the string respectively.
123 */
124 fg->pattern = grep_malloc(fg->len + 1);
125 memcpy(fg->pattern, pat, fg->len);
126 fg->pattern[fg->len] = '\0';
127
128 /* Look for ways to cheat...er...avoid the full regex engine. */
129 for (i = 0; i < fg->len; i++) {
130 /* Can still cheat? */
131 if (fg->pattern[i] == '.') {
132 hasDot = i;
133 if (i < fg->len / 2) {
134 if (firstHalfDot < 0)
135 /* Closest dot to the beginning */
136 firstHalfDot = i;
137 } else {
138 /* Closest dot to the end of the pattern. */
139 lastHalfDot = i;
140 if (firstLastHalfDot < 0)
141 firstLastHalfDot = i;
142 }
143 } else {
144 /* Free memory and let others know this is empty. */
145 free(fg->pattern);
146 fg->pattern = NULL;
147 return (-1);
148 }
149 }
150
151 /*
152 * Determine if a reverse search would be faster based on the placement
153 * of the dots.
154 */
155 if ((!(lflag || cflag)) && ((!(fg->bol || fg->eol)) &&
156 ((lastHalfDot) && ((firstHalfDot < 0) ||
157 ((fg->len - (lastHalfDot + 1)) < (size_t)firstHalfDot)))) &&
158 !oflag && !color) {
159 fg->reversed = true;
160 hasDot = fg->len - (firstHalfDot < 0 ?
161 firstLastHalfDot : firstHalfDot) - 1;
162 grep_revstr(fg->pattern, fg->len);
163 }
164
165 /*
166 * Normal Quick Search would require a shift based on the position the
167 * next character after the comparison is within the pattern. With
168 * wildcards, the position of the last dot effects the maximum shift
169 * distance.
170 * The closer to the end the wild card is the slower the search. A
171 * reverse version of this algorithm would be useful for wildcards near
172 * the end of the string.
173 *
174 * Examples:
175 * Pattern Max shift
176 * ------- ---------
177 * this 5
178 * .his 4
179 * t.is 3
180 * th.s 2
181 * thi. 1
182 */
183
184 /* Adjust the shift based on location of the last dot ('.'). */
185 shiftPatternLen = fg->len - hasDot;
186
187 /* Preprocess pattern. */
188 for (i = 0; i <= (signed)UCHAR_MAX; i++)
189 fg->qsBc[i] = shiftPatternLen;
190 for (i = hasDot + 1; i < fg->len; i++) {
191 fg->qsBc[fg->pattern[i]] = fg->len - i;
192 }
193
194 /*
195 * Put pattern back to normal after pre-processing to allow for easy
196 * comparisons later.
197 */
198 if (fg->reversed)
199 grep_revstr(fg->pattern, fg->len);
200
201 return (0);
202}
203
204int
205grep_search(fastgrep_t *fg, const unsigned char *data, size_t len, regmatch_t *pmatch)
206{
207 unsigned int j;
208 int ret = REG_NOMATCH;
209
210 if (pmatch->rm_so == (ssize_t)len)
211 return (ret);
212
213 if (fg->bol && pmatch->rm_so != 0) {
214 pmatch->rm_so = len;
215 pmatch->rm_eo = len;
216 return (ret);
217 }
218
219 /* No point in going farther if we do not have enough data. */
220 if (len < fg->len)
221 return (ret);
222
223 /* Only try once at the beginning or ending of the line. */
224 if (fg->bol || fg->eol) {
225 /* Simple text comparison. */
226 /* Verify data is >= pattern length before searching on it. */
227 if (len >= fg->len) {
228 /* Determine where in data to start search at. */
229 j = fg->eol ? len - fg->len : 0;
230 if (!((fg->bol && fg->eol) && (len != fg->len)))
231 if (grep_cmp(fg->pattern, data + j,
232 fg->len) == -1) {
233 pmatch->rm_so = j;
234 pmatch->rm_eo = j + fg->len;
235 ret = 0;
236 }
237 }
238 } else if (fg->reversed) {
239 /* Quick Search algorithm. */
240 j = len;
241 do {
242 if (grep_cmp(fg->pattern, data + j - fg->len,
243 fg->len) == -1) {
244 pmatch->rm_so = j - fg->len;
245 pmatch->rm_eo = j;
246 ret = 0;
247 break;
248 }
249 /* Shift if within bounds, otherwise, we are done. */
250 if (j == fg->len)
251 break;
252 j -= fg->qsBc[data[j - fg->len - 1]];
253 } while (j >= fg->len);
254 } else {
255 /* Quick Search algorithm. */
256 j = pmatch->rm_so;
257 do {
258 if (grep_cmp(fg->pattern, data + j, fg->len) == -1) {
259 pmatch->rm_so = j;
260 pmatch->rm_eo = j + fg->len;
261 ret = 0;
262 break;
263 }
264
265 /* Shift if within bounds, otherwise, we are done. */
266 if (j + fg->len == len)
267 break;
268 else
269 j += fg->qsBc[data[j + fg->len]];
270 } while (j <= (len - fg->len));
271 }
272
273 return (ret);
274}
275
276/*
277 * Returns: i >= 0 on failure (position that it failed)
278 * -1 on success
279 */
280static inline int
281grep_cmp(const unsigned char *pat, const unsigned char *data, size_t len)
282{
283 size_t size;
284 wchar_t *wdata, *wpat;
285 unsigned int i;
286
287 if (iflag) {
288 if ((size = mbstowcs(NULL, (const char *)data, 0)) ==
289 ((size_t) - 1))
290 return (-1);
291
292 wdata = grep_malloc(size * sizeof(wint_t));
293
294 if (mbstowcs(wdata, (const char *)data, size) ==
295 ((size_t) - 1))
296 return (-1);
297
298 if ((size = mbstowcs(NULL, (const char *)pat, 0)) ==
299 ((size_t) - 1))
300 return (-1);
301
302 wpat = grep_malloc(size * sizeof(wint_t));
303
304 if (mbstowcs(wpat, (const char *)pat, size) == ((size_t) - 1))
305 return (-1);
306 for (i = 0; i < len; i++) {
307 if ((towlower(wpat[i]) == towlower(wdata[i])) ||
308 ((grepbehave != GREP_FIXED) && wpat[i] == L'.'))
309 continue;
310 free(wpat);
311 free(wdata);
312 return (i);
313 }
314 } else {
315 for (i = 0; i < len; i++) {
316 if ((pat[i] == data[i]) || ((grepbehave != GREP_FIXED) &&
317 pat[i] == '.'))
318 continue;
319 return (i);
320 }
321 }
322 return (-1);
323}
324
325static inline void
326grep_revstr(unsigned char *str, int len)
327{
328 int i;
329 char c;
330
331 for (i = 0; i < len / 2; i++) {
332 c = str[i];
333 str[i] = str[len - i - 1];
334 str[len - i - 1] = c;
335 }
336}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/file.c b/toolbox/upstream-netbsd/usr.bin/grep/file.c
new file mode 100644
index 000000000..cf4a0fa4e
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/file.c
@@ -0,0 +1,271 @@
1/* $NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $ */
2/* $FreeBSD: head/usr.bin/grep/file.c 211496 2010-08-19 09:28:59Z des $ */
3/* $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $ */
4
5/*-
6 * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
7 * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
8 * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
9 * All rights reserved.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 * notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 * notice, this list of conditions and the following disclaimer in the
18 * documentation and/or other materials provided with the distribution.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if HAVE_NBTOOL_CONFIG_H
34#include "nbtool_config.h"
35#endif
36
37#include <sys/cdefs.h>
38__RCSID("$NetBSD: file.c,v 1.7 2011/04/18 22:46:48 joerg Exp $");
39
40#include <sys/param.h>
41#include <sys/types.h>
42#include <sys/stat.h>
43
44#ifndef __ANDROID__
45#include <bzlib.h>
46#endif
47#include <err.h>
48#include <errno.h>
49#include <fcntl.h>
50#include <stddef.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54#include <wchar.h>
55#include <wctype.h>
56#ifndef __ANDROID__
57#include <zlib.h>
58#endif
59
60#include "grep.h"
61
62#define MAXBUFSIZ (32 * 1024)
63#define LNBUFBUMP 80
64
65#ifndef __ANDROID__
66static gzFile gzbufdesc;
67static BZFILE* bzbufdesc;
68#endif
69
70static unsigned char buffer[MAXBUFSIZ];
71static unsigned char *bufpos;
72static size_t bufrem;
73
74static unsigned char *lnbuf;
75static size_t lnbuflen;
76
77static inline int
78grep_refill(struct file *f)
79{
80 ssize_t nr;
81#ifndef __ANDROID__
82 int bzerr;
83#endif
84
85 bufpos = buffer;
86 bufrem = 0;
87
88#ifndef __ANDROID__
89 if (filebehave == FILE_GZIP)
90 nr = gzread(gzbufdesc, buffer, MAXBUFSIZ);
91 else if (filebehave == FILE_BZIP && bzbufdesc != NULL) {
92 nr = BZ2_bzRead(&bzerr, bzbufdesc, buffer, MAXBUFSIZ);
93 switch (bzerr) {
94 case BZ_OK:
95 case BZ_STREAM_END:
96 /* No problem, nr will be okay */
97 break;
98 case BZ_DATA_ERROR_MAGIC:
99 /*
100 * As opposed to gzread(), which simply returns the
101 * plain file data, if it is not in the correct
102 * compressed format, BZ2_bzRead() instead aborts.
103 *
104 * So, just restart at the beginning of the file again,
105 * and use plain reads from now on.
106 */
107 BZ2_bzReadClose(&bzerr, bzbufdesc);
108 bzbufdesc = NULL;
109 if (lseek(f->fd, 0, SEEK_SET) == -1)
110 return (-1);
111 nr = read(f->fd, buffer, MAXBUFSIZ);
112 break;
113 default:
114 /* Make sure we exit with an error */
115 nr = -1;
116 }
117 } else
118#endif
119 nr = read(f->fd, buffer, MAXBUFSIZ);
120
121 if (nr < 0)
122 return (-1);
123
124 bufrem = nr;
125 return (0);
126}
127
128static inline int
129grep_lnbufgrow(size_t newlen)
130{
131
132 if (lnbuflen < newlen) {
133 lnbuf = grep_realloc(lnbuf, newlen);
134 lnbuflen = newlen;
135 }
136
137 return (0);
138}
139
140char *
141grep_fgetln(struct file *f, size_t *lenp)
142{
143 unsigned char *p;
144 char *ret;
145 size_t len;
146 size_t off;
147 ptrdiff_t diff;
148
149 /* Fill the buffer, if necessary */
150 if (bufrem == 0 && grep_refill(f) != 0)
151 goto error;
152
153 if (bufrem == 0) {
154 /* Return zero length to indicate EOF */
155 *lenp = 0;
156 return ((char *)bufpos);
157 }
158
159 /* Look for a newline in the remaining part of the buffer */
160 if ((p = memchr(bufpos, line_sep, bufrem)) != NULL) {
161 ++p; /* advance over newline */
162 ret = (char *)bufpos;
163 len = p - bufpos;
164 bufrem -= len;
165 bufpos = p;
166 *lenp = len;
167 return (ret);
168 }
169
170 /* We have to copy the current buffered data to the line buffer */
171 for (len = bufrem, off = 0; ; len += bufrem) {
172 /* Make sure there is room for more data */
173 if (grep_lnbufgrow(len + LNBUFBUMP))
174 goto error;
175 memcpy(lnbuf + off, bufpos, len - off);
176 off = len;
177 if (grep_refill(f) != 0)
178 goto error;
179 if (bufrem == 0)
180 /* EOF: return partial line */
181 break;
182 if ((p = memchr(bufpos, line_sep, bufrem)) == NULL)
183 continue;
184 /* got it: finish up the line (like code above) */
185 ++p;
186 diff = p - bufpos;
187 len += diff;
188 if (grep_lnbufgrow(len))
189 goto error;
190 memcpy(lnbuf + off, bufpos, diff);
191 bufrem -= diff;
192 bufpos = p;
193 break;
194 }
195 *lenp = len;
196 return ((char *)lnbuf);
197
198error:
199 *lenp = 0;
200 return (NULL);
201}
202
203static inline struct file *
204grep_file_init(struct file *f)
205{
206
207#ifndef __ANDROID__
208 if (filebehave == FILE_GZIP &&
209 (gzbufdesc = gzdopen(f->fd, "r")) == NULL)
210 goto error;
211
212 if (filebehave == FILE_BZIP &&
213 (bzbufdesc = BZ2_bzdopen(f->fd, "r")) == NULL)
214 goto error;
215#endif
216
217 /* Fill read buffer, also catches errors early */
218 if (grep_refill(f) != 0)
219 goto error;
220
221 /* Check for binary stuff, if necessary */
222 if (!nulldataflag && binbehave != BINFILE_TEXT &&
223 memchr(bufpos, '\0', bufrem) != NULL)
224 f->binary = true;
225
226 return (f);
227error:
228 close(f->fd);
229 free(f);
230 return (NULL);
231}
232
233/*
234 * Opens a file for processing.
235 */
236struct file *
237grep_open(const char *path)
238{
239 struct file *f;
240
241 f = grep_malloc(sizeof *f);
242 memset(f, 0, sizeof *f);
243 if (path == NULL) {
244 /* Processing stdin implies --line-buffered. */
245 lbflag = true;
246 f->fd = STDIN_FILENO;
247 } else if ((f->fd = open(path, O_RDONLY)) == -1) {
248 free(f);
249 return (NULL);
250 }
251
252 return (grep_file_init(f));
253}
254
255/*
256 * Closes a file.
257 */
258void
259grep_close(struct file *f)
260{
261
262 close(f->fd);
263
264 /* Reset read buffer and line buffer */
265 bufpos = buffer;
266 bufrem = 0;
267
268 free(lnbuf);
269 lnbuf = NULL;
270 lnbuflen = 0;
271}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.c b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
new file mode 100644
index 000000000..1ea6ed3aa
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.c
@@ -0,0 +1,707 @@
1/* $NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $ */
2/* $FreeBSD: head/usr.bin/grep/grep.c 211519 2010-08-19 22:55:17Z delphij $ */
3/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
4
5/*-
6 * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
7 * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37__RCSID("$NetBSD: grep.c,v 1.12 2014/07/11 16:30:45 christos Exp $");
38
39#include <sys/stat.h>
40#include <sys/types.h>
41
42#include <ctype.h>
43#include <err.h>
44#include <errno.h>
45#include <getopt.h>
46#include <limits.h>
47#include <libgen.h>
48#include <locale.h>
49#include <stdbool.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <string.h>
53#include <unistd.h>
54
55#include "grep.h"
56
57#ifndef WITHOUT_NLS
58#include <nl_types.h>
59nl_catd catalog;
60#endif
61
62/*
63 * Default messags to use when NLS is disabled or no catalogue
64 * is found.
65 */
66const char *errstr[] = {
67 "",
68/* 1*/ "(standard input)",
69/* 2*/ "cannot read bzip2 compressed file",
70/* 3*/ "unknown %s option",
71/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
72/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
73/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
74/* 7*/ "\t[pattern] [file ...]\n",
75/* 8*/ "Binary file %s matches\n",
76/* 9*/ "%s (BSD grep) %s\n",
77};
78
79/* Flags passed to regcomp() and regexec() */
80int cflags = 0;
81int eflags = REG_STARTEND;
82
83/* Searching patterns */
84unsigned int patterns, pattern_sz;
85char **pattern;
86regex_t *r_pattern;
87fastgrep_t *fg_pattern;
88
89/* Filename exclusion/inclusion patterns */
90unsigned int fpatterns, fpattern_sz;
91unsigned int dpatterns, dpattern_sz;
92struct epat *dpattern, *fpattern;
93
94/* For regex errors */
95char re_error[RE_ERROR_BUF + 1];
96
97/* Command-line flags */
98unsigned long long Aflag; /* -A x: print x lines trailing each match */
99unsigned long long Bflag; /* -B x: print x lines leading each match */
100bool Hflag; /* -H: always print file name */
101bool Lflag; /* -L: only show names of files with no matches */
102bool bflag; /* -b: show block numbers for each match */
103bool cflag; /* -c: only show a count of matching lines */
104bool hflag; /* -h: don't print filename headers */
105bool iflag; /* -i: ignore case */
106bool lflag; /* -l: only show names of files with matches */
107bool mflag; /* -m x: stop reading the files after x matches */
108unsigned long long mcount; /* count for -m */
109bool nflag; /* -n: show line numbers in front of matching lines */
110bool oflag; /* -o: print only matching part */
111bool qflag; /* -q: quiet mode (don't output anything) */
112bool sflag; /* -s: silent mode (ignore errors) */
113bool vflag; /* -v: only show non-matching lines */
114bool wflag; /* -w: pattern must start and end on word boundaries */
115bool xflag; /* -x: pattern must match entire line */
116bool lbflag; /* --line-buffered */
117bool nullflag; /* --null */
118bool nulldataflag; /* --null-data */
119unsigned char line_sep = '\n'; /* 0 for --null-data */
120char *label; /* --label */
121const char *color; /* --color */
122int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
123int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
124int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
125int devbehave = DEV_READ; /* -D: handling of devices */
126int dirbehave = DIR_READ; /* -dRr: handling of directories */
127int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
128
129bool dexclude, dinclude; /* --exclude-dir and --include-dir */
130bool fexclude, finclude; /* --exclude and --include */
131
132enum {
133 BIN_OPT = CHAR_MAX + 1,
134 COLOR_OPT,
135 DECOMPRESS_OPT,
136 HELP_OPT,
137 MMAP_OPT,
138 LINEBUF_OPT,
139 LABEL_OPT,
140 R_EXCLUDE_OPT,
141 R_INCLUDE_OPT,
142 R_DEXCLUDE_OPT,
143 R_DINCLUDE_OPT
144};
145
146static inline const char *init_color(const char *);
147
148/* Housekeeping */
149int tail; /* lines left to print */
150bool notfound; /* file not found */
151
152extern char *__progname;
153
154/*
155 * Prints usage information and returns 2.
156 */
157__dead static void
158usage(void)
159{
160 fprintf(stderr, getstr(4), __progname);
161 fprintf(stderr, "%s", getstr(5));
162 fprintf(stderr, "%s", getstr(6));
163 fprintf(stderr, "%s", getstr(7));
164 exit(2);
165}
166
167static const char optstr[] =
168 "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxyz";
169
170struct option long_options[] =
171{
172 {"binary-files", required_argument, NULL, BIN_OPT},
173 {"decompress", no_argument, NULL, DECOMPRESS_OPT},
174 {"help", no_argument, NULL, HELP_OPT},
175 {"mmap", no_argument, NULL, MMAP_OPT},
176 {"line-buffered", no_argument, NULL, LINEBUF_OPT},
177 {"label", required_argument, NULL, LABEL_OPT},
178 {"color", optional_argument, NULL, COLOR_OPT},
179 {"colour", optional_argument, NULL, COLOR_OPT},
180 {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
181 {"include", required_argument, NULL, R_INCLUDE_OPT},
182 {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
183 {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
184 {"after-context", required_argument, NULL, 'A'},
185 {"text", no_argument, NULL, 'a'},
186 {"before-context", required_argument, NULL, 'B'},
187 {"byte-offset", no_argument, NULL, 'b'},
188 {"context", optional_argument, NULL, 'C'},
189 {"count", no_argument, NULL, 'c'},
190 {"devices", required_argument, NULL, 'D'},
191 {"directories", required_argument, NULL, 'd'},
192 {"extended-regexp", no_argument, NULL, 'E'},
193 {"regexp", required_argument, NULL, 'e'},
194 {"fixed-strings", no_argument, NULL, 'F'},
195 {"file", required_argument, NULL, 'f'},
196 {"basic-regexp", no_argument, NULL, 'G'},
197 {"no-filename", no_argument, NULL, 'h'},
198 {"with-filename", no_argument, NULL, 'H'},
199 {"ignore-case", no_argument, NULL, 'i'},
200 {"bz2decompress", no_argument, NULL, 'J'},
201 {"files-with-matches", no_argument, NULL, 'l'},
202 {"files-without-match", no_argument, NULL, 'L'},
203 {"max-count", required_argument, NULL, 'm'},
204 {"line-number", no_argument, NULL, 'n'},
205 {"only-matching", no_argument, NULL, 'o'},
206 {"quiet", no_argument, NULL, 'q'},
207 {"silent", no_argument, NULL, 'q'},
208 {"recursive", no_argument, NULL, 'r'},
209 {"no-messages", no_argument, NULL, 's'},
210 {"binary", no_argument, NULL, 'U'},
211 {"unix-byte-offsets", no_argument, NULL, 'u'},
212 {"invert-match", no_argument, NULL, 'v'},
213 {"version", no_argument, NULL, 'V'},
214 {"word-regexp", no_argument, NULL, 'w'},
215 {"line-regexp", no_argument, NULL, 'x'},
216 {"null", no_argument, NULL, 'Z'},
217 {"null-data", no_argument, NULL, 'z'},
218 {NULL, no_argument, NULL, 0}
219};
220
221/*
222 * Adds a searching pattern to the internal array.
223 */
224static void
225add_pattern(char *pat, size_t len)
226{
227
228 /* TODO: Check for empty patterns and shortcut */
229
230 /* Increase size if necessary */
231 if (patterns == pattern_sz) {
232 pattern_sz *= 2;
233 pattern = grep_realloc(pattern, ++pattern_sz *
234 sizeof(*pattern));
235 }
236 if (len > 0 && pat[len - 1] == '\n')
237 --len;
238 /* pat may not be NUL-terminated */
239 pattern[patterns] = grep_malloc(len + 1);
240 memcpy(pattern[patterns], pat, len);
241 pattern[patterns][len] = '\0';
242 ++patterns;
243}
244
245/*
246 * Adds a file include/exclude pattern to the internal array.
247 */
248static void
249add_fpattern(const char *pat, int mode)
250{
251
252 /* Increase size if necessary */
253 if (fpatterns == fpattern_sz) {
254 fpattern_sz *= 2;
255 fpattern = grep_realloc(fpattern, ++fpattern_sz *
256 sizeof(struct epat));
257 }
258 fpattern[fpatterns].pat = grep_strdup(pat);
259 fpattern[fpatterns].mode = mode;
260 ++fpatterns;
261}
262
263/*
264 * Adds a directory include/exclude pattern to the internal array.
265 */
266static void
267add_dpattern(const char *pat, int mode)
268{
269
270 /* Increase size if necessary */
271 if (dpatterns == dpattern_sz) {
272 dpattern_sz *= 2;
273 dpattern = grep_realloc(dpattern, ++dpattern_sz *
274 sizeof(struct epat));
275 }
276 dpattern[dpatterns].pat = grep_strdup(pat);
277 dpattern[dpatterns].mode = mode;
278 ++dpatterns;
279}
280
281/*
282 * Reads searching patterns from a file and adds them with add_pattern().
283 */
284static void
285read_patterns(const char *fn)
286{
287 FILE *f;
288 char *line;
289 size_t len;
290 ssize_t rlen;
291
292 if ((f = fopen(fn, "r")) == NULL)
293 err(2, "%s", fn);
294 line = NULL;
295 len = 0;
296 while ((rlen = getline(&line, &len, f)) != -1)
297 add_pattern(line, *line == '\n' ? 0 : (size_t)rlen);
298 free(line);
299 if (ferror(f))
300 err(2, "%s", fn);
301 fclose(f);
302}
303
304static inline const char *
305init_color(const char *d)
306{
307 char *c;
308
309 c = getenv("GREP_COLOR");
310 return (c != NULL ? c : d);
311}
312
313int
314main(int argc, char *argv[])
315{
316 char **aargv, **eargv, *eopts;
317 char *ep;
318 unsigned long long l;
319 unsigned int aargc, eargc, i, j;
320 int c, lastc, needpattern, newarg, prevoptind;
321
322 setlocale(LC_ALL, "");
323
324#ifndef WITHOUT_NLS
325 catalog = catopen("grep", NL_CAT_LOCALE);
326#endif
327
328 /* Check what is the program name of the binary. In this
329 way we can have all the funcionalities in one binary
330 without the need of scripting and using ugly hacks. */
331 switch (__progname[0]) {
332 case 'e':
333 grepbehave = GREP_EXTENDED;
334 break;
335 case 'f':
336 grepbehave = GREP_FIXED;
337 break;
338 case 'g':
339 grepbehave = GREP_BASIC;
340 break;
341 case 'z':
342 filebehave = FILE_GZIP;
343 switch(__progname[1]) {
344 case 'e':
345 grepbehave = GREP_EXTENDED;
346 break;
347 case 'f':
348 grepbehave = GREP_FIXED;
349 break;
350 case 'g':
351 grepbehave = GREP_BASIC;
352 break;
353 }
354 break;
355 }
356
357 lastc = '\0';
358 newarg = 1;
359 prevoptind = 1;
360 needpattern = 1;
361
362 eopts = getenv("GREP_OPTIONS");
363
364 /* support for extra arguments in GREP_OPTIONS */
365 eargc = 0;
366 if (eopts != NULL) {
367 char *str;
368
369 /* make an estimation of how many extra arguments we have */
370 for (j = 0; j < strlen(eopts); j++)
371 if (eopts[j] == ' ')
372 eargc++;
373
374 eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
375
376 eargc = 0;
377 /* parse extra arguments */
378 while ((str = strsep(&eopts, " ")) != NULL)
379 eargv[eargc++] = grep_strdup(str);
380
381 aargv = (char **)grep_calloc(eargc + argc + 1,
382 sizeof(char *));
383
384 aargv[0] = argv[0];
385 for (i = 0; i < eargc; i++)
386 aargv[i + 1] = eargv[i];
387 for (j = 1; j < (unsigned int)argc; j++, i++)
388 aargv[i + 1] = argv[j];
389
390 aargc = eargc + argc;
391 } else {
392 aargv = argv;
393 aargc = argc;
394 }
395
396 while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
397 -1)) {
398 switch (c) {
399 case '0': case '1': case '2': case '3': case '4':
400 case '5': case '6': case '7': case '8': case '9':
401 if (newarg || !isdigit(lastc))
402 Aflag = 0;
403 else if (Aflag > LLONG_MAX / 10) {
404 errno = ERANGE;
405 err(2, NULL);
406 }
407 Aflag = Bflag = (Aflag * 10) + (c - '0');
408 break;
409 case 'C':
410 if (optarg == NULL) {
411 Aflag = Bflag = 2;
412 break;
413 }
414 /* FALLTHROUGH */
415 case 'A':
416 /* FALLTHROUGH */
417 case 'B':
418 errno = 0;
419 l = strtoull(optarg, &ep, 10);
420 if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
421 ((errno == EINVAL) && (l == 0)))
422 err(2, NULL);
423 else if (ep[0] != '\0') {
424 errno = EINVAL;
425 err(2, NULL);
426 }
427 if (c == 'A')
428 Aflag = l;
429 else if (c == 'B')
430 Bflag = l;
431 else
432 Aflag = Bflag = l;
433 break;
434 case 'a':
435 binbehave = BINFILE_TEXT;
436 break;
437 case 'b':
438 bflag = true;
439 break;
440 case 'c':
441 cflag = true;
442 break;
443 case 'D':
444 if (strcasecmp(optarg, "skip") == 0)
445 devbehave = DEV_SKIP;
446 else if (strcasecmp(optarg, "read") == 0)
447 devbehave = DEV_READ;
448 else
449 errx(2, getstr(3), "--devices");
450 break;
451 case 'd':
452 if (strcasecmp("recurse", optarg) == 0) {
453 Hflag = true;
454 dirbehave = DIR_RECURSE;
455 } else if (strcasecmp("skip", optarg) == 0)
456 dirbehave = DIR_SKIP;
457 else if (strcasecmp("read", optarg) == 0)
458 dirbehave = DIR_READ;
459 else
460 errx(2, getstr(3), "--directories");
461 break;
462 case 'E':
463 grepbehave = GREP_EXTENDED;
464 break;
465 case 'e':
466 add_pattern(optarg, strlen(optarg));
467 needpattern = 0;
468 break;
469 case 'F':
470 grepbehave = GREP_FIXED;
471 break;
472 case 'f':
473 read_patterns(optarg);
474 needpattern = 0;
475 break;
476 case 'G':
477 grepbehave = GREP_BASIC;
478 break;
479 case 'H':
480 Hflag = true;
481 break;
482 case 'h':
483 Hflag = false;
484 hflag = true;
485 break;
486 case 'I':
487 binbehave = BINFILE_SKIP;
488 break;
489 case 'i':
490 case 'y':
491 iflag = true;
492 cflags |= REG_ICASE;
493 break;
494 case 'J':
495 filebehave = FILE_BZIP;
496 break;
497 case 'L':
498 lflag = false;
499 Lflag = true;
500 break;
501 case 'l':
502 Lflag = false;
503 lflag = true;
504 break;
505 case 'm':
506 mflag = true;
507 errno = 0;
508 mcount = strtoull(optarg, &ep, 10);
509 if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
510 ((errno == EINVAL) && (mcount == 0)))
511 err(2, NULL);
512 else if (ep[0] != '\0') {
513 errno = EINVAL;
514 err(2, NULL);
515 }
516 break;
517 case 'n':
518 nflag = true;
519 break;
520 case 'O':
521 linkbehave = LINK_EXPLICIT;
522 break;
523 case 'o':
524 oflag = true;
525 break;
526 case 'p':
527 linkbehave = LINK_SKIP;
528 break;
529 case 'q':
530 qflag = true;
531 break;
532 case 'S':
533 linkbehave = LINK_READ;
534 break;
535 case 'R':
536 case 'r':
537 dirbehave = DIR_RECURSE;
538 Hflag = true;
539 break;
540 case 's':
541 sflag = true;
542 break;
543 case 'U':
544 binbehave = BINFILE_BIN;
545 break;
546 case 'u':
547 case MMAP_OPT:
548 /* noop, compatibility */
549 break;
550 case 'V':
551 printf(getstr(9), __progname, VERSION);
552 exit(0);
553 case 'v':
554 vflag = true;
555 break;
556 case 'w':
557 wflag = true;
558 break;
559 case 'x':
560 xflag = true;
561 break;
562 case 'Z':
563 nullflag = true;
564 break;
565 case 'z':
566 nulldataflag = true;
567 line_sep = '\0';
568 break;
569 case BIN_OPT:
570 if (strcasecmp("binary", optarg) == 0)
571 binbehave = BINFILE_BIN;
572 else if (strcasecmp("without-match", optarg) == 0)
573 binbehave = BINFILE_SKIP;
574 else if (strcasecmp("text", optarg) == 0)
575 binbehave = BINFILE_TEXT;
576 else
577 errx(2, getstr(3), "--binary-files");
578 break;
579 case COLOR_OPT:
580 color = NULL;
581 if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
582 strcasecmp("tty", optarg) == 0 ||
583 strcasecmp("if-tty", optarg) == 0) {
584 char *term;
585
586 term = getenv("TERM");
587 if (isatty(STDOUT_FILENO) && term != NULL &&
588 strcasecmp(term, "dumb") != 0)
589 color = init_color("01;31");
590 } else if (strcasecmp("always", optarg) == 0 ||
591 strcasecmp("yes", optarg) == 0 ||
592 strcasecmp("force", optarg) == 0) {
593 color = init_color("01;31");
594 } else if (strcasecmp("never", optarg) != 0 &&
595 strcasecmp("none", optarg) != 0 &&
596 strcasecmp("no", optarg) != 0)
597 errx(2, getstr(3), "--color");
598 break;
599 case DECOMPRESS_OPT:
600 filebehave = FILE_GZIP;
601 break;
602 case LABEL_OPT:
603 label = optarg;
604 break;
605 case LINEBUF_OPT:
606 lbflag = true;
607 break;
608 case R_INCLUDE_OPT:
609 finclude = true;
610 add_fpattern(optarg, INCL_PAT);
611 break;
612 case R_EXCLUDE_OPT:
613 fexclude = true;
614 add_fpattern(optarg, EXCL_PAT);
615 break;
616 case R_DINCLUDE_OPT:
617 dinclude = true;
618 add_dpattern(optarg, INCL_PAT);
619 break;
620 case R_DEXCLUDE_OPT:
621 dexclude = true;
622 add_dpattern(optarg, EXCL_PAT);
623 break;
624 case HELP_OPT:
625 default:
626 usage();
627 }
628 lastc = c;
629 newarg = optind != prevoptind;
630 prevoptind = optind;
631 }
632 aargc -= optind;
633 aargv += optind;
634
635 /* Fail if we don't have any pattern */
636 if (aargc == 0 && needpattern)
637 usage();
638
639 /* Process patterns from command line */
640 if (aargc != 0 && needpattern) {
641 add_pattern(*aargv, strlen(*aargv));
642 --aargc;
643 ++aargv;
644 }
645
646 switch (grepbehave) {
647 case GREP_FIXED:
648 case GREP_BASIC:
649 break;
650 case GREP_EXTENDED:
651 cflags |= REG_EXTENDED;
652 break;
653 default:
654 /* NOTREACHED */
655 usage();
656 }
657
658 fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
659 r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
660/*
661 * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
662 * Optimizations should be done there.
663 */
664 /* Check if cheating is allowed (always is for fgrep). */
665 if (grepbehave == GREP_FIXED) {
666 for (i = 0; i < patterns; ++i)
667 fgrepcomp(&fg_pattern[i], pattern[i]);
668 } else {
669 for (i = 0; i < patterns; ++i) {
670 if (fastcomp(&fg_pattern[i], pattern[i])) {
671 /* Fall back to full regex library */
672 c = regcomp(&r_pattern[i], pattern[i], cflags);
673 if (c != 0) {
674 regerror(c, &r_pattern[i], re_error,
675 RE_ERROR_BUF);
676 errx(2, "%s", re_error);
677 }
678 }
679 }
680 }
681
682 if (lbflag)
683 setlinebuf(stdout);
684
685 if ((aargc == 0 || aargc == 1) && !Hflag)
686 hflag = true;
687
688 if (aargc == 0)
689 exit(!procfile("-"));
690
691 if (dirbehave == DIR_RECURSE)
692 c = grep_tree(aargv);
693 else
694 for (c = 0; aargc--; ++aargv) {
695 if ((finclude || fexclude) && !file_matching(*aargv))
696 continue;
697 c+= procfile(*aargv);
698 }
699
700#ifndef WITHOUT_NLS
701 catclose(catalog);
702#endif
703
704 /* Find out the correct return value according to the
705 results and the command line option. */
706 exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
707}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/grep.h b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
new file mode 100644
index 000000000..fa2a3e3ce
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/grep.h
@@ -0,0 +1,162 @@
1/* $NetBSD: grep.h,v 1.8 2012/05/06 22:27:00 joerg Exp $ */
2/* $OpenBSD: grep.h,v 1.15 2010/04/05 03:03:55 tedu Exp $ */
3/* $FreeBSD: head/usr.bin/grep/grep.h 211496 2010-08-19 09:28:59Z des $ */
4
5/*-
6 * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
7 * Copyright (c) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#ifndef __ANDROID__
33#include <bzlib.h>
34#endif
35#include <limits.h>
36#include <regex.h>
37#include <stdbool.h>
38#include <stdio.h>
39#ifndef __ANDROID__
40#include <zlib.h>
41#endif
42
43#ifdef WITHOUT_NLS
44#define getstr(n) errstr[n]
45#else
46#include <nl_types.h>
47
48extern nl_catd catalog;
49#define getstr(n) catgets(catalog, 1, n, errstr[n])
50#endif
51
52extern const char *errstr[];
53
54#define VERSION "2.5.1-FreeBSD"
55
56#define GREP_FIXED 0
57#define GREP_BASIC 1
58#define GREP_EXTENDED 2
59
60#define BINFILE_BIN 0
61#define BINFILE_SKIP 1
62#define BINFILE_TEXT 2
63
64#define FILE_STDIO 0
65#define FILE_GZIP 1
66#define FILE_BZIP 2
67
68#define DIR_READ 0
69#define DIR_SKIP 1
70#define DIR_RECURSE 2
71
72#define DEV_READ 0
73#define DEV_SKIP 1
74
75#define LINK_READ 0
76#define LINK_EXPLICIT 1
77#define LINK_SKIP 2
78
79#define EXCL_PAT 0
80#define INCL_PAT 1
81
82#define MAX_LINE_MATCHES 32
83
84struct file {
85 int fd;
86 bool binary;
87};
88
89struct str {
90 off_t off;
91 size_t len;
92 char *dat;
93 char *file;
94 int line_no;
95};
96
97struct epat {
98 char *pat;
99 int mode;
100};
101
102typedef struct {
103 size_t len;
104 unsigned char *pattern;
105 int qsBc[UCHAR_MAX + 1];
106 /* flags */
107 bool bol;
108 bool eol;
109 bool reversed;
110 bool word;
111} fastgrep_t;
112
113/* Flags passed to regcomp() and regexec() */
114extern int cflags, eflags;
115
116/* Command line flags */
117extern bool Eflag, Fflag, Gflag, Hflag, Lflag,
118 bflag, cflag, hflag, iflag, lflag, mflag, nflag, oflag,
119 qflag, sflag, vflag, wflag, xflag;
120extern bool dexclude, dinclude, fexclude, finclude, lbflag, nullflag, nulldataflag;
121extern unsigned char line_sep;
122extern unsigned long long Aflag, Bflag, mcount;
123extern char *label;
124extern const char *color;
125extern int binbehave, devbehave, dirbehave, filebehave, grepbehave, linkbehave;
126
127extern bool notfound;
128extern int tail;
129extern unsigned int dpatterns, fpatterns, patterns;
130extern char **pattern;
131extern struct epat *dpattern, *fpattern;
132extern regex_t *er_pattern, *r_pattern;
133extern fastgrep_t *fg_pattern;
134
135/* For regex errors */
136#define RE_ERROR_BUF 512
137extern char re_error[RE_ERROR_BUF + 1]; /* Seems big enough */
138
139/* util.c */
140bool file_matching(const char *fname);
141int procfile(const char *fn);
142int grep_tree(char **argv);
143void *grep_malloc(size_t size);
144void *grep_calloc(size_t nmemb, size_t size);
145void *grep_realloc(void *ptr, size_t size);
146char *grep_strdup(const char *str);
147void printline(struct str *line, int sep, regmatch_t *matches, int m);
148
149/* queue.c */
150void enqueue(struct str *x);
151void printqueue(void);
152void clearqueue(void);
153
154/* file.c */
155void grep_close(struct file *f);
156struct file *grep_open(const char *path);
157char *grep_fgetln(struct file *f, size_t *len);
158
159/* fastgrep.c */
160int fastcomp(fastgrep_t *, const char *);
161void fgrepcomp(fastgrep_t *, const char *);
162int grep_search(fastgrep_t *, const unsigned char *, size_t, regmatch_t *);
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/queue.c b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
new file mode 100644
index 000000000..e3c6be17a
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/queue.c
@@ -0,0 +1,116 @@
1/* $NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $ */
2/* $FreeBSD: head/usr.bin/grep/queue.c 211496 2010-08-19 09:28:59Z des $ */
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/*
30 * A really poor man's queue. It does only what it has to and gets out of
31 * Dodge. It is used in place of <sys/queue.h> to get a better performance.
32 */
33
34#if HAVE_NBTOOL_CONFIG_H
35#include "nbtool_config.h"
36#endif
37
38#include <sys/cdefs.h>
39__RCSID("$NetBSD: queue.c,v 1.5 2011/08/31 16:24:57 plunky Exp $");
40
41#include <sys/param.h>
42#include <sys/queue.h>
43
44#include <stdlib.h>
45#include <string.h>
46
47#include "grep.h"
48
49struct qentry {
50 STAILQ_ENTRY(qentry) list;
51 struct str data;
52};
53
54static STAILQ_HEAD(, qentry) queue = STAILQ_HEAD_INITIALIZER(queue);
55static unsigned long long count;
56
57static struct qentry *dequeue(void);
58
59void
60enqueue(struct str *x)
61{
62 struct qentry *item;
63
64 item = grep_malloc(sizeof(struct qentry));
65 item->data.dat = grep_malloc(sizeof(char) * x->len);
66 item->data.len = x->len;
67 item->data.line_no = x->line_no;
68 item->data.off = x->off;
69 memcpy(item->data.dat, x->dat, x->len);
70 item->data.file = x->file;
71
72 STAILQ_INSERT_TAIL(&queue, item, list);
73
74 if (++count > Bflag) {
75 item = dequeue();
76 free(item->data.dat);
77 free(item);
78 }
79}
80
81static struct qentry *
82dequeue(void)
83{
84 struct qentry *item;
85
86 item = STAILQ_FIRST(&queue);
87 if (item == NULL)
88 return (NULL);
89
90 STAILQ_REMOVE_HEAD(&queue, list);
91 --count;
92 return (item);
93}
94
95void
96printqueue(void)
97{
98 struct qentry *item;
99
100 while ((item = dequeue()) != NULL) {
101 printline(&item->data, '-', NULL, 0);
102 free(item->data.dat);
103 free(item);
104 }
105}
106
107void
108clearqueue(void)
109{
110 struct qentry *item;
111
112 while ((item = dequeue()) != NULL) {
113 free(item->data.dat);
114 free(item);
115 }
116}
diff --git a/toolbox/upstream-netbsd/usr.bin/grep/util.c b/toolbox/upstream-netbsd/usr.bin/grep/util.c
new file mode 100644
index 000000000..ecd948da4
--- /dev/null
+++ b/toolbox/upstream-netbsd/usr.bin/grep/util.c
@@ -0,0 +1,499 @@
1/* $NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $ */
2/* $FreeBSD: head/usr.bin/grep/util.c 211496 2010-08-19 09:28:59Z des $ */
3/* $OpenBSD: util.c,v 1.39 2010/07/02 22:18:03 tedu Exp $ */
4
5/*-
6 * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
7 * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
8 * All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#if HAVE_NBTOOL_CONFIG_H
33#include "nbtool_config.h"
34#endif
35
36#include <sys/cdefs.h>
37__RCSID("$NetBSD: util.c,v 1.17 2013/01/21 03:24:43 msaitoh Exp $");
38
39#include <sys/stat.h>
40#include <sys/types.h>
41
42#include <ctype.h>
43#include <err.h>
44#include <errno.h>
45#include <fnmatch.h>
46#include <fts.h>
47#include <libgen.h>
48#include <stdbool.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53#include <wchar.h>
54#include <wctype.h>
55
56#include "grep.h"
57
58static bool first, first_global = true;
59static unsigned long long since_printed;
60
61static int procline(struct str *l, int);
62
63bool
64file_matching(const char *fname)
65{
66 char *fname_base, *fname_copy;
67 unsigned int i;
68 bool ret;
69
70 ret = finclude ? false : true;
71 fname_copy = grep_strdup(fname);
72 fname_base = basename(fname_copy);
73
74 for (i = 0; i < fpatterns; ++i) {
75 if (fnmatch(fpattern[i].pat, fname, 0) == 0 ||
76 fnmatch(fpattern[i].pat, fname_base, 0) == 0) {
77 if (fpattern[i].mode == EXCL_PAT) {
78 free(fname_copy);
79 return (false);
80 } else
81 ret = true;
82 }
83 }
84 free(fname_copy);
85 return (ret);
86}
87
88static inline bool
89dir_matching(const char *dname)
90{
91 unsigned int i;
92 bool ret;
93
94 ret = dinclude ? false : true;
95
96 for (i = 0; i < dpatterns; ++i) {
97 if (dname != NULL &&
98 fnmatch(dname, dpattern[i].pat, 0) == 0) {
99 if (dpattern[i].mode == EXCL_PAT)
100 return (false);
101 else
102 ret = true;
103 }
104 }
105 return (ret);
106}
107
108/*
109 * Processes a directory when a recursive search is performed with
110 * the -R option. Each appropriate file is passed to procfile().
111 */
112int
113grep_tree(char **argv)
114{
115 FTS *fts;
116 FTSENT *p;
117 char *d, *dir = NULL;
118 int c, fts_flags;
119 bool ok;
120
121 c = fts_flags = 0;
122
123 switch(linkbehave) {
124 case LINK_EXPLICIT:
125 fts_flags = FTS_COMFOLLOW;
126 break;
127 case LINK_SKIP:
128 fts_flags = FTS_PHYSICAL;
129 break;
130 default:
131 fts_flags = FTS_LOGICAL;
132
133 }
134
135 fts_flags |= FTS_NOSTAT | FTS_NOCHDIR;
136
137 if (!(fts = fts_open(argv, fts_flags, NULL)))
138 err(2, "fts_open");
139 while ((p = fts_read(fts)) != NULL) {
140 switch (p->fts_info) {
141 case FTS_DNR:
142 /* FALLTHROUGH */
143 case FTS_ERR:
144 errx(2, "%s: %s", p->fts_path, strerror(p->fts_errno));
145 break;
146 case FTS_D:
147 /* FALLTHROUGH */
148 case FTS_DP:
149 break;
150 case FTS_DC:
151 /* Print a warning for recursive directory loop */
152 warnx("warning: %s: recursive directory loop",
153 p->fts_path);
154 break;
155 default:
156 /* Check for file exclusion/inclusion */
157 ok = true;
158 if (dexclude || dinclude) {
159 if ((d = strrchr(p->fts_path, '/')) != NULL) {
160 dir = grep_malloc(sizeof(char) *
161 (d - p->fts_path + 1));
162 memcpy(dir, p->fts_path,
163 d - p->fts_path);
164 dir[d - p->fts_path] = '\0';
165 }
166 ok = dir_matching(dir);
167 free(dir);
168 dir = NULL;
169 }
170 if (fexclude || finclude)
171 ok &= file_matching(p->fts_path);
172
173 if (ok)
174 c += procfile(p->fts_path);
175 break;
176 }
177 }
178
179 fts_close(fts);
180 return (c);
181}
182
183/*
184 * Opens a file and processes it. Each file is processed line-by-line
185 * passing the lines to procline().
186 */
187int
188procfile(const char *fn)
189{
190 struct file *f;
191 struct stat sb;
192 struct str ln;
193 mode_t s;
194 int c, t;
195
196 if (mflag && (mcount <= 0))
197 return (0);
198
199 if (strcmp(fn, "-") == 0) {
200 fn = label != NULL ? label : getstr(1);
201 f = grep_open(NULL);
202 } else {
203 if (!stat(fn, &sb)) {
204 /* Check if we need to process the file */
205 s = sb.st_mode & S_IFMT;
206 if (s == S_IFDIR && dirbehave == DIR_SKIP)
207 return (0);
208 if ((s == S_IFIFO || s == S_IFCHR || s == S_IFBLK
209 || s == S_IFSOCK) && devbehave == DEV_SKIP)
210 return (0);
211 }
212 f = grep_open(fn);
213 }
214 if (f == NULL) {
215 if (!sflag)
216 warn("%s", fn);
217 if (errno == ENOENT)
218 notfound = true;
219 return (0);
220 }
221
222 ln.file = grep_malloc(strlen(fn) + 1);
223 strcpy(ln.file, fn);
224 ln.line_no = 0;
225 ln.len = 0;
226 tail = 0;
227 ln.off = -1;
228
229 for (first = true, c = 0; c == 0 || !(lflag || qflag); ) {
230 ln.off += ln.len + 1;
231 if ((ln.dat = grep_fgetln(f, &ln.len)) == NULL || ln.len == 0)
232 break;
233 if (ln.len > 0 && ln.dat[ln.len - 1] == line_sep)
234 --ln.len;
235 ln.line_no++;
236
237 /* Return if we need to skip a binary file */
238 if (f->binary && binbehave == BINFILE_SKIP) {
239 grep_close(f);
240 free(ln.file);
241 free(f);
242 return (0);
243 }
244 /* Process the file line-by-line */
245 t = procline(&ln, f->binary);
246 c += t;
247
248 /* Count the matches if we have a match limit */
249 if (mflag) {
250 mcount -= t;
251 if (mcount <= 0)
252 break;
253 }
254 }
255 if (Bflag > 0)
256 clearqueue();
257 grep_close(f);
258
259 if (cflag) {
260 if (!hflag)
261 printf("%s:", ln.file);
262 printf("%u%c", c, line_sep);
263 }
264 if (lflag && !qflag && c != 0)
265 printf("%s%c", fn, line_sep);
266 if (Lflag && !qflag && c == 0)
267 printf("%s%c", fn, line_sep);
268 if (c && !cflag && !lflag && !Lflag &&
269 binbehave == BINFILE_BIN && f->binary && !qflag)
270 printf(getstr(8), fn);
271
272 free(ln.file);
273 free(f);
274 return (c);
275}
276
277#define iswword(x) (iswalnum((x)) || (x) == L'_')
278
279/*
280 * Processes a line comparing it with the specified patterns. Each pattern
281 * is looped to be compared along with the full string, saving each and every
282 * match, which is necessary to colorize the output and to count the
283 * matches. The matching lines are passed to printline() to display the
284 * appropriate output.
285 */
286static int
287procline(struct str *l, int nottext)
288{
289 regmatch_t matches[MAX_LINE_MATCHES];
290 regmatch_t pmatch;
291 size_t st = 0;
292 unsigned int i;
293 int c = 0, m = 0, r = 0;
294
295 /* Loop to process the whole line */
296 while (st <= l->len) {
297 pmatch.rm_so = st;
298 pmatch.rm_eo = l->len;
299
300 /* Loop to compare with all the patterns */
301 for (i = 0; i < patterns; i++) {
302/*
303 * XXX: grep_search() is a workaround for speed up and should be
304 * removed in the future. See fastgrep.c.
305 */
306 if (fg_pattern[i].pattern) {
307 r = grep_search(&fg_pattern[i],
308 (unsigned char *)l->dat,
309 l->len, &pmatch);
310 r = (r == 0) ? 0 : REG_NOMATCH;
311 st = pmatch.rm_eo;
312 } else {
313 r = regexec(&r_pattern[i], l->dat, 1,
314 &pmatch, eflags);
315 r = (r == 0) ? 0 : REG_NOMATCH;
316 st = pmatch.rm_eo;
317 }
318 if (r == REG_NOMATCH)
319 continue;
320 /* Check for full match */
321 if (xflag &&
322 (pmatch.rm_so != 0 ||
323 (size_t)pmatch.rm_eo != l->len))
324 continue;
325 /* Check for whole word match */
326 if (fg_pattern[i].word && pmatch.rm_so != 0) {
327 wchar_t wbegin, wend;
328
329 wbegin = wend = L' ';
330 if (pmatch.rm_so != 0 &&
331 sscanf(&l->dat[pmatch.rm_so - 1],
332 "%lc", &wbegin) != 1)
333 continue;
334 if ((size_t)pmatch.rm_eo != l->len &&
335 sscanf(&l->dat[pmatch.rm_eo],
336 "%lc", &wend) != 1)
337 continue;
338 if (iswword(wbegin) || iswword(wend))
339 continue;
340 }
341 c = 1;
342 if (m < MAX_LINE_MATCHES)
343 matches[m++] = pmatch;
344 /* matches - skip further patterns */
345 if ((color != NULL && !oflag) || qflag || lflag)
346 break;
347 }
348
349 if (vflag) {
350 c = !c;
351 break;
352 }
353 /* One pass if we are not recording matches */
354 if ((color != NULL && !oflag) || qflag || lflag)
355 break;
356
357 if (st == (size_t)pmatch.rm_so)
358 break; /* No matches */
359 }
360
361 if (c && binbehave == BINFILE_BIN && nottext)
362 return (c); /* Binary file */
363
364 /* Dealing with the context */
365 if ((tail || c) && !cflag && !qflag && !lflag && !Lflag) {
366 if (c) {
367 if ((Aflag || Bflag) && !first_global &&
368 (first || since_printed > Bflag))
369 printf("--\n");
370 tail = Aflag;
371 if (Bflag > 0)
372 printqueue();
373 printline(l, ':', matches, m);
374 } else {
375 printline(l, '-', matches, m);
376 tail--;
377 }
378 first = false;
379 first_global = false;
380 since_printed = 0;
381 } else {
382 if (Bflag)
383 enqueue(l);
384 since_printed++;
385 }
386 return (c);
387}
388
389/*
390 * Safe malloc() for internal use.
391 */
392void *
393grep_malloc(size_t size)
394{
395 void *ptr;
396
397 if ((ptr = malloc(size)) == NULL)
398 err(2, "malloc");
399 return (ptr);
400}
401
402/*
403 * Safe calloc() for internal use.
404 */
405void *
406grep_calloc(size_t nmemb, size_t size)
407{
408 void *ptr;
409
410 if ((ptr = calloc(nmemb, size)) == NULL)
411 err(2, "calloc");
412 return (ptr);
413}
414
415/*
416 * Safe realloc() for internal use.
417 */
418void *
419grep_realloc(void *ptr, size_t size)
420{
421
422 if ((ptr = realloc(ptr, size)) == NULL)
423 err(2, "realloc");
424 return (ptr);
425}
426
427/*
428 * Safe strdup() for internal use.
429 */
430char *
431grep_strdup(const char *str)
432{
433 char *ret;
434
435 if ((ret = strdup(str)) == NULL)
436 err(2, "strdup");
437 return (ret);
438}
439
440/*
441 * Prints a matching line according to the command line options.
442 */
443void
444printline(struct str *line, int sep, regmatch_t *matches, int m)
445{
446 size_t a = 0;
447 int i, n = 0;
448
449 if (!hflag) {
450 if (nullflag == 0)
451 fputs(line->file, stdout);
452 else {
453 printf("%s", line->file);
454 putchar(0);
455 }
456 ++n;
457 }
458 if (nflag) {
459 if (n > 0)
460 putchar(sep);
461 printf("%d", line->line_no);
462 ++n;
463 }
464 if (bflag) {
465 if (n > 0)
466 putchar(sep);
467 printf("%lld", (long long)line->off);
468 ++n;
469 }
470 if (n)
471 putchar(sep);
472 /* --color and -o */
473 if ((oflag || color) && m > 0) {
474 for (i = 0; i < m; i++) {
475 if (!oflag)
476 fwrite(line->dat + a, matches[i].rm_so - a, 1,
477 stdout);
478 if (color)
479 fprintf(stdout, "\33[%sm\33[K", color);
480
481 fwrite(line->dat + matches[i].rm_so,
482 matches[i].rm_eo - matches[i].rm_so, 1,
483 stdout);
484 if (color)
485 fprintf(stdout, "\33[m\33[K");
486 a = matches[i].rm_eo;
487 if (oflag)
488 putchar('\n');
489 }
490 if (!oflag) {
491 if (line->len - a > 0)
492 fwrite(line->dat + a, line->len - a, 1, stdout);
493 putchar(line_sep);
494 }
495 } else {
496 fwrite(line->dat, line->len, 1, stdout);
497 putchar(line_sep);
498 }
499}