File Coverage

blib/lib/File/Finder/Steps.pm
Criterion Covered Total %
statement 110 215 51.1
branch 28 76 36.8
condition 2 9 22.2
subroutine 45 74 60.8
pod 31 31 100.0
total 216 405 53.3


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