File Coverage

blib/lib/Data/Printer/Filter/ContentType.pm
Criterion Covered Total %
statement 30 33 90.9
branch 12 16 75.0
condition n/a
subroutine 4 4 100.0
pod 1 1 100.0
total 47 54 87.0


line stmt bran cond sub pod time code
1             package Data::Printer::Filter::ContentType;
2 1     1   7 use strict;
  1         2  
  1         29  
3 1     1   5 use warnings;
  1         2  
  1         23  
4 1     1   5 use Data::Printer::Filter;
  1         1  
  1         9  
5              
6             filter 'SCALAR' => sub {
7             my ($data, $ddp) = @_;
8              
9             # don't bother looking on files that are just too small
10             return unless defined $$data;
11             my $len = length($$data);
12             return if $len < 22;
13              
14             my $hex = unpack('H22', $$data);
15             my $hex_8 = substr($hex,0,8);
16              
17             my $type;
18              
19             if ($hex_8 eq '89504e47') {
20             $type = 'PNG Image';
21             }
22             elsif ($hex_8 eq '4d4d002a' || $hex_8 eq '49492a00') {
23             $type = 'TIFF Image';
24             }
25             elsif ($hex_8 eq '00000100') {
26             $type = 'ICO Image';
27             }
28             elsif ($hex_8 eq '52494646') {
29             my $rest = substr($hex,12,8);
30             if ($rest eq '57415645') {
31             $type = 'WAV Audio';
32             }
33             elsif ($rest =~ /\A415649/) {
34             $type = 'AVI Video';
35             }
36             }
37             elsif ($hex_8 =~ /\A504b(?:30|50|70)(?:40|60|80)/) {
38             $type = 'Zip Archive';
39             }
40             elsif ($hex_8 eq '25504446') {
41             $type = 'PDF Document';
42             }
43             elsif ($hex_8 eq '7f454c46') {
44             $type = 'Binary ELF data';
45             }
46             elsif ($hex_8 eq '664c6143') {
47             $type = 'FLAC Audio';
48             }
49             elsif ($hex_8 eq '4f676753') {
50             $type = 'OGG Audio';
51             }
52             else {
53             my $hex_6 = substr($hex,0,6);
54             if($hex_6 eq '474946') {
55             $type = 'GIF Image';
56             }
57             elsif ($hex_6 eq 'ffd8ff') {
58             $type = 'JPEG Image';
59             }
60             elsif ($hex_6 eq '000001') {
61             if (hex(substr($hex,6,2)) >= 0xb0
62             && hex(substr($hex,8,2)) <= 0xbf
63             ) {
64             $type = 'MPEG Video';
65             }
66             }
67             elsif ($hex_6 eq '1f8b80') {
68             $type = 'Gzip Archive';
69             }
70             elsif ($hex_6 eq '494433') {
71             $type = 'MP3 Audio';
72             }
73             elsif ($hex_6 eq '425a68') {
74             $type = 'Bzip2 Archive';
75             }
76             else {
77             my $hex_4 = substr($hex,0,4);
78             if ($hex_4 eq 'fffb') {
79             $type = 'MP3 Audio';
80             }
81             elsif ($hex_4 eq '424d') {
82             $type = 'BMP Image';
83             }
84             elsif ($hex_4 eq '4d5a') {
85             $type = 'Binary Windows EXE data'
86             }
87             elsif ($hex_8 eq '3d73726c') {
88             my $v = substr($hex, 9, 1);
89             if ($v == 1 || $v == 2) {
90             $type = "Binary Sereal v$v data";
91             }
92             }
93             elsif ($hex_8 eq '3df3726c') {
94             my $v = substr($hex, 9, 1);
95             if ($v == 3 || $v == 4) {
96             $type = "Binary Sereal v$v data";
97             }
98             }
99             else {
100             # type not found! Let other filters have a go.
101             return;
102             }
103             }
104             }
105             return unless $type;
106              
107             my $unit = 'AUTO';
108             if (exists $ddp->extra_config->{filter_contenttype}{size_unit}) {
109             $unit = uc $ddp->extra_config->{filter_contenttype}{size_unit};
110             if (!$unit || ($unit ne 'AUTO' && $unit ne 'B' && $unit ne 'K' && $unit ne 'M')) {
111             Data::Printer::Common::_warn($ddp, 'filter_contenttype.size_unit must be auto, b, k or m');
112             $unit = 'auto';
113             }
114             }
115             if ($unit eq 'M' || ($unit eq 'AUTO' && $len > 1024*1024)) {
116             $len = $len / (1024*1024);
117             $unit = 'M';
118             }
119             elsif ($unit eq 'K' || ($unit eq 'AUTO' && $len > 1024)) {
120             $len = $len / 1024;
121             $unit = 'K';
122             }
123             else {
124             $unit = 'B';
125             }
126              
127             my $show_size = !exists $ddp->extra_config->{filter_contenttype}{show_size}
128             || $ddp->extra_config->{filter_contenttype}{show_size};
129              
130             my $symbol = '';
131             if (!exists $ddp->extra_config->{filter_contenttype}{show_symbol}
132             || $ddp->extra_config->{filter_contenttype}{show_symbol}
133             ) {
134             if ($type =~ /Image/) {
135             $symbol = "\x{f0}\x{9f}\x{96}\x{bc} "; # FRAME WITH PICTURE
136             }
137             elsif ($type =~ /Video/) {
138             $symbol = "\x{f0}\x{9f}\x{8e}\x{ac} "; # CLAPPER BOARD
139             }
140             elsif ($type =~ /Audio/) {
141             $symbol = "\x{f0}\x{9f}\x{8e}\x{b5} "; # MUSICAL NOTE
142             }
143             elsif ($type =~ /Archive/) {
144             $symbol = "\x{f0}\x{9f}\x{97}\x{84} "; # FILE CABINET
145             }
146             elsif ($type =~ /Document/) {
147             $symbol = "\x{f0}\x{9f}\x{93}\x{84} "; # PAGE FACING UP
148             }
149             elsif ($type =~ /Binary/) {
150             $symbol = "\x{f0}\x{9f}\x{96}\x{a5} "; # DESKTOP COMPUTER
151             }
152             }
153             my $output = $symbol . $ddp->maybe_colorize('(', 'brackets')
154             . $ddp->maybe_colorize(
155             $type
156             . ((', ' . ($len < 0 ? sprintf("%.2f", $len) : int($len)) . $unit)x!!$show_size),
157             'filter_contenttype',
158             '#ca88dd'
159             )
160             . $ddp->maybe_colorize(')', 'brackets')
161             ;
162              
163             return $output if !exists $ddp->extra_config->{filter_contenttype}{hexdump}
164             || !$ddp->extra_config->{filter_contenttype}{hexdump};
165              
166             my ($h_size, $h_offset, $h_indent) = (0, 0, 0);
167             $h_size = $ddp->extra_config->{filter_contenttype}{hexdump_size}
168             if exists $ddp->extra_config->{filter_contenttype}{hexdump_size};
169             $h_offset = $ddp->extra_config->{filter_contenttype}{hexdump_offset}
170             if exists $ddp->extra_config->{filter_contenttype}{hexdump_offset};
171             $h_indent = $ddp->extra_config->{filter_contenttype}{hexdump_indent}
172             if exists $ddp->extra_config->{filter_contenttype}{hexdump_indent};
173             $output .= hexdump($ddp, $$data, $h_size, $h_offset, $h_indent);
174             return $output;
175             };
176              
177             # inspired by https://www.perlmonks.org/?node_id=1140391
178             sub hexdump {
179 4     4 1 9 my ($ddp, $data, $size, $offset, $indent) = @_;
180              
181 4         7 my $output = '';
182 4         6 my $current_size = 0;
183 4         6 my $is_last = 0;
184 4 100       12 my $linebreak = $indent ? $ddp->newline : "\n";
185 4 100       13 if ($offset > 0) {
    50          
186 1 50       4 return '' if $offset >= length($data);
187 1         3 $data = substr($data, $offset);
188             }
189             elsif ($offset < 0) {
190 0         0 $offset = length($data) + $offset;
191 0 0       0 $offset = 0 if $offset < 0;
192 0         0 $data = substr($data, $offset);
193             }
194 4         19 foreach my $chunk (unpack "(a16)*", $data) {
195 9 100       20 if ($size) {
196 5         8 $current_size += length($chunk);
197 5 100       11 if ($current_size >= $size) {
198 3         7 $chunk = substr $chunk, 0, 16 - ($current_size - $size);
199 3         7 $is_last = 1;
200             }
201             }
202 9         21 my $hex = unpack "H*", $chunk;
203 9         20 $chunk =~ tr/ -~/./c; # replace unprintables
204 9         78 $hex =~ s/(.{1,8})/$1 /gs; # insert spaces
205 9         55 $output .= $linebreak . $ddp->maybe_colorize(
206             sprintf("0x%08x (%05u) %-*s %s", $offset, $offset, 36, $hex, $chunk),
207             'filter_contenttype_hexdump',
208             '#ffcb68'
209             );
210 9 100       22 last if $is_last;
211 6         11 $offset += 16;
212             }
213 4         12 return $output;
214             }
215              
216             1;
217             __END__