changeset 13817:8e6d00a24b13

3047 grep support for -r would be useful Reviewed by: Simon Klinkert <klinkert@webgods.de> Reviewed by: Gordon Ross <gordon.w.ross@gmail.com> Reviewed by: Albert Lee <trisk@nexenta.com> Approved by: Richard Lowe <richlowe@richlowe.net>
author Alexander Eremin <a.eremin@nexenta.com>
date Mon, 17 Sep 2012 05:34:00 -0500
parents 38db9df20f58
children e9ad0a945d45
files usr/src/cmd/grep/grep.c usr/src/cmd/grep_xpg4/grep.c usr/src/man/man1/grep.1
diffstat 3 files changed, 275 insertions(+), 65 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/cmd/grep/grep.c	Sun Jul 22 16:03:46 2012 -0400
+++ b/usr/src/cmd/grep/grep.c	Mon Sep 17 05:34:00 2012 -0500
@@ -52,6 +52,9 @@
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
+#include <ftw.h>
+#include <limits.h>
+#include <sys/param.h>
 
 static const char *errstr[] = {
 	"Range endpoint too large.",
@@ -73,6 +76,7 @@
 #define	errmsg(msg, arg)	(void) fprintf(stderr, gettext(msg), arg)
 #define	BLKSIZE	512
 #define	GBUFSIZ	8192
+#define	MAX_DEPTH	1000
 
 static int	temp;
 static long long	lnum;
@@ -83,6 +87,8 @@
 static int	bflag;
 static int	lflag;
 static int	cflag;
+static int	rflag;
+static int	Rflag;
 static int	vflag;
 static int	sflag;
 static int	iflag;
@@ -93,13 +99,16 @@
 static int	nfile;
 static long long	tln;
 static int	nsucc;
+static int	outfn = 0;
 static int	nlflag;
 static char	*ptr, *ptrend;
 static char	*expbuf;
 
-static void	execute(char *);
+static void	execute(const char *, int);
 static void	regerr(int);
-static int	succeed(char *);
+static void	prepare(const char *);
+static int	recursive(const char *, const struct stat *, int, struct FTW *);
+static int	succeed(const char *);
 
 int
 main(int argc, char **argv)
@@ -114,7 +123,7 @@
 #endif
 	(void) textdomain(TEXT_DOMAIN);
 
-	while ((c = getopt(argc, argv, "hqblcnsviyw")) != -1)
+	while ((c = getopt(argc, argv, "hqblcnRrsviyw")) != -1)
 		switch (c) {
 		case 'h':
 			hflag++;
@@ -131,6 +140,12 @@
 		case 'n':
 			nflag++;
 			break;
+		case 'R':
+			Rflag++;
+			/* FALLTHROUGH */
+		case 'r':
+			rflag++;
+			break;
 		case 'b':
 			bflag++;
 			break;
@@ -152,7 +167,8 @@
 		}
 
 	if (errflg || (optind >= argc)) {
-		errmsg("Usage: grep [-c|-l|-q] -hbnsviw pattern file . . .\n",
+		errmsg("Usage: grep [-c|-l|-q] [-r|-R] -hbnsviw "
+		    "pattern file . . .\n",
 		    (char *)NULL);
 		exit(2);
 	}
@@ -190,16 +206,80 @@
 		regerr(regerrno);
 
 	if (--argc == 0)
-		execute(NULL);
+		execute(NULL, 0);
 	else
 		while (argc-- > 0)
-			execute(*++argv);
+			prepare(*++argv);
 
 	return (nsucc == 2 ? 2 : (nsucc == 0 ? 1 : 0));
 }
 
 static void
-execute(char *file)
+prepare(const char *path)
+{
+	struct	stat st;
+	int	walkflags = FTW_CHDIR;
+	char	*buf = NULL;
+
+	if (rflag) {
+		if (stat(path, &st) != -1 &&
+		    (st.st_mode & S_IFMT) == S_IFDIR) {
+			outfn = 1;
+
+			/*
+			 * Add trailing slash if arg
+			 * is directory, to resolve symlinks.
+			 */
+			if (path[strlen(path) - 1] != '/') {
+				(void) asprintf(&buf, "%s/", path);
+				if (buf != NULL)
+					path = buf;
+			}
+
+			/*
+			 * Search through subdirs if path is directory.
+			 * Don't follow symlinks if Rflag is not set.
+			 */
+			if (!Rflag)
+				walkflags |= FTW_PHYS;
+
+			if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
+				if (!sflag)
+					errmsg("grep: can't open %s\n", path);
+				nsucc = 2;
+			}
+			return;
+		}
+	}
+	execute(path, 0);
+}
+
+static int
+recursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
+{
+	/*
+	 * process files and follow symlinks if Rflag set.
+	 */
+	if (info != FTW_F) {
+		if (!sflag &&
+		    (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
+			/* report broken symlinks and unreadable files */
+			errmsg("grep: can't open %s\n", name);
+		}
+		return (0);
+	}
+
+	/* skip devices and pipes if Rflag is not set */
+	if (!Rflag && !S_ISREG(statp->st_mode))
+		return (0);
+
+	/* pass offset to relative name from FTW_CHDIR */
+	execute(name, ftw->base);
+	return (0);
+}
+
+static void
+execute(const char *file, int base)
 {
 	char	*lbuf, *p;
 	long	count;
@@ -221,7 +301,7 @@
 
 	if (file == NULL)
 		temp = 0;
-	else if ((temp = open(file, O_RDONLY)) == -1) {
+	else if ((temp = open(file + base, O_RDONLY)) == -1) {
 		if (!sflag)
 			errmsg("grep: can't open %s\n", file);
 		nsucc = 2;
@@ -235,6 +315,7 @@
 		if (cflag && !qflag) {
 			if (nfile > 1 && !hflag && file)
 				(void) fprintf(stdout, "%s:", file);
+			if (!rflag)
 			(void) fprintf(stdout, "%lld\n", tln);
 		}
 		return;
@@ -329,14 +410,15 @@
 	(void) close(temp);
 
 	if (cflag && !qflag) {
-		if (nfile > 1 && !hflag && file)
+		if (!hflag && file && (nfile > 1 ||
+		    (rflag && outfn)))
 			(void) fprintf(stdout, "%s:", file);
 		(void) fprintf(stdout, "%lld\n", tln);
 	}
 }
 
 static int
-succeed(char *f)
+succeed(const char *f)
 {
 	int nchars;
 	nsucc = (nsucc == 2) ? 2 : 1;
@@ -359,9 +441,10 @@
 		return (1);
 	}
 
-	if (nfile > 1 && !hflag)
+	if (!hflag && (nfile > 1 || (rflag && outfn))) {
 		/* print filename */
 		(void) fprintf(stdout, "%s:", f);
+	}
 
 	if (bflag)
 		/* print block number */
--- a/usr/src/cmd/grep_xpg4/grep.c	Sun Jul 22 16:03:46 2012 -0400
+++ b/usr/src/cmd/grep_xpg4/grep.c	Mon Sep 17 05:34:00 2012 -0500
@@ -24,8 +24,6 @@
  * Use is subject to license terms.
  */
 
-#pragma ident	"%Z%%M%	%I%	%E% SMI"
-
 /*
  * grep - pattern matching program - combined grep, egrep, and fgrep.
  *	Based on MKS grep command, with XCU & Solaris mods.
@@ -36,6 +34,8 @@
  *
  */
 
+/* Copyright 2012 Nexenta Systems, Inc.  All rights reserved. */
+
 #include <string.h>
 #include <stdlib.h>
 #include <ctype.h>
@@ -51,9 +51,12 @@
 #include <errno.h>
 #include <unistd.h>
 #include <wctype.h>
+#include <ftw.h>
+#include <sys/param.h>
 
 #define	BSIZE		512		/* Size of block for -b */
 #define	BUFSIZE		8192		/* Input buffer size */
+#define	MAX_DEPTH	1000		/* how deep to recurse */
 
 #define	M_CSETSIZE	256		/* singlebyte chars */
 static int	bmglen;			/* length of BMG pattern */
@@ -70,6 +73,8 @@
 static PATTERN	*patterns;
 static char	errstr[128];		/* regerror string buffer */
 static int	regflags = 0;		/* regcomp options */
+static int	matched = 0;		/* return of the grep() */
+static int	errors = 0;		/* count of errors */
 static uchar_t	fgrep = 0;		/* Invoked as fgrep */
 static uchar_t	egrep = 0;		/* Invoked as egrep */
 static uchar_t	nvflag = 1;		/* Print matching lines */
@@ -78,6 +83,7 @@
 static uchar_t	hflag;			/* Supress printing of filename */
 static uchar_t	lflag;			/* Print file names of matches */
 static uchar_t	nflag;			/* Precede lines by line number */
+static uchar_t	rflag;			/* Search directories recursively */
 static uchar_t	bflag;			/* Preccede matches by block number */
 static uchar_t	sflag;			/* Suppress file error messages */
 static uchar_t	qflag;			/* Suppress standard output */
@@ -85,6 +91,7 @@
 static uchar_t	xflag;			/* Anchoring */
 static uchar_t	Eflag;			/* Egrep or -E flag */
 static uchar_t	Fflag;			/* Fgrep or -F flag */
+static uchar_t	Rflag;			/* Like rflag, but follow symlinks */
 static uchar_t	outfn;			/* Put out file name */
 static char	*cmdname;
 
@@ -94,13 +101,16 @@
 static char	*prntbuf;
 static wchar_t	*outline;
 
-static void	addfile(char *fn);
+static void	addfile(const char *fn);
 static void	addpattern(char *s);
 static void	fixpatterns(void);
 static void	usage(void);
-static int	grep(int, char *);
+static int	grep(int, const char *);
 static void	bmgcomp(char *, int);
 static char	*bmgexec(char *, char *);
+static int	recursive(const char *, const struct stat *, int, struct FTW *);
+static void	process_path(const char *);
+static void	process_file(const char *, int);
 
 /*
  * mainline for grep
@@ -109,10 +119,8 @@
 main(int argc, char **argv)
 {
 	char	*ap;
-	int	matched = 0;
 	int	c;
 	int	fflag = 0;
-	int	errors = 0;
 	int	i, n_pattern = 0, n_file = 0;
 	char	**pattern_list = NULL;
 	char	**file_list = NULL;
@@ -147,7 +155,7 @@
 		}
 	}
 
-	while ((c = getopt(argc, argv, "vwchilnbse:f:qxEFI")) != EOF) {
+	while ((c = getopt(argc, argv, "vwchilnrbse:f:qxEFIR")) != EOF) {
 		switch (c) {
 		case 'v':	/* POSIX: negate matches */
 			nvflag = 0;
@@ -170,6 +178,10 @@
 			nflag++;
 			break;
 
+		case 'r':	/* Solaris: search recursively */
+			rflag++;
+			break;
+
 		case 'b':	/* Solaris: Write file block numbers */
 			bflag++;
 			break;
@@ -230,6 +242,11 @@
 			Fflag++;
 			break;
 
+		case 'R':	/* Solaris: like rflag, but follow symlinks */
+			Rflag++;
+			rflag++;
+			break;
+
 		default:
 			usage();
 		}
@@ -334,21 +351,7 @@
 		if (argc > 2 && hflag == 0)
 			outfn = 1;	/* Print filename on match line */
 		for (argv++; *argv != NULL; argv++) {
-			int	fd;
-
-			if ((fd = open(*argv, O_RDONLY)) == -1) {
-				errors = 1;
-				if (sflag)
-					continue;
-				(void) fprintf(stderr, gettext(
-				    "%s: can't open \"%s\"\n"),
-				    cmdname, *argv);
-				continue;
-			}
-			matched |= grep(fd, *argv);
-			(void) close(fd);
-			if (ferror(stdout))
-				break;
+			process_path(*argv);
 		}
 	}
 	/*
@@ -362,11 +365,110 @@
 	return (matched ? 0 : 1);
 }
 
+static void
+process_path(const char *path)
+{
+	struct	stat st;
+	int	walkflags = FTW_CHDIR;
+	char	*buf = NULL;
+
+	if (rflag) {
+		if (stat(path, &st) != -1 &&
+		    (st.st_mode & S_IFMT) == S_IFDIR) {
+			outfn = 1; /* Print filename */
+
+			/*
+			 * Add trailing slash if arg
+			 * is directory, to resolve symlinks.
+			 */
+			if (path[strlen(path) - 1] != '/') {
+				(void) asprintf(&buf, "%s/", path);
+				if (buf != NULL)
+					path = buf;
+			}
+
+			/*
+			 * Search through subdirs if path is directory.
+			 * Don't follow symlinks if Rflag is not set.
+			 */
+			if (!Rflag)
+				walkflags |= FTW_PHYS;
+
+			if (nftw(path, recursive, MAX_DEPTH, walkflags) != 0) {
+				if (!sflag)
+					(void) fprintf(stderr,
+					    gettext("%s: can't open \"%s\"\n"),
+					    cmdname, path);
+				errors = 1;
+			}
+			return;
+		}
+	}
+	process_file(path, 0);
+}
+
+/*
+ * Read and process all files in directory recursively.
+ */
+static int
+recursive(const char *name, const struct stat *statp, int info, struct FTW *ftw)
+{
+	/*
+	 * Process files and follow symlinks if Rflag set.
+	 */
+	if (info != FTW_F) {
+		/* Report broken symlinks and unreadable files */
+		if (!sflag &&
+		    (info == FTW_SLN || info == FTW_DNR || info == FTW_NS)) {
+			(void) fprintf(stderr,
+			    gettext("%s: can't open \"%s\"\n"), cmdname, name);
+		}
+		return (0);
+	}
+
+
+	/* Skip devices and pipes if Rflag is not set */
+	if (!Rflag && !S_ISREG(statp->st_mode))
+		return (0);
+	/* Pass offset to relative name from FTW_CHDIR */
+	process_file(name, ftw->base);
+	return (0);
+}
+
+/*
+ * Opens file and call grep function.
+ */
+static void
+process_file(const char *name, int base)
+{
+	int fd;
+
+	if ((fd = open(name + base, O_RDONLY)) == -1) {
+		errors = 1;
+		if (!sflag) /* Silent mode */
+			(void) fprintf(stderr, gettext(
+			    "%s: can't open \"%s\"\n"),
+			    cmdname, name);
+		return;
+	}
+	matched |= grep(fd, name);
+	(void) close(fd);
+
+	if (ferror(stdout)) {
+		(void) fprintf(stderr, gettext(
+		    "%s: error writing to stdout\n"),
+		    cmdname);
+		(void) fflush(stdout);
+		exit(2);
+	}
+
+}
+
 /*
  * Add a file of strings to the pattern list.
  */
 static void
-addfile(char *fn)
+addfile(const char *fn)
 {
 	FILE	*fp;
 	char	*inbuf;
@@ -528,7 +630,7 @@
 				size_t	n;
 				n = strlen(pp->pattern) + 1;
 				if ((pp->wpattern =
-					malloc(sizeof (wchar_t) * n)) == NULL) {
+				    malloc(sizeof (wchar_t) * n)) == NULL) {
 					(void) fprintf(stderr,
 					    gettext("%s: out of memory\n"),
 					    cmdname);
@@ -538,7 +640,7 @@
 				    (size_t)-1) {
 					(void) fprintf(stderr,
 					    gettext("%s: failed to convert "
-						"\"%s\" to wide-characters\n"),
+					    "\"%s\" to wide-characters\n"),
 					    cmdname, pp->pattern);
 					exit(2);
 				}
@@ -579,7 +681,7 @@
 			(void) regerror(rv, &pp->re, errstr, sizeof (errstr));
 			(void) fprintf(stderr,
 			    gettext("%s: RE error in %s: %s\n"),
-				cmdname, pp->pattern, errstr);
+			    cmdname, pp->pattern, errstr);
 			exit(2);
 		}
 		free(pp->pattern);
@@ -675,7 +777,7 @@
  * and check for a match on each line.
  */
 static int
-grep(int fd, char *fn)
+grep(int fd, const char *fn)
 {
 	PATTERN *pp;
 	off_t	data_len;	/* length of the data chunk */
@@ -740,11 +842,11 @@
 			if (count < 0) {
 				/* read error */
 				if (cflag) {
-					if (outfn) {
+					if (outfn && !rflag) {
 						(void) fprintf(stdout,
 						    "%s:", fn);
 					}
-					if (!qflag) {
+					if (!qflag && !rflag) {
 						(void) fprintf(stdout, "%lld\n",
 						    matches);
 					}
@@ -859,7 +961,7 @@
 			 * need to handle xflag if specified
 			 */
 			if (xflag && (line_len != bmglen ||
-				strcmp(bmgpat, ptr) != 0)) {
+			    strcmp(bmgpat, ptr) != 0)) {
 				/* didn't match */
 				pp = NULL;
 			} else {
@@ -915,7 +1017,7 @@
 				for (pp = patterns; pp; pp = pp->next) {
 					if (outline[0] == pp->wpattern[0] &&
 					    wcscmp(outline,
-						pp->wpattern) == 0) {
+					    pp->wpattern) == 0) {
 						/* matched */
 						break;
 					}
@@ -1056,43 +1158,46 @@
 	if (egrep || fgrep) {
 		(void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
 		(void) fprintf(stderr,
-		    gettext(" [-c|-l|-q] [-bhinsvx] "
-			"pattern_list [file ...]\n"));
+		    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvx] "
+		    "pattern_list [file ...]\n"));
 
 		(void) fprintf(stderr, "\t%s", cmdname);
 		(void) fprintf(stderr,
-		    gettext(" [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
-			"[-f pattern_file]... [file...]\n"));
+		    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvx] "
+		    "[-e pattern_list]... "
+		    "[-f pattern_file]... [file...]\n"));
 	} else {
 		(void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
 		(void) fprintf(stderr,
-		    gettext(" [-c|-l|-q] [-bhinsvwx] "
-			"pattern_list [file ...]\n"));
+		    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvwx] "
+		    "pattern_list [file ...]\n"));
 
 		(void) fprintf(stderr, "\t%s", cmdname);
 		(void) fprintf(stderr,
-		    gettext(" [-c|-l|-q] [-bhinsvwx] [-e pattern_list]... "
-			"[-f pattern_file]... [file...]\n"));
+		    gettext(" [-c|-l|-q] [-r|-R] [-bhinsvwx] "
+		    "[-e pattern_list]... "
+		    "[-f pattern_file]... [file...]\n"));
 
 		(void) fprintf(stderr, "\t%s", cmdname);
 		(void) fprintf(stderr,
-		    gettext(" -E [-c|-l|-q] [-bhinsvx] "
-			"pattern_list [file ...]\n"));
+		    gettext(" -E [-c|-l|-q] [-r|-R] [-bhinsvx] "
+		    "pattern_list [file ...]\n"));
 
 		(void) fprintf(stderr, "\t%s", cmdname);
 		(void) fprintf(stderr,
-		    gettext(" -E [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
-			"[-f pattern_file]... [file...]\n"));
+		    gettext(" -E [-c|-l|-q] [-r|-R] [-bhinsvx] "
+		    "[-e pattern_list]... "
+		    "[-f pattern_file]... [file...]\n"));
 
 		(void) fprintf(stderr, "\t%s", cmdname);
 		(void) fprintf(stderr,
-		    gettext(" -F [-c|-l|-q] [-bhinsvx] "
-			"pattern_list [file ...]\n"));
+		    gettext(" -F [-c|-l|-q] [-r|-R] [-bhinsvx] "
+		    "pattern_list [file ...]\n"));
 
 		(void) fprintf(stderr, "\t%s", cmdname);
 		(void) fprintf(stderr,
 		    gettext(" -F [-c|-l|-q] [-bhinsvx] [-e pattern_list]... "
-			"[-f pattern_file]... [file...]\n"));
+		    "[-f pattern_file]... [file...]\n"));
 	}
 	exit(2);
 	/* NOTREACHED */
--- a/usr/src/man/man1/grep.1	Sun Jul 22 16:03:46 2012 -0400
+++ b/usr/src/man/man1/grep.1	Mon Sep 17 05:34:00 2012 -0500
@@ -16,26 +16,28 @@
 .SH SYNOPSIS
 .LP
 .nf
-\fB/usr/bin/grep\fR [\fB-c\fR | \fB-l\fR | \fB-q\fR] [\fB-bhinsvw\fR] \fIlimited-regular-expression\fR
-     [\fIfilename\fR]...
+\fB/usr/bin/grep\fR [\fB-c\fR | \fB-l\fR |\fB-q\fR] [\fB-r\fR | \fB-R\fR] [\fB-bhinsvw\fR]
+    \fIlimited-regular-expression\fR [\fIfilename\fR]...
 .fi
 
 .LP
 .nf
-\fB/usr/xpg4/bin/grep\fR [\fB-E\fR | \fB-F\fR] [\fB-c\fR | \fB-l\fR | \fB-q\fR] [\fB-bhinsvwx\fR] \fB-e\fR \fIpattern_list\fR...
-     [\fB-f\fR \fIpattern_file\fR]... [\fIfile\fR]...
+\fB/usr/xpg4/bin/grep\fR [\fB-E\fR | \fB-F\fR] [\fB-c\fR | \fB-l\fR | \fB-q\fR] [\fB-r\fR | \fB-R\fR]
+    [\fB-bhinsvwx\fR] \fB-e\fR \fIpattern_list\fR... [\fB-f\fR \fIpattern_file\fR]...
+    [\fIfile\fR]...
 .fi
 
 .LP
 .nf
-\fB/usr/xpg4/bin/grep\fR [\fB-E\fR | \fB-F\fR] [\fB-c\fR | \fB-l\fR | \fB-q\fR] [\fB-bhinsvwx\fR]
-     [\fB-e\fR \fIpattern_list\fR]... \fB-f\fR \fIpattern_file\fR... [\fIfile\fR]...
+\fB/usr/xpg4/bin/grep\fR [\fB-E\fR | \fB-F\fR] [\fB-c\fR | \fB-l\fR | \fB-q\fR] [\fB-r\fR | \fB-R\fR]
+    [\fB-bhinsvwx\fR] [\fB-e\fR \fIpattern_list\fR]... \fB-f\fR \fIpattern_file\fR...
+    [\fIfile\fR]...
 .fi
 
 .LP
 .nf
-\fB/usr/xpg4/bin/grep\fR [\fB-E\fR | \fB-F\fR] [\fB-c\fR | \fB-l\fR | \fB-q\fR] [\fB-bhinsvwx\fR] \fIpattern\fR
-     [\fIfile\fR]...
+\fB/usr/xpg4/bin/grep\fR [\fB-E\fR | \fB-F\fR] [\fB-c\fR | \fB-l\fR | \fB-q\fR] [\fB-r\fR | \fB-R\fR]
+    [\fB-bhinsvwx\fR] \fIpattern\fR [\fIfile\fR]...
 .fi
 
 .SH DESCRIPTION
@@ -135,6 +137,26 @@
 .sp
 .ne 2
 .na
+\fB\fB-r\fR\fR
+.ad
+.RS 6n
+Read all files under each directory, recursively. Follow symbolic links on
+the command line, but skip symlinks that are encountered recursively. If file
+is a device, FIFO, or socket, skip it.
+.RE
+
+.sp
+.ne 2
+.na
+\fB\fB-R\fR\fR
+.ad
+.RS 6n
+Read all files under each directory, recursively, following all symbolic links.
+.RE
+
+.sp
+.ne 2
+.na
 \fB\fB-q\fR\fR
 .ad
 .RS 6n