| line | stmt | bran | cond | sub | pod | time | code | 
| 1 | 9 |  |  | 9 |  | 445998 | use strict; | 
|  | 9 |  |  |  |  | 15 |  | 
|  | 9 |  |  |  |  | 252 |  | 
| 2 | 9 |  |  | 9 |  | 37 | use warnings; | 
|  | 9 |  |  |  |  | 12 |  | 
|  | 9 |  |  |  |  | 308 |  | 
| 3 |  |  |  |  |  |  |  | 
| 4 |  |  |  |  |  |  | package App::dategrep; | 
| 5 | 9 |  |  | 9 |  | 3168 | use App::dategrep::Date qw(intervall_to_epoch date_to_epoch minutes_ago); | 
|  | 9 |  |  |  |  | 22 |  | 
|  | 9 |  |  |  |  | 768 |  | 
| 6 | 9 |  |  | 9 |  | 4225 | use App::dategrep::Iterators; | 
|  | 9 |  |  |  |  | 31 |  | 
|  | 9 |  |  |  |  | 259 |  | 
| 7 | 9 |  |  | 9 |  | 4432 | use Config::Tiny; | 
|  | 9 |  |  |  |  | 6780 |  | 
|  | 9 |  |  |  |  | 228 |  | 
| 8 | 9 |  |  | 9 |  | 4239 | use Pod::Usage; | 
|  | 9 |  |  |  |  | 228850 |  | 
|  | 9 |  |  |  |  | 1026 |  | 
| 9 | 9 |  |  | 9 |  | 6367 | use Getopt::Long; | 
|  | 9 |  |  |  |  | 67287 |  | 
|  | 9 |  |  |  |  | 34 |  | 
| 10 | 9 |  |  | 9 |  | 1109 | use File::Basename qw(basename); | 
|  | 9 |  |  |  |  | 10 |  | 
|  | 9 |  |  |  |  | 560 |  | 
| 11 | 9 |  |  | 9 |  | 45 | use base 'Exporter'; | 
|  | 9 |  |  |  |  | 15 |  | 
|  | 9 |  |  |  |  | 1083 |  | 
| 12 |  |  |  |  |  |  | our @EXPORT_OK = qw(run); | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | our $VERSION = '0.58'; | 
| 15 |  |  |  |  |  |  |  | 
| 16 |  |  |  |  |  |  | our $app; | 
| 17 |  |  |  |  |  |  |  | 
| 18 |  |  |  |  |  |  | BEGIN { | 
| 19 | 9 |  |  | 9 |  | 6532 | $app = basename($0); | 
| 20 |  |  |  |  |  |  | } | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | sub error { | 
| 23 | 7 |  |  | 7 | 0 | 16 | my ( $msg, $rc ) = @_; | 
| 24 | 7 | 50 |  |  |  | 20 | $rc = defined $rc ? $rc : 1; | 
| 25 | 7 |  |  |  |  | 18 | chomp($msg); | 
| 26 | 7 |  |  |  |  | 275 | warn "$app: $msg\n"; | 
| 27 | 7 |  |  |  |  | 64 | return $rc; | 
| 28 |  |  |  |  |  |  | } | 
| 29 |  |  |  |  |  |  |  | 
| 30 |  |  |  |  |  |  | sub run { | 
| 31 | 37 |  |  | 37 | 0 | 63356 | my %options; | 
| 32 | 37 | 100 |  |  |  | 133 | if ( $ENV{DATEGREP_DEFAULT_FORMAT} ) { | 
| 33 | 19 |  |  |  |  | 56 | $options{format} = $ENV{DATEGREP_DEFAULT_FORMAT}; | 
| 34 |  |  |  |  |  |  | } | 
| 35 |  |  |  |  |  |  |  | 
| 36 | 37 |  |  |  |  | 203 | my $rc = GetOptions( | 
| 37 |  |  |  |  |  |  | \%options,        'start|from=s', | 
| 38 |  |  |  |  |  |  | 'end|to=s',       'format=s', | 
| 39 |  |  |  |  |  |  | 'last-minutes=i', 'multiline!', | 
| 40 |  |  |  |  |  |  | 'blocksize=i',    'help|?', | 
| 41 |  |  |  |  |  |  | 'sort-files',     'man', | 
| 42 |  |  |  |  |  |  | 'configfile=s',   'interleave', | 
| 43 |  |  |  |  |  |  | 'byte-offsets',   'debug=s', | 
| 44 |  |  |  |  |  |  | 'version!',       'skip-unparsable!', | 
| 45 |  |  |  |  |  |  | ); | 
| 46 | 37 | 100 |  |  |  | 29661 | if ( !$rc ) { | 
| 47 | 1 |  |  |  |  | 6 | pod2usage( -exitstatus => "NOEXIT", -verbose => 0 ); | 
| 48 | 1 |  |  |  |  | 4099 | return 2; | 
| 49 |  |  |  |  |  |  | } | 
| 50 |  |  |  |  |  |  |  | 
| 51 | 36 | 50 |  |  |  | 125 | if ( $options{version} ) { | 
| 52 | 0 |  |  |  |  | 0 | print "$VERSION\n"; | 
| 53 | 0 |  |  |  |  | 0 | return 0; | 
| 54 |  |  |  |  |  |  | } | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 36 | 50 |  |  |  | 94 | if ( $options{help} ) { | 
| 57 | 0 |  |  |  |  | 0 | pod2usage( -verbose => 1, -exitstatus => 'NOEXIT' ); | 
| 58 | 0 |  |  |  |  | 0 | return 0; | 
| 59 |  |  |  |  |  |  | } | 
| 60 | 36 | 50 |  |  |  | 88 | if ( $options{man} ) { | 
| 61 | 0 |  |  |  |  | 0 | pod2usage( -exitstatus => "NOEXIT", -verbose => 2 ); | 
| 62 | 0 |  |  |  |  | 0 | return 0; | 
| 63 |  |  |  |  |  |  | } | 
| 64 |  |  |  |  |  |  |  | 
| 65 | 36 |  |  |  |  | 133 | my $config = loadconfig( $options{configfile} ); | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 36 |  |  |  |  | 179 | my %named_formats = ( | 
| 68 |  |  |  |  |  |  | 'iso8601' => "%O%Z", | 
| 69 |  |  |  |  |  |  | 'rsyslog' => "%b %e %H:%M:%S", | 
| 70 |  |  |  |  |  |  | 'apache'  => "%d/%b/%Y:%T %z", | 
| 71 |  |  |  |  |  |  | ); | 
| 72 |  |  |  |  |  |  |  | 
| 73 | 36 | 100 |  |  |  | 100 | if ( exists $config->{formats} ) { | 
| 74 | 1 |  |  |  |  | 3 | %named_formats = ( %named_formats, %{ $config->{formats} } ); | 
|  | 1 |  |  |  |  | 5 |  | 
| 75 |  |  |  |  |  |  | } | 
| 76 |  |  |  |  |  |  |  | 
| 77 | 36 | 100 |  |  |  | 88 | if ( not defined $options{'format'} ) { | 
| 78 | 1 |  |  |  |  | 3 | return error("--format is a required parameter"); | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 | 35 | 100 |  |  |  | 92 | if ( exists $named_formats{ $options{'format'} } ) { | 
| 82 | 12 |  |  |  |  | 28 | $options{'format'} = $named_formats{ $options{'format'} }; | 
| 83 |  |  |  |  |  |  | } | 
| 84 |  |  |  |  |  |  |  | 
| 85 | 35 | 100 |  |  |  | 107 | if ( $options{'skip-unparsable'} ) { | 
| 86 | 1 |  |  |  |  | 2 | $options{'multiline'} = 0; | 
| 87 |  |  |  |  |  |  | } | 
| 88 |  |  |  |  |  |  |  | 
| 89 | 35 |  |  |  |  | 97 | my ( $start, $end ) = ( 0, time() ); | 
| 90 |  |  |  |  |  |  |  | 
| 91 | 35 | 100 |  |  |  | 167 | if ( defined $options{'start'} ) { | 
| 92 | 25 |  |  |  |  | 250 | ($start) = intervall_to_epoch( $options{'start'}, $options{'format'} ); | 
| 93 | 25 | 100 |  |  |  | 12394 | return error("Illegal start time.") if not defined $start; | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 33 | 100 |  |  |  | 122 | if ( defined $options{'end'} ) { | 
| 97 | 22 |  |  |  |  | 76 | ($end) = intervall_to_epoch( $options{'end'}, $options{'format'} ); | 
| 98 | 22 | 100 |  |  |  | 4443 | return error("Illegal end time.") if not defined $end; | 
| 99 |  |  |  |  |  |  | } | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 32 | 100 |  |  |  | 88 | if ( defined $options{'last-minutes'} ) { | 
| 102 | 1 |  |  |  |  | 7 | ( $start, $end ) = minutes_ago( $options{'last-minutes'} ); | 
| 103 |  |  |  |  |  |  | } | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 32 | 100 |  |  |  | 625 | if ( $end < $start ) { | 
| 106 | 5 |  |  |  |  | 8 | ( $start, $end ) = ( $end, $start ); | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  |  | 
| 109 | 32 | 100 | 66 |  |  | 106 | if ( defined $options{'debug'} && $options{'debug'} eq 'time' ) { | 
| 110 | 1 |  |  |  |  | 29 | print "Start: $start End: $end\n"; | 
| 111 | 1 |  |  |  |  | 7 | return 0; | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 31 | 100 |  |  |  | 75 | if ( !@ARGV ) { | 
| 115 | 2 |  |  |  |  | 4 | push @ARGV, '-'; | 
| 116 |  |  |  |  |  |  | } | 
| 117 |  |  |  |  |  |  |  | 
| 118 | 31 |  |  |  |  | 45 | eval { | 
| 119 |  |  |  |  |  |  |  | 
| 120 | 31 | 100 |  |  |  | 85 | if ( $options{'byte-offsets'} ) { | 
| 121 | 2 | 50 | 33 |  |  | 45 | if ( @ARGV == 1 and -f $ARGV[0] ) { | 
| 122 | 2 |  |  |  |  | 43 | my $iter = App::dategrep::Iterator::File->new( | 
| 123 |  |  |  |  |  |  | %options, | 
| 124 |  |  |  |  |  |  | filename => $ARGV[0], | 
| 125 |  |  |  |  |  |  | start    => $start, | 
| 126 |  |  |  |  |  |  | end      => $end, | 
| 127 |  |  |  |  |  |  | ); | 
| 128 | 2 |  |  |  |  | 13 | my ( $byte_beg, $byte_end ) = $iter->byte_offsets(); | 
| 129 | 2 | 100 |  |  |  | 6 | if ( not defined $byte_end ) { | 
| 130 | 1 |  |  |  |  | 15 | $byte_end = ( stat( $iter->fh ) )[7]; | 
| 131 |  |  |  |  |  |  | } | 
| 132 | 2 |  |  |  |  | 95 | print "$byte_beg $byte_end\n"; | 
| 133 | 2 |  |  |  |  | 32 | return 0; | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 | 29 |  |  |  |  | 745 | my $iterators = App::dategrep::Iterators->new( | 
| 138 |  |  |  |  |  |  | %options, | 
| 139 |  |  |  |  |  |  | filenames => \@ARGV, | 
| 140 |  |  |  |  |  |  | start     => $start, | 
| 141 |  |  |  |  |  |  | end       => $end, | 
| 142 |  |  |  |  |  |  | ); | 
| 143 |  |  |  |  |  |  |  | 
| 144 | 27 | 100 | 66 |  |  | 425 | if ( $options{'interleave'} && @ARGV > 1 ) { | 
| 145 | 1 |  |  |  |  | 4 | $iterators->interleave(); | 
| 146 | 1 |  |  |  |  | 6 | return 0; | 
| 147 |  |  |  |  |  |  | } | 
| 148 |  |  |  |  |  |  |  | 
| 149 | 26 | 100 | 66 |  |  | 91 | if ( $options{'sort-files'} && @ARGV > 1 ) { | 
| 150 | 1 |  |  |  |  | 5 | $iterators->sort; | 
| 151 |  |  |  |  |  |  | } | 
| 152 |  |  |  |  |  |  |  | 
| 153 | 26 |  |  |  |  | 91 | for my $iter ( $iterators->as_array ) { | 
| 154 | 28 | 50 |  |  |  | 58 | if ($iter) { | 
| 155 | 28 |  |  |  |  | 110 | $iter->print; | 
| 156 |  |  |  |  |  |  | } | 
| 157 |  |  |  |  |  |  | } | 
| 158 |  |  |  |  |  |  | }; | 
| 159 | 31 | 100 |  |  |  | 433 | return error($@) if $@; | 
| 160 | 28 |  |  |  |  | 460 | return 0; | 
| 161 |  |  |  |  |  |  | } | 
| 162 |  |  |  |  |  |  |  | 
| 163 |  |  |  |  |  |  | sub loadconfig { | 
| 164 | 36 |  |  | 36 | 0 | 75 | my $configfile = shift; | 
| 165 | 36 | 100 | 66 |  |  | 172 | if ( not $configfile and $ENV{HOME} ) { | 
| 166 | 34 |  |  |  |  | 67 | $configfile = "$ENV{HOME}/.dategreprc"; | 
| 167 |  |  |  |  |  |  | } | 
| 168 | 36 | 100 | 66 |  |  | 554 | if ( not defined $configfile or not -e $configfile ) { | 
| 169 | 35 |  |  |  |  | 95 | return; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  |  | 
| 172 | 1 |  |  |  |  | 11 | my $config = Config::Tiny->read($configfile); | 
| 173 | 1 | 50 |  |  |  | 115 | if ( not defined $config ) { | 
| 174 | 0 |  |  |  |  | 0 | die "Error while parsing configfile: " . Config::Tiny->errstr() . "\n"; | 
| 175 |  |  |  |  |  |  | } | 
| 176 | 1 |  |  |  |  | 3 | return $config; | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  |  | 
| 179 |  |  |  |  |  |  | 1; | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | =pod | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | =for stopwords dategrep DATESPEC datespec syslog apache blocksize zcat bzcat rsyslog timestamped logrotate ARGV Domgoergen merchantability configfile !syslog | 
| 184 |  |  |  |  |  |  |  | 
| 185 |  |  |  |  |  |  | =head1 NAME | 
| 186 |  |  |  |  |  |  |  | 
| 187 |  |  |  |  |  |  | App::dategrep - grep for dates | 
| 188 |  |  |  |  |  |  |  | 
| 189 |  |  |  |  |  |  | =head1 DESCRIPTION | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | Please read the usage and document of L. | 
| 192 |  |  |  |  |  |  |  | 
| 193 |  |  |  |  |  |  | =head1 SEE ALSO | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | L | 
| 196 |  |  |  |  |  |  |  | 
| 197 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | Copyright 2014 Mario Domgoergen C<<  >> | 
| 200 |  |  |  |  |  |  |  | 
| 201 |  |  |  |  |  |  | This program is free software: you can redistribute it and/or modify | 
| 202 |  |  |  |  |  |  | it under the terms of the GNU General Public License as published by | 
| 203 |  |  |  |  |  |  | the Free Software Foundation, either version 3 of the License, or | 
| 204 |  |  |  |  |  |  | (at your option) any later version. | 
| 205 |  |  |  |  |  |  |  | 
| 206 |  |  |  |  |  |  | This program is distributed in the hope that it will be useful, | 
| 207 |  |  |  |  |  |  | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| 208 |  |  |  |  |  |  | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| 209 |  |  |  |  |  |  | GNU General Public License for more details. | 
| 210 |  |  |  |  |  |  |  | 
| 211 |  |  |  |  |  |  | You should have received a copy of the GNU General Public License | 
| 212 |  |  |  |  |  |  | along with this program.  If not, see . | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | =cut |