File Coverage

Bio/Root/Utilities.pm
Criterion Covered Total %
statement 187 401 46.6
branch 81 238 34.0
condition 35 89 39.3
subroutine 18 30 60.0
pod 23 23 100.0
total 344 781 44.0


line stmt bran cond sub pod time code
1             package Bio::Root::Utilities;
2 1     1   607 use strict;
  1         2  
  1         27  
3 1     1   300 use Bio::Root::IO;
  1         3  
  1         45  
4 1     1   11 use Bio::Root::Exception;
  1         2  
  1         14  
5 1     1   76 use base qw(Bio::Root::Root Exporter);
  1         2  
  1         124  
6              
7             =head1 SYNOPSIS
8              
9             =head2 Object Creation
10              
11             # Using the supplied singleton object:
12             use Bio::Root::Utilities qw(:obj);
13             $Util->some_method();
14              
15             # Create an object manually:
16             use Bio::Root::Utilities;
17             my $util = Bio::Root::Utilities->new();
18             $util->some_method();
19              
20             $date_stamp = $Util->date_format('yyy-mm-dd');
21              
22             $clean = $Util->untaint($dirty);
23              
24             $compressed = $Util->compress('/home/me/myfile.txt')
25              
26             my ($mean, $stdev) = $Util->mean_stdev( @data );
27              
28             $Util->authority("me@example.com");
29             $Util->mail_authority("Something you should know about...");
30              
31             ...and a host of other methods. See below.
32              
33             =head1 DESCRIPTION
34              
35             Provides general-purpose utilities of potential interest to any Perl script.
36              
37             The C<:obj> tag is a convenience that imports a $Util symbol into your
38             namespace representing a Bio::Root::Utilities object. This saves you
39             from creating your own Bio::Root::Utilities object via
40             Cnew()> or from prefixing all method calls with
41             C, though feel free to do these things if desired.
42             Since there should normally not be a need for a script to have more
43             than one Bio::Root::Utilities object, this module thus comes with it's
44             own singleton.
45              
46             =head1 INSTALLATION
47              
48             This module is included with the central Bioperl distribution:
49              
50             http://www.bioperl.org/wiki/Getting_BioPerl
51             ftp://bio.perl.org/pub/DIST
52              
53             Follow the installation instructions included in the README file.
54              
55             =head1 DEPENDENCIES
56              
57             Inherits from L, and uses L
58             and L.
59              
60             Relies on external executables for file compression/uncompression
61             and sending mail. No paths to these are hard coded but are located
62             as needed.
63              
64             =head1 SEE ALSO
65              
66             http://bioperl.org - Bioperl Project Homepage
67              
68             =head1 ACKNOWLEDGEMENTS
69              
70             This module was originally developed under the auspices of the
71             Saccharomyces Genome Database: http://www.yeastgenome.org/
72              
73             =head1 AUTHOR Steve Chervitz
74              
75             =cut
76              
77 1     1   6 use vars qw(@EXPORT_OK %EXPORT_TAGS);
  1         2  
  1         115  
78             @EXPORT_OK = qw($Util);
79             %EXPORT_TAGS = ( obj => [qw($Util)],
80             std => [qw($Util)],);
81              
82 1         4662 use vars qw($ID $Util $GNU_PATH $TIMEOUT_SECS
83             @COMPRESSION_UTILS @UNCOMPRESSION_UTILS
84             $DEFAULT_NEWLINE $NEWLINE $AUTHORITY
85             @MONTHS @DAYS $BASE_YEAR $DEFAULT_CENTURY
86 1     1   6 );
  1         2  
87              
88             $ID = 'Bio::Root::Utilities';
89             # Number of seconds to wait before timing out when reading input (taste_file())
90             $TIMEOUT_SECS = 30;
91             $NEWLINE = $ENV{'NEWLINE'} || undef;
92             $BASE_YEAR = 1900; # perl's localtime() assumes this for it's year data.
93             # TODO: update this every hundred years. Y2K-sensitive code.
94             $DEFAULT_CENTURY = $BASE_YEAR + 100;
95             @MONTHS = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
96             @DAYS = qw(Sun Mon Tue Wed Thu Fri Sat);
97             # Sets the preference for compression utilities to be used by compress().
98             # The first executable in this list to be found in the current PATH will be used,
99             # unless overridden in the call to that function. See docs for details.
100             @COMPRESSION_UTILS = qw(gzip bzip2 zip compress);
101             @UNCOMPRESSION_UTILS = qw(gunzip gzip bunzip2 unzip uncompress);
102              
103             # Default person to receive feedback from users and possibly automatic error messages.
104             $AUTHORITY = '';
105              
106             # Note: $GNU_PATH is now deprecated, shouldn't be needed since now this module
107             # will automatically locate the compression utility in the current PATH.
108             # Retaining $GNU_PATH for backward compatibility.
109             #
110             # $GNU_PATH points to the directory containing the gzip and gunzip
111             # executables. It may be required for executing gzip/gunzip
112             # in some situations (e.g., when $ENV{PATH} doesn't contain this dir.
113             # Customize $GNU_PATH for your site if the compress() or
114             # uncompress() functions are generating exceptions.
115             $GNU_PATH = '';
116             #$GNU_PATH = '/tools/gnu/bin/';
117              
118             $DEFAULT_NEWLINE = "\012"; # \n (used if get_newline() fails for some reason)
119              
120             ## Static UTIL object.
121             $Util = Bio::Root::Root->new();
122              
123              
124             =head2 date_format
125              
126             Title : date_format
127             Usage : $Util->date_format( [FMT], [DATE])
128             Purpose : -- Get a string containing the formatted date or time
129             : taken when this routine is invoked.
130             : -- Provides a way to avoid using `date`.
131             : -- Provides an interface to localtime().
132             : -- Interconverts some date formats.
133             :
134             : (For additional functionality, use Date::Manip or
135             : Date::DateCalc available from CPAN).
136             Example : $Util->date_format();
137             : $date = $Util->date_format('yyyy-mmm-dd', '11/22/92');
138             Returns : String (unless 'list' is provided as argument, see below)
139             :
140             : 'yyyy-mm-dd' = 1996-05-03 # default format.
141             : 'yyyy-dd-mm' = 1996-03-05
142             : 'yyyy-mmm-dd' = 1996-May-03
143             : 'd-m-y' = 3-May-1996
144             : 'd m y' = 3 May 1996
145             : 'dmy' = 3may96
146             : 'mdy' = May 3, 1996
147             : 'ymd' = 96may3
148             : 'md' = may3
149             : 'year' = 1996
150             : 'hms' = 23:01:59 # when not converting a format, 'hms' can be
151             : # tacked on to any of the above options
152             : # to add the time stamp: eg 'dmyhms'
153             : 'full' | 'unix' = UNIX-style date: Tue May 5 22:00:00 1998
154             : 'list' = the contents of localtime(time) in an array.
155             Argument : (all are optional)
156             : FMT = yyyy-mm-dd | yyyy-dd-mm | yyyy-mmm-dd |
157             : mdy | ymd | md | d-m-y | hms | hm
158             : ('hms' may be appended to any of these to
159             : add a time stamp)
160             :
161             : DATE = String containing date to be converted.
162             : Acceptable input formats:
163             : 12/1/97 (for 1 December 1997)
164             : 1997-12-01
165             : 1997-Dec-01
166             Throws :
167             Comments : If you don't care about formatting or using backticks, you can
168             : always use: $date = `date`;
169             :
170             : For more features, use Date::Manip.pm, (which I should
171             : probably switch to...)
172              
173             See Also : L, L
174              
175             =cut
176              
177             #---------------'
178             sub date_format {
179             #---------------
180 6     6 1 2016 my $self = shift;
181 6         10 my $option = shift;
182 6         7 my $date = shift; # optional date to be converted.
183              
184 6         7 my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst);
185              
186 6   100     15 $option ||= 'yyyy-mm-dd';
187              
188 6         9 my ($month_txt, $day_txt, $month_num, $fullYear);
189 6         0 my ($converting, @date);
190              
191             # Load a supplied date for conversion:
192 6 100 100     35 if(defined($date) && ($date =~ /[\D-]+/)) {
193 3         6 $converting = 1;
194 3 100       14 if( $date =~ m{/}) {
    50          
    0          
195 2         8 ($mon,$mday,$year) = split(m{/}, $date);
196             } elsif($date =~ /(\d{4})-(\d{1,2})-(\d{1,2})/) {
197 1         5 ($year,$mon,$mday) = ($1, $2, $3);
198             } elsif($date =~ /(\d{4})-(\w{3,})-(\d{1,2})/) {
199 0         0 ($year,$mon,$mday) = ($1, $2, $3);
200 0         0 $mon = $self->month2num($2);
201             } else {
202 0         0 print STDERR "\n*** Unsupported input date format: $date\n";
203             }
204 3 100       7 if(length($year) == 4) {
205 1         3 $fullYear = $year;
206 1         2 $year = substr $year, 2;
207             } else {
208             # Heuristics to guess what century was intended when a 2-digit year is given
209             # If number is over 50, assume it's for prev century; under 50 = default century.
210             # TODO: keep an eye on this Y2K-sensitive code
211 2 100       6 if ($year > 50) {
212 1         2 $fullYear = $DEFAULT_CENTURY + $year - 100;
213             } else {
214 1         3 $fullYear = $DEFAULT_CENTURY + $year;
215             }
216             }
217 3         7 $mon -= 1;
218             } else {
219 3 100       105 ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = @date =
220             localtime(($date ? $date : time()));
221 3 50       13 return @date if $option =~ /list/i;
222 3         6 $fullYear = $BASE_YEAR+$year;
223             }
224 6         10 $month_txt = $MONTHS[$mon];
225 6 100       12 $day_txt = $DAYS[$wday] if defined $wday;
226 6         8 $month_num = $mon+1;
227              
228             # print "sec: $sec, min: $min, hour: $hour, month: $mon, m-day: $mday, year: $year\nwday: $wday, yday: $yday, dst: $isdst";;
229              
230 6 100 0     40 if( $option =~ /yyyy-mm-dd/i ) {
    50          
    100          
    50          
    100          
    50          
    50          
    100          
    50          
    0          
    0          
    0          
    0          
231 2         11 $date = sprintf "%4d-%02d-%02d",$fullYear,$month_num,$mday;
232             } elsif( $option =~ /yyyy-dd-mm/i ) {
233 0         0 $date = sprintf "%4d-%02d-%02d",$fullYear,$mday,$month_num;
234             } elsif( $option =~ /yyyy-mmm-dd/i ) {
235 1         5 $date = sprintf "%4d-%3s-%02d",$fullYear,$month_txt,$mday;
236             } elsif( $option =~ /full|unix/i ) {
237 0         0 $date = sprintf "%3s %3s %2d %02d:%02d:%02d %d",$day_txt, $month_txt, $mday, $hour, $min, $sec, $fullYear;
238             } elsif( $option =~ /mdy/i ) {
239 1         5 $date = "$month_txt $mday, $fullYear";
240             } elsif( $option =~ /ymd/i ) {
241 0         0 $date = $year."\l$month_txt$mday";
242             } elsif( $option =~ /dmy/i ) {
243 0         0 $date = $mday."\l$month_txt$year";
244             } elsif( $option =~ /md/i ) {
245 1         6 $date = "\l$month_txt$mday";
246             } elsif( $option =~ /d-m-y/i ) {
247 1         4 $date = "$mday-$month_txt-$fullYear";
248             } elsif( $option =~ /d m y/i ) {
249 0         0 $date = "$mday $month_txt $fullYear";
250             } elsif( $option =~ /year/i ) {
251 0         0 $date = $fullYear;
252             } elsif( $option =~ /dmy/i ) {
253 0         0 $date = $mday.'-'.$month_txt.'-'.$fullYear;
254             } elsif($option and $option !~ /hms/i) {
255 0         0 print STDERR "\n*** Unrecognized date format request: $option\n";
256             }
257              
258 6 100 66     26 if( $option =~ /hms/i and not $converting) {
259 1 50       6 $date .= " $hour:$min:$sec" if $date;
260 1   33     4 $date ||= "$hour:$min:$sec";
261             }
262              
263 6   33     22 return $date || join(" ", @date);
264             }
265              
266              
267             =head2 month2num
268              
269             Title : month2num
270             Purpose : Converts a string containing a name of a month to integer
271             : representing the number of the month in the year.
272             Example : $Util->month2num("march"); # returns 3
273             Argument : The string argument must contain at least the first
274             : three characters of the month's name. Case insensitive.
275             Throws : Exception if the conversion fails.
276              
277             =cut
278              
279             #--------------'
280             sub month2num {
281             #--------------
282 12     12 1 6407 my ($self, $str) = @_;
283              
284             # Get string in proper format for conversion.
285 12         31 $str = substr($str, 0, 3);
286 12         38 for my $month (0..$#MONTHS) {
287 78 100       509 return $month+1 if $str =~ /$MONTHS[$month]/i;
288             }
289 0         0 $self->throw("Invalid month name: $str");
290             }
291              
292             =head2 num2month
293              
294             Title : num2month
295             Purpose : Does the opposite of month2num.
296             : Converts a number into a string containing a name of a month.
297             Example : $Util->num2month(3); # returns 'Mar'
298             Throws : Exception if supplied number is out of range.
299              
300             =cut
301              
302             #-------------
303             sub num2month {
304             #-------------
305 12     12 1 30 my ($self, $num) = @_;
306              
307 12 50 33     54 $self->throw("Month out of range: $num") if $num < 1 or $num > 12;
308 12         53 return $MONTHS[$num-1];
309             }
310              
311             =head2 compress
312              
313             Title : compress
314             Usage : $Util->compress(full-path-filename);
315             : $Util->compress();
316             Purpose : Compress a file.
317             Example : $Util->compress("/usr/people/me/data.txt");
318             : $Util->compress(-file=>"/usr/people/me/data.txt",
319             : -tmp=>1,
320             : -outfile=>"/usr/people/share/data.txt.gz",
321             : -exe=>"/usr/local/bin/fancyzip");
322             Returns : String containing full, absolute path to compressed file
323             Argument : Named parameters (case-insensitive):
324             : -FILE => String (name of file to be compressed, full path).
325             : If the supplied filename ends with '.gz' or '.Z',
326             : that extension will be removed before attempting to compress.
327             : Optional:
328             : -TMP => boolean. If true, (or if user is not the owner of the file)
329             : the file is compressed to a temp file. If false, file may be
330             : clobbered with the compressed version (if using a utility like
331             : gzip, which is the default)
332             : -OUTFILE => String (name of the output compressed file, full path).
333             : -EXE => Name of executable for compression utility to use.
334             : Will supersede those in @COMPRESSION_UTILS defined by
335             : this module. If the absolute path to the executable is not provided,
336             : it will be searched in the PATH env variable.
337             Throws : Exception if file cannot be compressed.
338             : If user is not owner of the file, generates a warning and compresses to
339             : a tmp file. To avoid this warning, use the -o file test operator
340             : and call this function with -TMP=>1.
341             Comments : Attempts to compress using utilities defined in the @COMPRESSION_UTILS
342             : defined by this module, in the order defined. The first utility that is
343             : found to be executable will be used. Any utility defined in optional -EXE param
344             : will be tested for executability first.
345             : To minimize security risks, the -EXE parameter value is untained using
346             : the untaint() method of this module (in 'relaxed' mode to permit path separators).
347              
348             See Also : L
349              
350             =cut
351              
352             #------------'
353             sub compress {
354             #------------
355 2     2 1 1452 my ($self, @args) = @_;
356             # This method formerly didn't use named params and expected fileName, tmp
357             # in that order. This should be backward compatibile.
358 2         22 my ($fileName, $tmp, $outfile, $exe) = $self->_rearrange([qw(FILE TMP OUTFILE EXE)], @args);
359 2         8 my ($file, $get, $fmt);
360              
361             # in case the supplied name already has a compressed extension
362 2 50       19 if($fileName =~ /(\.gz|\.Z|\.bz2|\.zip)$/) { $fileName =~ s/$1$//; };
  0         0  
363 2         22 $self->debug("compressing file $fileName");
364              
365 2         12 my @util_to_use = @COMPRESSION_UTILS;
366              
367 2 50       7 if (defined $exe){
368 0         0 $exe = $self->untaint($exe, 1);
369 0         0 unshift @util_to_use, $exe;
370             }
371              
372 2         7 my @checked = @util_to_use;
373 2   50     16 $exe ||= '';
374 2   100     21 while (not -x $exe and scalar(@util_to_use)) {
375 2         12 $exe = $self->find_exe(shift @util_to_use);
376             }
377              
378 2 50       16 unless (-x $exe) {
379 0         0 $self->throw("Can't find compression utility. Looked for @checked");
380             }
381              
382 2         6 my ($compressed, @cmd, $handle);
383              
384 2 50 66     24 if(defined($outfile) or $tmp or not -o $fileName) {
      66        
385 2 100       7 if (defined $outfile) {
386 1         7 $compressed = $outfile;
387             } else {
388             # obtain a temporary file name (not using the handle)
389             # and insert some special text to flag it as a bioperl-based temp file
390 1         10 my $io = Bio::Root::IO->new();
391 1         5 ($handle, $compressed) = $io->tempfile();
392 1         9 $compressed .= '.tmp.bioperl.gz';
393             }
394              
395             # Use double quotes if executable path have empty spaces
396 2 50       12 if ($exe =~ m/ /) {
397 0         0 $exe = "\"$exe\"";
398             }
399              
400 2 50       20 if ($exe =~ /gzip|bzip2|compress/) {
    0          
401 2         15 @cmd = ("$exe -f < \"$fileName\" > \"$compressed\"");
402             } elsif ($exe eq 'zip') {
403 0         0 @cmd = ("$exe -r \"$fileName.zip\" \"$fileName\"");
404             }
405 2 100       17 not $tmp and
406             $self->warn("Not owner of file $fileName. Compressing to temp file $compressed.");
407 2         7 $tmp = 1;
408             } else {
409             # Need to compute the compressed name based on exe since we're returning it.
410 0         0 $compressed = $fileName;
411 0 0       0 if ($exe =~ /gzip/) {
    0          
    0          
    0          
412 0         0 $compressed .= '.gz';
413             } elsif ($exe =~ /bzip2/) {
414 0         0 $compressed .= '.bz2';
415             } elsif ($exe =~ /zip/) {
416 0         0 $compressed .= '.zip';
417             } elsif ($exe =~ /compress/) {
418 0         0 $compressed .= '.Z';
419             }
420 0 0       0 if ($exe =~ /gzip|bzip2|compress/) {
    0          
421 0         0 @cmd = ($exe, '-f', $fileName);
422             } elsif ($exe eq 'zip') {
423 0         0 @cmd = ($exe, '-r', "$compressed", $fileName);
424             }
425             }
426              
427 2 50       9941 if(system(@cmd) != 0) {
428 0         0 $self->throw( -class => 'Bio::Root::SystemException',
429             -text => "Failed to compress file $fileName using $exe: $!");
430             }
431              
432 2         123 return $compressed;
433             }
434              
435             =head2 uncompress
436              
437             Title : uncompress
438             Usage : $Util->uncompress(full-path-filename);
439             : $Util->uncompress();
440             Purpose : Uncompress a file.
441             Example : $Util->uncompress("/usr/people/me/data.txt");
442             : $Util->uncompress(-file=>"/usr/people/me/data.txt.gz",
443             : -tmp=>1,
444             : -outfile=>"/usr/people/share/data.txt",
445             : -exe=>"/usr/local/bin/fancyzip");
446             Returns : String containing full, absolute path to uncompressed file
447             Argument : Named parameters (case-insensitive):
448             : -FILE => String (name of file to be uncompressed, full path).
449             : If the supplied filename ends with '.gz' or '.Z',
450             : that extension will be removed before attempting to uncompress.
451             : Optional:
452             : -TMP => boolean. If true, (or if user is not the owner of the file)
453             : the file is uncompressed to a temp file. If false, file may be
454             : clobbered with the uncompressed version (if using a utility like
455             : gzip, which is the default)
456             : -OUTFILE => String (name of the output uncompressed file, full path).
457             : -EXE => Name of executable for uncompression utility to use.
458             : Will supersede those in @UNCOMPRESSION_UTILS defined by
459             : this module. If the absolute path to the executable is not provided,
460             : it will be searched in the PATH env variable.
461             Throws : Exception if file cannot be uncompressed.
462             : If user is not owner of the file, generates a warning and uncompresses to
463             : a tmp file. To avoid this warning, use the -o file test operator
464             : and call this function with -TMP=>1.
465             Comments : Attempts to uncompress using utilities defined in the @UNCOMPRESSION_UTILS
466             : defined by this module, in the order defined. The first utility that is
467             : found to be executable will be used. Any utility defined in optional -EXE param
468             : will be tested for executability first.
469             : To minimize security risks, the -EXE parameter value is untained using
470             : the untaint() method of this module (in 'relaxed' mode to permit path separators).
471              
472             See Also : L
473              
474             =cut
475              
476             #------------'
477             sub uncompress {
478             #------------
479 2     2 1 4605 my ($self, @args) = @_;
480             # This method formerly didn't use named params and expected fileName, tmp
481             # in that order. This should be backward compatibile.
482 2         33 my ($fileName, $tmp, $outfile, $exe) = $self->_rearrange([qw(FILE TMP OUTFILE EXE)], @args);
483 2         9 my ($file, $get, $fmt);
484              
485             # in case the supplied name lacks a compressed extension
486 2 50       64 if(not $fileName =~ /(\.gz|\.Z|\.bz2|\.zip)$/) { $fileName .= $1; };
  0         0  
487 2         28 $self->debug("uncompressing file $fileName");
488              
489 2         14 my @util_to_use = @UNCOMPRESSION_UTILS;
490              
491 2 50       12 if (defined $exe){
492 0         0 $exe = $self->untaint($exe, 1);
493 0         0 unshift @util_to_use, $exe;
494             }
495              
496 2   50     19 $exe ||= '';
497 2   100     24 while (not -x $exe and scalar(@util_to_use)) {
498 2         21 $exe = $self->find_exe(shift @util_to_use);
499             }
500              
501 2 50       19 unless (-x $exe) {
502 0         0 $self->throw("Can't find compression utility. Looked for @util_to_use");
503             }
504              
505 2         7 my ($uncompressed, @cmd, $handle);
506              
507 2         7 $uncompressed = $fileName;
508 2         22 $uncompressed =~ s/\.\w+$//;
509              
510 2 100 66     31 if(defined($outfile) or $tmp or not -o $fileName) {
      66        
511 1 50       11 if (defined $outfile) {
512 1         5 $uncompressed = $outfile;
513             } else {
514             # obtain a temporary file name (not using the handle)
515 0         0 my $io = Bio::Root::IO->new();
516 0         0 ($handle, $uncompressed) = $io->tempfile();
517             # insert some special text to flag it as a bioperl-based temp file
518 0         0 $uncompressed .= '.tmp.bioperl';
519             }
520              
521             # Use double quotes if executable path have empty spaces
522 1 50       9 if ($exe =~ m/ /) {
523 0         0 $exe = "\"$exe\"";
524             }
525              
526 1 50       14 if ($exe =~ /gunzip|bunzip2|uncompress/) {
    0          
    0          
527 1         10 @cmd = ("$exe -f < \"$fileName\" > \"$uncompressed\"");
528             } elsif ($exe =~ /gzip/) {
529 0         0 @cmd = ("$exe -df < \"$fileName\" > \"$uncompressed\"");
530             } elsif ($exe eq 'unzip') {
531 0         0 @cmd = ("$exe -p \"$fileName\" > \"$uncompressed\"");
532             }
533 1 50       6 not $tmp and
534             $self->warn("Not owner of file $fileName. Uncompressing to temp file $uncompressed.");
535 1         4 $tmp = 1;
536             } else {
537 1 50       16 if ($exe =~ /gunzip|bunzip2|uncompress/) {
    0          
    0          
538 1         6 @cmd = ($exe, '-f', $fileName);
539             } elsif ($exe =~ /gzip/) {
540 0         0 @cmd = ($exe, '-df', $fileName);
541             } elsif ($exe eq 'zip') {
542 0         0 @cmd = ($exe, $fileName);
543             }
544             }
545              
546 2 50       8383 if(system(@cmd) != 0) {
547 0         0 $self->throw( -class => 'Bio::Root::SystemException',
548             -text => "Failed to uncompress file $fileName using $exe: $!");
549             }
550              
551 2         110 return $uncompressed;
552             }
553              
554              
555             =head2 file_date
556              
557             Title : file_date
558             Usage : $Util->file_date( filename [,date_format])
559             Purpose : Obtains the date of a given file.
560             : Provides flexible formatting via date_format().
561             Returns : String = date of the file as: yyyy-mm-dd (e.g., 1997-10-15)
562             Argument : filename = string, full path name for file
563             : date_format = string, desired format for date (see date_format()).
564             : Default = yyyy-mm-dd
565             Thows : Exception if no file is provided or does not exist.
566             Comments : Uses the mtime field as obtained by stat().
567              
568             =cut
569              
570             #--------------
571             sub file_date {
572             #--------------
573 1     1 1 8 my ($self, $file, $fmt) = @_;
574              
575 1 50 33     40 $self->throw("No such file: $file") if not $file or not -e $file;
576              
577 1   50     7 $fmt ||= 'yyyy-mm-dd';
578              
579 1         7 my @file_data = stat($file);
580 1         5 return $self->date_format($fmt, $file_data[9]); # mtime field
581             }
582              
583              
584             =head2 untaint
585              
586             Title : untaint
587             Purpose : To remove nasty shell characters from untrusted data
588             : and allow a script to run with the -T switch.
589             : Potentially dangerous shell meta characters: &;`'\"|*?!~<>^()[]{}$\n\r
590             : Accept only the first block of contiguous characters:
591             : Default allowed chars = "-\w.', ()"
592             : If $relax is true = "-\w.', ()\/=%:^<>*"
593             Usage : $Util->untaint($value, $relax)
594             Returns : String containing the untained data.
595             Argument: $value = string
596             : $relax = boolean
597             Comments:
598             This general untaint() function may not be appropriate for every situation.
599             To allow only a more restricted subset of special characters
600             (for example, untainting a regular expression), then using a custom
601             untainting mechanism would permit more control.
602              
603             Note that special trusted vars (like $0) require untainting.
604              
605             =cut
606              
607             #------------`
608             sub untaint {
609             #------------
610 4     4 1 485 my($self,$value,$relax) = @_;
611 4   100     22 $relax ||= 0;
612 4         4 my $untainted;
613              
614 4         40 $self->debug("\nUNTAINT: $value\n");
615              
616 4 100 66     14 unless (defined $value and $value ne '') {
617 1         6 return $value;
618             }
619              
620 3 100       7 if( $relax ) {
621 1         6 $value =~ /([-\w.\', ()\/=%:^<>*]+)/;
622 1         4 $untainted = $1
623             # } elsif( $relax == 2 ) { # Could have several degrees of relax.
624             # $value =~ /([-\w.\', ()\/=%:^<>*]+)/;
625             # $untainted = $1
626             } else {
627 2         13 $value =~ /([-\w.\', ()]+)/;
628 2         6 $untainted = $1
629             }
630              
631 3         9 $self->debug("UNTAINTED: $untainted\n");
632              
633 3         11 $untainted;
634             }
635              
636              
637             =head2 mean_stdev
638              
639             Title : mean_stdev
640             Usage : ($mean, $stdev) = $Util->mean_stdev( @data )
641             Purpose : Calculates the mean and standard deviation given a list of numbers.
642             Returns : 2-element list (mean, stdev)
643             Argument : list of numbers (ints or floats)
644             Thows : n/a
645              
646             =cut
647              
648             #---------------
649             sub mean_stdev {
650             #---------------
651 3     3 1 1430 my ($self, @data) = @_;
652 3 100       10 return (undef, undef) if not @data; # case of empty @data list
653 2         4 my $mean = 0;
654 2         3 my $N = 0;
655 2         3 foreach my $num (@data) {
656 4         5 $mean += $num;
657 4         5 $N++
658             }
659 2         4 $mean /= $N;
660 2         3 my $sum_diff_sqd = 0;
661 2         4 foreach my $num (@data) {
662 4         8 $sum_diff_sqd += ($mean - $num) * ($mean - $num);
663             }
664             # if only one element in @data list, unbiased stdev is undefined
665 2 100       7 my $stdev = $N <= 1 ? undef : sqrt( $sum_diff_sqd / ($N-1) );
666 2         7 return ($mean, $stdev);
667             }
668              
669              
670             =head2 count_files
671              
672             Title : count_files
673             Purpose : Counts the number of files/directories within a given directory.
674             : Also reports the number of text and binary files in the dir
675             : as well as names of these files and directories.
676             Usage : count_files(\%data)
677             : $data{-DIR} is the directory to be analyzed. Default is ./
678             : $data{-PRINT} = 0|1; if 1, prints results to STDOUT, (default=0).
679             Argument : Hash reference (empty)
680             Returns : n/a;
681             : Modifies the hash ref passed in as the sole argument.
682             : $$href{-TOTAL} scalar
683             : $$href{-NUM_TEXT_FILES} scalar
684             : $$href{-NUM_BINARY_FILES} scalar
685             : $$href{-NUM_DIRS} scalar
686             : $$href{-T_FILE_NAMES} array ref
687             : $$href{-B_FILE_NAMES} array ref
688             : $$href{-DIRNAMES} array ref
689              
690             =cut
691              
692             #----------------
693             sub count_files {
694             #----------------
695 0     0 1 0 my $self = shift;
696 0         0 my $href = shift; # Reference to an empty hash.
697 0         0 my( $name, @fileLine);
698 0   0     0 my $dir = $$href{-DIR} || './'; # THIS IS UNIX SPECIFIC? FIXME/TODO
699 0   0     0 my $print = $$href{-PRINT} || 0;
700              
701             ### Make sure $dir ends with /
702 0 0       0 $dir !~ m{/$} and do{ $dir .= '/'; $$href{-DIR} = $dir; };
  0         0  
  0         0  
703              
704 0 0       0 open ( my $PIPE, "ls -1 $dir |" ) || $self->throw("Can't open input pipe: $!");
705              
706             ### Initialize the hash data.
707 0         0 $$href{-TOTAL} = 0;
708 0         0 $$href{-NUM_TEXT_FILES} = $$href{-NUM_BINARY_FILES} = $$href{-NUM_DIRS} = 0;
709 0         0 $$href{-T_FILE_NAMES} = [];
710 0         0 $$href{-B_FILE_NAMES} = [];
711 0         0 $$href{-DIR_NAMES} = [];
712 0         0 while( my $line = <$PIPE> ) {
713 0         0 chomp();
714 0         0 $$href{-TOTAL}++;
715 0 0       0 if( -T $dir.$line ) {
716 0         0 $$href{-NUM_TEXT_FILES}++;
717 0         0 push @{$$href{-T_FILE_NAMES}}, $line; }
  0         0  
718 0 0 0     0 if( -B $dir.$line and not -d $dir.$line) {
719 0         0 $$href{-NUM_BINARY_FILES}++;
720 0         0 push @{$$href{-B_FILE_NAMES}}, $line; }
  0         0  
721 0 0       0 if( -d $dir.$line ) {
722 0         0 $$href{-NUM_DIRS}++;
723 0         0 push @{$$href{-DIR_NAMES}}, $line; }
  0         0  
724             }
725 0         0 close $PIPE;
726              
727 0 0       0 if( $print) {
728 0         0 printf( "\n%4d %s\n", $$href{-TOTAL}, "total files+dirs in $dir");
729 0         0 printf( "%4d %s\n", $$href{-NUM_TEXT_FILES}, "text files");
730 0         0 printf( "%4d %s\n", $$href{-NUM_BINARY_FILES}, "binary files");
731 0         0 printf( "%4d %s\n", $$href{-NUM_DIRS}, "directories");
732             }
733             }
734              
735              
736             =head2 file_info
737              
738             Title : file_info
739             Purpose : Obtains a variety of date for a given file.
740             : Provides an interface to Perl's stat().
741             Status : Under development. Not ready. Don't use!
742              
743             =cut
744              
745             #--------------
746             sub file_info {
747             #--------------
748 0     0 1 0 my ($self, %param) = @_;
749 0         0 my ($file, $get, $fmt) = $self->_rearrange([qw(FILE GET FMT)], %param);
750 0   0     0 $get ||= 'all';
751 0   0     0 $fmt ||= 'yyyy-mm-dd';
752              
753 0         0 my($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size,
754             $atime, $mtime, $ctime, $blksize, $blocks) = stat $file;
755              
756 0 0       0 if($get =~ /date/i) {
    0          
757             ## I can get the elapsed time since the file was modified but
758             ## it's not so straightforward to get the date in a nice format...
759             ## Think about using a standard CPAN module for this, like
760             ## Date::Manip or Date::DateCalc.
761              
762 0         0 my $date = $mtime;
763 0         0 my $elsec = time - $mtime;
764 0         0 printf "\nFile age: %.0f sec %.0f hrs %.0f days", $elsec, $elsec/3600, $elsec/(3600*24);;
  0         0  
765 0         0 my $days = sprintf "%.0f", $elsec/(3600*24);
766             } elsif($get eq 'all') {
767 0         0 return stat $file;
768             }
769             }
770              
771             =head2 delete
772              
773             Title : delete
774             Purpose :
775              
776             =cut
777              
778             #------------
779             sub delete {
780             #------------
781 0     0 1 0 my $self = shift;
782 0         0 my $fileName = shift;
783 0 0       0 if(not -e $fileName) {
    0          
784 0         0 $self->throw("Could not delete file '$fileName': Does not exist.");
785             } elsif(not -o $fileName) {
786 0         0 $self->throw("Could not delete file '$fileName': Not owner.");
787             }
788 0 0       0 my $ulval = unlink($fileName) > 0
789             or $self->throw("Failed to delete file '$fileName': $!");
790             }
791              
792              
793             =head2 create_filehandle
794              
795             Usage : $object->create_filehandle();
796             Purpose : Create a FileHandle object from a file or STDIN.
797             : Mainly used as a helper method by read() and get_newline().
798             Example : $data = $object->create_filehandle(-FILE =>'usr/people/me/data.txt')
799             Argument : Named parameters (case-insensitive):
800             : (all optional)
801             : -CLIENT => object reference for the object submitting
802             : the request. Default = $Util.
803             : -FILE => string (full path to file) or a reference
804             : to a FileHandle object or typeglob. This is an
805             : optional parameter (if not defined, STDIN is used).
806             Returns : Reference to a FileHandle object.
807             Throws : Exception if cannot open a supplied file or if supplied with a
808             : reference that is not a FileHandle ref.
809             Comments : If given a FileHandle reference, this method simply returns it.
810             : This method assumes the user wants to read ascii data. So, if
811             : the file is binary, it will be treated as a compressed (gzipped)
812             : file and access it using gzip -ce. The problem here is that not
813             : all binary files are necessarily compressed. Therefore,
814             : this method should probably have a -mode parameter to
815             : specify ascii or binary.
816              
817             See Also : L
818              
819             =cut
820              
821             #---------------------
822             sub create_filehandle {
823             #---------------------
824 1     1 1 5 my($self, @param) = @_;
825 1         8 my($client, $file, $handle) =
826             $self->_rearrange([qw( CLIENT FILE HANDLE )], @param);
827              
828 1 50       6 if(not ref $client) { $client = $self; }
  1         2  
829 1   33     7 $file ||= $handle;
830 1 50       13 if( $client->can('file')) {
831 0         0 $file = $client->file($file);
832             }
833              
834 1         3 my $FH;
835             my ($handle_ref);
836              
837 1 50       8 if($handle_ref = ref($file)) {
    50          
838 0 0       0 if($handle_ref eq 'FileHandle') {
    0          
839 0         0 $FH = $file;
840 0         0 $client->{'_input_type'} = "FileHandle";
841             } elsif($handle_ref eq 'GLOB') {
842 0         0 $FH = $file;
843 0         0 $client->{'_input_type'} = "Glob";
844             } else {
845 0         0 $self->throw(-class => 'Bio::Root::IOException',
846             -text => "Could not read file '$file': Not a FileHandle or GLOB ref.");
847             }
848 0 0       0 $self->verbose > 0 and printf STDERR "$ID: reading data from FileHandle\n";
849              
850             } elsif($file) {
851 0         0 $client->{'_input_type'} = "FileHandle for $file";
852              
853             # Use gzip -cd to access compressed data.
854 0 0       0 if( -B $file ) {
855 0         0 $client->{'_input_type'} .= " (compressed)";
856 0         0 my $gzip = $self->find_exe('gzip');
857 0         0 $file = "$gzip -cd $file |"
858             }
859              
860 0         0 require FileHandle;
861 0         0 $FH = FileHandle->new();
862 0 0       0 open ($FH, $file) || $self->throw(-class=>'Bio::Root::FileOpenException',
863             -text =>"Could not access data file '$file': $!");
864 0 0       0 $self->verbose > 0 and printf STDERR "$ID: reading data from file '$file'\n";
865              
866             } else {
867             # Read from STDIN.
868 1         2 $FH = \*STDIN;
869 1 50       6 $self->verbose > 0 and printf STDERR "$ID: reading data from STDIN\n";
870 1         4 $client->{'_input_type'} = "STDIN";
871             }
872              
873 1         3 return $FH;
874             }
875              
876             =head2 get_newline
877              
878             Usage : $object->get_newline();
879             Purpose : Determine the character(s) used for newlines in a given file or
880             : input stream. Delegates to Bio::Root::Utilities::get_newline()
881             Example : $data = $object->get_newline(-CLIENT => $anObj,
882             : -FILE =>'usr/people/me/data.txt')
883             Argument : Same arguemnts as for create_filehandle().
884             Returns : Reference to a FileHandle object.
885             Throws : Propagates any exceptions thrown by Bio::Root::Utilities::get_newline().
886              
887             See Also : L, L
888              
889             =cut
890              
891             #-----------------
892             sub get_newline {
893             #-----------------
894 1     1 1 4 my($self, @param) = @_;
895              
896 1 50       4 return $NEWLINE if defined $NEWLINE;
897              
898 1         12 my($client ) =
899             $self->_rearrange([qw( CLIENT )], @param);
900              
901 1         9 my $FH = $self->create_filehandle(@param);
902              
903 1 50       4 if(not ref $client) { $client = $self; }
  1         3  
904              
905 1 50       13 if($client->{'_input_type'} =~ /STDIN|Glob|compressed/) {
906             # Can't taste from STDIN since we can't seek 0 on it.
907             # Are other non special Glob refs seek-able?
908             # Attempt to guess newline based on platform.
909             # Not robust since we could be reading Unix files on a Mac, e.g.
910 1 50       4 if(defined $ENV{'MACPERL'}) {
911 0         0 $NEWLINE = "\015"; # \r
912             } else {
913 1         3 $NEWLINE = "\012"; # \n
914             }
915             } else {
916 0         0 $NEWLINE = $self->taste_file($FH);
917             }
918              
919             close ($FH) unless ($client->{'_input_type'} eq 'STDIN' ||
920             $client->{'_input_type'} eq 'FileHandle' ||
921 1 0 33     5 $client->{'_input_type'} eq 'Glob' );
      33        
922              
923 1         2 delete $client->{'_input_type'};
924              
925 1   33     7 return $NEWLINE || $DEFAULT_NEWLINE;
926             }
927              
928              
929             =head2 taste_file
930              
931             Usage : $object->taste_file( );
932             : Mainly a utility method for get_newline().
933             Purpose : Sample a filehandle to determine the character(s) used for a newline.
934             Example : $char = $Util->taste_file($FH)
935             Argument : Reference to a FileHandle object.
936             Returns : String containing an octal represenation of the newline character string.
937             : Unix = "\012" ("\n")
938             : Win32 = "\012\015" ("\r\n")
939             : Mac = "\015" ("\r")
940             Throws : Exception if no input is read within $TIMEOUT_SECS seconds.
941             : Exception if argument is not FileHandle object reference.
942             : Warning if cannot determine neewline char(s).
943             Comments : Based on code submitted by Vicki Brown (vlb@deltagen.com).
944              
945             See Also : L
946              
947             =cut
948              
949             #---------------
950             sub taste_file {
951             #---------------
952 0     0 1 0 my ($self, $FH) = @_;
953 0         0 my $BUFSIZ = 256; # Number of bytes read from the file handle.
954 0         0 my ($buffer, $octal, $str, $irs, $i);
955              
956 0 0       0 ref($FH) eq 'FileHandle' or $self->throw("Can't taste file: not a FileHandle ref");
957              
958 0         0 $buffer = '';
959              
960             # this is a quick hack to check for availability of alarm(); just copied
961             # from Bio/Root/IOManager.pm HL 02/19/01
962 0         0 my $alarm_available = 1;
963 0         0 eval {
964 0         0 alarm(0);
965             };
966 0 0       0 if($@) {
967             # alarm() not available (ActiveState perl for win32 doesn't have it.
968             # See jitterbug PR#98)
969 0         0 $alarm_available = 0;
970             }
971 0     0   0 $SIG{ALRM} = sub { die "Timed out!"; };
  0         0  
972 0         0 my $result;
973 0         0 eval {
974 0 0       0 $alarm_available && alarm( $TIMEOUT_SECS );
975 0         0 $result = read($FH, $buffer, $BUFSIZ); # read the $BUFSIZ characters of file
976 0 0       0 $alarm_available && alarm(0);
977             };
978 0 0       0 if($@ =~ /Timed out!/) {
    0          
    0          
979 0         0 $self->throw( "Timed out while waiting for input.",
980             "Timeout period = $TIMEOUT_SECS seconds.\n"
981             ."For longer time before timing out, edit \$TIMEOUT_SECS in Bio::Root::Utilities.pm.");
982              
983             } elsif(not $result) {
984 0         0 my $err = $@;
985 0         0 $self->throw("read taste failed to read from FileHandle.", $err);
986              
987             } elsif($@ =~ /\S/) {
988 0         0 my $err = $@;
989 0         0 $self->throw("Unexpected error during read: $err");
990             }
991              
992 0 0       0 seek($FH, 0, 0) or $self->throw("seek failed to seek 0 on FileHandle.");
993              
994 0         0 my @chars = split(//, $buffer);
995 0         0 my $flavor;
996              
997 0         0 for ($i = 0; $i <$BUFSIZ; $i++) {
998 0 0 0     0 if (($chars[$i] eq "\012")) {
    0          
    0          
999 0 0       0 unless ($chars[$i-1] eq "\015") {
1000 0         0 $flavor='Unix';
1001 0         0 $octal = "\012";
1002 0         0 $str = '\n';
1003 0         0 $irs = "^J";
1004 0         0 last;
1005             }
1006             } elsif (($chars[$i] eq "\015") && ($chars[$i+1] eq "\012")) {
1007 0         0 $flavor='DOS';
1008 0         0 $octal = "\015\012";
1009 0         0 $str = '\r\n';
1010 0         0 $irs = "^M^J";
1011 0         0 last;
1012             } elsif (($chars[$i] eq "\015")) {
1013 0         0 $flavor='Mac';
1014 0         0 $octal = "\015";
1015 0         0 $str = '\r';
1016 0         0 $irs = "^M";
1017 0         0 last;
1018             }
1019             }
1020 0 0       0 if (not $octal) {
1021 0         0 $self->warn("Could not determine newline char. Using '\012'");
1022 0         0 $octal = "\012";
1023             } else {
1024             #print STDERR "FLAVOR=$flavor, NEWLINE CHAR = $irs\n";
1025             }
1026 0         0 return($octal);
1027             }
1028              
1029             =head2 file_flavor
1030              
1031             Usage : $object->file_flavor( );
1032             Purpose : Returns the 'flavor' of a given file (unix, dos, mac)
1033             Example : print "$file has flavor: ", $Util->file_flavor($file);
1034             Argument : filename = string, full path name for file
1035             Returns : String describing flavor of file and handy info about line endings.
1036             : One of these is returned:
1037             : unix (\n or 012 or ^J)
1038             : dos (\r\n or 015,012 or ^M^J)
1039             : mac (\r or 015 or ^M)
1040             : unknown
1041             Throws : Exception if argument is not a file
1042             : Propagates any exceptions thrown by Bio::Root::Utilities::get_newline().
1043              
1044             See Also : L, L
1045              
1046             =cut
1047              
1048             #---------------
1049             sub file_flavor {
1050             #---------------
1051 1     1 1 578 my ($self, $file) = @_;
1052 1         5 my %flavors=("\012" =>'unix (\n or 012 or ^J)',
1053             "\015\012" =>'dos (\r\n or 015,012 or ^M^J)',
1054             "\015" =>'mac (\r or 015 or ^M)'
1055             );
1056              
1057 1 50       15 -f $file or $self->throw("Could not determine flavor: arg '$file' is either non existant or is not a file.\n");
1058 1         5 my $octal = $self->get_newline($file);
1059 1   50     5 my $flavor = $flavors{$octal} || "unknown";
1060 1         7 return $flavor;
1061             }
1062              
1063             ######################################
1064             ##### Mail Functions ########
1065             ######################################
1066              
1067             =head2 mail_authority
1068              
1069             Title : mail_authority
1070             Usage : $Util->mail_authority( $message )
1071             Purpose : Syntactic sugar to send email to $Bio::Root::Global::AUTHORITY
1072              
1073             See Also : L
1074              
1075             =cut
1076              
1077             #---------------
1078             sub mail_authority {
1079             #---------------
1080 0     0 1 0 my( $self, $message ) = @_;
1081 0         0 my $script = $self->untaint($0,1);
1082              
1083 0   0     0 my $email = $self->{'_auth_email'} || $AUTHORITY;
1084 0 0       0 if (defined $email) {
1085 0         0 $self->send_mail( -TO=>$AUTHORITY, -SUBJ=>$script, -MSG=>$message);
1086             } else {
1087 0         0 $self->throw("Can't email authority. No email defined.");
1088             }
1089             }
1090              
1091             =head2 authority
1092              
1093             Title : authority
1094             Usage : $Util->authority('admin@example.com');
1095             Purpose : Set/get the email address that should be notified by mail_authority()
1096              
1097             See Also : L
1098              
1099             =cut
1100              
1101             #-------------
1102             sub authority {
1103             #-------------
1104 0     0 1 0 my( $self, $email ) = @_;
1105 0 0       0 $self->{'_auth_email'} = $email if defined $email;
1106 0         0 return $self->{'_auth_email'};
1107             }
1108              
1109              
1110             =head2 send_mail
1111              
1112             Title : send_mail
1113             Usage : $Util->send_mail( named_parameters )
1114             Purpose : Provides an interface to mail or sendmail, if available
1115             Returns : n/a
1116             Argument : Named parameters: (case-insensitive)
1117             : -TO => e-mail address to send to
1118             : -SUBJ => subject for message (optional)
1119             : -MSG => message to be sent (optional)
1120             : -CC => cc: e-mail address (optional)
1121             Thows : Exception if TO: address appears bad or is missing.
1122             : Exception if mail cannot be sent.
1123             Comments : Based on TomC's tip at:
1124             : http://www.perl.com/CPAN/doc/FMTEYEWTK/safe_shellings
1125             :
1126             : Using default 'From:' information.
1127             : sendmail options used:
1128             : -t: ignore the address given on the command line and
1129             : get To:address from the e-mail header.
1130             : -oi: prevents send_mail from ending the message if it
1131             : finds a period at the start of a line.
1132              
1133             See Also : L
1134              
1135             =cut
1136              
1137              
1138             #-------------
1139             sub send_mail {
1140             #-------------
1141 0     0 1 0 my( $self, @param) = @_;
1142 0         0 my($recipient,$subj,$message,$cc) = $self->_rearrange([qw(TO SUBJ MSG CC)],@param);
1143              
1144 0 0       0 $self->throw("Invalid or missing e-mail address: $recipient")
1145             if not $recipient =~ /\S+\@\S+/;
1146              
1147 0   0     0 $subj ||= 'empty subject'; $message ||= '';
  0   0     0  
1148              
1149             # Best to use mail rather than sendmail. Permissions on sendmail in
1150             # linux distros have been significantly locked down in recent years,
1151             # due to the perception that it is insecure.
1152 0         0 my ($exe, $ccinfo);
1153 0 0       0 if ($exe = $self->find_exe('mail')) {
    0          
1154 0 0       0 if (defined $cc) {
1155 0         0 $ccinfo = "-c $cc";
1156             }
1157 0         0 $self->debug("send_mail: $exe -s '$subj' $ccinfo $recipient\n");
1158 0 0       0 open (MAIL, "| $exe -s '$subj' $ccinfo $recipient") ||
1159             $self->throw("Can't send email: mail cannot fork: $!");
1160 0         0 print MAIL <
1161             $message
1162             QQ_EOFM_QQ
1163 0 0       0 $? and $self->warn("mail didn't exit nicely: $?");
1164 0         0 close(MAIL);
1165             } elsif ($exe = $self->find_exe('sendmail')) {
1166 0 0       0 open (SENDMAIL, "| $exe -oi -t") ||
1167             $self->throw("Can't send email: sendmail cannot fork: $!");
1168 0         0 print SENDMAIL <
1169             To: $recipient
1170             Subject: $subj
1171             Cc: $cc
1172              
1173             $message
1174              
1175             QQ_EOFSM_QQ
1176 0 0       0 $? and $self->warn("sendmail didn't exit nicely: $?");
1177              
1178 0         0 close(SENDMAIL);
1179             } else {
1180 0         0 $self->throw("Can't find executable for mail or sendmail.");
1181             }
1182             }
1183              
1184              
1185             =head2 find_exe
1186              
1187             Title : find_exe
1188             Usage : $Util->find_exe(name);
1189             Purpose : Locate an executable (for use in a system() call, e.g.))
1190             Example : $Util->find_exe("gzip");
1191             Returns : String containing executable that passes the -x test.
1192             Returns undef if an executable of the supplied name cannot be found.
1193             Argument : Name of executable to be found.
1194             : Can be a full path. If supplied name is not executable, an executable
1195             : of that name will be searched in all directories in the currently
1196             : defined PATH environment variable.
1197             Throws : No exceptions, but issues a warning if multiple paths are found
1198             : for a given name. The first one is used.
1199             Comments : TODO: Confirm functionality on all bioperl-supported platforms.
1200             May get tripped up by variation in path separator character used
1201             for splitting ENV{PATH}.
1202             See Also :
1203              
1204             =cut
1205              
1206             #------------
1207             sub find_exe {
1208             #------------
1209 6     6 1 746 my ($self, $name) = @_;
1210 6         10 my @bindirs;
1211 6 50       42 if ($^O =~ m/mswin/i) {
1212 0         0 @bindirs = split ';', $ENV{'PATH'};
1213             # Add usual executable extension if missing or -x won't work
1214 0 0       0 $name.= '.exe' if ($name !~ m/\.exe$/i);
1215             }
1216             else {
1217 6         46 @bindirs = split ':', $ENV{'PATH'};
1218             }
1219 6         17 my $exe = $name;
1220 6 50       139 unless (-x $exe) {
1221 6         17 undef $exe;
1222 6         8 my @exes;
1223 6         70 foreach my $d (@bindirs) {
1224             # Note: Windows also understand '/' as folder separator,
1225             # so there is no need to use a conditional with '\'
1226 54 100       544 push(@exes, "$d/$name") if -x "$d/$name";
1227             }
1228 6 100       24 if (scalar @exes) {
1229 5         10 $exe = $exes[0];
1230 5 50       20 if (defined $exes[1]) {
1231 0         0 $self->warn("find_exe: Multiple paths to '$name' found. Using $exe.");
1232             }
1233             }
1234             }
1235 6         54 return $exe;
1236             }
1237              
1238              
1239             ######################################
1240             ### Interactive Functions #####
1241             ######################################
1242              
1243              
1244             =head2 yes_reply
1245              
1246             Title : yes_reply()
1247             Usage : $Util->yes_reply( [query_string]);
1248             Purpose : To test an STDIN input value for affirmation.
1249             Example : print +( $Util->yes_reply('Are you ok') ? "great!\n" : "sorry.\n" );
1250             : $Util->yes_reply('Continue') || die;
1251             Returns : Boolean, true (1) if input string begins with 'y' or 'Y'
1252             Argument: query_string = string to be used to prompt user (optional)
1253             : If not provided, 'Yes or no' will be used.
1254             : Question mark is automatically appended.
1255              
1256             =cut
1257              
1258             #-------------
1259             sub yes_reply {
1260             #-------------
1261 0     0 1   my $self = shift;
1262 0           my $query = shift;
1263 0           my $reply;
1264 0   0       $query ||= 'Yes or no';
1265 0           print "\n$query? (y/n) [n] ";
1266 0           chomp( $reply = );
1267 0           $reply =~ /^y/i;
1268             }
1269              
1270              
1271              
1272             =head2 request_data
1273              
1274             Title : request_data()
1275             Usage : $Util->request_data( [value_name]);
1276             Purpose : To request data from a user to be entered via keyboard (STDIN).
1277             Example : $name = $Util->request_data('Name');
1278             : # User will see: % Enter Name:
1279             Returns : String, (data entered from keyboard, sans terminal newline.)
1280             Argument: value_name = string to be used to prompt user.
1281             : If not provided, 'data' will be used, (not very helpful).
1282             : Question mark is automatically appended.
1283              
1284             =cut
1285              
1286             #----------------
1287             sub request_data {
1288             #----------------
1289 0     0 1   my $self = shift;
1290 0   0       my $data = shift || 'data';
1291 0           print "Enter $data: ";
1292             # Remove the terminal newline char.
1293 0           chomp($data = );
1294 0           $data;
1295             }
1296              
1297             =head2 quit_reply
1298              
1299             Title : quit_reply
1300             Usage :
1301             Purpose :
1302              
1303             =cut
1304              
1305             sub quit_reply {
1306             # Not much used since you can use request_data()
1307             # and test for an empty string.
1308 0     0 1   my $self = shift;
1309 0           my $reply;
1310 0           chop( $reply = );
1311 0           $reply =~ /^q.*/i;
1312             }
1313              
1314              
1315             =head2 verify_version
1316              
1317             Purpose : Checks the version of Perl used to invoke the script.
1318             : Aborts program if version is less than the given argument.
1319             Usage : verify_version('5.000')
1320              
1321             =cut
1322              
1323             #------------------
1324             sub verify_version {
1325             #------------------
1326 0     0 1   my $self = shift;
1327 0           my $reqVersion = shift;
1328              
1329 0 0         $] < $reqVersion and do {
1330 0           printf STDERR ( "\a\n%s %0.3f.\n", "** Sorry. This Perl script requires at least version", $reqVersion);
1331 0           printf STDERR ( "%s %0.3f %s\n\n", "You are running Perl version", $], "Please update your Perl!\n\n" );
1332 0           exit(1);
1333             }
1334             }
1335              
1336             1;
1337              
1338             __END__