xref: /illumos-gate/usr/src/cmd/grep/grep.c (revision 925beec7a40f23f8a7663cf5e8289a88f9688e87)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
57c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
67c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
77c478bd9Sstevel@tonic-gate  * with the License.
87c478bd9Sstevel@tonic-gate  *
97c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
117c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
127c478bd9Sstevel@tonic-gate  * and limitations under the License.
137c478bd9Sstevel@tonic-gate  *
147c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
157c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
177c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
187c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bd9Sstevel@tonic-gate  *
207c478bd9Sstevel@tonic-gate  * CDDL HEADER END
217c478bd9Sstevel@tonic-gate  */
227c478bd9Sstevel@tonic-gate /*
23d2d52addSAlexander Pyhalov  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
257c478bd9Sstevel@tonic-gate  */
267c478bd9Sstevel@tonic-gate 
2741599e9fSDamian Bogel /*
28d2d52addSAlexander Pyhalov  * grep - pattern matching program - combined grep, egrep, and fgrep.
29d2d52addSAlexander Pyhalov  *	Based on MKS grep command, with XCU & Solaris mods.
3041599e9fSDamian Bogel  */
3141599e9fSDamian Bogel 
327c478bd9Sstevel@tonic-gate /*
33d2d52addSAlexander Pyhalov  * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
347c478bd9Sstevel@tonic-gate  *
357c478bd9Sstevel@tonic-gate  */
367c478bd9Sstevel@tonic-gate 
37d2d52addSAlexander Pyhalov /*
38*925beec7SYuri Pankov  * Copyright 2018 Nexenta Systems, Inc.
39d2d52addSAlexander Pyhalov  * Copyright 2013 Damian Bogel. All rights reserved.
40d2d52addSAlexander Pyhalov  */
41d2d52addSAlexander Pyhalov 
42d2d52addSAlexander Pyhalov #include <string.h>
43d2d52addSAlexander Pyhalov #include <stdlib.h>
447c478bd9Sstevel@tonic-gate #include <ctype.h>
45d2d52addSAlexander Pyhalov #include <stdarg.h>
46d2d52addSAlexander Pyhalov #include <regex.h>
47d2d52addSAlexander Pyhalov #include <limits.h>
48d2d52addSAlexander Pyhalov #include <sys/types.h>
49d2d52addSAlexander Pyhalov #include <sys/stat.h>
507c478bd9Sstevel@tonic-gate #include <fcntl.h>
517c478bd9Sstevel@tonic-gate #include <stdio.h>
52d2d52addSAlexander Pyhalov #include <locale.h>
53d2d52addSAlexander Pyhalov #include <wchar.h>
54d2d52addSAlexander Pyhalov #include <errno.h>
557c478bd9Sstevel@tonic-gate #include <unistd.h>
56d2d52addSAlexander Pyhalov #include <wctype.h>
57e52fb54bSAlexander Eremin #include <ftw.h>
58e52fb54bSAlexander Eremin #include <sys/param.h>
597c478bd9Sstevel@tonic-gate 
60d2d52addSAlexander Pyhalov #define	STDIN_FILENAME gettext("(standard input)")
61d2d52addSAlexander Pyhalov 
62d2d52addSAlexander Pyhalov #define	BSIZE		512		/* Size of block for -b */
63d2d52addSAlexander Pyhalov #define	BUFSIZE		8192		/* Input buffer size */
64d2d52addSAlexander Pyhalov #define	MAX_DEPTH	1000		/* how deep to recurse */
65d2d52addSAlexander Pyhalov 
66d2d52addSAlexander Pyhalov #define	AFTER 	1			/* 'After' Context */
67d2d52addSAlexander Pyhalov #define	BEFORE 	2			/* 'Before' Context */
68d2d52addSAlexander Pyhalov #define	CONTEXT	(AFTER|BEFORE)		/* Full Context */
69d2d52addSAlexander Pyhalov 
70d2d52addSAlexander Pyhalov #define	M_CSETSIZE	256		/* singlebyte chars */
71d2d52addSAlexander Pyhalov static int	bmglen;			/* length of BMG pattern */
72d2d52addSAlexander Pyhalov static char	*bmgpat;		/* BMG pattern */
73d2d52addSAlexander Pyhalov static int	bmgtab[M_CSETSIZE];	/* BMG delta1 table */
74d2d52addSAlexander Pyhalov 
75d2d52addSAlexander Pyhalov typedef	struct	_PATTERN	{
76d2d52addSAlexander Pyhalov 	char	*pattern;		/* original pattern */
77d2d52addSAlexander Pyhalov 	wchar_t	*wpattern;		/* wide, lowercased pattern */
78d2d52addSAlexander Pyhalov 	struct	_PATTERN	*next;
79d2d52addSAlexander Pyhalov 	regex_t	re;			/* compiled pattern */
80d2d52addSAlexander Pyhalov } PATTERN;
81d2d52addSAlexander Pyhalov 
82d2d52addSAlexander Pyhalov static PATTERN	*patterns;
83d2d52addSAlexander Pyhalov static char	errstr[128];		/* regerror string buffer */
84d2d52addSAlexander Pyhalov static int	regflags = 0;		/* regcomp options */
85d2d52addSAlexander Pyhalov static int	matched = 0;		/* return of the grep() */
86d2d52addSAlexander Pyhalov static int	errors = 0;		/* count of errors */
87d2d52addSAlexander Pyhalov static uchar_t	fgrep = 0;		/* Invoked as fgrep */
88d2d52addSAlexander Pyhalov static uchar_t	egrep = 0;		/* Invoked as egrep */
89d2d52addSAlexander Pyhalov static boolean_t	nvflag = B_TRUE;	/* Print matching lines */
90d2d52addSAlexander Pyhalov static uchar_t	cflag;			/* Count of matches */
91d2d52addSAlexander Pyhalov static uchar_t	iflag;			/* Case insensitve matching */
92d2d52addSAlexander Pyhalov static uchar_t	Hflag;			/* Precede lines by file name */
93d2d52addSAlexander Pyhalov static uchar_t	hflag;			/* Supress printing of filename */
94d2d52addSAlexander Pyhalov static uchar_t	lflag;			/* Print file names of matches */
95d2d52addSAlexander Pyhalov static uchar_t	nflag;			/* Precede lines by line number */
96d2d52addSAlexander Pyhalov static uchar_t	rflag;			/* Search directories recursively */
97d2d52addSAlexander Pyhalov static uchar_t	bflag;			/* Preccede matches by block number */
98d2d52addSAlexander Pyhalov static uchar_t	sflag;			/* Suppress file error messages */
99d2d52addSAlexander Pyhalov static uchar_t	qflag;			/* Suppress standard output */
100d2d52addSAlexander Pyhalov static uchar_t	wflag;			/* Search for expression as a word */
101d2d52addSAlexander Pyhalov static uchar_t	xflag;			/* Anchoring */
102d2d52addSAlexander Pyhalov static uchar_t	Eflag;			/* Egrep or -E flag */
103d2d52addSAlexander Pyhalov static uchar_t	Fflag;			/* Fgrep or -F flag */
104d2d52addSAlexander Pyhalov static uchar_t	Rflag;			/* Like rflag, but follow symlinks */
105d2d52addSAlexander Pyhalov static uchar_t	outfn;			/* Put out file name */
106d2d52addSAlexander Pyhalov static uchar_t	conflag;		/* show context of matches */
107d2d52addSAlexander Pyhalov static char	*cmdname;
108d2d52addSAlexander Pyhalov 
109d2d52addSAlexander Pyhalov static int	use_wchar, use_bmg, mblocale;
110d2d52addSAlexander Pyhalov 
111d2d52addSAlexander Pyhalov static size_t	outbuflen, prntbuflen, conbuflen;
112d2d52addSAlexander Pyhalov static unsigned long 	conalen, conblen, conmatches;
113d2d52addSAlexander Pyhalov static char	*prntbuf, *conbuf;
114d2d52addSAlexander Pyhalov static wchar_t	*outline;
115d2d52addSAlexander Pyhalov 
116d2d52addSAlexander Pyhalov static void	addfile(const char *fn);
117d2d52addSAlexander Pyhalov static void	addpattern(char *s);
118d2d52addSAlexander Pyhalov static void	fixpatterns(void);
119d2d52addSAlexander Pyhalov static void	usage(void);
120d2d52addSAlexander Pyhalov static int	grep(int, const char *);
121d2d52addSAlexander Pyhalov static void	bmgcomp(char *, int);
122d2d52addSAlexander Pyhalov static char	*bmgexec(char *, char *);
123e52fb54bSAlexander Eremin static int	recursive(const char *, const struct stat *, int, struct FTW *);
124d2d52addSAlexander Pyhalov static void	process_path(const char *);
125d2d52addSAlexander Pyhalov static void	process_file(const char *, int);
1267c478bd9Sstevel@tonic-gate 
127d2d52addSAlexander Pyhalov /*
128d2d52addSAlexander Pyhalov  * mainline for grep
129d2d52addSAlexander Pyhalov  */
1307c478bd9Sstevel@tonic-gate int
13155f91622Sceastha main(int argc, char **argv)
1327c478bd9Sstevel@tonic-gate {
133d2d52addSAlexander Pyhalov 	char	*ap, *test;
1347c478bd9Sstevel@tonic-gate 	int	c;
135d2d52addSAlexander Pyhalov 	int	fflag = 0;
136d2d52addSAlexander Pyhalov 	int	i, n_pattern = 0, n_file = 0;
137d2d52addSAlexander Pyhalov 	char	**pattern_list = NULL;
138d2d52addSAlexander Pyhalov 	char	**file_list = NULL;
1397c478bd9Sstevel@tonic-gate 
1407c478bd9Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
1417c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
142d2d52addSAlexander Pyhalov #define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
1437c478bd9Sstevel@tonic-gate #endif
1447c478bd9Sstevel@tonic-gate 	(void) textdomain(TEXT_DOMAIN);
1457c478bd9Sstevel@tonic-gate 
146d2d52addSAlexander Pyhalov 	/*
147d2d52addSAlexander Pyhalov 	 * true if this is running on the multibyte locale
148d2d52addSAlexander Pyhalov 	 */
149d2d52addSAlexander Pyhalov 	mblocale = (MB_CUR_MAX > 1);
150d2d52addSAlexander Pyhalov 	/*
151d2d52addSAlexander Pyhalov 	 * Skip leading slashes
152d2d52addSAlexander Pyhalov 	 */
153d2d52addSAlexander Pyhalov 	cmdname = argv[0];
154d2d52addSAlexander Pyhalov 	if (ap = strrchr(cmdname, '/'))
155d2d52addSAlexander Pyhalov 		cmdname = ap + 1;
156d2d52addSAlexander Pyhalov 
157d2d52addSAlexander Pyhalov 	ap = cmdname;
158d2d52addSAlexander Pyhalov 	/*
159d2d52addSAlexander Pyhalov 	 * Detect egrep/fgrep via command name, map to -E and -F options.
160d2d52addSAlexander Pyhalov 	 */
161d2d52addSAlexander Pyhalov 	if (*ap == 'e' || *ap == 'E') {
162d2d52addSAlexander Pyhalov 		regflags |= REG_EXTENDED;
163d2d52addSAlexander Pyhalov 		egrep++;
164d2d52addSAlexander Pyhalov 	} else {
165d2d52addSAlexander Pyhalov 		if (*ap == 'f' || *ap == 'F') {
166d2d52addSAlexander Pyhalov 			fgrep++;
167d2d52addSAlexander Pyhalov 		}
168d2d52addSAlexander Pyhalov 	}
169d2d52addSAlexander Pyhalov 
170d2d52addSAlexander Pyhalov 	/* check for non-standard "-line-count" option */
171d2d52addSAlexander Pyhalov 	for (i = 1; i < argc; i++) {
172d2d52addSAlexander Pyhalov 		if (strcmp(argv[i], "--") == 0)
17341599e9fSDamian Bogel 			break;
174d2d52addSAlexander Pyhalov 
175d2d52addSAlexander Pyhalov 		/* isdigit() check prevents negative arguments */
176d2d52addSAlexander Pyhalov 		if ((argv[i][0] == '-') && isdigit(argv[i][1])) {
177d2d52addSAlexander Pyhalov 			if (strlen(&argv[i][1]) !=
178d2d52addSAlexander Pyhalov 			    strspn(&argv[i][1], "0123456789")) {
179d2d52addSAlexander Pyhalov 				(void) fprintf(stderr, gettext(
180d2d52addSAlexander Pyhalov 				    "%s: Bad number flag\n"), argv[0]);
181d2d52addSAlexander Pyhalov 				usage();
182d2d52addSAlexander Pyhalov 			}
183d2d52addSAlexander Pyhalov 
184d2d52addSAlexander Pyhalov 			errno = 0;
185d2d52addSAlexander Pyhalov 			conalen = conblen = strtoul(&argv[i][1], (char **)NULL,
186d2d52addSAlexander Pyhalov 			    10);
187d2d52addSAlexander Pyhalov 
188d2d52addSAlexander Pyhalov 			if (errno != 0 || conalen >= ULONG_MAX) {
189d2d52addSAlexander Pyhalov 				(void) fprintf(stderr, gettext(
190d2d52addSAlexander Pyhalov 				    "%s: Bad context argument\n"), argv[0]);
191d2d52addSAlexander Pyhalov 			} else if (conalen)
192d2d52addSAlexander Pyhalov 				conflag = CONTEXT;
193d2d52addSAlexander Pyhalov 
194d2d52addSAlexander Pyhalov 			while (i < argc) {
195d2d52addSAlexander Pyhalov 				argv[i] = argv[i + 1];
196d2d52addSAlexander Pyhalov 				i++;
197d2d52addSAlexander Pyhalov 			}
198d2d52addSAlexander Pyhalov 			argc--;
199d2d52addSAlexander Pyhalov 		}
200d2d52addSAlexander Pyhalov 	}
201d2d52addSAlexander Pyhalov 
202d2d52addSAlexander Pyhalov 	while ((c = getopt(argc, argv, "vwchHilnrbse:f:qxEFIRA:B:C:")) != EOF) {
203d2d52addSAlexander Pyhalov 		unsigned long tval;
204d2d52addSAlexander Pyhalov 		switch (c) {
205d2d52addSAlexander Pyhalov 		case 'v':	/* POSIX: negate matches */
206d2d52addSAlexander Pyhalov 			nvflag = B_FALSE;
2077c478bd9Sstevel@tonic-gate 			break;
208d2d52addSAlexander Pyhalov 
209d2d52addSAlexander Pyhalov 		case 'c':	/* POSIX: write count */
210d2d52addSAlexander Pyhalov 			cflag++;
2113ed621bcSAlexander Eremin 			break;
212d2d52addSAlexander Pyhalov 
213d2d52addSAlexander Pyhalov 		case 'i':	/* POSIX: ignore case */
214d2d52addSAlexander Pyhalov 			iflag++;
215d2d52addSAlexander Pyhalov 			regflags |= REG_ICASE;
2167c478bd9Sstevel@tonic-gate 			break;
217d2d52addSAlexander Pyhalov 
218d2d52addSAlexander Pyhalov 		case 'l':	/* POSIX: Write filenames only */
219d2d52addSAlexander Pyhalov 			lflag++;
2207c478bd9Sstevel@tonic-gate 			break;
221d2d52addSAlexander Pyhalov 
222d2d52addSAlexander Pyhalov 		case 'n':	/* POSIX: Write line numbers */
2237c478bd9Sstevel@tonic-gate 			nflag++;
2247c478bd9Sstevel@tonic-gate 			break;
225d2d52addSAlexander Pyhalov 
226d2d52addSAlexander Pyhalov 		case 'r':	/* Solaris: search recursively */
227e52fb54bSAlexander Eremin 			rflag++;
228e52fb54bSAlexander Eremin 			break;
229d2d52addSAlexander Pyhalov 
230d2d52addSAlexander Pyhalov 		case 'b':	/* Solaris: Write file block numbers */
2317c478bd9Sstevel@tonic-gate 			bflag++;
2327c478bd9Sstevel@tonic-gate 			break;
233d2d52addSAlexander Pyhalov 
234d2d52addSAlexander Pyhalov 		case 's':	/* POSIX: No error msgs for files */
2357c478bd9Sstevel@tonic-gate 			sflag++;
2367c478bd9Sstevel@tonic-gate 			break;
237d2d52addSAlexander Pyhalov 
238d2d52addSAlexander Pyhalov 		case 'e':	/* POSIX: pattern list */
239d2d52addSAlexander Pyhalov 			n_pattern++;
240d2d52addSAlexander Pyhalov 			pattern_list = realloc(pattern_list,
241d2d52addSAlexander Pyhalov 			    sizeof (char *) * n_pattern);
242d2d52addSAlexander Pyhalov 			if (pattern_list == NULL) {
243d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
244d2d52addSAlexander Pyhalov 				    gettext("%s: out of memory\n"),
245d2d52addSAlexander Pyhalov 				    cmdname);
246d2d52addSAlexander Pyhalov 				exit(2);
247d2d52addSAlexander Pyhalov 			}
248d2d52addSAlexander Pyhalov 			*(pattern_list + n_pattern - 1) = optarg;
2497c478bd9Sstevel@tonic-gate 			break;
250d2d52addSAlexander Pyhalov 
251d2d52addSAlexander Pyhalov 		case 'f':	/* POSIX: pattern file */
252d2d52addSAlexander Pyhalov 			fflag = 1;
253d2d52addSAlexander Pyhalov 			n_file++;
254d2d52addSAlexander Pyhalov 			file_list = realloc(file_list,
255d2d52addSAlexander Pyhalov 			    sizeof (char *) * n_file);
256d2d52addSAlexander Pyhalov 			if (file_list == NULL) {
257d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
258d2d52addSAlexander Pyhalov 				    gettext("%s: out of memory\n"),
259d2d52addSAlexander Pyhalov 				    cmdname);
260d2d52addSAlexander Pyhalov 				exit(2);
261d2d52addSAlexander Pyhalov 			}
262d2d52addSAlexander Pyhalov 			*(file_list + n_file - 1) = optarg;
2637c478bd9Sstevel@tonic-gate 			break;
264d2d52addSAlexander Pyhalov 
265d2d52addSAlexander Pyhalov 		/* based on options order h or H is set as in GNU grep */
266d2d52addSAlexander Pyhalov 		case 'h':	/* Solaris: supress printing of file name */
267d2d52addSAlexander Pyhalov 			hflag = 1;
268d2d52addSAlexander Pyhalov 			Hflag = 0;
269d2d52addSAlexander Pyhalov 			break;
270d2d52addSAlexander Pyhalov 		/* Solaris: precede every matching with file name */
271d2d52addSAlexander Pyhalov 		case 'H':
272d2d52addSAlexander Pyhalov 			Hflag = 1;
273d2d52addSAlexander Pyhalov 			hflag = 0;
274d2d52addSAlexander Pyhalov 			break;
275d2d52addSAlexander Pyhalov 
276d2d52addSAlexander Pyhalov 		case 'q':	/* POSIX: quiet: status only */
277d2d52addSAlexander Pyhalov 			qflag++;
278d2d52addSAlexander Pyhalov 			break;
279d2d52addSAlexander Pyhalov 
280d2d52addSAlexander Pyhalov 		case 'w':	/* Solaris: treat pattern as word */
2817c478bd9Sstevel@tonic-gate 			wflag++;
2827c478bd9Sstevel@tonic-gate 			break;
283d2d52addSAlexander Pyhalov 
284d2d52addSAlexander Pyhalov 		case 'x':	/* POSIX: full line matches */
285d2d52addSAlexander Pyhalov 			xflag++;
286d2d52addSAlexander Pyhalov 			break;
287d2d52addSAlexander Pyhalov 
288d2d52addSAlexander Pyhalov 		case 'E':	/* POSIX: Extended RE's */
289d2d52addSAlexander Pyhalov 			regflags |= REG_EXTENDED;
290d2d52addSAlexander Pyhalov 			Eflag++;
291d2d52addSAlexander Pyhalov 			break;
292d2d52addSAlexander Pyhalov 
293d2d52addSAlexander Pyhalov 		case 'F':	/* POSIX: strings, not RE's */
294d2d52addSAlexander Pyhalov 			Fflag++;
295d2d52addSAlexander Pyhalov 			break;
296d2d52addSAlexander Pyhalov 
297d2d52addSAlexander Pyhalov 		case 'R':	/* Solaris: like rflag, but follow symlinks */
298d2d52addSAlexander Pyhalov 			Rflag++;
299d2d52addSAlexander Pyhalov 			rflag++;
300d2d52addSAlexander Pyhalov 			break;
301d2d52addSAlexander Pyhalov 
302d2d52addSAlexander Pyhalov 		case 'A':	/* print N lines after each match */
303d2d52addSAlexander Pyhalov 			errno = 0;
304d2d52addSAlexander Pyhalov 			conalen = strtoul(optarg, &test, 10);
305d2d52addSAlexander Pyhalov 			/* *test will be non-null if optarg is negative */
306d2d52addSAlexander Pyhalov 			if (errno != 0 || *test != '\0' ||
307d2d52addSAlexander Pyhalov 			    conalen >= ULONG_MAX) {
308d2d52addSAlexander Pyhalov 				(void) fprintf(stderr, gettext(
309d2d52addSAlexander Pyhalov 				    "%s: Bad context argument: %s\n"),
310d2d52addSAlexander Pyhalov 				    argv[0], optarg);
311d2d52addSAlexander Pyhalov 				exit(2);
312d2d52addSAlexander Pyhalov 			}
313d2d52addSAlexander Pyhalov 			if (conalen)
314d2d52addSAlexander Pyhalov 				conflag |= AFTER;
315d2d52addSAlexander Pyhalov 			else
316d2d52addSAlexander Pyhalov 				conflag &= ~AFTER;
317d2d52addSAlexander Pyhalov 			break;
318d2d52addSAlexander Pyhalov 		case 'B':	/* print N lines before each match */
319d2d52addSAlexander Pyhalov 			errno = 0;
320d2d52addSAlexander Pyhalov 			conblen = strtoul(optarg, &test, 10);
321d2d52addSAlexander Pyhalov 			/* *test will be non-null if optarg is negative */
322d2d52addSAlexander Pyhalov 			if (errno != 0 || *test != '\0' ||
323d2d52addSAlexander Pyhalov 			    conblen >= ULONG_MAX) {
324d2d52addSAlexander Pyhalov 				(void) fprintf(stderr, gettext(
325d2d52addSAlexander Pyhalov 				    "%s: Bad context argument: %s\n"),
326d2d52addSAlexander Pyhalov 				    argv[0], optarg);
327d2d52addSAlexander Pyhalov 				exit(2);
328d2d52addSAlexander Pyhalov 			}
329d2d52addSAlexander Pyhalov 			if (conblen)
330d2d52addSAlexander Pyhalov 				conflag |= BEFORE;
331d2d52addSAlexander Pyhalov 			else
332d2d52addSAlexander Pyhalov 				conflag &= ~BEFORE;
333d2d52addSAlexander Pyhalov 			break;
334d2d52addSAlexander Pyhalov 		case 'C':	/* print N lines around each match */
335d2d52addSAlexander Pyhalov 			errno = 0;
336d2d52addSAlexander Pyhalov 			tval = strtoul(optarg, &test, 10);
337d2d52addSAlexander Pyhalov 			/* *test will be non-null if optarg is negative */
338d2d52addSAlexander Pyhalov 			if (errno != 0 || *test != '\0' || tval >= ULONG_MAX) {
339d2d52addSAlexander Pyhalov 				(void) fprintf(stderr, gettext(
340d2d52addSAlexander Pyhalov 				    "%s: Bad context argument: %s\n"),
341d2d52addSAlexander Pyhalov 				    argv[0], optarg);
342d2d52addSAlexander Pyhalov 				exit(2);
343d2d52addSAlexander Pyhalov 			}
344d2d52addSAlexander Pyhalov 			if (tval) {
345d2d52addSAlexander Pyhalov 				if ((conflag & BEFORE) == 0)
346d2d52addSAlexander Pyhalov 					conblen = tval;
347d2d52addSAlexander Pyhalov 				if ((conflag & AFTER) == 0)
348d2d52addSAlexander Pyhalov 					conalen = tval;
349d2d52addSAlexander Pyhalov 				conflag = CONTEXT;
350d2d52addSAlexander Pyhalov 			}
351d2d52addSAlexander Pyhalov 			break;
352d2d52addSAlexander Pyhalov 
353d2d52addSAlexander Pyhalov 		default:
354d2d52addSAlexander Pyhalov 			usage();
3557c478bd9Sstevel@tonic-gate 		}
356d2d52addSAlexander Pyhalov 	}
357d2d52addSAlexander Pyhalov 	/*
358d2d52addSAlexander Pyhalov 	 * If we're invoked as egrep or fgrep we need to do some checks
359d2d52addSAlexander Pyhalov 	 */
3607c478bd9Sstevel@tonic-gate 
361d2d52addSAlexander Pyhalov 	if (egrep || fgrep) {
362d2d52addSAlexander Pyhalov 		/*
363d2d52addSAlexander Pyhalov 		 * Use of -E or -F with egrep or fgrep is illegal
364d2d52addSAlexander Pyhalov 		 */
365d2d52addSAlexander Pyhalov 		if (Eflag || Fflag)
366d2d52addSAlexander Pyhalov 			usage();
367d2d52addSAlexander Pyhalov 		/*
368d2d52addSAlexander Pyhalov 		 * Don't allow use of wflag with egrep / fgrep
369d2d52addSAlexander Pyhalov 		 */
370d2d52addSAlexander Pyhalov 		if (wflag)
371d2d52addSAlexander Pyhalov 			usage();
372d2d52addSAlexander Pyhalov 		/*
373d2d52addSAlexander Pyhalov 		 * For Solaris the -s flag is equivalent to XCU -q
374d2d52addSAlexander Pyhalov 		 */
375d2d52addSAlexander Pyhalov 		if (sflag)
376d2d52addSAlexander Pyhalov 			qflag++;
377d2d52addSAlexander Pyhalov 		/*
378d2d52addSAlexander Pyhalov 		 * done with above checks - set the appropriate flags
379d2d52addSAlexander Pyhalov 		 */
380d2d52addSAlexander Pyhalov 		if (egrep)
381d2d52addSAlexander Pyhalov 			Eflag++;
382d2d52addSAlexander Pyhalov 		else			/* Else fgrep */
383d2d52addSAlexander Pyhalov 			Fflag++;
3847c478bd9Sstevel@tonic-gate 	}
3857c478bd9Sstevel@tonic-gate 
386d2d52addSAlexander Pyhalov 	if (wflag && (Eflag || Fflag)) {
387d2d52addSAlexander Pyhalov 		/*
388d2d52addSAlexander Pyhalov 		 * -w cannot be specified with grep -F
389d2d52addSAlexander Pyhalov 		 */
390d2d52addSAlexander Pyhalov 		usage();
391d2d52addSAlexander Pyhalov 	}
3927c478bd9Sstevel@tonic-gate 
393d2d52addSAlexander Pyhalov 	/*
394d2d52addSAlexander Pyhalov 	 * -E and -F flags are mutually exclusive - check for this
395d2d52addSAlexander Pyhalov 	 */
396d2d52addSAlexander Pyhalov 	if (Eflag && Fflag)
397d2d52addSAlexander Pyhalov 		usage();
3987c478bd9Sstevel@tonic-gate 
399d2d52addSAlexander Pyhalov 	/*
400d2d52addSAlexander Pyhalov 	 * -l overrides -H like in GNU grep
401d2d52addSAlexander Pyhalov 	 */
402d2d52addSAlexander Pyhalov 	if (lflag)
403d2d52addSAlexander Pyhalov 		Hflag = 0;
404d2d52addSAlexander Pyhalov 
405d2d52addSAlexander Pyhalov 	/*
406d2d52addSAlexander Pyhalov 	 * -c, -l and -q flags are mutually exclusive
407d2d52addSAlexander Pyhalov 	 * We have -c override -l like in Solaris.
408d2d52addSAlexander Pyhalov 	 * -q overrides -l & -c programmatically in grep() function.
409d2d52addSAlexander Pyhalov 	 */
410d2d52addSAlexander Pyhalov 	if (cflag && lflag)
411d2d52addSAlexander Pyhalov 		lflag = 0;
4127c478bd9Sstevel@tonic-gate 
413d2d52addSAlexander Pyhalov 	argv += optind - 1;
414d2d52addSAlexander Pyhalov 	argc -= optind - 1;
4157c478bd9Sstevel@tonic-gate 
416d2d52addSAlexander Pyhalov 	/*
417d2d52addSAlexander Pyhalov 	 * Now handling -e and -f option
418d2d52addSAlexander Pyhalov 	 */
419d2d52addSAlexander Pyhalov 	if (pattern_list) {
420d2d52addSAlexander Pyhalov 		for (i = 0; i < n_pattern; i++) {
421d2d52addSAlexander Pyhalov 			addpattern(pattern_list[i]);
422d2d52addSAlexander Pyhalov 		}
423d2d52addSAlexander Pyhalov 		free(pattern_list);
424d2d52addSAlexander Pyhalov 	}
425d2d52addSAlexander Pyhalov 	if (file_list) {
426d2d52addSAlexander Pyhalov 		for (i = 0; i < n_file; i++) {
427d2d52addSAlexander Pyhalov 			addfile(file_list[i]);
4287c478bd9Sstevel@tonic-gate 		}
429d2d52addSAlexander Pyhalov 		free(file_list);
430d2d52addSAlexander Pyhalov 	}
4317c478bd9Sstevel@tonic-gate 
432d2d52addSAlexander Pyhalov 	/*
433d2d52addSAlexander Pyhalov 	 * No -e or -f?  Make sure there is one more arg, use it as the pattern.
434d2d52addSAlexander Pyhalov 	 */
435d2d52addSAlexander Pyhalov 	if (patterns == NULL && !fflag) {
436d2d52addSAlexander Pyhalov 		if (argc < 2)
437d2d52addSAlexander Pyhalov 			usage();
438d2d52addSAlexander Pyhalov 		addpattern(argv[1]);
439d2d52addSAlexander Pyhalov 		argc--;
440d2d52addSAlexander Pyhalov 		argv++;
4417c478bd9Sstevel@tonic-gate 	}
4427c478bd9Sstevel@tonic-gate 
443d2d52addSAlexander Pyhalov 	/*
444d2d52addSAlexander Pyhalov 	 * If -x flag is not specified or -i flag is specified
445d2d52addSAlexander Pyhalov 	 * with fgrep in a multibyte locale, need to use
446d2d52addSAlexander Pyhalov 	 * the wide character APIs.  Otherwise, byte-oriented
447d2d52addSAlexander Pyhalov 	 * process will be done.
448d2d52addSAlexander Pyhalov 	 */
449d2d52addSAlexander Pyhalov 	use_wchar = Fflag && mblocale && (!xflag || iflag);
450d2d52addSAlexander Pyhalov 
451d2d52addSAlexander Pyhalov 	/*
452d2d52addSAlexander Pyhalov 	 * Compile Patterns and also decide if BMG can be used
453d2d52addSAlexander Pyhalov 	 */
454d2d52addSAlexander Pyhalov 	fixpatterns();
455d2d52addSAlexander Pyhalov 
456d2d52addSAlexander Pyhalov 	/* Process all files: stdin, or rest of arg list */
457d2d52addSAlexander Pyhalov 	if (argc < 2) {
458d2d52addSAlexander Pyhalov 		matched = grep(0, STDIN_FILENAME);
459d2d52addSAlexander Pyhalov 	} else {
460d2d52addSAlexander Pyhalov 		if (Hflag || (argc > 2 && hflag == 0))
461d2d52addSAlexander Pyhalov 			outfn = 1;	/* Print filename on match line */
462d2d52addSAlexander Pyhalov 		for (argv++; *argv != NULL; argv++) {
463d2d52addSAlexander Pyhalov 			process_path(*argv);
464d2d52addSAlexander Pyhalov 		}
465d2d52addSAlexander Pyhalov 	}
466d2d52addSAlexander Pyhalov 	/*
467d2d52addSAlexander Pyhalov 	 * Return() here is used instead of exit
468d2d52addSAlexander Pyhalov 	 */
4697c478bd9Sstevel@tonic-gate 
470d2d52addSAlexander Pyhalov 	(void) fflush(stdout);
4717c478bd9Sstevel@tonic-gate 
472d2d52addSAlexander Pyhalov 	if (errors)
473d2d52addSAlexander Pyhalov 		return (2);
474d2d52addSAlexander Pyhalov 	return (matched ? 0 : 1);
4757c478bd9Sstevel@tonic-gate }
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate static void
478d2d52addSAlexander Pyhalov process_path(const char *path)
479e52fb54bSAlexander Eremin {
480e52fb54bSAlexander Eremin 	struct	stat st;
481e52fb54bSAlexander Eremin 	int	walkflags = FTW_CHDIR;
482e52fb54bSAlexander Eremin 	char	*buf = NULL;
483e52fb54bSAlexander Eremin 
484e52fb54bSAlexander Eremin 	if (rflag) {
485e52fb54bSAlexander Eremin 		if (stat(path, &st) != -1 &&
486e52fb54bSAlexander Eremin 		    (st.st_mode & S_IFMT) == S_IFDIR) {
487d2d52addSAlexander Pyhalov 			outfn = 1; /* Print filename */
488e52fb54bSAlexander Eremin 
489e52fb54bSAlexander Eremin 			/*
490e52fb54bSAlexander Eremin 			 * Add trailing slash if arg
491e52fb54bSAlexander Eremin 			 * is directory, to resolve symlinks.
492e52fb54bSAlexander Eremin 			 */
493e52fb54bSAlexander Eremin 			if (path[strlen(path) - 1] != '/') {
494e52fb54bSAlexander Eremin 				(void) asprintf(&buf, "%s/", path);
495e52fb54bSAlexander Eremin 				if (buf != NULL)
496e52fb54bSAlexander Eremin 					path = buf;
497e52fb54bSAlexander Eremin 			}
498e52fb54bSAlexander Eremin 
499e52fb54bSAlexander Eremin 			/*
500e52fb54bSAlexander Eremin 			 * Search through subdirs if path is directory.
501e52fb54bSAlexander Eremin 			 * Don't follow symlinks if Rflag is not set.
502e52fb54bSAlexander Eremin 			 */
503e52fb54bSAlexander Eremin 			if (!Rflag)
504e52fb54bSAlexander Eremin 				walkflags |= FTW_PHYS;
505e52fb54bSAlexander Eremin 
506e52fb54bSAlexander Eremin 			if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
507e52fb54bSAlexander Eremin 				if (!sflag)
508d2d52addSAlexander Pyhalov 					(void) fprintf(stderr,
509d2d52addSAlexander Pyhalov 					    gettext("%s: can't open \"%s\"\n"),
510d2d52addSAlexander Pyhalov 					    cmdname, path);
511d2d52addSAlexander Pyhalov 				errors = 1;
512e52fb54bSAlexander Eremin 			}
513e52fb54bSAlexander Eremin 			return;
514e52fb54bSAlexander Eremin 		}
515e52fb54bSAlexander Eremin 	}
516d2d52addSAlexander Pyhalov 	process_file(path, 0);
517e52fb54bSAlexander Eremin }
518e52fb54bSAlexander Eremin 
519d2d52addSAlexander Pyhalov /*
520d2d52addSAlexander Pyhalov  * Read and process all files in directory recursively.
521d2d52addSAlexander Pyhalov  */
522e52fb54bSAlexander Eremin static int
523e52fb54bSAlexander Eremin recursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
524e52fb54bSAlexander Eremin {
525e52fb54bSAlexander Eremin 	/*
526d2d52addSAlexander Pyhalov 	 * Process files and follow symlinks if Rflag set.
527e52fb54bSAlexander Eremin 	 */
528e52fb54bSAlexander Eremin 	if (info != FTW_F) {
529d2d52addSAlexander Pyhalov 		/* Report broken symlinks and unreadable files */
530e52fb54bSAlexander Eremin 		if (!sflag &&
531e52fb54bSAlexander Eremin 		    (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
532d2d52addSAlexander Pyhalov 			(void) fprintf(stderr,
533d2d52addSAlexander Pyhalov 			    gettext("%s: can't open \"%s\"\n"), cmdname, name);
534e52fb54bSAlexander Eremin 		}
535e52fb54bSAlexander Eremin 		return (0);
536e52fb54bSAlexander Eremin 	}
537e52fb54bSAlexander Eremin 
538d2d52addSAlexander Pyhalov 
539d2d52addSAlexander Pyhalov 	/* Skip devices and pipes if Rflag is not set */
540e52fb54bSAlexander Eremin 	if (!Rflag && !S_ISREG(statp->st_mode))
541e52fb54bSAlexander Eremin 		return (0);
542d2d52addSAlexander Pyhalov 	/* Pass offset to relative name from FTW_CHDIR */
543d2d52addSAlexander Pyhalov 	process_file(name, ftw->base);
544e52fb54bSAlexander Eremin 	return (0);
545e52fb54bSAlexander Eremin }
546e52fb54bSAlexander Eremin 
547d2d52addSAlexander Pyhalov /*
548d2d52addSAlexander Pyhalov  * Opens file and call grep function.
549d2d52addSAlexander Pyhalov  */
550e52fb54bSAlexander Eremin static void
551d2d52addSAlexander Pyhalov process_file(const char *name, int base)
5527c478bd9Sstevel@tonic-gate {
553d2d52addSAlexander Pyhalov 	int fd;
5547c478bd9Sstevel@tonic-gate 
555d2d52addSAlexander Pyhalov 	if ((fd = open(name + base, O_RDONLY)) == -1) {
556d2d52addSAlexander Pyhalov 		errors = 1;
557d2d52addSAlexander Pyhalov 		if (!sflag) /* Silent mode */
558d2d52addSAlexander Pyhalov 			(void) fprintf(stderr, gettext(
559d2d52addSAlexander Pyhalov 			    "%s: can't open \"%s\"\n"),
560d2d52addSAlexander Pyhalov 			    cmdname, name);
561d2d52addSAlexander Pyhalov 		return;
562d2d52addSAlexander Pyhalov 	}
563d2d52addSAlexander Pyhalov 	matched |= grep(fd, name);
564d2d52addSAlexander Pyhalov 	(void) close(fd);
5657c478bd9Sstevel@tonic-gate 
566d2d52addSAlexander Pyhalov 	if (ferror(stdout)) {
567d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, gettext(
568d2d52addSAlexander Pyhalov 		    "%s: error writing to stdout\n"),
569d2d52addSAlexander Pyhalov 		    cmdname);
570d2d52addSAlexander Pyhalov 		(void) fflush(stdout);
571d2d52addSAlexander Pyhalov 		exit(2);
572d2d52addSAlexander Pyhalov 	}
573d2d52addSAlexander Pyhalov 
574d2d52addSAlexander Pyhalov }
575d2d52addSAlexander Pyhalov 
576d2d52addSAlexander Pyhalov /*
577d2d52addSAlexander Pyhalov  * Add a file of strings to the pattern list.
578d2d52addSAlexander Pyhalov  */
579d2d52addSAlexander Pyhalov static void
580d2d52addSAlexander Pyhalov addfile(const char *fn)
581d2d52addSAlexander Pyhalov {
582d2d52addSAlexander Pyhalov 	FILE	*fp;
583d2d52addSAlexander Pyhalov 	char	*inbuf;
584d2d52addSAlexander Pyhalov 	char	*bufp;
585d2d52addSAlexander Pyhalov 	size_t	bufsiz, buflen, bufused;
586d2d52addSAlexander Pyhalov 
587d2d52addSAlexander Pyhalov 	/*
588d2d52addSAlexander Pyhalov 	 * Open the pattern file
589d2d52addSAlexander Pyhalov 	 */
590d2d52addSAlexander Pyhalov 	if ((fp = fopen(fn, "r")) == NULL) {
591d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, gettext("%s: can't open \"%s\"\n"),
592d2d52addSAlexander Pyhalov 		    cmdname, fn);
593d2d52addSAlexander Pyhalov 		exit(2);
594d2d52addSAlexander Pyhalov 	}
595d2d52addSAlexander Pyhalov 	bufsiz = BUFSIZE;
596d2d52addSAlexander Pyhalov 	if ((inbuf = malloc(bufsiz)) == NULL) {
597d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
598d2d52addSAlexander Pyhalov 		    gettext("%s: out of memory\n"), cmdname);
599d2d52addSAlexander Pyhalov 		exit(2);
600d2d52addSAlexander Pyhalov 	}
601d2d52addSAlexander Pyhalov 	bufp = inbuf;
602d2d52addSAlexander Pyhalov 	bufused = 0;
603d2d52addSAlexander Pyhalov 	/*
604d2d52addSAlexander Pyhalov 	 * Read in the file, reallocing as we need more memory
605d2d52addSAlexander Pyhalov 	 */
606d2d52addSAlexander Pyhalov 	while (fgets(bufp, bufsiz - bufused, fp) != NULL) {
607d2d52addSAlexander Pyhalov 		buflen = strlen(bufp);
608d2d52addSAlexander Pyhalov 		bufused += buflen;
609d2d52addSAlexander Pyhalov 		if (bufused + 1 == bufsiz && bufp[buflen - 1] != '\n') {
610d2d52addSAlexander Pyhalov 			/*
611d2d52addSAlexander Pyhalov 			 * if this line does not fit to the buffer,
612d2d52addSAlexander Pyhalov 			 * realloc larger buffer
613d2d52addSAlexander Pyhalov 			 */
614d2d52addSAlexander Pyhalov 			bufsiz += BUFSIZE;
615d2d52addSAlexander Pyhalov 			if ((inbuf = realloc(inbuf, bufsiz)) == NULL) {
616d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
617d2d52addSAlexander Pyhalov 				    gettext("%s: out of memory\n"),
618d2d52addSAlexander Pyhalov 				    cmdname);
619d2d52addSAlexander Pyhalov 				exit(2);
620d2d52addSAlexander Pyhalov 			}
621d2d52addSAlexander Pyhalov 			bufp = inbuf + bufused;
622d2d52addSAlexander Pyhalov 			continue;
6237c478bd9Sstevel@tonic-gate 		}
624d2d52addSAlexander Pyhalov 		if (bufp[buflen - 1] == '\n') {
625d2d52addSAlexander Pyhalov 			bufp[--buflen] = '\0';
6267c478bd9Sstevel@tonic-gate 		}
627d2d52addSAlexander Pyhalov 		addpattern(inbuf);
628d2d52addSAlexander Pyhalov 
629d2d52addSAlexander Pyhalov 		bufp = inbuf;
630d2d52addSAlexander Pyhalov 		bufused = 0;
6317c478bd9Sstevel@tonic-gate 	}
632d2d52addSAlexander Pyhalov 	free(inbuf);
633d2d52addSAlexander Pyhalov 	free(prntbuf);
634d2d52addSAlexander Pyhalov 	free(conbuf);
635d2d52addSAlexander Pyhalov 	(void) fclose(fp);
636d2d52addSAlexander Pyhalov }
6377c478bd9Sstevel@tonic-gate 
638d2d52addSAlexander Pyhalov /*
639d2d52addSAlexander Pyhalov  * Add a string to the pattern list.
640d2d52addSAlexander Pyhalov  */
641d2d52addSAlexander Pyhalov static void
642d2d52addSAlexander Pyhalov addpattern(char *s)
643d2d52addSAlexander Pyhalov {
644d2d52addSAlexander Pyhalov 	PATTERN	*pp;
645d2d52addSAlexander Pyhalov 	char	*wordbuf;
646d2d52addSAlexander Pyhalov 	char	*np;
647d2d52addSAlexander Pyhalov 
648d2d52addSAlexander Pyhalov 	for (; ; ) {
649d2d52addSAlexander Pyhalov 		np = strchr(s, '\n');
650d2d52addSAlexander Pyhalov 		if (np != NULL)
651d2d52addSAlexander Pyhalov 			*np = '\0';
652d2d52addSAlexander Pyhalov 		if ((pp = malloc(sizeof (PATTERN))) == NULL) {
653d2d52addSAlexander Pyhalov 			(void) fprintf(stderr, gettext(
654d2d52addSAlexander Pyhalov 			    "%s: out of memory\n"),
655d2d52addSAlexander Pyhalov 			    cmdname);
656d2d52addSAlexander Pyhalov 			exit(2);
657d2d52addSAlexander Pyhalov 		}
658d2d52addSAlexander Pyhalov 		if (wflag) {
659d2d52addSAlexander Pyhalov 			/*
660d2d52addSAlexander Pyhalov 			 * Solaris wflag support: Add '<' '>' to pattern to
661d2d52addSAlexander Pyhalov 			 * select it as a word. Doesn't make sense with -F
662d2d52addSAlexander Pyhalov 			 * but we're Libertarian.
663d2d52addSAlexander Pyhalov 			 */
664d2d52addSAlexander Pyhalov 			size_t	slen, wordlen;
665d2d52addSAlexander Pyhalov 
666d2d52addSAlexander Pyhalov 			slen = strlen(s);
667d2d52addSAlexander Pyhalov 			wordlen = slen + 5; /* '\\' '<' s '\\' '>' '\0' */
668d2d52addSAlexander Pyhalov 			if ((wordbuf = malloc(wordlen)) == NULL) {
669d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
670d2d52addSAlexander Pyhalov 				    gettext("%s: out of memory\n"),
671d2d52addSAlexander Pyhalov 				    cmdname);
672d2d52addSAlexander Pyhalov 				exit(2);
673d2d52addSAlexander Pyhalov 			}
674d2d52addSAlexander Pyhalov 			(void) strcpy(wordbuf, "\\<");
675d2d52addSAlexander Pyhalov 			(void) strcpy(wordbuf + 2, s);
676d2d52addSAlexander Pyhalov 			(void) strcpy(wordbuf + 2 + slen, "\\>");
677d2d52addSAlexander Pyhalov 		} else {
678d2d52addSAlexander Pyhalov 			if ((wordbuf = strdup(s)) == NULL) {
679d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
680d2d52addSAlexander Pyhalov 				    gettext("%s: out of memory\n"),
681d2d52addSAlexander Pyhalov 				    cmdname);
682d2d52addSAlexander Pyhalov 				exit(2);
683d2d52addSAlexander Pyhalov 			}
684d2d52addSAlexander Pyhalov 		}
685d2d52addSAlexander Pyhalov 		pp->pattern = wordbuf;
686d2d52addSAlexander Pyhalov 		pp->next = patterns;
687d2d52addSAlexander Pyhalov 		patterns = pp;
688d2d52addSAlexander Pyhalov 		if (np == NULL)
689d2d52addSAlexander Pyhalov 			break;
690d2d52addSAlexander Pyhalov 		s = np + 1;
6917c478bd9Sstevel@tonic-gate 	}
692d2d52addSAlexander Pyhalov }
693d2d52addSAlexander Pyhalov 
694d2d52addSAlexander Pyhalov /*
695d2d52addSAlexander Pyhalov  * Fix patterns.
696d2d52addSAlexander Pyhalov  * Must do after all arguments read, in case later -i option.
697d2d52addSAlexander Pyhalov  */
698d2d52addSAlexander Pyhalov static void
699d2d52addSAlexander Pyhalov fixpatterns(void)
700d2d52addSAlexander Pyhalov {
701d2d52addSAlexander Pyhalov 	PATTERN	*pp;
702d2d52addSAlexander Pyhalov 	int	rv, fix_pattern, npatterns;
7037c478bd9Sstevel@tonic-gate 
704d2d52addSAlexander Pyhalov 	/*
705*925beec7SYuri Pankov 	 * Fix the specified pattern if -x is specified.
706d2d52addSAlexander Pyhalov 	 */
707d2d52addSAlexander Pyhalov 	fix_pattern = !Fflag && xflag;
708d2d52addSAlexander Pyhalov 
709d2d52addSAlexander Pyhalov 	for (npatterns = 0, pp = patterns; pp != NULL; pp = pp->next) {
710d2d52addSAlexander Pyhalov 		npatterns++;
711d2d52addSAlexander Pyhalov 		if (fix_pattern) {
712d2d52addSAlexander Pyhalov 			char	*cp, *cq;
713d2d52addSAlexander Pyhalov 			size_t	plen, nplen;
7147c478bd9Sstevel@tonic-gate 
715d2d52addSAlexander Pyhalov 			plen = strlen(pp->pattern);
716d2d52addSAlexander Pyhalov 			/* '^' pattern '$' */
717d2d52addSAlexander Pyhalov 			nplen = 1 + plen + 1 + 1;
718d2d52addSAlexander Pyhalov 			if ((cp = malloc(nplen)) == NULL) {
719d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
720d2d52addSAlexander Pyhalov 				    gettext("%s: out of memory\n"),
721d2d52addSAlexander Pyhalov 				    cmdname);
722d2d52addSAlexander Pyhalov 				exit(2);
723d2d52addSAlexander Pyhalov 			}
724d2d52addSAlexander Pyhalov 			cq = cp;
725d2d52addSAlexander Pyhalov 			*cq++ = '^';
726d2d52addSAlexander Pyhalov 			cq = strcpy(cq, pp->pattern) + plen;
727d2d52addSAlexander Pyhalov 			*cq++ = '$';
728d2d52addSAlexander Pyhalov 			*cq = '\0';
729d2d52addSAlexander Pyhalov 			free(pp->pattern);
730d2d52addSAlexander Pyhalov 			pp->pattern = cp;
7317c478bd9Sstevel@tonic-gate 		}
732d2d52addSAlexander Pyhalov 
733d2d52addSAlexander Pyhalov 		if (Fflag) {
734d2d52addSAlexander Pyhalov 			if (use_wchar) {
735d2d52addSAlexander Pyhalov 				/*
736d2d52addSAlexander Pyhalov 				 * Fflag && mblocale && iflag
737d2d52addSAlexander Pyhalov 				 * Fflag && mblocale && !xflag
738d2d52addSAlexander Pyhalov 				 */
739d2d52addSAlexander Pyhalov 				size_t	n;
740d2d52addSAlexander Pyhalov 				n = strlen(pp->pattern) + 1;
741d2d52addSAlexander Pyhalov 				if ((pp->wpattern =
742d2d52addSAlexander Pyhalov 				    malloc(sizeof (wchar_t) * n)) == NULL) {
743d2d52addSAlexander Pyhalov 					(void) fprintf(stderr,
744d2d52addSAlexander Pyhalov 					    gettext("%s: out of memory\n"),
745d2d52addSAlexander Pyhalov 					    cmdname);
746d2d52addSAlexander Pyhalov 					exit(2);
747d2d52addSAlexander Pyhalov 				}
748d2d52addSAlexander Pyhalov 				if (mbstowcs(pp->wpattern, pp->pattern, n) ==
749d2d52addSAlexander Pyhalov 				    (size_t)-1) {
750d2d52addSAlexander Pyhalov 					(void) fprintf(stderr,
751d2d52addSAlexander Pyhalov 					    gettext("%s: failed to convert "
752d2d52addSAlexander Pyhalov 					    "\"%s\" to wide-characters\n"),
753d2d52addSAlexander Pyhalov 					    cmdname, pp->pattern);
754d2d52addSAlexander Pyhalov 					exit(2);
755d2d52addSAlexander Pyhalov 				}
756d2d52addSAlexander Pyhalov 				if (iflag) {
757d2d52addSAlexander Pyhalov 					wchar_t	*wp;
758d2d52addSAlexander Pyhalov 					for (wp = pp->wpattern; *wp != L'\0';
759d2d52addSAlexander Pyhalov 					    wp++) {
760d2d52addSAlexander Pyhalov 						*wp = towlower((wint_t)*wp);
761d2d52addSAlexander Pyhalov 					}
762d2d52addSAlexander Pyhalov 				}
763d2d52addSAlexander Pyhalov 				free(pp->pattern);
764d2d52addSAlexander Pyhalov 			} else {
765d2d52addSAlexander Pyhalov 				/*
766d2d52addSAlexander Pyhalov 				 * Fflag && mblocale && !iflag
767d2d52addSAlexander Pyhalov 				 * Fflag && !mblocale && iflag
768d2d52addSAlexander Pyhalov 				 * Fflag && !mblocale && !iflag
769d2d52addSAlexander Pyhalov 				 */
770d2d52addSAlexander Pyhalov 				if (iflag) {
771d2d52addSAlexander Pyhalov 					unsigned char	*cp;
772d2d52addSAlexander Pyhalov 					for (cp = (unsigned char *)pp->pattern;
773d2d52addSAlexander Pyhalov 					    *cp != '\0'; cp++) {
774d2d52addSAlexander Pyhalov 						*cp = tolower(*cp);
775d2d52addSAlexander Pyhalov 					}
776d2d52addSAlexander Pyhalov 				}
777d2d52addSAlexander Pyhalov 			}
778d2d52addSAlexander Pyhalov 			/*
779d2d52addSAlexander Pyhalov 			 * fgrep: No regular expressions.
780d2d52addSAlexander Pyhalov 			 */
781d2d52addSAlexander Pyhalov 			continue;
782d2d52addSAlexander Pyhalov 		}
783d2d52addSAlexander Pyhalov 
784d2d52addSAlexander Pyhalov 		/*
785d2d52addSAlexander Pyhalov 		 * For non-fgrep, compile the regular expression,
786d2d52addSAlexander Pyhalov 		 * give an informative error message, and exit if
787d2d52addSAlexander Pyhalov 		 * it didn't compile.
788d2d52addSAlexander Pyhalov 		 */
789d2d52addSAlexander Pyhalov 		if ((rv = regcomp(&pp->re, pp->pattern, regflags)) != 0) {
790d2d52addSAlexander Pyhalov 			(void) regerror(rv, &pp->re, errstr, sizeof (errstr));
791d2d52addSAlexander Pyhalov 			(void) fprintf(stderr,
792d2d52addSAlexander Pyhalov 			    gettext("%s: RE error in %s: %s\n"),
793d2d52addSAlexander Pyhalov 			    cmdname, pp->pattern, errstr);
794d2d52addSAlexander Pyhalov 			exit(2);
795d2d52addSAlexander Pyhalov 		}
796d2d52addSAlexander Pyhalov 		free(pp->pattern);
797d2d52addSAlexander Pyhalov 	}
798d2d52addSAlexander Pyhalov 
799d2d52addSAlexander Pyhalov 	/*
800d2d52addSAlexander Pyhalov 	 * Decide if we are able to run the Boyer-Moore-Gosper algorithm.
801d2d52addSAlexander Pyhalov 	 * Use the Boyer-Moore-Gosper algorithm if:
802d2d52addSAlexander Pyhalov 	 * - fgrep			(Fflag)
803d2d52addSAlexander Pyhalov 	 * - singlebyte locale		(!mblocale)
804d2d52addSAlexander Pyhalov 	 * - no ignoring case		(!iflag)
805d2d52addSAlexander Pyhalov 	 * - no printing line numbers	(!nflag)
806d2d52addSAlexander Pyhalov 	 * - no negating the output	(nvflag)
807d2d52addSAlexander Pyhalov 	 * - only one pattern		(npatterns == 1)
808d2d52addSAlexander Pyhalov 	 * - non zero length pattern	(strlen(patterns->pattern) != 0)
809d2d52addSAlexander Pyhalov 	 * - no context required	(conflag == 0)
810d2d52addSAlexander Pyhalov 	 *
811d2d52addSAlexander Pyhalov 	 * It's guaranteed patterns->pattern is still alive
812d2d52addSAlexander Pyhalov 	 * when Fflag && !mblocale.
813d2d52addSAlexander Pyhalov 	 */
814d2d52addSAlexander Pyhalov 	use_bmg = Fflag && !mblocale && !iflag && !nflag && nvflag &&
815d2d52addSAlexander Pyhalov 	    (npatterns == 1) && (strlen(patterns->pattern) != 0) &&
816d2d52addSAlexander Pyhalov 	    conflag == 0;
817d2d52addSAlexander Pyhalov }
818d2d52addSAlexander Pyhalov 
819d2d52addSAlexander Pyhalov /*
820d2d52addSAlexander Pyhalov  * Search a newline from the beginning of the string
821d2d52addSAlexander Pyhalov  */
822d2d52addSAlexander Pyhalov static char *
823d2d52addSAlexander Pyhalov find_nl(const char *ptr, size_t len)
824d2d52addSAlexander Pyhalov {
825d2d52addSAlexander Pyhalov 	while (len-- != 0) {
826d2d52addSAlexander Pyhalov 		if (*ptr++ == '\n') {
827d2d52addSAlexander Pyhalov 			return ((char *)--ptr);
828d2d52addSAlexander Pyhalov 		}
829d2d52addSAlexander Pyhalov 	}
830d2d52addSAlexander Pyhalov 	return (NULL);
831d2d52addSAlexander Pyhalov }
832d2d52addSAlexander Pyhalov 
833d2d52addSAlexander Pyhalov /*
834d2d52addSAlexander Pyhalov  * Search a newline from the end of the string
835d2d52addSAlexander Pyhalov  */
836d2d52addSAlexander Pyhalov static char *
837d2d52addSAlexander Pyhalov rfind_nl(const char *ptr, size_t len)
838d2d52addSAlexander Pyhalov {
839d2d52addSAlexander Pyhalov 	const char	*uptr = ptr + len;
840d2d52addSAlexander Pyhalov 	while (len--) {
841d2d52addSAlexander Pyhalov 		if (*--uptr == '\n') {
842d2d52addSAlexander Pyhalov 			return ((char *)uptr);
843d2d52addSAlexander Pyhalov 		}
844d2d52addSAlexander Pyhalov 	}
845d2d52addSAlexander Pyhalov 	return (NULL);
846d2d52addSAlexander Pyhalov }
847d2d52addSAlexander Pyhalov 
848d2d52addSAlexander Pyhalov /*
849d2d52addSAlexander Pyhalov  * Duplicate the specified string converting each character
850d2d52addSAlexander Pyhalov  * into a lower case.
851d2d52addSAlexander Pyhalov  */
852d2d52addSAlexander Pyhalov static char *
853d2d52addSAlexander Pyhalov istrdup(const char *s1)
854d2d52addSAlexander Pyhalov {
855d2d52addSAlexander Pyhalov 	static size_t	ibuflen = 0;
856d2d52addSAlexander Pyhalov 	static char	*ibuf = NULL;
857d2d52addSAlexander Pyhalov 	size_t	slen;
858d2d52addSAlexander Pyhalov 	char	*p;
859d2d52addSAlexander Pyhalov 
860d2d52addSAlexander Pyhalov 	slen = strlen(s1);
861d2d52addSAlexander Pyhalov 	if (slen >= ibuflen) {
862d2d52addSAlexander Pyhalov 		/* ibuf does not fit to s1 */
863d2d52addSAlexander Pyhalov 		ibuflen = slen + 1;
864d2d52addSAlexander Pyhalov 		ibuf = realloc(ibuf, ibuflen);
865d2d52addSAlexander Pyhalov 		if (ibuf == NULL) {
866d2d52addSAlexander Pyhalov 			(void) fprintf(stderr,
867d2d52addSAlexander Pyhalov 			    gettext("%s: out of memory\n"), cmdname);
868d2d52addSAlexander Pyhalov 			exit(2);
869d2d52addSAlexander Pyhalov 		}
870d2d52addSAlexander Pyhalov 	}
871d2d52addSAlexander Pyhalov 	p = ibuf;
872d2d52addSAlexander Pyhalov 	do {
873d2d52addSAlexander Pyhalov 		*p++ = tolower(*s1);
874d2d52addSAlexander Pyhalov 	} while (*s1++ != '\0');
875d2d52addSAlexander Pyhalov 	return (ibuf);
876d2d52addSAlexander Pyhalov }
877d2d52addSAlexander Pyhalov 
878d2d52addSAlexander Pyhalov /*
879d2d52addSAlexander Pyhalov  * Do grep on a single file.
880d2d52addSAlexander Pyhalov  * Return true in any lines matched.
881d2d52addSAlexander Pyhalov  *
882d2d52addSAlexander Pyhalov  * We have two strategies:
883d2d52addSAlexander Pyhalov  * The fast one is used when we have a single pattern with
884d2d52addSAlexander Pyhalov  * a string known to occur in the pattern. We can then
885d2d52addSAlexander Pyhalov  * do a BMG match on the whole buffer.
886d2d52addSAlexander Pyhalov  * This is an order of magnitude faster.
887d2d52addSAlexander Pyhalov  * Otherwise we split the buffer into lines,
888d2d52addSAlexander Pyhalov  * and check for a match on each line.
889d2d52addSAlexander Pyhalov  */
890d2d52addSAlexander Pyhalov static int
891d2d52addSAlexander Pyhalov grep(int fd, const char *fn)
892d2d52addSAlexander Pyhalov {
893d2d52addSAlexander Pyhalov 	PATTERN *pp;
894d2d52addSAlexander Pyhalov 	off_t	data_len;	/* length of the data chunk */
895d2d52addSAlexander Pyhalov 	off_t	line_len;	/* length of the current line */
896d2d52addSAlexander Pyhalov 	off_t	line_offset;	/* current line's offset from the beginning */
897d2d52addSAlexander Pyhalov 	off_t	blkoffset;	/* line_offset but context-compatible */
898d2d52addSAlexander Pyhalov 	long long	lineno, linenum;
899d2d52addSAlexander Pyhalov 	long long	matches = 0;	/* Number of matching lines */
900d2d52addSAlexander Pyhalov 	long long	conacnt = 0, conbcnt = 0; 	/* context line count */
901d2d52addSAlexander Pyhalov 	int	newlinep;	/* 0 if the last line of file has no newline */
902d2d52addSAlexander Pyhalov 	char	*ptr, *ptrend, *prntptr, *prntptrend;
903d2d52addSAlexander Pyhalov 	char	*nextptr = NULL, *nextend = NULL;
904d2d52addSAlexander Pyhalov 	char	*conptr = NULL, *conptrend = NULL;
905d2d52addSAlexander Pyhalov 	char	*matchptr = NULL;
906d2d52addSAlexander Pyhalov 	int	conaprnt = 0, conbprnt = 0, lastmatch = 0;
907d2d52addSAlexander Pyhalov 	boolean_t	nearmatch; /* w/in N+1 of last match */
908d2d52addSAlexander Pyhalov 	boolean_t	havematch = B_FALSE; /* have a match in context */
909d2d52addSAlexander Pyhalov 	size_t	prntlen;
910d2d52addSAlexander Pyhalov 
911d2d52addSAlexander Pyhalov 	if (patterns == NULL)
912d2d52addSAlexander Pyhalov 		return (0);	/* no patterns to match -- just return */
913d2d52addSAlexander Pyhalov 
914d2d52addSAlexander Pyhalov 	pp = patterns;
915d2d52addSAlexander Pyhalov 
916d2d52addSAlexander Pyhalov 	if (use_bmg) {
917d2d52addSAlexander Pyhalov 		bmgcomp(pp->pattern, strlen(pp->pattern));
9187c478bd9Sstevel@tonic-gate 	}
9197c478bd9Sstevel@tonic-gate 
920d2d52addSAlexander Pyhalov 	if (use_wchar && outline == NULL) {
921d2d52addSAlexander Pyhalov 		outbuflen = BUFSIZE + 1;
922d2d52addSAlexander Pyhalov 		outline = malloc(sizeof (wchar_t) * outbuflen);
923d2d52addSAlexander Pyhalov 		if (outline == NULL) {
924d2d52addSAlexander Pyhalov 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
925d2d52addSAlexander Pyhalov 			    cmdname);
926d2d52addSAlexander Pyhalov 			exit(2);
927d2d52addSAlexander Pyhalov 		}
928d2d52addSAlexander Pyhalov 	}
929d2d52addSAlexander Pyhalov 
930d2d52addSAlexander Pyhalov 	if (prntbuf == NULL) {
931d2d52addSAlexander Pyhalov 		prntbuflen = BUFSIZE;
932d2d52addSAlexander Pyhalov 		if ((prntbuf = malloc(prntbuflen + 1)) == NULL) {
933d2d52addSAlexander Pyhalov 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
934d2d52addSAlexander Pyhalov 			    cmdname);
935d2d52addSAlexander Pyhalov 			exit(2);
936d2d52addSAlexander Pyhalov 		}
937d2d52addSAlexander Pyhalov 	}
938d2d52addSAlexander Pyhalov 
939d2d52addSAlexander Pyhalov 	if (conflag != 0 && (conbuf == NULL)) {
940d2d52addSAlexander Pyhalov 		conbuflen = BUFSIZE;
941d2d52addSAlexander Pyhalov 		if ((conbuf = malloc(BUFSIZE+1)) == NULL) {
942d2d52addSAlexander Pyhalov 			(void) fprintf(stderr, gettext("%s: out of memory\n"),
943d2d52addSAlexander Pyhalov 			    cmdname);
944d2d52addSAlexander Pyhalov 			exit(2);
945d2d52addSAlexander Pyhalov 		}
946d2d52addSAlexander Pyhalov 	}
9477c478bd9Sstevel@tonic-gate 
948d2d52addSAlexander Pyhalov 	nearmatch = (conmatches != 0);
949d2d52addSAlexander Pyhalov 	blkoffset = line_offset = 0;
950d2d52addSAlexander Pyhalov 	lineno = 0;
951d2d52addSAlexander Pyhalov 	linenum = 1;
952d2d52addSAlexander Pyhalov 	newlinep = 1;
953d2d52addSAlexander Pyhalov 	data_len = 0;
954d2d52addSAlexander Pyhalov 	for (; ; ) {
955d2d52addSAlexander Pyhalov 		long	count;
956d2d52addSAlexander Pyhalov 		off_t	offset = 0;
957d2d52addSAlexander Pyhalov 		char	separate;
958d2d52addSAlexander Pyhalov 		boolean_t	last_ctx = B_FALSE, eof = B_FALSE;
959d2d52addSAlexander Pyhalov 
960d2d52addSAlexander Pyhalov 		if (data_len == 0) {
961d2d52addSAlexander Pyhalov 			/*
962d2d52addSAlexander Pyhalov 			 * If no data in the buffer, reset ptr
963d2d52addSAlexander Pyhalov 			 */
964d2d52addSAlexander Pyhalov 			ptr = prntbuf;
965d2d52addSAlexander Pyhalov 			if (conflag != 0 && conptr == NULL) {
966d2d52addSAlexander Pyhalov 				conptr = conbuf;
967d2d52addSAlexander Pyhalov 				conptrend = conptr - 1;
968d2d52addSAlexander Pyhalov 			}
969d2d52addSAlexander Pyhalov 		}
970d2d52addSAlexander Pyhalov 		if (ptr == prntbuf) {
9717c478bd9Sstevel@tonic-gate 			/*
972d2d52addSAlexander Pyhalov 			 * The current data chunk starts from prntbuf.
973d2d52addSAlexander Pyhalov 			 * This means either the buffer has no data
974d2d52addSAlexander Pyhalov 			 * or the buffer has no newline.
975d2d52addSAlexander Pyhalov 			 * So, read more data from input.
9767c478bd9Sstevel@tonic-gate 			 */
977d2d52addSAlexander Pyhalov 			count = read(fd, ptr + data_len, prntbuflen - data_len);
978d2d52addSAlexander Pyhalov 			if (count < 0) {
979d2d52addSAlexander Pyhalov 				/* read error */
980d2d52addSAlexander Pyhalov 				if (cflag) {
981d2d52addSAlexander Pyhalov 					if (outfn && !rflag) {
982d2d52addSAlexander Pyhalov 						(void) fprintf(stdout,
983d2d52addSAlexander Pyhalov 						    "%s:", fn);
984d2d52addSAlexander Pyhalov 					}
985d2d52addSAlexander Pyhalov 					if (!qflag && !rflag) {
986d2d52addSAlexander Pyhalov 						(void) fprintf(stdout, "%lld\n",
987d2d52addSAlexander Pyhalov 						    matches);
988d2d52addSAlexander Pyhalov 					}
989d2d52addSAlexander Pyhalov 				}
990d2d52addSAlexander Pyhalov 				return (0);
991d2d52addSAlexander Pyhalov 			} else if (count == 0) {
992d2d52addSAlexander Pyhalov 				/* no new data */
993d2d52addSAlexander Pyhalov 				eof = B_TRUE;
994d2d52addSAlexander Pyhalov 
995d2d52addSAlexander Pyhalov 				if (data_len == 0) {
996d2d52addSAlexander Pyhalov 					/* end of file already reached */
997d2d52addSAlexander Pyhalov 					if (conflag != 0) {
998d2d52addSAlexander Pyhalov 						if (conptrend >= conptr)
999d2d52addSAlexander Pyhalov 							*conptrend = '\n';
1000d2d52addSAlexander Pyhalov 						last_ctx = B_TRUE;
1001d2d52addSAlexander Pyhalov 						goto L_next_line;
1002d2d52addSAlexander Pyhalov 					} else {
1003d2d52addSAlexander Pyhalov 						goto out;
1004d2d52addSAlexander Pyhalov 					}
1005d2d52addSAlexander Pyhalov 				}
1006d2d52addSAlexander Pyhalov 				/* last line of file has no newline */
1007d2d52addSAlexander Pyhalov 				ptrend = ptr + data_len;
1008d2d52addSAlexander Pyhalov 				newlinep = 0;
1009d2d52addSAlexander Pyhalov 				goto L_start_process;
1010d2d52addSAlexander Pyhalov 			}
1011d2d52addSAlexander Pyhalov 			offset = data_len;
1012d2d52addSAlexander Pyhalov 			data_len += count;
1013d2d52addSAlexander Pyhalov 		}
1014d2d52addSAlexander Pyhalov 
1015d2d52addSAlexander Pyhalov 		/*
1016d2d52addSAlexander Pyhalov 		 * Look for newline in the chunk
1017d2d52addSAlexander Pyhalov 		 * between ptr + offset and ptr + data_len - offset.
1018d2d52addSAlexander Pyhalov 		 */
1019d2d52addSAlexander Pyhalov 		ptrend = find_nl(ptr + offset, data_len - offset);
1020d2d52addSAlexander Pyhalov 		if (ptrend == NULL) {
1021d2d52addSAlexander Pyhalov 			/* no newline found in this chunk */
10227c478bd9Sstevel@tonic-gate 			if (ptr > prntbuf) {
1023d2d52addSAlexander Pyhalov 				/*
1024d2d52addSAlexander Pyhalov 				 * Move remaining data to the beginning
1025d2d52addSAlexander Pyhalov 				 * of the buffer.
1026d2d52addSAlexander Pyhalov 				 * Remaining data lie from ptr for
1027d2d52addSAlexander Pyhalov 				 * data_len bytes.
1028d2d52addSAlexander Pyhalov 				 */
1029d2d52addSAlexander Pyhalov 				(void) memmove(prntbuf, ptr, data_len);
10307c478bd9Sstevel@tonic-gate 			}
1031d2d52addSAlexander Pyhalov 			if (data_len == prntbuflen) {
1032d2d52addSAlexander Pyhalov 				/*
1033d2d52addSAlexander Pyhalov 				 * Not enough room in the buffer
1034d2d52addSAlexander Pyhalov 				 */
1035d2d52addSAlexander Pyhalov 				if (prntbuflen > SIZE_MAX - BUFSIZE) {
1036d2d52addSAlexander Pyhalov 					(void) fprintf(stderr,
1037d2d52addSAlexander Pyhalov 					    gettext("%s: buflen would"
1038d2d52addSAlexander Pyhalov 					    " overflow\n"),
1039d2d52addSAlexander Pyhalov 					    cmdname);
1040d2d52addSAlexander Pyhalov 					exit(2);
1041d2d52addSAlexander Pyhalov 				}
1042d2d52addSAlexander Pyhalov 
1043d2d52addSAlexander Pyhalov 				prntbuflen += BUFSIZE;
1044d2d52addSAlexander Pyhalov 				prntbuf = realloc(prntbuf, prntbuflen + 1);
1045d2d52addSAlexander Pyhalov 				if (prntbuf == NULL) {
1046d2d52addSAlexander Pyhalov 					(void) fprintf(stderr,
1047d2d52addSAlexander Pyhalov 					    gettext("%s: out of memory\n"),
1048d2d52addSAlexander Pyhalov 					    cmdname);
1049d2d52addSAlexander Pyhalov 					exit(2);
1050d2d52addSAlexander Pyhalov 				}
1051d2d52addSAlexander Pyhalov 			}
1052d2d52addSAlexander Pyhalov 			ptr = prntbuf;
1053d2d52addSAlexander Pyhalov 			/* read the next input */
1054d2d52addSAlexander Pyhalov 			continue;
1055d2d52addSAlexander Pyhalov 		}
1056d2d52addSAlexander Pyhalov L_start_process:
10577c478bd9Sstevel@tonic-gate 
1058d2d52addSAlexander Pyhalov 		/*
1059d2d52addSAlexander Pyhalov 		 * Beginning of the chunk:	ptr
1060d2d52addSAlexander Pyhalov 		 * End of the chunk:		ptr + data_len
1061d2d52addSAlexander Pyhalov 		 * Beginning of the line:	ptr
1062d2d52addSAlexander Pyhalov 		 * End of the line:		ptrend
1063d2d52addSAlexander Pyhalov 		 *
1064d2d52addSAlexander Pyhalov 		 * conptr:	Beginning of the context.
1065d2d52addSAlexander Pyhalov 		 * conptrend: If context is empty, conptr - 1 (invalid memory).
1066d2d52addSAlexander Pyhalov 		 *	Otherwise, Last newline in the context.
1067d2d52addSAlexander Pyhalov 		 */
1068d2d52addSAlexander Pyhalov 
1069d2d52addSAlexander Pyhalov 		if (use_bmg) {
10707c478bd9Sstevel@tonic-gate 			/*
1071d2d52addSAlexander Pyhalov 			 * Use Boyer-Moore-Gosper algorithm to find out if
1072d2d52addSAlexander Pyhalov 			 * this chunk (not this line) contains the specified
1073d2d52addSAlexander Pyhalov 			 * pattern.  If not, restart from the last line
1074d2d52addSAlexander Pyhalov 			 * of this chunk.
10757c478bd9Sstevel@tonic-gate 			 */
1076d2d52addSAlexander Pyhalov 			char	*bline;
1077d2d52addSAlexander Pyhalov 			bline = bmgexec(ptr, ptr + data_len);
1078d2d52addSAlexander Pyhalov 			if (bline == NULL) {
10797c478bd9Sstevel@tonic-gate 				/*
1080d2d52addSAlexander Pyhalov 				 * No pattern found in this chunk.
1081d2d52addSAlexander Pyhalov 				 * Need to find the last line
1082d2d52addSAlexander Pyhalov 				 * in this chunk.
10837c478bd9Sstevel@tonic-gate 				 */
1084d2d52addSAlexander Pyhalov 				ptrend = rfind_nl(ptr, data_len);
10857c478bd9Sstevel@tonic-gate 
10867c478bd9Sstevel@tonic-gate 				/*
1087d2d52addSAlexander Pyhalov 				 * When this chunk does not contain newline,
1088d2d52addSAlexander Pyhalov 				 * ptrend becomes NULL, which should happen
1089d2d52addSAlexander Pyhalov 				 * when the last line of file does not end
1090d2d52addSAlexander Pyhalov 				 * with a newline.  At such a point,
1091d2d52addSAlexander Pyhalov 				 * newlinep should have been set to 0.
1092d2d52addSAlexander Pyhalov 				 * Therefore, just after jumping to
1093d2d52addSAlexander Pyhalov 				 * L_skip_line, the main for-loop quits,
1094d2d52addSAlexander Pyhalov 				 * and the line_len value won't be
1095d2d52addSAlexander Pyhalov 				 * used.
1096d2d52addSAlexander Pyhalov 				 */
1097d2d52addSAlexander Pyhalov 				line_len = ptrend - ptr;
1098d2d52addSAlexander Pyhalov 				goto L_skip_line;
1099d2d52addSAlexander Pyhalov 			}
1100d2d52addSAlexander Pyhalov 			if (bline > ptrend) {
1101d2d52addSAlexander Pyhalov 				/*
1102d2d52addSAlexander Pyhalov 				 * Pattern found not in the first line
1103d2d52addSAlexander Pyhalov 				 * of this chunk.
1104d2d52addSAlexander Pyhalov 				 * Discard the first line.
11057c478bd9Sstevel@tonic-gate 				 */
1106d2d52addSAlexander Pyhalov 				line_len = ptrend - ptr;
1107d2d52addSAlexander Pyhalov 				goto L_skip_line;
1108d2d52addSAlexander Pyhalov 			}
1109d2d52addSAlexander Pyhalov 			/*
1110d2d52addSAlexander Pyhalov 			 * Pattern found in the first line of this chunk.
1111d2d52addSAlexander Pyhalov 			 * Using this result.
1112d2d52addSAlexander Pyhalov 			 */
1113d2d52addSAlexander Pyhalov 			*ptrend = '\0';
1114d2d52addSAlexander Pyhalov 			line_len = ptrend - ptr;
1115d2d52addSAlexander Pyhalov 
1116d2d52addSAlexander Pyhalov 			/*
1117d2d52addSAlexander Pyhalov 			 * before jumping to L_next_line,
1118d2d52addSAlexander Pyhalov 			 * need to handle xflag if specified
1119d2d52addSAlexander Pyhalov 			 */
1120d2d52addSAlexander Pyhalov 			if (xflag && (line_len != bmglen ||
1121d2d52addSAlexander Pyhalov 			    strcmp(bmgpat, ptr) != 0)) {
1122d2d52addSAlexander Pyhalov 				/* didn't match */
1123d2d52addSAlexander Pyhalov 				pp = NULL;
1124d2d52addSAlexander Pyhalov 			} else {
1125d2d52addSAlexander Pyhalov 				pp = patterns; /* to make it happen */
1126d2d52addSAlexander Pyhalov 			}
1127d2d52addSAlexander Pyhalov 			goto L_next_line;
1128d2d52addSAlexander Pyhalov 		}
1129d2d52addSAlexander Pyhalov 		lineno++;
1130d2d52addSAlexander Pyhalov 		/*
1131d2d52addSAlexander Pyhalov 		 * Line starts from ptr and ends at ptrend.
1132d2d52addSAlexander Pyhalov 		 * line_len will be the length of the line.
1133d2d52addSAlexander Pyhalov 		 */
1134d2d52addSAlexander Pyhalov 		*ptrend = '\0';
1135d2d52addSAlexander Pyhalov 		line_len = ptrend - ptr;
1136d2d52addSAlexander Pyhalov 
1137d2d52addSAlexander Pyhalov 		/*
1138d2d52addSAlexander Pyhalov 		 * From now, the process will be performed based
1139d2d52addSAlexander Pyhalov 		 * on the line from ptr to ptrend.
1140d2d52addSAlexander Pyhalov 		 */
1141d2d52addSAlexander Pyhalov 		if (use_wchar) {
1142d2d52addSAlexander Pyhalov 			size_t	len;
1143d2d52addSAlexander Pyhalov 
1144d2d52addSAlexander Pyhalov 			if (line_len >= outbuflen) {
1145d2d52addSAlexander Pyhalov 				outbuflen = line_len + 1;
1146d2d52addSAlexander Pyhalov 				outline = realloc(outline,
1147d2d52addSAlexander Pyhalov 				    sizeof (wchar_t) * outbuflen);
1148d2d52addSAlexander Pyhalov 				if (outline == NULL) {
1149d2d52addSAlexander Pyhalov 					(void) fprintf(stderr,
1150d2d52addSAlexander Pyhalov 					    gettext("%s: out of memory\n"),
1151d2d52addSAlexander Pyhalov 					    cmdname);
11527c478bd9Sstevel@tonic-gate 					exit(2);
1153d2d52addSAlexander Pyhalov 				}
1154d2d52addSAlexander Pyhalov 			}
11557c478bd9Sstevel@tonic-gate 
1156d2d52addSAlexander Pyhalov 			len = mbstowcs(outline, ptr, line_len);
1157d2d52addSAlexander Pyhalov 			if (len == (size_t)-1) {
1158d2d52addSAlexander Pyhalov 				(void) fprintf(stderr, gettext(
1159d2d52addSAlexander Pyhalov 	"%s: input file \"%s\": line %lld: invalid multibyte character\n"),
1160d2d52addSAlexander Pyhalov 				    cmdname, fn, lineno);
1161d2d52addSAlexander Pyhalov 				/* never match a line with invalid sequence */
1162d2d52addSAlexander Pyhalov 				goto L_skip_line;
11637c478bd9Sstevel@tonic-gate 			}
1164d2d52addSAlexander Pyhalov 			outline[len] = L'\0';
11657c478bd9Sstevel@tonic-gate 
1166d2d52addSAlexander Pyhalov 			if (iflag) {
1167d2d52addSAlexander Pyhalov 				wchar_t	*cp;
1168d2d52addSAlexander Pyhalov 				for (cp = outline; *cp != '\0'; cp++) {
1169d2d52addSAlexander Pyhalov 					*cp = towlower((wint_t)*cp);
1170d2d52addSAlexander Pyhalov 				}
1171d2d52addSAlexander Pyhalov 			}
11727c478bd9Sstevel@tonic-gate 
1173d2d52addSAlexander Pyhalov 			if (xflag) {
1174d2d52addSAlexander Pyhalov 				for (pp = patterns; pp; pp = pp->next) {
1175d2d52addSAlexander Pyhalov 					if (outline[0] == pp->wpattern[0] &&
1176d2d52addSAlexander Pyhalov 					    wcscmp(outline,
1177d2d52addSAlexander Pyhalov 					    pp->wpattern) == 0) {
1178d2d52addSAlexander Pyhalov 						/* matched */
1179d2d52addSAlexander Pyhalov 						break;
1180d2d52addSAlexander Pyhalov 					}
1181d2d52addSAlexander Pyhalov 				}
1182d2d52addSAlexander Pyhalov 			} else {
1183d2d52addSAlexander Pyhalov 				for (pp = patterns; pp; pp = pp->next) {
1184d2d52addSAlexander Pyhalov 					if (wcswcs(outline, pp->wpattern)
1185d2d52addSAlexander Pyhalov 					    != NULL) {
1186d2d52addSAlexander Pyhalov 						/* matched */
1187d2d52addSAlexander Pyhalov 						break;
1188d2d52addSAlexander Pyhalov 					}
1189d2d52addSAlexander Pyhalov 				}
1190d2d52addSAlexander Pyhalov 			}
1191d2d52addSAlexander Pyhalov 		} else if (Fflag) {
1192d2d52addSAlexander Pyhalov 			/* fgrep in byte-oriented handling */
1193d2d52addSAlexander Pyhalov 			char	*fptr;
1194d2d52addSAlexander Pyhalov 			if (iflag) {
1195d2d52addSAlexander Pyhalov 				fptr = istrdup(ptr);
1196d2d52addSAlexander Pyhalov 			} else {
1197d2d52addSAlexander Pyhalov 				fptr = ptr;
1198d2d52addSAlexander Pyhalov 			}
1199d2d52addSAlexander Pyhalov 			if (xflag) {
1200d2d52addSAlexander Pyhalov 				/* fgrep -x */
1201d2d52addSAlexander Pyhalov 				for (pp = patterns; pp; pp = pp->next) {
1202d2d52addSAlexander Pyhalov 					if (fptr[0] == pp->pattern[0] &&
1203d2d52addSAlexander Pyhalov 					    strcmp(fptr, pp->pattern) == 0) {
1204d2d52addSAlexander Pyhalov 						/* matched */
1205d2d52addSAlexander Pyhalov 						break;
1206d2d52addSAlexander Pyhalov 					}
1207d2d52addSAlexander Pyhalov 				}
1208d2d52addSAlexander Pyhalov 			} else {
1209d2d52addSAlexander Pyhalov 				for (pp = patterns; pp; pp = pp->next) {
1210d2d52addSAlexander Pyhalov 					if (strstr(fptr, pp->pattern) != NULL) {
1211d2d52addSAlexander Pyhalov 						/* matched */
1212d2d52addSAlexander Pyhalov 						break;
1213d2d52addSAlexander Pyhalov 					}
1214d2d52addSAlexander Pyhalov 				}
1215d2d52addSAlexander Pyhalov 			}
1216d2d52addSAlexander Pyhalov 		} else {
1217d2d52addSAlexander Pyhalov 			/* grep or egrep */
1218d2d52addSAlexander Pyhalov 			for (pp = patterns; pp; pp = pp->next) {
1219d2d52addSAlexander Pyhalov 				int	rv;
12207c478bd9Sstevel@tonic-gate 
1221d2d52addSAlexander Pyhalov 				rv = regexec(&pp->re, ptr, 0, NULL, 0);
1222d2d52addSAlexander Pyhalov 				if (rv == REG_OK) {
1223d2d52addSAlexander Pyhalov 					/* matched */
1224d2d52addSAlexander Pyhalov 					break;
1225d2d52addSAlexander Pyhalov 				}
1226d2d52addSAlexander Pyhalov 
1227d2d52addSAlexander Pyhalov 				switch (rv) {
1228d2d52addSAlexander Pyhalov 				case REG_NOMATCH:
1229d2d52addSAlexander Pyhalov 					break;
1230d2d52addSAlexander Pyhalov 				case REG_ECHAR:
1231d2d52addSAlexander Pyhalov 					(void) fprintf(stderr, gettext(
1232d2d52addSAlexander Pyhalov 	    "%s: input file \"%s\": line %lld: invalid multibyte character\n"),
1233d2d52addSAlexander Pyhalov 					    cmdname, fn, lineno);
1234d2d52addSAlexander Pyhalov 					break;
1235d2d52addSAlexander Pyhalov 				default:
1236d2d52addSAlexander Pyhalov 					(void) regerror(rv, &pp->re, errstr,
1237d2d52addSAlexander Pyhalov 					    sizeof (errstr));
1238d2d52addSAlexander Pyhalov 					(void) fprintf(stderr, gettext(
1239d2d52addSAlexander Pyhalov 	    "%s: input file \"%s\": line %lld: %s\n"),
1240d2d52addSAlexander Pyhalov 					    cmdname, fn, lineno, errstr);
1241d2d52addSAlexander Pyhalov 					exit(2);
1242d2d52addSAlexander Pyhalov 				}
1243d2d52addSAlexander Pyhalov 			}
1244d2d52addSAlexander Pyhalov 		}
1245d2d52addSAlexander Pyhalov 
1246d2d52addSAlexander Pyhalov 		/*
1247d2d52addSAlexander Pyhalov 		 * Context is set up as follows:
1248d2d52addSAlexander Pyhalov 		 * For a 'Before' context, we maintain a set of pointers
1249d2d52addSAlexander Pyhalov 		 * containing 'N' lines of context. If the current number of
1250d2d52addSAlexander Pyhalov 		 * lines contained is greater than N, and N isn't a match, the
1251d2d52addSAlexander Pyhalov 		 * start pointer is moved forward to the next newline.
1252d2d52addSAlexander Pyhalov 		 *
1253d2d52addSAlexander Pyhalov 		 * If we ever find a match, we print out immediately.
1254d2d52addSAlexander Pyhalov 		 * 'nearmatch' tells us if we're within N+1 lines of the last
1255d2d52addSAlexander Pyhalov 		 * match ; if we are, and we find another match, we don't
1256d2d52addSAlexander Pyhalov 		 * separate the matches. 'nearmatch' becomes false when
1257d2d52addSAlexander Pyhalov 		 * a line gets rotated out of the context.
1258d2d52addSAlexander Pyhalov 		 *
1259d2d52addSAlexander Pyhalov 		 * For an 'After' context, we simply wait until we've found a
1260d2d52addSAlexander Pyhalov 		 * match, then create a context N+1 lines big. If we don't find
1261d2d52addSAlexander Pyhalov 		 * a match within the context, we print out the current context.
1262d2d52addSAlexander Pyhalov 		 * Otherwise, we save a reference to the new matching line,
1263d2d52addSAlexander Pyhalov 		 * print out the other context, and reset our context pointers
1264d2d52addSAlexander Pyhalov 		 * to the new matching line.
1265d2d52addSAlexander Pyhalov 		 *
1266d2d52addSAlexander Pyhalov 		 * 'nearmatch' becomes false when we find a non-matching line
1267d2d52addSAlexander Pyhalov 		 * that isn't a part of any context.
1268d2d52addSAlexander Pyhalov 		 *
1269d2d52addSAlexander Pyhalov 		 * A full-context is implemented as a combination of the
1270d2d52addSAlexander Pyhalov 		 * 'Before' and 'After' context logic. Before we find a match,
1271d2d52addSAlexander Pyhalov 		 * we follow the Before logic. When we find a match, we
1272d2d52addSAlexander Pyhalov 		 * follow the After logic. 'nearmatch' is handled by the Before
1273d2d52addSAlexander Pyhalov 		 * logic.
1274d2d52addSAlexander Pyhalov 		 */
1275d2d52addSAlexander Pyhalov 
1276d2d52addSAlexander Pyhalov 		if (conflag == 0)
1277d2d52addSAlexander Pyhalov 			goto L_next_line;
1278d2d52addSAlexander Pyhalov 
1279d2d52addSAlexander Pyhalov 		/* Do we have room to add this line to the context buffer? */
1280d2d52addSAlexander Pyhalov 		if ((line_len + 1) > (conbuflen -
1281d2d52addSAlexander Pyhalov 		    (conptrend >= conptr) ? conptrend - conbuf : 0)) {
1282d2d52addSAlexander Pyhalov 			char *oldconbuf = conbuf;
1283d2d52addSAlexander Pyhalov 			char *oldconptr = conptr;
1284d2d52addSAlexander Pyhalov 			long tmp = matchptr - conptr;
1285d2d52addSAlexander Pyhalov 
1286d2d52addSAlexander Pyhalov 			if (conbuflen > SIZE_MAX - BUFSIZE) {
1287d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
1288d2d52addSAlexander Pyhalov 				    gettext("%s: buflen would overflow\n"),
1289d2d52addSAlexander Pyhalov 				    cmdname);
1290d2d52addSAlexander Pyhalov 				exit(2);
1291d2d52addSAlexander Pyhalov 			}
1292d2d52addSAlexander Pyhalov 
1293d2d52addSAlexander Pyhalov 			conbuflen += BUFSIZE;
1294d2d52addSAlexander Pyhalov 			conbuf = realloc(conbuf, conbuflen + 1);
1295d2d52addSAlexander Pyhalov 			if (conbuf == NULL) {
1296d2d52addSAlexander Pyhalov 				(void) fprintf(stderr,
1297d2d52addSAlexander Pyhalov 				    gettext("%s: out of memory\n"),
1298d2d52addSAlexander Pyhalov 				    cmdname);
1299d2d52addSAlexander Pyhalov 				exit(2);
1300d2d52addSAlexander Pyhalov 			}
1301d2d52addSAlexander Pyhalov 
1302d2d52addSAlexander Pyhalov 			conptr = conbuf + (conptr - oldconbuf);
1303d2d52addSAlexander Pyhalov 			conptrend = conptr + (conptrend - oldconptr);
1304d2d52addSAlexander Pyhalov 			if (matchptr)
1305d2d52addSAlexander Pyhalov 				matchptr = conptr + tmp;
1306d2d52addSAlexander Pyhalov 		}
1307d2d52addSAlexander Pyhalov 		(void) memcpy(conptrend + 1, ptr, line_len);
1308d2d52addSAlexander Pyhalov 		conptrend += line_len + 1;
1309d2d52addSAlexander Pyhalov 		*conptrend = '\n';
1310d2d52addSAlexander Pyhalov 
1311d2d52addSAlexander Pyhalov 		if (nvflag == (pp != NULL)) {
1312d2d52addSAlexander Pyhalov 			/* matched */
1313d2d52addSAlexander Pyhalov 			if (havematch) {
1314d2d52addSAlexander Pyhalov 				if ((conflag & AFTER) != 0) {
1315d2d52addSAlexander Pyhalov 					conaprnt = 1;
1316d2d52addSAlexander Pyhalov 					nextend = conptrend;
1317d2d52addSAlexander Pyhalov 					conptrend = conptr + lastmatch;
1318d2d52addSAlexander Pyhalov 					nextptr = conptrend + 1;
1319d2d52addSAlexander Pyhalov 					*nextend = '\n';
1320d2d52addSAlexander Pyhalov 				}
1321d2d52addSAlexander Pyhalov 			} else {
1322d2d52addSAlexander Pyhalov 				if (conflag == AFTER) {
1323d2d52addSAlexander Pyhalov 					conptr = conptrend - (line_len);
1324d2d52addSAlexander Pyhalov 					linenum = lineno;
1325d2d52addSAlexander Pyhalov 				}
1326d2d52addSAlexander Pyhalov 				blkoffset = line_offset -
1327d2d52addSAlexander Pyhalov 				    (conptrend - conptr - line_len);
1328d2d52addSAlexander Pyhalov 			}
1329d2d52addSAlexander Pyhalov 
1330d2d52addSAlexander Pyhalov 			if (conflag == BEFORE)
1331d2d52addSAlexander Pyhalov 				conbprnt = 1;
1332d2d52addSAlexander Pyhalov 
1333d2d52addSAlexander Pyhalov 			lastmatch = conptrend - conptr;
1334d2d52addSAlexander Pyhalov 			havematch = B_TRUE;
1335d2d52addSAlexander Pyhalov 			goto L_next_line;
1336d2d52addSAlexander Pyhalov 		}
1337d2d52addSAlexander Pyhalov 
1338d2d52addSAlexander Pyhalov 		if (!havematch) {
1339d2d52addSAlexander Pyhalov 			if ((conflag & BEFORE) != 0) {
1340d2d52addSAlexander Pyhalov 				if (conbcnt >= conblen) {
1341d2d52addSAlexander Pyhalov 					char *tmp = conptr;
1342d2d52addSAlexander Pyhalov 					conptr = find_nl(conptr,
1343d2d52addSAlexander Pyhalov 					    conptrend - conptr) + 1;
1344d2d52addSAlexander Pyhalov 					if (bflag)
1345d2d52addSAlexander Pyhalov 						blkoffset += conptr - tmp;
1346d2d52addSAlexander Pyhalov 					linenum++;
1347d2d52addSAlexander Pyhalov 					nearmatch = B_TRUE;
1348d2d52addSAlexander Pyhalov 				} else {
1349d2d52addSAlexander Pyhalov 					conbcnt++;
1350d2d52addSAlexander Pyhalov 				}
1351d2d52addSAlexander Pyhalov 			}
1352d2d52addSAlexander Pyhalov 			if (conflag == AFTER)
1353d2d52addSAlexander Pyhalov 				nearmatch = B_TRUE;
1354d2d52addSAlexander Pyhalov 		} else  {
1355d2d52addSAlexander Pyhalov 			if (++conacnt >= conalen && !conaprnt && conalen)
1356d2d52addSAlexander Pyhalov 				conaprnt = 1;
1357d2d52addSAlexander Pyhalov 			else
1358d2d52addSAlexander Pyhalov 				lastmatch = conptrend - conptr;
1359d2d52addSAlexander Pyhalov 		}
1360d2d52addSAlexander Pyhalov 
1361d2d52addSAlexander Pyhalov L_next_line:
1362d2d52addSAlexander Pyhalov 		/*
1363d2d52addSAlexander Pyhalov 		 * Here, if pp points to non-NULL, something has been matched
1364d2d52addSAlexander Pyhalov 		 * to the pattern.
1365d2d52addSAlexander Pyhalov 		 */
1366d2d52addSAlexander Pyhalov 		if (!last_ctx && nvflag == (pp != NULL)) {
1367d2d52addSAlexander Pyhalov 			matches++;
1368d2d52addSAlexander Pyhalov 			if (!nextend)
1369d2d52addSAlexander Pyhalov 				matchptr = (conflag != 0) ? conptrend : ptrend;
1370d2d52addSAlexander Pyhalov 		}
1371d2d52addSAlexander Pyhalov 
1372d2d52addSAlexander Pyhalov 		/*
1373d2d52addSAlexander Pyhalov 		 * Set up some print context so that we can treat
1374d2d52addSAlexander Pyhalov 		 * single-line matches as a zero-N context.
1375d2d52addSAlexander Pyhalov 		 * Apply CLI flags to each line of the context.
1376d2d52addSAlexander Pyhalov 		 *
1377d2d52addSAlexander Pyhalov 		 * For context, we only print if we both have a match and are
1378d2d52addSAlexander Pyhalov 		 * either at the end of the data stream, or we've previously
1379d2d52addSAlexander Pyhalov 		 * declared that we want to print for a particular context.
1380d2d52addSAlexander Pyhalov 		 */
1381d2d52addSAlexander Pyhalov 		if (havematch && (eof || conaprnt || conbprnt)) {
1382d2d52addSAlexander Pyhalov 
1383d2d52addSAlexander Pyhalov 			/*
1384d2d52addSAlexander Pyhalov 			 * We'd normally do this earlier, but we had to
1385d2d52addSAlexander Pyhalov 			 * escape early because we reached the end of the data.
1386d2d52addSAlexander Pyhalov 			 */
1387d2d52addSAlexander Pyhalov 			if (eof && nextptr)
1388d2d52addSAlexander Pyhalov 				conptrend = nextend;
1389d2d52addSAlexander Pyhalov 
1390d2d52addSAlexander Pyhalov 			prntlen = conptrend - conptr + 1;
1391d2d52addSAlexander Pyhalov 			prntptr = conptr;
1392d2d52addSAlexander Pyhalov 			if (conmatches++ && nearmatch && !cflag)
1393d2d52addSAlexander Pyhalov 				(void) fwrite("--\n", 1, 3, stdout);
1394d2d52addSAlexander Pyhalov 		} else if (conflag == 0 && nvflag == (pp != NULL)) {
1395d2d52addSAlexander Pyhalov 			*ptrend = '\n';
1396d2d52addSAlexander Pyhalov 			prntlen = line_len + 1;
1397d2d52addSAlexander Pyhalov 			prntptr = ptr;
1398d2d52addSAlexander Pyhalov 			linenum = lineno;
1399d2d52addSAlexander Pyhalov 			blkoffset = line_offset;
1400d2d52addSAlexander Pyhalov 		} else if (eof) {
1401d2d52addSAlexander Pyhalov 			/* No match and no more data */
1402d2d52addSAlexander Pyhalov 			goto out;
14037c478bd9Sstevel@tonic-gate 		} else {
1404d2d52addSAlexander Pyhalov 			/* No match, or we're not done building context */
1405d2d52addSAlexander Pyhalov 			goto L_skip_line;
14067c478bd9Sstevel@tonic-gate 		}
14077c478bd9Sstevel@tonic-gate 
1408d2d52addSAlexander Pyhalov 		prntptrend = prntptr - 1;
1409d2d52addSAlexander Pyhalov 		while ((prntptrend = find_nl(prntptrend + 1,
1410d2d52addSAlexander Pyhalov 		    prntlen)) != NULL) {
1411d2d52addSAlexander Pyhalov 
14127c478bd9Sstevel@tonic-gate 			/*
1413d2d52addSAlexander Pyhalov 			 * GNU grep uses '-' for context lines and ':' for
1414d2d52addSAlexander Pyhalov 			 * matching lines, so replicate that here.
14157c478bd9Sstevel@tonic-gate 			 */
1416d2d52addSAlexander Pyhalov 			if (prntptrend == matchptr) {
1417d2d52addSAlexander Pyhalov 				if (eof && nextptr) {
1418d2d52addSAlexander Pyhalov 					matchptr = nextend;
1419d2d52addSAlexander Pyhalov 					nextptr = NULL;
1420d2d52addSAlexander Pyhalov 				} else {
1421d2d52addSAlexander Pyhalov 					matchptr = NULL;
1422d2d52addSAlexander Pyhalov 				}
1423d2d52addSAlexander Pyhalov 				separate = ':';
1424d2d52addSAlexander Pyhalov 			} else {
1425d2d52addSAlexander Pyhalov 				separate = '-';
1426d2d52addSAlexander Pyhalov 			}
1427d2d52addSAlexander Pyhalov 
14287c478bd9Sstevel@tonic-gate 			/*
1429d2d52addSAlexander Pyhalov 			 * Handle q, l, and c flags.
14307c478bd9Sstevel@tonic-gate 			 */
1431d2d52addSAlexander Pyhalov 			if (qflag) {
1432d2d52addSAlexander Pyhalov 				/* no need to continue */
1433d2d52addSAlexander Pyhalov 				/*
1434d2d52addSAlexander Pyhalov 				 * End of this line is ptrend.
1435d2d52addSAlexander Pyhalov 				 * We have read up to ptr + data_len.
1436d2d52addSAlexander Pyhalov 				 */
1437d2d52addSAlexander Pyhalov 				off_t	pos;
1438d2d52addSAlexander Pyhalov 				pos = ptr + data_len - (ptrend + 1);
1439d2d52addSAlexander Pyhalov 				(void) lseek(fd, -pos, SEEK_CUR);
1440d2d52addSAlexander Pyhalov 				exit(0);
1441d2d52addSAlexander Pyhalov 			}
1442d2d52addSAlexander Pyhalov 			if (lflag) {
1443d2d52addSAlexander Pyhalov 				(void) printf("%s\n", fn);
1444d2d52addSAlexander Pyhalov 				goto out;
1445d2d52addSAlexander Pyhalov 			}
1446d2d52addSAlexander Pyhalov 			if (!cflag) {
1447d2d52addSAlexander Pyhalov 				if (Hflag || outfn) {
1448d2d52addSAlexander Pyhalov 					(void) printf("%s%c", fn, separate);
1449d2d52addSAlexander Pyhalov 				}
1450d2d52addSAlexander Pyhalov 				if (bflag) {
1451d2d52addSAlexander Pyhalov 					(void) printf("%lld%c", (offset_t)
1452d2d52addSAlexander Pyhalov 					    (blkoffset / BSIZE), separate);
1453d2d52addSAlexander Pyhalov 				}
1454d2d52addSAlexander Pyhalov 				if (nflag) {
1455d2d52addSAlexander Pyhalov 					(void) printf("%lld%c", linenum,
1456d2d52addSAlexander Pyhalov 					    separate);
1457d2d52addSAlexander Pyhalov 				}
1458d2d52addSAlexander Pyhalov 				(void) fwrite(prntptr, 1,
1459d2d52addSAlexander Pyhalov 				    prntptrend - prntptr + 1, stdout);
1460d2d52addSAlexander Pyhalov 			}
1461d2d52addSAlexander Pyhalov 			if (ferror(stdout)) {
1462d2d52addSAlexander Pyhalov 				return (0);
1463d2d52addSAlexander Pyhalov 			}
1464d2d52addSAlexander Pyhalov 			linenum++;
1465d2d52addSAlexander Pyhalov 			prntlen -= prntptrend - prntptr + 1;
1466d2d52addSAlexander Pyhalov 			blkoffset += prntptrend - prntptr + 1;
1467d2d52addSAlexander Pyhalov 			prntptr = prntptrend + 1;
1468d2d52addSAlexander Pyhalov 		}
14697c478bd9Sstevel@tonic-gate 
1470d2d52addSAlexander Pyhalov 		if (eof)
1471d2d52addSAlexander Pyhalov 			goto out;
1472d2d52addSAlexander Pyhalov 
1473d2d52addSAlexander Pyhalov 		/*
1474d2d52addSAlexander Pyhalov 		 * Update context buffer and variables post-print
1475d2d52addSAlexander Pyhalov 		 */
1476d2d52addSAlexander Pyhalov 		if (conflag != 0) {
1477d2d52addSAlexander Pyhalov 			conptr = conbuf;
1478d2d52addSAlexander Pyhalov 			conaprnt = conbprnt = 0;
1479d2d52addSAlexander Pyhalov 			nearmatch = B_FALSE;
1480d2d52addSAlexander Pyhalov 			conacnt = conbcnt = 0;
1481d2d52addSAlexander Pyhalov 
1482d2d52addSAlexander Pyhalov 			if (nextptr) {
1483d2d52addSAlexander Pyhalov 				(void) memmove(conbuf, nextptr,
1484d2d52addSAlexander Pyhalov 				    nextend - nextptr + 1);
1485d2d52addSAlexander Pyhalov 				blkoffset += nextptr - conptrend - 1;
1486d2d52addSAlexander Pyhalov 				conptrend = conptr + (nextend - nextptr);
1487d2d52addSAlexander Pyhalov 				matchptr = conptrend;
1488d2d52addSAlexander Pyhalov 				linenum = lineno;
1489d2d52addSAlexander Pyhalov 				lastmatch = conptrend - conptr;
1490d2d52addSAlexander Pyhalov 				havematch = B_TRUE;
1491d2d52addSAlexander Pyhalov 			} else {
1492d2d52addSAlexander Pyhalov 				conptrend = conptr - 1;
1493d2d52addSAlexander Pyhalov 				conacnt = 0;
1494d2d52addSAlexander Pyhalov 				lastmatch = 0;
1495d2d52addSAlexander Pyhalov 				havematch = B_FALSE;
1496d2d52addSAlexander Pyhalov 			}
1497d2d52addSAlexander Pyhalov 			nextptr = nextend = NULL;
1498d2d52addSAlexander Pyhalov 		}
14997c478bd9Sstevel@tonic-gate 
1500d2d52addSAlexander Pyhalov L_skip_line:
1501d2d52addSAlexander Pyhalov 		if (!newlinep)
15027c478bd9Sstevel@tonic-gate 			break;
15037c478bd9Sstevel@tonic-gate 
1504d2d52addSAlexander Pyhalov 		data_len -= line_len + 1;
1505d2d52addSAlexander Pyhalov 		line_offset += line_len + 1;
1506d2d52addSAlexander Pyhalov 		ptr = ptrend + 1;
15077c478bd9Sstevel@tonic-gate 	}
15087c478bd9Sstevel@tonic-gate 
1509d2d52addSAlexander Pyhalov out:
1510d2d52addSAlexander Pyhalov 	if (cflag) {
1511d2d52addSAlexander Pyhalov 		if (Hflag || outfn) {
1512d2d52addSAlexander Pyhalov 			(void) printf("%s:", fn);
1513d2d52addSAlexander Pyhalov 		}
1514d2d52addSAlexander Pyhalov 		if (!qflag) {
1515d2d52addSAlexander Pyhalov 			(void) printf("%lld\n", matches);
1516d2d52addSAlexander Pyhalov 		}
15177c478bd9Sstevel@tonic-gate 	}
1518d2d52addSAlexander Pyhalov 	return (matches != 0);
15197c478bd9Sstevel@tonic-gate }
15207c478bd9Sstevel@tonic-gate 
1521d2d52addSAlexander Pyhalov /*
1522d2d52addSAlexander Pyhalov  * usage message for grep
1523d2d52addSAlexander Pyhalov  */
1524d2d52addSAlexander Pyhalov static void
1525d2d52addSAlexander Pyhalov usage(void)
15267c478bd9Sstevel@tonic-gate {
1527d2d52addSAlexander Pyhalov 	if (egrep || fgrep) {
1528d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1529d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1530d2d52addSAlexander Pyhalov 		    gettext(" [-c|-l|-q] [-r|-R] "
1531d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1532d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] pattern_list [file ...]\n"));
15337c478bd9Sstevel@tonic-gate 
1534d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, "\t%s", cmdname);
1535d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1536d2d52addSAlexander Pyhalov 		    gettext(" [-c|-l|-q] [-r|-R] "
1537d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1538d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] [-e pattern_list]... "
1539d2d52addSAlexander Pyhalov 		    "[-f pattern_file]... [file...]\n"));
1540d2d52addSAlexander Pyhalov 	} else {
1541d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
1542d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1543d2d52addSAlexander Pyhalov 		    gettext(" [-c|-l|-q] [-r|-R] "
1544d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1545d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] pattern_list [file ...]\n"));
15463ed621bcSAlexander Eremin 
1547d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, "\t%s", cmdname);
1548d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1549d2d52addSAlexander Pyhalov 		    gettext(" [-c|-l|-q] [-r|-R] "
1550d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1551d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] [-e pattern_list]... "
1552d2d52addSAlexander Pyhalov 		    "[-f pattern_file]... [file...]\n"));
15537c478bd9Sstevel@tonic-gate 
1554d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, "\t%s", cmdname);
1555d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1556d2d52addSAlexander Pyhalov 		    gettext(" -E [-c|-l|-q] [-r|-R] "
1557d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1558d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] pattern_list [file ...]\n"));
15597c478bd9Sstevel@tonic-gate 
1560d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, "\t%s", cmdname);
1561d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1562d2d52addSAlexander Pyhalov 		    gettext(" -E [-c|-l|-q] [-r|-R] "
1563d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1564d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] [-e pattern_list]... "
1565d2d52addSAlexander Pyhalov 		    "[-f pattern_file]... [file...]\n"));
1566d2d52addSAlexander Pyhalov 
1567d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, "\t%s", cmdname);
1568d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1569d2d52addSAlexander Pyhalov 		    gettext(" -F [-c|-l|-q] [-r|-R] "
1570d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1571d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] pattern_list [file ...]\n"));
1572d2d52addSAlexander Pyhalov 
1573d2d52addSAlexander Pyhalov 		(void) fprintf(stderr, "\t%s", cmdname);
1574d2d52addSAlexander Pyhalov 		(void) fprintf(stderr,
1575d2d52addSAlexander Pyhalov 		    gettext(" -F [-c|-l|-q] "
1576d2d52addSAlexander Pyhalov 		    "[-A num] [-B num] [-C num|-num] "
1577d2d52addSAlexander Pyhalov 		    "[-bhHinsvx] [-e pattern_list]... "
1578d2d52addSAlexander Pyhalov 		    "[-f pattern_file]... [file...]\n"));
1579e52fb54bSAlexander Eremin 	}
1580d2d52addSAlexander Pyhalov 	exit(2);
1581d2d52addSAlexander Pyhalov 	/* NOTREACHED */
1582d2d52addSAlexander Pyhalov }
15837c478bd9Sstevel@tonic-gate 
1584d2d52addSAlexander Pyhalov /*
1585d2d52addSAlexander Pyhalov  * Compile literal pattern into BMG tables
1586d2d52addSAlexander Pyhalov  */
1587d2d52addSAlexander Pyhalov static void
1588d2d52addSAlexander Pyhalov bmgcomp(char *pat, int len)
1589d2d52addSAlexander Pyhalov {
1590d2d52addSAlexander Pyhalov 	int	i;
1591d2d52addSAlexander Pyhalov 	int	tlen;
1592d2d52addSAlexander Pyhalov 	unsigned char	*uc = (unsigned char *)pat;
15937c478bd9Sstevel@tonic-gate 
1594d2d52addSAlexander Pyhalov 	bmglen = len;
1595d2d52addSAlexander Pyhalov 	bmgpat = pat;
15967c478bd9Sstevel@tonic-gate 
1597d2d52addSAlexander Pyhalov 	for (i = 0; i < M_CSETSIZE; i++) {
1598d2d52addSAlexander Pyhalov 		bmgtab[i] = len;
15997c478bd9Sstevel@tonic-gate 	}
16007c478bd9Sstevel@tonic-gate 
1601d2d52addSAlexander Pyhalov 	len--;
1602d2d52addSAlexander Pyhalov 	for (tlen = len, i = 0; i <= len; i++, tlen--) {
1603d2d52addSAlexander Pyhalov 		bmgtab[*uc++] = tlen;
1604d2d52addSAlexander Pyhalov 	}
16057c478bd9Sstevel@tonic-gate }
16067c478bd9Sstevel@tonic-gate 
1607d2d52addSAlexander Pyhalov /*
1608d2d52addSAlexander Pyhalov  * BMG search.
1609d2d52addSAlexander Pyhalov  */
1610d2d52addSAlexander Pyhalov static char *
1611d2d52addSAlexander Pyhalov bmgexec(char *str, char *end)
16127c478bd9Sstevel@tonic-gate {
1613d2d52addSAlexander Pyhalov 	int	t;
1614d2d52addSAlexander Pyhalov 	char	*k, *s, *p;
16157c478bd9Sstevel@tonic-gate 
1616d2d52addSAlexander Pyhalov 	k = str + bmglen - 1;
1617d2d52addSAlexander Pyhalov 	if (bmglen == 1) {
1618d2d52addSAlexander Pyhalov 		return (memchr(str, bmgpat[0], end - str));
1619d2d52addSAlexander Pyhalov 	}
1620d2d52addSAlexander Pyhalov 	for (; ; ) {
1621d2d52addSAlexander Pyhalov 		/* inner loop, should be most optimized */
1622d2d52addSAlexander Pyhalov 		while (k < end && (t = bmgtab[(unsigned char)*k]) != 0) {
1623d2d52addSAlexander Pyhalov 			k += t;
1624d2d52addSAlexander Pyhalov 		}
1625d2d52addSAlexander Pyhalov 		if (k >= end) {
1626d2d52addSAlexander Pyhalov 			return (NULL);
1627d2d52addSAlexander Pyhalov 		}
1628d2d52addSAlexander Pyhalov 		for (s = k, p = bmgpat + bmglen - 1; *--s == *--p; ) {
1629d2d52addSAlexander Pyhalov 			if (p == bmgpat) {
1630d2d52addSAlexander Pyhalov 				return (s);
1631d2d52addSAlexander Pyhalov 			}
1632d2d52addSAlexander Pyhalov 		}
1633d2d52addSAlexander Pyhalov 		k++;
1634d2d52addSAlexander Pyhalov 	}
1635d2d52addSAlexander Pyhalov 	/* NOTREACHED */
16367c478bd9Sstevel@tonic-gate }
1637