#!/usr/bin/perl
# This file was preprocessed, do not edit!


use warnings;
use strict;
use POSIX;
use Fcntl;
use Getopt::Long;
use Debconf::Client::ConfModule ();

my ($config, $start, $from, $to, $stop);
my $progress=1;
my $dlwaypoint=15;
my ($logfile, $logstderr);
my $had_frontend;

our ($status_read, $status_write);
our ($command_read, $command_write);
our ($debconf_command_read, $debconf_command_write);
our ($debconf_reply_read, $debconf_reply_write);
our $apt_log;

sub checkopen (@) {
	my $file = $_[0];
	my $fd = POSIX::open($file, &POSIX::O_RDONLY);
	defined $fd or die "$0: can't open $file: $!\n";
	return $fd;
}

sub checkclose ($) {
	my $fd = $_[0];
	unless (POSIX::close($fd)) {
		return if $! == &POSIX::EBADF;
		die "$0: can't close fd $fd: $!\n";
	}
}

sub checkdup2 ($$) {
	my ($oldfd, $newfd) = @_;
	checkclose($newfd);
	POSIX::dup2($oldfd, $newfd)
		or die "$0: can't dup fd $oldfd to $newfd: $!\n";
}

sub nocloexec ($) {
	my $fh = shift;
	my $flags = fcntl($fh, F_GETFD, 0);
	fcntl($fh, F_SETFD, $flags & ~FD_CLOEXEC);
}

sub nonblock ($) {
	my $fh = shift;
	my $flags = fcntl($fh, F_GETFL, 0);
	fcntl($fh, F_SETFL, $flags | O_NONBLOCK);
}

sub reservefds (@) {
	my $null = checkopen('/dev/null');
	my $close = 1;
	for my $fd (@_) {
		if ($null == $fd) {
			$close = 0;
		} else {
			checkclose($fd);
			checkdup2($null, $fd);
		}
	}
	if ($close) {
		checkclose($null);
	}
}

sub envnonempty ($) {
	my $name = shift;
	return (exists $ENV{$name} and $ENV{$name} ne '');
}

sub start_debconf (@) {
	if (! $ENV{DEBIAN_HAS_FRONTEND}) {
		if (envnonempty('DEBCONF_DB_REPLACE')) {
			$ENV{DEBCONF_APT_PROGRESS_DB_REPLACE} =
				$ENV{DEBCONF_DB_REPLACE};
		}
		if (envnonempty('DEBCONF_DB_OVERRIDE')) {
			$ENV{DEBCONF_APT_PROGRESS_DB_OVERRIDE} =
				$ENV{DEBCONF_DB_OVERRIDE};
		}

		$ENV{DEBCONF_DB_REPLACE} = 'configdb';
		$ENV{DEBCONF_DB_OVERRIDE} = 'Pipe{infd:none outfd:none}';

		$ENV{DEBCONF_APT_PROGRESS_NO_FRONTEND} = 1;

		@ARGV = @_;
	}

	import Debconf::Client::ConfModule;
}

sub passthrough (@) {
	my $priority = Debconf::Client::ConfModule::get('debconf/priority');

	defined(my $pid = fork) or die "$0: can't fork: $!\n";
	if (!$pid) {
		close $status_read;
		close $command_write;
		close $debconf_command_read;
		close $debconf_reply_write;
		$^F = 6; # avoid close-on-exec
		if (fileno($command_read) != 0) {
			checkdup2(fileno($command_read), 0);
			close $command_read;
		}
		if (fileno($apt_log) != 1) {
			checkclose(1);
			checkdup2(fileno($apt_log), 1);
		}
		if (fileno($apt_log) != 2) {
			checkclose(2);
			checkdup2(fileno($apt_log), 2);
		}
		close $apt_log;
		delete $ENV{DEBIAN_HAS_FRONTEND};
		delete $ENV{DEBCONF_REDIR};
		delete $ENV{DEBCONF_SYSTEMRC};
		delete $ENV{DEBCONF_PIPE}; # just in case ...
		$ENV{DEBIAN_FRONTEND} = 'passthrough';
		$ENV{DEBIAN_PRIORITY} = $priority;
		$ENV{DEBCONF_READFD} = 5;
		$ENV{DEBCONF_WRITEFD} = 6;
		$ENV{APT_LISTCHANGES_FRONTEND} = 'none';
		if ($had_frontend) {
			$ENV{DEBCONF_DB_REPLACE} = 'configdb';
			$ENV{DEBCONF_DB_OVERRIDE} = 'Pipe{infd:none outfd:none}';
		}
		exec @_ or die "can't exec $_[0]: $!";
	}

	close $status_write;
	close $command_read;
	close $debconf_command_write;
	close $debconf_reply_read;
	return $pid;
}

sub handle_status ($$$) {
	my ($from, $to, $line) = @_;
	my ($status, $pkg, $percent, $description) = split ':', $line, 4;

	my ($min, $len);
	if ($status eq 'dlstatus') {
		$min = 0;
		$len = $dlwaypoint;
	}
	elsif ($status eq 'pmstatus') {
		$min = $dlwaypoint;
		$len = 100 - $dlwaypoint;
	}
	elsif ($status eq 'media-change') {
		Debconf::Client::ConfModule::subst(
			'debconf-apt-progress/media-change', 'MESSAGE',
			$description);
		my @ret = Debconf::Client::ConfModule::input(
			'critical', 'debconf-apt-progress/media-change');
		$ret[0] == 0 or die "Can't display media change request!\n";
		Debconf::Client::ConfModule::go();
		print $command_write "\n" ||
			die "can't talk to command fd: $!";
		return;
	}
	else {
		return;
	}

	$percent = ($percent * $len / 100 + $min);
	$percent = ($percent * ($to - $from) / 100 + $from);
	$percent =~ s/\..*//;
	if ($progress) {
		my @ret=Debconf::Client::ConfModule::progress('SET', $percent);
		if ($ret[0] eq '30') {
			cancel();
		}
	}
	Debconf::Client::ConfModule::subst(
		'debconf-apt-progress/info', 'DESCRIPTION', $description);
	my @ret=Debconf::Client::ConfModule::progress(
		'INFO', 'debconf-apt-progress/info');
	if ($ret[0] eq '30') {
		cancel();
	}
}

sub handle_debconf_command ($) {
	my $line = shift;

	print "$line\n" || die "can't write to stdout: $!";
	my $ret = <STDIN>;
	chomp $ret;
	print $debconf_reply_write "$ret\n" ||
		die "can't write to \$debconf_reply_write: $!";
}

my $pid;
sub run_progress ($$@) {
	my $from = shift;
	my $to = shift;
	my $command = shift;
	local ($status_read, $status_write);
	local ($command_read, $command_write);
	local ($debconf_command_read, $debconf_command_write);
	local ($debconf_reply_read, $debconf_reply_write);
	local $apt_log;
	use IO::Handle;

	if ($progress) {
		my @ret=Debconf::Client::ConfModule::progress(
			'INFO', 'debconf-apt-progress/preparing');
		if ($ret[0] eq '30') {
			cancel();
			return 30;
		}
	}

	reservefds(4, 5, 6);

	pipe $status_read, $status_write
		or die "$0: can't create status pipe: $!";
	nonblock($status_read);
	checkdup2(fileno($status_write), 4);
	open $status_write, '>&=', 4
		or die "$0: can't reopen \$status_write as fd 4: $!";
	nocloexec($status_write);

	pipe $command_read, $command_write
		or die "$0: can't create command pipe: $!";
	nocloexec($command_read);
	$command_write->autoflush(1);

	pipe $debconf_command_read, $debconf_command_write
		or die "$0: can't create debconf command pipe: $!";
	nonblock($debconf_command_read);
	checkdup2(fileno($debconf_command_write), 6);
	open $debconf_command_write, '>&=', 6
		or die "$0: can't reopen \$debconf_command_write as fd 6: $!";
	nocloexec($debconf_command_write);

	pipe $debconf_reply_read, $debconf_reply_write
		or die "$0: can't create debconf reply pipe: $!";
	checkdup2(fileno($debconf_reply_read), 5);
	open $debconf_reply_read, '<&=', 5
		or die "$0: can't reopen \$debconf_reply_read as fd 5: $!";
	nocloexec($debconf_reply_read);
	$debconf_reply_write->autoflush(1);

	if (defined $logfile) {
		open $apt_log, '>>', $logfile
			or die "$0: can't open $logfile: $!";
	} elsif ($logstderr) {
		open $apt_log, '>&', \*STDERR
			or die "$0: can't duplicate stderr: $!";
	} else {
		open $apt_log, '>', '/dev/null'
			or die "$0: can't open /dev/null: $!";
	}
	nocloexec($apt_log);

	$pid = passthrough $command,
		'-o', 'APT::Status-Fd=4',
		'-o', 'APT::Keep-Fds::=5',
		'-o', 'APT::Keep-Fds::=6',
		@_;

	my $status_eof = 0;
	my $debconf_command_eof = 0;
	my $status_buf = '';
	my $debconf_command_buf = '';

	while (not $status_eof) {
		my $rin = '';
		my $rout;
		vec($rin, fileno($status_read), 1) = 1;
		vec($rin, fileno($debconf_command_read), 1) = 1
			unless $debconf_command_eof;
		my $sel = select($rout = $rin, undef, undef, undef);
		if ($sel < 0) {
			next if $! == &POSIX::EINTR;
			die "$0: select failed: $!";
		}

		if (vec($rout, fileno($status_read), 1) == 1) {
			while (1) {
				my $r = sysread($status_read, $status_buf,
						4096, length $status_buf);
				if (not defined $r) {
					next if $! == &POSIX::EINTR;
					last if $! == &POSIX::EAGAIN or
						$! == &POSIX::EWOULDBLOCK;
					die "$0: read \$status_read failed: $!";
				}
				elsif ($r == 0) {
					if ($status_buf ne '' and
					    $status_buf !~ /\n$/) {
						$status_buf .= "\n";
					}
					$status_eof = 1;
					last;
				}
				last if $status_buf =~ /\n/;
			}

			while ($status_buf =~ /\n/) {
				my $status_line;
				($status_line, $status_buf) =
					split /\n/, $status_buf, 2;
				handle_status $from, $to, $status_line;
			}
		}

		if (vec($rout, fileno($debconf_command_read), 1) == 1) {
			while (1) {
				my $r = sysread($debconf_command_read,
						$debconf_command_buf, 4096,
						length $debconf_command_buf);
				if (not defined $r) {
					next if $! == &POSIX::EINTR;
					last if $! == &POSIX::EAGAIN or
						$! == &POSIX::EWOULDBLOCK;
					die "$0: read " .
					    "\$debconf_command_read " .
					    "failed: $!";
				}
				elsif ($r == 0) {
					if ($debconf_command_buf ne '' and
					    $debconf_command_buf !~ /\n$/) {
						$debconf_command_buf .= "\n";
					}
					$debconf_command_eof = 1;
					last;
				}
				last if $debconf_command_buf =~ /\n/;
			}

			while ($debconf_command_buf =~ /\n/) {
				my $debconf_command_line;
				($debconf_command_line, $debconf_command_buf) =
					split /\n/, $debconf_command_buf, 2;
				handle_debconf_command $debconf_command_line;
			}
		}
	}

	waitpid $pid, 0;
	undef $pid;
	my $status = $?;

	if ($progress) {
		my @ret=Debconf::Client::ConfModule::progress('SET', $to);
		if ($ret[0] eq '30') {
			cancel();
		}
	}

	if ($status & 127) {
		return 127;
	}

	return ($status >> 8);
}

my $cancelled=0;
my $cancel_sent_signal=0;
sub cancel () {
	$cancelled++;
	if (defined $pid) {
		$cancel_sent_signal++;
		if ($cancel_sent_signal == 1) {
			kill INT => $pid;
		}
		else {
			kill KILL => $pid;
		}
	}
}

sub start_bar ($$) {
	my ($from, $to) = @_;
	if ($progress) {
		Debconf::Client::ConfModule::progress(
			'START', $from, $to, 'debconf-apt-progress/title');
		my @ret=Debconf::Client::ConfModule::progress(
			'INFO', 'debconf-apt-progress/preparing');
		if ($ret[0] eq '30') {
			cancel();
		}
	}
}

sub stop_bar () {
	Debconf::Client::ConfModule::progress('STOP') if $progress;
	Debconf::Client::ConfModule::stop() unless $had_frontend;
}

if (envnonempty('DEBCONF_APT_PROGRESS_DB_REPLACE')) {
	$ENV{DEBCONF_DB_REPLACE} = $ENV{DEBCONF_APT_PROGRESS_DB_REPLACE};
} else {
	delete $ENV{DEBCONF_DB_REPLACE};
}
if (envnonempty('DEBCONF_APT_PROGRESS_DB_OVERRIDE')) {
	$ENV{DEBCONF_DB_OVERRIDE} = $ENV{DEBCONF_APT_PROGRESS_DB_OVERRIDE};
} else {
	delete $ENV{DEBCONF_DB_OVERRIDE};
}
$had_frontend = 1 unless $ENV{DEBCONF_APT_PROGRESS_NO_FRONTEND};
delete $ENV{DEBCONF_APT_PROGRESS_NO_FRONTEND}; # avoid inheritance

my @saved_argv = @ARGV;

my $result = GetOptions('config'       => \$config,
			'start'        => \$start,
			'from=i'       => \$from,
			'to=i'         => \$to,
			'stop'         => \$stop,
			'logfile=s'    => \$logfile,
			'logstderr'    => \$logstderr,
			'progress!'    => \$progress,
			'dlwaypoint=i' => \$dlwaypoint,
);

if (! $progress && ($start || $from || $to || $stop)) {
	die "--no-progress cannot be used with --start, --from, --to, or --stop\n";
}

unless ($start) {
	if (defined $from and not defined $to) {
		die "$0: --from requires --to\n";
	} elsif (defined $to and not defined $from) {
		die "$0: --to requires --from\n";
	}
}

my $mutex = 0;
++$mutex if $config;
++$mutex if $start;
++$mutex if $stop;
if ($mutex > 1) {
	die "$0: must use only one of --config, --start, or --stop\n";
}

if (($config or $stop) and (defined $from or defined $to)) {
	die "$0: cannot use --from or --to with --config or --stop\n";
}

start_debconf(@saved_argv) unless $config;

my $status = 0;

if ($config) {
	print <<'EOF';
DEBCONF_APT_PROGRESS_DB_REPLACE="$DEBCONF_DB_REPLACE"
DEBCONF_APT_PROGRESS_DB_OVERRIDE="$DEBCONF_DB_OVERRIDE"
export DEBCONF_APT_PROGRESS_DB_REPLACE DEBCONF_APT_PROGRESS_DB_OVERRIDE
DEBCONF_DB_REPLACE=configdb
DEBCONF_DB_OVERRIDE='Pipe{infd:none outfd:none}'
export DEBCONF_DB_REPLACE DEBCONF_DB_OVERRIDE
EOF
} elsif ($start) {
	$from = 0 unless defined $from;
	$to = 100 unless defined $to;
	start_bar($from, $to);
} elsif (defined $from) {
	$status = run_progress($from, $to, @ARGV);
} elsif ($stop) {
	stop_bar();
} else {
	start_bar(0, 100);
	if (! $cancelled) {
		$status = run_progress(0, 100, @ARGV);
		stop_bar();
	}
}

if ($cancelled) {
	Debconf::Client::ConfModule::get("debconf/priority");

	exit 30;
}
elsif ($status == 30) {
	exit 3;
}
else {
	exit $status;
}

