#!/usr/bin/perl -w

=head1 NAME

configure-packages - central configuration program for packages using debconf

=head1 SYNOPSIS

 configure-packages [options]

=head1 DESCRIPTION

This program is meant to be a simple frontend to the dpkg-reconfigure program. Many new users who do not know about this program are unaware of how to return to the debconf configuration seen during package install. Others who do know about it often forget or do not know the package name of the program they want to reconfigure. This program is meant to address both of those issues.

Note that this is not meant to be a complete configuration system!  Debconf is a purposely simple system, and is not meant to solve all configuration needs. Still, it is very flexible and can accomplish a lot with relative ease. The advantage to using it rather than a tool such as linuxconf is that the distributed nature of Debian allows package maintainers to write their own configuration scripts specially tailored to both the program that they know very well and Debian itself.

=head1 OPTIONS

configure-packages accepts all the command line options for B<dpkg-reconfigure(1)>, and passes them along to it when it calls the program. See its manpapge for details on these options.


=head1 SEE ALSO

L<dpkg-reconfigure(1)>

=cut

use strict;
use Debconf::Db;
use Debconf::Gettext;
use Debconf::AutoSelect qw(:all);
use Debconf::Template;
use Debconf::Question;
use Debconf::Config;

my $templatedir = "/var/lib/dpkg/info";

my @subsections = ("admin", "base", "comm", "contrib", "devel",
		   "doc", "editors", "electronics", "embedded", 
		   "games", "gnome", "graphics", "hamradio", 
		   "interpreters", "kde", "libdevel", "libs", "mail",
		   "math", "misc", "net", "news", "non-US", "non-free",
		   "oldlibs", "otherosfs", "perl", "python", "science",
		   "shells", "sound", "tex", "text", "utils", "web",
		   "x11");

my %programs = ();
my $frontend;
my $all=0;
my $unseen_only=0;
my $default_priority=0;
my $force=0;
my $reconf_args ='';

Debconf::Config->getopt( # TODO: i18n this? What's the best way to break it up?
qq{Usage: configure-packages [options] packages
  -a,  --all			Reconfigure all packages.
  -u,  --unseen-only		Show only not yet seen questions.
       --default-priority	Use default priority instead of low.
       --force			Force reconfiguration of broken packages.},
	"all|a"			=> \$all,
	"unseen-only|u"		=> \$unseen_only,
	"default-priority"	=> \$default_priority,
	"force"			=> \$force,
);

sub cd_init {
		
	Debconf::Db->load;

	#If frontend is noninteractive, set it to default. dpkg-
	#reconfigure does this as well, but we can't rely on it yet
	if (Debconf::Config->frontend eq 'Noninteractive') {
		Debconf::Config->frontend('Dialog');
	}

	$frontend = make_frontend();

	#FIXME:Here we should check for an option to negate this
	Debconf::Config->reshow(1);

	$frontend->default_title('Configure Packages');
	Debconf::Template->load("$templatedir/debconf.templates", "debconf");
	$frontend->capb_backup(1);
	
	#Build our args string for dpkg-reconfigure
	if ($all) {
		$reconf_args = $reconf_args . " -a ";
	}
	if ($unseen_only) {
		$reconf_args = $reconf_args . " -u ";
	}
	if ($default_priority) {
		$reconf_args = $reconf_args . " --default-priority ";
	}
	if ($force) {
		$reconf_args = $reconf_args . " --force ";
	}
	#If the frontend is changed with a flag, we'll
	#have to specifically pass it, so may as well do it anyway
	$reconf_args = $reconf_args . " -f " .  Debconf::Config->frontend;
	$reconf_args = $reconf_args . join(", ", @ARGV);
}

#Check for the all switch
if ($all) {
	exit (system("dpkg-reconfigure --all"));
}

if ($> != 0) {
	print STDERR sprintf("%s must be run as root", $0) . "\n";
	exit 1;
}

print "Loading. One moment...\n";

cd_init();

#Initialize this hash by hand. If anyone has a better method, let me
#know, since this is fugly
foreach my $item (@subsections) {
	$programs{$item} = "";
}

#Scan for templates files, giving us our list of apps
opendir(TEMPLATEDIR, $templatedir) || die "Can't open $templatedir: $!";
foreach my $name (sort readdir(TEMPLATEDIR)) {
	chomp $name;
	my $section;
	if ($name =~ /.*\.templates$/) {
		#Get rid of ourselves
		#if ($name =~ /^configure-packages/) {
		#	next;
		#}
		# We've got one, so first figure out which
		# section it goes in
		$name =~ s/(.*).templates/$1/;
		open APTCACHE, "apt-cache show $name |" or die "can't fork: $!";
		while (<APTCACHE>) {
			if (/^Section:/) {
				s/^Section: //;
				$section =  $_;
				chomp $section;
			}
		}
		unless ($section) {
			die "Couldn't figure out what section app $name is in.\n";
		}
		close APTCACHE or die "bad apt-cache: $! $?";
		if ($programs{$section}) {
			$programs{$section} = $programs{$section} . ", ";
			$programs{$section} = $programs{$section} . $name;
		}
		else {
			$programs{$section} = $name;
		}
	}
}
closedir(TEMPLATEDIR);

my $state = 1;
my $response;
my $lastval;
while (($state != 0) and ($state != 4)) {
	#Ask for subsection
	if ($state == 1) {
		my $qsubsection = Debconf::Question->get('debconf/configure_packages_subsection');
		#Here we make our list of subsections with debconf questions available
		my $subsel = "";
		foreach my $subsection (@subsections) {
			if ($programs{$subsection} ne "") {
				if ($subsel eq "") {
					$subsel = $subsection;
				} else {
					$subsel = $subsel . ", " . $subsection;
				}
			}
		}
		my $result = $qsubsection->variable('configure_packages_subsections', $subsel);
		my $esubsection = $frontend->makeelement($qsubsection);
		$frontend->add($esubsection);
		$response = $frontend->go();
		$lastval = $esubsection->value();
		$frontend->clear(); #cleanup required
	}
	#Ask for the program
	elsif ($state == 2) {
		my $qprogram = Debconf::Question->get('debconf/configure_packages_program');
		$qprogram->variable('configure_packages_programs', $programs{$lastval});
		my $eprogram = $frontend->makeelement($qprogram);
		$frontend->add($eprogram);
		$response = $frontend->go();
		$lastval = $eprogram->value();
		$frontend->clear(); #cleanup required
	}
	#Reconfigure and ask for a new one
	elsif ($state == 3) {
		$frontend->shutdown;
		Debconf::Db->save;
		my $progname = $lastval;
		if ($progname ne "") {
			my $launchstring = $progname;
			#if ($reconf_args) {
			#	$launchstring = $launchstring . ", as well as $reconf_args";
			#}
			print ("Launching dpkg-reconfigure for $launchstring. One moment...\n");
			system("dpkg-reconfigure $reconf_args $progname");
			print ("Done.\n");
		}
		#We must reinitialize the frontend here
		cd_init();
		
		#Ask if they want to configure another program
		my $qanother = Debconf::Question->get('debconf/configure_packages_another');
		my $eanother = $frontend->makeelement($qanother);
		$frontend->add($eanother);
		$response = $frontend->go();
		$frontend->clear();
		if ($response && $eanother->value() eq 'true') {
			$state = 0; #Set state to 0, to be set to 1 next
		}
	}
	if ($response) {
		$state++;
	} else {
		$state--;
	}
}

$frontend->shutdown;
Debconf::Db->save;

#Exit nicely if we just cancel out
if ($state == 0) {
	exit;
}


=head1 AUTHOR

David Nusinow <david_nusinow@yahoo.com>

=cut
