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#include "libbb.h"
57#include "common_bufsiz.h"
58#include "xregex.h"
59
60
61
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#define OPTSTR_GREP \
111 "lnqvscFiHhe:*f:*Lorm:+wx" \
112 IF_FEATURE_GREP_CONTEXT("A:+B:+C:+") \
113 "E" \
114 IF_EXTRA_COMPAT("z") \
115 "aI"
116
117
118enum {
119 OPTBIT_l,
120 OPTBIT_n,
121 OPTBIT_q,
122 OPTBIT_v,
123 OPTBIT_s,
124 OPTBIT_c,
125 OPTBIT_F,
126 OPTBIT_i,
127 OPTBIT_H,
128 OPTBIT_h,
129 OPTBIT_e,
130 OPTBIT_f,
131 OPTBIT_L,
132 OPTBIT_o,
133 OPTBIT_r,
134 OPTBIT_m,
135 OPTBIT_w,
136 OPTBIT_x,
137 IF_FEATURE_GREP_CONTEXT( OPTBIT_A ,)
138 IF_FEATURE_GREP_CONTEXT( OPTBIT_B ,)
139 IF_FEATURE_GREP_CONTEXT( OPTBIT_C ,)
140 OPTBIT_E,
141 IF_EXTRA_COMPAT( OPTBIT_z ,)
142 OPT_l = 1 << OPTBIT_l,
143 OPT_n = 1 << OPTBIT_n,
144 OPT_q = 1 << OPTBIT_q,
145 OPT_v = 1 << OPTBIT_v,
146 OPT_s = 1 << OPTBIT_s,
147 OPT_c = 1 << OPTBIT_c,
148 OPT_F = 1 << OPTBIT_F,
149 OPT_i = 1 << OPTBIT_i,
150 OPT_H = 1 << OPTBIT_H,
151 OPT_h = 1 << OPTBIT_h,
152 OPT_e = 1 << OPTBIT_e,
153 OPT_f = 1 << OPTBIT_f,
154 OPT_L = 1 << OPTBIT_L,
155 OPT_o = 1 << OPTBIT_o,
156 OPT_r = 1 << OPTBIT_r,
157 OPT_m = 1 << OPTBIT_m,
158 OPT_w = 1 << OPTBIT_w,
159 OPT_x = 1 << OPTBIT_x,
160 OPT_A = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_A)) + 0,
161 OPT_B = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_B)) + 0,
162 OPT_C = IF_FEATURE_GREP_CONTEXT( (1 << OPTBIT_C)) + 0,
163 OPT_E = 1 << OPTBIT_E,
164 OPT_z = IF_EXTRA_COMPAT( (1 << OPTBIT_z)) + 0,
165};
166
167#define PRINT_FILES_WITH_MATCHES (option_mask32 & OPT_l)
168#define PRINT_LINE_NUM (option_mask32 & OPT_n)
169#define BE_QUIET (option_mask32 & OPT_q)
170#define SUPPRESS_ERR_MSGS (option_mask32 & OPT_s)
171#define PRINT_MATCH_COUNTS (option_mask32 & OPT_c)
172#define FGREP_FLAG (option_mask32 & OPT_F)
173#define PRINT_FILES_WITHOUT_MATCHES (option_mask32 & OPT_L)
174#define NUL_DELIMITED (option_mask32 & OPT_z)
175
176struct globals {
177 int max_matches;
178#if !ENABLE_EXTRA_COMPAT
179 int reflags;
180#else
181 RE_TRANSLATE_TYPE case_fold;
182#endif
183 smalluint invert_search;
184 smalluint print_filename;
185 smalluint open_errors;
186#if ENABLE_FEATURE_GREP_CONTEXT
187 smalluint did_print_line;
188 int lines_before;
189 int lines_after;
190 char **before_buf;
191 IF_EXTRA_COMPAT(size_t *before_buf_size;)
192 int last_line_printed;
193#endif
194
195 llist_t *pattern_head;
196 const char *cur_file;
197} FIX_ALIASING;
198#define G (*(struct globals*)bb_common_bufsiz1)
199#define INIT_G() do { \
200 setup_common_bufsiz(); \
201 BUILD_BUG_ON(sizeof(G) > COMMON_BUFSIZE); \
202} while (0)
203#define max_matches (G.max_matches )
204#if !ENABLE_EXTRA_COMPAT
205# define reflags (G.reflags )
206#else
207# define case_fold (G.case_fold )
208
209# define reflags re_syntax_options
210# undef REG_NOSUB
211# undef REG_EXTENDED
212# undef REG_ICASE
213# define REG_NOSUB bug:is:here
214
215# define REG_EXTENDED (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES)
216# define REG_ICASE bug:is:here
217#endif
218#define invert_search (G.invert_search )
219#define print_filename (G.print_filename )
220#define open_errors (G.open_errors )
221#define did_print_line (G.did_print_line )
222#define lines_before (G.lines_before )
223#define lines_after (G.lines_after )
224#define before_buf (G.before_buf )
225#define before_buf_size (G.before_buf_size )
226#define last_line_printed (G.last_line_printed )
227#define pattern_head (G.pattern_head )
228#define cur_file (G.cur_file )
229
230
231typedef struct grep_list_data_t {
232 char *pattern;
233
234#if !ENABLE_EXTRA_COMPAT
235 regex_t compiled_regex;
236 regmatch_t matched_range;
237#else
238 struct re_pattern_buffer compiled_regex;
239 struct re_registers matched_range;
240#endif
241#define ALLOCATED 1
242#define COMPILED 2
243 int flg_mem_allocated_compiled;
244} grep_list_data_t;
245
246#if !ENABLE_EXTRA_COMPAT
247#define print_line(line, line_len, linenum, decoration) \
248 print_line(line, linenum, decoration)
249#endif
250static void print_line(const char *line, size_t line_len, int linenum, char decoration)
251{
252#if ENABLE_FEATURE_GREP_CONTEXT
253
254
255 if (linenum < 1)
256 return;
257
258 if ((lines_before || lines_after) && did_print_line
259 && last_line_printed != linenum - 1
260 ) {
261 puts("--");
262 }
263
264 did_print_line = 1;
265 last_line_printed = linenum;
266#endif
267 if (print_filename)
268 printf("%s%c", cur_file, decoration);
269 if (PRINT_LINE_NUM)
270 printf("%i%c", linenum, decoration);
271
272 if ((option_mask32 & (OPT_v|OPT_o)) != (OPT_v|OPT_o)) {
273#if !ENABLE_EXTRA_COMPAT
274 puts(line);
275#else
276 fwrite(line, 1, line_len, stdout);
277 putchar(NUL_DELIMITED ? '\0' : '\n');
278#endif
279 }
280}
281
282#if ENABLE_EXTRA_COMPAT
283
284static ssize_t FAST_FUNC bb_getline(char **line_ptr, size_t *line_alloc_len, FILE *file)
285{
286 ssize_t res_sz;
287 char *line;
288 int delim = (NUL_DELIMITED ? '\0' : '\n');
289
290 res_sz = getdelim(line_ptr, line_alloc_len, delim, file);
291 line = *line_ptr;
292
293 if (res_sz > 0) {
294 if (line[res_sz - 1] == delim)
295 line[--res_sz] = '\0';
296 } else {
297 free(line);
298 }
299 return res_sz;
300}
301#endif
302
303static int grep_file(FILE *file)
304{
305 smalluint found;
306 int linenum = 0;
307 int nmatches = 0;
308#if !ENABLE_EXTRA_COMPAT
309 char *line;
310#else
311 char *line = NULL;
312 ssize_t line_len;
313 size_t line_alloc_len;
314# define rm_so start[0]
315# define rm_eo end[0]
316#endif
317#if ENABLE_FEATURE_GREP_CONTEXT
318 int print_n_lines_after = 0;
319 int curpos = 0;
320 int idx = 0;
321#else
322 enum { print_n_lines_after = 0 };
323#endif
324
325 while (
326#if !ENABLE_EXTRA_COMPAT
327 (line = xmalloc_fgetline(file)) != NULL
328#else
329 (line_len = bb_getline(&line, &line_alloc_len, file)) >= 0
330#endif
331 ) {
332 llist_t *pattern_ptr = pattern_head;
333 grep_list_data_t *gl = gl;
334
335 linenum++;
336 found = 0;
337 while (pattern_ptr) {
338 gl = (grep_list_data_t *)pattern_ptr->data;
339 if (FGREP_FLAG) {
340 char *match;
341 char *str = line;
342 opt_f_again:
343 match = ((option_mask32 & OPT_i)
344 ? strcasestr(str, gl->pattern)
345 : strstr(str, gl->pattern)
346 );
347 if (match) {
348 if (option_mask32 & OPT_x) {
349 if (match != str)
350 goto opt_f_not_found;
351 if (str[strlen(gl->pattern)] != '\0')
352 goto opt_f_not_found;
353 } else
354 if (option_mask32 & OPT_w) {
355 char c = (match != line) ? match[-1] : ' ';
356 if (!isalnum(c) && c != '_') {
357 c = match[strlen(gl->pattern)];
358 if (!c || (!isalnum(c) && c != '_'))
359 goto opt_f_found;
360 }
361 str = match + 1;
362 goto opt_f_again;
363 }
364 opt_f_found:
365 found = 1;
366 opt_f_not_found: ;
367 }
368 } else {
369#if ENABLE_EXTRA_COMPAT
370 unsigned start_pos;
371#else
372 int match_flg;
373#endif
374 char *match_at;
375
376 if (!(gl->flg_mem_allocated_compiled & COMPILED)) {
377 gl->flg_mem_allocated_compiled |= COMPILED;
378#if !ENABLE_EXTRA_COMPAT
379 xregcomp(&gl->compiled_regex, gl->pattern, reflags);
380#else
381 memset(&gl->compiled_regex, 0, sizeof(gl->compiled_regex));
382 gl->compiled_regex.translate = case_fold;
383 if (re_compile_pattern(gl->pattern, strlen(gl->pattern), &gl->compiled_regex))
384 bb_error_msg_and_die("bad regex '%s'", gl->pattern);
385#endif
386 }
387#if !ENABLE_EXTRA_COMPAT
388 gl->matched_range.rm_so = 0;
389 gl->matched_range.rm_eo = 0;
390 match_flg = 0;
391#else
392 start_pos = 0;
393#endif
394 match_at = line;
395 opt_w_again:
396
397 if (
398#if !ENABLE_EXTRA_COMPAT
399 regexec(&gl->compiled_regex, match_at, 1, &gl->matched_range, match_flg) == 0
400#else
401 re_search(&gl->compiled_regex, match_at, line_len,
402 start_pos, line_len,
403 &gl->matched_range) >= 0
404#endif
405 ) {
406 if (option_mask32 & OPT_x) {
407 found = (gl->matched_range.rm_so == 0
408 && match_at[gl->matched_range.rm_eo] == '\0');
409 } else
410 if (!(option_mask32 & OPT_w)) {
411 found = 1;
412 } else {
413 char c = ' ';
414 if (match_at > line || gl->matched_range.rm_so != 0) {
415 c = match_at[gl->matched_range.rm_so - 1];
416 }
417 if (!isalnum(c) && c != '_') {
418 c = match_at[gl->matched_range.rm_eo];
419 }
420 if (!isalnum(c) && c != '_') {
421 found = 1;
422 } else {
423
424
425
426
427
428
429
430#if !ENABLE_EXTRA_COMPAT
431 if (gl->matched_range.rm_eo != 0) {
432 match_at += gl->matched_range.rm_eo;
433 match_flg |= REG_NOTBOL;
434 goto opt_w_again;
435 }
436#else
437 if (gl->matched_range.rm_eo > start_pos) {
438 start_pos = gl->matched_range.rm_eo;
439 goto opt_w_again;
440 }
441#endif
442 }
443 }
444 }
445 }
446
447
448 if (found && !invert_search)
449 goto do_found;
450 pattern_ptr = pattern_ptr->link;
451 }
452
453 if (found ^ invert_search) {
454 do_found:
455
456 nmatches++;
457
458
459 if (option_mask32 & (OPT_q|OPT_l|OPT_L)) {
460 free(line);
461 if (BE_QUIET) {
462
463
464
465
466 exit(EXIT_SUCCESS);
467 }
468
469 if (PRINT_FILES_WITH_MATCHES) {
470 puts(cur_file);
471
472 }
473
474 return 1;
475 }
476
477#if ENABLE_FEATURE_GREP_CONTEXT
478
479 if ((option_mask32 & OPT_m) && nmatches > max_matches)
480 break;
481#endif
482
483
484 if (PRINT_MATCH_COUNTS == 0) {
485#if ENABLE_FEATURE_GREP_CONTEXT
486 int prevpos = (curpos == 0) ? lines_before - 1 : curpos - 1;
487
488
489
490 if (lines_before && before_buf[prevpos] != NULL) {
491 int first_buf_entry_line_num = linenum - lines_before;
492
493
494
495
496 idx = curpos;
497 while (before_buf[idx] == NULL) {
498 idx = (idx + 1) % lines_before;
499 first_buf_entry_line_num++;
500 }
501
502
503 while (before_buf[idx] != NULL) {
504 print_line(before_buf[idx], before_buf_size[idx], first_buf_entry_line_num, '-');
505 free(before_buf[idx]);
506 before_buf[idx] = NULL;
507 idx = (idx + 1) % lines_before;
508 first_buf_entry_line_num++;
509 }
510 }
511
512
513 print_n_lines_after = lines_after;
514#endif
515 if (option_mask32 & OPT_o) {
516 if (FGREP_FLAG) {
517
518
519 if (found)
520 print_line(gl->pattern, strlen(gl->pattern), linenum, ':');
521 } else while (1) {
522 unsigned start = gl->matched_range.rm_so;
523 unsigned end = gl->matched_range.rm_eo;
524 unsigned len = end - start;
525 char old = line[end];
526 line[end] = '\0';
527
528 if (len != 0)
529 print_line(line + start, len, linenum, ':');
530 if (old == '\0')
531 break;
532 line[end] = old;
533 if (len == 0)
534 end++;
535#if !ENABLE_EXTRA_COMPAT
536 if (regexec(&gl->compiled_regex, line + end,
537 1, &gl->matched_range, REG_NOTBOL) != 0)
538 break;
539 gl->matched_range.rm_so += end;
540 gl->matched_range.rm_eo += end;
541#else
542 if (re_search(&gl->compiled_regex, line, line_len,
543 end, line_len - end,
544 &gl->matched_range) < 0)
545 break;
546#endif
547 }
548 } else {
549 print_line(line, line_len, linenum, ':');
550 }
551 }
552 }
553#if ENABLE_FEATURE_GREP_CONTEXT
554 else {
555
556 if (print_n_lines_after) {
557 print_line(line, strlen(line), linenum, '-');
558 print_n_lines_after--;
559 } else if (lines_before) {
560
561 free(before_buf[curpos]);
562 before_buf[curpos] = line;
563 IF_EXTRA_COMPAT(before_buf_size[curpos] = line_len;)
564 curpos = (curpos + 1) % lines_before;
565
566 line = NULL;
567 }
568 }
569
570#endif
571#if !ENABLE_EXTRA_COMPAT
572 free(line);
573#endif
574
575 if ((option_mask32 & OPT_m)
576 && !print_n_lines_after
577 && nmatches == max_matches
578 ) {
579 break;
580 }
581 }
582
583
584
585
586
587 if (PRINT_MATCH_COUNTS) {
588 if (print_filename)
589 printf("%s:", cur_file);
590 printf("%d\n", nmatches);
591 }
592
593
594 if (PRINT_FILES_WITHOUT_MATCHES) {
595
596
597
598 puts(cur_file);
599 }
600
601 return nmatches;
602}
603
604#if ENABLE_FEATURE_CLEAN_UP
605#define new_grep_list_data(p, m) add_grep_list_data(p, m)
606static char *add_grep_list_data(char *pattern, int flg_used_mem)
607#else
608#define new_grep_list_data(p, m) add_grep_list_data(p)
609static char *add_grep_list_data(char *pattern)
610#endif
611{
612 grep_list_data_t *gl = xzalloc(sizeof(*gl));
613 gl->pattern = pattern;
614#if ENABLE_FEATURE_CLEAN_UP
615 gl->flg_mem_allocated_compiled = flg_used_mem;
616#else
617
618#endif
619 return (char *)gl;
620}
621
622static void load_regexes_from_file(llist_t *fopt)
623{
624 while (fopt) {
625 char *line;
626 FILE *fp;
627 llist_t *cur = fopt;
628 char *ffile = cur->data;
629
630 fopt = cur->link;
631 free(cur);
632 fp = xfopen_stdin(ffile);
633 while ((line = xmalloc_fgetline(fp)) != NULL) {
634 llist_add_to(&pattern_head,
635 new_grep_list_data(line, ALLOCATED));
636 }
637 fclose_if_not_stdin(fp);
638 }
639}
640
641static int FAST_FUNC file_action_grep(const char *filename,
642 struct stat *statbuf,
643 void* matched,
644 int depth UNUSED_PARAM)
645{
646 FILE *file;
647
648
649
650
651
652 if (S_ISLNK(statbuf->st_mode)) {
653 struct stat sb;
654 if (stat(filename, &sb) != 0) {
655 if (!SUPPRESS_ERR_MSGS)
656 bb_simple_perror_msg(filename);
657 return 0;
658 }
659 if (S_ISDIR(sb.st_mode))
660 return 1;
661 }
662
663 file = fopen_for_read(filename);
664 if (file == NULL) {
665 if (!SUPPRESS_ERR_MSGS)
666 bb_simple_perror_msg(filename);
667 open_errors = 1;
668 return 0;
669 }
670 cur_file = filename;
671 *(int*)matched += grep_file(file);
672 fclose(file);
673 return 1;
674}
675
676static int grep_dir(const char *dir)
677{
678 int matched = 0;
679 recursive_action(dir,
680 ACTION_RECURSE |
681 ACTION_FOLLOWLINKS_L0 |
682 ACTION_DEPTHFIRST,
683 file_action_grep,
684 NULL,
685 &matched,
686 0);
687 return matched;
688}
689
690int grep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
691int grep_main(int argc UNUSED_PARAM, char **argv)
692{
693 FILE *file;
694 int matched;
695 llist_t *fopt = NULL;
696#if ENABLE_FEATURE_GREP_CONTEXT
697 int Copt, opts;
698#endif
699 INIT_G();
700
701
702 xfunc_error_retval = 2;
703
704
705#if ENABLE_FEATURE_GREP_CONTEXT
706
707 opts = getopt32long(argv, "^"
708 OPTSTR_GREP
709 "\0"
710 "H-h:C-AB",
711 "color\0" Optional_argument "\xff",
712 &pattern_head, &fopt, &max_matches,
713 &lines_after, &lines_before, &Copt
714 , NULL
715 );
716
717 if (opts & OPT_C) {
718
719
720 if (!(opts & OPT_A))
721 lines_after = Copt;
722 if (!(opts & OPT_B))
723 lines_before = Copt;
724 }
725
726 if (opts & (OPT_c|OPT_q|OPT_l|OPT_L)) {
727 option_mask32 &= ~OPT_n;
728 lines_before = 0;
729 lines_after = 0;
730 } else if (lines_before > 0) {
731 if (lines_before > INT_MAX / sizeof(long long))
732 lines_before = INT_MAX / sizeof(long long);
733
734 before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
735 IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
736 }
737#else
738
739 getopt32(argv, "^" OPTSTR_GREP "\0" "H-h:c-n:q-n:l-n:",
740 &pattern_head, &fopt, &max_matches);
741#endif
742 invert_search = ((option_mask32 & OPT_v) != 0);
743
744 {
745 llist_t *cur;
746 for (cur = pattern_head; cur; cur = cur->link)
747 cur->data = new_grep_list_data(cur->data, 0);
748 }
749 if (option_mask32 & OPT_f) {
750 load_regexes_from_file(fopt);
751 if (!pattern_head) {
752
753 llist_add_to(&pattern_head, new_grep_list_data((char*) "", 0));
754 invert_search ^= 1;
755 }
756 }
757
758 if (ENABLE_FGREP && applet_name[0] == 'f')
759 option_mask32 |= OPT_F;
760
761#if !ENABLE_EXTRA_COMPAT
762 if (!(option_mask32 & (OPT_o | OPT_w | OPT_x)))
763 reflags = REG_NOSUB;
764#endif
765
766 if ((ENABLE_EGREP && applet_name[0] == 'e')
767 || (option_mask32 & OPT_E)
768 ) {
769 reflags |= REG_EXTENDED;
770 }
771#if ENABLE_EXTRA_COMPAT
772 else {
773 reflags = RE_SYNTAX_GREP;
774 }
775#endif
776
777 if (option_mask32 & OPT_i) {
778#if !ENABLE_EXTRA_COMPAT
779 reflags |= REG_ICASE;
780#else
781 int i;
782 case_fold = xmalloc(256);
783 for (i = 0; i < 256; i++)
784 case_fold[i] = (unsigned char)i;
785 for (i = 'a'; i <= 'z'; i++)
786 case_fold[i] = (unsigned char)(i - ('a' - 'A'));
787#endif
788 }
789
790 argv += optind;
791
792
793
794 if (pattern_head == NULL) {
795 char *pattern;
796 if (*argv == NULL)
797 bb_show_usage();
798 pattern = new_grep_list_data(*argv++, 0);
799 llist_add_to(&pattern_head, pattern);
800 }
801
802
803
804 if (argv[0] && argv[1])
805 print_filename = 1;
806
807 if (option_mask32 & OPT_H)
808 print_filename = 1;
809 if (option_mask32 & OPT_h)
810 print_filename = 0;
811
812
813
814 matched = 0;
815 do {
816 cur_file = *argv;
817 file = stdin;
818 if (!cur_file || LONE_DASH(cur_file)) {
819 cur_file = "(standard input)";
820 } else {
821 if (option_mask32 & OPT_r) {
822 struct stat st;
823 if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
824 if (!(option_mask32 & OPT_h))
825 print_filename = 1;
826 matched += grep_dir(cur_file);
827 goto grep_done;
828 }
829 }
830
831 file = fopen_for_read(cur_file);
832 if (file == NULL) {
833 if (!SUPPRESS_ERR_MSGS)
834 bb_simple_perror_msg(cur_file);
835 open_errors = 1;
836 continue;
837 }
838 }
839 matched += grep_file(file);
840 fclose_if_not_stdin(file);
841 grep_done: ;
842 } while (*argv && *++argv);
843
844
845 if (ENABLE_FEATURE_CLEAN_UP) {
846 while (pattern_head) {
847 llist_t *pattern_head_ptr = pattern_head;
848 grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
849
850 pattern_head = pattern_head->link;
851 if (gl->flg_mem_allocated_compiled & ALLOCATED)
852 free(gl->pattern);
853 if (gl->flg_mem_allocated_compiled & COMPILED)
854 regfree(&gl->compiled_regex);
855 free(gl);
856 free(pattern_head_ptr);
857 }
858 }
859
860 if (open_errors)
861 return 2;
862 return !matched;
863}
864