--- 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.4Received 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