Print this page
3047 grep support for -r would be useful

@@ -50,10 +50,13 @@
 #include <regexpr.h>
 #include <stdio.h>
 #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.",
         "Bad number.",
         "``\\digit'' out of range.",

@@ -71,10 +74,11 @@
 };
 
 #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;
 static char     *linebuf;
 static char     *prntbuf = NULL;

@@ -81,10 +85,12 @@
 static long     fw_lPrntBufLen = 0;
 static int      nflag;
 static int      bflag;
 static int      lflag;
 static int      cflag;
+static int      rflag;
+static int      Rflag;
 static int      vflag;
 static int      sflag;
 static int      iflag;
 static int      wflag;
 static int      hflag;

@@ -91,17 +97,20 @@
 static int      qflag;
 static int      errflg;
 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)
 {
         int     c;

@@ -112,11 +121,11 @@
 #if !defined(TEXT_DOMAIN)       /* Should be defined by cc -D */
 #define TEXT_DOMAIN "SYS_TEST"  /* Use this only if it weren't */
 #endif
         (void) textdomain(TEXT_DOMAIN);
 
-        while ((c = getopt(argc, argv, "hqblcnsviyw")) != -1)
+        while ((c = getopt(argc, argv, "hqblcnRrsviyw")) != -1)
                 switch (c) {
                 case 'h':
                         hflag++;
                         break;
                 case 'q':       /* POSIX: quiet: status only */

@@ -129,10 +138,16 @@
                         cflag++;
                         break;
                 case 'n':
                         nflag++;
                         break;
+                case 'R':
+                        Rflag++;
+                        /* FALLTHROUGH */
+                case 'r':
+                        rflag++;
+                        break;
                 case 'b':
                         bflag++;
                         break;
                 case 's':
                         sflag++;

@@ -150,11 +165,12 @@
                 case '?':
                         errflg++;
                 }
 
         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);
         }
 
         argv = &argv[optind];

@@ -188,21 +204,85 @@
         expbuf = compile(*argv, (char *)0, (char *)0);
         if (regerrno)
                 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;
         long    offset = 0;
         char    *next_ptr = NULL;
         long    next_count = 0;

@@ -219,11 +299,11 @@
                 }
         }
 
         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;
                 return;
         }

@@ -233,10 +313,11 @@
                 (void) close(temp);
 
                 if (cflag && !qflag) {
                         if (nfile > 1 && !hflag && file)
                                 (void) fprintf(stdout, "%s:", file);
+                        if (!rflag)
                         (void) fprintf(stdout, "%lld\n", tln);
                 }
                 return;
         }
 

@@ -327,18 +408,19 @@
                 offset = 0;
         }
         (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;
 
         if (f == NULL)

@@ -357,13 +439,14 @@
         if (lflag) {
                 (void) fprintf(stdout, "%s\n", f);
                 return (1);
         }
 
-        if (nfile > 1 && !hflag)
+        if (!hflag && (nfile > 1 || (rflag && outfn))) {
                 /* print filename */
                 (void) fprintf(stdout, "%s:", f);
+        }
 
         if (bflag)
                 /* print block number */
                 (void) fprintf(stdout, "%lld:", (offset_t)
                     ((lseek(temp, (off_t)0, SEEK_CUR) - 1) / BLKSIZE));