#!/usr/bin/perl

## TODO: Filter out "directory ... could not be removed" (unless --verbose).
## TODO: Filter out "... conflicts with file from ..." (unless --verbose).

require "flush.pl";

$version = "1.0";
$install = 1;
$rpm_ok = 0;

# %shared_files;

@saved_configs = ();
@saved_configs_names = ();
@failed_packages = ();

print("Red Hat RPM system upgrader, version $version\n");
print("Copyright (c) 1995 Red Hat Software\n");

sub install_package {
    my ( $p, $n ) = @_;

    if ($install) {
#	print("Executing: rpm -Uvh --force $p\n");
	pipe(ERRIN, ERROUT);
	$pid = fork();
	if (! $pid) {
	    close(ERRIN);
	    close(STDERR);
	    open(STDERR, ">&ERROUT");
	    exec("rpm -Uvh --force $p");
	    print STDOUT "exec of rpm failed!\n";
	    exit(1);
	}
	close(ERROUT);
	while (<ERRIN>) {
	    print(STDERR $_);
	    if (/saved as/) {
		# This is a saved config file
		chop;
		split;
		unshift (@saved_configs, $_[1]);
		unshift (@saved_configs_names, $n);
	    }
	}
	wait;
	if ($?) {
	    push(@failed_packages, $p);
	}
    } else {
	print("Would run: rpm -Uvh --force $p\n");
    }
}

sub usage {
    print(STDERR "upgrade [--test] [--rpm-ok] [package]* [packagedirs]*\n");
    exit(1);
}

#################################################################
#
# First find available packages, and install any version of rpm
#

@NARGV = ();

foreach (@ARGV) {
    /^--rpm-ok/ && do {
	$rpm_ok = 1;
	next;
    };
    /^--test/ && do {
	$install = 0;
	next;
    };
    /^-.*/ && do {
	&usage;
    };
    push(@NARGV, $_);
}

@trydirs = ("RedHat/RPMS",
	    "/mnt/cdrom/RedHat/RPMS",
	    "/mnt/rhscd/RedHat/RPMS");

if (! @NARGV) {
    foreach (@trydirs) {
	if (-d $_) {
	    push(@NARGV, $_);
	    last;
	}
    }

    if (! @NARGV) {
	printf(STDERR "Could not find package directory:\n");
	foreach (@trydirs) {
	    printf(STDERR "\t$_\n");
	}
	exit(1);
    }
}

print("\nFinding packages...\n\n");

foreach (@NARGV) {
    if (-f $_) {
	push(@prospective_packages, $_);
    } elsif (-d $_) {
	opendir(D, $_);
	@files = readdir(D);
	closedir(D);
	foreach $f (@files) {
	    if ((-f "$_/$f") && ($f =~ /\.rpm/)) {
		push(@prospective_packages, "$_/$f");
	    }
	}
    } else {
	printf(STDERR "Ignoring: $_");
    }
}

if (! $rpm_ok) {
    $rpm_package = "";
    ($rpm_package) = grep(m|rpm-.*|, @prospective_packages);
    if ($rpm_package) {
	&install_package($rpm_package);
	print("\n");
    }
}

########################################################
#
# Query the system state and the available packages
#

print("Taking stock of things...\n\n");

# First get list of packages already installed
$x = 0;
printf("Packages installed: %5d", $x);
&flush(STDOUT);
open(F, "rpmq -qa|");
while (<F>) {
    chop;
    $installed_rpms{$_} = 1;
    m|(.*)-([^-]*-[^-]*)|;
    $installed_split{$1} = $2;
    $x++;
    printf("\b\b\b\b\b%5d", $x);
    &flush(STDOUT);
}
printf("\b\b\b\b\b%5d\n", $x);
close(F);

# Now get a list of all rpm-managed files
$x = 0;
printf("Files installed   : %5d", $x);
&flush(STDOUT);
open(F, "rpmq -qal|");
while (<F>) {
    chop;
    push(@installed_files, $_);
    $x++;
    if (int($x / 41) eq ($x / 41)) {
	printf("\b\b\b\b\b%5d", $x);
	&flush(STDOUT);
    }
}
printf("\b\b\b\b\b%5d\n", $x);
close(F);

# Now generate a map from new_files -> new_rpms
$x = 0;
printf("Packages available: %5d", $x);
&flush(STDOUT);
foreach $rpm (@prospective_packages) {
    open(F, "rpmq -qp $rpm|");
    $_ = <F>;
    close(F);
    chop;
    $rpmproper = $_;
    $new_rpms{$_} = $rpm;
    m|(.*)-([^-]*-[^-]*)|;
    $available_split{$1} = $2;
    
    open(F, "rpmq -qlp $rpm|");
    while (<F>) {
	chop;
	$shared_files{$_}++;
	$new_files{$_} = $rpmproper;
	s/\.so\..*/.so/;
	$new_files_noso{$_} = $rpmproper;
    }
    close(F);
    printf("\b\b\b\b\b%5d", $x);
    &flush(STDOUT);
    $x++;
}
printf("\b\b\b\b\b%5d\n", $x);
close(F);

###################################################################
#
# Determine rpms we need to install
#

printf("\nDetermining packages to upgrade...\n\n");

# Find new packages by just examining versions
$header = 0;
foreach (sort keys %installed_split) {
    if (defined($available_split{$_})) {
	if ($available_split{$_} ne $installed_split{$_}) {
	    if (!$header) {
		$header = 1;
		printf("%-35s%-20s%-20s\n", "Package", "New Version", "Old Version");
		printf("==================================================================\n");
	    }
	    printf("%-35s%-20s%-20s\n", $_, $available_split{$_}, $installed_split{$_});
	    $rpm = "$_-$available_split{$_}";
	    $rpms_to_install{$rpm} = 1;
	}
    }
}

# Find new packages by looking up file names (files that moved)
$header = 0;
foreach (@installed_files) {
    if (defined($new_files{$_})) {
	if ($shared_files{$_} > 1) {
	    next;
	}
	$rpm = $new_files{$_};
	if (defined($installed_rpms{$rpm})) {
	    # Not an upgrade -- already installed
	} else {
	    # Upgrade
	    if (defined($rpms_to_install{$rpm}) &&
		($rpms_to_install{$rpm} == 1)) {
		# Already got this one
	    } else {
		if (!$header) {
		    $header = 1;
		    printf("\n%-35s%-20s\n", "Files moved to", "Version");
		    printf("==================================================================\n");
		}
		$rpm =~ m|(.*)-([^-]*-[^-]*)|;
		printf("%-35s%-20s\n", $1, $2);
		$rpms_to_install{$rpm} = 1;
	    }
	}
    }
}

# And finally, find packages by looking up file names,
# ignoring any .so.(.*) stuff, for stuff that moved
# (shared libs that moved and changed version number)

$header = 0;
@installed_sofiles = grep(/.*\.so\..*/, @installed_files);
foreach (@installed_sofiles) {
    s/\.so\..*/.so/;
    if (defined($new_files_noso{$_})) {
	$rpm = $new_files_noso{$_};
	if (defined($installed_rpms{$rpm})) {
	    # Not an upgrade -- already installed
	} else {
	    # Upgrade
	    if (defined($rpms_to_install{$rpm}) &&
		($rpms_to_install{$rpm} == 1)) {
		# Already got this one
	    } else {
		if (!$header) {
		    $header = 1;
		    printf("\n%-35s%-20s\n", "Shared libs moved to", "Version");
		    printf("==================================================================\n");
		}
		$rpm =~ m|(.*)-([^-]*-[^-]*)|;
		printf("%-35s%-20s\n", $1, $2);
		$rpms_to_install{$rpm} = 1;
	    }
	}
    }
}

######################################################################
#
# Do something with what we just learned
#

$header = 0;
foreach (sort grep(m|rpm-.*|, (keys %rpms_to_install))) {
    if ($rpm_ok) {
	# If rpm_ok, then we skipped installing any rpm package
	# If !rpm_ok, then we already installed it at the beginning
	$header = 1;
	printf("\nUpgrading packages...\n\n");
	&install_package($new_rpms{$_}, $_);
    }
    delete($rpms_to_install{$_});
}
foreach (sort keys %rpms_to_install) {
    if (! $header) {
	$header = 1;
	printf("\nUpgrading packages...\n\n");
    }
    &install_package($new_rpms{$_}, $_);
    delete($rpms_to_install{$_});
}

if (@saved_configs) {
    printf("\nThe following config files have been changed.\n");
    printf("The originals are saved as <file>.orig\n");
    printf("\n%-35s%s\n", "Package", "File");
    printf("==================================================================\n");
    while (@saved_configs_names) {
	printf("%-35s%s\n",
	       shift(@saved_configs_names),
	       shift(@saved_configs));
    }
}

if (@failed_packages) {
    printf("\nThe following packages failed to install!\n\n");
    foreach (sort @failed_packages) {
	print "$_\n";
    }
}

exit(0);
