File Coverage

blib/lib/App/Greple/L.pm
Criterion Covered Total %
statement 69 69 100.0
branch 14 16 87.5
condition 7 11 63.6
subroutine 19 19 100.0
pod 1 7 14.2
total 110 122 90.1


line stmt bran cond sub pod time code
1             package App::Greple::L;
2              
3 27     27   355837 use v5.14;
  27         102  
4 27     27   147 use warnings;
  27         49  
  27         2948  
5              
6             our $VERSION = '1.01';
7              
8             =head1 NAME
9              
10             L - Greple module to produce result by line numbers
11              
12             =head1 SYNOPSIS
13              
14             greple -ML
15              
16             =head1 VERSION
17              
18             Version 1.01
19              
20             =head1 DESCRIPTION
21              
22             This module allows you to use line numbers to specify patterns or
23             regions which can be used in B options.
24              
25             =over 7
26              
27             =item B<-ML> I
28              
29             If a line number argument immediately follows B<-ML> module option, it
30             is recognized as a line number. Note that, this format implicitly
31             adds the C<--cm N> option to disable the coloring feature. Use the
32             C<--cm @> option to cancel it.
33              
34             Next command will show 42nd line.
35              
36             greple -ML 42 file
37              
38             Multiple lines can be specified by joining with comma:
39              
40             greple -ML 42,52,62
41              
42             Range can be specified by colon:
43              
44             greple -ML 42:84
45              
46             You can also specify the step with range. Next command will print
47             all even lines from line 10 to 20:
48              
49             greple -ML 10:20:2
50              
51             Any of them can be omitted. Next commands print all, odd and even
52             lines.
53              
54             greple -ML :: # all lines
55             greple -ML ::2 # odd lines
56             greple -ML 2::2 # even lines
57              
58             If start and end number is negative, they are subtracted from the
59             maxmum line number. If the end number is prefixed by plus (`+') sign,
60             it is summed with start number. Next commands print top and last 10
61             lines respectively.
62              
63             greple -ML :+9 # top 10 lines
64             greple -ML -9: # last 10 lines
65              
66             If forth parameter is given, it describes how many lines is included
67             in that step cycle. For example, next command prints top 3 lines in
68             every 10 lines.
69              
70             greple -ML ::10:3
71              
72             When step count is omitted, forth value is used if available. Next
73             command print every 10 lines in group.
74              
75             greple -ML :::10 --blockend=-- /etc/services
76              
77             =item B<-L>=I
78              
79             C<-L> is an option to explicitly specify line numbers. All of the
80             above commands can be specified using the C<-L> option. The only
81             difference is that the coloring feature is not automatically disabled.
82              
83             greple -ML -L 42
84             greple -ML -L 10:20:2
85             greple -ML -L :+9
86              
87             B<-L> option can be used multiple times, like:
88              
89             greple -ML -L 42 -L 52 -L 62
90              
91             But this command produce nothing, because each line definitions are
92             taken as a different pattern, and B prints lines only when all
93             patterns matched. You can relax the condition by C<--need 1> option
94             in such case, then you will get expected result. Next example will
95             display 42nd, 52nd and 62nd lines in different colors.
96              
97             greple -ML -L 42 -L 52 -L 62 --need 1
98              
99             Next example print all lines of the file, each line in four different
100             colors.
101              
102             greple -ML -L=1::4 -L=2::4 -L=3::4 -L=4::4 --need 1
103              
104             =item B=I
105              
106             This notation just define function spec, which can be used in
107             patterns, as well as blocks and regions. Actually, B<-L>=I is
108             equivalent to B<--le> B=I.
109              
110             Next command show patterns found in line number 1000-2000 area.
111              
112             greple -ML --inside L=1000:+1000 pattern
113              
114             Next command prints all 10 line blocks which include the pattern.
115              
116             greple -ML --block L=:::10 pattern
117              
118             In this case, however, it is faster and easier to use regex.
119              
120             greple --block '(.*\n){1,10}' pattern
121              
122             =item B<--offload>=I
123              
124             Set the offload command to retrieve the desired line numbers. The
125             numbers in the output, starting at the beginning of the line, are
126             treated as line numbers. This is compatible with B output.
127              
128             Next command print 10 to 20 lines.
129              
130             greple -ML --offload 'seq 10 20'
131              
132             =back
133              
134             Using this module, it is impossible to give single C in command
135             line arguments. Use like B<--le=L> to search letter C. You have a
136             file named F? Stop substitution by placing C<--> before the target
137             files.
138              
139             =head1 INSTALL
140              
141             =head2 CPANMINUS
142              
143             $ cpanm App::Greple::L
144              
145             =head2 GITHUB
146              
147             $ cpanm https://github.com/kaz-utashiro/greple-L.git
148              
149             =head1 SEE ALSO
150              
151             L, L
152              
153             L
154              
155             =head1 AUTHOR
156              
157             Kazumasa Utashiro
158              
159             =head1 LICENSE
160              
161             Copyright 2014-2025 Kazumasa Utashiro.
162              
163             This library is free software; you can redistribute it and/or modify
164             it under the same terms as Perl itself.
165              
166             =cut
167              
168 27     27   173 use Carp;
  27         565  
  27         2979  
169 27     27   289 use List::Util qw(min max reduce);
  27         94  
  27         2200  
170 27     27   822 use Data::Dumper;
  27         10103  
  27         1714  
171              
172 27     27   143 use Exporter qw(import);
  27         47  
  27         1960  
173             our @EXPORT = qw(&line &offload);
174              
175 27     27   140 use List::Util qw(pairmap);
  27         43  
  27         1516  
176 27     27   847 use App::Greple::Common;
  27         595  
  27         1814  
177 27     27   801 use App::Greple::Regions qw(match_borders borders_to_regions);
  27         3717  
  27         11434  
178              
179             my %param = (
180             auto => 1,
181             );
182              
183             sub is_number {
184 43     43 0 1298 $_[0] =~ m{^
185             (? (?: [-+]?\d+ | : )+ )
186             (?: , (?&SPEC) )*
187             $
188             }x;
189             }
190              
191             sub finalize {
192 26     26 0 37598 my($app, $argv) = @_;
193 26 50       108 $param{auto} or return;
194 26         66 my $number = 0;
195 26         115 for (my $i = 0; $i < @$argv; $i++) {
196 43         108 local $_ = $argv->[$i];
197 43 100 66     100 (is_number($_) and ! -f $_) or last;
198 17         127 splice(@$argv, $i, 1,
199             my @new = ('--le', sprintf("&line(%s)", $_)));
200 17         39 $i += @new - 1;
201 17         57 $number++;
202             }
203 26 100       131 if ($number > 0) {
204 13         31 my @default = qw(--cm N);
205 13 100       134 push @default, qw(--need=1) if $number > 1;
206 13         104 $app->setopt(default => @default);
207             }
208             }
209              
210 27     27   765 use Getopt::EX::Numbers;
  27         7021  
  27         17758  
211              
212             sub line_to_region {
213 31     31 0 154 state($target, @lines, $numbers);
214 31 100 66     297 if (not defined $target or $target != \$_) {
215 26         157 $target = \$_;
216 26         291 @lines = ([0, 0], borders_to_regions match_borders qr/^/m);
217 26         7976 $numbers = Getopt::EX::Numbers->new(min => 1, max => $#lines);
218             }
219 31         1517 do {
220 98         2379 map { [ $lines[$_->[0]]->[0], $lines[$_->[1]]->[1] ] }
221 147         1134 sort { $a->[0] <=> $b->[0] }
222 33         332 map { $numbers->parse($_)->range }
223 31         206 map { split /,+/ }
  33         185  
224             @_;
225             };
226             }
227              
228             sub line {
229 62 100   62 1 499 my @lines = pairmap { $a ne &FILELABEL ? $a : () } @_;
  30     30   5014  
230 30         225 line_to_region(@lines);
231             }
232              
233             sub line_to_range {
234 1         15 map { @$_ } reduce {
235 3 100 66 3   69 if (@$a > 0 and $a->[-1][1] + 1 == $b) {
236 2         4 $a->[-1][1] = $b;
237             } else {
238 1         9 push @$a, [ $b, $b ];
239             }
240 3         47 $a;
241 1     1 0 87 } [], @_;
242             }
243              
244             sub range_to_spec {
245             map {
246 1     1 0 5 my($a, $b) = @$_;
  1         4  
247 1 50       14 $a == $b ? "$a" : "$a:$b"
248             } @_;
249             }
250              
251             sub offload {
252 1     1 0 106 our $offload_command;
253 1   50     5810 my $result = qx($offload_command) || die "$offload_command: exec error\n";
254 1         61 line_to_region(range_to_spec(line_to_range($result =~ /^\d+/mg)));
255             }
256              
257             1;
258              
259             __DATA__