---
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