xref: /freebsd/usr.bin/grep/grep.c (revision d204af1e414662d5947241d853b4355db116e8f6)
1f3f50de6SPedro F. Giffuni /*	$NetBSD: grep.c,v 1.6 2011/04/18 03:48:23 joerg Exp $	*/
2b66a823bSGabor Kovesdan /* 	$FreeBSD$	*/
34dc88ebeSGabor Kovesdan /*	$OpenBSD: grep.c,v 1.42 2010/07/02 22:18:03 tedu Exp $	*/
44dc88ebeSGabor Kovesdan 
54dc88ebeSGabor Kovesdan /*-
6a0ef9ad6SDag-Erling Smørgrav  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
74dc88ebeSGabor Kovesdan  * Copyright (C) 2008-2009 Gabor Kovesdan <gabor@FreeBSD.org>
84dc88ebeSGabor Kovesdan  * All rights reserved.
94dc88ebeSGabor Kovesdan  *
104dc88ebeSGabor Kovesdan  * Redistribution and use in source and binary forms, with or without
114dc88ebeSGabor Kovesdan  * modification, are permitted provided that the following conditions
124dc88ebeSGabor Kovesdan  * are met:
134dc88ebeSGabor Kovesdan  * 1. Redistributions of source code must retain the above copyright
144dc88ebeSGabor Kovesdan  *    notice, this list of conditions and the following disclaimer.
154dc88ebeSGabor Kovesdan  * 2. Redistributions in binary form must reproduce the above copyright
164dc88ebeSGabor Kovesdan  *    notice, this list of conditions and the following disclaimer in the
174dc88ebeSGabor Kovesdan  *    documentation and/or other materials provided with the distribution.
184dc88ebeSGabor Kovesdan  *
194dc88ebeSGabor Kovesdan  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
204dc88ebeSGabor Kovesdan  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
214dc88ebeSGabor Kovesdan  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
224dc88ebeSGabor Kovesdan  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
234dc88ebeSGabor Kovesdan  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
244dc88ebeSGabor Kovesdan  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
254dc88ebeSGabor Kovesdan  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
264dc88ebeSGabor Kovesdan  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
274dc88ebeSGabor Kovesdan  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
284dc88ebeSGabor Kovesdan  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
294dc88ebeSGabor Kovesdan  * SUCH DAMAGE.
304dc88ebeSGabor Kovesdan  */
314dc88ebeSGabor Kovesdan 
324dc88ebeSGabor Kovesdan #include <sys/cdefs.h>
334dc88ebeSGabor Kovesdan __FBSDID("$FreeBSD$");
344dc88ebeSGabor Kovesdan 
354dc88ebeSGabor Kovesdan #include <sys/stat.h>
364dc88ebeSGabor Kovesdan #include <sys/types.h>
374dc88ebeSGabor Kovesdan 
384dc88ebeSGabor Kovesdan #include <ctype.h>
394dc88ebeSGabor Kovesdan #include <err.h>
404dc88ebeSGabor Kovesdan #include <errno.h>
41f20f6f3fSGabor Kovesdan #include <fcntl.h>
424dc88ebeSGabor Kovesdan #include <getopt.h>
434dc88ebeSGabor Kovesdan #include <limits.h>
444dc88ebeSGabor Kovesdan #include <libgen.h>
454dc88ebeSGabor Kovesdan #include <locale.h>
464dc88ebeSGabor Kovesdan #include <stdbool.h>
474dc88ebeSGabor Kovesdan #include <stdio.h>
484dc88ebeSGabor Kovesdan #include <stdlib.h>
494dc88ebeSGabor Kovesdan #include <string.h>
504dc88ebeSGabor Kovesdan #include <unistd.h>
514dc88ebeSGabor Kovesdan 
52f20f6f3fSGabor Kovesdan #include "fastmatch.h"
534dc88ebeSGabor Kovesdan #include "grep.h"
544dc88ebeSGabor Kovesdan 
554dc88ebeSGabor Kovesdan #ifndef WITHOUT_NLS
564dc88ebeSGabor Kovesdan #include <nl_types.h>
574dc88ebeSGabor Kovesdan nl_catd	 catalog;
584dc88ebeSGabor Kovesdan #endif
594dc88ebeSGabor Kovesdan 
604dc88ebeSGabor Kovesdan /*
614dc88ebeSGabor Kovesdan  * Default messags to use when NLS is disabled or no catalogue
624dc88ebeSGabor Kovesdan  * is found.
634dc88ebeSGabor Kovesdan  */
644dc88ebeSGabor Kovesdan const char	*errstr[] = {
654dc88ebeSGabor Kovesdan 	"",
664dc88ebeSGabor Kovesdan /* 1*/	"(standard input)",
674dc88ebeSGabor Kovesdan /* 2*/	"cannot read bzip2 compressed file",
6897a012f2SGabor Kovesdan /* 3*/	"unknown %s option",
694dc88ebeSGabor Kovesdan /* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZ] [-A num] [-B num] [-C[num]]\n",
704dc88ebeSGabor Kovesdan /* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
714dc88ebeSGabor Kovesdan /* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
724dc88ebeSGabor Kovesdan /* 7*/	"\t[--null] [pattern] [file ...]\n",
7397a012f2SGabor Kovesdan /* 8*/	"Binary file %s matches\n",
7497a012f2SGabor Kovesdan /* 9*/	"%s (BSD grep) %s\n",
754dc88ebeSGabor Kovesdan };
764dc88ebeSGabor Kovesdan 
774dc88ebeSGabor Kovesdan /* Flags passed to regcomp() and regexec() */
7869a6d198SGabor Kovesdan int		 cflags = REG_NOSUB;
794dc88ebeSGabor Kovesdan int		 eflags = REG_STARTEND;
804dc88ebeSGabor Kovesdan 
814dc88ebeSGabor Kovesdan /* Shortcut for matching all cases like empty regex */
824dc88ebeSGabor Kovesdan bool		 matchall;
834dc88ebeSGabor Kovesdan 
844dc88ebeSGabor Kovesdan /* Searching patterns */
85bf70beceSEd Schouten unsigned int	 patterns;
86bf70beceSEd Schouten static unsigned int pattern_sz;
87f20f6f3fSGabor Kovesdan struct pat	*pattern;
884dc88ebeSGabor Kovesdan regex_t		*r_pattern;
89f20f6f3fSGabor Kovesdan fastmatch_t	*fg_pattern;
904dc88ebeSGabor Kovesdan 
914dc88ebeSGabor Kovesdan /* Filename exclusion/inclusion patterns */
92bf70beceSEd Schouten unsigned int	fpatterns, dpatterns;
93bf70beceSEd Schouten static unsigned int fpattern_sz, dpattern_sz;
9455e44f51SGabor Kovesdan struct epat	*dpattern, *fpattern;
954dc88ebeSGabor Kovesdan 
964dc88ebeSGabor Kovesdan /* For regex errors  */
974dc88ebeSGabor Kovesdan char	 re_error[RE_ERROR_BUF + 1];
984dc88ebeSGabor Kovesdan 
994dc88ebeSGabor Kovesdan /* Command-line flags */
1004dc88ebeSGabor Kovesdan unsigned long long Aflag;	/* -A x: print x lines trailing each match */
1014dc88ebeSGabor Kovesdan unsigned long long Bflag;	/* -B x: print x lines leading each match */
1024dc88ebeSGabor Kovesdan bool	 Hflag;		/* -H: always print file name */
1034dc88ebeSGabor Kovesdan bool	 Lflag;		/* -L: only show names of files with no matches */
1044dc88ebeSGabor Kovesdan bool	 bflag;		/* -b: show block numbers for each match */
1054dc88ebeSGabor Kovesdan bool	 cflag;		/* -c: only show a count of matching lines */
1064dc88ebeSGabor Kovesdan bool	 hflag;		/* -h: don't print filename headers */
1074dc88ebeSGabor Kovesdan bool	 iflag;		/* -i: ignore case */
1084dc88ebeSGabor Kovesdan bool	 lflag;		/* -l: only show names of files with matches */
1094dc88ebeSGabor Kovesdan bool	 mflag;		/* -m x: stop reading the files after x matches */
110f20f6f3fSGabor Kovesdan long long mcount;	/* count for -m */
111924500b7SEitan Adler long long mlimit;	/* requested value for -m */
1124dc88ebeSGabor Kovesdan bool	 nflag;		/* -n: show line numbers in front of matching lines */
1134dc88ebeSGabor Kovesdan bool	 oflag;		/* -o: print only matching part */
1144dc88ebeSGabor Kovesdan bool	 qflag;		/* -q: quiet mode (don't output anything) */
1154dc88ebeSGabor Kovesdan bool	 sflag;		/* -s: silent mode (ignore errors) */
1164dc88ebeSGabor Kovesdan bool	 vflag;		/* -v: only show non-matching lines */
1174dc88ebeSGabor Kovesdan bool	 wflag;		/* -w: pattern must start and end on word boundaries */
1184dc88ebeSGabor Kovesdan bool	 xflag;		/* -x: pattern must match entire line */
1194dc88ebeSGabor Kovesdan bool	 lbflag;	/* --line-buffered */
1204dc88ebeSGabor Kovesdan bool	 nullflag;	/* --null */
1214dc88ebeSGabor Kovesdan char	*label;		/* --label */
12227116286SGabor Kovesdan const char *color;	/* --color */
1234dc88ebeSGabor Kovesdan int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
1244dc88ebeSGabor Kovesdan int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
1254dc88ebeSGabor Kovesdan int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
12627116286SGabor Kovesdan int	 devbehave = DEV_READ;		/* -D: handling of devices */
12727116286SGabor Kovesdan int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
12827116286SGabor Kovesdan int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
1294dc88ebeSGabor Kovesdan 
13059218eb7SGabor Kovesdan bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
13159218eb7SGabor Kovesdan bool	 fexclude, finclude;	/* --exclude and --include */
13255e44f51SGabor Kovesdan 
1334dc88ebeSGabor Kovesdan enum {
1344dc88ebeSGabor Kovesdan 	BIN_OPT = CHAR_MAX + 1,
1354dc88ebeSGabor Kovesdan 	COLOR_OPT,
1364dc88ebeSGabor Kovesdan 	HELP_OPT,
1374dc88ebeSGabor Kovesdan 	MMAP_OPT,
1384dc88ebeSGabor Kovesdan 	LINEBUF_OPT,
1394dc88ebeSGabor Kovesdan 	LABEL_OPT,
1404dc88ebeSGabor Kovesdan 	NULL_OPT,
1414dc88ebeSGabor Kovesdan 	R_EXCLUDE_OPT,
1424dc88ebeSGabor Kovesdan 	R_INCLUDE_OPT,
1434dc88ebeSGabor Kovesdan 	R_DEXCLUDE_OPT,
1444dc88ebeSGabor Kovesdan 	R_DINCLUDE_OPT
1454dc88ebeSGabor Kovesdan };
1464dc88ebeSGabor Kovesdan 
14727116286SGabor Kovesdan static inline const char	*init_color(const char *);
14827116286SGabor Kovesdan 
1494dc88ebeSGabor Kovesdan /* Housekeeping */
1504dc88ebeSGabor Kovesdan bool	 first = true;	/* flag whether we are processing the first match */
1514dc88ebeSGabor Kovesdan bool	 prev;		/* flag whether or not the previous line matched */
1524dc88ebeSGabor Kovesdan int	 tail;		/* lines left to print */
1536f4cbf7cSGabor Kovesdan bool	 file_err;	/* file reading error */
1544dc88ebeSGabor Kovesdan 
1554dc88ebeSGabor Kovesdan /*
1564dc88ebeSGabor Kovesdan  * Prints usage information and returns 2.
1574dc88ebeSGabor Kovesdan  */
1584dc88ebeSGabor Kovesdan static void
1594dc88ebeSGabor Kovesdan usage(void)
1604dc88ebeSGabor Kovesdan {
161afbbd357SGabor Kovesdan 	fprintf(stderr, getstr(4), getprogname());
1624dc88ebeSGabor Kovesdan 	fprintf(stderr, "%s", getstr(5));
1634dc88ebeSGabor Kovesdan 	fprintf(stderr, "%s", getstr(6));
1644dc88ebeSGabor Kovesdan 	fprintf(stderr, "%s", getstr(7));
1654dc88ebeSGabor Kovesdan 	exit(2);
1664dc88ebeSGabor Kovesdan }
1674dc88ebeSGabor Kovesdan 
168f20f6f3fSGabor Kovesdan static const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXy";
1694dc88ebeSGabor Kovesdan 
1708375d512SEd Schouten static const struct option long_options[] =
1714dc88ebeSGabor Kovesdan {
1724dc88ebeSGabor Kovesdan 	{"binary-files",	required_argument,	NULL, BIN_OPT},
1734dc88ebeSGabor Kovesdan 	{"help",		no_argument,		NULL, HELP_OPT},
1744dc88ebeSGabor Kovesdan 	{"mmap",		no_argument,		NULL, MMAP_OPT},
1754dc88ebeSGabor Kovesdan 	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
1764dc88ebeSGabor Kovesdan 	{"label",		required_argument,	NULL, LABEL_OPT},
1774dc88ebeSGabor Kovesdan 	{"null",		no_argument,		NULL, NULL_OPT},
1784dc88ebeSGabor Kovesdan 	{"color",		optional_argument,	NULL, COLOR_OPT},
1794dc88ebeSGabor Kovesdan 	{"colour",		optional_argument,	NULL, COLOR_OPT},
1804dc88ebeSGabor Kovesdan 	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
1814dc88ebeSGabor Kovesdan 	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
1824dc88ebeSGabor Kovesdan 	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
1834dc88ebeSGabor Kovesdan 	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
1844dc88ebeSGabor Kovesdan 	{"after-context",	required_argument,	NULL, 'A'},
1854dc88ebeSGabor Kovesdan 	{"text",		no_argument,		NULL, 'a'},
1864dc88ebeSGabor Kovesdan 	{"before-context",	required_argument,	NULL, 'B'},
1874dc88ebeSGabor Kovesdan 	{"byte-offset",		no_argument,		NULL, 'b'},
1884dc88ebeSGabor Kovesdan 	{"context",		optional_argument,	NULL, 'C'},
1894dc88ebeSGabor Kovesdan 	{"count",		no_argument,		NULL, 'c'},
1904dc88ebeSGabor Kovesdan 	{"devices",		required_argument,	NULL, 'D'},
1914dc88ebeSGabor Kovesdan         {"directories",		required_argument,	NULL, 'd'},
1924dc88ebeSGabor Kovesdan 	{"extended-regexp",	no_argument,		NULL, 'E'},
1934dc88ebeSGabor Kovesdan 	{"regexp",		required_argument,	NULL, 'e'},
1944dc88ebeSGabor Kovesdan 	{"fixed-strings",	no_argument,		NULL, 'F'},
1954dc88ebeSGabor Kovesdan 	{"file",		required_argument,	NULL, 'f'},
1964dc88ebeSGabor Kovesdan 	{"basic-regexp",	no_argument,		NULL, 'G'},
1974dc88ebeSGabor Kovesdan 	{"no-filename",		no_argument,		NULL, 'h'},
1984dc88ebeSGabor Kovesdan 	{"with-filename",	no_argument,		NULL, 'H'},
1994dc88ebeSGabor Kovesdan 	{"ignore-case",		no_argument,		NULL, 'i'},
2004dc88ebeSGabor Kovesdan 	{"bz2decompress",	no_argument,		NULL, 'J'},
2014dc88ebeSGabor Kovesdan 	{"files-with-matches",	no_argument,		NULL, 'l'},
2024dc88ebeSGabor Kovesdan 	{"files-without-match", no_argument,            NULL, 'L'},
2034dc88ebeSGabor Kovesdan 	{"max-count",		required_argument,	NULL, 'm'},
204f20f6f3fSGabor Kovesdan 	{"lzma",		no_argument,		NULL, 'M'},
2054dc88ebeSGabor Kovesdan 	{"line-number",		no_argument,		NULL, 'n'},
2064dc88ebeSGabor Kovesdan 	{"only-matching",	no_argument,		NULL, 'o'},
2074dc88ebeSGabor Kovesdan 	{"quiet",		no_argument,		NULL, 'q'},
2084dc88ebeSGabor Kovesdan 	{"silent",		no_argument,		NULL, 'q'},
2094dc88ebeSGabor Kovesdan 	{"recursive",		no_argument,		NULL, 'r'},
2104dc88ebeSGabor Kovesdan 	{"no-messages",		no_argument,		NULL, 's'},
2114dc88ebeSGabor Kovesdan 	{"binary",		no_argument,		NULL, 'U'},
2124dc88ebeSGabor Kovesdan 	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
2134dc88ebeSGabor Kovesdan 	{"invert-match",	no_argument,		NULL, 'v'},
2144dc88ebeSGabor Kovesdan 	{"version",		no_argument,		NULL, 'V'},
2154dc88ebeSGabor Kovesdan 	{"word-regexp",		no_argument,		NULL, 'w'},
2164dc88ebeSGabor Kovesdan 	{"line-regexp",		no_argument,		NULL, 'x'},
217f20f6f3fSGabor Kovesdan 	{"xz",			no_argument,		NULL, 'X'},
2184dc88ebeSGabor Kovesdan 	{"decompress",          no_argument,            NULL, 'Z'},
2194dc88ebeSGabor Kovesdan 	{NULL,			no_argument,		NULL, 0}
2204dc88ebeSGabor Kovesdan };
2214dc88ebeSGabor Kovesdan 
2224dc88ebeSGabor Kovesdan /*
2234dc88ebeSGabor Kovesdan  * Adds a searching pattern to the internal array.
2244dc88ebeSGabor Kovesdan  */
2254dc88ebeSGabor Kovesdan static void
2264dc88ebeSGabor Kovesdan add_pattern(char *pat, size_t len)
2274dc88ebeSGabor Kovesdan {
2284dc88ebeSGabor Kovesdan 
229f20f6f3fSGabor Kovesdan 	/* Do not add further pattern is we already match everything */
230f20f6f3fSGabor Kovesdan 	if (matchall)
231f20f6f3fSGabor Kovesdan 	  return;
232f20f6f3fSGabor Kovesdan 
2334dc88ebeSGabor Kovesdan 	/* Check if we can do a shortcut */
234f20f6f3fSGabor Kovesdan 	if (len == 0) {
2354dc88ebeSGabor Kovesdan 		matchall = true;
236f20f6f3fSGabor Kovesdan 		for (unsigned int i = 0; i < patterns; i++) {
237f20f6f3fSGabor Kovesdan 			free(pattern[i].pat);
238f20f6f3fSGabor Kovesdan 		}
239f20f6f3fSGabor Kovesdan 		pattern = grep_realloc(pattern, sizeof(struct pat));
240f20f6f3fSGabor Kovesdan 		pattern[0].pat = NULL;
241f20f6f3fSGabor Kovesdan 		pattern[0].len = 0;
242f20f6f3fSGabor Kovesdan 		patterns = 1;
2434dc88ebeSGabor Kovesdan 		return;
2444dc88ebeSGabor Kovesdan 	}
2454dc88ebeSGabor Kovesdan 	/* Increase size if necessary */
2464dc88ebeSGabor Kovesdan 	if (patterns == pattern_sz) {
2474dc88ebeSGabor Kovesdan 		pattern_sz *= 2;
2484dc88ebeSGabor Kovesdan 		pattern = grep_realloc(pattern, ++pattern_sz *
249f20f6f3fSGabor Kovesdan 		    sizeof(struct pat));
2504dc88ebeSGabor Kovesdan 	}
2514dc88ebeSGabor Kovesdan 	if (len > 0 && pat[len - 1] == '\n')
2524dc88ebeSGabor Kovesdan 		--len;
2534dc88ebeSGabor Kovesdan 	/* pat may not be NUL-terminated */
254f20f6f3fSGabor Kovesdan 	pattern[patterns].pat = grep_malloc(len + 1);
255f20f6f3fSGabor Kovesdan 	memcpy(pattern[patterns].pat, pat, len);
256f20f6f3fSGabor Kovesdan 	pattern[patterns].len = len;
257f20f6f3fSGabor Kovesdan 	pattern[patterns].pat[len] = '\0';
2584dc88ebeSGabor Kovesdan 	++patterns;
2594dc88ebeSGabor Kovesdan }
2604dc88ebeSGabor Kovesdan 
2614dc88ebeSGabor Kovesdan /*
26255e44f51SGabor Kovesdan  * Adds a file include/exclude pattern to the internal array.
2634dc88ebeSGabor Kovesdan  */
2644dc88ebeSGabor Kovesdan static void
26555e44f51SGabor Kovesdan add_fpattern(const char *pat, int mode)
2664dc88ebeSGabor Kovesdan {
2674dc88ebeSGabor Kovesdan 
2684dc88ebeSGabor Kovesdan 	/* Increase size if necessary */
26955e44f51SGabor Kovesdan 	if (fpatterns == fpattern_sz) {
27055e44f51SGabor Kovesdan 		fpattern_sz *= 2;
27155e44f51SGabor Kovesdan 		fpattern = grep_realloc(fpattern, ++fpattern_sz *
2724dc88ebeSGabor Kovesdan 		    sizeof(struct epat));
2734dc88ebeSGabor Kovesdan 	}
27455e44f51SGabor Kovesdan 	fpattern[fpatterns].pat = grep_strdup(pat);
27555e44f51SGabor Kovesdan 	fpattern[fpatterns].mode = mode;
27655e44f51SGabor Kovesdan 	++fpatterns;
27755e44f51SGabor Kovesdan }
27855e44f51SGabor Kovesdan 
27955e44f51SGabor Kovesdan /*
28055e44f51SGabor Kovesdan  * Adds a directory include/exclude pattern to the internal array.
28155e44f51SGabor Kovesdan  */
28255e44f51SGabor Kovesdan static void
28355e44f51SGabor Kovesdan add_dpattern(const char *pat, int mode)
28455e44f51SGabor Kovesdan {
28555e44f51SGabor Kovesdan 
28655e44f51SGabor Kovesdan 	/* Increase size if necessary */
28755e44f51SGabor Kovesdan 	if (dpatterns == dpattern_sz) {
28855e44f51SGabor Kovesdan 		dpattern_sz *= 2;
28955e44f51SGabor Kovesdan 		dpattern = grep_realloc(dpattern, ++dpattern_sz *
29055e44f51SGabor Kovesdan 		    sizeof(struct epat));
29155e44f51SGabor Kovesdan 	}
29255e44f51SGabor Kovesdan 	dpattern[dpatterns].pat = grep_strdup(pat);
29355e44f51SGabor Kovesdan 	dpattern[dpatterns].mode = mode;
29455e44f51SGabor Kovesdan 	++dpatterns;
2954dc88ebeSGabor Kovesdan }
2964dc88ebeSGabor Kovesdan 
2974dc88ebeSGabor Kovesdan /*
2984dc88ebeSGabor Kovesdan  * Reads searching patterns from a file and adds them with add_pattern().
2994dc88ebeSGabor Kovesdan  */
3004dc88ebeSGabor Kovesdan static void
3014dc88ebeSGabor Kovesdan read_patterns(const char *fn)
3024dc88ebeSGabor Kovesdan {
303f20f6f3fSGabor Kovesdan 	struct stat st;
3044dc88ebeSGabor Kovesdan 	FILE *f;
3054dc88ebeSGabor Kovesdan 	char *line;
3064dc88ebeSGabor Kovesdan 	size_t len;
307f3f50de6SPedro F. Giffuni 	ssize_t rlen;
3084dc88ebeSGabor Kovesdan 
3094dc88ebeSGabor Kovesdan 	if ((f = fopen(fn, "r")) == NULL)
3104dc88ebeSGabor Kovesdan 		err(2, "%s", fn);
311f20f6f3fSGabor Kovesdan 	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
312f20f6f3fSGabor Kovesdan 		fclose(f);
313f20f6f3fSGabor Kovesdan 		return;
314f20f6f3fSGabor Kovesdan 	}
315f3f50de6SPedro F. Giffuni 	len = 0;
316f3f50de6SPedro F. Giffuni 	line = NULL;
317*d204af1eSEd Maste 	while ((rlen = getline(&line, &len, f)) != -1) {
318*d204af1eSEd Maste 		if (line[0] == '\0')
319*d204af1eSEd Maste 			continue;
3202fa7a2afSPedro F. Giffuni 		add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen);
321*d204af1eSEd Maste 	}
322*d204af1eSEd Maste 
323f3f50de6SPedro F. Giffuni 	free(line);
3244dc88ebeSGabor Kovesdan 	if (ferror(f))
3254dc88ebeSGabor Kovesdan 		err(2, "%s", fn);
3264dc88ebeSGabor Kovesdan 	fclose(f);
3274dc88ebeSGabor Kovesdan }
3284dc88ebeSGabor Kovesdan 
32927116286SGabor Kovesdan static inline const char *
33027116286SGabor Kovesdan init_color(const char *d)
33127116286SGabor Kovesdan {
33227116286SGabor Kovesdan 	char *c;
33327116286SGabor Kovesdan 
33427116286SGabor Kovesdan 	c = getenv("GREP_COLOR");
335dab19f30SGabor Kovesdan 	return (c != NULL && c[0] != '\0' ? c : d);
33627116286SGabor Kovesdan }
33727116286SGabor Kovesdan 
3384dc88ebeSGabor Kovesdan int
3394dc88ebeSGabor Kovesdan main(int argc, char *argv[])
3404dc88ebeSGabor Kovesdan {
3414dc88ebeSGabor Kovesdan 	char **aargv, **eargv, *eopts;
342afbbd357SGabor Kovesdan 	char *ep;
343afbbd357SGabor Kovesdan 	const char *pn;
3444dc88ebeSGabor Kovesdan 	unsigned long long l;
3454dc88ebeSGabor Kovesdan 	unsigned int aargc, eargc, i;
3464dc88ebeSGabor Kovesdan 	int c, lastc, needpattern, newarg, prevoptind;
3474dc88ebeSGabor Kovesdan 
3484dc88ebeSGabor Kovesdan 	setlocale(LC_ALL, "");
3494dc88ebeSGabor Kovesdan 
3504dc88ebeSGabor Kovesdan #ifndef WITHOUT_NLS
3514dc88ebeSGabor Kovesdan 	catalog = catopen("grep", NL_CAT_LOCALE);
3524dc88ebeSGabor Kovesdan #endif
3534dc88ebeSGabor Kovesdan 
3544dc88ebeSGabor Kovesdan 	/* Check what is the program name of the binary.  In this
3554dc88ebeSGabor Kovesdan 	   way we can have all the funcionalities in one binary
3564dc88ebeSGabor Kovesdan 	   without the need of scripting and using ugly hacks. */
357afbbd357SGabor Kovesdan 	pn = getprogname();
358f20f6f3fSGabor Kovesdan 	if (pn[0] == 'b' && pn[1] == 'z') {
359f20f6f3fSGabor Kovesdan 		filebehave = FILE_BZIP;
360f20f6f3fSGabor Kovesdan 		pn += 2;
361f20f6f3fSGabor Kovesdan 	} else if (pn[0] == 'x' && pn[1] == 'z') {
362f20f6f3fSGabor Kovesdan 		filebehave = FILE_XZ;
363f20f6f3fSGabor Kovesdan 		pn += 2;
364f20f6f3fSGabor Kovesdan 	} else if (pn[0] == 'l' && pn[1] == 'z') {
365f20f6f3fSGabor Kovesdan 		filebehave = FILE_LZMA;
366f20f6f3fSGabor Kovesdan 		pn += 2;
367491b8b16SEd Maste 	} else if (pn[0] == 'r') {
368491b8b16SEd Maste 		dirbehave = DIR_RECURSE;
369491b8b16SEd Maste 		Hflag = true;
370f20f6f3fSGabor Kovesdan 	} else if (pn[0] == 'z') {
3714dc88ebeSGabor Kovesdan 		filebehave = FILE_GZIP;
372f20f6f3fSGabor Kovesdan 		pn += 1;
373f20f6f3fSGabor Kovesdan 	}
374f20f6f3fSGabor Kovesdan 	switch (pn[0]) {
3754dc88ebeSGabor Kovesdan 	case 'e':
3764dc88ebeSGabor Kovesdan 		grepbehave = GREP_EXTENDED;
3774dc88ebeSGabor Kovesdan 		break;
3784dc88ebeSGabor Kovesdan 	case 'f':
3794dc88ebeSGabor Kovesdan 		grepbehave = GREP_FIXED;
3804dc88ebeSGabor Kovesdan 		break;
3814dc88ebeSGabor Kovesdan 	}
3824dc88ebeSGabor Kovesdan 
3834dc88ebeSGabor Kovesdan 	lastc = '\0';
3844dc88ebeSGabor Kovesdan 	newarg = 1;
3854dc88ebeSGabor Kovesdan 	prevoptind = 1;
3864dc88ebeSGabor Kovesdan 	needpattern = 1;
3874dc88ebeSGabor Kovesdan 
3884dc88ebeSGabor Kovesdan 	eopts = getenv("GREP_OPTIONS");
3894dc88ebeSGabor Kovesdan 
39059218eb7SGabor Kovesdan 	/* support for extra arguments in GREP_OPTIONS */
39159218eb7SGabor Kovesdan 	eargc = 0;
392dab19f30SGabor Kovesdan 	if (eopts != NULL && eopts[0] != '\0') {
3934dc88ebeSGabor Kovesdan 		char *str;
3944dc88ebeSGabor Kovesdan 
39559218eb7SGabor Kovesdan 		/* make an estimation of how many extra arguments we have */
39659218eb7SGabor Kovesdan 		for (unsigned int j = 0; j < strlen(eopts); j++)
39759218eb7SGabor Kovesdan 			if (eopts[j] == ' ')
3984dc88ebeSGabor Kovesdan 				eargc++;
3994dc88ebeSGabor Kovesdan 
4004dc88ebeSGabor Kovesdan 		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
4014dc88ebeSGabor Kovesdan 
4024dc88ebeSGabor Kovesdan 		eargc = 0;
40359218eb7SGabor Kovesdan 		/* parse extra arguments */
40459218eb7SGabor Kovesdan 		while ((str = strsep(&eopts, " ")) != NULL)
405dab19f30SGabor Kovesdan 			if (str[0] != '\0')
40659218eb7SGabor Kovesdan 				eargv[eargc++] = grep_strdup(str);
4074dc88ebeSGabor Kovesdan 
4080c41ffb3SXin LI 		aargv = (char **)grep_calloc(eargc + argc + 1,
4090c41ffb3SXin LI 		    sizeof(char *));
41059218eb7SGabor Kovesdan 
4114dc88ebeSGabor Kovesdan 		aargv[0] = argv[0];
41259218eb7SGabor Kovesdan 		for (i = 0; i < eargc; i++)
41359218eb7SGabor Kovesdan 			aargv[i + 1] = eargv[i];
41459218eb7SGabor Kovesdan 		for (int j = 1; j < argc; j++, i++)
41559218eb7SGabor Kovesdan 			aargv[i + 1] = argv[j];
4164dc88ebeSGabor Kovesdan 
41759218eb7SGabor Kovesdan 		aargc = eargc + argc;
4184dc88ebeSGabor Kovesdan 	} else {
4194dc88ebeSGabor Kovesdan 		aargv = argv;
4204dc88ebeSGabor Kovesdan 		aargc = argc;
4214dc88ebeSGabor Kovesdan 	}
4224dc88ebeSGabor Kovesdan 
4234dc88ebeSGabor Kovesdan 	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
4244dc88ebeSGabor Kovesdan 	    -1)) {
4254dc88ebeSGabor Kovesdan 		switch (c) {
4264dc88ebeSGabor Kovesdan 		case '0': case '1': case '2': case '3': case '4':
4274dc88ebeSGabor Kovesdan 		case '5': case '6': case '7': case '8': case '9':
4284dc88ebeSGabor Kovesdan 			if (newarg || !isdigit(lastc))
4294dc88ebeSGabor Kovesdan 				Aflag = 0;
4304dc88ebeSGabor Kovesdan 			else if (Aflag > LLONG_MAX / 10) {
4314dc88ebeSGabor Kovesdan 				errno = ERANGE;
4324dc88ebeSGabor Kovesdan 				err(2, NULL);
4334dc88ebeSGabor Kovesdan 			}
4344dc88ebeSGabor Kovesdan 			Aflag = Bflag = (Aflag * 10) + (c - '0');
4354dc88ebeSGabor Kovesdan 			break;
4364dc88ebeSGabor Kovesdan 		case 'C':
4374dc88ebeSGabor Kovesdan 			if (optarg == NULL) {
4384dc88ebeSGabor Kovesdan 				Aflag = Bflag = 2;
4394dc88ebeSGabor Kovesdan 				break;
4404dc88ebeSGabor Kovesdan 			}
4414dc88ebeSGabor Kovesdan 			/* FALLTHROUGH */
4424dc88ebeSGabor Kovesdan 		case 'A':
4434dc88ebeSGabor Kovesdan 			/* FALLTHROUGH */
4444dc88ebeSGabor Kovesdan 		case 'B':
4454dc88ebeSGabor Kovesdan 			errno = 0;
4464dc88ebeSGabor Kovesdan 			l = strtoull(optarg, &ep, 10);
4474dc88ebeSGabor Kovesdan 			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
4484dc88ebeSGabor Kovesdan 			    ((errno == EINVAL) && (l == 0)))
4494dc88ebeSGabor Kovesdan 				err(2, NULL);
4504dc88ebeSGabor Kovesdan 			else if (ep[0] != '\0') {
4514dc88ebeSGabor Kovesdan 				errno = EINVAL;
4524dc88ebeSGabor Kovesdan 				err(2, NULL);
4534dc88ebeSGabor Kovesdan 			}
4544dc88ebeSGabor Kovesdan 			if (c == 'A')
4554dc88ebeSGabor Kovesdan 				Aflag = l;
4564dc88ebeSGabor Kovesdan 			else if (c == 'B')
4574dc88ebeSGabor Kovesdan 				Bflag = l;
4584dc88ebeSGabor Kovesdan 			else
4594dc88ebeSGabor Kovesdan 				Aflag = Bflag = l;
4604dc88ebeSGabor Kovesdan 			break;
4614dc88ebeSGabor Kovesdan 		case 'a':
4624dc88ebeSGabor Kovesdan 			binbehave = BINFILE_TEXT;
4634dc88ebeSGabor Kovesdan 			break;
4644dc88ebeSGabor Kovesdan 		case 'b':
4654dc88ebeSGabor Kovesdan 			bflag = true;
4664dc88ebeSGabor Kovesdan 			break;
4674dc88ebeSGabor Kovesdan 		case 'c':
4684dc88ebeSGabor Kovesdan 			cflag = true;
4694dc88ebeSGabor Kovesdan 			break;
4704dc88ebeSGabor Kovesdan 		case 'D':
47127116286SGabor Kovesdan 			if (strcasecmp(optarg, "skip") == 0)
4724dc88ebeSGabor Kovesdan 				devbehave = DEV_SKIP;
47327116286SGabor Kovesdan 			else if (strcasecmp(optarg, "read") == 0)
47427116286SGabor Kovesdan 				devbehave = DEV_READ;
47597a012f2SGabor Kovesdan 			else
47697a012f2SGabor Kovesdan 				errx(2, getstr(3), "--devices");
4774dc88ebeSGabor Kovesdan 			break;
4784dc88ebeSGabor Kovesdan 		case 'd':
47927116286SGabor Kovesdan 			if (strcasecmp("recurse", optarg) == 0) {
4804dc88ebeSGabor Kovesdan 				Hflag = true;
4814dc88ebeSGabor Kovesdan 				dirbehave = DIR_RECURSE;
48227116286SGabor Kovesdan 			} else if (strcasecmp("skip", optarg) == 0)
4834dc88ebeSGabor Kovesdan 				dirbehave = DIR_SKIP;
48427116286SGabor Kovesdan 			else if (strcasecmp("read", optarg) == 0)
48527116286SGabor Kovesdan 				dirbehave = DIR_READ;
48697a012f2SGabor Kovesdan 			else
48797a012f2SGabor Kovesdan 				errx(2, getstr(3), "--directories");
4884dc88ebeSGabor Kovesdan 			break;
4894dc88ebeSGabor Kovesdan 		case 'E':
4904dc88ebeSGabor Kovesdan 			grepbehave = GREP_EXTENDED;
4914dc88ebeSGabor Kovesdan 			break;
4924dc88ebeSGabor Kovesdan 		case 'e':
493e411593dSGabor Kovesdan 			{
494e411593dSGabor Kovesdan 				char *token;
495cd64c588SGabor Kovesdan 				char *string = optarg;
496e411593dSGabor Kovesdan 
497e411593dSGabor Kovesdan 				while ((token = strsep(&string, "\n")) != NULL)
498e411593dSGabor Kovesdan 					add_pattern(token, strlen(token));
499e411593dSGabor Kovesdan 			}
5004dc88ebeSGabor Kovesdan 			needpattern = 0;
5014dc88ebeSGabor Kovesdan 			break;
5024dc88ebeSGabor Kovesdan 		case 'F':
5034dc88ebeSGabor Kovesdan 			grepbehave = GREP_FIXED;
5044dc88ebeSGabor Kovesdan 			break;
5054dc88ebeSGabor Kovesdan 		case 'f':
5064dc88ebeSGabor Kovesdan 			read_patterns(optarg);
5074dc88ebeSGabor Kovesdan 			needpattern = 0;
5084dc88ebeSGabor Kovesdan 			break;
5094dc88ebeSGabor Kovesdan 		case 'G':
5104dc88ebeSGabor Kovesdan 			grepbehave = GREP_BASIC;
5114dc88ebeSGabor Kovesdan 			break;
5124dc88ebeSGabor Kovesdan 		case 'H':
5134dc88ebeSGabor Kovesdan 			Hflag = true;
5144dc88ebeSGabor Kovesdan 			break;
5154dc88ebeSGabor Kovesdan 		case 'h':
5164dc88ebeSGabor Kovesdan 			Hflag = false;
5174dc88ebeSGabor Kovesdan 			hflag = true;
5184dc88ebeSGabor Kovesdan 			break;
5194dc88ebeSGabor Kovesdan 		case 'I':
5204dc88ebeSGabor Kovesdan 			binbehave = BINFILE_SKIP;
5214dc88ebeSGabor Kovesdan 			break;
5224dc88ebeSGabor Kovesdan 		case 'i':
5234dc88ebeSGabor Kovesdan 		case 'y':
5244dc88ebeSGabor Kovesdan 			iflag =  true;
5254dc88ebeSGabor Kovesdan 			cflags |= REG_ICASE;
5264dc88ebeSGabor Kovesdan 			break;
5274dc88ebeSGabor Kovesdan 		case 'J':
528afbbd357SGabor Kovesdan #ifdef WITHOUT_BZIP2
529afbbd357SGabor Kovesdan 			errno = EOPNOTSUPP;
530afbbd357SGabor Kovesdan 			err(2, "bzip2 support was disabled at compile-time");
531afbbd357SGabor Kovesdan #endif
5324dc88ebeSGabor Kovesdan 			filebehave = FILE_BZIP;
5334dc88ebeSGabor Kovesdan 			break;
5344dc88ebeSGabor Kovesdan 		case 'L':
5354dc88ebeSGabor Kovesdan 			lflag = false;
53627116286SGabor Kovesdan 			Lflag = true;
5374dc88ebeSGabor Kovesdan 			break;
5384dc88ebeSGabor Kovesdan 		case 'l':
5394dc88ebeSGabor Kovesdan 			Lflag = false;
54027116286SGabor Kovesdan 			lflag = true;
5414dc88ebeSGabor Kovesdan 			break;
5424dc88ebeSGabor Kovesdan 		case 'm':
5434dc88ebeSGabor Kovesdan 			mflag = true;
5444dc88ebeSGabor Kovesdan 			errno = 0;
545924500b7SEitan Adler 			mlimit = mcount = strtoll(optarg, &ep, 10);
546f20f6f3fSGabor Kovesdan 			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
5474dc88ebeSGabor Kovesdan 			    ((errno == EINVAL) && (mcount == 0)))
5484dc88ebeSGabor Kovesdan 				err(2, NULL);
5494dc88ebeSGabor Kovesdan 			else if (ep[0] != '\0') {
5504dc88ebeSGabor Kovesdan 				errno = EINVAL;
5514dc88ebeSGabor Kovesdan 				err(2, NULL);
5524dc88ebeSGabor Kovesdan 			}
5534dc88ebeSGabor Kovesdan 			break;
554f20f6f3fSGabor Kovesdan 		case 'M':
555f20f6f3fSGabor Kovesdan 			filebehave = FILE_LZMA;
556f20f6f3fSGabor Kovesdan 			break;
5574dc88ebeSGabor Kovesdan 		case 'n':
5584dc88ebeSGabor Kovesdan 			nflag = true;
5594dc88ebeSGabor Kovesdan 			break;
5604dc88ebeSGabor Kovesdan 		case 'O':
5614dc88ebeSGabor Kovesdan 			linkbehave = LINK_EXPLICIT;
5624dc88ebeSGabor Kovesdan 			break;
5634dc88ebeSGabor Kovesdan 		case 'o':
5644dc88ebeSGabor Kovesdan 			oflag = true;
56569a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
5664dc88ebeSGabor Kovesdan 			break;
5674dc88ebeSGabor Kovesdan 		case 'p':
5684dc88ebeSGabor Kovesdan 			linkbehave = LINK_SKIP;
5694dc88ebeSGabor Kovesdan 			break;
5704dc88ebeSGabor Kovesdan 		case 'q':
5714dc88ebeSGabor Kovesdan 			qflag = true;
5724dc88ebeSGabor Kovesdan 			break;
5734dc88ebeSGabor Kovesdan 		case 'S':
57427116286SGabor Kovesdan 			linkbehave = LINK_READ;
5754dc88ebeSGabor Kovesdan 			break;
5764dc88ebeSGabor Kovesdan 		case 'R':
5774dc88ebeSGabor Kovesdan 		case 'r':
5784dc88ebeSGabor Kovesdan 			dirbehave = DIR_RECURSE;
5794dc88ebeSGabor Kovesdan 			Hflag = true;
5804dc88ebeSGabor Kovesdan 			break;
5814dc88ebeSGabor Kovesdan 		case 's':
5824dc88ebeSGabor Kovesdan 			sflag = true;
5834dc88ebeSGabor Kovesdan 			break;
5844dc88ebeSGabor Kovesdan 		case 'U':
5854dc88ebeSGabor Kovesdan 			binbehave = BINFILE_BIN;
5864dc88ebeSGabor Kovesdan 			break;
5874dc88ebeSGabor Kovesdan 		case 'u':
5884dc88ebeSGabor Kovesdan 		case MMAP_OPT:
589f20f6f3fSGabor Kovesdan 			filebehave = FILE_MMAP;
5904dc88ebeSGabor Kovesdan 			break;
5914dc88ebeSGabor Kovesdan 		case 'V':
592afbbd357SGabor Kovesdan 			printf(getstr(9), getprogname(), VERSION);
5934dc88ebeSGabor Kovesdan 			exit(0);
5944dc88ebeSGabor Kovesdan 		case 'v':
5954dc88ebeSGabor Kovesdan 			vflag = true;
5964dc88ebeSGabor Kovesdan 			break;
5974dc88ebeSGabor Kovesdan 		case 'w':
5984dc88ebeSGabor Kovesdan 			wflag = true;
59969a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
6004dc88ebeSGabor Kovesdan 			break;
6014dc88ebeSGabor Kovesdan 		case 'x':
6024dc88ebeSGabor Kovesdan 			xflag = true;
60369a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
6044dc88ebeSGabor Kovesdan 			break;
605f20f6f3fSGabor Kovesdan 		case 'X':
606f20f6f3fSGabor Kovesdan 			filebehave = FILE_XZ;
607f20f6f3fSGabor Kovesdan 			break;
6084dc88ebeSGabor Kovesdan 		case 'Z':
6094dc88ebeSGabor Kovesdan 			filebehave = FILE_GZIP;
6104dc88ebeSGabor Kovesdan 			break;
6114dc88ebeSGabor Kovesdan 		case BIN_OPT:
61227116286SGabor Kovesdan 			if (strcasecmp("binary", optarg) == 0)
6134dc88ebeSGabor Kovesdan 				binbehave = BINFILE_BIN;
61427116286SGabor Kovesdan 			else if (strcasecmp("without-match", optarg) == 0)
6154dc88ebeSGabor Kovesdan 				binbehave = BINFILE_SKIP;
61627116286SGabor Kovesdan 			else if (strcasecmp("text", optarg) == 0)
6174dc88ebeSGabor Kovesdan 				binbehave = BINFILE_TEXT;
6184dc88ebeSGabor Kovesdan 			else
61997a012f2SGabor Kovesdan 				errx(2, getstr(3), "--binary-files");
6204dc88ebeSGabor Kovesdan 			break;
6214dc88ebeSGabor Kovesdan 		case COLOR_OPT:
6224dc88ebeSGabor Kovesdan 			color = NULL;
62327116286SGabor Kovesdan 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
62427116286SGabor Kovesdan 			    strcasecmp("tty", optarg) == 0 ||
62527116286SGabor Kovesdan 			    strcasecmp("if-tty", optarg) == 0) {
62627116286SGabor Kovesdan 				char *term;
62727116286SGabor Kovesdan 
62827116286SGabor Kovesdan 				term = getenv("TERM");
62927116286SGabor Kovesdan 				if (isatty(STDOUT_FILENO) && term != NULL &&
63027116286SGabor Kovesdan 				    strcasecmp(term, "dumb") != 0)
63127116286SGabor Kovesdan 					color = init_color("01;31");
63227116286SGabor Kovesdan 			} else if (strcasecmp("always", optarg) == 0 ||
63327116286SGabor Kovesdan 			    strcasecmp("yes", optarg) == 0 ||
63427116286SGabor Kovesdan 			    strcasecmp("force", optarg) == 0) {
63527116286SGabor Kovesdan 				color = init_color("01;31");
63627116286SGabor Kovesdan 			} else if (strcasecmp("never", optarg) != 0 &&
63727116286SGabor Kovesdan 			    strcasecmp("none", optarg) != 0 &&
63827116286SGabor Kovesdan 			    strcasecmp("no", optarg) != 0)
63997a012f2SGabor Kovesdan 				errx(2, getstr(3), "--color");
64069a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
6414dc88ebeSGabor Kovesdan 			break;
6424dc88ebeSGabor Kovesdan 		case LABEL_OPT:
6434dc88ebeSGabor Kovesdan 			label = optarg;
6444dc88ebeSGabor Kovesdan 			break;
6454dc88ebeSGabor Kovesdan 		case LINEBUF_OPT:
6464dc88ebeSGabor Kovesdan 			lbflag = true;
6474dc88ebeSGabor Kovesdan 			break;
6484dc88ebeSGabor Kovesdan 		case NULL_OPT:
6494dc88ebeSGabor Kovesdan 			nullflag = true;
6504dc88ebeSGabor Kovesdan 			break;
6514dc88ebeSGabor Kovesdan 		case R_INCLUDE_OPT:
65255e44f51SGabor Kovesdan 			finclude = true;
65355e44f51SGabor Kovesdan 			add_fpattern(optarg, INCL_PAT);
6544dc88ebeSGabor Kovesdan 			break;
6554dc88ebeSGabor Kovesdan 		case R_EXCLUDE_OPT:
65655e44f51SGabor Kovesdan 			fexclude = true;
65755e44f51SGabor Kovesdan 			add_fpattern(optarg, EXCL_PAT);
6584dc88ebeSGabor Kovesdan 			break;
6594dc88ebeSGabor Kovesdan 		case R_DINCLUDE_OPT:
66059218eb7SGabor Kovesdan 			dinclude = true;
66155e44f51SGabor Kovesdan 			add_dpattern(optarg, INCL_PAT);
6624dc88ebeSGabor Kovesdan 			break;
6634dc88ebeSGabor Kovesdan 		case R_DEXCLUDE_OPT:
66459218eb7SGabor Kovesdan 			dexclude = true;
66555e44f51SGabor Kovesdan 			add_dpattern(optarg, EXCL_PAT);
6664dc88ebeSGabor Kovesdan 			break;
6674dc88ebeSGabor Kovesdan 		case HELP_OPT:
6684dc88ebeSGabor Kovesdan 		default:
6694dc88ebeSGabor Kovesdan 			usage();
6704dc88ebeSGabor Kovesdan 		}
6714dc88ebeSGabor Kovesdan 		lastc = c;
6724dc88ebeSGabor Kovesdan 		newarg = optind != prevoptind;
6734dc88ebeSGabor Kovesdan 		prevoptind = optind;
6744dc88ebeSGabor Kovesdan 	}
6754dc88ebeSGabor Kovesdan 	aargc -= optind;
6764dc88ebeSGabor Kovesdan 	aargv += optind;
6774dc88ebeSGabor Kovesdan 
678f20f6f3fSGabor Kovesdan 	/* Empty pattern file matches nothing */
679f20f6f3fSGabor Kovesdan 	if (!needpattern && (patterns == 0))
680f20f6f3fSGabor Kovesdan 		exit(1);
681f20f6f3fSGabor Kovesdan 
6824dc88ebeSGabor Kovesdan 	/* Fail if we don't have any pattern */
6834dc88ebeSGabor Kovesdan 	if (aargc == 0 && needpattern)
6844dc88ebeSGabor Kovesdan 		usage();
6854dc88ebeSGabor Kovesdan 
6864dc88ebeSGabor Kovesdan 	/* Process patterns from command line */
6874dc88ebeSGabor Kovesdan 	if (aargc != 0 && needpattern) {
688e411593dSGabor Kovesdan 		char *token;
689cd64c588SGabor Kovesdan 		char *string = *aargv;
690e411593dSGabor Kovesdan 
691e411593dSGabor Kovesdan 		while ((token = strsep(&string, "\n")) != NULL)
692e411593dSGabor Kovesdan 			add_pattern(token, strlen(token));
6934dc88ebeSGabor Kovesdan 		--aargc;
6944dc88ebeSGabor Kovesdan 		++aargv;
6954dc88ebeSGabor Kovesdan 	}
6964dc88ebeSGabor Kovesdan 
6974dc88ebeSGabor Kovesdan 	switch (grepbehave) {
6984dc88ebeSGabor Kovesdan 	case GREP_BASIC:
6994dc88ebeSGabor Kovesdan 		break;
700f20f6f3fSGabor Kovesdan 	case GREP_FIXED:
701f20f6f3fSGabor Kovesdan 		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
702f20f6f3fSGabor Kovesdan 		cflags |= 0020;
703f20f6f3fSGabor Kovesdan 		break;
7044dc88ebeSGabor Kovesdan 	case GREP_EXTENDED:
7054dc88ebeSGabor Kovesdan 		cflags |= REG_EXTENDED;
7064dc88ebeSGabor Kovesdan 		break;
7074dc88ebeSGabor Kovesdan 	default:
7084dc88ebeSGabor Kovesdan 		/* NOTREACHED */
7094dc88ebeSGabor Kovesdan 		usage();
7104dc88ebeSGabor Kovesdan 	}
7114dc88ebeSGabor Kovesdan 
7124dc88ebeSGabor Kovesdan 	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
7134dc88ebeSGabor Kovesdan 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
714f20f6f3fSGabor Kovesdan 
7154dc88ebeSGabor Kovesdan 	/* Check if cheating is allowed (always is for fgrep). */
7164dc88ebeSGabor Kovesdan 	for (i = 0; i < patterns; ++i) {
717f20f6f3fSGabor Kovesdan 		if (fastncomp(&fg_pattern[i], pattern[i].pat,
718f20f6f3fSGabor Kovesdan 		    pattern[i].len, cflags) != 0) {
7194dc88ebeSGabor Kovesdan 			/* Fall back to full regex library */
720f20f6f3fSGabor Kovesdan 			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
7214dc88ebeSGabor Kovesdan 			if (c != 0) {
7224dc88ebeSGabor Kovesdan 				regerror(c, &r_pattern[i], re_error,
7234dc88ebeSGabor Kovesdan 				    RE_ERROR_BUF);
7244dc88ebeSGabor Kovesdan 				errx(2, "%s", re_error);
7254dc88ebeSGabor Kovesdan 			}
7264dc88ebeSGabor Kovesdan 		}
7274dc88ebeSGabor Kovesdan 	}
7284dc88ebeSGabor Kovesdan 
7294dc88ebeSGabor Kovesdan 	if (lbflag)
7304dc88ebeSGabor Kovesdan 		setlinebuf(stdout);
7314dc88ebeSGabor Kovesdan 
7324dc88ebeSGabor Kovesdan 	if ((aargc == 0 || aargc == 1) && !Hflag)
7334dc88ebeSGabor Kovesdan 		hflag = true;
7344dc88ebeSGabor Kovesdan 
7354dc88ebeSGabor Kovesdan 	if (aargc == 0)
7364dc88ebeSGabor Kovesdan 		exit(!procfile("-"));
7374dc88ebeSGabor Kovesdan 
7384dc88ebeSGabor Kovesdan 	if (dirbehave == DIR_RECURSE)
7394dc88ebeSGabor Kovesdan 		c = grep_tree(aargv);
740c38208adSXin LI 	else
74155e44f51SGabor Kovesdan 		for (c = 0; aargc--; ++aargv) {
74255e44f51SGabor Kovesdan 			if ((finclude || fexclude) && !file_matching(*aargv))
74355e44f51SGabor Kovesdan 				continue;
7444dc88ebeSGabor Kovesdan 			c+= procfile(*aargv);
74555e44f51SGabor Kovesdan 		}
7464dc88ebeSGabor Kovesdan 
7474dc88ebeSGabor Kovesdan #ifndef WITHOUT_NLS
7484dc88ebeSGabor Kovesdan 	catclose(catalog);
7494dc88ebeSGabor Kovesdan #endif
7504dc88ebeSGabor Kovesdan 
7514dc88ebeSGabor Kovesdan 	/* Find out the correct return value according to the
7524dc88ebeSGabor Kovesdan 	   results and the command line option. */
7536f4cbf7cSGabor Kovesdan 	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
7544dc88ebeSGabor Kovesdan }
755