[win32] support win32_select(0,0,0,msec) (winsock doesn't)
[p5sagit/p5-mst-13.2.git] / lib / File / Find.pm
1 package File::Find;
2 require 5.000;
3 require Exporter;
4 use Config;
5 require Cwd;
6 require File::Basename;
7
8
9 =head1 NAME
10
11 find - traverse a file tree
12
13 finddepth - traverse a directory structure depth-first
14
15 =head1 SYNOPSIS
16
17     use File::Find;
18     find(\&wanted, '/foo','/bar');
19     sub wanted { ... }
20     
21     use File::Find;
22     finddepth(\&wanted, '/foo','/bar');
23     sub wanted { ... }
24
25 =head1 DESCRIPTION
26
27 The wanted() function does whatever verifications you want.
28 $File::Find::dir contains the current directory name, and $_ the
29 current filename within that directory.  $File::Find::name contains
30 C<"$File::Find::dir/$_">.  You are chdir()'d to $File::Find::dir when
31 the function is called.  The function may set $File::Find::prune to
32 prune the tree.
33
34 File::Find assumes that you don't alter the $_ variable.  If you do then
35 make sure you return it to its original value before exiting your function.
36
37 This library is primarily for the C<find2perl> tool, which when fed, 
38
39     find2perl / -name .nfs\* -mtime +7 \
40         -exec rm -f {} \; -o -fstype nfs -prune
41
42 produces something like:
43
44     sub wanted {
45         /^\.nfs.*$/ &&
46         (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_)) &&
47         int(-M _) > 7 &&
48         unlink($_)
49         ||
50         ($nlink || (($dev,$ino,$mode,$nlink,$uid,$gid) = lstat($_))) &&
51         $dev < 0 &&
52         ($File::Find::prune = 1);
53     }
54
55 Set the variable $File::Find::dont_use_nlink if you're using AFS,
56 since AFS cheats.
57
58 C<finddepth> is just like C<find>, except that it does a depth-first
59 search.
60
61 Here's another interesting wanted function.  It will find all symlinks
62 that don't resolve:
63
64     sub wanted {
65         -l && !-e && print "bogus link: $File::Find::name\n";
66     } 
67
68 =head1 BUGS
69
70 There is no way to make find or finddepth follow symlinks.
71
72 =cut
73
74 @ISA = qw(Exporter);
75 @EXPORT = qw(find finddepth);
76
77
78 sub find {
79     my $wanted = shift;
80     my $cwd = Cwd::cwd();
81     # Localize these rather than lexicalizing them for backwards
82     # compatibility.
83     local($topdir,$topdev,$topino,$topmode,$topnlink);
84     foreach $topdir (@_) {
85         (($topdev,$topino,$topmode,$topnlink) =
86           ($Is_VMS ? stat($topdir) : lstat($topdir)))
87           || (warn("Can't stat $topdir: $!\n"), next);
88         if (-d _) {
89             if (chdir($topdir)) {
90                 ($dir,$_) = ($topdir,'.');
91                 $name = $topdir;
92                 $prune = 0;
93                 &$wanted;
94                 if (!$prune) {
95                     my $fixtopdir = $topdir;
96                     $fixtopdir =~ s,/$,, ;
97                     $fixtopdir =~ s/\.dir$// if $Is_VMS;
98                     &finddir($wanted,$fixtopdir,$topnlink);
99                 }
100             }
101             else {
102                 warn "Can't cd to $topdir: $!\n";
103             }
104         }
105         else {
106             unless (($_,$dir) = File::Basename::fileparse($topdir)) {
107                 ($dir,$_) = ('.', $topdir);
108             }
109             $name = $topdir;
110             chdir $dir && &$wanted;
111         }
112         chdir $cwd;
113     }
114 }
115
116 sub finddir {
117     my($wanted, $nlink);
118     local($dir, $name);
119     ($wanted, $dir, $nlink) = @_;
120
121     my($dev, $ino, $mode, $subcount);
122
123     # Get the list of files in the current directory.
124     opendir(DIR,'.') || (warn "Can't open $dir: $!\n", return);
125     my(@filenames) = readdir(DIR);
126     closedir(DIR);
127
128     if ($nlink == 2 && !$dont_use_nlink) {  # This dir has no subdirectories.
129         for (@filenames) {
130             next if $_ eq '.';
131             next if $_ eq '..';
132             $name = "$dir/$_";
133             $nlink = 0;
134             &$wanted;
135         }
136     }
137     else {                    # This dir has subdirectories.
138         $subcount = $nlink - 2;
139         for (@filenames) {
140             next if $_ eq '.';
141             next if $_ eq '..';
142             $nlink = $prune = 0;
143             $name = "$dir/$_";
144             &$wanted;
145             if ($subcount > 0 || $dont_use_nlink) {    # Seen all the subdirs?
146
147                 # Get link count and check for directoriness.
148
149                 ($dev,$ino,$mode,$nlink) = ($Is_VMS ? stat($_) : lstat($_));
150                     # unless ($nlink || $dont_use_nlink);
151                 
152                 if (-d _) {
153
154                     # It really is a directory, so do it recursively.
155
156                     if (!$prune && chdir $_) {
157                         $name =~ s/\.dir$// if $Is_VMS;
158                         &finddir($wanted,$name,$nlink);
159                         chdir '..';
160                     }
161                     --$subcount;
162                 }
163             }
164         }
165     }
166 }
167
168
169 sub finddepth {
170     my $wanted = shift;
171
172     $cwd = Cwd::fastcwd();;
173
174     # Localize these rather than lexicalizing them for backwards
175     # compatibility.
176     local($topdir, $topdev, $topino, $topmode, $topnlink);
177     foreach $topdir (@_) {
178         (($topdev,$topino,$topmode,$topnlink) =
179           ($Is_VMS ? stat($topdir) : lstat($topdir)))
180           || (warn("Can't stat $topdir: $!\n"), next);
181         if (-d _) {
182             if (chdir($topdir)) {
183                 my $fixtopdir = $topdir;
184                 $fixtopdir =~ s,/$,, ;
185                 $fixtopdir =~ s/\.dir$// if $Is_VMS;
186                 &finddepthdir($wanted,$fixtopdir,$topnlink);
187                 ($dir,$_) = ($fixtopdir,'.');
188                 $name = $fixtopdir;
189                 &$wanted;
190             }
191             else {
192                 warn "Can't cd to $topdir: $!\n";
193             }
194         }
195         else {
196             unless (($_,$dir) = File::Basename::fileparse($topdir)) {
197                 ($dir,$_) = ('.', $topdir);
198             }
199             $name = $topdir;
200             chdir $dir && &$wanted;
201         }
202         chdir $cwd;
203     }
204 }
205
206 sub finddepthdir {
207     my($wanted, $nlink);
208     local($dir, $name);
209     ($wanted,$dir,$nlink) = @_;
210     my($dev, $ino, $mode, $subcount);
211
212     # Get the list of files in the current directory.
213     opendir(DIR,'.') || warn "Can't open $dir: $!\n";
214     my(@filenames) = readdir(DIR);
215     closedir(DIR);
216
217     if ($nlink == 2 && !$dont_use_nlink) {   # This dir has no subdirectories.
218         for (@filenames) {
219             next if $_ eq '.';
220             next if $_ eq '..';
221             $name = "$dir/$_";
222             $nlink = 0;
223             &$wanted;
224         }
225     }
226     else {                    # This dir has subdirectories.
227         $subcount = $nlink - 2;
228         for (@filenames) {
229             next if $_ eq '.';
230             next if $_ eq '..';
231             $nlink = 0;
232             $name = "$dir/$_";
233             if ($subcount > 0 || $dont_use_nlink) {    # Seen all the subdirs?
234
235                 # Get link count and check for directoriness.
236
237                 ($dev,$ino,$mode,$nlink) = ($Is_VMS ? stat($_) : lstat($_));
238                 
239                 if (-d _) {
240
241                     # It really is a directory, so do it recursively.
242
243                     if (chdir $_) {
244                         $name =~ s/\.dir$// if $Is_VMS;
245                         &finddepthdir($wanted,$name,$nlink);
246                         chdir '..';
247                     }
248                     --$subcount;
249                 }
250             }
251             &$wanted;
252         }
253     }
254 }
255
256 # Set dont_use_nlink in your hint file if your system's stat doesn't
257 # report the number of links in a directory as an indication
258 # of the number of files.
259 # See, e.g. hints/machten.sh for MachTen 2.2.
260 $dont_use_nlink = 1 if ($Config::Config{'dont_use_nlink'});
261
262 # These are hard-coded for now, but may move to hint files.
263 if ($^O eq 'VMS') {
264   $Is_VMS = 1;
265   $dont_use_nlink = 1;
266 }
267 if ($^O =~ m:^mswin32:i) {
268   $Is_NT = 1;
269   $dont_use_nlink = 1;
270 }
271
272 $dont_use_nlink = 1
273     if $^O eq 'os2' || $^O eq 'dos' || $^O eq 'amigaos';
274
275 1;
276