File Coverage

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