
use strict;
use warnings;

sub msg {
  print STDERR join("\n", @_), "\n";
}

sub fail {
  print STDERR "ls: ";
  msg @_;
  exit 1;
}

sub to_type_char {
  $_ = shift or return;
  return "d" if -d;
  return "b" if -b;
  return "c" if -c;
  return "l" if -l;
  return "p" if -p;
  return "s" if -S;
  return "-";
}

sub to_mode_str {
  my $mode = shift or return;
  my @b = split //, sprintf "%9b", $mode & 0777;
  for (0..8) {
    $b[$_] = $b[$_] ? substr "rwx", $_ % 3, 1 : "-";
  }
  return join "", @b;
}

sub to_time_str {
  my $t = shift or return;
  my @t = localtime($t);
  @t[5, 4] = ($t[5] + 1900, $t[4] + 1);
  return sprintf "%04d/%02d/%02d %02d:%02d:%02d", reverse @t[0..5];
}

my ($list, $sort_by_time, $reverse) = (0, 0, 0);
my @a = ();
for (@ARGV) {
  if (/^-(.*)$/) {
    my $p = $1;
    if (length($p) == 0) {
      fail "invalid option: $_";
    }
    for ($p =~ /./gm) {
      if ($_ eq "l") {
        $list = 1;
      }
      elsif ($_ eq "t") {
        $sort_by_time = 1;
      }
      elsif ($_ eq "r") {
        $reverse = 1;
      }
      elsif ($_ eq "h") {
        # show usage
        msg "List Files (argius)", "Usage: ls [options:-ltr] path [path...]";
        exit 1;
      }
      else {
        fail "bad option: -$_";
      }
    }
  } else {
    push @a, $_;
  }
}
if (@a == 0) {
  @a = (".");
}
my $count = 0;

for my $path (@a) {
  my $is_dir = $path ne "." && -d $path;
  if ($path ne ".") {
    print "\n$path:\n";
    chdir $path;
  }
  my @files = glob "*";
  if ($sort_by_time) {
    @files = sort { (stat $b)[9] <=> (stat $a)[9] } @{\@files};
  }
  if ($reverse) {
    @files = reverse @{\@files};
  }
  my $size_width = (@files) ? (sort { $b <=> $a } map { length(-s) } @files)[0] : 1;
  for (@files) {
    if ($list) {
      my ($dev,$ino,$mode,$nlink,
          $uid,$gid,$rdev,$size,
          $atime,$mtime,$ctime,
          $blksize,$blocks) = stat;
      my $owner = eval "getpwuid($uid)";
      my $group = eval "getgrgid($gid)";
      my $og = (defined $owner)
        ? sprintf "%-9s %-9s", $owner, $group
          : "? ?";
      printf "%s%9s %d %s %*d %14s %s\n",
        (to_type_char($_),
         to_mode_str($mode),
         $nlink,
         $og,
         $size_width,
         $size,
         to_time_str($mtime),
         $_);
    } else {
      my $width = int(((length) + 1) / 16 + 1) * 16;
      printf "%-*s ", $width - 1, $_;
    }
  }
  print "\n" unless $list;
  if ($is_dir) {
    chdir "..";
  }
}
