source: MondoRescue/branches/3.3/mindi-busybox/findutils/grep.c@ 3865

Last change on this file since 3865 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

File size: 25.2 KB
Line 
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini grep implementation for busybox using libc regex.
4 *
5 * Copyright (C) 1999,2000,2001 by Lineo, inc. and Mark Whitley
6 * Copyright (C) 1999,2000,2001 by Mark Whitley <markw@codepoet.org>
7 *
8 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
9 */
10/* BB_AUDIT SUSv3 defects - unsupported option -x "match whole line only". */
11/* BB_AUDIT GNU defects - always acts as -a. */
12/* http://www.opengroup.org/onlinepubs/007904975/utilities/grep.html */
13/*
14 * 2004,2006 (C) Vladimir Oleynik <dzo@simtreas.ru> -
15 * correction "-e pattern1 -e pattern2" logic and more optimizations.
16 * precompiled regex
17 *
18 * (C) 2006 Jac Goudsmit added -o option
19 */
20
21//applet:IF_GREP(APPLET(grep, BB_DIR_BIN, BB_SUID_DROP))
22//applet:IF_FEATURE_GREP_EGREP_ALIAS(APPLET_ODDNAME(egrep, grep, BB_DIR_BIN, BB_SUID_DROP, egrep))
23//applet:IF_FEATURE_GREP_FGREP_ALIAS(APPLET_ODDNAME(fgrep, grep, BB_DIR_BIN, BB_SUID_DROP, fgrep))
24
25//kbuild:lib-$(CONFIG_GREP) += grep.o
26
27//config:config GREP
28//config: bool "grep"
29//config: default y
30//config: help
31//config: grep is used to search files for a specified pattern.
32//config:
33//config:config FEATURE_GREP_EGREP_ALIAS
34//config: bool "Enable extended regular expressions (egrep & grep -E)"
35//config: default y
36//config: depends on GREP
37//config: help
38//config: Enabled support for extended regular expressions. Extended
39//config: regular expressions allow for alternation (foo|bar), grouping,
40//config: and various repetition operators.
41//config:
42//config:config FEATURE_GREP_FGREP_ALIAS
43//config: bool "Alias fgrep to grep -F"
44//config: default y
45//config: depends on GREP
46//config: help
47//config: fgrep sees the search pattern as a normal string rather than
48//config: regular expressions.
49//config: grep -F always works, this just creates the fgrep alias.
50//config:
51//config:config FEATURE_GREP_CONTEXT
52//config: bool "Enable before and after context flags (-A, -B and -C)"
53//config: default y
54//config: depends on GREP
55//config: help
56//config: Print the specified number of leading (-B) and/or trailing (-A)
57//config: context surrounding our matching lines.
58//config: Print the specified number of context lines (-C).
59
60#include "libbb.h"
61#include "common_bufsiz.h"
62#include "xregex.h"
63
64
65/* options */
66//usage:#define grep_trivial_usage
67//usage: "[-HhnlLoqvsriw"
68//usage: "F"
69//usage: IF_FEATURE_GREP_EGREP_ALIAS("E")
70//usage: IF_EXTRA_COMPAT("z")
71//usage: "] [-m N] "
72//usage: IF_FEATURE_GREP_CONTEXT("[-A/B/C N] ")
73//usage: "PATTERN/-e PATTERN.../-f FILE [FILE]..."
74//usage:#define grep_full_usage "\n\n"
75//usage: "Search for PATTERN in FILEs (or stdin)\n"
76//usage: "\n -H Add 'filename:' prefix"
77//usage: "\n -h Do not add 'filename:' prefix"
78//usage: "\n -n Add 'line_no:' prefix"
79//usage: "\n -l Show only names of files that match"
80//usage: "\n -L Show only names of files that don't match"
81//usage: "\n -c Show only count of matching lines"
82//usage: "\n -o Show only the matching part of line"
83//usage: "\n -q Quiet. Return 0 if PATTERN is found, 1 otherwise"
84//usage: "\n -v Select non-matching lines"
85//usage: "\n -s Suppress open and read errors"
86//usage: "\n -r Recurse"
87//usage: "\n -i Ignore case"
88//usage: "\n -w Match whole words only"
89//usage: "\n -x Match whole lines only"
90//usage: "\n -F PATTERN is a literal (not regexp)"
91//usage: IF_FEATURE_GREP_EGREP_ALIAS(
92//usage: "\n -E PATTERN is an extended regexp"
93//usage: )
94//usage: IF_EXTRA_COMPAT(
95//usage: "\n -z Input is NUL terminated"
96//usage: )
97//usage: "\n -m N Match up to N times per file"
98//usage: IF_FEATURE_GREP_CONTEXT(
99//usage: "\n -A N Print N lines of trailing context"
100//usage: "\n -B N Print N lines of leading context"
101//usage: "\n -C N Same as '-A N -B N'"
102//usage: )
103//usage: "\n -e PTRN Pattern to match"
104//usage: "\n -f FILE Read pattern from file"
105//usage:
106//usage:#define grep_example_usage
107//usage: "$ grep root /etc/passwd\n"
108//usage: "root:x:0:0:root:/root:/bin/bash\n"
109//usage: "$ grep ^[rR]oo. /etc/passwd\n"
110//usage: "root:x:0:0:root:/root:/bin/bash\n"
111//usage:
112//usage:#define egrep_trivial_usage NOUSAGE_STR
113//usage:#define egrep_full_usage ""
114//usage:#define fgrep_trivial_usage NOUSAGE_STR
115//usage:#define fgrep_full_usage ""
116
117#define OPTSTR_GREP \
118 "lnqvscFiHhe:f:Lorm:wx" \
119 IF_FEATURE_GREP_CONTEXT("A:B:C:") \
120 IF_FEATURE_GREP_EGREP_ALIAS("E") \
121 IF_EXTRA_COMPAT("z") \
122 "aI"
123/* ignored: -a "assume all files to be text" */
124/* ignored: -I "assume binary files have no matches" */
125enum {
126 OPTBIT_l, /* list matched file names only */
127 OPTBIT_n, /* print line# */
128 OPTBIT_q, /* quiet - exit(EXIT_SUCCESS) of first match */
129 OPTBIT_v, /* invert the match, to select non-matching lines */
130 OPTBIT_s, /* suppress errors about file open errors */
131 OPTBIT_c, /* count matches per file (suppresses normal output) */
132 OPTBIT_F, /* literal match */
133 OPTBIT_i, /* case-insensitive */
134 OPTBIT_H, /* force filename display */
135 OPTBIT_h, /* inhibit filename display */
136 OPTBIT_e, /* -e PATTERN */
137 OPTBIT_f, /* -f FILE_WITH_PATTERNS */
138 OPTBIT_L, /* list unmatched file names only */
139 OPTBIT_o, /* show only matching parts of lines */
140 OPTBIT_r, /* recurse dirs */
141 OPTBIT_m, /* -m MAX_MATCHES */
142 OPTBIT_w, /* -w whole word match */
143 OPTBIT_x, /* -x whole line match */
144 IF_FEATURE_GREP_CONTEXT( OPTBIT_A ,) /* -A NUM: after-match context */
145 IF_FEATURE_GREP_CONTEXT( OPTBIT_B ,) /* -B NUM: before-match context */
146 IF_FEATURE_GREP_CONTEXT( OPTBIT_C ,) /* -C NUM: -A and -B combined */
147 IF_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,) /* extended regexp */
148 IF_EXTRA_COMPAT( OPTBIT_z ,) /* input is NUL terminated */
149 OPT_l = 1 << OPTBIT_l,
150 OPT_n = 1 << OPTBIT_n,
151 OPT_q = 1 << OPTBIT_q,
152 OPT_v = 1 << OPTBIT_v,
153 OPT_s = 1 << OPTBIT_s,
154 OPT_c = 1 << OPTBIT_c,
155 OPT_F = 1 << OPTBIT_F,
156 OPT_i = 1 << OPTBIT_i,
157 OPT_H = 1 << OPTBIT_H,
158 OPT_h = 1 << OPTBIT_h,
159 OPT_e = 1 << OPTBIT_e,
160 OPT_f = 1 << OPTBIT_f,
161 OPT_L = 1 << OPTBIT_L,
162 OPT_o = 1 << OPTBIT_o,
163 OPT_r = 1 << OPTBIT_r,
164 OPT_m = 1 << OPTBIT_m,
165 OPT_w = 1 << OPTBIT_w,
166 OPT_x = 1 << OPTBIT_x,
167 OPT_A = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0,
168 OPT_B = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0,
169 OPT_C = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0,
170 OPT_E = IF_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0,
171 OPT_z = IF_EXTRA_COMPAT( (1 << OPTBIT_z)) + 0,
172};
173
174#define PRINT_FILES_WITH_MATCHES (option_mask32 & OPT_l)
175#define PRINT_LINE_NUM (option_mask32 & OPT_n)
176#define BE_QUIET (option_mask32 & OPT_q)
177#define SUPPRESS_ERR_MSGS (option_mask32 & OPT_s)
178#define PRINT_MATCH_COUNTS (option_mask32 & OPT_c)
179#define FGREP_FLAG (option_mask32 & OPT_F)
180#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
181#define NUL_DELIMITED (option_mask32 & OPT_z)
182
183struct globals {
184 int max_matches;
185#if !ENABLE_EXTRA_COMPAT
186 int reflags;
187#else
188 RE_TRANSLATE_TYPE case_fold; /* RE_TRANSLATE_TYPE is [[un]signed] char* */
189#endif
190 smalluint invert_search;
191 smalluint print_filename;
192 smalluint open_errors;
193#if ENABLE_FEATURE_GREP_CONTEXT
194 smalluint did_print_line;
195 int lines_before;
196 int lines_after;
197 char **before_buf;
198 IF_EXTRA_COMPAT(size_t *before_buf_size;)
199 int last_line_printed;
200#endif
201 /* globals used internally */
202 llist_t *pattern_head; /* growable list of patterns to match */
203 const char *cur_file; /* the current file we are reading */
204} FIX_ALIASING;
205#define G (*(struct globals*)bb_common_bufsiz1)
206#define INIT_G() do { \
207 setup_common_bufsiz(); \
208 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
209} while (0)
210#define max_matches (G.max_matches )
211#if !ENABLE_EXTRA_COMPAT
212# define reflags (G.reflags )
213#else
214# define case_fold (G.case_fold )
215/* http://www.delorie.com/gnu/docs/regex/regex_46.html */
216# define reflags re_syntax_options
217# undef REG_NOSUB
218# undef REG_EXTENDED
219# undef REG_ICASE
220# define REG_NOSUB bug:is:here /* should not be used */
221/* Just RE_SYNTAX_EGREP is not enough, need to enable {n[,[m]]} too */
222# define REG_EXTENDED (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
223# define REG_ICASE bug:is:here /* should not be used */
224#endif
225#define invert_search (G.invert_search )
226#define print_filename (G.print_filename )
227#define open_errors (G.open_errors )
228#define did_print_line (G.did_print_line )
229#define lines_before (G.lines_before )
230#define lines_after (G.lines_after )
231#define before_buf (G.before_buf )
232#define before_buf_size (G.before_buf_size )
233#define last_line_printed (G.last_line_printed )
234#define pattern_head (G.pattern_head )
235#define cur_file (G.cur_file )
236
237
238typedef struct grep_list_data_t {
239 char *pattern;
240/* for GNU regex, matched_range must be persistent across grep_file() calls */
241#if !ENABLE_EXTRA_COMPAT
242 regex_t compiled_regex;
243 regmatch_t matched_range;
244#else
245 struct re_pattern_buffer compiled_regex;
246 struct re_registers matched_range;
247#endif
248#define ALLOCATED 1
249#define COMPILED 2
250 int flg_mem_alocated_compiled;
251} grep_list_data_t;
252
253#if !ENABLE_EXTRA_COMPAT
254#define print_line(line, line_len, linenum, decoration) \
255 print_line(line, linenum, decoration)
256#endif
257static void print_line(const char *line, size_t line_len, int linenum, char decoration)
258{
259#if ENABLE_FEATURE_GREP_CONTEXT
260 /* Happens when we go to next file, immediately hit match
261 * and try to print prev context... from prev file! Don't do it */
262 if (linenum < 1)
263 return;
264 /* possibly print the little '--' separator */
265 if ((lines_before || lines_after) && did_print_line
266 && last_line_printed != linenum - 1
267 ) {
268 puts("--");
269 }
270 /* guard against printing "--" before first line of first file */
271 did_print_line = 1;
272 last_line_printed = linenum;
273#endif
274 if (print_filename)
275 printf("%s%c", cur_file, decoration);
276 if (PRINT_LINE_NUM)
277 printf("%i%c", linenum, decoration);
278 /* Emulate weird GNU grep behavior with -ov */
279 if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) {
280#if !ENABLE_EXTRA_COMPAT
281 puts(line);
282#else
283 fwrite(line, 1, line_len, stdout);
284 putchar(NUL_DELIMITED ? '\0' : '\n');
285#endif
286 }
287}
288
289#if ENABLE_EXTRA_COMPAT
290/* Unlike getline, this one removes trailing '\n' */
291static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file)
292{
293 ssize_t res_sz;
294 char *line;
295 int delim = (NUL_DELIMITED ? '\0' : '\n');
296
297 res_sz = getdelim(line_ptr, line_alloc_len, delim, file);
298 line = *line_ptr;
299
300 if (res_sz > 0) {
301 if (line[res_sz - 1] == delim)
302 line[--res_sz] = '\0';
303 } else {
304 free(line); /* uclibc allocates a buffer even on EOF. WTF? */
305 }
306 return res_sz;
307}
308#endif
309
310static int grep_file(FILE *file)
311{
312 smalluint found;
313 int linenum = 0;
314 int nmatches = 0;
315#if !ENABLE_EXTRA_COMPAT
316 char *line;
317#else
318 char *line = NULL;
319 ssize_t line_len;
320 size_t line_alloc_len;
321# define rm_so start[0]
322# define rm_eo end[0]
323#endif
324#if ENABLE_FEATURE_GREP_CONTEXT
325 int print_n_lines_after = 0;
326 int curpos = 0; /* track where we are in the circular 'before' buffer */
327 int idx = 0; /* used for iteration through the circular buffer */
328#else
329 enum { print_n_lines_after = 0 };
330#endif
331
332 while (
333#if !ENABLE_EXTRA_COMPAT
334 (line = xmalloc_fgetline(file)) != NULL
335#else
336 (line_len = bb_getline(&line, &line_alloc_len, file)) >= 0
337#endif
338 ) {
339 llist_t *pattern_ptr = pattern_head;
340 grep_list_data_t *gl = gl; /* for gcc */
341
342 linenum++;
343 found = 0;
344 while (pattern_ptr) {
345 gl = (grep_list_data_t *)pattern_ptr->data;
346 if (FGREP_FLAG) {
347 char *match;
348 char *str = line;
349 opt_f_again:
350 match = ((option_mask32 & OPT_i)
351 ? strcasestr(str, gl->pattern)
352 : strstr(str, gl->pattern)
353 );
354 if (match) {
355 if (option_mask32 & OPT_x) {
356 if (match != str)
357 goto opt_f_not_found;
358 if (str[strlen(gl->pattern)] != '\0')
359 goto opt_f_not_found;
360 } else
361 if (option_mask32 & OPT_w) {
362 char c = (match != str) ? match[-1] : ' ';
363 if (!isalnum(c) && c != '_') {
364 c = match[strlen(gl->pattern)];
365 if (!c || (!isalnum(c) && c != '_'))
366 goto opt_f_found;
367 }
368 str = match + 1;
369 goto opt_f_again;
370 }
371 opt_f_found:
372 found = 1;
373 opt_f_not_found: ;
374 }
375 } else {
376#if ENABLE_EXTRA_COMPAT
377 unsigned start_pos;
378#else
379 int match_flg;
380#endif
381 char *match_at;
382
383 if (!(gl->flg_mem_alocated_compiled & COMPILED)) {
384 gl->flg_mem_alocated_compiled |= COMPILED;
385#if !ENABLE_EXTRA_COMPAT
386 xregcomp(&gl->compiled_regex, gl->pattern, reflags);
387#else
388 memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex));
389 gl->compiled_regex.translate = case_fold; /* for -i */
390 if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex))
391 bb_error_msg_and_die("bad regex '%s'", gl->pattern);
392#endif
393 }
394#if !ENABLE_EXTRA_COMPAT
395 gl->matched_range.rm_so = 0;
396 gl->matched_range.rm_eo = 0;
397 match_flg = 0;
398#else
399 start_pos = 0;
400#endif
401 match_at = line;
402 opt_w_again:
403//bb_error_msg("'%s' start_pos:%d line_len:%d", match_at, start_pos, line_len);
404 if (
405#if !ENABLE_EXTRA_COMPAT
406 regexec(&gl->compiled_regex, match_at, 1, &gl->matched_range, match_flg) == 0
407#else
408 re_search(&gl->compiled_regex, match_at, line_len,
409 start_pos, /*range:*/ line_len,
410 &gl->matched_range) >= 0
411#endif
412 ) {
413 if (option_mask32 & OPT_x) {
414 found = (gl->matched_range.rm_so == 0
415 && match_at[gl->matched_range.rm_eo] == '\0');
416 } else
417 if (!(option_mask32 & OPT_w)) {
418 found = 1;
419 } else {
420 char c = ' ';
421 if (match_at > line || gl->matched_range.rm_so != 0) {
422 c = match_at[gl->matched_range.rm_so - 1];
423 }
424 if (!isalnum(c) && c != '_') {
425 c = match_at[gl->matched_range.rm_eo];
426 }
427 if (!isalnum(c) && c != '_') {
428 found = 1;
429 } else {
430 /*
431 * Why check gl->matched_range.rm_eo?
432 * Zero-length match makes -w skip the line:
433 * "echo foo | grep ^" prints "foo",
434 * "echo foo | grep -w ^" prints nothing.
435 * Without such check, we can loop forever.
436 */
437#if !ENABLE_EXTRA_COMPAT
438 if (gl->matched_range.rm_eo != 0) {
439 match_at += gl->matched_range.rm_eo;
440 match_flg |= REG_NOTBOL;
441 goto opt_w_again;
442 }
443#else
444 if (gl->matched_range.rm_eo > start_pos) {
445 start_pos = gl->matched_range.rm_eo;
446 goto opt_w_again;
447 }
448#endif
449 }
450 }
451 }
452 }
453 /* If it's non-inverted search, we can stop
454 * at first match */
455 if (found && !invert_search)
456 goto do_found;
457 pattern_ptr = pattern_ptr->link;
458 } /* while (pattern_ptr) */
459
460 if (found ^ invert_search) {
461 do_found:
462 /* keep track of matches */
463 nmatches++;
464
465 /* quiet/print (non)matching file names only? */
466 if (option_mask32 & (OPT_q|OPT_l|OPT_L)) {
467 free(line); /* we don't need line anymore */
468 if (BE_QUIET) {
469 /* manpage says about -q:
470 * "exit immediately with zero status
471 * if any match is found,
472 * even if errors were detected" */
473 exit(EXIT_SUCCESS);
474 }
475 /* if we're just printing filenames, we stop after the first match */
476 if (PRINT_FILES_WITH_MATCHES) {
477 puts(cur_file);
478 /* fall through to "return 1" */
479 }
480 /* OPT_L aka PRINT_FILES_WITHOUT_MATCHES: return early */
481 return 1; /* one match */
482 }
483
484#if ENABLE_FEATURE_GREP_CONTEXT
485 /* Were we printing context and saw next (unwanted) match? */
486 if ((option_mask32 & OPT_m) && nmatches > max_matches)
487 break;
488#endif
489
490 /* print the matched line */
491 if (PRINT_MATCH_COUNTS == 0) {
492#if ENABLE_FEATURE_GREP_CONTEXT
493 int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
494
495 /* if we were told to print 'before' lines and there is at least
496 * one line in the circular buffer, print them */
497 if (lines_before && before_buf[prevpos] != NULL) {
498 int first_buf_entry_line_num = linenum - lines_before;
499
500 /* advance to the first entry in the circular buffer, and
501 * figure out the line number is of the first line in the
502 * buffer */
503 idx = curpos;
504 while (before_buf[idx] == NULL) {
505 idx = (idx + 1) % lines_before;
506 first_buf_entry_line_num++;
507 }
508
509 /* now print each line in the buffer, clearing them as we go */
510 while (before_buf[idx] != NULL) {
511 print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-');
512 free(before_buf[idx]);
513 before_buf[idx] = NULL;
514 idx = (idx + 1) % lines_before;
515 first_buf_entry_line_num++;
516 }
517 }
518
519 /* make a note that we need to print 'after' lines */
520 print_n_lines_after = lines_after;
521#endif
522 if (option_mask32 & OPT_o) {
523 if (FGREP_FLAG) {
524 /* -Fo just prints the pattern
525 * (unless -v: -Fov doesnt print anything at all) */
526 if (found)
527 print_line(gl->pattern, strlen(gl->pattern), linenum, ':');
528 } else while (1) {
529 unsigned start = gl->matched_range.rm_so;
530 unsigned end = gl->matched_range.rm_eo;
531 unsigned len = end - start;
532 char old = line[end];
533 line[end] = '\0';
534 /* Empty match is not printed: try "echo test | grep -o ''" */
535 if (len != 0)
536 print_line(line + start, len, linenum, ':');
537 if (old == '\0')
538 break;
539 line[end] = old;
540 if (len == 0)
541 end++;
542#if !ENABLE_EXTRA_COMPAT
543 if (regexec(&gl->compiled_regex, line + end,
544 1, &gl->matched_range, REG_NOTBOL) != 0)
545 break;
546 gl->matched_range.rm_so += end;
547 gl->matched_range.rm_eo += end;
548#else
549 if (re_search(&gl->compiled_regex, line, line_len,
550 end, line_len - end,
551 &gl->matched_range) < 0)
552 break;
553#endif
554 }
555 } else {
556 print_line(line, line_len, linenum, ':');
557 }
558 }
559 }
560#if ENABLE_FEATURE_GREP_CONTEXT
561 else { /* no match */
562 /* if we need to print some context lines after the last match, do so */
563 if (print_n_lines_after) {
564 print_line(line, strlen(line), linenum, '-');
565 print_n_lines_after--;
566 } else if (lines_before) {
567 /* Add the line to the circular 'before' buffer */
568 free(before_buf[curpos]);
569 before_buf[curpos] = line;
570 IF_EXTRA_COMPAT(before_buf_size[curpos] = line_len;)
571 curpos = (curpos + 1) % lines_before;
572 /* avoid free(line) - we took the line */
573 line = NULL;
574 }
575 }
576
577#endif /* ENABLE_FEATURE_GREP_CONTEXT */
578#if !ENABLE_EXTRA_COMPAT
579 free(line);
580#endif
581 /* Did we print all context after last requested match? */
582 if ((option_mask32 & OPT_m)
583 && !print_n_lines_after
584 && nmatches == max_matches
585 ) {
586 break;
587 }
588 } /* while (read line) */
589
590 /* special-case file post-processing for options where we don't print line
591 * matches, just filenames and possibly match counts */
592
593 /* grep -c: print [filename:]count, even if count is zero */
594 if (PRINT_MATCH_COUNTS) {
595 if (print_filename)
596 printf("%s:", cur_file);
597 printf("%d\n", nmatches);
598 }
599
600 /* grep -L: print just the filename */
601 if (PRINT_FILES_WITHOUT_MATCHES) {
602 /* nmatches is zero, no need to check it:
603 * we return 1 early if we detected a match
604 * and PRINT_FILES_WITHOUT_MATCHES is set */
605 puts(cur_file);
606 }
607
608 return nmatches;
609}
610
611#if ENABLE_FEATURE_CLEAN_UP
612#define new_grep_list_data(p, m) add_grep_list_data(p, m)
613static char *add_grep_list_data(char *pattern, int flg_used_mem)
614#else
615#define new_grep_list_data(p, m) add_grep_list_data(p)
616static char *add_grep_list_data(char *pattern)
617#endif
618{
619 grep_list_data_t *gl = xzalloc(sizeof(*gl));
620 gl->pattern = pattern;
621#if ENABLE_FEATURE_CLEAN_UP
622 gl->flg_mem_alocated_compiled = flg_used_mem;
623#else
624 /*gl->flg_mem_alocated_compiled = 0;*/
625#endif
626 return (char *)gl;
627}
628
629static void load_regexes_from_file(llist_t *fopt)
630{
631 while (fopt) {
632 char *line;
633 FILE *fp;
634 llist_t *cur = fopt;
635 char *ffile = cur->data;
636
637 fopt = cur->link;
638 free(cur);
639 fp = xfopen_stdin(ffile);
640 while ((line = xmalloc_fgetline(fp)) != NULL) {
641 llist_add_to(&pattern_head,
642 new_grep_list_data(line, ALLOCATED));
643 }
644 fclose_if_not_stdin(fp);
645 }
646}
647
648static int FAST_FUNC file_action_grep(const char *filename,
649 struct stat *statbuf UNUSED_PARAM,
650 void* matched,
651 int depth UNUSED_PARAM)
652{
653 FILE *file = fopen_for_read(filename);
654 if (file == NULL) {
655 if (!SUPPRESS_ERR_MSGS)
656 bb_simple_perror_msg(filename);
657 open_errors = 1;
658 return 0;
659 }
660 cur_file = filename;
661 *(int*)matched += grep_file(file);
662 fclose(file);
663 return 1;
664}
665
666static int grep_dir(const char *dir)
667{
668 int matched = 0;
669 recursive_action(dir,
670 /* recurse=yes */ ACTION_RECURSE |
671 /* followLinks=command line only */ ACTION_FOLLOWLINKS_L0 |
672 /* depthFirst=yes */ ACTION_DEPTHFIRST,
673 /* fileAction= */ file_action_grep,
674 /* dirAction= */ NULL,
675 /* userData= */ &matched,
676 /* depth= */ 0);
677 return matched;
678}
679
680int grep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
681int grep_main(int argc UNUSED_PARAM, char **argv)
682{
683 FILE *file;
684 int matched;
685 llist_t *fopt = NULL;
686#if ENABLE_FEATURE_GREP_CONTEXT
687 int Copt, opts;
688#endif
689 INIT_G();
690
691 /* For grep, exitcode of 1 is "not found". Other errors are 2: */
692 xfunc_error_retval = 2;
693
694 /* do normal option parsing */
695#if ENABLE_FEATURE_GREP_CONTEXT
696 /* -H unsets -h; -C unsets -A,-B; -e,-f are lists;
697 * -m,-A,-B,-C have numeric param */
698 opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+";
699 opts = getopt32(argv,
700 OPTSTR_GREP,
701 &pattern_head, &fopt, &max_matches,
702 &lines_after, &lines_before, &Copt);
703
704 if (opts & OPT_C) {
705 /* -C unsets prev -A and -B, but following -A or -B
706 * may override it */
707 if (!(opts & OPT_A)) /* not overridden */
708 lines_after = Copt;
709 if (!(opts & OPT_B)) /* not overridden */
710 lines_before = Copt;
711 }
712 /* sanity checks */
713 if (opts & (OPT_c|OPT_q|OPT_l|OPT_L)) {
714 option_mask32 &= ~OPT_n;
715 lines_before = 0;
716 lines_after = 0;
717 } else if (lines_before > 0) {
718 if (lines_before > INT_MAX / sizeof(long long))
719 lines_before = INT_MAX / sizeof(long long);
720 /* overflow in (lines_before * sizeof(x)) is prevented (above) */
721 before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
722 IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
723 }
724#else
725 /* with auto sanity checks */
726 /* -H unsets -h; -c,-q or -l unset -n; -e,-f are lists; -m N */
727 opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+";
728 getopt32(argv, OPTSTR_GREP,
729 &pattern_head, &fopt, &max_matches);
730#endif
731 invert_search = ((option_mask32 & OPT_v) != 0); /* 0 | 1 */
732
733 { /* convert char **argv to grep_list_data_t */
734 llist_t *cur;
735 for (cur = pattern_head; cur; cur = cur->link)
736 cur->data = new_grep_list_data(cur->data, 0);
737 }
738 if (option_mask32 & OPT_f) {
739 load_regexes_from_file(fopt);
740 if (!pattern_head) { /* -f EMPTY_FILE? */
741 /* GNU grep treats it as "nothing matches" */
742 llist_add_to(&pattern_head, new_grep_list_data((char*) "", 0));
743 invert_search ^= 1;
744 }
745 }
746
747 if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
748 option_mask32 |= OPT_F;
749
750#if !ENABLE_EXTRA_COMPAT
751 if (!(option_mask32 & (OPT_o | OPT_w | OPT_x)))
752 reflags = REG_NOSUB;
753#endif
754
755 if (ENABLE_FEATURE_GREP_EGREP_ALIAS
756 && (applet_name[0] == 'e' || (option_mask32 & OPT_E))
757 ) {
758 reflags |= REG_EXTENDED;
759 }
760#if ENABLE_EXTRA_COMPAT
761 else {
762 reflags = RE_SYNTAX_GREP;
763 }
764#endif
765
766 if (option_mask32 & OPT_i) {
767#if !ENABLE_EXTRA_COMPAT
768 reflags |= REG_ICASE;
769#else
770 int i;
771 case_fold = xmalloc(256);
772 for (i = 0; i < 256; i++)
773 case_fold[i] = (unsigned char)i;
774 for (i = 'a'; i <= 'z'; i++)
775 case_fold[i] = (unsigned char)(i - ('a' - 'A'));
776#endif
777 }
778
779 argv += optind;
780
781 /* if we didn't get a pattern from -e and no command file was specified,
782 * first parameter should be the pattern. no pattern, no worky */
783 if (pattern_head == NULL) {
784 char *pattern;
785 if (*argv == NULL)
786 bb_show_usage();
787 pattern = new_grep_list_data(*argv++, 0);
788 llist_add_to(&pattern_head, pattern);
789 }
790
791 /* argv[0..(argc-1)] should be names of file to grep through. If
792 * there is more than one file to grep, we will print the filenames. */
793 if (argv[0] && argv[1])
794 print_filename = 1;
795 /* -H / -h of course override */
796 if (option_mask32 & OPT_H)
797 print_filename = 1;
798 if (option_mask32 & OPT_h)
799 print_filename = 0;
800
801 /* If no files were specified, or '-' was specified, take input from
802 * stdin. Otherwise, we grep through all the files specified. */
803 matched = 0;
804 do {
805 cur_file = *argv;
806 file = stdin;
807 if (!cur_file || LONE_DASH(cur_file)) {
808 cur_file = "(standard input)";
809 } else {
810 if (option_mask32 & OPT_r) {
811 struct stat st;
812 if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
813 if (!(option_mask32 & OPT_h))
814 print_filename = 1;
815 matched += grep_dir(cur_file);
816 goto grep_done;
817 }
818 }
819 /* else: fopen(dir) will succeed, but reading won't */
820 file = fopen_for_read(cur_file);
821 if (file == NULL) {
822 if (!SUPPRESS_ERR_MSGS)
823 bb_simple_perror_msg(cur_file);
824 open_errors = 1;
825 continue;
826 }
827 }
828 matched += grep_file(file);
829 fclose_if_not_stdin(file);
830 grep_done: ;
831 } while (*argv && *++argv);
832
833 /* destroy all the elments in the pattern list */
834 if (ENABLE_FEATURE_CLEAN_UP) {
835 while (pattern_head) {
836 llist_t *pattern_head_ptr = pattern_head;
837 grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
838
839 pattern_head = pattern_head->link;
840 if (gl->flg_mem_alocated_compiled & ALLOCATED)
841 free(gl->pattern);
842 if (gl->flg_mem_alocated_compiled & COMPILED)
843 regfree(&gl->compiled_regex);
844 free(gl);
845 free(pattern_head_ptr);
846 }
847 }
848 /* 0 = success, 1 = failed, 2 = error */
849 if (open_errors)
850 return 2;
851 return !matched; /* invert return value: 0 = success, 1 = failed */
852}
Note: See TracBrowser for help on using the repository browser.