Mercurial > repos > mahtabm > ensembl
diff variant_effect_predictor/Bio/Root/IO.pm @ 0:1f6dce3d34e0
Uploaded
author | mahtabm |
---|---|
date | Thu, 11 Apr 2013 02:01:53 -0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/variant_effect_predictor/Bio/Root/IO.pm Thu Apr 11 02:01:53 2013 -0400 @@ -0,0 +1,930 @@ +# $Id: IO.pm,v 1.37.2.3 2003/06/28 21:57:04 jason Exp $ +# +# BioPerl module for Bio::Root::IO +# +# Cared for by Hilmar Lapp <hlapp@gmx.net> +# +# Copyright Hilmar Lapp +# +# You may distribute this module under the same terms as perl itself + +# POD documentation - main docs before the code + +=head1 NAME + +Bio::Root::IO - module providing several methods often needed when dealing with file IO + +=head1 SYNOPSIS + + # utilize stream I/O in your module + $self->{'io'} = Bio::Root::IO->new(-file => "myfile"); + $self->{'io'}->_print("some stuff"); + $line = $self->{'io'}->_readline(); + $self->{'io'}->_pushback($line); + $self->{'io'}->close(); + + # obtain platform-compatible filenames + $path = Bio::Root::IO->catfile($dir, $subdir, $filename); + # obtain a temporary file (created in $TEMPDIR) + ($handle) = $io->tempfile(); + +=head1 DESCRIPTION + +This module provides methods that will usually be needed for any sort +of file- or stream-related input/output, e.g., keeping track of a file +handle, transient printing and reading from the file handle, a close +method, automatically closing the handle on garbage collection, etc. + +To use this for your own code you will either want to inherit from +this module, or instantiate an object for every file or stream you are +dealing with. In the first case this module will most likely not be +the first class off which your class inherits; therefore you need to +call _initialize_io() with the named parameters in order to set file +handle, open file, etc automatically. + +Most methods start with an underscore, indicating they are private. In +OO speak, they are not private but protected, that is, use them in +your module code, but a client code of your module will usually not +want to call them (except those not starting with an underscore). + +In addition this module contains a couple of convenience methods for +cross-platform safe tempfile creation and similar tasks. There are +some CPAN modules related that may not be available on all +platforms. At present, File::Spec and File::Temp are attempted. This +module defines $PATHSEP, $TEMPDIR, and $ROOTDIR, which will always be set, +and $OPENFLAGS, which will be set if either of File::Spec or File::Temp fails. + +The -noclose boolean (accessed via the noclose method) prevents a +filehandle from being closed when the IO object is cleaned up. This +is special behavior when a object like a parser might share a +filehandle with an object like an indexer where it is not proper to +close the filehandle as it will continue to be reused until the end of the +stream is reached. In general you won't want to play with this flag. + +=head1 FEEDBACK + +=head2 Mailing Lists + +User feedback is an integral part of the evolution of this +and other Bioperl modules. Send your comments and suggestions preferably + to one of the Bioperl mailing lists. +Your participation is much appreciated. + + bioperl-l@bioperl.org - General discussion + http://bio.perl.org/MailList.html - About the mailing lists + +=head2 Reporting Bugs + +Report bugs to the Bioperl bug tracking system to help us keep track + the bugs and their resolution. + Bug reports can be submitted via email or the web: + + bioperl-bugs@bio.perl.org + http://bugzilla.bioperl.org/ + +=head1 AUTHOR - Hilmar Lapp + +Email hlapp@gmx.net + +Describe contact details here + +=head1 APPENDIX + +The rest of the documentation details each of the object methods. Internal methods are usually preceded with a _ + +=cut + + +# Let the code begin... + + +package Bio::Root::IO; +use vars qw(@ISA $FILESPECLOADED $FILETEMPLOADED $FILEPATHLOADED + $TEMPDIR $PATHSEP $ROOTDIR $OPENFLAGS $VERBOSE); +use strict; + +use Symbol; +use POSIX qw(dup); +use IO::Handle; +use Bio::Root::Root; + +@ISA = qw(Bio::Root::Root); + +my $TEMPCOUNTER; +my $HAS_WIN32 = 0; + +BEGIN { + $TEMPCOUNTER = 0; + $FILESPECLOADED = 0; + $FILETEMPLOADED = 0; + $FILEPATHLOADED = 0; + $VERBOSE = 1; + + # try to load those modules that may cause trouble on some systems + eval { + require File::Path; + $FILEPATHLOADED = 1; + }; + if( $@ ) { + print STDERR "Cannot load File::Path: $@" if( $VERBOSE > 0 ); + # do nothing + } + + + # If on Win32, attempt to find Win32 package + + if($^O =~ /mswin/i) { + eval { + require Win32; + $HAS_WIN32 = 1; + }; + } + + # Try to provide a path separator. Why doesn't File::Spec export this, + # or did I miss it? + if($^O =~ /mswin/i) { + $PATHSEP = "\\"; + } elsif($^O =~ /macos/i) { + $PATHSEP = ":"; + } else { # unix + $PATHSEP = "/"; + } + eval { + require File::Spec; + $FILESPECLOADED = 1; + $TEMPDIR = File::Spec->tmpdir(); + $ROOTDIR = File::Spec->rootdir(); + require File::Temp; # tempfile creation + $FILETEMPLOADED = 1; + }; + if( $@ ) { + if(! defined($TEMPDIR)) { # File::Spec failed + # determine tempdir + if (defined $ENV{'TEMPDIR'} && -d $ENV{'TEMPDIR'} ) { + $TEMPDIR = $ENV{'TEMPDIR'}; + } elsif( defined $ENV{'TMPDIR'} && -d $ENV{'TMPDIR'} ) { + $TEMPDIR = $ENV{'TMPDIR'}; + } + if($^O =~ /mswin/i) { + $TEMPDIR = 'C:\TEMP' unless $TEMPDIR; + $ROOTDIR = 'C:'; + } elsif($^O =~ /macos/i) { + $TEMPDIR = "" unless $TEMPDIR; # what is a reasonable default on Macs? + $ROOTDIR = ""; # what is reasonable?? + } else { # unix + $TEMPDIR = "/tmp" unless $TEMPDIR; + $ROOTDIR = "/"; + } + if (!( -d $TEMPDIR && -w $TEMPDIR )) { + $TEMPDIR = '.'; # last resort + } + } + # File::Temp failed (alone, or File::Spec already failed) + # + # determine open flags for tempfile creation -- we'll have to do this + # ourselves + use Fcntl; + use Symbol; + $OPENFLAGS = O_CREAT | O_EXCL | O_RDWR; + for my $oflag (qw/FOLLOW BINARY LARGEFILE EXLOCK NOINHERIT TEMPORARY/){ + my ($bit, $func) = (0, "Fcntl::O_" . $oflag); + no strict 'refs'; + $OPENFLAGS |= $bit if eval { $bit = &$func(); 1 }; + } + } +} + +=head2 new + + Title : new + Usage : + Function: Overridden here to automatically call _initialize_io(). + Example : + Returns : new instance of this class + Args : named parameters + + +=cut + +sub new { + my ($caller, @args) = @_; + my $self = $caller->SUPER::new(@args); + + $self->_initialize_io(@args); + return $self; +} + +=head2 _initialize_io + + Title : initialize_io + Usage : $self->_initialize_io(@params); + Function: Initializes filehandle and other properties from the parameters. + + Currently recognizes the following named parameters: + -file name of file to open + -input name of file, or GLOB, or IO::Handle object + -fh file handle (mutually exclusive with -file) + -flush boolean flag to autoflush after each write + -noclose boolean flag, when set to true will not close a + filehandle (must explictly call close($io->_fh) + Returns : TRUE + Args : named parameters + + +=cut + +sub _initialize_io { + my($self, @args) = @_; + + $self->_register_for_cleanup(\&_io_cleanup); + + my ($input, $noclose, $file, $fh, $flush) = $self->_rearrange([qw(INPUT + NOCLOSE + FILE FH + FLUSH)], @args); + + delete $self->{'_readbuffer'}; + delete $self->{'_filehandle'}; + $self->noclose( $noclose) if defined $noclose; + # determine whether the input is a file(name) or a stream + if($input) { + if(ref(\$input) eq "SCALAR") { + # we assume that a scalar is a filename + if($file && ($file ne $input)) { + $self->throw("input file given twice: $file and $input disagree"); + } + $file = $input; + } elsif(ref($input) && + ((ref($input) eq "GLOB") || $input->isa('IO::Handle'))) { + # input is a stream + $fh = $input; + } else { + # let's be strict for now + $self->throw("unable to determine type of input $input: ". + "not string and not GLOB"); + } + } + if(defined($file) && defined($fh)) { + $self->throw("Providing both a file and a filehandle for reading - only one please!"); + } + + if(defined($file) && ($file ne '')) { + $fh = Symbol::gensym(); + open ($fh,$file) || + $self->throw("Could not open $file: $!"); + $self->file($file); + } + $self->_fh($fh) if $fh; # if not provided, defaults to STDIN and STDOUT + + $self->_flush_on_write(defined $flush ? $flush : 1); + + return 1; +} + +=head2 _fh + + Title : _fh + Usage : $obj->_fh($newval) + Function: Get/set the file handle for the stream encapsulated. + Example : + Returns : value of _filehandle + Args : newvalue (optional) + +=cut + +sub _fh { + my ($obj, $value) = @_; + if ( defined $value) { + $obj->{'_filehandle'} = $value; + } + return $obj->{'_filehandle'}; +} + +=head2 mode + + Title : mode + Usage : $obj->mode() + Function: + Example : + Returns : mode of filehandle: + 'r' for readable + 'w' for writeable + '?' if mode could not be determined + Args : -force (optional), see notes. + Notes : once mode() has been called, the filehandle's mode is cached + for further calls to mode(). to override this behavior so + that mode() re-checks the filehandle's mode, call with arg + -force + +=cut + +sub mode { + my ($obj, @arg) = @_; + my %param = @arg; + return $obj->{'_mode'} if defined $obj->{'_mode'} and !$param{-force}; + + print STDERR "testing mode... " if $obj->verbose; + + # we need to dup() the original filehandle because + # doing fdopen() calls on an already open handle causes + # the handle to go stale. is this going to work for non-unix + # filehandles? -allen + + my $fh = Symbol::gensym(); + + my $iotest = new IO::Handle; + + #test for a readable filehandle; + $iotest->fdopen( dup(fileno($obj->_fh)) , 'r' ); + if($iotest->error == 0){ + + # note the hack here, we actually have to try to read the line + # and if we get something, pushback() it into the readbuffer. + # this is because solaris and windows xp (others?) don't set + # IO::Handle::error. for non-linux the r/w testing is done + # inside this read-test, instead of the write test below. ugh. + + if($^O eq 'linux'){ + $obj->{'_mode'} = 'r'; + my $line = $iotest->getline; + $obj->_pushback($line) if defined $line; + $obj->{'_mode'} = defined $line ? 'r' : 'w'; + return $obj->{'_mode'}; + } else { + my $line = $iotest->getline; + $obj->_pushback($line) if defined $line; + $obj->{'_mode'} = defined $line ? 'r' : 'w'; + return $obj->{'_mode'}; + } + } + $iotest->clearerr; + + #test for a writeable filehandle; + $iotest->fdopen( dup(fileno($obj->_fh)) , 'w' ); + if($iotest->error == 0){ + $obj->{'_mode'} = 'w'; +# return $obj->{'_mode'}; + } + + #wtf type of filehandle is this? +# $obj->{'_mode'} = '?'; + return $obj->{'_mode'}; +} + +=head2 file + + Title : file + Usage : $obj->file($newval) + Function: Get/set the filename, if one has been designated. + Example : + Returns : value of file + Args : newvalue (optional) + + +=cut + +sub file { + my ($obj, $value) = @_; + if ( defined $value) { + $obj->{'_file'} = $value; + } + return $obj->{'_file'}; +} + +=head2 _print + + Title : _print + Usage : $obj->_print(@lines) + Function: + Example : + Returns : writes output + +=cut + +sub _print { + my $self = shift; + my $fh = $self->_fh() || \*STDOUT; + print $fh @_; +} + +=head2 _readline + + Title : _readline + Usage : $obj->_readline(%args) + Function: Reads a line of input. + + Note that this method implicitely uses the value of $/ that is + in effect when called. + + Note also that the current implementation does not handle pushed + back input correctly unless the pushed back input ends with the + value of $/. + + Example : + Args : Accepts a hash of arguments, currently only -raw is recognized + passing (-raw => 1) prevents \r\n sequences from being changed + to \n. The default value of -raw is undef, allowing \r\n to be + converted to \n. + Returns : + +=cut + +sub _readline { + my $self = shift; + my %param =@_; + my $fh = $self->_fh || \*ARGV; + my $line; + + # if the buffer been filled by _pushback then return the buffer + # contents, rather than read from the filehandle + $line = shift @{$self->{'_readbuffer'}} || <$fh>; + + #don't strip line endings if -raw is specified + $line =~ s/\r\n/\n/g if( (!$param{-raw}) && (defined $line) ); + + return $line; +} + +=head2 _pushback + + Title : _pushback + Usage : $obj->_pushback($newvalue) + Function: puts a line previously read with _readline back into a buffer. + buffer can hold as many lines as system memory permits. + Example : + Returns : + Args : newvalue + +=cut + +sub _pushback { + my ($obj, $value) = @_; + + $obj->{'_readbuffer'} ||= []; + push @{$obj->{'_readbuffer'}}, $value; +} + +=head2 close + + Title : close + Usage : $io->close() + Function: Closes the file handle associated with this IO instance. + Will not close the FH if -noclose is specified + Returns : none + Args : none + +=cut + +sub close { + my ($self) = @_; + return if $self->noclose; # don't close if we explictly asked not to + if( defined $self->{'_filehandle'} ) { + $self->flush; + return if( \*STDOUT == $self->_fh || + \*STDERR == $self->_fh || + \*STDIN == $self->_fh + ); # don't close STDOUT fh + if( ! ref($self->{'_filehandle'}) || + ! $self->{'_filehandle'}->isa('IO::String') ) { + close($self->{'_filehandle'}); + } + } + $self->{'_filehandle'} = undef; + delete $self->{'_readbuffer'}; +} + + +=head2 flush + + Title : flush + Usage : $io->flush() + Function: Flushes the filehandle + Returns : none + Args : none + +=cut + +sub flush { + my ($self) = shift; + + if( !defined $self->{'_filehandle'} ) { + $self->throw("Attempting to call flush but no filehandle active"); + } + + if( ref($self->{'_filehandle'}) =~ /GLOB/ ) { + my $oldh = select($self->{'_filehandle'}); + $| = 1; + select($oldh); + } else { + $self->{'_filehandle'}->flush(); + } +} + +=head2 noclose + + Title : noclose + Usage : $obj->noclose($newval) + Function: Get/Set the NOCLOSE flag - setting this to true will + prevent a filehandle from being closed + when an object is cleaned up or explicitly closed + This is a bit of hack + Returns : value of noclose (a scalar) + Args : on set, new value (a scalar or undef, optional) + + +=cut + +sub noclose{ + my $self = shift; + + return $self->{'_noclose'} = shift if @_; + return $self->{'_noclose'}; +} + +sub _io_cleanup { + my ($self) = @_; + + $self->close(); + my $v = $self->verbose; + + # we are planning to cleanup temp files no matter what + if( exists($self->{'_rootio_tempfiles'}) && + ref($self->{'_rootio_tempfiles'}) =~ /array/i) { + if( $v > 0 ) { + print STDERR "going to remove files ", + join(",", @{$self->{'_rootio_tempfiles'}}), "\n"; + } + unlink (@{$self->{'_rootio_tempfiles'}} ); + } + # cleanup if we are not using File::Temp + if( $self->{'_cleanuptempdir'} && + exists($self->{'_rootio_tempdirs'}) && + ref($self->{'_rootio_tempdirs'}) =~ /array/i) { + + if( $v > 0 ) { + print STDERR "going to remove dirs ", + join(",", @{$self->{'_rootio_tempdirs'}}), "\n"; + } + $self->rmtree( $self->{'_rootio_tempdirs'}); + } +} + +=head2 exists_exe + + Title : exists_exe + Usage : $exists = $obj->exists_exe('clustalw'); + $exists = Bio::Root::IO->exists_exe('clustalw') + $exists = Bio::Root::IO::exists_exe('clustalw') + Function: Determines whether the given executable exists either as file + or within the path environment. The latter requires File::Spec + to be installed. + On Win32-based system, .exe is automatically appended to the program + name unless the program name already ends in .exe. + Example : + Returns : 1 if the given program is callable as an executable, and 0 otherwise + Args : the name of the executable + +=cut + +sub exists_exe { + my ($self, $exe) = @_; + $exe = $self if(!(ref($self) || $exe)); + $exe .= '.exe' if(($^O =~ /mswin/i) && ($exe !~ /\.(exe|com|bat|cmd)$/i)); + return $exe if(-e $exe); # full path and exists + + # Ewan's comment. I don't think we need this. People should not be + # asking for a program with a pathseparator starting it + + # $exe =~ s/^$PATHSEP//; + + # Not a full path, or does not exist. Let's see whether it's in the path. + if($FILESPECLOADED) { + foreach my $dir (File::Spec->path()) { + my $f = Bio::Root::IO->catfile($dir, $exe); + return $f if(-e $f && -x $f ); + } + } + return 0; +} + +=head2 tempfile + + Title : tempfile + Usage : my ($handle,$tempfile) = $io->tempfile(); + Function: Returns a temporary filename and a handle opened for writing and + and reading. + + Caveats : If you do not have File::Temp on your system you should avoid + specifying TEMPLATE and SUFFIX. (We don't want to recode + everything, okay?) + Returns : a 2-element array, consisting of temporary handle and temporary + file name + Args : named parameters compatible with File::Temp: DIR (defaults to + $Bio::Root::IO::TEMPDIR), TEMPLATE, SUFFIX. + +=cut + +#' +sub tempfile { + my ($self, @args) = @_; + my ($tfh, $file); + my %params = @args; + + # map between naming with and without dash + foreach my $key (keys(%params)) { + if( $key =~ /^-/ ) { + my $v = $params{$key}; + delete $params{$key}; + $params{uc(substr($key,1))} = $v; + } else { + # this is to upper case + my $v = $params{$key}; + delete $params{$key}; + $params{uc($key)} = $v; + } + } + $params{'DIR'} = $TEMPDIR if(! exists($params{'DIR'})); + unless (exists $params{'UNLINK'} && + defined $params{'UNLINK'} && + ! $params{'UNLINK'} ) { + $params{'UNLINK'} = 1; + } else { $params{'UNLINK'} = 0 } + + if($FILETEMPLOADED) { + if(exists($params{'TEMPLATE'})) { + my $template = $params{'TEMPLATE'}; + delete $params{'TEMPLATE'}; + ($tfh, $file) = File::Temp::tempfile($template, %params); + } else { + ($tfh, $file) = File::Temp::tempfile(%params); + } + } else { + my $dir = $params{'DIR'}; + $file = $self->catfile($dir, + (exists($params{'TEMPLATE'}) ? + $params{'TEMPLATE'} : + sprintf( "%s.%s.%s", + $ENV{USER} || 'unknown', $$, + $TEMPCOUNTER++))); + + # sneakiness for getting around long filenames on Win32? + if( $HAS_WIN32 ) { + $file = Win32::GetShortPathName($file); + } + + # taken from File::Temp + if ($] < 5.006) { + $tfh = &Symbol::gensym; + } + # Try to make sure this will be marked close-on-exec + # XXX: Win32 doesn't respect this, nor the proper fcntl, + # but may have O_NOINHERIT. This may or may not be in Fcntl. + local $^F = 2; + # Store callers umask + my $umask = umask(); + # Set a known umaskr + umask(066); + # Attempt to open the file + if ( sysopen($tfh, $file, $OPENFLAGS, 0600) ) { + # Reset umask + umask($umask); + } else { + $self->throw("Could not open tempfile $file: $!\n"); + } + } + + if( $params{'UNLINK'} ) { + push @{$self->{'_rootio_tempfiles'}}, $file; + } + + + return wantarray ? ($tfh,$file) : $tfh; +} + +=head2 tempdir + + Title : tempdir + Usage : my ($tempdir) = $io->tempdir(CLEANUP=>1); + Function: Creates and returns the name of a new temporary directory. + + Note that you should not use this function for obtaining "the" + temp directory. Use $Bio::Root::IO::TEMPDIR for that. Calling this + method will in fact create a new directory. + + Returns : The name of a new temporary directory. + Args : args - ( key CLEANUP ) indicates whether or not to cleanup + dir on object destruction, other keys as specified by File::Temp + +=cut + +sub tempdir { + my ( $self, @args ) = @_; + if($FILETEMPLOADED && File::Temp->can('tempdir') ) { + return File::Temp::tempdir(@args); + } + + # we have to do this ourselves, not good + # + # we are planning to cleanup temp files no matter what + my %params = @args; + $self->{'_cleanuptempdir'} = ( defined $params{CLEANUP} && + $params{CLEANUP} == 1); + my $tdir = $self->catfile($TEMPDIR, + sprintf("dir_%s-%s-%s", + $ENV{USER} || 'unknown', $$, + $TEMPCOUNTER++)); + mkdir($tdir, 0755); + push @{$self->{'_rootio_tempdirs'}}, $tdir; + return $tdir; +} + +=head2 catfile + + Title : catfile + Usage : $path = Bio::Root::IO->catfile(@dirs,$filename); + Function: Constructs a full pathname in a cross-platform safe way. + + If File::Spec exists on your system, this routine will merely + delegate to it. Otherwise it tries to make a good guess. + + You should use this method whenever you construct a path name + from directory and filename. Otherwise you risk cross-platform + compatibility of your code. + + You can call this method both as a class and an instance method. + + Returns : a string + Args : components of the pathname (directories and filename, NOT an + extension) + +=cut + +sub catfile { + my ($self, @args) = @_; + + return File::Spec->catfile(@args) if($FILESPECLOADED); + # this is clumsy and not very appealing, but how do we specify the + # root directory? + if($args[0] eq '/') { + $args[0] = $ROOTDIR; + } + return join($PATHSEP, @args); +} + +=head2 rmtree + + Title : rmtree + Usage : Bio::Root::IO->rmtree($dirname ); + Function: Remove a full directory tree + + If File::Path exists on your system, this routine will merely + delegate to it. Otherwise it runs a local version of that code. + + You should use this method to remove directories which contain + files. + + You can call this method both as a class and an instance method. + + Returns : number of files successfully deleted + Args : roots - rootdir to delete or reference to list of dirs + + verbose - a boolean value, which if TRUE will cause + C<rmtree> to print a message each time it + examines a file, giving the name of the file, and + indicating whether it's using C<rmdir> or + C<unlink> to remove it, or that it's skipping it. + (defaults to FALSE) + + safe - a boolean value, which if TRUE will cause C<rmtree> + to skip any files to which you do not have delete + access (if running under VMS) or write access (if + running under another OS). This will change in the + future when a criterion for 'delete permission' + under OSs other than VMS is settled. (defaults to + FALSE) + +=cut + +# taken straight from File::Path VERSION = "1.0403" +sub rmtree { + my($self,$roots, $verbose, $safe) = @_; + if( $FILEPATHLOADED ) { + return File::Path::rmtree ($roots, $verbose, $safe); + } + + my $force_writeable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' + || $^O eq 'amigaos'); + my $Is_VMS = $^O eq 'VMS'; + + my(@files); + my($count) = 0; + $verbose ||= 0; + $safe ||= 0; + if ( defined($roots) && length($roots) ) { + $roots = [$roots] unless ref $roots; + } else { + $self->warn("No root path(s) specified\n"); + return 0; + } + + my($root); + foreach $root (@{$roots}) { + $root =~ s#/\z##; + (undef, undef, my $rp) = lstat $root or next; + $rp &= 07777; # don't forget setuid, setgid, sticky bits + if ( -d _ ) { + # notabene: 0777 is for making readable in the first place, + # it's also intended to change it to writable in case we have + # to recurse in which case we are better than rm -rf for + # subtrees with strange permissions + chmod(0777, ($Is_VMS ? VMS::Filespec::fileify($root) : $root)) + or $self->warn("Can't make directory $root read+writeable: $!") + unless $safe; + if (opendir(DIR, $root) ){ + @files = readdir DIR; + closedir(DIR); + } else { + $self->warn( "Can't read $root: $!"); + @files = (); + } + + # Deleting large numbers of files from VMS Files-11 filesystems + # is faster if done in reverse ASCIIbetical order + @files = reverse @files if $Is_VMS; + ($root = VMS::Filespec::unixify($root)) =~ s#\.dir\z## if $Is_VMS; + @files = map("$root/$_", grep $_!~/^\.{1,2}\z/s,@files); + $count += $self->rmtree([@files],$verbose,$safe); + if ($safe && + ($Is_VMS ? !&VMS::Filespec::candelete($root) : !-w $root)) { + print "skipped $root\n" if $verbose; + next; + } + chmod 0777, $root + or $self->warn( "Can't make directory $root writeable: $!") + if $force_writeable; + print "rmdir $root\n" if $verbose; + if (rmdir $root) { + ++$count; + } + else { + $self->warn( "Can't remove directory $root: $!"); + chmod($rp, ($Is_VMS ? VMS::Filespec::fileify($root) : $root)) + or $self->warn("and can't restore permissions to " + . sprintf("0%o",$rp) . "\n"); + } + } + else { + + if ($safe && + ($Is_VMS ? !&VMS::Filespec::candelete($root) + : !(-l $root || -w $root))) + { + print "skipped $root\n" if $verbose; + next; + } + chmod 0666, $root + or $self->warn( "Can't make file $root writeable: $!") + if $force_writeable; + print "unlink $root\n" if $verbose; + # delete all versions under VMS + for (;;) { + unless (unlink $root) { + $self->warn( "Can't unlink file $root: $!"); + if ($force_writeable) { + chmod $rp, $root + or $self->warn("and can't restore permissions to " + . sprintf("0%o",$rp) . "\n"); + } + last; + } + ++$count; + last unless $Is_VMS && lstat $root; + } + } + } + + $count; +} + +=head2 _flush_on_write + + Title : _flush_on_write + Usage : $obj->_flush_on_write($newval) + Function: Boolean flag to indicate whether to flush + the filehandle on writing when the end of + a component is finished (Sequences,Alignments,etc) + Returns : value of _flush_on_write + Args : newvalue (optional) + + +=cut + +sub _flush_on_write { + my ($self,$value) = @_; + if( defined $value) { + $self->{'_flush_on_write'} = $value; + } + return $self->{'_flush_on_write'}; +} + +1;