File manager - Edit - /var/www/payraty/helpdesk/public/storage/custom-code/assets/File.zip
Back
PK ! ө�L6 L6 Listing.pmnu �[��� package File::Listing; use strict; use warnings; use Carp (); use HTTP::Date qw(str2time); use base qw( Exporter ); # ABSTRACT: Parse directory listing our $VERSION = '6.14'; # VERSION sub Version { $File::Listing::VERSION; } our @EXPORT = qw(parse_dir); sub parse_dir ($;$$$) { my($dir, $tz, $fstype, $error) = @_; $fstype ||= 'unix'; $fstype = "File::Listing::" . lc $fstype; my @args = $_[0]; push(@args, $tz) if(@_ >= 2); push(@args, $error) if(@_ >= 4); $fstype->parse(@args); } sub line { Carp::croak("Not implemented yet"); } sub init { } # Dummy sub sub file_mode ($) { Carp::croak("Input to file_mode() must be a 10 character string.") unless length($_[0]) == 10; # This routine was originally borrowed from Graham Barr's # Net::FTP package. local $_ = shift; my $mode = 0; my($type); s/^(.)// and $type = $1; # When the set-group-ID bit (file mode bit 02000) is set, and the group # execution bit (file mode bit 00020) is unset, and it is a regular file, # some implementations of `ls' use the letter `S', others use `l' or `L'. # Convert this `S'. s/[Ll](...)$/S$1/; while (/(.)/g) { $mode <<= 1; $mode |= 1 if $1 ne "-" && $1 ne "*" && $1 ne 'S' && $1 ne 'T'; } $mode |= 0004000 if /^..s....../i; $mode |= 0002000 if /^.....s.../i; $mode |= 0001000 if /^........t/i; # De facto standard definitions. From 'stat.h' on Solaris 9. $type eq "p" and $mode |= 0010000 or # fifo $type eq "c" and $mode |= 0020000 or # character special $type eq "d" and $mode |= 0040000 or # directory $type eq "b" and $mode |= 0060000 or # block special $type eq "-" and $mode |= 0100000 or # regular $type eq "l" and $mode |= 0120000 or # symbolic link $type eq "s" and $mode |= 0140000 or # socket $type eq "D" and $mode |= 0150000 or # door Carp::croak("Unknown file type: $type"); $mode; } sub parse { my($pkg, $dir, $tz, $error) = @_; # First let's try to determine what kind of dir parameter we have # received. We allow both listings, reference to arrays and # file handles to read from. if (ref($dir) eq 'ARRAY') { # Already split up } elsif (ref($dir) eq 'GLOB') { # A file handle } elsif (ref($dir)) { Carp::croak("Illegal argument to parse_dir()"); } elsif ($dir =~ /^\*\w+(::\w+)+$/) { # This scalar looks like a file handle, so we assume it is } else { # A normal scalar listing $dir = [ split(/\n/, $dir) ]; } $pkg->init(); my @files = (); if (ref($dir) eq 'ARRAY') { for (@$dir) { push(@files, $pkg->line($_, $tz, $error)); } } else { local($_); while (my $line = <$dir>) { chomp $line; push(@files, $pkg->line($line, $tz, $error)); } } wantarray ? @files : \@files; ## no critic (Freenode::Wantarray) } package File::Listing::unix; use HTTP::Date qw(str2time); our @ISA = qw(File::Listing); # A place to remember current directory from last line parsed. our $curdir; sub init { $curdir = ''; } sub line { shift; # package name local($_) = shift; my($tz, $error) = @_; s/\015//g; #study; my ($kind, $size, $date, $name); if (($kind, $size, $date, $name) = /^([\-\*FlrwxsStTdD]{10}) # Type and permission bits .* # Graps \D(\d+) # File size \s+ # Some space (\w{3}\s+\d+\s+(?:\d{1,2}:\d{2}|\d{4})|\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}) # Date \s+ # Some more space (.*)$ # File name /x ) { return if $name eq '.' || $name eq '..'; $name = "$curdir/$name" if length $curdir; my $type = '?'; if ($kind =~ /^l/ && $name =~ /(.*) -> (.*)/ ) { $name = $1; $type = "l $2"; } elsif ($kind =~ /^[\-F]/) { # (hopefully) a regular file $type = 'f'; } elsif ($kind =~ /^[dD]/) { $type = 'd'; $size = undef; # Don't believe the reported size } return [$name, $type, $size, str2time($date, $tz), File::Listing::file_mode($kind)]; } elsif (/^(.+):$/ && !/^[dcbsp].*\s.*\s.*:$/ ) { my $dir = $1; return () if $dir eq '.'; $curdir = $dir; return (); } elsif (/^[Tt]otal\s+(\d+)$/ || /^\s*$/) { return (); } elsif (/not found/ || # OSF1, HPUX, and SunOS return # "$file not found" /No such file/ || # IRIX returns # "UX:ls: ERROR: Cannot access $file: No such file or directory" # Solaris returns # "$file: No such file or directory" /cannot find/ # Windows NT returns # "The system cannot find the path specified." ) { return () unless defined $error; &$error($_) if ref($error) eq 'CODE'; warn "Error: $_\n" if $error eq 'warn'; return (); } elsif ($_ eq '') { # AIX, and Linux return nothing return () unless defined $error; &$error("No such file or directory") if ref($error) eq 'CODE'; warn "Warning: No such file or directory\n" if $error eq 'warn'; return (); } else { # parse failed, check if the dosftp parse understands it File::Listing::dosftp->init(); return(File::Listing::dosftp->line($_,$tz,$error)); } } package File::Listing::dosftp; use HTTP::Date qw(str2time); our @ISA = qw(File::Listing); # A place to remember current directory from last line parsed. our $curdir; sub init { $curdir = ''; } sub line { shift; # package name local($_) = shift; my($tz, $error) = @_; s/\015//g; my ($date, $size_or_dir, $name, $size); # 02-05-96 10:48AM 1415 src.slf # 09-10-96 09:18AM <DIR> sl_util if (($date, $size_or_dir, $name) = /^(\d\d-\d\d-\d\d\s+\d\d:\d\d\wM) # Date and time info \s+ # Some space (<\w{3}>|\d+) # Dir or Size \s+ # Some more space (.+)$ # File name /x ) { return if $name eq '.' || $name eq '..'; $name = "$curdir/$name" if length $curdir; my $type = '?'; if ($size_or_dir eq '<DIR>') { $type = "d"; $size = ""; # directories have no size in the pc listing } else { $type = 'f'; $size = $size_or_dir; } return [$name, $type, $size, str2time($date, $tz), undef]; } else { return () unless defined $error; &$error($_) if ref($error) eq 'CODE'; warn "Can't parse: $_\n" if $error eq 'warn'; return (); } } package File::Listing::vms; our @ISA = qw(File::Listing); package File::Listing::netware; our @ISA = qw(File::Listing); package File::Listing::apache; our @ISA = qw(File::Listing); sub init { } sub line { shift; # package name local($_) = shift; my($tz, $error) = @_; # ignored for now... s!</?t[rd][^>]*>! !g; # clean away various table stuff if (m!<A\s+HREF=\"([^?\"]+)\">.*</A>.*?(\d+)-([a-zA-Z]+|\d+)-(\d+)\s+(\d+):(\d+)\s+(?:([\d\.]+[kMG]?|-))!i) { my($filename, $filesize) = ($1, $7); my($d,$m,$y, $H,$M) = ($2,$3,$4,$5,$6); if ($m =~ /^\d+$/) { ($d,$y) = ($y,$d) # iso date } else { $m = _monthabbrev_number($m); } $filesize = 0 if $filesize eq '-'; if ($filesize =~ s/k$//i) { $filesize *= 1024; } elsif ($filesize =~ s/M$//) { $filesize *= 1024*1024; } elsif ($filesize =~ s/G$//) { $filesize *= 1024*1024*1024; } $filesize = int $filesize; require Time::Local; my $filetime = Time::Local::timelocal(0,$M,$H,$d,$m-1,_guess_year($y)); my $filetype = ($filename =~ s|/$|| ? "d" : "f"); return [$filename, $filetype, $filesize, $filetime, undef]; } # the default listing doesn't include timestamps or file sizes # but we don't want to grab navigation links, so we ignore links # that have a non-trailing slash / character or ? elsif(m!<A\s+HREF=\"([^?/\"]+/?)\">.*</A>!i) { my $filename = $1; my $filetype = ($filename =~ s|/$|| ? "d" : "f"); return [$filename, $filetype, undef, undef, undef]; } return (); } sub _guess_year { my $y = shift; # if the year is already four digit then we shouldn't do # anything to modify it. if ($y >= 1900) { # do nothing # TODO: for hysterical er historical reasons we assume 9x is in the # 1990s we should probably not do that, but I don't have any examples # where apache provides two digit dates so I am leaving this as-is # for now. Possibly the right thing is to not handle two digit years. } elsif ($y >= 90) { $y = 1900+$y; } # TODO: likewise assuming 00-89 are 20xx is long term probably wrong. elsif ($y < 100) { $y = 2000+$y; } $y; } sub _monthabbrev_number { my $mon = shift; +{'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4, 'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12, }->{$mon}; } 1; __END__ =pod =encoding UTF-8 =head1 NAME File::Listing - Parse directory listing =head1 VERSION version 6.14 =head1 SYNOPSIS use File::Listing qw(parse_dir); $ENV{LANG} = "C"; # dates in non-English locales not supported foreach my $file (parse_dir(`ls -l`)) { my ($name, $type, $size, $mtime, $mode) = @$file; next if $type ne 'f'; # plain file #... } # directory listing can also be read from a file open my $listing, "zcat ls-lR.gz|"; $dir = parse_dir($listing, '+0000'); =head1 DESCRIPTION This module exports a single function called C<parse_dir>, which can be used to parse directory listings. =head1 FUNCTIONS =head2 parse_dir my $dir = parse_dir( $listing ); my $dir = parse_dir( $listing, $time_zone ); my $dir = parse_dir( $listing, $time_zone, $type ); my $dir = parse_dir( $listing, $time_zone, $type, $error ); my @files = parse_dir( $listing ); my @files = parse_dir( $listing, $time_zone ); my @files = parse_dir( $listing, $time_zone, $type ); my @files = parse_dir( $listing, $time_zone, $type, $error ); The first parameter (C<$listing>) is the directory listing to parse. It can be a scalar, a reference to an array of directory lines or a glob representing a filehandle to read the directory listing from. The second parameter (C<$time_zone>) is the time zone to use when parsing time stamps in the listing. If this value is undefined, then the local time zone is assumed. The third parameter (C<$type>) is the type of listing to assume. Currently supported formats are C<'unix'>, C<'apache'> and C<'dosftp'>. The default value is C<'unix'>. Ideally, the listing type should be determined automatically. The fourth parameter (C<$error>) specifies how unparseable lines should be treated. Values can be C<'ignore'>, C<'warn'> or a code reference. Warn means that the perl warn() function will be called. If a code reference is passed, then this routine will be called and the return value from it will be incorporated in the listing. The default is C<'ignore'>. Only the first parameter is mandatory. # list context foreach my $file (parse_dir($listing)) { my($name, $type, $size, $mtime, $mode) = @$file; } # scalar context my $dir = parse_dir($listing); foreach my $file (@$dir) { my($name, $type, $size, $mtime, $mode) = @$file; } The return value from parse_dir() is a list of directory entries. In a scalar context the return value is a reference to the list. The directory entries are represented by an array consisting of: =over 4 =item name The name of the file. =item type One of: C<f> file, C<d> directory, C<l> symlink, C<?> unknown. =item size The size of the file. =item time The number of seconds since January 1, 1970. =item mode Bitmask a la the mode returned by C<stat>. =back =head1 SEE ALSO =over 4 =item L<File::Listing::Ftpcopy> Provides the same interface but uses XS and the parser implementation from C<ftpcopy>. =back =head1 AUTHOR Original author: Gisle Aas Current maintainer: Graham Ollis E<lt>plicease@cpan.orgE<gt> Contributors: Adam Kennedy Adam Sjogren Alex Kapranoff Alexey Tourbin Andreas J. Koenig Bill Mann Bron Gondwana DAVIDRW Daniel Hedlund David E. Wheeler David Steinbrunner Erik Esterer FWILES Father Chrysostomos Gavin Peters Graeme Thompson Hans-H. Froehlich Ian Kilgore Jacob J Mark Stosberg Mike Schilli Ondrej Hanak Peter John Acklam Peter Rabbitson Robert Stone Rolf Grossmann Sean M. Burke Simon Legner Slaven Rezic Spiros Denaxas Steve Hay Todd Lipcon Tom Hukins Tony Finch Toru Yamaguchi Ville Skyttä Yuri Karaban Zefram amire80 jefflee john9art mschilli murphy phrstbrn ruff sasao uid39246 =head1 COPYRIGHT AND LICENSE This software is copyright (c) 1996-2020 by Gisle Aas. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut PK ! ��f]� ]� Temp.pmnu �[��� package File::Temp; # git description: v0.2310-3-gc7148fe # ABSTRACT: return name and handle of a temporary file safely our $VERSION = '0.2311'; #pod =begin :__INTERNALS #pod #pod =head1 PORTABILITY #pod #pod This section is at the top in order to provide easier access to #pod porters. It is not expected to be rendered by a standard pod #pod formatting tool. Please skip straight to the SYNOPSIS section if you #pod are not trying to port this module to a new platform. #pod #pod This module is designed to be portable across operating systems and it #pod currently supports Unix, VMS, DOS, OS/2, Windows and Mac OS #pod (Classic). When porting to a new OS there are generally three main #pod issues that have to be solved: #pod #pod =over 4 #pod #pod =item * #pod #pod Can the OS unlink an open file? If it can not then the #pod C<_can_unlink_opened_file> method should be modified. #pod #pod =item * #pod #pod Are the return values from C<stat> reliable? By default all the #pod return values from C<stat> are compared when unlinking a temporary #pod file using the filename and the handle. Operating systems other than #pod unix do not always have valid entries in all fields. If utility function #pod C<File::Temp::unlink0> fails then the C<stat> comparison should be #pod modified accordingly. #pod #pod =item * #pod #pod Security. Systems that can not support a test for the sticky bit #pod on a directory can not use the MEDIUM and HIGH security tests. #pod The C<_can_do_level> method should be modified accordingly. #pod #pod =back #pod #pod =end :__INTERNALS #pod #pod =head1 SYNOPSIS #pod #pod use File::Temp qw/ tempfile tempdir /; #pod #pod $fh = tempfile(); #pod ($fh, $filename) = tempfile(); #pod #pod ($fh, $filename) = tempfile( $template, DIR => $dir); #pod ($fh, $filename) = tempfile( $template, SUFFIX => '.dat'); #pod ($fh, $filename) = tempfile( $template, TMPDIR => 1 ); #pod #pod binmode( $fh, ":utf8" ); #pod #pod $dir = tempdir( CLEANUP => 1 ); #pod ($fh, $filename) = tempfile( DIR => $dir ); #pod #pod Object interface: #pod #pod require File::Temp; #pod use File::Temp (); #pod use File::Temp qw/ :seekable /; #pod #pod $fh = File::Temp->new(); #pod $fname = $fh->filename; #pod #pod $fh = File::Temp->new(TEMPLATE => $template); #pod $fname = $fh->filename; #pod #pod $tmp = File::Temp->new( UNLINK => 0, SUFFIX => '.dat' ); #pod print $tmp "Some data\n"; #pod print "Filename is $tmp\n"; #pod $tmp->seek( 0, SEEK_END ); #pod #pod $dir = File::Temp->newdir(); # CLEANUP => 1 by default #pod #pod The following interfaces are provided for compatibility with #pod existing APIs. They should not be used in new code. #pod #pod MkTemp family: #pod #pod use File::Temp qw/ :mktemp /; #pod #pod ($fh, $file) = mkstemp( "tmpfileXXXXX" ); #pod ($fh, $file) = mkstemps( "tmpfileXXXXXX", $suffix); #pod #pod $tmpdir = mkdtemp( $template ); #pod #pod $unopened_file = mktemp( $template ); #pod #pod POSIX functions: #pod #pod use File::Temp qw/ :POSIX /; #pod #pod $file = tmpnam(); #pod $fh = tmpfile(); #pod #pod ($fh, $file) = tmpnam(); #pod #pod Compatibility functions: #pod #pod $unopened_file = File::Temp::tempnam( $dir, $pfx ); #pod #pod =head1 DESCRIPTION #pod #pod C<File::Temp> can be used to create and open temporary files in a safe #pod way. There is both a function interface and an object-oriented #pod interface. The File::Temp constructor or the tempfile() function can #pod be used to return the name and the open filehandle of a temporary #pod file. The tempdir() function can be used to create a temporary #pod directory. #pod #pod The security aspect of temporary file creation is emphasized such that #pod a filehandle and filename are returned together. This helps guarantee #pod that a race condition can not occur where the temporary file is #pod created by another process between checking for the existence of the #pod file and its opening. Additional security levels are provided to #pod check, for example, that the sticky bit is set on world writable #pod directories. See L<"safe_level"> for more information. #pod #pod For compatibility with popular C library functions, Perl implementations of #pod the mkstemp() family of functions are provided. These are, mkstemp(), #pod mkstemps(), mkdtemp() and mktemp(). #pod #pod Additionally, implementations of the standard L<POSIX|POSIX> #pod tmpnam() and tmpfile() functions are provided if required. #pod #pod Implementations of mktemp(), tmpnam(), and tempnam() are provided, #pod but should be used with caution since they return only a filename #pod that was valid when function was called, so cannot guarantee #pod that the file will not exist by the time the caller opens the filename. #pod #pod Filehandles returned by these functions support the seekable methods. #pod #pod =cut # Toolchain targets v5.8.1, but we'll try to support back to v5.6 anyway. # It might be possible to make this v5.5, but many v5.6isms are creeping # into the code and tests. use 5.006; use strict; use Carp; use File::Spec 0.8; use Cwd (); use File::Path 2.06 qw/ rmtree /; use Fcntl 1.03; use IO::Seekable; # For SEEK_* use Errno; use Scalar::Util 'refaddr'; require VMS::Stdio if $^O eq 'VMS'; # pre-emptively load Carp::Heavy. If we don't when we run out of file # handles and attempt to call croak() we get an error message telling # us that Carp::Heavy won't load rather than an error telling us we # have run out of file handles. We either preload croak() or we # switch the calls to croak from _gettemp() to use die. eval { require Carp::Heavy; }; # Need the Symbol package if we are running older perl require Symbol if $] < 5.006; ### For the OO interface use parent 0.221 qw/ IO::Handle IO::Seekable /; use overload '""' => "STRINGIFY", '0+' => "NUMIFY", fallback => 1; our $DEBUG = 0; our $KEEP_ALL = 0; # We are exporting functions use Exporter 5.57 'import'; # 5.57 lets us import 'import' # Export list - to allow fine tuning of export table our @EXPORT_OK = qw{ tempfile tempdir tmpnam tmpfile mktemp mkstemp mkstemps mkdtemp unlink0 cleanup SEEK_SET SEEK_CUR SEEK_END }; # Groups of functions for export our %EXPORT_TAGS = ( 'POSIX' => [qw/ tmpnam tmpfile /], 'mktemp' => [qw/ mktemp mkstemp mkstemps mkdtemp/], 'seekable' => [qw/ SEEK_SET SEEK_CUR SEEK_END /], ); # add contents of these tags to @EXPORT Exporter::export_tags('POSIX','mktemp','seekable'); # This is a list of characters that can be used in random filenames my @CHARS = (qw/ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 _ /); # Maximum number of tries to make a temp file before failing use constant MAX_TRIES => 1000; # Minimum number of X characters that should be in a template use constant MINX => 4; # Default template when no template supplied use constant TEMPXXX => 'X' x 10; # Constants for the security level use constant STANDARD => 0; use constant MEDIUM => 1; use constant HIGH => 2; # OPENFLAGS. If we defined the flag to use with Sysopen here this gives # us an optimisation when many temporary files are requested my $OPENFLAGS = O_CREAT | O_EXCL | O_RDWR; my $LOCKFLAG; unless ($^O eq 'MacOS') { for my $oflag (qw/ NOFOLLOW BINARY LARGEFILE NOINHERIT /) { my ($bit, $func) = (0, "Fcntl::O_" . $oflag); no strict 'refs'; $OPENFLAGS |= $bit if eval { # Make sure that redefined die handlers do not cause problems # e.g. CGI::Carp local $SIG{__DIE__} = sub {}; local $SIG{__WARN__} = sub {}; $bit = &$func(); 1; }; } # Special case O_EXLOCK $LOCKFLAG = eval { local $SIG{__DIE__} = sub {}; local $SIG{__WARN__} = sub {}; &Fcntl::O_EXLOCK(); }; } # On some systems the O_TEMPORARY flag can be used to tell the OS # to automatically remove the file when it is closed. This is fine # in most cases but not if tempfile is called with UNLINK=>0 and # the filename is requested -- in the case where the filename is to # be passed to another routine. This happens on windows. We overcome # this by using a second open flags variable my $OPENTEMPFLAGS = $OPENFLAGS; unless ($^O eq 'MacOS') { for my $oflag (qw/ TEMPORARY /) { my ($bit, $func) = (0, "Fcntl::O_" . $oflag); local($@); no strict 'refs'; $OPENTEMPFLAGS |= $bit if eval { # Make sure that redefined die handlers do not cause problems # e.g. CGI::Carp local $SIG{__DIE__} = sub {}; local $SIG{__WARN__} = sub {}; $bit = &$func(); 1; }; } } # Private hash tracking which files have been created by each process id via the OO interface my %FILES_CREATED_BY_OBJECT; # INTERNAL ROUTINES - not to be used outside of package # Generic routine for getting a temporary filename # modelled on OpenBSD _gettemp() in mktemp.c # The template must contain X's that are to be replaced # with the random values # Arguments: # TEMPLATE - string containing the XXXXX's that is converted # to a random filename and opened if required # Optionally, a hash can also be supplied containing specific options # "open" => if true open the temp file, else just return the name # default is 0 # "mkdir"=> if true, we are creating a temp directory rather than tempfile # default is 0 # "suffixlen" => number of characters at end of PATH to be ignored. # default is 0. # "unlink_on_close" => indicates that, if possible, the OS should remove # the file as soon as it is closed. Usually indicates # use of the O_TEMPORARY flag to sysopen. # Usually irrelevant on unix # "use_exlock" => Indicates that O_EXLOCK should be used. Default is false. # "file_permissions" => file permissions for sysopen(). Default is 0600. # Optionally a reference to a scalar can be passed into the function # On error this will be used to store the reason for the error # "ErrStr" => \$errstr # "open" and "mkdir" can not both be true # "unlink_on_close" is not used when "mkdir" is true. # The default options are equivalent to mktemp(). # Returns: # filehandle - open file handle (if called with doopen=1, else undef) # temp name - name of the temp file or directory # For example: # ($fh, $name) = _gettemp($template, "open" => 1); # for the current version, failures are associated with # stored in an error string and returned to give the reason whilst debugging # This routine is not called by any external function sub _gettemp { croak 'Usage: ($fh, $name) = _gettemp($template, OPTIONS);' unless scalar(@_) >= 1; # the internal error string - expect it to be overridden # Need this in case the caller decides not to supply us a value # need an anonymous scalar my $tempErrStr; # Default options my %options = ( "open" => 0, "mkdir" => 0, "suffixlen" => 0, "unlink_on_close" => 0, "use_exlock" => 0, "ErrStr" => \$tempErrStr, "file_permissions" => undef, ); # Read the template my $template = shift; if (ref($template)) { # Use a warning here since we have not yet merged ErrStr carp "File::Temp::_gettemp: template must not be a reference"; return (); } # Check that the number of entries on stack are even if (scalar(@_) % 2 != 0) { # Use a warning here since we have not yet merged ErrStr carp "File::Temp::_gettemp: Must have even number of options"; return (); } # Read the options and merge with defaults %options = (%options, @_) if @_; # Make sure the error string is set to undef ${$options{ErrStr}} = undef; # Can not open the file and make a directory in a single call if ($options{"open"} && $options{"mkdir"}) { ${$options{ErrStr}} = "doopen and domkdir can not both be true\n"; return (); } # Find the start of the end of the Xs (position of last X) # Substr starts from 0 my $start = length($template) - 1 - $options{"suffixlen"}; # Check that we have at least MINX x X (e.g. 'XXXX") at the end of the string # (taking suffixlen into account). Any fewer is insecure. # Do it using substr - no reason to use a pattern match since # we know where we are looking and what we are looking for if (substr($template, $start - MINX + 1, MINX) ne 'X' x MINX) { ${$options{ErrStr}} = "The template must end with at least ". MINX . " 'X' characters\n"; return (); } # Replace all the X at the end of the substring with a # random character or just all the XX at the end of a full string. # Do it as an if, since the suffix adjusts which section to replace # and suffixlen=0 returns nothing if used in the substr directly # and generate a full path from the template my $path = _replace_XX($template, $options{"suffixlen"}); # Split the path into constituent parts - eventually we need to check # whether the directory exists # We need to know whether we are making a temp directory # or a tempfile my ($volume, $directories, $file); my $parent; # parent directory if ($options{"mkdir"}) { # There is no filename at the end ($volume, $directories, $file) = File::Spec->splitpath( $path, 1); # The parent is then $directories without the last directory # Split the directory and put it back together again my @dirs = File::Spec->splitdir($directories); # If @dirs only has one entry (i.e. the directory template) that means # we are in the current directory if ($#dirs == 0) { $parent = File::Spec->curdir; } else { if ($^O eq 'VMS') { # need volume to avoid relative dir spec $parent = File::Spec->catdir($volume, @dirs[0..$#dirs-1]); $parent = 'sys$disk:[]' if $parent eq ''; } else { # Put it back together without the last one $parent = File::Spec->catdir(@dirs[0..$#dirs-1]); # ...and attach the volume (no filename) $parent = File::Spec->catpath($volume, $parent, ''); } } } else { # Get rid of the last filename (use File::Basename for this?) ($volume, $directories, $file) = File::Spec->splitpath( $path ); # Join up without the file part $parent = File::Spec->catpath($volume,$directories,''); # If $parent is empty replace with curdir $parent = File::Spec->curdir unless $directories ne ''; } # Check that the parent directories exist # Do this even for the case where we are simply returning a name # not a file -- no point returning a name that includes a directory # that does not exist or is not writable unless (-e $parent) { ${$options{ErrStr}} = "Parent directory ($parent) does not exist"; return (); } unless (-d $parent) { ${$options{ErrStr}} = "Parent directory ($parent) is not a directory"; return (); } # Check the stickiness of the directory and chown giveaway if required # If the directory is world writable the sticky bit # must be set if (File::Temp->safe_level == MEDIUM) { my $safeerr; unless (_is_safe($parent,\$safeerr)) { ${$options{ErrStr}} = "Parent directory ($parent) is not safe ($safeerr)"; return (); } } elsif (File::Temp->safe_level == HIGH) { my $safeerr; unless (_is_verysafe($parent, \$safeerr)) { ${$options{ErrStr}} = "Parent directory ($parent) is not safe ($safeerr)"; return (); } } my $perms = $options{file_permissions}; my $has_perms = defined $perms; $perms = 0600 unless $has_perms; # Now try MAX_TRIES time to open the file for (my $i = 0; $i < MAX_TRIES; $i++) { # Try to open the file if requested if ($options{"open"}) { my $fh; # If we are running before perl5.6.0 we can not auto-vivify if ($] < 5.006) { $fh = &Symbol::gensym; } # Try to make sure this will be marked close-on-exec # XXX: Win32 doesn't respect this, nor the proper fcntl, # but may have O_NOINHERIT. This may or may not be in Fcntl. local $^F = 2; # Attempt to open the file my $open_success = undef; if ( $^O eq 'VMS' and $options{"unlink_on_close"} && !$KEEP_ALL) { # make it auto delete on close by setting FAB$V_DLT bit $fh = VMS::Stdio::vmssysopen($path, $OPENFLAGS, $perms, 'fop=dlt'); $open_success = $fh; } else { my $flags = ( ($options{"unlink_on_close"} && !$KEEP_ALL) ? $OPENTEMPFLAGS : $OPENFLAGS ); $flags |= $LOCKFLAG if (defined $LOCKFLAG && $options{use_exlock}); $open_success = sysopen($fh, $path, $flags, $perms); } if ( $open_success ) { # in case of odd umask force rw chmod($perms, $path) unless $has_perms; # Opened successfully - return file handle and name return ($fh, $path); } else { # Error opening file - abort with error # if the reason was anything but EEXIST unless ($!{EEXIST}) { ${$options{ErrStr}} = "Could not create temp file $path: $!"; return (); } # Loop round for another try } } elsif ($options{"mkdir"}) { # Open the temp directory if (mkdir( $path, 0700)) { # in case of odd umask chmod(0700, $path); return undef, $path; } else { # Abort with error if the reason for failure was anything # except EEXIST unless ($!{EEXIST}) { ${$options{ErrStr}} = "Could not create directory $path: $!"; return (); } # Loop round for another try } } else { # Return true if the file can not be found # Directory has been checked previously return (undef, $path) unless -e $path; # Try again until MAX_TRIES } # Did not successfully open the tempfile/dir # so try again with a different set of random letters # No point in trying to increment unless we have only # 1 X say and the randomness could come up with the same # file MAX_TRIES in a row. # Store current attempt - in principle this implies that the # 3rd time around the open attempt that the first temp file # name could be generated again. Probably should store each # attempt and make sure that none are repeated my $original = $path; my $counter = 0; # Stop infinite loop my $MAX_GUESS = 50; do { # Generate new name from original template $path = _replace_XX($template, $options{"suffixlen"}); $counter++; } until ($path ne $original || $counter > $MAX_GUESS); # Check for out of control looping if ($counter > $MAX_GUESS) { ${$options{ErrStr}} = "Tried to get a new temp name different to the previous value $MAX_GUESS times.\nSomething wrong with template?? ($template)"; return (); } } # If we get here, we have run out of tries ${ $options{ErrStr} } = "Have exceeded the maximum number of attempts (" . MAX_TRIES . ") to open temp file/dir"; return (); } # Internal routine to replace the XXXX... with random characters # This has to be done by _gettemp() every time it fails to # open a temp file/dir # Arguments: $template (the template with XXX), # $ignore (number of characters at end to ignore) # Returns: modified template sub _replace_XX { croak 'Usage: _replace_XX($template, $ignore)' unless scalar(@_) == 2; my ($path, $ignore) = @_; # Do it as an if, since the suffix adjusts which section to replace # and suffixlen=0 returns nothing if used in the substr directly # Alternatively, could simply set $ignore to length($path)-1 # Don't want to always use substr when not required though. my $end = ( $] >= 5.006 ? "\\z" : "\\Z" ); if ($ignore) { substr($path, 0, - $ignore) =~ s/X(?=X*$end)/$CHARS[ int( rand( @CHARS ) ) ]/ge; } else { $path =~ s/X(?=X*$end)/$CHARS[ int( rand( @CHARS ) ) ]/ge; } return $path; } # Internal routine to force a temp file to be writable after # it is created so that we can unlink it. Windows seems to occasionally # force a file to be readonly when written to certain temp locations sub _force_writable { my $file = shift; chmod 0600, $file; } # internal routine to check to see if the directory is safe # First checks to see if the directory is not owned by the # current user or root. Then checks to see if anyone else # can write to the directory and if so, checks to see if # it has the sticky bit set # Will not work on systems that do not support sticky bit #Args: directory path to check # Optionally: reference to scalar to contain error message # Returns true if the path is safe and false otherwise. # Returns undef if can not even run stat() on the path # This routine based on version written by Tom Christiansen # Presumably, by the time we actually attempt to create the # file or directory in this directory, it may not be safe # anymore... Have to run _is_safe directly after the open. sub _is_safe { my $path = shift; my $err_ref = shift; # Stat path my @info = stat($path); unless (scalar(@info)) { $$err_ref = "stat(path) returned no values"; return 0; } ; return 1 if $^O eq 'VMS'; # owner delete control at file level # Check to see whether owner is neither superuser (or a system uid) nor me # Use the effective uid from the $> variable # UID is in [4] if ($info[4] > File::Temp->top_system_uid() && $info[4] != $>) { Carp::cluck(sprintf "uid=$info[4] topuid=%s euid=$> path='$path'", File::Temp->top_system_uid()); $$err_ref = "Directory owned neither by root nor the current user" if ref($err_ref); return 0; } # check whether group or other can write file # use 066 to detect either reading or writing # use 022 to check writability # Do it with S_IWOTH and S_IWGRP for portability (maybe) # mode is in info[2] if (($info[2] & &Fcntl::S_IWGRP) || # Is group writable? ($info[2] & &Fcntl::S_IWOTH) ) { # Is world writable? # Must be a directory unless (-d $path) { $$err_ref = "Path ($path) is not a directory" if ref($err_ref); return 0; } # Must have sticky bit set unless (-k $path) { $$err_ref = "Sticky bit not set on $path when dir is group|world writable" if ref($err_ref); return 0; } } return 1; } # Internal routine to check whether a directory is safe # for temp files. Safer than _is_safe since it checks for # the possibility of chown giveaway and if that is a possibility # checks each directory in the path to see if it is safe (with _is_safe) # If _PC_CHOWN_RESTRICTED is not set, does the full test of each # directory anyway. # Takes optional second arg as scalar ref to error reason sub _is_verysafe { # Need POSIX - but only want to bother if really necessary due to overhead require POSIX; my $path = shift; print "_is_verysafe testing $path\n" if $DEBUG; return 1 if $^O eq 'VMS'; # owner delete control at file level my $err_ref = shift; # Should Get the value of _PC_CHOWN_RESTRICTED if it is defined # and If it is not there do the extensive test local($@); my $chown_restricted; $chown_restricted = &POSIX::_PC_CHOWN_RESTRICTED() if eval { &POSIX::_PC_CHOWN_RESTRICTED(); 1}; # If chown_resticted is set to some value we should test it if (defined $chown_restricted) { # Return if the current directory is safe return _is_safe($path,$err_ref) if POSIX::sysconf( $chown_restricted ); } # To reach this point either, the _PC_CHOWN_RESTRICTED symbol # was not available or the symbol was there but chown giveaway # is allowed. Either way, we now have to test the entire tree for # safety. # Convert path to an absolute directory if required unless (File::Spec->file_name_is_absolute($path)) { $path = File::Spec->rel2abs($path); } # Split directory into components - assume no file my ($volume, $directories, undef) = File::Spec->splitpath( $path, 1); # Slightly less efficient than having a function in File::Spec # to chop off the end of a directory or even a function that # can handle ../ in a directory tree # Sometimes splitdir() returns a blank at the end # so we will probably check the bottom directory twice in some cases my @dirs = File::Spec->splitdir($directories); # Concatenate one less directory each time around foreach my $pos (0.. $#dirs) { # Get a directory name my $dir = File::Spec->catpath($volume, File::Spec->catdir(@dirs[0.. $#dirs - $pos]), '' ); print "TESTING DIR $dir\n" if $DEBUG; # Check the directory return 0 unless _is_safe($dir,$err_ref); } return 1; } # internal routine to determine whether unlink works on this # platform for files that are currently open. # Returns true if we can, false otherwise. # Currently WinNT, OS/2 and VMS can not unlink an opened file # On VMS this is because the O_EXCL flag is used to open the # temporary file. Currently I do not know enough about the issues # on VMS to decide whether O_EXCL is a requirement. sub _can_unlink_opened_file { if (grep $^O eq $_, qw/MSWin32 os2 VMS dos MacOS haiku/) { return 0; } else { return 1; } } # internal routine to decide which security levels are allowed # see safe_level() for more information on this # Controls whether the supplied security level is allowed # $cando = _can_do_level( $level ) sub _can_do_level { # Get security level my $level = shift; # Always have to be able to do STANDARD return 1 if $level == STANDARD; # Currently, the systems that can do HIGH or MEDIUM are identical if ( $^O eq 'MSWin32' || $^O eq 'os2' || $^O eq 'cygwin' || $^O eq 'dos' || $^O eq 'MacOS' || $^O eq 'mpeix') { return 0; } else { return 1; } } # This routine sets up a deferred unlinking of a specified # filename and filehandle. It is used in the following cases: # - Called by unlink0 if an opened file can not be unlinked # - Called by tempfile() if files are to be removed on shutdown # - Called by tempdir() if directories are to be removed on shutdown # Arguments: # _deferred_unlink( $fh, $fname, $isdir ); # # - filehandle (so that it can be explicitly closed if open # - filename (the thing we want to remove) # - isdir (flag to indicate that we are being given a directory) # [and hence no filehandle] # Status is not referred to since all the magic is done with an END block { # Will set up two lexical variables to contain all the files to be # removed. One array for files, another for directories They will # only exist in this block. # This means we only have to set up a single END block to remove # all files. # in order to prevent child processes inadvertently deleting the parent # temp files we use a hash to store the temp files and directories # created by a particular process id. # %files_to_unlink contains values that are references to an array of # array references containing the filehandle and filename associated with # the temp file. my (%files_to_unlink, %dirs_to_unlink); # Set up an end block to use these arrays END { local($., $@, $!, $^E, $?); cleanup(at_exit => 1); } # Cleanup function. Always triggered on END (with at_exit => 1) but # can be invoked manually. sub cleanup { my %h = @_; my $at_exit = delete $h{at_exit}; $at_exit = 0 if not defined $at_exit; { my @k = sort keys %h; die "unrecognized parameters: @k" if @k } if (!$KEEP_ALL) { # Files my @files = (exists $files_to_unlink{$$} ? @{ $files_to_unlink{$$} } : () ); foreach my $file (@files) { # close the filehandle without checking its state # in order to make real sure that this is closed # if its already closed then I don't care about the answer # probably a better way to do this close($file->[0]); # file handle is [0] if (-f $file->[1]) { # file name is [1] _force_writable( $file->[1] ); # for windows unlink $file->[1] or warn "Error removing ".$file->[1]; } } # Dirs my @dirs = (exists $dirs_to_unlink{$$} ? @{ $dirs_to_unlink{$$} } : () ); my ($cwd, $cwd_to_remove); foreach my $dir (@dirs) { if (-d $dir) { # Some versions of rmtree will abort if you attempt to remove # the directory you are sitting in. For automatic cleanup # at program exit, we avoid this by chdir()ing out of the way # first. If not at program exit, it's best not to mess with the # current directory, so just let it fail with a warning. if ($at_exit) { $cwd = Cwd::abs_path(File::Spec->curdir) if not defined $cwd; my $abs = Cwd::abs_path($dir); if ($abs eq $cwd) { $cwd_to_remove = $dir; next; } } eval { rmtree($dir, $DEBUG, 0); }; warn $@ if ($@ && $^W); } } if (defined $cwd_to_remove) { # We do need to clean up the current directory, and everything # else is done, so get out of there and remove it. chdir $cwd_to_remove or die "cannot chdir to $cwd_to_remove: $!"; my $updir = File::Spec->updir; chdir $updir or die "cannot chdir to $updir: $!"; eval { rmtree($cwd_to_remove, $DEBUG, 0); }; warn $@ if ($@ && $^W); } # clear the arrays @{ $files_to_unlink{$$} } = () if exists $files_to_unlink{$$}; @{ $dirs_to_unlink{$$} } = () if exists $dirs_to_unlink{$$}; } } # This is the sub called to register a file for deferred unlinking # This could simply store the input parameters and defer everything # until the END block. For now we do a bit of checking at this # point in order to make sure that (1) we have a file/dir to delete # and (2) we have been called with the correct arguments. sub _deferred_unlink { croak 'Usage: _deferred_unlink($fh, $fname, $isdir)' unless scalar(@_) == 3; my ($fh, $fname, $isdir) = @_; warn "Setting up deferred removal of $fname\n" if $DEBUG; # make sure we save the absolute path for later cleanup # OK to untaint because we only ever use this internally # as a file path, never interpolating into the shell $fname = Cwd::abs_path($fname); ($fname) = $fname =~ /^(.*)$/; # If we have a directory, check that it is a directory if ($isdir) { if (-d $fname) { # Directory exists so store it # first on VMS turn []foo into [.foo] for rmtree $fname = VMS::Filespec::vmspath($fname) if $^O eq 'VMS'; $dirs_to_unlink{$$} = [] unless exists $dirs_to_unlink{$$}; push (@{ $dirs_to_unlink{$$} }, $fname); } else { carp "Request to remove directory $fname could not be completed since it does not exist!\n" if $^W; } } else { if (-f $fname) { # file exists so store handle and name for later removal $files_to_unlink{$$} = [] unless exists $files_to_unlink{$$}; push(@{ $files_to_unlink{$$} }, [$fh, $fname]); } else { carp "Request to remove file $fname could not be completed since it is not there!\n" if $^W; } } } } # normalize argument keys to upper case and do consistent handling # of leading template vs TEMPLATE sub _parse_args { my $leading_template = (scalar(@_) % 2 == 1 ? shift(@_) : '' ); my %args = @_; %args = map +(uc($_) => $args{$_}), keys %args; # template (store it in an array so that it will # disappear from the arg list of tempfile) my @template = ( exists $args{TEMPLATE} ? $args{TEMPLATE} : $leading_template ? $leading_template : () ); delete $args{TEMPLATE}; return( \@template, \%args ); } #pod =head1 OBJECT-ORIENTED INTERFACE #pod #pod This is the primary interface for interacting with #pod C<File::Temp>. Using the OO interface a temporary file can be created #pod when the object is constructed and the file can be removed when the #pod object is no longer required. #pod #pod Note that there is no method to obtain the filehandle from the #pod C<File::Temp> object. The object itself acts as a filehandle. The object #pod isa C<IO::Handle> and isa C<IO::Seekable> so all those methods are #pod available. #pod #pod Also, the object is configured such that it stringifies to the name of the #pod temporary file and so can be compared to a filename directly. It numifies #pod to the C<refaddr> the same as other handles and so can be compared to other #pod handles with C<==>. #pod #pod $fh eq $filename # as a string #pod $fh != \*STDOUT # as a number #pod #pod Available since 0.14. #pod #pod =over 4 #pod #pod =item B<new> #pod #pod Create a temporary file object. #pod #pod my $tmp = File::Temp->new(); #pod #pod by default the object is constructed as if C<tempfile> #pod was called without options, but with the additional behaviour #pod that the temporary file is removed by the object destructor #pod if UNLINK is set to true (the default). #pod #pod Supported arguments are the same as for C<tempfile>: UNLINK #pod (defaulting to true), DIR, EXLOCK, PERMS and SUFFIX. #pod Additionally, the filename #pod template is specified using the TEMPLATE option. The OPEN option #pod is not supported (the file is always opened). #pod #pod $tmp = File::Temp->new( TEMPLATE => 'tempXXXXX', #pod DIR => 'mydir', #pod SUFFIX => '.dat'); #pod #pod Arguments are case insensitive. #pod #pod Can call croak() if an error occurs. #pod #pod Available since 0.14. #pod #pod TEMPLATE available since 0.23 #pod #pod =cut sub new { my $proto = shift; my $class = ref($proto) || $proto; my ($maybe_template, $args) = _parse_args(@_); # see if they are unlinking (defaulting to yes) my $unlink = (exists $args->{UNLINK} ? $args->{UNLINK} : 1 ); delete $args->{UNLINK}; # Protect OPEN delete $args->{OPEN}; # Open the file and retain file handle and file name my ($fh, $path) = tempfile( @$maybe_template, %$args ); print "Tmp: $fh - $path\n" if $DEBUG; # Store the filename in the scalar slot ${*$fh} = $path; # Cache the filename by pid so that the destructor can decide whether to remove it $FILES_CREATED_BY_OBJECT{$$}{$path} = 1; # Store unlink information in hash slot (plus other constructor info) %{*$fh} = %$args; # create the object bless $fh, $class; # final method-based configuration $fh->unlink_on_destroy( $unlink ); return $fh; } #pod =item B<newdir> #pod #pod Create a temporary directory using an object oriented interface. #pod #pod $dir = File::Temp->newdir(); #pod #pod By default the directory is deleted when the object goes out of scope. #pod #pod Supports the same options as the C<tempdir> function. Note that directories #pod created with this method default to CLEANUP => 1. #pod #pod $dir = File::Temp->newdir( $template, %options ); #pod #pod A template may be specified either with a leading template or #pod with a TEMPLATE argument. #pod #pod Available since 0.19. #pod #pod TEMPLATE available since 0.23. #pod #pod =cut sub newdir { my $self = shift; my ($maybe_template, $args) = _parse_args(@_); # handle CLEANUP without passing CLEANUP to tempdir my $cleanup = (exists $args->{CLEANUP} ? $args->{CLEANUP} : 1 ); delete $args->{CLEANUP}; my $tempdir = tempdir( @$maybe_template, %$args); # get a safe absolute path for cleanup, just like # happens in _deferred_unlink my $real_dir = Cwd::abs_path( $tempdir ); ($real_dir) = $real_dir =~ /^(.*)$/; return bless { DIRNAME => $tempdir, REALNAME => $real_dir, CLEANUP => $cleanup, LAUNCHPID => $$, }, "File::Temp::Dir"; } #pod =item B<filename> #pod #pod Return the name of the temporary file associated with this object #pod (if the object was created using the "new" constructor). #pod #pod $filename = $tmp->filename; #pod #pod This method is called automatically when the object is used as #pod a string. #pod #pod Current API available since 0.14 #pod #pod =cut sub filename { my $self = shift; return ${*$self}; } sub STRINGIFY { my $self = shift; return $self->filename; } # For reference, can't use '0+'=>\&Scalar::Util::refaddr directly because # refaddr() demands one parameter only, whereas overload.pm calls with three # even for unary operations like '0+'. sub NUMIFY { return refaddr($_[0]); } #pod =item B<dirname> #pod #pod Return the name of the temporary directory associated with this #pod object (if the object was created using the "newdir" constructor). #pod #pod $dirname = $tmpdir->dirname; #pod #pod This method is called automatically when the object is used in string context. #pod #pod =item B<unlink_on_destroy> #pod #pod Control whether the file is unlinked when the object goes out of scope. #pod The file is removed if this value is true and $KEEP_ALL is not. #pod #pod $fh->unlink_on_destroy( 1 ); #pod #pod Default is for the file to be removed. #pod #pod Current API available since 0.15 #pod #pod =cut sub unlink_on_destroy { my $self = shift; if (@_) { ${*$self}{UNLINK} = shift; } return ${*$self}{UNLINK}; } #pod =item B<DESTROY> #pod #pod When the object goes out of scope, the destructor is called. This #pod destructor will attempt to unlink the file (using L<unlink1|"unlink1">) #pod if the constructor was called with UNLINK set to 1 (the default state #pod if UNLINK is not specified). #pod #pod No error is given if the unlink fails. #pod #pod If the object has been passed to a child process during a fork, the #pod file will be deleted when the object goes out of scope in the parent. #pod #pod For a temporary directory object the directory will be removed unless #pod the CLEANUP argument was used in the constructor (and set to false) or #pod C<unlink_on_destroy> was modified after creation. Note that if a temp #pod directory is your current directory, it cannot be removed - a warning #pod will be given in this case. C<chdir()> out of the directory before #pod letting the object go out of scope. #pod #pod If the global variable $KEEP_ALL is true, the file or directory #pod will not be removed. #pod #pod =cut sub DESTROY { local($., $@, $!, $^E, $?); my $self = shift; # Make sure we always remove the file from the global hash # on destruction. This prevents the hash from growing uncontrollably # and post-destruction there is no reason to know about the file. my $file = $self->filename; my $was_created_by_proc; if (exists $FILES_CREATED_BY_OBJECT{$$}{$file}) { $was_created_by_proc = 1; delete $FILES_CREATED_BY_OBJECT{$$}{$file}; } if (${*$self}{UNLINK} && !$KEEP_ALL) { print "# ---------> Unlinking $self\n" if $DEBUG; # only delete if this process created it return unless $was_created_by_proc; # The unlink1 may fail if the file has been closed # by the caller. This leaves us with the decision # of whether to refuse to remove the file or simply # do an unlink without test. Seems to be silly # to do this when we are trying to be careful # about security _force_writable( $file ); # for windows unlink1( $self, $file ) or unlink($file); } } #pod =back #pod #pod =head1 FUNCTIONS #pod #pod This section describes the recommended interface for generating #pod temporary files and directories. #pod #pod =over 4 #pod #pod =item B<tempfile> #pod #pod This is the basic function to generate temporary files. #pod The behaviour of the file can be changed using various options: #pod #pod $fh = tempfile(); #pod ($fh, $filename) = tempfile(); #pod #pod Create a temporary file in the directory specified for temporary #pod files, as specified by the tmpdir() function in L<File::Spec>. #pod #pod ($fh, $filename) = tempfile($template); #pod #pod Create a temporary file in the current directory using the supplied #pod template. Trailing `X' characters are replaced with random letters to #pod generate the filename. At least four `X' characters must be present #pod at the end of the template. #pod #pod ($fh, $filename) = tempfile($template, SUFFIX => $suffix) #pod #pod Same as previously, except that a suffix is added to the template #pod after the `X' translation. Useful for ensuring that a temporary #pod filename has a particular extension when needed by other applications. #pod But see the WARNING at the end. #pod #pod ($fh, $filename) = tempfile($template, DIR => $dir); #pod #pod Translates the template as before except that a directory name #pod is specified. #pod #pod ($fh, $filename) = tempfile($template, TMPDIR => 1); #pod #pod Equivalent to specifying a DIR of "File::Spec->tmpdir", writing the file #pod into the same temporary directory as would be used if no template was #pod specified at all. #pod #pod ($fh, $filename) = tempfile($template, UNLINK => 1); #pod #pod Return the filename and filehandle as before except that the file is #pod automatically removed when the program exits (dependent on #pod $KEEP_ALL). Default is for the file to be removed if a file handle is #pod requested and to be kept if the filename is requested. In a scalar #pod context (where no filename is returned) the file is always deleted #pod either (depending on the operating system) on exit or when it is #pod closed (unless $KEEP_ALL is true when the temp file is created). #pod #pod Use the object-oriented interface if fine-grained control of when #pod a file is removed is required. #pod #pod If the template is not specified, a template is always #pod automatically generated. This temporary file is placed in tmpdir() #pod (L<File::Spec>) unless a directory is specified explicitly with the #pod DIR option. #pod #pod $fh = tempfile( DIR => $dir ); #pod #pod If called in scalar context, only the filehandle is returned and the #pod file will automatically be deleted when closed on operating systems #pod that support this (see the description of tmpfile() elsewhere in this #pod document). This is the preferred mode of operation, as if you only #pod have a filehandle, you can never create a race condition by fumbling #pod with the filename. On systems that can not unlink an open file or can #pod not mark a file as temporary when it is opened (for example, Windows #pod NT uses the C<O_TEMPORARY> flag) the file is marked for deletion when #pod the program ends (equivalent to setting UNLINK to 1). The C<UNLINK> #pod flag is ignored if present. #pod #pod (undef, $filename) = tempfile($template, OPEN => 0); #pod #pod This will return the filename based on the template but #pod will not open this file. Cannot be used in conjunction with #pod UNLINK set to true. Default is to always open the file #pod to protect from possible race conditions. A warning is issued #pod if warnings are turned on. Consider using the tmpnam() #pod and mktemp() functions described elsewhere in this document #pod if opening the file is not required. #pod #pod To open the temporary filehandle with O_EXLOCK (open with exclusive #pod file lock) use C<< EXLOCK=>1 >>. This is supported only by some #pod operating systems (most notably BSD derived systems). By default #pod EXLOCK will be false. Former C<File::Temp> versions set EXLOCK to #pod true, so to be sure to get an unlocked filehandle also with older #pod versions, explicitly set C<< EXLOCK=>0 >>. #pod #pod ($fh, $filename) = tempfile($template, EXLOCK => 1); #pod #pod By default, the temp file is created with 0600 file permissions. #pod Use C<PERMS> to change this: #pod #pod ($fh, $filename) = tempfile($template, PERMS => 0666); #pod #pod Options can be combined as required. #pod #pod Will croak() if there is an error. #pod #pod Available since 0.05. #pod #pod UNLINK flag available since 0.10. #pod #pod TMPDIR flag available since 0.19. #pod #pod EXLOCK flag available since 0.19. #pod #pod PERMS flag available since 0.2310. #pod #pod =cut sub tempfile { if ( @_ && $_[0] eq 'File::Temp' ) { croak "'tempfile' can't be called as a method"; } # Can not check for argument count since we can have any # number of args # Default options my %options = ( "DIR" => undef, # Directory prefix "SUFFIX" => '', # Template suffix "UNLINK" => 0, # Do not unlink file on exit "OPEN" => 1, # Open file "TMPDIR" => 0, # Place tempfile in tempdir if template specified "EXLOCK" => 0, # Open file with O_EXLOCK "PERMS" => undef, # File permissions ); # Check to see whether we have an odd or even number of arguments my ($maybe_template, $args) = _parse_args(@_); my $template = @$maybe_template ? $maybe_template->[0] : undef; # Read the options and merge with defaults %options = (%options, %$args); # First decision is whether or not to open the file if (! $options{"OPEN"}) { warn "tempfile(): temporary filename requested but not opened.\nPossibly unsafe, consider using tempfile() with OPEN set to true\n" if $^W; } if ($options{"DIR"} and $^O eq 'VMS') { # on VMS turn []foo into [.foo] for concatenation $options{"DIR"} = VMS::Filespec::vmspath($options{"DIR"}); } # Construct the template # Have a choice of trying to work around the mkstemp/mktemp/tmpnam etc # functions or simply constructing a template and using _gettemp() # explicitly. Go for the latter # First generate a template if not defined and prefix the directory # If no template must prefix the temp directory if (defined $template) { # End up with current directory if neither DIR not TMPDIR are set if ($options{"DIR"}) { $template = File::Spec->catfile($options{"DIR"}, $template); } elsif ($options{TMPDIR}) { $template = File::Spec->catfile(_wrap_file_spec_tmpdir(), $template ); } } else { if ($options{"DIR"}) { $template = File::Spec->catfile($options{"DIR"}, TEMPXXX); } else { $template = File::Spec->catfile(_wrap_file_spec_tmpdir(), TEMPXXX); } } # Now add a suffix $template .= $options{"SUFFIX"}; # Determine whether we should tell _gettemp to unlink the file # On unix this is irrelevant and can be worked out after the file is # opened (simply by unlinking the open filehandle). On Windows or VMS # we have to indicate temporary-ness when we open the file. In general # we only want a true temporary file if we are returning just the # filehandle - if the user wants the filename they probably do not # want the file to disappear as soon as they close it (which may be # important if they want a child process to use the file) # For this reason, tie unlink_on_close to the return context regardless # of OS. my $unlink_on_close = ( wantarray ? 0 : 1); # Create the file my ($fh, $path, $errstr); croak "Error in tempfile() using template $template: $errstr" unless (($fh, $path) = _gettemp($template, "open" => $options{OPEN}, "mkdir" => 0, "unlink_on_close" => $unlink_on_close, "suffixlen" => length($options{SUFFIX}), "ErrStr" => \$errstr, "use_exlock" => $options{EXLOCK}, "file_permissions" => $options{PERMS}, ) ); # Set up an exit handler that can do whatever is right for the # system. This removes files at exit when requested explicitly or when # system is asked to unlink_on_close but is unable to do so because # of OS limitations. # The latter should be achieved by using a tied filehandle. # Do not check return status since this is all done with END blocks. _deferred_unlink($fh, $path, 0) if $options{"UNLINK"}; # Return if (wantarray()) { if ($options{'OPEN'}) { return ($fh, $path); } else { return (undef, $path); } } else { # Unlink the file. It is up to unlink0 to decide what to do with # this (whether to unlink now or to defer until later) unlink0($fh, $path) or croak "Error unlinking file $path using unlink0"; # Return just the filehandle. return $fh; } } # On Windows under taint mode, File::Spec could suggest "C:\" as a tempdir # which might not be writable. If that is the case, we fallback to a # user directory. See https://rt.cpan.org/Ticket/Display.html?id=60340 { my ($alt_tmpdir, $checked); sub _wrap_file_spec_tmpdir { return File::Spec->tmpdir unless $^O eq "MSWin32" && ${^TAINT}; if ( $checked ) { return $alt_tmpdir ? $alt_tmpdir : File::Spec->tmpdir; } # probe what File::Spec gives and find a fallback my $xxpath = _replace_XX( "X" x 10, 0 ); # First, see if File::Spec->tmpdir is writable my $tmpdir = File::Spec->tmpdir; my $testpath = File::Spec->catdir( $tmpdir, $xxpath ); if (mkdir( $testpath, 0700) ) { $checked = 1; rmdir $testpath; return $tmpdir; } # Next, see if CSIDL_LOCAL_APPDATA is writable require Win32; my $local_app = File::Spec->catdir( Win32::GetFolderPath( Win32::CSIDL_LOCAL_APPDATA() ), 'Temp' ); $testpath = File::Spec->catdir( $local_app, $xxpath ); if ( -e $local_app or mkdir( $local_app, 0700 ) ) { if (mkdir( $testpath, 0700) ) { $checked = 1; rmdir $testpath; return $alt_tmpdir = $local_app; } } # Can't find something writable croak << "HERE"; Couldn't find a writable temp directory in taint mode. Tried: $tmpdir $local_app Try setting and untainting the TMPDIR environment variable. HERE } } #pod =item B<tempdir> #pod #pod This is the recommended interface for creation of temporary #pod directories. By default the directory will not be removed on exit #pod (that is, it won't be temporary; this behaviour can not be changed #pod because of issues with backwards compatibility). To enable removal #pod either use the CLEANUP option which will trigger removal on program #pod exit, or consider using the "newdir" method in the object interface which #pod will allow the directory to be cleaned up when the object goes out of #pod scope. #pod #pod The behaviour of the function depends on the arguments: #pod #pod $tempdir = tempdir(); #pod #pod Create a directory in tmpdir() (see L<File::Spec|File::Spec>). #pod #pod $tempdir = tempdir( $template ); #pod #pod Create a directory from the supplied template. This template is #pod similar to that described for tempfile(). `X' characters at the end #pod of the template are replaced with random letters to construct the #pod directory name. At least four `X' characters must be in the template. #pod #pod $tempdir = tempdir ( DIR => $dir ); #pod #pod Specifies the directory to use for the temporary directory. #pod The temporary directory name is derived from an internal template. #pod #pod $tempdir = tempdir ( $template, DIR => $dir ); #pod #pod Prepend the supplied directory name to the template. The template #pod should not include parent directory specifications itself. Any parent #pod directory specifications are removed from the template before #pod prepending the supplied directory. #pod #pod $tempdir = tempdir ( $template, TMPDIR => 1 ); #pod #pod Using the supplied template, create the temporary directory in #pod a standard location for temporary files. Equivalent to doing #pod #pod $tempdir = tempdir ( $template, DIR => File::Spec->tmpdir); #pod #pod but shorter. Parent directory specifications are stripped from the #pod template itself. The C<TMPDIR> option is ignored if C<DIR> is set #pod explicitly. Additionally, C<TMPDIR> is implied if neither a template #pod nor a directory are supplied. #pod #pod $tempdir = tempdir( $template, CLEANUP => 1); #pod #pod Create a temporary directory using the supplied template, but #pod attempt to remove it (and all files inside it) when the program #pod exits. Note that an attempt will be made to remove all files from #pod the directory even if they were not created by this module (otherwise #pod why ask to clean it up?). The directory removal is made with #pod the rmtree() function from the L<File::Path|File::Path> module. #pod Of course, if the template is not specified, the temporary directory #pod will be created in tmpdir() and will also be removed at program exit. #pod #pod Will croak() if there is an error. #pod #pod Current API available since 0.05. #pod #pod =cut # ' sub tempdir { if ( @_ && $_[0] eq 'File::Temp' ) { croak "'tempdir' can't be called as a method"; } # Can not check for argument count since we can have any # number of args # Default options my %options = ( "CLEANUP" => 0, # Remove directory on exit "DIR" => '', # Root directory "TMPDIR" => 0, # Use tempdir with template ); # Check to see whether we have an odd or even number of arguments my ($maybe_template, $args) = _parse_args(@_); my $template = @$maybe_template ? $maybe_template->[0] : undef; # Read the options and merge with defaults %options = (%options, %$args); # Modify or generate the template # Deal with the DIR and TMPDIR options if (defined $template) { # Need to strip directory path if using DIR or TMPDIR if ($options{'TMPDIR'} || $options{'DIR'}) { # Strip parent directory from the filename # # There is no filename at the end $template = VMS::Filespec::vmspath($template) if $^O eq 'VMS'; my ($volume, $directories, undef) = File::Spec->splitpath( $template, 1); # Last directory is then our template $template = (File::Spec->splitdir($directories))[-1]; # Prepend the supplied directory or temp dir if ($options{"DIR"}) { $template = File::Spec->catdir($options{"DIR"}, $template); } elsif ($options{TMPDIR}) { # Prepend tmpdir $template = File::Spec->catdir(_wrap_file_spec_tmpdir(), $template); } } } else { if ($options{"DIR"}) { $template = File::Spec->catdir($options{"DIR"}, TEMPXXX); } else { $template = File::Spec->catdir(_wrap_file_spec_tmpdir(), TEMPXXX); } } # Create the directory my $tempdir; my $suffixlen = 0; if ($^O eq 'VMS') { # dir names can end in delimiters $template =~ m/([\.\]:>]+)$/; $suffixlen = length($1); } if ( ($^O eq 'MacOS') && (substr($template, -1) eq ':') ) { # dir name has a trailing ':' ++$suffixlen; } my $errstr; croak "Error in tempdir() using $template: $errstr" unless ((undef, $tempdir) = _gettemp($template, "open" => 0, "mkdir"=> 1 , "suffixlen" => $suffixlen, "ErrStr" => \$errstr, ) ); # Install exit handler; must be dynamic to get lexical if ( $options{'CLEANUP'} && -d $tempdir) { _deferred_unlink(undef, $tempdir, 1); } # Return the dir name return $tempdir; } #pod =back #pod #pod =head1 MKTEMP FUNCTIONS #pod #pod The following functions are Perl implementations of the #pod mktemp() family of temp file generation system calls. #pod #pod =over 4 #pod #pod =item B<mkstemp> #pod #pod Given a template, returns a filehandle to the temporary file and the name #pod of the file. #pod #pod ($fh, $name) = mkstemp( $template ); #pod #pod In scalar context, just the filehandle is returned. #pod #pod The template may be any filename with some number of X's appended #pod to it, for example F</tmp/temp.XXXX>. The trailing X's are replaced #pod with unique alphanumeric combinations. #pod #pod Will croak() if there is an error. #pod #pod Current API available since 0.05. #pod #pod =cut sub mkstemp { croak "Usage: mkstemp(template)" if scalar(@_) != 1; my $template = shift; my ($fh, $path, $errstr); croak "Error in mkstemp using $template: $errstr" unless (($fh, $path) = _gettemp($template, "open" => 1, "mkdir"=> 0 , "suffixlen" => 0, "ErrStr" => \$errstr, ) ); if (wantarray()) { return ($fh, $path); } else { return $fh; } } #pod =item B<mkstemps> #pod #pod Similar to mkstemp(), except that an extra argument can be supplied #pod with a suffix to be appended to the template. #pod #pod ($fh, $name) = mkstemps( $template, $suffix ); #pod #pod For example a template of C<testXXXXXX> and suffix of C<.dat> #pod would generate a file similar to F<testhGji_w.dat>. #pod #pod Returns just the filehandle alone when called in scalar context. #pod #pod Will croak() if there is an error. #pod #pod Current API available since 0.05. #pod #pod =cut sub mkstemps { croak "Usage: mkstemps(template, suffix)" if scalar(@_) != 2; my $template = shift; my $suffix = shift; $template .= $suffix; my ($fh, $path, $errstr); croak "Error in mkstemps using $template: $errstr" unless (($fh, $path) = _gettemp($template, "open" => 1, "mkdir"=> 0 , "suffixlen" => length($suffix), "ErrStr" => \$errstr, ) ); if (wantarray()) { return ($fh, $path); } else { return $fh; } } #pod =item B<mkdtemp> #pod #pod Create a directory from a template. The template must end in #pod X's that are replaced by the routine. #pod #pod $tmpdir_name = mkdtemp($template); #pod #pod Returns the name of the temporary directory created. #pod #pod Directory must be removed by the caller. #pod #pod Will croak() if there is an error. #pod #pod Current API available since 0.05. #pod #pod =cut #' # for emacs sub mkdtemp { croak "Usage: mkdtemp(template)" if scalar(@_) != 1; my $template = shift; my $suffixlen = 0; if ($^O eq 'VMS') { # dir names can end in delimiters $template =~ m/([\.\]:>]+)$/; $suffixlen = length($1); } if ( ($^O eq 'MacOS') && (substr($template, -1) eq ':') ) { # dir name has a trailing ':' ++$suffixlen; } my ($junk, $tmpdir, $errstr); croak "Error creating temp directory from template $template\: $errstr" unless (($junk, $tmpdir) = _gettemp($template, "open" => 0, "mkdir"=> 1 , "suffixlen" => $suffixlen, "ErrStr" => \$errstr, ) ); return $tmpdir; } #pod =item B<mktemp> #pod #pod Returns a valid temporary filename but does not guarantee #pod that the file will not be opened by someone else. #pod #pod $unopened_file = mktemp($template); #pod #pod Template is the same as that required by mkstemp(). #pod #pod Will croak() if there is an error. #pod #pod Current API available since 0.05. #pod #pod =cut sub mktemp { croak "Usage: mktemp(template)" if scalar(@_) != 1; my $template = shift; my ($tmpname, $junk, $errstr); croak "Error getting name to temp file from template $template: $errstr" unless (($junk, $tmpname) = _gettemp($template, "open" => 0, "mkdir"=> 0 , "suffixlen" => 0, "ErrStr" => \$errstr, ) ); return $tmpname; } #pod =back #pod #pod =head1 POSIX FUNCTIONS #pod #pod This section describes the re-implementation of the tmpnam() #pod and tmpfile() functions described in L<POSIX> #pod using the mkstemp() from this module. #pod #pod Unlike the L<POSIX|POSIX> implementations, the directory used #pod for the temporary file is not specified in a system include #pod file (C<P_tmpdir>) but simply depends on the choice of tmpdir() #pod returned by L<File::Spec|File::Spec>. On some implementations this #pod location can be set using the C<TMPDIR> environment variable, which #pod may not be secure. #pod If this is a problem, simply use mkstemp() and specify a template. #pod #pod =over 4 #pod #pod =item B<tmpnam> #pod #pod When called in scalar context, returns the full name (including path) #pod of a temporary file (uses mktemp()). The only check is that the file does #pod not already exist, but there is no guarantee that that condition will #pod continue to apply. #pod #pod $file = tmpnam(); #pod #pod When called in list context, a filehandle to the open file and #pod a filename are returned. This is achieved by calling mkstemp() #pod after constructing a suitable template. #pod #pod ($fh, $file) = tmpnam(); #pod #pod If possible, this form should be used to prevent possible #pod race conditions. #pod #pod See L<File::Spec/tmpdir> for information on the choice of temporary #pod directory for a particular operating system. #pod #pod Will croak() if there is an error. #pod #pod Current API available since 0.05. #pod #pod =cut sub tmpnam { # Retrieve the temporary directory name my $tmpdir = _wrap_file_spec_tmpdir(); # XXX I don't know under what circumstances this occurs, -- xdg 2016-04-02 croak "Error temporary directory is not writable" if $tmpdir eq ''; # Use a ten character template and append to tmpdir my $template = File::Spec->catfile($tmpdir, TEMPXXX); if (wantarray() ) { return mkstemp($template); } else { return mktemp($template); } } #pod =item B<tmpfile> #pod #pod Returns the filehandle of a temporary file. #pod #pod $fh = tmpfile(); #pod #pod The file is removed when the filehandle is closed or when the program #pod exits. No access to the filename is provided. #pod #pod If the temporary file can not be created undef is returned. #pod Currently this command will probably not work when the temporary #pod directory is on an NFS file system. #pod #pod Will croak() if there is an error. #pod #pod Available since 0.05. #pod #pod Returning undef if unable to create file added in 0.12. #pod #pod =cut sub tmpfile { # Simply call tmpnam() in a list context my ($fh, $file) = tmpnam(); # Make sure file is removed when filehandle is closed # This will fail on NFS unlink0($fh, $file) or return undef; return $fh; } #pod =back #pod #pod =head1 ADDITIONAL FUNCTIONS #pod #pod These functions are provided for backwards compatibility #pod with common tempfile generation C library functions. #pod #pod They are not exported and must be addressed using the full package #pod name. #pod #pod =over 4 #pod #pod =item B<tempnam> #pod #pod Return the name of a temporary file in the specified directory #pod using a prefix. The file is guaranteed not to exist at the time #pod the function was called, but such guarantees are good for one #pod clock tick only. Always use the proper form of C<sysopen> #pod with C<O_CREAT | O_EXCL> if you must open such a filename. #pod #pod $filename = File::Temp::tempnam( $dir, $prefix ); #pod #pod Equivalent to running mktemp() with $dir/$prefixXXXXXXXX #pod (using unix file convention as an example) #pod #pod Because this function uses mktemp(), it can suffer from race conditions. #pod #pod Will croak() if there is an error. #pod #pod Current API available since 0.05. #pod #pod =cut sub tempnam { croak 'Usage tempnam($dir, $prefix)' unless scalar(@_) == 2; my ($dir, $prefix) = @_; # Add a string to the prefix $prefix .= 'XXXXXXXX'; # Concatenate the directory to the file my $template = File::Spec->catfile($dir, $prefix); return mktemp($template); } #pod =back #pod #pod =head1 UTILITY FUNCTIONS #pod #pod Useful functions for dealing with the filehandle and filename. #pod #pod =over 4 #pod #pod =item B<unlink0> #pod #pod Given an open filehandle and the associated filename, make a safe #pod unlink. This is achieved by first checking that the filename and #pod filehandle initially point to the same file and that the number of #pod links to the file is 1 (all fields returned by stat() are compared). #pod Then the filename is unlinked and the filehandle checked once again to #pod verify that the number of links on that file is now 0. This is the #pod closest you can come to making sure that the filename unlinked was the #pod same as the file whose descriptor you hold. #pod #pod unlink0($fh, $path) #pod or die "Error unlinking file $path safely"; #pod #pod Returns false on error but croaks() if there is a security #pod anomaly. The filehandle is not closed since on some occasions this is #pod not required. #pod #pod On some platforms, for example Windows NT, it is not possible to #pod unlink an open file (the file must be closed first). On those #pod platforms, the actual unlinking is deferred until the program ends and #pod good status is returned. A check is still performed to make sure that #pod the filehandle and filename are pointing to the same thing (but not at #pod the time the end block is executed since the deferred removal may not #pod have access to the filehandle). #pod #pod Additionally, on Windows NT not all the fields returned by stat() can #pod be compared. For example, the C<dev> and C<rdev> fields seem to be #pod different. Also, it seems that the size of the file returned by stat() #pod does not always agree, with C<stat(FH)> being more accurate than #pod C<stat(filename)>, presumably because of caching issues even when #pod using autoflush (this is usually overcome by waiting a while after #pod writing to the tempfile before attempting to C<unlink0> it). #pod #pod Finally, on NFS file systems the link count of the file handle does #pod not always go to zero immediately after unlinking. Currently, this #pod command is expected to fail on NFS disks. #pod #pod This function is disabled if the global variable $KEEP_ALL is true #pod and an unlink on open file is supported. If the unlink is to be deferred #pod to the END block, the file is still registered for removal. #pod #pod This function should not be called if you are using the object oriented #pod interface since the it will interfere with the object destructor deleting #pod the file. #pod #pod Available Since 0.05. #pod #pod If can not unlink open file, defer removal until later available since 0.06. #pod #pod =cut sub unlink0 { croak 'Usage: unlink0(filehandle, filename)' unless scalar(@_) == 2; # Read args my ($fh, $path) = @_; cmpstat($fh, $path) or return 0; # attempt remove the file (does not work on some platforms) if (_can_unlink_opened_file()) { # return early (Without unlink) if we have been instructed to retain files. return 1 if $KEEP_ALL; # XXX: do *not* call this on a directory; possible race # resulting in recursive removal croak "unlink0: $path has become a directory!" if -d $path; unlink($path) or return 0; # Stat the filehandle my @fh = stat $fh; print "Link count = $fh[3] \n" if $DEBUG; # Make sure that the link count is zero # - Cygwin provides deferred unlinking, however, # on Win9x the link count remains 1 # On NFS the link count may still be 1 but we can't know that # we are on NFS. Since we can't be sure, we'll defer it return 1 if $fh[3] == 0 || $^O eq 'cygwin'; } # fall-through if we can't unlink now _deferred_unlink($fh, $path, 0); return 1; } #pod =item B<cmpstat> #pod #pod Compare C<stat> of filehandle with C<stat> of provided filename. This #pod can be used to check that the filename and filehandle initially point #pod to the same file and that the number of links to the file is 1 (all #pod fields returned by stat() are compared). #pod #pod cmpstat($fh, $path) #pod or die "Error comparing handle with file"; #pod #pod Returns false if the stat information differs or if the link count is #pod greater than 1. Calls croak if there is a security anomaly. #pod #pod On certain platforms, for example Windows, not all the fields returned by stat() #pod can be compared. For example, the C<dev> and C<rdev> fields seem to be #pod different in Windows. Also, it seems that the size of the file #pod returned by stat() does not always agree, with C<stat(FH)> being more #pod accurate than C<stat(filename)>, presumably because of caching issues #pod even when using autoflush (this is usually overcome by waiting a while #pod after writing to the tempfile before attempting to C<unlink0> it). #pod #pod Not exported by default. #pod #pod Current API available since 0.14. #pod #pod =cut sub cmpstat { croak 'Usage: cmpstat(filehandle, filename)' unless scalar(@_) == 2; # Read args my ($fh, $path) = @_; warn "Comparing stat\n" if $DEBUG; # Stat the filehandle - which may be closed if someone has manually # closed the file. Can not turn off warnings without using $^W # unless we upgrade to 5.006 minimum requirement my @fh; { local ($^W) = 0; @fh = stat $fh; } return unless @fh; if ($fh[3] > 1 && $^W) { carp "unlink0: fstat found too many links; SB=@fh" if $^W; } # Stat the path my @path = stat $path; unless (@path) { carp "unlink0: $path is gone already" if $^W; return; } # this is no longer a file, but may be a directory, or worse unless (-f $path) { confess "panic: $path is no longer a file: SB=@fh"; } # Do comparison of each member of the array # On WinNT dev and rdev seem to be different # depending on whether it is a file or a handle. # Cannot simply compare all members of the stat return # Select the ones we can use my @okstat = (0..$#fh); # Use all by default if ($^O eq 'MSWin32') { @okstat = (1,2,3,4,5,7,8,9,10); } elsif ($^O eq 'os2') { @okstat = (0, 2..$#fh); } elsif ($^O eq 'VMS') { # device and file ID are sufficient @okstat = (0, 1); } elsif ($^O eq 'dos') { @okstat = (0,2..7,11..$#fh); } elsif ($^O eq 'mpeix') { @okstat = (0..4,8..10); } # Now compare each entry explicitly by number for (@okstat) { print "Comparing: $_ : $fh[$_] and $path[$_]\n" if $DEBUG; # Use eq rather than == since rdev, blksize, and blocks (6, 11, # and 12) will be '' on platforms that do not support them. This # is fine since we are only comparing integers. unless ($fh[$_] eq $path[$_]) { warn "Did not match $_ element of stat\n" if $DEBUG; return 0; } } return 1; } #pod =item B<unlink1> #pod #pod Similar to C<unlink0> except after file comparison using cmpstat, the #pod filehandle is closed prior to attempting to unlink the file. This #pod allows the file to be removed without using an END block, but does #pod mean that the post-unlink comparison of the filehandle state provided #pod by C<unlink0> is not available. #pod #pod unlink1($fh, $path) #pod or die "Error closing and unlinking file"; #pod #pod Usually called from the object destructor when using the OO interface. #pod #pod Not exported by default. #pod #pod This function is disabled if the global variable $KEEP_ALL is true. #pod #pod Can call croak() if there is a security anomaly during the stat() #pod comparison. #pod #pod Current API available since 0.14. #pod #pod =cut sub unlink1 { croak 'Usage: unlink1(filehandle, filename)' unless scalar(@_) == 2; # Read args my ($fh, $path) = @_; cmpstat($fh, $path) or return 0; # Close the file close( $fh ) or return 0; # Make sure the file is writable (for windows) _force_writable( $path ); # return early (without unlink) if we have been instructed to retain files. return 1 if $KEEP_ALL; # remove the file return unlink($path); } #pod =item B<cleanup> #pod #pod Calling this function will cause any temp files or temp directories #pod that are registered for removal to be removed. This happens automatically #pod when the process exits but can be triggered manually if the caller is sure #pod that none of the temp files are required. This method can be registered as #pod an Apache callback. #pod #pod Note that if a temp directory is your current directory, it cannot be #pod removed. C<chdir()> out of the directory first before calling #pod C<cleanup()>. (For the cleanup at program exit when the CLEANUP flag #pod is set, this happens automatically.) #pod #pod On OSes where temp files are automatically removed when the temp file #pod is closed, calling this function will have no effect other than to remove #pod temporary directories (which may include temporary files). #pod #pod File::Temp::cleanup(); #pod #pod Not exported by default. #pod #pod Current API available since 0.15. #pod #pod =back #pod #pod =head1 PACKAGE VARIABLES #pod #pod These functions control the global state of the package. #pod #pod =over 4 #pod #pod =item B<safe_level> #pod #pod Controls the lengths to which the module will go to check the safety of the #pod temporary file or directory before proceeding. #pod Options are: #pod #pod =over 8 #pod #pod =item STANDARD #pod #pod Do the basic security measures to ensure the directory exists and is #pod writable, that temporary files are opened only if they do not already #pod exist, and that possible race conditions are avoided. Finally the #pod L<unlink0|"unlink0"> function is used to remove files safely. #pod #pod =item MEDIUM #pod #pod In addition to the STANDARD security, the output directory is checked #pod to make sure that it is owned either by root or the user running the #pod program. If the directory is writable by group or by other, it is then #pod checked to make sure that the sticky bit is set. #pod #pod Will not work on platforms that do not support the C<-k> test #pod for sticky bit. #pod #pod =item HIGH #pod #pod In addition to the MEDIUM security checks, also check for the #pod possibility of ``chown() giveaway'' using the L<POSIX|POSIX> #pod sysconf() function. If this is a possibility, each directory in the #pod path is checked in turn for safeness, recursively walking back to the #pod root directory. #pod #pod For platforms that do not support the L<POSIX|POSIX> #pod C<_PC_CHOWN_RESTRICTED> symbol (for example, Windows NT) it is #pod assumed that ``chown() giveaway'' is possible and the recursive test #pod is performed. #pod #pod =back #pod #pod The level can be changed as follows: #pod #pod File::Temp->safe_level( File::Temp::HIGH ); #pod #pod The level constants are not exported by the module. #pod #pod Currently, you must be running at least perl v5.6.0 in order to #pod run with MEDIUM or HIGH security. This is simply because the #pod safety tests use functions from L<Fcntl|Fcntl> that are not #pod available in older versions of perl. The problem is that the version #pod number for Fcntl is the same in perl 5.6.0 and in 5.005_03 even though #pod they are different versions. #pod #pod On systems that do not support the HIGH or MEDIUM safety levels #pod (for example Win NT or OS/2) any attempt to change the level will #pod be ignored. The decision to ignore rather than raise an exception #pod allows portable programs to be written with high security in mind #pod for the systems that can support this without those programs failing #pod on systems where the extra tests are irrelevant. #pod #pod If you really need to see whether the change has been accepted #pod simply examine the return value of C<safe_level>. #pod #pod $newlevel = File::Temp->safe_level( File::Temp::HIGH ); #pod die "Could not change to high security" #pod if $newlevel != File::Temp::HIGH; #pod #pod Available since 0.05. #pod #pod =cut { # protect from using the variable itself my $LEVEL = STANDARD; sub safe_level { my $self = shift; if (@_) { my $level = shift; if (($level != STANDARD) && ($level != MEDIUM) && ($level != HIGH)) { carp "safe_level: Specified level ($level) not STANDARD, MEDIUM or HIGH - ignoring\n" if $^W; } else { # Don't allow this on perl 5.005 or earlier if ($] < 5.006 && $level != STANDARD) { # Cant do MEDIUM or HIGH checks croak "Currently requires perl 5.006 or newer to do the safe checks"; } # Check that we are allowed to change level # Silently ignore if we can not. $LEVEL = $level if _can_do_level($level); } } return $LEVEL; } } #pod =item TopSystemUID #pod #pod This is the highest UID on the current system that refers to a root #pod UID. This is used to make sure that the temporary directory is #pod owned by a system UID (C<root>, C<bin>, C<sys> etc) rather than #pod simply by root. #pod #pod This is required since on many unix systems C</tmp> is not owned #pod by root. #pod #pod Default is to assume that any UID less than or equal to 10 is a root #pod UID. #pod #pod File::Temp->top_system_uid(10); #pod my $topid = File::Temp->top_system_uid; #pod #pod This value can be adjusted to reduce security checking if required. #pod The value is only relevant when C<safe_level> is set to MEDIUM or higher. #pod #pod Available since 0.05. #pod #pod =cut { my $TopSystemUID = 10; $TopSystemUID = 197108 if $^O eq 'interix'; # "Administrator" sub top_system_uid { my $self = shift; if (@_) { my $newuid = shift; croak "top_system_uid: UIDs should be numeric" unless $newuid =~ /^\d+$/s; $TopSystemUID = $newuid; } return $TopSystemUID; } } #pod =item B<$KEEP_ALL> #pod #pod Controls whether temporary files and directories should be retained #pod regardless of any instructions in the program to remove them #pod automatically. This is useful for debugging but should not be used in #pod production code. #pod #pod $File::Temp::KEEP_ALL = 1; #pod #pod Default is for files to be removed as requested by the caller. #pod #pod In some cases, files will only be retained if this variable is true #pod when the file is created. This means that you can not create a temporary #pod file, set this variable and expect the temp file to still be around #pod when the program exits. #pod #pod =item B<$DEBUG> #pod #pod Controls whether debugging messages should be enabled. #pod #pod $File::Temp::DEBUG = 1; #pod #pod Default is for debugging mode to be disabled. #pod #pod Available since 0.15. #pod #pod =back #pod #pod =head1 WARNING #pod #pod For maximum security, endeavour always to avoid ever looking at, #pod touching, or even imputing the existence of the filename. You do not #pod know that that filename is connected to the same file as the handle #pod you have, and attempts to check this can only trigger more race #pod conditions. It's far more secure to use the filehandle alone and #pod dispense with the filename altogether. #pod #pod If you need to pass the handle to something that expects a filename #pod then on a unix system you can use C<"/dev/fd/" . fileno($fh)> for #pod arbitrary programs. Perl code that uses the 2-argument version of #pod C<< open >> can be passed C<< "+<=&" . fileno($fh) >>. Otherwise you #pod will need to pass the filename. You will have to clear the #pod close-on-exec bit on that file descriptor before passing it to another #pod process. #pod #pod use Fcntl qw/F_SETFD F_GETFD/; #pod fcntl($tmpfh, F_SETFD, 0) #pod or die "Can't clear close-on-exec flag on temp fh: $!\n"; #pod #pod =head2 Temporary files and NFS #pod #pod Some problems are associated with using temporary files that reside #pod on NFS file systems and it is recommended that a local filesystem #pod is used whenever possible. Some of the security tests will most probably #pod fail when the temp file is not local. Additionally, be aware that #pod the performance of I/O operations over NFS will not be as good as for #pod a local disk. #pod #pod =head2 Forking #pod #pod In some cases files created by File::Temp are removed from within an #pod END block. Since END blocks are triggered when a child process exits #pod (unless C<POSIX::_exit()> is used by the child) File::Temp takes care #pod to only remove those temp files created by a particular process ID. This #pod means that a child will not attempt to remove temp files created by the #pod parent process. #pod #pod If you are forking many processes in parallel that are all creating #pod temporary files, you may need to reset the random number seed using #pod srand(EXPR) in each child else all the children will attempt to walk #pod through the same set of random file names and may well cause #pod themselves to give up if they exceed the number of retry attempts. #pod #pod =head2 Directory removal #pod #pod Note that if you have chdir'ed into the temporary directory and it is #pod subsequently cleaned up (either in the END block or as part of object #pod destruction), then you will get a warning from File::Path::rmtree(). #pod #pod =head2 Taint mode #pod #pod If you need to run code under taint mode, updating to the latest #pod L<File::Spec> is highly recommended. On Windows, if the directory #pod given by L<File::Spec::tmpdir> isn't writable, File::Temp will attempt #pod to fallback to the user's local application data directory or croak #pod with an error. #pod #pod =head2 BINMODE #pod #pod The file returned by File::Temp will have been opened in binary mode #pod if such a mode is available. If that is not correct, use the C<binmode()> #pod function to change the mode of the filehandle. #pod #pod Note that you can modify the encoding of a file opened by File::Temp #pod also by using C<binmode()>. #pod #pod =head1 HISTORY #pod #pod Originally began life in May 1999 as an XS interface to the system #pod mkstemp() function. In March 2000, the OpenBSD mkstemp() code was #pod translated to Perl for total control of the code's #pod security checking, to ensure the presence of the function regardless of #pod operating system and to help with portability. The module was shipped #pod as a standard part of perl from v5.6.1. #pod #pod Thanks to Tom Christiansen for suggesting that this module #pod should be written and providing ideas for code improvements and #pod security enhancements. #pod #pod =head1 SEE ALSO #pod #pod L<POSIX/tmpnam>, L<POSIX/tmpfile>, L<File::Spec>, L<File::Path> #pod #pod See L<IO::File> and L<File::MkTemp>, L<Apache::TempFile> for #pod different implementations of temporary file handling. #pod #pod See L<File::Tempdir> for an alternative object-oriented wrapper for #pod the C<tempdir> function. #pod #pod =cut package ## hide from PAUSE File::Temp::Dir; our $VERSION = '0.2311'; use File::Path qw/ rmtree /; use strict; use overload '""' => "STRINGIFY", '0+' => \&File::Temp::NUMIFY, fallback => 1; # private class specifically to support tempdir objects # created by File::Temp->newdir # ostensibly the same method interface as File::Temp but without # inheriting all the IO::Seekable methods and other cruft # Read-only - returns the name of the temp directory sub dirname { my $self = shift; return $self->{DIRNAME}; } sub STRINGIFY { my $self = shift; return $self->dirname; } sub unlink_on_destroy { my $self = shift; if (@_) { $self->{CLEANUP} = shift; } return $self->{CLEANUP}; } sub DESTROY { my $self = shift; local($., $@, $!, $^E, $?); if ($self->unlink_on_destroy && $$ == $self->{LAUNCHPID} && !$File::Temp::KEEP_ALL) { if (-d $self->{REALNAME}) { # Some versions of rmtree will abort if you attempt to remove # the directory you are sitting in. We protect that and turn it # into a warning. We do this because this occurs during object # destruction and so can not be caught by the user. eval { rmtree($self->{REALNAME}, $File::Temp::DEBUG, 0); }; warn $@ if ($@ && $^W); } } } 1; # vim: ts=2 sts=2 sw=2 et: __END__ =pod =encoding UTF-8 =head1 NAME File::Temp - return name and handle of a temporary file safely =head1 VERSION version 0.2311 =head1 SYNOPSIS use File::Temp qw/ tempfile tempdir /; $fh = tempfile(); ($fh, $filename) = tempfile(); ($fh, $filename) = tempfile( $template, DIR => $dir); ($fh, $filename) = tempfile( $template, SUFFIX => '.dat'); ($fh, $filename) = tempfile( $template, TMPDIR => 1 ); binmode( $fh, ":utf8" ); $dir = tempdir( CLEANUP => 1 ); ($fh, $filename) = tempfile( DIR => $dir ); Object interface: require File::Temp; use File::Temp (); use File::Temp qw/ :seekable /; $fh = File::Temp->new(); $fname = $fh->filename; $fh = File::Temp->new(TEMPLATE => $template); $fname = $fh->filename; $tmp = File::Temp->new( UNLINK => 0, SUFFIX => '.dat' ); print $tmp "Some data\n"; print "Filename is $tmp\n"; $tmp->seek( 0, SEEK_END ); $dir = File::Temp->newdir(); # CLEANUP => 1 by default The following interfaces are provided for compatibility with existing APIs. They should not be used in new code. MkTemp family: use File::Temp qw/ :mktemp /; ($fh, $file) = mkstemp( "tmpfileXXXXX" ); ($fh, $file) = mkstemps( "tmpfileXXXXXX", $suffix); $tmpdir = mkdtemp( $template ); $unopened_file = mktemp( $template ); POSIX functions: use File::Temp qw/ :POSIX /; $file = tmpnam(); $fh = tmpfile(); ($fh, $file) = tmpnam(); Compatibility functions: $unopened_file = File::Temp::tempnam( $dir, $pfx ); =head1 DESCRIPTION C<File::Temp> can be used to create and open temporary files in a safe way. There is both a function interface and an object-oriented interface. The File::Temp constructor or the tempfile() function can be used to return the name and the open filehandle of a temporary file. The tempdir() function can be used to create a temporary directory. The security aspect of temporary file creation is emphasized such that a filehandle and filename are returned together. This helps guarantee that a race condition can not occur where the temporary file is created by another process between checking for the existence of the file and its opening. Additional security levels are provided to check, for example, that the sticky bit is set on world writable directories. See L<"safe_level"> for more information. For compatibility with popular C library functions, Perl implementations of the mkstemp() family of functions are provided. These are, mkstemp(), mkstemps(), mkdtemp() and mktemp(). Additionally, implementations of the standard L<POSIX|POSIX> tmpnam() and tmpfile() functions are provided if required. Implementations of mktemp(), tmpnam(), and tempnam() are provided, but should be used with caution since they return only a filename that was valid when function was called, so cannot guarantee that the file will not exist by the time the caller opens the filename. Filehandles returned by these functions support the seekable methods. =begin :__INTERNALS =head1 PORTABILITY This section is at the top in order to provide easier access to porters. It is not expected to be rendered by a standard pod formatting tool. Please skip straight to the SYNOPSIS section if you are not trying to port this module to a new platform. This module is designed to be portable across operating systems and it currently supports Unix, VMS, DOS, OS/2, Windows and Mac OS (Classic). When porting to a new OS there are generally three main issues that have to be solved: =over 4 =item * Can the OS unlink an open file? If it can not then the C<_can_unlink_opened_file> method should be modified. =item * Are the return values from C<stat> reliable? By default all the return values from C<stat> are compared when unlinking a temporary file using the filename and the handle. Operating systems other than unix do not always have valid entries in all fields. If utility function C<File::Temp::unlink0> fails then the C<stat> comparison should be modified accordingly. =item * Security. Systems that can not support a test for the sticky bit on a directory can not use the MEDIUM and HIGH security tests. The C<_can_do_level> method should be modified accordingly. =back =end :__INTERNALS =head1 OBJECT-ORIENTED INTERFACE This is the primary interface for interacting with C<File::Temp>. Using the OO interface a temporary file can be created when the object is constructed and the file can be removed when the object is no longer required. Note that there is no method to obtain the filehandle from the C<File::Temp> object. The object itself acts as a filehandle. The object isa C<IO::Handle> and isa C<IO::Seekable> so all those methods are available. Also, the object is configured such that it stringifies to the name of the temporary file and so can be compared to a filename directly. It numifies to the C<refaddr> the same as other handles and so can be compared to other handles with C<==>. $fh eq $filename # as a string $fh != \*STDOUT # as a number Available since 0.14. =over 4 =item B<new> Create a temporary file object. my $tmp = File::Temp->new(); by default the object is constructed as if C<tempfile> was called without options, but with the additional behaviour that the temporary file is removed by the object destructor if UNLINK is set to true (the default). Supported arguments are the same as for C<tempfile>: UNLINK (defaulting to true), DIR, EXLOCK, PERMS and SUFFIX. Additionally, the filename template is specified using the TEMPLATE option. The OPEN option is not supported (the file is always opened). $tmp = File::Temp->new( TEMPLATE => 'tempXXXXX', DIR => 'mydir', SUFFIX => '.dat'); Arguments are case insensitive. Can call croak() if an error occurs. Available since 0.14. TEMPLATE available since 0.23 =item B<newdir> Create a temporary directory using an object oriented interface. $dir = File::Temp->newdir(); By default the directory is deleted when the object goes out of scope. Supports the same options as the C<tempdir> function. Note that directories created with this method default to CLEANUP => 1. $dir = File::Temp->newdir( $template, %options ); A template may be specified either with a leading template or with a TEMPLATE argument. Available since 0.19. TEMPLATE available since 0.23. =item B<filename> Return the name of the temporary file associated with this object (if the object was created using the "new" constructor). $filename = $tmp->filename; This method is called automatically when the object is used as a string. Current API available since 0.14 =item B<dirname> Return the name of the temporary directory associated with this object (if the object was created using the "newdir" constructor). $dirname = $tmpdir->dirname; This method is called automatically when the object is used in string context. =item B<unlink_on_destroy> Control whether the file is unlinked when the object goes out of scope. The file is removed if this value is true and $KEEP_ALL is not. $fh->unlink_on_destroy( 1 ); Default is for the file to be removed. Current API available since 0.15 =item B<DESTROY> When the object goes out of scope, the destructor is called. This destructor will attempt to unlink the file (using L<unlink1|"unlink1">) if the constructor was called with UNLINK set to 1 (the default state if UNLINK is not specified). No error is given if the unlink fails. If the object has been passed to a child process during a fork, the file will be deleted when the object goes out of scope in the parent. For a temporary directory object the directory will be removed unless the CLEANUP argument was used in the constructor (and set to false) or C<unlink_on_destroy> was modified after creation. Note that if a temp directory is your current directory, it cannot be removed - a warning will be given in this case. C<chdir()> out of the directory before letting the object go out of scope. If the global variable $KEEP_ALL is true, the file or directory will not be removed. =back =head1 FUNCTIONS This section describes the recommended interface for generating temporary files and directories. =over 4 =item B<tempfile> This is the basic function to generate temporary files. The behaviour of the file can be changed using various options: $fh = tempfile(); ($fh, $filename) = tempfile(); Create a temporary file in the directory specified for temporary files, as specified by the tmpdir() function in L<File::Spec>. ($fh, $filename) = tempfile($template); Create a temporary file in the current directory using the supplied template. Trailing `X' characters are replaced with random letters to generate the filename. At least four `X' characters must be present at the end of the template. ($fh, $filename) = tempfile($template, SUFFIX => $suffix) Same as previously, except that a suffix is added to the template after the `X' translation. Useful for ensuring that a temporary filename has a particular extension when needed by other applications. But see the WARNING at the end. ($fh, $filename) = tempfile($template, DIR => $dir); Translates the template as before except that a directory name is specified. ($fh, $filename) = tempfile($template, TMPDIR => 1); Equivalent to specifying a DIR of "File::Spec->tmpdir", writing the file into the same temporary directory as would be used if no template was specified at all. ($fh, $filename) = tempfile($template, UNLINK => 1); Return the filename and filehandle as before except that the file is automatically removed when the program exits (dependent on $KEEP_ALL). Default is for the file to be removed if a file handle is requested and to be kept if the filename is requested. In a scalar context (where no filename is returned) the file is always deleted either (depending on the operating system) on exit or when it is closed (unless $KEEP_ALL is true when the temp file is created). Use the object-oriented interface if fine-grained control of when a file is removed is required. If the template is not specified, a template is always automatically generated. This temporary file is placed in tmpdir() (L<File::Spec>) unless a directory is specified explicitly with the DIR option. $fh = tempfile( DIR => $dir ); If called in scalar context, only the filehandle is returned and the file will automatically be deleted when closed on operating systems that support this (see the description of tmpfile() elsewhere in this document). This is the preferred mode of operation, as if you only have a filehandle, you can never create a race condition by fumbling with the filename. On systems that can not unlink an open file or can not mark a file as temporary when it is opened (for example, Windows NT uses the C<O_TEMPORARY> flag) the file is marked for deletion when the program ends (equivalent to setting UNLINK to 1). The C<UNLINK> flag is ignored if present. (undef, $filename) = tempfile($template, OPEN => 0); This will return the filename based on the template but will not open this file. Cannot be used in conjunction with UNLINK set to true. Default is to always open the file to protect from possible race conditions. A warning is issued if warnings are turned on. Consider using the tmpnam() and mktemp() functions described elsewhere in this document if opening the file is not required. To open the temporary filehandle with O_EXLOCK (open with exclusive file lock) use C<< EXLOCK=>1 >>. This is supported only by some operating systems (most notably BSD derived systems). By default EXLOCK will be false. Former C<File::Temp> versions set EXLOCK to true, so to be sure to get an unlocked filehandle also with older versions, explicitly set C<< EXLOCK=>0 >>. ($fh, $filename) = tempfile($template, EXLOCK => 1); By default, the temp file is created with 0600 file permissions. Use C<PERMS> to change this: ($fh, $filename) = tempfile($template, PERMS => 0666); Options can be combined as required. Will croak() if there is an error. Available since 0.05. UNLINK flag available since 0.10. TMPDIR flag available since 0.19. EXLOCK flag available since 0.19. PERMS flag available since 0.2310. =item B<tempdir> This is the recommended interface for creation of temporary directories. By default the directory will not be removed on exit (that is, it won't be temporary; this behaviour can not be changed because of issues with backwards compatibility). To enable removal either use the CLEANUP option which will trigger removal on program exit, or consider using the "newdir" method in the object interface which will allow the directory to be cleaned up when the object goes out of scope. The behaviour of the function depends on the arguments: $tempdir = tempdir(); Create a directory in tmpdir() (see L<File::Spec|File::Spec>). $tempdir = tempdir( $template ); Create a directory from the supplied template. This template is similar to that described for tempfile(). `X' characters at the end of the template are replaced with random letters to construct the directory name. At least four `X' characters must be in the template. $tempdir = tempdir ( DIR => $dir ); Specifies the directory to use for the temporary directory. The temporary directory name is derived from an internal template. $tempdir = tempdir ( $template, DIR => $dir ); Prepend the supplied directory name to the template. The template should not include parent directory specifications itself. Any parent directory specifications are removed from the template before prepending the supplied directory. $tempdir = tempdir ( $template, TMPDIR => 1 ); Using the supplied template, create the temporary directory in a standard location for temporary files. Equivalent to doing $tempdir = tempdir ( $template, DIR => File::Spec->tmpdir); but shorter. Parent directory specifications are stripped from the template itself. The C<TMPDIR> option is ignored if C<DIR> is set explicitly. Additionally, C<TMPDIR> is implied if neither a template nor a directory are supplied. $tempdir = tempdir( $template, CLEANUP => 1); Create a temporary directory using the supplied template, but attempt to remove it (and all files inside it) when the program exits. Note that an attempt will be made to remove all files from the directory even if they were not created by this module (otherwise why ask to clean it up?). The directory removal is made with the rmtree() function from the L<File::Path|File::Path> module. Of course, if the template is not specified, the temporary directory will be created in tmpdir() and will also be removed at program exit. Will croak() if there is an error. Current API available since 0.05. =back =head1 MKTEMP FUNCTIONS The following functions are Perl implementations of the mktemp() family of temp file generation system calls. =over 4 =item B<mkstemp> Given a template, returns a filehandle to the temporary file and the name of the file. ($fh, $name) = mkstemp( $template ); In scalar context, just the filehandle is returned. The template may be any filename with some number of X's appended to it, for example F</tmp/temp.XXXX>. The trailing X's are replaced with unique alphanumeric combinations. Will croak() if there is an error. Current API available since 0.05. =item B<mkstemps> Similar to mkstemp(), except that an extra argument can be supplied with a suffix to be appended to the template. ($fh, $name) = mkstemps( $template, $suffix ); For example a template of C<testXXXXXX> and suffix of C<.dat> would generate a file similar to F<testhGji_w.dat>. Returns just the filehandle alone when called in scalar context. Will croak() if there is an error. Current API available since 0.05. =item B<mkdtemp> Create a directory from a template. The template must end in X's that are replaced by the routine. $tmpdir_name = mkdtemp($template); Returns the name of the temporary directory created. Directory must be removed by the caller. Will croak() if there is an error. Current API available since 0.05. =item B<mktemp> Returns a valid temporary filename but does not guarantee that the file will not be opened by someone else. $unopened_file = mktemp($template); Template is the same as that required by mkstemp(). Will croak() if there is an error. Current API available since 0.05. =back =head1 POSIX FUNCTIONS This section describes the re-implementation of the tmpnam() and tmpfile() functions described in L<POSIX> using the mkstemp() from this module. Unlike the L<POSIX|POSIX> implementations, the directory used for the temporary file is not specified in a system include file (C<P_tmpdir>) but simply depends on the choice of tmpdir() returned by L<File::Spec|File::Spec>. On some implementations this location can be set using the C<TMPDIR> environment variable, which may not be secure. If this is a problem, simply use mkstemp() and specify a template. =over 4 =item B<tmpnam> When called in scalar context, returns the full name (including path) of a temporary file (uses mktemp()). The only check is that the file does not already exist, but there is no guarantee that that condition will continue to apply. $file = tmpnam(); When called in list context, a filehandle to the open file and a filename are returned. This is achieved by calling mkstemp() after constructing a suitable template. ($fh, $file) = tmpnam(); If possible, this form should be used to prevent possible race conditions. See L<File::Spec/tmpdir> for information on the choice of temporary directory for a particular operating system. Will croak() if there is an error. Current API available since 0.05. =item B<tmpfile> Returns the filehandle of a temporary file. $fh = tmpfile(); The file is removed when the filehandle is closed or when the program exits. No access to the filename is provided. If the temporary file can not be created undef is returned. Currently this command will probably not work when the temporary directory is on an NFS file system. Will croak() if there is an error. Available since 0.05. Returning undef if unable to create file added in 0.12. =back =head1 ADDITIONAL FUNCTIONS These functions are provided for backwards compatibility with common tempfile generation C library functions. They are not exported and must be addressed using the full package name. =over 4 =item B<tempnam> Return the name of a temporary file in the specified directory using a prefix. The file is guaranteed not to exist at the time the function was called, but such guarantees are good for one clock tick only. Always use the proper form of C<sysopen> with C<O_CREAT | O_EXCL> if you must open such a filename. $filename = File::Temp::tempnam( $dir, $prefix ); Equivalent to running mktemp() with $dir/$prefixXXXXXXXX (using unix file convention as an example) Because this function uses mktemp(), it can suffer from race conditions. Will croak() if there is an error. Current API available since 0.05. =back =head1 UTILITY FUNCTIONS Useful functions for dealing with the filehandle and filename. =over 4 =item B<unlink0> Given an open filehandle and the associated filename, make a safe unlink. This is achieved by first checking that the filename and filehandle initially point to the same file and that the number of links to the file is 1 (all fields returned by stat() are compared). Then the filename is unlinked and the filehandle checked once again to verify that the number of links on that file is now 0. This is the closest you can come to making sure that the filename unlinked was the same as the file whose descriptor you hold. unlink0($fh, $path) or die "Error unlinking file $path safely"; Returns false on error but croaks() if there is a security anomaly. The filehandle is not closed since on some occasions this is not required. On some platforms, for example Windows NT, it is not possible to unlink an open file (the file must be closed first). On those platforms, the actual unlinking is deferred until the program ends and good status is returned. A check is still performed to make sure that the filehandle and filename are pointing to the same thing (but not at the time the end block is executed since the deferred removal may not have access to the filehandle). Additionally, on Windows NT not all the fields returned by stat() can be compared. For example, the C<dev> and C<rdev> fields seem to be different. Also, it seems that the size of the file returned by stat() does not always agree, with C<stat(FH)> being more accurate than C<stat(filename)>, presumably because of caching issues even when using autoflush (this is usually overcome by waiting a while after writing to the tempfile before attempting to C<unlink0> it). Finally, on NFS file systems the link count of the file handle does not always go to zero immediately after unlinking. Currently, this command is expected to fail on NFS disks. This function is disabled if the global variable $KEEP_ALL is true and an unlink on open file is supported. If the unlink is to be deferred to the END block, the file is still registered for removal. This function should not be called if you are using the object oriented interface since the it will interfere with the object destructor deleting the file. Available Since 0.05. If can not unlink open file, defer removal until later available since 0.06. =item B<cmpstat> Compare C<stat> of filehandle with C<stat> of provided filename. This can be used to check that the filename and filehandle initially point to the same file and that the number of links to the file is 1 (all fields returned by stat() are compared). cmpstat($fh, $path) or die "Error comparing handle with file"; Returns false if the stat information differs or if the link count is greater than 1. Calls croak if there is a security anomaly. On certain platforms, for example Windows, not all the fields returned by stat() can be compared. For example, the C<dev> and C<rdev> fields seem to be different in Windows. Also, it seems that the size of the file returned by stat() does not always agree, with C<stat(FH)> being more accurate than C<stat(filename)>, presumably because of caching issues even when using autoflush (this is usually overcome by waiting a while after writing to the tempfile before attempting to C<unlink0> it). Not exported by default. Current API available since 0.14. =item B<unlink1> Similar to C<unlink0> except after file comparison using cmpstat, the filehandle is closed prior to attempting to unlink the file. This allows the file to be removed without using an END block, but does mean that the post-unlink comparison of the filehandle state provided by C<unlink0> is not available. unlink1($fh, $path) or die "Error closing and unlinking file"; Usually called from the object destructor when using the OO interface. Not exported by default. This function is disabled if the global variable $KEEP_ALL is true. Can call croak() if there is a security anomaly during the stat() comparison. Current API available since 0.14. =item B<cleanup> Calling this function will cause any temp files or temp directories that are registered for removal to be removed. This happens automatically when the process exits but can be triggered manually if the caller is sure that none of the temp files are required. This method can be registered as an Apache callback. Note that if a temp directory is your current directory, it cannot be removed. C<chdir()> out of the directory first before calling C<cleanup()>. (For the cleanup at program exit when the CLEANUP flag is set, this happens automatically.) On OSes where temp files are automatically removed when the temp file is closed, calling this function will have no effect other than to remove temporary directories (which may include temporary files). File::Temp::cleanup(); Not exported by default. Current API available since 0.15. =back =head1 PACKAGE VARIABLES These functions control the global state of the package. =over 4 =item B<safe_level> Controls the lengths to which the module will go to check the safety of the temporary file or directory before proceeding. Options are: =over 8 =item STANDARD Do the basic security measures to ensure the directory exists and is writable, that temporary files are opened only if they do not already exist, and that possible race conditions are avoided. Finally the L<unlink0|"unlink0"> function is used to remove files safely. =item MEDIUM In addition to the STANDARD security, the output directory is checked to make sure that it is owned either by root or the user running the program. If the directory is writable by group or by other, it is then checked to make sure that the sticky bit is set. Will not work on platforms that do not support the C<-k> test for sticky bit. =item HIGH In addition to the MEDIUM security checks, also check for the possibility of ``chown() giveaway'' using the L<POSIX|POSIX> sysconf() function. If this is a possibility, each directory in the path is checked in turn for safeness, recursively walking back to the root directory. For platforms that do not support the L<POSIX|POSIX> C<_PC_CHOWN_RESTRICTED> symbol (for example, Windows NT) it is assumed that ``chown() giveaway'' is possible and the recursive test is performed. =back The level can be changed as follows: File::Temp->safe_level( File::Temp::HIGH ); The level constants are not exported by the module. Currently, you must be running at least perl v5.6.0 in order to run with MEDIUM or HIGH security. This is simply because the safety tests use functions from L<Fcntl|Fcntl> that are not available in older versions of perl. The problem is that the version number for Fcntl is the same in perl 5.6.0 and in 5.005_03 even though they are different versions. On systems that do not support the HIGH or MEDIUM safety levels (for example Win NT or OS/2) any attempt to change the level will be ignored. The decision to ignore rather than raise an exception allows portable programs to be written with high security in mind for the systems that can support this without those programs failing on systems where the extra tests are irrelevant. If you really need to see whether the change has been accepted simply examine the return value of C<safe_level>. $newlevel = File::Temp->safe_level( File::Temp::HIGH ); die "Could not change to high security" if $newlevel != File::Temp::HIGH; Available since 0.05. =item TopSystemUID This is the highest UID on the current system that refers to a root UID. This is used to make sure that the temporary directory is owned by a system UID (C<root>, C<bin>, C<sys> etc) rather than simply by root. This is required since on many unix systems C</tmp> is not owned by root. Default is to assume that any UID less than or equal to 10 is a root UID. File::Temp->top_system_uid(10); my $topid = File::Temp->top_system_uid; This value can be adjusted to reduce security checking if required. The value is only relevant when C<safe_level> is set to MEDIUM or higher. Available since 0.05. =item B<$KEEP_ALL> Controls whether temporary files and directories should be retained regardless of any instructions in the program to remove them automatically. This is useful for debugging but should not be used in production code. $File::Temp::KEEP_ALL = 1; Default is for files to be removed as requested by the caller. In some cases, files will only be retained if this variable is true when the file is created. This means that you can not create a temporary file, set this variable and expect the temp file to still be around when the program exits. =item B<$DEBUG> Controls whether debugging messages should be enabled. $File::Temp::DEBUG = 1; Default is for debugging mode to be disabled. Available since 0.15. =back =head1 WARNING For maximum security, endeavour always to avoid ever looking at, touching, or even imputing the existence of the filename. You do not know that that filename is connected to the same file as the handle you have, and attempts to check this can only trigger more race conditions. It's far more secure to use the filehandle alone and dispense with the filename altogether. If you need to pass the handle to something that expects a filename then on a unix system you can use C<"/dev/fd/" . fileno($fh)> for arbitrary programs. Perl code that uses the 2-argument version of C<< open >> can be passed C<< "+<=&" . fileno($fh) >>. Otherwise you will need to pass the filename. You will have to clear the close-on-exec bit on that file descriptor before passing it to another process. use Fcntl qw/F_SETFD F_GETFD/; fcntl($tmpfh, F_SETFD, 0) or die "Can't clear close-on-exec flag on temp fh: $!\n"; =head2 Temporary files and NFS Some problems are associated with using temporary files that reside on NFS file systems and it is recommended that a local filesystem is used whenever possible. Some of the security tests will most probably fail when the temp file is not local. Additionally, be aware that the performance of I/O operations over NFS will not be as good as for a local disk. =head2 Forking In some cases files created by File::Temp are removed from within an END block. Since END blocks are triggered when a child process exits (unless C<POSIX::_exit()> is used by the child) File::Temp takes care to only remove those temp files created by a particular process ID. This means that a child will not attempt to remove temp files created by the parent process. If you are forking many processes in parallel that are all creating temporary files, you may need to reset the random number seed using srand(EXPR) in each child else all the children will attempt to walk through the same set of random file names and may well cause themselves to give up if they exceed the number of retry attempts. =head2 Directory removal Note that if you have chdir'ed into the temporary directory and it is subsequently cleaned up (either in the END block or as part of object destruction), then you will get a warning from File::Path::rmtree(). =head2 Taint mode If you need to run code under taint mode, updating to the latest L<File::Spec> is highly recommended. On Windows, if the directory given by L<File::Spec::tmpdir> isn't writable, File::Temp will attempt to fallback to the user's local application data directory or croak with an error. =head2 BINMODE The file returned by File::Temp will have been opened in binary mode if such a mode is available. If that is not correct, use the C<binmode()> function to change the mode of the filehandle. Note that you can modify the encoding of a file opened by File::Temp also by using C<binmode()>. =head1 HISTORY Originally began life in May 1999 as an XS interface to the system mkstemp() function. In March 2000, the OpenBSD mkstemp() code was translated to Perl for total control of the code's security checking, to ensure the presence of the function regardless of operating system and to help with portability. The module was shipped as a standard part of perl from v5.6.1. Thanks to Tom Christiansen for suggesting that this module should be written and providing ideas for code improvements and security enhancements. =head1 SEE ALSO L<POSIX/tmpnam>, L<POSIX/tmpfile>, L<File::Spec>, L<File::Path> See L<IO::File> and L<File::MkTemp>, L<Apache::TempFile> for different implementations of temporary file handling. See L<File::Tempdir> for an alternative object-oriented wrapper for the C<tempdir> function. =for Pod::Coverage STRINGIFY NUMIFY top_system_uid =head1 SUPPORT Bugs may be submitted through L<the RT bug tracker|https://rt.cpan.org/Public/Dist/Display.html?Name=File-Temp> (or L<bug-File-Temp@rt.cpan.org|mailto:bug-File-Temp@rt.cpan.org>). There is also a mailing list available for users of this distribution, at L<http://lists.perl.org/list/cpan-workers.html>. There is also an irc channel available for users of this distribution, at L<C<#toolchain> on C<irc.perl.org>|irc://irc.perl.org/#toolchain>. =head1 AUTHOR Tim Jenness <tjenness@cpan.org> =head1 CONTRIBUTORS =for stopwords Tim Jenness Karen Etheridge David Golden Slaven Rezic mohawk2 Roy Ivy III Peter Rabbitson Olivier Mengué John Acklam Gim Yee Nicolas R Brian Mowrey Dagfinn Ilmari Mannsåker Steinbrunner Ed Avis Guillem Jover James E. Keenan Kevin Ryde Ben Tilly =over 4 =item * Tim Jenness <t.jenness@jach.hawaii.edu> =item * Karen Etheridge <ether@cpan.org> =item * David Golden <dagolden@cpan.org> =item * Slaven Rezic <srezic@cpan.org> =item * mohawk2 <mohawk2@users.noreply.github.com> =item * Roy Ivy III <rivy.dev@gmail.com> =item * Peter Rabbitson <ribasushi@cpan.org> =item * Olivier Mengué <dolmen@cpan.org> =item * Peter John Acklam <pjacklam@online.no> =item * Tim Gim Yee <tim.gim.yee@gmail.com> =item * Nicolas R <atoomic@cpan.org> =item * Brian Mowrey <brian@drlabs.org> =item * Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> =item * David Steinbrunner <dsteinbrunner@pobox.com> =item * Ed Avis <eda@linux01.wcl.local> =item * Guillem Jover <guillem@hadrons.org> =item * James E. Keenan <jkeen@verizon.net> =item * Kevin Ryde <user42@zip.com.au> =item * Ben Tilly <btilly@gmail.com> =back =head1 COPYRIGHT AND LICENSE This software is copyright (c) 2020 by Tim Jenness and the UK Particle Physics and Astronomy Research Council. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. =cut PK ! 3�w�+ �+ Basename.pmnu �[��� =head1 NAME File::Basename - Parse file paths into directory, filename and suffix. =head1 SYNOPSIS use File::Basename; ($name,$path,$suffix) = fileparse($fullname,@suffixlist); $name = fileparse($fullname,@suffixlist); $basename = basename($fullname,@suffixlist); $dirname = dirname($fullname); =head1 DESCRIPTION These routines allow you to parse file paths into their directory, filename and suffix. B<NOTE>: C<dirname()> and C<basename()> emulate the behaviours, and quirks, of the shell and C functions of the same name. See each function's documentation for details. If your concern is just parsing paths it is safer to use L<File::Spec>'s C<splitpath()> and C<splitdir()> methods. It is guaranteed that # Where $path_separator is / for Unix, \ for Windows, etc... dirname($path) . $path_separator . basename($path); is equivalent to the original path for all systems but VMS. =cut package File::Basename; # File::Basename is used during the Perl build, when the re extension may # not be available, but we only actually need it if running under tainting. BEGIN { if (${^TAINT}) { require re; re->import('taint'); } } use strict; use 5.006; use warnings; our(@ISA, @EXPORT, $VERSION, $Fileparse_fstype, $Fileparse_igncase); require Exporter; @ISA = qw(Exporter); @EXPORT = qw(fileparse fileparse_set_fstype basename dirname); $VERSION = "2.85"; fileparse_set_fstype($^O); =over 4 =item C<fileparse> X<fileparse> my($filename, $dirs, $suffix) = fileparse($path); my($filename, $dirs, $suffix) = fileparse($path, @suffixes); my $filename = fileparse($path, @suffixes); The C<fileparse()> routine divides a file path into its $dirs, $filename and (optionally) the filename $suffix. $dirs contains everything up to and including the last directory separator in the $path including the volume (if applicable). The remainder of the $path is the $filename. # On Unix returns ("baz", "/foo/bar/", "") fileparse("/foo/bar/baz"); # On Windows returns ("baz", 'C:\foo\bar\', "") fileparse('C:\foo\bar\baz'); # On Unix returns ("", "/foo/bar/baz/", "") fileparse("/foo/bar/baz/"); If @suffixes are given each element is a pattern (either a string or a C<qr//>) matched against the end of the $filename. The matching portion is removed and becomes the $suffix. # On Unix returns ("baz", "/foo/bar/", ".txt") fileparse("/foo/bar/baz.txt", qr/\.[^.]*/); If type is non-Unix (see L</fileparse_set_fstype>) then the pattern matching for suffix removal is performed case-insensitively, since those systems are not case-sensitive when opening existing files. You are guaranteed that C<$dirs . $filename . $suffix> will denote the same location as the original $path. =cut sub fileparse { my($fullname,@suffices) = @_; unless (defined $fullname) { require Carp; Carp::croak("fileparse(): need a valid pathname"); } my $orig_type = ''; my($type,$igncase) = ($Fileparse_fstype, $Fileparse_igncase); my($taint) = substr($fullname,0,0); # Is $fullname tainted? if ($type eq "VMS" and $fullname =~ m{/} ) { # We're doing Unix emulation $orig_type = $type; $type = 'Unix'; } my($dirpath, $basename); if (grep { $type eq $_ } qw(MSDOS DOS MSWin32 Epoc)) { ($dirpath,$basename) = ($fullname =~ /^((?:.*[:\\\/])?)(.*)/s); $dirpath .= '.\\' unless $dirpath =~ /[\\\/]\z/; } elsif ($type eq "OS2") { ($dirpath,$basename) = ($fullname =~ m#^((?:.*[:\\/])?)(.*)#s); $dirpath = './' unless $dirpath; # Can't be 0 $dirpath .= '/' unless $dirpath =~ m#[\\/]\z#; } elsif ($type eq "MacOS") { ($dirpath,$basename) = ($fullname =~ /^(.*:)?(.*)/s); $dirpath = ':' unless $dirpath; } elsif ($type eq "AmigaOS") { ($dirpath,$basename) = ($fullname =~ /(.*[:\/])?(.*)/s); $dirpath = './' unless $dirpath; } elsif ($type eq 'VMS' ) { ($dirpath,$basename) = ($fullname =~ /^(.*[:>\]])?(.*)/s); $dirpath ||= ''; # should always be defined } else { # Default to Unix semantics. ($dirpath,$basename) = ($fullname =~ m{^(.*/)?(.*)}s); if ($orig_type eq 'VMS' and $fullname =~ m{^(/[^/]+/000000(/|$))(.*)}) { # dev:[000000] is top of VMS tree, similar to Unix '/' # so strip it off and treat the rest as "normal" my $devspec = $1; my $remainder = $3; ($dirpath,$basename) = ($remainder =~ m{^(.*/)?(.*)}s); $dirpath ||= ''; # should always be defined $dirpath = $devspec.$dirpath; } $dirpath = './' unless $dirpath; } my $tail = ''; my $suffix = ''; if (@suffices) { foreach $suffix (@suffices) { my $pat = ($igncase ? '(?i)' : '') . "($suffix)\$"; if ($basename =~ s/$pat//s) { $taint .= substr($suffix,0,0); $tail = $1 . $tail; } } } # Ensure taint is propagated from the path to its pieces. $tail .= $taint; wantarray ? ($basename .= $taint, $dirpath .= $taint, $tail) : ($basename .= $taint); } =item C<basename> X<basename> X<filename> my $filename = basename($path); my $filename = basename($path, @suffixes); This function is provided for compatibility with the Unix shell command C<basename(1)>. It does B<NOT> always return the file name portion of a path as you might expect. To be safe, if you want the file name portion of a path use C<fileparse()>. C<basename()> returns the last level of a filepath even if the last level is clearly directory. In effect, it is acting like C<pop()> for paths. This differs from C<fileparse()>'s behaviour. # Both return "bar" basename("/foo/bar"); basename("/foo/bar/"); @suffixes work as in C<fileparse()> except all regex metacharacters are quoted. # These two function calls are equivalent. my $filename = basename("/foo/bar/baz.txt", ".txt"); my $filename = fileparse("/foo/bar/baz.txt", qr/\Q.txt\E/); Also note that in order to be compatible with the shell command, C<basename()> does not strip off a suffix if it is identical to the remaining characters in the filename. =cut sub basename { my($path) = shift; # From BSD basename(1) # The basename utility deletes any prefix ending with the last slash '/' # character present in string (after first stripping trailing slashes) _strip_trailing_sep($path); my($basename, $dirname, $suffix) = fileparse( $path, map("\Q$_\E",@_) ); # From BSD basename(1) # The suffix is not stripped if it is identical to the remaining # characters in string. if( length $suffix and !length $basename ) { $basename = $suffix; } # Ensure that basename '/' == '/' if( !length $basename ) { $basename = $dirname; } return $basename; } =item C<dirname> X<dirname> This function is provided for compatibility with the Unix shell command C<dirname(1)> and has inherited some of its quirks. In spite of its name it does B<NOT> always return the directory name as you might expect. To be safe, if you want the directory name of a path use C<fileparse()>. Only on VMS (where there is no ambiguity between the file and directory portions of a path) and AmigaOS (possibly due to an implementation quirk in this module) does C<dirname()> work like C<fileparse($path)>, returning just the $dirs. # On VMS and AmigaOS my $dirs = dirname($path); When using Unix or MSDOS syntax this emulates the C<dirname(1)> shell function which is subtly different from how C<fileparse()> works. It returns all but the last level of a file path even if the last level is clearly a directory. In effect, it is not returning the directory portion but simply the path one level up acting like C<chop()> for file paths. Also unlike C<fileparse()>, C<dirname()> does not include a trailing slash on its returned path. # returns /foo/bar. fileparse() would return /foo/bar/ dirname("/foo/bar/baz"); # also returns /foo/bar despite the fact that baz is clearly a # directory. fileparse() would return /foo/bar/baz/ dirname("/foo/bar/baz/"); # returns '.'. fileparse() would return 'foo/' dirname("foo/"); Under VMS, if there is no directory information in the $path, then the current default device and directory is used. =cut sub dirname { my $path = shift; my($type) = $Fileparse_fstype; if( $type eq 'VMS' and $path =~ m{/} ) { # Parse as Unix local($File::Basename::Fileparse_fstype) = ''; return dirname($path); } my($basename, $dirname) = fileparse($path); if ($type eq 'VMS') { $dirname ||= $ENV{DEFAULT}; } elsif ($type eq 'MacOS') { if( !length($basename) && $dirname !~ /^[^:]+:\z/) { _strip_trailing_sep($dirname); ($basename,$dirname) = fileparse $dirname; } $dirname .= ":" unless $dirname =~ /:\z/; } elsif (grep { $type eq $_ } qw(MSDOS DOS MSWin32 OS2)) { _strip_trailing_sep($dirname); unless( length($basename) ) { ($basename,$dirname) = fileparse $dirname; _strip_trailing_sep($dirname); } } elsif ($type eq 'AmigaOS') { if ( $dirname =~ /:\z/) { return $dirname } chop $dirname; $dirname =~ s{[^:/]+\z}{} unless length($basename); } else { _strip_trailing_sep($dirname); unless( length($basename) ) { ($basename,$dirname) = fileparse $dirname; _strip_trailing_sep($dirname); } } $dirname; } # Strip the trailing path separator. sub _strip_trailing_sep { my $type = $Fileparse_fstype; if ($type eq 'MacOS') { $_[0] =~ s/([^:]):\z/$1/s; } elsif (grep { $type eq $_ } qw(MSDOS DOS MSWin32 OS2)) { $_[0] =~ s/([^:])[\\\/]*\z/$1/; } else { $_[0] =~ s{(.)/*\z}{$1}s; } } =item C<fileparse_set_fstype> X<filesystem> my $type = fileparse_set_fstype(); my $previous_type = fileparse_set_fstype($type); Normally File::Basename will assume a file path type native to your current operating system (ie. /foo/bar style on Unix, \foo\bar on Windows, etc...). With this function you can override that assumption. Valid $types are "MacOS", "VMS", "AmigaOS", "OS2", "RISCOS", "MSWin32", "DOS" (also "MSDOS" for backwards bug compatibility), "Epoc" and "Unix" (all case-insensitive). If an unrecognized $type is given "Unix" will be assumed. If you've selected VMS syntax, and the file specification you pass to one of these routines contains a "/", they assume you are using Unix emulation and apply the Unix syntax rules instead, for that function call only. =back =cut BEGIN { my @Ignore_Case = qw(MacOS VMS AmigaOS OS2 RISCOS MSWin32 MSDOS DOS Epoc); my @Types = (@Ignore_Case, qw(Unix)); sub fileparse_set_fstype { my $old = $Fileparse_fstype; if (@_) { my $new_type = shift; $Fileparse_fstype = 'Unix'; # default foreach my $type (@Types) { $Fileparse_fstype = $type if $new_type =~ /^$type/i; } $Fileparse_igncase = (grep $Fileparse_fstype eq $_, @Ignore_Case) ? 1 : 0; } return $old; } } 1; =head1 SEE ALSO L<dirname(1)>, L<basename(1)>, L<File::Spec> PK ! ō�">( >( stat.pmnu �[��� package File::stat; use 5.006; use strict; use warnings; use warnings::register; use Carp; use constant _IS_CYGWIN => $^O eq "cygwin"; BEGIN { *warnif = \&warnings::warnif } our(@EXPORT, @EXPORT_OK, %EXPORT_TAGS); our $VERSION = '1.09'; our @fields; our ( $st_dev, $st_ino, $st_mode, $st_nlink, $st_uid, $st_gid, $st_rdev, $st_size, $st_atime, $st_mtime, $st_ctime, $st_blksize, $st_blocks ); BEGIN { use Exporter (); @EXPORT = qw(stat lstat); @fields = qw( $st_dev $st_ino $st_mode $st_nlink $st_uid $st_gid $st_rdev $st_size $st_atime $st_mtime $st_ctime $st_blksize $st_blocks ); @EXPORT_OK = ( @fields, "stat_cando" ); %EXPORT_TAGS = ( FIELDS => [ @fields, @EXPORT ] ); } use Fcntl qw(S_IRUSR S_IWUSR S_IXUSR); BEGIN { # These constants will croak on use if the platform doesn't define # them. It's important to avoid inflicting that on the user. no strict 'refs'; for (qw(suid sgid svtx)) { my $val = eval { &{"Fcntl::S_I\U$_"} }; *{"_$_"} = defined $val ? sub { $_[0] & $val ? 1 : "" } : sub { "" }; } for (qw(SOCK CHR BLK REG DIR LNK)) { *{"S_IS$_"} = defined eval { &{"Fcntl::S_IF$_"} } ? \&{"Fcntl::S_IS$_"} : sub { "" }; } # FIFO flag and macro don't quite follow the S_IF/S_IS pattern above # RT #111638 *{"S_ISFIFO"} = defined &Fcntl::S_IFIFO ? \&Fcntl::S_ISFIFO : sub { "" }; } # from doio.c sub _ingroup { my ($gid, $eff) = @_; # I am assuming that since VMS doesn't have getgroups(2), $) will # always only contain a single entry. $^O eq "VMS" and return $_[0] == $); my ($egid, @supp) = split " ", $); my ($rgid) = split " ", $(; $gid == ($eff ? $egid : $rgid) and return 1; grep $gid == $_, @supp and return 1; return ""; } # VMS uses the Unix version of the routine, even though this is very # suboptimal. VMS has a permissions structure that doesn't really fit # into struct stat, and unlike on Win32 the normal -X operators respect # that, but unfortunately by the time we get here we've already lost the # information we need. It looks to me as though if we were to preserve # the st_devnam entry of vmsish.h's fake struct stat (which actually # holds the filename) it might be possible to do this right, but both # getting that value out of the struct (perl's stat doesn't return it) # and interpreting it later would require this module to have an XS # component (at which point we might as well just call Perl_cando and # have done with it). if (grep $^O eq $_, qw/os2 MSWin32 dos/) { # from doio.c *cando = sub { ($_[0][2] & $_[1]) ? 1 : "" }; } else { # from doio.c *cando = sub { my ($s, $mode, $eff) = @_; my $uid = $eff ? $> : $<; my ($stmode, $stuid, $stgid) = @$s[2,4,5]; # This code basically assumes that the rwx bits of the mode are # the 0777 bits, but so does Perl_cando. if (_IS_CYGWIN ? _ingroup(544, $eff) : ($uid == 0 && $^O ne "VMS")) { # If we're root on unix # not testing for executable status => all file tests are true return 1 if !($mode & 0111); # testing for executable status => # for a file, any x bit will do # for a directory, always true return 1 if $stmode & 0111 || S_ISDIR($stmode); return ""; } if ($stuid == $uid) { $stmode & $mode and return 1; } elsif (_ingroup($stgid, $eff)) { $stmode & ($mode >> 3) and return 1; } else { $stmode & ($mode >> 6) and return 1; } return ""; }; } # alias for those who don't like objects *stat_cando = \&cando; my %op = ( r => sub { cando($_[0], S_IRUSR, 1) }, w => sub { cando($_[0], S_IWUSR, 1) }, x => sub { cando($_[0], S_IXUSR, 1) }, o => sub { $_[0][4] == $> }, R => sub { cando($_[0], S_IRUSR, 0) }, W => sub { cando($_[0], S_IWUSR, 0) }, X => sub { cando($_[0], S_IXUSR, 0) }, O => sub { $_[0][4] == $< }, e => sub { 1 }, z => sub { $_[0][7] == 0 }, s => sub { $_[0][7] }, f => sub { S_ISREG ($_[0][2]) }, d => sub { S_ISDIR ($_[0][2]) }, l => sub { S_ISLNK ($_[0][2]) }, p => sub { S_ISFIFO($_[0][2]) }, S => sub { S_ISSOCK($_[0][2]) }, b => sub { S_ISBLK ($_[0][2]) }, c => sub { S_ISCHR ($_[0][2]) }, u => sub { _suid($_[0][2]) }, g => sub { _sgid($_[0][2]) }, k => sub { _svtx($_[0][2]) }, M => sub { ($^T - $_[0][9] ) / 86400 }, C => sub { ($^T - $_[0][10]) / 86400 }, A => sub { ($^T - $_[0][8] ) / 86400 }, ); use constant HINT_FILETEST_ACCESS => 0x00400000; # we need fallback=>1 or stringifying breaks use overload fallback => 1, -X => sub { my ($s, $op) = @_; if (index("rwxRWX", $op) >= 0) { (caller 0)[8] & HINT_FILETEST_ACCESS and warnif("File::stat ignores use filetest 'access'"); $^O eq "VMS" and warnif("File::stat ignores VMS ACLs"); # It would be nice to have a warning about using -l on a # non-lstat, but that would require an extra member in the # object. } if ($op{$op}) { return $op{$op}->($_[0]); } else { croak "-$op is not implemented on a File::stat object"; } }; # Class::Struct forbids use of @ISA sub import { goto &Exporter::import } use Class::Struct qw(struct); struct 'File::stat' => [ map { $_ => '$' } qw{ dev ino mode nlink uid gid rdev size atime mtime ctime blksize blocks } ]; sub populate (@) { return unless @_; my $stob = new(); @$stob = ( $st_dev, $st_ino, $st_mode, $st_nlink, $st_uid, $st_gid, $st_rdev, $st_size, $st_atime, $st_mtime, $st_ctime, $st_blksize, $st_blocks ) = @_; return $stob; } sub lstat ($) { populate(CORE::lstat(shift)) } sub stat ($) { my $arg = shift; my $st = populate(CORE::stat $arg); return $st if defined $st; my $fh; { local $!; no strict 'refs'; require Symbol; $fh = \*{ Symbol::qualify( $arg, caller() )}; return unless defined fileno $fh; } return populate(CORE::stat $fh); } 1; __END__ =head1 NAME File::stat - by-name interface to Perl's built-in stat() functions =head1 SYNOPSIS use File::stat; $st = stat($file) or die "No $file: $!"; if ( ($st->mode & 0111) && $st->nlink > 1) ) { print "$file is executable with lotsa links\n"; } if ( -x $st ) { print "$file is executable\n"; } use Fcntl "S_IRUSR"; if ( $st->cando(S_IRUSR, 1) ) { print "My effective uid can read $file\n"; } use File::stat qw(:FIELDS); stat($file) or die "No $file: $!"; if ( ($st_mode & 0111) && ($st_nlink > 1) ) { print "$file is executable with lotsa links\n"; } =head1 DESCRIPTION This module's default exports override the core stat() and lstat() functions, replacing them with versions that return "File::stat" objects. This object has methods that return the similarly named structure field name from the stat(2) function; namely, dev, ino, mode, nlink, uid, gid, rdev, size, atime, mtime, ctime, blksize, and blocks. As of version 1.02 (provided with perl 5.12) the object provides C<"-X"> overloading, so you can call filetest operators (C<-f>, C<-x>, and so on) on it. It also provides a C<< ->cando >> method, called like $st->cando( ACCESS, EFFECTIVE ) where I<ACCESS> is one of C<S_IRUSR>, C<S_IWUSR> or C<S_IXUSR> from the L<Fcntl|Fcntl> module, and I<EFFECTIVE> indicates whether to use effective (true) or real (false) ids. The method interprets the C<mode>, C<uid> and C<gid> fields, and returns whether or not the current process would be allowed the specified access. If you don't want to use the objects, you may import the C<< ->cando >> method into your namespace as a regular function called C<stat_cando>. This takes an arrayref containing the return values of C<stat> or C<lstat> as its first argument, and interprets it for you. You may also import all the structure fields directly into your namespace as regular variables using the :FIELDS import tag. (Note that this still overrides your stat() and lstat() functions.) Access these fields as variables named with a preceding C<st_> in front their method names. Thus, C<$stat_obj-E<gt>dev()> corresponds to $st_dev if you import the fields. To access this functionality without the core overrides, pass the C<use> an empty import list, and then access function functions with their full qualified names. On the other hand, the built-ins are still available via the C<CORE::> pseudo-package. =head1 BUGS As of Perl 5.8.0 after using this module you cannot use the implicit C<$_> or the special filehandle C<_> with stat() or lstat(), trying to do so leads into strange errors. The workaround is for C<$_> to be explicit my $stat_obj = stat $_; and for C<_> to explicitly populate the object using the unexported and undocumented populate() function with CORE::stat(): my $stat_obj = File::stat::populate(CORE::stat(_)); =head1 ERRORS =over 4 =item -%s is not implemented on a File::stat object The filetest operators C<-t>, C<-T> and C<-B> are not implemented, as they require more information than just a stat buffer. =back =head1 WARNINGS These can all be disabled with no warnings "File::stat"; =over 4 =item File::stat ignores use filetest 'access' You have tried to use one of the C<-rwxRWX> filetests with C<use filetest 'access'> in effect. C<File::stat> will ignore the pragma, and just use the information in the C<mode> member as usual. =item File::stat ignores VMS ACLs VMS systems have a permissions structure that cannot be completely represented in a stat buffer, and unlike on other systems the builtin filetest operators respect this. The C<File::stat> overloads, however, do not, since the information required is not available. =back =head1 NOTE While this class is currently implemented using the Class::Struct module to build a struct-like class, you shouldn't rely upon this. =head1 AUTHOR Tom Christiansen PK ! Y]lj? j? Copy.pmnu �[��� # File/Copy.pm. Written in 1994 by Aaron Sherman <ajs@ajs.com>. This # source code has been placed in the public domain by the author. # Please be kind and preserve the documentation. # # Additions copyright 1996 by Charles Bailey. Permission is granted # to distribute the revised code under the same terms as Perl itself. package File::Copy; use 5.006; use strict; use warnings; no warnings 'newline'; use File::Spec; use Config; # During perl build, we need File::Copy but Scalar::Util might not be built yet # And then we need these games to avoid loading overload, as that will # confuse miniperl during the bootstrap of perl. my $Scalar_Util_loaded = eval q{ require Scalar::Util; require overload; 1 }; # We want HiRes stat and utime if available BEGIN { eval q{ use Time::HiRes qw( stat utime ) } }; our(@ISA, @EXPORT, @EXPORT_OK, $VERSION, $Too_Big, $Syscopy_is_copy); sub copy; sub syscopy; sub cp; sub mv; $VERSION = '2.35'; require Exporter; @ISA = qw(Exporter); @EXPORT = qw(copy move); @EXPORT_OK = qw(cp mv); $Too_Big = 1024 * 1024 * 2; sub croak { require Carp; goto &Carp::croak; } sub carp { require Carp; goto &Carp::carp; } sub _catname { my($from, $to) = @_; if (not defined &basename) { require File::Basename; import File::Basename 'basename'; } return File::Spec->catfile($to, basename($from)); } # _eq($from, $to) tells whether $from and $to are identical sub _eq { my ($from, $to) = map { $Scalar_Util_loaded && Scalar::Util::blessed($_) && overload::Method($_, q{""}) ? "$_" : $_ } (@_); return '' if ( (ref $from) xor (ref $to) ); return $from == $to if ref $from; return $from eq $to; } sub copy { croak("Usage: copy(FROM, TO [, BUFFERSIZE]) ") unless(@_ == 2 || @_ == 3); my $from = shift; my $to = shift; my $size; if (@_) { $size = shift(@_) + 0; croak("Bad buffer size for copy: $size\n") unless ($size > 0); } my $from_a_handle = (ref($from) ? (ref($from) eq 'GLOB' || UNIVERSAL::isa($from, 'GLOB') || UNIVERSAL::isa($from, 'IO::Handle')) : (ref(\$from) eq 'GLOB')); my $to_a_handle = (ref($to) ? (ref($to) eq 'GLOB' || UNIVERSAL::isa($to, 'GLOB') || UNIVERSAL::isa($to, 'IO::Handle')) : (ref(\$to) eq 'GLOB')); if (_eq($from, $to)) { # works for references, too carp("'$from' and '$to' are identical (not copied)"); return 0; } if (!$from_a_handle && !$to_a_handle && -d $to && ! -d $from) { $to = _catname($from, $to); } if ((($Config{d_symlink} && $Config{d_readlink}) || $Config{d_link}) && !($^O eq 'os2')) { my @fs = stat($from); if (@fs) { my @ts = stat($to); if (@ts && $fs[0] == $ts[0] && $fs[1] == $ts[1] && !-p $from) { carp("'$from' and '$to' are identical (not copied)"); return 0; } } } elsif (_eq($from, $to)) { carp("'$from' and '$to' are identical (not copied)"); return 0; } if (defined &syscopy && !$Syscopy_is_copy && !$to_a_handle && !($from_a_handle && $^O eq 'os2' ) # OS/2 cannot handle handles && !($from_a_handle && $^O eq 'MSWin32') && !($from_a_handle && $^O eq 'NetWare') ) { if ($^O eq 'VMS' && -e $from && ! -d $to && ! -d $from) { # VMS natively inherits path components from the source of a # copy, but we want the Unixy behavior of inheriting from # the current working directory. Also, default in a trailing # dot for null file types. $to = VMS::Filespec::rmsexpand(VMS::Filespec::vmsify($to), '.'); # Get rid of the old versions to be like UNIX 1 while unlink $to; } return syscopy($from, $to) || 0; } my $closefrom = 0; my $closeto = 0; my ($status, $r, $buf); local($\) = ''; my $from_h; if ($from_a_handle) { $from_h = $from; } else { open $from_h, "<", $from or goto fail_open1; binmode $from_h or die "($!,$^E)"; $closefrom = 1; } # Seems most logical to do this here, in case future changes would want to # make this croak for some reason. unless (defined $size) { $size = tied(*$from_h) ? 0 : -s $from_h || 0; $size = 1024 if ($size < 512); $size = $Too_Big if ($size > $Too_Big); } my $to_h; if ($to_a_handle) { $to_h = $to; } else { $to_h = \do { local *FH }; # XXX is this line obsolete? open $to_h, ">", $to or goto fail_open2; binmode $to_h or die "($!,$^E)"; $closeto = 1; } $! = 0; for (;;) { my ($r, $w, $t); defined($r = sysread($from_h, $buf, $size)) or goto fail_inner; last unless $r; for ($w = 0; $w < $r; $w += $t) { $t = syswrite($to_h, $buf, $r - $w, $w) or goto fail_inner; } } close($to_h) || goto fail_open2 if $closeto; close($from_h) || goto fail_open1 if $closefrom; # Use this idiom to avoid uninitialized value warning. return 1; # All of these contortions try to preserve error messages... fail_inner: if ($closeto) { $status = $!; $! = 0; close $to_h; $! = $status unless $!; } fail_open2: if ($closefrom) { $status = $!; $! = 0; close $from_h; $! = $status unless $!; } fail_open1: return 0; } sub cp { my($from,$to) = @_; my(@fromstat) = stat $from; my(@tostat) = stat $to; my $perm; return 0 unless copy(@_) and @fromstat; if (@tostat) { $perm = $tostat[2]; } else { $perm = $fromstat[2] & ~(umask || 0); @tostat = stat $to; } # Might be more robust to look for S_I* in Fcntl, but we're # trying to avoid dependence on any XS-containing modules, # since File::Copy is used during the Perl build. $perm &= 07777; if ($perm & 06000) { croak("Unable to check setuid/setgid permissions for $to: $!") unless @tostat; if ($perm & 04000 and # setuid $fromstat[4] != $tostat[4]) { # owner must match $perm &= ~06000; } if ($perm & 02000 && $> != 0) { # if not root, setgid my $ok = $fromstat[5] == $tostat[5]; # group must match if ($ok) { # and we must be in group $ok = grep { $_ == $fromstat[5] } split /\s+/, $) } $perm &= ~06000 unless $ok; } } return 0 unless @tostat; return 1 if $perm == ($tostat[2] & 07777); return eval { chmod $perm, $to; } ? 1 : 0; } sub _move { croak("Usage: move(FROM, TO) ") unless @_ == 3; my($from,$to,$fallback) = @_; my($fromsz,$tosz1,$tomt1,$tosz2,$tomt2,$sts,$ossts); if (-d $to && ! -d $from) { $to = _catname($from, $to); } ($tosz1,$tomt1) = (stat($to))[7,9]; $fromsz = -s $from; if ($^O eq 'os2' and defined $tosz1 and defined $fromsz) { # will not rename with overwrite unlink $to; } if ($^O eq 'VMS' && -e $from && ! -d $to && ! -d $from) { # VMS natively inherits path components from the source of a # copy, but we want the Unixy behavior of inheriting from # the current working directory. Also, default in a trailing # dot for null file types. $to = VMS::Filespec::rmsexpand(VMS::Filespec::vmsify($to), '.'); # Get rid of the old versions to be like UNIX 1 while unlink $to; } return 1 if rename $from, $to; # Did rename return an error even though it succeeded, because $to # is on a remote NFS file system, and NFS lost the server's ack? return 1 if defined($fromsz) && !-e $from && # $from disappeared (($tosz2,$tomt2) = (stat($to))[7,9]) && # $to's there ((!defined $tosz1) || # not before or ($tosz1 != $tosz2 or $tomt1 != $tomt2)) && # was changed $tosz2 == $fromsz; # it's all there ($tosz1,$tomt1) = (stat($to))[7,9]; # just in case rename did something { local $@; eval { local $SIG{__DIE__}; $fallback->($from,$to) or die; my($atime, $mtime) = (stat($from))[8,9]; utime($atime, $mtime, $to); unlink($from) or die; }; return 1 unless $@; } ($sts,$ossts) = ($! + 0, $^E + 0); ($tosz2,$tomt2) = ((stat($to))[7,9],0,0) if defined $tomt1; unlink($to) if !defined($tomt1) or $tomt1 != $tomt2 or $tosz1 != $tosz2; ($!,$^E) = ($sts,$ossts); return 0; } sub move { _move(@_,\©); } sub mv { _move(@_,\&cp); } # &syscopy is an XSUB under OS/2 unless (defined &syscopy) { if ($^O eq 'VMS') { *syscopy = \&rmscopy; } elsif ($^O eq 'MSWin32' && defined &DynaLoader::boot_DynaLoader) { # Win32::CopyFile() fill only work if we can load Win32.xs *syscopy = sub { return 0 unless @_ == 2; return Win32::CopyFile(@_, 1); }; } else { $Syscopy_is_copy = 1; *syscopy = \© } } 1; __END__ =head1 NAME File::Copy - Copy files or filehandles =head1 SYNOPSIS use File::Copy; copy("sourcefile","destinationfile") or die "Copy failed: $!"; copy("Copy.pm",\*STDOUT); move("/dev1/sourcefile","/dev2/destinationfile"); use File::Copy "cp"; $n = FileHandle->new("/a/file","r"); cp($n,"x"); =head1 DESCRIPTION The File::Copy module provides two basic functions, C<copy> and C<move>, which are useful for getting the contents of a file from one place to another. =over 4 =item copy X<copy> X<cp> The C<copy> function takes two parameters: a file to copy from and a file to copy to. Either argument may be a string, a FileHandle reference or a FileHandle glob. Obviously, if the first argument is a filehandle of some sort, it will be read from, and if it is a file I<name> it will be opened for reading. Likewise, the second argument will be written to. If the second argument does not exist but the parent directory does exist, then it will be created. Trying to copy a file into a non-existent directory is an error. Trying to copy a file on top of itself is also an error. C<copy> will not overwrite read-only files. If the destination (second argument) already exists and is a directory, and the source (first argument) is not a filehandle, then the source file will be copied into the directory specified by the destination, using the same base name as the source file. It's a failure to have a filehandle as the source when the destination is a directory. B<Note that passing in files as handles instead of names may lead to loss of information on some operating systems; it is recommended that you use file names whenever possible.> Files are opened in binary mode where applicable. To get a consistent behaviour when copying from a filehandle to a file, use C<binmode> on the filehandle. An optional third parameter can be used to specify the buffer size used for copying. This is the number of bytes from the first file, that will be held in memory at any given time, before being written to the second file. The default buffer size depends upon the file, but will generally be the whole file (up to 2MB), or 1k for filehandles that do not reference files (eg. sockets). You may use the syntax C<use File::Copy "cp"> to get at the C<cp> alias for this function. The syntax is I<exactly> the same. The behavior is nearly the same as well: as of version 2.15, C<cp> will preserve the source file's permission bits like the shell utility C<cp(1)> would do, while C<copy> uses the default permissions for the target file (which may depend on the process' C<umask>, file ownership, inherited ACLs, etc.). If an error occurs in setting permissions, C<cp> will return 0, regardless of whether the file was successfully copied. =item move X<move> X<mv> X<rename> The C<move> function also takes two parameters: the current name and the intended name of the file to be moved. If the destination already exists and is a directory, and the source is not a directory, then the source file will be renamed into the directory specified by the destination. If possible, move() will simply rename the file. Otherwise, it copies the file to the new location and deletes the original. If an error occurs during this copy-and-delete process, you may be left with a (possibly partial) copy of the file under the destination name. You may use the C<mv> alias for this function in the same way that you may use the C<cp> alias for C<copy>. =item syscopy X<syscopy> File::Copy also provides the C<syscopy> routine, which copies the file specified in the first parameter to the file specified in the second parameter, preserving OS-specific attributes and file structure. For Unix systems, this is equivalent to the simple C<copy> routine, which doesn't preserve OS-specific attributes. For VMS systems, this calls the C<rmscopy> routine (see below). For OS/2 systems, this calls the C<syscopy> XSUB directly. For Win32 systems, this calls C<Win32::CopyFile>. B<Special behaviour if C<syscopy> is defined (OS/2, VMS and Win32)>: If both arguments to C<copy> are not file handles, then C<copy> will perform a "system copy" of the input file to a new output file, in order to preserve file attributes, indexed file structure, I<etc.> The buffer size parameter is ignored. If either argument to C<copy> is a handle to an opened file, then data is copied using Perl operators, and no effort is made to preserve file attributes or record structure. The system copy routine may also be called directly under VMS and OS/2 as C<File::Copy::syscopy> (or under VMS as C<File::Copy::rmscopy>, which is the routine that does the actual work for syscopy). =item rmscopy($from,$to[,$date_flag]) X<rmscopy> The first and second arguments may be strings, typeglobs, typeglob references, or objects inheriting from IO::Handle; they are used in all cases to obtain the I<filespec> of the input and output files, respectively. The name and type of the input file are used as defaults for the output file, if necessary. A new version of the output file is always created, which inherits the structure and RMS attributes of the input file, except for owner and protections (and possibly timestamps; see below). All data from the input file is copied to the output file; if either of the first two parameters to C<rmscopy> is a file handle, its position is unchanged. (Note that this means a file handle pointing to the output file will be associated with an old version of that file after C<rmscopy> returns, not the newly created version.) The third parameter is an integer flag, which tells C<rmscopy> how to handle timestamps. If it is E<lt> 0, none of the input file's timestamps are propagated to the output file. If it is E<gt> 0, then it is interpreted as a bitmask: if bit 0 (the LSB) is set, then timestamps other than the revision date are propagated; if bit 1 is set, the revision date is propagated. If the third parameter to C<rmscopy> is 0, then it behaves much like the DCL COPY command: if the name or type of the output file was explicitly specified, then no timestamps are propagated, but if they were taken implicitly from the input filespec, then all timestamps other than the revision date are propagated. If this parameter is not supplied, it defaults to 0. C<rmscopy> is VMS specific and cannot be exported; it must be referenced by its full name, e.g.: File::Copy::rmscopy($from, $to) or die $!; Like C<copy>, C<rmscopy> returns 1 on success. If an error occurs, it sets C<$!>, deletes the output file, and returns 0. =back =head1 RETURN All functions return 1 on success, 0 on failure. $! will be set if an error was encountered. =head1 NOTES Before calling copy() or move() on a filehandle, the caller should close or flush() the file to avoid writes being lost. Note that this is the case even for move(), because it may actually copy the file, depending on the OS-specific implementation, and the underlying filesystem(s). =head1 AUTHOR File::Copy was written by Aaron Sherman I<E<lt>ajs@ajs.comE<gt>> in 1995, and updated by Charles Bailey I<E<lt>bailey@newman.upenn.eduE<gt>> in 1996. =cut PK ! nh��� �� Path.pmnu �[��� package File::Path; use 5.005_04; use strict; use Cwd 'getcwd'; use File::Basename (); use File::Spec (); BEGIN { if ( $] < 5.006 ) { # can't say 'opendir my $dh, $dirname' # need to initialise $dh eval 'use Symbol'; } } use Exporter (); use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); $VERSION = '2.18'; $VERSION = eval $VERSION; @ISA = qw(Exporter); @EXPORT = qw(mkpath rmtree); @EXPORT_OK = qw(make_path remove_tree); BEGIN { for (qw(VMS MacOS MSWin32 os2)) { no strict 'refs'; *{"_IS_\U$_"} = $^O eq $_ ? sub () { 1 } : sub () { 0 }; } # These OSes complain if you want to remove a file that you have no # write permission to: *_FORCE_WRITABLE = ( grep { $^O eq $_ } qw(amigaos dos epoc MSWin32 MacOS os2) ) ? sub () { 1 } : sub () { 0 }; # Unix-like systems need to stat each directory in order to detect # race condition. MS-Windows is immune to this particular attack. *_NEED_STAT_CHECK = !(_IS_MSWIN32()) ? sub () { 1 } : sub () { 0 }; } sub _carp { require Carp; goto &Carp::carp; } sub _croak { require Carp; goto &Carp::croak; } sub _error { my $arg = shift; my $message = shift; my $object = shift; if ( $arg->{error} ) { $object = '' unless defined $object; $message .= ": $!" if $!; push @{ ${ $arg->{error} } }, { $object => $message }; } else { _carp( defined($object) ? "$message for $object: $!" : "$message: $!" ); } } sub __is_arg { my ($arg) = @_; # If client code blessed an array ref to HASH, this will not work # properly. We could have done $arg->isa() wrapped in eval, but # that would be expensive. This implementation should suffice. # We could have also used Scalar::Util:blessed, but we choose not # to add this dependency return ( ref $arg eq 'HASH' ); } sub make_path { push @_, {} unless @_ and __is_arg( $_[-1] ); goto &mkpath; } sub mkpath { my $old_style = !( @_ and __is_arg( $_[-1] ) ); my $data; my $paths; if ($old_style) { my ( $verbose, $mode ); ( $paths, $verbose, $mode ) = @_; $paths = [$paths] unless UNIVERSAL::isa( $paths, 'ARRAY' ); $data->{verbose} = $verbose; $data->{mode} = defined $mode ? $mode : oct '777'; } else { my %args_permitted = map { $_ => 1 } ( qw| chmod error group mask mode owner uid user verbose | ); my %not_on_win32_args = map { $_ => 1 } ( qw| group owner uid user | ); my @bad_args = (); my @win32_implausible_args = (); my $arg = pop @_; for my $k (sort keys %{$arg}) { if (! $args_permitted{$k}) { push @bad_args, $k; } elsif ($not_on_win32_args{$k} and _IS_MSWIN32) { push @win32_implausible_args, $k; } else { $data->{$k} = $arg->{$k}; } } _carp("Unrecognized option(s) passed to mkpath() or make_path(): @bad_args") if @bad_args; _carp("Option(s) implausible on Win32 passed to mkpath() or make_path(): @win32_implausible_args") if @win32_implausible_args; $data->{mode} = delete $data->{mask} if exists $data->{mask}; $data->{mode} = oct '777' unless exists $data->{mode}; ${ $data->{error} } = [] if exists $data->{error}; unless (@win32_implausible_args) { $data->{owner} = delete $data->{user} if exists $data->{user}; $data->{owner} = delete $data->{uid} if exists $data->{uid}; if ( exists $data->{owner} and $data->{owner} =~ /\D/ ) { my $uid = ( getpwnam $data->{owner} )[2]; if ( defined $uid ) { $data->{owner} = $uid; } else { _error( $data, "unable to map $data->{owner} to a uid, ownership not changed" ); delete $data->{owner}; } } if ( exists $data->{group} and $data->{group} =~ /\D/ ) { my $gid = ( getgrnam $data->{group} )[2]; if ( defined $gid ) { $data->{group} = $gid; } else { _error( $data, "unable to map $data->{group} to a gid, group ownership not changed" ); delete $data->{group}; } } if ( exists $data->{owner} and not exists $data->{group} ) { $data->{group} = -1; # chown will leave group unchanged } if ( exists $data->{group} and not exists $data->{owner} ) { $data->{owner} = -1; # chown will leave owner unchanged } } $paths = [@_]; } return _mkpath( $data, $paths ); } sub _mkpath { my $data = shift; my $paths = shift; my ( @created ); foreach my $path ( @{$paths} ) { next unless defined($path) and length($path); $path .= '/' if _IS_OS2 and $path =~ /^\w:\z/s; # feature of CRT # Logic wants Unix paths, so go with the flow. if (_IS_VMS) { next if $path eq '/'; $path = VMS::Filespec::unixify($path); } next if -d $path; my $parent = File::Basename::dirname($path); # Coverage note: It's not clear how we would test the condition: # '-d $parent or $path eq $parent' unless ( -d $parent or $path eq $parent ) { push( @created, _mkpath( $data, [$parent] ) ); } print "mkdir $path\n" if $data->{verbose}; if ( mkdir( $path, $data->{mode} ) ) { push( @created, $path ); if ( exists $data->{owner} ) { # NB: $data->{group} guaranteed to be set during initialisation if ( !chown $data->{owner}, $data->{group}, $path ) { _error( $data, "Cannot change ownership of $path to $data->{owner}:$data->{group}" ); } } if ( exists $data->{chmod} ) { # Coverage note: It's not clear how we would trigger the next # 'if' block. Failure of 'chmod' might first result in a # system error: "Permission denied". if ( !chmod $data->{chmod}, $path ) { _error( $data, "Cannot change permissions of $path to $data->{chmod}" ); } } } else { my $save_bang = $!; # From 'perldoc perlvar': $EXTENDED_OS_ERROR ($^E) is documented # as: # Error information specific to the current operating system. At the # moment, this differs from "$!" under only VMS, OS/2, and Win32 # (and for MacPerl). On all other platforms, $^E is always just the # same as $!. my ( $e, $e1 ) = ( $save_bang, $^E ); $e .= "; $e1" if $e ne $e1; # allow for another process to have created it meanwhile if ( ! -d $path ) { $! = $save_bang; if ( $data->{error} ) { push @{ ${ $data->{error} } }, { $path => $e }; } else { _croak("mkdir $path: $e"); } } } } return @created; } sub remove_tree { push @_, {} unless @_ and __is_arg( $_[-1] ); goto &rmtree; } sub _is_subdir { my ( $dir, $test ) = @_; my ( $dv, $dd ) = File::Spec->splitpath( $dir, 1 ); my ( $tv, $td ) = File::Spec->splitpath( $test, 1 ); # not on same volume return 0 if $dv ne $tv; my @d = File::Spec->splitdir($dd); my @t = File::Spec->splitdir($td); # @t can't be a subdir if it's shorter than @d return 0 if @t < @d; return join( '/', @d ) eq join( '/', splice @t, 0, +@d ); } sub rmtree { my $old_style = !( @_ and __is_arg( $_[-1] ) ); my ($arg, $data, $paths); if ($old_style) { my ( $verbose, $safe ); ( $paths, $verbose, $safe ) = @_; $data->{verbose} = $verbose; $data->{safe} = defined $safe ? $safe : 0; if ( defined($paths) and length($paths) ) { $paths = [$paths] unless UNIVERSAL::isa( $paths, 'ARRAY' ); } else { _carp("No root path(s) specified\n"); return 0; } } else { my %args_permitted = map { $_ => 1 } ( qw| error keep_root result safe verbose | ); my @bad_args = (); my $arg = pop @_; for my $k (sort keys %{$arg}) { if (! $args_permitted{$k}) { push @bad_args, $k; } else { $data->{$k} = $arg->{$k}; } } _carp("Unrecognized option(s) passed to remove_tree(): @bad_args") if @bad_args; ${ $data->{error} } = [] if exists $data->{error}; ${ $data->{result} } = [] if exists $data->{result}; # Wouldn't it make sense to do some validation on @_ before assigning # to $paths here? # In the $old_style case we guarantee that each path is both defined # and non-empty. We don't check that here, which means we have to # check it later in the first condition in this line: # if ( $ortho_root_length && _is_subdir( $ortho_root, $ortho_cwd ) ) { # Granted, that would be a change in behavior for the two # non-old-style interfaces. $paths = [@_]; } $data->{prefix} = ''; $data->{depth} = 0; my @clean_path; $data->{cwd} = getcwd() or do { _error( $data, "cannot fetch initial working directory" ); return 0; }; for ( $data->{cwd} ) { /\A(.*)\Z/s; $_ = $1 } # untaint for my $p (@$paths) { # need to fixup case and map \ to / on Windows my $ortho_root = _IS_MSWIN32 ? _slash_lc($p) : $p; my $ortho_cwd = _IS_MSWIN32 ? _slash_lc( $data->{cwd} ) : $data->{cwd}; my $ortho_root_length = length($ortho_root); $ortho_root_length-- if _IS_VMS; # don't compare '.' with ']' if ( $ortho_root_length && _is_subdir( $ortho_root, $ortho_cwd ) ) { local $! = 0; _error( $data, "cannot remove path when cwd is $data->{cwd}", $p ); next; } if (_IS_MACOS) { $p = ":$p" unless $p =~ /:/; $p .= ":" unless $p =~ /:\z/; } elsif ( _IS_MSWIN32 ) { $p =~ s{[/\\]\z}{}; } else { $p =~ s{/\z}{}; } push @clean_path, $p; } @{$data}{qw(device inode)} = ( lstat $data->{cwd} )[ 0, 1 ] or do { _error( $data, "cannot stat initial working directory", $data->{cwd} ); return 0; }; return _rmtree( $data, \@clean_path ); } sub _rmtree { my $data = shift; my $paths = shift; my $count = 0; my $curdir = File::Spec->curdir(); my $updir = File::Spec->updir(); my ( @files, $root ); ROOT_DIR: foreach my $root (@$paths) { # since we chdir into each directory, it may not be obvious # to figure out where we are if we generate a message about # a file name. We therefore construct a semi-canonical # filename, anchored from the directory being unlinked (as # opposed to being truly canonical, anchored from the root (/). my $canon = $data->{prefix} ? File::Spec->catfile( $data->{prefix}, $root ) : $root; my ( $ldev, $lino, $perm ) = ( lstat $root )[ 0, 1, 2 ] or next ROOT_DIR; if ( -d _ ) { $root = VMS::Filespec::vmspath( VMS::Filespec::pathify($root) ) if _IS_VMS; if ( !chdir($root) ) { # see if we can escalate privileges to get in # (e.g. funny protection mask such as -w- instead of rwx) # This uses fchmod to avoid traversing outside of the proper # location (CVE-2017-6512) my $root_fh; if (open($root_fh, '<', $root)) { my ($fh_dev, $fh_inode) = (stat $root_fh )[0,1]; $perm &= oct '7777'; my $nperm = $perm | oct '700'; local $@; if ( !( $data->{safe} or $nperm == $perm or !-d _ or $fh_dev ne $ldev or $fh_inode ne $lino or eval { chmod( $nperm, $root_fh ) } ) ) { _error( $data, "cannot make child directory read-write-exec", $canon ); next ROOT_DIR; } close $root_fh; } if ( !chdir($root) ) { _error( $data, "cannot chdir to child", $canon ); next ROOT_DIR; } } my ( $cur_dev, $cur_inode, $perm ) = ( stat $curdir )[ 0, 1, 2 ] or do { _error( $data, "cannot stat current working directory", $canon ); next ROOT_DIR; }; if (_NEED_STAT_CHECK) { ( $ldev eq $cur_dev and $lino eq $cur_inode ) or _croak( "directory $canon changed before chdir, expected dev=$ldev ino=$lino, actual dev=$cur_dev ino=$cur_inode, aborting." ); } $perm &= oct '7777'; # don't forget setuid, setgid, sticky bits my $nperm = $perm | oct '700'; # notabene: 0700 is for making readable in the first place, # it's also intended to change it to writable in case we have # to recurse in which case we are better than rm -rf for # subtrees with strange permissions if ( !( $data->{safe} or $nperm == $perm or chmod( $nperm, $curdir ) ) ) { _error( $data, "cannot make directory read+writeable", $canon ); $nperm = $perm; } my $d; $d = gensym() if $] < 5.006; if ( !opendir $d, $curdir ) { _error( $data, "cannot opendir", $canon ); @files = (); } else { if ( !defined ${^TAINT} or ${^TAINT} ) { # Blindly untaint dir names if taint mode is active @files = map { /\A(.*)\z/s; $1 } readdir $d; } else { @files = readdir $d; } closedir $d; } if (_IS_VMS) { # Deleting large numbers of files from VMS Files-11 # filesystems is faster if done in reverse ASCIIbetical order. # include '.' to '.;' from blead patch #31775 @files = map { $_ eq '.' ? '.;' : $_ } reverse @files; } @files = grep { $_ ne $updir and $_ ne $curdir } @files; if (@files) { # remove the contained files before the directory itself my $narg = {%$data}; @{$narg}{qw(device inode cwd prefix depth)} = ( $cur_dev, $cur_inode, $updir, $canon, $data->{depth} + 1 ); $count += _rmtree( $narg, \@files ); } # restore directory permissions of required now (in case the rmdir # below fails), while we are still in the directory and may do so # without a race via '.' if ( $nperm != $perm and not chmod( $perm, $curdir ) ) { _error( $data, "cannot reset chmod", $canon ); } # don't leave the client code in an unexpected directory chdir( $data->{cwd} ) or _croak("cannot chdir to $data->{cwd} from $canon: $!, aborting."); # ensure that a chdir upwards didn't take us somewhere other # than we expected (see CVE-2002-0435) ( $cur_dev, $cur_inode ) = ( stat $curdir )[ 0, 1 ] or _croak( "cannot stat prior working directory $data->{cwd}: $!, aborting." ); if (_NEED_STAT_CHECK) { ( $data->{device} eq $cur_dev and $data->{inode} eq $cur_inode ) or _croak( "previous directory $data->{cwd} " . "changed before entering $canon, " . "expected dev=$ldev ino=$lino, " . "actual dev=$cur_dev ino=$cur_inode, aborting." ); } if ( $data->{depth} or !$data->{keep_root} ) { if ( $data->{safe} && ( _IS_VMS ? !&VMS::Filespec::candelete($root) : !-w $root ) ) { print "skipped $root\n" if $data->{verbose}; next ROOT_DIR; } if ( _FORCE_WRITABLE and !chmod $perm | oct '700', $root ) { _error( $data, "cannot make directory writeable", $canon ); } print "rmdir $root\n" if $data->{verbose}; if ( rmdir $root ) { push @{ ${ $data->{result} } }, $root if $data->{result}; ++$count; } else { _error( $data, "cannot remove directory", $canon ); if ( _FORCE_WRITABLE && !chmod( $perm, ( _IS_VMS ? VMS::Filespec::fileify($root) : $root ) ) ) { _error( $data, sprintf( "cannot restore permissions to 0%o", $perm ), $canon ); } } } } else { # not a directory $root = VMS::Filespec::vmsify("./$root") if _IS_VMS && !File::Spec->file_name_is_absolute($root) && ( $root !~ m/(?<!\^)[\]>]+/ ); # not already in VMS syntax if ( $data->{safe} && ( _IS_VMS ? !&VMS::Filespec::candelete($root) : !( -l $root || -w $root ) ) ) { print "skipped $root\n" if $data->{verbose}; next ROOT_DIR; } my $nperm = $perm & oct '7777' | oct '600'; if ( _FORCE_WRITABLE and $nperm != $perm and not chmod $nperm, $root ) { _error( $data, "cannot make file writeable", $canon ); } print "unlink $canon\n" if $data->{verbose}; # delete all versions under VMS for ( ; ; ) { if ( unlink $root ) { push @{ ${ $data->{result} } }, $root if $data->{result}; } else { _error( $data, "cannot unlink file", $canon ); _FORCE_WRITABLE and chmod( $perm, $root ) or _error( $data, sprintf( "cannot restore permissions to 0%o", $perm ), $canon ); last; } ++$count; last unless _IS_VMS && lstat $root; } } } return $count; } sub _slash_lc { # fix up slashes and case on MSWin32 so that we can determine that # c:\path\to\dir is underneath C:/Path/To my $path = shift; $path =~ tr{\\}{/}; return lc($path); } 1; __END__ =head1 NAME File::Path - Create or remove directory trees =head1 VERSION 2.18 - released November 4 2020. =head1 SYNOPSIS use File::Path qw(make_path remove_tree); @created = make_path('foo/bar/baz', '/zug/zwang'); @created = make_path('foo/bar/baz', '/zug/zwang', { verbose => 1, mode => 0711, }); make_path('foo/bar/baz', '/zug/zwang', { chmod => 0777, }); $removed_count = remove_tree('foo/bar/baz', '/zug/zwang', { verbose => 1, error => \my $err_list, safe => 1, }); # legacy (interface promoted before v2.00) @created = mkpath('/foo/bar/baz'); @created = mkpath('/foo/bar/baz', 1, 0711); @created = mkpath(['/foo/bar/baz', 'blurfl/quux'], 1, 0711); $removed_count = rmtree('foo/bar/baz', 1, 1); $removed_count = rmtree(['foo/bar/baz', 'blurfl/quux'], 1, 1); # legacy (interface promoted before v2.06) @created = mkpath('foo/bar/baz', '/zug/zwang', { verbose => 1, mode => 0711 }); $removed_count = rmtree('foo/bar/baz', '/zug/zwang', { verbose => 1, mode => 0711 }); =head1 DESCRIPTION This module provides a convenient way to create directories of arbitrary depth and to delete an entire directory subtree from the filesystem. The following functions are provided: =over =item make_path( $dir1, $dir2, .... ) =item make_path( $dir1, $dir2, ...., \%opts ) The C<make_path> function creates the given directories if they don't exist before, much like the Unix command C<mkdir -p>. The function accepts a list of directories to be created. Its behaviour may be tuned by an optional hashref appearing as the last parameter on the call. The function returns the list of directories actually created during the call; in scalar context the number of directories created. The following keys are recognised in the option hash: =over =item mode => $num The numeric permissions mode to apply to each created directory (defaults to C<0777>), to be modified by the current C<umask>. If the directory already exists (and thus does not need to be created), the permissions will not be modified. C<mask> is recognised as an alias for this parameter. =item chmod => $num Takes a numeric mode to apply to each created directory (not modified by the current C<umask>). If the directory already exists (and thus does not need to be created), the permissions will not be modified. =item verbose => $bool If present, will cause C<make_path> to print the name of each directory as it is created. By default nothing is printed. =item error => \$err If present, it should be a reference to a scalar. This scalar will be made to reference an array, which will be used to store any errors that are encountered. See the L</"ERROR HANDLING"> section for more information. If this parameter is not used, certain error conditions may raise a fatal error that will cause the program to halt, unless trapped in an C<eval> block. =item owner => $owner =item user => $owner =item uid => $owner If present, will cause any created directory to be owned by C<$owner>. If the value is numeric, it will be interpreted as a uid; otherwise a username is assumed. An error will be issued if the username cannot be mapped to a uid, the uid does not exist or the process lacks the privileges to change ownership. Ownership of directories that already exist will not be changed. C<user> and C<uid> are aliases of C<owner>. =item group => $group If present, will cause any created directory to be owned by the group C<$group>. If the value is numeric, it will be interpreted as a gid; otherwise a group name is assumed. An error will be issued if the group name cannot be mapped to a gid, the gid does not exist or the process lacks the privileges to change group ownership. Group ownership of directories that already exist will not be changed. make_path '/var/tmp/webcache', {owner=>'nobody', group=>'nogroup'}; =back =item mkpath( $dir ) =item mkpath( $dir, $verbose, $mode ) =item mkpath( [$dir1, $dir2,...], $verbose, $mode ) =item mkpath( $dir1, $dir2,..., \%opt ) The C<mkpath()> function provide the legacy interface of C<make_path()> with a different interpretation of the arguments passed. The behaviour and return value of the function is otherwise identical to C<make_path()>. =item remove_tree( $dir1, $dir2, .... ) =item remove_tree( $dir1, $dir2, ...., \%opts ) The C<remove_tree> function deletes the given directories and any files and subdirectories they might contain, much like the Unix command C<rm -rf> or the Windows commands C<rmdir /s> and C<rd /s>. The function accepts a list of directories to be removed. (In point of fact, it will also accept filesystem entries which are not directories, such as regular files and symlinks. But, as its name suggests, its intent is to remove trees rather than individual files.) C<remove_tree()>'s behaviour may be tuned by an optional hashref appearing as the last parameter on the call. If an empty string is passed to C<remove_tree>, an error will occur. B<NOTE:> For security reasons, we strongly advise use of the hashref-as-final-argument syntax -- specifically, with a setting of the C<safe> element to a true value. remove_tree( $dir1, $dir2, ...., { safe => 1, ... # other key-value pairs }, ); The function returns the number of files successfully deleted. The following keys are recognised in the option hash: =over =item verbose => $bool If present, will cause C<remove_tree> to print the name of each file as it is unlinked. By default nothing is printed. =item safe => $bool When set to a true value, will cause C<remove_tree> to skip the files for which the process lacks the required privileges needed to delete files, such as delete privileges on VMS. In other words, the code will make no attempt to alter file permissions. Thus, if the process is interrupted, no filesystem object will be left in a more permissive mode. =item keep_root => $bool When set to a true value, will cause all files and subdirectories to be removed, except the initially specified directories. This comes in handy when cleaning out an application's scratch directory. remove_tree( '/tmp', {keep_root => 1} ); =item result => \$res If present, it should be a reference to a scalar. This scalar will be made to reference an array, which will be used to store all files and directories unlinked during the call. If nothing is unlinked, the array will be empty. remove_tree( '/tmp', {result => \my $list} ); print "unlinked $_\n" for @$list; This is a useful alternative to the C<verbose> key. =item error => \$err If present, it should be a reference to a scalar. This scalar will be made to reference an array, which will be used to store any errors that are encountered. See the L</"ERROR HANDLING"> section for more information. Removing things is a much more dangerous proposition than creating things. As such, there are certain conditions that C<remove_tree> may encounter that are so dangerous that the only sane action left is to kill the program. Use C<error> to trap all that is reasonable (problems with permissions and the like), and let it die if things get out of hand. This is the safest course of action. =back =item rmtree( $dir ) =item rmtree( $dir, $verbose, $safe ) =item rmtree( [$dir1, $dir2,...], $verbose, $safe ) =item rmtree( $dir1, $dir2,..., \%opt ) The C<rmtree()> function provide the legacy interface of C<remove_tree()> with a different interpretation of the arguments passed. The behaviour and return value of the function is otherwise identical to C<remove_tree()>. B<NOTE:> For security reasons, we strongly advise use of the hashref-as-final-argument syntax, specifically with a setting of the C<safe> element to a true value. rmtree( $dir1, $dir2, ...., { safe => 1, ... # other key-value pairs }, ); =back =head2 ERROR HANDLING =over 4 =item B<NOTE:> The following error handling mechanism is consistent throughout all code paths EXCEPT in cases where the ROOT node is nonexistent. In version 2.11 the maintainers attempted to rectify this inconsistency but too many downstream modules encountered problems. In such case, if you require root node evaluation or error checking prior to calling C<make_path> or C<remove_tree>, you should take additional precautions. =back If C<make_path> or C<remove_tree> encounters an error, a diagnostic message will be printed to C<STDERR> via C<carp> (for non-fatal errors) or via C<croak> (for fatal errors). If this behaviour is not desirable, the C<error> attribute may be used to hold a reference to a variable, which will be used to store the diagnostics. The variable is made a reference to an array of hash references. Each hash contain a single key/value pair where the key is the name of the file, and the value is the error message (including the contents of C<$!> when appropriate). If a general error is encountered the diagnostic key will be empty. An example usage looks like: remove_tree( 'foo/bar', 'bar/rat', {error => \my $err} ); if ($err && @$err) { for my $diag (@$err) { my ($file, $message) = %$diag; if ($file eq '') { print "general error: $message\n"; } else { print "problem unlinking $file: $message\n"; } } } else { print "No error encountered\n"; } Note that if no errors are encountered, C<$err> will reference an empty array. This means that C<$err> will always end up TRUE; so you need to test C<@$err> to determine if errors occurred. =head2 NOTES C<File::Path> blindly exports C<mkpath> and C<rmtree> into the current namespace. These days, this is considered bad style, but to change it now would break too much code. Nonetheless, you are invited to specify what it is you are expecting to use: use File::Path 'rmtree'; The routines C<make_path> and C<remove_tree> are B<not> exported by default. You must specify which ones you want to use. use File::Path 'remove_tree'; Note that a side-effect of the above is that C<mkpath> and C<rmtree> are no longer exported at all. This is due to the way the C<Exporter> module works. If you are migrating a codebase to use the new interface, you will have to list everything explicitly. But that's just good practice anyway. use File::Path qw(remove_tree rmtree); =head3 API CHANGES The API was changed in the 2.0 branch. For a time, C<mkpath> and C<rmtree> tried, unsuccessfully, to deal with the two different calling mechanisms. This approach was considered a failure. The new semantics are now only available with C<make_path> and C<remove_tree>. The old semantics are only available through C<mkpath> and C<rmtree>. Users are strongly encouraged to upgrade to at least 2.08 in order to avoid surprises. =head3 SECURITY CONSIDERATIONS There were race conditions in the 1.x implementations of File::Path's C<rmtree> function (although sometimes patched depending on the OS distribution or platform). The 2.0 version contains code to avoid the problem mentioned in CVE-2002-0435. See the following pages for more information: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=286905 http://www.nntp.perl.org/group/perl.perl5.porters/2005/01/msg97623.html http://www.debian.org/security/2005/dsa-696 Additionally, unless the C<safe> parameter is set (or the third parameter in the traditional interface is TRUE), should a C<remove_tree> be interrupted, files that were originally in read-only mode may now have their permissions set to a read-write (or "delete OK") mode. The following CVE reports were previously filed against File-Path and are believed to have been addressed: =over 4 =item * L<http://cve.circl.lu/cve/CVE-2004-0452> =item * L<http://cve.circl.lu/cve/CVE-2005-0448> =back In February 2017 the cPanel Security Team reported an additional vulnerability in File-Path. The C<chmod()> logic to make directories traversable can be abused to set the mode on an attacker-chosen file to an attacker-chosen value. This is due to the time-of-check-to-time-of-use (TOCTTOU) race condition (L<https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use>) between the C<stat()> that decides the inode is a directory and the C<chmod()> that tries to make it user-rwx. CPAN versions 2.13 and later incorporate a patch provided by John Lightsey to address this problem. This vulnerability has been reported as CVE-2017-6512. =head1 DIAGNOSTICS FATAL errors will cause the program to halt (C<croak>), since the problem is so severe that it would be dangerous to continue. (This can always be trapped with C<eval>, but it's not a good idea. Under the circumstances, dying is the best thing to do). SEVERE errors may be trapped using the modern interface. If the they are not trapped, or if the old interface is used, such an error will cause the program will halt. All other errors may be trapped using the modern interface, otherwise they will be C<carp>ed about. Program execution will not be halted. =over 4 =item mkdir [path]: [errmsg] (SEVERE) C<make_path> was unable to create the path. Probably some sort of permissions error at the point of departure or insufficient resources (such as free inodes on Unix). =item No root path(s) specified C<make_path> was not given any paths to create. This message is only emitted if the routine is called with the traditional interface. The modern interface will remain silent if given nothing to do. =item No such file or directory On Windows, if C<make_path> gives you this warning, it may mean that you have exceeded your filesystem's maximum path length. =item cannot fetch initial working directory: [errmsg] C<remove_tree> attempted to determine the initial directory by calling C<Cwd::getcwd>, but the call failed for some reason. No attempt will be made to delete anything. =item cannot stat initial working directory: [errmsg] C<remove_tree> attempted to stat the initial directory (after having successfully obtained its name via C<getcwd>), however, the call failed for some reason. No attempt will be made to delete anything. =item cannot chdir to [dir]: [errmsg] C<remove_tree> attempted to set the working directory in order to begin deleting the objects therein, but was unsuccessful. This is usually a permissions issue. The routine will continue to delete other things, but this directory will be left intact. =item directory [dir] changed before chdir, expected dev=[n] ino=[n], actual dev=[n] ino=[n], aborting. (FATAL) C<remove_tree> recorded the device and inode of a directory, and then moved into it. It then performed a C<stat> on the current directory and detected that the device and inode were no longer the same. As this is at the heart of the race condition problem, the program will die at this point. =item cannot make directory [dir] read+writeable: [errmsg] C<remove_tree> attempted to change the permissions on the current directory to ensure that subsequent unlinkings would not run into problems, but was unable to do so. The permissions remain as they were, and the program will carry on, doing the best it can. =item cannot read [dir]: [errmsg] C<remove_tree> tried to read the contents of the directory in order to acquire the names of the directory entries to be unlinked, but was unsuccessful. This is usually a permissions issue. The program will continue, but the files in this directory will remain after the call. =item cannot reset chmod [dir]: [errmsg] C<remove_tree>, after having deleted everything in a directory, attempted to restore its permissions to the original state but failed. The directory may wind up being left behind. =item cannot remove [dir] when cwd is [dir] The current working directory of the program is F</some/path/to/here> and you are attempting to remove an ancestor, such as F</some/path>. The directory tree is left untouched. The solution is to C<chdir> out of the child directory to a place outside the directory tree to be removed. =item cannot chdir to [parent-dir] from [child-dir]: [errmsg], aborting. (FATAL) C<remove_tree>, after having deleted everything and restored the permissions of a directory, was unable to chdir back to the parent. The program halts to avoid a race condition from occurring. =item cannot stat prior working directory [dir]: [errmsg], aborting. (FATAL) C<remove_tree> was unable to stat the parent directory after having returned from the child. Since there is no way of knowing if we returned to where we think we should be (by comparing device and inode) the only way out is to C<croak>. =item previous directory [parent-dir] changed before entering [child-dir], expected dev=[n] ino=[n], actual dev=[n] ino=[n], aborting. (FATAL) When C<remove_tree> returned from deleting files in a child directory, a check revealed that the parent directory it returned to wasn't the one it started out from. This is considered a sign of malicious activity. =item cannot make directory [dir] writeable: [errmsg] Just before removing a directory (after having successfully removed everything it contained), C<remove_tree> attempted to set the permissions on the directory to ensure it could be removed and failed. Program execution continues, but the directory may possibly not be deleted. =item cannot remove directory [dir]: [errmsg] C<remove_tree> attempted to remove a directory, but failed. This may be because some objects that were unable to be removed remain in the directory, or it could be a permissions issue. The directory will be left behind. =item cannot restore permissions of [dir] to [0nnn]: [errmsg] After having failed to remove a directory, C<remove_tree> was unable to restore its permissions from a permissive state back to a possibly more restrictive setting. (Permissions given in octal). =item cannot make file [file] writeable: [errmsg] C<remove_tree> attempted to force the permissions of a file to ensure it could be deleted, but failed to do so. It will, however, still attempt to unlink the file. =item cannot unlink file [file]: [errmsg] C<remove_tree> failed to remove a file. Probably a permissions issue. =item cannot restore permissions of [file] to [0nnn]: [errmsg] After having failed to remove a file, C<remove_tree> was also unable to restore the permissions on the file to a possibly less permissive setting. (Permissions given in octal). =item unable to map [owner] to a uid, ownership not changed"); C<make_path> was instructed to give the ownership of created directories to the symbolic name [owner], but C<getpwnam> did not return the corresponding numeric uid. The directory will be created, but ownership will not be changed. =item unable to map [group] to a gid, group ownership not changed C<make_path> was instructed to give the group ownership of created directories to the symbolic name [group], but C<getgrnam> did not return the corresponding numeric gid. The directory will be created, but group ownership will not be changed. =back =head1 SEE ALSO =over 4 =item * L<File::Remove> Allows files and directories to be moved to the Trashcan/Recycle Bin (where they may later be restored if necessary) if the operating system supports such functionality. This feature may one day be made available directly in C<File::Path>. =item * L<File::Find::Rule> When removing directory trees, if you want to examine each file to decide whether to delete it (and possibly leaving large swathes alone), F<File::Find::Rule> offers a convenient and flexible approach to examining directory trees. =back =head1 BUGS AND LIMITATIONS The following describes F<File::Path> limitations and how to report bugs. =head2 MULTITHREADED APPLICATIONS F<File::Path> C<rmtree> and C<remove_tree> will not work with multithreaded applications due to its use of C<chdir>. At this time, no warning or error is generated in this situation. You will certainly encounter unexpected results. The implementation that surfaces this limitation will not be changed. See the F<File::Path::Tiny> module for functionality similar to F<File::Path> but which does not C<chdir>. =head2 NFS Mount Points F<File::Path> is not responsible for triggering the automounts, mirror mounts, and the contents of network mounted filesystems. If your NFS implementation requires an action to be performed on the filesystem in order for F<File::Path> to perform operations, it is strongly suggested you assure filesystem availability by reading the root of the mounted filesystem. =head2 REPORTING BUGS Please report all bugs on the RT queue, either via the web interface: L<http://rt.cpan.org/NoAuth/Bugs.html?Dist=File-Path> or by email: bug-File-Path@rt.cpan.org In either case, please B<attach> patches to the bug report rather than including them inline in the web post or the body of the email. You can also send pull requests to the Github repository: L<https://github.com/rpcme/File-Path> =head1 ACKNOWLEDGEMENTS Paul Szabo identified the race condition originally, and Brendan O'Dea wrote an implementation for Debian that addressed the problem. That code was used as a basis for the current code. Their efforts are greatly appreciated. Gisle Aas made a number of improvements to the documentation for 2.07 and his advice and assistance is also greatly appreciated. =head1 AUTHORS Prior authors and maintainers: Tim Bunce, Charles Bailey, and David Landgren <F<david@landgren.net>>. Current maintainers are Richard Elberger <F<riche@cpan.org>> and James (Jim) Keenan <F<jkeenan@cpan.org>>. =head1 CONTRIBUTORS Contributors to File::Path, in alphabetical order by first name. =over 1 =item <F<bulkdd@cpan.org>> =item Charlie Gonzalez <F<itcharlie@cpan.org>> =item Craig A. Berry <F<craigberry@mac.com>> =item James E Keenan <F<jkeenan@cpan.org>> =item John Lightsey <F<john@perlsec.org>> =item Nigel Horne <F<njh@bandsman.co.uk>> =item Richard Elberger <F<riche@cpan.org>> =item Ryan Yee <F<ryee@cpan.org>> =item Skye Shaw <F<shaw@cpan.org>> =item Tom Lutz <F<tommylutz@gmail.com>> =item Will Sheppard <F<willsheppard@github>> =back =head1 COPYRIGHT This module is copyright (C) Charles Bailey, Tim Bunce, David Landgren, James Keenan and Richard Elberger 1995-2020. All rights reserved. =head1 LICENSE This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut PK ! 7�� �� Find.pmnu �[��� package File::Find; use 5.006; use strict; use warnings; use warnings::register; our $VERSION = '1.39'; require Exporter; require Cwd; our @ISA = qw(Exporter); our @EXPORT = qw(find finddepth); use strict; my $Is_VMS = $^O eq 'VMS'; my $Is_Win32 = $^O eq 'MSWin32'; require File::Basename; require File::Spec; # Should ideally be my() not our() but local() currently # refuses to operate on lexicals our %SLnkSeen; our ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow, $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat, $pre_process, $post_process, $dangling_symlinks); sub contract_name { my ($cdir,$fn) = @_; return substr($cdir,0,rindex($cdir,'/')) if $fn eq $File::Find::current_dir; $cdir = substr($cdir,0,rindex($cdir,'/')+1); $fn =~ s|^\./||; my $abs_name= $cdir . $fn; if (substr($fn,0,3) eq '../') { 1 while $abs_name =~ s!/[^/]*/\.\./+!/!; } return $abs_name; } sub PathCombine($$) { my ($Base,$Name) = @_; my $AbsName; if (substr($Name,0,1) eq '/') { $AbsName= $Name; } else { $AbsName= contract_name($Base,$Name); } # (simple) check for recursion my $newlen= length($AbsName); if ($newlen <= length($Base)) { if (($newlen == length($Base) || substr($Base,$newlen,1) eq '/') && $AbsName eq substr($Base,0,$newlen)) { return undef; } } return $AbsName; } sub Follow_SymLink($) { my ($AbsName) = @_; my ($NewName,$DEV, $INO); ($DEV, $INO)= lstat $AbsName; while (-l _) { if ($SLnkSeen{$DEV, $INO}++) { if ($follow_skip < 2) { die "$AbsName is encountered a second time"; } else { return undef; } } $NewName= PathCombine($AbsName, readlink($AbsName)); unless(defined $NewName) { if ($follow_skip < 2) { die "$AbsName is a recursive symbolic link"; } else { return undef; } } else { $AbsName= $NewName; } ($DEV, $INO) = lstat($AbsName); return undef unless defined $DEV; # dangling symbolic link } if ($full_check && defined $DEV && $SLnkSeen{$DEV, $INO}++) { if ( ($follow_skip < 1) || ((-d _) && ($follow_skip < 2)) ) { die "$AbsName encountered a second time"; } else { return undef; } } return $AbsName; } our($dir, $name, $fullname, $prune); sub _find_dir_symlnk($$$); sub _find_dir($$$); # check whether or not a scalar variable is tainted # (code straight from the Camel, 3rd ed., page 561) sub is_tainted_pp { my $arg = shift; my $nada = substr($arg, 0, 0); # zero-length local $@; eval { eval "# $nada" }; return length($@) != 0; } sub _find_opt { my $wanted = shift; return unless @_; die "invalid top directory" unless defined $_[0]; # This function must local()ize everything because callbacks may # call find() or finddepth() local %SLnkSeen; local ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow, $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat, $pre_process, $post_process, $dangling_symlinks); local($dir, $name, $fullname, $prune); local *_ = \my $a; my $cwd = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::getcwd(); if ($Is_VMS) { # VMS returns this by default in VMS format which just doesn't # work for the rest of this module. $cwd = VMS::Filespec::unixpath($cwd); # Apparently this is not expected to have a trailing space. # To attempt to make VMS/UNIX conversions mostly reversible, # a trailing slash is needed. The run-time functions ignore the # resulting double slash, but it causes the perl tests to fail. $cwd =~ s#/\z##; # This comes up in upper case now, but should be lower. # In the future this could be exact case, no need to change. } my $cwd_untainted = $cwd; my $check_t_cwd = 1; $wanted_callback = $wanted->{wanted}; $bydepth = $wanted->{bydepth}; $pre_process = $wanted->{preprocess}; $post_process = $wanted->{postprocess}; $no_chdir = $wanted->{no_chdir}; $full_check = $wanted->{follow}; $follow = $full_check || $wanted->{follow_fast}; $follow_skip = $wanted->{follow_skip}; $untaint = $wanted->{untaint}; $untaint_pat = $wanted->{untaint_pattern}; $untaint_skip = $wanted->{untaint_skip}; $dangling_symlinks = $wanted->{dangling_symlinks}; # for compatibility reasons (find.pl, find2perl) local our ($topdir, $topdev, $topino, $topmode, $topnlink); # a symbolic link to a directory doesn't increase the link count $avoid_nlink = $follow || $File::Find::dont_use_nlink; my ($abs_dir, $Is_Dir); Proc_Top_Item: foreach my $TOP (@_) { my $top_item = $TOP; $top_item = VMS::Filespec::unixify($top_item) if $Is_VMS; ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item; if ($Is_Win32) { $top_item =~ s|[/\\]\z|| unless $top_item =~ m{^(?:\w:)?[/\\]$}; } else { $top_item =~ s|/\z|| unless $top_item eq '/'; } $Is_Dir= 0; if ($follow) { if (substr($top_item,0,1) eq '/') { $abs_dir = $top_item; } elsif ($top_item eq $File::Find::current_dir) { $abs_dir = $cwd; } else { # care about any ../ $top_item =~ s/\.dir\z//i if $Is_VMS; $abs_dir = contract_name("$cwd/",$top_item); } $abs_dir= Follow_SymLink($abs_dir); unless (defined $abs_dir) { if ($dangling_symlinks) { if (ref $dangling_symlinks eq 'CODE') { $dangling_symlinks->($top_item, $cwd); } else { warnings::warnif "$top_item is a dangling symbolic link\n"; } } next Proc_Top_Item; } if (-d _) { $top_item =~ s/\.dir\z//i if $Is_VMS; _find_dir_symlnk($wanted, $abs_dir, $top_item); $Is_Dir= 1; } } else { # no follow $topdir = $top_item; unless (defined $topnlink) { warnings::warnif "Can't stat $top_item: $!\n"; next Proc_Top_Item; } if (-d _) { $top_item =~ s/\.dir\z//i if $Is_VMS; _find_dir($wanted, $top_item, $topnlink); $Is_Dir= 1; } else { $abs_dir= $top_item; } } unless ($Is_Dir) { unless (($_,$dir) = File::Basename::fileparse($abs_dir)) { ($dir,$_) = ('./', $top_item); } $abs_dir = $dir; if (( $untaint ) && (is_tainted($dir) )) { ( $abs_dir ) = $dir =~ m|$untaint_pat|; unless (defined $abs_dir) { if ($untaint_skip == 0) { die "directory $dir is still tainted"; } else { next Proc_Top_Item; } } } unless ($no_chdir || chdir $abs_dir) { warnings::warnif "Couldn't chdir $abs_dir: $!\n"; next Proc_Top_Item; } $name = $abs_dir . $_; # $File::Find::name $_ = $name if $no_chdir; { $wanted_callback->() }; # protect against wild "next" } unless ( $no_chdir ) { if ( ($check_t_cwd) && (($untaint) && (is_tainted($cwd) )) ) { ( $cwd_untainted ) = $cwd =~ m|$untaint_pat|; unless (defined $cwd_untainted) { die "insecure cwd in find(depth)"; } $check_t_cwd = 0; } unless (chdir $cwd_untainted) { die "Can't cd to $cwd: $!\n"; } } } } # API: # $wanted # $p_dir : "parent directory" # $nlink : what came back from the stat # preconditions: # chdir (if not no_chdir) to dir sub _find_dir($$$) { my ($wanted, $p_dir, $nlink) = @_; my ($CdLvl,$Level) = (0,0); my @Stack; my @filenames; my ($subcount,$sub_nlink); my $SE= []; my $dir_name= $p_dir; my $dir_pref; my $dir_rel = $File::Find::current_dir; my $tainted = 0; my $no_nlink; if ($Is_Win32) { $dir_pref = ($p_dir =~ m{^(?:\w:[/\\]?|[/\\])$} ? $p_dir : "$p_dir/" ); } elsif ($Is_VMS) { # VMS is returning trailing .dir on directories # and trailing . on files and symbolic links # in UNIX syntax. # $p_dir =~ s/\.(dir)?$//i unless $p_dir eq '.'; $dir_pref = ($p_dir =~ m/[\]>]+$/ ? $p_dir : "$p_dir/" ); } else { $dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" ); } local ($dir, $name, $prune, *DIR); unless ( $no_chdir || ($p_dir eq $File::Find::current_dir)) { my $udir = $p_dir; if (( $untaint ) && (is_tainted($p_dir) )) { ( $udir ) = $p_dir =~ m|$untaint_pat|; unless (defined $udir) { if ($untaint_skip == 0) { die "directory $p_dir is still tainted"; } else { return; } } } unless (chdir ($Is_VMS && $udir !~ /[\/\[<]+/ ? "./$udir" : $udir)) { warnings::warnif "Can't cd to $udir: $!\n"; return; } } # push the starting directory push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth; while (defined $SE) { unless ($bydepth) { $dir= $p_dir; # $File::Find::dir $name= $dir_name; # $File::Find::name $_= ($no_chdir ? $dir_name : $dir_rel ); # $_ # prune may happen here $prune= 0; { $wanted_callback->() }; # protect against wild "next" next if $prune; } # change to that directory unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) { my $udir= $dir_rel; if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_rel) )) ) { ( $udir ) = $dir_rel =~ m|$untaint_pat|; unless (defined $udir) { if ($untaint_skip == 0) { die "directory (" . ($p_dir ne '/' ? $p_dir : '') . "/) $dir_rel is still tainted"; } else { # $untaint_skip == 1 next; } } } unless (chdir ($Is_VMS && $udir !~ /[\/\[<]+/ ? "./$udir" : $udir)) { warnings::warnif "Can't cd to (" . ($p_dir ne '/' ? $p_dir : '') . "/) $udir: $!\n"; next; } $CdLvl++; } $dir= $dir_name; # $File::Find::dir # Get the list of files in the current directory. unless (opendir DIR, ($no_chdir ? $dir_name : $File::Find::current_dir)) { warnings::warnif "Can't opendir($dir_name): $!\n"; next; } @filenames = readdir DIR; closedir(DIR); @filenames = $pre_process->(@filenames) if $pre_process; push @Stack,[$CdLvl,$dir_name,"",-2] if $post_process; # default: use whatever was specified # (if $nlink >= 2, and $avoid_nlink == 0, this will switch back) $no_nlink = $avoid_nlink; # if dir has wrong nlink count, force switch to slower stat method $no_nlink = 1 if ($nlink < 2); if ($nlink == 2 && !$no_nlink) { # This dir has no subdirectories. for my $FN (@filenames) { if ($Is_VMS) { # Big hammer here - Compensate for VMS trailing . and .dir # No win situation until this is changed, but this # will handle the majority of the cases with breaking the fewest $FN =~ s/\.dir\z//i; $FN =~ s#\.$## if ($FN ne '.'); } next if $FN =~ $File::Find::skip_pattern; $name = $dir_pref . $FN; # $File::Find::name $_ = ($no_chdir ? $name : $FN); # $_ { $wanted_callback->() }; # protect against wild "next" } } else { # This dir has subdirectories. $subcount = $nlink - 2; # HACK: insert directories at this position, so as to preserve # the user pre-processed ordering of files (thus ensuring # directory traversal is in user sorted order, not at random). my $stack_top = @Stack; for my $FN (@filenames) { next if $FN =~ $File::Find::skip_pattern; if ($subcount > 0 || $no_nlink) { # Seen all the subdirs? # check for directoriness. # stat is faster for a file in the current directory $sub_nlink = (lstat ($no_chdir ? $dir_pref . $FN : $FN))[3]; if (-d _) { --$subcount; $FN =~ s/\.dir\z//i if $Is_VMS; # HACK: replace push to preserve dir traversal order #push @Stack,[$CdLvl,$dir_name,$FN,$sub_nlink]; splice @Stack, $stack_top, 0, [$CdLvl,$dir_name,$FN,$sub_nlink]; } else { $name = $dir_pref . $FN; # $File::Find::name $_= ($no_chdir ? $name : $FN); # $_ { $wanted_callback->() }; # protect against wild "next" } } else { $name = $dir_pref . $FN; # $File::Find::name $_= ($no_chdir ? $name : $FN); # $_ { $wanted_callback->() }; # protect against wild "next" } } } } continue { while ( defined ($SE = pop @Stack) ) { ($Level, $p_dir, $dir_rel, $nlink) = @$SE; if ($CdLvl > $Level && !$no_chdir) { my $tmp; if ($Is_VMS) { $tmp = '[' . ('-' x ($CdLvl-$Level)) . ']'; } else { $tmp = join('/',('..') x ($CdLvl-$Level)); } die "Can't cd to $tmp from $dir_name: $!" unless chdir ($tmp); $CdLvl = $Level; } if ($Is_Win32) { $dir_name = ($p_dir =~ m{^(?:\w:[/\\]?|[/\\])$} ? "$p_dir$dir_rel" : "$p_dir/$dir_rel"); $dir_pref = "$dir_name/"; } elsif ($^O eq 'VMS') { if ($p_dir =~ m/[\]>]+$/) { $dir_name = $p_dir; $dir_name =~ s/([\]>]+)$/.$dir_rel$1/; $dir_pref = $dir_name; } else { $dir_name = "$p_dir/$dir_rel"; $dir_pref = "$dir_name/"; } } else { $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel"); $dir_pref = "$dir_name/"; } if ( $nlink == -2 ) { $name = $dir = $p_dir; # $File::Find::name / dir $_ = $File::Find::current_dir; $post_process->(); # End-of-directory processing } elsif ( $nlink < 0 ) { # must be finddepth, report dirname now $name = $dir_name; if ( substr($name,-2) eq '/.' ) { substr($name, length($name) == 2 ? -1 : -2) = ''; } $dir = $p_dir; $_ = ($no_chdir ? $dir_name : $dir_rel ); if ( substr($_,-2) eq '/.' ) { substr($_, length($_) == 2 ? -1 : -2) = ''; } { $wanted_callback->() }; # protect against wild "next" } else { push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth; last; } } } } # API: # $wanted # $dir_loc : absolute location of a dir # $p_dir : "parent directory" # preconditions: # chdir (if not no_chdir) to dir sub _find_dir_symlnk($$$) { my ($wanted, $dir_loc, $p_dir) = @_; # $dir_loc is the absolute directory my @Stack; my @filenames; my $new_loc; my $updir_loc = $dir_loc; # untainted parent directory my $SE = []; my $dir_name = $p_dir; my $dir_pref; my $loc_pref; my $dir_rel = $File::Find::current_dir; my $byd_flag; # flag for pending stack entry if $bydepth my $tainted = 0; my $ok = 1; $dir_pref = ( $p_dir eq '/' ? '/' : "$p_dir/" ); $loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" ); local ($dir, $name, $fullname, $prune, *DIR); unless ($no_chdir) { # untaint the topdir if (( $untaint ) && (is_tainted($dir_loc) )) { ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|; # parent dir, now untainted # once untainted, $updir_loc is pushed on the stack (as parent directory); # hence, we don't need to untaint the parent directory every time we chdir # to it later unless (defined $updir_loc) { if ($untaint_skip == 0) { die "directory $dir_loc is still tainted"; } else { return; } } } $ok = chdir($updir_loc) unless ($p_dir eq $File::Find::current_dir); unless ($ok) { warnings::warnif "Can't cd to $updir_loc: $!\n"; return; } } push @Stack,[$dir_loc,$updir_loc,$p_dir,$dir_rel,-1] if $bydepth; while (defined $SE) { unless ($bydepth) { # change (back) to parent directory (always untainted) unless ($no_chdir) { unless (chdir $updir_loc) { warnings::warnif "Can't cd to $updir_loc: $!\n"; next; } } $dir= $p_dir; # $File::Find::dir $name= $dir_name; # $File::Find::name $_= ($no_chdir ? $dir_name : $dir_rel ); # $_ $fullname= $dir_loc; # $File::Find::fullname # prune may happen here $prune= 0; lstat($_); # make sure file tests with '_' work { $wanted_callback->() }; # protect against wild "next" next if $prune; } # change to that directory unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) { $updir_loc = $dir_loc; if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_loc) )) ) { # untaint $dir_loc, what will be pushed on the stack as (untainted) parent dir ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|; unless (defined $updir_loc) { if ($untaint_skip == 0) { die "directory $dir_loc is still tainted"; } else { next; } } } unless (chdir $updir_loc) { warnings::warnif "Can't cd to $updir_loc: $!\n"; next; } } $dir = $dir_name; # $File::Find::dir # Get the list of files in the current directory. unless (opendir DIR, ($no_chdir ? $dir_loc : $File::Find::current_dir)) { warnings::warnif "Can't opendir($dir_loc): $!\n"; next; } @filenames = readdir DIR; closedir(DIR); for my $FN (@filenames) { if ($Is_VMS) { # Big hammer here - Compensate for VMS trailing . and .dir # No win situation until this is changed, but this # will handle the majority of the cases with breaking the fewest. $FN =~ s/\.dir\z//i; $FN =~ s#\.$## if ($FN ne '.'); } next if $FN =~ $File::Find::skip_pattern; # follow symbolic links / do an lstat $new_loc = Follow_SymLink($loc_pref.$FN); # ignore if invalid symlink unless (defined $new_loc) { if (!defined -l _ && $dangling_symlinks) { $fullname = undef; if (ref $dangling_symlinks eq 'CODE') { $dangling_symlinks->($FN, $dir_pref); } else { warnings::warnif "$dir_pref$FN is a dangling symbolic link\n"; } } else { $fullname = $loc_pref . $FN; } $name = $dir_pref . $FN; $_ = ($no_chdir ? $name : $FN); { $wanted_callback->() }; next; } if (-d _) { if ($Is_VMS) { $FN =~ s/\.dir\z//i; $FN =~ s#\.$## if ($FN ne '.'); $new_loc =~ s/\.dir\z//i; $new_loc =~ s#\.$## if ($new_loc ne '.'); } push @Stack,[$new_loc,$updir_loc,$dir_name,$FN,1]; } else { $fullname = $new_loc; # $File::Find::fullname $name = $dir_pref . $FN; # $File::Find::name $_ = ($no_chdir ? $name : $FN); # $_ { $wanted_callback->() }; # protect against wild "next" } } } continue { while (defined($SE = pop @Stack)) { ($dir_loc, $updir_loc, $p_dir, $dir_rel, $byd_flag) = @$SE; $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel"); $dir_pref = "$dir_name/"; $loc_pref = "$dir_loc/"; if ( $byd_flag < 0 ) { # must be finddepth, report dirname now unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) { unless (chdir $updir_loc) { # $updir_loc (parent dir) is always untainted warnings::warnif "Can't cd to $updir_loc: $!\n"; next; } } $fullname = $dir_loc; # $File::Find::fullname $name = $dir_name; # $File::Find::name if ( substr($name,-2) eq '/.' ) { substr($name, length($name) == 2 ? -1 : -2) = ''; # $File::Find::name } $dir = $p_dir; # $File::Find::dir $_ = ($no_chdir ? $dir_name : $dir_rel); # $_ if ( substr($_,-2) eq '/.' ) { substr($_, length($_) == 2 ? -1 : -2) = ''; } lstat($_); # make sure file tests with '_' work { $wanted_callback->() }; # protect against wild "next" } else { push @Stack,[$dir_loc, $updir_loc, $p_dir, $dir_rel,-1] if $bydepth; last; } } } } sub wrap_wanted { my $wanted = shift; if ( ref($wanted) eq 'HASH' ) { # RT #122547 my %valid_options = map {$_ => 1} qw( wanted bydepth preprocess postprocess follow follow_fast follow_skip dangling_symlinks no_chdir untaint untaint_pattern untaint_skip ); my @invalid_options = (); for my $v (keys %{$wanted}) { push @invalid_options, $v unless exists $valid_options{$v}; } warn "Invalid option(s): @invalid_options" if @invalid_options; unless( exists $wanted->{wanted} and ref( $wanted->{wanted} ) eq 'CODE' ) { die 'no &wanted subroutine given'; } if ( $wanted->{follow} || $wanted->{follow_fast}) { $wanted->{follow_skip} = 1 unless defined $wanted->{follow_skip}; } if ( $wanted->{untaint} ) { $wanted->{untaint_pattern} = $File::Find::untaint_pattern unless defined $wanted->{untaint_pattern}; $wanted->{untaint_skip} = 0 unless defined $wanted->{untaint_skip}; } return $wanted; } elsif( ref( $wanted ) eq 'CODE' ) { return { wanted => $wanted }; } else { die 'no &wanted subroutine given'; } } sub find { my $wanted = shift; _find_opt(wrap_wanted($wanted), @_); } sub finddepth { my $wanted = wrap_wanted(shift); $wanted->{bydepth} = 1; _find_opt($wanted, @_); } # default $File::Find::skip_pattern = qr/^\.{1,2}\z/; $File::Find::untaint_pattern = qr|^([-+@\w./]+)$|; # this _should_ work properly on all platforms # where File::Find can be expected to work $File::Find::current_dir = File::Spec->curdir || '.'; $File::Find::dont_use_nlink = 1; # We need a function that checks if a scalar is tainted. Either use the # Scalar::Util module's tainted() function or our (slower) pure Perl # fallback is_tainted_pp() { local $@; eval { require Scalar::Util }; *is_tainted = $@ ? \&is_tainted_pp : \&Scalar::Util::tainted; } 1; __END__ =head1 NAME File::Find - Traverse a directory tree. =head1 SYNOPSIS use File::Find; find(\&wanted, @directories_to_search); sub wanted { ... } use File::Find; finddepth(\&wanted, @directories_to_search); sub wanted { ... } use File::Find; find({ wanted => \&process, follow => 1 }, '.'); =head1 DESCRIPTION These are functions for searching through directory trees doing work on each file found similar to the Unix I<find> command. File::Find exports two functions, C<find> and C<finddepth>. They work similarly but have subtle differences. =over 4 =item B<find> find(\&wanted, @directories); find(\%options, @directories); C<find()> does a depth-first search over the given C<@directories> in the order they are given. For each file or directory found, it calls the C<&wanted> subroutine. (See below for details on how to use the C<&wanted> function). Additionally, for each directory found, it will C<chdir()> into that directory and continue the search, invoking the C<&wanted> function on each file or subdirectory in the directory. =item B<finddepth> finddepth(\&wanted, @directories); finddepth(\%options, @directories); C<finddepth()> works just like C<find()> except that it invokes the C<&wanted> function for a directory I<after> invoking it for the directory's contents. It does a postorder traversal instead of a preorder traversal, working from the bottom of the directory tree up where C<find()> works from the top of the tree down. =back Despite the name of the C<finddepth()> function, both C<find()> and C<finddepth()> perform a depth-first search of the directory hierarchy. =head2 %options The first argument to C<find()> is either a code reference to your C<&wanted> function, or a hash reference describing the operations to be performed for each file. The code reference is described in L</The wanted function> below. Here are the possible keys for the hash: =over 4 =item C<wanted> The value should be a code reference. This code reference is described in L</The wanted function> below. The C<&wanted> subroutine is mandatory. =item C<bydepth> Reports the name of a directory only AFTER all its entries have been reported. Entry point C<finddepth()> is a shortcut for specifying C<< { bydepth => 1 } >> in the first argument of C<find()>. =item C<preprocess> The value should be a code reference. This code reference is used to preprocess the current directory. The name of the currently processed directory is in C<$File::Find::dir>. Your preprocessing function is called after C<readdir()>, but before the loop that calls the C<wanted()> function. It is called with a list of strings (actually file/directory names) and is expected to return a list of strings. The code can be used to sort the file/directory names alphabetically, numerically, or to filter out directory entries based on their name alone. When I<follow> or I<follow_fast> are in effect, C<preprocess> is a no-op. =item C<postprocess> The value should be a code reference. It is invoked just before leaving the currently processed directory. It is called in void context with no arguments. The name of the current directory is in C<$File::Find::dir>. This hook is handy for summarizing a directory, such as calculating its disk usage. When I<follow> or I<follow_fast> are in effect, C<postprocess> is a no-op. =item C<follow> Causes symbolic links to be followed. Since directory trees with symbolic links (followed) may contain files more than once and may even have cycles, a hash has to be built up with an entry for each file. This might be expensive both in space and time for a large directory tree. See L</follow_fast> and L</follow_skip> below. If either I<follow> or I<follow_fast> is in effect: =over 4 =item * It is guaranteed that an I<lstat> has been called before the user's C<wanted()> function is called. This enables fast file checks involving C<_>. Note that this guarantee no longer holds if I<follow> or I<follow_fast> are not set. =item * There is a variable C<$File::Find::fullname> which holds the absolute pathname of the file with all symbolic links resolved. If the link is a dangling symbolic link, then fullname will be set to C<undef>. =back This is a no-op on Win32. =item C<follow_fast> This is similar to I<follow> except that it may report some files more than once. It does detect cycles, however. Since only symbolic links have to be hashed, this is much cheaper both in space and time. If processing a file more than once (by the user's C<wanted()> function) is worse than just taking time, the option I<follow> should be used. This is also a no-op on Win32. =item C<follow_skip> C<follow_skip==1>, which is the default, causes all files which are neither directories nor symbolic links to be ignored if they are about to be processed a second time. If a directory or a symbolic link are about to be processed a second time, File::Find dies. C<follow_skip==0> causes File::Find to die if any file is about to be processed a second time. C<follow_skip==2> causes File::Find to ignore any duplicate files and directories but to proceed normally otherwise. =item C<dangling_symlinks> Specifies what to do with symbolic links whose target doesn't exist. If true and a code reference, will be called with the symbolic link name and the directory it lives in as arguments. Otherwise, if true and warnings are on, a warning of the form C<"symbolic_link_name is a dangling symbolic link\n"> will be issued. If false, the dangling symbolic link will be silently ignored. =item C<no_chdir> Does not C<chdir()> to each directory as it recurses. The C<wanted()> function will need to be aware of this, of course. In this case, C<$_> will be the same as C<$File::Find::name>. =item C<untaint> If find is used in L<taint-mode|perlsec/Taint mode> (-T command line switch or if EUID != UID or if EGID != GID), then internally directory names have to be untainted before they can be C<chdir>'d to. Therefore they are checked against a regular expression I<untaint_pattern>. Note that all names passed to the user's C<wanted()> function are still tainted. If this option is used while not in taint-mode, C<untaint> is a no-op. =item C<untaint_pattern> See above. This should be set using the C<qr> quoting operator. The default is set to C<qr|^([-+@\w./]+)$|>. Note that the parentheses are vital. =item C<untaint_skip> If set, a directory which fails the I<untaint_pattern> is skipped, including all its sub-directories. The default is to C<die> in such a case. =back =head2 The wanted function The C<wanted()> function does whatever verifications you want on each file and directory. Note that despite its name, the C<wanted()> function is a generic callback function, and does B<not> tell File::Find if a file is "wanted" or not. In fact, its return value is ignored. The wanted function takes no arguments but rather does its work through a collection of variables. =over 4 =item C<$File::Find::dir> is the current directory name, =item C<$_> is the current filename within that directory =item C<$File::Find::name> is the complete pathname to the file. =back The above variables have all been localized and may be changed without affecting data outside of the wanted function. For example, when examining the file F</some/path/foo.ext> you will have: $File::Find::dir = /some/path/ $_ = foo.ext $File::Find::name = /some/path/foo.ext You are chdir()'d to C<$File::Find::dir> when the function is called, unless C<no_chdir> was specified. Note that when changing to directories is in effect, the root directory (F</>) is a somewhat special case inasmuch as the concatenation of C<$File::Find::dir>, C<'/'> and C<$_> is not literally equal to C<$File::Find::name>. The table below summarizes all variants: $File::Find::name $File::Find::dir $_ default / / . no_chdir=>0 /etc / etc /etc/x /etc x no_chdir=>1 / / / /etc / /etc /etc/x /etc /etc/x When C<follow> or C<follow_fast> are in effect, there is also a C<$File::Find::fullname>. The function may set C<$File::Find::prune> to prune the tree unless C<bydepth> was specified. Unless C<follow> or C<follow_fast> is specified, for compatibility reasons (find.pl, find2perl) there are in addition the following globals available: C<$File::Find::topdir>, C<$File::Find::topdev>, C<$File::Find::topino>, C<$File::Find::topmode> and C<$File::Find::topnlink>. This library is useful for the C<find2perl> tool (distributed as part of the App-find2perl CPAN distribution), which when fed, find2perl / -name .nfs\* -mtime +7 \ -exec rm -f {} \; -o -fstype nfs -prune produces something like: sub wanted { /^\.nfs.*\z/s && (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) && int(-M _) > 7 && unlink($_) || ($nlink || (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_))) && $dev < 0 && ($File::Find::prune = 1); } Notice the C<_> in the above C<int(-M _)>: the C<_> is a magical filehandle that caches the information from the preceding C<stat()>, C<lstat()>, or filetest. Here's another interesting wanted function. It will find all symbolic links that don't resolve: sub wanted { -l && !-e && print "bogus link: $File::Find::name\n"; } Note that you may mix directories and (non-directory) files in the list of directories to be searched by the C<wanted()> function. find(\&wanted, "./foo", "./bar", "./baz/epsilon"); In the example above, no file in F<./baz/> other than F<./baz/epsilon> will be evaluated by C<wanted()>. See also the script C<pfind> on CPAN for a nice application of this module. =head1 WARNINGS If you run your program with the C<-w> switch, or if you use the C<warnings> pragma, File::Find will report warnings for several weird situations. You can disable these warnings by putting the statement no warnings 'File::Find'; in the appropriate scope. See L<warnings> for more info about lexical warnings. =head1 BUGS AND CAVEATS =over 4 =item $dont_use_nlink You can set the variable C<$File::Find::dont_use_nlink> to 0 if you are sure the filesystem you are scanning reflects the number of subdirectories in the parent directory's C<nlink> count. If you do set C<$File::Find::dont_use_nlink> to 0, you may notice an improvement in speed at the risk of not recursing into subdirectories if a filesystem doesn't populate C<nlink> as expected. C<$File::Find::dont_use_nlink> now defaults to 1 on all platforms. =item symlinks Be aware that the option to follow symbolic links can be dangerous. Depending on the structure of the directory tree (including symbolic links to directories) you might traverse a given (physical) directory more than once (only if C<follow_fast> is in effect). Furthermore, deleting or changing files in a symbolically linked directory might cause very unpleasant surprises, since you delete or change files in an unknown directory. =back =head1 HISTORY File::Find used to produce incorrect results if called recursively. During the development of perl 5.8 this bug was fixed. The first fixed version of File::Find was 1.01. =head1 SEE ALSO L<find(1)>, find2perl. =cut PK ! ~ �C= C= GlobMapper.pmnu �[��� package File::GlobMapper; use strict; use warnings; use Carp; our ($CSH_GLOB); BEGIN { if ($] < 5.006) { require File::BSDGlob; import File::BSDGlob qw(:glob) ; $CSH_GLOB = File::BSDGlob::GLOB_CSH() ; *globber = \&File::BSDGlob::csh_glob; } else { require File::Glob; import File::Glob qw(:glob) ; $CSH_GLOB = File::Glob::GLOB_CSH() ; #*globber = \&File::Glob::bsd_glob; *globber = \&File::Glob::csh_glob; } } our ($Error); our ($VERSION, @EXPORT_OK); $VERSION = '1.001'; @EXPORT_OK = qw( globmap ); our ($noPreBS, $metachars, $matchMetaRE, %mapping, %wildCount); $noPreBS = '(?<!\\\)' ; # no preceding backslash $metachars = '.*?[](){}'; $matchMetaRE = '[' . quotemeta($metachars) . ']'; %mapping = ( '*' => '([^/]*)', '?' => '([^/])', '.' => '\.', '[' => '([', '(' => '(', ')' => ')', ); %wildCount = map { $_ => 1 } qw/ * ? . { ( [ /; sub globmap ($$;) { my $inputGlob = shift ; my $outputGlob = shift ; my $obj = File::GlobMapper->new($inputGlob, $outputGlob, @_) or croak "globmap: $Error" ; return $obj->getFileMap(); } sub new { my $class = shift ; my $inputGlob = shift ; my $outputGlob = shift ; # TODO -- flags needs to default to whatever File::Glob does my $flags = shift || $CSH_GLOB ; #my $flags = shift ; $inputGlob =~ s/^\s*\<\s*//; $inputGlob =~ s/\s*\>\s*$//; $outputGlob =~ s/^\s*\<\s*//; $outputGlob =~ s/\s*\>\s*$//; my %object = ( InputGlob => $inputGlob, OutputGlob => $outputGlob, GlobFlags => $flags, Braces => 0, WildCount => 0, Pairs => [], Sigil => '#', ); my $self = bless \%object, ref($class) || $class ; $self->_parseInputGlob() or return undef ; $self->_parseOutputGlob() or return undef ; my @inputFiles = globber($self->{InputGlob}, $flags) ; if (GLOB_ERROR) { $Error = $!; return undef ; } #if (whatever) { my $missing = grep { ! -e $_ } @inputFiles ; if ($missing) { $Error = "$missing input files do not exist"; return undef ; } } $self->{InputFiles} = \@inputFiles ; $self->_getFiles() or return undef ; return $self; } sub _retError { my $string = shift ; $Error = "$string in input fileglob" ; return undef ; } sub _unmatched { my $delimeter = shift ; _retError("Unmatched $delimeter"); return undef ; } sub _parseBit { my $self = shift ; my $string = shift ; my $out = ''; my $depth = 0 ; while ($string =~ s/(.*?)$noPreBS(,|$matchMetaRE)//) { $out .= quotemeta($1) ; $out .= $mapping{$2} if defined $mapping{$2}; ++ $self->{WildCount} if $wildCount{$2} ; if ($2 eq ',') { return _unmatched("(") if $depth ; $out .= '|'; } elsif ($2 eq '(') { ++ $depth ; } elsif ($2 eq ')') { return _unmatched(")") if ! $depth ; -- $depth ; } elsif ($2 eq '[') { # TODO -- quotemeta & check no '/' # TODO -- check for \] & other \ within the [] $string =~ s#(.*?\])## or return _unmatched("["); $out .= "$1)" ; } elsif ($2 eq ']') { return _unmatched("]"); } elsif ($2 eq '{' || $2 eq '}') { return _retError("Nested {} not allowed"); } } $out .= quotemeta $string; return _unmatched("(") if $depth ; return $out ; } sub _parseInputGlob { my $self = shift ; my $string = $self->{InputGlob} ; my $inGlob = ''; # Multiple concatenated *'s don't make sense #$string =~ s#\*\*+#*# ; # TODO -- Allow space to delimit patterns? #my @strings = split /\s+/, $string ; #for my $str (@strings) my $out = ''; my $depth = 0 ; while ($string =~ s/(.*?)$noPreBS($matchMetaRE)//) { $out .= quotemeta($1) ; $out .= $mapping{$2} if defined $mapping{$2}; ++ $self->{WildCount} if $wildCount{$2} ; if ($2 eq '(') { ++ $depth ; } elsif ($2 eq ')') { return _unmatched(")") if ! $depth ; -- $depth ; } elsif ($2 eq '[') { # TODO -- quotemeta & check no '/' or '(' or ')' # TODO -- check for \] & other \ within the [] $string =~ s#(.*?\])## or return _unmatched("["); $out .= "$1)" ; } elsif ($2 eq ']') { return _unmatched("]"); } elsif ($2 eq '}') { return _unmatched("}"); } elsif ($2 eq '{') { # TODO -- check no '/' within the {} # TODO -- check for \} & other \ within the {} my $tmp ; unless ( $string =~ s/(.*?)$noPreBS\}//) { return _unmatched("{"); } #$string =~ s#(.*?)\}##; #my $alt = join '|', # map { quotemeta $_ } # split "$noPreBS,", $1 ; my $alt = $self->_parseBit($1); defined $alt or return 0 ; $out .= "($alt)" ; ++ $self->{Braces} ; } } return _unmatched("(") if $depth ; $out .= quotemeta $string ; $self->{InputGlob} =~ s/$noPreBS[\(\)]//g; $self->{InputPattern} = $out ; #print "# INPUT '$self->{InputGlob}' => '$out'\n"; return 1 ; } sub _parseOutputGlob { my $self = shift ; my $string = $self->{OutputGlob} ; my $maxwild = $self->{WildCount}; if ($self->{GlobFlags} & GLOB_TILDE) #if (1) { $string =~ s{ ^ ~ # find a leading tilde ( # save this in $1 [^/] # a non-slash character * # repeated 0 or more times (0 means me) ) }{ $1 ? (getpwnam($1))[7] : ( $ENV{HOME} || $ENV{LOGDIR} ) }ex; } # max #1 must be == to max no of '*' in input while ( $string =~ m/#(\d)/g ) { croak "Max wild is #$maxwild, you tried #$1" if $1 > $maxwild ; } my $noPreBS = '(?<!\\\)' ; # no preceding backslash #warn "noPreBS = '$noPreBS'\n"; #$string =~ s/${noPreBS}\$(\d)/\${$1}/g; $string =~ s/${noPreBS}#(\d)/\${$1}/g; $string =~ s#${noPreBS}\*#\${inFile}#g; $string = '"' . $string . '"'; #print "OUTPUT '$self->{OutputGlob}' => '$string'\n"; $self->{OutputPattern} = $string ; return 1 ; } sub _getFiles { my $self = shift ; my %outInMapping = (); my %inFiles = () ; foreach my $inFile (@{ $self->{InputFiles} }) { next if $inFiles{$inFile} ++ ; my $outFile = $inFile ; if ( $inFile =~ m/$self->{InputPattern}/ ) { no warnings 'uninitialized'; eval "\$outFile = $self->{OutputPattern};" ; if (defined $outInMapping{$outFile}) { $Error = "multiple input files map to one output file"; return undef ; } $outInMapping{$outFile} = $inFile; push @{ $self->{Pairs} }, [$inFile, $outFile]; } } return 1 ; } sub getFileMap { my $self = shift ; return $self->{Pairs} ; } sub getHash { my $self = shift ; return { map { $_->[0] => $_->[1] } @{ $self->{Pairs} } } ; } 1; __END__ =head1 NAME File::GlobMapper - Extend File Glob to Allow Input and Output Files =head1 SYNOPSIS use File::GlobMapper qw( globmap ); my $aref = globmap $input => $output or die $File::GlobMapper::Error ; my $gm = File::GlobMapper->new( $input => $output ) or die $File::GlobMapper::Error ; =head1 DESCRIPTION This module needs Perl5.005 or better. This module takes the existing C<File::Glob> module as a starting point and extends it to allow new filenames to be derived from the files matched by C<File::Glob>. This can be useful when carrying out batch operations on multiple files that have both an input filename and output filename and the output file can be derived from the input filename. Examples of operations where this can be useful include, file renaming, file copying and file compression. =head2 Behind The Scenes To help explain what C<File::GlobMapper> does, consider what code you would write if you wanted to rename all files in the current directory that ended in C<.tar.gz> to C<.tgz>. So say these files are in the current directory alpha.tar.gz beta.tar.gz gamma.tar.gz and they need renamed to this alpha.tgz beta.tgz gamma.tgz Below is a possible implementation of a script to carry out the rename (error cases have been omitted) foreach my $old ( glob "*.tar.gz" ) { my $new = $old; $new =~ s#(.*)\.tar\.gz$#$1.tgz# ; rename $old => $new or die "Cannot rename '$old' to '$new': $!\n; } Notice that a file glob pattern C<*.tar.gz> was used to match the C<.tar.gz> files, then a fairly similar regular expression was used in the substitute to allow the new filename to be created. Given that the file glob is just a cut-down regular expression and that it has already done a lot of the hard work in pattern matching the filenames, wouldn't it be handy to be able to use the patterns in the fileglob to drive the new filename? Well, that's I<exactly> what C<File::GlobMapper> does. Here is same snippet of code rewritten using C<globmap> for my $pair (globmap '<*.tar.gz>' => '<#1.tgz>' ) { my ($from, $to) = @$pair; rename $from => $to or die "Cannot rename '$old' to '$new': $!\n; } So how does it work? Behind the scenes the C<globmap> function does a combination of a file glob to match existing filenames followed by a substitute to create the new filenames. Notice how both parameters to C<globmap> are strings that are delimited by <>. This is done to make them look more like file globs - it is just syntactic sugar, but it can be handy when you want the strings to be visually distinctive. The enclosing <> are optional, so you don't have to use them - in fact the first thing globmap will do is remove these delimiters if they are present. The first parameter to C<globmap>, C<*.tar.gz>, is an I<Input File Glob>. Once the enclosing "< ... >" is removed, this is passed (more or less) unchanged to C<File::Glob> to carry out a file match. Next the fileglob C<*.tar.gz> is transformed behind the scenes into a full Perl regular expression, with the additional step of wrapping each transformed wildcard metacharacter sequence in parenthesis. In this case the input fileglob C<*.tar.gz> will be transformed into this Perl regular expression ([^/]*)\.tar\.gz Wrapping with parenthesis allows the wildcard parts of the Input File Glob to be referenced by the second parameter to C<globmap>, C<#1.tgz>, the I<Output File Glob>. This parameter operates just like the replacement part of a substitute command. The difference is that the C<#1> syntax is used to reference sub-patterns matched in the input fileglob, rather than the C<$1> syntax that is used with perl regular expressions. In this case C<#1> is used to refer to the text matched by the C<*> in the Input File Glob. This makes it easier to use this module where the parameters to C<globmap> are typed at the command line. The final step involves passing each filename matched by the C<*.tar.gz> file glob through the derived Perl regular expression in turn and expanding the output fileglob using it. The end result of all this is a list of pairs of filenames. By default that is what is returned by C<globmap>. In this example the data structure returned will look like this ( ['alpha.tar.gz' => 'alpha.tgz'], ['beta.tar.gz' => 'beta.tgz' ], ['gamma.tar.gz' => 'gamma.tgz'] ) Each pair is an array reference with two elements - namely the I<from> filename, that C<File::Glob> has matched, and a I<to> filename that is derived from the I<from> filename. =head2 Limitations C<File::GlobMapper> has been kept simple deliberately, so it isn't intended to solve all filename mapping operations. Under the hood C<File::Glob> (or for older versions of Perl, C<File::BSDGlob>) is used to match the files, so you will never have the flexibility of full Perl regular expression. =head2 Input File Glob The syntax for an Input FileGlob is identical to C<File::Glob>, except for the following =over 5 =item 1. No nested {} =item 2. Whitespace does not delimit fileglobs. =item 3. The use of parenthesis can be used to capture parts of the input filename. =item 4. If an Input glob matches the same file more than once, only the first will be used. =back The syntax =over 5 =item B<~> =item B<~user> =item B<.> Matches a literal '.'. Equivalent to the Perl regular expression \. =item B<*> Matches zero or more characters, except '/'. Equivalent to the Perl regular expression [^/]* =item B<?> Matches zero or one character, except '/'. Equivalent to the Perl regular expression [^/]? =item B<\> Backslash is used, as usual, to escape the next character. =item B<[]> Character class. =item B<{,}> Alternation =item B<()> Capturing parenthesis that work just like perl =back Any other character it taken literally. =head2 Output File Glob The Output File Glob is a normal string, with 2 glob-like features. The first is the '*' metacharacter. This will be replaced by the complete filename matched by the input file glob. So *.c *.Z The second is Output FileGlobs take the =over 5 =item "*" The "*" character will be replaced with the complete input filename. =item #1 Patterns of the form /#\d/ will be replaced with the =back =head2 Returned Data =head1 EXAMPLES =head2 A Rename script Below is a simple "rename" script that uses C<globmap> to determine the source and destination filenames. use File::GlobMapper qw(globmap) ; use File::Copy; die "rename: Usage rename 'from' 'to'\n" unless @ARGV == 2 ; my $fromGlob = shift @ARGV; my $toGlob = shift @ARGV; my $pairs = globmap($fromGlob, $toGlob) or die $File::GlobMapper::Error; for my $pair (@$pairs) { my ($from, $to) = @$pair; move $from => $to ; } Here is an example that renames all c files to cpp. $ rename '*.c' '#1.cpp' =head2 A few example globmaps Below are a few examples of globmaps To copy all your .c file to a backup directory '</my/home/*.c>' '</my/backup/#1.c>' If you want to compress all '</my/home/*.[ch]>' '<*.gz>' To uncompress '</my/home/*.[ch].gz>' '</my/home/#1.#2>' =head1 SEE ALSO L<File::Glob|File::Glob> =head1 AUTHOR The I<File::GlobMapper> module was written by Paul Marquess, F<pmqs@cpan.org>. =head1 COPYRIGHT AND LICENSE Copyright (c) 2005 Paul Marquess. All rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. PK ! �U�y� y� Fetch.pmnu �[��� package File::Fetch; use strict; use FileHandle; use File::Temp; use File::Copy; use File::Spec; use File::Spec::Unix; use File::Basename qw[dirname]; use Cwd qw[cwd]; use Carp qw[carp]; use IPC::Cmd qw[can_run run QUOTE]; use File::Path qw[mkpath]; use File::Temp qw[tempdir]; use Params::Check qw[check]; use Module::Load::Conditional qw[can_load]; use Locale::Maketext::Simple Style => 'gettext'; use vars qw[ $VERBOSE $PREFER_BIN $FROM_EMAIL $USER_AGENT $BLACKLIST $METHOD_FAIL $VERSION $METHODS $FTP_PASSIVE $TIMEOUT $DEBUG $WARN $FORCEIPV4 ]; $VERSION = '1.00'; $VERSION = eval $VERSION; # avoid warnings with development releases $PREFER_BIN = 0; # XXX TODO implement $FROM_EMAIL = 'File-Fetch@example.com'; $USER_AGENT = "File::Fetch/$VERSION"; $BLACKLIST = [qw|ftp|]; push @$BLACKLIST, qw|lftp| if $^O eq 'dragonfly' || $^O eq 'hpux'; $METHOD_FAIL = { }; $FTP_PASSIVE = 1; $TIMEOUT = 0; $DEBUG = 0; $WARN = 1; $FORCEIPV4 = 0; ### methods available to fetch the file depending on the scheme $METHODS = { http => [ qw|lwp httptiny wget curl lftp fetch httplite lynx iosock| ], https => [ qw|lwp wget curl| ], ftp => [ qw|lwp netftp wget curl lftp fetch ncftp ftp| ], file => [ qw|lwp lftp file| ], rsync => [ qw|rsync| ], git => [ qw|git| ], }; ### silly warnings ### local $Params::Check::VERBOSE = 1; local $Params::Check::VERBOSE = 1; local $Module::Load::Conditional::VERBOSE = 0; local $Module::Load::Conditional::VERBOSE = 0; ### see what OS we are on, important for file:// uris ### use constant ON_WIN => ($^O eq 'MSWin32'); use constant ON_VMS => ($^O eq 'VMS'); use constant ON_UNIX => (!ON_WIN); use constant HAS_VOL => (ON_WIN); use constant HAS_SHARE => (ON_WIN); use constant HAS_FETCH => ( $^O =~ m!^(freebsd|netbsd|dragonfly)$! ); =pod =head1 NAME File::Fetch - A generic file fetching mechanism =head1 SYNOPSIS use File::Fetch; ### build a File::Fetch object ### my $ff = File::Fetch->new(uri => 'http://some.where.com/dir/a.txt'); ### fetch the uri to cwd() ### my $where = $ff->fetch() or die $ff->error; ### fetch the uri to /tmp ### my $where = $ff->fetch( to => '/tmp' ); ### parsed bits from the uri ### $ff->uri; $ff->scheme; $ff->host; $ff->path; $ff->file; =head1 DESCRIPTION File::Fetch is a generic file fetching mechanism. It allows you to fetch any file pointed to by a C<ftp>, C<http>, C<file>, C<git> or C<rsync> uri by a number of different means. See the C<HOW IT WORKS> section further down for details. =head1 ACCESSORS A C<File::Fetch> object has the following accessors =over 4 =item $ff->uri The uri you passed to the constructor =item $ff->scheme The scheme from the uri (like 'file', 'http', etc) =item $ff->host The hostname in the uri. Will be empty if host was originally 'localhost' for a 'file://' url. =item $ff->vol On operating systems with the concept of a volume the second element of a file:// is considered to the be volume specification for the file. Thus on Win32 this routine returns the volume, on other operating systems this returns nothing. On Windows this value may be empty if the uri is to a network share, in which case the 'share' property will be defined. Additionally, volume specifications that use '|' as ':' will be converted on read to use ':'. On VMS, which has a volume concept, this field will be empty because VMS file specifications are converted to absolute UNIX format and the volume information is transparently included. =item $ff->share On systems with the concept of a network share (currently only Windows) returns the sharename from a file://// url. On other operating systems returns empty. =item $ff->path The path from the uri, will be at least a single '/'. =item $ff->file The name of the remote file. For the local file name, the result of $ff->output_file will be used. =item $ff->file_default The name of the default local file, that $ff->output_file falls back to if it would otherwise return no filename. For example when fetching a URI like http://www.abc.net.au/ the contents retrieved may be from a remote file called 'index.html'. The default value of this attribute is literally 'file_default'. =cut ########################## ### Object & Accessors ### ########################## { ### template for autogenerated accessors ### my $Tmpl = { scheme => { default => 'http' }, host => { default => 'localhost' }, path => { default => '/' }, file => { required => 1 }, uri => { required => 1 }, userinfo => { default => '' }, vol => { default => '' }, # windows for file:// uris share => { default => '' }, # windows for file:// uris file_default => { default => 'file_default' }, tempdir_root => { required => 1 }, # Should be lazy-set at ->new() _error_msg => { no_override => 1 }, _error_msg_long => { no_override => 1 }, }; for my $method ( keys %$Tmpl ) { no strict 'refs'; *$method = sub { my $self = shift; $self->{$method} = $_[0] if @_; return $self->{$method}; } } sub _create { my $class = shift; my %hash = @_; my $args = check( $Tmpl, \%hash ) or return; bless $args, $class; if( lc($args->scheme) ne 'file' and not $args->host ) { return $class->_error(loc( "Hostname required when fetching from '%1'",$args->scheme)); } for (qw[path]) { unless( $args->$_() ) { # 5.5.x needs the () return $class->_error(loc("No '%1' specified",$_)); } } return $args; } } =item $ff->output_file The name of the output file. This is the same as $ff->file, but any query parameters are stripped off. For example: http://example.com/index.html?x=y would make the output file be C<index.html> rather than C<index.html?x=y>. =back =cut sub output_file { my $self = shift; my $file = $self->file; $file =~ s/\?.*$//g; $file ||= $self->file_default; return $file; } ### XXX do this or just point to URI::Escape? # =head2 $esc_uri = $ff->escaped_uri # # =cut # # ### most of this is stolen straight from URI::escape # { ### Build a char->hex map # my %escapes = map { chr($_) => sprintf("%%%02X", $_) } 0..255; # # sub escaped_uri { # my $self = shift; # my $uri = $self->uri; # # ### Default unsafe characters. RFC 2732 ^(uric - reserved) # $uri =~ s/([^A-Za-z0-9\-_.!~*'()])/ # $escapes{$1} || $self->_fail_hi($1)/ge; # # return $uri; # } # # sub _fail_hi { # my $self = shift; # my $char = shift; # # $self->_error(loc( # "Can't escape '%1', try using the '%2' module instead", # sprintf("\\x{%04X}", ord($char)), 'URI::Escape' # )); # } # # sub output_file { # # } # # # } =head1 METHODS =head2 $ff = File::Fetch->new( uri => 'http://some.where.com/dir/file.txt' ); Parses the uri and creates a corresponding File::Fetch::Item object, that is ready to be C<fetch>ed and returns it. Returns false on failure. =cut sub new { my $class = shift; my %hash = @_; my ($uri, $file_default, $tempdir_root); my $tmpl = { uri => { required => 1, store => \$uri }, file_default => { required => 0, store => \$file_default }, tempdir_root => { required => 0, store => \$tempdir_root }, }; check( $tmpl, \%hash ) or return; ### parse the uri to usable parts ### my $href = $class->_parse_uri( $uri ) or return; $href->{file_default} = $file_default if $file_default; $href->{tempdir_root} = File::Spec->rel2abs( $tempdir_root ) if $tempdir_root; $href->{tempdir_root} = File::Spec->rel2abs( Cwd::cwd ) if not $href->{tempdir_root}; ### make it into a FFI object ### my $ff = $class->_create( %$href ) or return; ### return the object ### return $ff; } ### parses an uri to a hash structure: ### ### $class->_parse_uri( 'ftp://ftp.cpan.org/pub/mirror/index.txt' ) ### ### becomes: ### ### $href = { ### scheme => 'ftp', ### host => 'ftp.cpan.org', ### path => '/pub/mirror', ### file => 'index.html' ### }; ### ### In the case of file:// urls there maybe be additional fields ### ### For systems with volume specifications such as Win32 there will be ### a volume specifier provided in the 'vol' field. ### ### 'vol' => 'volumename' ### ### For windows file shares there may be a 'share' key specified ### ### 'share' => 'sharename' ### ### Note that the rules of what a file:// url means vary by the operating system ### of the host being addressed. Thus file:///d|/foo/bar.txt means the obvious ### 'D:\foo\bar.txt' on windows, but on unix it means '/d|/foo/bar.txt' and ### not '/foo/bar.txt' ### ### Similarly if the host interpreting the url is VMS then ### file:///disk$user/my/notes/note12345.txt' means ### 'DISK$USER:[MY.NOTES]NOTE123456.TXT' but will be returned the same as ### if it is unix where it means /disk$user/my/notes/note12345.txt'. ### Except for some cases in the File::Spec methods, Perl on VMS will generally ### handle UNIX format file specifications. ### ### This means it is impossible to serve certain file:// urls on certain systems. ### ### Thus are the problems with a protocol-less specification. :-( ### sub _parse_uri { my $self = shift; my $uri = shift or return; my $href = { uri => $uri }; ### find the scheme ### $uri =~ s|^(\w+)://||; $href->{scheme} = $1; ### See rfc 1738 section 3.10 ### http://www.faqs.org/rfcs/rfc1738.html ### And wikipedia for more on windows file:// urls ### http://en.wikipedia.org/wiki/File:// if( $href->{scheme} eq 'file' ) { my @parts = split '/',$uri; ### file://hostname/... ### file://hostname/... ### normalize file://localhost with file:/// $href->{host} = $parts[0] || ''; ### index in @parts where the path components begin; my $index = 1; ### file:////hostname/sharename/blah.txt if ( HAS_SHARE and not length $parts[0] and not length $parts[1] ) { $href->{host} = $parts[2] || ''; # avoid warnings $href->{share} = $parts[3] || ''; # avoid warnings $index = 4 # index after the share ### file:///D|/blah.txt ### file:///D:/blah.txt } elsif (HAS_VOL) { ### this code comes from dmq's patch, but: ### XXX if volume is empty, wouldn't that be an error? --kane ### if so, our file://localhost test needs to be fixed as wel $href->{vol} = $parts[1] || ''; ### correct D| style colume descriptors $href->{vol} =~ s/\A([A-Z])\|\z/$1:/i if ON_WIN; $index = 2; # index after the volume } ### rebuild the path from the leftover parts; $href->{path} = join '/', '', splice( @parts, $index, $#parts ); } else { ### using anything but qw() in hash slices may produce warnings ### in older perls :-( @{$href}{ qw(userinfo host path) } = $uri =~ m|(?:([^\@:]*:[^\:\@]*)@)?([^/]*)(/.*)$|s; } ### split the path into file + dir ### { my @parts = File::Spec::Unix->splitpath( delete $href->{path} ); $href->{path} = $parts[1]; $href->{file} = $parts[2]; } ### host will be empty if the target was 'localhost' and the ### scheme was 'file' $href->{host} = '' if ($href->{host} eq 'localhost') and ($href->{scheme} eq 'file'); return $href; } =head2 $where = $ff->fetch( [to => /my/output/dir/ | \$scalar] ) Fetches the file you requested and returns the full path to the file. By default it writes to C<cwd()>, but you can override that by specifying the C<to> argument: ### file fetch to /tmp, full path to the file in $where $where = $ff->fetch( to => '/tmp' ); ### file slurped into $scalar, full path to the file in $where ### file is downloaded to a temp directory and cleaned up at exit time $where = $ff->fetch( to => \$scalar ); Returns the full path to the downloaded file on success, and false on failure. =cut sub fetch { my $self = shift or return; my %hash = @_; my $target; my $tmpl = { to => { default => cwd(), store => \$target }, }; check( $tmpl, \%hash ) or return; my ($to, $fh); ### you want us to slurp the contents if( ref $target and UNIVERSAL::isa( $target, 'SCALAR' ) ) { $to = tempdir( 'FileFetch.XXXXXX', DIR => $self->tempdir_root, CLEANUP => 1 ); ### plain old fetch } else { $to = $target; ### On VMS force to VMS format so File::Spec will work. $to = VMS::Filespec::vmspath($to) if ON_VMS; ### create the path if it doesn't exist yet ### unless( -d $to ) { eval { mkpath( $to ) }; return $self->_error(loc("Could not create path '%1'",$to)) if $@; } } ### set passive ftp if required ### local $ENV{FTP_PASSIVE} = $FTP_PASSIVE; ### we dont use catfile on win32 because if we are using a cygwin tool ### under cmd.exe they wont understand windows style separators. my $out_to = ON_WIN ? $to.'/'.$self->output_file : File::Spec->catfile( $to, $self->output_file ); for my $method ( @{ $METHODS->{$self->scheme} } ) { my $sub = '_'.$method.'_fetch'; unless( __PACKAGE__->can($sub) ) { $self->_error(loc("Cannot call method for '%1' -- WEIRD!", $method)); next; } ### method is blacklisted ### next if grep { lc $_ eq $method } @$BLACKLIST; ### method is known to fail ### next if $METHOD_FAIL->{$method}; ### there's serious issues with IPC::Run and quoting of command ### line arguments. using quotes in the wrong place breaks things, ### and in the case of say, ### C:\cygwin\bin\wget.EXE --quiet --passive-ftp --output-document ### "index.html" "http://www.cpan.org/index.html?q=1&y=2" ### it doesn't matter how you quote, it always fails. local $IPC::Cmd::USE_IPC_RUN = 0; if( my $file = $self->$sub( to => $out_to )){ unless( -e $file && -s _ ) { $self->_error(loc("'%1' said it fetched '%2', ". "but it was not created",$method,$file)); ### mark the failure ### $METHOD_FAIL->{$method} = 1; next; } else { ### slurp mode? if( ref $target and UNIVERSAL::isa( $target, 'SCALAR' ) ) { ### open the file open my $fh, "<$file" or do { $self->_error( loc("Could not open '%1': %2", $file, $!)); return; }; ### slurp $$target = do { local $/; <$fh> }; } my $abs = File::Spec->rel2abs( $file ); return $abs; } } } ### if we got here, we looped over all methods, but we weren't able ### to fetch it. return; } ######################## ### _*_fetch methods ### ######################## ### LWP fetching ### sub _lwp_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### modules required to download with lwp ### my $use_list = { LWP => '0.0', 'LWP::UserAgent' => '0.0', 'HTTP::Request' => '0.0', 'HTTP::Status' => '0.0', URI => '0.0', }; if ($self->scheme eq 'https') { $use_list->{'LWP::Protocol::https'} = '0'; } ### Fix CVE-2016-1238 ### local $Module::Load::Conditional::FORCE_SAFE_INC = 1; unless( can_load( modules => $use_list ) ) { $METHOD_FAIL->{'lwp'} = 1; return; } ### setup the uri object my $uri = URI->new( File::Spec::Unix->catfile( $self->path, $self->file ) ); ### special rules apply for file:// uris ### $uri->scheme( $self->scheme ); $uri->host( $self->scheme eq 'file' ? '' : $self->host ); if ($self->userinfo) { $uri->userinfo($self->userinfo); } elsif ($self->scheme ne 'file') { $uri->userinfo("anonymous:$FROM_EMAIL"); } ### set up the useragent object my $ua = LWP::UserAgent->new(); $ua->timeout( $TIMEOUT ) if $TIMEOUT; $ua->agent( $USER_AGENT ); $ua->from( $FROM_EMAIL ); $ua->env_proxy; my $res = $ua->mirror($uri, $to) or return; ### uptodate or fetched ok ### if ( $res->code == 304 or $res->code == 200 ) { return $to; } else { return $self->_error(loc("Fetch failed! HTTP response: %1 %2 [%3]", $res->code, HTTP::Status::status_message($res->code), $res->status_line)); } } ### HTTP::Tiny fetching ### sub _httptiny_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; my $use_list = { 'HTTP::Tiny' => '0.008', }; ### Fix CVE-2016-1238 ### local $Module::Load::Conditional::FORCE_SAFE_INC = 1; unless( can_load(modules => $use_list) ) { $METHOD_FAIL->{'httptiny'} = 1; return; } my $uri = $self->uri; my $http = HTTP::Tiny->new( ( $TIMEOUT ? ( timeout => $TIMEOUT ) : () ) ); my $rc = $http->mirror( $uri, $to ); unless ( $rc->{success} ) { return $self->_error(loc( "Fetch failed! HTTP response: %1 [%2]", $rc->{status}, $rc->{reason} ) ); } return $to; } ### HTTP::Lite fetching ### sub _httplite_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### modules required to download with lwp ### my $use_list = { 'HTTP::Lite' => '2.2', 'MIME::Base64' => '0', }; ### Fix CVE-2016-1238 ### local $Module::Load::Conditional::FORCE_SAFE_INC = 1; unless( can_load(modules => $use_list) ) { $METHOD_FAIL->{'httplite'} = 1; return; } my $uri = $self->uri; my $retries = 0; RETRIES: while ( $retries++ < 5 ) { my $http = HTTP::Lite->new(); # Naughty naughty but there isn't any accessor/setter $http->{timeout} = $TIMEOUT if $TIMEOUT; $http->http11_mode(1); if ($self->userinfo) { my $encoded = MIME::Base64::encode($self->userinfo, ''); $http->add_req_header("Authorization", "Basic $encoded"); } my $fh = FileHandle->new; unless ( $fh->open($to,'>') ) { return $self->_error(loc( "Could not open '%1' for writing: %2",$to,$!)); } $fh->autoflush(1); binmode $fh; my $rc = $http->request( $uri, sub { my ($self,$dref,$cbargs) = @_; local $\; print {$cbargs} $$dref }, $fh ); close $fh; if ( $rc == 301 || $rc == 302 ) { my $loc; HEADERS: for ($http->headers_array) { /Location: (\S+)/ and $loc = $1, last HEADERS; } #$loc or last; # Think we should squeal here. if ($loc =~ m!^/!) { $uri =~ s{^(\w+?://[^/]+)/.*$}{$1}; $uri .= $loc; } else { $uri = $loc; } next RETRIES; } elsif ( $rc == 200 ) { return $to; } else { return $self->_error(loc("Fetch failed! HTTP response: %1 [%2]", $rc, $http->status_message)); } } # Loop for 5 retries. return $self->_error("Fetch failed! Gave up after 5 tries"); } ### Simple IO::Socket::INET fetching ### sub _iosock_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; my $use_list = { 'IO::Socket::INET' => '0.0', 'IO::Select' => '0.0', }; ### Fix CVE-2016-1238 ### local $Module::Load::Conditional::FORCE_SAFE_INC = 1; unless( can_load(modules => $use_list) ) { $METHOD_FAIL->{'iosock'} = 1; return; } my $sock = IO::Socket::INET->new( PeerHost => $self->host, ( $self->host =~ /:/ ? () : ( PeerPort => 80 ) ), ); unless ( $sock ) { return $self->_error(loc("Could not open socket to '%1', '%2'",$self->host,$!)); } my $fh = FileHandle->new; # Check open() unless ( $fh->open($to,'>') ) { return $self->_error(loc( "Could not open '%1' for writing: %2",$to,$!)); } $fh->autoflush(1); binmode $fh; my $path = File::Spec::Unix->catfile( $self->path, $self->file ); my $req = "GET $path HTTP/1.0\x0d\x0aHost: " . $self->host . "\x0d\x0a\x0d\x0a"; $sock->send( $req ); my $select = IO::Select->new( $sock ); my $resp = ''; my $normal = 0; while ( $select->can_read( $TIMEOUT || 60 ) ) { my $ret = $sock->sysread( $resp, 4096, length($resp) ); if ( !defined $ret or $ret == 0 ) { $select->remove( $sock ); $normal++; } } close $sock; unless ( $normal ) { return $self->_error(loc("Socket timed out after '%1' seconds", ( $TIMEOUT || 60 ))); } # Check the "response" # Strip preceding blank lines apparently they are allowed (RFC 2616 4.1) $resp =~ s/^(\x0d?\x0a)+//; # Check it is an HTTP response unless ( $resp =~ m!^HTTP/(\d+)\.(\d+)!i ) { return $self->_error(loc("Did not get a HTTP response from '%1'",$self->host)); } # Check for OK my ($code) = $resp =~ m!^HTTP/\d+\.\d+\s+(\d+)!i; unless ( $code eq '200' ) { return $self->_error(loc("Got a '%1' from '%2' expected '200'",$code,$self->host)); } { local $\; print $fh +($resp =~ m/\x0d\x0a\x0d\x0a(.*)$/s )[0]; } close $fh; return $to; } ### Net::FTP fetching sub _netftp_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### required modules ### my $use_list = { 'Net::FTP' => 0 }; ### Fix CVE-2016-1238 ### local $Module::Load::Conditional::FORCE_SAFE_INC = 1; unless( can_load( modules => $use_list ) ) { $METHOD_FAIL->{'netftp'} = 1; return; } ### make connection ### my $ftp; my @options = ($self->host); push(@options, Timeout => $TIMEOUT) if $TIMEOUT; unless( $ftp = Net::FTP->new( @options ) ) { return $self->_error(loc("Ftp creation failed: %1",$@)); } ### login ### unless( $ftp->login( anonymous => $FROM_EMAIL ) ) { return $self->_error(loc("Could not login to '%1'",$self->host)); } ### set binary mode, just in case ### $ftp->binary; ### create the remote path ### remember remote paths are unix paths! [#11483] my $remote = File::Spec::Unix->catfile( $self->path, $self->file ); ### fetch the file ### my $target; unless( $target = $ftp->get( $remote, $to ) ) { return $self->_error(loc("Could not fetch '%1' from '%2'", $remote, $self->host)); } ### log out ### $ftp->quit; return $target; } ### /bin/wget fetch ### sub _wget_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; my $wget; ### see if we have a wget binary ### unless( $wget = can_run('wget') ) { $METHOD_FAIL->{'wget'} = 1; return; } ### no verboseness, thanks ### my $cmd = [ $wget, '--quiet' ]; ### if a timeout is set, add it ### push(@$cmd, '--timeout=' . $TIMEOUT) if $TIMEOUT; ### run passive if specified ### push @$cmd, '--passive-ftp' if $FTP_PASSIVE; ### set the output document, add the uri ### push @$cmd, '--output-document', $to, $self->uri; ### with IPC::Cmd > 0.41, this is fixed in teh library, ### and there's no need for special casing any more. ### DO NOT quote things for IPC::Run, it breaks stuff. # $IPC::Cmd::USE_IPC_RUN # ? ($to, $self->uri) # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE); ### shell out ### my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG )) { ### wget creates the output document always, even if the fetch ### fails.. so unlink it in that case 1 while unlink $to; return $self->_error(loc( "Command failed: %1", $captured || '' )); } return $to; } ### /bin/lftp fetch ### sub _lftp_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### see if we have a lftp binary ### my $lftp; unless( $lftp = can_run('lftp') ) { $METHOD_FAIL->{'lftp'} = 1; return; } ### no verboseness, thanks ### my $cmd = [ $lftp, '-f' ]; my $fh = File::Temp->new; my $str; ### if a timeout is set, add it ### $str .= "set net:timeout $TIMEOUT;\n" if $TIMEOUT; ### run passive if specified ### $str .= "set ftp:passive-mode 1;\n" if $FTP_PASSIVE; ### set the output document, add the uri ### ### quote the URI, because lftp supports certain shell ### expansions, most notably & for backgrounding. ### ' quote does nto work, must be " $str .= q[get ']. $self->uri .q[' -o ]. $to . $/; if( $DEBUG ) { my $pp_str = join ' ', split $/, $str; print "# lftp command: $pp_str\n"; } ### write straight to the file. $fh->autoflush(1); print $fh $str; ### the command needs to be 1 string to be executed push @$cmd, $fh->filename; ### with IPC::Cmd > 0.41, this is fixed in teh library, ### and there's no need for special casing any more. ### DO NOT quote things for IPC::Run, it breaks stuff. # $IPC::Cmd::USE_IPC_RUN # ? ($to, $self->uri) # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE); ### shell out ### my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG )) { ### wget creates the output document always, even if the fetch ### fails.. so unlink it in that case 1 while unlink $to; return $self->_error(loc( "Command failed: %1", $captured || '' )); } return $to; } ### /bin/ftp fetch ### sub _ftp_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### see if we have a ftp binary ### my $ftp; unless( $ftp = can_run('ftp') ) { $METHOD_FAIL->{'ftp'} = 1; return; } my $fh = FileHandle->new; local $SIG{CHLD} = 'IGNORE'; unless ($fh->open("$ftp -n", '|-')) { return $self->_error(loc("%1 creation failed: %2", $ftp, $!)); } my @dialog = ( "lcd " . dirname($to), "open " . $self->host, "user anonymous $FROM_EMAIL", "cd /", "cd " . $self->path, "binary", "get " . $self->file . " " . $self->output_file, "quit", ); foreach (@dialog) { $fh->print($_, "\n") } $fh->close or return; return $to; } ### lynx is stupid - it decompresses any .gz file it finds to be text ### use /bin/lynx to fetch files sub _lynx_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### see if we have a lynx binary ### my $lynx; unless ( $lynx = can_run('lynx') ){ $METHOD_FAIL->{'lynx'} = 1; return; } unless( IPC::Cmd->can_capture_buffer ) { $METHOD_FAIL->{'lynx'} = 1; return $self->_error(loc( "Can not capture buffers. Can not use '%1' to fetch files", 'lynx' )); } ### check if the HTTP resource exists ### if ($self->uri =~ /^https?:\/\//i) { my $cmd = [ $lynx, '-head', '-source', "-auth=anonymous:$FROM_EMAIL", ]; push @$cmd, "-connect_timeout=$TIMEOUT" if $TIMEOUT; push @$cmd, $self->uri; ### shell out ### my $head; unless(run( command => $cmd, buffer => \$head, verbose => $DEBUG ) ) { return $self->_error(loc("Command failed: %1", $head || '')); } unless($head =~ /^HTTP\/\d+\.\d+ 200\b/) { return $self->_error(loc("Command failed: %1", $head || '')); } } ### write to the output file ourselves, since lynx ass_u_mes to much my $local = FileHandle->new( $to, 'w' ) or return $self->_error(loc( "Could not open '%1' for writing: %2",$to,$!)); ### dump to stdout ### my $cmd = [ $lynx, '-source', "-auth=anonymous:$FROM_EMAIL", ]; push @$cmd, "-connect_timeout=$TIMEOUT" if $TIMEOUT; ### DO NOT quote things for IPC::Run, it breaks stuff. push @$cmd, $self->uri; ### with IPC::Cmd > 0.41, this is fixed in teh library, ### and there's no need for special casing any more. ### DO NOT quote things for IPC::Run, it breaks stuff. # $IPC::Cmd::USE_IPC_RUN # ? $self->uri # : QUOTE. $self->uri .QUOTE; ### shell out ### my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG ) ) { return $self->_error(loc("Command failed: %1", $captured || '')); } ### print to local file ### ### XXX on a 404 with a special error page, $captured will actually ### hold the contents of that page, and make it *appear* like the ### request was a success, when really it wasn't :( ### there doesn't seem to be an option for lynx to change the exit ### code based on a 4XX status or so. ### the closest we can come is using --error_file and parsing that, ### which is very unreliable ;( $local->print( $captured ); $local->close or return; return $to; } ### use /bin/ncftp to fetch files sub _ncftp_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### we can only set passive mode in interactive sessions, so bail out ### if $FTP_PASSIVE is set return if $FTP_PASSIVE; ### see if we have a ncftp binary ### my $ncftp; unless( $ncftp = can_run('ncftp') ) { $METHOD_FAIL->{'ncftp'} = 1; return; } my $cmd = [ $ncftp, '-V', # do not be verbose '-p', $FROM_EMAIL, # email as password $self->host, # hostname dirname($to), # local dir for the file # remote path to the file ### DO NOT quote things for IPC::Run, it breaks stuff. $IPC::Cmd::USE_IPC_RUN ? File::Spec::Unix->catdir( $self->path, $self->file ) : QUOTE. File::Spec::Unix->catdir( $self->path, $self->file ) .QUOTE ]; ### shell out ### my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG ) ) { return $self->_error(loc("Command failed: %1", $captured || '')); } return $to; } ### use /bin/curl to fetch files sub _curl_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; my $curl; unless ( $curl = can_run('curl') ) { $METHOD_FAIL->{'curl'} = 1; return; } ### these long opts are self explanatory - I like that -jmb my $cmd = [ $curl, '-q' ]; push(@$cmd, '-4') if $^O eq 'netbsd' && $FORCEIPV4; # only seen this on NetBSD so far push(@$cmd, '--connect-timeout', $TIMEOUT) if $TIMEOUT; push(@$cmd, '--silent') unless $DEBUG; ### curl does the right thing with passive, regardless ### if ($self->scheme eq 'ftp') { push(@$cmd, '--user', "anonymous:$FROM_EMAIL"); } ### curl doesn't follow 302 (temporarily moved) etc automatically ### so we add --location to enable that. push @$cmd, '--fail', '--location', '--output', $to, $self->uri; ### with IPC::Cmd > 0.41, this is fixed in teh library, ### and there's no need for special casing any more. ### DO NOT quote things for IPC::Run, it breaks stuff. # $IPC::Cmd::USE_IPC_RUN # ? ($to, $self->uri) # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE); my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG ) ) { return $self->_error(loc("Command failed: %1", $captured || '')); } return $to; } ### /usr/bin/fetch fetch! ### sub _fetch_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### see if we have a fetch binary ### my $fetch; unless( HAS_FETCH and $fetch = can_run('fetch') ) { $METHOD_FAIL->{'fetch'} = 1; return; } ### no verboseness, thanks ### my $cmd = [ $fetch, '-q' ]; ### if a timeout is set, add it ### push(@$cmd, '-T', $TIMEOUT) if $TIMEOUT; ### run passive if specified ### #push @$cmd, '-p' if $FTP_PASSIVE; local $ENV{'FTP_PASSIVE_MODE'} = 1 if $FTP_PASSIVE; ### set the output document, add the uri ### push @$cmd, '-o', $to, $self->uri; ### with IPC::Cmd > 0.41, this is fixed in teh library, ### and there's no need for special casing any more. ### DO NOT quote things for IPC::Run, it breaks stuff. # $IPC::Cmd::USE_IPC_RUN # ? ($to, $self->uri) # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE); ### shell out ### my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG )) { ### wget creates the output document always, even if the fetch ### fails.. so unlink it in that case 1 while unlink $to; return $self->_error(loc( "Command failed: %1", $captured || '' )); } return $to; } ### use File::Copy for fetching file:// urls ### ### ### See section 3.10 of RFC 1738 (http://www.faqs.org/rfcs/rfc1738.html) ### Also see wikipedia on file:// (http://en.wikipedia.org/wiki/File://) ### sub _file_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; ### prefix a / on unix systems with a file uri, since it would ### look somewhat like this: ### file:///home/kane/file ### whereas windows file uris for 'c:\some\dir\file' might look like: ### file:///C:/some/dir/file ### file:///C|/some/dir/file ### or for a network share '\\host\share\some\dir\file': ### file:////host/share/some/dir/file ### ### VMS file uri's for 'DISK$USER:[MY.NOTES]NOTE123456.TXT' might look like: ### file://vms.host.edu/disk$user/my/notes/note12345.txt ### my $path = $self->path; my $vol = $self->vol; my $share = $self->share; my $remote; if (!$share and $self->host) { return $self->_error(loc( "Currently %1 cannot handle hosts in %2 urls", 'File::Fetch', 'file://' )); } if( $vol ) { $path = File::Spec->catdir( split /\//, $path ); $remote = File::Spec->catpath( $vol, $path, $self->file); } elsif( $share ) { ### win32 specific, and a share name, so we wont bother with File::Spec $path =~ s|/+|\\|g; $remote = "\\\\".$self->host."\\$share\\$path"; } else { ### File::Spec on VMS can not currently handle UNIX syntax. my $file_class = ON_VMS ? 'File::Spec::Unix' : 'File::Spec'; $remote = $file_class->catfile( $path, $self->file ); } ### File::Copy is littered with 'die' statements :( ### my $rv = eval { File::Copy::copy( $remote, $to ) }; ### something went wrong ### if( !$rv or $@ ) { return $self->_error(loc("Could not copy '%1' to '%2': %3 %4", $remote, $to, $!, $@)); } return $to; } ### use /usr/bin/rsync to fetch files sub _rsync_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; my $rsync; unless ( $rsync = can_run('rsync') ) { $METHOD_FAIL->{'rsync'} = 1; return; } my $cmd = [ $rsync ]; ### XXX: rsync has no I/O timeouts at all, by default push(@$cmd, '--timeout=' . $TIMEOUT) if $TIMEOUT; push(@$cmd, '--quiet') unless $DEBUG; ### DO NOT quote things for IPC::Run, it breaks stuff. push @$cmd, $self->uri, $to; ### with IPC::Cmd > 0.41, this is fixed in teh library, ### and there's no need for special casing any more. ### DO NOT quote things for IPC::Run, it breaks stuff. # $IPC::Cmd::USE_IPC_RUN # ? ($to, $self->uri) # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE); my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG ) ) { return $self->_error(loc("Command %1 failed: %2", "@$cmd" || '', $captured || '')); } return $to; } ### use git to fetch files sub _git_fetch { my $self = shift; my %hash = @_; my ($to); my $tmpl = { to => { required => 1, store => \$to } }; check( $tmpl, \%hash ) or return; my $git; unless ( $git = can_run('git') ) { $METHOD_FAIL->{'git'} = 1; return; } my $cmd = [ $git, 'clone' ]; #push(@$cmd, '--timeout=' . $TIMEOUT) if $TIMEOUT; push(@$cmd, '--quiet') unless $DEBUG; ### DO NOT quote things for IPC::Run, it breaks stuff. push @$cmd, $self->uri, $to; ### with IPC::Cmd > 0.41, this is fixed in teh library, ### and there's no need for special casing any more. ### DO NOT quote things for IPC::Run, it breaks stuff. # $IPC::Cmd::USE_IPC_RUN # ? ($to, $self->uri) # : (QUOTE. $to .QUOTE, QUOTE. $self->uri .QUOTE); my $captured; unless(run( command => $cmd, buffer => \$captured, verbose => $DEBUG ) ) { return $self->_error(loc("Command %1 failed: %2", "@$cmd" || '', $captured || '')); } return $to; } ################################# # # Error code # ################################# =pod =head2 $ff->error([BOOL]) Returns the last encountered error as string. Pass it a true value to get the C<Carp::longmess()> output instead. =cut ### error handling the way Archive::Extract does it sub _error { my $self = shift; my $error = shift; $self->_error_msg( $error ); $self->_error_msg_long( Carp::longmess($error) ); if( $WARN ) { carp $DEBUG ? $self->_error_msg_long : $self->_error_msg; } return; } sub error { my $self = shift; return shift() ? $self->_error_msg_long : $self->_error_msg; } 1; =pod =head1 HOW IT WORKS File::Fetch is able to fetch a variety of uris, by using several external programs and modules. Below is a mapping of what utilities will be used in what order for what schemes, if available: file => LWP, lftp, file http => LWP, HTTP::Tiny, wget, curl, lftp, fetch, HTTP::Lite, lynx, iosock ftp => LWP, Net::FTP, wget, curl, lftp, fetch, ncftp, ftp rsync => rsync git => git If you'd like to disable the use of one or more of these utilities and/or modules, see the C<$BLACKLIST> variable further down. If a utility or module isn't available, it will be marked in a cache (see the C<$METHOD_FAIL> variable further down), so it will not be tried again. The C<fetch> method will only fail when all options are exhausted, and it was not able to retrieve the file. The C<fetch> utility is available on FreeBSD. NetBSD and Dragonfly BSD may also have it from C<pkgsrc>. We only check for C<fetch> on those three platforms. C<iosock> is a very limited L<IO::Socket::INET> based mechanism for retrieving C<http> schemed urls. It doesn't follow redirects for instance. C<git> only supports C<git://> style urls. A special note about fetching files from an ftp uri: By default, all ftp connections are done in passive mode. To change that, see the C<$FTP_PASSIVE> variable further down. Furthermore, ftp uris only support anonymous connections, so no named user/password pair can be passed along. C</bin/ftp> is blacklisted by default; see the C<$BLACKLIST> variable further down. =head1 GLOBAL VARIABLES The behaviour of File::Fetch can be altered by changing the following global variables: =head2 $File::Fetch::FROM_EMAIL This is the email address that will be sent as your anonymous ftp password. Default is C<File-Fetch@example.com>. =head2 $File::Fetch::USER_AGENT This is the useragent as C<LWP> will report it. Default is C<File::Fetch/$VERSION>. =head2 $File::Fetch::FTP_PASSIVE This variable controls whether the environment variable C<FTP_PASSIVE> and any passive switches to commandline tools will be set to true. Default value is 1. Note: When $FTP_PASSIVE is true, C<ncftp> will not be used to fetch files, since passive mode can only be set interactively for this binary =head2 $File::Fetch::TIMEOUT When set, controls the network timeout (counted in seconds). Default value is 0. =head2 $File::Fetch::WARN This variable controls whether errors encountered internally by C<File::Fetch> should be C<carp>'d or not. Set to false to silence warnings. Inspect the output of the C<error()> method manually to see what went wrong. Defaults to C<true>. =head2 $File::Fetch::DEBUG This enables debugging output when calling commandline utilities to fetch files. This also enables C<Carp::longmess> errors, instead of the regular C<carp> errors. Good for tracking down why things don't work with your particular setup. Default is 0. =head2 $File::Fetch::BLACKLIST This is an array ref holding blacklisted modules/utilities for fetching files with. To disallow the use of, for example, C<LWP> and C<Net::FTP>, you could set $File::Fetch::BLACKLIST to: $File::Fetch::BLACKLIST = [qw|lwp netftp|] The default blacklist is [qw|ftp|], as C</bin/ftp> is rather unreliable. See the note on C<MAPPING> below. =head2 $File::Fetch::METHOD_FAIL This is a hashref registering what modules/utilities were known to fail for fetching files (mostly because they weren't installed). You can reset this cache by assigning an empty hashref to it, or individually remove keys. See the note on C<MAPPING> below. =head1 MAPPING Here's a quick mapping for the utilities/modules, and their names for the $BLACKLIST, $METHOD_FAIL and other internal functions. LWP => lwp HTTP::Lite => httplite HTTP::Tiny => httptiny Net::FTP => netftp wget => wget lynx => lynx ncftp => ncftp ftp => ftp curl => curl rsync => rsync lftp => lftp fetch => fetch IO::Socket => iosock =head1 FREQUENTLY ASKED QUESTIONS =head2 So how do I use a proxy with File::Fetch? C<File::Fetch> currently only supports proxies with LWP::UserAgent. You will need to set your environment variables accordingly. For example, to use an ftp proxy: $ENV{ftp_proxy} = 'foo.com'; Refer to the LWP::UserAgent manpage for more details. =head2 I used 'lynx' to fetch a file, but its contents is all wrong! C<lynx> can only fetch remote files by dumping its contents to C<STDOUT>, which we in turn capture. If that content is a 'custom' error file (like, say, a C<404 handler>), you will get that contents instead. Sadly, C<lynx> doesn't support any options to return a different exit code on non-C<200 OK> status, giving us no way to tell the difference between a 'successful' fetch and a custom error page. Therefor, we recommend to only use C<lynx> as a last resort. This is why it is at the back of our list of methods to try as well. =head2 Files I'm trying to fetch have reserved characters or non-ASCII characters in them. What do I do? C<File::Fetch> is relatively smart about things. When trying to write a file to disk, it removes the C<query parameters> (see the C<output_file> method for details) from the file name before creating it. In most cases this suffices. If you have any other characters you need to escape, please install the C<URI::Escape> module from CPAN, and pre-encode your URI before passing it to C<File::Fetch>. You can read about the details of URIs and URI encoding here: http://www.faqs.org/rfcs/rfc2396.html =head1 TODO =over 4 =item Implement $PREFER_BIN To indicate to rather use commandline tools than modules =back =head1 BUG REPORTS Please report bugs or other issues to E<lt>bug-file-fetch@rt.cpan.org<gt>. =head1 AUTHOR This module by Jos Boumans E<lt>kane@cpan.orgE<gt>. =head1 COPYRIGHT This library is free software; you may redistribute and/or modify it under the same terms as Perl itself. =cut # Local variables: # c-indentation-style: bsd # c-basic-offset: 4 # indent-tabs-mode: nil # End: # vim: expandtab shiftwidth=4: PK ! �tڪ� � Compare.pmnu �[��� package File::Compare; use 5.006; use strict; use warnings; our($VERSION, @ISA, @EXPORT, @EXPORT_OK, $Too_Big); require Exporter; $VERSION = '1.1006'; @ISA = qw(Exporter); @EXPORT = qw(compare); @EXPORT_OK = qw(cmp compare_text); $Too_Big = 1024 * 1024 * 2; sub croak { require Carp; goto &Carp::croak; } sub compare { croak("Usage: compare( file1, file2 [, buffersize]) ") unless(@_ == 2 || @_ == 3); my ($from,$to,$size) = @_; my $text_mode = defined($size) && (ref($size) eq 'CODE' || $size < 0); my ($fromsize,$closefrom,$closeto); local (*FROM, *TO); croak("from undefined") unless (defined $from); croak("to undefined") unless (defined $to); if (ref($from) && (UNIVERSAL::isa($from,'GLOB') || UNIVERSAL::isa($from,'IO::Handle'))) { *FROM = *$from; } elsif (ref(\$from) eq 'GLOB') { *FROM = $from; } else { open(FROM,"<",$from) or goto fail_open1; unless ($text_mode) { binmode FROM; $fromsize = -s FROM; } $closefrom = 1; } if (ref($to) && (UNIVERSAL::isa($to,'GLOB') || UNIVERSAL::isa($to,'IO::Handle'))) { *TO = *$to; } elsif (ref(\$to) eq 'GLOB') { *TO = $to; } else { open(TO,"<",$to) or goto fail_open2; binmode TO unless $text_mode; $closeto = 1; } if (!$text_mode && $closefrom && $closeto) { # If both are opened files we know they differ if their size differ goto fail_inner if $fromsize != -s TO; } if ($text_mode) { local $/ = "\n"; my ($fline,$tline); while (defined($fline = <FROM>)) { goto fail_inner unless defined($tline = <TO>); if (ref $size) { # $size contains ref to comparison function goto fail_inner if &$size($fline, $tline); } else { goto fail_inner if $fline ne $tline; } } goto fail_inner if defined($tline = <TO>); } else { unless (defined($size) && $size > 0) { $size = $fromsize || -s TO || 0; $size = 1024 if $size < 512; $size = $Too_Big if $size > $Too_Big; } my ($fr,$tr,$fbuf,$tbuf); $fbuf = $tbuf = ''; while(defined($fr = read(FROM,$fbuf,$size)) && $fr > 0) { unless (defined($tr = read(TO,$tbuf,$fr)) && $tbuf eq $fbuf) { goto fail_inner; } } goto fail_inner if defined($tr = read(TO,$tbuf,$size)) && $tr > 0; } close(TO) || goto fail_open2 if $closeto; close(FROM) || goto fail_open1 if $closefrom; return 0; # All of these contortions try to preserve error messages... fail_inner: close(TO) || goto fail_open2 if $closeto; close(FROM) || goto fail_open1 if $closefrom; return 1; fail_open2: if ($closefrom) { my $status = $!; $! = 0; close FROM; $! = $status unless $!; } fail_open1: return -1; } sub cmp; *cmp = \&compare; sub compare_text { my ($from,$to,$cmp) = @_; croak("Usage: compare_text( file1, file2 [, cmp-function])") unless @_ == 2 || @_ == 3; croak("Third arg to compare_text() function must be a code reference") if @_ == 3 && ref($cmp) ne 'CODE'; # Using a negative buffer size puts compare into text_mode too $cmp = -1 unless defined $cmp; compare($from, $to, $cmp); } 1; __END__ =head1 NAME File::Compare - Compare files or filehandles =head1 SYNOPSIS use File::Compare; if (compare("file1","file2") == 0) { print "They're equal\n"; } =head1 DESCRIPTION The File::Compare::compare function compares the contents of two sources, each of which can be a file or a file handle. It is exported from File::Compare by default. File::Compare::cmp is a synonym for File::Compare::compare. It is exported from File::Compare only by request. File::Compare::compare_text does a line by line comparison of the two files. It stops as soon as a difference is detected. compare_text() accepts an optional third argument: This must be a CODE reference to a line comparison function, which returns 0 when both lines are considered equal. For example: compare_text($file1, $file2) is basically equivalent to compare_text($file1, $file2, sub {$_[0] ne $_[1]} ) =head1 RETURN File::Compare::compare and its sibling functions return 0 if the files are equal, 1 if the files are unequal, or -1 if an error was encountered. =head1 AUTHOR File::Compare was written by Nick Ing-Simmons. Its original documentation was written by Chip Salzenberg. =cut PK ! ���P �P Find/Rule.pmnu �[��� # $Id$ package File::Find::Rule; use strict; use File::Spec; use Text::Glob 'glob_to_regex'; use Number::Compare; use Carp qw/croak/; use File::Find (); # we're only wrapping for now our $VERSION = '0.34'; # we'd just inherit from Exporter, but I want the colon sub import { my $pkg = shift; my $to = caller; for my $sym ( qw( find rule ) ) { no strict 'refs'; *{"$to\::$sym"} = \&{$sym}; } for (grep /^:/, @_) { my ($extension) = /^:(.*)/; eval "require File::Find::Rule::$extension"; croak "couldn't bootstrap File::Find::Rule::$extension: $@" if $@; } } =head1 NAME File::Find::Rule - Alternative interface to File::Find =head1 SYNOPSIS use File::Find::Rule; # find all the subdirectories of a given directory my @subdirs = File::Find::Rule->directory->in( $directory ); # find all the .pm files in @INC my @files = File::Find::Rule->file() ->name( '*.pm' ) ->in( @INC ); # as above, but without method chaining my $rule = File::Find::Rule->new; $rule->file; $rule->name( '*.pm' ); my @files = $rule->in( @INC ); =head1 DESCRIPTION File::Find::Rule is a friendlier interface to File::Find. It allows you to build rules which specify the desired files and directories. =cut # the procedural shim *rule = \&find; sub find { my $object = __PACKAGE__->new(); my $not = 0; while (@_) { my $method = shift; my @args; if ($method =~ s/^\!//) { # jinkies, we're really negating this unshift @_, $method; $not = 1; next; } unless (defined prototype $method) { my $args = shift; @args = ref $args eq 'ARRAY' ? @$args : $args; } if ($not) { $not = 0; @args = $object->new->$method(@args); $method = "not"; } my @return = $object->$method(@args); return @return if $method eq 'in'; } $object; } =head1 METHODS =over =item C<new> A constructor. You need not invoke C<new> manually unless you wish to, as each of the rule-making methods will auto-create a suitable object if called as class methods. =cut sub new { my $referent = shift; my $class = ref $referent || $referent; bless { rules => [], subs => {}, iterator => [], extras => {}, maxdepth => undef, mindepth => undef, }, $class; } sub _force_object { my $object = shift; $object = $object->new() unless ref $object; $object; } =back =head2 Matching Rules =over =item C<name( @patterns )> Specifies names that should match. May be globs or regular expressions. $set->name( '*.mp3', '*.ogg' ); # mp3s or oggs $set->name( qr/\.(mp3|ogg)$/ ); # the same as a regex $set->name( 'foo.bar' ); # just things named foo.bar =cut sub _flatten { my @flat; while (@_) { my $item = shift; ref $item eq 'ARRAY' ? push @_, @{ $item } : push @flat, $item; } return @flat; } sub name { my $self = _force_object shift; my @names = map { ref $_ eq "Regexp" ? $_ : glob_to_regex $_ } _flatten( @_ ); push @{ $self->{rules} }, { rule => 'name', code => join( ' || ', map { "m{$_}" } @names ), args => \@_, }; $self; } =item -X tests Synonyms are provided for each of the -X tests. See L<perlfunc/-X> for details. None of these methods take arguments. Test | Method Test | Method ------|------------- ------|---------------- -r | readable -R | r_readable -w | writeable -W | r_writeable -w | writable -W | r_writable -x | executable -X | r_executable -o | owned -O | r_owned | | -e | exists -f | file -z | empty -d | directory -s | nonempty -l | symlink | -p | fifo -u | setuid -S | socket -g | setgid -b | block -k | sticky -c | character | -t | tty -M | modified | -A | accessed -T | ascii -C | changed -B | binary Though some tests are fairly meaningless as binary flags (C<modified>, C<accessed>, C<changed>), they have been included for completeness. # find nonempty files $rule->file, ->nonempty; =cut use vars qw( %X_tests ); %X_tests = ( -r => readable => -R => r_readable => -w => writeable => -W => r_writeable => -w => writable => -W => r_writable => -x => executable => -X => r_executable => -o => owned => -O => r_owned => -e => exists => -f => file => -z => empty => -d => directory => -s => nonempty => -l => symlink => => -p => fifo => -u => setuid => -S => socket => -g => setgid => -b => block => -k => sticky => -c => character => => -t => tty => -M => modified => -A => accessed => -T => ascii => -C => changed => -B => binary => ); for my $test (keys %X_tests) { my $sub = eval 'sub () { my $self = _force_object shift; push @{ $self->{rules} }, { code => "' . $test . ' \$_", rule => "'.$X_tests{$test}.'", }; $self; } '; no strict 'refs'; *{ $X_tests{$test} } = $sub; } =item stat tests The following C<stat> based methods are provided: C<dev>, C<ino>, C<mode>, C<nlink>, C<uid>, C<gid>, C<rdev>, C<size>, C<atime>, C<mtime>, C<ctime>, C<blksize>, and C<blocks>. See L<perlfunc/stat> for details. Each of these can take a number of targets, which will follow L<Number::Compare> semantics. $rule->size( 7 ); # exactly 7 $rule->size( ">7Ki" ); # larger than 7 * 1024 * 1024 bytes $rule->size( ">=7" ) ->size( "<=90" ); # between 7 and 90, inclusive $rule->size( 7, 9, 42 ); # 7, 9 or 42 =cut use vars qw( @stat_tests ); @stat_tests = qw( dev ino mode nlink uid gid rdev size atime mtime ctime blksize blocks ); { my $i = 0; for my $test (@stat_tests) { my $index = $i++; # to close over my $sub = sub { my $self = _force_object shift; my @tests = map { Number::Compare->parse_to_perl($_) } @_; push @{ $self->{rules} }, { rule => $test, args => \@_, code => 'do { my $val = (stat $_)['.$index.'] || 0;'. join ('||', map { "(\$val $_)" } @tests ).' }', }; $self; }; no strict 'refs'; *$test = $sub; } } =item C<any( @rules )> =item C<or( @rules )> Allows shortcircuiting boolean evaluation as an alternative to the default and-like nature of combined rules. C<any> and C<or> are interchangeable. # find avis, movs, things over 200M and empty files $rule->any( File::Find::Rule->name( '*.avi', '*.mov' ), File::Find::Rule->size( '>200M' ), File::Find::Rule->file->empty, ); =cut sub any { my $self = _force_object shift; # compile all the subrules to code fragments push @{ $self->{rules} }, { rule => "any", code => '(' . join( ' || ', map '( ' . $_->_compile . ' )', @_ ). ')', args => \@_, }; # merge all the subs hashes of the kids into ourself %{ $self->{subs} } = map { %{ $_->{subs} } } $self, @_; $self; } *or = \&any; =item C<none( @rules )> =item C<not( @rules )> Negates a rule. (The inverse of C<any>.) C<none> and C<not> are interchangeable. # files that aren't 8.3 safe $rule->file ->not( $rule->new->name( qr/^[^.]{1,8}(\.[^.]{0,3})?$/ ) ); =cut sub not { my $self = _force_object shift; push @{ $self->{rules} }, { rule => 'not', args => \@_, code => '(' . join ( ' && ', map { "!(". $_->_compile . ")" } @_ ) . ")", }; # merge all the subs hashes into us %{ $self->{subs} } = map { %{ $_->{subs} } } $self, @_; $self; } *none = \¬ =item C<prune> Traverse no further. This rule always matches. =cut sub prune () { my $self = _force_object shift; push @{ $self->{rules} }, { rule => 'prune', code => '$File::Find::prune = 1' }; $self; } =item C<discard> Don't keep this file. This rule always matches. =cut sub discard () { my $self = _force_object shift; push @{ $self->{rules} }, { rule => 'discard', code => '$discarded = 1', }; $self; } =item C<exec( \&subroutine( $shortname, $path, $fullname ) )> Allows user-defined rules. Your subroutine will be invoked with C<$_> set to the current short name, and with parameters of the name, the path you're in, and the full relative filename. Return a true value if your rule matched. # get things with long names $rules->exec( sub { length > 20 } ); =cut sub exec { my $self = _force_object shift; my $code = shift; push @{ $self->{rules} }, { rule => 'exec', code => $code, }; $self; } =item C<grep( @specifiers )> Opens a file and tests it each line at a time. For each line it evaluates each of the specifiers, stopping at the first successful match. A specifier may be a regular expression or a subroutine. The subroutine will be invoked with the same parameters as an ->exec subroutine. It is possible to provide a set of negative specifiers by enclosing them in anonymous arrays. Should a negative specifier match the iteration is aborted and the clause is failed. For example: $rule->grep( qr/^#!.*\bperl/, [ sub { 1 } ] ); Is a passing clause if the first line of a file looks like a perl shebang line. =cut sub grep { my $self = _force_object shift; my @pattern = map { ref $_ ? ref $_ eq 'ARRAY' ? map { [ ( ref $_ ? $_ : qr/$_/ ) => 0 ] } @$_ : [ $_ => 1 ] : [ qr/$_/ => 1 ] } @_; $self->exec( sub { local *FILE; open FILE, $_ or return; local ($_, $.); while (<FILE>) { for my $p (@pattern) { my ($rule, $ret) = @$p; return $ret if ref $rule eq 'Regexp' ? /$rule/ : $rule->(@_); } } return; } ); } =item C<maxdepth( $level )> Descend at most C<$level> (a non-negative integer) levels of directories below the starting point. May be invoked many times per rule, but only the most recent value is used. =item C<mindepth( $level )> Do not apply any tests at levels less than C<$level> (a non-negative integer). =item C<extras( \%extras )> Specifies extra values to pass through to C<File::File::find> as part of the options hash. For example this allows you to specify following of symlinks like so: my $rule = File::Find::Rule->extras({ follow => 1 }); May be invoked many times per rule, but only the most recent value is used. =cut for my $setter (qw( maxdepth mindepth extras )) { my $sub = sub { my $self = _force_object shift; $self->{$setter} = shift; $self; }; no strict 'refs'; *$setter = $sub; } =item C<relative> Trim the leading portion of any path found =cut sub relative () { my $self = _force_object shift; $self->{relative} = 1; $self; } =item C<canonpath> Normalize paths found using C<File::Spec->canonpath>. This will return paths with a file-seperator that is native to your OS (as determined by L<File::Spec>), instead of the default C</>. For example, this will return C<tmp/foobar> on Unix-ish OSes and C<tmp\foobar> on Win32. =cut sub canonpath () { my $self = _force_object shift; $self->{canonpath} = 1; $self; } =item C<not_*> Negated version of the rule. An effective shortand related to ! in the procedural interface. $foo->not_name('*.pl'); $foo->not( $foo->new->name('*.pl' ) ); =cut sub DESTROY {} sub AUTOLOAD { our $AUTOLOAD; $AUTOLOAD =~ /::not_([^:]*)$/ or croak "Can't locate method $AUTOLOAD"; my $method = $1; my $sub = sub { my $self = _force_object shift; $self->not( $self->new->$method(@_) ); }; { no strict 'refs'; *$AUTOLOAD = $sub; } &$sub; } =back =head2 Query Methods =over =item C<in( @directories )> Evaluates the rule, returns a list of paths to matching files and directories. =cut sub in { my $self = _force_object shift; my @found; my $fragment = $self->_compile; my %subs = %{ $self->{subs} }; warn "relative mode handed multiple paths - that's a bit silly\n" if $self->{relative} && @_ > 1; my $topdir; my $code = 'sub { (my $path = $File::Find::name) =~ s#^(?:\./+)+##; $path = "." if ($path eq ""); # See Debian bug #329377 my @args = ($_, $File::Find::dir, $path); my $maxdepth = $self->{maxdepth}; my $mindepth = $self->{mindepth}; my $relative = $self->{relative}; my $canonpath = $self->{canonpath}; # figure out the relative path and depth my $relpath = $File::Find::name; $relpath =~ s{^\Q$topdir\E/?}{}; my $depth = scalar File::Spec->splitdir($relpath); #print "name: \'$File::Find::name\' "; #print "relpath: \'$relpath\' depth: $depth relative: $relative\n"; defined $maxdepth && $depth >= $maxdepth and $File::Find::prune = 1; defined $mindepth && $depth < $mindepth and return; #print "Testing \'$_\'\n"; my $discarded; return unless ' . $fragment . '; return if $discarded; if ($relative) { if ($relpath ne "") { push @found, $canonpath ? File::Spec->canonpath($relpath) : $relpath; } } else { push @found, $canonpath ? File::Spec->canonpath($path) : $path; } }'; #use Data::Dumper; #print Dumper \%subs; #warn "Compiled sub: '$code'\n"; my $sub = eval "$code" or die "compile error '$code' $@"; for my $path (@_) { # $topdir is used for relative and maxdepth $topdir = $path; # slice off the trailing slash if there is one (the # maxdepth/mindepth code is fussy) $topdir =~ s{/?$}{} unless $topdir eq '/'; $self->_call_find( { %{ $self->{extras} }, wanted => $sub }, $path ); } return @found; } sub _call_find { my $self = shift; File::Find::find( @_ ); } sub _compile { my $self = shift; return '1' unless @{ $self->{rules} }; my $code = join " && ", map { if (ref $_->{code}) { my $key = "$_->{code}"; $self->{subs}{$key} = $_->{code}; "\$subs{'$key'}->(\@args) # $_->{rule}\n"; } else { "( $_->{code} ) # $_->{rule}\n"; } } @{ $self->{rules} }; #warn $code; return $code; } =item C<start( @directories )> Starts a find across the specified directories. Matching items may then be queried using L</match>. This allows you to use a rule as an iterator. my $rule = File::Find::Rule->file->name("*.jpeg")->start( "/web" ); while ( defined ( my $image = $rule->match ) ) { ... } =cut sub start { my $self = _force_object shift; $self->{iterator} = [ $self->in( @_ ) ]; $self; } =item C<match> Returns the next file which matches, false if there are no more. =cut sub match { my $self = _force_object shift; return shift @{ $self->{iterator} }; } 1; __END__ =back =head2 Extensions Extension modules are available from CPAN in the File::Find::Rule namespace. In order to use these extensions either use them directly: use File::Find::Rule::ImageSize; use File::Find::Rule::MMagic; # now your rules can use the clauses supplied by the ImageSize and # MMagic extension or, specify that File::Find::Rule should load them for you: use File::Find::Rule qw( :ImageSize :MMagic ); For notes on implementing your own extensions, consult L<File::Find::Rule::Extending> =head2 Further examples =over =item Finding perl scripts my $finder = File::Find::Rule->or ( File::Find::Rule->name( '*.pl' ), File::Find::Rule->exec( sub { if (open my $fh, $_) { my $shebang = <$fh>; close $fh; return $shebang =~ /^#!.*\bperl/; } return 0; } ), ); Based upon this message http://use.perl.org/comments.pl?sid=7052&cid=10842 =item ignore CVS directories my $rule = File::Find::Rule->new; $rule->or($rule->new ->directory ->name('CVS') ->prune ->discard, $rule->new); Note here the use of a null rule. Null rules match anything they see, so the effect is to match (and discard) directories called 'CVS' or to match anything. =back =head1 TWO FOR THE PRICE OF ONE File::Find::Rule also gives you a procedural interface. This is documented in L<File::Find::Rule::Procedural> =head1 EXPORTS L</find>, L</rule> =head1 TAINT MODE INTERACTION As of 0.32 File::Find::Rule doesn't capture the current working directory in a taint-unsafe manner. File::Find itself still does operations that the taint system will flag as insecure but you can use the L</extras> feature to ask L<File::Find> to internally C<untaint> file paths with a regex like so: my $rule = File::Find::Rule->extras({ untaint => 1 }); Please consult L<File::Find>'s documentation for C<untaint>, C<untaint_pattern>, and C<untaint_skip> for more information. =head1 BUGS The code makes use of the C<our> keyword and as such requires perl version 5.6.0 or newer. Currently it isn't possible to remove a clause from a rule object. If this becomes a significant issue it will be addressed. =head1 AUTHOR Richard Clamp <richardc@unixbeard.net> with input gained from this use.perl discussion: http://use.perl.org/~richardc/journal/6467 Additional proofreading and input provided by Kake, Greg McCarroll, and Andy Lester andy@petdance.com. =head1 COPYRIGHT Copyright (C) 2002, 2003, 2004, 2006, 2009, 2011 Richard Clamp. All Rights Reserved. This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SEE ALSO L<File::Find>, L<Text::Glob>, L<Number::Compare>, find(1) If you want to know about the procedural interface, see L<File::Find::Rule::Procedural>, and if you have an idea for a neat extension L<File::Find::Rule::Extending> =cut Implementation notes: $self->rules is an array of hashrefs. it may be a code fragment or a call to a subroutine. Anonymous subroutines are stored in the $self->subs hashref keyed on the stringfied version of the coderef. When one File::Find::Rule object is combined with another, such as in the any and not operations, this entire hash is merged. The _compile method walks the rules element and simply glues the code fragments together so they can be compiled into an anyonymous File::Find match sub for speed [*] There's probably a win to be made with the current model in making stat calls use C<_>. For find( file => size => "> 20M" => size => "< 400M" ); up to 3 stats will happen for each candidate. Adding a priming _ would be a bit blind if the first operation was C< name => 'foo' >, since that can be tested by a single regex. Simply checking what the next type of operation doesn't work since any arbritary exec sub may or may not stat. Potentially worse, they could stat something else like so: # extract from the worlds stupidest make(1) find( exec => sub { my $f = $_; $f =~ s/\.c$/.o/ && !-e $f } ); Maybe the best way is to treat C<_> as invalid after calling an exec, and doc that C<_> will only be meaningful after stat and -X tests if they're wanted in exec blocks. PK ! ө�L6 L6 Listing.pmnu �[��� PK ! ��f]� ]� �6 Temp.pmnu �[��� PK ! 3�w�+ �+ Basename.pmnu �[��� PK ! ō�">( >( 3 stat.pmnu �[��� PK ! Y]lj? j? �[ Copy.pmnu �[��� PK ! nh��� �� %� Path.pmnu �[��� PK ! 7�� �� RD Find.pmnu �[��� PK ! ~ �C= C= �� GlobMapper.pmnu �[��� PK ! �U�y� y� Fetch.pmnu �[��� PK ! �tڪ� � �� Compare.pmnu �[��� PK ! ���P �P �� Find/Rule.pmnu �[��� PK # �
| ver. 1.4 |
Github
|
.
| PHP 8.3.30 | Generation time: 0.02 |
proxy
|
phpinfo
|
Settings