publicscripts/mvwrap/mvwrap

311 lines
6.9 KiB
Text
Raw Normal View History

2001-07-31 08:40:13 +00:00
#!/usr/bin/perl -w
2001-07-31 11:59:16 +00:00
# $Id$
2001-07-31 08:40:13 +00:00
#
2001-07-31 11:50:31 +00:00
# Script to let an editor loose on a directory listing
# Much easier than doing 6000 moves by hand
# I think it's relatively safe now
2001-07-31 08:40:13 +00:00
# (C) 2001 Ward Wouts
#
use IO::File;
use File::Copy;
use POSIX qw(tmpnam);
use Getopt::Long;
2002-02-05 21:11:23 +00:00
&lock;
&cmdline;
unless (defined @source) { @source = &read_cur_dir; }
if (@pattern) {
@target = @source;
&pattern_edit;
} else {
$temp_file = &open_temp;
&edit($temp_file);
&read_temp($temp_file);
}
2001-07-31 08:40:13 +00:00
2001-08-01 21:31:45 +00:00
&run_checks;
2001-07-31 08:40:13 +00:00
if ($paranoia) { # extra checks for a specified list of files
&paranoia(\@source, \@target);
}
2001-07-31 11:50:31 +00:00
# if it's unsafe to move files directly, move the unsafe ones to a
# temporary file first
if (@unsafe = &check_safety(\@source, \@target)) {
&safety_belt(\@unsafe, \@source);
}
2001-07-31 11:50:31 +00:00
&move_files(\@source, \@target);
&unlock;
2001-07-31 11:50:31 +00:00
2001-08-01 21:31:45 +00:00
########################################################
2001-08-01 21:31:45 +00:00
# sub routines from here on...
#
#Read current dir
sub read_cur_dir {
my (@dir, $filename);
2002-02-05 21:11:23 +00:00
opendir(DIRHANDLE, ".") or &cdie("couldn't open .: $!\n");
2001-08-01 21:31:45 +00:00
while ( defined ($filename = readdir(DIRHANDLE)) ) {
2002-02-05 21:41:17 +00:00
unless ($filename eq "." | $filename eq ".." | $filename eq ".mvwrap" ) {
chomp ($filename);
push @dir, $filename;
2001-08-30 08:04:22 +00:00
}
2001-08-01 21:31:45 +00:00
}
closedir(DIRHANDLE);
return sort @dir;
2001-08-01 21:31:45 +00:00
}
2001-07-31 11:50:31 +00:00
# call EDITOR or vi with filename
# edit($filename);
2001-07-31 11:50:31 +00:00
sub edit {
my $filename = shift;
@editor = (defined $ENV{EDITOR} ? $ENV{EDITOR} : "vi", "$filename");
if ($opt_e) { @editor = ($opt_e, "$filename") }
2001-07-31 11:50:31 +00:00
system(@editor) == 0
2002-02-05 21:11:23 +00:00
or &cdie("System @editor failed: $!");
2001-07-31 11:50:31 +00:00
}
# make tempfiles and install handler to remove them
sub open_temp {
my $target;
do { $target_name = tmpnam() }
until $target = IO::File->new($target_name, O_RDWR|O_CREAT|O_EXCL);
2002-02-05 21:11:23 +00:00
END { if ($opt_e) { unlink($target_name) or &cdie("Couldn't unlink $target_name: $!");} }
foreach (@source) {
print $target "$_\n";
}
close ($target);
return $target_name;
}
sub read_temp {
my $target_name = shift;
2002-02-05 21:11:23 +00:00
open TARGET, $target_name or &cdie("Couldn't open tempfile: $target_name: $!");
foreach (<TARGET>) {
chomp;
push @target, $_;
}
2001-08-30 08:04:22 +00:00
close TARGET;
}
2001-08-01 21:31:45 +00:00
# Moves files from the names in one array to the names in another array
# Must be called like:
2001-07-31 11:50:31 +00:00
# move_files(\@source, \@target)
sub move_files {
my ($from, $to) = @_;
my ($i, $source, $target);
for ( $i=0; $i < scalar(@$from); $i++ ) {
2001-07-31 11:50:31 +00:00
$source=$from->[$i];
$target=$to->[$i];
unless ( $source eq $target ) {
if ($opt_v||$opt_n) {
print "mv \"$source\" \"$target\"\n";
}
unless ($opt_n) {
move("$source", "$target")
2002-02-05 21:11:23 +00:00
or &cdie("move failed: $!");
}
2001-07-31 11:50:31 +00:00
}
2001-07-31 08:40:13 +00:00
}
}
2001-08-01 21:31:45 +00:00
# read pattern file
sub read_pattern {
2002-02-05 21:11:23 +00:00
open PAT, "< $opt_f" or &cdie("Couldn't open pattern file: $!\n");
@pattern = <PAT>;
close PAT;
}
2001-08-01 21:31:45 +00:00
# use patterns to change filenames
sub pattern_edit {
my (@new_target, $pat);
foreach $pat (@pattern) {
@new_target=();
foreach(@target) {
eval $pat;
push @new_target, $_;
}
@target = @new_target;
}
}
2001-08-01 21:31:45 +00:00
sub run_checks {
my $line;
unless ( scalar(@source) == scalar(@target) ) {
2002-02-05 21:11:23 +00:00
&cdie("Aborting. Source and target list don't have the same number of lines.\n");
2001-08-01 21:31:45 +00:00
}
foreach $line (@target) {
2001-08-30 08:04:22 +00:00
if ( $line =~ m/^$/ ) {
2002-02-05 21:11:23 +00:00
&cdie("Aborting. You can't move to empty names.\n");
2001-12-06 12:19:43 +00:00
}
2001-08-01 21:31:45 +00:00
}
if ( &check_unique(@target) ) {
2002-02-05 21:11:23 +00:00
&cdie("Aborting. You're trying to move multiple files to the same name.\n");
2001-08-01 21:31:45 +00:00
}
}
# returns 0 if all entries in @target_list are unique. 1 if not.
2002-11-11 18:56:10 +00:00
sub check_unique (@){
my @uniqu;
my @target_list = @_;
my %seen = ();
@uniqu = grep { ! $seen{$_} ++ } @target_list;
return (scalar(@uniqu) != scalar(@target))
}
2001-07-31 11:50:31 +00:00
2001-08-01 21:31:45 +00:00
# Compares one array of file names to another, making sure files
2001-07-31 11:50:31 +00:00
# can be moved safely without overwriting each other
2001-08-01 21:31:45 +00:00
# Must be called like:
2001-07-31 11:50:31 +00:00
# check_safety(\@source, \@target)
2001-08-01 21:31:45 +00:00
# Returns an array of unsafe line numbers
2001-07-31 11:50:31 +00:00
sub check_safety {
my ($from, $to) = @_;
my ($i, $j, @changed, @danger, @unique, %seen);
2002-01-20 11:34:44 +00:00
my ($a, $b);
my ($n, %to);
# $n=0; %to = map { ($_, $n++) } @to;
for ($i=0; $i < scalar(@$from); $i++){
$a = $from->[$i];
$b = $to->[$i];
push @changed, $i if ($a ne $b);
}
foreach $i (@changed) {
$a = $from->[$i];
for ($j=0; $j < scalar(@$to); $j++){
$b = $to->[$j];
2002-01-20 11:34:44 +00:00
if (($a eq $b) && ($i != $j)) {
2001-07-31 11:50:31 +00:00
push @danger, $i;
2002-01-19 22:14:12 +00:00
push @danger, $j;
2001-07-31 11:50:31 +00:00
}
}
}
2002-01-20 11:34:44 +00:00
%seen = ();
@unique = grep { ! $seen{$_} ++ } @danger;
return @unique;
2001-07-31 11:50:31 +00:00
}
# generate random filename, which does not exist
sub rand_file {
my $filename;
my @chars=( "A" .. "Z", "a" .. "z", 0 .. 9);
while ( -e ($filename = join("", @chars[ map { rand @chars } ( 1 .. 8 ) ]))) {}
2001-07-31 11:50:31 +00:00
return $filename;
}
2001-08-01 21:31:45 +00:00
# Moves files to a random filename and updates the source array
# Must be called like:
2001-07-31 11:50:31 +00:00
# safety_belt(\@unsafe, \@source)
sub safety_belt {
my ($unsafe, $source) = @_;
my($filenr, $filename, $rand);
foreach $filenr (@$unsafe) {
$filename = $source->[$filenr];
$rand = &rand_file;
if ($opt_v||$opt_n) {
print "mv \"$filename\" \"$rand\"\n";
}
unless ($opt_n) {
move("$filename", "$rand")
2002-02-05 21:11:23 +00:00
or &cdie("move failed: $!\n");
}
$source->[$filenr] = "$rand";
2001-07-31 11:50:31 +00:00
}
}
# Extra safety checks when one's useing a specified list of files
# Must be called like:
# paranoia(\@unsafe, \@source)
sub paranoia {
my ($source, $target) =@_;
my $safe;
foreach $ctarget (@$target) {
$safe = 0;
foreach $csource (@$source) {
2002-01-19 22:21:21 +00:00
if ($ctarget eq $csource) { $safe = 1; }
}
2002-02-05 21:11:23 +00:00
if (! $safe && -e $ctarget) { &cdie("That would overwrite files\n"); }
}
}
2002-02-05 21:11:23 +00:00
# lock, unlock and cdie are the only subs that should use die;
# all others should use &cdie. A handler for this would probably
# be nicer.
# place lockfile in dir, or exit if one exists.
sub lock {
2002-02-05 21:11:23 +00:00
if ( -e ".mvwrap") {
die "Another mvwrap process is active in this direcory\n"
}
else {
2002-02-05 21:11:23 +00:00
open LOCK, "> .mvwrap" or die "Couldn't open .mvwrap for writing: $!\n";
print LOCK $$;
close LOCK;
}
}
2002-02-05 21:11:23 +00:00
# clean up lockfile
sub unlock {
2002-02-05 21:11:23 +00:00
if ( -e ".mvwrap" ) {
unlink(".mvwrap") or die "Couldn't remove lock file: $!\n";
}
}
2002-02-05 21:11:23 +00:00
# clean up lock & die
sub cdie {
$message = shift;
&unlock;
die "$message";
}
# parse commandline
sub cmdline {
%optctl = ();
&GetOptions("e=s", "h", "p=s", \@pattern, "f=s", "v", "n");
&GetOptions(\%optctl, "e");
&help if $opt_h;
2001-08-01 21:31:45 +00:00
if ($opt_f) { &read_pattern; }
if (scalar(@ARGV)>0) {
foreach (@ARGV) {
push @source, "$_";
}
$paranoia = 1;
}
}
2002-02-05 21:11:23 +00:00
# show help message
sub help {
$opt_h = 1; # just get rid of that stupid message from -w
print << "EOT";
This is a little script to make renaming large quantities of files easy.
It basically lets you edit the names in a directory with an editor
of your choice. By default this will be "vi", but it honors the EDITOR
environment variable. Which in turn can be overridden with the -e option.
$0 [options] [--] [files]
2001-08-30 08:04:22 +00:00
At the moment it takes the following options:
-h this help message
2001-08-30 08:04:22 +00:00
-e <editor> invoke with this editor; ignored if -f or -p is given
-p <pattern> use a pattern to edit; ignored if -f is given
-f <file> use a pattern file to edit
-v verbose; shows file moves
-n test; doesn't move, implies -v
EOT
2002-02-05 21:11:23 +00:00
&unlock;
exit;
}