123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273 |
- #!/usr/bin/perl
- use strict;
- use warnings FATAL => 'all';
- use utf8;
- use open qw(:std :utf8);
- #use encoding 'utf8';
- use vars qw($VERSION);
- $VERSION = '0.040-woldrich';
- use File::Basename;
- use File::Spec;
- use File::Temp;
- use File::Copy;
- use File::Path ();
- use Getopt::Long;
- my($error, $verbose) = (0, 0);
- if (! GetOptions("verbose|v" => \$verbose)) {
- die "Usage: $0 [--verbose] [directory|file|-]\n";
- }
- my(@files, @directories, @sorted);
- @ARGV = @ARGV > 0 ? @ARGV : './';
- for my $item(@ARGV) {
- if($item eq '-') {
- push(@files, map { chomp; $_ } <STDIN>);
- close STDIN or die "Cant close STDIN: $!\n";
- open(STDIN, '/dev/tty') or die "Cant reopen STDIN: $!\n";
- next;
- }
- if(-d $item) {
- opendir(my $dh, $item) or die "Cant opendir $item: $!\n";
- for(grep { ! /\A[.]{1,2}\Z/ } readdir($dh)) {
- $item =~ s{/$}{};
- if(-d "$item/$_") {
- push(@directories, "$item/$_/");
- }
- else {
- push(@files, "$item/$_");
- }
- }
- closedir $dh or die "Cant close dirhandle $item: $!\n";
- }
- }
- @sorted = (sort(@directories), sort(@files));
- if (grep(/[[:cntrl:]]/, @sorted)) {
- die "$0: control characters in filenames are not supported\n";
- }
- my $tmp=File::Temp->new(TEMPLATE => "dirXXXXX", DIR => File::Spec->tmpdir);
- open (OUT, ">".$tmp->filename) || die "$0: cannot create ".$tmp->filename.": $!\n";
- my %item;
- my %done;
- my $c=0;
- foreach (@sorted) {
- $item{++$c}=$_;
- print OUT sprintf("%5d %s\n", $c, $_);
- }
- @sorted=();
- close OUT || die "$0: cannot write ".$tmp->filename.": $!\n";
- my @editor = 'vi';
- if(exists($ENV{VIDIR_EDITOR})) {
- @editor = $ENV{VIDIR_EDITOR};
- }
- elsif(exists($ENV{EDITOR})) {
- @editor = split(' ', $ENV{EDITOR});
- }
- elsif(-x '/usr/bin/editor') {
- @editor = '/usr/bin/editor';
- }
- elsif(exists($ENV{VISUAL})) {
- @editor = split(' ', $ENV{VISUAL});
- }
- if( (exists($ENV{VIDIR_EDITOR_ARGS})) && ($ENV{VIDIR_EDITOR_ARGS} ne '') ) {
- system(@editor, $ENV{VIDIR_EDITOR_ARGS}, $tmp);
- }
- else {
- system(@editor, $tmp);
- }
- open (IN, $tmp->filename) || die "$0: cannot read ".$tmp->filename.": $!\n";
- while (<IN>) {
- chomp;
- if(/^\s*(\d+) {0,1}(.*)/) {
- my $num=int($1);
- my $name=$2;
- my $iscopy=exists $done{$num};
- #$name =~ s{/+$}{}g;
- if (! exists $item{$num} && ! $iscopy) {
- die "$0: unknown item number $num\n";
- }
- elsif ($iscopy || $name ne $item{$num}) {
- next unless length $name;
- my $src=$iscopy ? $done{$num} : $item{$num};
- if (! (-e $src || -l $src) ) {
- print STDERR "$0: $src does not exist\n";
- delete $item{$num};
- next;
- }
- # deal with swaps
- if (-e $name || -l $name) {
- my $tmp=$name."~";
- my $c=0;
- while (-e $tmp || -l $tmp) {
- $c++;
- $tmp=$name."~$c";
- }
- if (! rename($name, $tmp)) {
- print STDERR "$0: failed to rename $name to $tmp: $!\n";
- $error=1;
- }
- elsif ($verbose) {
- print "'$name' -> '$tmp'\n";
- }
- foreach my $item (keys %item) {
- if ($item{$item} eq $name) {
- $item{$item}=$tmp;
- }
- }
- }
- File::Path::make_path(dirname($name));
- my $result=$iscopy ? copy($src, $name) : move($src, $name);
- if (! $result) {
- print STDERR "$0: failed to ".($iscopy ? "copy" : "rename")." $src to $name: $!\n";
- $error=1;
- }
- if (-d $name && ! $iscopy) {
- foreach (values %item) {
- s/^\Q$src\E/$name/;
- }
- }
- if ($verbose) {
- print "'$src' => '$name'\n" unless $iscopy;
- print "'$src' ~> '$name'\n" if $iscopy;
- }
- }
- $done{$num}=$name;
- delete $item{$num};
- }
- elsif (/^\s*$/) {
- # skip empty line
- }
- else {
- die "$0: unable to parse line \"$_\", aborting\n";
- }
- }
- close IN || die "$0: cannot read ".$tmp->filename.": $!\n";
- unlink($tmp.'~') if -e $tmp.'~';
- sub rm {
- my $file = shift;
- if (-d $file && ! -l $file) {
- return File::Path::rmtree($file);
- }
- else {
- return unlink $file;
- }
- }
- foreach my $item (reverse sort values %item) {
- if (! rm($item)) {
- print STDERR "$0: failed to remove $item: $!\n";
- $error=1;
- }
- if ($verbose) {
- print "removed '$item'\n";
- }
- }
- exit $error;
- __END__
- =head1 NAME
- vidir - edit directory
- =head1 SYNOPSIS
- B<vidir> [--verbose] [directory|file|-] ...
- =head1 DESCRIPTION
- vidir allows editing of the contents of a directory in a text editor. If no
- directory is specified, the current directory is edited.
- When editing a directory, each item in the directory will appear on its own
- numbered line. These numbers are how vidir keeps track of what items are
- changed. Delete lines to remove files from the directory, or
- edit filenames to rename files. You can also switch pairs of numbers to
- swap filenames.
- Note that if "-" is specified as the directory to edit, it reads a list of
- filenames from stdin and displays those for editing. Alternatively, a list
- of files can be specified on the command line.
- =head1 OPTIONS
- =over 4
- =item -v, --verbose
- Verbosely display the actions taken by the program.
- =back
- =head1 EXAMPLES
- =over 4
- =item vidir
- =item vidir *.jpeg
- Typical uses.
- =item find | vidir -
- Edit subdirectory contents too. To delete subdirectories,
- delete all their contents and the subdirectory itself in the editor.
- =item find -type f | vidir -
- Edit all files under the current directory and subdirectories.
- =back
- =head1 ENVIRONMENT VARIABLES
- =over 4
- =item EDITOR
- Editor to use.
- =item VISUAL
- Also supported to determine what editor to use.
- =item VIDIR_EDITOR_ARGS
- Optional args for editor
- =back
- =head1 AUTHOR
- Joey Hess <joey@kitenet.net> 2006-2010
- Modifications by Magnus Woldrich <m@japh.se> 2011
- =head1 COPYRIGHT
- Copyright 2006-2011 the B<vidir> L</AUTHOR>s as listed above.
- Licensed under the GNU GPL.
- =cut
|