Perl日記

PerlとかRubyとかPHPとかPythonとか

続・lsコマンドをPerlで

lsコマンドをPerlで - Perl日記
前回の「ls」コマンドをもう一度やってみた。
画面幅の取得は「Term::ReadKey」を使えばいいみたい。
でもPerl5.8.5では入ってなかったので、一応eval()。
「Getopt::Std」もまた今度。

#!/usr/bin/perl -T
# ls.pl
# "ls"コマンドをPerlで実装
use strict;
use warnings;
use List::Util qw(max first);

my $min_space = q{  }; # 最小スペース2マス

# 画面幅取得
my $wchar = eval {
  local $SIG{__DIE__}; local $SIG{__WARN__};
  require Term::ReadKey; # 端末画面幅取得用
  return (Term::ReadKey::GetTerminalSize())[0];
};

# use Getopt::Std; # 引数取得用
# と思ったけど自分で書いてみる
my %opt = ();
if (@ARGV) {
  LOOP_ARGV :
  for my $argv (@ARGV) {
    if ('-a' eq $argv) { # とりあえず -a だけ
      $opt{a} = 1;
      undef $argv;
    }
  }
  if (1 == grep($_, @ARGV)) {
#    $opt{path} = first { defined($_) }, @ARGV;
    ($opt{path}) = grep($_, @ARGV);
  }
}

# 表示ディレクトリ
$opt{path} ||= '.'; # 引数が指定されていなければカレントをデフォルトにする

# untainted
$opt{path} =~ s/^(\w+)$/$1/;

# ディレクトリ内ファイル取得
opendir my $dh, $opt{path}
  or die "ls.pl: $opt{path}: No Such file or directory\n"; # 改行を入れてスクリプトの死ではないように見せかける
my @files = map  { {name => $_, len => length($_)} }
#           sort grep { !/^\./ } # 不可視ファイルを無視
            sort
            readdir $dh;
closedir $dh;

if (!$opt{a}) { # -aオプションがなければ
  @files = grep { $_->{name} !~ /^\./ } @files; # 不可視ファイルを無視
}

# ファイル名最大長
my $maxlen_fname = max(@{[map($_->{len}, @files)]});
# ファイル数
my $count_f = @files;

if ($count_f) {

  # 列数
  my $cols = ($wchar)
           ? int($wchar / ($maxlen_fname + 2))
           : 4; # デフォルト値
  # 行数
  my $rows = ($count_f % $cols)
           ? int($count_f / $cols + 1)
           : int($count_f / $cols    );

  my @print_rows = ('dummy'); # [0]にダミーをセット

  # 表示ファイルを縦(列)に作っていく
  PUSH_FILENAME :
  while (@files) {
    my @temp = ('dummy'); # [0]にダミーをセット
    push @temp, shift(@files) for 1 .. $rows; # 添え字[1]からpush
    my $col_maxlen = max(@{[map($_->{len}, @temp[1 .. $rows])]});

    PUSH_FILENAME_ROWS :
    for my $i (1 .. $rows) { # 各行
      if ($temp[$i]) {
        if (my $diff_len = $col_maxlen - $temp[$i]->{len}) {
          $temp[$i]->{name} = $temp[$i]->{name}.(q{ } x $diff_len);
        }
        # ★ここが重要
        my $index = ($i % $rows) ? ($i % $rows) : $rows;
        push @{$print_rows[$index]}, $temp[$i]->{name}.$min_space;
      }
    }
  }

  shift @print_rows; # ダミーを捨てる

  local $" = q{}; #"
  for my $print_row (@print_rows) {
    print "@$print_row\n";
  }
}

__END__