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]... "