Mercurial > illumos > illumos-gate
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