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