1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60#include "libbb.h"
61#include "xregex.h"
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116#define OPTSTR_GREP \
117 "lnqvscFiHhe:f:Lorm:wx" \
118 IF_FEATURE_GREP_CONTEXT("A:B:C:") \
119 IF_FEATURE_GREP_EGREP_ALIAS("E") \
120 IF_EXTRA_COMPAT("z") \
121 "aI"
122
123
124enum {
125 OPTBIT_l,
126 OPTBIT_n,
127 OPTBIT_q,
128 OPTBIT_v,
129 OPTBIT_s,
130 OPTBIT_c,
131 OPTBIT_F,
132 OPTBIT_i,
133 OPTBIT_H,
134 OPTBIT_h,
135 OPTBIT_e,
136 OPTBIT_f,
137 OPTBIT_L,
138 OPTBIT_o,
139 OPTBIT_r,
140 OPTBIT_m,
141 OPTBIT_w,
142 OPTBIT_x,
143 IF_FEATURE_GREP_CONTEXT( OPTBIT_A ,)
144 IF_FEATURE_GREP_CONTEXT( OPTBIT_B ,)
145 IF_FEATURE_GREP_CONTEXT( OPTBIT_C ,)
146 IF_FEATURE_GREP_EGREP_ALIAS(OPTBIT_E ,)
147 IF_EXTRA_COMPAT( OPTBIT_z ,)
148 OPT_l = 1 << OPTBIT_l,
149 OPT_n = 1 << OPTBIT_n,
150 OPT_q = 1 << OPTBIT_q,
151 OPT_v = 1 << OPTBIT_v,
152 OPT_s = 1 << OPTBIT_s,
153 OPT_c = 1 << OPTBIT_c,
154 OPT_F = 1 << OPTBIT_F,
155 OPT_i = 1 << OPTBIT_i,
156 OPT_H = 1 << OPTBIT_H,
157 OPT_h = 1 << OPTBIT_h,
158 OPT_e = 1 << OPTBIT_e,
159 OPT_f = 1 << OPTBIT_f,
160 OPT_L = 1 << OPTBIT_L,
161 OPT_o = 1 << OPTBIT_o,
162 OPT_r = 1 << OPTBIT_r,
163 OPT_m = 1 << OPTBIT_m,
164 OPT_w = 1 << OPTBIT_w,
165 OPT_x = 1 << OPTBIT_x,
166 OPT_A = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0,
167 OPT_B = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0,
168 OPT_C = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0,
169 OPT_E = IF_FEATURE_GREP_EGREP_ALIAS((1 << OPTBIT_E)) + 0,
170 OPT_z = IF_EXTRA_COMPAT( (1 << OPTBIT_z)) + 0,
171};
172
173#define PRINT_FILES_WITH_MATCHES (option_mask32 & OPT_l)
174#define PRINT_LINE_NUM (option_mask32 & OPT_n)
175#define BE_QUIET (option_mask32 & OPT_q)
176#define SUPPRESS_ERR_MSGS (option_mask32 & OPT_s)
177#define PRINT_MATCH_COUNTS (option_mask32 & OPT_c)
178#define FGREP_FLAG (option_mask32 & OPT_F)
179#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
180#define NUL_DELIMITED (option_mask32 & OPT_z)
181
182struct globals {
183 int max_matches;
184#if !ENABLE_EXTRA_COMPAT
185 int reflags;
186#else
187 RE_TRANSLATE_TYPE case_fold;
188#endif
189 smalluint invert_search;
190 smalluint print_filename;
191 smalluint open_errors;
192#if ENABLE_FEATURE_GREP_CONTEXT
193 smalluint did_print_line;
194 int lines_before;
195 int lines_after;
196 char **before_buf;
197 IF_EXTRA_COMPAT(size_t *before_buf_size;)
198 int last_line_printed;
199#endif
200
201 llist_t *pattern_head;
202 const char *cur_file;
203} FIX_ALIASING;
204#define G (*(struct globals*)&bb_common_bufsiz1)
205#define INIT_G() do { \
206 struct G_sizecheck { \
207 char G_sizecheck[sizeof(G) > COMMON_BUFSIZE ? -1 : 1]; \
208 }; \
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
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
221
222# define REG_EXTENDED (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
223# define REG_ICASE bug:is:here
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
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
261
262 if (linenum < 1)
263 return;
264
265 if ((lines_before || lines_after) && did_print_line
266 && last_line_printed != linenum - 1
267 ) {
268 puts("--");
269 }
270
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
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
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);
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;
327 int idx = 0;
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;
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;
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
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, 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
432
433
434
435
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
454
455 if (found && !invert_search)
456 goto do_found;
457 pattern_ptr = pattern_ptr->link;
458 }
459
460 if (found ^ invert_search) {
461 do_found:
462
463 nmatches++;
464
465
466 if (option_mask32 & (OPT_q|OPT_l|OPT_L)) {
467 free(line);
468 if (BE_QUIET) {
469
470
471
472
473 exit(EXIT_SUCCESS);
474 }
475
476 if (PRINT_FILES_WITH_MATCHES) {
477 puts(cur_file);
478
479 }
480
481 return 1;
482 }
483
484#if ENABLE_FEATURE_GREP_CONTEXT
485
486 if ((option_mask32 & OPT_m) && nmatches > max_matches)
487 break;
488#endif
489
490
491 if (PRINT_MATCH_COUNTS == 0) {
492#if ENABLE_FEATURE_GREP_CONTEXT
493 int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
494
495
496
497 if (lines_before && before_buf[prevpos] != NULL) {
498 int first_buf_entry_line_num = linenum - lines_before;
499
500
501
502
503 idx = curpos;
504 while (before_buf[idx] == NULL) {
505 idx = (idx + 1) % lines_before;
506 first_buf_entry_line_num++;
507 }
508
509
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
520 print_n_lines_after = lines_after;
521#endif
522 if (option_mask32 & OPT_o) {
523 if (FGREP_FLAG) {
524
525
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
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 {
562
563 if (print_n_lines_after) {
564 print_line(line, strlen(line), linenum, '-');
565 print_n_lines_after--;
566 } else if (lines_before) {
567
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
573 line = NULL;
574 }
575 }
576
577#endif
578#if !ENABLE_EXTRA_COMPAT
579 free(line);
580#endif
581
582 if ((option_mask32 & OPT_m)
583 && !print_n_lines_after
584 && nmatches == max_matches
585 ) {
586 break;
587 }
588 }
589
590
591
592
593
594 if (PRINT_MATCH_COUNTS) {
595 if (print_filename)
596 printf("%s:", cur_file);
597 printf("%d\n", nmatches);
598 }
599
600
601 if (PRINT_FILES_WITHOUT_MATCHES) {
602
603
604
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
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 ACTION_RECURSE |
671 ACTION_FOLLOWLINKS_L0 |
672 ACTION_DEPTHFIRST,
673 file_action_grep,
674 NULL,
675 &matched,
676 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
687
688#if ENABLE_FEATURE_GREP_CONTEXT
689 int Copt, opts;
690
691
692
693 opt_complementary = "H-h:C-AB:e::f::m+:A+:B+:C+";
694 opts = getopt32(argv,
695 OPTSTR_GREP,
696 &pattern_head, &fopt, &max_matches,
697 &lines_after, &lines_before, &Copt);
698
699 if (opts & OPT_C) {
700
701
702 if (!(opts & OPT_A))
703 lines_after = Copt;
704 if (!(opts & OPT_B))
705 lines_before = Copt;
706 }
707
708 if (opts & (OPT_c|OPT_q|OPT_l|OPT_L)) {
709 option_mask32 &= ~OPT_n;
710 lines_before = 0;
711 lines_after = 0;
712 } else if (lines_before > 0) {
713 if (lines_before > INT_MAX / sizeof(long long))
714 lines_before = INT_MAX / sizeof(long long);
715
716 before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
717 IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
718 }
719#else
720
721
722 opt_complementary = "H-h:c-n:q-n:l-n:e::f::m+";
723 getopt32(argv, OPTSTR_GREP,
724 &pattern_head, &fopt, &max_matches);
725#endif
726 invert_search = ((option_mask32 & OPT_v) != 0);
727
728 {
729 llist_t *cur;
730 for (cur = pattern_head; cur; cur = cur->link)
731 cur->data = new_grep_list_data(cur->data, 0);
732 }
733 if (option_mask32 & OPT_f) {
734 load_regexes_from_file(fopt);
735 if (!pattern_head) {
736
737 llist_add_to(&pattern_head, new_grep_list_data((char*) "", 0));
738 invert_search ^= 1;
739 }
740 }
741
742 if (ENABLE_FEATURE_GREP_FGREP_ALIAS && applet_name[0] == 'f')
743 option_mask32 |= OPT_F;
744
745#if !ENABLE_EXTRA_COMPAT
746 if (!(option_mask32 & (OPT_o | OPT_w | OPT_x)))
747 reflags = REG_NOSUB;
748#endif
749
750 if (ENABLE_FEATURE_GREP_EGREP_ALIAS
751 && (applet_name[0] == 'e' || (option_mask32 & OPT_E))
752 ) {
753 reflags |= REG_EXTENDED;
754 }
755#if ENABLE_EXTRA_COMPAT
756 else {
757 reflags = RE_SYNTAX_GREP;
758 }
759#endif
760
761 if (option_mask32 & OPT_i) {
762#if !ENABLE_EXTRA_COMPAT
763 reflags |= REG_ICASE;
764#else
765 int i;
766 case_fold = xmalloc(256);
767 for (i = 0; i < 256; i++)
768 case_fold[i] = (unsigned char)i;
769 for (i = 'a'; i <= 'z'; i++)
770 case_fold[i] = (unsigned char)(i - ('a' - 'A'));
771#endif
772 }
773
774 argv += optind;
775
776
777
778 if (pattern_head == NULL) {
779 char *pattern;
780 if (*argv == NULL)
781 bb_show_usage();
782 pattern = new_grep_list_data(*argv++, 0);
783 llist_add_to(&pattern_head, pattern);
784 }
785
786
787
788 if (argv[0] && argv[1])
789 print_filename = 1;
790
791 if (option_mask32 & OPT_H)
792 print_filename = 1;
793 if (option_mask32 & OPT_h)
794 print_filename = 0;
795
796
797
798 matched = 0;
799 do {
800 cur_file = *argv;
801 file = stdin;
802 if (!cur_file || LONE_DASH(cur_file)) {
803 cur_file = "(standard input)";
804 } else {
805 if (option_mask32 & OPT_r) {
806 struct stat st;
807 if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
808 if (!(option_mask32 & OPT_h))
809 print_filename = 1;
810 matched += grep_dir(cur_file);
811 goto grep_done;
812 }
813 }
814
815 file = fopen_for_read(cur_file);
816 if (file == NULL) {
817 if (!SUPPRESS_ERR_MSGS)
818 bb_simple_perror_msg(cur_file);
819 open_errors = 1;
820 continue;
821 }
822 }
823 matched += grep_file(file);
824 fclose_if_not_stdin(file);
825 grep_done: ;
826 } while (*argv && *++argv);
827
828
829 if (ENABLE_FEATURE_CLEAN_UP) {
830 while (pattern_head) {
831 llist_t *pattern_head_ptr = pattern_head;
832 grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
833
834 pattern_head = pattern_head->link;
835 if (gl->flg_mem_alocated_compiled & ALLOCATED)
836 free(gl->pattern);
837 if (gl->flg_mem_alocated_compiled & COMPILED)
838 regfree(&gl->compiled_regex);
839 free(gl);
840 free(pattern_head_ptr);
841 }
842 }
843
844 if (open_errors)
845 return 2;
846 return !matched;
847}
848