File Coverage

blib/lib/Bio/Gonzales/Util/File.pm
Criterion Covered Total %
statement 106 150 70.6
branch 44 90 48.8
condition 16 39 41.0
subroutine 19 30 63.3
pod 8 17 47.0
total 193 326 59.2


line stmt bran cond sub pod time code
1             package Bio::Gonzales::Util::File;
2              
3 30     30   216 use warnings;
  30         70  
  30         1019  
4 30     30   184 use strict;
  30         72  
  30         581  
5 30     30   152 use Carp;
  30         60  
  30         1545  
6 30     30   214 use File::Spec;
  30         81  
  30         906  
7              
8 30     30   381 use Scalar::Util;
  30         84  
  30         1409  
9              
10 30     30   10938 use IO::Handle;
  30         114076  
  30         1412  
11 30     30   19159 use IO::Zlib;
  30         1946337  
  30         254  
12 30     30   20250 use IO::Uncompress::Bunzip2 qw($Bunzip2Error);
  30         132466  
  30         3476  
13 30     30   14615 use File::Which qw/which/;
  30         28013  
  30         1778  
14 30     30   14995 use Bio::Gonzales::Util::IO::Compressed;
  30         105  
  30         963  
15 30     30   15635 use Tie::IxHash;
  30         95522  
  30         3406  
16              
17             our $ZIP_MAGIC = Tie::IxHash->new(
18             BGZF => [ "\037\213\010\4\0\0\0\0\0\377\6\0\102\103\2\0", 'bgzip' ],
19             GZIP => [ "\037\213", 'gzip' ],
20             COMPRESS => [ "\037\235", 'gzip' ],
21             BZIP2 => [ "BZh", 'bzip2' ],
22             XZ => [ "\xFD7zXZ", 'xz' ]
23             );
24             #{ ct_lzip, 4, "LZIP" },
25             #{ ct_lzma, 6, "\xFFLZMA" },
26             #{ ct_lzop, 4, "\211LZO" },
27              
28             #use constant BZIP2_MAGIC => "\102\132\150";
29              
30             our %ZMODES = (
31             '>' => 'wb',
32             '>>' => 'ab',
33             '<', => 'rb',
34             );
35              
36 30     30   262 use base 'Exporter';
  30         66  
  30         68350  
37             our ( @EXPORT, @EXPORT_OK, %EXPORT_TAGS );
38             our $VERSION = '0.083'; # VERSION
39             our $EXTERNAL_GZ = which('gzip'); #which('pigz') // which('gzip');
40             our $EXTERNAL_BZIP2 = which('bzip2');
41              
42             @EXPORT = qw(glob_regex epath bname openod spath);
43             %EXPORT_TAGS = ();
44             @EXPORT_OK
45             = qw(expand_path slurpc basename regex_glob open_on_demand is_newer splitpath %ZMODES is_archive expand_home gonzopen gz_type);
46              
47 0     0 0 0 sub epath { expand_path(@_) }
48              
49             sub expand_path {
50 0     0 1 0 my @files = @_;
51              
52 0         0 my @expanded;
53 0         0 for my $file (@files) {
54 0         0 push @expanded, File::Spec->rel2abs( expand_home($file) );
55             }
56 0 0       0 return wantarray ? @expanded : ( shift @expanded );
57             }
58              
59             sub expand_home {
60 40     40 0 96 my $file = shift;
61 40         189 $file =~ s{ ^ ~ ( [^/]* ) }
62             { $1
63 0 0 0     0 ? (getpwnam($1))[7]
64             : ( $ENV{HOME} || $ENV{LOGDIR} || (getpwuid($>))[7] )
65             }ex;
66 40         109  
67             return $file;
68             }
69 0     0 0 0  
70             sub regex_glob { return glob_regex(@_) }
71              
72 0     0 1 0 sub glob_regex {
73             my ( $dir, $re ) = @_;
74 0         0  
75             $dir = expand_path($dir);
76 0 0       0  
77 0         0 opendir( my $dh, $dir ) || die "can't opendir >$dir< $!";
78 0         0 my @res;
79 0 0 0     0 for ( readdir($dh) ) {
80             push @res, File::Spec->catfile( $dir, $_ ) if ( /$re/ && $_ !~ /^\.\.?$/ );
81 0         0 }
82 0 0       0 closedir $dh;
83             return wantarray ? @res : \@res;
84             }
85              
86             sub slurpc {
87 10     10 1 8230  
88 10         570 my ( $fh, $was_open ) = open_on_demand( $_[0], '<' );
  307         552  
  307         440  
  307         542  
89 10 50       100 my @lines = map { s/\r\n/\n/; chomp; $_ } <$fh>;
90             $fh->close if ( !$was_open );
91 10 100       319  
92             return wantarray ? @lines : \@lines;
93             }
94 0     0 0 0  
95             sub bname { return basename(@_) }
96              
97 0     0 1 0 sub basename {
98 0         0 my $f = shift;
99 0         0 my ( $dir, $base ) = ( File::Spec->splitpath($f) )[ 1, 2 ];
100 0         0 $base =~ s/\.([^.]*?)$//;
101             my $suffix = $1;
102 0 0       0  
103             return wantarray ? ( $dir, $base, $suffix ) : $base;
104             }
105 0     0 0 0  
106             sub spath { return splitpath(@_) }
107              
108 0     0 1 0 sub splitpath {
109 0         0 my $f = shift;
110 0         0 my ( $dir, $filename ) = ( File::Spec->splitpath($f) )[ 1, 2 ];
111 0         0 $dir =~ s![\/\\]$!!;
112             return ( $dir, $filename );
113             }
114 0     0 1 0  
115             sub openod { confess "this function is deprecated, use gonzopen instead"; }
116 0     0 0 0  
  0         0  
117             sub gonzopen { ( my $fh ) = open_on_demand(@_); return $fh; }
118              
119 56     56 1 27313 sub open_on_demand {
120             my ( $src, $mode ) = @_;
121 56 50       252  
122 56 50 33     589 confess "no file or filehandle given" unless ($src);
123             confess "no file open mode given or mode not known: $mode" unless ( $mode && exists( $ZMODES{$mode} ) );
124 56         138  
125             my $fh;
126             my $fh_was_open;
127 56 50 66     392  
      66        
128 0 0       0 if ( $src && !ref($src) && $src eq '-' ) {
    0          
129 0         0 if ( $mode eq '<' ) {
130             $src = \*STDIN;
131 0         0 } elsif ( $mode eq '>' ) {
132             $src = \*STDOUT;
133             }
134             }
135 56 100       270  
    100          
136 13         28 if ( is_fh($src) ) {
137 13         21 $fh = $src;
138             $fh_was_open = 1;
139 40         261 } elsif ( !ref($src) ) {
140 40 100 66     1681 $src = expand_home($src);
    50 66        
      66        
141 4 100       46 if ( $src =~ /.+?\.b?gz$/i || ( $mode eq '<' && -e $src && gz_type($src) ) ) {
142 2         28 if ($EXTERNAL_GZ) {
143             $fh = _pipe_z( $EXTERNAL_GZ, $src, $mode );
144 2 50       122 } else {
145             $fh = IO::Zlib->new( $src, $ZMODES{$mode} ) or die "IO::Zlib failed\n";
146             }
147 0 0       0 } elsif ( $src =~ /.+?\.bz2$/i ) {
148 0         0 if ($EXTERNAL_BZIP2) {
149             $fh = _pipe_z( $EXTERNAL_BZIP2, $src, $mode );
150 0 0       0 } else {
151             $fh = IO::Uncompress::Bunzip2->new($src) or die "IO::Uncompress::Bunzip2 failed: $Bunzip2Error\n";
152             }
153 36 50       1955 } else {
154             open $fh, $mode, $src or confess "Can't open filehandle $src: $!";
155             }
156             } else {
157             # try to open it anyway, let's see what happens
158 3 50       25 # could be a reference to a scalar, supported since perl 5.10
159             open $fh, $mode, $src or confess "Can't open filehandle $src: $!";
160             }
161 55 100       7293  
    50          
162 52         306 if (wantarray) {
163             return ( $fh, $fh_was_open );
164 0         0 } elsif ($fh_was_open) {
165             carp "it is not advisable to use open_on_demand\n in scalar context with previously opened filehandle";
166 3         44 }
167             return $fh;
168             }
169              
170 5     5   5216 sub _pipe_z {
171 5 50 33     128 my ( $gz, $f, $mode ) = @_;
172 5 50       33 return unless ( $gz && -x $gz );
    50          
173 0 0       0 if ( $mode eq '<' ) {
174 0         0 open my $fh, '-|', $gz, '-c', '-d', $f or die "Can't open filehandle $f: $!";
175             return $fh;
176 5         12 } elsif ( $mode eq '>' ) {
177 5 50       205 my ( $r, $w );
178 5         5371 pipe( $r, $w ) || die "gz pipe failed: $!";
179             my $pid = fork();
180 5 50       391 #$SIG{PIPE} = sub { die "whoops, gz pipe broke" };
181 5 100       249 defined($pid) || die "gz fork failed: $!";
182 3         215 if ($pid) {
183             $r->close;
184 3         294 #return $w;
185             return Bio::Gonzales::Util::IO::Compressed->new( $w, $pid );
186 2 50       446 } else {
187 2 50       164 open( STDIN, "<&", $r ) || die "can't reopen gz STDIN: $!";
188 2 50       335 $w->close || die "can't close gz WRITER: $!";
189 2         0 open STDOUT, '>', $f or die "Can't open filehandle: $!";
190             exec( $gz, '-c' );
191             }
192             }
193 0         0  
194             return;
195             }
196              
197 5     5 0 8499 sub is_archive {
198             my $f = shift;
199 5 50       247  
200 5         20 open my $fh, '<', $f or die "Can't open filehandle: $!";
201 5         2536 binmode $fh;
202 5         84 my $nread = read( $fh, my $buffer, 32 );
203             close $fh;
204 5 50       28  
205             return unless ( $nread >= 4 );
206 5         90  
207 17         113 for ( my $i = 0; $i < $ZIP_MAGIC->Length; $i++ ) {
208 17         168 my ($type) = $ZIP_MAGIC->Keys($i);
209 17         104 my ($magic) = $ZIP_MAGIC->Values($i);
210 17 100       187 my $size = length( $magic->[0] );
211             return lc($type) if ( unpack( 'H' . $size, $buffer ) eq unpack( 'H' . $size, $magic->[0] ) );
212 1         21 }
213             return;
214             }
215              
216 0     0 1 0 sub is_newer {
217             my ( $a, $b ) = @_;
218 0 0       0  
219             confess "$a doesn't exist"
220 0 0 0     0 unless ( -f $a );
      0        
221             return 1
222 0         0 if ( !-e $b || ( -e $b && ( stat $a )[9] > ( stat $b )[9] ) );
223             return;
224             }
225              
226 66     66 0 158 sub is_fh {
227             my $fh = shift;
228 66         223  
229             my $reftype = Scalar::Util::reftype($fh);
230              
231             return 1
232 66 100 66     328 if ( $reftype
      66        
233             && ( $reftype eq 'IO' or $reftype eq 'GLOB' && *{$fh}{IO} or ( Scalar::Util::blessed($fh) && $fh->isa("IO::Handle") ) )
234             );
235 43         220  
236             return;
237             }
238              
239 32     32 0 164 sub gz_type {
240             my $f = shift;
241 32         289  
242 32 50       2230 my ($magic) = $ZIP_MAGIC->Values( $ZIP_MAGIC->Indices("BGZF") );
243 32         165 open my $fh, '<', $f or die "Can't open filehandle: $!";
244 32         181 binmode $fh;
245 32         8195 my $size = length( $magic->[0] );
246 32         568 my $nread = read( $fh, my $buffer, $size );
247 32 100       174 close $fh;
248             return unless ( $nread == $size );
249 31 100       457  
    100          
250 1         56 if ( unpack( 'H' . $size, $buffer ) eq unpack( 'H' . $size, $magic->[0] ) ) {
251             return 'bgzf';
252 1         20 } elsif ( unpack( 'H2', $buffer ) eq unpack( 'H2', $magic->[0] ) ) {
253             return 'gzip';
254 29         720 } else {
255             return;
256             }
257             }
258              
259             1;
260             __END__
261              
262             =head1 NAME
263              
264             Bio::Gonzales::Util::File - Utility functions for file stuff
265              
266             =head1 SYNOPSIS
267              
268             use Bio::Gonzales::Util::File qw(glob_regex expand_path slurpc basename open_on_demand is_newer);
269              
270             =head1 DESCRIPTION
271              
272             =head1 SUBROUTINES
273              
274             =over 4
275              
276             =item B<< my ($fh, $was_already_open) = open_on_demand($filename_or_fh, $mode) >>
277              
278             =item B<< my ($fh, $was_already_open) = openod($filename_or_fh, $mode) >>
279              
280             Opens the file if C<$filename_or_fh> is a filename or returns
281             C<$filename_or_fh> if it is already a filehandle, that is opened.
282              
283             =item B<< my $fh = open_on_demand($filename, $mode) >>
284              
285             =item B<< my $fh = openod($filename, $mode) >>
286              
287             Opens the file C<$filename> and returns an handle to it.
288              
289             =item B<< $true_if_a_is_newer = is_newer($a, $b) >>
290              
291             Return true if C<$b> does not exist or C<$a> is newer than C<$b>. Dies if C<$a> does not exist.
292              
293             =item B<< ($dir, $basename, $suffix) = basename($file) >>
294              
295             =item B<< $basename = basename($file) >>
296              
297             Returns the basename of C<$file> in scalar context and the ( C<$dir>,
298             C<$basename>, C<$suffix> ) in list context. Filename example:
299              
300             /path/to/file.txt
301             scalar basename: 'file'
302             list basename: ('path/to', 'file', 'txt')
303              
304             =item B<< @lines = slurpc($file) >>
305              
306             =item B<< @expanded = expand_path(@files) >>
307              
308             =item B<< $expanded_ref = expand_path(@files) >>
309              
310             Expands F<~> in all supplied files and returns the crap.
311              
312             =item B<< @files = glob_regex($dir, $file_regex) >>
313              
314             Selects files from C<$dir> based on the supplied C<$file_regex>.
315              
316             =item B<< ($dirname, $filename) = splitpath($path) >>
317              
318             Splits a $path into directory and filename.
319              
320             =back
321              
322             =head1 SEE ALSO
323              
324             =head1 AUTHOR
325              
326             jw bargsten, C<< <joachim.bargsten at wur.nl> >>