#!/usr/bin/perl -T

#- Copyright (C) 1999 MandrakeSoft (pixel@linux-mandrake.com)
#-
#- 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, 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, write to the Free Software
#- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#use strict qw(subs vars refs);
use urpm;
# for i18n
use POSIX;
use Locale::GetText;

setlocale (LC_ALL, "");
Locale::GetText::textdomain ("urpmi");

import Locale::GetText I_;
*_ = *I_;

#- default options.
my $auto = 0;
my $auto_select = 0;
my $force = 0;
my $X = 0;
my $WID = 0;
my $all = 0;
my $complete = 0;
my $minimal = 1;
my $rpm_opt = "-Uvh";
my $verbose = 0;

my $uid;
my @files;
my @names;

#my %I18N;
#load_po();

$ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin";
delete @ENV{qw(ENV BASH_ENV IFS CDPATH)};
($<, $uid) = ($>, $<);
    
sub usage {
    #die(_("usage: urpmi [-h] [--auto] [--force] [-a] [-v] package_name|rpm_file [package_names|rpm_files...]\n"));
    die( sprintf (_("urpmi version %s
Copyright (C) 1999, 2000, 2001 MandrakeSoft.
This is free software and may be redistributed under the terms of the GNU GPL.

usage:
  --help           - print this help message.
  --auto           - automatically select a good package in choices.
  --auto-select    - automatically select packages for upgrading the system.
  --force          - force invocation even if some package do not exist.
  --X              - use X interface.
  --best-output    - choose best interface according to the environment:
                     X or text mode.
  -a               - select all matches on command line.
  -m               - choose minimum closure of requires (default).
  -M               - choose maximun closure of requires.
  -c               - choose complete method for resolving requires closure.
  -q               - quiet mode.
  -v               - verbose mode.

  names or rpm files (only for root) given on command line are installed.
"), $urpm::VERSION));
}

#- parse arguments list.
my @nextargv;
for (@ARGV) {
    /^--help$/ and do { usage; next };
    /^--auto$/ and do { $auto = 1; next };
    /^--auto-select$/ and do { $auto_select = 1; $minimal = 0; next };
    /^--force$/ and do { $force = 1; next };
    /^--X$/ and do { $X = 1; next };
    /^--WID=(.*)$/ and do { $WID = $1; next };
    /^--WID$/ and do { push @nextargv, \$WID; next };
    /^--best-output$/ and do { $X ||= $ENV{DISPLAY} && system('/usr/X11R6/bin/xtest', '') == 0; next };
    /^--comment$/ and do { push @nextargv, undef; next };
    /^-(.*)$/ and do { foreach (split //, $1) {
	/[\?h]/ and do { usage; next };
	/a/ and do { $all = 1; next };
	/c/ and do { $complete = 1; next };
	/m/ and do { $minimal = 1; next };
	/M/ and do { $minimal = 0; next };
	/q/ and do { $rpm_opt = "-U"; next };
	/v/ and do { $verbose = 1; next };
	die "urpmi: unknown option \"-%1\", check usage with --help\n"; } next };
    @nextargv and do { my $r = shift @nextargv; $r and $$r = $_; next };
    /\.rpm$/ and do { push @files, untaint($_); next };
    push @names, $_;
}

#- log only at this point in case of query usage.
log_it(scalar localtime, " urpmi called with @ARGV\n");

my ($pid_out, $pid_err);

open SAVEOUT, ">&STDOUT"; select SAVEOUT; $| = 1;
open SAVEERR, ">&STDERR"; select SAVEERR; $| = 1;
unless ($pid_out = open STDOUT, "|-") {
    open F, ">>/var/log/urpmi.log"; select F; $| = 1;
    select SAVEOUT; $| = 1;
    $/ = \1;
    while (<STDIN>) {
	print SAVEOUT $_;
	print F $_;
    }
    close F;
    exit 0;
}
unless ($pid_err = open STDERR, "|-") {
    open F, ">>/var/log/urpmi.log"; select F; $| = 1;
    select SAVEERR; $| = 1;
    $/ = \1;
    while (<STDIN>) {
	print SAVEERR $_;
	print F $_;
    }
    close F;
    exit 0;
}
select STDERR; $| = 1;     # make unbuffered
select STDOUT; $| = 1;     # make unbuffered

#- params contains informations to parse installed system.
my $urpm = new urpm;

#- remove verbose if not asked.
$verbose or $urpm->{log} = sub {};

$urpm->read_depslist;

if (@files) {
    $uid == 0 or die _("Only superuser is allowed to install local packages");

    #- sanity check of pathname.
    m|^/| or $_ = "./$_" foreach @files;

    #- read provides file which is needed only to compute incremental
    #- dependancies, of files provided.
    $urpm->read_provides;

    #- build closure with local package and return list of names.
    push @names, $urpm->register_local_packages(@files);
}

#- reparse whole internal depslist to match against newer packages only.
#- ignored medium MUST HAVE BEEN taken into account for building hdlist before!
$urpm->relocate_depslist;


#- search the packages according the selection given by the user,
#- basesystem is added to the list so if it need to be upgraded, all its dependancy
#- will be updated too.
my %packages;
$urpm->search_packages(\%packages, [ ($minimal ? () : ('basesystem')), @names], all => $all) or $force or exit 1;

#- filter to add in packages selected required packages.
my $ask_choice = sub {
    my ($urpm, @choices_id) = @_;
    my $n = 1; #- default value.
    my @l = map { my $info = $urpm->{params}{depslist}[$_]; "$info->{name}-$info->{version}-$info->{release}" } @choices_id;

    if (@l > 1 && !$auto) {
	my $msg = _("One of the following packages is needed:");
	if ($X) {
	    `gchooser "$msg" @l`;
	    $n = $? >> 8 || die;
	} else {
	    print SAVEOUT "$msg\n";
	    my $i = 0; foreach (@l) { print SAVEOUT " ", ++$i, "- $_\n"; }
	    while (1) {
		printf SAVEOUT sprintf(_("What is your choice? (1-%d) "), $i);
		$n = <STDIN>;
		1 <= $n && $n <= $i and last;
		print SAVEOUT _("Sorry, bad choice, try again\n");
	    }
	}
    }

    $choices_id[$n - 1];
};

#- auto select package for upgrading the distribution.
if ($auto_select) {
    my (%to_remove, %keep_files);

    $urpm->select_packages_to_upgrade('', \%packages, \%to_remove, \%keep_files);

    if (keys(%to_remove) > 0) {
	print STDERR "some package have to be removed for being upgraded, this is not supported yet\n";
    }
}

if ($minimal) {
    $urpm->read_provides;
    $urpm->read_config;
    $urpm->filter_minimal_packages_to_upgrade(\%packages, $ask_choice);
} else {
    $urpm->filter_packages_to_upgrade(\%packages, $ask_choice, complete => $complete);
}

#- get out of package that should not be upgraded.
$urpm->deselect_unwanted_packages(\%packages);

#- package to install as a array of strings.
my @to_install;

#- check if there is at least one package to install that
#- has not been given by the user.
my $ask_user = $auto_select && scalar(keys %packages);
my $sum = 0;
foreach (keys %packages) {
    defined $packages{$_} and $ask_user = 1;
    my $info  = $urpm->{params}{depslist}[$_];
    $sum += $info->{size};
    push @to_install, "$info->{name}-$info->{version}-$info->{release}";
}
if (!$auto) {
    if ($ask_user) {
	my $msg = sprintf(_("To satisfy dependencies, the following packages are going to be installed (%d MB)"), toMb($sum));
	my $msg2 = _("Is it ok?");
	if ($X) {
	    my $p = join "\n", @to_install;
	    my $ok = _("Ok");
	    my $cancel = _("Cancel");
	    `gmessage -default $ok -buttons "$ok:0,$cancel:2" "$msg:\n$p\n\n$msg2"`;
	    $? and exit 0;
	} else {
	    $noexpr = _("Nn");
	    $yesexpr = _("Yy");
	    print SAVEOUT "$msg:\n@to_install\n$msg2" . _(" (Y/n) ");
	    <STDIN> =~ /[$noexpr]/ and exit 0;
	}
    }
}

$urpm->read_config;

my ($local_sources, $list, $local_to_removes) = $urpm->get_source_packages(\%packages);
unless ($local_sources || $list) {
    die("unable to get source packages, aborting");
    exit 1;
}
#- clean cache with file that are not necessary with this transaction.
#- TODO check not another urpmi is doing the same...
foreach (@$local_to_removes) {
    unlink $_;
}

my @sources = $urpm->upload_source_packages($local_sources, $list, ($X ? '' : 'force_local'), sub {
						my $msg = sprintf(_("Please insert the medium named \"%s\" on device [%s]"), @_);
						my $msg2 = _("Press enter when it's done...");
						if ($X) {
						    my $ok = _("Ok");
						    my $cancel = _("Cancel");
						    $msg =~ s/"/\\"/g;
						    `gmessage -default $ok -buttons "$ok:0,$cancel:2" "$msg"`;
						    !$?;
						} else {
						    print SAVEOUT "$msg\n$msg2 ";
						    <STDIN>; 1;
						}
					    });

install(@sources);
@sources or message_auto(_("everything already installed"));

#- this help flushing correctly by closing this file before (piped on tee).
#- but killing them is generally better.
fork() or do { sleep 1; kill 15, $pid_err, $pid_out };
close STDERR;
close STDOUT;

sub install {
    @_ or return;
	
    printf SAVEOUT sprintf(_("installing %s\n"), join(' ', @_));
    log_it(scalar localtime, " @_\n");
    $urpm->{log}("starting installing packages");
    system($X ? ("grpmi", $WID ? ("--WID=$WID") : ()) : ("rpm", $rpm_opt), @_);
    if ($?) {
	message(_("Installation failed"));
	$X and exit ($? >> 8) || 1; #- grpmi handles --nodeps and --force by itself, forward grpmi error.

	m|^/| && !-e $_ and exit 1 foreach @_; #- missing local file

	$noexpr = _("Nn");
	$yesexpr = _("Yy");
	print SAVEOUT _("Try installation without checking dependencies? (y/N) ");
	$force or <STDIN> =~ /[$yesexpr]/ or exit 1;
	$urpm->{log}("starting installing packages without deps");
	system("rpm", $rpm_opt, "--nodeps", @_);	

	if ($?) {
	    message(_("Installation failed"));
	    print SAVEOUT _("Try installation even more strongly (--force)? (y/N) ");
	    $force or <STDIN> =~ /[$yesexpr]/ or exit 0;
	    $urpm->{log}("starting force installing packages without deps");
	    system("rpm", $rpm_opt, "--nodeps", "--force", @_);	
	}
    }
}

sub toMb {
    my $nb = $_[0] / 1024 / 1024;
    int $nb + 0.5;
}

sub message { $X ? `gmessage -default Ok -buttons Ok "$_[0]"` : print SAVEOUT "$_[0]\n"; }

sub message_auto { $auto ? print SAVEOUT "$_[0]\n" : message($_[0]); }

#sub _ {
#    my $s = shift @_; my $t = translate($s);
#    $t && ref $t or return sprintf $t, @_;
#    my ($T, @p) = @$t;
#    sprintf $T, @_[@p];
#}
#sub translate { $I18N{$_[0]} || $_[0]; }

sub log_it {
    local *LOG;
    open LOG, ">>/var/log/urpmi.log" or die "can't ouptput to log file\n";
    print LOG @_;
}

sub untaint {
    my @r = ();
    foreach (@_) {
        /(.*)/;
	push @r, $1;
    }
    @r == 1 ? $r[0] : @r
}

sub load_po {
    my ($from, $to, $state, $fuzzy);

    my $lang = $ENV{LANGUAGE} || $ENV{LC_ALL} || $ENV{LC_MESSAGES} || $ENV{LANG};
    my $f; -e ($f = "/usr/share/locale/$_/LC_MESSAGES/urpmi.po") and last foreach split ':', $lang;

    local *F; open F, $f or return;
    foreach (<F>) {
	/^msgstr/ and $state = 1;
	/^msgid/  && !$fuzzy and $state = 2;

	if (/^(#|$)/ && $state != 3) {
	    $state = 3;
            s/\\n/\n/g foreach $from, $to;

	    if (my @l = $to =~ /%(\d+)\$/g) {
		$to =~ s/%(\d+)\$/%/g;
		$to = [ $to, map { $_ - 1 } @l ];
	    }
	    $I18N{$from} = $to if $from;
	    $from = $to = '';
	}
	$to .= (/"(.*)"/)[0] if $state == 1;
	$from .= (/"(.*)"/)[0] if $state == 2;

	$fuzzy = /^#, fuzzy/;
    }
}