File Coverage

blib/lib/File/Finder/Steps.pm
Criterion Covered Total %
statement 114 224 50.8
branch 32 86 37.2
condition 2 9 22.2
subroutine 45 74 60.8
pod 31 31 100.0
total 224 424 52.8


line stmt bran cond sub pod time code
1             package File::Finder::Steps;
2              
3             our $VERSION = '1.01';
4              
5 4     4   1929 use strict;
  4         6  
  4         1305  
6              
7 4     4   21 use Carp qw(croak);
  4         13  
  4         530  
8              
9             =head1 NAME
10              
11             File::Finder::Steps - steps for File::Finder
12              
13             =head1 SYNOPSIS
14              
15             ## See File::Finder for normal use of steps
16              
17             ## subclassing example:
18             BEGIN {
19             package My::File::Finder;
20             use base File::Finder;
21              
22             sub _steps_class { "My::File::Finder::Steps" }
23             }
24             BEGIN {
25             package My::File::Finder::Steps;
26             use base File::Finder::Steps;
27              
28             sub bigger_than { # true if bigger than N bytes
29             my $self = shift;
30             my $bytes = shift;
31             return sub {
32             -s > $bytes;
33             }
34             }
35             }
36              
37             my $over_1k = My::File::Finder->bigger_than(1024);
38             print "Temp files over 1k:\n";
39             $over_1k->ls->in("/tmp");
40              
41             =head1 DESCRIPTION
42              
43             C provide the predicates being tested for
44             C.
45              
46             =head2 STEPS METHODS
47              
48             These methods are called on a class or instance to add a "step". Each
49             step adds itself to a list of steps, returning the new object. This
50             allows you to chain steps together to form a formula.
51              
52             As in I, the default operator is "and", and short-circuiting is
53             performed.
54              
55             Note: the C, C, C, C, and C methods
56             are not available on Win32 systems.
57              
58             =over
59              
60             =item or
61              
62             Like I's C.
63              
64             =cut
65              
66 63     63 1 4245 sub or { return "or" }
67              
68             =item left
69              
70             Like a left parenthesis. Used in nesting pairs with C.
71              
72             =cut
73              
74 27     27 1 81 sub left { return "left" }
75 4     4   201 BEGIN { *begin = \&left; }
76              
77             =item right
78              
79             Like a right parenthesis. Used in nesting pairs with C.
80             For example:
81              
82             my $big_or_old = File::Finder
83             ->type('f')
84             ->left
85             ->size("+100")->or->mtime("+90")
86             ->right;
87             find($big_or_old->ls, "/tmp");
88              
89             You need parens because the "or" operator is lower precedence than
90             the implied "and", for the same reason you need them here:
91              
92             find /tmp -type f '(' -size +100 -o -mtime +90 ')' -print
93              
94             Without the parens, the -type would bind to -size, and not to the
95             choice of -size or -mtime.
96              
97             Mismatched parens will not be found until the formula is used, causing
98             a fatal error.
99              
100             =cut
101              
102 27     27 1 75 sub right { return "right" }
103 4     4   2867 BEGIN { *end = \&right; }
104              
105             =item begin
106              
107             Alias for C.
108              
109             =item end
110              
111             Alias for C.
112              
113             =item not
114              
115             Like I's C. Prefix operator, can be placed in front of
116             individual terms or open parens. Can be nested, but what's the point?
117              
118             # list all non-files in /tmp
119             File::Finder->not->type('f')->ls->in("/tmp");
120              
121             =cut
122              
123 13     13 1 42 sub not { return "not" }
124              
125             =item true
126              
127             Always returns true. Useful when a subexpression might fail, but
128             you don't want the overall code to fail:
129              
130             ... ->left-> ...[might return false]... ->or->true->right-> ...
131              
132             Of course, this is the I command's idiom of:
133              
134             find .... '(' .... -o -true ')' ...
135              
136             =cut
137              
138 5     5 1 10 sub true { return sub { 1 } }
  8     8   64  
139              
140             =item false
141              
142             Always returns false.
143              
144             =cut
145              
146 11     11 1 24 sub false { return sub { 0 } }
  15     15   70  
147              
148             =item comma
149              
150             Like GNU I's ",". The result of the expression (or
151             subexpression if in parens) up to this point is discarded, and
152             execution continues afresh. Useful when a part of the expression is
153             needed for its side effects, but shouldn't affect the rest of the
154             "and"-ed chain.
155              
156             # list all files and dirs, but don't descend into CVS dir contents:
157             File::Finder->type('d')->name('CVS')->prune->comma->ls->in('.');
158              
159             =cut
160              
161 12     12 1 78 sub comma { return "comma" } # gnu extension
162              
163             =item follow
164              
165             Enables symlink following, and returns true.
166              
167             =cut
168              
169             sub follow {
170 1     1 1 3 my $self = shift;
171 1         4 $self->{options}{follow} = 1;
172 1     0   6 return sub { 1 };
  0         0  
173             }
174              
175             =item name(NAME)
176              
177             True if basename matches NAME, which can be given as a glob
178             pattern or a regular expression object:
179              
180             my $pm_files = File::Finder->name('*.pm')->in('.');
181             my $pm_files_too = File::Finder->name(qr/pm$/)->in('.');
182              
183             =cut
184              
185             sub name {
186 2     2 1 5 my $self = shift;
187 2         4 my $name = shift;
188              
189 2 100       13 unless (UNIVERSAL::isa($name, "Regexp")) {
190 1         1765 require Text::Glob;
191 1         886 $name = Text::Glob::glob_to_regex($name);
192             }
193              
194             return sub {
195 170     170   567 /$name/;
196 2         91 };
197             }
198              
199             =item perm(PERMISSION)
200              
201             Like I's C<-perm>. Leading "-" means "all of these bits".
202             Leading "+" means "any of these bits". Value is de-octalized if a
203             leading 0 is present, which is likely only if it's being passed as a
204             string.
205              
206             my $files = File::Finder->type('f');
207             # find files that are exactly mode 644
208             my $files_644 = $files->perm(0644);
209             # find files that are at least world executable:
210             my $files_world_exec = $files->perm("-1");
211             # find files that have some executable bit set:
212             my $files_exec = $files->perm("+0111");
213              
214             =cut
215              
216             sub perm {
217 7     7 1 19 my $self = shift;
218 7         16 my $perm = shift;
219 7 50       63 $perm =~ /^(\+|-)?\d+\z/ or croak "bad permissions $perm";
220 7 100       47 if ($perm =~ s/^-//) {
    100          
221 1 50       7 $perm = oct($perm) if $perm =~ /^0/;
222             return sub {
223 85     85   215 ((stat _)[2] & $perm) == $perm;
224 1         4 };
225             } elsif ($perm =~ s/^\+//) { # gnu extension
226 3 50       16 $perm = oct($perm) if $perm =~ /^0/;
227             return sub {
228 255     255   759 ((stat _)[2] & $perm);
229 3         17 };
230             } else {
231 3 100       21 $perm = oct($perm) if $perm =~ /^0/;
232             return sub {
233 255     255   1238 ((stat _)[2] & 0777) == $perm;
234 3         24 };
235             }
236             }
237              
238             =item type(TYPE)
239              
240             Like I's C<-type>. All native Perl types are supported. Note
241             that C is a socket, mapping to Perl's C<-S>, to be consistent with
242             I. Returns true or false, as appropriate.
243              
244             =cut
245              
246 0         0 BEGIN {
247 4     4   8967 my %typecast;
248              
249             sub type {
250 2     2 1 6 my $self = shift;
251 2         6 my $type = shift;
252              
253 2 50       17 $type =~ /^[a-z]\z/i or croak "bad type $type";
254 2         7 $type =~ s/s/S/;
255              
256 2   66     147 $typecast{$type} ||= eval "sub { -$type _ }";
257             }
258             }
259              
260             =item print
261              
262             Prints the fullname to C, followed by a newline. Returns true.
263              
264             =cut
265              
266             sub print {
267             return sub {
268 0     0   0 print $File::Find::name, "\n";
269 0         0 1;
270 0     0 1 0 };
271             }
272              
273             =item print0
274              
275             Prints the fullname to C, followed by a NUL. Returns true.
276              
277             =cut
278              
279             sub print0 {
280             return sub {
281 0     0   0 print $File::Find::name, "\0";
282 0         0 1;
283 0     0 1 0 };
284             }
285              
286             =item fstype
287              
288             Not implemented yet.
289              
290             =item user(USERNAME|UID)
291              
292             True if the owner is USERNAME or UID.
293              
294             =cut
295              
296             sub user {
297 2     2 1 4 my $self = shift;
298 2         13 my $user = shift;
299              
300 2 50       6 croak 'user not supported on this platform' if $^O eq 'MSWin32';
301              
302 2 50       15 my $uid = ($user =~ /^\d+\z/) ? $user : _user_to_uid($user);
303 2 50       6 die "bad user $user" unless defined $uid;
304              
305             return sub {
306 170     170   446 (stat _)[4] == $uid;
307 2         9 };
308             }
309              
310             =item group(GROUPNAME|GID)
311              
312             True if the group is GROUPNAME or GID.
313              
314             =cut
315              
316             sub group {
317 2     2 1 5 my $self = shift;
318 2         2 my $group = shift;
319              
320 2 50       10 croak 'group not supported on this platform' if $^O eq 'MSWin32';
321              
322 2 50       14 my $gid = ($group =~ /^\d+\z/) ? $group : _group_to_gid($group);
323 2 50       6 die "bad group $gid" unless defined $gid;
324              
325             return sub {
326 170     170   467 (stat _)[5] == $gid;
327 2         11 };
328             }
329              
330             =item nouser
331              
332             True if the entry doesn't belong to any known user.
333              
334             =cut
335              
336             sub nouser {
337 2 50   2 1 10 croak 'nouser not supported on this platform' if $^O eq 'MSWin32';
338             return sub {
339 170     170   291 CORE::not defined _uid_to_user((stat _)[4]);
340             }
341 2         11 }
342              
343             =item nogroup
344              
345             True if the entry doesn't belong to any known group.
346              
347             =cut
348              
349             sub nogroup {
350 2 50   2 1 11 croak 'nogroup not supported on this platform' if $^O eq 'MSWin32';
351             return sub {
352 170     170   300 CORE::not defined _gid_to_group((stat _)[5]);
353             }
354 2         10 }
355              
356             =item links( +/- N )
357              
358             Like I's C<-links N>. Leading plus means "more than", minus
359             means "less than".
360              
361             =cut
362              
363             sub links {
364 2     2 1 5 my $self = shift;
365 2         12 my ($prefix, $n) = shift =~ /^(\+|-|)(.*)/;
366              
367             return sub {
368 170     170   296 _n($prefix, $n, (stat(_))[3]);
369 2         11 };
370             }
371              
372             =item inum( +/- N )
373              
374             True if the inode number meets the qualification.
375              
376             =cut
377              
378             sub inum {
379 0     0 1 0 my $self = shift;
380 0         0 my ($prefix, $n) = shift =~ /^(\+|-|)(.*)/;
381              
382             return sub {
383 0     0   0 _n($prefix, $n, (stat(_))[1]);
384 0         0 };
385             }
386              
387             =item size( +/- N [c/k])
388              
389             True if the file size meets the qualification. By default, N is
390             in half-K blocks. Append a trailing "k" to the number to indicate
391             1K blocks, or "c" to indicate characters (bytes).
392              
393             =cut
394              
395             sub size {
396 2     2 1 8 my $self = shift;
397 2         16 my ($prefix, $n) = shift =~ /^(\+|-|)(.*)/;
398              
399 2 50       33 if ($n =~ s/c\z//) {
400             return sub {
401 170     170   282 _n($prefix, $n, int(-s _));
402 2         16 };
403             }
404 0 0       0 if ($n =~ s/k\z//) {
405             return sub {
406 0     0   0 _n($prefix, $n, int(((-s _)+1023) / 1024));
407 0         0 };
408             }
409             return sub {
410 0     0   0 _n($prefix, $n, int(((-s _)+511) / 512));
411 0         0 };
412             }
413              
414             =item atime( +/- N )
415              
416             True if access time (in days) meets the qualification.
417              
418             =cut
419              
420             sub atime {
421 0     0 1 0 my $self = shift;
422 0         0 my ($prefix, $n) = shift =~ /^(\+|-|)(.*)/;
423              
424             return sub {
425 0     0   0 _n($prefix, $n, int(-A _));
426 0         0 };
427             }
428              
429             =item mtime( +/- N )
430              
431             True if modification time (in days) meets the qualification.
432              
433             =cut
434              
435             sub mtime {
436 0     0 1 0 my $self = shift;
437 0         0 my ($prefix, $n) = shift =~ /^(\+|-|)(.*)/;
438              
439             return sub {
440 0     0   0 _n($prefix, $n, int(-M _));
441 0         0 };
442             }
443              
444             =item ctime( +/- N )
445              
446             True if inode change time (in days) meets the qualification.
447              
448             =cut
449              
450             sub ctime {
451 0     0 1 0 my $self = shift;
452 0         0 my ($prefix, $n) = shift =~ /^(\+|-|)(.*)/;
453              
454             return sub {
455 0     0   0 _n($prefix, $n, int(-C _));
456 0         0 };
457             }
458              
459             =item exec(@COMMAND)
460              
461             Forks the child process via C. Any appearance of C<{}> in
462             any argument is replaced by the current filename. Returns true if the
463             child exit status is 0. The list is passed directly to C,
464             so if it's a single arg, it can contain C syntax. Otherwise,
465             it's a pre-parsed command that must be found on the PATH.
466              
467             Note that I couldn't figure out how to horse around with the current
468             directory very well, so I'm using C<$_> here instead of the more
469             traditional C. It still works, because we're still
470             chdir'ed down into the directory, but it looks weird on a trace.
471             Trigger C in C if you want a traditional I full
472             path.
473              
474             my $f = File::Finder->exec('ls', '-ldg', '{}');
475             find({ no_chdir => 1, wanted => $f }, @starting_dirs);
476              
477             Yeah, it'd be trivial for me to add a no_chdir method. Soon.
478              
479             =cut
480              
481             sub exec {
482 0     0 1 0 my $self = shift;
483 0         0 my @command = @ _;
484              
485             return sub {
486 0     0   0 my @mapped = @command;
487 0         0 for my $one (@mapped) {
488 0         0 $one =~ s/{}/$_/g;
489             }
490 0         0 system @mapped;
491 0         0 return !$?;
492 0         0 };
493             }
494              
495             =item ok(@COMMAND)
496              
497             Like C, but displays the command line first, and waits for a
498             response. If the response begins with C or C, runs the command.
499             If the command fails, or the response wasn't yes, returns false,
500             otherwise true.
501              
502             =cut
503              
504             sub ok {
505 0     0 1 0 my $self = shift;
506 0         0 my @command = @ _;
507              
508             return sub {
509 0     0   0 my @mapped = @command;
510 0         0 for my $one (@mapped) {
511 0         0 $one =~ s/{}/$_/g;
512             }
513 0         0 my $old = select(STDOUT);
514 0         0 $|++;
515 0         0 print "@mapped? ";
516 0         0 select $old;
517 0 0       0 return 0 unless =~ /^y/i;
518 0         0 system @mapped;
519 0         0 return !$?;
520 0         0 };
521             }
522              
523             =item prune
524              
525             Sets C<$File::Find::prune>, and returns true.
526              
527             =cut
528              
529             sub prune {
530 0     0 1 0 return sub { $File::Find::prune = 1 };
  0     0   0  
531             }
532              
533             =item xdev
534              
535             Not yet implemented.
536              
537             =item newer
538              
539             Not yet implemented.
540              
541             =item eval(CODEREF)
542              
543             Ah yes, the master escape, with extra benefits. Give it a coderef,
544             and it evaluates that code at the proper time. The return value is noted
545             for true/false and used accordingly.
546              
547             my $blaster = File::Finder->atime("+30")->eval(sub { unlink });
548              
549             But wait, there's more. If the parameter is an object that responds
550             to C, that method is automatically called, hoping for a
551             coderef return. This neat feature allows subroutines to be created and
552             nested:
553              
554             my $old = File::Finder->atime("+30");
555             my $big = File::Finder->size("+100");
556             my $old_or_big = File::Finder->eval($old)->or->eval($big);
557             my $killer = File::Finder->eval(sub { unlink });
558             my $kill_old_or_big = File::Finder->eval($old_or_big)->ls->eval($killer);
559             $kill_old_or_big->in('/tmp');
560              
561             Almost too cool for words.
562              
563             =cut
564              
565             sub eval {
566 117     117 1 179 my $self = shift;
567 117         145 my $eval = shift;
568              
569             ## if this is another File::Finder object... then cheat:
570 117 50       283 $eval = $eval->as_wanted if UNIVERSAL::can($eval, "as_wanted");
571              
572 117         350 return $eval; # just reuse the coderef
573             }
574              
575             =item depth
576              
577             Like I's C<-depth>. Sets a flag for C, and returns true.
578              
579             =cut
580              
581             sub depth {
582 1     1 1 2 my $self = shift;
583 1         4 $self->{options}{bydepth} = 1;
584 1     0   5 return sub { 1 };
  0         0  
585             }
586              
587             =item ls
588              
589             Like I's C<-ls>. Performs a C on the entry to
590             C (without forking), and returns true.
591              
592             =cut
593              
594             sub ls {
595 0 0   0 1 0 croak 'ls not supported on this platform' if $^O eq 'MSWin32';
596 0         0 return \&_ls;
597             }
598              
599             =item tar
600              
601             Not yet implemented.
602              
603             =item [n]cpio
604              
605             Not yet implemented.
606              
607             =item ffr($ffr_object)
608              
609             Incorporate a C object as a step. Note that this
610             must be a rule object, and not a result, so don't call or pass C.
611             For example, using C to define a
612             predicate for image files that are bigger than a megapixel in my
613             friends folder, I get:
614              
615             require File::Finder;
616             require File::Find::Rule;
617             require File::Find::Rule::ImageSize;
618             my $ffr = File::Find::Rule->file->image_x('>1000')->image_y('>1000');
619             my @big_friends = File::Finder->ffr($ffr)
620             ->in("/Users/merlyn/Pictures/Sorted/Friends");
621              
622             =cut
623              
624             sub ffr {
625 0     0 1 0 my $self = shift;
626 0         0 my $ffr_object = shift;
627              
628 0         0 my $their_wanted;
629              
630 4     4   55 no warnings;
  4         8  
  4         2101  
631             local *File::Find::find = sub {
632 0     0   0 my ($options) = @ _;
633 0         0 for (my ($k, $v) = each %$options) {
634 0 0       0 if ($k eq "wanted") {
635 0         0 $their_wanted = $v;
636             } else {
637 0         0 $self->{options}->{$k} = $v;
638             }
639             }
640 0         0 };
641 0         0 $ffr_object->in("/DUMMY"); # boom!
642 0 0       0 croak "no wanted defined" unless defined $their_wanted;
643 0         0 return $their_wanted;
644             }
645              
646             =item contains(pattern)
647              
648             True if the file contains C (either a literal string
649             treated as a regex, or a true regex object).
650              
651             my $plugh_files = File::Finder->type('f')->contains(qr/plugh/);
652              
653             Searching is performed on a line-by-line basis, respecting the
654             current value of C<$/>.
655              
656             =cut
657              
658             sub contains {
659 1     1 1 2 my $self = shift;
660 1         3 my $pat = shift;
661             return sub {
662 85 50   85   2075 open my $f, "<" . $_ or return 0;
663 85         1154 while (<$f>) {
664 4373 100       8722 return 1 if /$pat/;
665             }
666 84         839 return 0;
667 1         7 };
668             }
669              
670              
671             =back
672              
673             =head2 EXTENDING
674              
675             A step consists of a compile-time and a run-time component.
676              
677             During the creation of a C object, step methods are
678             called as if they were methods against the slowly-growing
679             C instance, including any additional parameters as in a
680             normal method call. The step is expected to return a coderef
681             (possibly a closure) to be executed at run-time.
682              
683             When a C object is being evaluated as the C
684             C routine, the collected coderefs are evaluated in sequence,
685             again as method calls against the C object. No
686             additional parameters are passed. However, the normal C
687             values are available, such as C<$_>, C<$File::Find::name>, and so on.
688             The C<_> pseudo-handle has been set properly, so you can safely
689             use C<-X> filetests and C against the pseudo-handle.
690             The routine is expected to return a true/false value, which becomes
691             the value of the step.
692              
693             Although a C object is passed both to the compile-time
694             invocation and the resulting run-time invocation, only the C
695             self-hash element is properly duplicated through the cloning process.
696             Do not be tempted to add additional self-hash elements without
697             overriding C's C<_clone>. Instead, pass values from the
698             compile-time phase to the run-time phase using closure variables, as
699             shown in the synopsis.
700              
701             For simplicity, you can also just mix-in your methods to the existing
702             C class, rather than subclassing both classes as
703             shown above. However, this may result in conflicting implementations
704             of a given step name, so beware.
705              
706             =head1 SEE ALSO
707              
708             L
709              
710             =head1 BUGS
711              
712             None known yet.
713              
714             =head1 AUTHOR
715              
716             Randal L. Schwartz, Emerlyn@stonehenge.comE
717              
718             =head1 COPYRIGHT AND LICENSE
719              
720             Copyright (C) 2003,2004 by Randal L. Schwartz,
721             Stonehenge Consulting Services, Inc.
722              
723             This library is free software; you can redistribute it and/or modify
724             it under the same terms as Perl itself, either Perl version 5.8.2 or,
725             at your option, any later version of Perl 5 you may have available.
726              
727             =cut
728              
729             ## utility subroutines
730              
731             sub _n {
732 340     340   464 my ($prefix, $arg, $value) = @ _;
733 340 100       471 if ($prefix eq "+") {
    50          
734 170         390 $value > $arg;
735             } elsif ($prefix eq "-") {
736 170         373 $value < $arg;
737             } else {
738 0         0 $value == $arg;
739             }
740             }
741              
742 0         0 BEGIN {
743              
744 4     4   17 my %user_to_uid;
745             my %uid_to_user;
746              
747             my $initialize = sub {
748 1         330 while (my ($user, $pw, $uid) = getpwent) {
749 19         44 $user_to_uid{$user} = $uid;
750 19         549 $uid_to_user{$uid} = $user;
751             }
752 4         669 };
753              
754             sub _user_to_uid {
755 0     0   0 my $user = shift;
756              
757 0 0       0 %user_to_uid or $initialize->();
758 0         0 $user_to_uid{$user};
759             }
760              
761             sub _uid_to_user {
762 170     170   186 my $uid = shift;
763              
764 170 100       234 %uid_to_user or $initialize->();
765 170         489 $uid_to_user{$uid};
766             }
767              
768             }
769              
770 0         0 BEGIN {
771              
772 4     4   19 my %group_to_gid;
773             my %gid_to_group;
774              
775             my $initialize = sub {
776 1         46 while (my ($group, $pw, $gid) = getgrent) {
777 39         70 $group_to_gid{$group} = $gid;
778 39         115 $gid_to_group{$gid} = $group;
779             }
780 4         2726 };
781              
782             sub _group_to_gid {
783 0     0   0 my $group = shift;
784              
785 0 0       0 %group_to_gid or $initialize->();
786 0         0 $group_to_gid{$group};
787             }
788              
789             sub _gid_to_group {
790 170     170   191 my $gid = shift;
791              
792 170 100       240 %gid_to_group or $initialize->();
793 170         491 $gid_to_group{$gid};
794             }
795              
796             }
797              
798 0         0 BEGIN {
799             ## from find2perl
800              
801 4     4   32 my @rwx = qw(--- --x -w- -wx r-- r-x rw- rwx);
802 4         134 my @moname = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
803              
804             sub _sizemm {
805 0     0     my $rdev = shift;
806 0           sprintf("%3d, %3d", ($rdev >> 8) & 0xff, $rdev & 0xff);
807             }
808              
809             sub _ls {
810 0     0     my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,
811             $atime,$mtime,$ctime,$blksize,$blocks) = stat(_);
812 0           my $pname = $File::Find::name;
813              
814 0 0         $blocks
815             or $blocks = int(($size + 1023) / 1024);
816              
817 0           my $perms = $rwx[$mode & 7];
818 0           $mode >>= 3;
819 0           $perms = $rwx[$mode & 7] . $perms;
820 0           $mode >>= 3;
821 0           $perms = $rwx[$mode & 7] . $perms;
822 0 0         substr($perms, 2, 1) =~ tr/-x/Ss/ if -u _;
823 0 0         substr($perms, 5, 1) =~ tr/-x/Ss/ if -g _;
824 0 0         substr($perms, 8, 1) =~ tr/-x/Tt/ if -k _;
825 0 0         if (-f _) { $perms = '-' . $perms; }
  0 0          
    0          
    0          
    0          
    0          
    0          
826 0           elsif (-d _) { $perms = 'd' . $perms; }
827 0           elsif (-l _) { $perms = 'l' . $perms; $pname .= ' -> ' . readlink($_); }
  0            
828 0           elsif (-c _) { $perms = 'c' . $perms; $size = _sizemm($rdev); }
  0            
829 0           elsif (-b _) { $perms = 'b' . $perms; $size = _sizemm($rdev); }
  0            
830 0           elsif (-p _) { $perms = 'p' . $perms; }
831 0           elsif (-S _) { $perms = 's' . $perms; }
832 0           else { $perms = '?' . $perms; }
833              
834 0   0       my $user = _uid_to_user($uid) || $uid;
835 0   0       my $group = _gid_to_group($gid) || $gid;
836              
837 0           my ($sec,$min,$hour,$mday,$mon,$timeyear) = localtime($mtime);
838 0 0         if (-M _ > 365.25 / 2) {
839 0           $timeyear += 1900;
840             } else {
841 0           $timeyear = sprintf("%02d:%02d", $hour, $min);
842             }
843              
844 0           printf "%5lu %4ld %-10s %3d %-8s %-8s %8s %s %2d %5s %s\n",
845             $ino, $blocks, $perms, $nlink, $user, $group, $size,
846             $moname[$mon], $mday, $timeyear, $pname;
847 0           1;
848             }
849             }
850              
851             1;
852             __END__