line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
## no critic : Modules::ProhibitAutomaticExportation |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
package File::SortedSeek::PERLANCAR; |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY |
6
|
|
|
|
|
|
|
our $DATE = '2021-04-25'; # DATE |
7
|
|
|
|
|
|
|
our $DIST = 'File-SortedSeek-PERLANCAR'; # DIST |
8
|
|
|
|
|
|
|
our $VERSION = '0.015.1'; # VERSION |
9
|
|
|
|
|
|
|
|
10
|
1
|
|
|
1
|
|
77034
|
use strict; |
|
1
|
|
|
|
|
12
|
|
|
1
|
|
|
|
|
42
|
|
11
|
1
|
|
|
1
|
|
7
|
use warnings; |
|
1
|
|
|
|
|
1
|
|
|
1
|
|
|
|
|
29
|
|
12
|
1
|
|
|
1
|
|
573
|
use Time::Local; |
|
1
|
|
|
|
|
3136
|
|
|
1
|
|
|
|
|
88
|
|
13
|
|
|
|
|
|
|
require Exporter; |
14
|
|
|
|
|
|
|
|
15
|
1
|
|
|
1
|
|
8
|
use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS $VERSION ); |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
1928
|
|
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
@ISA = qw( Exporter ); |
18
|
|
|
|
|
|
|
@EXPORT = (); |
19
|
|
|
|
|
|
|
@EXPORT_OK = qw( binsearch alphabetic numeric find_time get_between get_last ); |
20
|
|
|
|
|
|
|
%EXPORT_TAGS = ( 'all' => \@EXPORT_OK ); |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
my $descending = 0; |
23
|
|
|
|
|
|
|
my $cuddle = 0; |
24
|
|
|
|
|
|
|
my $line_length = 80; |
25
|
|
|
|
|
|
|
my $error_msg = ''; |
26
|
|
|
|
|
|
|
my $silent = 0; |
27
|
|
|
|
|
|
|
my $exact_match = 0; |
28
|
|
|
|
|
|
|
my %months = ( Jan => 0, Feb => 1, Mar => 2, Apr => 3, |
29
|
|
|
|
|
|
|
May => 4, Jun => 5, Jul => 6, Aug => 7, |
30
|
|
|
|
|
|
|
Sep => 8, Oct => 9, Nov => 10,Dec => 11); |
31
|
|
|
|
|
|
|
my $default_rec_sep = qw/\015\012|\015|\012/; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# some subs to set optional vars OO style |
34
|
2
|
|
|
2
|
1
|
5853
|
sub set_cuddle { $cuddle = 1 }; |
35
|
2
|
|
|
2
|
1
|
472
|
sub set_no_cuddle { $cuddle = 0 }; |
36
|
0
|
|
|
0
|
1
|
0
|
sub set_descending { $descending = 1 }; |
37
|
0
|
|
|
0
|
1
|
0
|
sub set_ascending { $descending = 0 }; |
38
|
|
|
|
0
|
0
|
|
sub set_max_tries { }; # legacy method, no effect |
39
|
|
|
|
0
|
0
|
|
sub set_line_length { }; # legacy method, no effect |
40
|
0
|
|
|
0
|
1
|
0
|
sub set_silent { $silent = 1 }; |
41
|
0
|
|
|
0
|
1
|
0
|
sub set_verbose { $silent = 0 }; |
42
|
31
|
|
|
31
|
1
|
20905
|
sub was_exact { $exact_match }; |
43
|
0
|
|
|
0
|
1
|
0
|
sub error { $error_msg; }; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
sub binsearch { |
46
|
10
|
|
|
10
|
1
|
9138
|
local *FILE = shift; |
47
|
10
|
|
|
|
|
22
|
my $string = shift; |
48
|
10
|
|
|
|
|
14
|
my $compare_ref = shift; |
49
|
10
|
|
|
|
|
19
|
my $munge_ref = shift; |
50
|
10
|
|
|
|
|
13
|
my $minoffset = shift; |
51
|
10
|
|
|
|
|
17
|
my $maxoffset = shift; |
52
|
10
|
|
|
|
|
16
|
$error_msg = ''; |
53
|
10
|
50
|
|
|
|
22
|
if ($descending) { |
54
|
0
|
|
|
0
|
|
0
|
_look( *FILE, $string, sub { -$compare_ref->(@_) }, $munge_ref, $minoffset, $maxoffset ); |
|
0
|
|
|
|
|
0
|
|
55
|
|
|
|
|
|
|
} else { |
56
|
10
|
|
|
|
|
27
|
_look( *FILE, $string, $compare_ref, $munge_ref, $minoffset, $maxoffset ); |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
|
60
|
702
|
50
|
|
702
|
|
1302
|
sub _alphabetic_compare { $descending ? $_[1] cmp $_[0] : $_[0] cmp $_[1] } |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
sub alphabetic { |
63
|
52
|
|
|
52
|
1
|
36651
|
local *FILE = shift; |
64
|
52
|
|
|
|
|
148
|
my $string = shift; |
65
|
52
|
|
|
|
|
81
|
my $munge_ref = shift; |
66
|
52
|
|
|
|
|
82
|
my $minoffset = shift; |
67
|
52
|
|
|
|
|
83
|
my $maxoffset = shift; |
68
|
52
|
|
|
|
|
86
|
$error_msg = ''; |
69
|
52
|
|
|
|
|
153
|
_look( *FILE, $string, \&_alphabetic_compare, $munge_ref, $minoffset, $maxoffset ); |
70
|
|
|
|
|
|
|
} |
71
|
|
|
|
|
|
|
|
72
|
207
|
50
|
|
207
|
|
441
|
sub _numeric_compare { $descending ? $_[1] <=> $_[0] : $_[0] <=> $_[1] } |
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub numeric { |
75
|
41
|
|
|
41
|
1
|
20872
|
local *FILE = shift; |
76
|
41
|
|
|
|
|
78
|
my $number = shift; |
77
|
41
|
|
|
|
|
68
|
my $munge_ref = shift; |
78
|
41
|
|
|
|
|
64
|
my $minoffset = shift; |
79
|
41
|
|
|
|
|
61
|
my $maxoffset = shift; |
80
|
41
|
|
|
|
|
84
|
$error_msg = ''; |
81
|
41
|
|
|
|
|
117
|
_look( *FILE, $number, \&_numeric_compare, $munge_ref, $minoffset, $maxoffset ); |
82
|
|
|
|
|
|
|
} |
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
sub find_time { |
85
|
0
|
|
|
0
|
1
|
0
|
local *FILE = shift; |
86
|
0
|
|
0
|
|
|
0
|
my $find = shift || time; |
87
|
0
|
|
|
|
|
0
|
my $not_gmtime = shift; |
88
|
0
|
|
|
|
|
0
|
$error_msg = ''; |
89
|
0
|
0
|
|
|
|
0
|
$find = get_epoch_seconds($find,$not_gmtime) unless $find =~ m/^[\d.]+$/; |
90
|
0
|
|
|
|
|
0
|
_look( *FILE, $find, \&_numeric_compare, \&get_epoch_seconds ); |
91
|
|
|
|
|
|
|
} |
92
|
|
|
|
|
|
|
|
93
|
|
|
|
|
|
|
sub get_epoch_seconds { |
94
|
0
|
|
|
0
|
1
|
0
|
my ($line, $not_gmtime) = @_; |
95
|
0
|
0
|
|
|
|
0
|
return undef unless defined $line; |
96
|
0
|
|
|
|
|
0
|
my ($wday,$mon,$mday,$hours,$min,$sec,$year); |
97
|
|
|
|
|
|
|
# look for asctime format: Tue May 27 15:45:00 2008 |
98
|
|
|
|
|
|
|
# ignore wday token as this is often dropped ie linux kernel messages |
99
|
0
|
0
|
|
|
|
0
|
if ($line =~ m/(\w{3})\s+(\d{1,2})\s+(\d\d):(\d\d):(\d\d)\s+(\d{4})/ ) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
100
|
0
|
|
|
|
|
0
|
($mon,$mday,$hours,$min,$sec,$year) = ($1,$2,$3,$4,$5,$6); |
101
|
|
|
|
|
|
|
} |
102
|
|
|
|
|
|
|
# look for apache time format: [21/May/2008:17:49:39 +1000] |
103
|
|
|
|
|
|
|
# ignore the time offset |
104
|
|
|
|
|
|
|
elsif($line =~ m!\[(\d{1,2})/(\w{3})/(\d{4}):(\d\d):(\d\d):(\d\d)!x ) { |
105
|
0
|
|
|
|
|
0
|
($mday,$mon,$year,$hours,$min,$sec) = ($1,$2,$3,$4,$5,$6); |
106
|
|
|
|
|
|
|
} |
107
|
|
|
|
|
|
|
# look for straight epochtime data (ie squid log) |
108
|
|
|
|
|
|
|
elsif($line =~ m/^(\d+)/) { |
109
|
0
|
|
|
|
|
0
|
return $1; |
110
|
|
|
|
|
|
|
} |
111
|
0
|
0
|
|
|
|
0
|
unless ($year) { |
112
|
0
|
|
|
|
|
0
|
$error_msg = "Unable to find time like string in line:\n$line"; |
113
|
0
|
0
|
|
|
|
0
|
warn $error_msg unless $silent; |
114
|
0
|
|
|
|
|
0
|
return undef; |
115
|
|
|
|
|
|
|
} |
116
|
0
|
|
|
|
|
0
|
$mon = $months{$mon}; # convert to numerical months 0 - 11 |
117
|
0
|
0
|
|
|
|
0
|
return $not_gmtime ? timelocal($sec,$min,$hours,$mday,$mon,$year): |
118
|
|
|
|
|
|
|
timegm($sec,$min,$hours,$mday,$mon,$year); |
119
|
|
|
|
|
|
|
} |
120
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
sub get_between { |
122
|
0
|
|
|
0
|
1
|
0
|
local *FILE = shift; |
123
|
0
|
|
0
|
|
|
0
|
my $begin = shift || 0; |
124
|
0
|
|
0
|
|
|
0
|
my $finish = shift || 0; |
125
|
0
|
|
0
|
|
|
0
|
my $rec_sep = shift || $default_rec_sep; |
126
|
0
|
|
|
|
|
0
|
$error_msg = ''; |
127
|
0
|
0
|
|
|
|
0
|
($begin , $finish) = ($finish, $begin) if $begin > $finish; |
128
|
0
|
|
|
|
|
0
|
my $bytes = $finish - $begin; |
129
|
0
|
|
|
|
|
0
|
sysseek FILE, $begin, 0; |
130
|
0
|
|
|
|
|
0
|
my $read = sysread(FILE, my $buffer, $bytes); |
131
|
0
|
0
|
|
|
|
0
|
if ( $read < $bytes ) { |
132
|
0
|
|
|
|
|
0
|
$error_msg = "Short read\nWanted: $bytes Got: $read\n"; |
133
|
0
|
0
|
|
|
|
0
|
warn $error_msg unless $silent; |
134
|
0
|
|
|
|
|
0
|
return undef; |
135
|
|
|
|
|
|
|
} |
136
|
0
|
|
|
|
|
0
|
$buffer = substr $buffer, 0, $bytes; |
137
|
0
|
|
|
|
|
0
|
my @lines = split $rec_sep, $buffer; |
138
|
0
|
0
|
|
|
|
0
|
return wantarray ? @lines : [ @lines ]; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
sub get_last { |
142
|
0
|
|
|
0
|
1
|
0
|
local *FILE = shift; |
143
|
0
|
|
|
|
|
0
|
my $num_lines = shift; |
144
|
0
|
|
0
|
|
|
0
|
my $rec_sep = shift || $default_rec_sep; |
145
|
0
|
|
|
|
|
0
|
$error_msg = ''; |
146
|
0
|
0
|
|
|
|
0
|
my @stat = stat(FILE) or return undef; |
147
|
0
|
|
|
|
|
0
|
my($size,$blksize) = @stat[7,11]; |
148
|
0
|
|
0
|
|
|
0
|
$blksize ||= 8192; |
149
|
|
|
|
|
|
|
# grab the first chunk back from eof at block offset |
150
|
0
|
|
0
|
|
|
0
|
my $pos = $size - (($size % $blksize)|| $blksize ); |
151
|
0
|
|
|
|
|
0
|
my $file = ''; |
152
|
0
|
|
|
|
|
0
|
my ($buf, $lines); |
153
|
0
|
|
|
|
|
0
|
for(;;) { |
154
|
0
|
0
|
|
|
|
0
|
$pos = 0 if $pos < 0; |
155
|
0
|
|
|
|
|
0
|
sysseek(FILE,$pos,0); |
156
|
0
|
0
|
|
|
|
0
|
sysread(FILE, $buf, $blksize) or last; # returns 0 at eof; |
157
|
0
|
|
|
|
|
0
|
$file = $buf.$file; |
158
|
0
|
|
|
|
|
0
|
my $lines = () = $file =~ m/$rec_sep/g; |
159
|
0
|
0
|
0
|
|
|
0
|
last if $lines > $num_lines or $pos == 0; |
160
|
0
|
|
|
|
|
0
|
$pos -= $blksize; |
161
|
|
|
|
|
|
|
} |
162
|
0
|
|
|
|
|
0
|
my @file = split /$rec_sep/, $file; |
163
|
0
|
0
|
|
|
|
0
|
if ( $num_lines > @file ) { |
164
|
0
|
|
|
|
|
0
|
$error_msg = "Unable to find $num_lines\n"; |
165
|
0
|
0
|
|
|
|
0
|
warn $error_msg unless $silent; |
166
|
0
|
0
|
|
|
|
0
|
return wantarray ? @file : \@file; |
167
|
|
|
|
|
|
|
} |
168
|
|
|
|
|
|
|
else { |
169
|
0
|
|
|
|
|
0
|
$num_lines = $#file - $num_lines + 1; |
170
|
0
|
0
|
|
|
|
0
|
return wantarray ? @file[$num_lines..$#file] : [@file[$num_lines..$#file]]; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
} |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
# Modified version of Perl Search::Dict's look() |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
sub _look { |
177
|
103
|
|
|
103
|
|
175
|
local *FILE = shift; |
178
|
103
|
|
|
|
|
217
|
my($key,$comp,$xfrm,$minoffset,$maxoffset) = @_; |
179
|
103
|
|
|
|
|
183
|
local $_; |
180
|
103
|
50
|
|
|
|
253
|
return undef if not defined $key; |
181
|
103
|
|
|
|
|
210
|
my($size, $blksize); |
182
|
103
|
100
|
|
|
|
250
|
if (defined $maxoffset) { |
183
|
16
|
|
|
|
|
45
|
$size = $maxoffset; |
184
|
|
|
|
|
|
|
} else { |
185
|
87
|
50
|
|
|
|
1306
|
my @stat = stat(FILE) or return undef; |
186
|
87
|
|
|
|
|
303
|
($size, $blksize) = @stat[7,11]; |
187
|
87
|
|
|
|
|
208
|
$maxoffset = $size-1; |
188
|
|
|
|
|
|
|
} |
189
|
103
|
|
100
|
|
|
344
|
$blksize ||= 8192; |
190
|
103
|
|
100
|
|
|
436
|
$minoffset ||= 0; |
191
|
103
|
|
|
|
|
171
|
$size -= $minoffset; |
192
|
|
|
|
|
|
|
|
193
|
|
|
|
|
|
|
# find the right block |
194
|
103
|
|
|
|
|
310
|
my($min, $max) = (0, int($size / $blksize)); |
195
|
103
|
|
|
|
|
158
|
my $mid; |
196
|
103
|
|
|
|
|
278
|
while ($max - $min > 1) { |
197
|
0
|
|
|
|
|
0
|
$mid = int(($max + $min) / 2); |
198
|
0
|
0
|
|
|
|
0
|
seek(FILE, $mid * $blksize + $minoffset, 0) or return undef; |
199
|
0
|
0
|
|
|
|
0
|
if $mid; # probably a partial line |
200
|
0
|
|
|
|
|
0
|
$_ = ; |
201
|
0
|
0
|
|
|
|
0
|
$_ = $xfrm->($_) if $xfrm; |
202
|
0
|
|
|
|
|
0
|
chomp; |
203
|
0
|
0
|
0
|
|
|
0
|
(defined($_) && $comp->($_, $key) < 0) ? $min = $mid : $max = $mid; |
204
|
|
|
|
|
|
|
} |
205
|
|
|
|
|
|
|
# find the right line |
206
|
103
|
|
|
|
|
178
|
$min *= $blksize; |
207
|
103
|
50
|
|
|
|
1213
|
seek(FILE,$min+$minoffset,0) or return undef; |
208
|
103
|
50
|
|
|
|
301
|
if $min; # probably a partial line |
209
|
103
|
|
|
|
|
180
|
my $prev_min = $min; |
210
|
103
|
|
|
|
|
147
|
for (;;) { |
211
|
967
|
|
|
|
|
1462
|
$min = tell(FILE) - $minoffset; |
212
|
967
|
100
|
|
|
|
2824
|
defined($_ = ) or last; |
213
|
964
|
50
|
|
|
|
1811
|
$_ = $xfrm->($_) if $xfrm; |
214
|
964
|
|
|
|
|
1750
|
chomp; |
215
|
964
|
|
|
|
|
1591
|
my $cmp = $comp->($_, $key); |
216
|
964
|
100
|
|
|
|
1760
|
$exact_match = $cmp==0 ? 1 : 0; |
217
|
964
|
100
|
100
|
|
|
2631
|
if(!$cuddle and $cmp >= 0){ |
218
|
91
|
|
|
|
|
915
|
seek(FILE,$min+$minoffset,0); |
219
|
91
|
100
|
|
|
|
267
|
if ($min+$minoffset > $maxoffset) { |
220
|
1
|
|
|
|
|
3
|
$exact_match = 0; |
221
|
1
|
|
|
|
|
8
|
return undef; |
222
|
|
|
|
|
|
|
} else { |
223
|
90
|
|
|
|
|
479
|
return $min+$minoffset; |
224
|
|
|
|
|
|
|
} |
225
|
|
|
|
|
|
|
} |
226
|
873
|
100
|
100
|
|
|
1682
|
if($cuddle and $cmp > 0){ |
227
|
9
|
|
|
|
|
93
|
seek(FILE,$prev_min+$minoffset,0); |
228
|
9
|
100
|
|
|
|
33
|
if ($prev_min+$minoffset > $maxoffset) { |
229
|
1
|
|
|
|
|
2
|
$exact_match = 0; |
230
|
1
|
|
|
|
|
6
|
return undef; |
231
|
|
|
|
|
|
|
} else { |
232
|
8
|
|
|
|
|
44
|
return $prev_min+$minoffset; |
233
|
|
|
|
|
|
|
} |
234
|
|
|
|
|
|
|
} |
235
|
864
|
|
|
|
|
1239
|
$prev_min = $min; |
236
|
|
|
|
|
|
|
} |
237
|
3
|
|
|
|
|
18
|
return undef; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
1; |
241
|
|
|
|
|
|
|
# ABSTRACT: A fork of File::SortedSeek with some more options |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
__END__ |