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