7 |
* |
* |
8 |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
* Licensed under the GPL v2 or later, see the file LICENSE in this tarball. |
9 |
*/ |
*/ |
10 |
/* BB_AUDIT SUSv3 defects - unsupported option -x. */ |
/* BB_AUDIT SUSv3 defects - unsupported option -x "match whole line only". */ |
11 |
/* BB_AUDIT GNU defects - always acts as -a. */ |
/* BB_AUDIT GNU defects - always acts as -a. */ |
12 |
/* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */ |
/* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */ |
13 |
/* |
/* |
19 |
* (C) 2006 Jac Goudsmit added -o option |
* (C) 2006 Jac Goudsmit added -o option |
20 |
*/ |
*/ |
21 |
|
|
22 |
#include "busybox.h" |
#include "libbb.h" |
23 |
#include "xregex.h" |
#include "xregex.h" |
24 |
|
|
|
|
|
25 |
/* options */ |
/* options */ |
26 |
#define GREP_OPTS "lnqvscFiHhe:f:Lor" |
#define OPTSTR_GREP \ |
27 |
#define GREP_OPT_l (1<<0) |
"lnqvscFiHhe:f:Lorm:" \ |
28 |
#define PRINT_FILES_WITH_MATCHES (option_mask32 & GREP_OPT_l) |
USE_FEATURE_GREP_CONTEXT("A:B:C:") \ |
29 |
#define GREP_OPT_n (1<<1) |
USE_FEATURE_GREP_EGREP_ALIAS("E") \ |
30 |
#define PRINT_LINE_NUM (option_mask32 & GREP_OPT_n) |
USE_DESKTOP("w") \ |
31 |
#define GREP_OPT_q (1<<2) |
"aI" |
32 |
#define BE_QUIET (option_mask32 & GREP_OPT_q) |
/* ignored: -a "assume all files to be text" */ |
33 |
#define GREP_OPT_v (1<<3) |
/* ignored: -I "assume binary files have no matches" */ |
34 |
#define GREP_OPT_s (1<<4) |
|
35 |
#define SUPPRESS_ERR_MSGS (option_mask32 & GREP_OPT_s) |
enum { |
36 |
#define GREP_OPT_c (1<<5) |
OPTBIT_l, /* list matched file names only */ |
37 |
#define PRINT_MATCH_COUNTS (option_mask32 & GREP_OPT_c) |
OPTBIT_n, /* print line# */ |
38 |
#define GREP_OPT_F (1<<6) |
OPTBIT_q, /* quiet - exit(EXIT_SUCCESS) of first match */ |
39 |
#define FGREP_FLAG (option_mask32 & GREP_OPT_F) |
OPTBIT_v, /* invert the match, to select non-matching lines */ |
40 |
#define GREP_OPT_i (1<<7) |
OPTBIT_s, /* suppress errors about file open errors */ |
41 |
#define GREP_OPT_H (1<<8) |
OPTBIT_c, /* count matches per file (suppresses normal output) */ |
42 |
#define GREP_OPT_h (1<<9) |
OPTBIT_F, /* literal match */ |
43 |
#define GREP_OPT_e (1<<10) |
OPTBIT_i, /* case-insensitive */ |
44 |
#define GREP_OPT_f (1<<11) |
OPTBIT_H, /* force filename display */ |
45 |
#define GREP_OPT_L (1<<12) |
OPTBIT_h, /* inhibit filename display */ |
46 |
#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & GREP_OPT_L) |
OPTBIT_e, /* -e PATTERN */ |
47 |
#define GREP_OPT_o (1<<13) |
OPTBIT_f, /* -f FILE_WITH_PATTERNS */ |
48 |
#define GREP_OPT_r (1<<14) |
OPTBIT_L, /* list unmatched file names only */ |
49 |
#if ENABLE_FEATURE_GREP_CONTEXT |
OPTBIT_o, /* show only matching parts of lines */ |
50 |
# define GREP_OPT_CONTEXT "A:B:C:" |
OPTBIT_r, /* recurse dirs */ |
51 |
# define GREP_OPT_A (1<<15) |
OPTBIT_m, /* -m MAX_MATCHES */ |
52 |
# define GREP_OPT_B (1<<16) |
USE_FEATURE_GREP_CONTEXT( OPTBIT_A ,) /* -A NUM: after-match context */ |
53 |
# define GREP_OPT_C (1<<17) |
USE_FEATURE_GREP_CONTEXT( OPTBIT_B ,) /* -B NUM: before-match context */ |
54 |
# define GREP_OPT_E (1<<18) |
USE_FEATURE_GREP_CONTEXT( OPTBIT_C ,) /* -C NUM: -A and -B combined */ |
55 |
#else |
USE_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */ |
56 |
# define GREP_OPT_CONTEXT "" |
USE_DESKTOP( OPTBIT_w ,) /* whole word match */ |
57 |
# define GREP_OPT_A 0 |
OPT_l = 1 << OPTBIT_l, |
58 |
# define GREP_OPT_B 0 |
OPT_n = 1 << OPTBIT_n, |
59 |
# define GREP_OPT_C 0 |
OPT_q = 1 << OPTBIT_q, |
60 |
# define GREP_OPT_E (1<<15) |
OPT_v = 1 << OPTBIT_v, |
61 |
#endif |
OPT_s = 1 << OPTBIT_s, |
62 |
#if ENABLE_FEATURE_GREP_EGREP_ALIAS |
OPT_c = 1 << OPTBIT_c, |
63 |
# define OPT_EGREP "E" |
OPT_F = 1 << OPTBIT_F, |
64 |
#else |
OPT_i = 1 << OPTBIT_i, |
65 |
# define OPT_EGREP "" |
OPT_H = 1 << OPTBIT_H, |
66 |
#endif |
OPT_h = 1 << OPTBIT_h, |
67 |
|
OPT_e = 1 << OPTBIT_e, |
68 |
typedef unsigned char byte_t; |
OPT_f = 1 << OPTBIT_f, |
69 |
|
OPT_L = 1 << OPTBIT_L, |
70 |
static int reflags; |
OPT_o = 1 << OPTBIT_o, |
71 |
static byte_t invert_search; |
OPT_r = 1 << OPTBIT_r, |
72 |
static byte_t print_filename; |
OPT_m = 1 << OPTBIT_m, |
73 |
static byte_t open_errors; |
OPT_A = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0, |
74 |
|
OPT_B = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0, |
75 |
|
OPT_C = USE_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0, |
76 |
|
OPT_E = USE_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0, |
77 |
|
OPT_w = USE_DESKTOP( (1 << OPTBIT_w)) + 0, |
78 |
|
}; |
79 |
|
|
80 |
|
#define PRINT_FILES_WITH_MATCHES (option_mask32 & OPT_l) |
81 |
|
#define PRINT_LINE_NUM (option_mask32 & OPT_n) |
82 |
|
#define BE_QUIET (option_mask32 & OPT_q) |
83 |
|
#define SUPPRESS_ERR_MSGS (option_mask32 & OPT_s) |
84 |
|
#define PRINT_MATCH_COUNTS (option_mask32 & OPT_c) |
85 |
|
#define FGREP_FLAG (option_mask32 & OPT_F) |
86 |
|
#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L) |
87 |
|
|
88 |
|
struct globals { |
89 |
|
int max_matches; |
90 |
|
#if !ENABLE_EXTRA_COMPAT |
91 |
|
int reflags; |
92 |
|
#else |
93 |
|
RE_TRANSLATE_TYPE case_fold; /* RE_TRANSLATE_TYPE is [[un]signed] char* */ |
94 |
|
#endif |
95 |
|
smalluint invert_search; |
96 |
|
smalluint print_filename; |
97 |
|
smalluint open_errors; |
98 |
#if ENABLE_FEATURE_GREP_CONTEXT |
#if ENABLE_FEATURE_GREP_CONTEXT |
99 |
static int lines_before; |
smalluint did_print_line; |
100 |
static int lines_after; |
int lines_before; |
101 |
static char **before_buf; |
int lines_after; |
102 |
static int last_line_printed; |
char **before_buf; |
103 |
#endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
USE_EXTRA_COMPAT(size_t *before_buf_size;) |
104 |
|
int last_line_printed; |
105 |
|
#endif |
106 |
|
/* globals used internally */ |
107 |
|
llist_t *pattern_head; /* growable list of patterns to match */ |
108 |
|
const char *cur_file; /* the current file we are reading */ |
109 |
|
}; |
110 |
|
#define G (*(struct globals*)&bb_common_bufsiz1) |
111 |
|
#define INIT_G() do { \ |
112 |
|
struct G_sizecheck { \ |
113 |
|
char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \ |
114 |
|
}; \ |
115 |
|
} while (0) |
116 |
|
#define max_matches (G.max_matches ) |
117 |
|
#if !ENABLE_EXTRA_COMPAT |
118 |
|
#define reflags (G.reflags ) |
119 |
|
#else |
120 |
|
#define case_fold (G.case_fold ) |
121 |
|
/* http://www.delorie.com/gnu/docs/regex/regex_46.html */ |
122 |
|
#define reflags re_syntax_options |
123 |
|
#undef REG_NOSUB |
124 |
|
#undef REG_EXTENDED |
125 |
|
#undef REG_ICASE |
126 |
|
#define REG_NOSUB bug:is:here /* should not be used */ |
127 |
|
#define REG_EXTENDED RE_SYNTAX_EGREP |
128 |
|
#define REG_ICASE bug:is:here /* should not be used */ |
129 |
|
#endif |
130 |
|
#define invert_search (G.invert_search ) |
131 |
|
#define print_filename (G.print_filename ) |
132 |
|
#define open_errors (G.open_errors ) |
133 |
|
#define did_print_line (G.did_print_line ) |
134 |
|
#define lines_before (G.lines_before ) |
135 |
|
#define lines_after (G.lines_after ) |
136 |
|
#define before_buf (G.before_buf ) |
137 |
|
#define before_buf_size (G.before_buf_size ) |
138 |
|
#define last_line_printed (G.last_line_printed ) |
139 |
|
#define pattern_head (G.pattern_head ) |
140 |
|
#define cur_file (G.cur_file ) |
141 |
|
|
|
/* globals used internally */ |
|
|
static llist_t *pattern_head; /* growable list of patterns to match */ |
|
|
static const char *cur_file; /* the current file we are reading */ |
|
142 |
|
|
143 |
typedef struct GREP_LIST_DATA { |
typedef struct grep_list_data_t { |
144 |
char *pattern; |
char *pattern; |
145 |
regex_t preg; |
/* for GNU regex, matched_range must be persistent across grep_file() calls */ |
146 |
#define PATTERN_MEM_A 1 |
#if !ENABLE_EXTRA_COMPAT |
147 |
|
regex_t compiled_regex; |
148 |
|
regmatch_t matched_range; |
149 |
|
#else |
150 |
|
struct re_pattern_buffer compiled_regex; |
151 |
|
struct re_registers matched_range; |
152 |
|
#endif |
153 |
|
#define ALLOCATED 1 |
154 |
#define COMPILED 2 |
#define COMPILED 2 |
155 |
int flg_mem_alocated_compiled; |
int flg_mem_alocated_compiled; |
156 |
} grep_list_data_t; |
} grep_list_data_t; |
157 |
|
|
158 |
static void print_line(const char *line, int linenum, char decoration) |
#if !ENABLE_EXTRA_COMPAT |
159 |
|
#define print_line(line, line_len, linenum, decoration) \ |
160 |
|
print_line(line, linenum, decoration) |
161 |
|
#endif |
162 |
|
static void print_line(const char *line, size_t line_len, int linenum, char decoration) |
163 |
{ |
{ |
164 |
#if ENABLE_FEATURE_GREP_CONTEXT |
#if ENABLE_FEATURE_GREP_CONTEXT |
165 |
|
/* Happens when we go to next file, immediately hit match |
166 |
|
* and try to print prev context... from prev file! Don't do it */ |
167 |
|
if (linenum < 1) |
168 |
|
return; |
169 |
/* possibly print the little '--' separator */ |
/* possibly print the little '--' separator */ |
170 |
if ((lines_before || lines_after) && last_line_printed && |
if ((lines_before || lines_after) && did_print_line |
171 |
last_line_printed < linenum - 1) { |
&& last_line_printed != linenum - 1 |
172 |
|
) { |
173 |
puts("--"); |
puts("--"); |
174 |
} |
} |
175 |
|
/* guard against printing "--" before first line of first file */ |
176 |
|
did_print_line = 1; |
177 |
last_line_printed = linenum; |
last_line_printed = linenum; |
178 |
#endif |
#endif |
179 |
if (print_filename) |
if (print_filename) |
181 |
if (PRINT_LINE_NUM) |
if (PRINT_LINE_NUM) |
182 |
printf("%i%c", linenum, decoration); |
printf("%i%c", linenum, decoration); |
183 |
/* Emulate weird GNU grep behavior with -ov */ |
/* Emulate weird GNU grep behavior with -ov */ |
184 |
if ((option_mask32 & (GREP_OPT_v+GREP_OPT_o)) != (GREP_OPT_v+GREP_OPT_o)) |
if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) { |
185 |
|
#if !ENABLE_EXTRA_COMPAT |
186 |
puts(line); |
puts(line); |
187 |
|
#else |
188 |
|
fwrite(line, 1, line_len, stdout); |
189 |
|
putchar('\n'); |
190 |
|
#endif |
191 |
|
} |
192 |
} |
} |
193 |
|
|
194 |
|
#if ENABLE_EXTRA_COMPAT |
195 |
|
/* Unlike getline, this one removes trailing '\n' */ |
196 |
|
static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file) |
197 |
|
{ |
198 |
|
ssize_t res_sz; |
199 |
|
char *line; |
200 |
|
|
201 |
|
res_sz = getline(line_ptr, line_alloc_len, file); |
202 |
|
line = *line_ptr; |
203 |
|
|
204 |
|
if (res_sz > 0) { |
205 |
|
if (line[res_sz - 1] == '\n') |
206 |
|
line[--res_sz] = '\0'; |
207 |
|
} else { |
208 |
|
free(line); /* uclibc allocates a buffer even on EOF. WTF? */ |
209 |
|
} |
210 |
|
return res_sz; |
211 |
|
} |
212 |
|
#endif |
213 |
|
|
214 |
static int grep_file(FILE *file) |
static int grep_file(FILE *file) |
215 |
{ |
{ |
216 |
char *line; |
smalluint found; |
|
byte_t ret; |
|
217 |
int linenum = 0; |
int linenum = 0; |
218 |
int nmatches = 0; |
int nmatches = 0; |
219 |
regmatch_t regmatch; |
#if !ENABLE_EXTRA_COMPAT |
220 |
|
char *line; |
221 |
|
#else |
222 |
|
char *line = NULL; |
223 |
|
ssize_t line_len; |
224 |
|
size_t line_alloc_len; |
225 |
|
#define rm_so start[0] |
226 |
|
#define rm_eo end[0] |
227 |
|
#endif |
228 |
#if ENABLE_FEATURE_GREP_CONTEXT |
#if ENABLE_FEATURE_GREP_CONTEXT |
229 |
int print_n_lines_after = 0; |
int print_n_lines_after = 0; |
230 |
int curpos = 0; /* track where we are in the circular 'before' buffer */ |
int curpos = 0; /* track where we are in the circular 'before' buffer */ |
231 |
int idx = 0; /* used for iteration through the circular buffer */ |
int idx = 0; /* used for iteration through the circular buffer */ |
232 |
|
#else |
233 |
|
enum { print_n_lines_after = 0 }; |
234 |
#endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
#endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
235 |
|
|
236 |
while ((line = xmalloc_getline(file)) != NULL) { |
while ( |
237 |
|
#if !ENABLE_EXTRA_COMPAT |
238 |
|
(line = xmalloc_fgetline(file)) != NULL |
239 |
|
#else |
240 |
|
(line_len = bb_getline(&line, &line_alloc_len, file)) >= 0 |
241 |
|
#endif |
242 |
|
) { |
243 |
llist_t *pattern_ptr = pattern_head; |
llist_t *pattern_ptr = pattern_head; |
244 |
grep_list_data_t * gl; |
grep_list_data_t *gl = gl; /* for gcc */ |
245 |
|
|
246 |
linenum++; |
linenum++; |
247 |
ret = 0; |
found = 0; |
248 |
while (pattern_ptr) { |
while (pattern_ptr) { |
249 |
gl = (grep_list_data_t *)pattern_ptr->data; |
gl = (grep_list_data_t *)pattern_ptr->data; |
250 |
if (FGREP_FLAG) { |
if (FGREP_FLAG) { |
251 |
ret = strstr(line, gl->pattern) != NULL; |
found |= (strstr(line, gl->pattern) != NULL); |
252 |
} else { |
} else { |
|
/* |
|
|
* test for a postitive-assertion match (regexec returns success (0) |
|
|
* and the user did not specify invert search), or a negative-assertion |
|
|
* match (regexec returns failure (REG_NOMATCH) and the user specified |
|
|
* invert search) |
|
|
*/ |
|
253 |
if (!(gl->flg_mem_alocated_compiled & COMPILED)) { |
if (!(gl->flg_mem_alocated_compiled & COMPILED)) { |
254 |
gl->flg_mem_alocated_compiled |= COMPILED; |
gl->flg_mem_alocated_compiled |= COMPILED; |
255 |
xregcomp(&(gl->preg), gl->pattern, reflags); |
#if !ENABLE_EXTRA_COMPAT |
256 |
|
xregcomp(&gl->compiled_regex, gl->pattern, reflags); |
257 |
|
#else |
258 |
|
memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex)); |
259 |
|
gl->compiled_regex.translate = case_fold; /* for -i */ |
260 |
|
if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex)) |
261 |
|
bb_error_msg_and_die("bad regex '%s'", gl->pattern); |
262 |
|
#endif |
263 |
|
} |
264 |
|
#if !ENABLE_EXTRA_COMPAT |
265 |
|
gl->matched_range.rm_so = 0; |
266 |
|
gl->matched_range.rm_eo = 0; |
267 |
|
#endif |
268 |
|
if ( |
269 |
|
#if !ENABLE_EXTRA_COMPAT |
270 |
|
regexec(&gl->compiled_regex, line, 1, &gl->matched_range, 0) == 0 |
271 |
|
#else |
272 |
|
re_search(&gl->compiled_regex, line, line_len, |
273 |
|
/*start:*/ 0, /*range:*/ line_len, |
274 |
|
&gl->matched_range) >= 0 |
275 |
|
#endif |
276 |
|
) { |
277 |
|
if (!(option_mask32 & OPT_w)) |
278 |
|
found = 1; |
279 |
|
else { |
280 |
|
char c = ' '; |
281 |
|
if (gl->matched_range.rm_so) |
282 |
|
c = line[gl->matched_range.rm_so - 1]; |
283 |
|
if (!isalnum(c) && c != '_') { |
284 |
|
c = line[gl->matched_range.rm_eo]; |
285 |
|
if (!c || (!isalnum(c) && c != '_')) |
286 |
|
found = 1; |
287 |
|
} |
288 |
|
} |
289 |
} |
} |
|
regmatch.rm_so = 0; |
|
|
regmatch.rm_eo = 0; |
|
|
ret |= regexec(&(gl->preg), line, 1, ®match, 0) == 0; |
|
290 |
} |
} |
291 |
|
/* If it's non-inverted search, we can stop |
292 |
|
* at first match */ |
293 |
|
if (found && !invert_search) |
294 |
|
goto do_found; |
295 |
pattern_ptr = pattern_ptr->link; |
pattern_ptr = pattern_ptr->link; |
296 |
} /* while (pattern_ptr) */ |
} /* while (pattern_ptr) */ |
297 |
|
|
298 |
if (ret ^ invert_search) { |
if (found ^ invert_search) { |
299 |
|
do_found: |
300 |
if (PRINT_FILES_WITH_MATCHES || BE_QUIET) |
/* keep track of matches */ |
301 |
free(line); |
nmatches++; |
302 |
|
|
303 |
/* if we found a match but were told to be quiet, stop here */ |
/* quiet/print (non)matching file names only? */ |
304 |
if (BE_QUIET || PRINT_FILES_WITHOUT_MATCHES) |
if (option_mask32 & (OPT_q|OPT_l|OPT_L)) { |
305 |
return -1; |
free(line); /* we don't need line anymore */ |
306 |
|
if (BE_QUIET) { |
307 |
/* keep track of matches */ |
/* manpage says about -q: |
308 |
nmatches++; |
* "exit immediately with zero status |
309 |
|
* if any match is found, |
310 |
|
* even if errors were detected" */ |
311 |
|
exit(EXIT_SUCCESS); |
312 |
|
} |
313 |
/* if we're just printing filenames, we stop after the first match */ |
/* if we're just printing filenames, we stop after the first match */ |
314 |
if (PRINT_FILES_WITH_MATCHES) |
if (PRINT_FILES_WITH_MATCHES) { |
315 |
break; |
puts(cur_file); |
316 |
|
/* fall through to "return 1" */ |
317 |
|
} |
318 |
|
/* OPT_L aka PRINT_FILES_WITHOUT_MATCHES: return early */ |
319 |
|
return 1; /* one match */ |
320 |
|
} |
321 |
|
|
|
/* print the matched line */ |
|
|
if (PRINT_MATCH_COUNTS == 0) { |
|
322 |
#if ENABLE_FEATURE_GREP_CONTEXT |
#if ENABLE_FEATURE_GREP_CONTEXT |
323 |
int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1; |
/* Were we printing context and saw next (unwanted) match? */ |
324 |
|
if ((option_mask32 & OPT_m) && nmatches > max_matches) |
325 |
|
break; |
326 |
|
#endif |
327 |
|
|
328 |
/* if we were told to print 'before' lines and there is at least |
/* print the matched line */ |
329 |
* one line in the circular buffer, print them */ |
if (PRINT_MATCH_COUNTS == 0) { |
330 |
if (lines_before && before_buf[prevpos] != NULL) { |
#if ENABLE_FEATURE_GREP_CONTEXT |
331 |
int first_buf_entry_line_num = linenum - lines_before; |
int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1; |
|
|
|
|
/* advance to the first entry in the circular buffer, and |
|
|
* figure out the line number is of the first line in the |
|
|
* buffer */ |
|
|
idx = curpos; |
|
|
while (before_buf[idx] == NULL) { |
|
|
idx = (idx + 1) % lines_before; |
|
|
first_buf_entry_line_num++; |
|
|
} |
|
332 |
|
|
333 |
/* now print each line in the buffer, clearing them as we go */ |
/* if we were told to print 'before' lines and there is at least |
334 |
while (before_buf[idx] != NULL) { |
* one line in the circular buffer, print them */ |
335 |
print_line(before_buf[idx], first_buf_entry_line_num, '-'); |
if (lines_before && before_buf[prevpos] != NULL) { |
336 |
free(before_buf[idx]); |
int first_buf_entry_line_num = linenum - lines_before; |
337 |
before_buf[idx] = NULL; |
|
338 |
idx = (idx + 1) % lines_before; |
/* advance to the first entry in the circular buffer, and |
339 |
first_buf_entry_line_num++; |
* figure out the line number is of the first line in the |
340 |
} |
* buffer */ |
341 |
|
idx = curpos; |
342 |
|
while (before_buf[idx] == NULL) { |
343 |
|
idx = (idx + 1) % lines_before; |
344 |
|
first_buf_entry_line_num++; |
345 |
} |
} |
346 |
|
|
347 |
/* make a note that we need to print 'after' lines */ |
/* now print each line in the buffer, clearing them as we go */ |
348 |
print_n_lines_after = lines_after; |
while (before_buf[idx] != NULL) { |
349 |
#endif |
print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-'); |
350 |
if (option_mask32 & GREP_OPT_o) { |
free(before_buf[idx]); |
351 |
line[regmatch.rm_eo] = '\0'; |
before_buf[idx] = NULL; |
352 |
print_line(line + regmatch.rm_so, linenum, ':'); |
idx = (idx + 1) % lines_before; |
353 |
} else { |
first_buf_entry_line_num++; |
|
print_line(line, linenum, ':'); |
|
354 |
} |
} |
355 |
} |
} |
356 |
} |
|
357 |
#if ENABLE_FEATURE_GREP_CONTEXT |
/* make a note that we need to print 'after' lines */ |
358 |
else { /* no match */ |
print_n_lines_after = lines_after; |
359 |
/* Add the line to the circular 'before' buffer */ |
#endif |
360 |
if (lines_before) { |
if (option_mask32 & OPT_o) { |
361 |
free(before_buf[curpos]); |
if (FGREP_FLAG) { |
362 |
before_buf[curpos] = xstrdup(line); |
/* -Fo just prints the pattern |
363 |
curpos = (curpos + 1) % lines_before; |
* (unless -v: -Fov doesnt print anything at all) */ |
364 |
|
if (found) |
365 |
|
print_line(gl->pattern, strlen(gl->pattern), linenum, ':'); |
366 |
|
} else while (1) { |
367 |
|
char old = line[gl->matched_range.rm_eo]; |
368 |
|
line[gl->matched_range.rm_eo] = '\0'; |
369 |
|
print_line(line + gl->matched_range.rm_so, |
370 |
|
gl->matched_range.rm_eo - gl->matched_range.rm_so, |
371 |
|
linenum, ':'); |
372 |
|
line[gl->matched_range.rm_eo] = old; |
373 |
|
#if !ENABLE_EXTRA_COMPAT |
374 |
|
break; |
375 |
|
#else |
376 |
|
if (re_search(&gl->compiled_regex, line, line_len, |
377 |
|
gl->matched_range.rm_eo, line_len - gl->matched_range.rm_eo, |
378 |
|
&gl->matched_range) < 0) |
379 |
|
break; |
380 |
|
#endif |
381 |
|
} |
382 |
|
} else { |
383 |
|
print_line(line, line_len, linenum, ':'); |
384 |
} |
} |
385 |
} |
} |
386 |
|
} |
387 |
|
#if ENABLE_FEATURE_GREP_CONTEXT |
388 |
|
else { /* no match */ |
389 |
/* if we need to print some context lines after the last match, do so */ |
/* if we need to print some context lines after the last match, do so */ |
390 |
if (print_n_lines_after && (last_line_printed != linenum)) { |
if (print_n_lines_after) { |
391 |
print_line(line, linenum, '-'); |
print_line(line, strlen(line), linenum, '-'); |
392 |
print_n_lines_after--; |
print_n_lines_after--; |
393 |
|
} else if (lines_before) { |
394 |
|
/* Add the line to the circular 'before' buffer */ |
395 |
|
free(before_buf[curpos]); |
396 |
|
before_buf[curpos] = line; |
397 |
|
USE_EXTRA_COMPAT(before_buf_size[curpos] = line_len;) |
398 |
|
curpos = (curpos + 1) % lines_before; |
399 |
|
/* avoid free(line) - we took the line */ |
400 |
|
line = NULL; |
401 |
} |
} |
402 |
|
} |
403 |
|
|
404 |
#endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
#endif /* ENABLE_FEATURE_GREP_CONTEXT */ |
405 |
|
#if !ENABLE_EXTRA_COMPAT |
406 |
free(line); |
free(line); |
407 |
} |
#endif |
408 |
|
/* Did we print all context after last requested match? */ |
409 |
|
if ((option_mask32 & OPT_m) |
410 |
|
&& !print_n_lines_after && nmatches == max_matches) |
411 |
|
break; |
412 |
|
} /* while (read line) */ |
413 |
|
|
414 |
/* special-case file post-processing for options where we don't print line |
/* special-case file post-processing for options where we don't print line |
415 |
* matches, just filenames and possibly match counts */ |
* matches, just filenames and possibly match counts */ |
421 |
printf("%d\n", nmatches); |
printf("%d\n", nmatches); |
422 |
} |
} |
423 |
|
|
424 |
/* grep -l: print just the filename, but only if we grepped the line in the file */ |
/* grep -L: print just the filename */ |
425 |
if (PRINT_FILES_WITH_MATCHES && nmatches > 0) { |
if (PRINT_FILES_WITHOUT_MATCHES) { |
426 |
puts(cur_file); |
/* nmatches is zero, no need to check it: |
427 |
} |
* we return 1 early if we detected a match |
428 |
|
* and PRINT_FILES_WITHOUT_MATCHES is set */ |
|
/* grep -L: print just the filename, but only if we didn't grep the line in the file */ |
|
|
if (PRINT_FILES_WITHOUT_MATCHES && nmatches == 0) { |
|
429 |
puts(cur_file); |
puts(cur_file); |
430 |
} |
} |
431 |
|
|
434 |
|
|
435 |
#if ENABLE_FEATURE_CLEAN_UP |
#if ENABLE_FEATURE_CLEAN_UP |
436 |
#define new_grep_list_data(p, m) add_grep_list_data(p, m) |
#define new_grep_list_data(p, m) add_grep_list_data(p, m) |
437 |
static char * add_grep_list_data(char *pattern, int flg_used_mem) |
static char *add_grep_list_data(char *pattern, int flg_used_mem) |
438 |
#else |
#else |
439 |
#define new_grep_list_data(p, m) add_grep_list_data(p) |
#define new_grep_list_data(p, m) add_grep_list_data(p) |
440 |
static char * add_grep_list_data(char *pattern) |
static char *add_grep_list_data(char *pattern) |
441 |
#endif |
#endif |
442 |
{ |
{ |
443 |
grep_list_data_t *gl = xmalloc(sizeof(grep_list_data_t)); |
grep_list_data_t *gl = xzalloc(sizeof(*gl)); |
444 |
gl->pattern = pattern; |
gl->pattern = pattern; |
445 |
#if ENABLE_FEATURE_CLEAN_UP |
#if ENABLE_FEATURE_CLEAN_UP |
446 |
gl->flg_mem_alocated_compiled = flg_used_mem; |
gl->flg_mem_alocated_compiled = flg_used_mem; |
447 |
#else |
#else |
448 |
gl->flg_mem_alocated_compiled = 0; |
/*gl->flg_mem_alocated_compiled = 0;*/ |
449 |
#endif |
#endif |
450 |
return (char *)gl; |
return (char *)gl; |
451 |
} |
} |
452 |
|
|
|
|
|
453 |
static void load_regexes_from_file(llist_t *fopt) |
static void load_regexes_from_file(llist_t *fopt) |
454 |
{ |
{ |
455 |
char *line; |
char *line; |
461 |
|
|
462 |
fopt = cur->link; |
fopt = cur->link; |
463 |
free(cur); |
free(cur); |
464 |
f = xfopen(ffile, "r"); |
f = xfopen_stdin(ffile); |
465 |
while ((line = xmalloc_getline(f)) != NULL) { |
while ((line = xmalloc_fgetline(f)) != NULL) { |
466 |
llist_add_to(&pattern_head, |
llist_add_to(&pattern_head, |
467 |
new_grep_list_data(line, PATTERN_MEM_A)); |
new_grep_list_data(line, ALLOCATED)); |
468 |
} |
} |
469 |
} |
} |
470 |
} |
} |
471 |
|
|
472 |
|
static int FAST_FUNC file_action_grep(const char *filename, |
473 |
static int file_action_grep(const char *filename, struct stat *statbuf, void* matched, int depth) |
struct stat *statbuf UNUSED_PARAM, |
474 |
|
void* matched, |
475 |
|
int depth UNUSED_PARAM) |
476 |
{ |
{ |
477 |
FILE *file = fopen(filename, "r"); |
FILE *file = fopen_for_read(filename); |
478 |
if (file == NULL) { |
if (file == NULL) { |
479 |
if (!SUPPRESS_ERR_MSGS) |
if (!SUPPRESS_ERR_MSGS) |
480 |
bb_perror_msg("%s", cur_file); |
bb_simple_perror_msg(filename); |
481 |
open_errors = 1; |
open_errors = 1; |
482 |
return 0; |
return 0; |
483 |
} |
} |
487 |
return 1; |
return 1; |
488 |
} |
} |
489 |
|
|
|
|
|
490 |
static int grep_dir(const char *dir) |
static int grep_dir(const char *dir) |
491 |
{ |
{ |
492 |
int matched = 0; |
int matched = 0; |
493 |
recursive_action(dir, |
recursive_action(dir, |
494 |
/* recurse= */ 1, |
/* recurse=yes */ ACTION_RECURSE | |
495 |
/* followLinks= */ 0, |
/* followLinks=no */ |
496 |
/* depthFirst= */ 1, |
/* depthFirst=yes */ ACTION_DEPTHFIRST, |
497 |
/* fileAction= */ file_action_grep, |
/* fileAction= */ file_action_grep, |
498 |
/* dirAction= */ NULL, |
/* dirAction= */ NULL, |
499 |
/* userData= */ &matched, |
/* userData= */ &matched, |
501 |
return matched; |
return matched; |
502 |
} |
} |
503 |
|
|
504 |
|
int grep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
505 |
int grep_main(int argc, char **argv) |
int grep_main(int argc, char **argv) |
506 |
{ |
{ |
507 |
FILE *file; |
FILE *file; |
510 |
|
|
511 |
/* do normal option parsing */ |
/* do normal option parsing */ |
512 |
#if ENABLE_FEATURE_GREP_CONTEXT |
#if ENABLE_FEATURE_GREP_CONTEXT |
513 |
char *slines_after; |
int Copt; |
514 |
char *slines_before; |
|
515 |
char *Copt; |
/* -H unsets -h; -C unsets -A,-B; -e,-f are lists; |
516 |
|
* -m,-A,-B,-C have numeric param */ |
517 |
opt_complementary = "H-h:e::f::C-AB"; |
opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+"; |
518 |
getopt32(argc, argv, |
getopt32(argv, |
519 |
GREP_OPTS GREP_OPT_CONTEXT OPT_EGREP, |
OPTSTR_GREP, |
520 |
&pattern_head, &fopt, |
&pattern_head, &fopt, &max_matches, |
521 |
&slines_after, &slines_before, &Copt); |
&lines_after, &lines_before, &Copt); |
522 |
|
|
523 |
if (option_mask32 & GREP_OPT_C) { |
if (option_mask32 & OPT_C) { |
524 |
/* -C unsets prev -A and -B, but following -A or -B |
/* -C unsets prev -A and -B, but following -A or -B |
525 |
may override it */ |
may override it */ |
526 |
if (!(option_mask32 & GREP_OPT_A)) /* not overridden */ |
if (!(option_mask32 & OPT_A)) /* not overridden */ |
527 |
slines_after = Copt; |
lines_after = Copt; |
528 |
if (!(option_mask32 & GREP_OPT_B)) /* not overridden */ |
if (!(option_mask32 & OPT_B)) /* not overridden */ |
529 |
slines_before = Copt; |
lines_before = Copt; |
|
option_mask32 |= GREP_OPT_A|GREP_OPT_B; /* for parser */ |
|
|
} |
|
|
if (option_mask32 & GREP_OPT_A) { |
|
|
lines_after = xatoi_u(slines_after); |
|
|
} |
|
|
if (option_mask32 & GREP_OPT_B) { |
|
|
lines_before = xatoi_u(slines_before); |
|
530 |
} |
} |
531 |
/* sanity checks */ |
/* sanity checks */ |
532 |
if (option_mask32 & (GREP_OPT_c|GREP_OPT_q|GREP_OPT_l|GREP_OPT_L)) { |
if (option_mask32 & (OPT_c|OPT_q|OPT_l|OPT_L)) { |
533 |
option_mask32 &= ~GREP_OPT_n; |
option_mask32 &= ~OPT_n; |
534 |
lines_before = 0; |
lines_before = 0; |
535 |
lines_after = 0; |
lines_after = 0; |
536 |
} else if (lines_before > 0) |
} else if (lines_before > 0) { |
537 |
before_buf = xzalloc(lines_before * sizeof(char *)); |
before_buf = xzalloc(lines_before * sizeof(before_buf[0])); |
538 |
|
USE_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));) |
539 |
|
} |
540 |
#else |
#else |
541 |
/* with auto sanity checks */ |
/* with auto sanity checks */ |
542 |
opt_complementary = "H-h:e::f::c-n:q-n:l-n"; |
/* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */ |
543 |
getopt32(argc, argv, GREP_OPTS OPT_EGREP, |
opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+"; |
544 |
&pattern_head, &fopt); |
getopt32(argv, OPTSTR_GREP, |
545 |
|
&pattern_head, &fopt, &max_matches); |
546 |
#endif |
#endif |
547 |
invert_search = ((option_mask32 & GREP_OPT_v) != 0); /* 0 | 1 */ |
invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */ |
548 |
|
|
549 |
if (pattern_head != NULL) { |
if (pattern_head != NULL) { |
550 |
/* convert char *argv[] to grep_list_data_t */ |
/* convert char **argv to grep_list_data_t */ |
551 |
llist_t *cur; |
llist_t *cur; |
552 |
|
|
553 |
for (cur = pattern_head; cur; cur = cur->link) |
for (cur = pattern_head; cur; cur = cur->link) |
554 |
cur->data = new_grep_list_data(cur->data, 0); |
cur->data = new_grep_list_data(cur->data, 0); |
555 |
} |
} |
556 |
if (option_mask32 & GREP_OPT_f) |
if (option_mask32 & OPT_f) |
557 |
load_regexes_from_file(fopt); |
load_regexes_from_file(fopt); |
558 |
|
|
559 |
if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f') |
if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f') |
560 |
option_mask32 |= GREP_OPT_F; |
option_mask32 |= OPT_F; |
561 |
|
|
562 |
if (!(option_mask32 & GREP_OPT_o)) |
#if !ENABLE_EXTRA_COMPAT |
563 |
|
if (!(option_mask32 & (OPT_o | OPT_w))) |
564 |
reflags = REG_NOSUB; |
reflags = REG_NOSUB; |
565 |
|
#endif |
566 |
|
|
567 |
if (ENABLE_FEATURE_GREP_EGREP_ALIAS && |
if (ENABLE_FEATURE_GREP_EGREP_ALIAS |
568 |
(applet_name[0] == 'e' || (option_mask32 & GREP_OPT_E))) |
&& (applet_name[0] == 'e' || (option_mask32 & OPT_E)) |
569 |
|
) { |
570 |
reflags |= REG_EXTENDED; |
reflags |= REG_EXTENDED; |
571 |
|
} |
572 |
|
#if ENABLE_EXTRA_COMPAT |
573 |
|
else { |
574 |
|
reflags = RE_SYNTAX_GREP; |
575 |
|
} |
576 |
|
#endif |
577 |
|
|
578 |
if (option_mask32 & GREP_OPT_i) |
if (option_mask32 & OPT_i) { |
579 |
|
#if !ENABLE_EXTRA_COMPAT |
580 |
reflags |= REG_ICASE; |
reflags |= REG_ICASE; |
581 |
|
#else |
582 |
|
int i; |
583 |
|
case_fold = xmalloc(256); |
584 |
|
for (i = 0; i < 256; i++) |
585 |
|
case_fold[i] = (unsigned char)i; |
586 |
|
for (i = 'a'; i <= 'z'; i++) |
587 |
|
case_fold[i] = (unsigned char)(i - ('a' - 'A')); |
588 |
|
#endif |
589 |
|
} |
590 |
|
|
591 |
argv += optind; |
argv += optind; |
592 |
argc -= optind; |
argc -= optind; |
593 |
|
|
594 |
/* if we didn't get a pattern from a -e and no command file was specified, |
/* if we didn't get a pattern from -e and no command file was specified, |
595 |
* argv[optind] should be the pattern. no pattern, no worky */ |
* first parameter should be the pattern. no pattern, no worky */ |
596 |
if (pattern_head == NULL) { |
if (pattern_head == NULL) { |
597 |
|
char *pattern; |
598 |
if (*argv == NULL) |
if (*argv == NULL) |
599 |
bb_show_usage(); |
bb_show_usage(); |
600 |
else { |
pattern = new_grep_list_data(*argv++, 0); |
601 |
char *pattern = new_grep_list_data(*argv++, 0); |
llist_add_to(&pattern_head, pattern); |
602 |
|
argc--; |
|
llist_add_to(&pattern_head, pattern); |
|
|
argc--; |
|
|
} |
|
603 |
} |
} |
604 |
|
|
605 |
/* argv[(optind)..(argc-1)] should be names of file to grep through. If |
/* argv[0..(argc-1)] should be names of file to grep through. If |
606 |
* 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. */ |
607 |
if (argc > 1) |
if (argc > 1) |
608 |
print_filename = 1; |
print_filename = 1; |
609 |
/* -H / -h of course override */ |
/* -H / -h of course override */ |
610 |
if (option_mask32 & GREP_OPT_H) |
if (option_mask32 & OPT_H) |
611 |
print_filename = 1; |
print_filename = 1; |
612 |
if (option_mask32 & GREP_OPT_h) |
if (option_mask32 & OPT_h) |
613 |
print_filename = 0; |
print_filename = 0; |
614 |
|
|
615 |
/* If no files were specified, or '-' was specified, take input from |
/* If no files were specified, or '-' was specified, take input from |
616 |
* stdin. Otherwise, we grep through all the files specified. */ |
* stdin. Otherwise, we grep through all the files specified. */ |
|
if (argc == 0) |
|
|
argc++; |
|
617 |
matched = 0; |
matched = 0; |
618 |
while (argc--) { |
do { |
619 |
cur_file = *argv++; |
cur_file = *argv++; |
620 |
file = stdin; |
file = stdin; |
621 |
if (!cur_file || (*cur_file == '-' && !cur_file[1])) { |
if (!cur_file || LONE_DASH(cur_file)) { |
622 |
cur_file = "(standard input)"; |
cur_file = "(standard input)"; |
623 |
} else { |
} else { |
624 |
if (option_mask32 & GREP_OPT_r) { |
if (option_mask32 & OPT_r) { |
625 |
struct stat st; |
struct stat st; |
626 |
if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) { |
if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) { |
627 |
if (!(option_mask32 & GREP_OPT_h)) |
if (!(option_mask32 & OPT_h)) |
628 |
print_filename = 1; |
print_filename = 1; |
629 |
matched += grep_dir(cur_file); |
matched += grep_dir(cur_file); |
630 |
goto grep_done; |
goto grep_done; |
631 |
} |
} |
632 |
} |
} |
633 |
/* else: fopen(dir) will succeed, but reading won't */ |
/* else: fopen(dir) will succeed, but reading won't */ |
634 |
file = fopen(cur_file, "r"); |
file = fopen_for_read(cur_file); |
635 |
if (file == NULL) { |
if (file == NULL) { |
636 |
if (!SUPPRESS_ERR_MSGS) |
if (!SUPPRESS_ERR_MSGS) |
637 |
bb_perror_msg("%s", cur_file); |
bb_simple_perror_msg(cur_file); |
638 |
open_errors = 1; |
open_errors = 1; |
639 |
continue; |
continue; |
640 |
} |
} |
641 |
} |
} |
642 |
matched += grep_file(file); |
matched += grep_file(file); |
643 |
fclose_if_not_stdin(file); |
fclose_if_not_stdin(file); |
644 |
grep_done: |
grep_done: ; |
645 |
if (matched < 0) { |
} while (--argc > 0); |
|
/* we found a match but were told to be quiet, stop here and |
|
|
* return success */ |
|
|
break; |
|
|
} |
|
|
} |
|
646 |
|
|
647 |
/* destroy all the elments in the pattern list */ |
/* destroy all the elments in the pattern list */ |
648 |
if (ENABLE_FEATURE_CLEAN_UP) { |
if (ENABLE_FEATURE_CLEAN_UP) { |
649 |
while (pattern_head) { |
while (pattern_head) { |
650 |
llist_t *pattern_head_ptr = pattern_head; |
llist_t *pattern_head_ptr = pattern_head; |
651 |
grep_list_data_t *gl = |
grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data; |
|
(grep_list_data_t *)pattern_head_ptr->data; |
|
652 |
|
|
653 |
pattern_head = pattern_head->link; |
pattern_head = pattern_head->link; |
654 |
if ((gl->flg_mem_alocated_compiled & PATTERN_MEM_A)) |
if (gl->flg_mem_alocated_compiled & ALLOCATED) |
655 |
free(gl->pattern); |
free(gl->pattern); |
656 |
if ((gl->flg_mem_alocated_compiled & COMPILED)) |
if (gl->flg_mem_alocated_compiled & COMPILED) |
657 |
regfree(&(gl->preg)); |
regfree(&gl->compiled_regex); |
658 |
|
free(gl); |
659 |
free(pattern_head_ptr); |
free(pattern_head_ptr); |
660 |
} |
} |
661 |
} |
} |
662 |
/* 0 = success, 1 = failed, 2 = error */ |
/* 0 = success, 1 = failed, 2 = error */ |
|
/* If the -q option is specified, the exit status shall be zero |
|
|
* if an input line is selected, even if an error was detected. */ |
|
|
if (BE_QUIET && matched) |
|
|
return 0; |
|
663 |
if (open_errors) |
if (open_errors) |
664 |
return 2; |
return 2; |
665 |
return !matched; /* invert return value 0 = success, 1 = failed */ |
return !matched; /* invert return value: 0 = success, 1 = failed */ |
666 |
} |
} |