1*0Sstevel@tonic-gatepackage File::Find; 2*0Sstevel@tonic-gateuse 5.006; 3*0Sstevel@tonic-gateuse strict; 4*0Sstevel@tonic-gateuse warnings; 5*0Sstevel@tonic-gateuse warnings::register; 6*0Sstevel@tonic-gateour $VERSION = '1.07'; 7*0Sstevel@tonic-gaterequire Exporter; 8*0Sstevel@tonic-gaterequire Cwd; 9*0Sstevel@tonic-gate 10*0Sstevel@tonic-gate# 11*0Sstevel@tonic-gate# Modified to ensure sub-directory traversal order is not inverded by stack 12*0Sstevel@tonic-gate# push and pops. That is remains in the same order as in the directory file, 13*0Sstevel@tonic-gate# or user pre-processing (EG:sorted). 14*0Sstevel@tonic-gate# 15*0Sstevel@tonic-gate 16*0Sstevel@tonic-gate=head1 NAME 17*0Sstevel@tonic-gate 18*0Sstevel@tonic-gateFile::Find - Traverse a directory tree. 19*0Sstevel@tonic-gate 20*0Sstevel@tonic-gate=head1 SYNOPSIS 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gate use File::Find; 23*0Sstevel@tonic-gate find(\&wanted, @directories_to_search); 24*0Sstevel@tonic-gate sub wanted { ... } 25*0Sstevel@tonic-gate 26*0Sstevel@tonic-gate use File::Find; 27*0Sstevel@tonic-gate finddepth(\&wanted, @directories_to_search); 28*0Sstevel@tonic-gate sub wanted { ... } 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate use File::Find; 31*0Sstevel@tonic-gate find({ wanted => \&process, follow => 1 }, '.'); 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate=head1 DESCRIPTION 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gateThese are functions for searching through directory trees doing work 36*0Sstevel@tonic-gateon each file found similar to the Unix I<find> command. File::Find 37*0Sstevel@tonic-gateexports two functions, C<find> and C<finddepth>. They work similarly 38*0Sstevel@tonic-gatebut have subtle differences. 39*0Sstevel@tonic-gate 40*0Sstevel@tonic-gate=over 4 41*0Sstevel@tonic-gate 42*0Sstevel@tonic-gate=item B<find> 43*0Sstevel@tonic-gate 44*0Sstevel@tonic-gate find(\&wanted, @directories); 45*0Sstevel@tonic-gate find(\%options, @directories); 46*0Sstevel@tonic-gate 47*0Sstevel@tonic-gateC<find()> does a depth-first search over the given C<@directories> in 48*0Sstevel@tonic-gatethe order they are given. For each file or directory found, it calls 49*0Sstevel@tonic-gatethe C<&wanted> subroutine. (See below for details on how to use the 50*0Sstevel@tonic-gateC<&wanted> function). Additionally, for each directory found, it will 51*0Sstevel@tonic-gateC<chdir()> into that directory and continue the search, invoking the 52*0Sstevel@tonic-gateC<&wanted> function on each file or subdirectory in the directory. 53*0Sstevel@tonic-gate 54*0Sstevel@tonic-gate=item B<finddepth> 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate finddepth(\&wanted, @directories); 57*0Sstevel@tonic-gate finddepth(\%options, @directories); 58*0Sstevel@tonic-gate 59*0Sstevel@tonic-gateC<finddepth()> works just like C<find()> except that is invokes the 60*0Sstevel@tonic-gateC<&wanted> function for a directory I<after> invoking it for the 61*0Sstevel@tonic-gatedirectory's contents. It does a postorder traversal instead of a 62*0Sstevel@tonic-gatepreorder traversal, working from the bottom of the directory tree up 63*0Sstevel@tonic-gatewhere C<find()> works from the top of the tree down. 64*0Sstevel@tonic-gate 65*0Sstevel@tonic-gate=back 66*0Sstevel@tonic-gate 67*0Sstevel@tonic-gate=head2 %options 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gateThe first argument to C<find()> is either a code reference to your 70*0Sstevel@tonic-gateC<&wanted> function, or a hash reference describing the operations 71*0Sstevel@tonic-gateto be performed for each file. The 72*0Sstevel@tonic-gatecode reference is described in L<The wanted function> below. 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gateHere are the possible keys for the hash: 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate=over 3 77*0Sstevel@tonic-gate 78*0Sstevel@tonic-gate=item C<wanted> 79*0Sstevel@tonic-gate 80*0Sstevel@tonic-gateThe value should be a code reference. This code reference is 81*0Sstevel@tonic-gatedescribed in L<The wanted function> below. 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate=item C<bydepth> 84*0Sstevel@tonic-gate 85*0Sstevel@tonic-gateReports the name of a directory only AFTER all its entries 86*0Sstevel@tonic-gatehave been reported. Entry point C<finddepth()> is a shortcut for 87*0Sstevel@tonic-gatespecifying C<<{ bydepth => 1 }>> in the first argument of C<find()>. 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gate=item C<preprocess> 90*0Sstevel@tonic-gate 91*0Sstevel@tonic-gateThe value should be a code reference. This code reference is used to 92*0Sstevel@tonic-gatepreprocess the current directory. The name of the currently processed 93*0Sstevel@tonic-gatedirectory is in C<$File::Find::dir>. Your preprocessing function is 94*0Sstevel@tonic-gatecalled after C<readdir()>, but before the loop that calls the C<wanted()> 95*0Sstevel@tonic-gatefunction. It is called with a list of strings (actually file/directory 96*0Sstevel@tonic-gatenames) and is expected to return a list of strings. The code can be 97*0Sstevel@tonic-gateused to sort the file/directory names alphabetically, numerically, 98*0Sstevel@tonic-gateor to filter out directory entries based on their name alone. When 99*0Sstevel@tonic-gateI<follow> or I<follow_fast> are in effect, C<preprocess> is a no-op. 100*0Sstevel@tonic-gate 101*0Sstevel@tonic-gate=item C<postprocess> 102*0Sstevel@tonic-gate 103*0Sstevel@tonic-gateThe value should be a code reference. It is invoked just before leaving 104*0Sstevel@tonic-gatethe currently processed directory. It is called in void context with no 105*0Sstevel@tonic-gatearguments. The name of the current directory is in C<$File::Find::dir>. This 106*0Sstevel@tonic-gatehook is handy for summarizing a directory, such as calculating its disk 107*0Sstevel@tonic-gateusage. When I<follow> or I<follow_fast> are in effect, C<postprocess> is a 108*0Sstevel@tonic-gateno-op. 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate=item C<follow> 111*0Sstevel@tonic-gate 112*0Sstevel@tonic-gateCauses symbolic links to be followed. Since directory trees with symbolic 113*0Sstevel@tonic-gatelinks (followed) may contain files more than once and may even have 114*0Sstevel@tonic-gatecycles, a hash has to be built up with an entry for each file. 115*0Sstevel@tonic-gateThis might be expensive both in space and time for a large 116*0Sstevel@tonic-gatedirectory tree. See I<follow_fast> and I<follow_skip> below. 117*0Sstevel@tonic-gateIf either I<follow> or I<follow_fast> is in effect: 118*0Sstevel@tonic-gate 119*0Sstevel@tonic-gate=over 6 120*0Sstevel@tonic-gate 121*0Sstevel@tonic-gate=item * 122*0Sstevel@tonic-gate 123*0Sstevel@tonic-gateIt is guaranteed that an I<lstat> has been called before the user's 124*0Sstevel@tonic-gateC<wanted()> function is called. This enables fast file checks involving S< _>. 125*0Sstevel@tonic-gate 126*0Sstevel@tonic-gate=item * 127*0Sstevel@tonic-gate 128*0Sstevel@tonic-gateThere is a variable C<$File::Find::fullname> which holds the absolute 129*0Sstevel@tonic-gatepathname of the file with all symbolic links resolved 130*0Sstevel@tonic-gate 131*0Sstevel@tonic-gate=back 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate=item C<follow_fast> 134*0Sstevel@tonic-gate 135*0Sstevel@tonic-gateThis is similar to I<follow> except that it may report some files more 136*0Sstevel@tonic-gatethan once. It does detect cycles, however. Since only symbolic links 137*0Sstevel@tonic-gatehave to be hashed, this is much cheaper both in space and time. If 138*0Sstevel@tonic-gateprocessing a file more than once (by the user's C<wanted()> function) 139*0Sstevel@tonic-gateis worse than just taking time, the option I<follow> should be used. 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate=item C<follow_skip> 142*0Sstevel@tonic-gate 143*0Sstevel@tonic-gateC<follow_skip==1>, which is the default, causes all files which are 144*0Sstevel@tonic-gateneither directories nor symbolic links to be ignored if they are about 145*0Sstevel@tonic-gateto be processed a second time. If a directory or a symbolic link 146*0Sstevel@tonic-gateare about to be processed a second time, File::Find dies. 147*0Sstevel@tonic-gate 148*0Sstevel@tonic-gateC<follow_skip==0> causes File::Find to die if any file is about to be 149*0Sstevel@tonic-gateprocessed a second time. 150*0Sstevel@tonic-gate 151*0Sstevel@tonic-gateC<follow_skip==2> causes File::Find to ignore any duplicate files and 152*0Sstevel@tonic-gatedirectories but to proceed normally otherwise. 153*0Sstevel@tonic-gate 154*0Sstevel@tonic-gate=item C<dangling_symlinks> 155*0Sstevel@tonic-gate 156*0Sstevel@tonic-gateIf true and a code reference, will be called with the symbolic link 157*0Sstevel@tonic-gatename and the directory it lives in as arguments. Otherwise, if true 158*0Sstevel@tonic-gateand warnings are on, warning "symbolic_link_name is a dangling 159*0Sstevel@tonic-gatesymbolic link\n" will be issued. If false, the dangling symbolic link 160*0Sstevel@tonic-gatewill be silently ignored. 161*0Sstevel@tonic-gate 162*0Sstevel@tonic-gate=item C<no_chdir> 163*0Sstevel@tonic-gate 164*0Sstevel@tonic-gateDoes not C<chdir()> to each directory as it recurses. The C<wanted()> 165*0Sstevel@tonic-gatefunction will need to be aware of this, of course. In this case, 166*0Sstevel@tonic-gateC<$_> will be the same as C<$File::Find::name>. 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate=item C<untaint> 169*0Sstevel@tonic-gate 170*0Sstevel@tonic-gateIf find is used in taint-mode (-T command line switch or if EUID != UID 171*0Sstevel@tonic-gateor if EGID != GID) then internally directory names have to be untainted 172*0Sstevel@tonic-gatebefore they can be chdir'ed to. Therefore they are checked against a regular 173*0Sstevel@tonic-gateexpression I<untaint_pattern>. Note that all names passed to the user's 174*0Sstevel@tonic-gateI<wanted()> function are still tainted. If this option is used while 175*0Sstevel@tonic-gatenot in taint-mode, C<untaint> is a no-op. 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate=item C<untaint_pattern> 178*0Sstevel@tonic-gate 179*0Sstevel@tonic-gateSee above. This should be set using the C<qr> quoting operator. 180*0Sstevel@tonic-gateThe default is set to C<qr|^([-+@\w./]+)$|>. 181*0Sstevel@tonic-gateNote that the parentheses are vital. 182*0Sstevel@tonic-gate 183*0Sstevel@tonic-gate=item C<untaint_skip> 184*0Sstevel@tonic-gate 185*0Sstevel@tonic-gateIf set, a directory which fails the I<untaint_pattern> is skipped, 186*0Sstevel@tonic-gateincluding all its sub-directories. The default is to 'die' in such a case. 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate=back 189*0Sstevel@tonic-gate 190*0Sstevel@tonic-gate=head2 The wanted function 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gateThe C<wanted()> function does whatever verifications you want on 193*0Sstevel@tonic-gateeach file and directory. Note that despite its name, the C<wanted()> 194*0Sstevel@tonic-gatefunction is a generic callback function, and does B<not> tell 195*0Sstevel@tonic-gateFile::Find if a file is "wanted" or not. In fact, its return value 196*0Sstevel@tonic-gateis ignored. 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gateThe wanted function takes no arguments but rather does its work 199*0Sstevel@tonic-gatethrough a collection of variables. 200*0Sstevel@tonic-gate 201*0Sstevel@tonic-gate=over 4 202*0Sstevel@tonic-gate 203*0Sstevel@tonic-gate=item C<$File::Find::dir> is the current directory name, 204*0Sstevel@tonic-gate 205*0Sstevel@tonic-gate=item C<$_> is the current filename within that directory 206*0Sstevel@tonic-gate 207*0Sstevel@tonic-gate=item C<$File::Find::name> is the complete pathname to the file. 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate=back 210*0Sstevel@tonic-gate 211*0Sstevel@tonic-gateDon't modify these variables. 212*0Sstevel@tonic-gate 213*0Sstevel@tonic-gateFor example, when examining the file F</some/path/foo.ext> you will have: 214*0Sstevel@tonic-gate 215*0Sstevel@tonic-gate $File::Find::dir = /some/path/ 216*0Sstevel@tonic-gate $_ = foo.ext 217*0Sstevel@tonic-gate $File::Find::name = /some/path/foo.ext 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gateYou are chdir()'d toC<$File::Find::dir> when the function is called, 220*0Sstevel@tonic-gateunless C<no_chdir> was specified. Note that when changing to 221*0Sstevel@tonic-gatedirectories is in effect the root directory (F</>) is a somewhat 222*0Sstevel@tonic-gatespecial case inasmuch as the concatenation of C<$File::Find::dir>, 223*0Sstevel@tonic-gateC<'/'> and C<$_> is not literally equal to C<$File::Find::name>. The 224*0Sstevel@tonic-gatetable below summarizes all variants: 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate $File::Find::name $File::Find::dir $_ 227*0Sstevel@tonic-gate default / / . 228*0Sstevel@tonic-gate no_chdir=>0 /etc / etc 229*0Sstevel@tonic-gate /etc/x /etc x 230*0Sstevel@tonic-gate 231*0Sstevel@tonic-gate no_chdir=>1 / / / 232*0Sstevel@tonic-gate /etc / /etc 233*0Sstevel@tonic-gate /etc/x /etc /etc/x 234*0Sstevel@tonic-gate 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gateWhen <follow> or <follow_fast> are in effect, there is 237*0Sstevel@tonic-gatealso a C<$File::Find::fullname>. The function may set 238*0Sstevel@tonic-gateC<$File::Find::prune> to prune the tree unless C<bydepth> was 239*0Sstevel@tonic-gatespecified. Unless C<follow> or C<follow_fast> is specified, for 240*0Sstevel@tonic-gatecompatibility reasons (find.pl, find2perl) there are in addition the 241*0Sstevel@tonic-gatefollowing globals available: C<$File::Find::topdir>, 242*0Sstevel@tonic-gateC<$File::Find::topdev>, C<$File::Find::topino>, 243*0Sstevel@tonic-gateC<$File::Find::topmode> and C<$File::Find::topnlink>. 244*0Sstevel@tonic-gate 245*0Sstevel@tonic-gateThis library is useful for the C<find2perl> tool, which when fed, 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate find2perl / -name .nfs\* -mtime +7 \ 248*0Sstevel@tonic-gate -exec rm -f {} \; -o -fstype nfs -prune 249*0Sstevel@tonic-gate 250*0Sstevel@tonic-gateproduces something like: 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate sub wanted { 253*0Sstevel@tonic-gate /^\.nfs.*\z/s && 254*0Sstevel@tonic-gate (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) && 255*0Sstevel@tonic-gate int(-M _) > 7 && 256*0Sstevel@tonic-gate unlink($_) 257*0Sstevel@tonic-gate || 258*0Sstevel@tonic-gate ($nlink || (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_))) && 259*0Sstevel@tonic-gate $dev < 0 && 260*0Sstevel@tonic-gate ($File::Find::prune = 1); 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gateNotice the C<_> in the above C<int(-M _)>: the C<_> is a magical 264*0Sstevel@tonic-gatefilehandle that caches the information from the preceding 265*0Sstevel@tonic-gateC<stat()>, C<lstat()>, or filetest. 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gateHere's another interesting wanted function. It will find all symbolic 268*0Sstevel@tonic-gatelinks that don't resolve: 269*0Sstevel@tonic-gate 270*0Sstevel@tonic-gate sub wanted { 271*0Sstevel@tonic-gate -l && !-e && print "bogus link: $File::Find::name\n"; 272*0Sstevel@tonic-gate } 273*0Sstevel@tonic-gate 274*0Sstevel@tonic-gateSee also the script C<pfind> on CPAN for a nice application of this 275*0Sstevel@tonic-gatemodule. 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate=head1 WARNINGS 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gateIf you run your program with the C<-w> switch, or if you use the 280*0Sstevel@tonic-gateC<warnings> pragma, File::Find will report warnings for several weird 281*0Sstevel@tonic-gatesituations. You can disable these warnings by putting the statement 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate no warnings 'File::Find'; 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gatein the appropriate scope. See L<perllexwarn> for more info about lexical 286*0Sstevel@tonic-gatewarnings. 287*0Sstevel@tonic-gate 288*0Sstevel@tonic-gate=head1 CAVEAT 289*0Sstevel@tonic-gate 290*0Sstevel@tonic-gate=over 2 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate=item $dont_use_nlink 293*0Sstevel@tonic-gate 294*0Sstevel@tonic-gateYou can set the variable C<$File::Find::dont_use_nlink> to 1, if you want to 295*0Sstevel@tonic-gateforce File::Find to always stat directories. This was used for file systems 296*0Sstevel@tonic-gatethat do not have an C<nlink> count matching the number of sub-directories. 297*0Sstevel@tonic-gateExamples are ISO-9660 (CD-ROM), AFS, HPFS (OS/2 file system), FAT (DOS file 298*0Sstevel@tonic-gatesystem) and a couple of others. 299*0Sstevel@tonic-gate 300*0Sstevel@tonic-gateYou shouldn't need to set this variable, since File::Find should now detect 301*0Sstevel@tonic-gatesuch file systems on-the-fly and switch itself to using stat. This works even 302*0Sstevel@tonic-gatefor parts of your file system, like a mounted CD-ROM. 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gateIf you do set C<$File::Find::dont_use_nlink> to 1, you will notice slow-downs. 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate=item symlinks 307*0Sstevel@tonic-gate 308*0Sstevel@tonic-gateBe aware that the option to follow symbolic links can be dangerous. 309*0Sstevel@tonic-gateDepending on the structure of the directory tree (including symbolic 310*0Sstevel@tonic-gatelinks to directories) you might traverse a given (physical) directory 311*0Sstevel@tonic-gatemore than once (only if C<follow_fast> is in effect). 312*0Sstevel@tonic-gateFurthermore, deleting or changing files in a symbolically linked directory 313*0Sstevel@tonic-gatemight cause very unpleasant surprises, since you delete or change files 314*0Sstevel@tonic-gatein an unknown directory. 315*0Sstevel@tonic-gate 316*0Sstevel@tonic-gate=back 317*0Sstevel@tonic-gate 318*0Sstevel@tonic-gate=head1 NOTES 319*0Sstevel@tonic-gate 320*0Sstevel@tonic-gate=over 4 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate=item * 323*0Sstevel@tonic-gate 324*0Sstevel@tonic-gateMac OS (Classic) users should note a few differences: 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gate=over 4 327*0Sstevel@tonic-gate 328*0Sstevel@tonic-gate=item * 329*0Sstevel@tonic-gate 330*0Sstevel@tonic-gateThe path separator is ':', not '/', and the current directory is denoted 331*0Sstevel@tonic-gateas ':', not '.'. You should be careful about specifying relative pathnames. 332*0Sstevel@tonic-gateWhile a full path always begins with a volume name, a relative pathname 333*0Sstevel@tonic-gateshould always begin with a ':'. If specifying a volume name only, a 334*0Sstevel@tonic-gatetrailing ':' is required. 335*0Sstevel@tonic-gate 336*0Sstevel@tonic-gate=item * 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gateC<$File::Find::dir> is guaranteed to end with a ':'. If C<$_> 339*0Sstevel@tonic-gatecontains the name of a directory, that name may or may not end with a 340*0Sstevel@tonic-gate':'. Likewise, C<$File::Find::name>, which contains the complete 341*0Sstevel@tonic-gatepathname to that directory, and C<$File::Find::fullname>, which holds 342*0Sstevel@tonic-gatethe absolute pathname of that directory with all symbolic links resolved, 343*0Sstevel@tonic-gatemay or may not end with a ':'. 344*0Sstevel@tonic-gate 345*0Sstevel@tonic-gate=item * 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gateThe default C<untaint_pattern> (see above) on Mac OS is set to 348*0Sstevel@tonic-gateC<qr|^(.+)$|>. Note that the parentheses are vital. 349*0Sstevel@tonic-gate 350*0Sstevel@tonic-gate=item * 351*0Sstevel@tonic-gate 352*0Sstevel@tonic-gateThe invisible system file "Icon\015" is ignored. While this file may 353*0Sstevel@tonic-gateappear in every directory, there are some more invisible system files 354*0Sstevel@tonic-gateon every volume, which are all located at the volume root level (i.e. 355*0Sstevel@tonic-gate"MacintoshHD:"). These system files are B<not> excluded automatically. 356*0Sstevel@tonic-gateYour filter may use the following code to recognize invisible files or 357*0Sstevel@tonic-gatedirectories (requires Mac::Files): 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gate use Mac::Files; 360*0Sstevel@tonic-gate 361*0Sstevel@tonic-gate # invisible() -- returns 1 if file/directory is invisible, 362*0Sstevel@tonic-gate # 0 if it's visible or undef if an error occurred 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate sub invisible($) { 365*0Sstevel@tonic-gate my $file = shift; 366*0Sstevel@tonic-gate my ($fileCat, $fileInfo); 367*0Sstevel@tonic-gate my $invisible_flag = 1 << 14; 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate if ( $fileCat = FSpGetCatInfo($file) ) { 370*0Sstevel@tonic-gate if ($fileInfo = $fileCat->ioFlFndrInfo() ) { 371*0Sstevel@tonic-gate return (($fileInfo->fdFlags & $invisible_flag) && 1); 372*0Sstevel@tonic-gate } 373*0Sstevel@tonic-gate } 374*0Sstevel@tonic-gate return undef; 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gateGenerally, invisible files are system files, unless an odd application 378*0Sstevel@tonic-gatedecides to use invisible files for its own purposes. To distinguish 379*0Sstevel@tonic-gatesuch files from system files, you have to look at the B<type> and B<creator> 380*0Sstevel@tonic-gatefile attributes. The MacPerl built-in functions C<GetFileInfo(FILE)> and 381*0Sstevel@tonic-gateC<SetFileInfo(CREATOR, TYPE, FILES)> offer access to these attributes 382*0Sstevel@tonic-gate(see MacPerl.pm for details). 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gateFiles that appear on the desktop actually reside in an (hidden) directory 385*0Sstevel@tonic-gatenamed "Desktop Folder" on the particular disk volume. Note that, although 386*0Sstevel@tonic-gateall desktop files appear to be on the same "virtual" desktop, each disk 387*0Sstevel@tonic-gatevolume actually maintains its own "Desktop Folder" directory. 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate=back 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate=back 392*0Sstevel@tonic-gate 393*0Sstevel@tonic-gate=head1 BUGS AND CAVEATS 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gateDespite the name of the C<finddepth()> function, both C<find()> and 396*0Sstevel@tonic-gateC<finddepth()> perform a depth-first search of the directory 397*0Sstevel@tonic-gatehierarchy. 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate=head1 HISTORY 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gateFile::Find used to produce incorrect results if called recursively. 402*0Sstevel@tonic-gateDuring the development of perl 5.8 this bug was fixed. 403*0Sstevel@tonic-gateThe first fixed version of File::Find was 1.01. 404*0Sstevel@tonic-gate 405*0Sstevel@tonic-gate=cut 406*0Sstevel@tonic-gate 407*0Sstevel@tonic-gateour @ISA = qw(Exporter); 408*0Sstevel@tonic-gateour @EXPORT = qw(find finddepth); 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gateuse strict; 412*0Sstevel@tonic-gatemy $Is_VMS; 413*0Sstevel@tonic-gatemy $Is_MacOS; 414*0Sstevel@tonic-gate 415*0Sstevel@tonic-gaterequire File::Basename; 416*0Sstevel@tonic-gaterequire File::Spec; 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate# Should ideally be my() not our() but local() currently 419*0Sstevel@tonic-gate# refuses to operate on lexicals 420*0Sstevel@tonic-gate 421*0Sstevel@tonic-gateour %SLnkSeen; 422*0Sstevel@tonic-gateour ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow, 423*0Sstevel@tonic-gate $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat, 424*0Sstevel@tonic-gate $pre_process, $post_process, $dangling_symlinks); 425*0Sstevel@tonic-gate 426*0Sstevel@tonic-gatesub contract_name { 427*0Sstevel@tonic-gate my ($cdir,$fn) = @_; 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate return substr($cdir,0,rindex($cdir,'/')) if $fn eq $File::Find::current_dir; 430*0Sstevel@tonic-gate 431*0Sstevel@tonic-gate $cdir = substr($cdir,0,rindex($cdir,'/')+1); 432*0Sstevel@tonic-gate 433*0Sstevel@tonic-gate $fn =~ s|^\./||; 434*0Sstevel@tonic-gate 435*0Sstevel@tonic-gate my $abs_name= $cdir . $fn; 436*0Sstevel@tonic-gate 437*0Sstevel@tonic-gate if (substr($fn,0,3) eq '../') { 438*0Sstevel@tonic-gate 1 while $abs_name =~ s!/[^/]*/\.\./!/!; 439*0Sstevel@tonic-gate } 440*0Sstevel@tonic-gate 441*0Sstevel@tonic-gate return $abs_name; 442*0Sstevel@tonic-gate} 443*0Sstevel@tonic-gate 444*0Sstevel@tonic-gate# return the absolute name of a directory or file 445*0Sstevel@tonic-gatesub contract_name_Mac { 446*0Sstevel@tonic-gate my ($cdir,$fn) = @_; 447*0Sstevel@tonic-gate my $abs_name; 448*0Sstevel@tonic-gate 449*0Sstevel@tonic-gate if ($fn =~ /^(:+)(.*)$/) { # valid pathname starting with a ':' 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate my $colon_count = length ($1); 452*0Sstevel@tonic-gate if ($colon_count == 1) { 453*0Sstevel@tonic-gate $abs_name = $cdir . $2; 454*0Sstevel@tonic-gate return $abs_name; 455*0Sstevel@tonic-gate } 456*0Sstevel@tonic-gate else { 457*0Sstevel@tonic-gate # need to move up the tree, but 458*0Sstevel@tonic-gate # only if it's not a volume name 459*0Sstevel@tonic-gate for (my $i=1; $i<$colon_count; $i++) { 460*0Sstevel@tonic-gate unless ($cdir =~ /^[^:]+:$/) { # volume name 461*0Sstevel@tonic-gate $cdir =~ s/[^:]+:$//; 462*0Sstevel@tonic-gate } 463*0Sstevel@tonic-gate else { 464*0Sstevel@tonic-gate return undef; 465*0Sstevel@tonic-gate } 466*0Sstevel@tonic-gate } 467*0Sstevel@tonic-gate $abs_name = $cdir . $2; 468*0Sstevel@tonic-gate return $abs_name; 469*0Sstevel@tonic-gate } 470*0Sstevel@tonic-gate 471*0Sstevel@tonic-gate } 472*0Sstevel@tonic-gate else { 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate # $fn may be a valid path to a directory or file or (dangling) 475*0Sstevel@tonic-gate # symlink, without a leading ':' 476*0Sstevel@tonic-gate if ( (-e $fn) || (-l $fn) ) { 477*0Sstevel@tonic-gate if ($fn =~ /^[^:]+:/) { # a volume name like DataHD:* 478*0Sstevel@tonic-gate return $fn; # $fn is already an absolute path 479*0Sstevel@tonic-gate } 480*0Sstevel@tonic-gate else { 481*0Sstevel@tonic-gate $abs_name = $cdir . $fn; 482*0Sstevel@tonic-gate return $abs_name; 483*0Sstevel@tonic-gate } 484*0Sstevel@tonic-gate } 485*0Sstevel@tonic-gate else { # argh!, $fn is not a valid directory/file 486*0Sstevel@tonic-gate return undef; 487*0Sstevel@tonic-gate } 488*0Sstevel@tonic-gate } 489*0Sstevel@tonic-gate} 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gatesub PathCombine($$) { 492*0Sstevel@tonic-gate my ($Base,$Name) = @_; 493*0Sstevel@tonic-gate my $AbsName; 494*0Sstevel@tonic-gate 495*0Sstevel@tonic-gate if ($Is_MacOS) { 496*0Sstevel@tonic-gate # $Name is the resolved symlink (always a full path on MacOS), 497*0Sstevel@tonic-gate # i.e. there's no need to call contract_name_Mac() 498*0Sstevel@tonic-gate $AbsName = $Name; 499*0Sstevel@tonic-gate 500*0Sstevel@tonic-gate # (simple) check for recursion 501*0Sstevel@tonic-gate if ( ( $Base =~ /^$AbsName/) && (-d $AbsName) ) { # recursion 502*0Sstevel@tonic-gate return undef; 503*0Sstevel@tonic-gate } 504*0Sstevel@tonic-gate } 505*0Sstevel@tonic-gate else { 506*0Sstevel@tonic-gate if (substr($Name,0,1) eq '/') { 507*0Sstevel@tonic-gate $AbsName= $Name; 508*0Sstevel@tonic-gate } 509*0Sstevel@tonic-gate else { 510*0Sstevel@tonic-gate $AbsName= contract_name($Base,$Name); 511*0Sstevel@tonic-gate } 512*0Sstevel@tonic-gate 513*0Sstevel@tonic-gate # (simple) check for recursion 514*0Sstevel@tonic-gate my $newlen= length($AbsName); 515*0Sstevel@tonic-gate if ($newlen <= length($Base)) { 516*0Sstevel@tonic-gate if (($newlen == length($Base) || substr($Base,$newlen,1) eq '/') 517*0Sstevel@tonic-gate && $AbsName eq substr($Base,0,$newlen)) 518*0Sstevel@tonic-gate { 519*0Sstevel@tonic-gate return undef; 520*0Sstevel@tonic-gate } 521*0Sstevel@tonic-gate } 522*0Sstevel@tonic-gate } 523*0Sstevel@tonic-gate return $AbsName; 524*0Sstevel@tonic-gate} 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gatesub Follow_SymLink($) { 527*0Sstevel@tonic-gate my ($AbsName) = @_; 528*0Sstevel@tonic-gate 529*0Sstevel@tonic-gate my ($NewName,$DEV, $INO); 530*0Sstevel@tonic-gate ($DEV, $INO)= lstat $AbsName; 531*0Sstevel@tonic-gate 532*0Sstevel@tonic-gate while (-l _) { 533*0Sstevel@tonic-gate if ($SLnkSeen{$DEV, $INO}++) { 534*0Sstevel@tonic-gate if ($follow_skip < 2) { 535*0Sstevel@tonic-gate die "$AbsName is encountered a second time"; 536*0Sstevel@tonic-gate } 537*0Sstevel@tonic-gate else { 538*0Sstevel@tonic-gate return undef; 539*0Sstevel@tonic-gate } 540*0Sstevel@tonic-gate } 541*0Sstevel@tonic-gate $NewName= PathCombine($AbsName, readlink($AbsName)); 542*0Sstevel@tonic-gate unless(defined $NewName) { 543*0Sstevel@tonic-gate if ($follow_skip < 2) { 544*0Sstevel@tonic-gate die "$AbsName is a recursive symbolic link"; 545*0Sstevel@tonic-gate } 546*0Sstevel@tonic-gate else { 547*0Sstevel@tonic-gate return undef; 548*0Sstevel@tonic-gate } 549*0Sstevel@tonic-gate } 550*0Sstevel@tonic-gate else { 551*0Sstevel@tonic-gate $AbsName= $NewName; 552*0Sstevel@tonic-gate } 553*0Sstevel@tonic-gate ($DEV, $INO) = lstat($AbsName); 554*0Sstevel@tonic-gate return undef unless defined $DEV; # dangling symbolic link 555*0Sstevel@tonic-gate } 556*0Sstevel@tonic-gate 557*0Sstevel@tonic-gate if ($full_check && defined $DEV && $SLnkSeen{$DEV, $INO}++) { 558*0Sstevel@tonic-gate if ( ($follow_skip < 1) || ((-d _) && ($follow_skip < 2)) ) { 559*0Sstevel@tonic-gate die "$AbsName encountered a second time"; 560*0Sstevel@tonic-gate } 561*0Sstevel@tonic-gate else { 562*0Sstevel@tonic-gate return undef; 563*0Sstevel@tonic-gate } 564*0Sstevel@tonic-gate } 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate return $AbsName; 567*0Sstevel@tonic-gate} 568*0Sstevel@tonic-gate 569*0Sstevel@tonic-gateour($dir, $name, $fullname, $prune); 570*0Sstevel@tonic-gatesub _find_dir_symlnk($$$); 571*0Sstevel@tonic-gatesub _find_dir($$$); 572*0Sstevel@tonic-gate 573*0Sstevel@tonic-gate# check whether or not a scalar variable is tainted 574*0Sstevel@tonic-gate# (code straight from the Camel, 3rd ed., page 561) 575*0Sstevel@tonic-gatesub is_tainted_pp { 576*0Sstevel@tonic-gate my $arg = shift; 577*0Sstevel@tonic-gate my $nada = substr($arg, 0, 0); # zero-length 578*0Sstevel@tonic-gate local $@; 579*0Sstevel@tonic-gate eval { eval "# $nada" }; 580*0Sstevel@tonic-gate return length($@) != 0; 581*0Sstevel@tonic-gate} 582*0Sstevel@tonic-gate 583*0Sstevel@tonic-gatesub _find_opt { 584*0Sstevel@tonic-gate my $wanted = shift; 585*0Sstevel@tonic-gate die "invalid top directory" unless defined $_[0]; 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate # This function must local()ize everything because callbacks may 588*0Sstevel@tonic-gate # call find() or finddepth() 589*0Sstevel@tonic-gate 590*0Sstevel@tonic-gate local %SLnkSeen; 591*0Sstevel@tonic-gate local ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow, 592*0Sstevel@tonic-gate $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat, 593*0Sstevel@tonic-gate $pre_process, $post_process, $dangling_symlinks); 594*0Sstevel@tonic-gate local($dir, $name, $fullname, $prune); 595*0Sstevel@tonic-gate local *_ = \my $a; 596*0Sstevel@tonic-gate 597*0Sstevel@tonic-gate my $cwd = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::getcwd(); 598*0Sstevel@tonic-gate my $cwd_untainted = $cwd; 599*0Sstevel@tonic-gate my $check_t_cwd = 1; 600*0Sstevel@tonic-gate $wanted_callback = $wanted->{wanted}; 601*0Sstevel@tonic-gate $bydepth = $wanted->{bydepth}; 602*0Sstevel@tonic-gate $pre_process = $wanted->{preprocess}; 603*0Sstevel@tonic-gate $post_process = $wanted->{postprocess}; 604*0Sstevel@tonic-gate $no_chdir = $wanted->{no_chdir}; 605*0Sstevel@tonic-gate $full_check = $wanted->{follow}; 606*0Sstevel@tonic-gate $follow = $full_check || $wanted->{follow_fast}; 607*0Sstevel@tonic-gate $follow_skip = $wanted->{follow_skip}; 608*0Sstevel@tonic-gate $untaint = $wanted->{untaint}; 609*0Sstevel@tonic-gate $untaint_pat = $wanted->{untaint_pattern}; 610*0Sstevel@tonic-gate $untaint_skip = $wanted->{untaint_skip}; 611*0Sstevel@tonic-gate $dangling_symlinks = $wanted->{dangling_symlinks}; 612*0Sstevel@tonic-gate 613*0Sstevel@tonic-gate # for compatibility reasons (find.pl, find2perl) 614*0Sstevel@tonic-gate local our ($topdir, $topdev, $topino, $topmode, $topnlink); 615*0Sstevel@tonic-gate 616*0Sstevel@tonic-gate # a symbolic link to a directory doesn't increase the link count 617*0Sstevel@tonic-gate $avoid_nlink = $follow || $File::Find::dont_use_nlink; 618*0Sstevel@tonic-gate 619*0Sstevel@tonic-gate my ($abs_dir, $Is_Dir); 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate Proc_Top_Item: 622*0Sstevel@tonic-gate foreach my $TOP (@_) { 623*0Sstevel@tonic-gate my $top_item = $TOP; 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate if ($Is_MacOS) { 626*0Sstevel@tonic-gate ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item; 627*0Sstevel@tonic-gate $top_item = ":$top_item" 628*0Sstevel@tonic-gate if ( (-d _) && ( $top_item !~ /:/ ) ); 629*0Sstevel@tonic-gate } 630*0Sstevel@tonic-gate else { 631*0Sstevel@tonic-gate $top_item =~ s|/\z|| unless $top_item eq '/'; 632*0Sstevel@tonic-gate ($topdev,$topino,$topmode,$topnlink) = $follow ? stat $top_item : lstat $top_item; 633*0Sstevel@tonic-gate } 634*0Sstevel@tonic-gate 635*0Sstevel@tonic-gate $Is_Dir= 0; 636*0Sstevel@tonic-gate 637*0Sstevel@tonic-gate if ($follow) { 638*0Sstevel@tonic-gate 639*0Sstevel@tonic-gate if ($Is_MacOS) { 640*0Sstevel@tonic-gate $cwd = "$cwd:" unless ($cwd =~ /:$/); # for safety 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate if ($top_item eq $File::Find::current_dir) { 643*0Sstevel@tonic-gate $abs_dir = $cwd; 644*0Sstevel@tonic-gate } 645*0Sstevel@tonic-gate else { 646*0Sstevel@tonic-gate $abs_dir = contract_name_Mac($cwd, $top_item); 647*0Sstevel@tonic-gate unless (defined $abs_dir) { 648*0Sstevel@tonic-gate warnings::warnif "Can't determine absolute path for $top_item (No such file or directory)\n"; 649*0Sstevel@tonic-gate next Proc_Top_Item; 650*0Sstevel@tonic-gate } 651*0Sstevel@tonic-gate } 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate } 654*0Sstevel@tonic-gate else { 655*0Sstevel@tonic-gate if (substr($top_item,0,1) eq '/') { 656*0Sstevel@tonic-gate $abs_dir = $top_item; 657*0Sstevel@tonic-gate } 658*0Sstevel@tonic-gate elsif ($top_item eq $File::Find::current_dir) { 659*0Sstevel@tonic-gate $abs_dir = $cwd; 660*0Sstevel@tonic-gate } 661*0Sstevel@tonic-gate else { # care about any ../ 662*0Sstevel@tonic-gate $abs_dir = contract_name("$cwd/",$top_item); 663*0Sstevel@tonic-gate } 664*0Sstevel@tonic-gate } 665*0Sstevel@tonic-gate $abs_dir= Follow_SymLink($abs_dir); 666*0Sstevel@tonic-gate unless (defined $abs_dir) { 667*0Sstevel@tonic-gate if ($dangling_symlinks) { 668*0Sstevel@tonic-gate if (ref $dangling_symlinks eq 'CODE') { 669*0Sstevel@tonic-gate $dangling_symlinks->($top_item, $cwd); 670*0Sstevel@tonic-gate } else { 671*0Sstevel@tonic-gate warnings::warnif "$top_item is a dangling symbolic link\n"; 672*0Sstevel@tonic-gate } 673*0Sstevel@tonic-gate } 674*0Sstevel@tonic-gate next Proc_Top_Item; 675*0Sstevel@tonic-gate } 676*0Sstevel@tonic-gate 677*0Sstevel@tonic-gate if (-d _) { 678*0Sstevel@tonic-gate _find_dir_symlnk($wanted, $abs_dir, $top_item); 679*0Sstevel@tonic-gate $Is_Dir= 1; 680*0Sstevel@tonic-gate } 681*0Sstevel@tonic-gate } 682*0Sstevel@tonic-gate else { # no follow 683*0Sstevel@tonic-gate $topdir = $top_item; 684*0Sstevel@tonic-gate unless (defined $topnlink) { 685*0Sstevel@tonic-gate warnings::warnif "Can't stat $top_item: $!\n"; 686*0Sstevel@tonic-gate next Proc_Top_Item; 687*0Sstevel@tonic-gate } 688*0Sstevel@tonic-gate if (-d _) { 689*0Sstevel@tonic-gate $top_item =~ s/\.dir\z//i if $Is_VMS; 690*0Sstevel@tonic-gate _find_dir($wanted, $top_item, $topnlink); 691*0Sstevel@tonic-gate $Is_Dir= 1; 692*0Sstevel@tonic-gate } 693*0Sstevel@tonic-gate else { 694*0Sstevel@tonic-gate $abs_dir= $top_item; 695*0Sstevel@tonic-gate } 696*0Sstevel@tonic-gate } 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gate unless ($Is_Dir) { 699*0Sstevel@tonic-gate unless (($_,$dir) = File::Basename::fileparse($abs_dir)) { 700*0Sstevel@tonic-gate if ($Is_MacOS) { 701*0Sstevel@tonic-gate ($dir,$_) = (':', $top_item); # $File::Find::dir, $_ 702*0Sstevel@tonic-gate } 703*0Sstevel@tonic-gate else { 704*0Sstevel@tonic-gate ($dir,$_) = ('./', $top_item); 705*0Sstevel@tonic-gate } 706*0Sstevel@tonic-gate } 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate $abs_dir = $dir; 709*0Sstevel@tonic-gate if (( $untaint ) && (is_tainted($dir) )) { 710*0Sstevel@tonic-gate ( $abs_dir ) = $dir =~ m|$untaint_pat|; 711*0Sstevel@tonic-gate unless (defined $abs_dir) { 712*0Sstevel@tonic-gate if ($untaint_skip == 0) { 713*0Sstevel@tonic-gate die "directory $dir is still tainted"; 714*0Sstevel@tonic-gate } 715*0Sstevel@tonic-gate else { 716*0Sstevel@tonic-gate next Proc_Top_Item; 717*0Sstevel@tonic-gate } 718*0Sstevel@tonic-gate } 719*0Sstevel@tonic-gate } 720*0Sstevel@tonic-gate 721*0Sstevel@tonic-gate unless ($no_chdir || chdir $abs_dir) { 722*0Sstevel@tonic-gate warnings::warnif "Couldn't chdir $abs_dir: $!\n"; 723*0Sstevel@tonic-gate next Proc_Top_Item; 724*0Sstevel@tonic-gate } 725*0Sstevel@tonic-gate 726*0Sstevel@tonic-gate $name = $abs_dir . $_; # $File::Find::name 727*0Sstevel@tonic-gate $_ = $name if $no_chdir; 728*0Sstevel@tonic-gate 729*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 730*0Sstevel@tonic-gate 731*0Sstevel@tonic-gate } 732*0Sstevel@tonic-gate 733*0Sstevel@tonic-gate unless ( $no_chdir ) { 734*0Sstevel@tonic-gate if ( ($check_t_cwd) && (($untaint) && (is_tainted($cwd) )) ) { 735*0Sstevel@tonic-gate ( $cwd_untainted ) = $cwd =~ m|$untaint_pat|; 736*0Sstevel@tonic-gate unless (defined $cwd_untainted) { 737*0Sstevel@tonic-gate die "insecure cwd in find(depth)"; 738*0Sstevel@tonic-gate } 739*0Sstevel@tonic-gate $check_t_cwd = 0; 740*0Sstevel@tonic-gate } 741*0Sstevel@tonic-gate unless (chdir $cwd_untainted) { 742*0Sstevel@tonic-gate die "Can't cd to $cwd: $!\n"; 743*0Sstevel@tonic-gate } 744*0Sstevel@tonic-gate } 745*0Sstevel@tonic-gate } 746*0Sstevel@tonic-gate} 747*0Sstevel@tonic-gate 748*0Sstevel@tonic-gate# API: 749*0Sstevel@tonic-gate# $wanted 750*0Sstevel@tonic-gate# $p_dir : "parent directory" 751*0Sstevel@tonic-gate# $nlink : what came back from the stat 752*0Sstevel@tonic-gate# preconditions: 753*0Sstevel@tonic-gate# chdir (if not no_chdir) to dir 754*0Sstevel@tonic-gate 755*0Sstevel@tonic-gatesub _find_dir($$$) { 756*0Sstevel@tonic-gate my ($wanted, $p_dir, $nlink) = @_; 757*0Sstevel@tonic-gate my ($CdLvl,$Level) = (0,0); 758*0Sstevel@tonic-gate my @Stack; 759*0Sstevel@tonic-gate my @filenames; 760*0Sstevel@tonic-gate my ($subcount,$sub_nlink); 761*0Sstevel@tonic-gate my $SE= []; 762*0Sstevel@tonic-gate my $dir_name= $p_dir; 763*0Sstevel@tonic-gate my $dir_pref; 764*0Sstevel@tonic-gate my $dir_rel = $File::Find::current_dir; 765*0Sstevel@tonic-gate my $tainted = 0; 766*0Sstevel@tonic-gate my $no_nlink; 767*0Sstevel@tonic-gate 768*0Sstevel@tonic-gate if ($Is_MacOS) { 769*0Sstevel@tonic-gate $dir_pref= ($p_dir =~ /:$/) ? $p_dir : "$p_dir:"; # preface 770*0Sstevel@tonic-gate } 771*0Sstevel@tonic-gate else { 772*0Sstevel@tonic-gate $dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" ); 773*0Sstevel@tonic-gate } 774*0Sstevel@tonic-gate 775*0Sstevel@tonic-gate local ($dir, $name, $prune, *DIR); 776*0Sstevel@tonic-gate 777*0Sstevel@tonic-gate unless ( $no_chdir || ($p_dir eq $File::Find::current_dir)) { 778*0Sstevel@tonic-gate my $udir = $p_dir; 779*0Sstevel@tonic-gate if (( $untaint ) && (is_tainted($p_dir) )) { 780*0Sstevel@tonic-gate ( $udir ) = $p_dir =~ m|$untaint_pat|; 781*0Sstevel@tonic-gate unless (defined $udir) { 782*0Sstevel@tonic-gate if ($untaint_skip == 0) { 783*0Sstevel@tonic-gate die "directory $p_dir is still tainted"; 784*0Sstevel@tonic-gate } 785*0Sstevel@tonic-gate else { 786*0Sstevel@tonic-gate return; 787*0Sstevel@tonic-gate } 788*0Sstevel@tonic-gate } 789*0Sstevel@tonic-gate } 790*0Sstevel@tonic-gate unless (chdir ($Is_VMS && $udir !~ /[\/\[<]+/ ? "./$udir" : $udir)) { 791*0Sstevel@tonic-gate warnings::warnif "Can't cd to $udir: $!\n"; 792*0Sstevel@tonic-gate return; 793*0Sstevel@tonic-gate } 794*0Sstevel@tonic-gate } 795*0Sstevel@tonic-gate 796*0Sstevel@tonic-gate # push the starting directory 797*0Sstevel@tonic-gate push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth; 798*0Sstevel@tonic-gate 799*0Sstevel@tonic-gate if ($Is_MacOS) { 800*0Sstevel@tonic-gate $p_dir = $dir_pref; # ensure trailing ':' 801*0Sstevel@tonic-gate } 802*0Sstevel@tonic-gate 803*0Sstevel@tonic-gate while (defined $SE) { 804*0Sstevel@tonic-gate unless ($bydepth) { 805*0Sstevel@tonic-gate $dir= $p_dir; # $File::Find::dir 806*0Sstevel@tonic-gate $name= $dir_name; # $File::Find::name 807*0Sstevel@tonic-gate $_= ($no_chdir ? $dir_name : $dir_rel ); # $_ 808*0Sstevel@tonic-gate # prune may happen here 809*0Sstevel@tonic-gate $prune= 0; 810*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 811*0Sstevel@tonic-gate next if $prune; 812*0Sstevel@tonic-gate } 813*0Sstevel@tonic-gate 814*0Sstevel@tonic-gate # change to that directory 815*0Sstevel@tonic-gate unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) { 816*0Sstevel@tonic-gate my $udir= $dir_rel; 817*0Sstevel@tonic-gate if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_rel) )) ) { 818*0Sstevel@tonic-gate ( $udir ) = $dir_rel =~ m|$untaint_pat|; 819*0Sstevel@tonic-gate unless (defined $udir) { 820*0Sstevel@tonic-gate if ($untaint_skip == 0) { 821*0Sstevel@tonic-gate if ($Is_MacOS) { 822*0Sstevel@tonic-gate die "directory ($p_dir) $dir_rel is still tainted"; 823*0Sstevel@tonic-gate } 824*0Sstevel@tonic-gate else { 825*0Sstevel@tonic-gate die "directory (" . ($p_dir ne '/' ? $p_dir : '') . "/) $dir_rel is still tainted"; 826*0Sstevel@tonic-gate } 827*0Sstevel@tonic-gate } else { # $untaint_skip == 1 828*0Sstevel@tonic-gate next; 829*0Sstevel@tonic-gate } 830*0Sstevel@tonic-gate } 831*0Sstevel@tonic-gate } 832*0Sstevel@tonic-gate unless (chdir ($Is_VMS && $udir !~ /[\/\[<]+/ ? "./$udir" : $udir)) { 833*0Sstevel@tonic-gate if ($Is_MacOS) { 834*0Sstevel@tonic-gate warnings::warnif "Can't cd to ($p_dir) $udir: $!\n"; 835*0Sstevel@tonic-gate } 836*0Sstevel@tonic-gate else { 837*0Sstevel@tonic-gate warnings::warnif "Can't cd to (" . 838*0Sstevel@tonic-gate ($p_dir ne '/' ? $p_dir : '') . "/) $udir: $!\n"; 839*0Sstevel@tonic-gate } 840*0Sstevel@tonic-gate next; 841*0Sstevel@tonic-gate } 842*0Sstevel@tonic-gate $CdLvl++; 843*0Sstevel@tonic-gate } 844*0Sstevel@tonic-gate 845*0Sstevel@tonic-gate if ($Is_MacOS) { 846*0Sstevel@tonic-gate $dir_name = "$dir_name:" unless ($dir_name =~ /:$/); 847*0Sstevel@tonic-gate } 848*0Sstevel@tonic-gate 849*0Sstevel@tonic-gate $dir= $dir_name; # $File::Find::dir 850*0Sstevel@tonic-gate 851*0Sstevel@tonic-gate # Get the list of files in the current directory. 852*0Sstevel@tonic-gate unless (opendir DIR, ($no_chdir ? $dir_name : $File::Find::current_dir)) { 853*0Sstevel@tonic-gate warnings::warnif "Can't opendir($dir_name): $!\n"; 854*0Sstevel@tonic-gate next; 855*0Sstevel@tonic-gate } 856*0Sstevel@tonic-gate @filenames = readdir DIR; 857*0Sstevel@tonic-gate closedir(DIR); 858*0Sstevel@tonic-gate @filenames = $pre_process->(@filenames) if $pre_process; 859*0Sstevel@tonic-gate push @Stack,[$CdLvl,$dir_name,"",-2] if $post_process; 860*0Sstevel@tonic-gate 861*0Sstevel@tonic-gate # default: use whatever was specifid 862*0Sstevel@tonic-gate # (if $nlink >= 2, and $avoid_nlink == 0, this will switch back) 863*0Sstevel@tonic-gate $no_nlink = $avoid_nlink; 864*0Sstevel@tonic-gate # if dir has wrong nlink count, force switch to slower stat method 865*0Sstevel@tonic-gate $no_nlink = 1 if ($nlink < 2); 866*0Sstevel@tonic-gate 867*0Sstevel@tonic-gate if ($nlink == 2 && !$no_nlink) { 868*0Sstevel@tonic-gate # This dir has no subdirectories. 869*0Sstevel@tonic-gate for my $FN (@filenames) { 870*0Sstevel@tonic-gate next if $FN =~ $File::Find::skip_pattern; 871*0Sstevel@tonic-gate 872*0Sstevel@tonic-gate $name = $dir_pref . $FN; # $File::Find::name 873*0Sstevel@tonic-gate $_ = ($no_chdir ? $name : $FN); # $_ 874*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 875*0Sstevel@tonic-gate } 876*0Sstevel@tonic-gate 877*0Sstevel@tonic-gate } 878*0Sstevel@tonic-gate else { 879*0Sstevel@tonic-gate # This dir has subdirectories. 880*0Sstevel@tonic-gate $subcount = $nlink - 2; 881*0Sstevel@tonic-gate 882*0Sstevel@tonic-gate # HACK: insert directories at this position. so as to preserve 883*0Sstevel@tonic-gate # the user pre-processed ordering of files. 884*0Sstevel@tonic-gate # EG: directory traversal is in user sorted order, not at random. 885*0Sstevel@tonic-gate my $stack_top = @Stack; 886*0Sstevel@tonic-gate 887*0Sstevel@tonic-gate for my $FN (@filenames) { 888*0Sstevel@tonic-gate next if $FN =~ $File::Find::skip_pattern; 889*0Sstevel@tonic-gate if ($subcount > 0 || $no_nlink) { 890*0Sstevel@tonic-gate # Seen all the subdirs? 891*0Sstevel@tonic-gate # check for directoriness. 892*0Sstevel@tonic-gate # stat is faster for a file in the current directory 893*0Sstevel@tonic-gate $sub_nlink = (lstat ($no_chdir ? $dir_pref . $FN : $FN))[3]; 894*0Sstevel@tonic-gate 895*0Sstevel@tonic-gate if (-d _) { 896*0Sstevel@tonic-gate --$subcount; 897*0Sstevel@tonic-gate $FN =~ s/\.dir\z//i if $Is_VMS; 898*0Sstevel@tonic-gate # HACK: replace push to preserve dir traversal order 899*0Sstevel@tonic-gate #push @Stack,[$CdLvl,$dir_name,$FN,$sub_nlink]; 900*0Sstevel@tonic-gate splice @Stack, $stack_top, 0, 901*0Sstevel@tonic-gate [$CdLvl,$dir_name,$FN,$sub_nlink]; 902*0Sstevel@tonic-gate } 903*0Sstevel@tonic-gate else { 904*0Sstevel@tonic-gate $name = $dir_pref . $FN; # $File::Find::name 905*0Sstevel@tonic-gate $_= ($no_chdir ? $name : $FN); # $_ 906*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 907*0Sstevel@tonic-gate } 908*0Sstevel@tonic-gate } 909*0Sstevel@tonic-gate else { 910*0Sstevel@tonic-gate $name = $dir_pref . $FN; # $File::Find::name 911*0Sstevel@tonic-gate $_= ($no_chdir ? $name : $FN); # $_ 912*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 913*0Sstevel@tonic-gate } 914*0Sstevel@tonic-gate } 915*0Sstevel@tonic-gate } 916*0Sstevel@tonic-gate } 917*0Sstevel@tonic-gate continue { 918*0Sstevel@tonic-gate while ( defined ($SE = pop @Stack) ) { 919*0Sstevel@tonic-gate ($Level, $p_dir, $dir_rel, $nlink) = @$SE; 920*0Sstevel@tonic-gate if ($CdLvl > $Level && !$no_chdir) { 921*0Sstevel@tonic-gate my $tmp; 922*0Sstevel@tonic-gate if ($Is_MacOS) { 923*0Sstevel@tonic-gate $tmp = (':' x ($CdLvl-$Level)) . ':'; 924*0Sstevel@tonic-gate } 925*0Sstevel@tonic-gate else { 926*0Sstevel@tonic-gate $tmp = join('/',('..') x ($CdLvl-$Level)); 927*0Sstevel@tonic-gate } 928*0Sstevel@tonic-gate die "Can't cd to $dir_name" . $tmp 929*0Sstevel@tonic-gate unless chdir ($tmp); 930*0Sstevel@tonic-gate $CdLvl = $Level; 931*0Sstevel@tonic-gate } 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate if ($Is_MacOS) { 934*0Sstevel@tonic-gate # $pdir always has a trailing ':', except for the starting dir, 935*0Sstevel@tonic-gate # where $dir_rel eq ':' 936*0Sstevel@tonic-gate $dir_name = "$p_dir$dir_rel"; 937*0Sstevel@tonic-gate $dir_pref = "$dir_name:"; 938*0Sstevel@tonic-gate } 939*0Sstevel@tonic-gate else { 940*0Sstevel@tonic-gate $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel"); 941*0Sstevel@tonic-gate $dir_pref = "$dir_name/"; 942*0Sstevel@tonic-gate } 943*0Sstevel@tonic-gate 944*0Sstevel@tonic-gate if ( $nlink == -2 ) { 945*0Sstevel@tonic-gate $name = $dir = $p_dir; # $File::Find::name / dir 946*0Sstevel@tonic-gate $_ = $File::Find::current_dir; 947*0Sstevel@tonic-gate $post_process->(); # End-of-directory processing 948*0Sstevel@tonic-gate } 949*0Sstevel@tonic-gate elsif ( $nlink < 0 ) { # must be finddepth, report dirname now 950*0Sstevel@tonic-gate $name = $dir_name; 951*0Sstevel@tonic-gate if ($Is_MacOS) { 952*0Sstevel@tonic-gate if ($dir_rel eq ':') { # must be the top dir, where we started 953*0Sstevel@tonic-gate $name =~ s|:$||; # $File::Find::name 954*0Sstevel@tonic-gate $p_dir = "$p_dir:" unless ($p_dir =~ /:$/); 955*0Sstevel@tonic-gate } 956*0Sstevel@tonic-gate $dir = $p_dir; # $File::Find::dir 957*0Sstevel@tonic-gate $_ = ($no_chdir ? $name : $dir_rel); # $_ 958*0Sstevel@tonic-gate } 959*0Sstevel@tonic-gate else { 960*0Sstevel@tonic-gate if ( substr($name,-2) eq '/.' ) { 961*0Sstevel@tonic-gate substr($name, length($name) == 2 ? -1 : -2) = ''; 962*0Sstevel@tonic-gate } 963*0Sstevel@tonic-gate $dir = $p_dir; 964*0Sstevel@tonic-gate $_ = ($no_chdir ? $dir_name : $dir_rel ); 965*0Sstevel@tonic-gate if ( substr($_,-2) eq '/.' ) { 966*0Sstevel@tonic-gate substr($_, length($_) == 2 ? -1 : -2) = ''; 967*0Sstevel@tonic-gate } 968*0Sstevel@tonic-gate } 969*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 970*0Sstevel@tonic-gate } 971*0Sstevel@tonic-gate else { 972*0Sstevel@tonic-gate push @Stack,[$CdLvl,$p_dir,$dir_rel,-1] if $bydepth; 973*0Sstevel@tonic-gate last; 974*0Sstevel@tonic-gate } 975*0Sstevel@tonic-gate } 976*0Sstevel@tonic-gate } 977*0Sstevel@tonic-gate} 978*0Sstevel@tonic-gate 979*0Sstevel@tonic-gate 980*0Sstevel@tonic-gate# API: 981*0Sstevel@tonic-gate# $wanted 982*0Sstevel@tonic-gate# $dir_loc : absolute location of a dir 983*0Sstevel@tonic-gate# $p_dir : "parent directory" 984*0Sstevel@tonic-gate# preconditions: 985*0Sstevel@tonic-gate# chdir (if not no_chdir) to dir 986*0Sstevel@tonic-gate 987*0Sstevel@tonic-gatesub _find_dir_symlnk($$$) { 988*0Sstevel@tonic-gate my ($wanted, $dir_loc, $p_dir) = @_; # $dir_loc is the absolute directory 989*0Sstevel@tonic-gate my @Stack; 990*0Sstevel@tonic-gate my @filenames; 991*0Sstevel@tonic-gate my $new_loc; 992*0Sstevel@tonic-gate my $updir_loc = $dir_loc; # untainted parent directory 993*0Sstevel@tonic-gate my $SE = []; 994*0Sstevel@tonic-gate my $dir_name = $p_dir; 995*0Sstevel@tonic-gate my $dir_pref; 996*0Sstevel@tonic-gate my $loc_pref; 997*0Sstevel@tonic-gate my $dir_rel = $File::Find::current_dir; 998*0Sstevel@tonic-gate my $byd_flag; # flag for pending stack entry if $bydepth 999*0Sstevel@tonic-gate my $tainted = 0; 1000*0Sstevel@tonic-gate my $ok = 1; 1001*0Sstevel@tonic-gate 1002*0Sstevel@tonic-gate if ($Is_MacOS) { 1003*0Sstevel@tonic-gate $dir_pref = ($p_dir =~ /:$/) ? "$p_dir" : "$p_dir:"; 1004*0Sstevel@tonic-gate $loc_pref = ($dir_loc =~ /:$/) ? "$dir_loc" : "$dir_loc:"; 1005*0Sstevel@tonic-gate } else { 1006*0Sstevel@tonic-gate $dir_pref = ( $p_dir eq '/' ? '/' : "$p_dir/" ); 1007*0Sstevel@tonic-gate $loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" ); 1008*0Sstevel@tonic-gate } 1009*0Sstevel@tonic-gate 1010*0Sstevel@tonic-gate local ($dir, $name, $fullname, $prune, *DIR); 1011*0Sstevel@tonic-gate 1012*0Sstevel@tonic-gate unless ($no_chdir) { 1013*0Sstevel@tonic-gate # untaint the topdir 1014*0Sstevel@tonic-gate if (( $untaint ) && (is_tainted($dir_loc) )) { 1015*0Sstevel@tonic-gate ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|; # parent dir, now untainted 1016*0Sstevel@tonic-gate # once untainted, $updir_loc is pushed on the stack (as parent directory); 1017*0Sstevel@tonic-gate # hence, we don't need to untaint the parent directory every time we chdir 1018*0Sstevel@tonic-gate # to it later 1019*0Sstevel@tonic-gate unless (defined $updir_loc) { 1020*0Sstevel@tonic-gate if ($untaint_skip == 0) { 1021*0Sstevel@tonic-gate die "directory $dir_loc is still tainted"; 1022*0Sstevel@tonic-gate } 1023*0Sstevel@tonic-gate else { 1024*0Sstevel@tonic-gate return; 1025*0Sstevel@tonic-gate } 1026*0Sstevel@tonic-gate } 1027*0Sstevel@tonic-gate } 1028*0Sstevel@tonic-gate $ok = chdir($updir_loc) unless ($p_dir eq $File::Find::current_dir); 1029*0Sstevel@tonic-gate unless ($ok) { 1030*0Sstevel@tonic-gate warnings::warnif "Can't cd to $updir_loc: $!\n"; 1031*0Sstevel@tonic-gate return; 1032*0Sstevel@tonic-gate } 1033*0Sstevel@tonic-gate } 1034*0Sstevel@tonic-gate 1035*0Sstevel@tonic-gate push @Stack,[$dir_loc,$updir_loc,$p_dir,$dir_rel,-1] if $bydepth; 1036*0Sstevel@tonic-gate 1037*0Sstevel@tonic-gate if ($Is_MacOS) { 1038*0Sstevel@tonic-gate $p_dir = $dir_pref; # ensure trailing ':' 1039*0Sstevel@tonic-gate } 1040*0Sstevel@tonic-gate 1041*0Sstevel@tonic-gate while (defined $SE) { 1042*0Sstevel@tonic-gate 1043*0Sstevel@tonic-gate unless ($bydepth) { 1044*0Sstevel@tonic-gate # change (back) to parent directory (always untainted) 1045*0Sstevel@tonic-gate unless ($no_chdir) { 1046*0Sstevel@tonic-gate unless (chdir $updir_loc) { 1047*0Sstevel@tonic-gate warnings::warnif "Can't cd to $updir_loc: $!\n"; 1048*0Sstevel@tonic-gate next; 1049*0Sstevel@tonic-gate } 1050*0Sstevel@tonic-gate } 1051*0Sstevel@tonic-gate $dir= $p_dir; # $File::Find::dir 1052*0Sstevel@tonic-gate $name= $dir_name; # $File::Find::name 1053*0Sstevel@tonic-gate $_= ($no_chdir ? $dir_name : $dir_rel ); # $_ 1054*0Sstevel@tonic-gate $fullname= $dir_loc; # $File::Find::fullname 1055*0Sstevel@tonic-gate # prune may happen here 1056*0Sstevel@tonic-gate $prune= 0; 1057*0Sstevel@tonic-gate lstat($_); # make sure file tests with '_' work 1058*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 1059*0Sstevel@tonic-gate next if $prune; 1060*0Sstevel@tonic-gate } 1061*0Sstevel@tonic-gate 1062*0Sstevel@tonic-gate # change to that directory 1063*0Sstevel@tonic-gate unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) { 1064*0Sstevel@tonic-gate $updir_loc = $dir_loc; 1065*0Sstevel@tonic-gate if ( ($untaint) && (($tainted) || ($tainted = is_tainted($dir_loc) )) ) { 1066*0Sstevel@tonic-gate # untaint $dir_loc, what will be pushed on the stack as (untainted) parent dir 1067*0Sstevel@tonic-gate ( $updir_loc ) = $dir_loc =~ m|$untaint_pat|; 1068*0Sstevel@tonic-gate unless (defined $updir_loc) { 1069*0Sstevel@tonic-gate if ($untaint_skip == 0) { 1070*0Sstevel@tonic-gate die "directory $dir_loc is still tainted"; 1071*0Sstevel@tonic-gate } 1072*0Sstevel@tonic-gate else { 1073*0Sstevel@tonic-gate next; 1074*0Sstevel@tonic-gate } 1075*0Sstevel@tonic-gate } 1076*0Sstevel@tonic-gate } 1077*0Sstevel@tonic-gate unless (chdir $updir_loc) { 1078*0Sstevel@tonic-gate warnings::warnif "Can't cd to $updir_loc: $!\n"; 1079*0Sstevel@tonic-gate next; 1080*0Sstevel@tonic-gate } 1081*0Sstevel@tonic-gate } 1082*0Sstevel@tonic-gate 1083*0Sstevel@tonic-gate if ($Is_MacOS) { 1084*0Sstevel@tonic-gate $dir_name = "$dir_name:" unless ($dir_name =~ /:$/); 1085*0Sstevel@tonic-gate } 1086*0Sstevel@tonic-gate 1087*0Sstevel@tonic-gate $dir = $dir_name; # $File::Find::dir 1088*0Sstevel@tonic-gate 1089*0Sstevel@tonic-gate # Get the list of files in the current directory. 1090*0Sstevel@tonic-gate unless (opendir DIR, ($no_chdir ? $dir_loc : $File::Find::current_dir)) { 1091*0Sstevel@tonic-gate warnings::warnif "Can't opendir($dir_loc): $!\n"; 1092*0Sstevel@tonic-gate next; 1093*0Sstevel@tonic-gate } 1094*0Sstevel@tonic-gate @filenames = readdir DIR; 1095*0Sstevel@tonic-gate closedir(DIR); 1096*0Sstevel@tonic-gate 1097*0Sstevel@tonic-gate for my $FN (@filenames) { 1098*0Sstevel@tonic-gate next if $FN =~ $File::Find::skip_pattern; 1099*0Sstevel@tonic-gate 1100*0Sstevel@tonic-gate # follow symbolic links / do an lstat 1101*0Sstevel@tonic-gate $new_loc = Follow_SymLink($loc_pref.$FN); 1102*0Sstevel@tonic-gate 1103*0Sstevel@tonic-gate # ignore if invalid symlink 1104*0Sstevel@tonic-gate next unless defined $new_loc; 1105*0Sstevel@tonic-gate 1106*0Sstevel@tonic-gate if (-d _) { 1107*0Sstevel@tonic-gate push @Stack,[$new_loc,$updir_loc,$dir_name,$FN,1]; 1108*0Sstevel@tonic-gate } 1109*0Sstevel@tonic-gate else { 1110*0Sstevel@tonic-gate $fullname = $new_loc; # $File::Find::fullname 1111*0Sstevel@tonic-gate $name = $dir_pref . $FN; # $File::Find::name 1112*0Sstevel@tonic-gate $_ = ($no_chdir ? $name : $FN); # $_ 1113*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 1114*0Sstevel@tonic-gate } 1115*0Sstevel@tonic-gate } 1116*0Sstevel@tonic-gate 1117*0Sstevel@tonic-gate } 1118*0Sstevel@tonic-gate continue { 1119*0Sstevel@tonic-gate while (defined($SE = pop @Stack)) { 1120*0Sstevel@tonic-gate ($dir_loc, $updir_loc, $p_dir, $dir_rel, $byd_flag) = @$SE; 1121*0Sstevel@tonic-gate if ($Is_MacOS) { 1122*0Sstevel@tonic-gate # $p_dir always has a trailing ':', except for the starting dir, 1123*0Sstevel@tonic-gate # where $dir_rel eq ':' 1124*0Sstevel@tonic-gate $dir_name = "$p_dir$dir_rel"; 1125*0Sstevel@tonic-gate $dir_pref = "$dir_name:"; 1126*0Sstevel@tonic-gate $loc_pref = ($dir_loc =~ /:$/) ? $dir_loc : "$dir_loc:"; 1127*0Sstevel@tonic-gate } 1128*0Sstevel@tonic-gate else { 1129*0Sstevel@tonic-gate $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel"); 1130*0Sstevel@tonic-gate $dir_pref = "$dir_name/"; 1131*0Sstevel@tonic-gate $loc_pref = "$dir_loc/"; 1132*0Sstevel@tonic-gate } 1133*0Sstevel@tonic-gate if ( $byd_flag < 0 ) { # must be finddepth, report dirname now 1134*0Sstevel@tonic-gate unless ($no_chdir || ($dir_rel eq $File::Find::current_dir)) { 1135*0Sstevel@tonic-gate unless (chdir $updir_loc) { # $updir_loc (parent dir) is always untainted 1136*0Sstevel@tonic-gate warnings::warnif "Can't cd to $updir_loc: $!\n"; 1137*0Sstevel@tonic-gate next; 1138*0Sstevel@tonic-gate } 1139*0Sstevel@tonic-gate } 1140*0Sstevel@tonic-gate $fullname = $dir_loc; # $File::Find::fullname 1141*0Sstevel@tonic-gate $name = $dir_name; # $File::Find::name 1142*0Sstevel@tonic-gate if ($Is_MacOS) { 1143*0Sstevel@tonic-gate if ($dir_rel eq ':') { # must be the top dir, where we started 1144*0Sstevel@tonic-gate $name =~ s|:$||; # $File::Find::name 1145*0Sstevel@tonic-gate $p_dir = "$p_dir:" unless ($p_dir =~ /:$/); 1146*0Sstevel@tonic-gate } 1147*0Sstevel@tonic-gate $dir = $p_dir; # $File::Find::dir 1148*0Sstevel@tonic-gate $_ = ($no_chdir ? $name : $dir_rel); # $_ 1149*0Sstevel@tonic-gate } 1150*0Sstevel@tonic-gate else { 1151*0Sstevel@tonic-gate if ( substr($name,-2) eq '/.' ) { 1152*0Sstevel@tonic-gate substr($name, length($name) == 2 ? -1 : -2) = ''; # $File::Find::name 1153*0Sstevel@tonic-gate } 1154*0Sstevel@tonic-gate $dir = $p_dir; # $File::Find::dir 1155*0Sstevel@tonic-gate $_ = ($no_chdir ? $dir_name : $dir_rel); # $_ 1156*0Sstevel@tonic-gate if ( substr($_,-2) eq '/.' ) { 1157*0Sstevel@tonic-gate substr($_, length($_) == 2 ? -1 : -2) = ''; 1158*0Sstevel@tonic-gate } 1159*0Sstevel@tonic-gate } 1160*0Sstevel@tonic-gate 1161*0Sstevel@tonic-gate lstat($_); # make sure file tests with '_' work 1162*0Sstevel@tonic-gate { $wanted_callback->() }; # protect against wild "next" 1163*0Sstevel@tonic-gate } 1164*0Sstevel@tonic-gate else { 1165*0Sstevel@tonic-gate push @Stack,[$dir_loc, $updir_loc, $p_dir, $dir_rel,-1] if $bydepth; 1166*0Sstevel@tonic-gate last; 1167*0Sstevel@tonic-gate } 1168*0Sstevel@tonic-gate } 1169*0Sstevel@tonic-gate } 1170*0Sstevel@tonic-gate} 1171*0Sstevel@tonic-gate 1172*0Sstevel@tonic-gate 1173*0Sstevel@tonic-gatesub wrap_wanted { 1174*0Sstevel@tonic-gate my $wanted = shift; 1175*0Sstevel@tonic-gate if ( ref($wanted) eq 'HASH' ) { 1176*0Sstevel@tonic-gate if ( $wanted->{follow} || $wanted->{follow_fast}) { 1177*0Sstevel@tonic-gate $wanted->{follow_skip} = 1 unless defined $wanted->{follow_skip}; 1178*0Sstevel@tonic-gate } 1179*0Sstevel@tonic-gate if ( $wanted->{untaint} ) { 1180*0Sstevel@tonic-gate $wanted->{untaint_pattern} = $File::Find::untaint_pattern 1181*0Sstevel@tonic-gate unless defined $wanted->{untaint_pattern}; 1182*0Sstevel@tonic-gate $wanted->{untaint_skip} = 0 unless defined $wanted->{untaint_skip}; 1183*0Sstevel@tonic-gate } 1184*0Sstevel@tonic-gate return $wanted; 1185*0Sstevel@tonic-gate } 1186*0Sstevel@tonic-gate else { 1187*0Sstevel@tonic-gate return { wanted => $wanted }; 1188*0Sstevel@tonic-gate } 1189*0Sstevel@tonic-gate} 1190*0Sstevel@tonic-gate 1191*0Sstevel@tonic-gatesub find { 1192*0Sstevel@tonic-gate my $wanted = shift; 1193*0Sstevel@tonic-gate _find_opt(wrap_wanted($wanted), @_); 1194*0Sstevel@tonic-gate} 1195*0Sstevel@tonic-gate 1196*0Sstevel@tonic-gatesub finddepth { 1197*0Sstevel@tonic-gate my $wanted = wrap_wanted(shift); 1198*0Sstevel@tonic-gate $wanted->{bydepth} = 1; 1199*0Sstevel@tonic-gate _find_opt($wanted, @_); 1200*0Sstevel@tonic-gate} 1201*0Sstevel@tonic-gate 1202*0Sstevel@tonic-gate# default 1203*0Sstevel@tonic-gate$File::Find::skip_pattern = qr/^\.{1,2}\z/; 1204*0Sstevel@tonic-gate$File::Find::untaint_pattern = qr|^([-+@\w./]+)$|; 1205*0Sstevel@tonic-gate 1206*0Sstevel@tonic-gate# These are hard-coded for now, but may move to hint files. 1207*0Sstevel@tonic-gateif ($^O eq 'VMS') { 1208*0Sstevel@tonic-gate $Is_VMS = 1; 1209*0Sstevel@tonic-gate $File::Find::dont_use_nlink = 1; 1210*0Sstevel@tonic-gate} 1211*0Sstevel@tonic-gateelsif ($^O eq 'MacOS') { 1212*0Sstevel@tonic-gate $Is_MacOS = 1; 1213*0Sstevel@tonic-gate $File::Find::dont_use_nlink = 1; 1214*0Sstevel@tonic-gate $File::Find::skip_pattern = qr/^Icon\015\z/; 1215*0Sstevel@tonic-gate $File::Find::untaint_pattern = qr|^(.+)$|; 1216*0Sstevel@tonic-gate} 1217*0Sstevel@tonic-gate 1218*0Sstevel@tonic-gate# this _should_ work properly on all platforms 1219*0Sstevel@tonic-gate# where File::Find can be expected to work 1220*0Sstevel@tonic-gate$File::Find::current_dir = File::Spec->curdir || '.'; 1221*0Sstevel@tonic-gate 1222*0Sstevel@tonic-gate$File::Find::dont_use_nlink = 1 1223*0Sstevel@tonic-gate if $^O eq 'os2' || $^O eq 'dos' || $^O eq 'amigaos' || $^O eq 'MSWin32' || 1224*0Sstevel@tonic-gate $^O eq 'cygwin' || $^O eq 'epoc' || $^O eq 'qnx' || 1225*0Sstevel@tonic-gate $^O eq 'nto'; 1226*0Sstevel@tonic-gate 1227*0Sstevel@tonic-gate# Set dont_use_nlink in your hint file if your system's stat doesn't 1228*0Sstevel@tonic-gate# report the number of links in a directory as an indication 1229*0Sstevel@tonic-gate# of the number of files. 1230*0Sstevel@tonic-gate# See, e.g. hints/machten.sh for MachTen 2.2. 1231*0Sstevel@tonic-gateunless ($File::Find::dont_use_nlink) { 1232*0Sstevel@tonic-gate require Config; 1233*0Sstevel@tonic-gate $File::Find::dont_use_nlink = 1 if ($Config::Config{'dont_use_nlink'}); 1234*0Sstevel@tonic-gate} 1235*0Sstevel@tonic-gate 1236*0Sstevel@tonic-gate# We need a function that checks if a scalar is tainted. Either use the 1237*0Sstevel@tonic-gate# Scalar::Util module's tainted() function or our (slower) pure Perl 1238*0Sstevel@tonic-gate# fallback is_tainted_pp() 1239*0Sstevel@tonic-gate{ 1240*0Sstevel@tonic-gate local $@; 1241*0Sstevel@tonic-gate eval { require Scalar::Util }; 1242*0Sstevel@tonic-gate *is_tainted = $@ ? \&is_tainted_pp : \&Scalar::Util::tainted; 1243*0Sstevel@tonic-gate} 1244*0Sstevel@tonic-gate 1245*0Sstevel@tonic-gate1; 1246