source: trunk/third/perl/lib/File/Find.pm @ 17035

Revision 17035, 20.5 KB checked in by zacheiss, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r17034, which included commits to RCS files with non-trunk default branches.
Line 
1package File::Find;
2use 5.005_64;
3require Exporter;
4require Cwd;
5
6=head1 NAME
7
8find - traverse a file tree
9
10finddepth - traverse a directory structure depth-first
11
12=head1 SYNOPSIS
13
14    use File::Find;
15    find(\&wanted, '/foo', '/bar');
16    sub wanted { ... }
17
18    use File::Find;
19    finddepth(\&wanted, '/foo', '/bar');
20    sub wanted { ... }
21
22    use File::Find;
23    find({ wanted => \&process, follow => 1 }, '.');
24
25=head1 DESCRIPTION
26
27The first argument to find() is either a hash reference describing the
28operations to be performed for each file, or a code reference.
29
30Here are the possible keys for the hash:
31
32=over 3
33
34=item C<wanted>
35
36The value should be a code reference.  This code reference is called
37I<the wanted() function> below.
38
39=item C<bydepth>
40
41Reports the name of a directory only AFTER all its entries
42have been reported.  Entry point finddepth() is a shortcut for
43specifying C<{ bydepth => 1 }> in the first argument of find().
44
45=item C<preprocess>
46
47The value should be a code reference.  This code reference is used to
48preprocess a directory; it is called after readdir() but before the loop that
49calls the wanted() function.  It is called with a list of strings and is
50expected to return a list of strings.  The code can be used to sort the
51strings alphabetically, numerically, or to filter out directory entries based
52on their name alone.
53
54=item C<postprocess>
55
56The value should be a code reference.  It is invoked just before leaving the
57current directory.  It is called in void context with no arguments.  The name
58of the current directory is in $File::Find::dir.  This hook is handy for
59summarizing a directory, such as calculating its disk usage.
60
61=item C<follow>
62
63Causes symbolic links to be followed. Since directory trees with symbolic
64links (followed) may contain files more than once and may even have
65cycles, a hash has to be built up with an entry for each file.
66This might be expensive both in space and time for a large
67directory tree. See I<follow_fast> and I<follow_skip> below.
68If either I<follow> or I<follow_fast> is in effect:
69
70=over 6
71
72=item *
73
74It is guaranteed that an I<lstat> has been called before the user's
75I<wanted()> function is called. This enables fast file checks involving S< _>.
76
77=item *
78
79There is a variable C<$File::Find::fullname> which holds the absolute
80pathname of the file with all symbolic links resolved
81
82=back
83
84=item C<follow_fast>
85
86This is similar to I<follow> except that it may report some files more
87than once.  It does detect cycles, however.  Since only symbolic links
88have to be hashed, this is much cheaper both in space and time.  If
89processing a file more than once (by the user's I<wanted()> function)
90is worse than just taking time, the option I<follow> should be used.
91
92=item C<follow_skip>
93
94C<follow_skip==1>, which is the default, causes all files which are
95neither directories nor symbolic links to be ignored if they are about
96to be processed a second time. If a directory or a symbolic link
97are about to be processed a second time, File::Find dies.
98C<follow_skip==0> causes File::Find to die if any file is about to be
99processed a second time.
100C<follow_skip==2> causes File::Find to ignore any duplicate files and
101dirctories but to proceed normally otherwise.
102
103
104=item C<no_chdir>
105
106Does not C<chdir()> to each directory as it recurses. The wanted()
107function will need to be aware of this, of course. In this case,
108C<$_> will be the same as C<$File::Find::name>.
109
110=item C<untaint>
111
112If find is used in taint-mode (-T command line switch or if EUID != UID
113or if EGID != GID) then internally directory names have to be untainted
114before they can be cd'ed to. Therefore they are checked against a regular
115expression I<untaint_pattern>.  Note that all names passed to the
116user's I<wanted()> function are still tainted.
117
118=item C<untaint_pattern>
119
120See above. This should be set using the C<qr> quoting operator.
121The default is set to  C<qr|^([-+@\w./]+)$|>.
122Note that the parantheses are vital.
123
124=item C<untaint_skip>
125
126If set, directories (subtrees) which fail the I<untaint_pattern>
127are skipped. The default is to 'die' in such a case.
128
129=back
130
131The wanted() function does whatever verifications you want.
132C<$File::Find::dir> contains the current directory name, and C<$_> the
133current filename within that directory.  C<$File::Find::name> contains
134the complete pathname to the file. You are chdir()'d to
135C<$File::Find::dir> when the function is called, unless C<no_chdir>
136was specified.  When <follow> or <follow_fast> are in effect, there is
137also a C<$File::Find::fullname>.  The function may set
138C<$File::Find::prune> to prune the tree unless C<bydepth> was
139specified.  Unless C<follow> or C<follow_fast> is specified, for
140compatibility reasons (find.pl, find2perl) there are in addition the
141following globals available: C<$File::Find::topdir>,
142C<$File::Find::topdev>, C<$File::Find::topino>,
143C<$File::Find::topmode> and C<$File::Find::topnlink>.
144
145This library is useful for the C<find2perl> tool, which when fed,
146
147    find2perl / -name .nfs\* -mtime +7 \
148        -exec rm -f {} \; -o -fstype nfs -prune
149
150produces something like:
151
152    sub wanted {
153        /^\.nfs.*\z/s &&
154        (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_)) &&
155        int(-M _) > 7 &&
156        unlink($_)
157        ||
158        ($nlink || (($dev, $ino, $mode, $nlink, $uid, $gid) = lstat($_))) &&
159        $dev < 0 &&
160        ($File::Find::prune = 1);
161    }
162
163Set the variable C<$File::Find::dont_use_nlink> if you're using AFS,
164since AFS cheats.
165
166
167Here's another interesting wanted function.  It will find all symlinks
168that don't resolve:
169
170    sub wanted {
171         -l && !-e && print "bogus link: $File::Find::name\n";
172    }
173
174See also the script C<pfind> on CPAN for a nice application of this
175module.
176
177=head1 CAVEAT
178
179Be aware that the option to follow symbolic links can be dangerous.
180Depending on the structure of the directory tree (including symbolic
181links to directories) you might traverse a given (physical) directory
182more than once (only if C<follow_fast> is in effect).
183Furthermore, deleting or changing files in a symbolically linked directory
184might cause very unpleasant surprises, since you delete or change files
185in an unknown directory.
186
187
188=cut
189
190@ISA = qw(Exporter);
191@EXPORT = qw(find finddepth);
192
193
194use strict;
195my $Is_VMS;
196
197require File::Basename;
198
199my %SLnkSeen;
200my ($wanted_callback, $avoid_nlink, $bydepth, $no_chdir, $follow,
201    $follow_skip, $full_check, $untaint, $untaint_skip, $untaint_pat,
202    $pre_process, $post_process);
203
204sub contract_name {
205    my ($cdir,$fn) = @_;
206
207    return substr($cdir,0,rindex($cdir,'/')) if $fn eq '.';
208
209    $cdir = substr($cdir,0,rindex($cdir,'/')+1);
210
211    $fn =~ s|^\./||;
212
213    my $abs_name= $cdir . $fn;
214
215    if (substr($fn,0,3) eq '../') {
216        do 1 while ($abs_name=~ s|/(?>[^/]+)/\.\./|/|);
217    }
218
219    return $abs_name;
220}
221
222
223sub PathCombine($$) {
224    my ($Base,$Name) = @_;
225    my $AbsName;
226
227    if (substr($Name,0,1) eq '/') {
228        $AbsName= $Name;
229    }
230    else {
231        $AbsName= contract_name($Base,$Name);
232    }
233
234    # (simple) check for recursion
235    my $newlen= length($AbsName);
236    if ($newlen <= length($Base)) {
237        if (($newlen == length($Base) || substr($Base,$newlen,1) eq '/')
238            && $AbsName eq substr($Base,0,$newlen))
239        {
240            return undef;
241        }
242    }
243    return $AbsName;
244}
245
246sub Follow_SymLink($) {
247    my ($AbsName) = @_;
248
249    my ($NewName,$DEV, $INO);
250    ($DEV, $INO)= lstat $AbsName;
251
252    while (-l _) {
253        if ($SLnkSeen{$DEV, $INO}++) {
254            if ($follow_skip < 2) {
255                die "$AbsName is encountered a second time";
256            }
257            else {
258                return undef;
259            }
260        }
261        $NewName= PathCombine($AbsName, readlink($AbsName));
262        unless(defined $NewName) {
263            if ($follow_skip < 2) {
264                die "$AbsName is a recursive symbolic link";
265            }
266            else {
267                return undef;
268            }
269        }
270        else {
271            $AbsName= $NewName;
272        }
273        ($DEV, $INO) = lstat($AbsName);
274        return undef unless defined $DEV;  #  dangling symbolic link
275    }
276
277    if ($full_check && $SLnkSeen{$DEV, $INO}++) {
278        if ($follow_skip < 1) {
279            die "$AbsName encountered a second time";
280        }
281        else {
282            return undef;
283        }
284    }
285
286    return $AbsName;
287}
288
289our($dir, $name, $fullname, $prune);
290sub _find_dir_symlnk($$$);
291sub _find_dir($$$);
292
293sub _find_opt {
294    my $wanted = shift;
295    die "invalid top directory" unless defined $_[0];
296
297    my $cwd           = $wanted->{bydepth} ? Cwd::fastcwd() : Cwd::cwd();
298    my $cwd_untainted = $cwd;
299    $wanted_callback  = $wanted->{wanted};
300    $bydepth          = $wanted->{bydepth};
301    $pre_process      = $wanted->{preprocess};
302    $post_process     = $wanted->{postprocess};
303    $no_chdir         = $wanted->{no_chdir};
304    $full_check       = $wanted->{follow};
305    $follow           = $full_check || $wanted->{follow_fast};
306    $follow_skip      = $wanted->{follow_skip};
307    $untaint          = $wanted->{untaint};
308    $untaint_pat      = $wanted->{untaint_pattern};
309    $untaint_skip     = $wanted->{untaint_skip};
310
311    # for compatability reasons (find.pl, find2perl)
312    our ($topdir, $topdev, $topino, $topmode, $topnlink);
313
314    # a symbolic link to a directory doesn't increase the link count
315    $avoid_nlink      = $follow || $File::Find::dont_use_nlink;
316   
317    if ( $untaint ) {
318        $cwd_untainted= $1 if $cwd_untainted =~ m|$untaint_pat|;
319        die "insecure cwd in find(depth)"  unless defined($cwd_untainted);
320    }
321   
322    my ($abs_dir, $Is_Dir);
323
324    Proc_Top_Item:
325    foreach my $TOP (@_) {
326        my $top_item = $TOP;
327        $top_item =~ s|/\z|| unless $top_item eq '/';
328        $Is_Dir= 0;
329       
330        ($topdev,$topino,$topmode,$topnlink) = stat $top_item;
331
332        if ($follow) {
333            if (substr($top_item,0,1) eq '/') {
334                $abs_dir = $top_item;
335            }
336            elsif ($top_item eq '.') {
337                $abs_dir = $cwd;
338            }
339            else {  # care about any  ../
340                $abs_dir = contract_name("$cwd/",$top_item);
341            }
342            $abs_dir= Follow_SymLink($abs_dir);
343            unless (defined $abs_dir) {
344                warn "$top_item is a dangling symbolic link\n";
345                next Proc_Top_Item;
346            }
347            if (-d _) {
348                _find_dir_symlnk($wanted, $abs_dir, $top_item);
349                $Is_Dir= 1;
350            }
351        }
352        else { # no follow
353            $topdir = $top_item;
354            unless (defined $topnlink) {
355                warn "Can't stat $top_item: $!\n";
356                next Proc_Top_Item;
357            }
358            if (-d _) {
359                $top_item =~ s/\.dir\z// if $Is_VMS;
360                _find_dir($wanted, $top_item, $topnlink);
361                $Is_Dir= 1;
362            }
363            else {
364                $abs_dir= $top_item;
365            }
366        }
367
368        unless ($Is_Dir) {
369            unless (($_,$dir) = File::Basename::fileparse($abs_dir)) {
370                ($dir,$_) = ('./', $top_item);
371            }
372
373            $abs_dir = $dir;
374            if ($untaint) {
375                my $abs_dir_save = $abs_dir;
376                $abs_dir = $1 if $abs_dir =~ m|$untaint_pat|;
377                unless (defined $abs_dir) {
378                    if ($untaint_skip == 0) {
379                        die "directory $abs_dir_save is still tainted";
380                    }
381                    else {
382                        next Proc_Top_Item;
383                    }
384                }
385            }
386
387            unless ($no_chdir or chdir $abs_dir) {
388                warn "Couldn't chdir $abs_dir: $!\n";
389                next Proc_Top_Item;
390            }
391
392            $name = $abs_dir . $_;
393
394            { &$wanted_callback }; # protect against wild "next"
395
396        }
397
398        $no_chdir or chdir $cwd_untainted;
399    }
400}
401
402# API:
403#  $wanted
404#  $p_dir :  "parent directory"
405#  $nlink :  what came back from the stat
406# preconditions:
407#  chdir (if not no_chdir) to dir
408
409sub _find_dir($$$) {
410    my ($wanted, $p_dir, $nlink) = @_;
411    my ($CdLvl,$Level) = (0,0);
412    my @Stack;
413    my @filenames;
414    my ($subcount,$sub_nlink);
415    my $SE= [];
416    my $dir_name= $p_dir;
417    my $dir_pref= ( $p_dir eq '/' ? '/' : "$p_dir/" );
418    my $dir_rel= '.';      # directory name relative to current directory
419
420    local ($dir, $name, $prune, *DIR);
421     
422    unless ($no_chdir or $p_dir eq '.') {
423        my $udir = $p_dir;
424        if ($untaint) {
425            $udir = $1 if $p_dir =~ m|$untaint_pat|;
426            unless (defined $udir) {
427                if ($untaint_skip == 0) {
428                    die "directory $p_dir is still tainted";
429                }
430                else {
431                    return;
432                }
433            }
434        }
435        unless (chdir $udir) {
436            warn "Can't cd to $udir: $!\n";
437            return;
438        }
439    }
440   
441    push @Stack,[$CdLvl,$p_dir,$dir_rel,-1]  if  $bydepth;
442
443    while (defined $SE) {
444        unless ($bydepth) {
445            $dir= $p_dir;
446            $name= $dir_name;
447            $_= ($no_chdir ? $dir_name : $dir_rel );
448            # prune may happen here
449            $prune= 0;
450            { &$wanted_callback };      # protect against wild "next"
451            next if $prune;
452        }
453     
454        # change to that directory
455        unless ($no_chdir or $dir_rel eq '.') {
456            my $udir= $dir_rel;
457            if ($untaint) {
458                $udir = $1 if $dir_rel =~ m|$untaint_pat|;
459                unless (defined $udir) {
460                    if ($untaint_skip == 0) {
461                        die "directory ("
462                            . ($p_dir ne '/' ? $p_dir : '')
463                            . "/) $dir_rel is still tainted";
464                    }
465                }
466            }
467            unless (chdir $udir) {
468                warn "Can't cd to ("
469                    . ($p_dir ne '/' ? $p_dir : '')
470                    . "/) $udir : $!\n";
471                next;
472            }
473            $CdLvl++;
474        }
475
476        $dir= $dir_name;
477
478        # Get the list of files in the current directory.
479        unless (opendir DIR, ($no_chdir ? $dir_name : '.')) {
480            warn "Can't opendir($dir_name): $!\n";
481            next;
482        }
483        @filenames = readdir DIR;
484        closedir(DIR);
485        @filenames = &$pre_process(@filenames) if $pre_process;
486        push @Stack,[$CdLvl,$dir_name,"",-2]   if $post_process;
487
488        if ($nlink == 2 && !$avoid_nlink) {
489            # This dir has no subdirectories.
490            for my $FN (@filenames) {
491                next if $FN =~ /^\.{1,2}\z/;
492               
493                $name = $dir_pref . $FN;
494                $_ = ($no_chdir ? $name : $FN);
495                { &$wanted_callback }; # protect against wild "next"
496            }
497
498        }
499        else {
500            # This dir has subdirectories.
501            $subcount = $nlink - 2;
502
503            for my $FN (@filenames) {
504                next if $FN =~ /^\.{1,2}\z/;
505                if ($subcount > 0 || $avoid_nlink) {
506                    # Seen all the subdirs?
507                    # check for directoriness.
508                    # stat is faster for a file in the current directory
509                    $sub_nlink = (lstat ($no_chdir ? $dir_pref . $FN : $FN))[3];
510
511                    if (-d _) {
512                        --$subcount;
513                        $FN =~ s/\.dir\z// if $Is_VMS;
514                        push @Stack,[$CdLvl,$dir_name,$FN,$sub_nlink];
515                    }
516                    else {
517                        $name = $dir_pref . $FN;
518                        $_= ($no_chdir ? $name : $FN);
519                        { &$wanted_callback }; # protect against wild "next"
520                    }
521                }
522                else {
523                    $name = $dir_pref . $FN;
524                    $_= ($no_chdir ? $name : $FN);
525                    { &$wanted_callback }; # protect against wild "next"
526                }
527            }
528        }
529    }
530    continue {
531        while ( defined ($SE = pop @Stack) ) {
532            ($Level, $p_dir, $dir_rel, $nlink) = @$SE;
533            if ($CdLvl > $Level && !$no_chdir) {
534                my $tmp = join('/',('..') x ($CdLvl-$Level));
535                die "Can't cd to $dir_name" . $tmp
536                    unless chdir ($tmp);
537                $CdLvl = $Level;
538            }
539            $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
540            $dir_pref = "$dir_name/";
541            if ( $nlink == -2 ) {
542                $name = $dir = $p_dir;
543                $_ = ".";
544                &$post_process;         # End-of-directory processing
545            } elsif ( $nlink < 0 ) {  # must be finddepth, report dirname now
546                $name = $dir_name;
547                if ( substr($name,-2) eq '/.' ) {
548                  $name =~ s|/\.$||;
549                }
550                $dir = $p_dir;
551                $_ = ($no_chdir ? $dir_name : $dir_rel );
552                if ( substr($_,-2) eq '/.' ) {
553                  s|/\.$||;
554                }
555                { &$wanted_callback }; # protect against wild "next"
556            } else {
557                push @Stack,[$CdLvl,$p_dir,$dir_rel,-1]  if  $bydepth;
558                last;
559            }
560        }
561    }
562}
563
564
565# API:
566#  $wanted
567#  $dir_loc : absolute location of a dir
568#  $p_dir   : "parent directory"
569# preconditions:
570#  chdir (if not no_chdir) to dir
571
572sub _find_dir_symlnk($$$) {
573    my ($wanted, $dir_loc, $p_dir) = @_;
574    my @Stack;
575    my @filenames;
576    my $new_loc;
577    my $pdir_loc = $dir_loc;
578    my $SE = [];
579    my $dir_name = $p_dir;
580    my $dir_pref = ( $p_dir   eq '/' ? '/' : "$p_dir/" );
581    my $loc_pref = ( $dir_loc eq '/' ? '/' : "$dir_loc/" );
582    my $dir_rel = '.';          # directory name relative to current directory
583    my $byd_flag;               # flag for pending stack entry if $bydepth
584
585    local ($dir, $name, $fullname, $prune, *DIR);
586   
587    unless ($no_chdir or $p_dir eq '.') {
588        my $udir = $dir_loc;
589        if ($untaint) {
590            $udir = $1 if $dir_loc =~ m|$untaint_pat|;
591            unless (defined $udir) {
592                if ($untaint_skip == 0) {
593                    die "directory $dir_loc is still tainted";
594                }
595                else {
596                    return;
597                }
598            }
599        }
600        unless (chdir $udir) {
601            warn "Can't cd to $udir: $!\n";
602            return;
603        }
604    }
605
606    push @Stack,[$dir_loc,$pdir_loc,$p_dir,$dir_rel,-1]  if  $bydepth;
607
608    while (defined $SE) {
609
610        unless ($bydepth) {
611            # change to parent directory
612            unless ($no_chdir) {
613                my $udir = $pdir_loc;
614                if ($untaint) {
615                    $udir = $1 if $pdir_loc =~ m|$untaint_pat|;
616                }
617                unless (chdir $udir) {
618                    warn "Can't cd to $udir: $!\n";
619                    next;
620                }
621            }
622            $dir= $p_dir;
623            $name= $dir_name;
624            $_= ($no_chdir ? $dir_name : $dir_rel );
625            $fullname= $dir_loc;
626            # prune may happen here
627            $prune= 0;
628            lstat($_); # make sure  file tests with '_' work
629            { &$wanted_callback }; # protect against wild "next"
630            next if  $prune;
631        }
632
633        # change to that directory
634        unless ($no_chdir or $dir_rel eq '.') {
635            my $udir = $dir_loc;
636            if ($untaint) {
637                $udir = $1 if $dir_loc =~ m|$untaint_pat|;
638                unless (defined $udir ) {
639                    if ($untaint_skip == 0) {
640                        die "directory $dir_loc is still tainted";
641                    }
642                    else {
643                        next;
644                    }
645                }
646            }
647            unless (chdir $udir) {
648                warn "Can't cd to $udir: $!\n";
649                next;
650            }
651        }
652
653        $dir = $dir_name;
654
655        # Get the list of files in the current directory.
656        unless (opendir DIR, ($no_chdir ? $dir_loc : '.')) {
657            warn "Can't opendir($dir_loc): $!\n";
658            next;
659        }
660        @filenames = readdir DIR;
661        closedir(DIR);
662
663        for my $FN (@filenames) {
664            next if $FN =~ /^\.{1,2}\z/;
665
666            # follow symbolic links / do an lstat
667            $new_loc = Follow_SymLink($loc_pref.$FN);
668
669            # ignore if invalid symlink
670            next unless defined $new_loc;
671     
672            if (-d _) {
673                push @Stack,[$new_loc,$dir_loc,$dir_name,$FN,1];
674            }
675            else {
676                $fullname = $new_loc;
677                $name = $dir_pref . $FN;
678                $_ = ($no_chdir ? $name : $FN);
679                { &$wanted_callback }; # protect against wild "next"
680            }
681        }
682
683    }
684    continue {
685        while (defined($SE = pop @Stack)) {
686            ($dir_loc, $pdir_loc, $p_dir, $dir_rel, $byd_flag) = @$SE;
687            $dir_name = ($p_dir eq '/' ? "/$dir_rel" : "$p_dir/$dir_rel");
688            $dir_pref = "$dir_name/";
689            $loc_pref = "$dir_loc/";
690            if ( $byd_flag < 0 ) {  # must be finddepth, report dirname now
691                unless ($no_chdir or $dir_rel eq '.') {
692                    my $udir = $pdir_loc;
693                    if ($untaint) {
694                        $udir = $1 if $dir_loc =~ m|$untaint_pat|;
695                    }
696                    unless (chdir $udir) {
697                        warn "Can't cd to $udir: $!\n";
698                        next;
699                    }
700                }
701                $fullname = $dir_loc;
702                $name = $dir_name;
703                if ( substr($name,-2) eq '/.' ) {
704                  $name =~ s|/\.$||;
705                }
706                $dir = $p_dir;
707                $_ = ($no_chdir ? $dir_name : $dir_rel);
708                if ( substr($_,-2) eq '/.' ) {
709                  s|/\.$||;
710                }
711
712                lstat($_); # make sure  file tests with '_' work
713                { &$wanted_callback }; # protect against wild "next"
714            } else {
715                push @Stack,[$dir_loc, $pdir_loc, $p_dir, $dir_rel,-1]  if  $bydepth;
716                last;
717            }
718        }
719    }
720}
721
722
723sub wrap_wanted {
724    my $wanted = shift;
725    if ( ref($wanted) eq 'HASH' ) {
726        if ( $wanted->{follow} || $wanted->{follow_fast}) {
727            $wanted->{follow_skip} = 1 unless defined $wanted->{follow_skip};
728        }
729        if ( $wanted->{untaint} ) {
730            $wanted->{untaint_pattern} = qr|^([-+@\w./]+)$| 
731                unless defined $wanted->{untaint_pattern};
732            $wanted->{untaint_skip} = 0 unless defined $wanted->{untaint_skip};
733        }
734        return $wanted;
735    }
736    else {
737        return { wanted => $wanted };
738    }
739}
740
741sub find {
742    my $wanted = shift;
743    _find_opt(wrap_wanted($wanted), @_);
744    %SLnkSeen= ();  # free memory
745}
746
747sub finddepth {
748    my $wanted = wrap_wanted(shift);
749    $wanted->{bydepth} = 1;
750    _find_opt($wanted, @_);
751    %SLnkSeen= ();  # free memory
752}
753
754# These are hard-coded for now, but may move to hint files.
755if ($^O eq 'VMS') {
756    $Is_VMS = 1;
757    $File::Find::dont_use_nlink = 1;
758}
759
760$File::Find::dont_use_nlink = 1
761    if $^O eq 'os2' || $^O eq 'dos' || $^O eq 'amigaos' || $^O eq 'MSWin32' ||
762       $^O eq 'cygwin' || $^O eq 'epoc';
763
764# Set dont_use_nlink in your hint file if your system's stat doesn't
765# report the number of links in a directory as an indication
766# of the number of files.
767# See, e.g. hints/machten.sh for MachTen 2.2.
768unless ($File::Find::dont_use_nlink) {
769    require Config;
770    $File::Find::dont_use_nlink = 1 if ($Config::Config{'dont_use_nlink'});
771}
772
7731;
Note: See TracBrowser for help on using the repository browser.