diff options
author | Jarkko Hietaniemi <jhi@iki.fi> | 2001-06-18 04:17:15 +0000 |
---|---|---|
committer | Jarkko Hietaniemi <jhi@iki.fi> | 2001-06-18 04:17:15 +0000 |
commit | b695f709e8a342e35e482b0437eb6cdacdc58b6b (patch) | |
tree | 2d16192636e6ba806ff7a907f682c74f7705a920 /lib/File | |
parent | d780cd7a0195e946e636d3ee546f6ef4f21d6acc (diff) | |
download | perl-b695f709e8a342e35e482b0437eb6cdacdc58b6b.tar.gz |
The Grand Trek: move the *.t files from t/ to lib/ and ext/.
No doubt I made some mistakes like missed some files or
misnamed some files. The naming rules were more or less:
(1) if the module is from CPAN, follows its ways, be it
t/*.t or test.pl.
(2) otherwise if there are multiple tests for a module
put them in a t/
(3) otherwise if there's only one test put it in Module.t
(4) helper files go to module/ (locale, strict, warnings)
(5) use longer filenames now that we can (but e.g. the
compat-0.6.t and the Text::Balanced test files still
were renamed to be more civil against the 8.3 people)
installperl was updated appropriately not to install the
*.t files or the help files from under lib.
TODO: some helper files still remain under t/ that could
follow their 'masters'. UPDATE: On second thoughts, why
should they. They can continue to live under t/lib, and
in fact the locale/strict/warnings helpers that were moved
could be moved back. This way the amount of non-installable
stuff under lib/ stays smaller.
p4raw-id: //depot/perl@10676
Diffstat (limited to 'lib/File')
-rwxr-xr-x | lib/File/Basename.t | 144 | ||||
-rwxr-xr-x | lib/File/CheckTree.t | 19 | ||||
-rw-r--r-- | lib/File/Compare.t | 114 | ||||
-rwxr-xr-x | lib/File/Copy.t | 147 | ||||
-rwxr-xr-x | lib/File/DosGlob.t | 111 | ||||
-rwxr-xr-x | lib/File/Find/find.t | 734 | ||||
-rw-r--r-- | lib/File/Find/taint.t | 388 | ||||
-rwxr-xr-x | lib/File/Glob/basic.t | 175 | ||||
-rwxr-xr-x | lib/File/Glob/case.t | 60 | ||||
-rwxr-xr-x | lib/File/Glob/global.t | 151 | ||||
-rwxr-xr-x | lib/File/Glob/taint.t | 31 | ||||
-rwxr-xr-x | lib/File/Path.t | 28 | ||||
-rwxr-xr-x | lib/File/Spec.t | 379 | ||||
-rwxr-xr-x | lib/File/Spec/Functions.t | 17 | ||||
-rwxr-xr-x | lib/File/Temp/mktemp.t | 115 | ||||
-rwxr-xr-x | lib/File/Temp/posix.t | 83 | ||||
-rwxr-xr-x | lib/File/Temp/security.t | 140 | ||||
-rwxr-xr-x | lib/File/Temp/tempfile.t | 145 | ||||
-rw-r--r-- | lib/File/stat.t | 70 |
19 files changed, 3051 insertions, 0 deletions
diff --git a/lib/File/Basename.t b/lib/File/Basename.t new file mode 100755 index 0000000000..9bee1bfb8b --- /dev/null +++ b/lib/File/Basename.t @@ -0,0 +1,144 @@ +#!./perl -T + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; +} + +use File::Basename qw(fileparse basename dirname); + +print "1..41\n"; + +# import correctly? +print +(defined(&basename) && !defined(&fileparse_set_fstype) ? + '' : 'not '),"ok 1\n"; + +# set fstype -- should replace non-null default +print +(length(File::Basename::fileparse_set_fstype('unix')) ? + '' : 'not '),"ok 2\n"; + +# Unix syntax tests +($base,$path,$type) = fileparse('/virgil/aeneid/draft.book7','\.book\d+'); +if ($base eq 'draft' and $path eq '/virgil/aeneid/' and $type eq '.book7') { + print "ok 3\n"; +} +else { + print "not ok 3 |$base|$path|$type|\n"; +} +print +(basename('/arma/virumque.cano') eq 'virumque.cano' ? + '' : 'not '),"ok 4\n"; +print +(dirname('/arma/virumque.cano') eq '/arma' ? '' : 'not '),"ok 5\n"; +print +(dirname('arma/') eq '.' ? '' : 'not '),"ok 6\n"; +print +(dirname('/') eq '/' ? '' : 'not '),"ok 7\n"; + + +# set fstype -- should replace non-null default +print +(File::Basename::fileparse_set_fstype('VMS') eq 'unix' ? + '' : 'not '),"ok 8\n"; + +# VMS syntax tests +($base,$path,$type) = fileparse('virgil:[aeneid]draft.book7','\.book\d+'); +if ($base eq 'draft' and $path eq 'virgil:[aeneid]' and $type eq '.book7') { + print "ok 9\n"; +} +else { + print "not ok 9 |$base|$path|$type|\n"; +} +print +(basename('arma:[virumque]cano.trojae') eq 'cano.trojae' ? + '' : 'not '),"ok 10\n"; +print +(dirname('arma:[virumque]cano.trojae') eq 'arma:[virumque]' ? + '' : 'not '),"ok 11\n"; +print +(dirname('arma:<virumque>cano.trojae') eq 'arma:<virumque>' ? + '' : 'not '),"ok 12\n"; +print +(dirname('arma:virumque.cano') eq 'arma:' ? '' : 'not '),"ok 13\n"; +$ENV{DEFAULT} = '' unless exists $ENV{DEFAULT}; +print +(dirname('virumque.cano') eq $ENV{DEFAULT} ? '' : 'not '),"ok 14\n"; +print +(dirname('arma/') eq '.' ? '' : 'not '),"ok 15\n"; + +# set fstype -- should replace non-null default +print +(File::Basename::fileparse_set_fstype('MSDOS') eq 'VMS' ? + '' : 'not '),"ok 16\n"; + +# MSDOS syntax tests +($base,$path,$type) = fileparse('C:\\virgil\\aeneid\\draft.book7','\.book\d+'); +if ($base eq 'draft' and $path eq 'C:\\virgil\\aeneid\\' and $type eq '.book7') { + print "ok 17\n"; +} +else { + print "not ok 17 |$base|$path|$type|\n"; +} +print +(basename('A:virumque\\cano.trojae') eq 'cano.trojae' ? + '' : 'not '),"ok 18\n"; +print +(dirname('A:\\virumque\\cano.trojae') eq 'A:\\virumque' ? + '' : 'not '),"ok 19\n"; +print +(dirname('A:\\') eq 'A:\\' ? '' : 'not '),"ok 20\n"; +print +(dirname('arma\\') eq '.' ? '' : 'not '),"ok 21\n"; + +# Yes "/" is a legal path separator under MSDOS +basename("lib/File/Basename.pm") eq "Basename.pm" or print "not "; +print "ok 22\n"; + + + +# set fstype -- should replace non-null default +print +(File::Basename::fileparse_set_fstype('MacOS') eq 'MSDOS' ? + '' : 'not '),"ok 23\n"; + +# MacOS syntax tests +($base,$path,$type) = fileparse('virgil:aeneid:draft.book7','\.book\d+'); +if ($base eq 'draft' and $path eq 'virgil:aeneid:' and $type eq '.book7') { + print "ok 24\n"; +} +else { + print "not ok 24 |$base|$path|$type|\n"; +} +print +(basename(':arma:virumque:cano.trojae') eq 'cano.trojae' ? + '' : 'not '),"ok 25\n"; +print +(dirname(':arma:virumque:cano.trojae') eq ':arma:virumque:' ? + '' : 'not '),"ok 26\n"; +print +(dirname(':arma:virumque:') eq ':arma:' ? '' : 'not '),"ok 27\n"; +print +(dirname(':arma:virumque') eq ':arma:' ? '' : 'not '),"ok 28\n"; +print +(dirname(':arma:') eq ':' ? '' : 'not '),"ok 29\n"; +print +(dirname(':arma') eq ':' ? '' : 'not '),"ok 30\n"; +print +(dirname('arma:') eq 'arma:' ? '' : 'not '),"ok 31\n"; +print +(dirname('arma') eq ':' ? '' : 'not '),"ok 32\n"; +print +(dirname(':') eq ':' ? '' : 'not '),"ok 33\n"; + + +# Check quoting of metacharacters in suffix arg by basename() +print +(basename(':arma:virumque:cano.trojae','.trojae') eq 'cano' ? + '' : 'not '),"ok 34\n"; +print +(basename(':arma:virumque:cano_trojae','.trojae') eq 'cano_trojae' ? + '' : 'not '),"ok 35\n"; + +# extra tests for a few specific bugs + +File::Basename::fileparse_set_fstype 'MSDOS'; +# perl5.003_18 gives C:/perl/.\ +print +((fileparse 'C:/perl/lib')[1] eq 'C:/perl/' ? '' : 'not '), "ok 36\n"; +# perl5.003_18 gives C:\perl\ +print +(dirname('C:\\perl\\lib\\') eq 'C:\\perl' ? '' : 'not '), "ok 37\n"; + +File::Basename::fileparse_set_fstype 'UNIX'; +# perl5.003_18 gives '.' +print +(dirname('/perl/') eq '/' ? '' : 'not '), "ok 38\n"; +# perl5.003_18 gives '/perl/lib' +print +(dirname('/perl/lib//') eq '/perl' ? '' : 'not '), "ok 39\n"; + +# The empty tainted value, for tainting strings +my $TAINT = substr($^X, 0, 0); +# How to identify taint when you see it +sub any_tainted (@) { + not eval { join("",@_), kill 0; 1 }; +} +sub tainted ($) { + any_tainted @_; +} +sub all_tainted (@) { + for (@_) { return 0 unless tainted $_ } + 1; +} + +print +(tainted(dirname($TAINT.'/perl/lib//')) ? '' : 'not '), "ok 40\n"; +print +(all_tainted(fileparse($TAINT.'/dir/draft.book7','\.book\d+')) + ? '' : 'not '), "ok 41\n"; diff --git a/lib/File/CheckTree.t b/lib/File/CheckTree.t new file mode 100755 index 0000000000..b445af4992 --- /dev/null +++ b/lib/File/CheckTree.t @@ -0,0 +1,19 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; +} + +print "1..1\n"; + +use File::CheckTree; + +# We assume that we run from the perl "t" directory. + +validate q{ + lib -d || die + TEST -f || die +}; + +print "ok 1\n"; diff --git a/lib/File/Compare.t b/lib/File/Compare.t new file mode 100644 index 0000000000..aedc32323e --- /dev/null +++ b/lib/File/Compare.t @@ -0,0 +1,114 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; +} + +BEGIN { + our @TEST = stat "TEST"; + our @README = stat "README"; + unless (@TEST && @README) { + print "1..0 # Skip: no file TEST or README\n"; + exit 0; + } +} + +print "1..12\n"; + +use File::Compare qw(compare compare_text); + +print "ok 1\n"; + +# named files, same, existing but different, cause an error +print "not " unless compare("README","README") == 0; +print "ok 2\n"; + +print "not " unless compare("TEST","README") == 1; +print "ok 3\n"; + +print "not " unless compare("README","HLAGHLAG") == -1; + # a file which doesn't exist +print "ok 4\n"; + +# compare_text, the same file, different but existing files +# cause error, test sub form. +print "not " unless compare_text("README","README") == 0; +print "ok 5\n"; + +print "not " unless compare_text("TEST","README") == 1; +print "ok 6\n"; + +print "not " unless compare_text("TEST","HLAGHLAG") == -1; +print "ok 7\n"; + +print "not " unless + compare_text("README","README",sub {$_[0] ne $_[1]}) == 0; +print "ok 8\n"; + +# filehandle and same file +{ + my $fh; + open ($fh, "<README") or print "not "; + binmode($fh); + print "not " unless compare($fh,"README") == 0; + print "ok 9\n"; + close $fh; +} + +# filehandle and different (but existing) file. +{ + my $fh; + open ($fh, "<README") or print "not "; + binmode($fh); + print "not " unless compare_text($fh,"TEST") == 1; + print "ok 10\n"; + close $fh; +} + +# Different file with contents of known file, +# will use File::Temp to do this, skip rest of +# tests if this doesn't seem to work + +my @donetests; +eval { + require File::Spec; import File::Spec; + require File::Path; import File::Path; + require File::Temp; import File::Temp qw/ :mktemp unlink0 /; + + my $template = File::Spec->catfile(File::Spec->tmpdir, 'fcmpXXXX'); + my($tfh,$filename) = mkstemp($template); + { + local $/; #slurp + my $fh; + open($fh,'README'); + binmode($fh); + my $data = <$fh>; + print $tfh $data; + close($fh); + } + seek($tfh,0,0); + $donetests[0] = compare($tfh, 'README'); + $donetests[1] = compare($filename, 'README'); + unlink0($tfh,$filename); +}; +print "# problems when testing with a tempory file\n" if $@; + +if (@donetests == 2) { + print "not " unless $donetests[0] == 0; + print "ok 11\n"; + if ($^O eq 'VMS') { + # The open attempt on FROM in File::Compare::compare should fail + # on this OS since files are not shared by default. + print "not " unless $donetests[1] == -1; + print "ok 12\n"; + } + else { + print "not " unless $donetests[1] == 0; + print "ok 12\n"; + } +} +else { + print "ok 11# Skip\nok 12 # Skip Likely due to File::Temp\n"; +} + diff --git a/lib/File/Copy.t b/lib/File/Copy.t new file mode 100755 index 0000000000..44b5827e72 --- /dev/null +++ b/lib/File/Copy.t @@ -0,0 +1,147 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; + push @INC, "::lib:$MacPerl::Architecture" if $^O eq 'MacOS'; +} + +$| = 1; + +my @pass = (0,1); +my $tests = $^O eq 'MacOS' ? 14 : 11; +printf "1..%d\n", $tests * scalar(@pass); + +use File::Copy; + +for my $pass (@pass) { + + my $loopconst = $pass*$tests; + + # First we create a file + open(F, ">file-$$") or die; + binmode F; # for DOSISH platforms, because test 3 copies to stdout + printf F "ok %d\n", 3 + $loopconst; + close F; + + copy "file-$$", "copy-$$"; + + open(F, "copy-$$") or die; + $foo = <F>; + close(F); + + print "not " if -s "file-$$" != -s "copy-$$"; + printf "ok %d\n", 1 + $loopconst; + + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 2+$loopconst; + + binmode STDOUT unless $^O eq 'VMS'; # Copy::copy works in binary mode + copy "copy-$$", \*STDOUT; + unlink "copy-$$" or die "unlink: $!"; + + open(F,"file-$$"); + copy(*F, "copy-$$"); + open(R, "copy-$$") or die "open copy-$$: $!"; $foo = <R>; close(R); + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 4+$loopconst; + unlink "copy-$$" or die "unlink: $!"; + open(F,"file-$$"); + copy(\*F, "copy-$$"); + close(F) or die "close: $!"; + open(R, "copy-$$") or die; $foo = <R>; close(R) or die "close: $!"; + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 5+$loopconst; + unlink "copy-$$" or die "unlink: $!"; + + require IO::File; + $fh = IO::File->new(">copy-$$") or die "Cannot open copy-$$:$!"; + binmode $fh or die; + copy("file-$$",$fh); + $fh->close or die "close: $!"; + open(R, "copy-$$") or die; $foo = <R>; close(R); + print "# foo=`$foo'\nnot " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 6+$loopconst; + unlink "copy-$$" or die "unlink: $!"; + require FileHandle; + my $fh = FileHandle->new(">copy-$$") or die "Cannot open copy-$$:$!"; + binmode $fh or die; + copy("file-$$",$fh); + $fh->close; + open(R, "copy-$$") or die; $foo = <R>; close(R); + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 7+$loopconst; + unlink "file-$$" or die "unlink: $!"; + + print "# moved missing file.\nnot " if move("file-$$", "copy-$$"); + print "# target disappeared.\nnot " if not -e "copy-$$"; + printf "ok %d\n", 8+$loopconst; + + move "copy-$$", "file-$$" or print "# move did not succeed.\n"; + print "# not moved: $!\nnot " unless -e "file-$$" and not -e "copy-$$"; + open(R, "file-$$") or die; $foo = <R>; close(R); + print "# foo=`$foo'\nnot " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 9+$loopconst; + + if ($^O eq 'MacOS') { + + copy "file-$$", "lib"; + open(R, ":lib:file-$$") or die; $foo = <R>; close(R); + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 10+$loopconst; + unlink ":lib:file-$$" or die "unlink: $!"; + + copy "file-$$", ":lib"; + open(R, ":lib:file-$$") or die; $foo = <R>; close(R); + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 11+$loopconst; + unlink ":lib:file-$$" or die "unlink: $!"; + + copy "file-$$", ":lib:"; + open(R, ":lib:file-$$") or die; $foo = <R>; close(R); + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 12+$loopconst; + unlink ":lib:file-$$" or die "unlink: $!"; + + unless (-e 'lib:') { # make sure there's no volume called 'lib' + undef $@; + eval { (copy "file-$$", "lib:") || die "'lib:' is not a volume name"; }; + print "# Died: $@"; + print "not " unless ( $@ =~ m|'lib:' is not a volume name| ); + } + printf "ok %d\n", 13+$loopconst; + + move "file-$$", ":lib:"; + open(R, ":lib:file-$$") or die "open :lib:file-$$: $!"; $foo = <R>; close(R); + print "not " unless $foo eq sprintf("ok %d\n", 3+$loopconst) + and not -e "file-$$";; + printf "ok %d\n", 14+$loopconst; + unlink ":lib:file-$$" or die "unlink: $!"; + + } else { + + copy "file-$$", "lib"; + open(R, "lib/file-$$") or die; $foo = <R>; close(R); + print "not " unless $foo eq sprintf "ok %d\n", 3+$loopconst; + printf "ok %d\n", 10+$loopconst; + unlink "lib/file-$$" or die "unlink: $!"; + + move "file-$$", "lib"; + open(R, "lib/file-$$") or die "open lib/file-$$: $!"; $foo = <R>; close(R); + print "not " unless $foo eq sprintf("ok %d\n", 3+$loopconst) + and not -e "file-$$";; + printf "ok %d\n", 11+$loopconst; + unlink "lib/file-$$" or die "unlink: $!"; + + } +} + + +END { + 1 while unlink "file-$$"; + if ($^O eq 'MacOS') { + 1 while unlink ":lib:file-$$"; + } else { + 1 while unlink "lib/file-$$"; + } +} diff --git a/lib/File/DosGlob.t b/lib/File/DosGlob.t new file mode 100755 index 0000000000..31e36e24dc --- /dev/null +++ b/lib/File/DosGlob.t @@ -0,0 +1,111 @@ +#!./perl + +# +# test glob() in File::DosGlob +# + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; +} + +print "1..10\n"; + +# override it in main:: +use File::DosGlob 'glob'; + +# test if $_ takes as the default +$_ = "op/a*.t"; +my @r = glob; +print "not " if $_ ne 'op/a*.t'; +print "ok 1\n"; +print "# |@r|\nnot " if @r < 9; +print "ok 2\n"; + +# check if <*/*> works +@r = <*/a*.t>; +# atleast {argv,abbrev,anydbm,autoloader,append,arith,array,assignwarn,auto}.t +print "not " if @r < 9; +print "ok 3\n"; +my $r = scalar @r; + +# check if scalar context works +@r = (); +while (defined($_ = <*/a*.t>)) { + print "# $_\n"; + push @r, $_; +} +print "not " if @r != $r; +print "ok 4\n"; + +# check if list context works +@r = (); +for (<*/a*.t>) { + print "# $_\n"; + push @r, $_; +} +print "not " if @r != $r; +print "ok 5\n"; + +# test if implicit assign to $_ in while() works +@r = (); +while (<*/a*.t>) { + print "# $_\n"; + push @r, $_; +} +print "not " if @r != $r; +print "ok 6\n"; + +# test if explicit glob() gets assign magic too +my @s = (); +while (glob '*/a*.t') { + print "# $_\n"; + push @s, $_; +} +print "not " if "@r" ne "@s"; +print "ok 7\n"; + +# how about in a different package, like? +package Foo; +use File::DosGlob 'glob'; +@s = (); +while (glob '*/a*.t') { + print "# $_\n"; + push @s, $_; +} +print "not " if "@r" ne "@s"; +print "ok 8\n"; + +# test if different glob ops maintain independent contexts +@s = (); +while (<*/a*.t>) { + my $i = 0; + print "# $_ <"; + push @s, $_; + while (<*/b*.t>) { + print " $_"; + $i++; + } + print " >\n"; +} +print "not " if "@r" ne "@s"; +print "ok 9\n"; + +# how about a global override, hm? +eval <<'EOT'; +use File::DosGlob 'GLOBAL_glob'; +package Bar; +@s = (); +while (<*/a*.t>) { + my $i = 0; + print "# $_ <"; + push @s, $_; + while (glob '*/b*.t') { + print " $_"; + $i++; + } + print " >\n"; +} +print "not " if "@r" ne "@s"; +print "ok 10\n"; +EOT diff --git a/lib/File/Find/find.t b/lib/File/Find/find.t new file mode 100755 index 0000000000..cf1b1f8599 --- /dev/null +++ b/lib/File/Find/find.t @@ -0,0 +1,734 @@ +#!./perl + + +my %Expect_File = (); # what we expect for $_ +my %Expect_Name = (); # what we expect for $File::Find::name/fullname +my %Expect_Dir = (); # what we expect for $File::Find::dir +my $symlink_exists = eval { symlink("",""); 1 }; +my $warn_msg; + + +BEGIN { + chdir 't' if -d 't'; + unshift @INC => '../lib'; + + $SIG{'__WARN__'} = sub { $warn_msg = $_[0]; warn "# $_[0]"; } +} + +if ( $symlink_exists ) { print "1..188\n"; } +else { print "1..78\n"; } + +use File::Find; +use File::Spec; + +cleanup(); + +find({wanted => sub { print "ok 1\n" if $_ eq 'commonsense.t'; } }, + File::Spec->curdir); + +finddepth({wanted => sub { print "ok 2\n" if $_ eq 'commonsense.t'; } }, + File::Spec->curdir); + +my $case = 2; +my $FastFileTests_OK = 0; + +sub cleanup { + if (-d dir_path('for_find')) { + chdir(dir_path('for_find')); + } + if (-d dir_path('fa')) { + unlink file_path('fa', 'fa_ord'), + file_path('fa', 'fsl'), + file_path('fa', 'faa', 'faa_ord'), + file_path('fa', 'fab', 'fab_ord'), + file_path('fa', 'fab', 'faba', 'faba_ord'), + file_path('fb', 'fb_ord'), + file_path('fb', 'fba', 'fba_ord'); + rmdir dir_path('fa', 'faa'); + rmdir dir_path('fa', 'fab', 'faba'); + rmdir dir_path('fa', 'fab'); + rmdir dir_path('fa'); + rmdir dir_path('fb', 'fba'); + rmdir dir_path('fb'); + chdir File::Spec->updir; + rmdir dir_path('for_find'); + } +} + +END { + cleanup(); +} + +sub Check($) { + $case++; + if ($_[0]) { print "ok $case\n"; } + else { print "not ok $case\n"; } +} + +sub CheckDie($) { + $case++; + if ($_[0]) { print "ok $case\n"; } + else { print "not ok $case\n $!\n"; exit 0; } +} + +sub touch { + CheckDie( open(my $T,'>',$_[0]) ); +} + +sub MkDir($$) { + CheckDie( mkdir($_[0],$_[1]) ); +} + +sub wanted_File_Dir { + print "# \$File::Find::dir => '$File::Find::dir'\n"; + print "# \$_ => '$_'\n"; + s#\.$## if ($^O eq 'VMS' && $_ ne '.'); + Check( $Expect_File{$_} ); + if ( $FastFileTests_OK ) { + delete $Expect_File{ $_} + unless ( $Expect_Dir{$_} && ! -d _ ); + } else { + delete $Expect_File{$_} + unless ( $Expect_Dir{$_} && ! -d $_ ); + } +} + +sub wanted_File_Dir_prune { + &wanted_File_Dir; + $File::Find::prune=1 if $_ eq 'faba'; +} + +sub wanted_Name { + my $n = $File::Find::name; + $n =~ s#\.$## if ($^O eq 'VMS' && $n ne '.'); + print "# \$File::Find::name => '$n'\n"; + my $i = rindex($n,'/'); + my $OK = exists($Expect_Name{$n}); + unless ($^O eq 'MacOS') { + if ( $OK ) { + $OK= exists($Expect_Name{substr($n,0,$i)}) if $i >= 0; + } + } + Check($OK); + delete $Expect_Name{$n}; +} + +sub wanted_File { + print "# \$_ => '$_'\n"; + s#\.$## if ($^O eq 'VMS' && $_ ne '.'); + my $i = rindex($_,'/'); + my $OK = exists($Expect_File{ $_}); + unless ($^O eq 'MacOS') { + if ( $OK ) { + $OK= exists($Expect_File{ substr($_,0,$i)}) if $i >= 0; + } + } + Check($OK); + delete $Expect_File{ $_}; +} + +sub simple_wanted { + print "# \$File::Find::dir => '$File::Find::dir'\n"; + print "# \$_ => '$_'\n"; +} + +sub noop_wanted {} + +sub my_preprocess { + @files = @_; + print "# --preprocess--\n"; + print "# \$File::Find::dir => '$File::Find::dir' \n"; + foreach $file (@files) { + print "# $file \n"; + delete $Expect_Dir{ $File::Find::dir }->{$file}; + } + print "# --end preprocess--\n"; + Check(scalar(keys %{$Expect_Dir{ $File::Find::dir }}) == 0); + if (scalar(keys %{$Expect_Dir{ $File::Find::dir }}) == 0) { + delete $Expect_Dir{ $File::Find::dir } + } + return @files; +} + +sub my_postprocess { + print "# postprocess: \$File::Find::dir => '$File::Find::dir' \n"; + delete $Expect_Dir{ $File::Find::dir}; +} + + +# Use dir_path() to specify a directory path that's expected for +# $File::Find::dir (%Expect_Dir). Also use it in file operations like +# chdir, rmdir etc. +# +# dir_path() concatenates directory names to form a _relative_ +# directory path, independant from the platform it's run on, although +# there are limitations. Don't try to create an absolute path, +# because that may fail on operating systems that have the concept of +# volume names (e.g. Mac OS). Be careful when you want to create an +# updir path like ../fa (Unix) or ::fa: (Mac OS). Plain directory +# names will work best. As a special case, you can pass it a "." as +# first argument, to create a directory path like "./fa/dir" on +# operating systems other than Mac OS (actually, Mac OS will ignore +# the ".", if it's the first argument). If there's no second argument, +# this function will return the empty string on Mac OS and the string +# "./" otherwise. + +sub dir_path { + my $first_item = shift @_; + + if ($first_item eq '.') { + if ($^O eq 'MacOS') { + return '' unless @_; + # ignore first argument; return a relative path + # with leading ":" and with trailing ":" + return File::Spec->catdir("", @_); + } else { # other OS + return './' unless @_; + my $path = File::Spec->catdir(@_); + # add leading "./" + $path = "./$path"; + return $path; + } + + } else { # $first_item ne '.' + return $first_item unless @_; # return plain filename + if ($^O eq 'MacOS') { + # relative path with leading ":" and with trailing ":" + return File::Spec->catdir("", $first_item, @_); + } else { # other OS + return File::Spec->catdir($first_item, @_); + } + } +} + + +# Use topdir() to specify a directory path that you want to pass to +#find/finddepth Basically, topdir() does the same as dir_path() (see +#above), except that there's no trailing ":" on Mac OS. + +sub topdir { + my $path = dir_path(@_); + $path =~ s/:$// if ($^O eq 'MacOS'); + return $path; +} + + +# Use file_path() to specify a file path that's expected for $_ +# (%Expect_File). Also suitable for file operations like unlink etc. +# +# file_path() concatenates directory names (if any) and a filename to +# form a _relative_ file path (the last argument is assumed to be a +# file). It's independant from the platform it's run on, although +# there are limitations (see the warnings for dir_path() above). As a +# special case, you can pass it a "." as first argument, to create a +# file path like "./fa/file" on operating systems other than Mac OS +# (actually, Mac OS will ignore the ".", if it's the first +# argument). If there's no second argument, this function will return +# the empty string on Mac OS and the string "./" otherwise. + +sub file_path { + my $first_item = shift @_; + + if ($first_item eq '.') { + if ($^O eq 'MacOS') { + return '' unless @_; + # ignore first argument; return a relative path + # with leading ":", but without trailing ":" + return File::Spec->catfile("", @_); + } else { # other OS + return './' unless @_; + my $path = File::Spec->catfile(@_); + # add leading "./" + $path = "./$path"; + return $path; + } + + } else { # $first_item ne '.' + return $first_item unless @_; # return plain filename + if ($^O eq 'MacOS') { + # relative path with leading ":", but without trailing ":" + return File::Spec->catfile("", $first_item, @_); + } else { # other OS + return File::Spec->catfile($first_item, @_); + } + } +} + + +# Use file_path_name() to specify a file path that's expected for +# $File::Find::Name (%Expect_Name). Note: When the no_chdir => 1 +# option is in effect, $_ is the same as $File::Find::Name. In that +# case, also use this function to specify a file path that's expected +# for $_. +# +# Basically, file_path_name() does the same as file_path() (see +# above), except that there's always a leading ":" on Mac OS, even for +# plain file/directory names. + +sub file_path_name { + my $path = file_path(@_); + $path = ":$path" if (($^O eq 'MacOS') && ($path !~ /:/)); + return $path; +} + + + +MkDir( dir_path('for_find'), 0770 ); +CheckDie(chdir( dir_path('for_find'))); +MkDir( dir_path('fa'), 0770 ); +MkDir( dir_path('fb'), 0770 ); +touch( file_path('fb', 'fb_ord') ); +MkDir( dir_path('fb', 'fba'), 0770 ); +touch( file_path('fb', 'fba', 'fba_ord') ); +if ($^O eq 'MacOS') { + CheckDie( symlink(':fb',':fa:fsl') ) if $symlink_exists; +} else { + CheckDie( symlink('../fb','fa/fsl') ) if $symlink_exists; +} +touch( file_path('fa', 'fa_ord') ); + +MkDir( dir_path('fa', 'faa'), 0770 ); +touch( file_path('fa', 'faa', 'faa_ord') ); +MkDir( dir_path('fa', 'fab'), 0770 ); +touch( file_path('fa', 'fab', 'fab_ord') ); +MkDir( dir_path('fa', 'fab', 'faba'), 0770 ); +touch( file_path('fa', 'fab', 'faba', 'faba_ord') ); + + +%Expect_File = (File::Spec->curdir => 1, file_path('fsl') => 1, + file_path('fa_ord') => 1, file_path('fab') => 1, + file_path('fab_ord') => 1, file_path('faba') => 1, + file_path('faa') => 1, file_path('faa_ord') => 1); + +delete $Expect_File{ file_path('fsl') } unless $symlink_exists; +%Expect_Name = (); + +%Expect_Dir = ( dir_path('fa') => 1, dir_path('faa') => 1, + dir_path('fab') => 1, dir_path('faba') => 1, + dir_path('fb') => 1, dir_path('fba') => 1); + +delete @Expect_Dir{ dir_path('fb'), dir_path('fba') } unless $symlink_exists; +File::Find::find( {wanted => \&wanted_File_Dir_prune}, topdir('fa') ); +Check( scalar(keys %Expect_File) == 0 ); + + +print "# check re-entrancy\n"; + +%Expect_File = (File::Spec->curdir => 1, file_path('fsl') => 1, + file_path('fa_ord') => 1, file_path('fab') => 1, + file_path('fab_ord') => 1, file_path('faba') => 1, + file_path('faa') => 1, file_path('faa_ord') => 1); + +delete $Expect_File{ file_path('fsl') } unless $symlink_exists; +%Expect_Name = (); + +%Expect_Dir = ( dir_path('fa') => 1, dir_path('faa') => 1, + dir_path('fab') => 1, dir_path('faba') => 1, + dir_path('fb') => 1, dir_path('fba') => 1); + +delete @Expect_Dir{ dir_path('fb'), dir_path('fba') } unless $symlink_exists; + +File::Find::find( {wanted => sub { wanted_File_Dir_prune(); + File::Find::find( {wanted => sub + {} }, File::Spec->curdir ); } }, + topdir('fa') ); + +Check( scalar(keys %Expect_File) == 0 ); + + +# no_chdir is in effect, hence we use file_path_name to specify the expected paths for %Expect_File + +%Expect_File = (file_path_name('fa') => 1, + file_path_name('fa', 'fsl') => 1, + file_path_name('fa', 'fa_ord') => 1, + file_path_name('fa', 'fab') => 1, + file_path_name('fa', 'fab', 'fab_ord') => 1, + file_path_name('fa', 'fab', 'faba') => 1, + file_path_name('fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('fa', 'faa') => 1, + file_path_name('fa', 'faa', 'faa_ord') => 1,); + +delete $Expect_File{ file_path_name('fa', 'fsl') } unless $symlink_exists; +%Expect_Name = (); + +%Expect_Dir = (dir_path('fa') => 1, + dir_path('fa', 'faa') => 1, + dir_path('fa', 'fab') => 1, + dir_path('fa', 'fab', 'faba') => 1, + dir_path('fb') => 1, + dir_path('fb', 'fba') => 1); + +delete @Expect_Dir{ dir_path('fb'), dir_path('fb', 'fba') } + unless $symlink_exists; + +File::Find::find( {wanted => \&wanted_File_Dir, no_chdir => 1}, + topdir('fa') ); Check( scalar(keys %Expect_File) == 0 ); + + +%Expect_File = (); + +%Expect_Name = (File::Spec->curdir => 1, + file_path_name('.', 'fa') => 1, + file_path_name('.', 'fa', 'fsl') => 1, + file_path_name('.', 'fa', 'fa_ord') => 1, + file_path_name('.', 'fa', 'fab') => 1, + file_path_name('.', 'fa', 'fab', 'fab_ord') => 1, + file_path_name('.', 'fa', 'fab', 'faba') => 1, + file_path_name('.', 'fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('.', 'fa', 'faa') => 1, + file_path_name('.', 'fa', 'faa', 'faa_ord') => 1, + file_path_name('.', 'fb') => 1, + file_path_name('.', 'fb', 'fba') => 1, + file_path_name('.', 'fb', 'fba', 'fba_ord') => 1, + file_path_name('.', 'fb', 'fb_ord') => 1); + +delete $Expect_Name{ file_path('.', 'fa', 'fsl') } unless $symlink_exists; +%Expect_Dir = (); +File::Find::finddepth( {wanted => \&wanted_Name}, File::Spec->curdir ); +Check( scalar(keys %Expect_Name) == 0 ); + + +# no_chdir is in effect, hence we use file_path_name to specify the +# expected paths for %Expect_File + +%Expect_File = (File::Spec->curdir => 1, + file_path_name('.', 'fa') => 1, + file_path_name('.', 'fa', 'fsl') => 1, + file_path_name('.', 'fa', 'fa_ord') => 1, + file_path_name('.', 'fa', 'fab') => 1, + file_path_name('.', 'fa', 'fab', 'fab_ord') => 1, + file_path_name('.', 'fa', 'fab', 'faba') => 1, + file_path_name('.', 'fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('.', 'fa', 'faa') => 1, + file_path_name('.', 'fa', 'faa', 'faa_ord') => 1, + file_path_name('.', 'fb') => 1, + file_path_name('.', 'fb', 'fba') => 1, + file_path_name('.', 'fb', 'fba', 'fba_ord') => 1, + file_path_name('.', 'fb', 'fb_ord') => 1); + +delete $Expect_File{ file_path_name('.', 'fa', 'fsl') } unless $symlink_exists; +%Expect_Name = (); +%Expect_Dir = (); + +File::Find::finddepth( {wanted => \&wanted_File, no_chdir => 1}, + File::Spec->curdir ); + +Check( scalar(keys %Expect_File) == 0 ); + + +print "# check preprocess\n"; +%Expect_File = (); +%Expect_Name = (); +%Expect_Dir = ( + File::Spec->curdir => {fa => 1, fb => 1}, + dir_path('.', 'fa') => {faa => 1, fab => 1, fa_ord => 1}, + dir_path('.', 'fa', 'faa') => {faa_ord => 1}, + dir_path('.', 'fa', 'fab') => {faba => 1, fab_ord => 1}, + dir_path('.', 'fa', 'fab', 'faba') => {faba_ord => 1}, + dir_path('.', 'fb') => {fba => 1, fb_ord => 1}, + dir_path('.', 'fb', 'fba') => {fba_ord => 1} + ); + +File::Find::find( {wanted => \&noop_wanted, + preprocess => \&my_preprocess}, File::Spec->curdir ); + +Check( scalar(keys %Expect_Dir) == 0 ); + + +print "# check postprocess\n"; +%Expect_File = (); +%Expect_Name = (); +%Expect_Dir = ( + File::Spec->curdir => 1, + dir_path('.', 'fa') => 1, + dir_path('.', 'fa', 'faa') => 1, + dir_path('.', 'fa', 'fab') => 1, + dir_path('.', 'fa', 'fab', 'faba') => 1, + dir_path('.', 'fb') => 1, + dir_path('.', 'fb', 'fba') => 1 + ); + +File::Find::find( {wanted => \&noop_wanted, + postprocess => \&my_postprocess}, File::Spec->curdir ); + +Check( scalar(keys %Expect_Dir) == 0 ); + + +if ( $symlink_exists ) { + print "# --- symbolic link tests --- \n"; + $FastFileTests_OK= 1; + + + # Verify that File::Find::find will call wanted even if the topdir of + # is a symlink to a directory, and it shouldn't follow the link + # unless follow is set, which it isn't in this case + %Expect_File = ( file_path('fsl') => 1 ); + %Expect_Name = (); + %Expect_Dir = (); + File::Find::find( {wanted => \&wanted_File_Dir}, topdir('fa', 'fsl') ); + Check( scalar(keys %Expect_File) == 0 ); + + + %Expect_File = (File::Spec->curdir => 1, file_path('fa_ord') => 1, + file_path('fsl') => 1, file_path('fb_ord') => 1, + file_path('fba') => 1, file_path('fba_ord') => 1, + file_path('fab') => 1, file_path('fab_ord') => 1, + file_path('faba') => 1, file_path('faa') => 1, + file_path('faa_ord') => 1); + + %Expect_Name = (); + + %Expect_Dir = (File::Spec->curdir => 1, dir_path('fa') => 1, + dir_path('faa') => 1, dir_path('fab') => 1, + dir_path('faba') => 1, dir_path('fb') => 1, + dir_path('fba') => 1); + + File::Find::find( {wanted => \&wanted_File_Dir_prune, + follow_fast => 1}, topdir('fa') ); + + Check( scalar(keys %Expect_File) == 0 ); + + + # no_chdir is in effect, hence we use file_path_name to specify + # the expected paths for %Expect_File + + %Expect_File = (file_path_name('fa') => 1, + file_path_name('fa', 'fa_ord') => 1, + file_path_name('fa', 'fsl') => 1, + file_path_name('fa', 'fsl', 'fb_ord') => 1, + file_path_name('fa', 'fsl', 'fba') => 1, + file_path_name('fa', 'fsl', 'fba', 'fba_ord') => 1, + file_path_name('fa', 'fab') => 1, + file_path_name('fa', 'fab', 'fab_ord') => 1, + file_path_name('fa', 'fab', 'faba') => 1, + file_path_name('fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('fa', 'faa') => 1, + file_path_name('fa', 'faa', 'faa_ord') => 1); + + %Expect_Name = (); + + %Expect_Dir = (dir_path('fa') => 1, + dir_path('fa', 'faa') => 1, + dir_path('fa', 'fab') => 1, + dir_path('fa', 'fab', 'faba') => 1, + dir_path('fb') => 1, + dir_path('fb', 'fba') => 1); + + File::Find::find( {wanted => \&wanted_File_Dir, follow_fast => 1, + no_chdir => 1}, topdir('fa') ); + + Check( scalar(keys %Expect_File) == 0 ); + + %Expect_File = (); + + %Expect_Name = (file_path_name('fa') => 1, + file_path_name('fa', 'fa_ord') => 1, + file_path_name('fa', 'fsl') => 1, + file_path_name('fa', 'fsl', 'fb_ord') => 1, + file_path_name('fa', 'fsl', 'fba') => 1, + file_path_name('fa', 'fsl', 'fba', 'fba_ord') => 1, + file_path_name('fa', 'fab') => 1, + file_path_name('fa', 'fab', 'fab_ord') => 1, + file_path_name('fa', 'fab', 'faba') => 1, + file_path_name('fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('fa', 'faa') => 1, + file_path_name('fa', 'faa', 'faa_ord') => 1); + + %Expect_Dir = (); + + File::Find::finddepth( {wanted => \&wanted_Name, + follow_fast => 1}, topdir('fa') ); + + Check( scalar(keys %Expect_Name) == 0 ); + + # no_chdir is in effect, hence we use file_path_name to specify + # the expected paths for %Expect_File + + %Expect_File = (file_path_name('fa') => 1, + file_path_name('fa', 'fa_ord') => 1, + file_path_name('fa', 'fsl') => 1, + file_path_name('fa', 'fsl', 'fb_ord') => 1, + file_path_name('fa', 'fsl', 'fba') => 1, + file_path_name('fa', 'fsl', 'fba', 'fba_ord') => 1, + file_path_name('fa', 'fab') => 1, + file_path_name('fa', 'fab', 'fab_ord') => 1, + file_path_name('fa', 'fab', 'faba') => 1, + file_path_name('fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('fa', 'faa') => 1, + file_path_name('fa', 'faa', 'faa_ord') => 1); + + %Expect_Name = (); + %Expect_Dir = (); + + File::Find::finddepth( {wanted => \&wanted_File, follow_fast => 1, + no_chdir => 1}, topdir('fa') ); + + Check( scalar(keys %Expect_File) == 0 ); + + + print "# check dangling symbolic links\n"; + MkDir( dir_path('dangling_dir'), 0770 ); + CheckDie( symlink( dir_path('dangling_dir'), + file_path('dangling_dir_sl') ) ); + rmdir dir_path('dangling_dir'); + touch(file_path('dangling_file')); + if ($^O eq 'MacOS') { + CheckDie( symlink('dangling_file', ':fa:dangling_file_sl') ); + } else { + CheckDie( symlink('../dangling_file','fa/dangling_file_sl') ); + } + unlink file_path('dangling_file'); + + { + # these tests should also emit a warning + use warnings; + + %Expect_File = (File::Spec->curdir => 1, + file_path('fa_ord') => 1, + file_path('fsl') => 1, + file_path('fb_ord') => 1, + file_path('fba') => 1, + file_path('fba_ord') => 1, + file_path('fab') => 1, + file_path('fab_ord') => 1, + file_path('faba') => 1, + file_path('faba_ord') => 1, + file_path('faa') => 1, + file_path('faa_ord') => 1); + + %Expect_Name = (); + %Expect_Dir = (); + undef $warn_msg; + + File::Find::find( {wanted => \&wanted_File, follow => 1, + dangling_symlinks => + sub { $warn_msg = "$_[0] is a dangling symbolic link" } + }, + topdir('dangling_dir_sl'), topdir('fa') ); + + Check( scalar(keys %Expect_File) == 0 ); + Check( $warn_msg =~ m|dangling_dir_sl is a dangling symbolic link| ); + unlink file_path('fa', 'dangling_file_sl'), + file_path('dangling_dir_sl'); + + } + + + print "# check recursion\n"; + if ($^O eq 'MacOS') { + CheckDie( symlink(':fa:faa',':fa:faa:faa_sl') ); + } else { + CheckDie( symlink('../faa','fa/faa/faa_sl') ); + } + undef $@; + eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, + no_chdir => 1}, topdir('fa') ); }; + Check( $@ =~ m|for_find[:/]fa[:/]faa[:/]faa_sl is a recursive symbolic link| ); + unlink file_path('fa', 'faa', 'faa_sl'); + + + print "# check follow_skip (file)\n"; + if ($^O eq 'MacOS') { + CheckDie( symlink(':fa:fa_ord',':fa:fa_ord_sl') ); # symlink to a file + } else { + CheckDie( symlink('./fa_ord','fa/fa_ord_sl') ); # symlink to a file + } + undef $@; + + eval {File::Find::finddepth( {wanted => \&simple_wanted, + follow => 1, + follow_skip => 0, no_chdir => 1}, + topdir('fa') );}; + + Check( $@ =~ m|for_find[:/]fa[:/]fa_ord encountered a second time| ); + + + # no_chdir is in effect, hence we use file_path_name to specify + # the expected paths for %Expect_File + + %Expect_File = (file_path_name('fa') => 1, + file_path_name('fa', 'fa_ord') => 1, + file_path_name('fa', 'fsl') => 1, + file_path_name('fa', 'fsl', 'fb_ord') => 1, + file_path_name('fa', 'fsl', 'fba') => 1, + file_path_name('fa', 'fsl', 'fba', 'fba_ord') => 1, + file_path_name('fa', 'fab') => 1, + file_path_name('fa', 'fab', 'fab_ord') => 1, + file_path_name('fa', 'fab', 'faba') => 1, + file_path_name('fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('fa', 'faa') => 1, + file_path_name('fa', 'faa', 'faa_ord') => 1); + + %Expect_Name = (); + + %Expect_Dir = (dir_path('fa') => 1, + dir_path('fa', 'faa') => 1, + dir_path('fa', 'fab') => 1, + dir_path('fa', 'fab', 'faba') => 1, + dir_path('fb') => 1, + dir_path('fb','fba') => 1); + + File::Find::finddepth( {wanted => \&wanted_File_Dir, follow => 1, + follow_skip => 1, no_chdir => 1}, + topdir('fa') ); + + Check( scalar(keys %Expect_File) == 0 ); + unlink file_path('fa', 'fa_ord_sl'); + + + print "# check follow_skip (directory)\n"; + if ($^O eq 'MacOS') { + CheckDie( symlink(':fa:faa',':fa:faa_sl') ); # symlink to a directory + } else { + CheckDie( symlink('./faa','fa/faa_sl') ); # symlink to a directory + } + undef $@; + + eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, + follow_skip => 0, no_chdir => 1}, + topdir('fa') );}; + + Check( $@ =~ m|for_find[:/]fa[:/]faa[:/]? encountered a second time| ); + + + undef $@; + + eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, + follow_skip => 1, no_chdir => 1}, + topdir('fa') );}; + + Check( $@ =~ m|for_find[:/]fa[:/]faa[:/]? encountered a second time| ); + + # no_chdir is in effect, hence we use file_path_name to specify + # the expected paths for %Expect_File + + %Expect_File = (file_path_name('fa') => 1, + file_path_name('fa', 'fa_ord') => 1, + file_path_name('fa', 'fsl') => 1, + file_path_name('fa', 'fsl', 'fb_ord') => 1, + file_path_name('fa', 'fsl', 'fba') => 1, + file_path_name('fa', 'fsl', 'fba', 'fba_ord') => 1, + file_path_name('fa', 'fab') => 1, + file_path_name('fa', 'fab', 'fab_ord') => 1, + file_path_name('fa', 'fab', 'faba') => 1, + file_path_name('fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('fa', 'faa') => 1, + file_path_name('fa', 'faa', 'faa_ord') => 1); + + %Expect_Name = (); + + %Expect_Dir = (dir_path('fa') => 1, + dir_path('fa', 'faa') => 1, + dir_path('fa', 'fab') => 1, + dir_path('fa', 'fab', 'faba') => 1, + dir_path('fb') => 1, + dir_path('fb', 'fba') => 1); + + File::Find::find( {wanted => \&wanted_File_Dir, follow => 1, + follow_skip => 2, no_chdir => 1}, topdir('fa') ); + + Check( scalar(keys %Expect_File) == 0 ); + unlink file_path('fa', 'faa_sl'); + +} + diff --git a/lib/File/Find/taint.t b/lib/File/Find/taint.t new file mode 100644 index 0000000000..5ee1c3dd6d --- /dev/null +++ b/lib/File/Find/taint.t @@ -0,0 +1,388 @@ +#!./perl -T + + +my %Expect_File = (); # what we expect for $_ +my %Expect_Name = (); # what we expect for $File::Find::name/fullname +my %Expect_Dir = (); # what we expect for $File::Find::dir +my $symlink_exists = eval { symlink("",""); 1 }; +my $cwd; +my $cwd_untainted; + +BEGIN { + chdir 't' if -d 't'; + unshift @INC => '../lib'; + + for (keys %ENV) { # untaint ENV + ($ENV{$_}) = $ENV{$_} =~ /(.*)/; + } +} + +if ( $symlink_exists ) { print "1..45\n"; } +else { print "1..27\n"; } + +use File::Find; +use File::Spec; +use Cwd; + +# Remove insecure directories from PATH +my @path; +my $sep = ($^O eq 'MSWin32') ? ';' : ':'; +foreach my $dir (split(/$sep/,$ENV{'PATH'})) + { + push(@path,$dir) unless -w $dir; + } +$ENV{'PATH'} = join($sep,@path); + +cleanup(); + +find({wanted => sub { print "ok 1\n" if $_ eq 'commonsense.t'; }, + untaint => 1, untaint_pattern => qr|^(.+)$|}, File::Spec->curdir); + +finddepth({wanted => sub { print "ok 2\n" if $_ eq 'commonsense.t'; }, + untaint => 1, untaint_pattern => qr|^(.+)$|}, + File::Spec->curdir); + +my $case = 2; +my $FastFileTests_OK = 0; + +sub cleanup { + if (-d dir_path('for_find')) { + chdir(dir_path('for_find')); + } + if (-d dir_path('fa')) { + unlink file_path('fa', 'fa_ord'), + file_path('fa', 'fsl'), + file_path('fa', 'faa', 'faa_ord'), + file_path('fa', 'fab', 'fab_ord'), + file_path('fa', 'fab', 'faba', 'faba_ord'), + file_path('fb', 'fb_ord'), + file_path('fb', 'fba', 'fba_ord'); + rmdir dir_path('fa', 'faa'); + rmdir dir_path('fa', 'fab', 'faba'); + rmdir dir_path('fa', 'fab'); + rmdir dir_path('fa'); + rmdir dir_path('fb', 'fba'); + rmdir dir_path('fb'); + chdir File::Spec->updir; + rmdir dir_path('for_find'); + } +} + +END { + cleanup(); +} + +sub Check($) { + $case++; + if ($_[0]) { print "ok $case\n"; } + else { print "not ok $case\n"; } +} + +sub CheckDie($) { + $case++; + if ($_[0]) { print "ok $case\n"; } + else { print "not ok $case\n $!\n"; exit 0; } +} + +sub touch { + CheckDie( open(my $T,'>',$_[0]) ); +} + +sub MkDir($$) { + CheckDie( mkdir($_[0],$_[1]) ); +} + +sub wanted_File_Dir { + print "# \$File::Find::dir => '$File::Find::dir'\n"; + print "# \$_ => '$_'\n"; + s#\.$## if ($^O eq 'VMS' && $_ ne '.'); + Check( $Expect_File{$_} ); + if ( $FastFileTests_OK ) { + delete $Expect_File{ $_} + unless ( $Expect_Dir{$_} && ! -d _ ); + } else { + delete $Expect_File{$_} + unless ( $Expect_Dir{$_} && ! -d $_ ); + } +} + +sub wanted_File_Dir_prune { + &wanted_File_Dir; + $File::Find::prune=1 if $_ eq 'faba'; +} + + +sub simple_wanted { + print "# \$File::Find::dir => '$File::Find::dir'\n"; + print "# \$_ => '$_'\n"; +} + + +# Use dir_path() to specify a directory path that's expected for +# $File::Find::dir (%Expect_Dir). Also use it in file operations like +# chdir, rmdir etc. +# +# dir_path() concatenates directory names to form a _relative_ +# directory path, independant from the platform it's run on, although +# there are limitations. Don't try to create an absolute path, +# because that may fail on operating systems that have the concept of +# volume names (e.g. Mac OS). Be careful when you want to create an +# updir path like ../fa (Unix) or ::fa: (Mac OS). Plain directory +# names will work best. As a special case, you can pass it a "." as +# first argument, to create a directory path like "./fa/dir" on +# operating systems other than Mac OS (actually, Mac OS will ignore +# the ".", if it's the first argument). If there's no second argument, +# this function will return the empty string on Mac OS and the string +# "./" otherwise. + +sub dir_path { + my $first_item = shift @_; + + if ($first_item eq '.') { + if ($^O eq 'MacOS') { + return '' unless @_; + # ignore first argument; return a relative path + # with leading ":" and with trailing ":" + return File::Spec->catdir("", @_); + } else { # other OS + return './' unless @_; + my $path = File::Spec->catdir(@_); + # add leading "./" + $path = "./$path"; + return $path; + } + + } else { # $first_item ne '.' + return $first_item unless @_; # return plain filename + if ($^O eq 'MacOS') { + # relative path with leading ":" and with trailing ":" + return File::Spec->catdir("", $first_item, @_); + } else { # other OS + return File::Spec->catdir($first_item, @_); + } + } +} + + +# Use topdir() to specify a directory path that you want to pass to +#find/finddepth Basically, topdir() does the same as dir_path() (see +#above), except that there's no trailing ":" on Mac OS. + +sub topdir { + my $path = dir_path(@_); + $path =~ s/:$// if ($^O eq 'MacOS'); + return $path; +} + + +# Use file_path() to specify a file path that's expected for $_ (%Expect_File). +# Also suitable for file operations like unlink etc. + +# file_path() concatenates directory names (if any) and a filename to +# form a _relative_ file path (the last argument is assumed to be a +# file). It's independant from the platform it's run on, although +# there are limitations (see the warnings for dir_path() above). As a +# special case, you can pass it a "." as first argument, to create a +# file path like "./fa/file" on operating systems other than Mac OS +# (actually, Mac OS will ignore the ".", if it's the first +# argument). If there's no second argument, this function will return +# the empty string on Mac OS and the string "./" otherwise. + +sub file_path { + my $first_item = shift @_; + + if ($first_item eq '.') { + if ($^O eq 'MacOS') { + return '' unless @_; + # ignore first argument; return a relative path + # with leading ":", but without trailing ":" + return File::Spec->catfile("", @_); + } else { # other OS + return './' unless @_; + my $path = File::Spec->catfile(@_); + # add leading "./" + $path = "./$path"; + return $path; + } + + } else { # $first_item ne '.' + return $first_item unless @_; # return plain filename + if ($^O eq 'MacOS') { + # relative path with leading ":", but without trailing ":" + return File::Spec->catfile("", $first_item, @_); + } else { # other OS + return File::Spec->catfile($first_item, @_); + } + } +} + + +# Use file_path_name() to specify a file path that's expected for +# $File::Find::Name (%Expect_Name). Note: When the no_chdir => 1 +# option is in effect, $_ is the same as $File::Find::Name. In that +# case, also use this function to specify a file path that's expected +# for $_. +# +# Basically, file_path_name() does the same as file_path() (see +# above), except that there's always a leading ":" on Mac OS, even for +# plain file/directory names. + +sub file_path_name { + my $path = file_path(@_); + $path = ":$path" if (($^O eq 'MacOS') && ($path !~ /:/)); + return $path; +} + + + +MkDir( dir_path('for_find'), 0770 ); +CheckDie(chdir( dir_path('for_find'))); + +$cwd = cwd(); # save cwd +( $cwd_untainted ) = $cwd =~ m|^(.+)$|; # untaint it + +MkDir( dir_path('fa'), 0770 ); +MkDir( dir_path('fb'), 0770 ); +touch( file_path('fb', 'fb_ord') ); +MkDir( dir_path('fb', 'fba'), 0770 ); +touch( file_path('fb', 'fba', 'fba_ord') ); +if ($^O eq 'MacOS') { + CheckDie( symlink(':fb',':fa:fsl') ) if $symlink_exists; +} else { + CheckDie( symlink('../fb','fa/fsl') ) if $symlink_exists; +} +touch( file_path('fa', 'fa_ord') ); + +MkDir( dir_path('fa', 'faa'), 0770 ); +touch( file_path('fa', 'faa', 'faa_ord') ); +MkDir( dir_path('fa', 'fab'), 0770 ); +touch( file_path('fa', 'fab', 'fab_ord') ); +MkDir( dir_path('fa', 'fab', 'faba'), 0770 ); +touch( file_path('fa', 'fab', 'faba', 'faba_ord') ); + +print "# check untainting (no follow)\n"; + +# untainting here should work correctly + +%Expect_File = (File::Spec->curdir => 1, file_path('fsl') => + 1,file_path('fa_ord') => 1, file_path('fab') => 1, + file_path('fab_ord') => 1, file_path('faba') => 1, + file_path('faa') => 1, file_path('faa_ord') => 1); +delete $Expect_File{ file_path('fsl') } unless $symlink_exists; +%Expect_Name = (); + +%Expect_Dir = ( dir_path('fa') => 1, dir_path('faa') => 1, + dir_path('fab') => 1, dir_path('faba') => 1, + dir_path('fb') => 1, dir_path('fba') => 1); + +delete @Expect_Dir{ dir_path('fb'), dir_path('fba') } unless $symlink_exists; + +File::Find::find( {wanted => \&wanted_File_Dir_prune, untaint => 1, + untaint_pattern => qr|^(.+)$|}, topdir('fa') ); + +Check( scalar(keys %Expect_File) == 0 ); + + +# don't untaint at all, should die +%Expect_File = (); +%Expect_Name = (); +%Expect_Dir = (); +undef $@; +eval {File::Find::find( {wanted => \&simple_wanted}, topdir('fa') );}; +Check( $@ =~ m|Insecure dependency| ); +chdir($cwd_untainted); + + +# untaint pattern doesn't match, should die +undef $@; + +eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1, + untaint_pattern => qr|^(NO_MATCH)$|}, + topdir('fa') );}; + +Check( $@ =~ m|is still tainted| ); +chdir($cwd_untainted); + + +# untaint pattern doesn't match, should die when we chdir to cwd +print "# check untaint_skip (no follow)\n"; +undef $@; + +eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1, + untaint_skip => 1, untaint_pattern => + qr|^(NO_MATCH)$|}, topdir('fa') );}; + +Check( $@ =~ m|insecure cwd| ); +chdir($cwd_untainted); + + +if ( $symlink_exists ) { + print "# --- symbolic link tests --- \n"; + $FastFileTests_OK= 1; + + print "# check untainting (follow)\n"; + + # untainting here should work correctly + # no_chdir is in effect, hence we use file_path_name to specify the expected paths for %Expect_File + + %Expect_File = (file_path_name('fa') => 1, + file_path_name('fa','fa_ord') => 1, + file_path_name('fa', 'fsl') => 1, + file_path_name('fa', 'fsl', 'fb_ord') => 1, + file_path_name('fa', 'fsl', 'fba') => 1, + file_path_name('fa', 'fsl', 'fba', 'fba_ord') => 1, + file_path_name('fa', 'fab') => 1, + file_path_name('fa', 'fab', 'fab_ord') => 1, + file_path_name('fa', 'fab', 'faba') => 1, + file_path_name('fa', 'fab', 'faba', 'faba_ord') => 1, + file_path_name('fa', 'faa') => 1, + file_path_name('fa', 'faa', 'faa_ord') => 1); + + %Expect_Name = (); + + %Expect_Dir = (dir_path('fa') => 1, + dir_path('fa', 'faa') => 1, + dir_path('fa', 'fab') => 1, + dir_path('fa', 'fab', 'faba') => 1, + dir_path('fb') => 1, + dir_path('fb', 'fba') => 1); + + File::Find::find( {wanted => \&wanted_File_Dir, follow_fast => 1, + no_chdir => 1, untaint => 1, untaint_pattern => + qr|^(.+)$| }, topdir('fa') ); + + Check( scalar(keys %Expect_File) == 0 ); + + + # don't untaint at all, should die + undef $@; + + eval {File::Find::find( {wanted => \&simple_wanted, follow => 1}, + topdir('fa') );}; + + Check( $@ =~ m|Insecure dependency| ); + chdir($cwd_untainted); + + # untaint pattern doesn't match, should die + undef $@; + + eval {File::Find::find( {wanted => \&simple_wanted, follow => 1, + untaint => 1, untaint_pattern => + qr|^(NO_MATCH)$|}, topdir('fa') );}; + + Check( $@ =~ m|is still tainted| ); + chdir($cwd_untainted); + + # untaint pattern doesn't match, should die when we chdir to cwd + print "# check untaint_skip (follow)\n"; + undef $@; + + eval {File::Find::find( {wanted => \&simple_wanted, untaint => 1, + untaint_skip => 1, untaint_pattern => + qr|^(NO_MATCH)$|}, topdir('fa') );}; + + Check( $@ =~ m|insecure cwd| ); + chdir($cwd_untainted); + +} + diff --git a/lib/File/Glob/basic.t b/lib/File/Glob/basic.t new file mode 100755 index 0000000000..ef9dd96495 --- /dev/null +++ b/lib/File/Glob/basic.t @@ -0,0 +1,175 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + if ($^O eq 'MacOS') { + @INC = qw(: ::lib ::macos:lib); + } else { + @INC = '.'; + push @INC, '../lib'; + } + require Config; import Config; + if ($Config{'extensions'} !~ /\bFile\/Glob\b/i) { + print "1..0\n"; + exit 0; + } + print "1..11\n"; +} +END { + print "not ok 1\n" unless $loaded; +} +use File::Glob ':glob'; +use Cwd (); +$loaded = 1; +print "ok 1\n"; + +sub array { + return '(', join(", ", map {defined $_ ? "\"$_\"" : "undef"} @a), ")\n"; +} + +# look for the contents of the current directory +$ENV{PATH} = "/bin"; +delete @ENV{BASH_ENV, CDPATH, ENV, IFS}; +@correct = (); +if (opendir(D, $^O eq "MacOS" ? ":" : ".")) { + @correct = grep { !/^\./ } sort readdir(D); + closedir D; +} +@a = File::Glob::glob("*", 0); +@a = sort @a; +if ("@a" ne "@correct" || GLOB_ERROR) { + print "# |@a| ne |@correct|\nnot "; +} +print "ok 2\n"; + +# look up the user's home directory +# should return a list with one item, and not set ERROR +if ($^O ne 'MSWin32' && $^O ne 'NetWare' && $^O ne 'VMS') { + eval { + ($name, $home) = (getpwuid($>))[0,7]; + 1; + } and do { + @a = bsd_glob("~$name", GLOB_TILDE); + if (scalar(@a) != 1 || $a[0] ne $home || GLOB_ERROR) { + print "not "; + } + }; +} +print "ok 3\n"; + +# check backslashing +# should return a list with one item, and not set ERROR +@a = bsd_glob('TEST', GLOB_QUOTE); +if (scalar @a != 1 || $a[0] ne 'TEST' || GLOB_ERROR) { + local $/ = "]["; + print "# [@a]\n"; + print "not "; +} +print "ok 4\n"; + +# check nonexistent checks +# should return an empty list +# XXX since errfunc is NULL on win32, this test is not valid there +@a = bsd_glob("asdfasdf", 0); +if (($^O ne 'MSWin32' && $^O ne 'NetWare') and scalar @a != 0) { + print "# |@a|\nnot "; +} +print "ok 5\n"; + +# check bad protections +# should return an empty list, and set ERROR +if ($^O eq 'mpeix' or $^O eq 'MSWin32' or $^O eq 'NetWare' or $^O eq 'os2' or $^O eq 'VMS' + or $^O eq 'cygwin' or Cwd::cwd() =~ m#^$Config{'afsroot'}#s or not $>) +{ + print "ok 6 # skipped\n"; +} +else { + $dir = "pteerslt"; + mkdir $dir, 0; + @a = bsd_glob("$dir/*", GLOB_ERR); + #print "\@a = ", array(@a); + rmdir $dir; + if (scalar(@a) != 0 || GLOB_ERROR == 0) { + print "not "; + } + print "ok 6\n"; +} + +# check for csh style globbing +@a = bsd_glob('{a,b}', GLOB_BRACE | GLOB_NOMAGIC); +unless (@a == 2 and $a[0] eq 'a' and $a[1] eq 'b') { + print "not "; +} +print "ok 7\n"; + +@a = bsd_glob( + '{TES*,doesntexist*,a,b}', + GLOB_BRACE | GLOB_NOMAGIC | ($^O eq 'VMS' ? GLOB_NOCASE : 0) +); + +# Working on t/TEST often causes this test to fail because it sees Emacs temp +# and RCS files. Filter them out, and .pm files too, and patch temp files. +@a = grep !/(,v$|~$|\.(pm|ori?g|rej)$)/, @a; + +print "# @a\n"; + +unless (@a == 3 + and $a[0] eq ($^O eq 'VMS'? 'test.' : 'TEST') + and $a[1] eq 'a' + and $a[2] eq 'b') +{ + print "not ok 8 # @a"; +} else { + print "ok 8\n"; +} + +# "~" should expand to $ENV{HOME} +$ENV{HOME} = "sweet home"; +@a = bsd_glob('~', GLOB_TILDE | GLOB_NOMAGIC); +unless ($^O eq "MacOS" || (@a == 1 and $a[0] eq $ENV{HOME})) { + print "not "; +} +print "ok 9\n"; + +# GLOB_ALPHASORT (default) should sort alphabetically regardless of case +mkdir "pteerslt", 0777; +chdir "pteerslt"; + +@f_names = qw(Ax.pl Bx.pl Cx.pl aY.pl bY.pl cY.pl); +@f_alpha = qw(Ax.pl aY.pl Bx.pl bY.pl Cx.pl cY.pl); +if ('a' lt 'A') { # EBCDIC char sets sort lower case before UPPER + @f_names = sort(@f_names); +} +if ($^O eq 'VMS') { # VMS is happily caseignorant + @f_alpha = qw(ax.pl ay.pl bx.pl by.pl cx.pl cy.pl); + @f_names = @f_alpha; +} + +for (@f_names) { + open T, "> $_"; + close T; +} + +$pat = "*.pl"; + +$ok = 1; +@g_names = bsd_glob($pat, 0); +print "# f_names = @f_names\n"; +print "# g_names = @g_names\n"; +for (@f_names) { + $ok = 0 unless $_ eq shift @g_names; +} +print $ok ? "ok 10\n" : "not ok 10\n"; + +$ok = 1; +@g_alpha = bsd_glob($pat); +print "# f_alpha = @f_alpha\n"; +print "# g_alpha = @g_alpha\n"; +for (@f_alpha) { + $ok = 0 unless $_ eq shift @g_alpha; +} +print $ok ? "ok 11\n" : "not ok 11\n"; + +unlink @f_names; +chdir ".."; +rmdir "pteerslt"; diff --git a/lib/File/Glob/case.t b/lib/File/Glob/case.t new file mode 100755 index 0000000000..87f3b9f694 --- /dev/null +++ b/lib/File/Glob/case.t @@ -0,0 +1,60 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + if ($^O eq 'MacOS') { + @INC = qw(: ::lib ::macos:lib); + } else { + @INC = '.'; + push @INC, '../lib'; + } + require Config; import Config; + if ($Config{'extensions'} !~ /\bFile\/Glob\b/i) { + print "1..0\n"; + exit 0; + } + print "1..7\n"; +} +END { + print "not ok 1\n" unless $loaded; +} +use File::Glob qw(:glob csh_glob); +$loaded = 1; +print "ok 1\n"; + +my $pat = $^O eq "MacOS" ? ":op:G*.t" : "op/G*.t"; + +# Test the actual use of the case sensitivity tags, via csh_glob() +import File::Glob ':nocase'; +@a = csh_glob($pat); +print "not " unless @a >= 8; +print "ok 2\n"; + +# This may fail on systems which are not case-PRESERVING +import File::Glob ':case'; +@a = csh_glob($pat); # None should be uppercase +print "not " unless @a == 0; +print "ok 3\n"; + +# Test the explicit use of the GLOB_NOCASE flag +@a = bsd_glob($pat, GLOB_NOCASE); +print "not " unless @a >= 3; +print "ok 4\n"; + +# Test Win32 backslash nastiness... +if ($^O ne 'MSWin32' && $^O ne 'NetWare') { + print "ok 5\nok 6\nok 7\n"; +} +else { + @a = File::Glob::glob("op\\g*.t"); + print "not " unless @a >= 8; + print "ok 5\n"; + mkdir "[]", 0; + @a = File::Glob::glob("\\[\\]", GLOB_QUOTE); + rmdir "[]"; + print "# returned @a\nnot " unless @a == 1; + print "ok 6\n"; + @a = bsd_glob("op\\*", GLOB_QUOTE); + print "not " if @a == 0; + print "ok 7\n"; +} diff --git a/lib/File/Glob/global.t b/lib/File/Glob/global.t new file mode 100755 index 0000000000..c0abbc5ea5 --- /dev/null +++ b/lib/File/Glob/global.t @@ -0,0 +1,151 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + if ($^O eq 'MacOS') { + @INC = qw(: ::lib ::macos:lib); + } else { + @INC = '.'; + push @INC, '../lib'; + } + require Config; import Config; + if ($Config{'extensions'} !~ /\bFile\/Glob\b/i) { + print "1..0\n"; + exit 0; + } + print "1..10\n"; +} +END { + print "not ok 1\n" unless $loaded; +} + +BEGIN { + *CORE::GLOBAL::glob = sub { "Just another Perl hacker," }; +} + +BEGIN { + if ("Just another Perl hacker," ne (<*>)[0]) { + die <<EOMessage; +Your version of perl ($]) doesn't seem to allow extensions to override +the core glob operator. +EOMessage + } +} + +use File::Glob ':globally'; +$loaded = 1; +print "ok 1\n"; + +$_ = $^O eq "MacOS" ? ":op:*.t" : "op/*.t"; +my @r = glob; +print "not " if $_ ne ($^O eq "MacOS" ? ":op:*.t" : "op/*.t"); +print "ok 2\n"; + +print "# |@r|\nnot " if @r < 3; +print "ok 3\n"; + +# check if <*/*> works +if ($^O eq "MacOS") { + @r = <:*:*.t>; +} else { + @r = <*/*.t>; +} +# at least t/global.t t/basic.t, t/taint.t +print "not " if @r < 3; +print "ok 4\n"; +my $r = scalar @r; + +# check if scalar context works +@r = (); +if ($^O eq "MacOS") { + while (defined($_ = <:*:*.t>)) { + #print "# $_\n"; + push @r, $_; + } +} else { + while (defined($_ = <*/*.t>)) { + #print "# $_\n"; + push @r, $_; + } +} +print "not " if @r != $r; +print "ok 5\n"; + +# check if list context works +@r = (); +if ($^O eq "MacOS") { + for (<:*:*.t>) { + #print "# $_\n"; + push @r, $_; + } +} else { + for (<*/*.t>) { + #print "# $_\n"; + push @r, $_; + } +} +print "not " if @r != $r; +print "ok 6\n"; + +# test if implicit assign to $_ in while() works +@r = (); +if ($^O eq "MacOS") { + while (<:*:*.t>) { + #print "# $_\n"; + push @r, $_; + } +} else { + while (<*/*.t>) { + #print "# $_\n"; + push @r, $_; + } +} +print "not " if @r != $r; +print "ok 7\n"; + +# test if explicit glob() gets assign magic too +my @s = (); +while (glob($^O eq 'MacOS' ? ':*:*.t' : '*/*.t')) { + #print "# $_\n"; + push @s, $_; +} +print "not " if "@r" ne "@s"; +print "ok 8\n"; + +# how about in a different package, like? +package Foo; +use File::Glob ':globally'; +@s = (); +while (glob($^O eq 'MacOS' ? ':*:*.t' : '*/*.t')) { + #print "# $_\n"; + push @s, $_; +} +print "not " if "@r" ne "@s"; +print "ok 9\n"; + +# test if different glob ops maintain independent contexts +@s = (); +my $i = 0; +if ($^O eq "MacOS") { + while (<:*:*.t>) { + #print "# $_ <"; + push @s, $_; + while (<:bas*:*.t>) { + #print " $_"; + $i++; + } + #print " >\n"; + } +} else { + while (<*/*.t>) { + #print "# $_ <"; + push @s, $_; + while (<bas*/*.t>) { + #print " $_"; + $i++; + } + #print " >\n"; + } +} +print "not " if "@r" ne "@s" or not $i; +print "ok 10\n"; diff --git a/lib/File/Glob/taint.t b/lib/File/Glob/taint.t new file mode 100755 index 0000000000..4c0990358d --- /dev/null +++ b/lib/File/Glob/taint.t @@ -0,0 +1,31 @@ +#!./perl -T + +BEGIN { + chdir 't' if -d 't'; + if ($^O eq 'MacOS') { + @INC = qw(: ::lib ::macos:lib); + } else { + @INC = '.'; + push @INC, '../lib'; + } + require Config; import Config; + if ($Config{'extensions'} !~ /\bFile\/Glob\b/i) { + print "1..0\n"; + exit 0; + } + print "1..2\n"; +} +END { + print "not ok 1\n" unless $loaded; +} +use File::Glob; +$loaded = 1; +print "ok 1\n"; + +# all filenames should be tainted +@a = File::Glob::bsd_glob("*"); +eval { $a = join("",@a), kill 0; 1 }; +unless ($@ =~ /Insecure dependency/) { + print "not "; +} +print "ok 2\n"; diff --git a/lib/File/Path.t b/lib/File/Path.t new file mode 100755 index 0000000000..42e0ae9f93 --- /dev/null +++ b/lib/File/Path.t @@ -0,0 +1,28 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; +} + +use File::Path; +use strict; + +my $count = 0; +use warnings; + +print "1..4\n"; + +# first check for stupid permissions second for full, so we clean up +# behind ourselves +for my $perm (0111,0777) { + mkpath("foo/bar"); + chmod $perm, "foo", "foo/bar"; + + print "not " unless -d "foo" && -d "foo/bar"; + print "ok ", ++$count, "\n"; + + rmtree("foo"); + print "not " if -e "foo"; + print "ok ", ++$count, "\n"; +} diff --git a/lib/File/Spec.t b/lib/File/Spec.t new file mode 100755 index 0000000000..c6d155fac1 --- /dev/null +++ b/lib/File/Spec.t @@ -0,0 +1,379 @@ +#!./perl + +BEGIN { + $^O = ''; + chdir 't' if -d 't'; + @INC = '../lib'; +} + +# Each element in this array is a single test. Storing them this way makes +# maintenance easy, and should be OK since perl should be pretty functional +# before these tests are run. + +@tests = ( +# Function Expected +[ "Unix->catfile('a','b','c')", 'a/b/c' ], + +[ "Unix->splitpath('file')", ',,file' ], +[ "Unix->splitpath('/d1/d2/d3/')", ',/d1/d2/d3/,' ], +[ "Unix->splitpath('d1/d2/d3/')", ',d1/d2/d3/,' ], +[ "Unix->splitpath('/d1/d2/d3/.')", ',/d1/d2/d3/.,' ], +[ "Unix->splitpath('/d1/d2/d3/..')", ',/d1/d2/d3/..,' ], +[ "Unix->splitpath('/d1/d2/d3/.file')", ',/d1/d2/d3/,.file' ], +[ "Unix->splitpath('d1/d2/d3/file')", ',d1/d2/d3/,file' ], +[ "Unix->splitpath('/../../d1/')", ',/../../d1/,' ], +[ "Unix->splitpath('/././d1/')", ',/././d1/,' ], + +[ "Unix->catpath('','','file')", 'file' ], +[ "Unix->catpath('','/d1/d2/d3/','')", '/d1/d2/d3/' ], +[ "Unix->catpath('','d1/d2/d3/','')", 'd1/d2/d3/' ], +[ "Unix->catpath('','/d1/d2/d3/.','')", '/d1/d2/d3/.' ], +[ "Unix->catpath('','/d1/d2/d3/..','')", '/d1/d2/d3/..' ], +[ "Unix->catpath('','/d1/d2/d3/','.file')", '/d1/d2/d3/.file' ], +[ "Unix->catpath('','d1/d2/d3/','file')", 'd1/d2/d3/file' ], +[ "Unix->catpath('','/../../d1/','')", '/../../d1/' ], +[ "Unix->catpath('','/././d1/','')", '/././d1/' ], +[ "Unix->catpath('d1','d2/d3/','')", 'd2/d3/' ], +[ "Unix->catpath('d1','d2','d3/')", 'd2/d3/' ], + +[ "Unix->splitdir('')", '' ], +[ "Unix->splitdir('/d1/d2/d3/')", ',d1,d2,d3,' ], +[ "Unix->splitdir('d1/d2/d3/')", 'd1,d2,d3,' ], +[ "Unix->splitdir('/d1/d2/d3')", ',d1,d2,d3' ], +[ "Unix->splitdir('d1/d2/d3')", 'd1,d2,d3' ], + +[ "Unix->catdir()", '' ], +[ "Unix->catdir('/')", '/' ], +[ "Unix->catdir('','d1','d2','d3','')", '/d1/d2/d3' ], +[ "Unix->catdir('d1','d2','d3','')", 'd1/d2/d3' ], +[ "Unix->catdir('','d1','d2','d3')", '/d1/d2/d3' ], +[ "Unix->catdir('d1','d2','d3')", 'd1/d2/d3' ], + +[ "Unix->catfile('a','b','c')", 'a/b/c' ], + +[ "Unix->canonpath('')", '' ], +[ "Unix->canonpath('///../../..//./././a//b/.././c/././')", '/a/b/../c' ], +[ "Unix->canonpath('/.')", '/.' ], + +[ "Unix->abs2rel('/t1/t2/t3','/t1/t2/t3')", '' ], +[ "Unix->abs2rel('/t1/t2/t4','/t1/t2/t3')", '../t4' ], +[ "Unix->abs2rel('/t1/t2','/t1/t2/t3')", '..' ], +[ "Unix->abs2rel('/t1/t2/t3/t4','/t1/t2/t3')", 't4' ], +[ "Unix->abs2rel('/t4/t5/t6','/t1/t2/t3')", '../../../t4/t5/t6' ], +#[ "Unix->abs2rel('../t4','/t1/t2/t3')", '../t4' ], +[ "Unix->abs2rel('/','/t1/t2/t3')", '../../..' ], +[ "Unix->abs2rel('///','/t1/t2/t3')", '../../..' ], +[ "Unix->abs2rel('/.','/t1/t2/t3')", '../../../.' ], +[ "Unix->abs2rel('/./','/t1/t2/t3')", '../../..' ], +#[ "Unix->abs2rel('../t4','/t1/t2/t3')", '../t4' ], + +[ "Unix->rel2abs('t4','/t1/t2/t3')", '/t1/t2/t3/t4' ], +[ "Unix->rel2abs('t4/t5','/t1/t2/t3')", '/t1/t2/t3/t4/t5' ], +[ "Unix->rel2abs('.','/t1/t2/t3')", '/t1/t2/t3' ], +[ "Unix->rel2abs('..','/t1/t2/t3')", '/t1/t2/t3/..' ], +[ "Unix->rel2abs('../t4','/t1/t2/t3')", '/t1/t2/t3/../t4' ], +[ "Unix->rel2abs('/t1','/t1/t2/t3')", '/t1' ], + +[ "Win32->splitpath('file')", ',,file' ], +[ "Win32->splitpath('\\d1/d2\\d3/')", ',\\d1/d2\\d3/,' ], +[ "Win32->splitpath('d1/d2\\d3/')", ',d1/d2\\d3/,' ], +[ "Win32->splitpath('\\d1/d2\\d3/.')", ',\\d1/d2\\d3/.,' ], +[ "Win32->splitpath('\\d1/d2\\d3/..')", ',\\d1/d2\\d3/..,' ], +[ "Win32->splitpath('\\d1/d2\\d3/.file')", ',\\d1/d2\\d3/,.file' ], +[ "Win32->splitpath('\\d1/d2\\d3/file')", ',\\d1/d2\\d3/,file' ], +[ "Win32->splitpath('d1/d2\\d3/file')", ',d1/d2\\d3/,file' ], +[ "Win32->splitpath('C:\\d1/d2\\d3/')", 'C:,\\d1/d2\\d3/,' ], +[ "Win32->splitpath('C:d1/d2\\d3/')", 'C:,d1/d2\\d3/,' ], +[ "Win32->splitpath('C:\\d1/d2\\d3/file')", 'C:,\\d1/d2\\d3/,file' ], +[ "Win32->splitpath('C:d1/d2\\d3/file')", 'C:,d1/d2\\d3/,file' ], +[ "Win32->splitpath('C:\\../d2\\d3/file')", 'C:,\\../d2\\d3/,file' ], +[ "Win32->splitpath('C:../d2\\d3/file')", 'C:,../d2\\d3/,file' ], +[ "Win32->splitpath('\\../..\\d1/')", ',\\../..\\d1/,' ], +[ "Win32->splitpath('\\./.\\d1/')", ',\\./.\\d1/,' ], +[ "Win32->splitpath('\\\\node\\share\\d1/d2\\d3/')", '\\\\node\\share,\\d1/d2\\d3/,' ], +[ "Win32->splitpath('\\\\node\\share\\d1/d2\\d3/file')", '\\\\node\\share,\\d1/d2\\d3/,file' ], +[ "Win32->splitpath('\\\\node\\share\\d1/d2\\file')", '\\\\node\\share,\\d1/d2\\,file' ], +[ "Win32->splitpath('file',1)", ',file,' ], +[ "Win32->splitpath('\\d1/d2\\d3/',1)", ',\\d1/d2\\d3/,' ], +[ "Win32->splitpath('d1/d2\\d3/',1)", ',d1/d2\\d3/,' ], +[ "Win32->splitpath('\\\\node\\share\\d1/d2\\d3/',1)", '\\\\node\\share,\\d1/d2\\d3/,' ], + +[ "Win32->catpath('','','file')", 'file' ], +[ "Win32->catpath('','\\d1/d2\\d3/','')", '\\d1/d2\\d3/' ], +[ "Win32->catpath('','d1/d2\\d3/','')", 'd1/d2\\d3/' ], +[ "Win32->catpath('','\\d1/d2\\d3/.','')", '\\d1/d2\\d3/.' ], +[ "Win32->catpath('','\\d1/d2\\d3/..','')", '\\d1/d2\\d3/..' ], +[ "Win32->catpath('','\\d1/d2\\d3/','.file')", '\\d1/d2\\d3/.file' ], +[ "Win32->catpath('','\\d1/d2\\d3/','file')", '\\d1/d2\\d3/file' ], +[ "Win32->catpath('','d1/d2\\d3/','file')", 'd1/d2\\d3/file' ], +[ "Win32->catpath('C:','\\d1/d2\\d3/','')", 'C:\\d1/d2\\d3/' ], +[ "Win32->catpath('C:','d1/d2\\d3/','')", 'C:d1/d2\\d3/' ], +[ "Win32->catpath('C:','\\d1/d2\\d3/','file')", 'C:\\d1/d2\\d3/file' ], +[ "Win32->catpath('C:','d1/d2\\d3/','file')", 'C:d1/d2\\d3/file' ], +[ "Win32->catpath('C:','\\../d2\\d3/','file')", 'C:\\../d2\\d3/file' ], +[ "Win32->catpath('C:','../d2\\d3/','file')", 'C:../d2\\d3/file' ], +[ "Win32->catpath('','\\../..\\d1/','')", '\\../..\\d1/' ], +[ "Win32->catpath('','\\./.\\d1/','')", '\\./.\\d1/' ], +[ "Win32->catpath('\\\\node\\share','\\d1/d2\\d3/','')", '\\\\node\\share\\d1/d2\\d3/' ], +[ "Win32->catpath('\\\\node\\share','\\d1/d2\\d3/','file')", '\\\\node\\share\\d1/d2\\d3/file' ], +[ "Win32->catpath('\\\\node\\share','\\d1/d2\\','file')", '\\\\node\\share\\d1/d2\\file' ], + +[ "Win32->splitdir('')", '' ], +[ "Win32->splitdir('\\d1/d2\\d3/')", ',d1,d2,d3,' ], +[ "Win32->splitdir('d1/d2\\d3/')", 'd1,d2,d3,' ], +[ "Win32->splitdir('\\d1/d2\\d3')", ',d1,d2,d3' ], +[ "Win32->splitdir('d1/d2\\d3')", 'd1,d2,d3' ], + +[ "Win32->catdir()", '' ], +[ "Win32->catdir('')", '\\' ], +[ "Win32->catdir('/')", '\\' ], +[ "Win32->catdir('//d1','d2')", '\\\\d1\\d2' ], +[ "Win32->catdir('','/d1','d2')", '\\\\d1\\d2' ], +[ "Win32->catdir('','','/d1','d2')", '\\\\\\d1\\d2' ], +[ "Win32->catdir('','//d1','d2')", '\\\\\\d1\\d2' ], +[ "Win32->catdir('','','//d1','d2')", '\\\\\\\\d1\\d2' ], +[ "Win32->catdir('','d1','','d2','')", '\\d1\\d2' ], +[ "Win32->catdir('','d1','d2','d3','')", '\\d1\\d2\\d3' ], +[ "Win32->catdir('d1','d2','d3','')", 'd1\\d2\\d3' ], +[ "Win32->catdir('','d1','d2','d3')", '\\d1\\d2\\d3' ], +[ "Win32->catdir('d1','d2','d3')", 'd1\\d2\\d3' ], +[ "Win32->catdir('A:/d1','d2','d3')", 'A:\\d1\\d2\\d3' ], +[ "Win32->catdir('A:/d1','d2','d3','')", 'A:\\d1\\d2\\d3' ], +#[ "Win32->catdir('A:/d1','B:/d2','d3','')", 'A:\\d1\\d2\\d3' ], +[ "Win32->catdir('A:/d1','B:/d2','d3','')", 'A:\\d1\\B:\\d2\\d3' ], +[ "Win32->catdir('A:/')", 'A:\\' ], + +[ "Win32->catfile('a','b','c')", 'a\\b\\c' ], + +[ "Win32->canonpath('')", '' ], +[ "Win32->canonpath('a:')", 'A:' ], +[ "Win32->canonpath('A:f')", 'A:f' ], +[ "Win32->canonpath('//a\\b//c')", '\\\\a\\b\\c' ], +[ "Win32->canonpath('/a/..../c')", '\\a\\....\\c' ], +[ "Win32->canonpath('//a/b\\c')", '\\\\a\\b\\c' ], +[ "Win32->canonpath('////')", '\\\\\\' ], +[ "Win32->canonpath('//')", '\\' ], +[ "Win32->canonpath('/.')", '\\.' ], +[ "Win32->canonpath('//a/b/../../c')", '\\\\a\\b\\..\\..\\c' ], +[ "Win32->canonpath('//a/../../c')", '\\\\a\\..\\..\\c' ], + +[ "Win32->abs2rel('/t1/t2/t3','/t1/t2/t3')", '' ], +[ "Win32->abs2rel('/t1/t2/t4','/t1/t2/t3')", '..\\t4' ], +[ "Win32->abs2rel('/t1/t2','/t1/t2/t3')", '..' ], +[ "Win32->abs2rel('/t1/t2/t3/t4','/t1/t2/t3')", 't4' ], +[ "Win32->abs2rel('/t4/t5/t6','/t1/t2/t3')", '..\\..\\..\\t4\\t5\\t6' ], +#[ "Win32->abs2rel('../t4','/t1/t2/t3')", '\\t1\\t2\\t3\\..\\t4' ], +[ "Win32->abs2rel('/','/t1/t2/t3')", '..\\..\\..' ], +[ "Win32->abs2rel('///','/t1/t2/t3')", '..\\..\\..' ], +[ "Win32->abs2rel('/.','/t1/t2/t3')", '..\\..\\..\\.' ], +[ "Win32->abs2rel('/./','/t1/t2/t3')", '..\\..\\..' ], +[ "Win32->abs2rel('\\\\a/t1/t2/t4','/t2/t3')", '..\\t4' ], +[ "Win32->abs2rel('//a/t1/t2/t4','/t2/t3')", '..\\t4' ], + +[ "Win32->rel2abs('temp','C:/')", 'C:\\temp' ], +[ "Win32->rel2abs('temp','C:/a')", 'C:\\a\\temp' ], +[ "Win32->rel2abs('temp','C:/a/')", 'C:\\a\\temp' ], +[ "Win32->rel2abs('../','C:/')", 'C:\\..' ], +[ "Win32->rel2abs('../','C:/a')", 'C:\\a\\..' ], +[ "Win32->rel2abs('temp','//prague_main/work/')", '\\\\prague_main\\work\\temp' ], +[ "Win32->rel2abs('../temp','//prague_main/work/')", '\\\\prague_main\\work\\..\\temp' ], +[ "Win32->rel2abs('temp','//prague_main/work')", '\\\\prague_main\\work\\temp' ], +[ "Win32->rel2abs('../','//prague_main/work')", '\\\\prague_main\\work\\..' ], + +[ "VMS->splitpath('file')", ',,file' ], +[ "VMS->splitpath('[d1.d2.d3]')", ',[d1.d2.d3],' ], +[ "VMS->splitpath('[.d1.d2.d3]')", ',[.d1.d2.d3],' ], +[ "VMS->splitpath('[d1.d2.d3]file')", ',[d1.d2.d3],file' ], +[ "VMS->splitpath('d1/d2/d3/file')", ',[.d1.d2.d3],file' ], +[ "VMS->splitpath('/d1/d2/d3/file')", 'd1:,[d2.d3],file' ], +[ "VMS->splitpath('[.d1.d2.d3]file')", ',[.d1.d2.d3],file' ], +[ "VMS->splitpath('node::volume:[d1.d2.d3]')", 'node::volume:,[d1.d2.d3],' ], +[ "VMS->splitpath('node::volume:[d1.d2.d3]file')", 'node::volume:,[d1.d2.d3],file' ], +[ "VMS->splitpath('node\"access_spec\"::volume:[d1.d2.d3]')", 'node"access_spec"::volume:,[d1.d2.d3],' ], +[ "VMS->splitpath('node\"access_spec\"::volume:[d1.d2.d3]file')", 'node"access_spec"::volume:,[d1.d2.d3],file' ], + +[ "VMS->catpath('','','file')", 'file' ], +[ "VMS->catpath('','[d1.d2.d3]','')", '[d1.d2.d3]' ], +[ "VMS->catpath('','[.d1.d2.d3]','')", '[.d1.d2.d3]' ], +[ "VMS->catpath('','[d1.d2.d3]','file')", '[d1.d2.d3]file' ], +[ "VMS->catpath('','[.d1.d2.d3]','file')", '[.d1.d2.d3]file' ], +[ "VMS->catpath('','d1/d2/d3','file')", '[.d1.d2.d3]file' ], +[ "VMS->catpath('v','d1/d2/d3','file')", 'v:[.d1.d2.d3]file' ], +[ "VMS->catpath('node::volume:','[d1.d2.d3]','')", 'node::volume:[d1.d2.d3]' ], +[ "VMS->catpath('node::volume:','[d1.d2.d3]','file')", 'node::volume:[d1.d2.d3]file' ], +[ "VMS->catpath('node\"access_spec\"::volume:','[d1.d2.d3]','')", 'node"access_spec"::volume:[d1.d2.d3]' ], +[ "VMS->catpath('node\"access_spec\"::volume:','[d1.d2.d3]','file')", 'node"access_spec"::volume:[d1.d2.d3]file' ], + +[ "VMS->canonpath('')", '' ], +[ "VMS->canonpath('volume:[d1]file')", 'volume:[d1]file' ], +[ "VMS->canonpath('volume:[d1.-.d2.][d3.d4.-]')", 'volume:[d2.d3]' ], +[ "VMS->canonpath('volume:[000000.d1]d2.dir;1')", 'volume:[d1]d2.dir;1' ], + +[ "VMS->splitdir('')", '' ], +[ "VMS->splitdir('[]')", '' ], +[ "VMS->splitdir('d1.d2.d3')", 'd1,d2,d3' ], +[ "VMS->splitdir('[d1.d2.d3]')", 'd1,d2,d3' ], +[ "VMS->splitdir('.d1.d2.d3')", ',d1,d2,d3' ], +[ "VMS->splitdir('[.d1.d2.d3]')", ',d1,d2,d3' ], +[ "VMS->splitdir('.-.d2.d3')", ',-,d2,d3' ], +[ "VMS->splitdir('[.-.d2.d3]')", ',-,d2,d3' ], + +[ "VMS->catdir('')", '' ], +[ "VMS->catdir('d1','d2','d3')", '[.d1.d2.d3]' ], +[ "VMS->catdir('d1','d2/','d3')", '[.d1.d2.d3]' ], +[ "VMS->catdir('','d1','d2','d3')", '[.d1.d2.d3]' ], +[ "VMS->catdir('','-','d2','d3')", '[-.d2.d3]' ], +[ "VMS->catdir('','-','','d3')", '[-.d3]' ], +[ "VMS->catdir('dir.dir','d2.dir','d3.dir')", '[.dir.d2.d3]' ], +[ "VMS->catdir('[.name]')", '[.name]' ], +[ "VMS->catdir('[.name]','[.name]')", '[.name.name]'], + +[ "VMS->abs2rel('node::volume:[t1.t2.t3]','[t1.t2.t3]')", '' ], +[ "VMS->abs2rel('node::volume:[t1.t2.t4]','[t1.t2.t3]')", '[-.t4]' ], +[ "VMS->abs2rel('[t1.t2.t3]','[t1.t2.t3]')", '' ], +[ "VMS->abs2rel('[t1.t2.t3]file','[t1.t2.t3]')", 'file' ], +[ "VMS->abs2rel('[t1.t2.t4]','[t1.t2.t3]')", '[-.t4]' ], +[ "VMS->abs2rel('[t1.t2]file','[t1.t2.t3]')", '[-]file' ], +[ "VMS->abs2rel('[t1.t2.t3.t4]','[t1.t2.t3]')", '[t4]' ], +[ "VMS->abs2rel('[t4.t5.t6]','[t1.t2.t3]')", '[---.t4.t5.t6]' ], +[ "VMS->abs2rel('[000000]','[t1.t2.t3]')", '[---.000000]' ], +[ "VMS->abs2rel('a:[t1.t2.t4]','[t1.t2.t3]')", '[-.t4]' ], +[ "VMS->abs2rel('[a.-.b.c.-]','[t1.t2.t3]')", '[---.b]' ], + +[ "VMS->rel2abs('[.t4]','[t1.t2.t3]')", '[t1.t2.t3.t4]' ], +[ "VMS->rel2abs('[.t4.t5]','[t1.t2.t3]')", '[t1.t2.t3.t4.t5]' ], +[ "VMS->rel2abs('[]','[t1.t2.t3]')", '[t1.t2.t3]' ], +[ "VMS->rel2abs('[-]','[t1.t2.t3]')", '[t1.t2]' ], +[ "VMS->rel2abs('[-.t4]','[t1.t2.t3]')", '[t1.t2.t4]' ], +[ "VMS->rel2abs('[t1]','[t1.t2.t3]')", '[t1]' ], + +[ "OS2->catdir('A:/d1','B:/d2','d3','')", 'A:/d1/B:/d2/d3' ], +[ "OS2->catfile('a','b','c')", 'a/b/c' ], + +[ "Mac->splitpath('file')", ',,file' ], +[ "Mac->splitpath(':file')", ',:,file' ], +[ "Mac->splitpath(':d1',1)", ',:d1:,' ], +[ "Mac->splitpath('d1',1)", 'd1:,,' ], +[ "Mac->splitpath('d1:d2:d3:')", 'd1:,d2:d3:,' ], +[ "Mac->splitpath('d1:d2:d3',1)", 'd1:,d2:d3:,' ], +[ "Mac->splitpath(':d1:d2:d3:')", ',:d1:d2:d3:,' ], +[ "Mac->splitpath(':d1:d2:d3:',1)", ',:d1:d2:d3:,' ], +[ "Mac->splitpath('d1:d2:d3:file')", 'd1:,d2:d3:,file' ], +[ "Mac->splitpath('d1:d2:d3',1)", 'd1:,d2:d3:,' ], + +[ "Mac->catdir('')", ':' ], +[ "Mac->catdir('d1','d2','d3')", 'd1:d2:d3:' ], +[ "Mac->catdir('d1','d2/','d3')", 'd1:d2/:d3:' ], +[ "Mac->catdir('','d1','d2','d3')", ':d1:d2:d3:' ], +[ "Mac->catdir('','','d2','d3')", '::d2:d3:' ], +[ "Mac->catdir('','','','d3')", ':::d3:' ], +[ "Mac->catdir(':name')", ':name:' ], +[ "Mac->catdir(':name',':name')", ':name:name:' ], + +[ "Mac->catfile('a','b','c')", 'a:b:c' ], + +[ "Mac->canonpath('')", '' ], +[ "Mac->canonpath(':')", ':' ], +[ "Mac->canonpath('::')", '::' ], +[ "Mac->canonpath('a::')", 'a::' ], +[ "Mac->canonpath(':a::')", ':a::' ], + +[ "Mac->abs2rel('t1:t2:t3','t1:t2:t3')", ':' ], +[ "Mac->abs2rel('t1:t2','t1:t2:t3')", '::' ], +[ "Mac->abs2rel('t1:t4','t1:t2:t3')", ':::t4' ], +[ "Mac->abs2rel('t1:t2:t4','t1:t2:t3')", '::t4' ], +[ "Mac->abs2rel('t1:t2:t3:t4','t1:t2:t3')", ':t4' ], +[ "Mac->abs2rel('t4:t5:t6','t1:t2:t3')", '::::t4:t5:t6' ], +[ "Mac->abs2rel('t1','t1:t2:t3')", ':::' ], + +[ "Mac->rel2abs(':t4','t1:t2:t3')", 't1:t2:t3:t4' ], +[ "Mac->rel2abs(':t4:t5','t1:t2:t3')", 't1:t2:t3:t4:t5' ], +[ "Mac->rel2abs('','t1:t2:t3')", '' ], +[ "Mac->rel2abs('::','t1:t2:t3')", 't1:t2:t3::' ], +[ "Mac->rel2abs('::t4','t1:t2:t3')", 't1:t2:t3::t4' ], +[ "Mac->rel2abs('t1','t1:t2:t3')", 't1' ], +) ; + +# Grab all of the plain routines from File::Spec +use File::Spec @File::Spec::EXPORT_OK ; + +require File::Spec::Unix ; +require File::Spec::Win32 ; + +eval { + require VMS::Filespec ; +} ; + +my $skip_exception = "Install VMS::Filespec (from vms/ext)" ; + +if ( $@ ) { + # Not pretty, but it allows testing of things not implemented soley + # on VMS. It might be better to change File::Spec::VMS to do this, + # making it more usable when running on (say) Unix but working with + # VMS paths. + eval qq- + sub File::Spec::VMS::vmsify { die "$skip_exception" } + sub File::Spec::VMS::unixify { die "$skip_exception" } + sub File::Spec::VMS::vmspath { die "$skip_exception" } + - ; + $INC{"VMS/Filespec.pm"} = 1 ; +} +require File::Spec::VMS ; + +require File::Spec::OS2 ; +require File::Spec::Mac ; + +print "1..", scalar( @tests ), "\n" ; + +my $current_test= 1 ; + +# Test out the class methods +for ( @tests ) { + tryfunc( @$_ ) ; +} + + + +# +# Tries a named function with the given args and compares the result against +# an expected result. Works with functions that return scalars or arrays. +# +sub tryfunc { + my $function = shift ; + my $expected = shift ; + my $platform = shift ; + + if ($platform && $^O ne $platform) { + print "ok $current_test # skipped: $function\n" ; + ++$current_test ; + return; + } + + $function =~ s#\\#\\\\#g ; + + my $got ; + if ( $function =~ /^[^\$].*->/ ) { + $got = eval( "join( ',', File::Spec::$function )" ) ; + } + else { + $got = eval( "join( ',', $function )" ) ; + } + + if ( $@ ) { + if ( substr( $@, 0, length $skip_exception ) eq $skip_exception ) { + chomp $@ ; + print "ok $current_test # skip $function: $@\n" ; + } + else { + chomp $@ ; + print "not ok $current_test # $function: $@\n" ; + } + } + elsif ( !defined( $got ) || $got ne $expected ) { + print "not ok $current_test # $function: got '$got', expected '$expected'\n" ; + } + else { + print "ok $current_test # $function\n" ; + } + ++$current_test ; +} diff --git a/lib/File/Spec/Functions.t b/lib/File/Spec/Functions.t new file mode 100755 index 0000000000..926812248c --- /dev/null +++ b/lib/File/Spec/Functions.t @@ -0,0 +1,17 @@ +#!./perl + +BEGIN { + $^O = ''; + chdir 't' if -d 't'; + @INC = '../lib'; +} + +print "1..1\n"; + +use File::Spec::Functions; + +if (catfile('a','b','c') eq 'a/b/c') { + print "ok 1\n"; +} else { + print "not ok 1\n"; +} diff --git a/lib/File/Temp/mktemp.t b/lib/File/Temp/mktemp.t new file mode 100755 index 0000000000..4e31d01a3f --- /dev/null +++ b/lib/File/Temp/mktemp.t @@ -0,0 +1,115 @@ +#!/usr/bin/perl -w + +# Test for mktemp family of commands in File::Temp +# Use STANDARD safe level for these tests + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; + require Test; import Test; + plan(tests => 9); +} + +use strict; + +use File::Spec; +use File::Path; +use File::Temp qw/ :mktemp unlink0 /; +use FileHandle; + +ok(1); + +# MKSTEMP - test + +# Create file in temp directory +my $template = File::Spec->catfile(File::Spec->tmpdir, 'wowserXXXX'); + +(my $fh, $template) = mkstemp($template); + +print "# MKSTEMP: FH is $fh File is $template fileno=".fileno($fh)."\n"; +# Check if the file exists +ok( (-e $template) ); + +# Autoflush +$fh->autoflush(1) if $] >= 5.006; + +# Try printing something to the file +my $string = "woohoo\n"; +print $fh $string; + +# rewind the file +ok(seek( $fh, 0, 0)); + +# Read from the file +my $line = <$fh>; + +# compare with previous string +ok($string, $line); + +# Tidy up +# This test fails on Windows NT since it seems that the size returned by +# stat(filehandle) does not always equal the size of the stat(filename) +# This must be due to caching. In particular this test writes 7 bytes +# to the file which are not recognised by stat(filename) +# Simply waiting 3 seconds seems to be enough for the system to update + +if ($^O eq 'MSWin32') { + sleep 3; +} +my $status = unlink0($fh, $template); +if ($status) { + ok( $status ); +} else { + skip("Skip test failed probably due to \$TMPDIR being on NFS",1); +} + +# MKSTEMPS +# File with suffix. This is created in the current directory so +# may be problematic on NFS + +$template = "suffixXXXXXX"; +my $suffix = ".dat"; + +($fh, my $fname) = mkstemps($template, $suffix); + +print "# MKSTEMPS: File is $template -> $fname fileno=".fileno($fh)."\n"; +# Check if the file exists +ok( (-e $fname) ); + +# This fails if you are running on NFS +# If this test fails simply skip it rather than doing a hard failure +$status = unlink0($fh, $fname); + +if ($status) { + ok($status); +} else { + skip("Skip test failed probably due to cwd being on NFS",1) +} + +# MKDTEMP +# Temp directory + +$template = File::Spec->catdir(File::Spec->tmpdir, 'tmpdirXXXXXX'); + +my $tmpdir = mkdtemp($template); + +print "# MKDTEMP: Name is $tmpdir from template $template\n"; + +ok( (-d $tmpdir ) ); + +# Need to tidy up after myself +rmtree($tmpdir); + +# MKTEMP +# Just a filename, not opened + +$template = File::Spec->catfile(File::Spec->tmpdir, 'mytestXXXXXX'); + +my $tmpfile = mktemp($template); + +print "# MKTEMP: Tempfile is $template -> $tmpfile\n"; + +# Okay if template no longer has XXXXX in + + +ok( ($tmpfile !~ /XXXXX$/) ); diff --git a/lib/File/Temp/posix.t b/lib/File/Temp/posix.t new file mode 100755 index 0000000000..0a5e86061b --- /dev/null +++ b/lib/File/Temp/posix.t @@ -0,0 +1,83 @@ +#!/usr/bin/perl -w +# Test for File::Temp - POSIX functions + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; + require Test; import Test; + plan(tests => 7); +} + +use strict; + +use File::Temp qw/ :POSIX unlink0 /; +use FileHandle; + +ok(1); + +# TMPNAM - scalar + +print "# TMPNAM: in a scalar context: \n"; +my $tmpnam = tmpnam(); + +# simply check that the file does not exist +# Not a 100% water tight test though if another program +# has managed to create one in the meantime. +ok( !(-e $tmpnam )); + +print "# TMPNAM file name: $tmpnam\n"; + +# TMPNAM list context +# Not strict posix behaviour +(my $fh, $tmpnam) = tmpnam(); + +print "# TMPNAM: in list context: $fh $tmpnam\n"; + +# File is opened - make sure it exists +ok( (-e $tmpnam )); + +# Unlink it - a possible NFS issue again if TMPDIR is not a local disk +my $status = unlink0($fh, $tmpnam); +if ($status) { + ok( $status ); +} else { + skip("Skip test failed probably due to \$TMPDIR being on NFS",1); +} + +# TMPFILE + +$fh = tmpfile(); + +if (defined $fh) { + ok( $fh ); + print "# TMPFILE: tmpfile got FH $fh\n"; + + $fh->autoflush(1) if $] >= 5.006; + + # print something to it + my $original = "Hello a test\n"; + print "# TMPFILE: Wrote line: $original"; + print $fh $original + or die "Error printing to tempfile\n"; + + # rewind it + ok( seek($fh,0,0) ); + + # Read from it + my $line = <$fh>; + + print "# TMPFILE: Read line: $line"; + ok( $original, $line); + + close($fh); + +} else { + # Skip all the remaining tests + foreach (1..3) { + skip("Skip test failed probably due to \$TMPDIR being on NFS",1); + } +} + + + + diff --git a/lib/File/Temp/security.t b/lib/File/Temp/security.t new file mode 100755 index 0000000000..f9be237dd3 --- /dev/null +++ b/lib/File/Temp/security.t @@ -0,0 +1,140 @@ +#!/usr/bin/perl -w +# Test for File::Temp - Security levels + +# Some of the security checking will not work on all platforms +# Test a simple open in the cwd and tmpdir foreach of the +# security levels + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; + require Test; import Test; + plan(tests => 13); +} + +use strict; +use File::Spec; + +# Set up END block - this needs to happen before we load +# File::Temp since this END block must be evaluated after the +# END block configured by File::Temp +my @files; # list of files to remove +END { foreach (@files) { ok( !(-e $_) )} } + +use File::Temp qw/ tempfile unlink0 /; +ok(1); + +# The high security tests must currently be skipped on some platforms +my $skipplat = ( ( + # No sticky bits. + $^O eq 'MSWin32' || $^O eq 'NetWare' || $^O eq 'os2' || $^O eq 'dos' + ) ? 1 : 0 ); + +# Can not run high security tests in perls before 5.6.0 +my $skipperl = ($] < 5.006 ? 1 : 0 ); + +# Determine whether we need to skip things and why +my $skip = 0; +if ($skipplat) { + $skip = "Skip Not supported on this platform"; +} elsif ($skipperl) { + $skip = "Skip Perl version must be v5.6.0 for these tests"; + +} + +print "# We will be skipping some tests : $skip\n" if $skip; + +# start off with basic checking + +File::Temp->safe_level( File::Temp::STANDARD ); + +print "# Testing with STANDARD security...\n"; + +&test_security(0); + +# Try medium + +File::Temp->safe_level( File::Temp::MEDIUM ) + unless $skip; + +print "# Testing with MEDIUM security...\n"; + +# Now we need to start skipping tests +&test_security($skip); + +# Try HIGH + +File::Temp->safe_level( File::Temp::HIGH ) + unless $skip; + +print "# Testing with HIGH security...\n"; + +&test_security($skip); + +exit; + +# Subroutine to open two temporary files. +# one is opened in the current dir and the other in the temp dir + +sub test_security { + + # Read in the skip flag + my $skip = shift; + + # If we are skipping we need to simply fake the correct number + # of tests -- we dont use skip since the tempfile() commands will + # fail with MEDIUM/HIGH security before the skip() command would be run + if ($skip) { + + skip($skip,1); + skip($skip,1); + + # plus we need an end block so the tests come out in the right order + eval q{ END { skip($skip,1); skip($skip,1) } 1; } || die; + + return; + } + + # Create the tempfile + my $template = "tmpXXXXX"; + my ($fh1, $fname1) = eval { tempfile ( $template, + DIR => File::Spec->tmpdir, + UNLINK => 1, + ); + }; + + if (defined $fname1) { + print "# fname1 = $fname1\n"; + ok( (-e $fname1) ); + push(@files, $fname1); # store for end block + } elsif (File::Temp->safe_level() != File::Temp::STANDARD) { + my $skip2 = "Skip system possibly insecure, see INSTALL, section 'make test'"; + skip($skip2, 1); + # plus we need an end block so the tests come out in the right order + eval q{ END { skip($skip2,1); } 1; } || die; + } else { + ok(0); + } + + # Explicitly + if ( $< < File::Temp->top_system_uid() ){ + skip("Skip Test inappropriate for root", 1); + eval q{ END { skip($skip,1); } 1; } || die; + return; + } + my ($fh2, $fname2) = eval { tempfile ($template, UNLINK => 1 ); }; + if (defined $fname2) { + print "# fname2 = $fname2\n"; + ok( (-e $fname2) ); + push(@files, $fname2); # store for end block + close($fh2); + } elsif (File::Temp->safe_level() != File::Temp::STANDARD) { + my $skip2 = "Skip system possibly insecure, see INSTALL, section 'make test'"; + skip($skip2, 1); + # plus we need an end block so the tests come out in the right order + eval q{ END { skip($skip2,1); } 1; } || die; + } else { + ok(0); + } + +} diff --git a/lib/File/Temp/tempfile.t b/lib/File/Temp/tempfile.t new file mode 100755 index 0000000000..ed59765a75 --- /dev/null +++ b/lib/File/Temp/tempfile.t @@ -0,0 +1,145 @@ +#!/usr/local/bin/perl -w +# Test for File::Temp - tempfile function + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; + require Test; import Test; + plan(tests => 20); +} + +use strict; +use File::Spec; + +# Will need to check that all files were unlinked correctly +# Set up an END block here to do it + +# Arrays containing list of dirs/files to test +my (@files, @dirs, @still_there); + +# And a test for files that should still be around +# These are tidied up +END { + foreach (@still_there) { + ok( -f $_ ); + ok( unlink( $_ ) ); + ok( !(-f $_) ); + } +} + +# Loop over an array hoping that the files dont exist +END { foreach (@files) { ok( !(-e $_) )} } + +# And a test for directories +END { foreach (@dirs) { ok( !(-d $_) )} } + +# Need to make sure that the END blocks are setup before +# the ones that File::Temp configures since END blocks are evaluated +# in revers order and we need to check the files *after* File::Temp +# removes them +use File::Temp qw/ tempfile tempdir/; + +# Now we start the tests properly +ok(1); + + +# Tempfile +# Open tempfile in some directory, unlink at end +my ($fh, $tempfile) = tempfile( + UNLINK => 1, + SUFFIX => '.txt', + ); + +ok( (-f $tempfile) ); +# Should still be around after closing +ok( close( $fh ) ); +ok( (-f $tempfile) ); +# Check again at exit +push(@files, $tempfile); + +# TEMPDIR test +# Create temp directory in current dir +my $template = 'tmpdirXXXXXX'; +print "# Template: $template\n"; +my $tempdir = tempdir( $template , + DIR => File::Spec->curdir, + CLEANUP => 1, + ); + +print "# TEMPDIR: $tempdir\n"; + +ok( (-d $tempdir) ); +push(@dirs, $tempdir); + +# Create file in the temp dir +($fh, $tempfile) = tempfile( + DIR => $tempdir, + UNLINK => 1, + SUFFIX => '.dat', + ); + +print "# TEMPFILE: Created $tempfile\n"; + +ok( (-f $tempfile)); +push(@files, $tempfile); + +# Test tempfile +# ..and again +($fh, $tempfile) = tempfile( + DIR => $tempdir, + ); + + +ok( (-f $tempfile )); +push(@files, $tempfile); + +print "# TEMPFILE: Created $tempfile\n"; + +# and another (with template) + +($fh, $tempfile) = tempfile( 'helloXXXXXXX', + DIR => $tempdir, + UNLINK => 1, + SUFFIX => '.dat', + ); + +print "# TEMPFILE: Created $tempfile\n"; + +ok( (-f $tempfile) ); +push(@files, $tempfile); + + +# Create a temporary file that should stay around after +# it has been closed +($fh, $tempfile) = tempfile( 'permXXXXXXX', UNLINK => 0 ); +print "# TEMPFILE: Created $tempfile\n"; +ok( -f $tempfile ); +ok( close( $fh ) ); +push( @still_there, $tempfile); # check at END + +# Would like to create a temp file and just retrieve the handle +# but the test is problematic since: +# - We dont know the filename so we cant check that it is tidied +# correctly +# - The unlink0 required on unix for tempfile creation will fail +# on NFS +# Try to do what we can. +# Tempfile croaks on error so we need an eval +$fh = eval { tempfile( 'ftmpXXXXX', DIR => File::Spec->tmpdir ) }; + +if ($fh) { + + # print something to it to make sure something is there + ok( print $fh "Test\n" ); + + # Close it - can not check it is gone since we dont know the name + ok( close($fh) ); + +} else { + skip "Skip Failed probably due to NFS", 1; + skip "Skip Failed probably due to NFS", 1; +} + +# Now END block will execute to test the removal of directories +print "# End of tests. Execute END blocks\n"; + diff --git a/lib/File/stat.t b/lib/File/stat.t new file mode 100644 index 0000000000..ac6d95f745 --- /dev/null +++ b/lib/File/stat.t @@ -0,0 +1,70 @@ +#!./perl + +BEGIN { + chdir 't' if -d 't'; + @INC = '../lib'; +} + +BEGIN { + our $hasst; + eval { my @n = stat "TEST" }; + $hasst = 1 unless $@ && $@ =~ /unimplemented/; + unless ($hasst) { print "1..0 # Skip: no stat\n"; exit 0 } + use Config; + $hasst = 0 unless $Config{'i_sysstat'} eq 'define'; + unless ($hasst) { print "1..0 # Skip: no sys/stat.h\n"; exit 0 } +} + +BEGIN { + our @stat = stat "TEST"; # This is the function stat. + unless (@stat) { print "1..0 # Skip: no file TEST\n"; exit 0 } +} + +print "1..14\n"; + +use File::stat; + +print "ok 1\n"; + +my $stat = stat "TEST"; # This is the OO stat. + +print "not " unless $stat->dev == $stat[ 0]; +print "ok 2\n"; + +print "not " unless $stat->ino == $stat[ 1]; +print "ok 3\n"; + +print "not " unless $stat->mode == $stat[ 2]; +print "ok 4\n"; + +print "not " unless $stat->nlink == $stat[ 3]; +print "ok 5\n"; + +print "not " unless $stat->uid == $stat[ 4]; +print "ok 6\n"; + +print "not " unless $stat->gid == $stat[ 5]; +print "ok 7\n"; + +print "not " unless $stat->rdev == $stat[ 6]; +print "ok 8\n"; + +print "not " unless $stat->size == $stat[ 7]; +print "ok 9\n"; + +print "not " unless $stat->atime == $stat[ 8]; +print "ok 10\n"; + +print "not " unless $stat->mtime == $stat[ 9]; +print "ok 11\n"; + +print "not " unless $stat->ctime == $stat[10]; +print "ok 12\n"; + +print "not " unless $stat->blksize == $stat[11]; +print "ok 13\n"; + +print "not " unless $stat->blocks == $stat[12]; +print "ok 14\n"; + +# Testing pretty much anything else is unportable. |