File Coverage

blib/script/greple
Criterion Covered Total %
statement 505 708 71.3
branch 203 400 50.7
condition 63 152 41.4
subroutine 66 91 72.5
pod n/a
total 837 1351 61.9


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 133     133   729794 use v5.24;
  133         521  
9 133     133   755 use warnings;
  133         232  
  133         8491  
10              
11 133     133   78961 use File::stat;
  133         1380706  
  133         10188  
12 133     133   81244 use IO::Handle;
  133         970562  
  133         8957  
13 133     133   80625 use Pod::Usage;
  133         10131611  
  133         24213  
14 133     133   73457 use Text::ParseWords qw(shellwords);
  133         233328  
  133         11341  
15 133     133   1035 use List::Util qw(min max first sum uniq shuffle notall pairs pairmap);
  133         251  
  133         20386  
16 133     133   78218 use Hash::Util qw(lock_keys);
  133         501381  
  133         982  
17 133     133   14826 use Cwd qw(getcwd abs_path);
  133         287  
  133         7356  
18 133     133   697 use Carp;
  133         220  
  133         6692  
19              
20 133     133   66088 use utf8;
  133         38196  
  133         872  
21 133     133   5312 use Encode;
  133         240  
  133         12346  
22 133     133   65438 use Encode::Guess;
  133         602572  
  133         543  
23 133     133   73574 use open IO => ':utf8', ':std';
  133         183560  
  133         949  
24              
25 133     133   97072 use Data::Dumper;
  133         1127432  
  133         13403  
26             {
27 133     133   30333679 no warnings 'redefine';
  133         1386  
  133         243  
  133         35837  
28 133     0   3757 *Data::Dumper::qquote = sub { qq["${\(shift)}"] };
  0         0  
  0         0  
29 133         1220 $Data::Dumper::Useperl = 1;
30             }
31              
32             ##
33             ## Setup greple/lib to be a module directory if exists.
34             ##
35             BEGIN {
36 133 50   133   12403 if (my $lib = abs_path($0) =~ s{/(?:script/|bin/)?\w+$}{/lib}r) {
37 133 50       7175 unshift @INC, $lib if -d "$lib/App/Greple";
38             }
39             }
40              
41 133     133   76210 use Getopt::EX::Loader;
  133         11622024  
  133         9981  
42 133     133   1328 use Getopt::EX::Func qw(parse_func callable);
  133         300  
  133         7766  
43              
44 133     133   73240 use App::Greple;
  133         406  
  133         4871  
45 133     133   57716 use App::Greple::Common;
  133         401  
  133         8592  
46 133     133   61689 use App::Greple::Util qw(shellquote);
  133         407  
  133         9362  
47 133     133   71270 use App::Greple::Grep;
  133         540  
  133         17149  
48 133     133   963 use App::Greple::Regions;
  133         253  
  133         11154  
49 133     133   840 use App::Greple::Pattern;
  133         246  
  133         9098  
50 133     133   77592 use App::Greple::Pattern::Holder;
  133         434  
  133         8051  
51 133     133   66173 use App::Greple::Filter;
  133         478  
  133         842644  
52              
53 133         1094 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 133         751 my @baseclass = qw( App::Greple Getopt::EX );
174 133         2419 my $rcloader = Getopt::EX::Loader
175             ->new(BASECLASS => \@baseclass);
176              
177 133         10172 my @optargs;
178             my %optargs;
179              
180             sub newopt {
181             push @optargs, pairmap {
182 12103     12103   17451 local $_ = $a;
183 12103         42897 s/\s+//g;
184 12103         26028 s/^(?=\w+-)([-\w]+)/$1 =~ tr[-][_]r . "|$1"/e; # "a-b" -> "a_b|a-b"
  1862         6800  
185 12103 100 50     48839 /^(\w+)/ and $optargs{$1} = $b if ref $b ne 'CODE';
186 12103         26850 $_ => $b;
187 133     133   2126 } @_;
188             }
189              
190             sub opt :lvalue {
191 4     4   11 my $name = shift;
192 4 50       18 my $var = $optargs{$name} or die "$name: invalid option name\n";
193 4 50 0     15 if (ref $var eq 'SCALAR') {
    0          
194 4         26 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 133         0 my @opt_pattern;
203             sub opt_pattern {
204 113     113   735955 push @opt_pattern, [ map "$_", @_ ];
205 113         2218 $opt_pattern[-1];
206             }
207              
208 133         0 my @opt_colormap;
209 2     2   20160 sub opt_colormap { push @opt_colormap, $_[1] }
210 1     1   1495 sub opt_colorsub { push @opt_colormap, "sub{ $_[1] }" }
211              
212 133         1036 my %opt_format = (LINE => '%d:', FILE => '%s:', BLOCK => '%s:');
213 133         656 my %opt_alert = (size => 512 * 1024, time => 2);
214 133         851 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 1     1   775 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 1     1   9886 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 9         37 map { $opt_warn{$_} = $_[2] }
363 3 100   3   2082 map { $_ eq 'all' ? keys %opt_warn : $_ }
  3         24  
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 133         7758 ' 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 133         123359 require Getopt::Long;
415 133         1848232 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 133 50       189528 configure_getopt qw(debug) if $ENV{DEBUG_GETOPT};
421 133 50       799 $Getopt::EX::Loader::debug = 1 if $ENV{DEBUG_GETOPTEX};
422              
423             ## decode
424             my @ORIG_ARGV =
425 133 50       544 @ARGV = map { utf8::is_utf8($_) ? $_ : decode('utf8', $_) } @ARGV;
  591         15402  
426              
427             ## ~/.greplerc
428 133 50 66     4334 unless ((@ARGV and $ARGV[0] eq "--norc" and shift)
      33        
      33        
429             or
430             ($ENV{GREPLE_NORC}) ) {
431 133         1558 $rcloader->load(FILE => "$ENV{HOME}/.greplerc");
432             }
433              
434             ## modules
435 133         4903 $rcloader->deal_with(\@ARGV);
436              
437 133         143377 push @optargs, $rcloader->builtins;
438              
439             ## ENV
440 133 50       2632 $ENV{'GREPLEOPTS'} and unshift @ARGV, shellwords($ENV{'GREPLEOPTS'});
441              
442              
443             ## GetOptions
444 133         626 my @SAVEDARGV = @ARGV;
445 133 50       1425 $parser->getoptions(@optargs) || usage();
446              
447 133 50       314000 if ($opt_version) {
448 0         0 print "$version\n";
449 0         0 exit 0;
450             }
451              
452 133         331 our %opt_d;
453 133         497 @opt_d = map { split // } @opt_d;
  0         0  
454 133         427 @opt_d{@opt_d} = @opt_d;
455              
456 133 50       687 if ($opt_d{o}) {
457 0         0 warn "\@ARGV = ", join(' ', shellquote(@SAVEDARGV)), "\n";
458             }
459              
460             ## -m option
461 133 100       663 my @splicer = (not defined $opt_m) ? () : do {
462 1         7 my @param = split /,/, $opt_m, -1;
463 1 50       7 push @param, '' if @param % 2;
464 1 50   2   13 if (notall { /^(-?\d+)?$/ } @param) {
  2         29  
465 0         0 die "$opt_m: option format error.\n";
466             }
467             map {
468 1         19 my($offset, $length) = @$_;
  1         15  
469 1 50       7 if ($length ne '') {
470 0   0 0   0 sub { splice @{+shift}, $offset || 0, $length }
  0         0  
471 0         0 } else {
472 1   50 1   3 sub { splice @{+shift}, $offset || 0 }
  1         11  
473 1         12 }
474             }
475             pairs @param;
476             };
477              
478 133         293 my $file_code;
479 133         371 my $default_icode = 'utf8'; # default input encoding
480 133         611 my @default_icode_list = qw(euc-jp 7bit-jis);
481 133         307 my $output_code;
482 133         350 my $default_ocode = 'utf8'; # default output encoding
483              
484 133   33     988 $output_code = $opt_ocode || $default_ocode;
485 133     133   8278 binmode STDOUT, ":encoding($output_code)";
  133         108468  
  133         2491  
  133         943  
486              
487             ## show unused option characters
488 133 50       155016 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 133 50 33     1338 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 133 100       554 if (@opt_icode) {
533 4         12 @opt_icode = map { split /[,\s]+/ } @opt_icode;
  4         28  
534 4 100       25 if (grep { s/^\+// } @opt_icode) {
  4         33  
535 1         3 unshift @opt_icode, @default_icode_list;
536             }
537 4         26 @opt_icode = uniq @opt_icode;
538 4 100       37 if (@opt_icode > 1) {
    100          
539 1         2 @opt_icode = grep { !/(?:auto|guess)$/i } @opt_icode;
  3         12  
540 1         10 Encode::Guess->set_suspects(@opt_icode);
541 1         8676 $file_code = 'Guess';
542             }
543             elsif ($opt_icode[0] =~ /^(?:guess|auto)$/i) {
544 1         12 Encode::Guess->set_suspects(@default_icode_list);
545 1         11022 $file_code = 'Guess';
546             } else {
547 2         6 $file_code = $opt_icode[0];
548             }
549             }
550             else {
551 129         447 $file_code = $default_icode;
552             }
553              
554             ##
555             ## --filter
556             ##
557 133 100       513 if ($opt_filter) {
558 6         11 $opt_all = 1;
559 6         15 push @opt_need, '0';
560 6   50     49 $opt_exit //= 0;
561             }
562              
563             ##
564             ## Patterns
565             ##
566              
567 133         2081 my $pat_holder = App::Greple::Pattern::Holder->new;
568              
569 133         368 my $FLAG_BASE = FLAG_NONE;
570 133 100       495 $FLAG_BASE |= FLAG_IGNORECASE if $opt_i;
571              
572 133 100       499 if (@opt_f) {
573 9         29 for my $opt_f (@opt_f) {
574 9 100       91 $pat_holder->append({ flag => $FLAG_BASE, type => 'file',
575             $opt_select ? (select => $opt_select) : (),
576             },
577             $opt_f);
578             }
579             } else {
580 124 100 100     997 unless ($opt_filter or grep { $_->[0] !~ /^(not|may)/ } @opt_pattern) {
  110         1049  
581 29   66     208 unshift @opt_pattern, [ le => shift @ARGV // &usage ];
582             }
583             }
584              
585 132         1381 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 132         454 for (@opt_pattern) {
595 141         552 my($attr, @opt) = @$_;
596 141         484 my $flag = $FLAG_BASE | $pat_flag{$attr};
597 141         1108 $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 130         337 my @patterns = $pat_holder->patterns;
  130         832  
606 130         436 my @posi = grep { $_->is_positive } @patterns;
  159         738  
607 130         350 my @opti = grep { $_->is_optional } @posi;
  155         662  
608 130 100       655 if (@opti > 0) {
609 5         13 for my $p (grep { !$_->is_optional } @posi) {
  10         26  
610 5         14 $p->flag($p->flag | FLAG_REQUIRED);
611             }
612             }
613             }
614              
615             ##
616             ## set $count_must, $count_need and $opt_allow
617             ##
618 130         303 my $count_must = 0;
619 130         277 my $count_need;
620 130         303 my $count_allow = 0;
621             {
622 130         259 my $must = grep({ $_->is_required } $pat_holder->patterns);
  130         520  
  159         613  
623 130         505 my $posi = grep({ $_->is_positive } $pat_holder->patterns) - $must;
  159         545  
624 130         504 my $nega = grep({ $_->is_negative } $pat_holder->patterns);
  159         678  
625              
626 130   50     572 $count_must = $must // 0;
627 130 100       598 $count_need = $must ? 0 : $posi;
628 130         436 for (@opt_need) {
629 14 50       130 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 14         57 $count_need = $1 - $must;
637             }
638             else {
639 0         0 die "$_ is not valid count.\n"
640             }
641             }
642              
643 130         268 $count_allow = 0;
644 130         409 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 130 100   130   752 local $_ = shift or return;
665 9 100       44 /[^\d,]/ and die "$_ is not valid count.\n";
666 8 100       39 my @c = map { $_ eq '' ? 0 : int } split(/,/, $_, -1);
  20         84  
667 8 100       37 if (@c == 1) {
668 1         6 return sub { $_[0] == $c[0] };
  9         28  
669             }
670 7 100       31 push @c, -1 if @c % 2;
671             return sub {
672 60         95 my @n = @c;
673 60         147 while (my($min, $max) = splice(@n, 0, 2)) {
674 84 100       207 return 0 if $_[0] < $min;
675 51 100 100     233 return 1 if $max <= 0 || $_[0] <= $max;
676             }
677 10         27 return 0;
678             }
679 130         1314 }->($opt_matchcount);
  7         75  
680              
681             ##
682             ## setup input/output filter
683             ##
684 129         2422 my $filter_d = App::Greple::Filter->new->parse(@opt_if);
685 129 50       677 unless ($opt_noif) {
686             $filter_d->append(
687 130     130   1199 [ sub { s/\.Z$// }, 'zcat' ],
688 130     130   958 [ sub { s/\.g?z$// }, 'gunzip -c' ],
689 130     130   1167 [ sub { m/\.pdf$/i }, 'pdftotext -nopgbrk - -' ],
690 129     130   2020 [ sub { s/\.gpg$// }, 'gpg --quiet --no-mdc-warning --decrypt' ],
  130         945  
691             );
692             }
693              
694             ##------------------------------------------------------------
695             ## miscellaneous setups
696             ##
697              
698 129         474 my @argv_files;
699             my $start_directory;
700 129   66     865 my $need_filename = ($opt_H or $opt_l);
701 129         330 my $current_file;
702              
703 129 50       863 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 129 50 66     858 push(@ARGV, '-') unless @ARGV || @argv_files || @opt_glob || $opt_readlist;
      66        
      33        
717 129 100 66     1320 if ((@ARGV > 1 or $opt_readlist) and not $opt_h) {
      66        
718 5         12 $need_filename++;
719             }
720              
721 129 100       712 $opt_filestyle = 'none' if not $need_filename;
722              
723 129 50       663 $opt_join = 1 if $opt_joinby ne "";
724              
725             ##------------------------------------------------------------
726             ## colors
727             ##
728 129         1926 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 129         329 our @colors;
741              
742 133     133   1480 use Getopt::EX::Colormap;
  133         291  
  133         34031  
743 129         2027 my $color_handler = Getopt::EX::Colormap
744             ->new(HASH => \%colormap, LIST => \@colors)
745             ->load_params(@opt_colormap);
746              
747 129 50       17776 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 129 100       1376 if ($color_handler->list == 0) {
761 126 50       2159 $color_handler->append
762             ($opt_colorful ? @default_color : $default_color[0]);
763             }
764              
765 129 50       12023 if ($opt_ansicolor eq '24bit') {
766 133     133   1129 no warnings 'once';
  133         266  
  133         434882  
767 0         0 $Getopt::EX::Colormap::RGB24 = 1;
768             }
769              
770 129         573 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 129   66     2348 my $need_color = (($opt_color eq 'always')
787             or (($opt_color eq 'auto') and (!$opt_o and -t STDOUT)));
788              
789 129 100       627 if (!$need_color) {
790 128         504 $Getopt::EX::Colormap::NO_COLOR = 1;
791             }
792              
793 129         963 my %_esc = ( t => "\t", n => "\n", r => "\r", f => "\f" );
794             sub expand_escape {
795 390   0 390   1424 $_[0] =~ s{\\(.)}{$_esc{$1} // $1}egr;
  0         0  
796             }
797              
798 129         980 $_ = expand_escape($_) for values %opt_format;
799              
800 129         379 my $blockend = "--";
801 129 100       520 if (defined $opt_blockend) {
802 3         10 $blockend = expand_escape($opt_blockend);
803             }
804              
805 129     9   764 my $_file = sub { $color_handler->color('FILE' , sprintf($opt_format{FILE}, $_[0])) };
  9         132  
806 129     4   596 my $_line = sub { $color_handler->color('LINE' , sprintf($opt_format{LINE}, $_[0])) };
  4         44  
807 129     0   552 my $_block = sub { $color_handler->color('BLOCK', sprintf($opt_format{BLOCK}, $_[0])) };
  0         0  
808 129     0   540 my $_text = sub { $color_handler->color('TEXT' , $_[0]) };
  0         0  
809 129         972 my $_blockend = $color_handler->color('BLOCKEND', $blockend);
810 129         8095 my $_top = $color_handler->color('TOP' , $opt_frame_top);
811 129         1883 my $_middle = $color_handler->color('MIDDLE' , $opt_frame_middle);
812 129         1617 my $_bottom = $color_handler->color('BOTTOM' , $opt_frame_bottom);
813              
814             sub index_color {
815 314     314   1618 $color_handler->index_color(@_);
816             }
817              
818             sub color {
819 0     0   0 $color_handler->color(@_);
820             }
821              
822 129         2532 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 129         596 my %color_index = map { uc $_ => 1 } $opt_colorindex =~ /\w/g;
  4         20  
839 129         252 my $indexer = do {
840 129 50       612 if ($color_index{S}) {
841 0         0 @colors = shuffle @colors;
842             }
843 129 50 33     1479 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 129         369 else { undef }
856             };
857 129         369 my $opt_uniqcolor = $color_index{U};
858              
859             # -dc
860 129 50       601 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 129         293 my $border_re = do {
874 129 100       906 if ($opt_border) {
    100          
875 1         18 qr/$opt_border/m; # custom
876             } elsif ($opt_p) {
877 4         27 qr/(?:\A|\R)\K\R+/; # paragraph
878             } else {
879 124         653 qr/^/m; # line
880             }
881             };
882              
883 129 100       531 if ($opt_C) {
884 4   33     27 $opt_A ||= $opt_C;
885 4   33     36 $opt_B ||= $opt_C;
886             }
887 129         1588 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 129         1129 lock_keys %stat;
900              
901             ##
902             ## Setup functions
903             ##
904 129         4322 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 1677         3042 my($cat, $opt, $pattern) = @$set;
920 1677         2197 for (@{$opt}) {
  1677         3165  
921 19 50       262 next if callable $_;
922 19 100 50     241 /^&\w+/ or next if $pattern;
923 3 50       10 $_ = parse_func($_) or die "$cat function format error: $_\n";
924             }
925             }
926              
927 129         2112 my $regions = App::Greple::Regions::Holder->new;
928 129         660 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 516         968 my($opt, $flag) = @$set;
935 516         977 for my $spec (@$opt) {
936 9         53 $regions->append(FLAG => $flag, SPEC => $spec);
937             }
938             }
939              
940             ##------------------------------------------------------------
941              
942 129 50       748 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 129 100       591 if (@opt_pf) {
962 2         10 push_output_filter(\*STDOUT, @opt_pf);
963             }
964              
965 128 50 0     566 usage() and exit if defined $opt_usage;
966              
967 128 50       4236 open SAVESTDIN, '<&', \*STDIN or die "open: $!";
968 128 50       2967 open SAVESTDOUT, '>&', \*STDOUT or die "open: $!";
969 128 50       9184 open SAVESTDERR, '>&', \*STDERR or die "open: $!";
970              
971             sub recover_stdin {
972 126 50   126   3443 open STDIN, '<&', \*SAVESTDIN or die "open: $!";
973             }
974             sub recover_stderr {
975 1 50   1   21 open STDERR, '>&', \*SAVESTDERR or die "open: $!";
976 1         12 binmode STDERR, ':encoding(utf8)';
977             }
978             sub recover_stdout {
979 3     3   5687955 close STDOUT;
980 3 50       197 open STDOUT, '>&', \*SAVESTDOUT or die "open: $!";
981             }
982             sub close_stdout {
983 123     123   1050 close SAVESTDOUT;
984 123         2012209 close STDOUT;
985             }
986              
987 0     0   0 sub read_stdin { }
988              
989 128         440 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 128 100       1455 if ($opt_error =~ /^(?: fatal | skip | retry )$/x) {
    100          
996 126 100       930 if ($opt_warn{read}) {
997             sub {
998 133     133   1231 use warnings FATAL => 'utf8';
  133         295  
  133         26544  
999 1     1   3 my $stdin = eval { local $/; };
  1         5  
  1         55  
1000 1 50       61 warn $@ if $@;
1001 1         5 $stdin;
1002             }
1003 1         7 } else {
1004             sub {
1005 133     133   1104 use warnings FATAL => 'utf8';
  133         302  
  133         44116  
1006 129     129   741 eval { local $/; };
  129         1062  
  129         1691958  
1007             }
1008 125         915 }
1009             } elsif ($opt_error eq 'ignore') {
1010 1 50       5 if ($opt_warn{read}) {
1011 0     0   0 sub { local $/; };
  0         0  
  0         0  
1012             } else {
1013             sub {
1014 1     1   7 close STDERR;
1015 1         2 my $stdin = do { local $/; };
  1         5  
  1         55  
1016 1         240 recover_stderr;
1017 1         44 $stdin;
1018             }
1019 1         7 }
1020             } else {
1021 1         0 die "$opt_error: invalid action.\n";
1022             }
1023             };
1024              
1025 133     133   1118 use Term::ANSIColor::Concise qw(ansi_code);
  133         298  
  133         23444  
1026              
1027             use constant {
1028 133         6764 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 133     133   1246 };
  133         277  
1036              
1037 127         349 my($progress_show, $progress_reset) = do {
1038 127         315 my $n;
1039             my($s, $e) = ! $need_color ? ('', '') :
1040 127 100       772 ( ansi_code $colormap{PROGRESS}, ansi_code 'Z');
1041 127     0   1165 my $print = sub { STDERR->printflush(DSC, $s, @_, $e, CR, DRC) };
  0         0  
1042 127         282 my $start = do {
1043 127 50 33     1507 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 127         341 undef;
1054             }
1055             };
1056 127         301 my $end = do {
1057 127 50       934 if ($opt_d{n}) {
1058 0 0   0   0 sub { STDERR->printflush(ED) if $n }
1059 0         0 } else {
1060 127         417 undef;
1061             }
1062             };
1063 127         967 ($start, $end);
1064             };
1065              
1066             ##------------------------------------------------------------
1067             ## now ready to run.
1068             ##
1069              
1070             ## record start time
1071 127 50       603 if ($opt_d{s}) {
1072 0         0 $stat{time_start} = [times];
1073             }
1074              
1075 127         484 for (@opt_prologue) { $_->call() }
  0         0  
1076              
1077 127         649 grep_files();
1078              
1079 123         466 for (@opt_epilogue) { $_->call() }
  0         0  
1080              
1081 123 50       677 if ($opt_d{n}) {
1082 0         0 print STDERR ED;
1083             }
1084              
1085 123 0 33     685 if ($opt_uniqcolor and $opt_d{c}) {
1086 0         0 dump_uniqcolor();
1087             }
1088              
1089             ## show statistic info
1090 123 50       580 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 123         632 close_stdout;
1108              
1109 123 50       562 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 123   100     0 exit($opt_exit // ($stat{match_effective} == 0));
1116              
1117             ######################################################################
1118              
1119             sub grep_files {
1120             FILE:
1121 127     127   647 while (defined($current_file = open_nextfile())) {
1122 129         912 my $content = $slurp->();
1123 129         5560 $stat{file_tried}++;
1124 129 100       826 if (not defined $content) {
1125 7 100       55 if ($opt_error eq 'fatal') {
1126 1         0 die "ABORT on $current_file\n";
1127             }
1128 6 100       20 if ($opt_error ne 'retry') {
1129 4 100       88 warn "SKIP $current_file\n" if $opt_warn{skip};
1130 4         18 next FILE;
1131             }
1132              
1133             # Try again
1134 2         16 binmode STDIN, ':raw';
1135 2 50       18 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 2         5 $content = $slurp->();
1141 2 50       7 if (not defined $content) {
1142 0 0       0 warn "SKIP* $current_file\n" if $opt_warn{skip};
1143 0         0 next FILE;
1144             }
1145 2 100       44 warn "RETRY $current_file\n" if $opt_warn{retry};
1146 2         12 $stat{read_retry}++;
1147 2         7 binmode STDOUT, ':raw';
1148             }
1149              
1150 124         1211 my $matched = grep_data(\$content);
1151              
1152 122         1920 $stat{match_effective} += $matched;
1153 122         444 $stat{file_searched}++;
1154 122         539 $stat{length} += length $content;
1155             } continue {
1156 126         3197 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 126         2504 recover_stdin;
1160 126         8325 binmode STDOUT, ":encoding($output_code)";
1161             }
1162             }
1163              
1164             sub usage {
1165 1     1   7 pod2usage(-verbose => 0, -exitval => "NOEXIT");
1166              
1167 1         513558 my $quote = qr/[\\(){}\|\*?]/;
1168 1         12 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 1         14 print "Version: $version\n";
1182              
1183 1         0 exit 2;
1184             }
1185              
1186             sub open_nextfile {
1187              
1188             ##
1189             ## --chdir
1190             ##
1191 253   66 253   7336 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 253         563 my $file;
1200 253   33     2058 while (defined($file = shift(@ARGV)) ||
      66        
1201             defined($file = $opt_readlist && read_stdin)) {
1202 130 100       723 $file = decode 'utf8', $file unless utf8::is_utf8 $file;
1203 130         700 $file =~ s/\n+$//;
1204              
1205 130 50       1214 if (0) {}
    100          
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 129 50       8715 open(STDIN, '<', $file) or do {
1214 0 0       0 warn "$file: $!\n" unless -l $file;
1215 0         0 next;
1216             };
1217             }
1218              
1219 130 100       1277 if (my @filters = $filter_d->get_filters($file)) {
1220 2         18 push_input_filter({ &FILELABEL => $file }, @filters);
1221             }
1222              
1223 129 50       810 if ($file_code eq 'binary') {
1224 0         0 binmode STDIN, ":raw";
1225             } else {
1226 129         1983 binmode STDIN, ":encoding($file_code)";
1227             }
1228              
1229 129         27178 return $file;
1230             }
1231 123         2176 undef;
1232             }
1233              
1234             ######################################################################
1235              
1236             sub grep_data {
1237 124     124   1128 local *_ = shift;
1238              
1239             ##
1240             ## --begin
1241             ##
1242 124         750 for my $f (@opt_begin) {
1243 0         0 eval { $f->call(&FILELABEL => $current_file) };
  0         0  
1244 0 0       0 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 124 50       928 $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 124         1063 group_index => do { local $_ = $opt_colorindex;
1274 124 100       2526 $opt_capture_group ? /G/i ? /P/i ? 3 : 2 : 1 : 0 },
    100          
    100          
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 124         687 join_blocks => $opt_join_blocks,
1281             )->run;
1282              
1283             ## --postgrep
1284 124         519 for my $f (@opt_postgrep) {
1285 0         0 $f->call($grep);
1286             # remove emptied results
1287 0         0 my $ref = $grep->result_ref;
1288 0         0 @$ref = grep { @{$_} > 0 } @$ref;
  0         0  
  0         0  
1289             }
1290             ## -m
1291 124         392 for my $splice (@splicer) {
1292 1         6 $splice->($grep->result_ref);
1293             }
1294              
1295 124         634 my $matched = $grep->matched;
1296 124 50       1046 if ($opt_l) {
    50          
    100          
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 114 50       501 $progress_reset->() if $progress_reset;
1311             # open output filter
1312 114 100       490 @opt_of && push_output_filter(
1313             { &FILELABEL => $current_file },
1314             \*STDOUT, @opt_of);
1315 112         940 output($grep);
1316 112 100       3982 @opt_of && recover_stdout;
1317             }
1318              
1319             ##
1320             ## --end
1321             ##
1322 122         799 for my $f (@opt_end) {
1323 0         0 $f->call(&FILELABEL => $current_file);
1324             }
1325              
1326 122 50       687 s/./\000/gs if $opt_clean;
1327              
1328 122         3607 $matched;
1329             }
1330              
1331             sub output {
1332 112     112   362 my $grep = shift;
1333 112         417 my $file = $grep->{filename};
1334              
1335 112 50       576 if ($opt_filestyle eq 'once') {
1336 0         0 print $_file->($file), "\n";
1337             }
1338              
1339 112   100     3169 my $need_blockend =
1340             !$opt_all &&
1341             $blockend ne '' &&
1342             ($opt_blockend || $opt_p || $opt_A || $opt_B || @opt_block);
1343              
1344 112         320 my $line = 1;
1345 112         294 my $lastpos = 0;
1346 112         558 my @results = $grep->result;
1347 112         464 for my $rix (keys @results) {
1348 245         622 my $is_top = $rix == 0;
1349 245         527 my $is_bottom = $rix == $#results;
1350              
1351 245         507 my $result = $results[$rix];
1352 245         372 my($blk, @result) = @{$result};
  245         702  
1353 245         607 my($block_start, $block_end, $block_number) = @$blk;
1354 245   100     761 $block_number //= 0;
1355 245         1815 my $block = $grep->cut($block_start, $block_end);
1356              
1357             ## --print
1358 245 50       824 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 245 100       654 if ($opt_n) {
1370 4         15 my $gap = $grep->cut($lastpos, $block_start);
1371 4         36 $line += $gap =~ tr/\n/\n/;
1372             }
1373 245         424 $lastpos = $block_end;
1374              
1375             # when --filestyle and/or --linestyle is "separate"
1376 735         1556 grep { $_ } (
1377             do {
1378 245 100       886 print $_file->($current_file)
1379             if $opt_filestyle eq 'separate';
1380             },
1381             do {
1382 245 100 100     1191 print $_line->($line)
1383             if $opt_n and $opt_linestyle eq 'separate';
1384             },
1385 245 100       399 do {
1386 245 50 33     1156 print $_block->($block_number)
1387             if $opt_b and $opt_blockstyle eq 'separate';
1388             },
1389             ) and print "\n";
1390              
1391 245 50       717 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 245         894 my @slice = $grep->slice_result($result);
1399 245         598 my $mark = "\001";
1400 245         723 for my $i (keys @result) {
1401 314         4383 my($start, $end, $pi, $callback) = $result[$i]->@*;
1402 314         1148 local *b = \$slice[$i * 2 + 1];
1403              
1404             ## run callback function
1405 314 100       931 if ($callback) {
1406 3   33     2 $b = do {
1407 3 50       22 if (ref $callback eq 'CODE') {
    50          
1408 0         0 $callback->($start, $end, $pi, $b);
1409             }
1410             elsif (callable($callback)) {
1411 3         36 $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 314 100       978 if ($opt_join) {
1423 1 50 33     4 if ($opt_n and $opt_linestyle eq 'line') {
1424 0         0 $b =~ s/(?
1425             } else {
1426 1         14 $b =~ s/(?
1427             }
1428             }
1429              
1430 314 50       740 $pi = $uniq_color->index($b) if $opt_uniqcolor;
1431 314         998 $b = index_color($pi, $b);
1432             }
1433              
1434 245         12644 $block = join '', @slice;
1435 245 50       818 next if $block eq "";
1436              
1437 245         466 my @line;
1438 245 100       682 if ($opt_n) {
1439 4 100       18 if ($opt_linestyle eq 'line') {
1440 2 50       79 my $increment = $block =~ /[\n$mark]/ ? 1 : 0;
1441 2         108 $block =~ s{(?:(?$mark)|(?<=\n)|\A)(?=.)}{
1442 2 50       30 push @line, $_line->($line) unless $+{mark};
1443 2         93 $line += $increment;
1444 2 50       36 $+{mark} ? $opt_joinby : '';
1445             }gse;
1446             } else {
1447 2         22 $line += $block =~ tr/\n/\n/;
1448             }
1449             }
1450              
1451 245 50       896 $block = $_text->($block) if $colormap{TEXT} ne "";
1452              
1453 245 100       714 if (@line) {
1454 2         9 $block =~ s/^/shift @line/mge;
  2         22  
1455             }
1456              
1457 245 50 33     938 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 245 100       798 if ($opt_filestyle eq 'line') {
1463 7         118 my $s = $_file->($file);
1464 7         298 $block =~ s/^/$s/mg;
1465             }
1466              
1467 245 50 66     1392 print "$_top\n" if $is_top && $_top ne '';
1468 245         1016 print $block;
1469 245 100 66     16774 print "\n" if $opt_newline and not $block =~ /\n\z/;
1470 245 100       810 print "$_blockend\n" if $need_blockend;
1471 245 100       729 if ($is_bottom) {
1472 112 50       2482 print "$_bottom\n" if $_bottom ne '';
1473             } else {
1474 133 50       2415 print "$_middle\n" if $_middle ne '';
1475             }
1476             }
1477             }
1478              
1479             __END__