File Coverage

bin/ls
Criterion Covered Total %
statement 104 354 29.3
branch 17 170 10.0
condition 1 32 3.1
subroutine 22 42 52.3
pod n/a
total 144 598 24.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             =begin metadata
4              
5             Name: ls
6             Description: list file/directory information
7             Author: Mark Leighton Fisher, fisherm@tce.com
8             License: perl
9              
10             =end metadata
11              
12             =cut
13              
14             package PerlPowerTools::ls;
15              
16 1     1   224751 use strict;
  1         1  
  1         36  
17              
18 1     1   4 use Config qw(%Config);
  1         1  
  1         33  
19 1     1   4 use File::Basename qw(basename);
  1         1  
  1         77  
20 1     1   4 use File::Spec;
  1         1  
  1         25  
21 1     1   401 use File::Spec::Functions;
  1         629  
  1         77  
22 1     1   413 use File::stat;
  1         5398  
  1         53  
23              
24 1     1   5 use constant EX_SUCCESS => 0;
  1         1  
  1         49  
25 1     1   4 use constant EX_FAILURE => 1;
  1         2  
  1         42  
26 1     1   2 use constant SIX_MONTHS_SECONDS => 60*60*24*(365/2);
  1         1  
  1         30  
27 1     1   3 use constant PROGRAM => 'ls';
  1         0  
  1         630  
28              
29             __PACKAGE__->run(@ARGV) unless caller;
30              
31             sub get_columns {
32 0     0   0 my $class = shift;
33 0         0 my @methods = qw(windows unix default);
34 0         0 foreach my $m ( @methods ) {
35 0         0 my $cols = $class->can($m)->();
36 0 0       0 next unless defined $cols;
37 0         0 return $cols;
38             }
39              
40 0         0 $class->default;
41             }
42              
43             sub windows {
44 0 0   0   0 return unless $^O eq 'MSWin32';
45 0         0 my @lines = `powershell -command "&{(get-host).ui.rawui.WindowSize;}"`;
46              
47 0 0       0 while( my $l = shift @lines ) { last if $l =~ /\A-----/ }
  0         0  
48 0 0       0 return $lines[0] =~ m/\A\s*(\d+)/ ? $1 : ();
49             }
50              
51             sub unix {
52 0 0   0   0 return if $^O eq 'MSWin32';
53 0         0 my $c = do {
54 0 0       0 if( has('tput') ) { `tput cols` }
  0 0       0  
55 0         0 elsif( has('ssty') ) { `stty size | cut -d' ' -f 2` }
56 0         0 else { undef };
57             };
58 0         0 chomp $c;
59 0         0 return $c;
60             }
61              
62 0     0   0 sub default { 80 }
63              
64             sub has {
65 0     0   0 my $program = shift;
66 0         0 foreach my $dir ( split /\Q$Config{path_sep}/, $ENV{PATH} ) {
67 0 0       0 next unless -x catfile( $dir, $program );
68 0         0 return 1;
69             }
70 0         0 return;
71             }
72              
73 0         0 BEGIN {
74 1     1   6 my @perms = qw(--- --x -w- -wx r-- r-x rw- rwx);
75 1         41 my @ftype = ( '', qw(p c ? d ? b ? - ? l ? s ? ? ?) );
76              
77             sub format_mode {
78 0     0   0 my( $class, $mode, %opts ) = @_;
79              
80 0         0 my $setids = ($mode & 07000)>>9;
81 0         0 my @permstrs = @perms[($mode&0700)>>6, ($mode&0070)>>3, $mode&0007];
82 0         0 my $ftype = $ftype[($mode & 0170000)>>12];
83              
84 0 0       0 if ($setids) {
85 0 0       0 if ($setids & 01) { # Sticky bit
86 0 0       0 $permstrs[2] =~ s/([-x])$/$1 eq 'x' ? 't' : 'T'/e;
  0         0  
87             }
88 0 0       0 if ($setids & 04) { # Setuid bit
89 0 0       0 $permstrs[0] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e;
  0         0  
90             }
91 0 0       0 if ($setids & 02) { # Setgid bit
92 0 0       0 $permstrs[1] =~ s/([-x])$/$1 eq 'x' ? 's' : 'S'/e;
  0         0  
93             }
94             }
95              
96 0         0 join '', $ftype, @permstrs;
97             }
98             } # BEGIN
99              
100 3     3   24 sub VERSION { '0.71' };
101              
102 1     1   493 use Data::Dumper;
  1         6079  
  1         851  
103             sub DirEntries {
104 0     0   0 my( $class, $Options, $file ) = @_;
105              
106 0         0 my %Attributes = ();
107 0         0 my @Entries = ();
108              
109 0 0 0     0 if( exists $Options->{'d'} || ! -d $file ) {
110 0 0 0     0 if (-l $file or -e $file) {
111 0         0 push @Entries, $file;
112 0 0       0 $Attributes{$file} = -l $file ? lstat($file) : stat($file);
113 0         0 push @Entries, \%Attributes;
114 0         0 return @Entries;
115             }
116 0         0 $class->my_warn( "can't access '$file': $!" );
117 0         0 return;
118             }
119              
120 0         0 my $dh;
121 0 0       0 unless( opendir $dh, $file ) {
122 0         0 $class->my_warn( "failed to open directory '$file': $!" );
123 0         0 return;
124             }
125              
126 0         0 my $Name = "";
127 0         0 while( $Name = readdir($dh) ) {
128 0 0 0     0 next if (! exists $Options->{'a'} && $Name =~ m/^\./o);
129 0         0 push @Entries, $Name;
130 0         0 my $path = File::Spec->catfile( $file, $Name );
131 0 0       0 $Attributes{$Name} = -l $path ? lstat($path) : stat($path);
132             }
133 0         0 closedir($dh);
134              
135             # ------ return list with %Attributes ref at end
136 0         0 push(@Entries, \%Attributes);
137 0         0 return @Entries;
138             }
139              
140 0         0 BEGIN { # EntryFormat
141 1     1   875 my @Month = (
142             "Jan",
143             "Feb",
144             "Mar",
145             "Apr",
146             "May",
147             "Jun",
148             "Jul",
149             "Aug",
150             "Sep",
151             "Oct",
152             "Nov",
153             "Dec"
154             );
155              
156             sub EntryFormat {
157 0     0   0 my( $class, $Options, $Attributes, $Dirname, $Entry ) = @_;
158              
159 0         0 my $max_file_size_length = do {
160             my( $max ) =
161 0         0 sort { $b <=> $a }
162 0         0 map { $Attributes->{$_}->size }
  0         0  
163             keys %$Attributes;
164              
165 0         0 length $max;
166             };
167              
168 0 0       0 my $BlockSize = exists($Options->{'k'}) ? 2 : 1; # block size in 512-byte units
169              
170 0 0       0 if( exists $Options->{'i'} ) {
171 0 0       0 if( defined $Attributes->{$Entry} ) {
172 0         0 $class->output( sprintf "%10d ", $Attributes->{$Entry}->ino );
173             }
174             else {
175 0         0 $class->output( "_________ " );
176             }
177             }
178              
179 0 0       0 if( exists $Options->{'s'} ) {
180 0 0       0 if( defined $Attributes->{$Entry} ) {
181 0         0 my $Blocks = $Attributes->{$Entry}->blocks;
182 0 0       0 $Blocks = 0 if $Blocks eq '';
183 0         0 $class->output( sprintf "%4d ", $Blocks / $BlockSize + (($Blocks % $BlockSize) > 0) );
184             }
185             else {
186 0         0 $class->output( "____ " );
187             }
188             }
189              
190 0 0       0 if( ! exists $Options->{'l'} ) {
191 0         0 $class->output( "$Entry\n" );
192             }
193             else {
194 0 0       0 if( ! defined $Attributes->{$Entry} ) {
195 0         0 $class->output( <<'UNDEFSTAT' );
196             __________ ___ ________ ________ ________ ___ __ _____
197             UNDEFSTAT
198             }
199             else {
200 0         0 my $mode = $class->format_mode($Attributes->{$Entry}->mode);
201 0         0 $class->output( "$mode " );
202 0         0 $class->output( sprintf "%3d ", $Attributes->{$Entry}->nlink );
203              
204 0         0 my $max_uid_length = 8;
205 0 0       0 if( exists $Options->{'n'} ) {
206 0         0 $class->output( sprintf '%-*2$8d ', $Attributes->{$Entry}->uid, $max_uid_length );
207             }
208             else {
209 0         0 my $uid = $class->Getpwuid($Attributes->{$Entry}->uid);
210 0 0       0 if( defined $uid ) {
211 0         0 $class->output( sprintf '%-*2$s ', $uid, $max_uid_length );
212             }
213             else {
214 0         0 $class->output( sprintf'%-*2$d ', $Attributes->{$Entry}->uid, $max_uid_length );
215             }
216             }
217              
218 0         0 my $max_gid_length = 8;
219 0 0       0 if( exists $Options->{'n'} ) {
220 0         0 $class->output( sprintf '%-*2$d ', $Attributes->{$Entry}->gid, $max_gid_length );
221             }
222             else {
223 0         0 my $gid = $class->Getgrgid($Attributes->{$Entry}->gid);
224 0         0 my $max_gid_length = 8;
225 0 0       0 if( defined $gid ) {
226 0         0 $class->output( sprintf '%-*2$s ', $gid, $max_gid_length );
227             }
228             else {
229 0         0 $class->output( sprintf '%-*2$d ', $Attributes->{$Entry}->gid, $max_gid_length );
230             }
231             }
232              
233 0         0 my $size_width = 9;
234 0 0       0 if( $Attributes->{$Entry}->mode & 0140000 ) {
235 0         0 $class->output( sprintf '%*2$d ', $Attributes->{$Entry}->size, $max_file_size_length );
236             }
237             else {
238             $class->output( sprintf "%4x,%4x ",
239             (($Attributes->{$Entry}->dev & 0xFFFF000) > 16),
240 0         0 $Attributes->{$Entry}->dev & 0xFFFF
241             );
242             }
243              
244 0         0 my $time = $Attributes->{$Entry}->mtime;
245 0 0       0 $time = $Attributes->{$Entry}->ctime if exists $Options->{'c'};
246 0 0       0 $time = $Attributes->{$Entry}->atime if exists $Options->{'u'};
247              
248 0         0 my($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($time);
249 0         0 $class->output( $Month[$mon] );
250 0         0 $class->output( sprintf " %2d ", $mday );
251              
252 0 0       0 if( start_time() - $time <= SIX_MONTHS_SECONDS ) {
253 0         0 $class->output( sprintf "%02d:%02d", $hour, $min );
254             }
255             else {
256 0         0 $class->output( sprintf " %04d", $year + 1900 );
257             }
258             }
259             }
260              
261 0         0 my $path = File::Spec->catfile($Dirname, $Entry);
262 0 0       0 if( -l $path ) {
263 0         0 my $target = readlink( $path );
264 0         0 $Entry .= " -> $target";
265             }
266 0         0 $class->output( " $Entry\n" );
267             }
268             }
269              
270             sub List {
271 0     0   0 my $Attributes = pop(@_);
272 0         0 my( $class, $dir_name, $Options, $is_dir, $Expand, @files ) = @_;
273              
274             # ------ precompute max entry length and total size
275 0         0 my $total_blocks = 0;
276 0         0 my $max_file_length = 0;
277 0         0 foreach my $file (@files) {
278             $total_blocks +=
279 0 0 0     0 (!defined($Attributes->{$file}) || ($Attributes->{$file}->blocks eq '')) ? 0 : $Attributes->{$file}->blocks;
280 0         0 my $l = length $file;
281 0 0       0 $max_file_length = $l if $l > $max_file_length;
282             }
283 0         0 $max_file_length += 1; # account for spaces
284              
285 0 0       0 if( $is_dir ) {
286 0 0       0 $class->output( "$dir_name:\n" ) if exists $Options->{'R'};
287 0 0       0 $class->output( "total $total_blocks\n" ) if grep { defined $Options->{$_} } qw(s i);
  0         0  
288             }
289              
290 0         0 my @SortedEntries = $class->Order($Options, $Attributes, @files);
291              
292             # ------ user requested 1 entry/line, long, size, or inode
293 0 0       0 if( grep { defined $Options->{$_} } qw(1 l s i) ) {
  0         0  
294 0         0 for my $entry (@SortedEntries) {
295 0         0 $class->EntryFormat( $Options, $Attributes, $dir_name, $entry );
296             }
297             }
298             # ------ multi-column output
299             else {
300             # ------ compute rows, columns, width mask
301 0 0       0 $Options->{'w'} = $class->get_columns() unless defined $Options->{'w'};
302 0   0     0 my $Cols = (int($Options->{'w'} / $max_file_length)) || 1;
303 0         0 my $Rows = int(($#_+$Cols) / $Cols);
304 0         0 my $template = sprintf "%%-%ds ", $max_file_length;
305              
306 0         0 my $elt;
307 0         0 for ($elt = 0; $elt < $Rows * $Cols; $elt++) {
308 0         0 my $target = ($elt % $Cols) * $Rows + int(($elt / $Cols));
309 0 0       0 my $piece = sprintf $template, $target < ($#SortedEntries + 1) ? $SortedEntries[$target] : "";
310             # don't blank pad to eol of line
311 0 0       0 $piece =~ s/\s+$// if (($elt+1) % $Cols == 0);
312 0         0 $class->output( $piece );
313 0 0       0 $class->output( "\n" ) if (($elt+1) % $Cols == 0);
314             }
315 0 0       0 $class->output( "\n" ) if (($elt+1) % $Cols == 0);
316             }
317              
318             # ------ print blank line if -R
319 0 0       0 $class->output( "\n" ) if exists $Options->{'R'};
320              
321             # ------ list subdirectories of this directory
322 0 0 0     0 if( !exists($Options->{'d'}) && ($Expand || exists $Options->{'R'} )) {
      0        
323 0         0 for my $entry ($class->Order($Options, $Attributes, @files)) {
324 0 0 0     0 next if ($entry eq "." || $entry eq "..");
325 0 0 0     0 if (defined($Attributes->{$entry}) && $Attributes->{$entry}->mode & 0040000) {
326 0         0 my $path = File::Spec->canonpath(File::Spec->catdir($dir_name,$entry));
327 0         0 my @dirs = $class->DirEntries($Options, $path);
328 0         0 $class->List($path, $Options, 1, 0, @dirs);
329             }
330             }
331             }
332             }
333              
334             # ------ sort file list based on %Options
335             sub Order {
336 0     0   0 my( $class, $Options, $A, @Entries ) = @_;
337              
338             # ------ sort by size, largest first
339 0 0       0 if( exists $Options->{'S'} ) {
    0          
    0          
340 0 0       0 if( exists $Options->{'r'} ) {
341 0         0 @Entries = sort { $A->{$a}->size <=> $A->{$b}->size } @Entries;
  0         0  
342             }
343             else {
344 0         0 @Entries = sort { $A->{$b}->size <=> $A->{$a}->size } @Entries;
  0         0  
345             }
346             }
347             # ------ sort by time, most recent first
348 0         0 elsif( grep { exists $Options->{$_} } qw(t c u) ) {
349 0 0       0 if( exists $Options->{'r'} ) {
350 0 0       0 if( exists $Options->{'u'} ) {
    0          
351 0         0 @Entries = sort { $A->{$a}->atime <=> $A->{$b}->atime } @Entries;
  0         0  
352             }
353             elsif( exists $Options->{'c'} ) {
354 0         0 @Entries = sort { $A->{$a}->ctime <=> $A->{$b}->ctime } @Entries;
  0         0  
355             }
356             else {
357 0         0 @Entries = sort { $A->{$a}->mtime <=> $A->{$b}->mtime } @Entries;
  0         0  
358             }
359             }
360             else {
361 0 0       0 if( exists $Options->{'u'} ) {
    0          
362 0         0 @Entries = sort { $A->{$b}->atime <=> $A->{$a}->atime } @Entries;
  0         0  
363             }
364             elsif( exists $Options->{'c'} ) {
365 0         0 @Entries = sort { $A->{$b}->ctime <=> $A->{$a}->ctime } @Entries;
  0         0  
366             }
367             else {
368 0         0 @Entries = sort { $A->{$b}->mtime <=> $A->{$a}->mtime } @Entries;
  0         0  
369             }
370             }
371             # ------ sort by name
372             }
373             elsif( ! exists $Options->{'f'} ) {
374 0 0       0 if( exists $Options->{'r'} ) {
375 0         0 @Entries = sort { $b cmp $a } @Entries;
  0         0  
376             }
377             else {
378 0         0 @Entries = sort { $a cmp $b } @Entries;
  0         0  
379             }
380             }
381              
382 0         0 return @Entries;
383             }
384              
385 0     0   0 sub Getgrgid { getgrgid($_[1]) }
386 0     0   0 sub Getpwuid { getpwuid($_[1]) }
387              
388             BEGIN {
389 1     1   7 my $NO_GETGRGID = ! eval { my $dummy = ""; $dummy = (getpwuid(0))[0] };
  1         1  
  1         296  
390              
391 1 50       690 if( $NO_GETGRGID ) {
392 1     1   6 no warnings qw(redefine);
  1         1  
  1         39  
393 1     1   4 no strict qw(refs);
  1         2  
  1         62  
394 0         0 *{'Getgrgid'} = sub { ($_[1], 0) };
  0         0  
  0         0  
395 0         0 *{'Getpwuid'} = sub { ($_[1], 0) };
  0         0  
  0         0  
396             }
397             }
398              
399             sub my_exit {
400 0     0   0 my( $class, $code ) = @_;
401 0         0 CORE::exit $code;
402             }
403              
404 0     0   0 sub warn_fh { \*STDERR }
405             sub my_warn {
406 0     0   0 my( $class, $message, $prepend ) = @_;
407 0 0       0 $prepend = 1 unless defined $prepend;
408 0 0       0 $message = PROGRAM . ": " . $message if $prepend;
409 0         0 print { $class->warn_fh } $message;
  0         0  
410             }
411              
412 0     0   0 sub output_fh { \*STDOUT }
413             sub output {
414 0     0   0 my( $class, @messages ) = @_;
415 0         0 print { $class->output_fh } @messages;
  0         0  
416             }
417              
418             sub _opts_string {
419             # macOS only * *
420             # 1ABCFGHILOPRSTUWabcdefghiklmnopqrstuvwxy%
421 77     77   77 my $opts_string = '1A CF L RST Wa cd fg iklmnopqrstu wx';
422 77         662 $opts_string =~ s/\s+//g;
423 77         142 $opts_string;
424             }
425              
426             sub process_options {
427 39     39   221621 require Getopt::Std;
428              
429 39         1750 my( $class, @args ) = @_;
430              
431 39         51 my $Options = {};
432              
433 39         98 my $Supported = $class->_supported_options;
434              
435 39         59 my %original_positions;
436 39         118 foreach my $i ( 0 .. $#args ) {
437 93         136 my $arg = $args[$i];
438 93 100       291 last unless $arg =~ /\A-/;
439 55 50       109 last if $arg eq '--';
440 55         188 ( my $letter = $arg ) =~ s/\A--?//;
441 55         167 $original_positions{$letter} = $i;
442             }
443              
444 39 100       81 return $class->VERSION_MESSAGE if exists $original_positions{'version'};
445             return $class->usage(
446             -verbose => 2,
447             -output => $class->error_fh,
448 38 50       72 ) if exists $original_positions{'help'};
449              
450 38         80 my $opts_string = $class->_opts_string;
451              
452 38         47 local $Getopt::Std::STANDARD_HELP_VERSION = 1;
453              
454 38         124 local @ARGV = @args;
455 38 100       84 unless( Getopt::Std::getopts($opts_string, $Options) ) {
456 1         102 $class->my_warn( "usage: " . PROGRAM . " [-$opts_string] [file ...]", 0 );
457 1         9 $class->my_exit( EX_FAILURE );
458             }
459 38         2402 @args = @ARGV;
460              
461 38         113 my %Defaults = ( w => $class->get_columns );
462              
463 38         167 foreach my $key ( keys %Defaults ) {
464 38 50       70 next if defined $Options->{$key};
465 38         81 $Options->{$key} = $Defaults{$key};
466             }
467              
468 38 100       81 $Options->{'a'} = 1 if $Options->{'f'};
469              
470 38 50       244 unless( -t *STDOUT ) {
471 38 50       126 $Options->{'1'} = 1 unless -t *STDOUT;
472             }
473              
474 38 50 33     155 delete $Options->{'w'} if( defined $Options->{'w'} and $Options->{'w'} == 0 );
475              
476             =pod
477              
478             The -1, -C, -x, and -l options all override each other; the last
479             one specified determines the format used.
480              
481             The -c, -u, and -U options all override each other; the last one
482             specified determines the file time used.
483              
484             The -S and -t options override each other; the last one specified
485             determines the sort order used.
486              
487             The -B, -b, -w, and -q options all override each other; the last
488             one specified determines the format used for non-printable
489             characters.
490              
491             The -H, -L and -P options all override each other (either
492             partially or fully); they are applied in the order specified.
493              
494             =cut
495              
496 38         144 my @overrides = (
497             [ qw(1 C x l) ],
498             [ qw(c u U) ], # -U not supported
499             [ qw(S t) ],
500             # [ qw(B b w q) ], # -B -b -w not supported
501             # [ qw(H L P) ], # -H -P not supported
502             );
503              
504             @overrides = map {
505 38         63 my @opts = @$_;
  114         199  
506 114         126 [ grep { exists $Supported->{$_} } @opts ],
  342         780  
507             } @overrides;
508              
509 38         60 foreach my $row ( @overrides ) {
510             my @order =
511 25         69 sort { $original_positions{$a} <=> $original_positions{$b} }
512 114         139 grep { exists $Options->{$_} }
  304         442  
513             @$row;
514 114         123 my $last = $order[-1];
515              
516 114         149 foreach my $opt ( @$row ) {
517 304 100       460 next if $opt eq $last;
518 258         322 delete $Options->{$opt};
519             }
520             }
521              
522 38         324 return ($Options, @args);
523             }
524              
525             sub run {
526             {
527 1     1   7 no strict 'refs';
  1     0   1  
  1         593  
  0         0  
528 0     0   0 *{"start_time"} = do { my $time = time; sub { $time } };
  0         0  
  0         0  
  0         0  
  0         0  
529             }
530              
531 0         0 my( $class, @args ) = @_;
532              
533 0         0 my( $Options, @args ) = $class->process_options(@args);
534 0 0       0 @args = qw(.) unless @args; # current directory if nothing else
535              
536 0         0 my $ArgCount = -1; # file/directory argument count
537 0         0 my %Attributes; # File::stat directory entry attributes
538             my @Dirs; # directories in ARGV
539 0         0 my @Files; # non-directories in ARGV
540 0         0 my $First = 1; # first directory entry on command line
541              
542 0         0 for my $Arg (@args) {
543 0         0 $ArgCount++;
544 0 0       0 $Attributes{$Arg} = -l $Arg ? lstat($Arg) : stat($Arg);
545 0 0 0     0 my $ref = ( -d $Arg and ! exists $Options->{'d'} ) ? \@Dirs : \@Files;
546 0         0 push @$ref, $Arg;
547             }
548              
549 0 0       0 if (@Files) {
550 0         0 $First = 0;
551 0         0 my %attrs;
552             my @okfiles;
553 0         0 foreach (@Files) {
554 0         0 my @ret = $class->DirEntries($Options, $_);
555 0 0       0 next unless @ret; # stat() failed
556 0         0 %attrs = (%attrs, %{ $ret[-1] });
  0         0  
557 0         0 push @okfiles, $_;
558             }
559 0         0 my @sorted = $class->Order($Options, \%Attributes, @okfiles);
560 0         0 $class->List('.', $Options, 0, 0, @sorted, \%attrs);
561             }
562              
563 0         0 for my $Arg ($class->Order($Options, \%Attributes, @Dirs)) {
564 0 0       0 if( ! exists $Options->{'R'} ) {
565 0 0       0 $class->output( "\n" ) unless $First;
566 0         0 $First = 0;
567 0 0       0 $class->output( "$Arg:\n" ) if $ArgCount > 0;
568             }
569 0         0 $class->List($Arg, $Options, 1, 0, $class->DirEntries($Options, $Arg));
570             }
571             }
572              
573             sub start_time;
574              
575             sub _supported_options {
576 39     39   59 my $class = shift;
577 39         77 my $opts_string = $class->_opts_string;
578 39         71 $opts_string =~ s/\s+//g;
579 39         247 my %Supported = map { $_, 1 } split //, $opts_string;
  1092         1775  
580 39         266 return \%Supported;
581             };
582              
583             sub usage {
584 0     0   0 my $class = shift;
585 0         0 require Pod::Usage;
586 0         0 Pod::Usage::pod2usage(
587             -verbose => 2,
588             -output => $class->error_fh,
589             );
590 0         0 $class->my_exit( EX_SUCCESS );
591             }
592              
593             sub VERSION_MESSAGE {
594 3     3   6471 my $class = shift;
595 3         9 $class->output( PROGRAM . " version " . $class->VERSION . " (Perl Power Tools, Perl $^V)\n" );
596 3         13 $class->my_exit( EX_SUCCESS );
597             }
598              
599             __PACKAGE__;
600              
601             =pod
602              
603             =encoding utf8
604              
605             =head1 NAME
606              
607             ls - list file/directory information
608              
609             =head1 SYNOPSIS
610              
611             ls [-1ACFLRSTWacdfgiklmnopqrstux] [file ...]
612              
613             =head1 DESCRIPTION
614              
615             This program lists information about files and directories. If it is
616             invoked without file/directory name arguments, it lists the contents
617             of the current directory. Otherwise, B lists information about the
618             files and information about the contents of the directories (but see
619             B<-d>). Furthermore, without any option arguments B just lists
620             the names of files and directories. All files are listed before all
621             directories. The default sort order is ascending ASCII on filename.
622              
623             =head2 OPTIONS
624              
625             The BSD options C<1ACFLRSTWacdfgiklmnopqrstux> are recognized, but
626             only C<1RSacdfiklnrstu> are implemented:
627              
628             =over 4
629              
630             =item -1
631              
632             List entries 1 per line (default if output is not a tty). This is
633             disabled by any of C<-C>, C<-x>, or C<-l>.
634              
635             =item -R
636              
637             Recursively list the contents of all directories, breadth-first.
638              
639             =item -S
640              
641             Sort descending by size. This is disabled by C<-t>.
642              
643             =item -a
644              
645             List all files (normally files starting with '.' are ignored).
646              
647             =item -c
648              
649             Sort by descending last modification time of inode. This is
650             disabled by any of C<-u> or C<-U>.
651              
652             =item -d
653              
654             Do not list directory contents.
655              
656             =item -f
657              
658             Do not sort -- list in whatever order files/directories are returned
659             by the directory read function. This option implies -a.
660              
661             =item -i
662              
663             List file inode number. (Doesn't mean much on non-inode systems.)
664              
665             =item -k
666              
667             When used with B<-s>, list file/directory size in 1024-byte blocks.
668              
669             =item -l
670              
671             Long format listing of mode -- # of links, owner name, group name,
672             size in bytes, time of last modification, and name.
673              
674             =item -n
675              
676             List numeric uid and gid (default on platforms without getpwuid()).
677              
678             =item -r
679              
680             Reverse sorting order.
681              
682             =item -s
683              
684             List file/directory size in 512-byte blocks. (May not mean much
685             on non-Unix systems.)
686              
687             =item -t
688              
689             Sort by descending last modification time.
690              
691             =item -u
692              
693             Sort by descending last access time.
694              
695             =item -w
696              
697             Set the column width for the output. The default is the window size,
698             or 80 if the program cannot determine the window size. A column
699             width of 0 means unlimited.
700              
701             =back
702              
703             =head1 ENVIRONMENT
704              
705             The working of I is not influenced by any environment variables.
706              
707             =head1 BUGS
708              
709             The file metadata from C is used, which may not necessarily mean
710             much on non-Unix systems. Specifically, the uid, gid, inode, and
711             block numbers may be meaningless (or less than meaningful at least).
712              
713             The C<-l> option does not yet list the major and minor device numbers
714             for special files, but it does list the value of the 'dev' field as 2
715             hex 16-bit words. Doing this properly would probably require
716             filesystem type probing.
717              
718             =head1 AUTHOR
719              
720             This Perl implementation of I was written by Mark Leighton Fisher
721             of Thomson Consumer Electronics, I.
722              
723             Portions of Stat::lsmode from Mark Jason Dominus have been inlined
724             into this program uder the perl license. See L.
725              
726             =head1 COPYRIGHT and LICENSE
727              
728             This program is free and open software. You may use, modify,
729             distribute, and sell this program (and any modified variants) in any
730             way you wish, provided you do not restrict others from doing the same.
731              
732             =cut
733              
734