grep: add support for -r

This commit is contained in:
Denis Vlasenko 2006-10-14 14:24:30 +00:00
parent c345d8e945
commit 3a6755f56f
5 changed files with 145 additions and 109 deletions

View File

@ -74,7 +74,7 @@ static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
tmp = fileName; tmp = fileName;
else else
tmp++; tmp++;
if (!(fnmatch(pattern, tmp, FNM_PERIOD) == 0)) if (fnmatch(pattern, tmp, FNM_PERIOD) != 0)
goto no_match; goto no_match;
} }
#ifdef CONFIG_FEATURE_FIND_TYPE #ifdef CONFIG_FEATURE_FIND_TYPE
@ -143,8 +143,8 @@ static int fileAction(const char *fileName, struct stat *statbuf, void* junk)
#else #else
puts(fileName); puts(fileName);
#endif #endif
no_match: no_match:
return (TRUE); return TRUE;
} }
#ifdef CONFIG_FEATURE_FIND_TYPE #ifdef CONFIG_FEATURE_FIND_TYPE
@ -153,27 +153,27 @@ static int find_type(char *type)
int mask = 0; int mask = 0;
switch (type[0]) { switch (type[0]) {
case 'b': case 'b':
mask = S_IFBLK; mask = S_IFBLK;
break; break;
case 'c': case 'c':
mask = S_IFCHR; mask = S_IFCHR;
break; break;
case 'd': case 'd':
mask = S_IFDIR; mask = S_IFDIR;
break; break;
case 'p': case 'p':
mask = S_IFIFO; mask = S_IFIFO;
break; break;
case 'f': case 'f':
mask = S_IFREG; mask = S_IFREG;
break; break;
case 'l': case 'l':
mask = S_IFLNK; mask = S_IFLNK;
break; break;
case 's': case 's':
mask = S_IFSOCK; mask = S_IFSOCK;
break; break;
} }
if (mask == 0 || type[1] != '\0') if (mask == 0 || type[1] != '\0')
@ -306,12 +306,12 @@ int find_main(int argc, char **argv)
} }
if (firstopt == 1) { if (firstopt == 1) {
if (! recursive_action(".", TRUE, dereference, FALSE, fileAction, if (!recursive_action(".", TRUE, dereference, FALSE, fileAction,
fileAction, NULL)) fileAction, NULL))
status = EXIT_FAILURE; status = EXIT_FAILURE;
} else { } else {
for (i = 1; i < firstopt; i++) { for (i = 1; i < firstopt; i++) {
if (! recursive_action(argv[i], TRUE, dereference, FALSE, fileAction, if (!recursive_action(argv[i], TRUE, dereference, FALSE, fileAction,
fileAction, NULL)) fileAction, NULL))
status = EXIT_FAILURE; status = EXIT_FAILURE;
} }

View File

@ -24,43 +24,41 @@
/* options */ /* options */
static unsigned opt; #define GREP_OPTS "lnqvscFiHhe:f:Lor"
#define GREP_OPTS "lnqvscFiHhe:f:Lo"
#define GREP_OPT_l (1<<0) #define GREP_OPT_l (1<<0)
#define PRINT_FILES_WITH_MATCHES (opt & GREP_OPT_l) #define PRINT_FILES_WITH_MATCHES (option_mask32 & GREP_OPT_l)
#define GREP_OPT_n (1<<1) #define GREP_OPT_n (1<<1)
#define PRINT_LINE_NUM (opt & GREP_OPT_n) #define PRINT_LINE_NUM (option_mask32 & GREP_OPT_n)
#define GREP_OPT_q (1<<2) #define GREP_OPT_q (1<<2)
#define BE_QUIET (opt & GREP_OPT_q) #define BE_QUIET (option_mask32 & GREP_OPT_q)
#define GREP_OPT_v (1<<3) #define GREP_OPT_v (1<<3)
typedef char invert_search_t;
static invert_search_t invert_search;
#define GREP_OPT_s (1<<4) #define GREP_OPT_s (1<<4)
#define SUPPRESS_ERR_MSGS (opt & GREP_OPT_s) #define SUPPRESS_ERR_MSGS (option_mask32 & GREP_OPT_s)
#define GREP_OPT_c (1<<5) #define GREP_OPT_c (1<<5)
#define PRINT_MATCH_COUNTS (opt & GREP_OPT_c) #define PRINT_MATCH_COUNTS (option_mask32 & GREP_OPT_c)
#define GREP_OPT_F (1<<6) #define GREP_OPT_F (1<<6)
#define FGREP_FLAG (opt & GREP_OPT_F) #define FGREP_FLAG (option_mask32 & GREP_OPT_F)
#define GREP_OPT_i (1<<7) #define GREP_OPT_i (1<<7)
#define GREP_OPT_H (1<<8) #define GREP_OPT_H (1<<8)
#define GREP_OPT_h (1<<9) #define GREP_OPT_h (1<<9)
#define GREP_OPT_e (1<<10) #define GREP_OPT_e (1<<10)
#define GREP_OPT_f (1<<11) #define GREP_OPT_f (1<<11)
#define GREP_OPT_L (1<<12) #define GREP_OPT_L (1<<12)
#define PRINT_FILES_WITHOUT_MATCHES (opt & GREP_OPT_L) #define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & GREP_OPT_L)
#define GREP_OPT_o (1<<13) #define GREP_OPT_o (1<<13)
#define GREP_OPT_r (1<<14)
#if ENABLE_FEATURE_GREP_CONTEXT #if ENABLE_FEATURE_GREP_CONTEXT
#define GREP_OPT_CONTEXT "A:B:C:" # define GREP_OPT_CONTEXT "A:B:C:"
#define GREP_OPT_A (1<<14) # define GREP_OPT_A (1<<15)
#define GREP_OPT_B (1<<15) # define GREP_OPT_B (1<<16)
#define GREP_OPT_C (1<<16) # define GREP_OPT_C (1<<17)
#define GREP_OPT_E (1<<17) # define GREP_OPT_E (1<<18)
#else #else
#define GREP_OPT_CONTEXT "" # define GREP_OPT_CONTEXT ""
#define GREP_OPT_A 0 # define GREP_OPT_A 0
#define GREP_OPT_B 0 # define GREP_OPT_B 0
#define GREP_OPT_C 0 # define GREP_OPT_C 0
#define GREP_OPT_E (1<<14) # define GREP_OPT_E (1<<15)
#endif #endif
#if ENABLE_FEATURE_GREP_EGREP_ALIAS #if ENABLE_FEATURE_GREP_EGREP_ALIAS
# define OPT_EGREP "E" # define OPT_EGREP "E"
@ -68,8 +66,12 @@ static invert_search_t invert_search;
# define OPT_EGREP "" # define OPT_EGREP ""
#endif #endif
typedef unsigned char byte_t;
static int reflags; static int reflags;
static int print_filename; static byte_t invert_search;
static byte_t print_filename;
static byte_t open_errors;
#if ENABLE_FEATURE_GREP_CONTEXT #if ENABLE_FEATURE_GREP_CONTEXT
static int lines_before; static int lines_before;
@ -80,7 +82,7 @@ static int last_line_printed;
/* globals used internally */ /* globals used internally */
static llist_t *pattern_head; /* growable list of patterns to match */ static llist_t *pattern_head; /* growable list of patterns to match */
static char *cur_file; /* the current file we are reading */ static const char *cur_file; /* the current file we are reading */
typedef struct GREP_LIST_DATA { typedef struct GREP_LIST_DATA {
char *pattern; char *pattern;
@ -100,12 +102,12 @@ static void print_line(const char *line, int linenum, char decoration)
} }
last_line_printed = linenum; last_line_printed = linenum;
#endif #endif
if (print_filename > 0) if (print_filename)
printf("%s%c", cur_file, decoration); printf("%s%c", cur_file, decoration);
if (PRINT_LINE_NUM) if (PRINT_LINE_NUM)
printf("%i%c", linenum, decoration); printf("%i%c", linenum, decoration);
/* Emulate weird GNU grep behavior with -ov */ /* Emulate weird GNU grep behavior with -ov */
if ((opt & (GREP_OPT_v+GREP_OPT_o)) != (GREP_OPT_v+GREP_OPT_o)) if ((option_mask32 & (GREP_OPT_v+GREP_OPT_o)) != (GREP_OPT_v+GREP_OPT_o))
puts(line); puts(line);
} }
@ -113,7 +115,7 @@ static void print_line(const char *line, int linenum, char decoration)
static int grep_file(FILE *file) static int grep_file(FILE *file)
{ {
char *line; char *line;
invert_search_t ret; byte_t ret;
int linenum = 0; int linenum = 0;
int nmatches = 0; int nmatches = 0;
regmatch_t regmatch; regmatch_t regmatch;
@ -199,7 +201,7 @@ static int grep_file(FILE *file)
/* make a note that we need to print 'after' lines */ /* make a note that we need to print 'after' lines */
print_n_lines_after = lines_after; print_n_lines_after = lines_after;
#endif #endif
if (opt & GREP_OPT_o) { if (option_mask32 & GREP_OPT_o) {
line[regmatch.rm_eo] = '\0'; line[regmatch.rm_eo] = '\0';
print_line(line + regmatch.rm_so, linenum, ':'); print_line(line + regmatch.rm_so, linenum, ':');
} else { } else {
@ -231,7 +233,7 @@ static int grep_file(FILE *file)
/* grep -c: print [filename:]count, even if count is zero */ /* grep -c: print [filename:]count, even if count is zero */
if (PRINT_MATCH_COUNTS) { if (PRINT_MATCH_COUNTS) {
if (print_filename > 0) if (print_filename)
printf("%s:", cur_file); printf("%s:", cur_file);
printf("%d\n", nmatches); printf("%d\n", nmatches);
} }
@ -288,48 +290,71 @@ static void load_regexes_from_file(llist_t *fopt)
} }
static int file_action_grep(const char *filename, struct stat *statbuf, void* matched)
{
FILE *file = fopen(filename, "r");
if (file == NULL) {
if (!SUPPRESS_ERR_MSGS)
bb_perror_msg("%s", cur_file);
open_errors = 1;
return 0;
}
cur_file = filename;
*(int*)matched += grep_file(file);
return 1;
}
static int grep_dir(const char *dir)
{
int matched = 0;
recursive_action(dir,
/* recurse= */ 1,
/* followLinks= */ 0,
/* depthFirst= */ 1,
/* fileAction= */ file_action_grep,
/* dirAction= */ NULL,
/* userData= */ &matched);
return matched;
}
int grep_main(int argc, char **argv) int grep_main(int argc, char **argv)
{ {
FILE *file; FILE *file;
int matched; int matched;
llist_t *fopt = NULL; llist_t *fopt = NULL;
int error_open_count = 0;
/* do normal option parsing */ /* do normal option parsing */
#if ENABLE_FEATURE_GREP_CONTEXT #if ENABLE_FEATURE_GREP_CONTEXT
char *junk;
char *slines_after; char *slines_after;
char *slines_before; char *slines_before;
char *Copt; char *Copt;
opt_complementary = "H-h:e::f::C-AB"; opt_complementary = "H-h:e::f::C-AB";
opt = getopt32(argc, argv, getopt32(argc, argv,
GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP, GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP,
&pattern_head, &fopt, &pattern_head, &fopt,
&slines_after, &slines_before, &Copt); &slines_after, &slines_before, &Copt);
if (opt & GREP_OPT_C) { if (option_mask32 & GREP_OPT_C) {
/* C option unseted A and B options, but next -A or -B /* C option unseted A and B options, but next -A or -B
may be ovewrite own option */ may be ovewrite own option */
if (!(opt & GREP_OPT_A)) /* not overwtited */ if (!(option_mask32 & GREP_OPT_A)) /* not overwtited */
slines_after = Copt; slines_after = Copt;
if (!(opt & GREP_OPT_B)) /* not overwtited */ if (!(option_mask32 & GREP_OPT_B)) /* not overwtited */
slines_before = Copt; slines_before = Copt;
opt |= GREP_OPT_A|GREP_OPT_B; /* set for parse now */ option_mask32 |= GREP_OPT_A|GREP_OPT_B; /* set for parse now */
} }
if (opt & GREP_OPT_A) { if (option_mask32 & GREP_OPT_A) {
lines_after = strtoul(slines_after, &junk, 10); lines_after = xatoi_u(slines_after);
if (*junk != '\0')
bb_error_msg_and_die(bb_msg_invalid_arg, slines_after, "-A");
} }
if (opt & GREP_OPT_B) { if (option_mask32 & GREP_OPT_B) {
lines_before = strtoul(slines_before, &junk, 10); lines_before = xatoi_u(slines_before);
if (*junk != '\0')
bb_error_msg_and_die(bb_msg_invalid_arg, slines_before, "-B");
} }
/* sanity checks after parse may be invalid numbers ;-) */ /* sanity checks after parse may be invalid numbers ;-) */
if (opt & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L)) { if (option_mask32 & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L)) {
opt &= ~GREP_OPT_n; option_mask32 &= ~GREP_OPT_n;
lines_before = 0; lines_before = 0;
lines_after = 0; lines_after = 0;
} else if (lines_before > 0) } else if (lines_before > 0)
@ -337,15 +362,15 @@ int grep_main(int argc, char **argv)
#else #else
/* with auto sanity checks */ /* with auto sanity checks */
opt_complementary = "H-h:e::f::c-n:q-n:l-n"; opt_complementary = "H-h:e::f::c-n:q-n:l-n";
opt = getopt32(argc, argv, GREP_OPTS OPT_EGREP, getopt32(argc, argv, GREP_OPTS OPT_EGREP,
&pattern_head, &fopt); &pattern_head, &fopt);
#endif #endif
invert_search = (opt & GREP_OPT_v) != 0; /* 0 | 1 */ invert_search = (option_mask32 & GREP_OPT_v) != 0; /* 0 | 1 */
if (opt & GREP_OPT_H) if (option_mask32 & GREP_OPT_H)
print_filename++; print_filename = 1;
if (opt & GREP_OPT_h) if (option_mask32 & GREP_OPT_h)
print_filename--; print_filename = 0;
if (pattern_head != NULL) { if (pattern_head != NULL) {
/* convert char *argv[] to grep_list_data_t */ /* convert char *argv[] to grep_list_data_t */
llist_t *cur; llist_t *cur;
@ -353,20 +378,20 @@ int grep_main(int argc, char **argv)
for (cur = pattern_head; cur; cur = cur->link) for (cur = pattern_head; cur; cur = cur->link)
cur->data = new_grep_list_data(cur->data, 0); cur->data = new_grep_list_data(cur->data, 0);
} }
if (opt & GREP_OPT_f) if (option_mask32 & GREP_OPT_f)
load_regexes_from_file(fopt); load_regexes_from_file(fopt);
if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f') if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
opt |= GREP_OPT_F; option_mask32 |= GREP_OPT_F;
if (!(opt & GREP_OPT_o)) if (!(option_mask32 & GREP_OPT_o))
reflags = REG_NOSUB; reflags = REG_NOSUB;
if (ENABLE_FEATURE_GREP_EGREP_ALIAS && if (ENABLE_FEATURE_GREP_EGREP_ALIAS &&
(applet_name[0] == 'e' || (opt & GREP_OPT_E))) (applet_name[0] == 'e' || (option_mask32 & GREP_OPT_E)))
reflags |= REG_EXTENDED; reflags |= REG_EXTENDED;
if (opt & GREP_OPT_i) if (option_mask32 & GREP_OPT_i)
reflags |= REG_ICASE; reflags |= REG_ICASE;
argv += optind; argv += optind;
@ -386,9 +411,9 @@ int grep_main(int argc, char **argv)
} }
/* argv[(optind)..(argc-1)] should be names of file to grep through. If /* argv[(optind)..(argc-1)] should be names of file to grep through. If
* there is more than one file to grep, we will print the filenames */ * there is more than one file to grep, we will print the filenames. */
if (argc > 1) { if (argc > 1) {
print_filename++; print_filename = 1;
/* If no files were specified, or '-' was specified, take input from /* If no files were specified, or '-' was specified, take input from
* stdin. Otherwise, we grep through all the files specified. */ * stdin. Otherwise, we grep through all the files specified. */
@ -398,25 +423,35 @@ int grep_main(int argc, char **argv)
matched = 0; matched = 0;
while (argc--) { while (argc--) {
cur_file = *argv++; cur_file = *argv++;
file = stdin;
if (!cur_file || (*cur_file == '-' && !cur_file[1])) { if (!cur_file || (*cur_file == '-' && !cur_file[1])) {
cur_file = "(standard input)"; cur_file = "(standard input)";
file = stdin;
} else { } else {
file = fopen(cur_file, "r"); if (option_mask32 & GREP_OPT_r) {
} struct stat st;
if (file == NULL) { if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
if (!SUPPRESS_ERR_MSGS) print_filename = 1;
bb_perror_msg("%s", cur_file); matched += grep_dir(cur_file);
error_open_count++; goto grep_done;
} else { }
matched += grep_file(file); }
if (matched < 0) { /* else: fopen(dir) will succeed, but reading won't */
/* we found a match but were told to be quiet, stop here and file = fopen(cur_file, "r");
* return success */ if (file == NULL) {
break; if (!SUPPRESS_ERR_MSGS)
bb_perror_msg("%s", cur_file);
open_errors = 1;
continue;
} }
fclose(file);
} }
matched += grep_file(file);
grep_done:
if (matched < 0) {
/* we found a match but were told to be quiet, stop here and
* return success */
break;
}
bb_fclose_nonstdin(file);
} }
/* destroy all the elments in the pattern list */ /* destroy all the elments in the pattern list */
@ -439,7 +474,7 @@ int grep_main(int argc, char **argv)
* if an input line is selected, even if an error was detected. */ * if an input line is selected, even if an error was detected. */
if (BE_QUIET && matched) if (BE_QUIET && matched)
return 0; return 0;
if (error_open_count) if (open_errors)
return 2; return 2;
return !matched; /* invert return value 0 = success, 1 = failed */ return !matched; /* invert return value 0 = success, 1 = failed */
} }

View File

@ -252,8 +252,8 @@ extern FILE *bb_wfopen(const char *path, const char *mode);
extern FILE *bb_wfopen_input(const char *filename); extern FILE *bb_wfopen_input(const char *filename);
extern FILE *xfopen(const char *path, const char *mode); extern FILE *xfopen(const char *path, const char *mode);
extern int bb_fclose_nonstdin(FILE *f); extern int bb_fclose_nonstdin(FILE *f);
extern void bb_fflush_stdout_and_exit(int retval) ATTRIBUTE_NORETURN; extern void bb_fflush_stdout_and_exit(int retval) ATTRIBUTE_NORETURN;
extern void xstat(char *filename, struct stat *buf); extern void xstat(char *filename, struct stat *buf);
extern int xsocket(int domain, int type, int protocol); extern int xsocket(int domain, int type, int protocol);

View File

@ -992,7 +992,7 @@ USE_FEATURE_DATE_ISOFMT( \
"\t-H login_host\tLog login_host into the utmp file as the hostname" "\t-H login_host\tLog login_host into the utmp file as the hostname"
#define grep_trivial_usage \ #define grep_trivial_usage \
"[-ihHnqvso" \ "[-rihHnqvso" \
USE_FEATURE_GREP_EGREP_ALIAS("E") \ USE_FEATURE_GREP_EGREP_ALIAS("E") \
USE_FEATURE_GREP_CONTEXT("ABC") \ USE_FEATURE_GREP_CONTEXT("ABC") \
"] PATTERN [FILEs...]" "] PATTERN [FILEs...]"
@ -1001,6 +1001,7 @@ USE_FEATURE_DATE_ISOFMT( \
"Options:\n" \ "Options:\n" \
"\t-H\tprefix output lines with filename where match was found\n" \ "\t-H\tprefix output lines with filename where match was found\n" \
"\t-h\tsuppress the prefixing filename on output\n" \ "\t-h\tsuppress the prefixing filename on output\n" \
"\t-r\trecurse subdirectories\n" \
"\t-i\tignore case distinctions\n" \ "\t-i\tignore case distinctions\n" \
"\t-l\tlist names of files that match\n" \ "\t-l\tlist names of files that match\n" \
"\t-L\tlist names of files that do not match\n" \ "\t-L\tlist names of files that do not match\n" \

View File

@ -46,14 +46,14 @@ int recursive_action(const char *fileName,
return FALSE; return FALSE;
} }
if (! followLinks && (S_ISLNK(statbuf.st_mode))) { if (!followLinks && (S_ISLNK(statbuf.st_mode))) {
if (fileAction == NULL) if (fileAction == NULL)
return TRUE; return TRUE;
else else
return fileAction(fileName, &statbuf, userData); return fileAction(fileName, &statbuf, userData);
} }
if (! recurse) { if (!recurse) {
if (S_ISDIR(statbuf.st_mode)) { if (S_ISDIR(statbuf.st_mode)) {
if (dirAction != NULL) if (dirAction != NULL)
return (dirAction(fileName, &statbuf, userData)); return (dirAction(fileName, &statbuf, userData));
@ -65,9 +65,9 @@ int recursive_action(const char *fileName,
if (S_ISDIR(statbuf.st_mode)) { if (S_ISDIR(statbuf.st_mode)) {
DIR *dir; DIR *dir;
if (dirAction != NULL && ! depthFirst) { if (dirAction != NULL && !depthFirst) {
status = dirAction(fileName, &statbuf, userData); status = dirAction(fileName, &statbuf, userData);
if (! status) { if (!status) {
bb_perror_msg("%s", fileName); bb_perror_msg("%s", fileName);
return FALSE; return FALSE;
} else if (status == SKIP) } else if (status == SKIP)
@ -84,7 +84,7 @@ int recursive_action(const char *fileName,
nextFile = concat_subpath_file(fileName, next->d_name); nextFile = concat_subpath_file(fileName, next->d_name);
if(nextFile == NULL) if(nextFile == NULL)
continue; continue;
if (! recursive_action(nextFile, TRUE, followLinks, depthFirst, if (!recursive_action(nextFile, TRUE, followLinks, depthFirst,
fileAction, dirAction, userData)) { fileAction, dirAction, userData)) {
status = FALSE; status = FALSE;
} }
@ -92,12 +92,12 @@ int recursive_action(const char *fileName,
} }
closedir(dir); closedir(dir);
if (dirAction != NULL && depthFirst) { if (dirAction != NULL && depthFirst) {
if (! dirAction(fileName, &statbuf, userData)) { if (!dirAction(fileName, &statbuf, userData)) {
bb_perror_msg("%s", fileName); bb_perror_msg("%s", fileName);
return FALSE; return FALSE;
} }
} }
if (! status) if (!status)
return FALSE; return FALSE;
} else { } else {
if (fileAction == NULL) if (fileAction == NULL)