Sys::Hostname fails under Solaris 2.5 when setuid
[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 =cut
69
70 @ISA = qw(Exporter);
71 @EXPORT = qw(find finddepth);
72
73
74 sub find {
75     my $wanted = shift;
76     my $cwd = Cwd::cwd();
77     # Localize these rather than lexicalizing them for backwards
78     # compatibility.
79     local($topdir,$topdev,$topino,$topmode,$topnlink);
80     foreach $topdir (@_) {
81         (($topdev,$topino,$topmode,$topnlink) =
82           ($Is_VMS ? stat($topdir) : lstat($topdir)))
83           || (warn("Can't stat $topdir: $!\n"), next);
84         if (-d _) {
85             if (chdir($topdir)) {
86                 ($dir,$_) = ($topdir,'.');
87                 $name = $topdir;
88                 $prune = 0;
89                 &$wanted;
90                 if (!$prune) {
91                     my $fixtopdir = $topdir;
92                     $fixtopdir =~ s,/$,, ;
93                     $fixtopdir =~ s/\.dir$// if $Is_VMS;
94                     $fixtopdir =~ s/\\dir$// if $Is_NT;
95                     &finddir($wanted,$fixtopdir,$topnlink);
96                 }
97             }
98             else {
99                 warn "Can't cd to $topdir: $!\n";
100             }
101         }
102         else {
103             unless (($_,$dir) = File::Basename::fileparse($topdir)) {
104                 ($dir,$_) = ('.', $topdir);
105             }
106             $name = $topdir;
107             chdir $dir && &$wanted;
108         }
109         chdir $cwd;
110     }
111 }
112
113 sub finddir {
114     my($wanted, $nlink);
115     local($dir, $name);
116     ($wanted, $dir, $nlink) = @_;
117
118     my($dev, $ino, $mode, $subcount);
119
120     # Get the list of files in the current directory.
121     opendir(DIR,'.') || (warn "Can't open $dir: $!\n", return);
122     my(@filenames) = readdir(DIR);
123     closedir(DIR);
124
125     if ($nlink == 2 && !$dont_use_nlink) {  # This dir has no subdirectories.
126         for (@filenames) {
127             next if $_ eq '.';
128             next if $_ eq '..';
129             $name = "$dir/$_";
130             $nlink = 0;
131             &$wanted;
132         }
133     }
134     else {                    # This dir has subdirectories.
135         $subcount = $nlink - 2;
136         for (@filenames) {
137             next if $_ eq '.';
138             next if $_ eq '..';
139             $nlink = $prune = 0;
140             $name = "$dir/$_";
141             &$wanted;
142             if ($subcount > 0 || $dont_use_nlink) {    # Seen all the subdirs?
143
144                 # Get link count and check for directoriness.
145
146                 ($dev,$ino,$mode,$nlink) = ($Is_VMS ? stat($_) : lstat($_));
147                     # unless ($nlink || $dont_use_nlink);
148                 
149                 if (-d _) {
150
151                     # It really is a directory, so do it recursively.
152
153                     if (!$prune && chdir $_) {
154                         $name =~ s/\.dir$// if $Is_VMS;
155                         $name =~ s/\\dir$// if $Is_NT;
156                         &finddir($wanted,$name,$nlink);
157                         chdir '..';
158                     }
159                     --$subcount;
160                 }
161             }
162         }
163     }
164 }
165
166
167 sub finddepth {
168     my $wanted = shift;
169
170     $cwd = Cwd::fastcwd();;
171
172     # Localize these rather than lexicalizing them for backwards
173     # compatibility.
174     local($topdir, $topdev, $topino, $topmode, $topnlink);
175     foreach $topdir (@_) {
176         (($topdev,$topino,$topmode,$topnlink) =
177           ($Is_VMS ? stat($topdir) : lstat($topdir)))
178           || (warn("Can't stat $topdir: $!\n"), next);
179         if (-d _) {
180             if (chdir($topdir)) {
181                 my $fixtopdir = $topdir;
182                 $fixtopdir =~ s,/$,, ;
183                 $fixtopdir =~ s/\.dir$// if $Is_VMS;
184                 $fixtopdir =~ s/\\dir$// if $Is_NT;
185                 &finddepthdir($wanted,$fixtopdir,$topnlink);
186                 ($dir,$_) = ($fixtopdir,'.');
187                 $name = $fixtopdir;
188                 &$wanted;
189             }
190             else {
191                 warn "Can't cd to $topdir: $!\n";
192             }
193         }
194         else {
195             unless (($_,$dir) = File::Basename::fileparse($topdir)) {
196                 ($dir,$_) = ('.', $topdir);
197             }
198             $name = $topdir;
199             chdir $dir && &$wanted;
200         }
201         chdir $cwd;
202     }
203 }
204
205 sub finddepthdir {
206     my($wanted, $nlink);
207     local($dir, $name);
208     ($wanted,$dir,$nlink) = @_;
209     my($dev, $ino, $mode, $subcount);
210
211     # Get the list of files in the current directory.
212     opendir(DIR,'.') || warn "Can't open $dir: $!\n";
213     my(@filenames) = readdir(DIR);
214     closedir(DIR);
215
216     if ($nlink == 2 && !$dont_use_nlink) {   # This dir has no subdirectories.
217         for (@filenames) {
218             next if $_ eq '.';
219             next if $_ eq '..';
220             $name = "$dir/$_";
221             $nlink = 0;
222             &$wanted;
223         }
224     }
225     else {                    # This dir has subdirectories.
226         $subcount = $nlink - 2;
227         for (@filenames) {
228             next if $_ eq '.';
229             next if $_ eq '..';
230             $nlink = 0;
231             $name = "$dir/$_";
232             if ($subcount > 0 || $dont_use_nlink) {    # Seen all the subdirs?
233
234                 # Get link count and check for directoriness.
235
236                 ($dev,$ino,$mode,$nlink) = ($Is_VMS ? stat($_) : lstat($_));
237                 
238                 if (-d _) {
239
240                     # It really is a directory, so do it recursively.
241
242                     if (chdir $_) {
243                         $name =~ s/\.dir$// if $Is_VMS;
244                         $name =~ s/\\dir$// if $Is_NT;
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 'msdos' || $^O eq 'amigaos';
274
275 1;
276