17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
57c478bdstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only
67c478bdstevel@tonic-gate * (the "License").  You may not use this file except in compliance
77c478bdstevel@tonic-gate * with the License.
87c478bdstevel@tonic-gate *
97c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
107c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
117c478bdstevel@tonic-gate * See the License for the specific language governing permissions
127c478bdstevel@tonic-gate * and limitations under the License.
137c478bdstevel@tonic-gate *
147c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
157c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
167c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
177c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
187c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
197c478bdstevel@tonic-gate *
207c478bdstevel@tonic-gate * CDDL HEADER END
217c478bdstevel@tonic-gate */
227c478bdstevel@tonic-gate/*
23d2d52adAlexander Pyhalov * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
247c478bdstevel@tonic-gate * Use is subject to license terms.
257c478bdstevel@tonic-gate */
267c478bdstevel@tonic-gate
277c478bdstevel@tonic-gate/*
28d2d52adAlexander Pyhalov * grep - pattern matching program - combined grep, egrep, and fgrep.
29d2d52adAlexander Pyhalov *	Based on MKS grep command, with XCU & Solaris mods.
3041599e9Damian Bogel */
3141599e9Damian Bogel
3241599e9Damian Bogel/*
33d2d52adAlexander Pyhalov * Copyright 1985, 1992 by Mortice Kern Systems Inc.  All rights reserved.
347c478bdstevel@tonic-gate *
357c478bdstevel@tonic-gate */
367c478bdstevel@tonic-gate
37d2d52adAlexander Pyhalov/*
384adc6f1Peter Tribble * Copyright 2020 Peter Tribble.
39d9241f9Andrew Stormont * Copyright 2018 RackTop Systems.
40925beecYuri Pankov * Copyright 2018 Nexenta Systems, Inc.
41d2d52adAlexander Pyhalov * Copyright 2013 Damian Bogel. All rights reserved.
422e5ac46Robert Mustacchi * Copyright 2020 Oxide Computer Company
43d2d52adAlexander Pyhalov */
44d2d52adAlexander Pyhalov
45d2d52adAlexander Pyhalov#include <string.h>
46d2d52adAlexander Pyhalov#include <stdlib.h>
477c478bdstevel@tonic-gate#include <ctype.h>
48d2d52adAlexander Pyhalov#include <stdarg.h>
49d2d52adAlexander Pyhalov#include <regex.h>
50d2d52adAlexander Pyhalov#include <limits.h>
51d2d52adAlexander Pyhalov#include <sys/types.h>
52d2d52adAlexander Pyhalov#include <sys/stat.h>
537c478bdstevel@tonic-gate#include <fcntl.h>
547c478bdstevel@tonic-gate#include <stdio.h>
55d2d52adAlexander Pyhalov#include <locale.h>
56d2d52adAlexander Pyhalov#include <wchar.h>
57d2d52adAlexander Pyhalov#include <errno.h>
587c478bdstevel@tonic-gate#include <unistd.h>
59d2d52adAlexander Pyhalov#include <wctype.h>
60e52fb54Alexander Eremin#include <ftw.h>
61e52fb54Alexander Eremin#include <sys/param.h>
628ccd021Robert Mustacchi#include <getopt.h>
637c478bdstevel@tonic-gate
64d2d52adAlexander Pyhalov#define	STDIN_FILENAME gettext("(standard input)")
65d2d52adAlexander Pyhalov
66d2d52adAlexander Pyhalov#define	BSIZE		512		/* Size of block for -b */
67d2d52adAlexander Pyhalov#define	BUFSIZE		8192		/* Input buffer size */
68d2d52adAlexander Pyhalov#define	MAX_DEPTH	1000		/* how deep to recurse */
69d2d52adAlexander Pyhalov
704adc6f1Peter Tribble#define	AFTER	1			/* 'After' Context */
714adc6f1Peter Tribble#define	BEFORE	2			/* 'Before' Context */
72d2d52adAlexander Pyhalov#define	CONTEXT	(AFTER|BEFORE)		/* Full Context */
73d2d52adAlexander Pyhalov
74d2d52adAlexander Pyhalov#define	M_CSETSIZE	256		/* singlebyte chars */
75d2d52adAlexander Pyhalovstatic int	bmglen;			/* length of BMG pattern */
76d2d52adAlexander Pyhalovstatic char	*bmgpat;		/* BMG pattern */
77d2d52adAlexander Pyhalovstatic int	bmgtab[M_CSETSIZE];	/* BMG delta1 table */
78d2d52adAlexander Pyhalov
79d2d52adAlexander Pyhalovtypedef	struct	_PATTERN	{
80d2d52adAlexander Pyhalov	char	*pattern;		/* original pattern */
81d2d52adAlexander Pyhalov	struct	_PATTERN	*next;
82d2d52adAlexander Pyhalov	regex_t	re;			/* compiled pattern */
83d2d52adAlexander Pyhalov} PATTERN;
84d2d52adAlexander Pyhalov
85d2d52adAlexander Pyhalovstatic PATTERN	*patterns;
86d2d52adAlexander Pyhalovstatic char	errstr[128];		/* regerror string buffer */
87d2d52adAlexander Pyhalovstatic int	regflags = 0;		/* regcomp options */
88d2d52adAlexander Pyhalovstatic int	matched = 0;		/* return of the grep() */
89d2d52adAlexander Pyhalovstatic int	errors = 0;		/* count of errors */
90d2d52adAlexander Pyhalovstatic uchar_t	fgrep = 0;		/* Invoked as fgrep */
91d2d52adAlexander Pyhalovstatic uchar_t	egrep = 0;		/* Invoked as egrep */
92d2d52adAlexander Pyhalovstatic boolean_t	nvflag = B_TRUE;	/* Print matching lines */
93d2d52adAlexander Pyhalovstatic uchar_t	cflag;			/* Count of matches */
94d2d52adAlexander Pyhalovstatic uchar_t	iflag;			/* Case insensitve matching */
95d2d52adAlexander Pyhalovstatic uchar_t	Hflag;			/* Precede lines by file name */
964adc6f1Peter Tribblestatic uchar_t	hflag;			/* Suppress printing of filename */
97d2d52adAlexander Pyhalovstatic uchar_t	lflag;			/* Print file names of matches */
982e5ac46Robert Mustacchistatic uchar_t	Lflag;			/* Print file names of non-matches */
99d2d52adAlexander Pyhalovstatic uchar_t	nflag;			/* Precede lines by line number */
100d2d52adAlexander Pyhalovstatic uchar_t	rflag;			/* Search directories recursively */
1014adc6f1Peter Tribblestatic uchar_t	bflag;			/* Precede matches by block number */
102d2d52adAlexander Pyhalovstatic uchar_t	sflag;			/* Suppress file error messages */
103d2d52adAlexander Pyhalovstatic uchar_t	qflag;			/* Suppress standard output */
104d2d52adAlexander Pyhalovstatic uchar_t	wflag;			/* Search for expression as a word */
105d2d52adAlexander Pyhalovstatic uchar_t	xflag;			/* Anchoring */
106d2d52adAlexander Pyhalovstatic uchar_t	Eflag;			/* Egrep or -E flag */
107d2d52adAlexander Pyhalovstatic uchar_t	Fflag;			/* Fgrep or -F flag */
108d2d52adAlexander Pyhalovstatic uchar_t	Rflag;			/* Like rflag, but follow symlinks */
109d2d52adAlexander Pyhalovstatic uchar_t	outfn;			/* Put out file name */
110d2d52adAlexander Pyhalovstatic uchar_t	conflag;		/* show context of matches */
11181dd18dRobert Mustacchistatic uchar_t	oflag;			/* Print only matching output */
112d2d52adAlexander Pyhalovstatic char	*cmdname;
1138ccd021Robert Mustacchistatic char	*stdin_label;		/* Optional lable for stdin */
114d2d52adAlexander Pyhalov
11581dd18dRobert Mustacchistatic int	use_bmg, mblocale;
116d2d52adAlexander Pyhalov
11781dd18dRobert Mustacchistatic size_t	prntbuflen, conbuflen;
1184adc6f1Peter Tribblestatic unsigned long	conalen, conblen, conmatches;
119d2d52adAlexander Pyhalovstatic char	*prntbuf, *conbuf;
120d2d52adAlexander Pyhalov
121d2d52adAlexander Pyhalovstatic void	addfile(const char *fn);
122d2d52adAlexander Pyhalovstatic void	addpattern(char *s);
123d2d52adAlexander Pyhalovstatic void	fixpatterns(void);
124d2d52adAlexander Pyhalovstatic void	usage(void);
125d2d52adAlexander Pyhalovstatic int	grep(int, const char *);
126d2d52adAlexander Pyhalovstatic void	bmgcomp(char *, int);
127d2d52adAlexander Pyhalovstatic char	*bmgexec(char *, char *);
128e52fb54Alexander Ereminstatic int	recursive(const char *, const struct stat *, int, struct FTW *);
129d2d52adAlexander Pyhalovstatic void	process_path(const char *);
130d2d52adAlexander Pyhalovstatic void	process_file(const char *, int);
1317c478bdstevel@tonic-gate
132d2d52adAlexander Pyhalov/*
1338ccd021Robert Mustacchi * These are values that we use to return from getopt_long. They start at
1348ccd021Robert Mustacchi * SHRT_MAX to avoid any possible conflict with the normal options. These are
1358ccd021Robert Mustacchi * used for long options that have no short option equivalent.
1368ccd021Robert Mustacchi */
1378ccd021Robert Mustacchienum grep_opts {
1388ccd021Robert Mustacchi	OPT_LABEL = SHRT_MAX + 1
1398ccd021Robert Mustacchi};
1408ccd021Robert Mustacchi
1418ccd021Robert Mustacchistatic struct option grep_options[] = {
1428ccd021Robert Mustacchi	{ "label", required_argument, NULL, OPT_LABEL },
1438ccd021Robert Mustacchi	{ NULL }
1448ccd021Robert Mustacchi};
1458ccd021Robert Mustacchi
1468ccd021Robert Mustacchi/*
147d2d52adAlexander Pyhalov * mainline for grep
148d2d52adAlexander Pyhalov */
1497c478bdstevel@tonic-gateint
15055f9162ceasthamain(int argc, char **argv)
1517c478bdstevel@tonic-gate{
152d2d52adAlexander Pyhalov	char	*ap, *test;
1537c478bdstevel@tonic-gate	int	c;
154d2d52adAlexander Pyhalov	int	fflag = 0;
155d2d52adAlexander Pyhalov	int	i, n_pattern = 0, n_file = 0;
156d2d52adAlexander Pyhalov	char	**pattern_list = NULL;
157d2d52adAlexander Pyhalov	char	**file_list = NULL;
1587c478bdstevel@tonic-gate
1597c478bdstevel@tonic-gate	(void) setlocale(LC_ALL, "");
1607c478bdstevel@tonic-gate#if !defined(TEXT_DOMAIN)	/* Should be defined by cc -D */
161d2d52adAlexander Pyhalov#define	TEXT_DOMAIN	"SYS_TEST"	/* Use this only if it weren't */
1627c478bdstevel@tonic-gate#endif
1637c478bdstevel@tonic-gate	(void) textdomain(TEXT_DOMAIN);
1647c478bdstevel@tonic-gate
165d2d52adAlexander Pyhalov	/*
166d2d52adAlexander Pyhalov	 * true if this is running on the multibyte locale
167d2d52adAlexander Pyhalov	 */
168d2d52adAlexander Pyhalov	mblocale = (MB_CUR_MAX > 1);
169d2d52adAlexander Pyhalov	/*
170d2d52adAlexander Pyhalov	 * Skip leading slashes
171d2d52adAlexander Pyhalov	 */
172d2d52adAlexander Pyhalov	cmdname = argv[0];
173d2d52adAlexander Pyhalov	if (ap = strrchr(cmdname, '/'))
174d2d52adAlexander Pyhalov		cmdname = ap + 1;
175d2d52adAlexander Pyhalov
176d2d52adAlexander Pyhalov	ap = cmdname;
177d2d52adAlexander Pyhalov	/*
178d2d52adAlexander Pyhalov	 * Detect egrep/fgrep via command name, map to -E and -F options.
179d2d52adAlexander Pyhalov	 */
180d2d52adAlexander Pyhalov	if (*ap == 'e' || *ap == 'E') {
181d2d52adAlexander Pyhalov		regflags |= REG_EXTENDED;
182d2d52adAlexander Pyhalov		egrep++;
183d2d52adAlexander Pyhalov	} else {
184d2d52adAlexander Pyhalov		if (*ap == 'f' || *ap == 'F') {
185d2d52adAlexander Pyhalov			fgrep++;
18681dd18dRobert Mustacchi			regflags |= REG_NOSPEC;
187d2d52adAlexander Pyhalov		}
188d2d52adAlexander Pyhalov	}
189d2d52adAlexander Pyhalov
190d2d52adAlexander Pyhalov	/* check for non-standard "-line-count" option */
191d2d52adAlexander Pyhalov	for (i = 1; i < argc; i++) {
192d2d52adAlexander Pyhalov		if (strcmp(argv[i], "--") == 0)
19341599e9Damian Bogel			break;
194d2d52adAlexander Pyhalov
195d2d52adAlexander Pyhalov		/* isdigit() check prevents negative arguments */
196d2d52adAlexander Pyhalov		if ((argv[i][0] == '-') && isdigit(argv[i][1])) {
197d2d52adAlexander Pyhalov			if (strlen(&argv[i][1]) !=
198d2d52adAlexander Pyhalov			    strspn(&argv[i][1], "0123456789")) {
199d2d52adAlexander Pyhalov				(void) fprintf(stderr, gettext(
200d2d52adAlexander Pyhalov				    "%s: Bad number flag\n"), argv[0]);
201d2d52adAlexander Pyhalov				usage();
202d2d52adAlexander Pyhalov			}
203d2d52adAlexander Pyhalov
204d2d52adAlexander Pyhalov			errno = 0;
205d2d52adAlexander Pyhalov			conalen = conblen = strtoul(&argv[i][1], (char **)NULL,
206d2d52adAlexander Pyhalov			    10);
207d2d52adAlexander Pyhalov
208d2d52adAlexander Pyhalov			if (errno != 0 || conalen >= ULONG_MAX) {
209d2d52adAlexander Pyhalov				(void) fprintf(stderr, gettext(
210d2d52adAlexander Pyhalov				    "%s: Bad context argument\n"), argv[0]);
211d2d52adAlexander Pyhalov			} else if (conalen)
212d2d52adAlexander Pyhalov				conflag = CONTEXT;
213d2d52adAlexander Pyhalov
214d2d52adAlexander Pyhalov			while (i < argc) {
215d2d52adAlexander Pyhalov				argv[i] = argv[i + 1];
216d2d52adAlexander Pyhalov				i++;
217d2d52adAlexander Pyhalov			}
218d2d52adAlexander Pyhalov			argc--;
219d2d52adAlexander Pyhalov		}
220d2d52adAlexander Pyhalov	}
221d2d52adAlexander Pyhalov
22281dd18dRobert Mustacchi	while ((c = getopt_long(argc, argv, "+vwchHilLnrbse:f:qxEFIRA:B:C:o",
2238ccd021Robert Mustacchi	    grep_options, NULL)) != EOF) {
224d2d52adAlexander Pyhalov		unsigned long tval;
225d2d52adAlexander Pyhalov		switch (c) {
226d2d52adAlexander Pyhalov		case 'v':	/* POSIX: negate matches */
227d2d52adAlexander Pyhalov			nvflag = B_FALSE;
2287c478bdstevel@tonic-gate			break;
229d2d52adAlexander Pyhalov
230d2d52adAlexander Pyhalov		case 'c':	/* POSIX: write count */
231d2d52adAlexander Pyhalov			cflag++;
2323ed621bAlexander Eremin			break;
233d2d52adAlexander Pyhalov
234d2d52adAlexander Pyhalov		case 'i':	/* POSIX: ignore case */
235d2d52adAlexander Pyhalov			iflag++;
236d2d52adAlexander Pyhalov			regflags |= REG_ICASE;
2377c478bdstevel@tonic-gate			break;
238d2d52adAlexander Pyhalov
2392e5ac46Robert Mustacchi		/*
2402e5ac46Robert Mustacchi		 * The last of -l and -L are honored.
2412e5ac46Robert Mustacchi		 */
242d2d52adAlexander Pyhalov		case 'l':	/* POSIX: Write filenames only */
243d2d52adAlexander Pyhalov			lflag++;
2442e5ac46Robert Mustacchi			Lflag = 0;
2452e5ac46Robert Mustacchi			break;
2462e5ac46Robert Mustacchi
2472e5ac46Robert Mustacchi		case 'L':	/* Write non-matching filenames */
2482e5ac46Robert Mustacchi			Lflag++;
2492e5ac46Robert Mustacchi			lflag = 0;
2507c478bdstevel@tonic-gate			break;
251d2d52adAlexander Pyhalov
252d2d52adAlexander Pyhalov		case 'n':	/* POSIX: Write line numbers */
2537c478bdstevel@tonic-gate			nflag++;
2547c478bdstevel@tonic-gate			break;
255d2d52adAlexander Pyhalov
256d2d52adAlexander Pyhalov		case 'r':	/* Solaris: search recursively */
257e52fb54Alexander Eremin			rflag++;
258e52fb54Alexander Eremin			break;
259d2d52adAlexander Pyhalov
260d2d52adAlexander Pyhalov		case 'b':	/* Solaris: Write file block numbers */
2617c478bdstevel@tonic-gate			bflag++;
2627c478bdstevel@tonic-gate			break;
263d2d52adAlexander Pyhalov
264d2d52adAlexander Pyhalov		case 's':	/* POSIX: No error msgs for files */
2657c478bdstevel@tonic-gate			sflag++;
2667c478bdstevel@tonic-gate			break;
267d2d52adAlexander Pyhalov
268d2d52adAlexander Pyhalov		case 'e':	/* POSIX: pattern list */
269d2d52adAlexander Pyhalov			n_pattern++;
270d2d52adAlexander Pyhalov			pattern_list = realloc(pattern_list,
271d2d52adAlexander Pyhalov			    sizeof (char *) * n_pattern);
272d2d52adAlexander Pyhalov			if (pattern_list == NULL) {
273d2d52adAlexander Pyhalov				(void) fprintf(stderr,
274d2d52adAlexander Pyhalov				    gettext("%s: out of memory\n"),
275d2d52adAlexander Pyhalov				    cmdname);
276d2d52adAlexander Pyhalov				exit(2);
277d2d52adAlexander Pyhalov			}
278d2d52adAlexander Pyhalov			*(pattern_list + n_pattern - 1) = optarg;
2797c478bdstevel@tonic-gate			break;
280d2d52adAlexander Pyhalov
281d2d52adAlexander Pyhalov		case 'f':	/* POSIX: pattern file */
282d2d52adAlexander Pyhalov			fflag = 1;
283d2d52adAlexander Pyhalov			n_file++;
284d2d52adAlexander Pyhalov			file_list = realloc(file_list,
285d2d52adAlexander Pyhalov			    sizeof (char *) * n_file);
286d2d52adAlexander Pyhalov			if (file_list == NULL) {
287d2d52adAlexander Pyhalov				(void) fprintf(stderr,
288d2d52adAlexander Pyhalov				    gettext("%s: out of memory\n"),
289d2d52adAlexander Pyhalov				    cmdname);
290d2d52adAlexander Pyhalov				exit(2);
291d2d52adAlexander Pyhalov			}
292d2d52adAlexander Pyhalov			*(file_list + n_file - 1) = optarg;
2937c478bdstevel@tonic-gate			break;
294d2d52adAlexander Pyhalov
295d2d52adAlexander Pyhalov		/* based on options order h or H is set as in GNU grep */
2964adc6f1Peter Tribble		case 'h':	/* Solaris: suppress printing of file name */
297d2d52adAlexander Pyhalov			hflag = 1;
298d2d52adAlexander Pyhalov			Hflag = 0;
299d2d52adAlexander Pyhalov			break;
3004adc6f1Peter Tribble		/* Solaris: precede every match with file name */
301d2d52adAlexander Pyhalov		case 'H':
302d2d52adAlexander Pyhalov			Hflag = 1;
303d2d52adAlexander Pyhalov			hflag = 0;
304d2d52adAlexander Pyhalov			break;
305d2d52adAlexander Pyhalov
306d2d52adAlexander Pyhalov		case 'q':	/* POSIX: quiet: status only */
307d2d52adAlexander Pyhalov			qflag++;
308d2d52adAlexander Pyhalov			break;
309d2d52adAlexander Pyhalov
310d2d52adAlexander Pyhalov		case 'w':	/* Solaris: treat pattern as word */
3117c478bdstevel@tonic-gate			wflag++;
3127c478bdstevel@tonic-gate			break;
313d2d52adAlexander Pyhalov
314d2d52adAlexander Pyhalov		case 'x':	/* POSIX: full line matches */
315d2d52adAlexander Pyhalov			xflag++;
316d2d52adAlexander Pyhalov			break;
317d2d52adAlexander Pyhalov
318d2d52adAlexander Pyhalov		case 'E':	/* POSIX: Extended RE's */
319d2d52adAlexander Pyhalov			regflags |= REG_EXTENDED;
320d2d52adAlexander Pyhalov			Eflag++;
321d2d52adAlexander Pyhalov			break;
322d2d52adAlexander Pyhalov
323d2d52adAlexander Pyhalov		case 'F':	/* POSIX: strings, not RE's */
324d2d52adAlexander Pyhalov			Fflag++;
32581dd18dRobert Mustacchi			regflags |= REG_NOSPEC;
326d2d52adAlexander Pyhalov			break;
327d2d52adAlexander Pyhalov
328d2d52adAlexander Pyhalov		case 'R':	/* Solaris: like rflag, but follow symlinks */
329d2d52adAlexander Pyhalov			Rflag++;
330d2d52adAlexander Pyhalov			rflag++;
331d2d52adAlexander Pyhalov			break;
332d2d52adAlexander Pyhalov
333d2d52adAlexander Pyhalov		case 'A':	/* print N lines after each match */
334d2d52adAlexander Pyhalov			errno = 0;
335d2d52adAlexander Pyhalov			conalen = strtoul(optarg, &test, 10);
336d2d52adAlexander Pyhalov			/* *test will be non-null if optarg is negative */
337d2d52adAlexander Pyhalov			if (errno != 0 || *test != '\0' ||
338d2d52adAlexander Pyhalov			    conalen >= ULONG_MAX) {
339d2d52adAlexander Pyhalov				(void) fprintf(stderr, gettext(
340d2d52adAlexander Pyhalov				    "%s: Bad context argument: %s\n"),
341d2d52adAlexander Pyhalov				    argv[0], optarg);
342d2d52adAlexander Pyhalov				exit(2);
343d2d52adAlexander Pyhalov			}
344d2d52adAlexander Pyhalov			if (conalen)
345d2d52adAlexander Pyhalov				conflag |= AFTER;
346d2d52adAlexander Pyhalov			else
347d2d52adAlexander Pyhalov				conflag &= ~AFTER;
348d2d52adAlexander Pyhalov			break;
349d2d52adAlexander Pyhalov		case 'B':	/* print N lines before each match */
350d2d52adAlexander Pyhalov			errno = 0;
351d2d52adAlexander Pyhalov			conblen = strtoul(optarg, &test, 10);
352d2d52adAlexander Pyhalov			/* *test will be non-null if optarg is negative */
353d2d52adAlexander Pyhalov			if (errno != 0 || *test != '\0' ||
354d2d52adAlexander Pyhalov			    conblen >= ULONG_MAX) {
355d2d52adAlexander Pyhalov				(void) fprintf(stderr, gettext(
356d2d52adAlexander Pyhalov				    "%s: Bad context argument: %s\n"),
357d2d52adAlexander Pyhalov				    argv[0], optarg);
358d2d52adAlexander Pyhalov				exit(2);
359d2d52adAlexander Pyhalov			}
360d2d52adAlexander Pyhalov			if (conblen)
361d2d52adAlexander Pyhalov				conflag |= BEFORE;
362d2d52adAlexander Pyhalov			else
363d2d52adAlexander Pyhalov				conflag &= ~BEFORE;
364d2d52adAlexander Pyhalov			break;
365d2d52adAlexander Pyhalov		case 'C':	/* print N lines around each match */
366d2d52adAlexander Pyhalov			errno = 0;
367d2d52adAlexander Pyhalov			tval = strtoul(optarg, &test, 10);
368d2d52adAlexander Pyhalov			/* *test will be non-null if optarg is negative */
369d2d52adAlexander Pyhalov			if (errno != 0 || *test != '\0' || tval >= ULONG_MAX) {
370d2d52adAlexander Pyhalov				(void) fprintf(stderr, gettext(
371d2d52adAlexander Pyhalov				    "%s: Bad context argument: %s\n"),
372d2d52adAlexander Pyhalov				    argv[0], optarg);
373d2d52adAlexander Pyhalov				exit(2);
374d2d52adAlexander Pyhalov			}
375d2d52adAlexander Pyhalov			if (tval) {
376d2d52adAlexander Pyhalov				if ((conflag & BEFORE) == 0)
377d2d52adAlexander Pyhalov					conblen = tval;
378d2d52adAlexander Pyhalov				if ((conflag & AFTER) == 0)
379d2d52adAlexander Pyhalov					conalen = tval;
380d2d52adAlexander Pyhalov				conflag = CONTEXT;
381d2d52adAlexander Pyhalov			}
382d2d52adAlexander Pyhalov			break;
383d2d52adAlexander Pyhalov
3848ccd021Robert Mustacchi		case OPT_LABEL:
3858ccd021Robert Mustacchi			stdin_label = optarg;
3868ccd021Robert Mustacchi			break;
3878ccd021Robert Mustacchi
38881dd18dRobert Mustacchi		case 'o':
38981dd18dRobert Mustacchi			oflag++;
39081dd18dRobert Mustacchi			break;
39181dd18dRobert Mustacchi
392d2d52adAlexander Pyhalov		default:
393d2d52adAlexander Pyhalov			usage();
3947c478bdstevel@tonic-gate		}
395d2d52adAlexander Pyhalov	}
396d2d52adAlexander Pyhalov	/*
397d2d52adAlexander Pyhalov	 * If we're invoked as egrep or fgrep we need to do some checks
398d2d52adAlexander Pyhalov	 */
3997c478bdstevel@tonic-gate
400d2d52adAlexander Pyhalov	if (egrep || fgrep) {
401d2d52adAlexander Pyhalov		/*
402d2d52adAlexander Pyhalov		 * Use of -E or -F with egrep or fgrep is illegal
403d2d52adAlexander Pyhalov		 */
404d2d52adAlexander Pyhalov		if (Eflag || Fflag)
405d2d52adAlexander Pyhalov			usage();
406d2d52adAlexander Pyhalov		/*
407d2d52adAlexander Pyhalov		 * Don't allow use of wflag with egrep / fgrep
408d2d52adAlexander Pyhalov		 */
409d2d52adAlexander Pyhalov		if (wflag)
410d2d52adAlexander Pyhalov			usage();
411d2d52adAlexander Pyhalov		/*
412d2d52adAlexander Pyhalov		 * For Solaris the -s flag is equivalent to XCU -q
413d2d52adAlexander Pyhalov		 */
414d2d52adAlexander Pyhalov		if (sflag)
415d2d52adAlexander Pyhalov			qflag++;
416d2d52adAlexander Pyhalov		/*
417d2d52adAlexander Pyhalov		 * done with above checks - set the appropriate flags
418d2d52adAlexander Pyhalov		 */
419d2d52adAlexander Pyhalov		if (egrep)
420d2d52adAlexander Pyhalov			Eflag++;
421d2d52adAlexander Pyhalov		else			/* Else fgrep */
422d2d52adAlexander Pyhalov			Fflag++;
4237c478bdstevel@tonic-gate	}
4247c478bdstevel@tonic-gate
425d2d52adAlexander Pyhalov	if (wflag && (Eflag || Fflag)) {
426d2d52adAlexander Pyhalov		/*
427d2d52adAlexander Pyhalov		 * -w cannot be specified with grep -F
428d2d52adAlexander Pyhalov		 */
429d2d52adAlexander Pyhalov		usage();
430d2d52adAlexander Pyhalov	}
4317c478bdstevel@tonic-gate
432d2d52adAlexander Pyhalov	/*
433d2d52adAlexander Pyhalov	 * -E and -F flags are mutually exclusive - check for this
434d2d52adAlexander Pyhalov	 */
435d2d52adAlexander Pyhalov	if (Eflag && Fflag)
436d2d52adAlexander Pyhalov		usage();
4377c478bdstevel@tonic-gate
438d2d52adAlexander Pyhalov	/*
43981dd18dRobert Mustacchi	 * -l or -L overrides -H like in GNU grep. It also overrides -o.
440d2d52adAlexander Pyhalov	 */
44181dd18dRobert Mustacchi	if (lflag || Lflag) {
442d2d52adAlexander Pyhalov		Hflag = 0;
44381dd18dRobert Mustacchi		oflag = 0;
44481dd18dRobert Mustacchi	}
445d2d52adAlexander Pyhalov
446d2d52adAlexander Pyhalov	/*
447d2d52adAlexander Pyhalov	 * -c, -l and -q flags are mutually exclusive
448d2d52adAlexander Pyhalov	 * We have -c override -l like in Solaris.
449d2d52adAlexander Pyhalov	 * -q overrides -l & -c programmatically in grep() function.
45081dd18dRobert Mustacchi	 * -c overrides -o in GNU grep, we honor that.
451d2d52adAlexander Pyhalov	 */
45281dd18dRobert Mustacchi	if (cflag) {
453d2d52adAlexander Pyhalov		lflag = 0;
4542e5ac46Robert Mustacchi		Lflag = 0;
45581dd18dRobert Mustacchi		oflag = 0;
45681dd18dRobert Mustacchi	}
45781dd18dRobert Mustacchi
45881dd18dRobert Mustacchi	/*
45981dd18dRobert Mustacchi	 * If -o is set then we ignore all context related options, like other
46081dd18dRobert Mustacchi	 * greps.
46181dd18dRobert Mustacchi	 */
46281dd18dRobert Mustacchi	if (oflag) {
46381dd18dRobert Mustacchi		conflag = 0;
46481dd18dRobert Mustacchi	}
46581dd18dRobert Mustacchi
46681dd18dRobert Mustacchi	/*
46781dd18dRobert Mustacchi	 * These flags are a semantic mess with no clear answers as to their
46881dd18dRobert Mustacchi	 * behvaior. Based on some experimentation GNU grep will exit zero if a
46981dd18dRobert Mustacchi	 * non-match is present, but never print anything. BSD grep seems to
47081dd18dRobert Mustacchi	 * exit 1 and not print anything, even if there would have been a match.
47181dd18dRobert Mustacchi	 * Also, you probably don't want to ask about what happens with grep -x
47281dd18dRobert Mustacchi	 * -o -v, some implementations seem to just ignore -v.
47381dd18dRobert Mustacchi	 */
47481dd18dRobert Mustacchi	if (oflag && !nvflag) {
47581dd18dRobert Mustacchi		(void) fprintf(stderr, gettext("%s: the combination of -v and "
47681dd18dRobert Mustacchi		    "-o is not supported currently\n"), argv[0]);
47781dd18dRobert Mustacchi		exit(2);
4782e5ac46Robert Mustacchi	}
4797c478bdstevel@tonic-gate
480d2d52adAlexander Pyhalov	argv += optind - 1;
481d2d52adAlexander Pyhalov	argc -= optind - 1;
4827c478bdstevel@tonic-gate
483d2d52adAlexander Pyhalov	/*
484d2d52adAlexander Pyhalov	 * Now handling -e and -f option
485d2d52adAlexander Pyhalov	 */
486d2d52adAlexander Pyhalov	if (pattern_list) {
487d2d52adAlexander Pyhalov		for (i = 0; i < n_pattern; i++) {
488d2d52adAlexander Pyhalov			addpattern(pattern_list[i]);
489d2d52adAlexander Pyhalov		}
490d2d52adAlexander Pyhalov		free(pattern_list);
491d2d52adAlexander Pyhalov	}
492d2d52adAlexander Pyhalov	if (file_list) {
493d2d52adAlexander Pyhalov		for (i = 0; i < n_file; i++) {
494d2d52adAlexander Pyhalov			addfile(file_list[i]);
4957c478bdstevel@tonic-gate		}
496d2d52adAlexander Pyhalov		free(file_list);
497d2d52adAlexander Pyhalov	}
4987c478bdstevel@tonic-gate
499d2d52adAlexander Pyhalov	/*
500d2d52adAlexander Pyhalov	 * No -e or -f?  Make sure there is one more arg, use it as the pattern.
501d2d52adAlexander Pyhalov	 */
502d2d52adAlexander Pyhalov	if (patterns == NULL && !fflag) {
503d2d52adAlexander Pyhalov		if (argc < 2)
504d2d52adAlexander Pyhalov			usage();
505d2d52adAlexander Pyhalov		addpattern(argv[1]);
506d2d52adAlexander Pyhalov		argc--;
507d2d52adAlexander Pyhalov		argv++;
5087c478bdstevel@tonic-gate	}
5097c478bdstevel@tonic-gate
510d2d52adAlexander Pyhalov	/*
511d2d52adAlexander Pyhalov	 * Compile Patterns and also decide if BMG can be used
512d2d52adAlexander Pyhalov	 */
513d2d52adAlexander Pyhalov	fixpatterns();
514d2d52adAlexander Pyhalov
5158ccd021Robert Mustacchi	if (stdin_label == NULL) {
5168ccd021Robert Mustacchi		stdin_label = STDIN_FILENAME;
5178ccd021Robert Mustacchi	}
5188ccd021Robert Mustacchi
519d2d52adAlexander Pyhalov	/* Process all files: stdin, or rest of arg list */
520d2d52adAlexander Pyhalov	if (argc < 2) {
5218ccd021Robert Mustacchi		matched = grep(0, stdin_label);
522d2d52adAlexander Pyhalov	} else {
523d2d52adAlexander Pyhalov		if (Hflag || (argc > 2 && hflag == 0))
524d2d52adAlexander Pyhalov			outfn = 1;	/* Print filename on match line */
525d2d52adAlexander Pyhalov		for (argv++; *argv != NULL; argv++) {
526d2d52adAlexander Pyhalov			process_path(*argv);
527d2d52adAlexander Pyhalov		}
528d2d52adAlexander Pyhalov	}
529d2d52adAlexander Pyhalov	/*
530d2d52adAlexander Pyhalov	 * Return() here is used instead of exit
531d2d52adAlexander Pyhalov	 */
5327c478bdstevel@tonic-gate
533d2d52adAlexander Pyhalov	(void) fflush(stdout);
5347c478bdstevel@tonic-gate
535d2d52adAlexander Pyhalov	if (errors)
536d2d52adAlexander Pyhalov		return (2);
537d2d52adAlexander Pyhalov	return (matched ? 0 : 1);
5387c478bdstevel@tonic-gate}
5397c478bdstevel@tonic-gate
5407c478bdstevel@tonic-gatestatic void
541d2d52adAlexander Pyhalovprocess_path(const char *path)
542e52fb54Alexander Eremin{
543e52fb54Alexander Eremin	struct	stat st;
544e52fb54Alexander Eremin	int	walkflags = FTW_CHDIR;
545e52fb54Alexander Eremin	char	*buf = NULL;
546e52fb54Alexander Eremin
547e52fb54Alexander Eremin	if (rflag) {
548e52fb54Alexander Eremin		if (stat(path, &st) != -1 &&
549e52fb54Alexander Eremin		    (st.st_mode & S_IFMT) == S_IFDIR) {
5504adc6f1Peter Tribble			if (!hflag)
5514adc6f1Peter Tribble				outfn = 1; /* Print filename unless -h */
552e52fb54Alexander Eremin
553e52fb54Alexander Eremin			/*
554e52fb54Alexander Eremin			 * Add trailing slash if arg
555e52fb54Alexander Eremin			 * is directory, to resolve symlinks.
556e52fb54Alexander Eremin			 */
557e52fb54Alexander Eremin			if (path[strlen(path) - 1] != '/') {
558e52fb54Alexander Eremin				(void) asprintf(&buf, "%s/", path);
559e52fb54Alexander Eremin				if (buf != NULL)
560e52fb54Alexander Eremin					path = buf;
561e52fb54Alexander Eremin			}
562e52fb54Alexander Eremin
563e52fb54Alexander Eremin			/*
564e52fb54Alexander Eremin			 * Search through subdirs if path is directory.
565e52fb54Alexander Eremin			 * Don't follow symlinks if Rflag is not set.
566e52fb54Alexander Eremin			 */
567e52fb54Alexander Eremin			if (!Rflag)
568e52fb54Alexander Eremin				walkflags |= FTW_PHYS;
569e52fb54Alexander Eremin
570e52fb54Alexander Eremin			if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
571e52fb54Alexander Eremin				if (!sflag)
572d2d52adAlexander Pyhalov					(void) fprintf(stderr,
573d2d52adAlexander Pyhalov					    gettext("%s: can't open \"%s\"\n"),
574d2d52adAlexander Pyhalov					    cmdname, path);
575d2d52adAlexander Pyhalov				errors = 1;
576e52fb54Alexander Eremin			}
577e52fb54Alexander Eremin			return;
578e52fb54Alexander Eremin		}
579e52fb54Alexander Eremin	}
580d2d52adAlexander Pyhalov	process_file(path, 0);
581e52fb54Alexander Eremin}
582e52fb54Alexander Eremin
583d2d52adAlexander Pyhalov/*
584d2d52adAlexander Pyhalov * Read and process all files in directory recursively.
585d2d52adAlexander Pyhalov */
586e52fb54Alexander Ereminstatic int
587e52fb54Alexander Ereminrecursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
588e52fb54Alexander Eremin{
589e52fb54Alexander Eremin	/*
590d2d52adAlexander Pyhalov	 * Process files and follow symlinks if Rflag set.
591e52fb54Alexander Eremin	 */
592e52fb54Alexander Eremin	if (info != FTW_F) {
593d2d52adAlexander Pyhalov		/* Report broken symlinks and unreadable files */
594e52fb54Alexander Eremin		if (!sflag &&
595e52fb54Alexander Eremin		    (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
596d2d52adAlexander Pyhalov			(void) fprintf(stderr,
597d2d52adAlexander Pyhalov			    gettext("%s: can't open \"%s\"\n"), cmdname, name);
598e52fb54Alexander Eremin		}
599e52fb54Alexander Eremin		return (0);
600e52fb54Alexander Eremin	}
601e52fb54Alexander Eremin
602d2d52adAlexander Pyhalov
603d2d52adAlexander Pyhalov	/* Skip devices and pipes if Rflag is not set */
604e52fb54Alexander Eremin	if (!Rflag && !S_ISREG(statp->st_mode))
605e52fb54Alexander Eremin		return (0);
606d2d52adAlexander Pyhalov	/* Pass offset to relative name from FTW_CHDIR */
607d2d52adAlexander Pyhalov	process_file(name, ftw->base);
608e52fb54Alexander Eremin	return (0);
609e52fb54Alexander Eremin}
610e52fb54Alexander Eremin
611d2d52adAlexander Pyhalov/*
612d2d52adAlexander Pyhalov * Opens file and call grep function.
613d2d52adAlexander Pyhalov */
614e52fb54Alexander Ereminstatic void
615d2d52adAlexander Pyhalovprocess_file(const char *name, int base)
6167c478bdstevel@tonic-gate{
617d2d52adAlexander Pyhalov	int fd;
6187c478bdstevel@tonic-gate
619d2d52adAlexander Pyhalov	if ((fd = open(name + base, O_RDONLY)) == -1) {
620d2d52adAlexander Pyhalov		errors = 1;
621d2d52adAlexander Pyhalov		if (!sflag) /* Silent mode */
622d2d52adAlexander Pyhalov			(void) fprintf(stderr, gettext(
623d2d52adAlexander Pyhalov			    "%s: can't open \"%s\"\n"),
624d2d52adAlexander Pyhalov			    cmdname, name);
625d2d52adAlexander Pyhalov		return;
626d2d52adAlexander Pyhalov	}
627d2d52adAlexander Pyhalov	matched |= grep(fd, name);
628d2d52adAlexander Pyhalov	(void) close(fd);
6297c478bdstevel@tonic-gate
630d2d52adAlexander Pyhalov	if (ferror(stdout)) {
631d2d52adAlexander Pyhalov		(void) fprintf(stderr, gettext(
632d2d52adAlexander Pyhalov		    "%s: error writing to stdout\n"),
633d2d52adAlexander Pyhalov		    cmdname);
634d2d52adAlexander Pyhalov		(void) fflush(stdout);
635d2d52adAlexander Pyhalov		exit(2);
636d2d52adAlexander Pyhalov	}
637d2d52adAlexander Pyhalov
638d2d52adAlexander Pyhalov}
639d2d52adAlexander Pyhalov
640d2d52adAlexander Pyhalov/*
641d2d52adAlexander Pyhalov * Add a file of strings to the pattern list.
642d2d52adAlexander Pyhalov */
643d2d52adAlexander Pyhalovstatic void
644d2d52adAlexander Pyhalovaddfile(const char *fn)
645d2d52adAlexander Pyhalov{
646d2d52adAlexander Pyhalov	FILE	*fp;
647d2d52adAlexander Pyhalov	char	*inbuf;
648d2d52adAlexander Pyhalov	char	*bufp;
649d2d52adAlexander Pyhalov	size_t	bufsiz, buflen, bufused;
650d2d52adAlexander Pyhalov
651d2d52adAlexander Pyhalov	/*
652d2d52adAlexander Pyhalov	 * Open the pattern file
653d2d52adAlexander Pyhalov	 */
654d2d52adAlexander Pyhalov	if ((fp = fopen(fn, "r")) == NULL) {
655d2d52adAlexander Pyhalov		(void) fprintf(stderr, gettext("%s: can't open \"%s\"\n"),
656d2d52adAlexander Pyhalov		    cmdname, fn);
657d2d52adAlexander Pyhalov		exit(2);
658d2d52adAlexander Pyhalov	}
659d2d52adAlexander Pyhalov	bufsiz = BUFSIZE;
660d2d52adAlexander Pyhalov	if ((inbuf = malloc(bufsiz)) == NULL) {
661d2d52adAlexander Pyhalov		(void) fprintf(stderr,
662d2d52adAlexander Pyhalov		    gettext("%s: out of memory\n"), cmdname);
663d2d52adAlexander Pyhalov		exit(2);
664d2d52adAlexander Pyhalov	}
665d2d52adAlexander Pyhalov	bufp = inbuf;
666d2d52adAlexander Pyhalov	bufused = 0;
667d2d52adAlexander Pyhalov	/*
668d2d52adAlexander Pyhalov	 * Read in the file, reallocing as we need more memory
669d2d52adAlexander Pyhalov	 */
670d2d52adAlexander Pyhalov	while (fgets(bufp, bufsiz - bufused, fp) != NULL) {
671d2d52adAlexander Pyhalov		buflen = strlen(bufp);
672d2d52adAlexander Pyhalov		bufused += buflen;
673d2d52adAlexander Pyhalov		if (bufused + 1 == bufsiz && bufp[buflen - 1] != '\n') {
674d2d52adAlexander Pyhalov			/*
675d2d52adAlexander Pyhalov			 * if this line does not fit to the buffer,
676d2d52adAlexander Pyhalov			 * realloc larger buffer
677d2d52adAlexander Pyhalov			 */
678d2d52adAlexander Pyhalov			bufsiz += BUFSIZE;
679d2d52adAlexander Pyhalov			if ((inbuf = realloc(inbuf, bufsiz)) == NULL) {
680d2d52adAlexander Pyhalov				(void) fprintf(stderr,
681d2d52adAlexander Pyhalov				    gettext("%s: out of memory\n"),
682d2d52adAlexander Pyhalov				    cmdname);
683d2d52adAlexander Pyhalov				exit(2);
684d2d52adAlexander Pyhalov			}
685d2d52adAlexander Pyhalov			bufp = inbuf + bufused;
686d2d52adAlexander Pyhalov			continue;
6877c478bdstevel@tonic-gate		}
688d2d52adAlexander Pyhalov		if (bufp[buflen - 1] == '\n') {
689d2d52adAlexander Pyhalov			bufp[--buflen] = '\0';
6907c478bdstevel@tonic-gate		}
691d2d52adAlexander Pyhalov		addpattern(inbuf);
692d2d52adAlexander Pyhalov
693d2d52adAlexander Pyhalov		bufp = inbuf;
694d2d52adAlexander Pyhalov		bufused = 0;
6957c478bdstevel@tonic-gate	}
696d2d52adAlexander Pyhalov	free(inbuf);
697d2d52adAlexander Pyhalov	free(prntbuf);
698d2d52adAlexander Pyhalov	free(conbuf);
699d2d52adAlexander Pyhalov	(void) fclose(fp);
700d2d52adAlexander Pyhalov}
7017c478bdstevel@tonic-gate
702d2d52adAlexander Pyhalov/*
703d2d52adAlexander Pyhalov * Add a string to the pattern list.
704d2d52adAlexander Pyhalov */
705d2d52adAlexander Pyhalovstatic void
706d2d52adAlexander Pyhalovaddpattern(char *s)
707d2d52adAlexander Pyhalov{
708d2d52adAlexander Pyhalov	PATTERN	*pp;
709d2d52adAlexander Pyhalov	char	*wordbuf;
710d2d52adAlexander Pyhalov	char	*np;
711d2d52adAlexander Pyhalov
712d2d52adAlexander Pyhalov	for (; ; ) {
713d2d52adAlexander Pyhalov		np = strchr(s, '\n');
714d2d52adAlexander Pyhalov		if (np != NULL)
715d2d52adAlexander Pyhalov			*np = '\0';
716d2d52adAlexander Pyhalov		if ((pp = malloc(sizeof (PATTERN))) == NULL) {
717d2d52adAlexander Pyhalov			(void) fprintf(stderr, gettext(
718d2d52adAlexander Pyhalov			    "%s: out of memory\n"),
719d2d52adAlexander Pyhalov			    cmdname);
720d2d52adAlexander Pyhalov			exit(2);
721d2d52adAlexander Pyhalov		}
722d2d52adAlexander Pyhalov		if (wflag) {
723d2d52adAlexander Pyhalov			/*
724d2d52adAlexander Pyhalov			 * Solaris wflag support: Add '<' '>' to pattern to
725d2d52adAlexander Pyhalov			 * select it as a word. Doesn't make sense with -F
726d2d52adAlexander Pyhalov			 * but we're Libertarian.
727d2d52adAlexander Pyhalov			 */
728d2d52adAlexander Pyhalov			size_t	slen, wordlen;
729d2d52adAlexander Pyhalov
730d2d52adAlexander Pyhalov			slen = strlen(s);
731d2d52adAlexander Pyhalov			wordlen = slen + 5; /* '\\' '<' s '\\' '>' '\0' */
732d2d52adAlexander Pyhalov			if ((wordbuf = malloc(wordlen)) == NULL) {
733d2d52adAlexander Pyhalov				(void) fprintf(stderr,
734d2d52adAlexander Pyhalov				    gettext("%s: out of memory\n"),
735d2d52adAlexander Pyhalov				    cmdname);
736d2d52adAlexander Pyhalov				exit(2);
737d2d52adAlexander Pyhalov			}
738d2d52adAlexander Pyhalov			(void) strcpy(wordbuf, "\\<");
739d2d52adAlexander Pyhalov			(void) strcpy(wordbuf + 2, s);
740d2d52adAlexander Pyhalov			(void) strcpy(wordbuf + 2 + slen, "\\>");
741d2d52adAlexander Pyhalov		} else {
742d2d52adAlexander Pyhalov			if ((wordbuf = strdup(s)) == NULL) {
743d2d52adAlexander Pyhalov				(void) fprintf(stderr,
744d2d52adAlexander Pyhalov				    gettext("%s: out of memory\n"),
745d2d52adAlexander Pyhalov				    cmdname);
746d2d52adAlexander Pyhalov				exit(2);
747d2d52adAlexander Pyhalov			}
748d2d52adAlexander Pyhalov		}
749d2d52adAlexander Pyhalov		pp->pattern = wordbuf;
750d2d52adAlexander Pyhalov		pp->next = patterns;
751d2d52adAlexander Pyhalov		patterns = pp;
752d2d52adAlexander Pyhalov		if (np == NULL)
753d2d52adAlexander Pyhalov			break;
754d2d52adAlexander Pyhalov		s = np + 1;
7557c478bdstevel@tonic-gate	}
756d2d52adAlexander Pyhalov}
757d2d52adAlexander Pyhalov
758d2d52adAlexander Pyhalov/*
759dbe930bRobert Mustacchi * Check if a given grep pattern that is being used with egrep or grep can be
760dbe930bRobert Mustacchi * considered 'simple'. That is there are no characters that would be treated
761dbe930bRobert Mustacchi * differently from fgrep. In this particular case, we're a little bit
762dbe930bRobert Mustacchi * conservative and look for characters that are:
763dbe930bRobert Mustacchi *
764dbe930bRobert Mustacchi *  o 7-bit ASCII
765dbe930bRobert Mustacchi *  o Letters
766dbe930bRobert Mustacchi *  o Numbers
767dbe930bRobert Mustacchi *  o Meta-characters not used in BREs/EREs: !, @, #, /, -, _, <, >, =
768dbe930bRobert Mustacchi *
769dbe930bRobert Mustacchi * This can certianly be made more complex and less restrictive with additional
770dbe930bRobert Mustacchi * testing.
771dbe930bRobert Mustacchi */
772dbe930bRobert Mustacchistatic boolean_t
773dbe930bRobert Mustacchisimple_pattern(const char *str)
774dbe930bRobert Mustacchi{
775dbe930bRobert Mustacchi	for (; *str != '\0'; str++) {
776dbe930bRobert Mustacchi		if (!isascii(*str)) {
777dbe930bRobert Mustacchi			return (B_FALSE);
778dbe930bRobert Mustacchi		}
779dbe930bRobert Mustacchi
780dbe930bRobert Mustacchi		if (isalnum(*str)) {
781dbe930bRobert Mustacchi			continue;
782dbe930bRobert Mustacchi		}
783dbe930bRobert Mustacchi
784dbe930bRobert Mustacchi		switch (*str) {
785dbe930bRobert Mustacchi		case '!':
786dbe930bRobert Mustacchi		case '@':
787dbe930bRobert Mustacchi		case '#':
788dbe930bRobert Mustacchi		case '/':
789dbe930bRobert Mustacchi		case '-':
790dbe930bRobert Mustacchi		case '_':
791dbe930bRobert Mustacchi		case '<':
792dbe930bRobert Mustacchi		case '>':
793dbe930bRobert Mustacchi		case '=':
794dbe930bRobert Mustacchi			continue;
795dbe930bRobert Mustacchi		default:
796dbe930bRobert Mustacchi			return (B_FALSE);
797dbe930bRobert Mustacchi		}
798dbe930bRobert Mustacchi	}
799dbe930bRobert Mustacchi
800dbe930bRobert Mustacchi	return (B_TRUE);
801dbe930bRobert Mustacchi}
802dbe930bRobert Mustacchi
803dbe930bRobert Mustacchi/*
804d2d52adAlexander Pyhalov * Fix patterns.
805d2d52adAlexander Pyhalov * Must do after all arguments read, in case later -i option.
806d2d52adAlexander Pyhalov */
807d2d52adAlexander Pyhalovstatic void
808d2d52adAlexander Pyhalovfixpatterns(void)
809d2d52adAlexander Pyhalov{
810d2d52adAlexander Pyhalov	PATTERN	*pp;
81181dd18dRobert Mustacchi	int	rv, fix_pattern;
81281dd18dRobert Mustacchi
81381dd18dRobert Mustacchi	/*
81481dd18dRobert Mustacchi	 * Decide if we are able to run the Boyer-Moore-Gosper algorithm.
81581dd18dRobert Mustacchi	 * Use the Boyer-Moore-Gosper algorithm if:
816dbe930bRobert Mustacchi	 * - fgrep or non-BRE/ERE	(Fflag || simple_pattern())
81781dd18dRobert Mustacchi	 * - singlebyte locale		(!mblocale)
81881dd18dRobert Mustacchi	 * - no ignoring case		(!iflag)
81981dd18dRobert Mustacchi	 * - no printing line numbers	(!nflag)
82081dd18dRobert Mustacchi	 * - no negating the output	(nvflag)
82181dd18dRobert Mustacchi	 * - only one pattern		(patterns != NULL && patterns->next ==
82281dd18dRobert Mustacchi	 *				    NULL)
82381dd18dRobert Mustacchi	 * - non zero length pattern	(strlen(patterns->pattern) != 0)
82481dd18dRobert Mustacchi	 * - no context required	(conflag == 0)
82581dd18dRobert Mustacchi	 * - no exact matches		(!oflag)
826dbe930bRobert Mustacchi	 * - no word matches		(!wlag)
82781dd18dRobert Mustacchi	 */
828dbe930bRobert Mustacchi	use_bmg = !mblocale && !iflag && !nflag && nvflag && !oflag &&
829dbe930bRobert Mustacchi	    (patterns != NULL && patterns->next == NULL) && !wflag &&
830dbe930bRobert Mustacchi	    (strlen(patterns->pattern) != 0) && conflag == 0 &&
831dbe930bRobert Mustacchi	    (Fflag || simple_pattern(patterns->pattern));
83281dd18dRobert Mustacchi
83381dd18dRobert Mustacchi	if (use_bmg) {
83481dd18dRobert Mustacchi		return;
83581dd18dRobert Mustacchi	}
8367c478bdstevel@tonic-gate
837d2d52adAlexander Pyhalov	/*
838925beecYuri Pankov	 * Fix the specified pattern if -x is specified.
839d2d52adAlexander Pyhalov	 */
840d2d52adAlexander Pyhalov	fix_pattern = !Fflag && xflag;
841d2d52adAlexander Pyhalov
84281dd18dRobert Mustacchi	for (pp = patterns; pp != NULL; pp = pp->next) {
843d2d52adAlexander Pyhalov		if (fix_pattern) {
844d2d52adAlexander Pyhalov			char	*cp, *cq;
845d2d52adAlexander Pyhalov			size_t	plen, nplen;
8467c478bdstevel@tonic-gate
847d2d52adAlexander Pyhalov			plen = strlen(pp->pattern);
848d2d52adAlexander Pyhalov			/* '^' pattern '$' */
849d2d52adAlexander Pyhalov			nplen = 1 + plen + 1 + 1;
850d2d52adAlexander Pyhalov			if ((cp = malloc(nplen)) == NULL) {
851d2d52adAlexander Pyhalov				(void) fprintf(stderr,
852d2d52adAlexander Pyhalov				    gettext("%s: out of memory\n"),
853d2d52adAlexander Pyhalov				    cmdname);
854d2d52adAlexander Pyhalov				exit(2);
855d2d52adAlexander Pyhalov			}
856d2d52adAlexander Pyhalov			cq = cp;
857d2d52adAlexander Pyhalov			*cq++ = '^';
858d2d52adAlexander Pyhalov			cq = strcpy(cq, pp->pattern) + plen;
859d2d52adAlexander Pyhalov			*cq++ = '$';
860d2d52adAlexander Pyhalov			*cq = '\0';
861d2d52adAlexander Pyhalov			free(pp->pattern);
862