#!/usr/bin/perl #***************************************************************************** # # Copyright (c) 2004 Guillaume Cottenceau # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2, as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # #****************************************************************************** use Fcntl ':mode'; #- extract arguments @ARGV = map { /^-?-(\S+)$/ ? do { $options{$1} = 1; () } : $_ } @ARGV; if (@ARGV != 1) { die "Usage: " . basename($0) . " [OPTION]... DIRECTORY Recursively sort files in the specified directory, more recent last. Options: -noF do not emulate `ls -F' -nocolor do not emulate `ls --color' -l slightly longer listing format "; } sub mtime { my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = lstat $_[0]; $mtime; } my %colors; #- collect all LS_COLORS mappings foreach (split /:/, $ENV{LS_COLORS}) { /(.*)=(.*)/ and $colors{$1} = $2; } my %extensions; #- put file extensions mappings in a special hash foreach (keys %colors) { /^*(\..*)/ or next; $extensions{$1} = $colors{$_}; } my (%users, %groups); if ($options{l}) { foreach (cat_('/etc/passwd')) { my @fields = split /:/; $users{$fields[2]} = $fields[0]; } foreach (cat_('/etc/group')) { my @fields = split /:/; $groups{$fields[2]} = $fields[0]; } } sub col { my ($f, $orig_dir) = @_; my $retstring; if (!$options{nocolor}) { my $col; #- need chdir for link targets $orig_dir && $f =~ /^\.\./ and chdir $orig_dir; if (!-e $f) { #- first set (or)phan color (symlink to nonexistant file) $col = $colors{or}; } else { foreach (keys %extensions) { if ($f =~ /\Q$_\E$/) { #- set color on file extension match $col = $extensions{$_} ; goto col_done; } } } #- set color on file type $col ||= -d $f ? $colors{di} : -l $f ? $colors{ln} : -S $f ? $colors{so} : -p $f ? $colors{pi} : -b $f ? $colors{bd} : -c $f ? $colors{cd} : -u $f ? $colors{su} : -g $f ? $colors{sg} : -k $f ? $colors{wt} : -x $f ? $colors{ex} : ''; col_done: $retstring = "\033[${col}m$f\033[0;39m"; } else { $retstring = $f; } my $a; if (!$options{noF}) { #- appends a character for file type (like ls -F) $a = -d $f ? '/' : -S $f ? '=' : -p $f ? '|' : -x $f ? '*' : ''; $f =~ /\Q$a\E$/ and $a = ''; } $orig_dir and chdir $ENV{PWD}; return $retstring . "$a"; } #- generates the mode string just like `ls -l' sub filemode { my ($mode) = @_; return ( S_ISREG($mode) ? '-' : S_ISDIR($mode) ? 'd' : S_ISLNK($mode) ? 'l' : S_ISBLK($mode) ? 'b' : S_ISCHR($mode) ? 'c' : S_ISFIFO($mode) ? 'p' : S_ISSOCK($mode) ? 's' : '?' ) . ( ($mode & S_IRUSR) ? 'r' : '-' ) . ( ($mode & S_IWUSR) ? 'w' : '-' ) . ( ($mode & S_ISUID) ? (($mode & S_IXUSR) ? 's' : 'S') : (($mode & S_IXUSR) ? 'x' : '-') ) . ( ($mode & S_IRGRP) ? 'r' : '-' ) . ( ($mode & S_IWGRP) ? 'w' : '-' ) . ( ($mode & S_ISGID) ? (($mode & S_IXGRP) ? 's' : 'S') : (($mode & S_IXGRP) ? 'x' : '-') ) . ( ($mode & S_IROTH) ? 'r' : '-' ) . ( ($mode & S_IWOTH) ? 'w' : '-' ) . ( ($mode & S_ISVTX) ? (($mode & S_IXOTH) ? 't' : 'T') : (($mode & S_IXOTH) ? 'x' : '-') ); } #- generates additional information on file, an excerpt of `ls -l' sub infos { my ($f) = @_; if ($options{l}) { my ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,$blksize,$blocks) = stat $f; #- display mode first, then user/group, then size return filemode($mode) . sprintf(" %-8s %-8s", $users{$uid}, $groups{$gid}) . sprintf(" %8s ", (-b $f || -c $f ? join(", ", divide($rdev, 256)) : $size)); } } #- sort the results of find according to mtime to get more recent last using #- schwartzian transform foreach (sort { $a->[1] <=> $b->[1] } map { [ $_, mtime($_) ] } chomp_(`find $ARGV[0]`)) { my $f = $_->[0]; print scalar(localtime($_->[1])), " ", infos($f), col($f); if (-l $f) { print " -> ", col(readlink($f), dirname($f)), "\n"; } else { print "\n"; } } #- functions from perl-MDK-Common sub chomp_ { my @l = map { my $l = $_; chomp $l; $l } @_; wantarray() ? @l : $l[0] } sub dirname { local $_ = shift; s|[^/]*/*\s*$||; s|(.)/*$|$1|; $_ || '.' } sub basename { local $_ = shift; s|/*\s*$||; s|.*/||; $_ } sub divide { my $d = int $_[0] / $_[1]; wantarray() ? ($d, $_[0] % $_[1]) : $d } sub cat_ { local *F; open F, $_[0] or return; my @l = ; wantarray() ? @l : join '', @l }