xref: /freebsd/usr.bin/grep/grep.c (revision a4f3f02be6995f004fe520eb92414886e5138760)
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 
523f39ffc8SEd Maste #ifndef WITHOUT_FASTMATCH
53f20f6f3fSGabor Kovesdan #include "fastmatch.h"
543f39ffc8SEd Maste #endif
554dc88ebeSGabor Kovesdan #include "grep.h"
564dc88ebeSGabor Kovesdan 
574dc88ebeSGabor Kovesdan #ifndef WITHOUT_NLS
584dc88ebeSGabor Kovesdan #include <nl_types.h>
594dc88ebeSGabor Kovesdan nl_catd	 catalog;
604dc88ebeSGabor Kovesdan #endif
614dc88ebeSGabor Kovesdan 
624dc88ebeSGabor Kovesdan /*
634dc88ebeSGabor Kovesdan  * Default messags to use when NLS is disabled or no catalogue
644dc88ebeSGabor Kovesdan  * is found.
654dc88ebeSGabor Kovesdan  */
664dc88ebeSGabor Kovesdan const char	*errstr[] = {
674dc88ebeSGabor Kovesdan 	"",
684dc88ebeSGabor Kovesdan /* 1*/	"(standard input)",
694dc88ebeSGabor Kovesdan /* 2*/	"cannot read bzip2 compressed file",
7097a012f2SGabor Kovesdan /* 3*/	"unknown %s option",
715ee1ea02SEd Maste /* 4*/	"usage: %s [-abcDEFGHhIiJLlmnOoPqRSsUVvwxZz] [-A num] [-B num] [-C[num]]\n",
724dc88ebeSGabor Kovesdan /* 5*/	"\t[-e pattern] [-f file] [--binary-files=value] [--color=when]\n",
734dc88ebeSGabor Kovesdan /* 6*/	"\t[--context[=num]] [--directories=action] [--label] [--line-buffered]\n",
744dc88ebeSGabor Kovesdan /* 7*/	"\t[--null] [pattern] [file ...]\n",
7597a012f2SGabor Kovesdan /* 8*/	"Binary file %s matches\n",
7697a012f2SGabor Kovesdan /* 9*/	"%s (BSD grep) %s\n",
77cc41ba26SEd Maste /* 10*/	"%s (BSD grep, GNU compatible) %s\n",
784dc88ebeSGabor Kovesdan };
794dc88ebeSGabor Kovesdan 
804dc88ebeSGabor Kovesdan /* Flags passed to regcomp() and regexec() */
8169a6d198SGabor Kovesdan int		 cflags = REG_NOSUB;
824dc88ebeSGabor Kovesdan int		 eflags = REG_STARTEND;
834dc88ebeSGabor Kovesdan 
84*a4f3f02bSEd Maste /* XXX TODO: Get rid of this flag.
85*a4f3f02bSEd Maste  * matchall is a gross hack that means that an empty pattern was passed to us.
86*a4f3f02bSEd Maste  * It is a necessary evil at the moment because our regex(3) implementation
87*a4f3f02bSEd Maste  * does not allow for empty patterns, as supported by POSIX's definition of
88*a4f3f02bSEd Maste  * grammar for BREs/EREs. When libregex becomes available, it would be wise
89*a4f3f02bSEd Maste  * to remove this and let regex(3) handle the dirty details of empty patterns.
90*a4f3f02bSEd Maste  */
914dc88ebeSGabor Kovesdan bool		 matchall;
924dc88ebeSGabor Kovesdan 
934dc88ebeSGabor Kovesdan /* Searching patterns */
94bf70beceSEd Schouten unsigned int	 patterns;
95bf70beceSEd Schouten static unsigned int pattern_sz;
96f20f6f3fSGabor Kovesdan struct pat	*pattern;
974dc88ebeSGabor Kovesdan regex_t		*r_pattern;
983f39ffc8SEd Maste #ifndef WITHOUT_FASTMATCH
99f20f6f3fSGabor Kovesdan fastmatch_t	*fg_pattern;
1003f39ffc8SEd Maste #endif
1014dc88ebeSGabor Kovesdan 
1024dc88ebeSGabor Kovesdan /* Filename exclusion/inclusion patterns */
103bf70beceSEd Schouten unsigned int	fpatterns, dpatterns;
104bf70beceSEd Schouten static unsigned int fpattern_sz, dpattern_sz;
10555e44f51SGabor Kovesdan struct epat	*dpattern, *fpattern;
1064dc88ebeSGabor Kovesdan 
1074dc88ebeSGabor Kovesdan /* For regex errors  */
1084dc88ebeSGabor Kovesdan char	 re_error[RE_ERROR_BUF + 1];
1094dc88ebeSGabor Kovesdan 
1104dc88ebeSGabor Kovesdan /* Command-line flags */
1114dc88ebeSGabor Kovesdan unsigned long long Aflag;	/* -A x: print x lines trailing each match */
1124dc88ebeSGabor Kovesdan unsigned long long Bflag;	/* -B x: print x lines leading each match */
1134dc88ebeSGabor Kovesdan bool	 Hflag;		/* -H: always print file name */
1144dc88ebeSGabor Kovesdan bool	 Lflag;		/* -L: only show names of files with no matches */
1154dc88ebeSGabor Kovesdan bool	 bflag;		/* -b: show block numbers for each match */
1164dc88ebeSGabor Kovesdan bool	 cflag;		/* -c: only show a count of matching lines */
1174dc88ebeSGabor Kovesdan bool	 hflag;		/* -h: don't print filename headers */
1184dc88ebeSGabor Kovesdan bool	 iflag;		/* -i: ignore case */
1194dc88ebeSGabor Kovesdan bool	 lflag;		/* -l: only show names of files with matches */
1204dc88ebeSGabor Kovesdan bool	 mflag;		/* -m x: stop reading the files after x matches */
121f20f6f3fSGabor Kovesdan long long mcount;	/* count for -m */
122924500b7SEitan Adler long long mlimit;	/* requested value for -m */
1235ee1ea02SEd Maste char	 fileeol;	/* indicator for eol */
1244dc88ebeSGabor Kovesdan bool	 nflag;		/* -n: show line numbers in front of matching lines */
1254dc88ebeSGabor Kovesdan bool	 oflag;		/* -o: print only matching part */
1264dc88ebeSGabor Kovesdan bool	 qflag;		/* -q: quiet mode (don't output anything) */
1274dc88ebeSGabor Kovesdan bool	 sflag;		/* -s: silent mode (ignore errors) */
1284dc88ebeSGabor Kovesdan bool	 vflag;		/* -v: only show non-matching lines */
1294dc88ebeSGabor Kovesdan bool	 wflag;		/* -w: pattern must start and end on word boundaries */
1304dc88ebeSGabor Kovesdan bool	 xflag;		/* -x: pattern must match entire line */
1314dc88ebeSGabor Kovesdan bool	 lbflag;	/* --line-buffered */
1324dc88ebeSGabor Kovesdan bool	 nullflag;	/* --null */
1334dc88ebeSGabor Kovesdan char	*label;		/* --label */
13427116286SGabor Kovesdan const char *color;	/* --color */
1354dc88ebeSGabor Kovesdan int	 grepbehave = GREP_BASIC;	/* -EFGP: type of the regex */
1364dc88ebeSGabor Kovesdan int	 binbehave = BINFILE_BIN;	/* -aIU: handling of binary files */
1374dc88ebeSGabor Kovesdan int	 filebehave = FILE_STDIO;	/* -JZ: normal, gzip or bzip2 file */
13827116286SGabor Kovesdan int	 devbehave = DEV_READ;		/* -D: handling of devices */
13927116286SGabor Kovesdan int	 dirbehave = DIR_READ;		/* -dRr: handling of directories */
14027116286SGabor Kovesdan int	 linkbehave = LINK_READ;	/* -OpS: handling of symlinks */
1414dc88ebeSGabor Kovesdan 
14259218eb7SGabor Kovesdan bool	 dexclude, dinclude;	/* --exclude-dir and --include-dir */
14359218eb7SGabor Kovesdan bool	 fexclude, finclude;	/* --exclude and --include */
14455e44f51SGabor Kovesdan 
1454dc88ebeSGabor Kovesdan enum {
1464dc88ebeSGabor Kovesdan 	BIN_OPT = CHAR_MAX + 1,
1474dc88ebeSGabor Kovesdan 	COLOR_OPT,
1484dc88ebeSGabor Kovesdan 	HELP_OPT,
1494dc88ebeSGabor Kovesdan 	MMAP_OPT,
1504dc88ebeSGabor Kovesdan 	LINEBUF_OPT,
1514dc88ebeSGabor Kovesdan 	LABEL_OPT,
1524dc88ebeSGabor Kovesdan 	NULL_OPT,
1534dc88ebeSGabor Kovesdan 	R_EXCLUDE_OPT,
1544dc88ebeSGabor Kovesdan 	R_INCLUDE_OPT,
1554dc88ebeSGabor Kovesdan 	R_DEXCLUDE_OPT,
1564dc88ebeSGabor Kovesdan 	R_DINCLUDE_OPT
1574dc88ebeSGabor Kovesdan };
1584dc88ebeSGabor Kovesdan 
15927116286SGabor Kovesdan static inline const char	*init_color(const char *);
16027116286SGabor Kovesdan 
1614dc88ebeSGabor Kovesdan /* Housekeeping */
1626f4cbf7cSGabor Kovesdan bool	 file_err;	/* file reading error */
1634dc88ebeSGabor Kovesdan 
1644dc88ebeSGabor Kovesdan /*
1654dc88ebeSGabor Kovesdan  * Prints usage information and returns 2.
1664dc88ebeSGabor Kovesdan  */
1674dc88ebeSGabor Kovesdan static void
1684dc88ebeSGabor Kovesdan usage(void)
1694dc88ebeSGabor Kovesdan {
170afbbd357SGabor Kovesdan 	fprintf(stderr, getstr(4), getprogname());
1714dc88ebeSGabor Kovesdan 	fprintf(stderr, "%s", getstr(5));
1724dc88ebeSGabor Kovesdan 	fprintf(stderr, "%s", getstr(6));
1734dc88ebeSGabor Kovesdan 	fprintf(stderr, "%s", getstr(7));
1744dc88ebeSGabor Kovesdan 	exit(2);
1754dc88ebeSGabor Kovesdan }
1764dc88ebeSGabor Kovesdan 
1775ee1ea02SEd Maste static const char	*optstr = "0123456789A:B:C:D:EFGHIJMLOPSRUVZabcd:e:f:hilm:nopqrsuvwxXyz";
1784dc88ebeSGabor Kovesdan 
1798375d512SEd Schouten static const struct option long_options[] =
1804dc88ebeSGabor Kovesdan {
1814dc88ebeSGabor Kovesdan 	{"binary-files",	required_argument,	NULL, BIN_OPT},
1824dc88ebeSGabor Kovesdan 	{"help",		no_argument,		NULL, HELP_OPT},
1834dc88ebeSGabor Kovesdan 	{"mmap",		no_argument,		NULL, MMAP_OPT},
1844dc88ebeSGabor Kovesdan 	{"line-buffered",	no_argument,		NULL, LINEBUF_OPT},
1854dc88ebeSGabor Kovesdan 	{"label",		required_argument,	NULL, LABEL_OPT},
1864dc88ebeSGabor Kovesdan 	{"null",		no_argument,		NULL, NULL_OPT},
1874dc88ebeSGabor Kovesdan 	{"color",		optional_argument,	NULL, COLOR_OPT},
1884dc88ebeSGabor Kovesdan 	{"colour",		optional_argument,	NULL, COLOR_OPT},
1894dc88ebeSGabor Kovesdan 	{"exclude",		required_argument,	NULL, R_EXCLUDE_OPT},
1904dc88ebeSGabor Kovesdan 	{"include",		required_argument,	NULL, R_INCLUDE_OPT},
1914dc88ebeSGabor Kovesdan 	{"exclude-dir",		required_argument,	NULL, R_DEXCLUDE_OPT},
1924dc88ebeSGabor Kovesdan 	{"include-dir",		required_argument,	NULL, R_DINCLUDE_OPT},
1934dc88ebeSGabor Kovesdan 	{"after-context",	required_argument,	NULL, 'A'},
1944dc88ebeSGabor Kovesdan 	{"text",		no_argument,		NULL, 'a'},
1954dc88ebeSGabor Kovesdan 	{"before-context",	required_argument,	NULL, 'B'},
1964dc88ebeSGabor Kovesdan 	{"byte-offset",		no_argument,		NULL, 'b'},
1974dc88ebeSGabor Kovesdan 	{"context",		optional_argument,	NULL, 'C'},
1984dc88ebeSGabor Kovesdan 	{"count",		no_argument,		NULL, 'c'},
1994dc88ebeSGabor Kovesdan 	{"devices",		required_argument,	NULL, 'D'},
2004dc88ebeSGabor Kovesdan         {"directories",		required_argument,	NULL, 'd'},
2014dc88ebeSGabor Kovesdan 	{"extended-regexp",	no_argument,		NULL, 'E'},
2024dc88ebeSGabor Kovesdan 	{"regexp",		required_argument,	NULL, 'e'},
2034dc88ebeSGabor Kovesdan 	{"fixed-strings",	no_argument,		NULL, 'F'},
2044dc88ebeSGabor Kovesdan 	{"file",		required_argument,	NULL, 'f'},
2054dc88ebeSGabor Kovesdan 	{"basic-regexp",	no_argument,		NULL, 'G'},
2064dc88ebeSGabor Kovesdan 	{"no-filename",		no_argument,		NULL, 'h'},
2074dc88ebeSGabor Kovesdan 	{"with-filename",	no_argument,		NULL, 'H'},
2084dc88ebeSGabor Kovesdan 	{"ignore-case",		no_argument,		NULL, 'i'},
2094dc88ebeSGabor Kovesdan 	{"bz2decompress",	no_argument,		NULL, 'J'},
2104dc88ebeSGabor Kovesdan 	{"files-with-matches",	no_argument,		NULL, 'l'},
2114dc88ebeSGabor Kovesdan 	{"files-without-match", no_argument,            NULL, 'L'},
2124dc88ebeSGabor Kovesdan 	{"max-count",		required_argument,	NULL, 'm'},
213f20f6f3fSGabor Kovesdan 	{"lzma",		no_argument,		NULL, 'M'},
2144dc88ebeSGabor Kovesdan 	{"line-number",		no_argument,		NULL, 'n'},
2154dc88ebeSGabor Kovesdan 	{"only-matching",	no_argument,		NULL, 'o'},
2164dc88ebeSGabor Kovesdan 	{"quiet",		no_argument,		NULL, 'q'},
2174dc88ebeSGabor Kovesdan 	{"silent",		no_argument,		NULL, 'q'},
2184dc88ebeSGabor Kovesdan 	{"recursive",		no_argument,		NULL, 'r'},
2194dc88ebeSGabor Kovesdan 	{"no-messages",		no_argument,		NULL, 's'},
2204dc88ebeSGabor Kovesdan 	{"binary",		no_argument,		NULL, 'U'},
2214dc88ebeSGabor Kovesdan 	{"unix-byte-offsets",	no_argument,		NULL, 'u'},
2224dc88ebeSGabor Kovesdan 	{"invert-match",	no_argument,		NULL, 'v'},
2234dc88ebeSGabor Kovesdan 	{"version",		no_argument,		NULL, 'V'},
2244dc88ebeSGabor Kovesdan 	{"word-regexp",		no_argument,		NULL, 'w'},
2254dc88ebeSGabor Kovesdan 	{"line-regexp",		no_argument,		NULL, 'x'},
226f20f6f3fSGabor Kovesdan 	{"xz",			no_argument,		NULL, 'X'},
2275ee1ea02SEd Maste 	{"null-data",		no_argument,		NULL, 'z'},
2284dc88ebeSGabor Kovesdan 	{"decompress",          no_argument,            NULL, 'Z'},
2294dc88ebeSGabor Kovesdan 	{NULL,			no_argument,		NULL, 0}
2304dc88ebeSGabor Kovesdan };
2314dc88ebeSGabor Kovesdan 
2324dc88ebeSGabor Kovesdan /*
2334dc88ebeSGabor Kovesdan  * Adds a searching pattern to the internal array.
2344dc88ebeSGabor Kovesdan  */
2354dc88ebeSGabor Kovesdan static void
2364dc88ebeSGabor Kovesdan add_pattern(char *pat, size_t len)
2374dc88ebeSGabor Kovesdan {
2384dc88ebeSGabor Kovesdan 
239f20f6f3fSGabor Kovesdan 	/* Do not add further pattern is we already match everything */
240f20f6f3fSGabor Kovesdan 	if (matchall)
241f20f6f3fSGabor Kovesdan 	  return;
242f20f6f3fSGabor Kovesdan 
2434dc88ebeSGabor Kovesdan 	/* Check if we can do a shortcut */
244f20f6f3fSGabor Kovesdan 	if (len == 0) {
2454dc88ebeSGabor Kovesdan 		matchall = true;
246f20f6f3fSGabor Kovesdan 		for (unsigned int i = 0; i < patterns; i++) {
247f20f6f3fSGabor Kovesdan 			free(pattern[i].pat);
248f20f6f3fSGabor Kovesdan 		}
249f20f6f3fSGabor Kovesdan 		pattern = grep_realloc(pattern, sizeof(struct pat));
250f20f6f3fSGabor Kovesdan 		pattern[0].pat = NULL;
251f20f6f3fSGabor Kovesdan 		pattern[0].len = 0;
252f20f6f3fSGabor Kovesdan 		patterns = 1;
2534dc88ebeSGabor Kovesdan 		return;
2544dc88ebeSGabor Kovesdan 	}
2554dc88ebeSGabor Kovesdan 	/* Increase size if necessary */
2564dc88ebeSGabor Kovesdan 	if (patterns == pattern_sz) {
2574dc88ebeSGabor Kovesdan 		pattern_sz *= 2;
2584dc88ebeSGabor Kovesdan 		pattern = grep_realloc(pattern, ++pattern_sz *
259f20f6f3fSGabor Kovesdan 		    sizeof(struct pat));
2604dc88ebeSGabor Kovesdan 	}
2614dc88ebeSGabor Kovesdan 	if (len > 0 && pat[len - 1] == '\n')
2624dc88ebeSGabor Kovesdan 		--len;
2634dc88ebeSGabor Kovesdan 	/* pat may not be NUL-terminated */
264f20f6f3fSGabor Kovesdan 	pattern[patterns].pat = grep_malloc(len + 1);
265f20f6f3fSGabor Kovesdan 	memcpy(pattern[patterns].pat, pat, len);
266f20f6f3fSGabor Kovesdan 	pattern[patterns].len = len;
267f20f6f3fSGabor Kovesdan 	pattern[patterns].pat[len] = '\0';
2684dc88ebeSGabor Kovesdan 	++patterns;
2694dc88ebeSGabor Kovesdan }
2704dc88ebeSGabor Kovesdan 
2714dc88ebeSGabor Kovesdan /*
27255e44f51SGabor Kovesdan  * Adds a file include/exclude pattern to the internal array.
2734dc88ebeSGabor Kovesdan  */
2744dc88ebeSGabor Kovesdan static void
27555e44f51SGabor Kovesdan add_fpattern(const char *pat, int mode)
2764dc88ebeSGabor Kovesdan {
2774dc88ebeSGabor Kovesdan 
2784dc88ebeSGabor Kovesdan 	/* Increase size if necessary */
27955e44f51SGabor Kovesdan 	if (fpatterns == fpattern_sz) {
28055e44f51SGabor Kovesdan 		fpattern_sz *= 2;
28155e44f51SGabor Kovesdan 		fpattern = grep_realloc(fpattern, ++fpattern_sz *
2824dc88ebeSGabor Kovesdan 		    sizeof(struct epat));
2834dc88ebeSGabor Kovesdan 	}
28455e44f51SGabor Kovesdan 	fpattern[fpatterns].pat = grep_strdup(pat);
28555e44f51SGabor Kovesdan 	fpattern[fpatterns].mode = mode;
28655e44f51SGabor Kovesdan 	++fpatterns;
28755e44f51SGabor Kovesdan }
28855e44f51SGabor Kovesdan 
28955e44f51SGabor Kovesdan /*
29055e44f51SGabor Kovesdan  * Adds a directory include/exclude pattern to the internal array.
29155e44f51SGabor Kovesdan  */
29255e44f51SGabor Kovesdan static void
29355e44f51SGabor Kovesdan add_dpattern(const char *pat, int mode)
29455e44f51SGabor Kovesdan {
29555e44f51SGabor Kovesdan 
29655e44f51SGabor Kovesdan 	/* Increase size if necessary */
29755e44f51SGabor Kovesdan 	if (dpatterns == dpattern_sz) {
29855e44f51SGabor Kovesdan 		dpattern_sz *= 2;
29955e44f51SGabor Kovesdan 		dpattern = grep_realloc(dpattern, ++dpattern_sz *
30055e44f51SGabor Kovesdan 		    sizeof(struct epat));
30155e44f51SGabor Kovesdan 	}
30255e44f51SGabor Kovesdan 	dpattern[dpatterns].pat = grep_strdup(pat);
30355e44f51SGabor Kovesdan 	dpattern[dpatterns].mode = mode;
30455e44f51SGabor Kovesdan 	++dpatterns;
3054dc88ebeSGabor Kovesdan }
3064dc88ebeSGabor Kovesdan 
3074dc88ebeSGabor Kovesdan /*
3084dc88ebeSGabor Kovesdan  * Reads searching patterns from a file and adds them with add_pattern().
3094dc88ebeSGabor Kovesdan  */
3104dc88ebeSGabor Kovesdan static void
3114dc88ebeSGabor Kovesdan read_patterns(const char *fn)
3124dc88ebeSGabor Kovesdan {
313f20f6f3fSGabor Kovesdan 	struct stat st;
3144dc88ebeSGabor Kovesdan 	FILE *f;
3154dc88ebeSGabor Kovesdan 	char *line;
3164dc88ebeSGabor Kovesdan 	size_t len;
317f3f50de6SPedro F. Giffuni 	ssize_t rlen;
3184dc88ebeSGabor Kovesdan 
3194dc88ebeSGabor Kovesdan 	if ((f = fopen(fn, "r")) == NULL)
3204dc88ebeSGabor Kovesdan 		err(2, "%s", fn);
321f20f6f3fSGabor Kovesdan 	if ((fstat(fileno(f), &st) == -1) || (S_ISDIR(st.st_mode))) {
322f20f6f3fSGabor Kovesdan 		fclose(f);
323f20f6f3fSGabor Kovesdan 		return;
324f20f6f3fSGabor Kovesdan 	}
325f3f50de6SPedro F. Giffuni 	len = 0;
326f3f50de6SPedro F. Giffuni 	line = NULL;
327d204af1eSEd Maste 	while ((rlen = getline(&line, &len, f)) != -1) {
328d204af1eSEd Maste 		if (line[0] == '\0')
329d204af1eSEd Maste 			continue;
3302fa7a2afSPedro F. Giffuni 		add_pattern(line, line[0] == '\n' ? 0 : (size_t)rlen);
331d204af1eSEd Maste 	}
332d204af1eSEd Maste 
333f3f50de6SPedro F. Giffuni 	free(line);
3344dc88ebeSGabor Kovesdan 	if (ferror(f))
3354dc88ebeSGabor Kovesdan 		err(2, "%s", fn);
3364dc88ebeSGabor Kovesdan 	fclose(f);
3374dc88ebeSGabor Kovesdan }
3384dc88ebeSGabor Kovesdan 
33927116286SGabor Kovesdan static inline const char *
34027116286SGabor Kovesdan init_color(const char *d)
34127116286SGabor Kovesdan {
34227116286SGabor Kovesdan 	char *c;
34327116286SGabor Kovesdan 
34427116286SGabor Kovesdan 	c = getenv("GREP_COLOR");
345dab19f30SGabor Kovesdan 	return (c != NULL && c[0] != '\0' ? c : d);
34627116286SGabor Kovesdan }
34727116286SGabor Kovesdan 
3484dc88ebeSGabor Kovesdan int
3494dc88ebeSGabor Kovesdan main(int argc, char *argv[])
3504dc88ebeSGabor Kovesdan {
3514dc88ebeSGabor Kovesdan 	char **aargv, **eargv, *eopts;
352afbbd357SGabor Kovesdan 	char *ep;
353afbbd357SGabor Kovesdan 	const char *pn;
3544dc88ebeSGabor Kovesdan 	unsigned long long l;
3554dc88ebeSGabor Kovesdan 	unsigned int aargc, eargc, i;
3564dc88ebeSGabor Kovesdan 	int c, lastc, needpattern, newarg, prevoptind;
3574dc88ebeSGabor Kovesdan 
3584dc88ebeSGabor Kovesdan 	setlocale(LC_ALL, "");
3594dc88ebeSGabor Kovesdan 
3604dc88ebeSGabor Kovesdan #ifndef WITHOUT_NLS
3614dc88ebeSGabor Kovesdan 	catalog = catopen("grep", NL_CAT_LOCALE);
3624dc88ebeSGabor Kovesdan #endif
3634dc88ebeSGabor Kovesdan 
3644dc88ebeSGabor Kovesdan 	/* Check what is the program name of the binary.  In this
3654dc88ebeSGabor Kovesdan 	   way we can have all the funcionalities in one binary
3664dc88ebeSGabor Kovesdan 	   without the need of scripting and using ugly hacks. */
367afbbd357SGabor Kovesdan 	pn = getprogname();
368f20f6f3fSGabor Kovesdan 	if (pn[0] == 'b' && pn[1] == 'z') {
369f20f6f3fSGabor Kovesdan 		filebehave = FILE_BZIP;
370f20f6f3fSGabor Kovesdan 		pn += 2;
371f20f6f3fSGabor Kovesdan 	} else if (pn[0] == 'x' && pn[1] == 'z') {
372f20f6f3fSGabor Kovesdan 		filebehave = FILE_XZ;
373f20f6f3fSGabor Kovesdan 		pn += 2;
374f20f6f3fSGabor Kovesdan 	} else if (pn[0] == 'l' && pn[1] == 'z') {
375f20f6f3fSGabor Kovesdan 		filebehave = FILE_LZMA;
376f20f6f3fSGabor Kovesdan 		pn += 2;
377491b8b16SEd Maste 	} else if (pn[0] == 'r') {
378491b8b16SEd Maste 		dirbehave = DIR_RECURSE;
379491b8b16SEd Maste 		Hflag = true;
380f20f6f3fSGabor Kovesdan 	} else if (pn[0] == 'z') {
3814dc88ebeSGabor Kovesdan 		filebehave = FILE_GZIP;
382f20f6f3fSGabor Kovesdan 		pn += 1;
383f20f6f3fSGabor Kovesdan 	}
384f20f6f3fSGabor Kovesdan 	switch (pn[0]) {
3854dc88ebeSGabor Kovesdan 	case 'e':
3864dc88ebeSGabor Kovesdan 		grepbehave = GREP_EXTENDED;
3874dc88ebeSGabor Kovesdan 		break;
3884dc88ebeSGabor Kovesdan 	case 'f':
3894dc88ebeSGabor Kovesdan 		grepbehave = GREP_FIXED;
3904dc88ebeSGabor Kovesdan 		break;
3914dc88ebeSGabor Kovesdan 	}
3924dc88ebeSGabor Kovesdan 
3934dc88ebeSGabor Kovesdan 	lastc = '\0';
3944dc88ebeSGabor Kovesdan 	newarg = 1;
3954dc88ebeSGabor Kovesdan 	prevoptind = 1;
3964dc88ebeSGabor Kovesdan 	needpattern = 1;
3975ee1ea02SEd Maste 	fileeol = '\n';
3984dc88ebeSGabor Kovesdan 
3994dc88ebeSGabor Kovesdan 	eopts = getenv("GREP_OPTIONS");
4004dc88ebeSGabor Kovesdan 
40159218eb7SGabor Kovesdan 	/* support for extra arguments in GREP_OPTIONS */
40259218eb7SGabor Kovesdan 	eargc = 0;
403dab19f30SGabor Kovesdan 	if (eopts != NULL && eopts[0] != '\0') {
4044dc88ebeSGabor Kovesdan 		char *str;
4054dc88ebeSGabor Kovesdan 
40659218eb7SGabor Kovesdan 		/* make an estimation of how many extra arguments we have */
40759218eb7SGabor Kovesdan 		for (unsigned int j = 0; j < strlen(eopts); j++)
40859218eb7SGabor Kovesdan 			if (eopts[j] == ' ')
4094dc88ebeSGabor Kovesdan 				eargc++;
4104dc88ebeSGabor Kovesdan 
4114dc88ebeSGabor Kovesdan 		eargv = (char **)grep_malloc(sizeof(char *) * (eargc + 1));
4124dc88ebeSGabor Kovesdan 
4134dc88ebeSGabor Kovesdan 		eargc = 0;
41459218eb7SGabor Kovesdan 		/* parse extra arguments */
41559218eb7SGabor Kovesdan 		while ((str = strsep(&eopts, " ")) != NULL)
416dab19f30SGabor Kovesdan 			if (str[0] != '\0')
41759218eb7SGabor Kovesdan 				eargv[eargc++] = grep_strdup(str);
4184dc88ebeSGabor Kovesdan 
4190c41ffb3SXin LI 		aargv = (char **)grep_calloc(eargc + argc + 1,
4200c41ffb3SXin LI 		    sizeof(char *));
42159218eb7SGabor Kovesdan 
4224dc88ebeSGabor Kovesdan 		aargv[0] = argv[0];
42359218eb7SGabor Kovesdan 		for (i = 0; i < eargc; i++)
42459218eb7SGabor Kovesdan 			aargv[i + 1] = eargv[i];
42559218eb7SGabor Kovesdan 		for (int j = 1; j < argc; j++, i++)
42659218eb7SGabor Kovesdan 			aargv[i + 1] = argv[j];
4274dc88ebeSGabor Kovesdan 
42859218eb7SGabor Kovesdan 		aargc = eargc + argc;
4294dc88ebeSGabor Kovesdan 	} else {
4304dc88ebeSGabor Kovesdan 		aargv = argv;
4314dc88ebeSGabor Kovesdan 		aargc = argc;
4324dc88ebeSGabor Kovesdan 	}
4334dc88ebeSGabor Kovesdan 
4344dc88ebeSGabor Kovesdan 	while (((c = getopt_long(aargc, aargv, optstr, long_options, NULL)) !=
4354dc88ebeSGabor Kovesdan 	    -1)) {
4364dc88ebeSGabor Kovesdan 		switch (c) {
4374dc88ebeSGabor Kovesdan 		case '0': case '1': case '2': case '3': case '4':
4384dc88ebeSGabor Kovesdan 		case '5': case '6': case '7': case '8': case '9':
4394dc88ebeSGabor Kovesdan 			if (newarg || !isdigit(lastc))
4404dc88ebeSGabor Kovesdan 				Aflag = 0;
4414dc88ebeSGabor Kovesdan 			else if (Aflag > LLONG_MAX / 10) {
4424dc88ebeSGabor Kovesdan 				errno = ERANGE;
4434dc88ebeSGabor Kovesdan 				err(2, NULL);
4444dc88ebeSGabor Kovesdan 			}
4454dc88ebeSGabor Kovesdan 			Aflag = Bflag = (Aflag * 10) + (c - '0');
4464dc88ebeSGabor Kovesdan 			break;
4474dc88ebeSGabor Kovesdan 		case 'C':
4484dc88ebeSGabor Kovesdan 			if (optarg == NULL) {
4494dc88ebeSGabor Kovesdan 				Aflag = Bflag = 2;
4504dc88ebeSGabor Kovesdan 				break;
4514dc88ebeSGabor Kovesdan 			}
4524dc88ebeSGabor Kovesdan 			/* FALLTHROUGH */
4534dc88ebeSGabor Kovesdan 		case 'A':
4544dc88ebeSGabor Kovesdan 			/* FALLTHROUGH */
4554dc88ebeSGabor Kovesdan 		case 'B':
4564dc88ebeSGabor Kovesdan 			errno = 0;
4574dc88ebeSGabor Kovesdan 			l = strtoull(optarg, &ep, 10);
4584dc88ebeSGabor Kovesdan 			if (((errno == ERANGE) && (l == ULLONG_MAX)) ||
4594dc88ebeSGabor Kovesdan 			    ((errno == EINVAL) && (l == 0)))
4604dc88ebeSGabor Kovesdan 				err(2, NULL);
4614dc88ebeSGabor Kovesdan 			else if (ep[0] != '\0') {
4624dc88ebeSGabor Kovesdan 				errno = EINVAL;
4634dc88ebeSGabor Kovesdan 				err(2, NULL);
4644dc88ebeSGabor Kovesdan 			}
4654dc88ebeSGabor Kovesdan 			if (c == 'A')
4664dc88ebeSGabor Kovesdan 				Aflag = l;
4674dc88ebeSGabor Kovesdan 			else if (c == 'B')
4684dc88ebeSGabor Kovesdan 				Bflag = l;
4694dc88ebeSGabor Kovesdan 			else
4704dc88ebeSGabor Kovesdan 				Aflag = Bflag = l;
4714dc88ebeSGabor Kovesdan 			break;
4724dc88ebeSGabor Kovesdan 		case 'a':
4734dc88ebeSGabor Kovesdan 			binbehave = BINFILE_TEXT;
4744dc88ebeSGabor Kovesdan 			break;
4754dc88ebeSGabor Kovesdan 		case 'b':
4764dc88ebeSGabor Kovesdan 			bflag = true;
4774dc88ebeSGabor Kovesdan 			break;
4784dc88ebeSGabor Kovesdan 		case 'c':
4794dc88ebeSGabor Kovesdan 			cflag = true;
4804dc88ebeSGabor Kovesdan 			break;
4814dc88ebeSGabor Kovesdan 		case 'D':
48227116286SGabor Kovesdan 			if (strcasecmp(optarg, "skip") == 0)
4834dc88ebeSGabor Kovesdan 				devbehave = DEV_SKIP;
48427116286SGabor Kovesdan 			else if (strcasecmp(optarg, "read") == 0)
48527116286SGabor Kovesdan 				devbehave = DEV_READ;
48697a012f2SGabor Kovesdan 			else
48797a012f2SGabor Kovesdan 				errx(2, getstr(3), "--devices");
4884dc88ebeSGabor Kovesdan 			break;
4894dc88ebeSGabor Kovesdan 		case 'd':
49027116286SGabor Kovesdan 			if (strcasecmp("recurse", optarg) == 0) {
4914dc88ebeSGabor Kovesdan 				Hflag = true;
4924dc88ebeSGabor Kovesdan 				dirbehave = DIR_RECURSE;
49327116286SGabor Kovesdan 			} else if (strcasecmp("skip", optarg) == 0)
4944dc88ebeSGabor Kovesdan 				dirbehave = DIR_SKIP;
49527116286SGabor Kovesdan 			else if (strcasecmp("read", optarg) == 0)
49627116286SGabor Kovesdan 				dirbehave = DIR_READ;
49797a012f2SGabor Kovesdan 			else
49897a012f2SGabor Kovesdan 				errx(2, getstr(3), "--directories");
4994dc88ebeSGabor Kovesdan 			break;
5004dc88ebeSGabor Kovesdan 		case 'E':
5014dc88ebeSGabor Kovesdan 			grepbehave = GREP_EXTENDED;
5024dc88ebeSGabor Kovesdan 			break;
5034dc88ebeSGabor Kovesdan 		case 'e':
504e411593dSGabor Kovesdan 			{
505e411593dSGabor Kovesdan 				char *token;
506cd64c588SGabor Kovesdan 				char *string = optarg;
507e411593dSGabor Kovesdan 
508e411593dSGabor Kovesdan 				while ((token = strsep(&string, "\n")) != NULL)
509e411593dSGabor Kovesdan 					add_pattern(token, strlen(token));
510e411593dSGabor Kovesdan 			}
5114dc88ebeSGabor Kovesdan 			needpattern = 0;
5124dc88ebeSGabor Kovesdan 			break;
5134dc88ebeSGabor Kovesdan 		case 'F':
5144dc88ebeSGabor Kovesdan 			grepbehave = GREP_FIXED;
5154dc88ebeSGabor Kovesdan 			break;
5164dc88ebeSGabor Kovesdan 		case 'f':
5174dc88ebeSGabor Kovesdan 			read_patterns(optarg);
5184dc88ebeSGabor Kovesdan 			needpattern = 0;
5194dc88ebeSGabor Kovesdan 			break;
5204dc88ebeSGabor Kovesdan 		case 'G':
5214dc88ebeSGabor Kovesdan 			grepbehave = GREP_BASIC;
5224dc88ebeSGabor Kovesdan 			break;
5234dc88ebeSGabor Kovesdan 		case 'H':
5244dc88ebeSGabor Kovesdan 			Hflag = true;
5254dc88ebeSGabor Kovesdan 			break;
5264dc88ebeSGabor Kovesdan 		case 'h':
5274dc88ebeSGabor Kovesdan 			Hflag = false;
5284dc88ebeSGabor Kovesdan 			hflag = true;
5294dc88ebeSGabor Kovesdan 			break;
5304dc88ebeSGabor Kovesdan 		case 'I':
5314dc88ebeSGabor Kovesdan 			binbehave = BINFILE_SKIP;
5324dc88ebeSGabor Kovesdan 			break;
5334dc88ebeSGabor Kovesdan 		case 'i':
5344dc88ebeSGabor Kovesdan 		case 'y':
5354dc88ebeSGabor Kovesdan 			iflag =  true;
5364dc88ebeSGabor Kovesdan 			cflags |= REG_ICASE;
5374dc88ebeSGabor Kovesdan 			break;
5384dc88ebeSGabor Kovesdan 		case 'J':
539afbbd357SGabor Kovesdan #ifdef WITHOUT_BZIP2
540afbbd357SGabor Kovesdan 			errno = EOPNOTSUPP;
541afbbd357SGabor Kovesdan 			err(2, "bzip2 support was disabled at compile-time");
542afbbd357SGabor Kovesdan #endif
5434dc88ebeSGabor Kovesdan 			filebehave = FILE_BZIP;
5444dc88ebeSGabor Kovesdan 			break;
5454dc88ebeSGabor Kovesdan 		case 'L':
5464dc88ebeSGabor Kovesdan 			lflag = false;
54727116286SGabor Kovesdan 			Lflag = true;
5484dc88ebeSGabor Kovesdan 			break;
5494dc88ebeSGabor Kovesdan 		case 'l':
5504dc88ebeSGabor Kovesdan 			Lflag = false;
55127116286SGabor Kovesdan 			lflag = true;
5524dc88ebeSGabor Kovesdan 			break;
5534dc88ebeSGabor Kovesdan 		case 'm':
5544dc88ebeSGabor Kovesdan 			mflag = true;
5554dc88ebeSGabor Kovesdan 			errno = 0;
556924500b7SEitan Adler 			mlimit = mcount = strtoll(optarg, &ep, 10);
557f20f6f3fSGabor Kovesdan 			if (((errno == ERANGE) && (mcount == LLONG_MAX)) ||
5584dc88ebeSGabor Kovesdan 			    ((errno == EINVAL) && (mcount == 0)))
5594dc88ebeSGabor Kovesdan 				err(2, NULL);
5604dc88ebeSGabor Kovesdan 			else if (ep[0] != '\0') {
5614dc88ebeSGabor Kovesdan 				errno = EINVAL;
5624dc88ebeSGabor Kovesdan 				err(2, NULL);
5634dc88ebeSGabor Kovesdan 			}
5644dc88ebeSGabor Kovesdan 			break;
565f20f6f3fSGabor Kovesdan 		case 'M':
566f20f6f3fSGabor Kovesdan 			filebehave = FILE_LZMA;
567f20f6f3fSGabor Kovesdan 			break;
5684dc88ebeSGabor Kovesdan 		case 'n':
5694dc88ebeSGabor Kovesdan 			nflag = true;
5704dc88ebeSGabor Kovesdan 			break;
5714dc88ebeSGabor Kovesdan 		case 'O':
5724dc88ebeSGabor Kovesdan 			linkbehave = LINK_EXPLICIT;
5734dc88ebeSGabor Kovesdan 			break;
5744dc88ebeSGabor Kovesdan 		case 'o':
5754dc88ebeSGabor Kovesdan 			oflag = true;
57669a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
5774dc88ebeSGabor Kovesdan 			break;
5784dc88ebeSGabor Kovesdan 		case 'p':
5794dc88ebeSGabor Kovesdan 			linkbehave = LINK_SKIP;
5804dc88ebeSGabor Kovesdan 			break;
5814dc88ebeSGabor Kovesdan 		case 'q':
5824dc88ebeSGabor Kovesdan 			qflag = true;
5834dc88ebeSGabor Kovesdan 			break;
5844dc88ebeSGabor Kovesdan 		case 'S':
58527116286SGabor Kovesdan 			linkbehave = LINK_READ;
5864dc88ebeSGabor Kovesdan 			break;
5874dc88ebeSGabor Kovesdan 		case 'R':
5884dc88ebeSGabor Kovesdan 		case 'r':
5894dc88ebeSGabor Kovesdan 			dirbehave = DIR_RECURSE;
5904dc88ebeSGabor Kovesdan 			Hflag = true;
5914dc88ebeSGabor Kovesdan 			break;
5924dc88ebeSGabor Kovesdan 		case 's':
5934dc88ebeSGabor Kovesdan 			sflag = true;
5944dc88ebeSGabor Kovesdan 			break;
5954dc88ebeSGabor Kovesdan 		case 'U':
5964dc88ebeSGabor Kovesdan 			binbehave = BINFILE_BIN;
5974dc88ebeSGabor Kovesdan 			break;
5984dc88ebeSGabor Kovesdan 		case 'u':
5994dc88ebeSGabor Kovesdan 		case MMAP_OPT:
600f20f6f3fSGabor Kovesdan 			filebehave = FILE_MMAP;
6014dc88ebeSGabor Kovesdan 			break;
6024dc88ebeSGabor Kovesdan 		case 'V':
603cc41ba26SEd Maste #ifdef WITH_GNU
604cc41ba26SEd Maste 			printf(getstr(10), getprogname(), VERSION);
605cc41ba26SEd Maste #else
606afbbd357SGabor Kovesdan 			printf(getstr(9), getprogname(), VERSION);
607cc41ba26SEd Maste #endif
6084dc88ebeSGabor Kovesdan 			exit(0);
6094dc88ebeSGabor Kovesdan 		case 'v':
6104dc88ebeSGabor Kovesdan 			vflag = true;
6114dc88ebeSGabor Kovesdan 			break;
6124dc88ebeSGabor Kovesdan 		case 'w':
6134dc88ebeSGabor Kovesdan 			wflag = true;
61469a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
6154dc88ebeSGabor Kovesdan 			break;
6164dc88ebeSGabor Kovesdan 		case 'x':
6174dc88ebeSGabor Kovesdan 			xflag = true;
61869a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
6194dc88ebeSGabor Kovesdan 			break;
620f20f6f3fSGabor Kovesdan 		case 'X':
621f20f6f3fSGabor Kovesdan 			filebehave = FILE_XZ;
622f20f6f3fSGabor Kovesdan 			break;
6235ee1ea02SEd Maste 		case 'z':
6245ee1ea02SEd Maste 			fileeol = '\0';
6255ee1ea02SEd Maste 			break;
6264dc88ebeSGabor Kovesdan 		case 'Z':
6274dc88ebeSGabor Kovesdan 			filebehave = FILE_GZIP;
6284dc88ebeSGabor Kovesdan 			break;
6294dc88ebeSGabor Kovesdan 		case BIN_OPT:
63027116286SGabor Kovesdan 			if (strcasecmp("binary", optarg) == 0)
6314dc88ebeSGabor Kovesdan 				binbehave = BINFILE_BIN;
63227116286SGabor Kovesdan 			else if (strcasecmp("without-match", optarg) == 0)
6334dc88ebeSGabor Kovesdan 				binbehave = BINFILE_SKIP;
63427116286SGabor Kovesdan 			else if (strcasecmp("text", optarg) == 0)
6354dc88ebeSGabor Kovesdan 				binbehave = BINFILE_TEXT;
6364dc88ebeSGabor Kovesdan 			else
63797a012f2SGabor Kovesdan 				errx(2, getstr(3), "--binary-files");
6384dc88ebeSGabor Kovesdan 			break;
6394dc88ebeSGabor Kovesdan 		case COLOR_OPT:
6404dc88ebeSGabor Kovesdan 			color = NULL;
64127116286SGabor Kovesdan 			if (optarg == NULL || strcasecmp("auto", optarg) == 0 ||
64227116286SGabor Kovesdan 			    strcasecmp("tty", optarg) == 0 ||
64327116286SGabor Kovesdan 			    strcasecmp("if-tty", optarg) == 0) {
64427116286SGabor Kovesdan 				char *term;
64527116286SGabor Kovesdan 
64627116286SGabor Kovesdan 				term = getenv("TERM");
64727116286SGabor Kovesdan 				if (isatty(STDOUT_FILENO) && term != NULL &&
64827116286SGabor Kovesdan 				    strcasecmp(term, "dumb") != 0)
64927116286SGabor Kovesdan 					color = init_color("01;31");
65027116286SGabor Kovesdan 			} else if (strcasecmp("always", optarg) == 0 ||
65127116286SGabor Kovesdan 			    strcasecmp("yes", optarg) == 0 ||
65227116286SGabor Kovesdan 			    strcasecmp("force", optarg) == 0) {
65327116286SGabor Kovesdan 				color = init_color("01;31");
65427116286SGabor Kovesdan 			} else if (strcasecmp("never", optarg) != 0 &&
65527116286SGabor Kovesdan 			    strcasecmp("none", optarg) != 0 &&
65627116286SGabor Kovesdan 			    strcasecmp("no", optarg) != 0)
65797a012f2SGabor Kovesdan 				errx(2, getstr(3), "--color");
65869a6d198SGabor Kovesdan 			cflags &= ~REG_NOSUB;
6594dc88ebeSGabor Kovesdan 			break;
6604dc88ebeSGabor Kovesdan 		case LABEL_OPT:
6614dc88ebeSGabor Kovesdan 			label = optarg;
6624dc88ebeSGabor Kovesdan 			break;
6634dc88ebeSGabor Kovesdan 		case LINEBUF_OPT:
6644dc88ebeSGabor Kovesdan 			lbflag = true;
6654dc88ebeSGabor Kovesdan 			break;
6664dc88ebeSGabor Kovesdan 		case NULL_OPT:
6674dc88ebeSGabor Kovesdan 			nullflag = true;
6684dc88ebeSGabor Kovesdan 			break;
6694dc88ebeSGabor Kovesdan 		case R_INCLUDE_OPT:
67055e44f51SGabor Kovesdan 			finclude = true;
67155e44f51SGabor Kovesdan 			add_fpattern(optarg, INCL_PAT);
6724dc88ebeSGabor Kovesdan 			break;
6734dc88ebeSGabor Kovesdan 		case R_EXCLUDE_OPT:
67455e44f51SGabor Kovesdan 			fexclude = true;
67555e44f51SGabor Kovesdan 			add_fpattern(optarg, EXCL_PAT);
6764dc88ebeSGabor Kovesdan 			break;
6774dc88ebeSGabor Kovesdan 		case R_DINCLUDE_OPT:
67859218eb7SGabor Kovesdan 			dinclude = true;
67955e44f51SGabor Kovesdan 			add_dpattern(optarg, INCL_PAT);
6804dc88ebeSGabor Kovesdan 			break;
6814dc88ebeSGabor Kovesdan 		case R_DEXCLUDE_OPT:
68259218eb7SGabor Kovesdan 			dexclude = true;
68355e44f51SGabor Kovesdan 			add_dpattern(optarg, EXCL_PAT);
6844dc88ebeSGabor Kovesdan 			break;
6854dc88ebeSGabor Kovesdan 		case HELP_OPT:
6864dc88ebeSGabor Kovesdan 		default:
6874dc88ebeSGabor Kovesdan 			usage();
6884dc88ebeSGabor Kovesdan 		}
6894dc88ebeSGabor Kovesdan 		lastc = c;
6904dc88ebeSGabor Kovesdan 		newarg = optind != prevoptind;
6914dc88ebeSGabor Kovesdan 		prevoptind = optind;
6924dc88ebeSGabor Kovesdan 	}
6934dc88ebeSGabor Kovesdan 	aargc -= optind;
6944dc88ebeSGabor Kovesdan 	aargv += optind;
6954dc88ebeSGabor Kovesdan 
696f20f6f3fSGabor Kovesdan 	/* Empty pattern file matches nothing */
697f20f6f3fSGabor Kovesdan 	if (!needpattern && (patterns == 0))
698f20f6f3fSGabor Kovesdan 		exit(1);
699f20f6f3fSGabor Kovesdan 
7004dc88ebeSGabor Kovesdan 	/* Fail if we don't have any pattern */
7014dc88ebeSGabor Kovesdan 	if (aargc == 0 && needpattern)
7024dc88ebeSGabor Kovesdan 		usage();
7034dc88ebeSGabor Kovesdan 
7044dc88ebeSGabor Kovesdan 	/* Process patterns from command line */
7054dc88ebeSGabor Kovesdan 	if (aargc != 0 && needpattern) {
706e411593dSGabor Kovesdan 		char *token;
707cd64c588SGabor Kovesdan 		char *string = *aargv;
708e411593dSGabor Kovesdan 
709e411593dSGabor Kovesdan 		while ((token = strsep(&string, "\n")) != NULL)
710e411593dSGabor Kovesdan 			add_pattern(token, strlen(token));
7114dc88ebeSGabor Kovesdan 		--aargc;
7124dc88ebeSGabor Kovesdan 		++aargv;
7134dc88ebeSGabor Kovesdan 	}
7144dc88ebeSGabor Kovesdan 
7154dc88ebeSGabor Kovesdan 	switch (grepbehave) {
7164dc88ebeSGabor Kovesdan 	case GREP_BASIC:
7174dc88ebeSGabor Kovesdan 		break;
718f20f6f3fSGabor Kovesdan 	case GREP_FIXED:
719f20f6f3fSGabor Kovesdan 		/* XXX: header mess, REG_LITERAL not defined in gnu/regex.h */
720f20f6f3fSGabor Kovesdan 		cflags |= 0020;
721f20f6f3fSGabor Kovesdan 		break;
7224dc88ebeSGabor Kovesdan 	case GREP_EXTENDED:
7234dc88ebeSGabor Kovesdan 		cflags |= REG_EXTENDED;
7244dc88ebeSGabor Kovesdan 		break;
7254dc88ebeSGabor Kovesdan 	default:
7264dc88ebeSGabor Kovesdan 		/* NOTREACHED */
7274dc88ebeSGabor Kovesdan 		usage();
7284dc88ebeSGabor Kovesdan 	}
7294dc88ebeSGabor Kovesdan 
7303f39ffc8SEd Maste #ifndef WITHOUT_FASTMATCH
7314dc88ebeSGabor Kovesdan 	fg_pattern = grep_calloc(patterns, sizeof(*fg_pattern));
7323f39ffc8SEd Maste #endif
7334dc88ebeSGabor Kovesdan 	r_pattern = grep_calloc(patterns, sizeof(*r_pattern));
734f20f6f3fSGabor Kovesdan 
735*a4f3f02bSEd Maste 	/* Don't process any patterns if we have a blank one */
736*a4f3f02bSEd Maste 	if (!matchall) {
7374dc88ebeSGabor Kovesdan 		/* Check if cheating is allowed (always is for fgrep). */
7384dc88ebeSGabor Kovesdan 		for (i = 0; i < patterns; ++i) {
7393f39ffc8SEd Maste #ifndef WITHOUT_FASTMATCH
740*a4f3f02bSEd Maste 			/*
741*a4f3f02bSEd Maste 			 * Attempt compilation with fastmatch regex and
742*a4f3f02bSEd Maste 			 * fallback to regex(3) if it fails.
743*a4f3f02bSEd Maste 			 */
744f20f6f3fSGabor Kovesdan 			if (fastncomp(&fg_pattern[i], pattern[i].pat,
7453f39ffc8SEd Maste 			    pattern[i].len, cflags) == 0)
7463f39ffc8SEd Maste 				continue;
7473f39ffc8SEd Maste #endif
748f20f6f3fSGabor Kovesdan 			c = regcomp(&r_pattern[i], pattern[i].pat, cflags);
7494dc88ebeSGabor Kovesdan 			if (c != 0) {
7504dc88ebeSGabor Kovesdan 				regerror(c, &r_pattern[i], re_error,
7514dc88ebeSGabor Kovesdan 				    RE_ERROR_BUF);
7524dc88ebeSGabor Kovesdan 				errx(2, "%s", re_error);
7534dc88ebeSGabor Kovesdan 			}
7544dc88ebeSGabor Kovesdan 		}
755*a4f3f02bSEd Maste 	}
7564dc88ebeSGabor Kovesdan 
7574dc88ebeSGabor Kovesdan 	if (lbflag)
7584dc88ebeSGabor Kovesdan 		setlinebuf(stdout);
7594dc88ebeSGabor Kovesdan 
7604dc88ebeSGabor Kovesdan 	if ((aargc == 0 || aargc == 1) && !Hflag)
7614dc88ebeSGabor Kovesdan 		hflag = true;
7624dc88ebeSGabor Kovesdan 
763a461896aSEd Maste 	if (aargc == 0 && dirbehave != DIR_RECURSE)
7644dc88ebeSGabor Kovesdan 		exit(!procfile("-"));
7654dc88ebeSGabor Kovesdan 
7664dc88ebeSGabor Kovesdan 	if (dirbehave == DIR_RECURSE)
7674dc88ebeSGabor Kovesdan 		c = grep_tree(aargv);
768c38208adSXin LI 	else
76955e44f51SGabor Kovesdan 		for (c = 0; aargc--; ++aargv) {
77055e44f51SGabor Kovesdan 			if ((finclude || fexclude) && !file_matching(*aargv))
77155e44f51SGabor Kovesdan 				continue;
7724dc88ebeSGabor Kovesdan 			c+= procfile(*aargv);
77355e44f51SGabor Kovesdan 		}
7744dc88ebeSGabor Kovesdan 
7754dc88ebeSGabor Kovesdan #ifndef WITHOUT_NLS
7764dc88ebeSGabor Kovesdan 	catclose(catalog);
7774dc88ebeSGabor Kovesdan #endif
7784dc88ebeSGabor Kovesdan 
7794dc88ebeSGabor Kovesdan 	/* Find out the correct return value according to the
7804dc88ebeSGabor Kovesdan 	   results and the command line option. */
7816f4cbf7cSGabor Kovesdan 	exit(c ? (file_err ? (qflag ? 0 : 2) : 0) : (file_err ? 2 : 1));
7824dc88ebeSGabor Kovesdan }
783