Print this page
3047 grep support for -r would be useful
@@ -22,12 +22,10 @@
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* 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.
*/
@@ -34,10 +32,12 @@
/*
* Copyright 1985, 1992 by Mortice Kern Systems Inc. All rights reserved.
*
*/
+/* Copyright 2012 Nexenta Systems, Inc. All rights reserved. */
+
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stdarg.h>
#include <regex.h>
@@ -49,13 +49,16 @@
#include <locale.h>
#include <wchar.h>
#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 */
static char *bmgpat; /* BMG pattern */
static int bmgtab[M_CSETSIZE]; /* BMG delta1 table */
@@ -68,53 +71,58 @@
} PATTERN;
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 */
static uchar_t cflag; /* Count of matches */
static uchar_t iflag; /* Case insensitve matching */
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 */
static uchar_t wflag; /* Search for expression as a word */
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;
static int use_wchar, use_bmg, mblocale;
static size_t outbuflen, prntbuflen;
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
*/
int
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;
(void) setlocale(LC_ALL, "");
@@ -145,11 +153,11 @@
if (*ap == 'f' || *ap == 'F') {
fgrep++;
}
}
- 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;
break;
@@ -168,10 +176,14 @@
case 'n': /* POSIX: Write line numbers */
nflag++;
break;
+ case 'r': /* Solaris: search recursively */
+ rflag++;
+ break;
+
case 'b': /* Solaris: Write file block numbers */
bflag++;
break;
case 's': /* POSIX: No error msgs for files */
@@ -228,10 +240,15 @@
case 'F': /* POSIX: strings, not RE's */
Fflag++;
break;
+ case 'R': /* Solaris: like rflag, but follow symlinks */
+ Rflag++;
+ rflag++;
+ break;
+
default:
usage();
}
}
/*
@@ -332,27 +349,13 @@
matched = grep(0, gettext("(standard input)"));
} else {
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;
+ process_path(*argv);
}
- matched |= grep(fd, *argv);
- (void) close(fd);
- if (ferror(stdout))
- break;
}
- }
/*
* Return() here is used instead of exit
*/
(void) fflush(stdout);
@@ -360,15 +363,114 @@
if (errors)
return (2);
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;
char *bufp;
size_t bufsiz, buflen, bufused;
@@ -673,11 +775,11 @@
* This is an order of magnitude faster.
* Otherwise we split the buffer into lines,
* 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 */
off_t line_len; /* length of the current line */
off_t line_offset; /* current line's offset from the beginning */
@@ -738,15 +840,15 @@
*/
count = read(fd, ptr + data_len, prntbuflen - data_len);
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);
}
}
return (0);
@@ -1054,41 +1156,44 @@
usage(void)
{
if (egrep || fgrep) {
(void) fprintf(stderr, gettext("Usage:\t%s"), cmdname);
(void) fprintf(stderr,
- gettext(" [-c|-l|-q] [-bhinsvx] "
+ 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]... "
+ 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] "
+ 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]... "
+ 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] "
+ 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]... "
+ 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] "
+ 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]... "