diff options
author | Mark Whitley <markw@lineo.com> | 2000-06-28 22:00:26 +0000 |
---|---|---|
committer | Mark Whitley <markw@lineo.com> | 2000-06-28 22:00:26 +0000 |
commit | d37218941c37795cc8e96ddb3312d83fb2269d5a (patch) | |
tree | f0b2aa0f49da8e0d4fb76a81a491e1d1ee5c4a05 /findutils/grep.c | |
parent | 268b8c4f387b9019bbb3591fb07403925d55d0c5 (diff) | |
download | busybox-w32-d37218941c37795cc8e96ddb3312d83fb2269d5a.tar.gz busybox-w32-d37218941c37795cc8e96ddb3312d83fb2269d5a.tar.bz2 busybox-w32-d37218941c37795cc8e96ddb3312d83fb2269d5a.zip |
Brand, spankin', new grep that uses libc regex routines instead of the
hand-rolled ones. Sed still needs to be replaced and then the regexp stuff can
be axed.
Diffstat (limited to 'findutils/grep.c')
-rw-r--r-- | findutils/grep.c | 255 |
1 files changed, 155 insertions, 100 deletions
diff --git a/findutils/grep.c b/findutils/grep.c index 0e495ff87..aca469e2f 100644 --- a/findutils/grep.c +++ b/findutils/grep.c | |||
@@ -1,10 +1,8 @@ | |||
1 | /* vi: set sw=4 ts=4: */ | ||
2 | /* | 1 | /* |
3 | * Mini grep implementation for busybox | 2 | * Mini grep implementation for busybox using libc regex. |
4 | * | ||
5 | * | 3 | * |
6 | * Copyright (C) 1999,2000 by Lineo, inc. | 4 | * Copyright (C) 1999,2000 by Lineo, inc. |
7 | * Written by Erik Andersen <andersen@lineo.com>, <andersee@debian.org> | 5 | * Written by Mark Whitley <markw@lineo.com>, <markw@enol.com> |
8 | * | 6 | * |
9 | * This program is free software; you can redistribute it and/or modify | 7 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License as published by | 8 | * it under the terms of the GNU General Public License as published by |
@@ -22,149 +20,206 @@ | |||
22 | * | 20 | * |
23 | */ | 21 | */ |
24 | 22 | ||
25 | /* | ||
26 | 18-Dec-1999 Konstantin Boldyshev <konst@voshod.com> | ||
27 | |||
28 | + -q option (be quiet) | ||
29 | + exit code depending on grep result (TRUE or FALSE) | ||
30 | (useful for scripts) | ||
31 | */ | ||
32 | |||
33 | #include "internal.h" | ||
34 | #include "regexp.h" | ||
35 | #include <stdio.h> | 23 | #include <stdio.h> |
36 | #include <dirent.h> | 24 | #include <stdlib.h> |
25 | #include <unistd.h> /* for getopt() */ | ||
26 | #include <regex.h> | ||
27 | #include <string.h> /* for strerror() */ | ||
37 | #include <errno.h> | 28 | #include <errno.h> |
38 | #include <fcntl.h> | 29 | #include "internal.h" |
39 | #include <signal.h> | 30 | |
40 | #include <time.h> | 31 | extern int optind; /* in unistd.h */ |
41 | #include <ctype.h> | 32 | extern int errno; /* for use with strerror() */ |
42 | #define BB_DECLARE_EXTERN | ||
43 | #define bb_need_too_few_args | ||
44 | #include "messages.c" | ||
45 | 33 | ||
46 | static const char grep_usage[] = | 34 | static const char grep_usage[] = |
47 | "grep [OPTIONS]... PATTERN [FILE]...\n" | 35 | "grep [-ihHnqvs] pattern [files...]\n" |
48 | #ifndef BB_FEATURE_TRIVIAL_HELP | 36 | #ifndef BB_FEATURE_TRIVIAL_HELP |
49 | "\nSearch for PATTERN in each FILE or standard input.\n\n" | 37 | "\nSearch for PATTERN in each FILE or standard input.\n\n" |
50 | "OPTIONS:\n" | 38 | "OPTIONS:\n" |
39 | "\t-H\tprefix output lines with filename where match was found\n" | ||
51 | "\t-h\tsuppress the prefixing filename on output\n" | 40 | "\t-h\tsuppress the prefixing filename on output\n" |
52 | "\t-i\tignore case distinctions\n" | 41 | "\t-i\tignore case distinctions\n" |
53 | "\t-n\tprint line number with output lines\n" | 42 | "\t-n\tprint line number with output lines\n" |
54 | "\t-q\tbe quiet. Returns 0 if result was found, 1 otherwise\n" | 43 | "\t-q\tbe quiet. Returns 0 if result was found, 1 otherwise\n" |
55 | "\t-v\tselect non-matching lines\n\n" | 44 | "\t-v\tselect non-matching lines\n" |
56 | #if defined BB_REGEXP | 45 | "\t-s\tsuppress file open/read error messages\n\n" |
57 | "This version of grep matches full regular expressions.\n"; | ||
58 | #else | ||
59 | "This version of grep matches strings (not regular expressions).\n" | ||
60 | #endif | ||
61 | #endif | 46 | #endif |
62 | ; | 47 | ; |
63 | 48 | ||
64 | static int match = FALSE, beQuiet = FALSE; | 49 | static const int GROWBY = 80; /* how large we will grow strings by */ |
50 | |||
51 | /* options */ | ||
52 | static int ignore_case = 0; | ||
53 | static int print_filename = 0; | ||
54 | static int print_line_num = 0; | ||
55 | static int be_quiet = 0; | ||
56 | static int invert_search = 0; | ||
57 | static int suppress_err_msgs = 0; | ||
58 | |||
59 | /* globals */ | ||
60 | static regex_t regex; /* storage space for compiled regular expression */ | ||
61 | static int nmatches = 0; /* keeps track of the number of matches */ | ||
62 | static char *cur_file = NULL; /* the current file we are reading */ | ||
63 | |||
65 | 64 | ||
66 | static void do_grep(FILE * fp, char *needle, char *fileName, int tellName, | 65 | /* This returns a malloc'ed char * which must be stored and free'ed */ |
67 | int ignoreCase, int tellLine, int invertSearch) | 66 | /* XXX: This function should probably go in a 'common'/'util'/'misc' file |
67 | * somewhere so it can be used by other folks. */ | ||
68 | static char *get_line_from_file(FILE *file) | ||
68 | { | 69 | { |
69 | long line = 0; | 70 | int ch; |
70 | char *haystack; | 71 | int idx = 0; |
71 | int truth = !invertSearch; | 72 | char *linebuf = NULL; |
73 | int linebufsz = 0; | ||
74 | |||
75 | while (1) { | ||
76 | ch = fgetc(file); | ||
77 | if (ch == EOF) | ||
78 | break; | ||
79 | /* grow the line buffer as necessary */ | ||
80 | if (idx > linebufsz-1) | ||
81 | linebuf = realloc(linebuf, linebufsz += GROWBY); | ||
82 | linebuf[idx++] = (char)ch; | ||
83 | if ((char)ch == '\n') | ||
84 | break; | ||
85 | } | ||
86 | |||
87 | if (idx == 0) | ||
88 | return NULL; | ||
89 | |||
90 | linebuf[idx] = 0; | ||
91 | return linebuf; | ||
92 | } | ||
93 | |||
94 | static void print_matched_line(char *line, int linenum) | ||
95 | { | ||
96 | if (print_filename) | ||
97 | printf("%s:", cur_file); | ||
98 | if (print_line_num) | ||
99 | printf("%i:", linenum); | ||
100 | |||
101 | printf("%s", line); | ||
102 | } | ||
72 | 103 | ||
73 | while ((haystack = cstring_lineFromFile(fp))) { | 104 | static void grep_file(FILE *file) |
74 | line++; | 105 | { |
75 | if (find_match(haystack, needle, ignoreCase) == truth) { | 106 | char *line = NULL; |
76 | if (tellName == TRUE) | 107 | int ret; |
77 | printf("%s:", fileName); | 108 | int linenum = 0; |
109 | |||
110 | while ((line = get_line_from_file(file)) != NULL) { | ||
111 | linenum++; | ||
112 | ret = regexec(®ex, line, 0, NULL, 0); | ||
113 | if (ret == 0 && !invert_search) { /* match */ | ||
114 | |||
115 | /* if we found a match but were told to be quiet, stop here and | ||
116 | * return success */ | ||
117 | if (be_quiet) { | ||
118 | regfree(®ex); | ||
119 | exit(0); | ||
120 | } | ||
78 | 121 | ||
79 | if (tellLine == TRUE) | 122 | nmatches++; |
80 | printf("%ld:", line); | ||
81 | 123 | ||
82 | if (beQuiet == FALSE) | 124 | print_matched_line(line, linenum); |
83 | fputs(haystack, stdout); | ||
84 | 125 | ||
85 | match = TRUE; | 126 | } else if (ret == REG_NOMATCH && invert_search) { |
127 | print_matched_line(line, linenum); | ||
86 | } | 128 | } |
87 | free(haystack); | 129 | |
130 | free(line); | ||
88 | } | 131 | } |
89 | } | 132 | } |
90 | 133 | ||
91 | |||
92 | extern int grep_main(int argc, char **argv) | 134 | extern int grep_main(int argc, char **argv) |
93 | { | 135 | { |
94 | FILE *fp; | 136 | int opt; |
95 | char *needle; | 137 | int reflags; |
96 | char *fileName; | 138 | int ret; |
97 | int tellName = TRUE; | 139 | |
98 | int ignoreCase = FALSE; | 140 | /* do special-case option parsing */ |
99 | int tellLine = FALSE; | 141 | if (argv[1] && (strcmp(argv[1], "--help") == 0)) |
100 | int invertSearch = FALSE; | ||
101 | |||
102 | if (argc < 1) { | ||
103 | usage(grep_usage); | 142 | usage(grep_usage); |
104 | } | ||
105 | argv++; | ||
106 | 143 | ||
107 | while (--argc >= 0 && *argv && (**argv == '-')) { | 144 | /* do normal option parsing */ |
108 | while (*++(*argv)) { | 145 | while ((opt = getopt(argc, argv, "iHhnqvs")) > 0) { |
109 | switch (**argv) { | 146 | switch (opt) { |
110 | case 'i': | 147 | case 'i': |
111 | ignoreCase = TRUE; | 148 | ignore_case++; |
149 | break; | ||
150 | case 'H': | ||
151 | print_filename++; | ||
112 | break; | 152 | break; |
113 | |||
114 | case 'h': | 153 | case 'h': |
115 | tellName = FALSE; | 154 | print_filename--; |
116 | break; | 155 | break; |
117 | |||
118 | case 'n': | 156 | case 'n': |
119 | tellLine = TRUE; | 157 | print_line_num++; |
120 | break; | 158 | break; |
121 | |||
122 | case 'q': | 159 | case 'q': |
123 | beQuiet = TRUE; | 160 | be_quiet++; |
124 | break; | 161 | break; |
125 | |||
126 | case 'v': | 162 | case 'v': |
127 | invertSearch = TRUE; | 163 | invert_search++; |
164 | break; | ||
165 | case 's': | ||
166 | suppress_err_msgs++; | ||
128 | break; | 167 | break; |
129 | |||
130 | default: | ||
131 | usage(grep_usage); | ||
132 | } | ||
133 | } | 168 | } |
134 | argv++; | ||
135 | } | 169 | } |
136 | 170 | ||
137 | if (argc == 0 || *argv == NULL) { | 171 | /* argv[optind] should be the regex pattern; no pattern, no worky */ |
138 | fatalError(too_few_args, "grep"); | 172 | if (argv[optind] == NULL) |
173 | usage(grep_usage); | ||
174 | |||
175 | /* compile the regular expression */ | ||
176 | reflags = REG_NOSUB; /* we're not going to mess with sub-expressions */ | ||
177 | if (ignore_case) | ||
178 | reflags |= REG_ICASE; | ||
179 | if ((ret = regcomp(®ex, argv[optind], reflags)) != 0) { | ||
180 | int errmsgsz = regerror(ret, ®ex, NULL, 0); | ||
181 | char *errmsg = malloc(errmsgsz); | ||
182 | if (errmsg == NULL) { | ||
183 | fprintf(stderr, "grep: memory error\n"); | ||
184 | regfree(®ex); | ||
185 | exit(1); | ||
186 | } | ||
187 | regerror(ret, ®ex, errmsg, errmsgsz); | ||
188 | fprintf(stderr, "grep: %s\n", errmsg); | ||
189 | free(errmsg); | ||
190 | regfree(®ex); | ||
191 | exit(1); | ||
139 | } | 192 | } |
140 | 193 | ||
141 | needle = *argv++; | 194 | /* argv[(optind+1)..(argc-1)] should be names of file to grep through. If |
142 | argc--; | 195 | * there is more than one file to grep, we will print the filenames */ |
196 | if ((argc-1) - (optind+1) > 0) | ||
197 | print_filename++; | ||
143 | 198 | ||
144 | if (argc == 0) { | 199 | /* If no files were specified, take input from stdin. Otherwise, we grep |
145 | do_grep(stdin, needle, "stdin", FALSE, ignoreCase, tellLine, invertSearch); | 200 | * through all the files specified. */ |
201 | if (argv[optind+1] == NULL) { | ||
202 | grep_file(stdin); | ||
146 | } else { | 203 | } else { |
147 | /* Never print the filename for just one file */ | 204 | int i; |
148 | if (argc == 1) | 205 | FILE *file; |
149 | tellName = FALSE; | 206 | for (i = optind + 1; i < argc; i++) { |
150 | while (argc-- > 0) { | 207 | cur_file = argv[i]; |
151 | fileName = *argv++; | 208 | file = fopen(cur_file, "r"); |
152 | 209 | if (file == NULL) { | |
153 | fp = fopen(fileName, "r"); | 210 | if (!suppress_err_msgs) |
154 | if (fp == NULL) { | 211 | fprintf(stderr, "grep: %s: %s\n", cur_file, strerror(errno)); |
155 | perror(fileName); | 212 | } else { |
156 | continue; | 213 | grep_file(file); |
214 | fclose(file); | ||
157 | } | 215 | } |
158 | |||
159 | do_grep(fp, needle, fileName, tellName, ignoreCase, tellLine, invertSearch); | ||
160 | |||
161 | if (ferror(fp)) | ||
162 | perror(fileName); | ||
163 | fclose(fp); | ||
164 | } | 216 | } |
165 | } | 217 | } |
166 | return(match); | ||
167 | } | ||
168 | 218 | ||
219 | regfree(®ex); | ||
169 | 220 | ||
170 | /* END CODE */ | 221 | if (nmatches == 0) |
222 | return 1; | ||
223 | |||
224 | return 0; | ||
225 | } | ||