#!/usr/bin/perl -w

=head1 NAME

configure-debian - central configuration program for packages using debconf

=head1 SYNOPSIS

 configure-debian [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

-l --list	List all packages initially, don't show them by section

configure-debian 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 warnings;
use Locale::gettext;
use Getopt::Long;

my $templatedir = "/var/lib/dpkg/info";
my $debconf_helper = "/usr/share/configure-debian/configure-debian-debconf";

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 @allprograms = ();
my $allprograms = '';
my $all='';
my $nosections='';
my $unseen_only='';
my $default_priority='';
my $force='';
my $frontend='';
my $priority=0;
my $reconf_args ='';
my $real_frontend='';

sub warning {
    print STDERR "configure-debian: @_\n";
}

sub error {
    print STDERR "configure-debian: @_\n";
    exit 1;
}

sub usage {
    print STDERR gettext(q{Usage: 
configure-debian [options] packages
  -h,  --help           	Print this message.
  -l,  --list			List all packages instead of by section
  -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.
});
exit 0;
}
   

sub cd_init {
	
    my $help; 
    
    #Parse options
    GetOptions ("h|help" => \$help, "l|list" => \$nosections, "a|all" => \$all, "u|unseen-only" => \$unseen_only, "default-priority" => \$default_priority, "force" => \$force, "f|frontend=s" => \$frontend, "p|priority=s" => \$priority);

    if ($help) {
        usage();
    }
    
	#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 ($frontend) {
        # If we launch from a menu item, check our environment variables to see
        # if we should launch the gnome or kde frontend
        if ($frontend eq '##CHECK##') {
            #TODO: Implement this
        }
        else {
            my $tmpfile = `tempfile`;
            chomp $tmpfile;
            system ($debconf_helper, $frontend, $tmpfile); #Set the frontend
            $reconf_args = $reconf_args . " --frontend=$frontend ";
            open(IN, "<$tmpfile");
            $real_frontend=<IN>;
            chomp $real_frontend;
            close IN;
            unlink $tmpfile;
        }
    }
    else {
        $frontend = "FALSE";
    }
    if ($priority) {
        $reconf_args = $reconf_args . " --priority=$priority ";
    }
	$reconf_args = $reconf_args . join(", ", @ARGV);
}

### Begin Program Here ###
if ($> != 0) {
	error("$0 must be run as root\n");
}

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

cd_init();

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

#Initialize this hash by hand. 
map { $programs{$_} = '' } @subsections;

#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-debian/) {
			next;
		}
		# We've got one, so first figure out which
		# section it goes in
		$name =~ s/(.*).templates/$1/;
		if (!$nosections) {
			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;
			}
		}
		else { # We're just listing all the programs in one list here
			push (@allprograms, $name);
		}
	}
}
closedir(TEMPLATEDIR);

if ($nosections) {
	@allprograms = sort(@allprograms);
	$allprograms = join(", ", @allprograms);
}

my $state = 1;
if ($nosections) {
	$state = 2;
}
my $last_subsection = "";
my $last_program = "";
while (($state != 0) and ($state != 4)) {
	#Ask for subsection
	if ($state == 1) {
		#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 $tmpfile = `tempfile`;
        chomp $tmpfile;
        if ( $last_subsection eq "" ) {
            system ($debconf_helper, "FALSE", $tmpfile, "configure-debian/packages_subsection", $subsel);
        } else {
            system ($debconf_helper, "FALSE", $tmpfile, "configure-debian/packages_subsection", $subsel, $last_subsection);
        }
        open(IN, "<$tmpfile");
        $last_subsection=<IN>;
        chomp $last_subsection;
        close IN;
        unlink $tmpfile;
        if ( $last_subsection eq '##BACKUP##') {
            $state=-1;
        }
        elsif ($last_subsection eq "") {
            error("last_subsection was blank\n");
        }
	}
	#Ask for the program
	elsif ($state == 2) {
        my $tmpfile = `tempfile`;
        chomp $tmpfile;
		if (!$nosections) {
        	if ( $last_program eq "") {
        	    system($debconf_helper, "FALSE", $tmpfile, "configure-debian/packages_program", $programs{$last_subsection});
        	} else {
        	    system($debconf_helper, "FALSE", $tmpfile, "configure-debian/packages_program", $programs{$last_subsection}, $last_program);
        	}
		}
		else { # Don't list sections, just programs
        	system($debconf_helper, "FALSE", $tmpfile, "configure-debian/packages_program", $allprograms);
		}
        open(IN, "<$tmpfile");
        $last_program=<IN>;
        chomp $last_program;
        close IN;
        unlink $tmpfile;
        if ($last_program eq '##BACKUP##') {
            $state=0;
        }
        elsif ($last_program eq "") {
            error("last_program was blank\n");
        }
	}
	#Reconfigure and ask for a new one
	elsif ($state == 3) {
		if ($last_program ne "") {
			print ("Launching dpkg-reconfigure for $last_program. One moment...\n");
			system("dpkg-reconfigure $reconf_args $last_program");
			print ("Done.\n");
		}
		
		#Ask if they want to configure another program
        my $tmpfile = `tempfile`;
        chomp $tmpfile;
        system($debconf_helper, "FALSE", $tmpfile, "configure-debian/packages_another");
        open(IN, "<$tmpfile");
        my $ret=<IN>;
        chomp $ret;
        close IN;
        unlink $tmpfile;
        if ($ret eq "") {
            error("ret was blank\n");
        } elsif ($ret eq "true") {
			if (!$nosections) {
				$state = 0; #Set state to 0, to be set to 1 next
			}
			else {
				$state = 1;
			}
        }
	}
	$state++;
}

#Exit nicely if we just cancel out
if ($state == 0) {
    my $tmpfile = `tempfile`;
    chomp $tmpfile;
    if ( $real_frontend ne '' ) {
        system($debconf_helper, $real_frontend, $tmpfile); #Repair the frontend
    }
	exit;
}


=head1 AUTHOR

David Nusinow <dnusinow@debian.org>

=cut
