File Coverage

/usr/local/bin/greple
Criterion Covered Total %
statement 395 708 55.7
branch 110 400 27.5
condition 36 152 23.6
subroutine 52 91 57.1
pod n/a
total 593 1351 43.8


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2             ##
3             ## greple: extensible grep with lexical expression and region handling
4             ##
5             ## Since Mar 29 1991
6             ##
7              
8 30     30   158192 use v5.24;
  30         99  
9 30     30   134 use warnings;
  30         39  
  30         1616  
10              
11 30     30   14325 use File::stat;
  30         301553  
  30         1993  
12 30     30   18274 use IO::Handle;
  30         202337  
  30         1817  
13 30     30   17378 use Pod::Usage;
  30         2288026  
  30         4717  
14 30     30   16583 use Text::ParseWords qw(shellwords);
  30         58701  
  30         2620  
15 30     30   264 use List::Util qw(min max first sum uniq shuffle notall pairs pairmap);
  30         49  
  30         4824  
16 30     30   17913 use Hash::Util qw(lock_keys);
  30         115043  
  30         251  
17 30     30   3425 use Cwd qw(getcwd abs_path);
  30         67  
  30         1652  
18 30     30   189 use Carp;
  30         49  
  30         1620  
19              
20 30     30   15781 use utf8;
  30         8649  
  30         207  
21 30     30   1163 use Encode;
  30         51  
  30         2677  
22 30     30   15325 use Encode::Guess;
  30         143998  
  30         129  
23 30     30   17558 use open IO => ':utf8', ':std';
  30         43099  
  30         201  
24              
25 30     30   22370 use Data::Dumper;
  30         256593  
  30         3074  
26             {
27 30     30   6820797 no warnings 'redefine';
  30         321  
  30         61  
  30         8537  
28 30     0   417 *Data::Dumper::qquote = sub { qq["${\(shift)}"] };
  0         0  
  0         0  
29 30         120 $Data::Dumper::Useperl = 1;
30             }
31              
32             ##
33             ## Setup greple/lib to be a module directory if exists.
34             ##
35             BEGIN {
36 30 50   30   1948 if (my $lib = abs_path($0) =~ s{/(?:script/|bin/)?\w+$}{/lib}r) {
37 30 50       1281 unshift @INC, $lib if -d "$lib/App/Greple";
38             }
39             }
40              
41 30     30   17420 use Getopt::EX::Loader;
  30         2817822  
  30         2733  
42 30     30   431 use Getopt::EX::Func qw(parse_func callable);
  30         63  
  30         1796  
43              
44 30     30   15527 use App::Greple;
  30         4905  
  30         1207  
45 30     30   15438 use App::Greple::Common;
  30         14729  
  30         2035  
46 30     30   14518 use App::Greple::Util qw(shellquote);
  30         41061  
  30         2159  
47 30     30   16430 use App::Greple::Grep;
  30         499520  
  30         3299  
48 30     30   269 use App::Greple::Regions;
  30         58  
  30         2805  
49 30     30   195 use App::Greple::Pattern;
  30         56  
  30         2223  
50 30     30   18466 use App::Greple::Pattern::Holder;
  30         138515  
  30         1994  
51 30     30   15647 use App::Greple::Filter;
  30         46279  
  30         194537  
52              
53 30         104 my $version = $App::Greple::VERSION;
54              
55             =encoding utf8
56              
57             =head1 NAME
58              
59              
60             greple - extensible grep with lexical expression and region control
61              
62              
63             =head1 VERSION
64              
65              
66             Version 10.04
67              
68              
69             =head1 SYNOPSIS
70              
71              
72             B [B<-M>I] [ B<-options> ] pattern [ file... ]
73              
74             PATTERN
75             pattern 'and +must -not ?optional &function'
76             -x, --le pattern lexical expression (same as bare pattern)
77             -e, --and pattern pattern match across line boundary
78             -r, --must pattern pattern cannot be compromised
79             -t, --may pattern pattern may exist
80             -v, --not pattern pattern not to be matched
81             -E, --re pattern regular expression
82             --fe pattern fixed expression
83             -f, --file file file contains search pattern
84             --select index select indexed pattern from -f file
85             MATCH
86             -i, --ignore-case ignore case
87             -G, --capture-group match capture groups rather than the whole pattern
88             -S, --stretch stretch the matched area to the enclosing block
89             --need=[+-]n required positive match count
90             --allow=[+-]n acceptable negative match count
91             --matchcount=n[,m] required match count for each block
92             STYLE
93             -l list filename only
94             -c print count of matched block only
95             -n print line number
96             -b print block number
97             -H, -h do or do not display filenames
98             -o print only the matching part
99             --all print entire data
100             -F, --filter use as a filter (implies --all --need=0 --exit=0)
101             -m, --max=n[,m] max count of blocks to be shown
102             -A,-B,-C [n] after/before/both match context
103             --join remove newline in the matched part
104             --joinby=string replace newline in the matched text with a string
105             --nonewline do not add newline character at the end of block
106             --filestyle=style how filenames are printed (once, separate, line)
107             --linestyle=style how line numbers are printed (separate, line)
108             --blockstyle=style how block numbers are printed (separate, line)
109             --separate set filestyle, linestyle, blockstyle "separate"
110             --format LABEL=... define the format for line number and file name
111             --frame-top top frame line
112             --frame-middle middle frame line
113             --frame-bottom bottom frame line
114             FILE
115             --glob=glob glob target files
116             --chdir=dir change directory before search
117             --readlist get filenames from stdin
118             COLOR
119             --color=when use terminal colors (auto, always, never)
120             --nocolor same as --color=never
121             --colormap=color R, G, B, C, M, Y, etc.
122             --colorsub=... shortcut for --colormap="sub{...}"
123             --colorful use default multiple colors
124             --colorindex=flags color index method: Ascend/Descend/Block/Random/Unique/Group/GP
125             --random use a random color each time (--colorindex=R)
126             --uniqcolor use a different color for each unique string (--colorindex=U)
127             --uniqsub=func preprocess function to check uniqueness
128             --ansicolor=s ANSI color 16, 256 or 24bit
129             --[no]256 same as --ansicolor 256 or 16
130             --regioncolor use different color for inside and outside regions
131             --face enable or disable visual effects
132             BLOCK
133             -p, --paragraph enable paragraph mode
134             --border=pattern specify a border pattern
135             --block=pattern specify a block of records
136             --blockend=s block-end mark (Default: "--")
137             --join-blocks join consecutive blocks that are back-to-back
138             REGION
139             --inside=pattern select matches inside of pattern
140             --outside=pattern select matches outside of pattern
141             --include=pattern limit matches to the area
142             --exclude=pattern limit matches to outside of the area
143             --strict enable strict mode for --inside/outside --block
144             CHARACTER CODE
145             --icode=name input file encoding
146             --ocode=name output file encoding
147             FILTER
148             --if,--of=filter input/output filter command
149             --pf=filter post-process filter command
150             --noif disable the default input filter
151             RUNTIME FUNCTION
152             --begin=func call a function before starting the search
153             --end=func call a function after completing the search
154             --prologue=func call a function before executing the command
155             --epilogue=func call a function after executing the command
156             --postgrep=func call a function after each grep operation
157             --callback=func callback function for each matched string
158             OTHER
159             --usage[=expand] show this help message
160             --version show version
161             --exit=n set the command exit status
162             --norc skip reading startup file
163             --man display the manual page for the command or module
164             --show display the module file contents
165             --path display the path to the module file
166             --error=action action to take after a read error occurs
167             --warn=type runtime error handling type
168             --alert [name=#] set alert parameters (size/time)
169             -d flags display info (f:file d:dir c:color m:misc s:stat)
170              
171             =cut
172              
173 30         137 my @baseclass = qw( App::Greple Getopt::EX );
174 30         492 my $rcloader = Getopt::EX::Loader
175             ->new(BASECLASS => \@baseclass);
176              
177 30         2420 my @optargs;
178             my %optargs;
179              
180             sub newopt {
181             push @optargs, pairmap {
182 2730     2730   3807 local $_ = $a;
183 2730         10462 s/\s+//g;
184 2730         6156 s/^(?=\w+-)([-\w]+)/$1 =~ tr[-][_]r . "|$1"/e; # "a-b" -> "a_b|a-b"
  420         1578  
185 2730 100 50     11491 /^(\w+)/ and $optargs{$1} = $b if ref $b ne 'CODE';
186 2730         5835 $_ => $b;
187 30     30   526 } @_;
188             }
189              
190             sub opt :lvalue {
191 0     0   0 my $name = shift;
192 0 0       0 my $var = $optargs{$name} or die "$name: invalid option name\n";
193 0 0 0     0 if (ref $var eq 'SCALAR') {
    0          
194 0         0 return $$var;
195             } elsif (ref $var eq 'HASH' and @_ == 1) {
196 0         0 return $var->{+shift};
197             } else {
198 0         0 return $var;
199             }
200             }
201              
202 30         0 my @opt_pattern;
203             sub opt_pattern {
204 20     20   2778 push @opt_pattern, [ map "$_", @_ ];
205 20         246 $opt_pattern[-1];
206             }
207              
208 30         0 my @opt_colormap;
209 38     38   242373 sub opt_colormap { push @opt_colormap, $_[1] }
210 0     0   0 sub opt_colorsub { push @opt_colormap, "sub{ $_[1] }" }
211              
212 30         259 my %opt_format = (LINE => '%d:', FILE => '%s:', BLOCK => '%s:');
213 30         700 my %opt_alert = (size => 512 * 1024, time => 2);
214 30         228 my %opt_warn = (read => 0, skip => 1, retry => 0, begin => 0);
215              
216             newopt
217              
218             ##
219             ## PATTERN
220             ##
221             ' and |e =s ' => \&opt_pattern ,
222             ' must |r =s ' => \&opt_pattern ,
223             ' may |t =s ' => \&opt_pattern ,
224             ' not |v =s ' => \&opt_pattern ,
225             ' le |x =s ' => \&opt_pattern ,
226             ' re |E =s ' => \&opt_pattern ,
227             ' fe =s ' => \&opt_pattern ,
228             ' file |f =s ' => \ my @opt_f ,
229             ' select =s ' => \ my $opt_select ,
230              
231             ##
232             ## MATCH
233             ##
234             ' ignore-case |i ! ' => \ my $opt_i ,
235             ' need =s ' => \ my @opt_need ,
236             ' allow =s ' => \ my @opt_allow ,
237             ' matchcount |mc =s ' => \ my $opt_matchcount ,
238             ' capture-group |G ! ' => \ my $opt_capture_group ,
239             ' stretch |S ! ' => \ my $opt_stretch ,
240              
241             ##
242             ## STYLE
243             ##
244             ' files-with-matches |l ' => \ my $opt_l ,
245             ' count |c ' => \ my $opt_c ,
246             ' line-number |n ! ' => \ my $opt_n ,
247             ' block-number |b ! ' => \ my $opt_b ,
248             ' filename |H ' => \ my $opt_H ,
249             ' no-filename |h ' => \ my $opt_h ,
250             ' only-matching |o ! ' => \ my $opt_o ,
251             ' all ! ' => \ my $opt_all ,
252             ' filter |F ! ' => \ my $opt_filter ,
253             ' max-count |m =s ' => \ my $opt_m ,
254             ' after-context |A :2 ' => \(my $opt_A = 0) ,
255             ' before-context |B :2 ' => \(my $opt_B = 0) ,
256             ' context |C :2 ' => \(my $opt_C = 0) ,
257             ' join ! ' => \ my $opt_join ,
258             ' joinby =s ' => \(my $opt_joinby = "") ,
259             ' newline ! ' => \(my $opt_newline = 1) ,
260             ' filestyle |fs =s ' => \(my $opt_filestyle = 'line') ,
261             ' linestyle |ls =s ' => \(my $opt_linestyle = 'line') ,
262             ' blockstyle |bs =s ' => \(my $opt_blockstyle = 'line') ,
263             ' separate ' => sub {
264 0     0   0 opt('filestyle') = opt('linestyle') = opt('blockstyle') = $_[0];
265             },
266             ' format =s ' => \ %opt_format ,
267             ' frame-top :s ' => \(my $opt_frame_top = '') ,
268             ' frame-middle :s ' => \(my $opt_frame_middle = '') ,
269             ' frame-bottom :s ' => \(my $opt_frame_bottom = '') ,
270              
271             ##
272             ## FILE
273             ##
274             ' glob =s ' => \ my @opt_glob ,
275             ' chdir =s ' => \ my @opt_chdir ,
276             ' readlist ! ' => \ my $opt_readlist ,
277              
278             ##
279             ## COLOR
280             ##
281             ' color =s ' => \(my $opt_color = 'auto') ,
282             ' colormap |cm =s ' => \&opt_colormap ,
283             ' colorsub |cs =s ' => \&opt_colorsub ,
284             ' colorful ! ' => \(my $opt_colorful = 1) ,
285             ' colorindex |ci =s ' => \(my $opt_colorindex = '') ,
286             ' ansicolor =s ' => \(my $opt_ansicolor = '256') ,
287             ' regioncolor |rc ! ' => \ my $opt_regioncolor ,
288             ' uniqsub |us =s ' => \ my @opt_uniqsub ,
289             ' face =s ' => \ my @opt_face ,
290             ' nocolor | no-color ' => sub {
291 0     0   0 opt('color') = 'never';
292             },
293             ' 256! ' => sub {
294 0 0   0   0 opt('ansicolor') = $_[1] ? '256' : '16';
295             },
296             ' random! ' => sub {
297 0 0   0   0 if ($_[1]) { opt('colorindex') .= 'R' }
  0         0  
298 0         0 else { opt('colorindex') =~ s/R//gi }
299             },
300             ' uniqcolor |uc ' => sub {
301 0     0   0 opt('colorindex') = 'U';
302             },
303              
304             ##
305             ## BLOCK
306             ##
307             ' paragraph |p ! ' => \ my $opt_p ,
308             ' border =s ' => \ my $opt_border ,
309             ' block =s ' => \ my @opt_block ,
310             ' blockend :s ' => \(my $opt_blockend) ,
311             ' join-blocks ! ' => \(my $opt_join_blocks = 0) ,
312              
313             ##
314             ## REGION
315             ##
316             ' inside =s ' => \ my @opt_inside ,
317             ' outside =s ' => \ my @opt_outside ,
318             ' include =s ' => \ my @opt_include ,
319             ' exclude =s ' => \ my @opt_exclude ,
320             ' strict ! ' => \(my $opt_strict = 0) ,
321              
322             ##
323             ## CHARACTER CODE
324             ##
325             ' icode =s ' => \ my @opt_icode ,
326             ' ocode =s ' => \ my $opt_ocode ,
327              
328             ##
329             ## FILTER
330             ##
331             ' if =s ' => \ my @opt_if ,
332             ' of =s ' => \ my @opt_of ,
333             ' pf =s ' => \ my @opt_pf ,
334             ' noif ' => \ my $opt_noif ,
335              
336             ##
337             ## RUNTIME FUNCTION
338             ##
339             ' print =s ' => \ my @opt_print ,
340             ' continue ! ' => \ my $opt_continue ,
341             ' callback =s ' => \ my @opt_callback ,
342             ' begin =s ' => \ my @opt_begin ,
343             ' end =s ' => \ my @opt_end ,
344             ' prologue =s ' => \ my @opt_prologue ,
345             ' epilogue =s ' => \ my @opt_epilogue ,
346             ' postgrep =s ' => \ my @opt_postgrep ,
347              
348             ##
349             ## OTHERS
350             ##
351             ' usage :s ' => \ my $opt_usage ,
352             ' version ' => \ my $opt_version ,
353             ' exit =i ' => \ my $opt_exit ,
354             # norc
355             ' man | doc ' => \ my $opt_man ,
356             ' show | less ' => \ my $opt_show ,
357             ' path ' => \ my $opt_path ,
358             ' error =s ' => \(my $opt_error = 'skip') ,
359             ' alert =i ' => \ %opt_alert ,
360             ' debug |d =s ' => \ my @opt_d ,
361             'warn|w:1%' => sub {
362 0         0 map { $opt_warn{$_} = $_[2] }
363 0 0   0   0 map { $_ eq 'all' ? keys %opt_warn : $_ }
  0         0  
364             $_[1] =~ /\w+/g;
365             },
366              
367             ##
368             ## MODULE
369             ##
370             'M:s' => sub {
371 0     0   0 warn "Use -M option at the beginning with module name.\n";
372 0 0       0 if (my @modules = uniq($rcloader->modules())) {
373 0         0 warn "Available modules:\n";
374 0         0 warn "\t", join("\n\t", @modules), "\n";
375             }
376 0         0 exit 2;
377             },
378              
379             ##
380             ## UNDOCUMENTED
381             ##
382 30         2080 ' clean ! ' => \ my $opt_clean ,
383              
384             ;
385              
386             sub setopt {
387 0 0   0   0 my $opt = ref $_[0] eq 'HASH' ? shift : {};
388 0         0 my $name = shift;
389 0 0       0 if (exists $optargs{$name}) {
390 0         0 my $ref = $optargs{$name};
391 0 0       0 if (ref $ref eq 'ARRAY') {
    0          
    0          
392 0 0       0 if ($opt->{append}) {
393 0         0 push @$ref, @_;
394             } else {
395 0         0 @$ref = @_;
396             }
397             }
398             elsif (ref $ref eq 'CODE') {
399 0         0 &$ref($name, @_);
400             }
401             elsif (ref $ref eq 'SCALAR') {
402 0         0 $$ref = shift;
403             }
404             else {
405 0         0 die "Object error.";
406             }
407             }
408             }
409              
410             ##
411             ## @ARGV stuff
412             ##
413              
414 30         28490 require Getopt::Long;
415 30         427513 my $parser = Getopt::Long::Parser->new(
416             config => [ qw(bundling no_getopt_compat no_ignore_case) ],
417             );
418 0     0   0 sub configure_getopt { $parser->configure(@_) }
419              
420 30 50       41111 configure_getopt qw(debug) if $ENV{DEBUG_GETOPT};
421 30 50       156 $Getopt::EX::Loader::debug = 1 if $ENV{DEBUG_GETOPTEX};
422              
423             ## decode
424             my @ORIG_ARGV =
425 30 50       104 @ARGV = map { utf8::is_utf8($_) ? $_ : decode('utf8', $_) } @ARGV;
  305         5837  
426              
427             ## ~/.greplerc
428 30 50 33     1016 unless ((@ARGV and $ARGV[0] eq "--norc" and shift)
      33        
      33        
429             or
430             ($ENV{GREPLE_NORC}) ) {
431 30         341 $rcloader->load(FILE => "$ENV{HOME}/.greplerc");
432             }
433              
434             ## modules
435 30         1086 $rcloader->deal_with(\@ARGV);
436              
437 30         319055 push @optargs, $rcloader->builtins;
438              
439             ## ENV
440 30 50       940 $ENV{'GREPLEOPTS'} and unshift @ARGV, shellwords($ENV{'GREPLEOPTS'});
441              
442              
443             ## GetOptions
444 30         268 my @SAVEDARGV = @ARGV;
445 30 50       374 $parser->getoptions(@optargs) || usage();
446              
447 30 50       142465 if ($opt_version) {
448 0         0 print "$version\n";
449 0         0 exit 0;
450             }
451              
452 30         109 our %opt_d;
453 30         108 @opt_d = map { split // } @opt_d;
  0         0  
454 30         99 @opt_d{@opt_d} = @opt_d;
455              
456 30 50       155 if ($opt_d{o}) {
457 0         0 warn "\@ARGV = ", join(' ', shellquote(@SAVEDARGV)), "\n";
458             }
459              
460             ## -m option
461 30 50       147 my @splicer = (not defined $opt_m) ? () : do {
462 0         0 my @param = split /,/, $opt_m, -1;
463 0 0       0 push @param, '' if @param % 2;
464 0 0   0   0 if (notall { /^(-?\d+)?$/ } @param) {
  0         0  
465 0         0 die "$opt_m: option format error.\n";
466             }
467             map {
468 0         0 my($offset, $length) = @$_;
  0         0  
469 0 0       0 if ($length ne '') {
470 0   0 0   0 sub { splice @{+shift}, $offset || 0, $length }
  0         0  
471 0         0 } else {
472 0   0 0   0 sub { splice @{+shift}, $offset || 0 }
  0         0  
473 0         0 }
474             }
475             pairs @param;
476             };
477              
478 30         58 my $file_code;
479 30         73 my $default_icode = 'utf8'; # default input encoding
480 30         105 my @default_icode_list = qw(euc-jp 7bit-jis);
481 30         56 my $output_code;
482 30         65 my $default_ocode = 'utf8'; # default output encoding
483              
484 30   33     180 $output_code = $opt_ocode || $default_ocode;
485 30     30   1977 binmode STDOUT, ":encoding($output_code)";
  30         33023  
  30         580  
  30         212  
486              
487             ## show unused option characters
488 30 50       36769 if ($opt_d{u}) {
489 0         0 my $s = join('','0'..'9',"\n",'a'..'z',"\n",'A'..'Z',"\n");
490 0 0       0 map { /\|([0-9a-zA-Z])\b/ && $s =~ s/$1/./ } @optargs;
  0         0  
491 0         0 die $s;
492             }
493              
494             ## show man pages
495 30 50 33     363 if ($opt_man or $opt_show or $opt_path) {
      33        
496             my @module = map {
497 0 0       0 /^-M(\w+(::\w++(?![=(]))*)/ ? "App::Greple::$1" : ()
  0         0  
498             } @ORIG_ARGV;
499 0 0       0 if (@module) {
500 0         0 my $module = $module[-1];
501 0     0   0 my $jp = first { -x "$_/perldocjp" } split /:/, $ENV{PATH};
  0         0  
502 0 0       0 my $perldoc = $jp ? "perldocjp" : "perldoc";
503 0         0 $ENV{PERL5LIB} = join ':', @INC;
504 0         0 my $file = $module =~ s[::][/]gr . '.pm';
505 0 0       0 die unless $INC{$file};
506 0 0       0 if ($opt_man) {
507 0 0       0 exec "$perldoc $module" or die $!;
508             } else {
509 0         0 chomp(my $file = `$perldoc -ml $module`);
510 0 0       0 if ($opt_path) {
511 0         0 say $file;
512             } else {
513 0   0     0 my $pager = $ENV{PAGER} || 'less';
514 0 0       0 exec "$pager $file" or die $!;
515             }
516             }
517 0         0 exit;
518             }
519 0         0 pod2usage({-verbose => 2});
520 0         0 die;
521             }
522              
523             sub default_module {
524 0     0   0 my $mod = shift;
525 0         0 my $module = $mod->module;
526 0 0       0 return 1 if $module =~ /\b \.greplerc $/x;
527 0 0       0 return 1 if $module =~ /\b default $/x;
528 0         0 return 0;
529             }
530              
531             ## setup file encoding
532 30 50       124 if (@opt_icode) {
533 0         0 @opt_icode = map { split /[,\s]+/ } @opt_icode;
  0         0  
534 0 0       0 if (grep { s/^\+// } @opt_icode) {
  0         0  
535 0         0 unshift @opt_icode, @default_icode_list;
536             }
537 0         0 @opt_icode = uniq @opt_icode;
538 0 0       0 if (@opt_icode > 1) {
    0          
539 0         0 @opt_icode = grep { !/(?:auto|guess)$/i } @opt_icode;
  0         0  
540 0         0 Encode::Guess->set_suspects(@opt_icode);
541 0         0 $file_code = 'Guess';
542             }
543             elsif ($opt_icode[0] =~ /^(?:guess|auto)$/i) {
544 0         0 Encode::Guess->set_suspects(@default_icode_list);
545 0         0 $file_code = 'Guess';
546             } else {
547 0         0 $file_code = $opt_icode[0];
548             }
549             }
550             else {
551 30         75 $file_code = $default_icode;
552             }
553              
554             ##
555             ## --filter
556             ##
557 30 50       129 if ($opt_filter) {
558 30         71 $opt_all = 1;
559 30         96 push @opt_need, '0';
560 30   50     165 $opt_exit //= 0;
561             }
562              
563             ##
564             ## Patterns
565             ##
566              
567 30         453 my $pat_holder = App::Greple::Pattern::Holder->new;
568              
569 30         276 my $FLAG_BASE = FLAG_NONE;
570 30 50       102 $FLAG_BASE |= FLAG_IGNORECASE if $opt_i;
571              
572 30 50       99 if (@opt_f) {
573 0         0 for my $opt_f (@opt_f) {
574 0 0       0 $pat_holder->append({ flag => $FLAG_BASE, type => 'file',
575             $opt_select ? (select => $opt_select) : (),
576             },
577             $opt_f);
578             }
579             } else {
580 30 50 33     142 unless ($opt_filter or grep { $_->[0] !~ /^(not|may)/ } @opt_pattern) {
  0         0  
581 0   0     0 unshift @opt_pattern, [ le => shift @ARGV // &usage ];
582             }
583             }
584              
585 30         344 my %pat_flag = (
586             must => FLAG_REGEX | FLAG_COOK | FLAG_REQUIRED,
587             not => FLAG_REGEX | FLAG_COOK | FLAG_NEGATIVE,
588             may => FLAG_REGEX | FLAG_COOK | FLAG_OPTIONAL,
589             le => FLAG_REGEX | FLAG_COOK | FLAG_LEXICAL,
590             and => FLAG_REGEX | FLAG_COOK,
591             re => FLAG_REGEX,
592             fe => FLAG_NONE,
593             );
594 30         96 for (@opt_pattern) {
595 20         2678 my($attr, @opt) = @$_;
596 20         58 my $flag = $FLAG_BASE | $pat_flag{$attr};
597 20         171 $pat_holder->append({ flag => $flag, type => 'pattern' }, @opt);
598             }
599             # $pat_holder->optimize;
600              
601             ##
602             ## if optional pattern exist, make all non-optional pattern as required
603             ##
604             {
605 30         1861 my @patterns = $pat_holder->patterns;
  30         212  
606 30         406 my @posi = grep { $_->is_positive } @patterns;
  20         172  
607 30         164 my @opti = grep { $_->is_optional } @posi;
  20         141  
608 30 50       256 if (@opti > 0) {
609 0         0 for my $p (grep { !$_->is_optional } @posi) {
  0         0  
610 0         0 $p->flag($p->flag | FLAG_REQUIRED);
611             }
612             }
613             }
614              
615             ##
616             ## set $count_must, $count_need and $opt_allow
617             ##
618 30         70 my $count_must = 0;
619 30         54 my $count_need;
620 30         67 my $count_allow = 0;
621             {
622 30         56 my $must = grep({ $_->is_required } $pat_holder->patterns);
  30         135  
  20         184  
623 30         291 my $posi = grep({ $_->is_positive } $pat_holder->patterns) - $must;
  20         174  
624 30         265 my $nega = grep({ $_->is_negative } $pat_holder->patterns);
  20         166  
625              
626 30   50     267 $count_must = $must // 0;
627 30 50       109 $count_need = $must ? 0 : $posi;
628 30         106 for (@opt_need) {
629 30 50       344 if (/^-(\d+)$/) { # --need -n
    50          
    50          
630 0         0 $count_need = $posi - $1;
631             }
632             elsif (/^\+(\d+)$/) { # --need +n
633 0         0 $count_need += $1;
634             }
635             elsif (/^(\d+)$/) { # --need n
636 30         133 $count_need = $1 - $must;
637             }
638             else {
639 0         0 die "$_ is not valid count.\n"
640             }
641             }
642              
643 30         59 $count_allow = 0;
644 30         88 for (@opt_allow) {
645 0 0       0 if (/^-(\d+)$/) { # --allow -n
    0          
    0          
646 0         0 $count_allow = $nega - $1;
647             }
648             elsif (/^\+(\d+)$/) { # --allow +n
649 0         0 $count_allow += $1;
650             }
651             elsif (/^(\d+)$/) { # --allow n
652 0         0 $count_allow = $1;
653             }
654             else {
655 0         0 die "$_ is not valid count.\n"
656             }
657             }
658             }
659              
660             ##
661             ## --matchcount
662             ##
663             my $count_match_sub = sub {
664 30 50   30   138 local $_ = shift or return;
665 0 0       0 /[^\d,]/ and die "$_ is not valid count.\n";
666 0 0       0 my @c = map { $_ eq '' ? 0 : int } split(/,/, $_, -1);
  0         0  
667 0 0       0 if (@c == 1) {
668 0         0 return sub { $_[0] == $c[0] };
  0         0  
669             }
670 0 0       0 push @c, -1 if @c % 2;
671             return sub {
672 0         0 my @n = @c;
673 0         0 while (my($min, $max) = splice(@n, 0, 2)) {
674 0 0       0 return 0 if $_[0] < $min;
675 0 0 0     0 return 1 if $max <= 0 || $_[0] <= $max;
676             }
677 0         0 return 0;
678             }
679 30         278 }->($opt_matchcount);
  0         0  
680              
681             ##
682             ## setup input/output filter
683             ##
684 30         533 my $filter_d = App::Greple::Filter->new->parse(@opt_if);
685 30 50       855 unless ($opt_noif) {
686             $filter_d->append(
687 30     30   887 [ sub { s/\.Z$// }, 'zcat' ],
688 30     30   385 [ sub { s/\.g?z$// }, 'gunzip -c' ],
689 30     30   380 [ sub { m/\.pdf$/i }, 'pdftotext -nopgbrk - -' ],
690 30     30   421 [ sub { s/\.gpg$// }, 'gpg --quiet --no-mdc-warning --decrypt' ],
  30         297  
691             );
692             }
693              
694             ##------------------------------------------------------------
695             ## miscellaneous setups
696             ##
697              
698 30         371 my @argv_files;
699             my $start_directory;
700 30   33     168 my $need_filename = ($opt_H or $opt_l);
701 30         76 my $current_file;
702              
703 30 50       145 if (@opt_chdir) {
    50          
704 0         0 $start_directory = getcwd;
705 0         0 @opt_chdir = uniq(map { glob $_ } @opt_chdir);
  0         0  
706 0         0 push @argv_files, splice(@ARGV);
707 0 0 0     0 unless ($opt_h or
      0        
      0        
708             (@opt_chdir == 1 and @argv_files == 1 and @opt_glob == 0)) {
709 0         0 $need_filename++;
710             }
711             }
712             elsif (@opt_glob) {
713 0         0 push @ARGV, map(glob, @opt_glob);
714             }
715              
716 30 0 33     174 push(@ARGV, '-') unless @ARGV || @argv_files || @opt_glob || $opt_readlist;
      33        
      0        
717 30 50 33     213 if ((@ARGV > 1 or $opt_readlist) and not $opt_h) {
      33        
718 0         0 $need_filename++;
719             }
720              
721 30 50       107 $opt_filestyle = 'none' if not $need_filename;
722              
723 30 50       103 $opt_join = 1 if $opt_joinby ne "";
724              
725             ##------------------------------------------------------------
726             ## colors
727             ##
728 30         377 our %colormap = (
729             FILE => "G",
730             LINE => "Y",
731             BLOCK => "B",
732             TEXT => "",
733             BLOCKEND => "/WE",
734             PROGRESS => "B",
735             TOP => "",
736             MIDDLE => "",
737             BOTTOM => "",
738             );
739              
740 30         57 our @colors;
741              
742 30     30   326 use Getopt::EX::Colormap;
  30         56  
  30         7562  
743 30         445 my $color_handler = Getopt::EX::Colormap
744             ->new(HASH => \%colormap, LIST => \@colors)
745             ->load_params(@opt_colormap);
746              
747 30 50       10866 my @default_color =
748             $opt_ansicolor eq '16'
749             ? qw(RD GD BD CD MD YD)
750             : qw(000D/544 000D/454 000D/445
751             000D/455 000D/545 000D/554
752             000D/543 000D/453 000D/435
753             000D/534 000D/354 000D/345
754             000D/444
755             000D/433 000D/343 000D/334
756             000D/344 000D/434 000D/443
757             000D/333)
758             ;
759              
760 30 100       306 if ($color_handler->list == 0) {
761 20 50       372 $color_handler->append
762             ($opt_colorful ? @default_color : $default_color[0]);
763             }
764              
765 30 50       2283 if ($opt_ansicolor eq '24bit') {
766 30     30   247 no warnings 'once';
  30         108  
  30         101631  
767 0         0 $Getopt::EX::Colormap::RGB24 = 1;
768             }
769              
770 30         92 for my $opt (@opt_face) {
771 0         0 while ($opt =~ /(?[-+=]) (?[^-+=]*) | (?[^-+=]+) /xg) {
772 0   0     0 my($mk, $s) = ($+{mk} // '', $+{s});
773 0         0 for my $c (@colors) {
774 0 0       0 if ($mk eq '-') {
    0          
    0          
775 0 0       0 $c =~ s/[\Q$s\E]//g if $s ne '';
776             } elsif ($mk eq '=') {
777 0         0 $c = $s;
778             } elsif ($s ne '') {
779 0 0       0 $c .= "^" if $c ne '';
780 0         0 $c .= $s;
781             }
782             }
783             }
784             }
785              
786 30   33     173 my $need_color = (($opt_color eq 'always')
787             or (($opt_color eq 'auto') and (!$opt_o and -t STDOUT)));
788              
789 30 50       122 if (!$need_color) {
790 0         0 $Getopt::EX::Colormap::NO_COLOR = 1;
791             }
792              
793 30         205 my %_esc = ( t => "\t", n => "\n", r => "\r", f => "\f" );
794             sub expand_escape {
795 90   33 90   409 $_[0] =~ s{\\(.)}{$_esc{$1} // $1}egr;
  54         378  
796             }
797              
798 30         196 $_ = expand_escape($_) for values %opt_format;
799              
800 30         68 my $blockend = "--";
801 30 50       115 if (defined $opt_blockend) {
802 0         0 $blockend = expand_escape($opt_blockend);
803             }
804              
805 30     0   174 my $_file = sub { $color_handler->color('FILE' , sprintf($opt_format{FILE}, $_[0])) };
  0         0  
806 30     0   118 my $_line = sub { $color_handler->color('LINE' , sprintf($opt_format{LINE}, $_[0])) };
  0         0  
807 30     0   139 my $_block = sub { $color_handler->color('BLOCK', sprintf($opt_format{BLOCK}, $_[0])) };
  0         0  
808 30     0   99 my $_text = sub { $color_handler->color('TEXT' , $_[0]) };
  0         0  
809 30         209 my $_blockend = $color_handler->color('BLOCKEND', $blockend);
810 30         17008 my $_top = $color_handler->color('TOP' , $opt_frame_top);
811 30         562 my $_middle = $color_handler->color('MIDDLE' , $opt_frame_middle);
812 30         438 my $_bottom = $color_handler->color('BOTTOM' , $opt_frame_bottom);
813              
814             sub index_color {
815 40     40   184 $color_handler->index_color(@_);
816             }
817              
818             sub color {
819 0     0   0 $color_handler->color(@_);
820             }
821              
822 30         599 my $uniq_color = UniqIndex->new(
823             ignore_newline => 1,
824             prepare => \@opt_uniqsub,
825             );
826              
827             sub dump_uniqcolor {
828 0     0   0 my $list = $uniq_color->list;
829 0         0 my $count = $uniq_color->count;
830 0         0 for my $i (keys @$list) {
831 0         0 warn sprintf("%3d (%3d) %s\n",
832             $i, $count->[$i],
833             index_color($i, $list->[$i]));
834             }
835             }
836              
837             # --colorindex
838 30         1184 my %color_index = map { uc $_ => 1 } $opt_colorindex =~ /\w/g;
  0         0  
839 30         69 my $indexer = do {
840 30 50       181 if ($color_index{S}) {
841 0         0 @colors = shuffle @colors;
842             }
843 30 50 33     489 if ($color_index{A} or $color_index{D}) {
    50          
844 0         0 my $i = 0;
845             Indexer->new(
846 0     0   0 index => sub { $i++ },
847 0     0   0 reset => sub { $i = 0 },
848             block => $color_index{B},
849             reverse => $color_index{D},
850 0         0 );
851             }
852             elsif ($color_index{R}) {
853 0     0   0 Indexer->new(index => sub { int rand @colors });
  0         0  
854             }
855 30         163 else { undef }
856             };
857 30         121 my $opt_uniqcolor = $color_index{U};
858              
859             # -dc
860 30 50       136 if ($opt_d{c}) {
861             my $dump = sub {
862 0     0   0 local $_ = Dumper shift;
863 0         0 s/^\s*'\K([^'\s]+)(?=')/color($1, $1)/mge;
  0         0  
864 0         0 $_;
865 0         0 };
866 0         0 warn 'colormap = ', $dump->(\%colormap);
867 0         0 warn 'colors = ', $dump->(\@colors);
868             }
869              
870             ##
871             ## border regex
872             ##
873 30         62 my $border_re = do {
874 30 50       135 if ($opt_border) {
    50          
875 0         0 qr/$opt_border/m; # custom
876             } elsif ($opt_p) {
877 0         0 qr/(?:\A|\R)\K\R+/; # paragraph
878             } else {
879 30         152 qr/^/m; # line
880             }
881             };
882              
883 30 50       109 if ($opt_C) {
884 0   0     0 $opt_A ||= $opt_C;
885 0   0     0 $opt_B ||= $opt_C;
886             }
887 30         423 my %stat = (
888             file_searched => 0,
889             file_tried => 0,
890             length => 0,
891             match_effective => 0,
892             match_positive => 0,
893             match_negative => 0,
894             match_block => 0,
895             read_retry => 0,
896             time_start => [],
897             time_end => [],
898             );
899 30         235 lock_keys %stat;
900              
901             ##
902             ## Setup functions
903             ##
904 30         965 for my $set (
905             [ "print" , \@opt_print , 0 ],
906             [ "begin" , \@opt_begin , 0 ],
907             [ "end" , \@opt_end , 0 ],
908             [ "prologue", \@opt_prologue, 0 ],
909             [ "epilogue", \@opt_epilogue, 0 ],
910             [ "callback", \@opt_callback, 0 ],
911             [ "uniqsub" , \@opt_uniqsub , 0 ],
912             [ "postgrep", \@opt_postgrep, 0 ],
913             [ "block" , \@opt_block , 1 ], # need &
914             [ "inside" , \@opt_inside , 1 ], # need &
915             [ "outside" , \@opt_outside , 1 ], # need &
916             [ "include" , \@opt_include , 1 ], # need &
917             [ "exclude" , \@opt_exclude , 1 ], # need &
918             ) {
919 390         7849 my($cat, $opt, $pattern) = @$set;
920 390         539 for (@{$opt}) {
  390         761  
921 98 50       1945 next if callable $_;
922 98 100 50     784 /^&\w+/ or next if $pattern;
923 68 50       413 $_ = parse_func($_) or die "$cat function format error: $_\n";
924             }
925             }
926              
927 30         704 my $regions = App::Greple::Regions::Holder->new;
928 30         318 for my $set (
929             [ \@opt_inside, REGION_INSIDE | REGION_UNION ],
930             [ \@opt_outside, REGION_OUTSIDE | REGION_UNION ],
931             [ \@opt_include, REGION_INSIDE | REGION_INTERSECT ],
932             [ \@opt_exclude, REGION_OUTSIDE | REGION_INTERSECT ])
933             {
934 120         216 my($opt, $flag) = @$set;
935 120         360 for my $spec (@$opt) {
936 30         760 $regions->append(FLAG => $flag, SPEC => $spec);
937             }
938             }
939              
940             ##------------------------------------------------------------
941              
942 30 50       348 if ($opt_d{m}) {
943 0         0 warn "Search pattern:\n";
944 0         0 my $i;
945 0         0 for my $pat ($pat_holder->patterns) {
946 0 0       0 my $type =
    0          
    0          
947             $pat->is_required ? 'must' :
948             $pat->is_negative ? 'not' :
949             $pat->is_positive ? 'and' : 'else';
950 0   0     0 my $target = $pat->regex // $pat->string;
951 0 0       0 warn sprintf(" %4s %1s %s\n",
    0          
952             $type,
953             $pat->is_function ? '&' : '',
954             @colors > 1 ? index_color($i++, $target) : $target);
955             }
956 0         0 warn sprintf("must = %d, need = %d, allow = %d\n",
957             $count_must, $count_need, $count_allow);
958             }
959              
960             ## push post-process filter
961 30 50       149 if (@opt_pf) {
962 0         0 push_output_filter(\*STDOUT, @opt_pf);
963             }
964              
965 30 50 0     117 usage() and exit if defined $opt_usage;
966              
967 30 50       1076 open SAVESTDIN, '<&', \*STDIN or die "open: $!";
968 30 50       661 open SAVESTDOUT, '>&', \*STDOUT or die "open: $!";
969 30 50       2133 open SAVESTDERR, '>&', \*STDERR or die "open: $!";
970              
971             sub recover_stdin {
972 28 50   28   818 open STDIN, '<&', \*SAVESTDIN or die "open: $!";
973             }
974             sub recover_stderr {
975 0 0   0   0 open STDERR, '>&', \*SAVESTDERR or die "open: $!";
976 0         0 binmode STDERR, ':encoding(utf8)';
977             }
978             sub recover_stdout {
979 0     0   0 close STDOUT;
980 0 0       0 open STDOUT, '>&', \*SAVESTDOUT or die "open: $!";
981             }
982             sub close_stdout {
983 28     28   244 close SAVESTDOUT;
984 28         224 close STDOUT;
985             }
986              
987 0     0   0 sub read_stdin { }
988              
989 30         80 my $slurp = do {
990             ##
991             ## Setting utf8 warnings fatal makes it easy to find code conversion
992             ## error, so you can choose appropriate file code or automatic code
993             ## recognition, but loose a chance to find string in unrelated area.
994             ##
995 30 50       549 if ($opt_error =~ /^(?: fatal | skip | retry )$/x) {
    0          
996 30 50       219 if ($opt_warn{read}) {
997             sub {
998 30     30   515 use warnings FATAL => 'utf8';
  30         181  
  30         6118  
999 0     0   0 my $stdin = eval { local $/; };
  0         0  
  0         0  
1000 0 0       0 warn $@ if $@;
1001 0         0 $stdin;
1002             }
1003 0         0 } else {
1004             sub {
1005 30     30   233 use warnings FATAL => 'utf8';
  30         52  
  30         10575  
1006 30     30   131 eval { local $/; };
  30         151  
  30         1883  
1007             }
1008 30         637 }
1009             } elsif ($opt_error eq 'ignore') {
1010 0 0       0 if ($opt_warn{read}) {
1011 0     0   0 sub { local $/; };
  0         0  
  0         0  
1012             } else {
1013             sub {
1014 0     0   0 close STDERR;
1015 0         0 my $stdin = do { local $/; };
  0         0  
  0         0  
1016 0         0 recover_stderr;
1017 0         0 $stdin;
1018             }
1019 0         0 }
1020             } else {
1021 0         0 die "$opt_error: invalid action.\n";
1022             }
1023             };
1024              
1025 30     30   241 use Term::ANSIColor::Concise qw(ansi_code);
  30         75  
  30         5151  
1026              
1027             use constant {
1028 30         170 EL => ansi_code('{EL}'), # Erase Line
1029             ED => ansi_code('{ED}'), # Erase Display
1030             SCP => ansi_code('{SCP}'), # Save Cursor Position
1031             RCP => ansi_code('{RCP}'), # Restore Cursor Position
1032             DSC => ansi_code('{DECSC}'), # DEC Save Cursor
1033             DRC => ansi_code('{DECRC}'), # DEC Restore Cursor
1034             CR => "\r",
1035 30     30   225 };
  30         59  
1036              
1037 30         373 my($progress_show, $progress_reset) = do {
1038 30         71 my $n;
1039             my($s, $e) = ! $need_color ? ('', '') :
1040 30 50       285 ( ansi_code $colormap{PROGRESS}, ansi_code 'Z');
1041 30     0   8831 my $print = sub { STDERR->printflush(DSC, $s, @_, $e, CR, DRC) };
  0         0  
1042 30         128 my $start = do {
1043 30 50 33     308 if ($opt_d{n} and $opt_d{f}) {
    50          
    50          
1044 0     0   0 sub { $print->(++$n, " ", $current_file, ED) }
1045 0         0 }
1046             elsif ($opt_d{n}) {
1047 0     0   0 sub { $print->(++$n) }
1048 0         0 }
1049             elsif ($opt_d{f}) {
1050 0     0   0 sub { STDERR->printflush($current_file, ":\n") }
1051 0         0 }
1052             else {
1053 30         88 undef;
1054             }
1055             };
1056 30         76 my $end = do {
1057 30 50       108 if ($opt_d{n}) {
1058 0 0   0   0 sub { STDERR->printflush(ED) if $n }
1059 0         0 } else {
1060 30         66 undef;
1061             }
1062             };
1063 30         187 ($start, $end);
1064             };
1065              
1066             ##------------------------------------------------------------
1067             ## now ready to run.
1068             ##
1069              
1070             ## record start time
1071 30 50       115 if ($opt_d{s}) {
1072 0         0 $stat{time_start} = [times];
1073             }
1074              
1075 30         102 for (@opt_prologue) { $_->call() }
  0         0  
1076              
1077 30         184 grep_files();
1078              
1079 28         99 for (@opt_epilogue) { $_->call() }
  0         0  
1080              
1081 28 50       150 if ($opt_d{n}) {
1082 0         0 print STDERR ED;
1083             }
1084              
1085 28 0 33     104 if ($opt_uniqcolor and $opt_d{c}) {
1086 0         0 dump_uniqcolor();
1087             }
1088              
1089             ## show statistic info
1090 28 50       109 if ($opt_d{s}) {
1091              
1092 0         0 $stat{time_end} = [times];
1093 0         0 my @s = $stat{time_start}->@*;
1094 0         0 my @e = $stat{time_end}->@*;
1095 0         0 printf(STDERR "cpu %.3fu %.3fs\n", $e[0]-$s[0], $e[1]-$s[1]);
1096              
1097 0         0 local $" = ', ';
1098 0         0 for my $k (sort keys %stat) {
1099 0         0 my $v = $stat{$k};
1100 0 0       0 print STDERR
1101             "$k: ",
1102             ref $v eq 'ARRAY' ? "(@$v)" : $v,
1103             "\n";
1104             }
1105             }
1106              
1107 28         187 close_stdout;
1108              
1109 28 50       111 if ($opt_d{p}) {
1110 0         0 open STDOUT, ">&STDERR";
1111 0         0 system "ps -lww -p $$";
1112 0         0 system "lsof -p $$";
1113             }
1114              
1115 28   33     0 exit($opt_exit // ($stat{match_effective} == 0));
1116              
1117             ######################################################################
1118              
1119             sub grep_files {
1120             FILE:
1121 30     30   180 while (defined($current_file = open_nextfile())) {
1122 30         375 my $content = $slurp->();
1123 30         936 $stat{file_tried}++;
1124 30 50       167 if (not defined $content) {
1125 0 0       0 if ($opt_error eq 'fatal') {
1126 0         0 die "ABORT on $current_file\n";
1127             }
1128 0 0       0 if ($opt_error ne 'retry') {
1129 0 0       0 warn "SKIP $current_file\n" if $opt_warn{skip};
1130 0         0 next FILE;
1131             }
1132              
1133             # Try again
1134 0         0 binmode STDIN, ':raw';
1135 0 0       0 seek STDIN, 0, 0 or do {
1136             warn "SKIP $current_file (not seekable)\n"
1137 0 0       0 if $opt_warn{skip};
1138 0         0 next FILE;
1139             };
1140 0         0 $content = $slurp->();
1141 0 0       0 if (not defined $content) {
1142 0 0       0 warn "SKIP* $current_file\n" if $opt_warn{skip};
1143 0         0 next FILE;
1144             }
1145 0 0       0 warn "RETRY $current_file\n" if $opt_warn{retry};
1146 0         0 $stat{read_retry}++;
1147 0         0 binmode STDOUT, ':raw';
1148             }
1149              
1150 30         158 my $matched = grep_data(\$content);
1151              
1152 28         132 $stat{match_effective} += $matched;
1153 28         77 $stat{file_searched}++;
1154 28         124 $stat{length} += length $content;
1155             } continue {
1156 28         670 close STDIN; # wait; # wait for 4.019 or earlier?
1157             # recover STDIN for opening '-' and some weird command which needs
1158             # STDIN opened (like unzip)
1159 28         208 recover_stdin;
1160 28         917 binmode STDOUT, ":encoding($output_code)";
1161             }
1162             }
1163              
1164             sub usage {
1165 0     0   0 pod2usage(-verbose => 0, -exitval => "NOEXIT");
1166              
1167 0         0 my $quote = qr/[\\(){}\|\*?]/;
1168 0         0 for my $bucket ($rcloader->buckets) {
1169 0         0 my $module = $bucket->module;
1170 0         0 print " $module options:\n";
1171 0         0 for my $name ($bucket->options) {
1172 0 0 0     0 my $help = $opt_usage ? "" : $bucket->help($name) // "";
1173 0 0       0 next if $help eq 'ignore';
1174 0         0 my @option = $bucket->getopt($name, ALL => 1);
1175 0   0     0 printf(" %-20s %s\n", $name,
1176             $help || join(' ', shellquote(@option)));
1177             }
1178 0         0 print "\n";
1179             }
1180              
1181 0         0 print "Version: $version\n";
1182              
1183 0         0 exit 2;
1184             }
1185              
1186             sub open_nextfile {
1187              
1188             ##
1189             ## --chdir
1190             ##
1191 58   66 58   1818 while (@ARGV == 0 and @opt_chdir and (@argv_files or @opt_glob)) {
      0        
      33        
1192 0         0 my $dir = shift @opt_chdir;
1193 0 0       0 warn "chdir $dir/\n" if $opt_d{d};
1194 0 0       0 chdir $start_directory or die "$!: $start_directory\n";
1195 0 0       0 chdir $dir or die "$!: $dir\n";
1196 0         0 push @ARGV, @argv_files, map(glob, @opt_glob);
1197             }
1198              
1199 58         133 my $file;
1200 58   33     541 while (defined($file = shift(@ARGV)) ||
      66        
1201             defined($file = $opt_readlist && read_stdin)) {
1202 30 50       716 $file = decode 'utf8', $file unless utf8::is_utf8 $file;
1203 30         154 $file =~ s/\n+$//;
1204              
1205 30 50       357 if (0) {}
    50          
1206 0         0 elsif ($file =~ /^https?:\/\//) {
1207 0 0 0     0 open(STDIN, '-|') || exec("w3m -dump $file") || die "w3m: $!\n";
1208             }
1209             elsif ($file eq '-') {
1210             # nothing to do
1211             }
1212             else {
1213 30 50       2047 open(STDIN, '<', $file) or do {
1214 0 0       0 warn "$file: $!\n" unless -l $file;
1215 0         0 next;
1216             };
1217             }
1218              
1219 30 50       247 if (my @filters = $filter_d->get_filters($file)) {
1220 0         0 push_input_filter({ &FILELABEL => $file }, @filters);
1221             }
1222              
1223 30 50       404 if ($file_code eq 'binary') {
1224 0         0 binmode STDIN, ":raw";
1225             } else {
1226 30         463 binmode STDIN, ":encoding($file_code)";
1227             }
1228              
1229 30         1506 return $file;
1230             }
1231 28         142 undef;
1232             }
1233              
1234             ######################################################################
1235              
1236             sub grep_data {
1237 30     30   102 local *_ = shift;
1238              
1239             ##
1240             ## --begin
1241             ##
1242 30         90 for my $f (@opt_begin) {
1243 48         102 eval { $f->call(&FILELABEL => $current_file) };
  48         422  
1244 46 50       1378 if (my $msg = $@) {
1245 0 0       0 if ($msg =~ /^SKIP/i) {
1246 0 0       0 warn $@ if $opt_warn{begin};
1247 0         0 return 0;
1248             } else {
1249 0         0 die $msg;
1250             }
1251             }
1252             }
1253              
1254 28 50       130 $progress_show->() if $progress_show;
1255              
1256             my $grep = App::Greple::Grep->new(
1257             text => \$_,
1258             filename => $current_file,
1259             pattern => $pat_holder,
1260             regions => $regions,
1261             border => $border_re,
1262             after => $opt_A,
1263             before => $opt_B,
1264             only => $opt_o,
1265             all => $opt_all,
1266             block => \@opt_block,
1267             stretch => $opt_stretch,
1268             must => $count_must,
1269             need => $count_need,
1270             countcheck => $count_match_sub,
1271             allow => $count_allow,
1272             strict => $opt_strict,
1273 28         86 group_index => do { local $_ = $opt_colorindex;
1274 28 0       829 $opt_capture_group ? /G/i ? /P/i ? 3 : 2 : 1 : 0 },
    50          
    50          
1275             region_index => $opt_regioncolor,
1276             stat => \%stat,
1277             callback => \@opt_callback,
1278             alert_size => $opt_alert{size},
1279             alert_time => $opt_alert{time},
1280 28         137 join_blocks => $opt_join_blocks,
1281             )->run;
1282              
1283             ## --postgrep
1284 28         43258 for my $f (@opt_postgrep) {
1285 10         66 $f->call($grep);
1286             # remove emptied results
1287 10         855186 my $ref = $grep->result_ref;
1288 10         88 @$ref = grep { @{$_} > 0 } @$ref;
  10         24  
  10         62  
1289             }
1290             ## -m
1291 28         91 for my $splice (@splicer) {
1292 0         0 $splice->($grep->result_ref);
1293             }
1294              
1295 28         195 my $matched = $grep->matched;
1296 28 50       752 if ($opt_l) {
    50          
    50          
1297 0 0       0 if ($matched) {
1298 0 0       0 $progress_reset->() if $progress_reset;
1299 0         0 print $current_file;
1300 0 0       0 printf ":%d", scalar $grep->result if $opt_c;
1301 0         0 print "\n";
1302             }
1303             }
1304             elsif ($opt_c) {
1305 0 0       0 $progress_reset->() if $progress_reset;
1306 0 0       0 print "$current_file:" if $need_filename;
1307 0         0 print scalar $grep->result, "\n";
1308             }
1309             elsif ($grep->result_ref->@*) {
1310 28 50       284 $progress_reset->() if $progress_reset;
1311             # open output filter
1312 28 50       109 @opt_of && push_output_filter(
1313             { &FILELABEL => $current_file },
1314             \*STDOUT, @opt_of);
1315 28         175 output($grep);
1316 28 50       107 @opt_of && recover_stdout;
1317             }
1318              
1319             ##
1320             ## --end
1321             ##
1322 28         83 for my $f (@opt_end) {
1323 0         0 $f->call(&FILELABEL => $current_file);
1324             }
1325              
1326 28 50       98 s/./\000/gs if $opt_clean;
1327              
1328 28         909 $matched;
1329             }
1330              
1331             sub output {
1332 28     28   69 my $grep = shift;
1333 28         93 my $file = $grep->{filename};
1334              
1335 28 50       163 if ($opt_filestyle eq 'once') {
1336 0         0 print $_file->($file), "\n";
1337             }
1338              
1339 28   33     223 my $need_blockend =
1340             !$opt_all &&
1341             $blockend ne '' &&
1342             ($opt_blockend || $opt_p || $opt_A || $opt_B || @opt_block);
1343              
1344 28         73 my $line = 1;
1345 28         60 my $lastpos = 0;
1346 28         100 my @results = $grep->result;
1347 28         253 for my $rix (keys @results) {
1348 28         80 my $is_top = $rix == 0;
1349 28         76 my $is_bottom = $rix == $#results;
1350              
1351 28         108 my $result = $results[$rix];
1352 28         53 my($blk, @result) = @{$result};
  28         87  
1353 28         96 my($block_start, $block_end, $block_number) = @$blk;
1354 28   50     215 $block_number //= 0;
1355 28         243 my $block = $grep->cut($block_start, $block_end);
1356              
1357             ## --print
1358 28 50       584 if (@opt_print) {
1359 0         0 local *_ = \$block;
1360 0         0 for my $func (@opt_print) {
1361 0         0 $_ = $func->call(&FILELABEL => $file);
1362             }
1363 0 0       0 if (not $opt_continue) {
1364 0 0       0 print $block if defined $block;
1365 0         0 next;
1366             }
1367             }
1368              
1369 28 50       109 if ($opt_n) {
1370 0         0 my $gap = $grep->cut($lastpos, $block_start);
1371 0         0 $line += $gap =~ tr/\n/\n/;
1372             }
1373 28         69 $lastpos = $block_end;
1374              
1375             # when --filestyle and/or --linestyle is "separate"
1376 84         217 grep { $_ } (
1377             do {
1378 28 50       160 print $_file->($current_file)
1379             if $opt_filestyle eq 'separate';
1380             },
1381             do {
1382 28 50 33     140 print $_line->($line)
1383             if $opt_n and $opt_linestyle eq 'separate';
1384             },
1385 28 50       103 do {
1386 28 50 33     196 print $_block->($block_number)
1387             if $opt_b and $opt_blockstyle eq 'separate';
1388             },
1389             ) and print "\n";
1390              
1391 28 50       131 if ($indexer) {
1392 0 0       0 $indexer->reset if $indexer->block;
1393 0 0       0 for ($indexer->reverse ? reverse @result : @result) {
1394 0         0 $_->[2] = $indexer->index;
1395             }
1396             }
1397              
1398 28         148 my @slice = $grep->slice_result($result);
1399 28         2333 my $mark = "\001";
1400 28         113 for my $i (keys @result) {
1401 40         7007 my($start, $end, $pi, $callback) = $result[$i]->@*;
1402 40         130 local *b = \$slice[$i * 2 + 1];
1403              
1404             ## run callback function
1405 40 50       116 if ($callback) {
1406 40   33     88 $b = do {
1407 40 50       217 if (ref $callback eq 'CODE') {
    50          
1408 0         0 $callback->($start, $end, $pi, $b);
1409             }
1410             elsif (callable($callback)) {
1411 40         591 $callback->call(
1412             &FILELABEL => $file,
1413             start => $start,
1414             end => $end,
1415             index => $pi,
1416             match => $b);
1417             }
1418 0         0 else { die }
1419             } // $b;
1420             }
1421              
1422 40 50       930 if ($opt_join) {
1423 0 0 0     0 if ($opt_n and $opt_linestyle eq 'line') {
1424 0         0 $b =~ s/(?
1425             } else {
1426 0         0 $b =~ s/(?
1427             }
1428             }
1429              
1430 40 50       90 $pi = $uniq_color->index($b) if $opt_uniqcolor;
1431 40         137 $b = index_color($pi, $b);
1432             }
1433              
1434 28         3401 $block = join '', @slice;
1435 28 50       124 next if $block eq "";
1436              
1437 28         63 my @line;
1438 28 50       99 if ($opt_n) {
1439 0 0       0 if ($opt_linestyle eq 'line') {
1440 0 0       0 my $increment = $block =~ /[\n$mark]/ ? 1 : 0;
1441 0         0 $block =~ s{(?:(?$mark)|(?<=\n)|\A)(?=.)}{
1442 0 0       0 push @line, $_line->($line) unless $+{mark};
1443 0         0 $line += $increment;
1444 0 0       0 $+{mark} ? $opt_joinby : '';
1445             }gse;
1446             } else {
1447 0         0 $line += $block =~ tr/\n/\n/;
1448             }
1449             }
1450              
1451 28 50       142 $block = $_text->($block) if $colormap{TEXT} ne "";
1452              
1453 28 50       92 if (@line) {
1454 0         0 $block =~ s/^/shift @line/mge;
  0         0  
1455             }
1456              
1457 28 50 33     132 if ($opt_b and $opt_blockstyle eq 'line') {
1458 0         0 my $s = $_block->($block_number);
1459 0         0 $block =~ s/^/$s/mg;
1460             }
1461              
1462 28 50       106 if ($opt_filestyle eq 'line') {
1463 0         0 my $s = $_file->($file);
1464 0         0 $block =~ s/^/$s/mg;
1465             }
1466              
1467 28 50 33     230 print "$_top\n" if $is_top && $_top ne '';
1468 28         2439 print $block;
1469 28 50 33     518 print "\n" if $opt_newline and not $block =~ /\n\z/;
1470 28 50       108 print "$_blockend\n" if $need_blockend;
1471 28 50       105 if ($is_bottom) {
1472 28 50       255 print "$_bottom\n" if $_bottom ne '';
1473             } else {
1474 0 0         print "$_middle\n" if $_middle ne '';
1475             }
1476             }
1477             }
1478              
1479             __END__