#!/usr/bin/perl -w
# $Date: 2020/03/01 18:12:03 $

# simple script to make real local commits in a format (mainly) compatible to 
# the old cvsq, so that a cvsq upload will correctly upload these things.
#
# see cvsq(1) and cvsq-files(1) for a documentation
#
# If you received this file without a man page, see
# http://www.linta.de/~aehlig/cvsq/

use strict;
use Cwd;
use Getopt::Long qw(:config require_order);
use File::Temp;


my $cvs = "cvs";
my $CVSdir = "CVS";
my $command = "ci"; # The default for this script...

my $cleancommand = $ENV{'CVSQ_MAKE_CLEAN'};

my $home=$ENV{'HOME'};
defined $home && -d $home
	or die "Invalid home directory.\n";

my $pwd = getcwd();
my $date = `date`; chomp($date);
my $host = `hostname`; chomp($host);

### The special call with file environentvariable CVSQ_ACTION being hardlink


if (defined $ENV{'CVSQ_ACTION'} && $ENV{'CVSQ_ACTION'} eq "hardlink") {
	@ARGV == 4 && $ARGV[0] eq "hardlink"
		or die "The hardlink call is $0 hardlink <sourcedir> <targetdir> <slot>.\n";

	say_hardlink($ARGV[1],$ARGV[2],$ARGV[3] . "-late");

	exit 0;
}

## Ensure, all the needed directories are present.

foreach my $dir (qw! .cvsq .cvsq/todo .cvsq/data .cvsq/tmp .cvsq/config .cvsq/dirnames
				     .cvsq/todo-pre .cvsq/todo-post .cvsq/description !) {
     -d "$home/$dir" and next;
     mkdir "$home/$dir", 0700
         or die "Unable to create directory `$home/$dir' ($!)\n";
 }

### The main entrypoint

my $force = 0;

GetOptions(
		   'force'      => sub {$force =1},
		   'svn'        => sub {$cvs = "svn"; $CVSdir=".svn"},
		   'noclean|nc' => sub {$cleancommand = undef},
		   'clean=s'    => \$cleancommand
		   );


# After the internal options are removed something that we know should remain...

if (@ARGV != 0) {
	for ($ARGV[0]) {
		(/^ci$/ || 
		 /^commit$/)    && do { @ARGV == 1 or die 
									"Paramters given for a commit; only full ci supported.\n";
								last;};
		/^dir$/         && do { $command = "dir"; shift; 
								@ARGV == 1 or die 
									"adding directories only strictly one at at time!.\n";
								last; };
		/^add$/         && do { $command = "add"; shift; last; };
		/^ambient$/     && do { $command = "ambient"; shift; last; };
		/^ambientpost$/ && do { $command = "ambientpost"; shift; last; };
		
		(/^up$/ || 
		 /^upload$/)      && do { $command = "up"; @ARGV == 1 or die
									"The upload command does not support any parameters.\n";
								last;};
		
		(/^pre$/    ||
		 /^do$/     ||
		 /^immediate$/) && do { $command = "pre"; shift; last;};
		
		/^queue$/       && do { print_queue(); exit 0;};
		/^desc$/        && do { print_desc(); exit 0;};
		/^diff$/        && do { print_diff($ARGV[1]); exit 0;};


		die "cvsq command $_ not supported. Did you mean \"cvsq do $_ ...\"?\n";
	}
}

## Parse the file with the command prefixes

my $prefix ="";

if (-f "$home/.cvsq/config/prefix" && -f "./CVS/Root") {
	open(ROOT, "< ./CVS/Root") or die "Couldn't read CVS/Root ($!)\n";
	my $cvsroot = do {local $/; <ROOT>};
	close(ROOT) or die "Closing CVS/Root failed ($!)\n";

	open(PREFIX, "< $home/.cvsq/config/prefix") or 
		die "failed opening $home/.cvsq/config/prefix ($!).\n";

	while(<PREFIX>) {
		/^(\S+)\s(.*)$/ or next;
		
		if (index($cvsroot,$1) == 0) {
			$prefix = $2;
		}
	}
	
	close(PREFIX) or
		die "failed closing $home/.cvsq/config/prefix ($!).\n";
}


## Sanity check whether we're really in a working directory

($command eq "up") or ($command eq "ambient") or ($command eq "ambientpost") or (-d "./$CVSdir") or $force or 
	die "No directory $CVSdir found. Is this really a $cvs working directory? (Use -force carry out the action anyway)\n";


######################################
### The command PRE is immediately ###
######################################

if ($command eq "pre") {
	my ($tmp,$tmpname) = File::Temp::tempfile('pre-XXXXXX', DIR => "$home/.cvsq/tmp",
											  UNLINK => 1);

	print $tmp "$prefix $cvs"; 
	foreach(@ARGV) {
		print $tmp " \Q$_\E";
	}
	print $tmp "\nexit \$?\n";
	
	system "sh", $tmpname;
	
	exit $?;
	
}

##############################################################
## For an adddir we have to make a fake CVS directory       ##
## this must be done BEFORE allocating a place in the queue ##
##############################################################

if ($command eq "dir") {
	my $dir = $ARGV[0];
	
	(-d $dir) or die "$dir is not a directory\n";

	(-e "$dir/$CVSdir") and die 
		"$dir/$CVSdir exists. Maybe this directory is already (queued) to be added?\n";

	mkdir "$dir/$CVSdir";
}


###################################
### QUEUED COMMANDS FOLLOW HERE ###
###################################

## Find name in the .cvsq queue

opendir my $queue, "$home/.cvsq/todo"
    or die "Unable to open todo directory `$home/.cvsq/todo' ($!)\n";

my $slot = 1;
while (defined( my $spooled = readdir $queue )) {
    $spooled =~ /^\d+$/ or next;
    $spooled >= $slot and $slot = $spooled + 1;
}

closedir $queue;

###############################################################################
###### For uploads slot number 0 severes as lock. This has the advantage ######
###### to also block old cvsq upload scripts.                            ######
###############################################################################

# make sure we have the slot. And block everything from beeing commited
# note that there is a race condition!

# First create a temporary file, with contents being "exit -1"

my ($tmp,$tmpname) = File::Temp::tempfile('fail-XXXXXX', DIR => "$home/.cvsq/tmp");

print $tmp "exit -1\n";
close $tmp or die "Couldn't close tmp file ($!).\n";

# we assume that link is atomic and in deed will fail, if the file already exists

if ($command eq "up") {
	symlink $tmpname, "$home/.cvsq/todo/0"
		or die "Couldn't $home/.cvsq due to a seeming race condition ($!).\n";
} else {
	symlink $tmpname, "$home/.cvsq/todo/$slot"
		or die "Couldn't allocate a new slot due to a seeming race condition ($!).\n";
}


################################################################################
### Now the UPLOAD command which is IMMEDIATE                                ###
################################################################################



if ($command eq "up") {
	print "\n\nStarting pre upload...\n\n";

	opendir my $queue, "$home/.cvsq/todo-pre"
		or die "Unable to open pre todo directory `$home/.cvsq/todo-pre' ($!)\n";

	my @TASKS = readdir $queue;

	close $queue;

	@TASKS = grep(/^[0-9]+$/,@TASKS);

	@TASKS = sort {$a <=> $b} (@TASKS);

	foreach (@TASKS) {
		$_ =~ /^[0-9]+$/ or next;
		$_ == "0" and next;

		print "$_...";
		system "/bin/sh", "$home/.cvsq/todo-pre/$_";

		printf "   [return value %s]\n", $? >> 8;

		$? == 0 or die
			"Call failed. Please fix problems manually!
Exiting. Leaving lock to prevent further unwanted commits. 
Remove $home/.cvsq/todo/0 and $tmpname after fixing problems with the
commit scheduled as $home/.cvsq/todo-pre/$_\n";

		unlink "$home/.cvsq/todo-pre/$_" or die
			"Couldn't unlink $home/.cvsq/todo-pre/$_
Exiting. Leaving lock to prevent double-commits.
Remove $home/.cvsq/todo/0 and $tmpname after removing the above file.\n";

	}


	print "\n\nStarting main upload...\n\n";

	opendir $queue, "$home/.cvsq/todo"
		or die "Unable to open todo directory `$home/.cvsq/todo' ($!)\n";

	@TASKS = readdir $queue;

	close $queue;

	@TASKS = grep(/^[0-9]+$/,@TASKS);

	@TASKS = sort {$a <=> $b} (@TASKS);

	foreach (@TASKS) {
		$_ =~ /^[0-9]+$/ or next;
		$_ == "0" and next;

		print "$_...\n";
		system "/bin/sh", "$home/.cvsq/todo/$_";

		printf "\n[return value %s]\n\n\n", $? >> 8;

		$? == 0 or die
			"Call failed. Please fix problems manually!
Exiting. Leaving lock to prevent further unwanted commits. 
Remove $home/.cvsq/todo/0 and $tmpname after fixing problems with the
commit scheduled as $home/.cvsq/todo/$_\n";

		unlink "$home/.cvsq/todo/$_" or die
			"Couldn't unlink $home/.cvsq/todo/$_
Exiting. Leaving lock to prevent double-commits.
Remove $home/.cvsq/todo/0 and $tmpname after removing the above file.\n";

	}


	print "\n\nStarting post upload...\n\n";

	opendir $queue, "$home/.cvsq/todo-post"
		or die "Unable to open pre todo directory `$home/.cvsq/todo-post' ($!)\n";

	@TASKS = readdir $queue;

	close $queue;

	@TASKS = grep(/^[0-9]+$/,@TASKS);

	@TASKS = sort {$a <=> $b} (@TASKS);

	foreach (@TASKS) {
		$_ =~ /^[0-9]+$/ or next;
		$_ == "0" and next;

		print "$_...";
		system "/bin/sh", "$home/.cvsq/todo-post/$_";

		printf "   [return value %s]\n", $? >> 8;

		$? == 0 or die
			"Call failed. Please fix problems manually!
Exiting. Leaving lock to prevent further unwanted commits. 
Remove $home/.cvsq/todo/0 and $tmpname after fixing problems with the
commit scheduled as $home/.cvsq/todo-post/$_\n";

		unlink "$home/.cvsq/todo-post/$_" or die
			"Couldn't unlink $home/.cvsq/todo-pre/$_
Exiting. Leaving lock to prevent double-commits.
Remove $home/.cvsq/todo/0 and $tmpname after removing the above file.\n";

	}

	
	print "\n\nUpload finished.\n";

	unlink "$home/.cvsq/todo/0"
		or die "Couldn't remove lock ($!).\n";

	unlink $tmpname
		or print "Warning: couldn't remove temporary file $tmpname ($!).\n";

	exit 0;

}



###########################################################################
## For a commit we actually have to copy files and ask for a log message ##
###########################################################################

my $message;

if ($command eq "ci") {
    # make a safe copy of the data

	system "cp", "-r", "-p", $pwd, "$home/.cvsq/data/$slot"
		and die "Copying failed ($!).\n";

    # honor the CVSQ_MAKE_CLEAN option

	defined $cleancommand and system("cd \Q$home/.cvsq/data/$slot\E; $cleancommand")
		and print "Warning, the cleaning failed.\n";

    # Replace all subdirs by symbolic links to the original CVS subdir


	my @cvsDirs = `cd \Q$home/.cvsq/data/$slot\E; find . -name $CVSdir`;


	foreach(@cvsDirs) {
		chomp;
		s/^\.\///;
		
		system "rm", "-rf", "$home/.cvsq/data/$slot/$_"
			and die "Couldn't remove $home/.cvsq/data/$slot/$_ ($!)\n";
		system "ln", "-s", "$pwd/$_", "$home/.cvsq/data/$slot/$_"
			and die "Couldn't link $pwd/$_ to $home/.cvsq/data/$slot/$_ ($!)\n";
	}

	add_hardlinks();

	open(DESC,"> $home/.cvsq/description/$slot")
			or die "Couldn't open $home/.cvsq/description/$slot ($!)\n";
	print DESC "ci: $pwd";
	close(DESC) or die "Closing $home/.cvsq/description/$_ failed ($!)\n";

    # Ask for a log message. First find out, what the corresponding editor is

	my $editor = $ENV{'CVSQEDITOR'} || $ENV{'CVSEDITOR'} 
	|| $ENV{'VISUAL'} || $ENV{'EDITOR'} || '/bin/ed';

	open (TMPFILE, "> $home/.cvsq/tmp/$slot.txt")
		or die "Couldn't generate temporary file ($!).\n";

	print TMPFILE <<"EOI";
cvsq: ------------------------------------------------------------	
cvsq: Please enter log message for local commit of
cvsq: directory $pwd
cvsq: 
cvsq: Lines starting with cvsq: are removed automatically
cvsq: ------------------------------------------------------------	
EOI

    close(TMPFILE) or die "Closing tmp-file failed ($!)\n";


	system $editor, "$home/.cvsq/tmp/$slot.txt"
		and print "Warning: editor session failed ($?)\n";

	open(TMPFILE, "< $home/.cvsq/tmp/$slot.txt") or die "Couldn't read log message ($!)\n";
	$message = do {local $/; <TMPFILE>};
	close(TMPFILE) or die "Closing tmp-file failed ($!)\n";

	$message =~ s/(^|\n)cvsq:.*//g;
	$message .=  "\n\n[commit scheduled on $host via $0 at $date]\n";
	$message =~ s/\'/\'\\\'\'/g;
}

###########################################################
## Most other commands just register their description... #
###########################################################

## add ##
if ($command eq "add") {
	open(DESC,"> $home/.cvsq/description/$slot")
		or die "Couldn't open $home/.cvsq/description/$slot ($!)\n";
	print DESC "add ($pwd): @ARGV";
	close(DESC) or die "Closing $home/.cvsq/description/$_ failed ($!)\n";
}

## ambient ##
if ($command eq "ambient") {
	open(DESC,"> $home/.cvsq/description/$slot")
		or die "Couldn't open $home/.cvsq/description/$slot ($!)\n";
	print DESC "ambient ($pwd): @ARGV";
	close(DESC) or die "Closing $home/.cvsq/description/$_ failed ($!)\n";
}

## ambientpost ##
if ($command eq "ambientpost") {
	open(DESC,"> $home/.cvsq/description/$slot")
		or die "Couldn't open $home/.cvsq/description/$slot ($!)\n";
	print DESC "ambientpost ($pwd): @ARGV";
	close(DESC) or die "Closing $home/.cvsq/description/$_ failed ($!)\n";
}

## dir ##
if ($command eq "dir") {
	open(DESC,"> $home/.cvsq/description/$slot")
		or die "Couldn't open $home/.cvsq/description/$slot ($!)\n";
	print DESC "dir ($pwd): $ARGV[0]";
	close(DESC) or die "Closing $home/.cvsq/description/$_ failed ($!)\n";
}

######################################
## generate pre commmit Skript file ##
######################################

# Write the at-upload script. The file conventions of cvq say, it has to be
# a script that can be executed via /bin/sh

# For the case that we die during it's generation, we first put it at some (well defined)
# temporary place.

if ($command eq "ci") {
	symlink $tmpname, "$home/.cvsq/todo-pre/$slot"
		or die "Couldn't allocate a slot for pre commit ($!).\n";

	open(ATUPLOAD, "> $home/.cvsq/tmp/$slot-pre-script") 
		or die "Could not generate script file ($!)\n";


print ATUPLOAD <<"EOI";
CVSQ_ACTION="hardlink" $0 hardlink \Q$pwd\E \Q$home/.cvsq/data/$slot\E $slot 
exit \$?
EOI

    close(ATUPLOAD) or die "Could not close script file ($!)\n";
# By a rename put the actual script over the place where we have our current "exit -1";

	rename "$home/.cvsq/tmp/$slot-pre-script", "$home/.cvsq/todo-pre/$slot"
		or die "Rename failed ($!).\n";
}

#######################################
## generate post commmit Skript file ##
#######################################

# Write the at-upload script. The file conventions of cvq say, it has to be
# a script that can be executed via /bin/sh

# For the case that we die during it's generation, we first put it at some (well defined)
# temporary place.


## ci ##
if ($command eq "ci") {
	symlink $tmpname, "$home/.cvsq/todo-post/$slot"
		or die "Couldn't allocate a slot for post commit ($!).\n";

	open(ATUPLOAD, "> $home/.cvsq/tmp/$slot-post-script") 
		or die "Could not generate script file ($!)\n";


print ATUPLOAD <<"EOI";
rm -rf \Q$home/.cvsq/data/$slot\E && rm -rf \Q$home/.cvsq/dirnames/$slot\E && rm -rf \Q$home/.cvsq/description/$slot\E 
exit \$?
EOI

    close(ATUPLOAD) or die "Could not close script file ($!)\n";
# By a rename put the actual script over the place where we have our current "exit -1";

	rename "$home/.cvsq/tmp/$slot-post-script", "$home/.cvsq/todo-post/$slot"
		or die "Rename failed ($!).\n";
}

### ambientpost actually have to do some work in the post commit phase

if ($command eq "ambientpost") {
	symlink $tmpname, "$home/.cvsq/todo-post/$slot"
		or die "Couldn't allocate a slot for post commit ($!).\n";

	open(ATUPLOAD, "> $home/.cvsq/tmp/$slot-post-script") 
		or die "Could not generate script file ($!)\n";

	print ATUPLOAD "cd \Q$pwd\E\n";
	foreach(@ARGV) {
		my $quotedarg = $_;
		$quotedarg =~ s/\'/\'\\\'\'/g;
		print ATUPLOAD " '$quotedarg'";
	}
	print ATUPLOAD " && rm -rf \Q$home/.cvsq/description/$slot\E\n";
	print ATUPLOAD "exit \$?";
		
    close(ATUPLOAD) or die "Could not close script file ($!)\n";
# By a rename put the actual script over the place where we have our current "exit -1";

	rename "$home/.cvsq/tmp/$slot-post-script", "$home/.cvsq/todo-post/$slot"
		or die "Rename failed ($!).\n";
}


### clean up the description files (most commands, except ci)

if (($command eq "ambient") or
	($command eq "add") or
	($command eq "dir")) {
	symlink $tmpname, "$home/.cvsq/todo-post/$slot"
		or die "Couldn't allocate a slot for post commit ($!).\n";

	open(ATUPLOAD, "> $home/.cvsq/tmp/$slot-post-script") 
		or die "Could not generate script file ($!)\n";

print ATUPLOAD <<"EOI";
rm -rf \Q$home/.cvsq/description/$slot\E 
exit \$?
EOI

    close(ATUPLOAD) or die "Could not close script file ($!)\n";
# By a rename put the actual script over the place where we have our current "exit -1";

	rename "$home/.cvsq/tmp/$slot-post-script", "$home/.cvsq/todo-post/$slot"
		or die "Rename failed ($!).\n";
}



##########################
## generate Skript file ##
##########################

# Write the at-upload script. The file conventions of cvq say, it has to be
# a script that can be executed via /bin/sh
	
# For the case that we die during it's generation, we first put it at some (well defined)
# temporary place.

open(ATUPLOAD, "> $home/.cvsq/tmp/$slot-script") or die "Could not generate script file ($!)\n";

### ci ###
if ($command eq "ci") {
print ATUPLOAD <<"EOI";
cd \Q$home/.cvsq/data/$slot\E
test -f \Q$home/.cvsq/todo/$slot-hardlink\E && /bin/sh \Q$home/.cvsq/todo/$slot-hardlink\E && rm -f \Q$home/.cvsq/todo/$slot-hardlink\E
$prefix $cvs ci -m '$message' && /bin/sh \Q$home/.cvsq/todo/$slot-late-hardlink\E && rm -f \Q$home/.cvsq/todo/$slot-late-hardlink\E 
exit \$?
EOI

unlink "$home/.cvsq/tmp/$slot.txt" or die "Unable to remove message file ($!).\n";
}

## add ##
if ($command eq "add") {
	print ATUPLOAD "cd \Q$pwd\E\n";
	print ATUPLOAD "$prefix $cvs add";
	foreach(@ARGV) {
		print ATUPLOAD " \Q$_\E";
	}
	print ATUPLOAD "\nexit \$?\n";
}

## dir ##
if ($command eq "dir") {
	print ATUPLOAD "cd \Q$pwd\E\n";
	print ATUPLOAD "rm -rf \Q$pwd/$ARGV[0]/$CVSdir\E\n";
	print ATUPLOAD "$prefix $cvs add \Q$ARGV[0]\E\n";
	print ATUPLOAD "\nexit \$?\n";
}


## ambient ##
if ($command eq "ambient") {
	print ATUPLOAD "cd \Q$pwd\E\n";
	foreach(@ARGV) {
		my $quotedarg = $_;
		$quotedarg =~ s/\'/\'\\\'\'/g;
		print ATUPLOAD " '$quotedarg'";
	}
	print ATUPLOAD "\nexit \$?\n";
}

## ambientpost ##
if ($command eq "ambientpost") {
	print ATUPLOAD "exit 0\n";
}


close(ATUPLOAD) or die "Could not close script file ($!)\n";


#########################
## activate the script ##
#########################


# By a rename put the actual script over the place where we have our current "exit -1";

rename "$home/.cvsq/tmp/$slot-script", "$home/.cvsq/todo/$slot"
	or die "Rename failed ($!).\n";

#

unlink $tmpname
	or print "Warning: couldn't clean up the lock skript $tmpname ($!).\n";

##################################################################################
## Write some happy welcome messages to the user, reminding what has happend... ##
##################################################################################

## ci ##
if ($command eq "ci") {
print <<"EOI";

Locally committed the current subdirectory. It is scheduled as slot number $slot.
Use "$0 upload" to enter the changes into the real cvs repository.
A safe copy can be found at $home/.cvsq/data/$slot.
EOI
}

## add ##
if ($command eq "add") {
print <<"EOI";

Scheduled the addition of the following files as slot number $slot.
@ARGV
Use "$0 ci" to also schedule the commit.
EOI
}

## dir ##
if ($command eq "dir") {
print <<"EOI";

Scheduled the addition directory $ARGV[0] as slot number $slot.
Use "$0 ci" to also schedule the commit!
EOI
}

## ambient ##
if ($command eq "ambient") {
print <<"EOI";

Scheduled ambient command '@ARGV' as slot number $slot.
This command will be executed in directory $pwd at the time of upload.
EOI
}


## ambientpost ##
if ($command eq "ambientpost") {
print <<"EOI";

Scheduled ambient post command '@ARGV' as slot number $slot.
This command will be executed in directory $pwd at the time of the post upload.
EOI
}


###############################################################################################
############### Subroutines ###################################################################
###############################################################################################


sub say_hardlink
{
	
	my $source = shift;
	my $target = shift;
	my $hardslot = shift;

	$source eq $target and die "cannot hardlink $source into it self.\n";

	my @files = `cd $source; find . -print`;

	open(ATHARDLINK,">> $home/.cvsq/todo/$hardslot-hardlink")
		or die "Couldn't open hardlink file $home/.cvsq/todo/$slot-hardlink ($!)\n";

	foreach(@files) {
		chomp;
		/\/CVS\// and next;
		/\/\.svn\// and next;

		s/^\.\///;

		
		-f "$source/$_" && (! -l "$source/$_") or next;
		-f "$target/$_" && (! -l "$target/$_") or next;
		
		system "cmp", "-s", "$source/$_", "$target/$_";
		
		($? >> 8) <= 1 or die "Call to cmp failded ($?).\n";
		
		if (($? >> 8) == 0) {
			print ATHARDLINK "rm -f \Q$source/$_\E\n";
			print ATHARDLINK "cp -p \Q$target/$_\E \Q$source/$_\E\n";
		}
	}
	close(ATHARDLINK) or die "Couldn't close hardlink file $hardslot";
}

sub add_hardlinks
{
	opendir my $dirnames, "$home/.cvsq/dirnames"
		or die "Unable to open directory `$home/.cvsq/dirnames' ($!)\n";

	my @DIRS = readdir $dirnames;
	
	close $dirnames;

	@DIRS = grep(/^[0-9]+$/,@DIRS);
	@DIRS = sort {$b <=> $a} (@DIRS);

	my $name;

	foreach(@DIRS) {

		open(NAME, "< $home/.cvsq/dirnames/$_") 
			or die "Couldn't read $home/.cvsq/dirnames/$_ ($!)\n";
		$name = do {local $/; <NAME>};
		close(NAME) or die "Closing $home/.cvsq/dirnames/$_ failed ($!)\n";

		if ($name eq $pwd) {
			say_hardlink("$home/.cvsq/data/$slot","$home/.cvsq/data/$_",$slot);
			last;
		}
	}

	open(NAME,"> $home/.cvsq/dirnames/$slot")
			or die "Couldn't open $home/.cvsq/dirnames/$slot ($!)\n";
	print NAME $pwd;
	close(NAME) or die "Closing $home/.cvsq/dirnames/$_ failed ($!)\n";
}
	


sub print_queue
{
	opendir my $dirnames, "$home/.cvsq/dirnames"
		or die "Unable to open directory `$home/.cvsq/dirnames' ($!)\n";

	my @DIRS = readdir $dirnames;
	
	close $dirnames;

	@DIRS = grep(/^[0-9]+$/,@DIRS);
	@DIRS = sort {$a <=> $b} (@DIRS);

	my $name;

	print " [nr] Scheduled dir:\n";
	foreach(@DIRS) {
		printf "%4s  %s\n", $_  , `cat $home/.cvsq/dirnames/$_`;
	}
	print "[end]\n\n";
}

sub print_desc
{
	opendir my $desc, "$home/.cvsq/description"
		or die "Unable to open directory `$home/.cvsq/description' ($!)\n";

	my @DESCS = readdir $desc;
	
	close $desc;

	@DESCS = grep(/^[0-9]+$/,@DESCS);
	@DESCS = sort {$a <=> $b} (@DESCS);

	my $name;

	print " [nr] description:\n";
	foreach(@DESCS) {
		printf "%4s  %s\n", $_  , `cat $home/.cvsq/description/$_`;
	}
	print "[end]\n\n";
}


sub print_diff
{
	my $filename = shift;
	defined($filename) or $filename=".";

	opendir my $dirnames, "$home/.cvsq/dirnames"
		or die "Unable to open directory `$home/.cvsq/dirnames' ($!)\n";

	my @DIRS = readdir $dirnames;
	
	close $dirnames;

	@DIRS = grep(/^[0-9]+$/,@DIRS);
	@DIRS = sort {$b <=> $a} (@DIRS);

	foreach(@DIRS) {

		open(NAME, "< $home/.cvsq/dirnames/$_") 
			or die "Couldn't read $home/.cvsq/dirnames/$_ ($!)\n";
		my $name = do {local $/; <NAME>};
		close(NAME) or die "Closing $home/.cvsq/dirnames/$_ failed ($!)\n";

		if ($name eq $pwd) {
			printf "diff -r %s %s\n", "$home/.cvsq/data/$_/$filename", $filename;
			system "diff", "-r", "$home/.cvsq/data/$_/$filename", $filename;
			printf "[return value %d]\n", $? >> 8;
			last;
		}
	}
}
