This is a code clinic question. I'm trying to polish a script that prints a directory tree in
a format that's roughly equivalent to 'ls *'. For example:
dir1:
+[ dir2 ] item4 item8
item1 item5 item9
item2 item6
item3 item7
dir2:
item1 itemwithareallyreallystupidlylongname4
item2 item5
item3
What I've written is more complicated than I suspect it should be; and, it doesn't quite
do all that I want. How can I improve it?
The example below shows the four subroutines that do the work. I apologize for how much code there is, but I thought it might be helpful to give you a working program to play with.
build_list()
collects the directory contents. distribute_evenly() divides these lists into roughly even-sized
arrays using maxsize() to determine the number of columns, if $cols is not set.
printto_columns() prints out the list, padding the columns to fill the
width of the page or screen.
I don't know: it just looks clumsy, to me. I'm particularly unhappy with printto_columns(), and
although distribute_evenly is a marvel of brevity (thanks to I0), I'm not sure that I'm taking the
best approach, here (see the '*1.10' in distribute_evenly()? - there must be a more proper way to do that). May I bother you for some ideas of better ways to do each of these various tasks?
Thanks in advance for your opinions...
...I won't be able to read them until next week.
#!/usr/bin/perl -w
use strict;
use File::Find;
my $breakpage = 0;
my $short = 1;
my $cols = 0;
my $width = 70;
my $underchar = "\x{00AF}"; # or try "\x{007E}" (tilde)
my $list = build_list(\@ARGV);
printto_columns($list);
sub build_list{
my %gather_list;
my $directory_list = shift;
for my $topdir (sort(@$directory_list)){
print "Listing $topdir\n";
$topdir =~ tr~\\~/~;
find(\&{sub{
unless (defined $gather_list{$File::Find::dir}){
+
($gather_list{$File::Find::dir}) = map{
[ m{([^/]+)$} ]
} "$File::Find::dir" ;
}
return if m{\.$};
my $item = -d $_ ? "+[ $_ ]" : $_;
push @{$gather_list{$File::Find::dir}}, $item;
}}, $topdir);
};
return \%gather_list;
}
sub printto_columns{
my $hashoflists = shift;
for my $key_hdr (sort keys %$hashoflists){
my $short_hdr = shift @{$hashoflists->{$key_hdr}};
# assemble a header for each list
my $header = "";
$header .= "\f" if $breakpage;
if ($short){
$header .= "\n$short_hdr:\n" .
($underchar x (length $short_hdr)) . "\n";
}else{
my ($path_hdr) = $key_hdr;
$path_hdr =~ tr{/}{\\} if $^O =~ /Win/i;
$header .= "\n$path_hdr\n" .
($underchar x (length $path_hdr)) . "\n" ;
}
print $header unless $header eq "\f";
# split to columns called for, here.
my ($grid,$columns) = distribute_evenly($hashoflists->{$key_h
+dr}) ;
my $pad_to_page = int($width / $columns);
for my $x_coord (0..scalar @{$grid->[0]}){
my $row = "";
for my $y_coord (0..$columns-1){
my $cell = defined $grid->[$y_coord]->[$x_coord] ?
+
$grid->[$y_coord]->[$x_coord] :
"";
my $spacer = $pad_to_page - length ($cell);
$spacer = 1 if $spacer <= 0;
my $pad_col = $y_coord < $columns-1 ? "\x{0020}" x $s
+pacer : "";
$row .= length ($row) + length ($cell) > $width
+ ?
"\n".
("\x{0020}" x ($width - (length $cell))
+)."$cell\n" :
${cell}.${pad_col};
}
print "$row\n";
}
}
}
sub distribute_evenly{
my $chunks = shift;
my $bowls = $cols || int( $width / (maxsize($chunks)*1.
+10) ) ;
my @alphabet_soup = sort @$chunks;
my @ladels = map {int((@alphabet_soup)*$_/$bowls)} -$bow
+ls..0;
([map {[@alphabet_soup[$ladels[$_-1]..($ladels[$_]-1)]]} 1..$bowls],
+$bowls);
}
sub maxsize{
my $maxsize = 0;
my $to_measure = shift;
for (@$to_measure){
my $thissize = length;
$maxsize = $thissize unless $maxsize > $thissize;
}
$maxsize ;
}
__END__
mkmcconn