File Coverage

lib/File/MagicPP.pm
Criterion Covered Total %
statement 28 31 90.3
branch 3 6 50.0
condition n/a
subroutine 7 7 100.0
pod 1 1 100.0
total 39 45 86.6


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3             package File::MagicPP;
4              
5 1     1   82455 use strict;
  1         3  
  1         31  
6 1     1   5 use warnings;
  1         2  
  1         47  
7 1     1   12 use Data::Dumper;
  1         2  
  1         66  
8 1     1   457 use version 0.77;
  1         2027  
  1         7  
9 1     1   102 use List::Util qw/min max/;
  1         2  
  1         117  
10              
11 1     1   12 use Exporter qw/import/;
  1         2  
  1         445  
12              
13             our @EXPORT_OK = qw(
14             file
15             $VERSION
16             %magicLiteral
17             );
18              
19             =pod
20              
21             =head1 NAME
22              
23             File::MagicPP
24              
25             =head1 SYNOPSIS
26              
27             This module provides file magic through pure perl and does not rely on
28             libraries external to Perl.
29              
30             use File::MagicPP qw/file/;
31             my $type = file($0);
32             # $type now holds "script"
33              
34             =cut
35              
36             =pod
37              
38             =head1 VARIABLES
39              
40             =head2 $VERSION
41              
42             Describes the library version
43              
44             =cut
45              
46             our $VERSION = '0.2.1';
47              
48             =pod
49              
50             =head2 %magicLiteral
51              
52             Provides a hash of magic bits to file type, e.g.,
53             BZh => "bz"
54              
55             =cut
56              
57             my %magicLiteral = (
58             '@' => "fastq",
59             'BZh' => "bz",
60             '\x1f\x8b' => "gz",
61             #'\x1f\x8b\x08' => "gz",
62             '>' => "fasta",
63             '\x5c\x67\x31' => "bam",
64             '#!' => "script",
65             'GIF87a' => "gif",
66             'GIF89a' => "gif",
67             '\x49\x49\x2a\x00' => 'tif',
68             '\x4d\x4d\x00\x2a' => 'tiff',
69             '\xff\xd8\xff\xe0\x00\x10\x4a\x46' => 'jpg',
70             '\x89\x50\x4e\x47\x0d\x0a\x1a\x0a' => 'png',
71             ' 'svg',
72             '%PDF' => 'pdf',
73             'BM' => 'bmp',
74             '\xfd\x37\x7a\x58\x5a\x00' => 'xz',
75              
76             );
77              
78             my $maxMagicLength = max(map{length($_)} keys(%magicLiteral));
79              
80             =pod
81              
82             =head1 FUNCTIONS
83              
84             =head2 file()
85              
86             Give it a file path and it will tell you the file type.
87              
88             =cut
89              
90             sub file{
91 1     1 1 65118 my ($file_path) = @_;
92 1 50       106 open(my $fh, '<:raw', $file_path) or die "Could not open file '$file_path': $!";
93 1         33 my $header = <$fh>;
94 1         3 chomp($header);
95 1         15 close($fh);
96              
97 1         19 my $maxLength = min(length($header), $maxMagicLength);
98              
99             # For each possible length, look for keys that match the
100             # header, longest to shortest.
101 1         8 for(my $length=$maxLength; $length > 0; $length--){
102 18         33 my $headerSubstr = substr($header, 0, $length);
103 18 100       44 if($magicLiteral{$headerSubstr}){
104 1         18 return $magicLiteral{$headerSubstr};
105             }
106             }
107              
108             # If we haven't found anything yet, see if it's just
109             # letters and numbers for plaintext.
110 0 0         if($header =~ /^[\w\d]+\s*/){
111 0           return "plaintext";
112             }
113              
114             # At this point, we just don't know
115 0           return "UNKNOWN";
116             }
117