File Coverage

blib/lib/Bro/Log/Parse.pm
Criterion Covered Total %
statement 84 88 95.4
branch 34 40 85.0
condition 8 11 72.7
subroutine 12 12 100.0
pod 2 3 66.6
total 140 154 90.9


line stmt bran cond sub pod time code
1             package Bro::Log::Parse;
2             # ABSTRACT: Perl interface for parsing Bro logfiles
3              
4 7     7   154330 use strict;
  7         16  
  7         237  
5 7     7   36 use warnings;
  7         10  
  7         206  
6 7     7   81 use 5.10.1;
  7         26  
7              
8             # use Exporter;
9 7     7   5449 use autodie;
  7         146151  
  7         38  
10 7     7   49138 use Carp;
  7         16  
  7         669  
11 7     7   41 use Scalar::Util qw/openhandle/;
  7         10  
  7         805  
12              
13             our $VERSION = '0.06';
14              
15             #@EXPORT_OK = qw//;
16              
17             BEGIN {
18 7     7   28 my @accessors = qw/fh file line headers fields/;
19              
20 7         14 for my $accessor ( @accessors ) {
21 7     7   42 no strict 'refs';
  7         9  
  7         656  
22             *$accessor = sub {
23 6     6   902 my $self = shift;
24 6         37 return $self->{$accessor};
25             }
26 35         7198 }
27              
28             }
29              
30             sub new {
31 10     10 1 2868 my $class = shift;
32 10         20 my $arg = shift;
33              
34 10         19 my $self = {};
35 10         30 $self->{line} = undef;
36              
37 10 100       69 if ( !defined($arg) ) {
    100          
    100          
38 1         4 $self->{diamond} = 1;
39             } elsif ( ref($arg) eq 'HASH' ) {
40 5         7 $self = $arg;
41             } elsif ( defined(openhandle($arg)) ) {
42 1         3 $self->{fh} = $arg;
43             } else {
44 3         9 $self->{file} = $arg;
45             }
46              
47 10         24 bless $self, $class;
48              
49 10 100 66     89 if ( defined($self->{file}) && !(defined($self->{fh})) ) {
50 5 50       115 unless ( -f $self->{file} ) {
51 0         0 croak("Could not open ".$self->{file});
52             }
53              
54             open( my $fh, "<", $self->{file} )
55 5 50       34 or croak("Cannot open ".$self->{file});
56 5         12673 $self->{fh} = $fh;
57             }
58              
59 10 100 66     55 if ( !defined($self->{fh}) && ( !defined($self->{diamond}) || !$self->{diamond} ) ) {
      66        
60 1         142 croak("No filename given in constructor. Aborting");
61             }
62              
63 9 100       28 if ( defined($self->{fh}) ) {
64 7         28 $self->{names} = [ $self->readheader($self->{fh}) ];
65             } else {
66 2         7 $self->{names} = [ $self->readheader() ];
67             }
68              
69 9         36 $self->{fields} = $self->{names};
70              
71 9   100     56 $self->{empty_as_undef} //= 0;
72              
73 9         27 return $self;
74             }
75              
76             sub readheader {
77 9     9 0 14 my $self = shift;
78 9         14 my $in = shift;
79              
80 9         13 my @headerlines;
81             my @names;
82             # first: read header line. This is a little brittle, but... welll... well, it is.
83 9 100       224 while ( my $line = defined($in) ? <$in> : <> ) {
84 72         74 chomp($line);
85 72         85 push(@headerlines, $line);
86              
87 72         187 my @fields = split /\t/,$line;
88              
89 72 50       190 unless ( $line =~ /^#/ ) {
90 0         0 croak("Did not find required fields and types header lines: $line");
91             }
92              
93 72         78 my $type = shift(@fields);
94 72 100       297 if ( "#fields" eq $type ) {
    100          
95             # yay.
96             # we have our field names...
97 9         69 @names = @fields;
98             } elsif ( "#types" eq $type) {
99 9         30 last;
100             }
101             }
102              
103 9         29 $self->{headers} = \@headerlines;
104              
105 9         81 return @names;
106             }
107              
108              
109             sub getLine {
110 19     19 1 32354 my $self = shift;
111              
112 19         110 my $fh = $self->{fh};
113 19         27 my @names = @{$self->{names}};
  19         114  
114              
115 19 100       198 while ( my $line = defined($fh) ? <$fh> : <> ) {
116 28         58 my $removed = chomp($line);
117 28         47 $self->{line} = $line;
118              
119 28         163 my @fields = split "\t", $line;
120              
121 28 100       103 if ( $line =~ /^#/ ) {
122 12 100       37 if ( "#fields" eq shift(@fields) ) {
123 1         6 @names = @fields;
124 1         3 $self->{names} = \@fields;
125             # This is not really nice, but for the moment we do not really need any
126             # of the other header lines for parsing files - and we do not keep track
127             # of them. Sorry...
128 1         7 $self->{headers} = [ join("\t", ("#fields", @fields)) ];
129             }
130 12         119 next;
131             }
132 16         21 my %f;
133              
134 16 50       50 unless (scalar @names == scalar @fields) {
135 0 0       0 next if ( $removed == 0 );
136 0         0 croak("Number of expected fields does not match number of fields in file");
137             }
138              
139 16         38 for my $name ( @names ) {
140 320         275 my $field = shift(@fields);
141 320 100       517 if ( ( $field eq "-" ) ) {
    100          
142 80         133 $f{$name} = undef;
143             } elsif ( $field eq "(empty)" ) {
144 14 100       80 $f{$name} = $self->{empty_as_undef} ? undef : [];
145             } else {
146 226         391 $f{$name} = $field;
147             }
148             }
149              
150 16         76 return \%f;
151             }
152             }
153              
154             1;