package Emdebian::Grip;
use Carp;
use strict;
use warnings;
use IO::File;
use Exporter;
use POSIX qw(locale_h);
use Locale::gettext;
use File::Copy;
use File::Basename;
use Parse::Debian::Packages;
use Debian::Packages::Compare;

use vars qw/ %debianunstable %debiantesting %gripunstable %griptesting
 %tdebunstable %tdebtesting $base @archlist $packages $suite @locroots
 $our_version $mirror $noskip @bin @cmd $update $dryrun $noskip
 $mode $filter_name $grip_name @ISA @EXPORT $base /;

@ISA=qw(Exporter);
@EXPORT=qw( check_dirs clean_incoming cleanup convert_prefix edos
 extend_filter grip_binary grip_source incoming_locale print_missing
 setup_repos update_filter set_repo_names set_noskip grip_britney
 print_testing_status update_repo set_dry_run migrate_missing
 check_syscall print_build_deps scripts_version _g 
 switch_component );

=pod

=head1 NAME

Emdebian::Grip - internal Emdebian module for Emdebian Grip repositories

=head1 Description

It is worthwhile being familiar with C<reprepro> (1) when working with
this module.

Emdebian::Grip provides repository management support for the
F<em_autogrip> scripting to manage a three-way repository hierarchy for
Emdebian Grip, including support for migrating packages from unstable
into testing.

Considering just C<unstable> initially, the hierarchy is as follows:

Debian unstable is filtered using F<${base}${filter_name}/conf/pkglist>
which in turn is based on the output of dpkg --get-selections. Only
packages that are actually installed on machines running Emdebian Grip
get added to the pkglist. C<reprepro> then updates F<${filter_name}>
from the chosen Debian mirror, downloading only the binary and source
packages specified in pkglist, to create a partial local mirror for the
list of supported architectures.

Grip unstable is built from the filtered mirror by a combined process of
converting the source package and then identifying the missing binary
packages from the pkglist. During the C<emgrip> process, Emdebian TDebs
are created which get included into the locale repository:

Locale is a very specialised repository that is designed for Debian
source packages and Emdebian TDebs only, organised by locale root.
e.g. en_GB support is in the F<en> component. Whereas Debian has
three components (F<main>, F<contrib> and F<non-free>), the B<locale
repository has ninety seven components> (including main for the source
packages). In the locale pool, packages are organised by component so
whereas in the filter or grip repository, C<apt> packages are under
F<pool/main/a/apt> in the locale repository the F<en> TDebs for apt are
in F<pool/en/a/apt/> and the French under F<pool/fr/a/apt/> etc. The
list of locale roots supported by the locale repository is returned by
F<&get_locale_roots>, part of the C<Debian::Packages::Compare> module.
For more information on locale roots as components see:
L<http://www.emdebian.org/emdebian/langupdate.html>

As far as testing is concerned, the hierarchy works in a similar way
to Debian, but with extra support for repositories that are started
after packages have migrated into Debian testing and a newer version
exists in Debian unstable.

Note that the testing repository data needs to be setup manually
in many cases - editing conf/distributions for each repository. The
current Emdebian conf files will find their way onto the Debian Wiki
in due course.

With a fully populated Debian unstable, Grip unstable and locale
unstable, F<&grip_britney> can then migrate packages into Grip testing
using the criteria implemented in F<&get_britney_list> from
C<Debian::Packages::Compare> and the C<reprepro copysrc> command. As
most, if not all, Grip repositories will begin at a time when some
packages in Debian have already migrated into testing and had a newer
version uploaded to unstable, C<Emdebian::Grip> also includes
I<catch-up> support via F<&migrate_missing> which includes the relevant
packages from Debian testing as if that version had been uploaded
directly to Grip testing (which, in effect, is what is happening).

The I<emgrip> process can take B<a significant amount of time> - think
many hours, not minutes - principally due to the overhead of
generating the TDebs at one per source per locale per architecture.
To create a full unstable and testing set is likely to take a day or
more but is mostly automated.

The problems that remain are related to dependency issues. The F<&edos>
function in C<Emdebian::Grip> does have problems trying to calculate the
solution for dependency problems identified by C<edos-debcheck> and the
problems become particularly acute if the repository itself is not
in good shape at the start. Therefore, F<&edos> is not run automatically
by the current tools. The repository must be fully up to date and
all relevant packages need to exist in the filter and in Grip before
using F<&edos> support. Initially, use C<edos-debcheck> directly to
identify major problems:

 edos-debcheck -explain -failures < ${base}${grip_name}/dists/sid/main/binary-i386/Packages

Packages that are I<NOT AVAILABLE> need to be gripped as source or
binary packages as appropriate but care is needed to correctly interpret
the C<edos-debcheck> output to identify the correct link in the chain.

Two major problems exist with automating this process, see
C<em_autogrip> (1) for specific information, but suffice it to say here
that there exist in Debian unstable, packages that will appear in the
list of I<NOT AVAILABLE> packages where the correct action is to
B<REMOVE> the package that depends on the unavailable package. These
issues are being discussed within Debian.

=head1 Copyright and Licence

 Copyright (C) 2007-2009  Neil Williams <codehelp@debian.org>

 This package is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 3 of the License, or
 (at your option) any later version.

 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, see <http://www.gnu.org/licenses/>.

=cut

=head1 Example

 use strict;
 use warnings;
 use Emdebian::Grip;
 use Debian::Packages::Compare;

 use vars qw/ $filter_name $grip_name $suite $base $verbose
  $noskip @archlist @locroots @lines $line %pkg @filter $have
  %debianunstable %gripunstable %tdebunstable /;

 my $mirror='http://ftp.uk.debian.org/debian'; # default
 $filter_name = 'filter';
 $grip_name = 'grip';
 $suite = "unstable"; # at first
 $base = '/opt/reprepro/';
 $verbose = 0;

 &set_base($base);
 &set_repo_names ($filter_name, $grip_name);
 my $a = &get_archlist ($suite, $filter_name);
 @archlist = (not defined $a or not @$a) ?
   qw/i386 amd64 arm armel powerpc mips mipsel/ : @$a;
 my $l = &get_locale_roots ($suite, 'locale');
 @locroots = (not defined $l or not @$l) ? qw/ af am ang ar as ast az be bg
  bn br bs ca cs cy da de dz el en eo es et eu fa fi fr ga gl gu he hi hr
  hu hy ia id io is it ja ka kn km ko ku ky lg li lt lv mai mg mi mk ml mn mr
  ms nb ne nl nn no ns nso oc or pa pl ps pt rm ro ru rw si sk sl sq sr sv
  ta te th tk tl tr tt ug uk ur uz vi wa wo xh yi zh zu / : @$l;

 &setup_repos if ( not -f "${base}${filter_name}/conf/pkglist" );

 my $debu  = &read_packages ('unstable', $filter_name);
 my $gripu = &read_packages ('unstable', $grip_name);
 my $tdebu = &read_locale   ('unstable', 'locale');
 %debianunstable = %$debu   if (defined $debu);
 %gripunstable   = %$gripu  if (defined $gripu);
 %tdebunstable   = %$tdebu  if (defined $tdebu);
 &update_filter;
 &update_repo($verbose);

 # begin the work.

=cut

=head1 set_repo_names

Copies the default or user-specified filter and grip repository
names into the internal module.

=cut

sub set_repo_names {
	($filter_name, $grip_name) = @_;
}

=head1 set_noskip

Copies the user-specified --noskipold option into the internal
module for use when calling reprepro.

=cut

sub set_noskip {
	$noskip = shift;
}

=head1 set_dry_run

Configures a dry-run where external commands are printed instead
of executed.

=cut

sub set_dry_run {
	$dryrun = 1;
}

=head1 print_missing

Outputs the list returned by
C<Debian::Package::Compare::get_missing_sources> in a simple format

=cut

sub print_missing {
	my ($c, $s, $total);
	my @lines=();
	return undef unless (defined $filter_name and defined $grip_name);
	my $missing = &get_missing_sources('unstable', "$filter_name", "$grip_name");
	if (defined %$missing) {
		$c = scalar (keys %$missing);
		$s = sprintf (ngettext("source", "sources", $c));
		# Translators: INF is an abbreviation for 'information' for the user.
		printf(_g("INF: %d %s in the filter repository but not in Grip:\n"), $c, $s);
		$c = 0;
		foreach my $pkg (sort keys %$missing) {
			$c++;
			push @lines, "$c: $pkg\n\t(". $$missing{$pkg}{'source'}.")\n";
		}
		print join ("\n", @lines)."\n";
	}
	$total = $c;
	undef ($missing);
	$missing = &get_missing_binaries('unstable', "$filter_name", "$grip_name");
	return unless defined (%$missing);
	@lines=();
	$c = scalar (keys %$missing);
	$s = sprintf (ngettext("binary", "binaries", $c));
	printf(_g("INF: %d %s in the filter repository but not in Grip:\n"), $c, $s);
	$c = $total;
	foreach my $pkg (sort keys %$missing) {
		my @alines=();
		$c++;
		my $line = "$c: $pkg\n\t";
		foreach my $a (sort keys %{$$missing{$pkg}}) {
			push @alines, "(". $$missing{$pkg}{$a}{"$filter_name"}.") ".'{'.$a.'}'."\n";
		}
		$line .= join ("\t", @alines);
		push @lines, $line;
	}
	print join ("\n", @lines)."\n";
}

=head1 print_build_deps

Outputs the list returned by
C<Debian::Package::Compare::get_missing_builddeps> in a simple format

=cut

sub print_build_deps {
	$base = &get_base;
	my $host = `LC_ALL=C dpkg-architecture -qDEB_BUILD_ARCH_CPU`;
	chomp ($host);
	my ($c, $s, $total);
	my @lines=();
	return undef unless (defined $filter_name and defined $grip_name);
	my $missing = &get_missing_builddeps('unstable', "$filter_name", "$grip_name");
	if (defined %$missing) {
		foreach my $p (sort keys %$missing) {
			my @k = split (/ /, $p);
			my $pkg = $k[0];
			next if (not defined $pkg);
			my $res = `LC_ALL=C apt-cache show $pkg 2>/dev/null`;
			chomp ($res);
			next if ($res ne "");
			my $cache = `LC_ALL=C apt-cache showpkg $pkg 2>/dev/null`;
			$cache =~ s/\n/ /g;
			if ($cache !~ /Reverse Provides:/) {
				printf(_g("INF: Unable to check '%s'. It may be an virtual ".
				"alternative package or might not be available on '%s'\n"),
					$pkg, $host);
				next;
			}
			delete $$missing{$p};
			$cache =~ /Reverse Provides: (.*)/;
			my $match = $1;
			$match =~ s/^ +//;
			$match =~ s/ +$//;
			my @pkgver = split (" ", $match);
			my %pkgs = ();
			foreach my $block (@pkgver) {
				next if ($block =~ /^[0-9]/);
				next if ($block =~ /^$/);
				$pkgs{$block}++;
			}
			my $rpkg = join (" | ", sort keys %pkgs);
			next if ($rpkg eq "");
			my @alternatives = ();
			if (scalar keys %pkgs > 1) {
				@alternatives = sort keys %pkgs;
				$rpkg = $alternatives[0];
				my $end = scalar @alternatives;
				if (scalar @alternatives > 2) {
					$end--;
					print "INF: Alternatives for $rpkg: ";
					print join (" | ", @alternatives[1..$end])."\n";
				}
				if (scalar @alternatives == 2) {
					$end--;
					print "INF: Alternative for $rpkg: ";
					print join (" ", $alternatives[$end])."\n";
				}
			}
			my $check = `reprepro -b ${base}grip list sid $rpkg`;
			chomp $check;
			$check =~ s/.*: //g;
			if ($check ne "") {
				printf(_g("INF: '%s' Provides '%s' and already exists ".
					"in the Grip repository, skipping '%s'.\n"),
					$rpkg, $pkg, $rpkg);
				next;
			}
			printf(_g("INF: Adding '%s' to Provide: '%s'.\n"), $rpkg, $pkg);
			$$missing{$rpkg} = \@alternatives;
		}
		$c = scalar (keys %$missing);
		$s = sprintf (ngettext("dependency", "dependencies", $c));
		printf(_g("INF: %s build %s not yet in Grip:\n"), $c, $s);
		$c = 0;
		print join (" ", sort keys %$missing)."\n";
	}
	printf (_g("INF: Done.\n"));
}

=head1 print_testing_status

Provides a detailed output of the status of the testing repository for
Grip, comparing Debian unstable (filter) against Debian testing (filter)
and identifying which source packages in Emdebian Grip testing are
behind Debian.

This list is further split into those where the source version in Debian
unstable is precisely the same as the source version in Debian testing
(packages that need to be migrated into Grip testing) and those where
the version in testing differs.

=cut

sub print_testing_status {
	my ($c, $plural);
	return undef unless (defined $filter_name and defined $grip_name);
	my $origu = &read_packages ('unstable', $filter_name);
	my $origt = &read_packages ('testing', $filter_name);
	my $britney = &get_britney_list("$filter_name", "$grip_name");
	$c = scalar (keys %$britney);
	if ($c > 0) {
		$plural = sprintf (ngettext("source package needs to be migrated",
		"source packages need to be migrated", $c));
		printf(_g("INF: %s %s into testing.\n"), $c, $plural);
		foreach my $brit (sort keys %$britney) {
			my $src = $$origu{$brit}{'Src'};
			if ($src eq $brit) {
				printf (_g("INF: Source: %s Binary: %s\n"), $src, $brit);
			} else {
				printf( _g("INF: Source: %s\n"), $src);
			}
			next if (not defined $$origu{$src}{'source'});
			print "\tUnstable: ".$$origu{$src}{'source'}."\n";
			print "\tTesting : ";
			print $$origt{$src}{'source'} 
				if (defined $$origt{$src}{'source'});
			print "\n\tFilterU : ".$$origu{$src}{'source'}."\n";
			print "\tFilterT : ".$$origt{$src}{'source'}."\n";
			print "\n\n";
		}
	}
}

=head1 grip_britney

Grip version of the Britney script that migrates packages into
Debian testing. See C<Debian::Packages::Compare> (3) for details
on F<get_britney_list> criteria.

In addition, in order to allow new repositories to catch up with
Debian, packages in Grip unstable that are already ahead of Debian
testing need to be gripped directly from Debian testing.

This can also happen when packages are put directly into testing
using testing-proposed-updates during a release freeze in Debian.

Note that grip_britney only migrates packages where the version in
Debian unstable is the same as the version in Debian testing - i.e.
where a migration has already taken place in Debian. Packages that
arrive in testing via testing-proposed-updates or migrated into
testing before the Grip repository was started and which have a
later version now in unstable, can be migrated using migrate_missing.

If you are running F<grip_britney> and F<migrate_missing> together,
B<run grip_britney first and reload all the data> using F<read_packages>
before attempting to run F<migrate_missing>. See F<migrate_missing> for
more information.

=cut

sub grip_britney {
	$base = &get_base;
	return undef unless (-d $base);
	my ($c, $plural, $str, $vers);
	return undef unless (defined $filter_name and defined $grip_name);
	my $britney = &get_britney_list("$filter_name", "$grip_name");
	my $complain = get_britney_complaint();
	$c = scalar (keys %$britney);
	if ($c > 0) {
		$plural = sprintf (ngettext("source package needs to be migrated",
		"source packages need to be migrated", $c));
		printf(_g("INF: %s %s into testing.\n"), $c, $plural);
		print _g("INF: ").join (' ', sort keys %$britney)."\n";
		foreach my $brit (sort keys %$britney) {
			my $mode = 'copysrc';
			$vers = $$britney{$brit}{'source'}{'unstable'};
			# if no source version exists, this probably isn't a source
			# package, so use copy, not copysrc
			if (not defined $vers) {
				$mode = 'copy';
				printf( _g("INF: migrating %s into testing.\n"), $brit);
			} else {
				# Translators: strings are package : version
				printf( _g("INF: migrating %s %s into testing.\n"), $brit, $vers);
			}
			$str = "reprepro -b ${base}${grip_name} $mode testing unstable $brit";
			(defined $dryrun) ? print "$str\n" : system ("$str 2>&1");
			# will fail if $brit is not a source package.
			$str = "reprepro -v -b ${base}locale $mode testing unstable $brit";
			(defined $dryrun) ? print "$str\n" : system ("$str 2>&1");
		}
	}
	return $complain;
}

=head1 migrate_missing

Corollary of grip_britney that completes the process by including into
testing, packages that have a different version in Debian unstable.

Missing packages in testing are included using F<grip_source> and
F<grip_binary> as if uploaded directly to testing (which, in effect, is
what is happening). This means that F<migrate_missing> will take just
as long to process the package in testing as it did to process the
later version that went into unstable. Depending on the number of
packages affected, B<migrate_missing can take a lot longer than
grip_britney> but it should be needed less often once the repository
is complete.

=cut

sub migrate_missing {
	my ($c, $s, $total);
	my @lines=();
	return undef unless (defined $filter_name and defined $grip_name);
	my $missing = &get_missing_sources('testing', "$filter_name", "$grip_name");
	if (defined %$missing) {
		$c = scalar (keys %$missing);
		my $debt  = &read_packages ('testing', $filter_name);
		my $gripu  = &read_packages ('unstable', $grip_name);
		my $gript  = &read_packages ('testing', $grip_name);
		# refresh internal data
		%gripunstable = %$gripu;
		%griptesting = %$gript;
		%debiantesting = %$debt;
		$s = sprintf (ngettext("source", "sources", $c));
		printf( _g("INF: %s %s in the filter repository but not in Grip:\n"), $c, $s);
		$c = 0;
		foreach my $pkg (sort keys %$missing) {
			$c++;
			if (not defined $griptesting{$pkg}{'source'}) {
				# both sides of the dpkg --compare-versions must exist
				# or we get lots of horrible output from dpkg.
				if ((not defined $gripunstable{$pkg}{'source'}) or
					(not defined $debiantesting{$pkg}{'source'})) {
					my $msg = sprintf (_g("INF: Removing %s from filter testing.\n"), $pkg);
					system ("reprepro -b ${base}${filter_name} -v removesrc $pkg");
					carp ($msg);
					next;
				}
				my $retval = system ("dpkg --compare-versions ".
					$debiantesting{$pkg}{'source'}." '>>' ".
					$gripunstable{$pkg}{'source'});
				$retval /= 256;
				if ($retval == 1) {
					my $str = sprintf(_g("INF: %s is newer in Grip unstable (%s)".
						" than in Debian testing (%s)\n".
						"Unable to migrate %s. Need to grip the package in".
						" Debian testing.\n"), $pkg, $gripunstable{$pkg}{'source'},
					$debiantesting{$pkg}{'source'}, $pkg);
					warn ($str);
					my $initial = convert_prefix ($pkg);
					# source only supports main.
					my $dir = "${base}${filter_name}/pool/main/$initial/$pkg/";
					my $ver = $debiantesting{$pkg}{'source'};
					# allow for epochs
					$ver =~ s/^[0-9]://;
					my $dsc = "${pkg}_${ver}.dsc";
					printf( _g("Checking for %s\n"), $dsc);
					my $msg = sprintf(_g("Cannot open filter pool directory for %s\n"), $pkg);
					opendir (DIR, "$dir") or die ($msg);
					my @dsc=grep(/\Q$dsc\E/, readdir (DIR));
					closedir (DIR);
					if ((exists $dsc[0]) and (-f "$dir/$dsc")) {
						grip_source ($pkg, $ver, 'testing', 'source');
						&clean_incoming ($grip_name);
					}
				}
			}
		}
	}
	$total = $c;
	undef ($missing);
	$missing = &get_missing_binaries('testing', "$filter_name", "$grip_name");
	return unless defined (%$missing);
	@lines=();
	$c = scalar (keys %$missing);
	$s = sprintf (ngettext("binary", "binaries", $c));
	printf(_g("INF: %d %s in the filter repository but not in Grip.\n"), $c, $s);
	$c = $total;
	foreach my $pkg (sort keys %$missing) {
		foreach my $a (sort keys %{$$missing{$pkg}}) {
			# first get the source package name.
			my $src= $debianunstable{$pkg}{'Src'};
			# skip omitted packages
			next if (not defined $src);
			chomp ($src);
			$src =~ s/ //g;
			my $ver = $$missing{$pkg}{$a}{"$filter_name"};
			$ver =~ s/^[0-9]://;
			my $initial = convert_prefix ($src);
			# sources only use main
			my $dir = "${base}${filter_name}/pool/main/$initial/$src/";
			grip_binary ($pkg, $ver, 'testing', $a);
		}
	}
}

=head1 edos

Only a basic framework at this stage, this function needs to eventually
collate the results by architecture and repository and try to present
a solution.

See C<em_autogrip> (1) for problems with edos recursion.

In $mode eq 'edos', simply outputs the check data for each
of the filter and grip repositories.

=cut

sub edos {
	$base = get_base();
	my ($mode, $suite) = @_;
	return undef unless (defined $filter_name and defined $grip_name);
	my $a = &get_archlist ($suite, $filter_name);
	@archlist = @$a;
	my %edos=();
	my @checks = ();
	push @checks, $filter_name;
	push @checks, $grip_name;
	foreach my $rep (@checks) {
		print "\n";
		foreach my $arch (@archlist) {
			next if ($arch eq 'source');
			# need to support components other than main.
			my $str="edos-debcheck -explain -failures < ".
				"${base}${rep}/dists/$suite/main/binary-${arch}/Packages";
			my $ret = `$str 2>/dev/null | grep "NOT AVAILABLE" | sort -u`;
			next unless ($ret);
			$ret =~ s/.* depends on //g;
			$ret =~ s:{NOT AVAILABLE}::g;
			$ret =~ s/\n//g;
			$edos{$ret}{$arch}++;
		}
		my $c = scalar (keys %edos);
		if ($c == 0) {
			printf( _g("INF: '%s' appears well trimmed.\n"), $rep);
			exit 0 if ($mode eq 'edos');
			return;
		}
		foreach my $detail (keys %edos) {
			print "\n";
			printf( _g("INF: Missing dependencies in the '%s' repository:\n"), $rep);
			printf( _g("INF: %s\n"), $detail);
			print _g("INF: Affecting: ") if (exists $edos{$detail});
			foreach my $affected ($edos{$detail}) {
				print join(' ', keys %$affected);
			}
			print "\n";
		}
		print "\n";
	}
	print _g("INF: Done.\n");
	print "\n";
#	exit 0 if ($mode eq 'edos');
	my @list=();
	foreach my $pkg (sort keys %edos) {
		$pkg =~ s/\(.*?\)//g;
		$pkg =~ s/\|//g;
		push @list, split (" ", $pkg);
	}
	my %hash=();
	# ensure the list only contains unique names
	my $debu  = &read_packages ('unstable', $filter_name);
	foreach my $l (@list) {
		# alternatives can sometimes cause duplicates to appear.
		next if (exists $$debu{$l});
		# special-case lsb because it brings in all of Qt
		next if ($l eq 'lsb');
		$hash{$l}++;
	}
	$noskip='--noskipold' if (keys %hash);
	if (not defined $dryrun) {
		&extend_filter(join(" ", sort keys %hash));
		&update_repo ();
	} else {
		print _g("INF: Need to add: ").join(" ", sort keys %hash)."\n";
		foreach my $pkg (sort keys %hash) {
			my $check = `reprepro -b ${base}filter list sid $pkg`;
			# Translators: first is the package name, second is 
			# output data from reprepro.
			printf( _g("ERR: '%s' exists in filter:\n%s\n"), $pkg, $check)
				if ($check ne "");
		}
	}
	exit 0 if ($mode eq 'edos');
	# add packages to the list in batches.
	&edos if (keys %hash);
}

=head1 clean_incoming

Call intermittently during long runs.

=cut

sub clean_incoming {
	$base = &get_base;
	return undef unless (defined $grip_name);
	print _g("INF: cleaning grip incoming directory\n");
	# Translators: timestamp.
	print _g("INF: Stamp: ") . `date`;
	# stage removals in case the argument list gets too long.
	# probably do an opendir readdir instead.
	unlink <${base}${grip_name}/incoming/*.changes>;
	unlink <${base}${grip_name}/incoming/*.deb>;
	unlink <${base}${grip_name}/incoming/*.tdeb>;
	unlink <${base}${grip_name}/incoming/*.dsc>;
	unlink <${base}${grip_name}/incoming/*>;
	system "rm -rf ${base}${grip_name}/incoming/*";
}

=head1 cleanup

Only call before exit.

=cut

sub cleanup {
	&clean_incoming;
	&update_filter;
}

=head1 update_filter

Fill in the FilterList so that automatic updates can proceed -
without a filter, the entire archive is added. However,
updating takes an appreciable amount of time, so
call only when necessary.

=cut

sub update_filter {
	return undef unless (defined $filter_name and defined $grip_name);
	$base = &get_base;
	my $debu  = &read_packages ('unstable', $filter_name);
	# output the pkglist from the arrays.
	my $file = `mktemp -t "autogrip.XXXXXX"`;
	chomp ($file);
	open (PKGLIST, ">$file") or croak ("Unable to write pkglist");
	foreach my $pkg (sort keys %$debu) {
		print PKGLIST "$pkg\t\t\t\tinstall\n";
	}
	close (PKGLIST);
	# rename won't work on ant because of filesystem boundaries.
	system ("mv $file ${base}${filter_name}/conf/pkglist");
	chmod (0644, "${base}${filter_name}/conf/pkglist");
}

=head1 extend_filter

Adds package(s) to the filter list, a space separated list is
acceptable.

=cut

sub extend_filter {
	$base = get_base;
	return undef unless (defined $filter_name and defined $grip_name);
	my $name = shift;
	my @a = split(" ", $name);
	printf( _g("INF: Adding %s\n"), $name);
	# add the package to the pkglist from the arrays.
	# names that don't exist will simply be ignored
	my $file = `mktemp -t "autogrip.XXXXXX"`;
	chomp ($file);
	open (PKGLIST, ">$file") or croak (_g("Unable to extend pkglist."));
	my $debu  = &read_packages ('unstable', $filter_name);
	my @list = keys %$debu;
	push @list, @a;
	my %uniq=();
	foreach my $u (@list) {
		$uniq{$u}++;
	}
	@list = sort keys %uniq;
	foreach my $pkg (sort @list) {
		print PKGLIST "$pkg\t\t\t\tinstall\n";
	}
	close (PKGLIST);
	# rename won't work on ant because of filesystem boundaries.
	system ("mv $file ${base}${filter_name}/conf/pkglist");
}

=head1 update_repo

Once the pkglist filter file is correct, call reprepro
to get updated packages.

=cut

sub update_repo {
	$base = get_base;
	my ($c, $v);
	my $verbose = shift;
	$verbose = (defined $verbose) ? $verbose : 0;
	$verbose = ($verbose > 0) ? $verbose : 0;
	$v='';
	for ($c = 0; $c <= $verbose; $c++) {
		$v .= " -v";
	}
	print _g("INF: Updating filter repository\n");
	if ((not defined $noskip) or ($noskip ne "--noskipold")) { $noskip = '' ; }
	else { printf( _g("INF: %s option is set.\n"), $noskip); }
	if (not defined $dryrun) {
		my $retval = system ("reprepro $v -b ${base}${filter_name} $noskip update 2>&1");
		print "INF: Done\n";
		# in case of error, reinitialise the pkglist file.
		unlink "${base}${filter_name}/conf/pkglist" if ($retval);
	} else {
		print _g("INF: Dry run - no update done.\n");
	}
	&update_filter();
}

=head1 incoming_locale

Checks the incoming directory for Emdebian TDebs and includes them
into reprepro.

Until the .tdeb extension is supported, reprepro needs to be persuaded
to allow such files using the C<--ignore=extension> option.

Amongst all the expected I<error> messages, some useful output is
sent to C<STDERR> by reprepro when handling TDebs so errors are
not redirected to F</dev/null>.

=cut

sub incoming_locale {
	$base = get_base;
	return undef unless (defined $grip_name);
	my ($arch, $package, $version, $suite) = @_;
	my ($str, $suffix, $cmpnt, @files, $t);
	my $l = &get_locale_roots ($suite, 'locale');
	@locroots = sort @$l;
	opendir (INCOMING, "${base}${grip_name}/incoming/")
		or croak (_g("Unable to open")." ${base}${grip_name}/incoming/ : $!");
	@files=grep(/^${package}-locale-.*_${version}.*_.*\.tdeb$/, readdir (INCOMING));
	closedir (INCOMING);
	foreach $t (sort @files) {
		# look for each component
		next unless ($t =~ /${package}-locale-(.*)_${version}.*_.*\.tdeb$/);
		next if (not defined $1);
		# convert suffix to a component
		$suffix = $1;
		$suffix =~ s/-.*$//;
		$suffix =~ s/\+.*$//;
		$cmpnt = grep (/$suffix/, @locroots) ? $suffix : undef;
		if (not defined $cmpnt) {
			# Translators: TDeb componentisation is a process of identifying
			# the locale component from the translation package filename.
			my $msg = sprintf(_g("TDeb componentisation failed for %s:%s "),
				$suffix, $t);
			carp ($msg);
		} else {
			$str = "reprepro --ignore=extension -C $cmpnt ".
			"--export=never -b ${base}locale includedeb $suite ".
			"${base}${grip_name}/incoming/$t";
			(defined $dryrun) ? print "$str\n" : system ("$str 2>/dev/null");
		}
	}
	if (scalar @files > 1) {
		$str = "reprepro -v -b ${base}locale export $suite";
		(defined $dryrun) ? print "$str\n" : system ("$str 2>/dev/null");
	}
}

=head1 check_dirs

Check that critical directories in the three-way repository
hierarchy exist and create any that are missing.

=cut

sub check_dirs {
	return undef unless (defined $filter_name and defined $grip_name);
	mkdir ("$base/$filter_name")        if (not -d "$base/$filter_name");
	mkdir ("$base/locale")              if (not -d "$base/locale");
	mkdir ("$base/$grip_name")          if (not -d "$base/$grip_name");
	mkdir ("$base/$filter_name/conf")   if (not -d "$base/$filter_name/conf");
	mkdir ("$base/locale/conf")         if (not -d "$base/locale/conf");
	mkdir ("$base/$grip_name/conf")     if (not -d "$base/$grip_name/conf");
	mkdir ("$base/$grip_name/logs")     if (not -d "$base/$grip_name/logs");
	mkdir ("$base/$grip_name/temp")     if (not -d "$base/$grip_name/temp");
	mkdir ("$base/$grip_name/incoming") if (not -d "$base/$grip_name/incoming");
}

=head1 setup_repos

Note: When setting up your own repository, remember that including
the Contents: specifier in reprepro costs B<a lot> of time during
each and every operation on the repository. Contents is not enabled
by C<setup_repos> and it is strongly recommended that it is not
re-enabled in later changes.

Need to decide whether this will cope with multiple suites
or just enforce suite=unstable.

locale is shared with Crush. It's not particularly easy to add a new
locale root as the scripts and the repos need to be changed at the
same time.

necessary reprepro data (needs config support):
these files need to be created for reprepro
final repos will also need C<SignWith: 0x97BB3B58>
except stable which should be signed manually to
bridge the security gap between Grip and Debian.

=cut

sub setup_repos {
	$base = &get_base;
	my ($mirror, $suite) = @_;
	return undef unless (defined $filter_name and defined $grip_name);
	my $a = &get_archlist ($suite, $filter_name);
	@archlist = (not defined $a or not @$a) ?
	sort qw/i386 amd64 arm armel powerpc mips mipsel/ : sort @$a;
	my $l = &get_locale_roots ($suite, 'locale');
	@locroots = (not defined $l or not @$l) ? qw/ af am ang ar as ast az
	be bg bn br bs ca cs cy da de dz el en eo es et eu fa fi fr ga gl
	gu he hi hr hu hy ia id io is it ja ka kn km ko ku ky lg li lt lv
	mai mg mi mk ml mn mr ms nb ne nl nn no ns nso oc or pa pl ps pt
	rm ro ru rw si sk sl sq sr sv ta te th tk tl tr tt ug uk ur uz vi
	wa wo xh yi zh zu / : @$l;
	my $architectures = join (" ", @archlist) . " source";
	my $cmpnts = "main " . join (" ", @locroots);
	my $desc = sprintf(_g("Emdebian %s translation repository.\n"), $suite);
	my $label = sprintf(_g("Emdebian-translation-%s\n"), $suite);
	my $suite_name = ($suite eq "unstable") ? "sid" : "lenny";
	my $udebcmpnts;
	my $update;
	my $override = "override.${suite_name}.extra.main";

	# Translators: reprepro is the name of the command.
	print _g("INF: Initialising the reprepro directories\n");
	if ((not -d "$base/$filter_name") and ($filter_name ne 'filter')) {
		printf( _g("The '%s' directory does not exist.\n".
		"If this is the correct directory, please create it and\n".
		"ensure it is writable.\n"), "${base}${filter_name}");
		my $msg = sprintf (_g("Cannot write repository data:").
			" ${base}${filter_name}: $!");
		croak ($msg);
	} else {
		mkdir ("${base}${filter_name}");
	}
	if ((not -d "$base/$grip_name") and ($grip_name ne 'grip')) {
		printf( _g("The '%s' directory does not exist.\n".
			"If this is the correct directory, please create it and\n".
			"ensure it is writable.\n"), "${base}${grip_name}");
		my $msg = sprintf (_g("Cannot write repository data:").
			" ${base}${grip_name}: $!");
		croak ($msg);
	} else {
		mkdir ("${base}${grip_name}");
	}
	mkdir ("${base}locale") if (not -d "${base}locale");
	mkdir ("${base}${filter_name}/conf") if (not -d "${base}${filter_name}/conf");
	mkdir ("${base}${grip_name}/conf") if (not -d "${base}${grip_name}/conf");
	mkdir ("${base}locale/conf") if (not -d "${base}locale/conf");
	if (not -f "$base/locale/conf/distributions") {
		# Translators, the string is a directory prefix.
		my $msg = sprintf(_g("Cannot write %s/locale/conf/distributions"), $base);
		open (DIST, ">$base/locale/conf/distributions")
			or croak ($msg." :$!");
		print DIST "Origin: Debian\n";
		print DIST "Label: $label\n";
		print DIST "Suite: $suite\n";
		print DIST "Codename: $suite_name\nVersion: 0.1\n";
		print DIST "Architectures: $architectures\n";
		print DIST "Components: $cmpnts\n";
		print DIST "Description: $desc\n";
		close (DIST);
	}
	# -dbg package => debug component
	$cmpnts = "main dev doc debug";
	$udebcmpnts = $cmpnts;
	if (not -f "$base/$filter_name/conf/distributions") {
		$label = "DebianFilter";
		$desc = "Debian unstable - filtered";
		$update = "gripper1 udeb-unstable";
		# Translators, the strings are a directory prefix.
		my $msg = sprintf(_g("Cannot write %s/%s/conf/distributions"),
			$base, $filter_name);
		open (DIST, ">$base/$filter_name/conf/distributions")
			or croak ($msg." :$!");
		print DIST "Origin: Debian\n";
		print DIST "Label: $label\n";
		print DIST "Suite: $suite\n";
		print DIST "Codename: $suite_name\nVersion: 0.1\n";
		print DIST "Architectures: $architectures\n";
		print DIST "Components: $cmpnts\n";
		print DIST "UDebComponents: $udebcmpnts\n";
		print DIST "Description: $desc\n";
		print DIST "Update: $update\n";
		close (DIST);
	}
	if (not -f "$base/$filter_name/conf/updates") {
		$label = "DebianFilter";
		$desc = "Debian unstable - filtered";
		$update = "gripper1";
		my $msg = sprintf(_g("Cannot write %s/%s/conf/updates"),
			$base, $filter_name);
		open (DIST, ">$base/$filter_name/conf/updates")
			or croak ($msg." :$!");
		print DIST "Name: $update\n";
		print DIST "Method: $mirror\n";
		print DIST "Suite: $suite\n";
		print DIST "Architectures: $architectures\n";
		print DIST "FilterList: deinstall pkglist\n";
		print DIST "\n";
		print DIST "Name: udeb-unstable\n";
		print DIST "Method: file:///$base/filter/\n";
		print DIST "Suite: unstable\n";
		# 'none' does not work - the line needs to be empty
		# so that only udebs are updated.
		print DIST "Components: \n";
		print DIST "UDebComponents: $udebcmpnts\n";
		close (DIST);
	}
	if (not -f "${base}${grip_name}/conf/distributions") {
		$label = "EmdebianGrip";
		$desc = "Emdebian Grip - unstable";
		my $msg = sprintf(_g("Cannot write %s/%s/conf/distributions"),
			$base, $grip_name);
		open (DIST, ">${base}${grip_name}/conf/distributions")
			or croak ($msg." :$!");
		print DIST "Origin: Debian\n";
		print DIST "Label: $label\n";
		print DIST "Suite: $suite\n";
		print DIST "Codename: $suite_name\nVersion: 0.1\n";
		print DIST "Architectures: $architectures\n";
		print DIST "Components: $cmpnts\n";
		print DIST "UDebComponents: $udebcmpnts\n";
		print DIST "Description: $desc\n";
		print DIST "DebOverride: $override\n";
		close (DIST);
	}
	if (not -f "${base}${grip_name}/conf/$override") {
		open (DIST, ">${base}${grip_name}/conf/$override")
			or croak ("${base}${grip_name}/conf/$override : $!");
		print DIST "\n";
		close (DIST);
	}
	if (not -f "${base}${grip_name}/conf/incoming") {
		open (DIST, ">$base/${grip_name}/conf/incoming")
			or croak ("$base/${grip_name}/conf/incoming :$!");
		print DIST "Name: autogrip\n";
		print DIST "IncomingDir: $base/${grip_name}/incoming\n";
		print DIST "TempDir: $base/${grip_name}/temp\n";
		print DIST "Default: unstable\n";
		close (DIST);
	}
	print "INF: Updating reprepro filter\n";
	if (not -f "${base}${filter_name}/conf/pkglist") {
		open (PKGLIST, ">${base}${filter_name}/conf/pkglist")
			or croak ("${base}${filter_name}/conf/pkglist :$!");
		close (PKGLIST);
	}
	mkdir "${base}${grip_name}/incoming"
		if (not -d "${base}${grip_name}/incoming");
	# the filter always needs at least one package.
	print _g("INF: Initialising the filter repository with a single package\n");
	&extend_filter ('apt');
	my $str = "reprepro -b ${base}${grip_name} export";
	(defined $dryrun) ? print "$str\n" : system ($str);
	$str = "reprepro -b ${base}${grip_name} createsymlinks";
	(defined $dryrun) ? print "$str\n" : system ($str);
	$str = "reprepro -b ${base}locale export";
	(defined $dryrun) ? print "$str\n" : system ($str);
	$str = "reprepro -b ${base}locale createsymlinks";
	(defined $dryrun) ? print "$str\n" : system ($str);
	$str = "reprepro -b ${base}${filter_name} export";
	(defined $dryrun) ? print "$str\n" : system ($str);
	$str = "reprepro -b ${base}${filter_name} createsymlinks";
	(defined $dryrun) ? print "$str\n" : system ($str);
}

=head1 convert_prefix

Debian repositories use the index character of the source
package name in the path to the package directory beneath the
pool in order to reduce the number of listings per directory
to a manageable level. The exception is source packages beginning
with 'C<lib>', because there are so many that the 'C<l>' section would
be too large. Whilst 'C<l>' remains, any source package that begins
with 'C<lib>' is put under a separate set of directories using the
first four letters instead of just 'C<l>', e.g. C<libaa> will be found
under C<pool/main/liba/libaa/> and C<libfoo> under
C<pool/main/libf/libfoo/> but C<limpet> would be under
C<pool/main/l/limpet/>

F<convert_prefix> is a quick function to determine the correct prefix
under these rules.

=cut

sub convert_prefix {
	my $package = shift;
	my ($index)=split(//, $package);
	$index = substr($package, 0, 4) if ($package =~ /^lib/);
	return $index;
}

=head1 grip_source

Wrapper for emgrip that takes a single Debian source package,
grips the binary package where the source package name matches
the binary package name (due to a hitch in how C<reprepro> works),
generates any Emdebian TDebs, includes the unchanged source
package into Grip, includes any gripped binary packages into
Grip and includes any generated Emdebian TDebs into locale.

Once the source package has been processed, runs F<&clean_incoming>.

=cut

sub grip_source {
	my ($retval, $str);
	$base = &get_base;
	my ($package, $version, $suite, $arch) = @_;
	return undef unless (defined $filter_name and defined $grip_name);
	return undef unless (defined $package and $version);
	# strip epochs only after finding the .dsc
	$version =~ s/^[0-9]://;
	my ($index) = convert_prefix($package);
	my $clist = get_components ($suite, $filter_name);
	my $dsc = "${base}${filter_name}/pool/main/$index/".
		"$package/${package}_${version}.dsc";
	if (not -f "$dsc") {
		foreach my $cmp (@$clist) {
			$dsc = "${base}${filter_name}/pool/$cmp/$index/".
				"$package/${package}_${version}.dsc";
			last if (-f "$dsc");
		}
	}
	croak (_g("Cannot read")." '$dsc' :$!") if (not -f "$dsc");
	# copy the DSC over then grip the binaries.
	my $dir = `mktemp -t -d "autogrip.XXXXXX"`;
	chomp ($dir);
	chdir ($dir);
	# need to get the .debs from the repo, emgrip will then grip these.
	# filter only uses main
	# allow binNMUs etc. by adding .* to version
	# this is source, so no $arch available.
	printf( _g("INF: getting source package: %s\n"), $dsc);
	$str = "dget -ud file://${dsc}";
	(defined $dryrun) ? print "$str\n" : system ($str);
	$dsc = basename ($dsc);
	my $a = get_archlist($suite, $grip_name);
	@archlist = @$a;
	# then run emgrip with a new .dsc mode which only processes the src.
	$str = "DEB_BUILD_OPTIONS='usegrip'".
		" /usr/bin/emgrip -o $base/$grip_name/incoming $dsc";
	(defined $dryrun) ? print "$str\n" : system ($str);
	chdir ("../");
	system ("rm -rf $dir");
	# handle dsc's that contain no Section or Priority or component
	my $single = get_single_package ("$suite", "$filter_name", "$package", "$arch");
	if (not exists $$single{'Section'}) {
		$single = get_single_package ("unstable", "$filter_name", "$package", "$arch");
	}
	my $priority = (not exists $$single{'Priority'}) ?
		"optional" : $$single{'Priority'};
	print _g("INF: adding source package to grip using reprepro. ");
	print "Section: ".$$single{'Section'}." ";
	print "Priority: ".$$single{'Priority'}."\n";
	# all source packages go into main
	$str = "reprepro -C main -S ".$$single{'Section'}." -P $priority".
		" --ignore=brokensignatures -b ${base}${grip_name} includedsc $suite ".
		"${base}${grip_name}/incoming/$dsc";
	$retval = (defined $dryrun) ? print "$str\n" : system ("$str 2>&1");
	&check_syscall ($retval);
	# locale repo needs component to be main
	print "INF: adding source package to locale using reprepro\n";
	$str = "reprepro -C main -S ".$$single{'Section'}." -P ".$$single{'Priority'}.
		" --ignore=brokensignatures -b ${base}locale includedsc $suite ".
		"${base}${grip_name}/incoming/$dsc";
	$retval = (defined $dryrun) ? print "$str\n" : system ("$str 2>&1");
	&check_syscall ($retval);
	# finally, add the gripped binaries
	# read list of components from grip repository
	&incoming_locale ($a, $package, $version, $suite);
	&clean_incoming();
}

=head1 grip_binary

Wrapper for emgrip that takes a single Debian binary package,
and grips it, generates any Emdebian TDebs, includes the gripped
binary package into Grip and includes any generated Emdebian TDebs
into locale.

Once the binary package has been processed, runs F<&clean_incoming>.

=head2 Components and Sections

Each binary package belongs to a C<Section> that broadly describes
the type of functionality in the package. If a component exists in
the repository with the same name as the C<Section>, the package will
be put into that component. Additionally, if the package name ends
in C<-dev> or the C<Section> is devel, the C<dev> component is
selected. If the package name ends in C<-doc> or the C<Section> is
C<doc>, the C<doc> component is selected. If there is no matching
component configured for the Grip repository, C<main> is used. Section
names themselves are read from the filter repository - i.e. set by
the Debian ftp-master overrides.

=cut

sub grip_binary {
	my ($retval, $debbldopts, $outdir, $str);
	my ($package, $version, $suite, $arch) = @_;
	if (not defined $filter_name or not defined $grip_name) {
		my $msg = _g("Unable to proceed: filter name or grip name not defined!");
		warn "$msg\n";
		return;
	}
	$base = &get_base;
	# strip epochs
	if (defined $version) {
		$version =~ s/^[0-9]://;
	} else {
		return;
	}
	# Translators: package name, version, architecture.
	printf( _g("INF: Preparing to add single %s binary (%s) on %s\n"),
		$package, $version, $arch);
	# identify the source package first.
	# (use the requested suite in case of a package split)
	# but handle -proposed-updates whereby the actual data is in
	# stable but the built package goes into stable-proposed-updates.
	my $proposed = $suite;
	$proposed =~ s/-proposed-updates$//;
	my $single = get_single_package ($proposed, $grip_name, $package, $arch);
	if (not exists $$single{'Section'}) {
		$single = get_single_package ("$proposed", "$filter_name", "$package", "$arch");
	}
	# allow for architectures pulled in from other mirrors.
	# not ideal, it is slow to do it this way.
	if (not exists $$single{'Section'}) {
		# Translators, fields are: package, repo name, architecture.
		printf(_g("Unable to find %s in %s for '%s', ignoring.\n"), $package, $filter_name, $arch);
		return;
	}
	my $srcname = (defined $$single{'Source'}) ? $$single{'Source'} : $package;
	$srcname =~ s/\(.*\)//;
	chomp ($srcname);
	$srcname =~ s/ //g;
	printf( _g("INF: Using source package '%s'. "), $srcname) if ($srcname ne $package);
	print "Section: ".$single->{'Section'}." ";
	print "Priority: ".$single->{'Priority'}."\n";
	$single = get_single_package ($proposed, $filter_name, $package, $arch);
	my $deb = $single->{'Filename'};
	my @debs = ();
	push @debs, "${base}$filter_name/$deb";
	my $cmpnt = &switch_component ($$single{'Section'}, $package);
	printf( _g("INF: Component: %s\n"), $cmpnt);
	foreach my $d (@debs) {
		next unless (-f "$d");
		$str = "dpkg-architecture -a${arch} -c \"DEB_BUILD_OPTIONS='usegrip' ".
			"/usr/bin/emgrip -o ${base}${grip_name}/incoming $d 2>&1 \" 2>/dev/null";
		$retval = (defined $dryrun) ? print "$str\n" : system ($str);
		&check_syscall($retval);
		my $msg = _g("Cannot open");
		opendir (INCOMING, "${base}${grip_name}/incoming/")
			or croak ("$msg ${base}${grip_name}/incoming/ :$!");
		my @files=grep(/^\Q${package}_${version}\Eem._(${arch}|all)\.deb$/, readdir (INCOMING));
		closedir (INCOMING);
		foreach my $f (@files) {
			$str = "reprepro -v -C $cmpnt -b ${base}${grip_name} includedeb".
				" $suite ${base}${grip_name}/incoming/$f";
			$retval = (defined $dryrun) ? print "$str\n" : system ("$str 2>&1");
			&check_syscall ($retval);
			unlink ("${base}${grip_name}/incoming/$f");
		}
		# should be unnecessary - TDebs should only be coming from source.
		&incoming_locale ($arch, $srcname, $version, $suite);
	}
}

=head1 switch_component

Single routine to work out which components should be used for
which sections.

=cut

sub switch_component {
	# relocate a package merely by matching the binary package name
	my @relocations = qw/ dev doc dbg /;
	my %comps=();
	my $cmpnt = "main";
	my ($section, $pkg) = @_;
	$cmpnt = $section if ($cmpnt ne $section);
	# relocate a package by dint of the Section:
	my $clist = get_components ('unstable', $grip_name);
	foreach my $cmpnt (@$clist) {
		$comps{$cmpnt}++;
	}
	# handle relocations - use main if fail.
	$cmpnt = "main"  if (not exists($comps{$section}));
	$cmpnt = "dev"   if ($section eq 'libdevel');
	$cmpnt = "dev"   if ($section eq 'devel');
	$cmpnt = "debug" if ($section eq 'debug');
	$cmpnt = "doc"   if ($section eq 'doc');
	$cmpnt = "main"  if (not defined $clist);
	printf(_g("INF: Switching component: %s\n"), $cmpnt)
		if ($cmpnt ne "main");
	foreach my $reloc (@relocations) {
		if ($pkg =~ /\-${reloc}$/) {
			my $actual = $reloc;
			$actual =~ s/^dbg$/debug/;
			$cmpnt = "$actual";
		}
	}
	return $cmpnt;
}

sub scripts_version {
	my $query = `dpkg-query -W -f='\${Version}' emdebian-grip-server`;
	(defined $query) ? return $query : return "";
}

sub check_syscall {
	my $signal = 0;
	my $retval = shift;
	return if (not defined $retval);
	return if ($retval == 0);
	$signal = $retval if ($retval < 256);
	$retval /= 256 if ($retval >= 256);
	printf( _g("INF: reprepro error: returned %d\n"), $retval) 
		if ($retval != 0 and $signal == 0);
	my $msg = sprintf(_g("ERR: reprepro died after receiving signal %d\n"), $signal);
	croak ($msg)
		if ($signal > 0);
}

sub _g {
	return gettext(shift);
}

=head1 AUTHOR

Neil Williams, C<< <codehelp@debian.org> >>

=cut

=head1 BUGS

Please report any bugs or feature requests to the Debian Bug Tracking
System using C<reportbug emdebian-tools>.

=head1 Known bugs

(Otherwise known as a ToDo list.)

The status of TDebs in the locale repository needs to be checked before
generating more but this means changes in emgrip too so that tdebs can
be skipped with a command line option. However, in normal operation, the
package is only processed because it is out of date so the TDebs and
other files need to be replaced anyway.

C<Architecture: all> are packages being continually reprocessed for
several architectures. Originally this was because the package could
contain translations which would need to be architecture-dependent
in Emdebian. However, TDebs should be being generated only from source
packages and this step probably needs to resolved. C<reprepro> correctly
handles such duplication, it is just a bit noisy when handling it and
it adds to the time required by the process as a whole.

Also need a general overview function that summaries all three
repositories.

=cut

1;
