#!/usr/bin/perl

# $Id: install_packages,v 1.25 2002/04/09 07:47:28 lange Exp $
#*********************************************************************
#
# install_packages -- read package config and install packages via apt-get
#
# This script is part of FAI (Fully Automatic Installation)
# (c) 2000-2002, Thomas Lange, lange@informatik.uni-koeln.de
#
#*********************************************************************
# This program 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 2 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; see the file COPYING. If not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
# MA 02111-1307, USA.
#*********************************************************************

use strict;
# global variables
my @commands;
my %command;
my $test;
my $verbose;
my $FAI_ROOT;
my @classes;
my $classpath;
my $rootcmd;
my @preloadlist;
my @preloadrmlist;
my %list;

# PRELOAD feature from Thomas Gebhardt  <gebhardt@hrz.uni-marburg.de>

$| = 1;
# order of commands to execute
@commands = qw/hold taskinst install remove dselect-upgrade/;
%command = (
	    install => 'apt-get -y --fix-missing install',
	    remove  => 'apt-get -y --purge remove',
  "dselect-upgrade" => 'apt-get -y dselect-upgrade',
         "taskinst" => 'tasksel -n install',
             "hold" => 'dpkg --set-selections',
);

# currently no test, always execute
$test = 0;

$verbose=$ENV{verbose};
$FAI_ROOT = $ENV{FAI_ROOT};
$classpath = "$ENV{FAI}/package_config";
$rootcmd = ($FAI_ROOT eq "/" ) ? '' : "chroot $FAI_ROOT";
@classes = grep { !/^#|^\s*$/ } split(/[\s\n]+/,$ENV{classes});

# read all package config files
foreach (@classes) {
  my $filename = "$classpath/$_";
  &readconfig($filename) if -f $filename;
}

# get file, which must exist before installing packages
foreach my $entry (@preloadlist,@preloadrmlist) {
  my ($url, $directory) = @$entry;
  if ($url =~ m!^file:/(.+)!) {
    my $file = $1;
    execute("cp $FAI_ROOT/$file $FAI_ROOT/$directory");
  } else {
    execute("wget -nv -P$FAI_ROOT/$directory $url");
  }
}

# call apt-get or tasksel for each type of command whith the list of packages
foreach my $type (@commands) {
  # skip if empty list
  next unless defined $list{$type};

  if ($type eq "dselect-upgrade") {
    dselectupgrade($type);
    next;
  }

  my $packlist = join(' ',@{$list{$type}});

  if ($type eq "hold") {
    my $hold = join " hold\n", @{$list{hold}};
    $hold .= " hold\n";
    execute("echo \"$hold\" | $rootcmd $command{hold}");
    next;
  }

  if ($type eq "install") {
    # check for packages, that don't exists
    # do not try to check packages that end in + or -
    # it's not easy to determine the real package name
    # example: g++; is the package called g+ and should be installed, or just g++ ?
    $packlist = join(' ',grep {! /[+-]$/} @{$list{$type}});
    my $plist = $packlist;
    my $misspelt = qx{$rootcmd apt-cache show $plist 2>&1 1>/dev/null};
    $misspelt =~ s/W: Unable to locate package //g;
    $misspelt =~ s/\n/ /g;
    warn "These unknown packages are removed from the installation list: $misspelt\n" if $misspelt;
    # remove misspelt package names from packlist
    my %misspelt = map { $_ => 1 } split /\s+/,$misspelt;
    my $newlist = "";
    grep { $misspelt{$_} or $newlist .= "$_ "} @{$list{install}};
    $packlist = $newlist;
  }
  execute("$rootcmd $command{$type} $packlist") if $packlist;
}

# remove prelaoded files
foreach my $entry (@preloadrmlist) {
  my ($url, $directory) = @$entry;
  $url =~ m#/([^/]+$)#;
  my $file =  "$directory/$1";
  print "rm $file\n" if $verbose;
  unlink $file || warn "Can't remove $file\n";
}

# check if all went right
execute("$rootcmd dpkg -C");
# clean apt cache
execute("$rootcmd apt-get clean");
# - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub readconfig {

  my $filename = shift;
  my ($package,$type);

  open (FILE,"$filename") || warn "ERROR $0: Can't read config file: $filename\n";
  print "$0: read config file $filename\n" if $verbose;

  while (<FILE>) {
    next if /^#/;    # skip comments
    s/#.*$//;        # delete comments
    next if /^\s*$/; # skip empty lines
    chomp;
    /^PRELOAD\s+([^\s]+)\s+([^\s]+)/ and push(@preloadlist, [$1, $2]),next;
    /^PRELOADRM\s+([^\s]+)\s+([^\s]+)/ and push(@preloadrmlist, [$1, $2]),next;
    /^PACKAGES\s+([^\s]+)/ and $type = $1,next;
    warn "PACKAGES .. line missing in $filename\n",next unless $type;
    push @{$list{$type}}, split;
  }
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub execute {

  # execute a command or only print it
  my $command = shift;
  my $error;

  $verbose && print "$command\n";
  $test && return;

  $error = system "$command";
  warn "ERROR: $error $?\n" if $error;
}
# - - - - - - - - - - - - - - - - - - - - - - - - - - -
sub dselectupgrade {
  my $type = shift;
  my ($package,$action,$list);
  my $tempfile = "$FAI_ROOT/tmp/dpkg-selections.tmp";

  while (@{$list{$type}}) {
    $package = shift @{$list{$type}};
    $action = shift @{$list{$type}};
    $list .= "$package $action\n";
  }

  open TMP, "> $tempfile" || die " Can't write to $tempfile";
  print TMP $list;
  close TMP;

  execute("$rootcmd dpkg --set-selections < $tempfile");
  execute("$rootcmd $command{$type}");
  unlink $tempfile;
}
