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 = getopt32(argv,
708 "^" OPTSTR_GREP "\0" "H-h:C-AB",
709 &pattern_head, &fopt, &max_matches,
710 &lines_after, &lines_before, &Copt);
711
712 if (opts & OPT_C) {
713
714
715 if (!(opts & OPT_A))
716 lines_after = Copt;
717 if (!(opts & OPT_B))
718 lines_before = Copt;
719 }
720
721 if (opts & (OPT_c|OPT_q|OPT_l|OPT_L)) {
722 option_mask32 &= ~OPT_n;
723 lines_before = 0;
724 lines_after = 0;
725 } else if (lines_before > 0) {
726 if (lines_before > INT_MAX / sizeof(long long))
727 lines_before = INT_MAX / sizeof(long long);
728
729 before_buf = xzalloc(lines_before * sizeof(before_buf[0]));
730 IF_EXTRA_COMPAT(before_buf_size = xzalloc(lines_before * sizeof(before_buf_size[0]));)
731 }
732#else
733
734 getopt32(argv, "^" OPTSTR_GREP "\0" "H-h:c-n:q-n:l-n:",
735 &pattern_head, &fopt, &max_matches);
736#endif
737 invert_search = ((option_mask32 & OPT_v) != 0);
738
739 {
740 llist_t *cur;
741 for (cur = pattern_head; cur; cur = cur->link)
742 cur->data = new_grep_list_data(cur->data, 0);
743 }
744 if (option_mask32 & OPT_f) {
745 load_regexes_from_file(fopt);
746 if (!pattern_head) {
747
748 llist_add_to(&pattern_head, new_grep_list_data((char*) "", 0));
749 invert_search ^= 1;
750 }
751 }
752
753 if (ENABLE_FGREP && applet_name[0] == 'f')
754 option_mask32 |= OPT_F;
755
756#if !ENABLE_EXTRA_COMPAT
757 if (!(option_mask32 & (OPT_o | OPT_w | OPT_x)))
758 reflags = REG_NOSUB;
759#endif
760
761 if ((ENABLE_EGREP && applet_name[0] == 'e')
762 || (option_mask32 & OPT_E)
763 ) {
764 reflags |= REG_EXTENDED;
765 }
766#if ENABLE_EXTRA_COMPAT
767 else {
768 reflags = RE_SYNTAX_GREP;
769 }
770#endif
771
772 if (option_mask32 & OPT_i) {
773#if !ENABLE_EXTRA_COMPAT
774 reflags |= REG_ICASE;
775#else
776 int i;
777 case_fold = xmalloc(256);
778 for (i = 0; i < 256; i++)
779 case_fold[i] = (unsigned char)i;
780 for (i = 'a'; i <= 'z'; i++)
781 case_fold[i] = (unsigned char)(i - ('a' - 'A'));
782#endif
783 }
784
785 argv += optind;
786
787
788
789 if (pattern_head == NULL) {
790 char *pattern;
791 if (*argv == NULL)
792 bb_show_usage();
793 pattern = new_grep_list_data(*argv++, 0);
794 llist_add_to(&pattern_head, pattern);
795 }
796
797
798
799 if (argv[0] && argv[1])
800 print_filename = 1;
801
802 if (option_mask32 & OPT_H)
803 print_filename = 1;
804 if (option_mask32 & OPT_h)
805 print_filename = 0;
806
807
808
809 matched = 0;
810 do {
811 cur_file = *argv;
812 file = stdin;
813 if (!cur_file || LONE_DASH(cur_file)) {
814 cur_file = "(standard input)";
815 } else {
816 if (option_mask32 & OPT_r) {
817 struct stat st;
818 if (stat(cur_file, &st) == 0 && S_ISDIR(st.st_mode)) {
819 if (!(option_mask32 & OPT_h))
820 print_filename = 1;
821 matched += grep_dir(cur_file);
822 goto grep_done;
823 }
824 }
825
826 file = fopen_for_read(cur_file);
827 if (file == NULL) {
828 if (!SUPPRESS_ERR_MSGS)
829 bb_simple_perror_msg(cur_file);
830 open_errors = 1;
831 continue;
832 }
833 }
834 matched += grep_file(file);
835 fclose_if_not_stdin(file);
836 grep_done: ;
837 } while (*argv && *++argv);
838
839
840 if (ENABLE_FEATURE_CLEAN_UP) {
841 while (pattern_head) {
842 llist_t *pattern_head_ptr = pattern_head;
843 grep_list_data_t *gl = (grep_list_data_t *)pattern_head_ptr->data;
844
845 pattern_head = pattern_head->link;
846 if (gl->flg_mem_allocated_compiled & ALLOCATED)
847 free(gl->pattern);
848 if (gl->flg_mem_allocated_compiled & COMPILED)
849 regfree(&gl->compiled_regex);
850 free(gl);
851 free(pattern_head_ptr);
852 }
853 }
854
855 if (open_errors)
856 return 2;
857 return !matched;
858}
859