Deleted Added
full compact
grep.c (210578) grep.c (210622)
1/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
2
3/*-
4 * Copyright (c) 1999 James Howard and Dag-Erling Co�dan Sm�rgrav
5 * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
1/* $OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $ */
2
3/*-
4 * Copyright (c) 1999 James Howard and Dag-Erling Co�dan Sm�rgrav
5 * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/usr.bin/grep/grep.c 210578 2010-07-29 00:11:14Z gabor $");
31__FBSDID("$FreeBSD: head/usr.bin/grep/grep.c 210622 2010-07-29 18:02:57Z gabor $");
32
33#include <sys/stat.h>
34#include <sys/types.h>
35
36#include <ctype.h>
37#include <err.h>
38#include <errno.h>
39#include <getopt.h>
40#include <limits.h>
41#include <libgen.h>
42#include <locale.h>
43#include <stdbool.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49#include "grep.h"
50
51#ifndef WITHOUT_NLS
52#include <nl_types.h>
53nl_catd catalog;
54#endif
55
56/*
57 * Default messags to use when NLS is disabled or no catalogue
58 * is found.
59 */
60const char *errstr[] = {
61 "",
62/* 1*/ "(standard input)",
63/* 2*/ "cannot read bzip2 compressed file",
32
33#include <sys/stat.h>
34#include <sys/types.h>
35
36#include <ctype.h>
37#include <err.h>
38#include <errno.h>
39#include <getopt.h>
40#include <limits.h>
41#include <libgen.h>
42#include <locale.h>
43#include <stdbool.h>
44#include <stdio.h>
45#include <stdlib.h>
46#include <string.h>
47#include <unistd.h>
48
49#include "grep.h"
50
51#ifndef WITHOUT_NLS
52#include <nl_types.h>
53nl_catd catalog;
54#endif
55
56/*
57 * Default messags to use when NLS is disabled or no catalogue
58 * is found.
59 */
60const char *errstr[] = {
61 "",
62/* 1*/ "(standard input)",
63/* 2*/ "cannot read bzip2 compressed file",
64/* 3*/ "unknown --color option",
64/* 3*/ "unknown %s option",
65/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
66/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
67/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
68/* 7*/ "\t[--null] [pattern] [file ...]\n",
65/* 4*/ "usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
66/* 5*/ "\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
67/* 6*/ "\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
68/* 7*/ "\t[--null] [pattern] [file ...]\n",
69/* 8*/ "unknown --binary-files option",
70/* 9*/ "Binary file %s matches\n",
71/*10*/ "%s (BSD grep) %s\n",
69/* 8*/ "Binary file %s matches\n",
70/* 9*/ "%s (BSD grep) %s\n",
72};
73
74/* Flags passed to regcomp() and regexec() */
75int cflags = 0;
76int eflags = REG_STARTEND;
77
78/* Shortcut for matching all cases like empty regex */
79bool matchall;
80
81/* Searching patterns */
82unsigned int patterns, pattern_sz;
83char **pattern;
84regex_t *r_pattern;
85fastgrep_t *fg_pattern;
86
87/* Filename exclusion/inclusion patterns */
88unsigned int fpatterns, fpattern_sz;
89unsigned int dpatterns, dpattern_sz;
90struct epat *dpattern, *fpattern;
91
92/* For regex errors */
93char re_error[RE_ERROR_BUF + 1];
94
95/* Command-line flags */
96unsigned long long Aflag; /* -A x: print x lines trailing each match */
97unsigned long long Bflag; /* -B x: print x lines leading each match */
98bool Hflag; /* -H: always print file name */
99bool Lflag; /* -L: only show names of files with no matches */
100bool bflag; /* -b: show block numbers for each match */
101bool cflag; /* -c: only show a count of matching lines */
102bool hflag; /* -h: don't print filename headers */
103bool iflag; /* -i: ignore case */
104bool lflag; /* -l: only show names of files with matches */
105bool mflag; /* -m x: stop reading the files after x matches */
106unsigned long long mcount; /* count for -m */
107bool nflag; /* -n: show line numbers in front of matching lines */
108bool oflag; /* -o: print only matching part */
109bool qflag; /* -q: quiet mode (don't output anything) */
110bool sflag; /* -s: silent mode (ignore errors) */
111bool vflag; /* -v: only show non-matching lines */
112bool wflag; /* -w: pattern must start and end on word boundaries */
113bool xflag; /* -x: pattern must match entire line */
114bool lbflag; /* --line-buffered */
115bool nullflag; /* --null */
116char *label; /* --label */
117const char *color; /* --color */
118int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
119int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
120int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
121int devbehave = DEV_READ; /* -D: handling of devices */
122int dirbehave = DIR_READ; /* -dRr: handling of directories */
123int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
124
125bool dexclude, dinclude; /* --exclude amd --include */
126bool fexclude, finclude; /* --exclude-dir and --include-dir */
127
128enum {
129 BIN_OPT = CHAR_MAX + 1,
130 COLOR_OPT,
131 HELP_OPT,
132 MMAP_OPT,
133 LINEBUF_OPT,
134 LABEL_OPT,
135 NULL_OPT,
136 R_EXCLUDE_OPT,
137 R_INCLUDE_OPT,
138 R_DEXCLUDE_OPT,
139 R_DINCLUDE_OPT
140};
141
142static inline const char *init_color(const char *);
143
144/* Housekeeping */
145bool first = true; /* flag whether we are processing the first match */
146bool prev; /* flag whether or not the previous line matched */
147int tail; /* lines left to print */
148bool notfound; /* file not found */
149
150extern char *__progname;
151
152/*
153 * Prints usage information and returns 2.
154 */
155static void
156usage(void)
157{
158 fprintf(stderr, getstr(4), __progname);
159 fprintf(stderr, "%s", getstr(5));
160 fprintf(stderr, "%s", getstr(5));
161 fprintf(stderr, "%s", getstr(6));
162 fprintf(stderr, "%s", getstr(7));
163 exit(2);
164}
165
166static const char *optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
167
168struct option long_options[] =
169{
170 {"binary-files", required_argument, NULL, BIN_OPT},
171 {"help", no_argument, NULL, HELP_OPT},
172 {"mmap", no_argument, NULL, MMAP_OPT},
173 {"line-buffered", no_argument, NULL, LINEBUF_OPT},
174 {"label", required_argument, NULL, LABEL_OPT},
175 {"null", no_argument, NULL, NULL_OPT},
176 {"color", optional_argument, NULL, COLOR_OPT},
177 {"colour", optional_argument, NULL, COLOR_OPT},
178 {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
179 {"include", required_argument, NULL, R_INCLUDE_OPT},
180 {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
181 {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
182 {"after-context", required_argument, NULL, 'A'},
183 {"text", no_argument, NULL, 'a'},
184 {"before-context", required_argument, NULL, 'B'},
185 {"byte-offset", no_argument, NULL, 'b'},
186 {"context", optional_argument, NULL, 'C'},
187 {"count", no_argument, NULL, 'c'},
188 {"devices", required_argument, NULL, 'D'},
189 {"directories", required_argument, NULL, 'd'},
190 {"extended-regexp", no_argument, NULL, 'E'},
191 {"regexp", required_argument, NULL, 'e'},
192 {"fixed-strings", no_argument, NULL, 'F'},
193 {"file", required_argument, NULL, 'f'},
194 {"basic-regexp", no_argument, NULL, 'G'},
195 {"no-filename", no_argument, NULL, 'h'},
196 {"with-filename", no_argument, NULL, 'H'},
197 {"ignore-case", no_argument, NULL, 'i'},
198 {"bz2decompress", no_argument, NULL, 'J'},
199 {"files-with-matches", no_argument, NULL, 'l'},
200 {"files-without-match", no_argument, NULL, 'L'},
201 {"max-count", required_argument, NULL, 'm'},
202 {"line-number", no_argument, NULL, 'n'},
203 {"only-matching", no_argument, NULL, 'o'},
204 {"quiet", no_argument, NULL, 'q'},
205 {"silent", no_argument, NULL, 'q'},
206 {"recursive", no_argument, NULL, 'r'},
207 {"no-messages", no_argument, NULL, 's'},
208 {"binary", no_argument, NULL, 'U'},
209 {"unix-byte-offsets", no_argument, NULL, 'u'},
210 {"invert-match", no_argument, NULL, 'v'},
211 {"version", no_argument, NULL, 'V'},
212 {"word-regexp", no_argument, NULL, 'w'},
213 {"line-regexp", no_argument, NULL, 'x'},
214 {"decompress", no_argument, NULL, 'Z'},
215 {NULL, no_argument, NULL, 0}
216};
217
218/*
219 * Adds a searching pattern to the internal array.
220 */
221static void
222add_pattern(char *pat, size_t len)
223{
224
225 /* Check if we can do a shortcut */
226 if (len == 0 || matchall) {
227 matchall = true;
228 return;
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 strlcpy(pattern[patterns], pat, len + 1);
241 ++patterns;
242}
243
244/*
245 * Adds a file include/exclude pattern to the internal array.
246 */
247static void
248add_fpattern(const char *pat, int mode)
249{
250
251 /* Increase size if necessary */
252 if (fpatterns == fpattern_sz) {
253 fpattern_sz *= 2;
254 fpattern = grep_realloc(fpattern, ++fpattern_sz *
255 sizeof(struct epat));
256 }
257 fpattern[fpatterns].pat = grep_strdup(pat);
258 fpattern[fpatterns].mode = mode;
259 ++fpatterns;
260}
261
262/*
263 * Adds a directory include/exclude pattern to the internal array.
264 */
265static void
266add_dpattern(const char *pat, int mode)
267{
268
269 /* Increase size if necessary */
270 if (dpatterns == dpattern_sz) {
271 dpattern_sz *= 2;
272 dpattern = grep_realloc(dpattern, ++dpattern_sz *
273 sizeof(struct epat));
274 }
275 dpattern[dpatterns].pat = grep_strdup(pat);
276 dpattern[dpatterns].mode = mode;
277 ++dpatterns;
278}
279
280/*
281 * Reads searching patterns from a file and adds them with add_pattern().
282 */
283static void
284read_patterns(const char *fn)
285{
286 FILE *f;
287 char *line;
288 size_t len;
289
290 if ((f = fopen(fn, "r")) == NULL)
291 err(2, "%s", fn);
292 while ((line = fgetln(f, &len)) != NULL)
293 add_pattern(line, *line == '\n' ? 0 : len);
294 if (ferror(f))
295 err(2, "%s", fn);
296 fclose(f);
297}
298
299static inline const char *
300init_color(const char *d)
301{
302 char *c;
303
304 c = getenv("GREP_COLOR");
305 return (c != NULL ? c : d);
306}
307
308int
309main(int argc, char *argv[])
310{
311 char **aargv, **eargv, *eopts;
312 char *ep;
313 unsigned long long l;
314 unsigned int aargc, eargc, i;
315 int c, lastc, needpattern, newarg, prevoptind;
316
317 setlocale(LC_ALL, "");
318
319#ifndef WITHOUT_NLS
320 catalog = catopen("grep", NL_CAT_LOCALE);
321#endif
322
323 /* Check what is the program name of the binary. In this
324 way we can have all the funcionalities in one binary
325 without the need of scripting and using ugly hacks. */
326 switch (__progname[0]) {
327 case 'e':
328 grepbehave = GREP_EXTENDED;
329 break;
330 case 'f':
331 grepbehave = GREP_FIXED;
332 break;
333 case 'g':
334 grepbehave = GREP_BASIC;
335 break;
336 case 'z':
337 filebehave = FILE_GZIP;
338 switch(__progname[1]) {
339 case 'e':
340 grepbehave = GREP_EXTENDED;
341 break;
342 case 'f':
343 grepbehave = GREP_FIXED;
344 break;
345 case 'g':
346 grepbehave = GREP_BASIC;
347 break;
348 }
349 break;
350 }
351
352 lastc = '\0';
353 newarg = 1;
354 prevoptind = 1;
355 needpattern = 1;
356
357 eopts = getenv("GREP_OPTIONS");
358
359 eargc = 1;
360 if (eopts != NULL) {
361 char *str;
362
363 for(i = 0; i < strlen(eopts); i++)
364 if (eopts[i] == ' ')
365 eargc++;
366
367 eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
368
369 str = strtok(eopts, " ");
370 eargc = 0;
371
372 while(str != NULL) {
373 eargv[++eargc] = (char *)grep_malloc(sizeof(char) *
374 (strlen(str) + 1));
375 strlcpy(eargv[eargc], str, strlen(str) + 1);
376 str = strtok(NULL, " ");
377 }
378 eargv[++eargc] = NULL;
379
380 aargv = (char **)grep_calloc(eargc + argc + 1,
381 sizeof(char *));
382 aargv[0] = argv[0];
383
384 for(i = 1; i < eargc; i++)
385 aargv[i] = eargv[i];
386 for(int j = 1; j < argc; j++)
387 aargv[i++] = argv[j];
388
389 aargc = eargc + argc - 1;
390
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;
71};
72
73/* Flags passed to regcomp() and regexec() */
74int cflags = 0;
75int eflags = REG_STARTEND;
76
77/* Shortcut for matching all cases like empty regex */
78bool matchall;
79
80/* Searching patterns */
81unsigned int patterns, pattern_sz;
82char **pattern;
83regex_t *r_pattern;
84fastgrep_t *fg_pattern;
85
86/* Filename exclusion/inclusion patterns */
87unsigned int fpatterns, fpattern_sz;
88unsigned int dpatterns, dpattern_sz;
89struct epat *dpattern, *fpattern;
90
91/* For regex errors */
92char re_error[RE_ERROR_BUF + 1];
93
94/* Command-line flags */
95unsigned long long Aflag; /* -A x: print x lines trailing each match */
96unsigned long long Bflag; /* -B x: print x lines leading each match */
97bool Hflag; /* -H: always print file name */
98bool Lflag; /* -L: only show names of files with no matches */
99bool bflag; /* -b: show block numbers for each match */
100bool cflag; /* -c: only show a count of matching lines */
101bool hflag; /* -h: don't print filename headers */
102bool iflag; /* -i: ignore case */
103bool lflag; /* -l: only show names of files with matches */
104bool mflag; /* -m x: stop reading the files after x matches */
105unsigned long long mcount; /* count for -m */
106bool nflag; /* -n: show line numbers in front of matching lines */
107bool oflag; /* -o: print only matching part */
108bool qflag; /* -q: quiet mode (don't output anything) */
109bool sflag; /* -s: silent mode (ignore errors) */
110bool vflag; /* -v: only show non-matching lines */
111bool wflag; /* -w: pattern must start and end on word boundaries */
112bool xflag; /* -x: pattern must match entire line */
113bool lbflag; /* --line-buffered */
114bool nullflag; /* --null */
115char *label; /* --label */
116const char *color; /* --color */
117int grepbehave = GREP_BASIC; /* -EFGP: type of the regex */
118int binbehave = BINFILE_BIN; /* -aIU: handling of binary files */
119int filebehave = FILE_STDIO; /* -JZ: normal, gzip or bzip2 file */
120int devbehave = DEV_READ; /* -D: handling of devices */
121int dirbehave = DIR_READ; /* -dRr: handling of directories */
122int linkbehave = LINK_READ; /* -OpS: handling of symlinks */
123
124bool dexclude, dinclude; /* --exclude amd --include */
125bool fexclude, finclude; /* --exclude-dir and --include-dir */
126
127enum {
128 BIN_OPT = CHAR_MAX + 1,
129 COLOR_OPT,
130 HELP_OPT,
131 MMAP_OPT,
132 LINEBUF_OPT,
133 LABEL_OPT,
134 NULL_OPT,
135 R_EXCLUDE_OPT,
136 R_INCLUDE_OPT,
137 R_DEXCLUDE_OPT,
138 R_DINCLUDE_OPT
139};
140
141static inline const char *init_color(const char *);
142
143/* Housekeeping */
144bool first = true; /* flag whether we are processing the first match */
145bool prev; /* flag whether or not the previous line matched */
146int tail; /* lines left to print */
147bool notfound; /* file not found */
148
149extern char *__progname;
150
151/*
152 * Prints usage information and returns 2.
153 */
154static void
155usage(void)
156{
157 fprintf(stderr, getstr(4), __progname);
158 fprintf(stderr, "%s", getstr(5));
159 fprintf(stderr, "%s", getstr(5));
160 fprintf(stderr, "%s", getstr(6));
161 fprintf(stderr, "%s", getstr(7));
162 exit(2);
163}
164
165static const char *optstr = "0123456789A:B:C:D:EFGHIJLOPSRUVZabcd:e:f:hilm:nopqrsuvwxy";
166
167struct option long_options[] =
168{
169 {"binary-files", required_argument, NULL, BIN_OPT},
170 {"help", no_argument, NULL, HELP_OPT},
171 {"mmap", no_argument, NULL, MMAP_OPT},
172 {"line-buffered", no_argument, NULL, LINEBUF_OPT},
173 {"label", required_argument, NULL, LABEL_OPT},
174 {"null", no_argument, NULL, NULL_OPT},
175 {"color", optional_argument, NULL, COLOR_OPT},
176 {"colour", optional_argument, NULL, COLOR_OPT},
177 {"exclude", required_argument, NULL, R_EXCLUDE_OPT},
178 {"include", required_argument, NULL, R_INCLUDE_OPT},
179 {"exclude-dir", required_argument, NULL, R_DEXCLUDE_OPT},
180 {"include-dir", required_argument, NULL, R_DINCLUDE_OPT},
181 {"after-context", required_argument, NULL, 'A'},
182 {"text", no_argument, NULL, 'a'},
183 {"before-context", required_argument, NULL, 'B'},
184 {"byte-offset", no_argument, NULL, 'b'},
185 {"context", optional_argument, NULL, 'C'},
186 {"count", no_argument, NULL, 'c'},
187 {"devices", required_argument, NULL, 'D'},
188 {"directories", required_argument, NULL, 'd'},
189 {"extended-regexp", no_argument, NULL, 'E'},
190 {"regexp", required_argument, NULL, 'e'},
191 {"fixed-strings", no_argument, NULL, 'F'},
192 {"file", required_argument, NULL, 'f'},
193 {"basic-regexp", no_argument, NULL, 'G'},
194 {"no-filename", no_argument, NULL, 'h'},
195 {"with-filename", no_argument, NULL, 'H'},
196 {"ignore-case", no_argument, NULL, 'i'},
197 {"bz2decompress", no_argument, NULL, 'J'},
198 {"files-with-matches", no_argument, NULL, 'l'},
199 {"files-without-match", no_argument, NULL, 'L'},
200 {"max-count", required_argument, NULL, 'm'},
201 {"line-number", no_argument, NULL, 'n'},
202 {"only-matching", no_argument, NULL, 'o'},
203 {"quiet", no_argument, NULL, 'q'},
204 {"silent", no_argument, NULL, 'q'},
205 {"recursive", no_argument, NULL, 'r'},
206 {"no-messages", no_argument, NULL, 's'},
207 {"binary", no_argument, NULL, 'U'},
208 {"unix-byte-offsets", no_argument, NULL, 'u'},
209 {"invert-match", no_argument, NULL, 'v'},
210 {"version", no_argument, NULL, 'V'},
211 {"word-regexp", no_argument, NULL, 'w'},
212 {"line-regexp", no_argument, NULL, 'x'},
213 {"decompress", no_argument, NULL, 'Z'},
214 {NULL, no_argument, NULL, 0}
215};
216
217/*
218 * Adds a searching pattern to the internal array.
219 */
220static void
221add_pattern(char *pat, size_t len)
222{
223
224 /* Check if we can do a shortcut */
225 if (len == 0 || matchall) {
226 matchall = true;
227 return;
228 }
229 /* Increase size if necessary */
230 if (patterns == pattern_sz) {
231 pattern_sz *= 2;
232 pattern = grep_realloc(pattern, ++pattern_sz *
233 sizeof(*pattern));
234 }
235 if (len > 0 && pat[len - 1] == '\n')
236 --len;
237 /* pat may not be NUL-terminated */
238 pattern[patterns] = grep_malloc(len + 1);
239 strlcpy(pattern[patterns], pat, len + 1);
240 ++patterns;
241}
242
243/*
244 * Adds a file include/exclude pattern to the internal array.
245 */
246static void
247add_fpattern(const char *pat, int mode)
248{
249
250 /* Increase size if necessary */
251 if (fpatterns == fpattern_sz) {
252 fpattern_sz *= 2;
253 fpattern = grep_realloc(fpattern, ++fpattern_sz *
254 sizeof(struct epat));
255 }
256 fpattern[fpatterns].pat = grep_strdup(pat);
257 fpattern[fpatterns].mode = mode;
258 ++fpatterns;
259}
260
261/*
262 * Adds a directory include/exclude pattern to the internal array.
263 */
264static void
265add_dpattern(const char *pat, int mode)
266{
267
268 /* Increase size if necessary */
269 if (dpatterns == dpattern_sz) {
270 dpattern_sz *= 2;
271 dpattern = grep_realloc(dpattern, ++dpattern_sz *
272 sizeof(struct epat));
273 }
274 dpattern[dpatterns].pat = grep_strdup(pat);
275 dpattern[dpatterns].mode = mode;
276 ++dpatterns;
277}
278
279/*
280 * Reads searching patterns from a file and adds them with add_pattern().
281 */
282static void
283read_patterns(const char *fn)
284{
285 FILE *f;
286 char *line;
287 size_t len;
288
289 if ((f = fopen(fn, "r")) == NULL)
290 err(2, "%s", fn);
291 while ((line = fgetln(f, &len)) != NULL)
292 add_pattern(line, *line == '\n' ? 0 : len);
293 if (ferror(f))
294 err(2, "%s", fn);
295 fclose(f);
296}
297
298static inline const char *
299init_color(const char *d)
300{
301 char *c;
302
303 c = getenv("GREP_COLOR");
304 return (c != NULL ? c : d);
305}
306
307int
308main(int argc, char *argv[])
309{
310 char **aargv, **eargv, *eopts;
311 char *ep;
312 unsigned long long l;
313 unsigned int aargc, eargc, i;
314 int c, lastc, needpattern, newarg, prevoptind;
315
316 setlocale(LC_ALL, "");
317
318#ifndef WITHOUT_NLS
319 catalog = catopen("grep", NL_CAT_LOCALE);
320#endif
321
322 /* Check what is the program name of the binary. In this
323 way we can have all the funcionalities in one binary
324 without the need of scripting and using ugly hacks. */
325 switch (__progname[0]) {
326 case 'e':
327 grepbehave = GREP_EXTENDED;
328 break;
329 case 'f':
330 grepbehave = GREP_FIXED;
331 break;
332 case 'g':
333 grepbehave = GREP_BASIC;
334 break;
335 case 'z':
336 filebehave = FILE_GZIP;
337 switch(__progname[1]) {
338 case 'e':
339 grepbehave = GREP_EXTENDED;
340 break;
341 case 'f':
342 grepbehave = GREP_FIXED;
343 break;
344 case 'g':
345 grepbehave = GREP_BASIC;
346 break;
347 }
348 break;
349 }
350
351 lastc = '\0';
352 newarg = 1;
353 prevoptind = 1;
354 needpattern = 1;
355
356 eopts = getenv("GREP_OPTIONS");
357
358 eargc = 1;
359 if (eopts != NULL) {
360 char *str;
361
362 for(i = 0; i < strlen(eopts); i++)
363 if (eopts[i] == ' ')
364 eargc++;
365
366 eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
367
368 str = strtok(eopts, " ");
369 eargc = 0;
370
371 while(str != NULL) {
372 eargv[++eargc] = (char *)grep_malloc(sizeof(char) *
373 (strlen(str) + 1));
374 strlcpy(eargv[eargc], str, strlen(str) + 1);
375 str = strtok(NULL, " ");
376 }
377 eargv[++eargc] = NULL;
378
379 aargv = (char **)grep_calloc(eargc + argc + 1,
380 sizeof(char *));
381 aargv[0] = argv[0];
382
383 for(i = 1; i < eargc; i++)
384 aargv[i] = eargv[i];
385 for(int j = 1; j < argc; j++)
386 aargv[i++] = argv[j];
387
388 aargc = eargc + argc - 1;
389
390 } else {
391 aargv = argv;
392 aargc = argc;
393 }
394
395 while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
396 -1)) {
397 switch (c) {
398 case '0': case '1': case '2': case '3': case '4':
399 case '5': case '6': case '7': case '8': case '9':
400 if (newarg || !isdigit(lastc))
401 Aflag = 0;
402 else if (Aflag > LLONG_MAX / 10) {
403 errno = ERANGE;
404 err(2, NULL);
405 }
406 Aflag = Bflag = (Aflag * 10) + (c - '0');
407 break;
408 case 'C':
409 if (optarg == NULL) {
410 Aflag = Bflag = 2;
411 break;
412 }
413 /* FALLTHROUGH */
414 case 'A':
415 /* FALLTHROUGH */
416 case 'B':
417 errno = 0;
418 l = strtoull(optarg, &ep, 10);
419 if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
420 ((errno == EINVAL) && (l == 0)))
421 err(2, NULL);
422 else if (ep[0] != '\0') {
423 errno = EINVAL;
424 err(2, NULL);
425 }
426 if (c == 'A')
427 Aflag = l;
428 else if (c == 'B')
429 Bflag = l;
430 else
431 Aflag = Bflag = l;
432 break;
433 case 'a':
434 binbehave = BINFILE_TEXT;
435 break;
436 case 'b':
437 bflag = true;
438 break;
439 case 'c':
440 cflag = true;
441 break;
442 case 'D':
443 if (strcasecmp(optarg, "skip") == 0)
444 devbehave = DEV_SKIP;
445 else if (strcasecmp(optarg, "read") == 0)
446 devbehave = DEV_READ;
448 else {
449 errno = EINVAL;
450 err(2, NULL);
451 }
447 else
448 errx(2, getstr(3), "--devices");
452 break;
453 case 'd':
454 if (strcasecmp("recurse", optarg) == 0) {
455 Hflag = true;
456 dirbehave = DIR_RECURSE;
457 } else if (strcasecmp("skip", optarg) == 0)
458 dirbehave = DIR_SKIP;
459 else if (strcasecmp("read", optarg) == 0)
460 dirbehave = DIR_READ;
449 break;
450 case 'd':
451 if (strcasecmp("recurse", optarg) == 0) {
452 Hflag = true;
453 dirbehave = DIR_RECURSE;
454 } else if (strcasecmp("skip", optarg) == 0)
455 dirbehave = DIR_SKIP;
456 else if (strcasecmp("read", optarg) == 0)
457 dirbehave = DIR_READ;
461 else {
462 errno = EINVAL;
463 err(2, NULL);
464 }
458 else
459 errx(2, getstr(3), "--directories");
465 break;
466 case 'E':
467 grepbehave = GREP_EXTENDED;
468 break;
469 case 'e':
470 add_pattern(optarg, strlen(optarg));
471 needpattern = 0;
472 break;
473 case 'F':
474 grepbehave = GREP_FIXED;
475 break;
476 case 'f':
477 read_patterns(optarg);
478 needpattern = 0;
479 break;
480 case 'G':
481 grepbehave = GREP_BASIC;
482 break;
483 case 'H':
484 Hflag = true;
485 break;
486 case 'h':
487 Hflag = false;
488 hflag = true;
489 break;
490 case 'I':
491 binbehave = BINFILE_SKIP;
492 break;
493 case 'i':
494 case 'y':
495 iflag = true;
496 cflags |= REG_ICASE;
497 break;
498 case 'J':
499 filebehave = FILE_BZIP;
500 break;
501 case 'L':
502 lflag = false;
503 Lflag = true;
504 break;
505 case 'l':
506 Lflag = false;
507 lflag = true;
508 break;
509 case 'm':
510 mflag = true;
511 errno = 0;
512 mcount = strtoull(optarg, &ep, 10);
513 if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
514 ((errno == EINVAL) && (mcount == 0)))
515 err(2, NULL);
516 else if (ep[0] != '\0') {
517 errno = EINVAL;
518 err(2, NULL);
519 }
520 break;
521 case 'n':
522 nflag = true;
523 break;
524 case 'O':
525 linkbehave = LINK_EXPLICIT;
526 break;
527 case 'o':
528 oflag = true;
529 break;
530 case 'p':
531 linkbehave = LINK_SKIP;
532 break;
533 case 'q':
534 qflag = true;
535 break;
536 case 'S':
537 linkbehave = LINK_READ;
538 break;
539 case 'R':
540 case 'r':
541 dirbehave = DIR_RECURSE;
542 Hflag = true;
543 break;
544 case 's':
545 sflag = true;
546 break;
547 case 'U':
548 binbehave = BINFILE_BIN;
549 break;
550 case 'u':
551 case MMAP_OPT:
552 /* noop, compatibility */
553 break;
554 case 'V':
460 break;
461 case 'E':
462 grepbehave = GREP_EXTENDED;
463 break;
464 case 'e':
465 add_pattern(optarg, strlen(optarg));
466 needpattern = 0;
467 break;
468 case 'F':
469 grepbehave = GREP_FIXED;
470 break;
471 case 'f':
472 read_patterns(optarg);
473 needpattern = 0;
474 break;
475 case 'G':
476 grepbehave = GREP_BASIC;
477 break;
478 case 'H':
479 Hflag = true;
480 break;
481 case 'h':
482 Hflag = false;
483 hflag = true;
484 break;
485 case 'I':
486 binbehave = BINFILE_SKIP;
487 break;
488 case 'i':
489 case 'y':
490 iflag = true;
491 cflags |= REG_ICASE;
492 break;
493 case 'J':
494 filebehave = FILE_BZIP;
495 break;
496 case 'L':
497 lflag = false;
498 Lflag = true;
499 break;
500 case 'l':
501 Lflag = false;
502 lflag = true;
503 break;
504 case 'm':
505 mflag = true;
506 errno = 0;
507 mcount = strtoull(optarg, &ep, 10);
508 if (((errno == ERANGE) && (mcount == ULLONG_MAX)) ||
509 ((errno == EINVAL) && (mcount == 0)))
510 err(2, NULL);
511 else if (ep[0] != '\0') {
512 errno = EINVAL;
513 err(2, NULL);
514 }
515 break;
516 case 'n':
517 nflag = true;
518 break;
519 case 'O':
520 linkbehave = LINK_EXPLICIT;
521 break;
522 case 'o':
523 oflag = true;
524 break;
525 case 'p':
526 linkbehave = LINK_SKIP;
527 break;
528 case 'q':
529 qflag = true;
530 break;
531 case 'S':
532 linkbehave = LINK_READ;
533 break;
534 case 'R':
535 case 'r':
536 dirbehave = DIR_RECURSE;
537 Hflag = true;
538 break;
539 case 's':
540 sflag = true;
541 break;
542 case 'U':
543 binbehave = BINFILE_BIN;
544 break;
545 case 'u':
546 case MMAP_OPT:
547 /* noop, compatibility */
548 break;
549 case 'V':
555 printf(getstr(10), __progname, VERSION);
550 printf(getstr(9), __progname, VERSION);
556 exit(0);
557 case 'v':
558 vflag = true;
559 break;
560 case 'w':
561 wflag = true;
562 break;
563 case 'x':
564 xflag = true;
565 break;
566 case 'Z':
567 filebehave = FILE_GZIP;
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
551 exit(0);
552 case 'v':
553 vflag = true;
554 break;
555 case 'w':
556 wflag = true;
557 break;
558 case 'x':
559 xflag = true;
560 break;
561 case 'Z':
562 filebehave = FILE_GZIP;
563 break;
564 case BIN_OPT:
565 if (strcasecmp("binary", optarg) == 0)
566 binbehave = BINFILE_BIN;
567 else if (strcasecmp("without-match", optarg) == 0)
568 binbehave = BINFILE_SKIP;
569 else if (strcasecmp("text", optarg) == 0)
570 binbehave = BINFILE_TEXT;
571 else
577 errx(2, "%s", getstr(8));
572 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)
573 break;
574 case COLOR_OPT:
575 color = NULL;
576 if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
577 strcasecmp("tty", optarg) == 0 ||
578 strcasecmp("if-tty", optarg) == 0) {
579 char *term;
580
581 term = getenv("TERM");
582 if (isatty(STDOUT_FILENO) && term != NULL &&
583 strcasecmp(term, "dumb") != 0)
584 color = init_color("01;31");
585 } else if (strcasecmp("always", optarg) == 0 ||
586 strcasecmp("yes", optarg) == 0 ||
587 strcasecmp("force", optarg) == 0) {
588 color = init_color("01;31");
589 } else if (strcasecmp("never", optarg) != 0 &&
590 strcasecmp("none", optarg) != 0 &&
591 strcasecmp("no", optarg) != 0)
597 errx(2, "%s", getstr(3));
592 errx(2, getstr(3), "--color");
598 break;
599 case LABEL_OPT:
600 label = optarg;
601 break;
602 case LINEBUF_OPT:
603 lbflag = true;
604 break;
605 case NULL_OPT:
606 nullflag = 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 dexclude = true;
618 add_dpattern(optarg, INCL_PAT);
619 break;
620 case R_DEXCLUDE_OPT:
621 dinclude = 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}
593 break;
594 case LABEL_OPT:
595 label = optarg;
596 break;
597 case LINEBUF_OPT:
598 lbflag = true;
599 break;
600 case NULL_OPT:
601 nullflag = true;
602 break;
603 case R_INCLUDE_OPT:
604 finclude = true;
605 add_fpattern(optarg, INCL_PAT);
606 break;
607 case R_EXCLUDE_OPT:
608 fexclude = true;
609 add_fpattern(optarg, EXCL_PAT);
610 break;
611 case R_DINCLUDE_OPT:
612 dexclude = true;
613 add_dpattern(optarg, INCL_PAT);
614 break;
615 case R_DEXCLUDE_OPT:
616 dinclude = true;
617 add_dpattern(optarg, EXCL_PAT);
618 break;
619 case HELP_OPT:
620 default:
621 usage();
622 }
623 lastc = c;
624 newarg = optind != prevoptind;
625 prevoptind = optind;
626 }
627 aargc -= optind;
628 aargv += optind;
629
630 /* Fail if we don't have any pattern */
631 if (aargc == 0 && needpattern)
632 usage();
633
634 /* Process patterns from command line */
635 if (aargc != 0 && needpattern) {
636 add_pattern(*aargv, strlen(*aargv));
637 --aargc;
638 ++aargv;
639 }
640
641 switch (grepbehave) {
642 case GREP_FIXED:
643 case GREP_BASIC:
644 break;
645 case GREP_EXTENDED:
646 cflags |= REG_EXTENDED;
647 break;
648 default:
649 /* NOTREACHED */
650 usage();
651 }
652
653 fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
654 r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
655/*
656 * XXX: fgrepcomp() and fastcomp() are workarounds for regexec() performance.
657 * Optimizations should be done there.
658 */
659 /* Check if cheating is allowed (always is for fgrep). */
660 if (grepbehave == GREP_FIXED) {
661 for (i = 0; i < patterns; ++i)
662 fgrepcomp(&fg_pattern[i], pattern[i]);
663 } else {
664 for (i = 0; i < patterns; ++i) {
665 if (fastcomp(&fg_pattern[i], pattern[i])) {
666 /* Fall back to full regex library */
667 c = regcomp(&r_pattern[i], pattern[i], cflags);
668 if (c != 0) {
669 regerror(c, &r_pattern[i], re_error,
670 RE_ERROR_BUF);
671 errx(2, "%s", re_error);
672 }
673 }
674 }
675 }
676
677 if (lbflag)
678 setlinebuf(stdout);
679
680 if ((aargc == 0 || aargc == 1) && !Hflag)
681 hflag = true;
682
683 if (aargc == 0)
684 exit(!procfile("-"));
685
686 if (dirbehave == DIR_RECURSE)
687 c = grep_tree(aargv);
688 else
689 for (c = 0; aargc--; ++aargv) {
690 if ((finclude || fexclude) && !file_matching(*aargv))
691 continue;
692 c+= procfile(*aargv);
693 }
694
695#ifndef WITHOUT_NLS
696 catclose(catalog);
697#endif
698
699 /* Find out the correct return value according to the
700 results and the command line option. */
701 exit(c ? (notfound ? (qflag ? 0 : 2) : 0) : (notfound ? 2 : 1));
702}