possible fix for #187
[bioperl-live.git] / Bio / Root / IO.pm
blob541a8ed1b9a6baf1058c36547ad3a707fa69c61d
1 package Bio::Root::IO;
3 use strict;
4 use Symbol;
5 use IO::Handle;
6 use File::Copy;
7 use Fcntl;
8 use base qw(Bio::Root::Root);
10 # as of 2016, worked on most systems, but will test this in a RC
11 my %modes = ( 0 => 'r', 1 => 'w', 2 => 'rw' );
13 =head1 SYNOPSIS
15 # Use stream I/O in your module
16 $self->{'io'} = Bio::Root::IO->new(-file => "myfile");
17 $self->{'io'}->_print("some stuff");
18 my $line = $self->{'io'}->_readline();
19 $self->{'io'}->_pushback($line);
20 $self->{'io'}->close();
22 # obtain platform-compatible filenames
23 $path = Bio::Root::IO->catfile($dir, $subdir, $filename);
24 # obtain a temporary file (created in $TEMPDIR)
25 ($handle) = $io->tempfile();
27 =head1 DESCRIPTION
29 This module provides methods that will usually be needed for any sort
30 of file- or stream-related input/output, e.g., keeping track of a file
31 handle, transient printing and reading from the file handle, a close
32 method, automatically closing the handle on garbage collection, etc.
34 To use this for your own code you will either want to inherit from
35 this module, or instantiate an object for every file or stream you are
36 dealing with. In the first case this module will most likely not be
37 the first class off which your class inherits; therefore you need to
38 call _initialize_io() with the named parameters in order to set file
39 handle, open file, etc automatically.
41 Most methods start with an underscore, indicating they are private. In
42 OO speak, they are not private but protected, that is, use them in
43 your module code, but a client code of your module will usually not
44 want to call them (except those not starting with an underscore).
46 In addition this module contains a couple of convenience methods for
47 cross-platform safe tempfile creation and similar tasks. There are
48 some CPAN modules related that may not be available on all
49 platforms. At present, File::Spec and File::Temp are attempted. This
50 module defines $PATHSEP, $TEMPDIR, and $ROOTDIR, which will always be set,
51 and $OPENFLAGS, which will be set if either of File::Spec or File::Temp fails.
53 The -noclose boolean (accessed via the noclose method) prevents a
54 filehandle from being closed when the IO object is cleaned up. This
55 is special behavior when a object like a parser might share a
56 filehandle with an object like an indexer where it is not proper to
57 close the filehandle as it will continue to be reused until the end of the
58 stream is reached. In general you won't want to play with this flag.
60 =head1 AUTHOR Hilmar Lapp
62 =cut
64 our ($FILESPECLOADED, $FILETEMPLOADED,
65 $FILEPATHLOADED, $TEMPDIR,
66 $PATHSEP, $ROOTDIR,
67 $OPENFLAGS, $VERBOSE,
68 $ONMAC, $HAS_EOL, );
70 my $TEMPCOUNTER;
71 my $HAS_WIN32 = 0;
73 BEGIN {
74 $TEMPCOUNTER = 0;
75 $FILESPECLOADED = 0;
76 $FILETEMPLOADED = 0;
77 $FILEPATHLOADED = 0;
78 $VERBOSE = 0;
80 # try to load those modules that may cause trouble on some systems
81 eval {
82 require File::Path;
83 $FILEPATHLOADED = 1;
85 if( $@ ) {
86 print STDERR "Cannot load File::Path: $@" if( $VERBOSE > 0 );
87 # do nothing
90 # If on Win32, attempt to find Win32 package
91 if($^O =~ /mswin/i) {
92 eval {
93 require Win32;
94 $HAS_WIN32 = 1;
98 # Try to provide a path separator. Why doesn't File::Spec export this,
99 # or did I miss it?
100 if ($^O =~ /mswin/i) {
101 $PATHSEP = "\\";
102 } elsif($^O =~ /macos/i) {
103 $PATHSEP = ":";
104 } else { # unix
105 $PATHSEP = "/";
107 eval {
108 require File::Spec;
109 $FILESPECLOADED = 1;
110 $TEMPDIR = File::Spec->tmpdir();
111 $ROOTDIR = File::Spec->rootdir();
112 require File::Temp; # tempfile creation
113 $FILETEMPLOADED = 1;
115 if( $@ ) {
116 if(! defined($TEMPDIR)) { # File::Spec failed
117 # determine tempdir
118 if (defined $ENV{'TEMPDIR'} && -d $ENV{'TEMPDIR'} ) {
119 $TEMPDIR = $ENV{'TEMPDIR'};
120 } elsif( defined $ENV{'TMPDIR'} && -d $ENV{'TMPDIR'} ) {
121 $TEMPDIR = $ENV{'TMPDIR'};
123 if($^O =~ /mswin/i) {
124 $TEMPDIR = 'C:\TEMP' unless $TEMPDIR;
125 $ROOTDIR = 'C:';
126 } elsif($^O =~ /macos/i) {
127 $TEMPDIR = "" unless $TEMPDIR; # what is a reasonable default on Macs?
128 $ROOTDIR = ""; # what is reasonable??
129 } else { # unix
130 $TEMPDIR = "/tmp" unless $TEMPDIR;
131 $ROOTDIR = "/";
133 if (!( -d $TEMPDIR && -w $TEMPDIR )) {
134 $TEMPDIR = '.'; # last resort
137 # File::Temp failed (alone, or File::Spec already failed)
138 # determine open flags for tempfile creation using Fcntl
139 $OPENFLAGS = O_CREAT | O_EXCL | O_RDWR;
140 for my $oflag (qw/FOLLOW BINARY LARGEFILE EXLOCK NOINHERIT TEMPORARY/){
141 my ($bit, $func) = (0, "Fcntl::O_" . $oflag);
142 no strict 'refs';
143 $OPENFLAGS |= $bit if eval { $bit = &$func(); 1 };
146 $ONMAC = "\015" eq "\n";
150 =head2 new
152 Title : new
153 Usage : my $io = Bio::Root::IO->new( -file => 'data.txt' );
154 Function: Create new class instance. It automatically calls C<_initialize_io>.
155 Args : Same named parameters as C<_initialize_io>.
156 Returns : A Bio::Root::IO object
158 =cut
160 sub new {
161 my ($caller, @args) = @_;
162 my $self = $caller->SUPER::new(@args);
163 $self->_initialize_io(@args);
164 return $self;
168 =head2 _initialize_io
170 Title : _initialize_io
171 Usage : $io->_initialize_io(@params);
172 Function: Initializes filehandle and other properties from the parameters.
173 Args : The following named parameters are currently recognized:
174 -file name of file to read or write to
175 -fh file handle to read or write to (mutually exclusive
176 with -file and -string)
177 -input name of file, or filehandle (GLOB or IO::Handle object)
178 to read of write to
179 -string string to read from (will be converted to filehandle)
180 -url name of URL to open
181 -flush boolean flag to autoflush after each write
182 -noclose boolean flag, when set to true will not close a
183 filehandle (must explicitly call close($io->_fh)
184 -retries number of times to try a web fetch before failure
185 -ua_parms when using -url, hashref of key => value parameters
186 to pass to LWP::UserAgent->new(). A useful value might
187 be, for example, {timeout => 60 } (ua defaults to 180s)
188 Returns : True
190 =cut
192 sub _initialize_io {
193 my($self, @args) = @_;
195 $self->_register_for_cleanup(\&_io_cleanup);
197 my ($input, $noclose, $file, $fh, $string,
198 $flush, $url, $retries, $ua_parms) =
199 $self->_rearrange([qw(INPUT NOCLOSE FILE FH STRING FLUSH URL RETRIES UA_PARMS)],
200 @args);
202 my $mode;
204 if ($url) {
205 $retries ||= 5;
207 require LWP::UserAgent;
208 my $ua = LWP::UserAgent->new(%$ua_parms);
209 my $http_result;
210 my ($handle, $tempfile) = $self->tempfile();
211 CORE::close($handle);
213 for (my $try = 1 ; $try <= $retries ; $try++) {
214 $http_result = $ua->get($url, ':content_file' => $tempfile);
215 $self->warn("[$try/$retries] tried to fetch $url, but server ".
216 "threw ". $http_result->code . ". retrying...")
217 if !$http_result->is_success;
218 last if $http_result->is_success;
220 $self->throw("Failed to fetch $url, server threw ".$http_result->code)
221 if !$http_result->is_success;
223 $file = $tempfile;
224 $mode = '>';
227 delete $self->{'_readbuffer'};
228 delete $self->{'_filehandle'};
229 $self->noclose( $noclose) if defined $noclose;
230 # determine whether the input is a file(name) or a stream
231 if ($input) {
232 if (ref(\$input) eq 'SCALAR') {
233 # we assume that a scalar is a filename
234 if ($file && ($file ne $input)) {
235 $self->throw("Input file given twice: '$file' and '$input' disagree");
237 $file = $input;
238 } elsif (ref($input) &&
239 ((ref($input) eq 'GLOB') || $input->isa('IO::Handle'))) {
240 # input is a stream
241 $fh = $input;
242 } else {
243 # let's be strict for now
244 $self->throw("Unable to determine type of input $input: ".
245 "not string and not GLOB");
249 if (defined($file) && defined($fh)) {
250 $self->throw("Providing both a file and a filehandle for reading - ".
251 "only one please!");
254 if ($string) {
255 if (defined($file) || defined($fh)) {
256 $self->throw("File or filehandle provided with -string, ".
257 "please unset if you are using -string as a file");
259 open $fh, '<', \$string or $self->throw("Could not read string: $!");
262 if (defined($file) && ($file ne '')) {
263 $self->file($file);
264 ($mode, $file) = $self->cleanfile;
265 $mode ||= '<';
266 my $action = ($mode =~ m/>/) ? 'write' : 'read';
267 $fh = Symbol::gensym();
268 open $fh, $mode, $file or $self->throw("Could not $action file '$file': $!");
271 if (defined $fh) {
272 # check filehandle to ensure it's one of:
273 # a GLOB reference, as in: open(my $fh, "myfile");
274 # an IO::Handle or IO::String object
275 # the UNIVERSAL::can added to fix Bug2863
276 unless ( ( ref $fh and ( ref $fh eq 'GLOB' ) )
277 or ( ref $fh and ( UNIVERSAL::can( $fh, 'can' ) )
278 and ( $fh->isa('IO::Handle')
279 or $fh->isa('IO::String') ) )
281 $self->throw("Object $fh does not appear to be a file handle");
283 if ($HAS_EOL) {
284 binmode $fh, ':raw:eol(LF-Native)';
286 $self->_fh($fh); # if $fh not provided, defaults to STDIN and STDOUT
289 $self->_flush_on_write(defined $flush ? $flush : 1);
291 return 1;
295 =head2 _fh
297 Title : _fh
298 Usage : $io->_fh($newval);
299 Function: Get or set the file handle for the stream encapsulated.
300 Args : Optional filehandle to use
301 Returns : Filehandle for the stream
303 =cut
305 sub _fh {
306 my ($self, $value) = @_;
307 if ( defined $value) {
308 $self->{'_filehandle'} = $value;
310 return $self->{'_filehandle'};
314 =head2 mode
316 Title : mode
317 Usage : $io->mode();
318 $io->mode(-force => 1);
319 Function: Determine if the object was opened for reading or writing
320 Args : -force: Boolean. Once mode() has been called, the mode is cached for
321 further calls to mode(). Use this argument to override this
322 behavior and re-check the object's mode.
323 Returns : Mode of the object:
324 'r' for readable
325 'w' for writable
326 'rw' for readable and writable
327 '?' if mode could not be determined (e.g. for a -url)
329 =cut
331 sub mode {
332 my ($self, %arg) = @_;
334 # Method 1: IO::Handle::fdopen
335 # my $iotest = new IO::Handle;
336 # $iotest->fdopen( dup(fileno($fh)) , 'r' );
337 # if ($iotest->error == 0) { ... }
338 # It did not actually seem to work under any platform, since there would no
339 # error if the filehandle had been opened writable only. It could not be
340 # hacked around when dealing with unseekable (piped) filehandles.
342 # Method 2: readline, a.k.a. the <> operator
343 # no warnings "io";
344 # my $line = <$fh>;
345 # if (defined $line) {
346 # $self->{'_mode'} = 'r';
347 # ...
348 # It did not work well either because <> returns undef, i.e. querying the
349 # mode() after having read an entire file returned 'w'.
351 if ( $arg{-force} || not exists $self->{'_mode'} ) {
352 # Determine stream mode
353 my $mode;
354 my $fh = $self->_fh;
355 if (defined $fh) {
356 # use fcntl if not Windows-based
357 if ($^O !~ /MSWin32/) {
358 my $m = fcntl($fh, F_GETFL, 0);
359 $mode = exists $modes{$m & 3} ? $modes{$m & 3} : '?';
360 } else {
361 # Determine read/write status of filehandle
362 no warnings 'io';
363 if ( defined( read $fh, my $content, 0 ) ) {
364 # Successfully read 0 bytes
365 $mode = 'r'
367 if ( defined( syswrite $fh, '') ) {
368 # Successfully wrote 0 bytes
369 $mode ||= '';
370 $mode .= 'w';
373 } else {
374 # Stream does not have a filehandle... cannot determine mode
375 $mode = '?';
377 # Save mode for future use
378 $self->{'_mode'} = $mode;
380 return $self->{'_mode'};
384 =head2 file
386 Title : file
387 Usage : $io->file('>'.$file);
388 my $file = $io->file;
389 Function: Get or set the name of the file to read or write.
390 Args : Optional file name (including its mode, e.g. '<' for reading or '>'
391 for writing)
392 Returns : A string representing the filename and its mode.
394 =cut
396 sub file {
397 my ($self, $value) = @_;
398 if ( defined $value) {
399 $self->{'_file'} = $value;
401 return $self->{'_file'};
405 =head2 cleanfile
407 Title : cleanfile
408 Usage : my ($mode, $file) = $io->cleanfile;
409 Function: Get the name of the file to read or write, stripped of its mode
410 ('>', '<', '+>', '>>', etc).
411 Args : None
412 Returns : In array context, an array of the mode and the clean filename.
414 =cut
416 sub cleanfile {
417 my ($self) = @_;
418 return ($self->{'_file'} =~ m/^ (\+?[><]{1,2})?\s*(.*) $/x);
422 =head2 format
424 Title : format
425 Usage : $io->format($newval)
426 Function: Get the format of a Bio::Root::IO sequence file or filehandle. Every
427 object inheriting Bio::Root::IO is guaranteed to have a format.
428 Args : None
429 Returns : Format of the file or filehandle, e.g. fasta, fastq, genbank, embl.
431 =cut
433 sub format {
434 my ($self) = @_;
435 my $format = (split '::', ref($self))[-1];
436 return $format;
440 =head2 variant
442 Title : format
443 Usage : $io->format($newval)
444 Function: Get the variant of a Bio::Root::IO sequence file or filehandle.
445 The format variant depends on the specific format used. Note that
446 not all formats have variants. Also, the Bio::Root::IO-implementing
447 modules that require access to variants need to define a global hash
448 that has the allowed variants as its keys.
449 Args : None
450 Returns : Variant of the file or filehandle, e.g. sanger, solexa or illumina for
451 the fastq format, or undef for formats that do not have variants.
453 =cut
455 sub variant {
456 my ($self, $variant) = @_;
457 if (defined $variant) {
458 $variant = lc $variant;
459 my $var_name = '%'.ref($self).'::variant';
460 my %ok_variants = eval $var_name; # e.g. %Bio::Assembly::IO::ace::variant
461 if (scalar keys %ok_variants == 0) {
462 $self->throw("Could not validate variant because global variant ".
463 "$var_name was not set or was empty\n");
465 if (not exists $ok_variants{$variant}) {
466 $self->throw("$variant is not a valid variant of the " .
467 $self->format . ' format');
469 $self->{variant} = $variant;
471 return $self->{variant};
475 =head2 _print
477 Title : _print
478 Usage : $io->_print(@lines)
479 Function: Print lines of text to the IO stream object.
480 Args : List of strings to print
481 Returns : True on success, undef on failure
483 =cut
485 sub _print {
486 my $self = shift;
487 my $fh = $self->_fh() || \*STDOUT;
488 my $ret = print $fh @_;
489 return $ret;
493 =head2 _insert
495 Title : _insert
496 Usage : $io->_insert($string,1)
497 Function: Insert some text in a file at the given line number (1-based).
498 Args : * string to write in file
499 * line number to insert the string at
500 Returns : True
502 =cut
504 sub _insert {
505 my ($self, $string, $line_num) = @_;
506 # Line number check
507 if ($line_num < 1) {
508 $self->throw("Could not insert text at line $line_num: the minimum ".
509 "line number possible is 1.");
511 # File check
512 my ($mode, $file) = $self->cleanfile;
513 if (not defined $file) {
514 $self->throw('Could not insert a line: IO object was initialized with '.
515 'something else than a file.');
517 # Everything that needs to be written is written before we read it
518 $self->flush;
520 # Edit the file line by line (no slurping)
521 $self->close;
522 my $temp_file;
523 my $number = 0;
524 while (-e "$file.$number.temp") {
525 $number++;
527 $temp_file = "$file.$number.temp";
528 copy($file, $temp_file);
529 open my $fh1, '<', $temp_file or $self->throw("Could not read temporary file '$temp_file': $!");
530 open my $fh2, '>', $file or $self->throw("Could not write file '$file': $!");
531 while (my $line = <$fh1>) {
532 if ($. == $line_num) { # right line for new data
533 print $fh2 $string . $line;
535 else {
536 print $fh2 $line;
539 CORE::close $fh1;
540 CORE::close $fh2;
541 unlink $temp_file or $self->throw("Could not delete temporary file '$temp_file': $!");
543 # Line number check (again)
544 if ( $. > 0 && $line_num > $. ) {
545 $self->throw("Could not insert text at line $line_num: there are only ".
546 "$. lines in file '$file'");
548 # Re-open the file in append mode to be ready to add text at the end of it
549 # when the next _print() statement comes
550 open my $new_fh, '>>', $file or $self->throw("Could not append to file '$file': $!");
551 $self->_fh($new_fh);
552 # If file is empty and we're inserting at line 1, simply append text to file
553 if ( $. == 0 && $line_num == 1 ) {
554 $self->_print($string);
556 return 1;
560 =head2 _readline
562 Title : _readline
563 Usage : local $Bio::Root::IO::HAS_EOL = 1;
564 my $io = Bio::Root::IO->new(-file => 'data.txt');
565 my $line = $io->_readline();
566 $io->close;
567 Function: Read a line of input and normalize all end of line characters.
569 End of line characters are typically "\n" on Linux platforms, "\r\n"
570 on Windows and "\r" on older Mac OS. By default, the _readline()
571 method uses the value of $/, Perl's input record separator, to
572 detect the end of each line. This means that you will not get the
573 expected lines if your input has Mac-formatted end of line characters.
574 Also, note that the current implementation does not handle pushed
575 back input correctly unless the pushed back input ends with the
576 value of $/. For each line parsed, its line ending, e.g. "\r\n" is
577 converted to "\n", unless you provide the -raw argument.
579 Altogether it is easier to let the PerlIO::eol module automatically
580 detect the proper end of line character and normalize it to "\n". Do
581 so by setting $Bio::Root::IO::HAS_EOL to 1.
583 Args : -raw : Avoid converting end of line characters to "\n" This option
584 has no effect when using $Bio::Root::IO::HAS_EOL = 1.
585 Returns : Line of input, or undef when there is nothing to read anymore
587 =cut
589 sub _readline {
590 my ($self, %param) = @_;
591 my $fh = $self->_fh or return;
592 my $line;
594 # if the buffer been filled by _pushback then return the buffer
595 # contents, rather than read from the filehandle
596 if( @{$self->{'_readbuffer'} || [] } ) {
597 $line = shift @{$self->{'_readbuffer'}};
598 } else {
599 $line = <$fh>;
602 # Note: In Windows the "-raw" parameter has no effect, because Perl already discards
603 # the '\r' from the line when reading in text mode from the filehandle
604 # ($line = <$fh>), and put it back automatically when printing
605 if( !$HAS_EOL && !$param{-raw} && (defined $line) ) {
606 # don't strip line endings if -raw or $HAS_EOL is specified
607 $line =~ s/\015\012/\012/g; # Change all CR/LF pairs to LF
608 $line =~ tr/\015/\n/ unless $ONMAC; # Change all single CRs to NEWLINE
610 return $line;
614 =head2 _pushback
616 Title : _pushback
617 Usage : $io->_pushback($newvalue)
618 Function: Puts a line previously read with _readline back into a buffer.
619 buffer can hold as many lines as system memory permits.
621 Note that this is only supported for pushing back data ending with
622 the current, localized value of $/. Using this method to push
623 modified data back onto the buffer stack is not supported; see bug
624 843.
626 Args : newvalue
627 Returns : True
629 =cut
631 # fix for bug 843, this reveals some unsupported behavior
633 #sub _pushback {
634 # my ($self, $value) = @_;
635 # if (index($value, $/) >= 0) {
636 # push @{$self->{'_readbuffer'}}, $value;
637 # } else {
638 # $self->throw("Pushing modifed data back not supported: $value");
642 sub _pushback {
643 my ($self, $value) = @_;
644 return unless $value;
645 unshift @{$self->{'_readbuffer'}}, $value;
646 return 1;
650 =head2 close
652 Title : close
653 Usage : $io->close()
654 Function: Closes the file handle associated with this IO instance,
655 excepted if -noclose was specified.
656 Args : None
657 Returns : True
659 =cut
661 sub close {
662 my ($self) = @_;
664 # do not close if we explicitly asked not to
665 return if $self->noclose;
667 if( defined( my $fh = $self->{'_filehandle'} )) {
668 $self->flush;
669 return if ref $fh eq 'GLOB' && (
670 \*STDOUT == $fh || \*STDERR == $fh || \*STDIN == $fh
673 # don't close IO::Strings
674 CORE::close $fh unless ref $fh && $fh->isa('IO::String');
676 $self->{'_filehandle'} = undef;
677 delete $self->{'_readbuffer'};
678 return 1;
682 =head2 flush
684 Title : flush
685 Usage : $io->flush()
686 Function: Flushes the filehandle
687 Args : None
688 Returns : True
690 =cut
692 sub flush {
693 my ($self) = shift;
695 if( !defined $self->{'_filehandle'} ) {
696 $self->throw("Flush failed: no filehandle was active");
699 if( ref($self->{'_filehandle'}) =~ /GLOB/ ) {
700 my $oldh = select($self->{'_filehandle'});
701 $| = 1;
702 select($oldh);
703 } else {
704 $self->{'_filehandle'}->flush();
706 return 1;
710 =head2 noclose
712 Title : noclose
713 Usage : $io->noclose($newval)
714 Function: Get or set the NOCLOSE flag - setting this to true will prevent a
715 filehandle from being closed when an object is cleaned up or
716 explicitly closed.
717 Args : Optional new value (a scalar or undef)
718 Returns : Value of noclose (a scalar)
720 =cut
722 sub noclose {
723 my $self = shift;
724 return $self->{'_noclose'} = shift if @_;
725 return $self->{'_noclose'};
729 =head2 _io_cleanup
731 =cut
733 sub _io_cleanup {
734 my ($self) = @_;
735 $self->close();
736 my $v = $self->verbose;
738 # we are planning to cleanup temp files no matter what
739 if ( exists($self->{'_rootio_tempfiles'})
740 and ref($self->{'_rootio_tempfiles'}) =~ /array/i
741 and not $self->save_tempfiles
743 if( $v > 0 ) {
744 warn( "going to remove files ",
745 join(",", @{$self->{'_rootio_tempfiles'}}),
746 "\n");
748 unlink (@{$self->{'_rootio_tempfiles'}} );
750 # cleanup if we are not using File::Temp
751 if ( $self->{'_cleanuptempdir'}
752 and exists($self->{'_rootio_tempdirs'})
753 and ref($self->{'_rootio_tempdirs'}) =~ /array/i
754 and not $self->save_tempfiles
756 if( $v > 0 ) {
757 warn( "going to remove dirs ",
758 join(",", @{$self->{'_rootio_tempdirs'}}),
759 "\n");
761 $self->rmtree( $self->{'_rootio_tempdirs'});
766 =head2 exists_exe
768 Title : exists_exe
769 Usage : $exists = $io->exists_exe('clustalw');
770 $exists = Bio::Root::IO->exists_exe('clustalw')
771 $exists = Bio::Root::IO::exists_exe('clustalw')
772 Function: Determines whether the given executable exists either as file
773 or within the path environment. The latter requires File::Spec
774 to be installed.
775 On Win32-based system, .exe is automatically appended to the program
776 name unless the program name already ends in .exe.
777 Args : Name of the executable
778 Returns : 1 if the given program is callable as an executable, and 0 otherwise
780 =cut
782 sub exists_exe {
783 my ($self, $exe) = @_;
784 $self->throw("Must pass a defined value to exists_exe") unless defined $exe;
785 $exe = $self if (!(ref($self) || $exe));
786 $exe .= '.exe' if(($^O =~ /mswin/i) && ($exe !~ /\.(exe|com|bat|cmd)$/i));
787 return $exe if ( -f $exe && -x $exe ); # full path and exists
789 # Ewan's comment. I don't think we need this. People should not be
790 # asking for a program with a pathseparator starting it
791 # $exe =~ s/^$PATHSEP//;
793 # Not a full path, or does not exist. Let's see whether it's in the path.
794 if($FILESPECLOADED) {
795 for my $dir (File::Spec->path()) {
796 my $f = Bio::Root::IO->catfile($dir, $exe);
797 return $f if( -f $f && -x $f );
800 return 0;
804 =head2 tempfile
806 Title : tempfile
807 Usage : my ($handle,$tempfile) = $io->tempfile();
808 Function: Create a temporary filename and a handle opened for reading and
809 writing.
810 Caveats: If you do not have File::Temp on your system you should
811 avoid specifying TEMPLATE and SUFFIX.
812 Args : Named parameters compatible with File::Temp: DIR (defaults to
813 $Bio::Root::IO::TEMPDIR), TEMPLATE, SUFFIX.
814 Returns : A 2-element array, consisting of temporary handle and temporary
815 file name.
817 =cut
819 sub tempfile {
820 my ($self, @args) = @_;
821 my ($tfh, $file);
822 my %params = @args;
824 # map between naming with and without dash
825 for my $key (keys(%params)) {
826 if( $key =~ /^-/ ) {
827 my $v = $params{$key};
828 delete $params{$key};
829 $params{uc(substr($key,1))} = $v;
830 } else {
831 # this is to upper case
832 my $v = $params{$key};
833 delete $params{$key};
834 $params{uc($key)} = $v;
837 $params{'DIR'} = $TEMPDIR if(! exists($params{'DIR'}));
838 unless (exists $params{'UNLINK'} &&
839 defined $params{'UNLINK'} &&
840 ! $params{'UNLINK'} ) {
841 $params{'UNLINK'} = 1;
842 } else {
843 $params{'UNLINK'} = 0;
846 if($FILETEMPLOADED) {
847 if(exists($params{'TEMPLATE'})) {
848 my $template = $params{'TEMPLATE'};
849 delete $params{'TEMPLATE'};
850 ($tfh, $file) = File::Temp::tempfile($template, %params);
851 } else {
852 ($tfh, $file) = File::Temp::tempfile(%params);
854 } else {
855 my $dir = $params{'DIR'};
856 $file = $self->catfile(
857 $dir,
858 (exists($params{'TEMPLATE'}) ?
859 $params{'TEMPLATE'} :
860 sprintf( "%s.%s.%s", $ENV{USER} || 'unknown', $$, $TEMPCOUNTER++))
863 # sneakiness for getting around long filenames on Win32?
864 if( $HAS_WIN32 ) {
865 $file = Win32::GetShortPathName($file);
868 # Try to make sure this will be marked close-on-exec
869 # XXX: Win32 doesn't respect this, nor the proper fcntl,
870 # but may have O_NOINHERIT. This may or may not be in Fcntl.
871 local $^F = 2;
872 # Store callers umask
873 my $umask = umask();
874 # Set a known umaskr
875 umask(066);
876 # Attempt to open the file
877 if ( sysopen($tfh, $file, $OPENFLAGS, 0600) ) {
878 # Reset umask
879 umask($umask);
880 } else {
881 $self->throw("Could not write temporary file '$file': $!");
885 if( $params{'UNLINK'} ) {
886 push @{$self->{'_rootio_tempfiles'}}, $file;
889 return wantarray ? ($tfh,$file) : $tfh;
893 =head2 tempdir
895 Title : tempdir
896 Usage : my ($tempdir) = $io->tempdir(CLEANUP=>1);
897 Function: Creates and returns the name of a new temporary directory.
899 Note that you should not use this function for obtaining "the"
900 temp directory. Use $Bio::Root::IO::TEMPDIR for that. Calling this
901 method will in fact create a new directory.
903 Args : args - ( key CLEANUP ) indicates whether or not to cleanup
904 dir on object destruction, other keys as specified by File::Temp
905 Returns : The name of a new temporary directory.
907 =cut
909 sub tempdir {
910 my ($self, @args) = @_;
911 if ($FILETEMPLOADED && File::Temp->can('tempdir')) {
912 return File::Temp::tempdir(@args);
915 # we have to do this ourselves, not good
916 # we are planning to cleanup temp files no matter what
917 my %params = @args;
918 print "cleanup is " . $params{CLEANUP} . "\n";
919 $self->{'_cleanuptempdir'} = ( defined $params{CLEANUP} &&
920 $params{CLEANUP} == 1);
921 my $tdir = $self->catfile( $TEMPDIR,
922 sprintf("dir_%s-%s-%s",
923 $ENV{USER} || 'unknown',
925 $TEMPCOUNTER++));
926 mkdir($tdir, 0755);
927 push @{$self->{'_rootio_tempdirs'}}, $tdir;
928 return $tdir;
932 =head2 catfile
934 Title : catfile
935 Usage : $path = Bio::Root::IO->catfile(@dirs, $filename);
936 Function: Constructs a full pathname in a cross-platform safe way.
938 If File::Spec exists on your system, this routine will merely
939 delegate to it. Otherwise it tries to make a good guess.
941 You should use this method whenever you construct a path name
942 from directory and filename. Otherwise you risk cross-platform
943 compatibility of your code.
945 You can call this method both as a class and an instance method.
947 Args : components of the pathname (directories and filename, NOT an
948 extension)
949 Returns : a string
951 =cut
953 sub catfile {
954 my ($self, @args) = @_;
956 return File::Spec->catfile(@args) if $FILESPECLOADED;
957 # this is clumsy and not very appealing, but how do we specify the
958 # root directory?
959 if($args[0] eq '/') {
960 $args[0] = $ROOTDIR;
962 return join($PATHSEP, @args);
966 =head2 rmtree
968 Title : rmtree
969 Usage : Bio::Root::IO->rmtree($dirname );
970 Function: Remove a full directory tree
972 If File::Path exists on your system, this routine will merely
973 delegate to it. Otherwise it runs a local version of that code.
975 You should use this method to remove directories which contain
976 files.
978 You can call this method both as a class and an instance method.
980 Args : roots - rootdir to delete or reference to list of dirs
982 verbose - a boolean value, which if TRUE will cause
983 C<rmtree> to print a message each time it
984 examines a file, giving the name of the file, and
985 indicating whether it's using C<rmdir> or
986 C<unlink> to remove it, or that it's skipping it.
987 (defaults to FALSE)
989 safe - a boolean value, which if TRUE will cause C<rmtree>
990 to skip any files to which you do not have delete
991 access (if running under VMS) or write access (if
992 running under another OS). This will change in the
993 future when a criterion for 'delete permission'
994 under OSs other than VMS is settled. (defaults to
995 FALSE)
996 Returns : number of files successfully deleted
998 =cut
1000 # taken straight from File::Path VERSION = "1.0403"
1001 sub rmtree {
1002 my ($self, $roots, $verbose, $safe) = @_;
1003 if ( $FILEPATHLOADED ) {
1004 return File::Path::rmtree ($roots, $verbose, $safe);
1007 my $force_writable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' ||
1008 $^O eq 'amigaos' || $^O eq 'cygwin');
1009 my $Is_VMS = $^O eq 'VMS';
1011 my @files;
1012 my $count = 0;
1013 $verbose ||= 0;
1014 $safe ||= 0;
1015 if ( defined($roots) && length($roots) ) {
1016 $roots = [$roots] unless ref $roots;
1017 } else {
1018 $self->warn("No root path(s) specified\n");
1019 return 0;
1022 my $root;
1023 for $root (@{$roots}) {
1024 $root =~ s#/\z##;
1025 (undef, undef, my $rp) = lstat $root or next;
1026 $rp &= 07777; # don't forget setuid, setgid, sticky bits
1027 if ( -d _ ) {
1028 # notabene: 0777 is for making readable in the first place,
1029 # it's also intended to change it to writable in case we have
1030 # to recurse in which case we are better than rm -rf for
1031 # subtrees with strange permissions
1032 chmod(0777, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
1033 or $self->warn("Could not make directory '$root' read+writable: $!")
1034 unless $safe;
1035 if (opendir DIR, $root){
1036 @files = readdir DIR;
1037 closedir DIR;
1038 } else {
1039 $self->warn("Could not read directory '$root': $!");
1040 @files = ();
1043 # Deleting large numbers of files from VMS Files-11 filesystems
1044 # is faster if done in reverse ASCIIbetical order
1045 @files = reverse @files if $Is_VMS;
1046 ($root = VMS::Filespec::unixify($root)) =~ s#\.dir\z## if $Is_VMS;
1047 @files = map("$root/$_", grep $_!~/^\.{1,2}\z/s,@files);
1048 $count += $self->rmtree([@files],$verbose,$safe);
1049 if ($safe &&
1050 ($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) {
1051 print "skipped '$root'\n" if $verbose;
1052 next;
1054 chmod 0777, $root
1055 or $self->warn("Could not make directory '$root' writable: $!")
1056 if $force_writable;
1057 print "rmdir '$root'\n" if $verbose;
1058 if (rmdir $root) {
1059 ++$count;
1061 else {
1062 $self->warn("Could not remove directory '$root': $!");
1063 chmod($rp, ($Is_VMS ? VMS::Filespec::fileify($root) : $root))
1064 or $self->warn("and can't restore permissions to "
1065 . sprintf("0%o",$rp) . "\n");
1068 else {
1069 if ( $safe
1070 and ($Is_VMS ? !&VMS::Filespec::candelete($root)
1071 : !(-l $root || -w $root))
1073 print "skipped '$root'\n" if $verbose;
1074 next;
1076 chmod 0666, $root
1077 or $self->warn( "Could not make file '$root' writable: $!")
1078 if $force_writable;
1079 warn "unlink '$root'\n" if $verbose;
1080 # delete all versions under VMS
1081 for (;;) {
1082 unless (unlink $root) {
1083 $self->warn("Could not unlink file '$root': $!");
1084 if ($force_writable) {
1085 chmod $rp, $root
1086 or $self->warn("and can't restore permissions to "
1087 . sprintf("0%o",$rp) . "\n");
1089 last;
1091 ++$count;
1092 last unless $Is_VMS && lstat $root;
1097 return $count;
1101 =head2 _flush_on_write
1103 Title : _flush_on_write
1104 Usage : $io->_flush_on_write($newval)
1105 Function: Boolean flag to indicate whether to flush
1106 the filehandle on writing when the end of
1107 a component is finished (Sequences, Alignments, etc)
1108 Args : Optional new value
1109 Returns : Value of _flush_on_write
1111 =cut
1113 sub _flush_on_write {
1114 my ($self, $value) = @_;
1115 if (defined $value) {
1116 $self->{'_flush_on_write'} = $value;
1118 return $self->{'_flush_on_write'};
1122 =head2 save_tempfiles
1124 Title : save_tempfiles
1125 Usage : $io->save_tempfiles(1)
1126 Function: Boolean flag to indicate whether to retain tempfiles/tempdir
1127 Args : Value evaluating to TRUE or FALSE
1128 Returns : Boolean value : 1 = save tempfiles/tempdirs, 0 = remove (default)
1130 =cut
1132 sub save_tempfiles {
1133 my $self = shift;
1134 if (@_) {
1135 my $value = shift;
1136 $self->{save_tempfiles} = $value ? 1 : 0;
1138 return $self->{save_tempfiles} || 0;