#!/usr/bin/perl -w use strict; package Futils; # use Handy::Dandy; @Futils::ISA = qw( Handy::Dandy Exporter ); @Futils::EXPORT_OK= qw( can_flock newline SL n ); $Futils::VERSION = 2.06; # 8/6/2002, 5:40:21 PM $Futils::NAME = __PACKAGE__; $Futils::started = time(); =pod AUTHOR -Tommy Butler, professional contractor and open source proponent Atrixnet™, for Internet Business Software® http://atrixnet.com 6711 Forest Park Dr Dallas, TX 76001 COPYRIGHT Copyright Tommy Butler. All rights reserved LISCENCE This software is free, and you may use and distribute it under the GNU GPL liscence. If you modify the code for your own purposes please acknowledge its original author. BUGS Please report any of the following to me - bugs - interface inconsistencies - suggestions - comments - complaints - smart remarks =cut BEGIN { require Config; # start by figuring out the os we're running under. Some systems support # the $^O variable. If not available then require() the Config library unless ($Futils::OS = $^O) { # I am so sick of the freakin 'used once' warning! Hence... $Futils::OS = eval(q[$Config::Config{'osname'}]); } if ($Futils::OS=~/darwin/i) { $Futils::OS = 'UNIX'; } elsif ($Futils::OS=~/cygwin/i) { $Futils::OS = 'CYGWIN'; } elsif ($Futils::OS=~/Win/i) { $Futils::OS = 'WINDOWS'; } elsif ($Futils::OS=~/vms/i) { $Futils::OS = 'VMS'; } elsif ($Futils::OS=~/bsdos/i) { $Futils::OS = 'UNIX'; } elsif ($Futils::OS=~/dos/i) { $Futils::OS = 'DOS'; } elsif ($Futils::OS=~/^MacOS$/i) { $Futils::OS = 'MACINTOSH'; } elsif ($Futils::OS=~/epoc/) { $Futils::OS = 'EPOC'; } elsif ($Futils::OS=~/os2/i) { $Futils::OS = 'OS2'; } else { $Futils::OS = 'UNIX'; } # Some OS logic. Binary mode enabled on DOS, NT and VMS $Futils::needs_binmode = 0; $Futils::needs_binmode = $Futils::OS=~/^(WINDOWS|DOS|OS2|MSWin)/o; $Futils::EBCDIC = qq[\t] ne qq[\011]; if ($Futils::OS eq 'VMS') { $Futils::n = eval(q[chr(10)]) } elsif ($Futils::EBCDIC) { $Futils::n = eval(q[chr(13).chr(10)]) } else { $Futils::n = qq[\015\012] } # The path separator is a slash, backslash or semicolon, depending # on the paltform. $Futils::SL = {}; $Futils::SL = { 'DOS' => '\\', 'EPOC' => '/', 'MACINTOSH' => ':', 'OS2' => '\\', 'UNIX' => '/', 'CYGWIN' => '/', 'VMS' => '/', 'WINDOWS' => '\\', }->{$Futils::OS}; # When all else fails... $Futils::SL ||= '/'; # set readlimit to a default of 10 megabytes $Futils::readlimit = 10000000; # maximum allowed directory depth for a recursive call to list_dir $Futils::maxdepth = 500; # a global recursion iterator $Futils::recursed = 0; # trail-ing directory separator $Futils::trailer = qr/(?:$Futils::SL$)|(?:\/$)|(?:\\$)|(?:\:$)/; # directory split $Futils::dirsplit = quotemeta($Futils::SL); $Futils::dirsplit = qr/$Futils::dirsplit|\\|\/|\:/; # allow empty writes $Futils::empty_writes= 0; =pod OPTIONS ON I/O RACE CONDITION POLICY set $Futils::onflockfail to any of the following strings: ex- @Futils::onflockfail = qw( wait fail ); --fatal fails with stack trace --fail same as above --warn warns() about the error with a stack trace --ignore ignores the failure to get an exclusive lock --undef returns undef --0 returns 0 * --wait waits to try getting an exclusive lock * --block same as above * used in tandem with another option. you must specify another option to fall back on if blocking should fail. otherwise Futils will throw a fatal error and dump a stack trace before exiting the process with a 0 status =cut @Futils::onflockfail = qw( --block --fail ); # use file locking $Futils::use_flock = 1; # characters not allowed in filenames (sorry, no spaces) $Futils::illegal_filename_chrs = qr/ (?sx) $Futils::SL| \\| \/| \:| \ | \|| \*| \?| \"| \<| \>| \t| \n| \r /; $Futils::canhackit = 0; $Futils::canhackit = 1 unless ($] < 5.006001); { # can your perl flock() $Futils::defineflock = $Config::Config{'d_flock'} || $Config::Config{'d_lockf'} || $Config::Config{'d_fcntl_can_lock'} ||0; $Futils::hasflock = 1 if (eval(<<__canflock__)); flock(STDOUT, Fcntl::LOCK_EX() | Fcntl::LOCK_NB()); flock(STDOUT, Fcntl::LOCK_UN()); __canflock__ $Futils::hasflock = 1 if (!$@); $Futils::hasflock||=0; $Futils::defineflock||=0; } use Fcntl qw( ); # keep your dad-blasted crap in your own namespace! use vars qw( $ebl $ebr $n ); $ebl = '»'; $ebr = '«'; $n = $Futils::n; ($ebl,$ebr) = ('~~>','<~~') if ($Futils::OS eq 'WINDOWS') } # end BEGIN-time loaded constants $Futils::modes->{'popen'} = { 'write' => '>', 'trunc' => '>', 'append' => '>>', 'read' => '<', }; $Futils::modes->{'sysopen'} = { 'read' => 'Fcntl::O_RDONLY()', 'write' => 'Fcntl::O_WRONLY() | Fcntl::O_CREAT()', 'append' => 'Fcntl::O_WRONLY() | Fcntl::O_APPEND() | Fcntl::O_CREAT()', 'trunc' => 'Fcntl::O_WRONLY() | Fcntl::O_CREAT() | Fcntl::O_TRUNC()', }; # use exception handler library # use expt_handler; # -------------------------------------------------------- # Constructor # -------------------------------------------------------- sub new { my($class) = shift(@_); $class = __PACKAGE__; # yeah, yeah --no hacking ;O) my($this) = {}; # bless object ref into the class' namespace bless($this, $class); # get input in hash context, no warnings my($opts) = $this->shave_opts(\@_); my($in) = $this->coerce_array(@_); $Futils::use_flock = 0 if ($opts->{'--no-flock'}); # set up class attributes $this->{'name'} = $Futils::NAME; $this->{'n'} = $Futils::n; $this->{'SL'} = $Futils::SL; $this->{'opts'} = $opts; $this->{'expt'} = expt_handler->new(); $Futils::readlimit = $in->{'readlimit'} if (defined($in->{'readlimit'})); $this->{'hasflock'} = $Futils::hasflock; $this->{'object'} = $this; # return object reference return($this); } # Futils::------------------------------------------------ # newline() can_flock() n() SL() # -------------------------------------------------------- sub can_flock { $Futils::hasflock } sub newline { $Futils::n } sub SL { $Futils::SL } sub n { $Futils::n } # -------------------------------------------------------- # Futils::list_dir() # -------------------------------------------------------- sub list_dir { my($this) = shift(@_); my($opts) = $this->shave_opts(\@_); my($dir) = shift(@_)||'.'; my($path) = $dir; my(@files) = (); my(@dirs) = (); my(@items) = (); my($r) = 0; # if the call to this method didn't include a directory name to open, # then complain about it $this-> throw ( 'meth' => 'list_dir', 'missing' => 'a directory name', 'error' => 'no input', ) unless (length($dir) > 0); # if the call to this method included a non-existant directory then # complain about it $this-> throw ( 'filename' => $dir, 'error' => 'no such file', ) unless (-e $dir); if ($opts->{'--recursing'}) { ++$Futils::recursed } else { $Futils::recursed = 0 } if ($Futils::recursed >= $Futils::maxdepth) { $this->{'expt'}->fail(<<__rbail__); recursion limit reached. Maximum recursive directory listings is set to $Futils::maxdepth. Try setting \$Futils::maxdepth to a higher number before calling list_dir() with option '--follow' or '--recurse' This operation aborted. __rbail__ } $r = 1 if ( $opts->{'--follow'} || $opts->{'--recurse'} ); # whack off any trailing directory separator unless (length($dir) == 1) { $dir =~ s/$Futils::trailer//o; $path =~ s/$Futils::trailer//o; } # can't open a directory if the argument isn't a diretory file type $this-> throw ( 'filename' => $dir, 'error' => 'called opendir on a file', ) unless (-d $dir); # localize filehandle that we're going to be using in this method local(*DIR); # open the directory for reading or fail with a diagnostic error message # if our attempt to open the directory was unsuccessful opendir(DIR, $dir) or $this-> throw ( 'dir' => $dir, 'error' => 'bad opendir', 'exception' => $!, ); # read from beginning of the directory (doesn't seem necessary on any # platforms I've run code on, but just in case...) rewinddir(DIR); # assign the contents of the directory to this lexically scoped array # variable(memory for *that* variable will be freed when execution # leaves this method / sub if ($opts->{'--count-only'}) { my($i) = 0; my($o) = ''; while ($o = readdir(DIR)) { ++$i unless (($o eq '.')||($o eq '..')) } return($i); } @files = readdir(DIR); # close the directory or send out a system warning with any diagnostics # about the error. if all else fails, Perl will close the directory # for us when the top-level process has finished execution closedir(DIR) or $this-> throw ( 'dir' => $dir, 'error' => 'close dir', 'exception' => $!, ); if ($opts->{'--no-fsdots'}) { my(@shadow) = @files; @files = (); while (@shadow) { my($f) = shift(@shadow); push(@files,$f) unless ( $this->strip_path($f) eq '.' or $this->strip_path($f) eq '..' ); } } foreach (@files) { my($i) = 0; my($listing) = ($opts->{'--with-paths'} or ($r==1)) ? $path.$Futils::SL.$_ : $_; if (-d $path.$Futils::SL.$_) { push(@dirs, $listing); } else { push(@items, $listing); } } if (($r) and (not $opts->{'--override-follow'})) { my(@shadow) = @dirs; @dirs = (); while (@shadow) { my($f) = shift(@shadow); push(@dirs,$f) unless ( $this->strip_path($f) eq '.' or $this->strip_path($f) eq '..' ); } foreach (@dirs) { my(@lsts) = $this-> list_dir ( $_, '--with-paths', '--dirs-as-ref', '--files-as-ref', '--recursing', '--no-fsdots', ); push(@dirs,@{$lsts[0]}); push(@items,@{$lsts[1]}); } } if ($opts->{'--sl-after-dirs'}) { @dirs = $this->dropdots(@dirs,'--save-dots'); my($dots) = shift(@dirs); @dirs = map ( ($_.=$Futils::SL), @dirs ); @dirs = (@{$dots},@dirs); } my($reta) = [sort(@dirs)]; my($retb) = [sort(@items)]; $reta=[$reta] if ( $opts->{'--dirs-as-ref'} || $opts->{'--as-ref'} ); $retb=[$retb] if ( $opts->{'--files-as-ref'} || $opts->{'--as-ref'} ); return(@$reta) if ($opts->{'--dirs-only'}); return(@$retb) if ($opts->{'--files-only'}); return(@$reta,@$retb); } # -------------------------------------------------------- # Futils::list_dir_a() # -------------------------------------------------------- sub list_dir_a { my($this) = shift(@_); my($path) = $_[0]; my(@files) = $this->list_dir(@_); my($SL) = $Futils::SL; foreach (@files) { my($file) = {}; $file->{'name'} = $_; if (-d $path.$SL.$_) { unless ( $file->{'name'} eq '..' or $file->{'name'} eq '.' ) { $file->{'name'} = $file->{'name'}.$SL; } } $file->{'absname'} = $path.$SL.$_; $file->{'size'} = $this->fsize( $path.$SL.$_ ); $file->{'created'} = $this->created( $path.$SL.$_ ); $file->{'last_mod'} = $this->last_mod( $path.$SL.$_ ); $file->{'last_access'} = $this->last_access( $path.$SL.$_ ); $_ = $file; } return(@files); } # -------------------------------------------------------- # Futils::dropdots() # -------------------------------------------------------- sub dropdots { my($this) = shift(@_); my(@out) = (); my($opts) = $this->shave_opts(\@_); my(@shadow) = @_; my(@dots) = (); my($gottadot) = 0; while (@shadow) { if ($gottadot == 2){ push(@out,@shadow) and last } my($thing) = shift(@shadow); if ($thing eq '.') {++$gottadot;push(@dots,$thing);next} if ($thing eq '..') {++$gottadot;push(@dots,$thing);next} push(@out,$thing); } return([@dots],@out) if ($opts->{'--save-dots'}); return(@out); } # -------------------------------------------------------- # Futils::list_dir_wpaths() # -------------------------------------------------------- sub list_dir_wpaths { my($this) = shift(@_); my($path) = $_[0]||''; my(@files) = $this->list_dir(@_); foreach (@files) { $_ = $path.$Futils::SL.$_; } return(@files); } # -------------------------------------------------------- # Futils::print_dir() # -------------------------------------------------------- sub print_dir { my($this) = shift(@_); my($dir) = shift(@_)||''; my($opts) = $this->shave_opts(\@_); my($cls) = 75; my($cap) = q[+] . (q[-] x $cls) . q[+]; my($toe) = $cap; my(@f) = (); my($msg) = ''; my($fdir) = ''; $msg = qq[Directory listing for $dir]; $msg .= '...' if (length($msg) > 70); $msg = sprintf(' % 0.70s%s', $msg); # get list of files from Futils::list_dir() @f = $this->list_dir($dir,@{[values(%$opts)]}); { # handle 'no-fancy' option if ($opts->{'--no-fancy'} or $opts->{'--raw'}) { # make a plain list of the directory foreach (@f) {$fdir .= $_ . $n} if (scalar(@f) == 0) {$fdir = 'This directory contains no items.'} return ( ($n x 2) . $fdir . ($n x 2) ) if ($opts->{'--raw'}); return ( ($n x 2) . ('-' x 75) . $n . $msg . $n . ('-' x 75) . ($n x 2) . $fdir . ($n x 2) ); } } # make a pretty, formatted table of the directory listings foreach (@f) { $fdir .= sprintf('| %s',$_) . (' 'x(int(($cls-1)-length($_)))) . $n } $cap .= sprintf(qq[$n| %s], $msg) . ' ' x (int(($cls - 1) - length($msg))) . $n . $cap; $fdir = join($n, $cap, $fdir) . $toe; return(qq[$n$fdir$n$n]); } # -------------------------------------------------------- # Futils::load_dir() # -------------------------------------------------------- sub load_dir { my($this) = shift(@_); my($dir) = shift(@_)||''; my($opts) = $this->shave_opts(\@_); my(@files) = (); my($dir_hash) = {}; my($dir_list) = []; # if the call to this method didn't include a directory name to open, # then complain about it $this-> throw ( 'meth' => 'load_dir', 'missing' => 'a directory name', 'error' => 'no input', ) unless (length($dir) > 0); # get list of files from Futils::list_dir() @files = $this->list_dir($dir,'--files-only'); # map the content of each file into a hash key-value element where the # key name for each file is the name of the file if (!$opts->{'--as-list'} and !$opts->{'--as-listref'}) { foreach (@files) { $dir_hash->{ $_ } = $this->load_file( $dir.$Futils::SL.$_ ); } return($dir_hash); } else { foreach (@files) { push ( @{$dir_list}, $this->load_file( $dir.$Futils::SL.$_ ) ); } return($dir_list) if ($opts->{'--as-listref'}); return(@{$dir_list}); } # return a reference to the hash containing all the filenames and contents return($dir_hash); } # -------------------------------------------------------- # Futils::file_type() # -------------------------------------------------------- sub file_type { my($this) = shift(@_); my($filename) = shift(@_)||''; my($ret) = ''; return(q[$filename does not exist. No such file or directory]) if (not $this->fexists($filename)); $ret .= 'plain' if (-f $filename); $ret .= 'text' if (-T $filename); $ret .= 'binary' if (-B $filename); $ret .= 'directory' if (-d $filename); $ret .= 'symlink' if (-l $filename); $ret .= 'pipe' if (-p $filename); $ret .= 'socket' if (-S $filename); $ret .= 'block' if (-b $filename); $ret .= 'character' if (-c $filename); $ret .= 'tty' if (-t $filename); return($ret || 'file type cannot be determined'); } # -------------------------------------------------------- # Futils::make_dir() # -------------------------------------------------------- sub make_dir { my($this,$dir,$bitmask) = @_; # if the call to this method didn't include a directory name to create, # then complain about it $this-> throw ( 'meth' => 'make_dir', 'missing' => 'a directory name', 'error' => 'no input', ) unless (length($dir) > 0); # if prospective directory name contains 2+ dir separators in sequence then # this is a syntax error we need to whine about $this-> throw ( 'error' => 'bad chars', 'string' => $dir, 'purpose' => 'the name of a directory', ) if ($dir =~ /(?o)$Futils::dirsplit{2,}/); $bitmask ||= 0777; if (length($bitmask) == 3) {$bitmask = '0'.$bitmask} $dir =~ s/\\$|\/$//o; my($split) = quotemeta($Futils::SL); my(@dirs_in_path) = split(/$split/o,$dir); my(@substitute) = @dirs_in_path; foreach (@dirs_in_path) { # if prospective directory name contains illegal chars then complain $this-> throw ( 'error' => 'bad chars', 'string' => $_, 'purpose' => 'the name of a directory', ) if ($this->isbad($_)) } my($depth) = 0; foreach (@substitute) { ++$depth; last if ($depth == scalar(@dirs_in_path)); $dirs_in_path[$depth] ||= '.'; $dirs_in_path[$depth] = join ( $Futils::SL, @dirs_in_path[($depth-1)..$depth] ); } my($i) = 0; foreach (@dirs_in_path) { my($dir) = $_; my($up) = $dirs_in_path[$i-1] if ($i > 0); ++$i; unless ($this->fexists($dir) or -f ($dir)) { # it's good to know beforehand whether or not we have permission to # create dirs here, which allows us to handle such an exception # before it handles us. $this-> throw ( 'filename' => $dir, 'dirname' => $up.$Futils::SL, 'error' => 'cant dcreate', ) unless ($this->can_write($up)); mkdir($dir, $bitmask) or $this-> throw ( 'error' => 'bad make_dir', 'exception' => $!, 'dir' => $dir, 'bitmask' => $bitmask, ); } } return($dir); } # -------------------------------------------------------- # Futils::fexists() # -------------------------------------------------------- sub fexists { my($this) = shift(@_); my($filename) = shift(@_)||''; # if the file doesn't exist, send back a zero value return(-e $filename); } # -------------------------------------------------------- # Futils::fsize() # -------------------------------------------------------- sub fsize { my($this) = shift(@_); my($filename) = shift(@_)||''; # if the file doesn't exist, send back an error $this-> throw ( 'filename' => $filename, 'error' => 'no such file', ) if (not (-e $filename)); # return the size of $filename return(-s $filename); } # -------------------------------------------------------- # Futils::created() # -------------------------------------------------------- sub created { my($this,$filename,$format) = @_; # if the file doesn't exist, send back an error $this-> throw ( 'filename' => $filename, 'error' => 'no such file', ) if (not (-e $filename)); # return the last modified time of $filename my($r) = $Futils::started - ((-M $filename) * 60 * 60 * 24); return($this->stamp($r,$format)) if (defined($format)); return($r); } # -------------------------------------------------------- # Futils::last_mod() # -------------------------------------------------------- sub last_mod { my($this,$filename,$format) = @_; # if the file doesn't exist, send back an error $this-> throw ( 'filename' => $filename, 'error' => 'no such file', ) if (not (-e $filename)); # return the last modified time of $filename my($r) = $Futils::started - ((-C $filename) * 60 * 60 * 24); return($this->stamp($r,$format)) if (defined($format)); return($r); } # -------------------------------------------------------- # Futils::last_access() # -------------------------------------------------------- sub last_access { my($this,$filename,$format) = @_; # if the file doesn't exist, send back an error $this-> throw ( 'filename' => $filename, 'error' => 'no such file', ) if (not (-e $filename)); # return the last accessed time of $filename my($r) = $Futils::started - ((-A $filename) * 60 * 60 * 24); return($this->stamp($r,$format)) if (defined($format)); return($r); } # -------------------------------------------------------- # Futils::can_read() Futils::can_write() # -------------------------------------------------------- sub can_read { -r $_[1] } sub can_write { -w $_[1] } # -------------------------------------------------------- # Futils::line_count() # -------------------------------------------------------- sub line_count { my($this,$filename) = @_; my($buffer) = ''; my($lines) = 0; my($cmd) = '<'.$filename; local(*LINES); open(LINES, $filename) or $this-> throw ( 'error' => 'bad open', 'filename' => $filename, 'mode' => 'read', 'exception' => $!, 'cmd' => $cmd, ); while (sysread(LINES, $buffer, 4096)) { $lines += ($buffer =~ tr/\n//); $buffer = ''; } close(LINES); return($lines); } # -------------------------------------------------------- # Futils::load_file() # -------------------------------------------------------- sub load_file { my($this) = shift(@_); my($opts) = $this->shave_opts(\@_); my($in) = $this->coerce_array(@_); my(@dirs) = (); my($blocksize) = 1024; # 1.24 kb my($FH_passed) = 0; my($FH) = undef; my($filename) = ''; my($path) = ''; my($content) = ''; my($FHstatus) = ''; my($mode) = 'read'; if (scalar(@_) == 1) { $filename = shift(@_)||''; # determine existance of the file path, make directory(ies) for the # path if the full directory path doesn't exist @dirs = split(/$Futils::dirsplit/, $filename); if (scalar(@dirs) > 0) { $filename = pop(@dirs); $path = join($Futils::SL, @dirs); } if (length($path) > 0) { $path = '.'.$Futils::SL.$path if ($path !~ /(?:^\/)|(?:^\w\:)/o); } else { $path = '.'; } $this-> throw ( 'meth' => 'load_file', 'missing' => 'a file name or file handle reference', 'error' => 'no input', ) if (length($path . $Futils::SL . $filename) == 0); } else { $FH = $in->{'FH'} || ''; $FHstatus = $in->{'FH_status'} || ''; # did we get a filehandle? if (length($FH) > 0) { $FH_passed = 1; } else { $this-> throw ( 'meth' => 'load_file', 'missing' => 'a file name or file handle reference', 'error' => 'no input', ); } } if ($FH_passed) { my($buffer) = 0; my($bytes_read) = 0; while (<$FH>) { if ($buffer < $Futils::readlimit) { $bytes_read = read( $FH, $content, $blocksize, ); $buffer += $bytes_read; } else { $this-> throw ( 'filename' => '', 'size' => qq[[truncated at $bytes_read]], 'error' => 'readlimit exceeded', ); } } # return an array of all lines in the file if the call to this method/ # subroutine asked for an array eg- my(@file) = load_file('file'); # otherwise, return a scalar value containing all of the file's content return(split(/$this->{'n'}|\r|\n/o,$content)) if $opts->{'--as-list'}; return($content); } # if the file doesn't exist, send back an error $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'error' => 'no such file', ) if (not (-e $path . $Futils::SL . $filename)); # it's good to know beforehand whether or not we have permission to open # and read from this file allowing us to handle such an exception before # it handles us. # --> first check the readability of the file's housing dir $this-> throw ( 'filename' => $path . $Futils::SL.$filename, 'dirname' => $path . $Futils::SL, 'error' => 'cant dread', ) unless ($this->can_read($path . $Futils::SL)); # --> now check the readability of the file itself $this-> throw ( 'filename' => $path . $Futils::SL.$filename, 'dirname' => $path . $Futils::SL, 'error' => 'cant fread', ) unless ($this->can_read($path . $Futils::SL . $filename)); # if the file is a directory it will not be opened $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'error' => 'called open on a dir', ) if (-d $path . $Futils::SL . $filename); $this->{'expt'}->fail( qq[ $this->{'name'} can't open " $filename " for reading because it is a a block special file.] ) if (-b $path . $Futils::SL . $filename); my($fsize) = $this->fsize($path . $Futils::SL . $filename); $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'size' => $fsize, 'error' => 'readlimit exceeded', ) if ($fsize > $Futils::readlimit); # we need a unique filehandle $FH = int(rand(time)) . $$; $FH = eval('*' . 'LOAD_FILE' . $FH); # localize the global output record separator so we can slurp it all # in one quick read. We fail if the filesize exceeds our limit. local($/,*LOAD_FILE); # open the file for reading (note the '<' syntax there) or fail with a # error message if our attempt to open the file was unsuccessful my($cmd) = '<' . $path . $Futils::SL . $filename; # lock file before I/O when possible if ($$opts{'--no-lock'} || $$this{'opts'}{'--no-lock'}) { # if you use the '--no-lock' option you are probably stupid open(LOAD_FILE, $cmd) or $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'mode' => $mode, 'error' => 'bad open', 'exception' => $!, 'cmd' => $cmd, ); } else { open(LOAD_FILE, $cmd) or $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'mode' => $mode, 'error' => 'bad open', 'exception' => $!, 'cmd' => $cmd, ); $this->seize($path . $Futils::SL . $filename, *LOAD_FILE); } # call binmode on binary files for portability accross platforms such # as MS flavor OS family CORE::binmode(LOAD_FILE) if (-B $path . $Futils::SL . $filename); # assign the content of the file to this lexically scoped scalar variable # (memory for *that* variable will be freed when execution leaves this # method / sub $content = ; if ($$opts{'--no-lock'} || $$this{'opts'}{'--no-lock'}) { # if execution gets here, you used the '--no-lock' option, and you # are probably stupid close(LOAD_FILE) or $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'mode' => $mode, 'error' => 'bad close', 'exception' => $!, ); } else { # release shadow-ed locks on the file $this->release(*LOAD_FILE); close(LOAD_FILE) or $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'mode' => $mode, 'error' => 'bad close', 'exception' => $!, ); } # return an array of all lines in the file if the call to this method/ # subroutine asked for an array eg- my(@file) = load_file('file'); # otherwise, return a scalar value containing all of the file's content return(split(/$this->{'n'}|\r|\n/o,$content)) if $opts->{'--as-list'}; return($content); } # -------------------------------------------------------- # Futils::open_to_FH() # -------------------------------------------------------- sub open_to_FH { my($this) = shift(@_); my($opts) = $this->shave_opts(\@_); my($in) = $this->coerce_array(@_); my($filename) = $in->{'file'}; my($mode) = $in->{'mode'} || 'write'; my(@dirs) = (); my($path) = ''; my($cmd) = ''; $filename ||= $$in{'filename'}; $filename ||= ''; $path = $filename; # if the call to this method didn't include a filename to which the caller # wants us to write, then complain about it $this-> throw ( 'meth' => 'open_to_FH', 'missing' => 'a file name to create, read, write, or append', 'error' => 'no input', ) unless (length($filename) > 0); # if prospective directory name contains 2+ dir separators in sequence then # this is a syntax error we need to whine about $this-> throw ( 'error' => 'bad chars', 'string' => $filename, 'purpose' => 'the name of a file or directory', ) if ($filename =~ /(?o)$Futils::dirsplit{2,}/); # determine existance of the file path, make directory(ies) for the # path if the full directory path doesn't exist @dirs = split(/$Futils::dirsplit/, $filename); # if file name has illegal chars then complain foreach (@dirs) { $this-> throw ( 'error' => 'bad chars', 'string' => $_, 'purpose' => 'the name of a file or directory', ) if ($this->isbad($_)); } if (scalar(@dirs) > 0) { $filename = pop(@dirs); $path = join($Futils::SL, @dirs); } if (length($path) > 0) { $path = '.' . $Futils::SL . $path if ($path !~ /(?:^\/)|(?:^\w\:)/o); } else { $path = '.'; } if (not $this->fexists($path)) { $this->make_dir($path,0644); } # determine whether the caller wants to over-write old data in the file # if it already exists, create a fresh new file from the content provided, # or just append the content onto the end of an existing file if ($mode eq 'write') { $cmd = '>' . $path . $Futils::SL . $filename; # While we're here... # it's good to know beforehand whether or not we have permission to # write/append to this file allowing us to handle such an exception # before it handles us. if (-e $path . $Futils::SL . $filename) { # --> now check the writability of the file itself $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'dirname' => $path . $Futils::SL, 'error' => 'cant fwrite', ) unless ($this->can_write($path.$Futils::SL.$filename)); } else { # --> if file doesn't exist, the error is one of creation $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'dirname' => $path . $Futils::SL, 'error' => 'cant fcreate', ) unless ($this->can_write($path . $Futils::SL)); } } elsif ($mode eq 'append') { $cmd = '>>' . $path . $Futils::SL . $filename; } elsif ($mode eq 'read') { # it's good to know beforehand whether or not we have permission to open # and read from this file allowing us to handle such an exception before # it handles us. # --> first check the readability of the file's housing dir $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'dirname' => $path . $Futils::SL, 'error' => 'cant dread', ) unless ($this->can_read($path.$Futils::SL)); # --> now check the readability of the file itself $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'dirname' => $path . $Futils::SL, 'error' => 'cant fread', ) unless ($this->can_read($path.$Futils::SL.$filename)); $cmd = '<' . $path . $Futils::SL . $filename; } # we need a unique filehandle my($FH) = int(rand(time())).$$; $FH = eval('*'.'OPEN_TO_FH'.$FH); # lock file before I/O when possible if ($$opts{'--no-lock'} || $$this{'opts'}{'--no-lock'}) { # if you use the '--no-lock' option you are probably stupid open($FH, $cmd) or $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'mode' => $mode, 'error' => 'bad open', 'exception' => $!, 'cmd' => $cmd, ); # return file handle reference to the caller return($FH); } else { open($FH, $cmd) or $this-> throw ( 'filename' => $path . $Futils::SL . $filename, 'mode' => $mode, 'error' => 'bad open', 'exception' => $!, 'cmd' => $cmd, ); $this->seize($path . $Futils::SL . $filename, $FH); } # return file handle reference to the caller return($FH); } # -------------------------------------------------------- # Futils::write_file() # -------------------------------------------------------- sub write_file { my($this) = shift(@_); my($opts) = $this->shave_opts(\@_); my($in) = $this->coerce_array(@_); my($filename) = $in->{'file'} || ''; my($content) = $in->{'content'} || ''; my($mode) = $in->{'mode'} || 'write'; my($shadow_FH) = undef; my($path) = ''; my($cmd) = ''; my(@dirs) = (); my($have_lock) = 0; my($FH_passed) = 0; $filename ||= $$in{'filename'}; $filename ||= ''; $path = $filename; local(*WRITE_FILE); $mode = 'trunc' if ($mode eq 'truncate'); # if the call to this method didn't include a filename to which the caller # wants us to write, then complain about it $this-> throw ( 'error' => 'no input', 'meth' => 'write_file', 'missing' => 'a file name to create, write, or append', ) if (length($filename) == 0); # if prospective filename contains 2+ dir separators in sequence then # this is a syntax error we need to whine about $this-> throw ( 'error' => 'bad chars', 'string' => $filename, 'purpose' => 'the name of a file or directory', ) if ($filename =~ /$Futils::dirsplit{2,}/); # if the call to this method didn't include any data which the caller # wants us to write or append to the file, then complain about it $this-> throw ( 'meth' => 'write_file', 'missing' => 'the content you want to write or append', 'error' => 'no input', ) if ( (length($content) == 0) and ($mode ne 'trunc') and (!$Futils::empty_writes) ); # take care of idiots $filename =~ s/$Futils::trailer//; # determine existance of the file path, make directory(ies) for the # path if the full directory path doesn't exist @dirs = split(/$Futils::dirsplit/, $filename); # if prospective file name has illegal chars then complain foreach (@dirs) { $this-> throw ( 'error' => 'bad chars', 'string' => $_, 'purpose' => 'the name of a file or directory', ) if ($this->isbad($_)); } if (scalar(@dirs) > 0) { $filename = pop(@dirs); $path = join($Futils::SL, @dirs); } if (length($path) > 0) { $path = '.'.$Futils::SL.$path if ($path !~ /(?:^\/)|(?:^\w\:)/o); } else { $path = '.'; } if (not $this->fexists($path)) { $this->make_dir($path,0644); } my($openarg) = qq[$path$Futils::SL$filename]; # it's good to know beforehand whether or not we have permission to # write/append to this file allowing us to handle such an exception # before it handles us. if (-e $openarg) { # --> now check the writability of the file itself $this-> throw ( 'filename' => $openarg, 'dirname' => $path . $Futils::SL, 'error' => 'cant fwrite', ) unless ($this->can_write($openarg)); } else { # --> if file doesn't exist, the error is one of creation $this-> throw ( 'filename' => $openarg, 'dirname' => $path.$Futils::SL, 'error' => 'cant fcreate', ) unless ($this->can_write($path . $Futils::SL)); } # determine whether the caller wants to over-write old data in the file # if it already exists, create a fresh new file from the content provided, # or just append the content onto the end of an existing file if ($$opts{'--no-lock'} || $$this{'opts'}{'--no-lock'}) { # get open mode $mode = $$Futils::modes{'popen'}{ $mode }; # if you use the '--no-lock' option you are probably stupid open(WRITE_FILE, $mode . $openarg) or $this-> throw ( 'filename' => $openarg, 'mode' => $mode, 'error' => 'bad open', 'exception' => $!, 'cmd' => $cmd, ); } else { sysopen ( *WRITE_FILE, $openarg, eval($$Futils::modes{'sysopen'}{ $mode }) ) or $this-> throw ( 'filename' => $openarg, 'mode' => $mode, 'error' => 'bad open', 'exception' => $!, 'cmd' => qq[$openarg, $mode], ); { my($current_FH) = select(WRITE_FILE); ++$|; select($current_FH) } # lock file before I/O when possible $this->seize($openarg, *WRITE_FILE); # now truncate if ($mode ne 'append') { truncate(WRITE_FILE,0) or $this-> throw ( 'filename' => $openarg, 'error' => 'bad systrunc', 'exception' => $!, ); } } # write the caller's data to the newly opened file, syswrite covers our # rear if the content is a binary stream and we're running on an OS that # needs binmode. otherwise it isn't going to matter, except that it might # be faster, since it bypasses stdio $in->{'content'}||=''; syswrite(WRITE_FILE, $in->{'content'}); # release locks on the file if ($$opts{'--no-lock'} || $$this{'opts'}{'--no-lock'}) { # if execution gets here and you didn't pass in a locked filehandle # to this method, you used the '--no-lock' option and you # are probably stupid close(WRITE_FILE) or $this-> throw ( 'filename' => $openarg, 'mode' => $mode, 'error' => 'bad close', 'exception' => $!, ); } else { $this->release(*WRITE_FILE); close(WRITE_FILE) or $this-> throw ( 'filename' => $openarg, 'mode' => $mode, 'error' => 'bad close', 'exception' => $!, ); } # return a quick success code to the caller in order to pass any checks # being made that the operation was successful return(1); } # -------------------------------------------------------- # Futils::seize() # -------------------------------------------------------- sub seize { # flock() helper for same-process race conditions # which aren't safely handled when left up to the native filesystem # locking mechanisms. Here, flock(2) lockf(3) and fcntl(2) can't # save your bacon. You have to help out a little yourself. =pod OPTIONS ON I/O RACE CONDITION POLICY set $Futils::onflockfail to any of the following strings: ex- @Futils::onflockfail = qw( wait fail ); --fatal fails with stack trace --fail same as above --warn warns() about the error with a stack trace --ignore ignores the failure to get an exclusive lock --undef returns undef --0 returns 0 * --wait waits to try getting an exclusive lock * --block same as above * used in tandem with another option. you must specify another option to fall back on if blocking should fail. otherwise Futils will throw a fatal error and dump a stack trace before exiting the process with a 0 status =cut my($this) = shift(@_); my($file) = shift(@_)||''; my($FH) = shift(@_)||''; my(@policy) = @Futils::onflockfail; my($policy) = $this->shave_opts(\@policy); $this->{'expt'}->fail(q[no file name passed to seize.]) unless ($file); $this->{'expt'}->fail(q[no handle passed to seize.]) unless ($FH); if ($Futils::hasflock) { if (flock($FH, Fcntl::LOCK_EX() | Fcntl::LOCK_NB())) { # at this point we know we have a non-blocking lock on $FH return($FH); } else { if ( $policy->{'--block'} or $policy->{'--wait'} ) { if (flock($FH, Fcntl::LOCK_EX())) { return($FH) } else { if ($policy->{'--0'}) { return(0) } elsif ($policy->{'--undef'}) { return(undef) } elsif ($policy->{'--warn'}) { return ( $this-> throw ( '--as-warning', 'filename' => $file, 'error' => 'bad lock', 'exception' => $!, ) ) } $this-> throw ( 'filename' => $file, 'error' => 'bad lock', 'exception' => $!, ) } } else { if ($policy->{'--0'}) { return(0) } elsif ($policy->{'--undef'}) { return(undef) } elsif ($policy->{'--warn'}) { $this-> throw ( '--as-warning', 'filename' => $file, 'error' => 'bad nblock', 'exception' => $!, ); return(undef); } $this-> throw ( 'filename' => $file, 'error' => 'bad nblock', 'exception' => $!, ) } return(undef) } } return($FH) } # -------------------------------------------------------- # Futils::release() # -------------------------------------------------------- sub release { my($this,$FH) = @_; $this->{'expt'}->fail(qq[can't unlock $ebl$FH$ebr: not a valid handle]) unless (ref(\$FH||'') eq 'GLOB'); if ($Futils::hasflock) { flock($FH, Fcntl::LOCK_UN()) } return(1); } # -------------------------------------------------------- # Futils::ftruncate() # -------------------------------------------------------- sub ftruncate { $_[0]->write_file('mode'=>'trunc','file'=>$_[1]) } # -------------------------------------------------------- # Futils::isbad() # -------------------------------------------------------- sub isbad { $_[1] =~ /$Futils::illegal_filename_chrs/ } # -------------------------------------------------------- # Futils::strip_path() # -------------------------------------------------------- sub strip_path { my($this) = shift(@_); my($filename) = shift(@_)||''; my(@dirs) = (); # determine existance of the file path, make directory(ies) for the # path if the full directory path doesn't exist @dirs = split(/$Futils::dirsplit/, $filename); return((pop(@dirs))||''); } # -------------------------------------------------------- # Futils::escape_filename() # -------------------------------------------------------- sub escape_filename { my($this) = shift(@_); my($filename) = shift(@_)||''; my($opts) = $this->shave_opts(\@_); my($escape) = $opts->{'--use-chr'}; my($also) = $opts->{'--also'}; my(@dirs) = (); my($path) = ''; my($mskpath) = ''; $escape = '_' if (not defined($escape)); $also ||= ''; $also = (length($also) > 0) ? '|'.$also : ''; # take care of idiots $filename =~ s/$Futils::trailer//; # determine existance of the file path, make directory(ies) for the # path if the full directory path doesn't exist @dirs = split(/$Futils::dirsplit/, $filename); if (scalar(@dirs) > 0) { $filename = pop(@dirs); $path = join($Futils::SL, @dirs); $mskpath = join($escape , @dirs); } if (length($path) > 0) { $path = '.' . $Futils::SL . $path if ($path !~ /(?:^\/)|(?:^\w\:)/o); } else { $path = '.'; } $filename =~ s/$Futils::illegal_filename_chrs$also/$escape/g; $filename =~ s/^(?:\.)+?\w//o; if ($opts->{'--strip-path'}) { # take care of relative path prefixes still present $filename =~ s/^\.+?//o; return($filename); } return($mskpath . $escape . $filename); } # -------------------------------------------------------- # Futils::throw() # -------------------------------------------------------- sub throw { my($this) = shift(@_); my($opts) = $this->shave_opts(\@_); my($in) = $this->coerce_array(@_); return(0) if ($this->{'opts'}{'--fatals-as-status'}); $this->{'expt'}||={}; unless (UNIVERSAL::isa($this->{'expt'},'expt_handler')) { $this->{'expt'} = expt_handler->new(); } foreach (keys(%{$in})) { $_ = (defined($_)) ? $_ : '[undefined value]'; } my($error) = (defined($in->{'error'})) ? $in->{'error'} : '[empty error]'; unless (defined( $Futils::errors->{$error} )) { $Futils::errors->{$error} = $Futils::errors->{'unknown error message'} } my($bad_news) = CORE::eval($Futils::errors->{$error}); if ($opts->{'--as-warning'}) { warn($this->{'expt'}->trace($@ || $bad_news)) and return() } elsif ( $this->{'opts'}{'--fatals-as-errmsg'} || $opts->{'--return'} ) { return($this->{'expt'}->trace($@ || $bad_news)) } foreach (keys(%{$in})) { $_ = (defined($_)) ? $_ : '[empty value]'; $bad_news .= qq[ $_ => $in->{$_}] . $n; } foreach (keys(%{$opts})) { $_ = (defined($_)) ? $_ : '[empty value]'; $bad_news .= qq[ $_ => $in->{$_}] . $n; } warn($this->{'expt'}->trace($@ || $bad_news)) if ($opts->{'--warn-also'}); return($this->{'expt'}->fail(($@ || $bad_news))) } # -------------------------------------------------------- # $Futils::errors [ref to anonymous hash of error msgs] # -------------------------------------------------------- BEGIN { $Futils::errors = {}; $Futils::errors = { # BAD CHARS 'bad chars' => <<'__bad_chars__', qq[ Futils v$Futils::VERSION Futils can't use this string for $ebl$in->{'purpose'}$ebr. $ebl$in->{'string'}$ebr It contains illegal characters. Illegal characters are: $ebl$Futils::SL$ebr $ebl\\$ebr (backslash) $ebl/$ebr (forward slash) $ebl:$ebr (colon) $ebl $ebr (space) $ebl|$ebr (pipe) $ebl*$ebr (asterisk) $ebl?$ebr (question mark) $ebl"$ebr (double quote) $ebl<$ebr (less than) $ebl>$ebr (greater than) $ebl\\t$ebr (tab) $ebl\\r$ebr (newline CR) $ebl\\n$ebr (newline LF) Origin: This is a human error. Solution: A human must remove the illegal characters from this string. Raw input as passed to this method: ] __bad_chars__ # NO SUCH FILE 'no such file' => <<'__bad_open__', qq[ Futils v$Futils::VERSION Futils can't open $ebl$in->{'filename'}$ebr because no such file or directory exists. Origin: This is *most likely* due to human error. Solution: Cannot diagnose. A human must investigate the problem. Raw input as passed to this method: ] __bad_open__ # INVALID ERROR TYPE 'unknown error message' => <<'__foobar_input__', qq[ Futils v$Futils::VERSION Futils failed with an invalid error-type designation. Origin: This is a human error. Solution: A human must fix the programming flaw. Raw input as passed to this method: ] __foobar_input__ # EMPTY ERROR TYPE 'empty error' => <<'__no_input__', qq[ Futils v$Futils::VERSION Futils failed with an empty error-type designation. Origin: This is a human error. Solution: A human must fix the programming flaw. Raw input as passed to this method: ] __no_input__ # BAD CALL TO METHOD x 'no input' => <<'__no_input__', qq[ Futils v$Futils::VERSION Futils can't honor your call to ${\$ebl}Futils::$in->{'meth'}()$ebr because you didn't provide $ebl@{[$in->{'missing'}||'the required input']}$ebr Origin: This is a human error. Solution: A human must fix the programming flaw. Raw input as passed to this method: ] __no_input__ # CAN'T READ FILE 'cant fread' => <<'__cant_read__', qq[ Futils v$Futils::VERSION Permissions conflict. Futils can't read the contents of this file: $ebl$in->{'filename'}$ebr Due to insufficient permissions, the system has denied Perl the right to view the contents of this file. It has a bitmask of: (octal number) $ebl@{[ sprintf('%04o',(stat($in->{'filename'}))[2] & 0777 ]}$ebr The directory housing it has a bitmask of: (octal number) $ebl@{[ sprintf('%04o',(stat($in->{'dirname'}))[2] & 0777) ]}$ebr Origin: This is *most likely* due to human error. External system errors can occur however, but this doesn't have to do with Futils. Solution: A human must fix the conflict by adjusting the file permissions of directories where a program asks Futils to perform I/O. Try using Perl's chmod command, or the native system chmod() command from a shell. Raw input as passed to this method: ] __cant_read__ # CAN'T CREATE FILE 'cant fcreate' => <<'__cant_write__', qq[ Futils v$Futils::VERSION Permissions conflict. Futils can't create this file: $ebl$in->{'filename'}$ebr Futils can't create this file because the system has denied Perl the right to create files in the parent directory. The -e test returns $ebl@{[-e $in->{'dirname'} ]}$ebr for the directory. The -r test returns $ebl@{[-r $in->{'dirname'} ]}$ebr for the directory. The -R test returns $ebl@{[-R $in->{'dirname'} ]}$ebr for the directory. The -w test returns $ebl@{[-w $in->{'dirname'} ]}$ebr for the directory The -W test returns $ebl@{[-w $in->{'dirname'} ]}$ebr for the directory Parent directory: (path may be relative or redundant) $ebl$in->{'dirname'}$ebr Parent directory has a bitmask of: (octal number) $ebl@{[ sprintf('%04o',(stat($in->{'dirname'}))[2] & 0777) ]}$ebr Origin: This is *most likely* due to human error. External system errors can occur however, but this doesn't have to do with Futils. Solution: A human must fix the conflict by adjusting the file permissions of directories where a program asks Futils to perform I/O. Try using Perl's chmod command, or the native system chmod() command from a shell. Raw input as passed to this method: ] __cant_write__ # CAN'T WRITE TO FILE 'cant fwrite' => <<'__cant_write__', qq[ Futils v$Futils::VERSION Permissions conflict. Futils can't write to this file: $ebl$in->{'filename'}$ebr Due to insufficient permissions, the system has denied Perl the right to modify the contents of this file. It has a bitmask of: (octal number) $ebl@{[ sprintf('%04o',(stat($in->{'filename'}))[2] & 0777) ]}$ebr Parent directory has a bitmask of: (octal number) $ebl@{[ sprintf('%04o',(stat($in->{'dirname'}))[2] & 0777) ]}$ebr Origin: This is *most likely* due to human error. External system errors can occur however, but this doesn't have to do with Futils. Solution: A human must fix the conflict by adjusting the file permissions of directories where a program asks Futils to perform I/O. Try using Perl's chmod command, or the native system chmod() command from a shell. Raw input as passed to this method: ] __cant_write__ # CAN'T LIST DIRECTORY 'cant dread' => <<'__cant_read__', qq[ Futils v$Futils::VERSION Permissions conflict. Futils can't list the contents of this directory: $ebl$in->{'filename'}$ebr Due to insufficient permissions, the system has denied Perl the right to view the contents of this directory. It has a bitmask of: (octal number) $ebl@{[ sprintf('%04o',(stat($in->{'filename'}))[2] & 0777) ]}$ebr Origin: This is *most likely* due to human error. External system errors can occur however, but this doesn't have to do with Futils. Solution: A human must fix the conflict by adjusting the file permissions of directories where a program asks Futils to perform I/O. Try using Perl's chmod command, or the native system chmod() command from a shell. Raw input as passed to this method: ] __cant_read__ # CAN'T CREATE DIRECTORY 'cant dcreate' => <<'__cant_write__', qq[ Futils v$Futils::VERSION Permissions conflict. Futils can't create: $ebl$in->{'filename'}$ebr Futils can't create this directory because the system has denied Perl the right to create files in the parent directory. Parent directory: (path may be relative or redundant) $ebl$in->{'dirname'}$ebr Parent directory has a bitmask of: (octal number) $ebl@{[ sprintf('%04o',(stat($in->{'dirname'}))[2] & 0777) ]}$ebr Origin: This is *most likely* due to human error. External system errors can occur however, but this doesn't have to do with Futils. Solution: A human must fix the conflict by adjusting the file permissions of directories where a program asks Futils to perform I/O. Try using Perl's chmod command, or the native system chmod() command from a shell. Raw input as passed to this method: ] __cant_write__ # CAN'T OPEN 'bad open' => <<'__bad_open__', qq[ Futils v$Futils::VERSION Futils can't open this file for $ebl$in->{'mode'}$ebr: $ebl$in->{'filename'}$ebr The system returned this error: $ebl$in->{'exception'}$ebr Futils used this directive in its attempt to open the file $ebl$in->{'cmd'}$ebr Origin: This is *most likely* due to human error. Solution: Cannot diagnose. A Human must investigate the problem. Raw input as passed to this method: ] __bad_open__ # ALREADY OPEN 'already open' => <<'__already_open__', qq[ Futils v$Futils::VERSION Futils can't open this file $ebl$in->{'filename'}$ebr This file is already in use somewhere else in your code. It is opened to descriptor number $ebl@{[ fileno($this->{'lockbox'}{'file names'}{ $in->{'filename'} }{'handle'}) || '[descriptor irretrievable]' ]}$ebr Origin: This is a human error. Solution: Turn off file locking in Futils or don't open the same file more than one time simultaneously. You risk data corruption. Raw input as passed to this method: ] __already_open__ # CAN'T GET FLOCK AFTER BLOCKING 'bad nblock' => <<'__bad_lock__', qq[ Futils v$Futils::VERSION Futils can't get a non-blocking exclusive lock on the file $ebl$in->{'filename'}$ebr The system returned this error: $ebl$in->{'exception'}$ebr Origin: Could be either human _or_ system error. Solution: Fall back to an attempt at getting a lock on the file by blocking. Investigate the reason why you can't get a lock on the file, it is usually because of improper programming which causes race conditions on one or more files. Raw input as passed to this method: ] __bad_lock__ # CAN'T GET NON-BLOCKING FLOCK 'bad lock' => <<'__bad_lock__', qq[ Futils v$Futils::VERSION Futils can't get a blocking exclusive lock on the file. $ebl$in->{'filename'}$ebr The system returned this error: $ebl$in->{'exception'}$ebr Origin: Could be either human _or_ system error. Solution: Investigate the reason why you can't get a lock on the file, it is usually because of improper programming which causes race conditions on one or more files. Raw input as passed to this method: ] __bad_lock__ # CAN'T OPEN ON A DIRECTORY 'called open on a dir' => <<'__bad_open__', qq[ Futils v$Futils::VERSION Futils can't call open() on this file because it is a directory $ebl$in->{'filename'}$ebr Origin: This is a human error. Solution: Use Futils::load_file() to load the contents of a file Use Futils::list_dir() to list the contents of a directory Raw input as passed to this method: ] __bad_open__ # CAN'T OPENDIR ON A FILE 'called opendir on a file' => <<'__bad_open__', qq[ Futils v$Futils::VERSION Futils can't opendir() on this file because it is not a directory. $ebl$in->{'filename'}$ebr Use Futils::load_file() to load the contents of a file Use Futils::list_dir() to list the contents of a directory Origin: This is a human error. Solution: Use Futils::load_file() to load the contents of a file Use Futils::list_dir() to list the contents of a directory Raw input as passed to this method: ] __bad_open__ # PASSED READLIMIT 'readlimit exceeded' => <<'__readlimit__', qq[ Futils v$Futils::VERSION Futils can't load file: $ebl$in->{'filename'}$ebr into memory because its size exceeds the maximum file size allowed for a read. The size of this file is $ebl$in->{'size'}$ebr bytes. Currently the read limit is set at $ebl$Futils::readlimit$ebr bytes. Origin: This is a human error. Solution: Consider setting the limit to a higher number of bytes. Raw input as passed to this method: ] __readlimit__ # BAD OPENDIR 'bad opendir' => <<'__bad_opendir__', qq[ Futils v$Futils::VERSION Futils can't opendir this on $ebl$dir$ebr The system returned this error: $ebl$in->{'exception'}$ebr Origin: Could be either human _or_ system error. Solution: Cannot diagnose. A Human must investigate the problem. Raw input as passed to this method: ] __bad_opendir__ # BAD MAKEDIR 'bad make_dir' => <<'__bad_make_dir__', qq[ Futils v$Futils::VERSION Futils had a problem with the system while attempting to create the directory you specified with a bitmask of $ebl$in->{'bitmask'}$ebr directory: $ebl$in->{'dir'}$ebr The system returned this error: $ebl$in->{'exception'}$ebr Origin: Could be either human _or_ system error. Solution: Cannot diagnose. A Human must investigate the problem. Raw input as passed to this method: ] __bad_make_dir__ # BAD CLOSE 'bad close' => <<'__bad_close__', qq[ Futils v$Futils::VERSION Futils couldn't close this file after $ebl$in->{'mode'}$ebr $ebl$in->{'filename'}$ebr The system returned this error: $ebl$in->{'exception'}$ebr Origin: Could be either human _or_ system error. Solution: Cannot diagnose. A Human must investigate the problem. Raw input as passed to this method: ] __bad_close__ # CAN'T TRUNCATE 'bad systrunc' => <<'__bad_systrunc__', qq[ Futils v$Futils::VERSION Futils couldn't truncate() on $ebl$in->{'filename'}$ebr after having successfully opened the file in write mode. The system returned this error: $ebl$in->{'exception'}$ebr This is most likely _not_ a human error, but has to do with your system's support for the C truncate() function. Raw input as passed to this method: ] __bad_systrunc__ } } # close surrounding BEGIN block for Futils::errors # -------------------------------------------------------- # Futils::DESTROY(), end Futils class package # -------------------------------------------------------- sub DESTROY {} 1; =pod PERL FILE CHECKS REFERENCE -r File is readable by effective uid/gid. -w File is writable by effective uid/gid. -x File is executable by effective uid/gid. -o File is owned by effective uid. -R File is readable by real uid/gid. -W File is writable by real uid/gid. -X File is executable by real uid/gid. -O File is owned by real uid. -e File exists. -z File has zero size. -s File has non-zero size (returns size). -f File is a plain file. -d File is a directory. -l File is a symbolic link. -p File is a named pipe (FIFO). -S File is a socket. -b File is a block special file. -c File is a character special file. -t Filehandle is opened to a tty. -u File has setuid bit set. -g File has setgid bit set. -k File has sticky bit set. -T File is a text file. -B File is a binary file (opposite of -T). -M Age of file in days when script started. -A Same for access time. -C Same for inode change time. SYSTEM FLOCK SUPPORT DEFINES d_fcntl_can_lock From d_fcntl_can_lock.U: This variable conditionally defines the FCNTL_CAN_LOCK symbol and indicates whether file locking with fcntl() works. d_flock From d_flock.U: This variable conditionally defines HAS_FLOCK if flock() is available to do file locking. d_lockf From d_lockf.U: This variable conditionally defines HAS_LOCKF if lockf() is available to do file locking. /FLOCKING CONSTANTS/ classic values for these constants are as listed, but this can vary from system to system LOCK_EX 2 LOCK_UN 8 LOCK_NB 4 LOCK_SH 1 =cut package expt_handler; use strict; $expt_handler::VERSION = 1.01; # 8/6/2002, 1:42:23 PM =pod AUTHOR -Tommy Butler, professional contractor and open source proponent Atrixnet™, for Internet Business Software® http://atrixnet.com 6711 Forest Park Dr Dallas, TX 76001 COPYRIGHT Copyright Tommy Butler. All rights reserved LISCENCE This software is free, and you may use and distribute it under the GNU GPL liscence. If you modify the code for your own purposes please acknowledge its original author. BUGS Please report any of the following to me - bugs - interface inconsistencies - suggestions - comments - complaints - smart remarks =cut use vars qw( $n ); $n = qq[\n]; # -------------------------------------------------------- # Constructor # -------------------------------------------------------- sub new { my($class) = shift; my($this) = {}; my($name) = __PACKAGE__; # ------------------------------------------- # bless object ref into the class' namespace # ------------------------------------------- bless($this, $name); # this is always a handy help $this->{'name'} = $name; # return object reference return($this); } # -------------------------------------------------------- # expt_handler::quit() # -------------------------------------------------------- sub quit { my($this) = shift; print( qq[Content-Type: text/html] . $n x 2 ) if ( $ENV{'REQUEST_METHOD'} =~ /^HEAD|^GET|^POST/io ); print( qq[PROCESS TERMINATED DUE TO ERRORS, see log...] ); warn($this->trace(@_)); exit; } # -------------------------------------------------------- # expt_handler::fail(), error(), belch() # -------------------------------------------------------- sub fail { my($this) = shift; my($throw_count) = $this->{'tflag'} || 0; { # I refuse to manually initialize a standard environment # variable. This is an example where the warnings pragma # is going too far. It's something we live with. local($^W) = undef; # if we're running in a CGI gateway iface, we need # to output the necessary HTTP headers if ( $ENV{'REQUEST_METHOD'} =~ /^HEAD|^GET|^POST/io ) { print(<<__crash__) and exit; Content-Type: text/html; charset=ISO-8859-1
PROCESS TERMINATED DUE TO ERRORS
@{[ $this->trace(@_) ]}
__crash__ } else { print(<<__crash__) and exit; PROCESS TERMINATED DUE TO ERRORS @{[ $this->trace(@_) ]} __crash__ } } exit; } sub error { my($this) = shift; return( $this->fail(@_) ) } sub belch { my($this) = shift; return( $this->fail(@_) ) } # -------------------------------------------------------- # expt_handler::trace() # -------------------------------------------------------- sub trace { my($this) = shift; my(@errors) = @_; my($errfile) = ''; my($caught) = 0; my( $pak, $filename, $linenum, $subroutine, $hasargs, $wantarray, $evaltext, $req_OR_use, @stack, $trace, $i, $ialias, ); $ialias = 0; while ( ( $pak, $filename, $linenum, $subroutine, $hasargs, $wantarray, $evaltext, $req_OR_use ) = caller( $i++ ) ) { $ialias = $i - 2; my(@tree) = split ( /\:\:/, $subroutine ); if ( $tree[0] ne $this->{'name'} ) { push ( @stack, qq[ $ialias. $subroutine -called at line ($linenum) of $filename @{[ ($hasargs) ? '-was called with args' : '-was called without args' ]} @{[ ($evaltext) ? '-was called to evalate text' : '-was not called to evaluate anything' ]}] ); } else { $caught = $n . uc(qq[exception was raised at]) . qq[ line ($linenum) of $filename]; } } $i = 0; if ( scalar(@errors) == 0 ) { push ( @errors, qq[[unreported error. stack frame no. $ialias...]] ); } foreach (@errors) { $_ = ( defined($_) ) ? $_ : ''; if ( length($_) == 0 ) { $_ = qq[Something is wrong. stack frame no. $ialias...]; } else { $_ =~ s/^(?:\r|\n)//o; $_ =~ s/(?:\r|\n)$//o; $_ = qq[$n$_$n]; } ++$i; } $trace = join ( $n x 2, @errors, ) . $caught . $n x 2; $trace .= join ( $n x 2, @stack, $n x 4 ); return($trace); } # -------------------------------------------------------- # expt_handler::DESTROY() # -------------------------------------------------------- sub DESTROY {} 1; package Handy::Dandy; use strict; $Handy::Dandy::VERSION = 1.08; # 8/4/2002, 6:28:46 PM =pod AUTHOR -Tommy Butler, professional contractor and open source proponent Atrixnet™, for Internet Business Software® http://atrixnet.com 6711 Forest Park Dr Dallas, TX 76001 COPYRIGHT Copyright Tommy Butler. All rights reserved LISCENCE This software is free, and you may use and distribute it under the GNU GPL liscence. If you modify the code for your own purposes please acknowledge its original author. BUGS Please report any of the following to me - bugs - interface inconsistencies - suggestions - comments - complaints - smart remarks =cut # -------------------------------------------------------- # Constructor # -------------------------------------------------------- sub new { my($class) = shift; my($this) = {}; my($name) = __PACKAGE__; # ------------------------------------------- # bless object ref into the class' namespace # ------------------------------------------- bless($this, $class); my($in) = $this->coerce_array(@_); # ------------------------------------------- # begin setting up class attributes # ------------------------------------------- $this->{'name'} = $name; # return object reference return($this); } # -------------------------------------------------------- # Handy::Dandy::utf8() # -------------------------------------------------------- sub utf8 { my($this) = shift; my($toencode) = join('',@_); my($no_encode) = 'a-zA-Z 0-9_\\-@.='; $toencode =~ s/([^&;$no_encode])/sprintf("%%%02X",ord($1))/ego; $toencode =~ s/ /+/gmo; return($toencode); } # -------------------------------------------------------- # Handy::Dandy::html_escape() # -------------------------------------------------------- sub html_escape { my($this) = shift; my(@chars) = split(//,shift(@_)); foreach (@chars) { $_ = '&#' .ord($_) .';'; } return(join('',@chars)); } # -------------------------------------------------------- # Handy::Dandy::convert() # -------------------------------------------------------- sub convert_size { my($this) = shift; my($amt) = shift; my($cmd) = shift; my(@specs) = split(/ /,$cmd); my($from) = $specs[0]; my($to) = $specs[-1]; my($b) = 1; my($k) = 1000; my($m) = 1000000; # FROM conversions if ($from =~ /^ki/io) { $amt *= $k; } elsif ($from =~ /^meg/io) { $amt *= $m; } elsif ($from =~ /^by/io) { $amt *= $b; } # TO conversions if ($to =~ /^ki/io) { $amt /= $k; } elsif ($to =~ /^meg/io) { $amt /= $m; } elsif ($to =~ /^by/io) { $amt /= $b; } return($amt); } # -------------------------------------------------------- # Handy::Dandy::to_seconds() # -------------------------------------------------------- sub time_units { my($this) = shift(@_); my($unit) = shift(@_) || '10 minutes'; my($amt) = 0; $unit =~ s/(?go)^(?: )+|(?: )+$//; ($unit,$amt) = split(/ /,$unit); if ($unit =~ /(?o)sec/) { return($amt); } elsif ($unit =~ /(?o)min/) { # 1 minute = 60 seconds return($amt * 60); } elsif ($unit =~ /(?o)hour/) { # 1 hour = 60 minutes # 60 minutes = 3600 seconds return($amt * 3600); } elsif ($unit =~ /(?o)day/) { # 1 day = 24 hours # 24 hours = 1440 minutes # 1440 minutes = 86400 seconds return($amt * 86400); } elsif ($unit =~ /(?o)week/) { # 1 week = 7.0347222 days # 7.0347222 days = 168.8333333 hours # 168.8333333 hours = 10130 minutes # 10130 minutes = 607800 seconds return($amt * 607800); } elsif ($unit =~ /(?o)year/) { # 1 year = 365.2425116 days # 365.2425116 days = 8765.8202778 hours # 8765.8202778 hours = 525949.2166667 minutes # 525949.2166667 minutes = 31556953 seconds return($amt * 31556953); } elsif ($unit =~ /(?o)never/) { return(-2) } else { return(-1) } } # -------------------------------------------------------- # Handy::Dandy::shave_opts() # -------------------------------------------------------- sub shave_opts { my($this) = shift; my($mamma) = shift; my($maid) = [@{$mamma}]; my($opts) = {}; my($i) = 0; while (@{$maid}) { my($e) = shift(@{$maid})||''; if ($e =~ /^\Q--\E\w/o) { splice(@{$mamma},$i,1); $opts->{$e} = $e if (length($e)); } ++$i; } return($opts); } # -------------------------------------------------------- # Handy::Dandy::coerce_array() # -------------------------------------------------------- sub coerce_array { my($this) = shift; my($hashref) = {}; my($i) = 0; my(@shadow) = @_; while (@shadow) { my($name,$val) = splice(@shadow,0,2); if (defined($name)) { $hashref->{$name} = (defined($val)) ? $val : ''; } else { ++$i; $hashref->{qq[un-named key no. $i]} = (defined($val)) ? $val : ''; } } return($hashref); } # -------------------------------------------------------- # sub Handy::Dandy::touch # -------------------------------------------------------- sub touch { my($this) = shift; my(@items) = @_; foreach(@items) { if (ref($_) eq 'SCALAR') { $$_ = '' if (not defined($$_)); } elsif (ref($_) eq 'HASH') { %$_ = {} if (not defined(%$_)); } elsif (ref($_) eq 'ARRAY') { @$_ = [] if (not defined(@$_)); } } return(@items); } # -------------------------------------------------------- # sub Handy::Dandy::soft_touch # -------------------------------------------------------- sub soft_touch {@_} # ---------------------------------- # sub Handy::Dandy::isnum # ---------------------------------- sub isnum { $_[0] !~ /\D/o } # -------------------------------------------------------- # Handy::Dandy::trim() # -------------------------------------------------------- sub trim { my($this) = shift; return('') if (scalar(@_) < 2); my($str) = shift; $str = (defined($str)) ? $str : ''; my($len) = shift; $len = (defined($len)) ? $len : 0; my($lst) = shift; $lst = (defined($lst)) ? $lst : 0; if ((length($str)==0) or (length($len)==0)) { return($str); } $len = int($len); if (length($str) <= $len) { return($str); } my($frag) = ''; $frag = join ( '', splice ( @{[split(//,$str)]}, $len ), ); $str = sprintf ( '%'.$len.'.'.$len.'s', $str ); return (wantarray or $lst) ? ($str,$frag) : $str; } # -------------------------------------------------------- # Handy::Dandy::stamp() # -------------------------------------------------------- sub stamp { my($this) = shift; my($opts) = $this->shave_opts(\@_); my($argtime) = shift; my($months) = [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December', ]; my($days) = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday' ]; $argtime ||= time(); my( $sec, $min, $hour, $date, $mon, $year, $wday, $yday, $isdst ) = localtime($argtime); my($AMPM) = ( $hour >= 12 ) ? 'PM' : 'AM'; $hour -= 12 if ( $hour > 12 ); $hour = 12 if ( $hour == 0 ); # 5/15/2002, 4.22.43 PM return ( sprintf( q[%s/%u/%u, %s:%02u:%02u %s], $mon + 1, $date, $year + 1900, $hour, $min, $sec, $AMPM ) ) if ($opts->{'--short'}); # -June-15-2002-4.22.43-PM return ( sprintf( q[-%s-%u-%u-%u.%02u.%02u-%s], $months->[$mon], $date, $year + 1900, $hour, $min, $sec, $AMPM ) ) if ($opts->{'--filename'} or $opts->{'--file'}); # Saturday, June 15, 2002, 4.22.43 PM return ( sprintf( q[%s, %s %u, %u, %s:%02u:%02u %s], $days->[$wday], $months->[$mon], $date, $year + 1900, $hour, $min, $sec, $AMPM ) ); } # -------------------------------------------------------- # Handy::Dandy::DESTROY() # -------------------------------------------------------- sub DESTROY {} # -------------------------------------------------------- # end Handy::Dandy Class, return true on import # -------------------------------------------------------- 1; package main; use strict; use Config; use Time::HiRes qw( time ); my($futils) = Futils->new(); print(qq[Content-Type: text/html; charset=ISO-8859-1\n\n]); my(@out) = `perl -v`; push(@out, qq[\n\n], `perl -V`, qq[\n\n]); if (defined(%Config::Config)) { my(%con) = (%Config::Config); $con{'osname'}||=$^O||'[osname variable not available]'; foreach (sort(keys(%con))) { if (defined($con{$_})) { if (length($con{$_}) == 0) { $con{$_} = '[defined empty]'; push(@out,qq[$_ => $con{$_}\n]); } else { push(@out,qq[$_ => $con{$_}\n]); } } else { push(@out,qq[$_ => [undefined]\n]); } } } else { push(@out,qq[\n\%Config::Config not defined or unavailable.\n\n]); } $Futils::maxdepth = 1000; push ( @out, $Futils::n x 2 . q[@INC is ] . join($Futils::n, @INC) . $Futils::n x 2 . q[@INC CONTAINS...] ); foreach (@INC) { next if ($_ eq '.'); push ( @out, $futils->print_dir ( $_, '--dirs-only', '--with-paths', '--follow', '--no-fsdots', ) ) } print(<<__endtemplate__); perl binary details

perl binary details

 

@{[ join('',@out) ]}
this page generated in @{[sprintf('%.2f',((time-$^T)))]} seconds
         

 

__endtemplate__ exit;