[hackers] [sbase][PATCH] grep: add -r

From: Mattias Andrée <maandree_AT_kth.se>
Date: Wed, 30 Mar 2016 05:23:08 +0200

Unlike your usual grep -r, this implementation
uses breadth-first search. It usually finds
makes it find what you are looking for faster.

Signed-off-by: Mattias Andrée <maandree_AT_kth.se>
---
 grep.1 |  10 ++++--
 grep.c | 121 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 121 insertions(+), 10 deletions(-)
diff --git a/grep.1 b/grep.1
index 6f80175..90bcc1f 100644
--- a/grep.1
+++ b/grep.1
_AT_@ -1,4 +1,4 @@
-.Dd 2015-10-08
+.Dd 2016-03-30
 .Dt GREP 1
 .Os sbase
 .Sh NAME
_AT_@ -6,7 +6,7 @@
 .Nd search files for patterns
 .Sh SYNOPSIS
 .Nm
-.Op Fl EFHchilnqsvx
+.Op Fl EFHchilnqrsvx
 .Op Fl e Ar pattern
 .Op Fl f Ar file
 .Op Ar pattern
_AT_@ -55,6 +55,10 @@ Print only the names of files with matching lines.
 Prefix each matching line with its line number in the input.
 .It Fl q
 Print nothing, only return status.
+.It Fl r
+Search directories recursively. If no
+.Ar file
+has been specified, the current working directory is searched.
 .It Fl s
 Suppress the error messages ordinarily written for nonexistent or unreadable
 files.
_AT_@ -89,5 +93,5 @@ utility is compliant with the
 specification.
 .Pp
 The
-.Op Fl Hhw
+.Op Fl Hhrw
 flags are an extension to that specification.
diff --git a/grep.c b/grep.c
index 64ffbe2..fadd661 100644
--- a/grep.c
+++ b/grep.c
_AT_@ -1,4 +1,8 @@
 /* See LICENSE file for copyright and license details. */
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <errno.h>
 #include <regex.h>
 #include <stdio.h>
 #include <stdlib.h>
_AT_@ -21,6 +25,7 @@ static int eflag;
 static int fflag;
 static int hflag;
 static int iflag;
+static int rflag;
 static int sflag;
 static int vflag;
 static int wflag;
_AT_@ -163,10 +168,100 @@ end:
 	return match;
 }
 
+static int
+isdir(const char *path)
+{
+	struct stat st;
+	if (stat(path, &st))
+		return 0;
+	return S_ISDIR(st.st_mode);
+}
+
+static int
+grepdir(char *path)
+{
+	const char *path_proper;
+	int m, match = NoMatch;
+	DIR *dir;
+	struct dirent *de;
+	FILE *fp;
+	char *filename;
+	size_t n;
+	struct dir { char *path; TAILQ_ENTRY(dir) entry; } *elem;
+	TAILQ_HEAD(queue, dir) queue = TAILQ_HEAD_INITIALIZER(queue);
+
+	/* FIXME is there a portable way to avoid loops? */
+
+	elem = emalloc(sizeof(*elem));
+	elem->path = estrdup(path);
+	TAILQ_INSERT_TAIL(&queue, elem, entry);
+
+next:
+	elem = TAILQ_FIRST(&queue);
+	path = elem->path;
+	TAILQ_REMOVE(&queue, elem, entry);
+	free(elem);
+
+	path_proper = *path ? path : ".";
+	if (!(dir = opendir(path_proper))) {
+		if (!sflag)
+			weprintf("opendir %s:", path_proper);
+		return Error;
+	}
+
+	path = erealloc(path, (n = strlen(path)) + 1 + sizeof(de->d_name));
+	filename = path + n;
+	if (n && path[n - 1] != '/')
+		*filename++ = '/';
+
+	while (errno = 0, (de = readdir(dir))) {
+		if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, ".."))
+			continue;
+
+		strcpy(filename, de->d_name);
+		if (isdir(path)) {
+			fp = 0;
+		} else if (!(fp = fopen(path, "r"))) {
+			if (!sflag)
+				weprintf("fopen %s:", path);
+			match = Error;
+			continue;
+		}
+
+		if (fp) {
+			m = grep(fp, path);
+		} else {
+			elem = emalloc(sizeof(*elem));
+			elem->path = estrdup(path);
+			TAILQ_INSERT_TAIL(&queue, elem, entry);
+			continue;
+		}
+
+		if (m == Error || (match != Error && m == Match))
+			match = m;
+		if (fp && fshut(fp, path))
+			match = Error;
+	}
+
+	free(path);
+
+	if (errno) {
+		if (!sflag)
+			weprintf("readdir %s:", path_proper);
+		return Error;
+	}
+	closedir(dir);
+
+	if (!TAILQ_EMPTY(&queue))
+		goto next;
+
+	return match;
+}
+
 static void
 usage(void)
 {
-	enprintf(Error, "usage: %s [-EFHchilnqsvwx] [-e pattern] [-f file] "
+	enprintf(Error, "usage: %s [-EFHchilnqrsvwx] [-e pattern] [-f file] "
 	         "[pattern] [file ...]\n", argv0);
 }
 
_AT_@ -226,6 +321,9 @@ main(int argc, char *argv[])
 		flags |= REG_ICASE;
 		iflag = 1;
 		break;
+	case 'r':
+		rflag = 1;
+		break;
 	case 's':
 		sflag = 1;
 		break;
_AT_@ -259,24 +357,33 @@ main(int argc, char *argv[])
 		/* Compile regex for all search patterns */
 		SLIST_FOREACH(pnode, &phead, entry)
 			enregcomp(Error, &pnode->preg, pnode->pattern, flags);
-	many = (argc > 1);
-	if (argc == 0) {
+	many = (argc > 1) || rflag;
+	if (argc == 0 && !rflag) {
 		match = grep(stdin, "<stdin>");
 	} else {
-		for (; *argv; argc--, argv++) {
+		if (!argc) {
+			*argv = "";
+			argc++;
+		}
+		for (; argc; argc--, argv++) {
 			if (!strcmp(*argv, "-")) {
 				*argv = "<stdin>";
 				fp = stdin;
+			} else if (rflag && (!**argv || isdir(*argv))) {
+				fp = 0;
 			} else if (!(fp = fopen(*argv, "r"))) {
 				if (!sflag)
-					weprintf("fopen %s:", *argv);
+					weprintf("fopen %s, %i:", *argv);
 				match = Error;
 				continue;
 			}
-			m = grep(fp, *argv);
+			if (fp)
+				m = grep(fp, *argv);
+			else
+				m = grepdir(*argv);
 			if (m == Error || (match != Error && m == Match))
 				match = m;
-			if (fp != stdin && fshut(fp, *argv))
+			if (fp && fp != stdin && fshut(fp, *argv))
 				match = Error;
 		}
 	}
-- 
2.7.4
Received on Wed Mar 30 2016 - 05:23:08 CEST

This archive was generated by hypermail 2.3.0 : Wed Mar 30 2016 - 05:24:15 CEST