#!/usr/bin/perl -w
##############################################
# autoupdate                                 #
# Gerald Teschl <Gerald.Teschl@univie.ac.at> #
# Copyright: GPL                             #
# Version: 4.6.1                             #
##############################################

use Getopt::Long;
use File::Copy;
use File::Basename;
use IO::Socket;
use DB_File;
use POSIX;
use lib '/usr/lib/perl5/site_perl';
use autoupdate;

use strict;
use vars qw($showhelp $showversion $showlatest $showget $StoreDir $Host $Port $User $Pass
	$Protocol $LWPua $DldResult $BaseDir $DirWarnings $DldDirInclude $DldRpmInclude @AllowedArch @SubDirs
	@DldDirs @DldConfig @InExclude @DldDataBase @UpdInExclude @AllRPMs @SelectedRPMs @SelectedKernelRPMs
	%ResolveRPMs %NewResolveRPMs %BadRPMs @UpdatedRPMs @DownloadedRPMs @DepList @GetList
	@SaveGetList @KernelNames @RemovedKernel @RPMOutput %Provides);

# Needed when run from cron on older versions 
autoflush STDOUT;
autoflush STDERR;

# Default behavior
my $DoLock = 1;
my $DoDld = 0;
my $DoGet = 0;
my $DldAll = 0;
my $DldRecursive = 0;
my $DldMatch = 0;
my $BestMatch = 1;
my $RemoveBad = 0;
my $GetAll = 0;
my $DefaultPassive = 1;
my $Passive = $DefaultPassive;
my $DefaultFTPRetry = 0;
my $FTPRetry = $DefaultFTPRetry;
my $DefaultFTPWait = 10;
my $FTPWait = $DefaultFTPWait;
my $Firewall = "";
my $FirewallType = 0;
my $DefaultUseLWP = 0;
my $UseLWP = $DefaultUseLWP;
my $LWPProtocols = "http,https";
my $Proxy = "";
my $ProxyUser = "";
my $ProxyPass = "";
my @NoProxy = ();
my $Timeout = 0;
my $SSHCipher;
my $DldAddInstalled = 0;
my $DldUseDB = 1;
my $DoAddToDB = 0;
my $MergeDB = "";
my $CreateDB = "";
my $DoUpdate = 0;
my $DoInstall = 0;
my $DoKernel = 0;
my $KernelExt = "smp,enterprise,bigmem,debug";
my $DoInitRD = 1;
my $DoBoot = 0;
my $CheckSize = 0;
my $CheckSig = 0;
my $CheckVendor = 0;
my $CheckGPG = 0;
my $GPGHome = "";
my $Repackage = 0;
my $RpmBindings = "";
my $Resolve = 1;
my $ResolveUseRPMDB = 1;
my $Recursive = 0;
my $DefaultStoreByArch = 0;
my $MaxRecursive = 100;
my $MaxRedirect = 10;
my $PostUpdateScript = "";
my $PostDldScript = "";
my $DistVersion = "";
my $DistVersionNoDot = "";
my $DistLang = "en";
my @DistLanguages = ( $DistLang );
if ($ENV{'LANG'}) {
	@DistLanguages = ( $ENV{'LANG'} );
}
my $DistArch = "";
my $CleanUp = 0;
my $CleanUpKernel = 1;
my $BootManager = "";
my $BootScript = "";
my $BootConf = "";
my $BootAddAsNew = 0;
my $BootTag= "";
my $UseBootTag= 1;
my $DoMerge = 0;
my $MergeAll = 0;
my $MergeMatch = 0;
my $DoPurge = 0;
my $DefaultUser = "anonymous";
my $DefaultPass = "autoupdate\@localhost";
my $DataBaseName = "autoprovides.db";
my $DataBaseRO = 0;
my $DoLog = 0;
my $HTTPExt= "html,htm,php,asp";
my $DefaultHTTPSendHost = 0;
my $HTTPSendHost= $DefaultHTTPSendHost;
my $ShellEscapes = 1;
my $Test = 0;
my $Verbose = 0;
my $Quiet = 0;
my $Warnings = 1;
my $DefaultRPMNameWarnings = $Warnings;
my $RPMNameWarnings = $DefaultRPMNameWarnings;
my $DefaultFixRPMNames = 1;
my $FixRPMNames = 0;
my $Debug = 0;
my $DebugHTTP = 0;
my $DebugLWP = 0;
my $DebugFTP = 0;
my $DebugAUPM = 0;
my $DebugCOMPS = 0;
my $StunnelPort = "9090";
my $StunnelPid = "0";

# Default file locations
my $URL = "";
my $UpdateDir = "/var/spool/autoupdate";
my $InstallDir = "";
my $RPMDir = "";
my $ConfigDir = "/etc/autoupdate.d";
my $DldConfigDir = $ConfigDir;
my $ConfigFile = "$ConfigDir/autoupdate.conf";
my $DataBase = "";
my $Comps = "";
my $CompsDefault = "1";
my $LogFile= "/var/log/autoupdate.log";
my $LiloConf = "/etc/lilo.conf";
my $BootMnt = "";
my $GrubConf = "/boot/grub/grub.conf";
my $BootDir = "/boot";
my $StunnelPidFile="/var/run/stunnel";
my $LockFile="/var/lock/autoupdate.pid";

# Path to helper applications
my $RPM = "/bin/rpm";
my $MKINITRD = "/sbin/mkinitrd";
my $LILO = "/sbin/lilo";
my $GRUB = "/sbin/grub";
my $GRUBBY = "/sbin/grubby";
my $DF = "/bin/df";
my $PS = "/bin/ps";
my $STUNNEL = "/usr/sbin/stunnel";
my @StunnelArgs= ("-c", "-d", $StunnelPort, "-r");

# Internal variables (do not change).
my $Version = "4.6.1";
my $USAGE = "Usage:  auto(dld|get|upd|ins|mrg|prg) [ options ], where options are:

  --(no)dld                      Download new rpms.
  --(no)get                      Get new rpms.
  --url [user\@]host[/dir]       Download new rpms from this url.
  --(no) uselwp                  Use LWP for downloads.
  --dldconfig file1[,file2[,..]] Download configuration files to include.
  --dldall                       Download all new rpms from remote site.
  --(no)dldrecursive             Recursively descent through all directories at url.
  --dlddirinclude regexp         Include only dirs matching regexp during recursive descent.
  --dldrpminclude regexp         Include only rpms whose filename plus path match regexp.
  --(no)dldaddinstalled          Add installed rpms for comparison during download.
  --(no)dldmatch                 Apply Include/Exclude match during download.
  --(no)dldusedb                 Use provides database from dld site.
  --(no)getall                   Search for all updates during get mode.
  --(no)bestmatch                Only download the rpm which matches best.
  --(no)removebad                Remove bad rpms from updates.
  --(no)passive                  Use (don't use) passive ftp.
  --(no)httpsendhost             Send host name as part of http requests.
  --(no)addtodb                  Add rpms to provides database.
  --adddbentry name:capability   Add an entry to provides database.
  --mergedb file                 Merge given database with provides database.
  --createdb file                Create given database.
  --(no)update                   Update all rpms.
  --(no)install                  Install new rpms.
  --(no)kernel                   Install new kernel rpms.
  --(no)initrd                   Create initrd for new kernels if needed.
  --(no)boot                     Update boot manager.
  --(no)checksize                Check rpm size.
  --(no)checksig                 Check rpm signature.
  --(no)checkgpg                 Check gpg signature.
  --(no)checkvendor              Check that rpm vendor strings match during upgrade.
  --gpghome directory            Set GNUPGHOME to this directory.
  --(no)repackage                Use the repackage option of rpm.
  --(no)resolve                  Try to resolve dependencies.
  --(no)resolveuserpmdb          Try to resolve dependencies using the rpm data base.
  --(no)recursive                Recursively descent through all local subdirectories.
  --updatedir                    Directory containing the rpms to be updated.
  --installdir                   Directory containing the rpms to be installed.
  --rpmdir                       Directory containing the distribution rpms.
  --postupdatescript             Script to execute after upgrading rpms.
  --postdldscript                Script to execute after downloading rpms.
  --distversion                  Distribution version.
  --distlang                     Distribution language.
  --distarch                     Distribution architecture.
  --database                     Provides database to use.
  --(no)databasero               Do not write to provides database.
  --(no)lock                     Create a lock file.
  --(no)log                      Log updated rpms.
  --logfile                      Log file to use.
  --(no)cleanup                  Remove rpms after upgrade.
  --(no)cleanupkernel            Remove old kernel rpms.
  --(no)bootaddasnew             Add kernel images as new (rename the current to old).
  --include regexp               Include rpms matching regexp for upgrade.
  --exclude regexp               Exclude rpms matching regexp for upgrade.
  --(no)merge                    Merge all new rpms into the distribution.
  --(no)mergematch               Apply Include/Exclude match during merge.
  --(no)mergeall                 Merge even if no old version exists.
  --(no)purge                    Remove old versions from updates.
  --arch                         Architecture to use.
  --comps file                   Comps file to parse.
  --compsdefault 0|1|2           Type of packages from an (xml) comps group to include.
  --languages lang1[,lang2,...]  Languages to accept when parsing a comps file.
  --config file                  Configuration file.
  --compare ver1,ver2            Compare two version strings.
  --whatprovides                 Query the provides database.
  --requires rpm1[,rpm2,...]     List all requirements for the given rpms.
  --requiresxml rpm1[,rpm2,...]  Same as above but produces xml output.
  --test                         Test mode.
  --(no)verbose                  Be verbose.
  --(no)quiet                    Be quiet.
  --(no)warnings                 Display warnings.
  --(no)rpmnamewarnings          Display warnings about bad rpm names.
  --(no)fixrpmnames              Fix rpm names after donwloading.
  --debug 1|2|3                  Show debugging info.
  --(no)debughttp                Show debugging info for http actions.
  --(no)debugftp                 Show debugging info for ftp actions.
  --(no)debuglwp                 Show debugging info for lwp actions.
  --(no)debugaupm                Show debugging info for rpm selection.
  --(no)debugcomps               Show debugging info for comps parsing.
  --showlatest                   Select and display latest rpms.
  --showget                      Select and display get list.
  --checklocal 0|1|2             Check against local rpms during showlatest. 
  --version                      Print version and exit.
See autoupdate(8) for more information.
";
my $LastTry = 0;
my $CheckLocal = 0;
my $CheckOldRequirements= 0;
my $HaveLock = 0;
my $RPMsFromCL= 0;
my $BufSize = 4096;
my $ChangedBoot = 0;
my $Kernel = 0; # Number of kernels upgraded
my $cwd = "";
my $Compare = "";
my $WhatProvides = "";
my $PrintRequires = 0;
my $XML = 0;
my $AddDBEntry = "";
my $DataBaseMode = "";
my $CurrentKernel = "";
my @NoWarnDirs = ();
my $RPM_nogpg = 1;
my $RetGen = 0;
my $RetUpd = 0;
my $RetDld = 0;
# Make sure we understand the errors of rpm
$ENV{'LANG'}= "en_US";

######################
# Something bad happend
######################

sub fatal
{
	print STDERR "Fatal: " . $_[0] . "\n";
	unlink $LockFile if ($HaveLock);
	exit(127);
}

######################
# Strips unwanted stuff from a directory
######################

sub Strip_Dir
{
my $old;
do {
	$old=$_[0];
	$_[0] =~ s/\/[^\/]+\/\.\.$//g;
	$_[0] =~ s/^[^\/]+\/\.\.$/./g;
	$_[0] =~ s/\/[^\/]+\/\.\.\//\//g;
	$_[0] =~ s/\/\.?\//\//g;
	$_[0] =~ s/^\/\.\.\//\//g;
} while ($_[0] ne $old);
# Strip / from end if second argument is present
if ($_[1] and $_[0] =~ /^(.*)\/\.?$/) {
	$_[0] = $1;
}

}

######################
# Checks if an rpm is excluded
######################

sub IsExcluded
{
my $item= $_[0]->rpmname;
my $noexclude = 1;
my ($rex, $excl);

return 0 unless (@InExclude);

foreach my $tmp ( @InExclude ) {
	next unless ($tmp =~ /^([01])(.+)$/);
	($excl, $rex) = ($1, $2);
	$noexclude = 0 if ($excl);
	if ( $item =~/$rex/ ) {	
		return $excl;
		last;
	}
}

if ($noexclude) {
	# there was no exclude pattern
	return 1;
}

return 0;
}

######################
# Check a lang condition from comps file
######################

sub CheckLang
{
my $cond= shift;
my ($ok, $lang);

foreach my $item (@DistLanguages) {
	$ok= 1;
	if ($cond !~ /_/ and $item =~ /^([^_]*)_/) {
		$lang= $1;
	} else {
		$lang= $item;
	}
	if ($cond eq "!$lang") {
		$ok= 0;
	} elsif ($cond !~ /^!/ and $cond ne $lang) {
		$ok= 0;
	}
	return 1 if ($ok);
}
return 0;
}

######################
# Check an arch condition from comps file
######################

sub CheckArch
{
my $cond= shift;
my $ok= 1;
if ($cond eq "!$DistArch") {
	$ok= 0;
} elsif ($cond !~ /^!/ and $cond ne $DistArch) {
	$ok= 0;
}
return $ok;
}

######################
# Parse a comps file and list all rpms
# If some Sections are given as argument, only list rpms from these sections
######################

sub ParseComps
{
my ($xml, $nr, $status, $statusupdated, $brackets, $ingroup, $inglist, $inplist, $Section, $SectionName, $gotrpms, $allrpms);
my %RPMs= ();
my %Sections= ();
my %SectionStatus= ();
my %SectionStatusGuess= ();

unless ($Comps) {
	print STDERR "Error: No comps file set.\n";
	return ();
}

if ((! -f $Comps) and -f "$Comps.xml") {
	$Comps= "$Comps.xml";
}

unless (open(COMPS,"<$Comps")) {
	print STDERR "Error: Could not open: $Comps ($!)\n";
	return ();
}

my $line= <COMPS>;
chomp($line);
if ($line eq "4") {
	$xml= 0;
} elsif ($line eq '<?xml version="1.0"?>') {
	$xml= 1;
} else {
	print "Error: $Comps: Unsupported comps file!?\n";
	close(COMPS);
	return ();
}

if (!@_) {
	$SectionStatus{"+"}= 1;
} else {
	foreach my $item (@_) {
		if ($item eq "++") {
			$SectionStatus{"++"}= 1;
		} elsif ($item eq "+") {
			$SectionStatus{"+"}= 1;
		}
	}
}

print "Parsing comps file: $Comps\n" if $Debug;
print "Accepting languages: @DistLanguages\n" if ($Debug>1);

$nr=1;
$brackets= 0;
$ingroup= 0;
$inglist=0;
$inplist=0;

for my $line (<COMPS>) {
	$nr++;
	chomp($line);
	# Ignore blank lines
	next if ($line =~ /^\s*$/);
	# Strip white space
	if ($line =~ /^\s+(\S.*)$/) {
		$line= $1;
	}
	if ($line =~ /^(.*\S)\s+$/) {
		$line= $1;
	}
	if ($xml) {	# xml comps file
		# Ignore comments
		next if ($line =~ s/<!--.*-->//);
		next if ($line =~ /^\s*$/);
		# Look for group
		if ($line eq "<group>" or $line eq "</group>") {
			if ($inplist or $inglist) {
				print STDERR "Warning: $Comps: grouplist or packagelist not closed at line $nr.\n" if $Debug;
				$inglist= 0;
				$inplist= 0;
			}
			$Section= "";
			$SectionName= "";
			$status= 0;
			$statusupdated= 0;
			if ($line eq "<group>") {
				$ingroup= 1;
			} else {
				$ingroup= 0;
				print "  Installed rpms: $gotrpms of $allrpms.\n" if $DebugCOMPS;
				if ($allrpms and $gotrpms/$allrpms >= 0.9) {
					$SectionStatusGuess{$Section}= 1;
				}
			}
			next;
		}
		next unless ($ingroup);
		unless ($inglist or $inplist) {
			if ($line =~ /<default>(.+)<\/default>/) {
				# section status
				if ($1 =~ /[Tt]rue/ and $SectionStatus{"+"}) {
					$status= 1;
				}
				next;
			} elsif ($line =~ /<id>(.+)<\/id>/) {
				# regular section
				$gotrpms= 0;
				$allrpms= 0;
				$Section= $1;
				$Sections{$Section}= [];
			} elsif ($line =~ /<name>(.+)<\/name>/) {
				$SectionName= $1;
			} elsif ($line =~ /<name.*>(.+)<\/name>/) {
				# Don't care
			} elsif ($line =~ /<description.*>.*<\/description>/) {
				# Don't care
			} elsif ($line =~ /<uservisible>.*<\/uservisible>/) {
				# Don't care
			} elsif ($line =~ /<langonly>(.+)<\/langonly>/) {
				if ( CheckLang($1) ) {
					$status= 1;
				}
			} elsif ($line =~ /<grouplist>/) {
				$inglist= 1;
			} elsif ($line =~ /<packagelist>/) {
				$inplist= 1;
			} else {
				print "Warning: Unknown line $nr: '$line'\n" if $Debug;
			}
			if ( $line =~ /<packagereq/ ) { #missing packagelist
				$inplist= 1;
			} elsif ( $line =~ /<groupreq/ ) { #missing groulist
				$inglist= 1;
			} else {
				next;
			}
		}
		unless ($statusupdated) {
			# update section status
			unless ($Section) {
				print STDERR "Error: $Comps: no id tag found at line $nr.\n";
				$ingroup= 0;
				next;
			}
			$statusupdated= 1;
			if ($SectionStatus{"++"}) {
				$status= 1;
			} elsif ( IsElement($Section, @_) ) {
				$status= 1;
			} elsif ( $SectionName and IsElement($SectionName, @_) ) {
				$SectionStatus{$SectionName}= $status; #to avoid warnings
				$status= 1;
			}
			$SectionStatus{$Section}= $status;
			$SectionStatusGuess{$Section}= $status;
			print "  Section: $Section ($status)\n" if $DebugCOMPS;
		}

		if ($line =~ /<\/grouplist>/) {
			$inglist= 0;
			next;
		} elsif ($line =~ /<\/packagelist>/) {
			$inplist= 0;
			next;
		}
		
		if ($line =~ /<groupreq>(.+)<\/groupreq>/) {
			# section
			push(@{$Sections{$Section}},"@ $1");
			next;
		}
		if ($line =~ /<metapkg\s+type="(.+)">(.+)<\/metapkg>/) {
			# section
			next if (($1 eq "default" and $CompsDefault == 0) or ($1 eq "optional" and $CompsDefault <= 1));
			push(@{$Sections{$Section}},"@ $2");
			next;
		}
		if ($line =~ /<packagereq\s+type="(.+)"\s+requires="(.+)">(.+)<\/packagereq>/) {
			# regular rpm
			next if (($1 eq "default" and $CompsDefault == 0) or ($1 eq "optional" and $CompsDefault <= 1));
			push (@{$Sections{$Section}}, "? $2 {");
			push(@{$Sections{$Section}}, $3);
			push(@{$Sections{$Section}}, "}");
			next;
		}
		if ($line =~ /<packagereq\s+type="(.+)">(.+)<\/packagereq>/) {
			# regular rpm
			next if (($1 eq "default" and $CompsDefault == 0) or ($1 eq "optional" and $CompsDefault <= 1));
			$allrpms++ if ($1 eq "mandatory");
			my $cl= CheckLocal($2);
			print "    $2 " if $DebugCOMPS;
			if ($cl) {
				$gotrpms++ if ($1 eq "mandatory");
				print "($cl)\n" if $DebugCOMPS;
				# next;
			} else {
				print "(not present)\n" if $DebugCOMPS;
			}
			push(@{$Sections{$Section}}, $2);
			next;
		}
		print "Warning: Unknown line $nr: '$line'\n" if $Debug;
		
	} else { # version 4 comps file
		$line =~ s/--hide\s//;
		# Check conditions
		if ($line =~ /^\s*(\S.*):\s+(.+)$/) {
			$line = $2;
			my $cond= $1;
			if ($cond =~ /^\((.+)\)$/) {
				$cond=$1;
				my @Cond = split(/\s+and\s+/,$cond);
				my $ok=1;
				for $cond (@Cond) {
					if ($cond =~ /^lang (!?\w+)$/) {
						unless (CheckLang($1)) {
							$ok=0;
							last;
						}	
					} elsif ($cond =~ /^arch (!?\w+)$/) {
						unless (CheckArch($1)) {
							$ok=0;
							last;
						}	
					} else {
						print STDERR "Error: $Comps: Illegal condition in line $nr: $cond\n";
					}
				}	
				next unless $ok;
			} else {
				next unless CheckArch($cond);
			}
		}
		# Closing curly bracket
		if ($line =~ /^\s*\}\s*$/) {
			$brackets--;
			if ($brackets == 0) {
				print "  Installed rpms: $gotrpms of $allrpms.\n" if $DebugCOMPS;
				if ($allrpms and $gotrpms/$allrpms >= 0.9) {
					$SectionStatusGuess{$Section}= 1;
				}
				$Section= "" ;
				next;
			} elsif ($brackets < 0) {
				print STDERR "Error: $Comps: Unmatched '}' at line $nr.\n";
				$RetGen= 1;
				return ();
			}
		} elsif ($line =~ /^\s*\?\s+(\S.*\S)\s+\{\s*$/) {
			# conditional section
			$brackets++;
		} elsif ($line =~ /([01])\s+(\S.*)\s+{/) {
			# regular section
			$status= 0;
			if ($SectionStatus{"++"}) {
				$status= 1;
			} elsif ( IsElement($2, @_) ) {
				$status= 1;
			} elsif ($SectionStatus{"+"}) {
				$status= $1;
			}
			if ($Section or $brackets != 0) {
				print STDERR "Error: $Comps: New section '$2'.\n";
				print STDERR "But old section(s) not closed in line $nr!\n";
				$RetGen= 1;
				return ();
			}
			$brackets++;
			$gotrpms= 0;
			$allrpms= 0;
			$Section= $2;
			$Sections{$Section}= [];
			$SectionStatus{$Section}= $status;
			$SectionStatusGuess{$Section}= $status;
			print "  Section: $Section ($status)\n" if $DebugCOMPS;
			next;
		} elsif ($line =~ /^\s*@/) {
			# section to include
		} elsif($brackets == 1 and $line =~ /^\s*(\S+)\s*$/) {
			# regular rpm
			$allrpms++;
			my $cl= CheckLocal($1);
			print "    $1 " if $DebugCOMPS;
			if ($cl) {
				$gotrpms++;
				print "($cl)\n" if $DebugCOMPS;
				# next;
			} else {
				print "(not present)\n" if $DebugCOMPS;
			}
		}
		unless ($Section) {
			print STDERR "Error: $Comps: Non blank line but no section open in line $nr\n";
			next;
		}
		push(@{$Sections{$Section}},$line)
	}
}
close(COMPS);

# See if we have found all requested sections
foreach $Section (@_) {
	next if ($SectionStatus{$Section});
	print STDERR "Error: Section '$Section' not found in comps file.\n";
	$RetGen= 1;
}

# Build an array of all selected sections
my @SectionList= ();
foreach $Section (keys %Sections) {
	next unless ($SectionStatus{$Section});
	push (@SectionList, $Section);
}
# Pull in required sections
foreach $Section (@SectionList) {
	$status= 1;
	$brackets= 0;
	my $sbrackets= 0;
	foreach $line (@{$Sections{$Section}}) {
		if ($line =~ /\?\s(.+)\s\{/) {
			foreach my $tmp (split(",", $1)) {
				unless ($SectionStatusGuess{$tmp}) {
					$status= 0;
					$sbrackets= $brackets;
				}
			}
			$brackets++;
		} elsif ($line =~ /\}/) {
			$brackets--;
			if ($status == 0 and $sbrackets == $brackets) {
				$status= 1;
			}
		} elsif ($status and $line =~ /@\s+(.+)\s*$/) {
			next if ($SectionStatus{$1}); # already have it
			$SectionStatus{$1}= "1";
			$SectionStatusGuess{$1}= "1";
			push (@SectionList, $1);
		}
	}
	if ($brackets != 0) {
		print STDERR "Error: $Comps: Unmatched brackets in Section '$Section'.\n";
		return ();
	}
}

foreach $Section (@SectionList) {
	$status= 1;
	$brackets= 0;
	my $sbrackets= 0;
	foreach $line (@{$Sections{$Section}}) {
		next if ();
		if ($line =~ /\?\s(.+)\s\{/) {
			foreach my $tmp (split(",", $1)) {
				unless ($SectionStatusGuess{$tmp}) {
					$status= 0;
					$sbrackets= $brackets;
				}
			}
			$brackets++;
		} elsif ($line =~ /\}/) {
			$brackets--;
			if ($status == 0 and $sbrackets == $brackets) {
				$status= 1;
			}
		} elsif ($line =~ /@\s+(.+)\s*$/) {
			next unless ($status);
			next if ($SectionStatus{$1}); # already have it
			$SectionStatus{$1}= "1";
			$SectionStatusGuess{$1}= "1";
			push (@SectionList, $1);
		} elsif ($line =~ /^\s*(\S+)\s*$/) {
			$RPMs{$1}= "1" if ($status);
		} else {
			print STDERR "Error: $Comps: Unknown line '$line' in Section '$Section'.\n";
		}
	}
}

return keys %RPMs
}

######################
# Set up @GetList from comps
######################

sub SetGetList
{

return 1 unless (@SaveGetList);

my $item;
my @tmp=();
my @sections=();

print "Setting up fake local list for get mode.\n" if $Debug;

foreach $item (@SaveGetList) {
	if ( $item =~ /^\@(.+)$/) {
		push(@sections, $1);
		next;
	}
	push(@tmp, $item);
}
if (@sections) {
	@GetList= ParseComps(@sections);
	foreach $item (@tmp) {
		push(@GetList, $item) unless (IsElement($item, @GetList));
	}
} else {
	@GetList=@tmp;
}
@SaveGetList= @GetList; #Don't parse again

return 1 unless (@GetList);
my $haveall = 1;
if (!$BestMatch and IsElement("kernel", @GetList)) {
	# kernel triggers kernel-smp,....
	for $item (@KernelNames) {
		next if ($item eq "kernel");
		next if (IsElement($item, @GetList));
		push(@GetList, $item);
	}
}
for $item (@GetList) {
	if (CheckLocal($item)) {
		print "Warning: Already have $item.\n" if $Debug;
	} else {
		$haveall = 0;
		AddLocalRPMs("r", "$item-0.0-0.noarch.rpm");
	}
}

return $haveall;
}

######################
# Open the provides data base
######################

sub OpenProvides
{
my $mode= shift;
my $flags;
$mode= "" unless ($mode);

if ($mode eq "rw" and $DataBaseRO) {
	$mode= "ro";
}

return $mode if ($mode eq $DataBaseMode);

if ($mode eq "ro") {
	$flags=O_RDONLY;
} elsif ($mode eq "rw") {
	$flags= O_RDWR;
	unless ( -f $DataBase ) {
		print "Creating provides database: $DataBase\n" if $Verbose;
		if (tie(%Provides, 'DB_File', $DataBase, O_CREAT, 0644)) {
			untie(%Provides);
		} else {
			fatal("Could not create $DataBase: $!\n");
		}
	}
} else {
	print "Closing provides database.\n" if $Debug;
	untie(%Provides);
	$DataBaseMode= "";
	return undef;
}

print "Opening provides database ($mode): $DataBase\n" if $Debug;
if (tie(%Provides, 'DB_File', $DataBase, $flags, 0)) {
	$DataBaseMode= $mode;
	return $DataBaseMode;
} else {
	print STDERR "Error: Failed to open $DataBase: $!\n";
	$DataBaseRO= 1 if ($mode eq "rw"); # Do not try again
	$RetGen= 1;
	$Resolve= 0 unless ($DataBaseMode eq "ro");
}

return undef;
}

######################
# Merge provides data base
######################

sub MergeProvides
{
my $file= shift;
my (%NewProvides, $item);

return 1 unless (-f $file );
unless (OpenProvides("rw")) {
	print "Error: Could not merge $file to provides database.\n";
	$RetGen= 1;
	return 1;
}

if (tie(%NewProvides, 'DB_File', $file, O_RDONLY, 0)) {
	for $item (keys %NewProvides) {
		$Provides{$item}= $NewProvides{$item};
	}
	untie(%NewProvides);
} else {
	print STDERR "Error: Could not open $file: $!\n";
	$RetGen= 1;
	return 1;
}

return 0;
}




######################
# Get RPM Header objects for rpms
######################

sub GetRPMHeaders
{
my ($item, $rpm, $esize, $asize, $thistype);
my $type= shift;
my @rpms= ();

foreach $item ( @_ ) {
	if ($type eq "t" or $type eq "c") {
		unless (-f $item) {
			print STDERR "Error: No such file: $item\n";
			next;
		}
		$thistype= "f";
	} else {
		$thistype= $type;
	}
	if ($type eq "c") {
		print "Checking: " . basename($item) . "\n" if ($Debug>1);
		my $queryformat= "%{NAME}-%{VERSION}-%{RELEASE}.";
		if ($item =~ /src\.rpm$/) {
			$queryformat .= "src.rpm\\n";
		} else {
			$queryformat .= "%{ARCH}.rpm\\n";
		}
		unless ( CallRPM(\@RPMOutput, "-qvvp", "--queryformat", $queryformat, $item) ==0 ) {
			print "Warning: Could not query $item\n" if ($Warnings);
			$BadRPMs{$item}= "1";
			next;
		}
		$rpm= undef;
		$esize= undef;
		$asize= undef;
		foreach my $line (@RPMOutput) {
			if ($line =~ /^D:\s+Expected size\s*:\s+(\d+)/i) {
				$esize= $1;
				next;
			} elsif ($line =~ /^D:\s+Actual size\s*:\s+(\d+)/i) {
				$asize= $1;
				next;
			} elsif ($line =~ /^D:\s+Header \+ Archive:\s+(\d+)/) {
				$asize= $1;
				next;
			} elsif ($line =~ /^D:.*:\s+MD5 digest:\s+OK/) {
				$esize= $asize;
				next;
			} elsif ($line =~ /^D:.*\s+signature:\s+OK/) {
				$esize= $asize;
				next;
			} elsif ($line =~ /^D:\s/) {
				next;
			} elsif ($line =~ /^[Ww]arning:\s/) {
				next;
			} elsif ($line =~ / failed/) {
				next;
			} elsif ($line !~ /\.rpm$/) {
				next;
			}
			$rpm= $line;
			last;
		}
		chomp($rpm);
		unless (defined $esize and defined $asize and defined $rpm) {
			print STDERR "Error: Could not check $item\n";
			next;				
		}
		unless ($asize == $esize) {
			print "Warning: " . basename($item) . ": Incorrect size.\n" if ($Warnings);
			$BadRPMs{$item}= "1";
			next;
		}
		if ($BadRPMs{$item}) {
			delete $BadRPMs{$item};
		}
		if ($item =~ /^(.*\/)([^\/]+)$/) {
			$rpm= $1 . $rpm;
		}			
		if ($rpm ne $item) {
			if ( ! $FixRPMNames ) {
				print STDERR "Warning: File name for " . basename($item) . " should be ". basename($rpm) . "\n" if $RPMNameWarnings;
			} elsif ( -f $rpm ) {
				print STDERR "Warning: New rpm " . basename($item) . " is identical to old rpm " . basename($rpm) . "\n" if $Warnings;
				unlink ($item);
				next;
			} else {
				print STDERR "Warning: Renaming " . basename($item) . " to " . basename($rpm) . "\n" if $Warnings;
				unless (rename($item, $rpm)) {
					print STDERR "Error: Failed to rename: $item to $rpm ($?)\n";
					$RetGen= 1;
					next;
				}
				$item= $rpm;
			}
		}
	}
	$rpm= Header->new($item, $thistype);
	unless ($rpm) {
		print STDERR "Warning: Illegal rpm name: $item\n" if $RPMNameWarnings;
		next;
	}
	if ( IsExcluded($rpm) ) {
		print "Excluded: " . $rpm->rpmname . "\n" if $Debug;
		next;
	}
	push(@rpms, $rpm);
}
return @rpms;
}


######################
# Check RPM signature
######################

sub Check_Sig
{
return 0 unless ($CheckSig or $CheckGPG);
my $item = shift;
my @flags = ("--checksig");
push(@flags, "--nogpg", "--nopgp") if (!$CheckGPG and $RPM_nogpg);

if ( CallRPM(\@RPMOutput, @flags, $item->filename) != 0) {
	print "Bad signature: ". $item->filename ."\n";
	unlink ($item) if ($RemoveBad and !$Test);
	return 1;
}

if ( $CheckGPG and $RPMOutput[0] !~ /gpg/ ) {
    print "No gpg signature: ". $item->filename ."\n";
    return 1;
}

print "  Good signature: ". $item->rpmname . "\n" if ($Debug >1);
return 0;
}

######################
# Check that Vendor for new rpm is same as installed
######################

sub Check_Vendor
{
return 0 unless ($CheckVendor);
my $item = shift;
my ($hdr, $newvendor, $oldvendor);

unless ($hdr= GetLocal($item->name)) {
	# No previous version
	return 0;
}
$newvendor= $hdr->gettag("VENDOR");
$oldvendor= $item->gettag("VENDOR");  

return 0 unless ($oldvendor);
return 1 unless ($newvendor);

# Simplify vendor of new rpm
# "Inc." seems to come and go with vendors.  Also remove leading commas,
# spaces and remove trailing dots

$newvendor =~ s/\s*\,\s*Inc\..*$//i;

unless ($oldvendor =~ /^$newvendor.*/i) {
	print "Warning: Vendor strings do not match: " . $item->rpmname . "\n" if $Warnings;
	return 1;
}

print "  Vendor strings match: " . $item->rpmname . "\n" if ($Debug >1);
return 0;
}

######################
# Convert a requirement to an rpm name using the rpm data base
######################

sub Requirement2NameRPMDB
{
	my $reqmnt= $_[0];
	my $name;
	my @RPMOut;

	if (CallRPM(\@RPMOut, "-q", "--queryformat", "%{NAME}\n", "--whatprovides", $reqmnt) == 0) {
		$name= $RPMOut[0];
	} elsif (CallRPM(\@RPMOut,"-q", "--queryformat", "%{NAME}\n", "--redhatprovides", $reqmnt) == 0) {
		$name= $RPMOut[0];
	}
	return undef unless ($name);
	return $name unless ($DataBaseRO);
	OpenProvides("rw");
	$Provides{$reqmnt}= $name;
	OpenProvides("ro");

	return $name;
}

######################
# Convert a requirement to an rpm name
######################

sub Requirement2Name
{
my $reqmnt= $_[0];
my $name= $Provides{$reqmnt};

return $name if ($name);
if ($reqmnt =~ /^rpmlib\(.*\)/) { #RPM stuff
	return undef;
}

if ($ResolveUseRPMDB) {
	$name= Requirement2NameRPMDB($reqmnt);
	return $name if ($name);
}

print "Warning: Requirement \"$reqmnt\" is not in provides database.\n" if $Debug;
# Let's try our best
if ($reqmnt =~ /lib\/(.+)$/) { #Strip path from libraries
	$reqmnt= $1;
} elsif ($reqmnt =~ /^\//) { #File
	return undef;
} elsif ($reqmnt =~ /perl\(/) { #Perl stuff
	return undef;
}
if ($Provides{$reqmnt}) {
	$name=$Provides{$reqmnt};
} elsif ($reqmnt =~ /^(.+)\.so(\.\d+)*(\(.*\))?$/) { #Library
	$name = $1;
	if ($name =~ /^(.+)-[\d.]+$/) { #Strip version
		$name= $1;
	}
} else  {
	$name = $reqmnt;
}

return $name;
}

######################
# Query rpms for other rpms they require.
######################

sub Get_Requirements
{
my ($item, $rpm, $name, $version, @RPMOut);
my @requirements =();

print "  Getting requirements:\n" if ($Debug >1);
foreach $rpm (@_) {
	print "    " . $rpm->rpmname ."\n" if ($Debug >1);
	foreach $item ($rpm->requires) {
		chomp($item);
		next if ($item =~ /^\s*$/);
		next if ($item =~ /^\s*warning:/i);
		next if ($item =~ /^\s*error:/i);
		if ($item =~ /^(\S+)\s+[>=]+\s+(\S+)\s*$/) {
			$item= $1;
			$version= $2;
			unless ( $GetAll or IsElement($item, @GetList) ) {
				$name= $Provides{$item};
				if ($name and $name eq $item) {
					$version .= "-0" unless ($version =~ /-/);
					my $tmp= Header->new("$name-$version.noarch.rpm", "r");
					if (CompareLocal($tmp) > 0) {
						print "      Requirement: Newer version of $name\n" if $Debug;
						push(@GetList, $name);
					}
				}
			}
		} elsif ($item =~ /^(\S+)\s/) {
			$item= $1;
		}
		$name= Requirement2Name($item);
		next unless ($name);
		next if (IsElement($name, @requirements));
		if ($DldAddInstalled) {
			next if ( CallRPM(\@RPMOut, "-q", "--whatprovides", $item)==0 );
			push(@GetList, $name) unless (IsElement($name, @GetList));
			if (CheckLocal($name)) {
				print"      Requirement: Newer version of $item ($name)\n" if $Debug;
				next;
			}
		}
		next if (CheckLocal($name)); #Already have item
		AddLocalRPMs("r", "$name-0.0-0.noarch.rpm"); #Pretend we have an older version
		push(@GetList, $name) unless (IsElement($name, @GetList));
		print "      Requirement: $item ($name)\n" if $Debug;
		push(@requirements, $name);
	}
}

return @requirements;
}

######################
# Query rpms for what they provide.
######################

sub Get_Provides
{
my ($rpm, $item, $file);
my $local=0;
my $flags = "-qp";

unless (OpenProvides("rw")) {
	print STDERR "Error: Failed to update provides database.\n";
	$RetGen= 1;
	return;
}

unless (@_) { # Use installed rpms if no rpms given
	$local=1;
	$flags = "-q";
	open(RPM,"$RPM -qa --queryformat \"%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\\n\"|") || fatal("Could not query local rpms ($!).");
	@_= <RPM>;
	close(RPM);	
}

print "  Getting provides:\n" if ($Debug >1);
foreach $rpm (@_) {
	if ($local) {
		chomp($rpm);
		$rpm= Header->new($rpm, "l");
		next unless ($rpm);
		$file= $rpm->name;
	} else {
		$file= $rpm->filename;
	}
	print "    " . $rpm->rpmname . "\n" if ($Debug >1);
	foreach $item ($rpm->provides) {
		chomp($item);
		next if ($item =~ /^\s*$/);
		next if ($item =~ /^\s*warning:/i);
		next if ($item =~ /^\s*error:/i);
		if ( $item =~ /^(\S+)/ ) {
			$item= $1;
			if ($rpm->name =~ "^kernel-" and $Provides{$item} and $Provides{$item} eq "kernel") {
				# don't overwrite kernel with kernel-smp
			} elsif ($rpm->name =~ /-debug(-.+)?$/ and $Provides{$item}) {
				# don't overwrite glibc with glibc-debug
			} else {
				$Provides{$item}= $rpm->name;
			}
		} else {
			print STDERR "Warning: Unknown provides: '$item'" if $Warnings;
		}
	}
	# Add executables as well
	foreach $item ($rpm->files) {
		chomp($item);
		next unless ($item =~ /^\/./);
		next unless ($item =~ /\/s?bin\/./ or $item =~ /^\/usr\/games\/[^\/]+$/ or $item =~ /^\/etc\/./);
		$Provides{$item}= $rpm->name;
	}
}

return 0;
}

######################
# Execute post script
######################

sub Do_PostScript
{

return 0 unless $_[0];

my @script = split(/\s+/,$_[0]);
print "Executing post script:" if $Verbose;
print " @script:" if $Debug;
print "\n" if $Verbose;
unless ( system(@script)==0 ) {
	print STDERR "Warning: $script[0] failed: $!\n" if $Warnings;
	return 1;
	}

return 0
}

######################
# Log messages
######################

sub Do_Log
{

return 0 unless (@_);
return 0 if ($Test and $LogFile ne "-");

my $now= localtime();
if ($now =~ /^\S+\s+(.*)\s+\S+$/) {
	$now= $1;
}
print "Writing log file: $LogFile ($now)\n" if ($Debug);
if ($LogFile eq "syslog" ) {
	Sys::Syslog::setlogsock('unix');
	unless (openlog('autoupdate', 'cons,pid', 'user')) {
		print STDERR "Error: Could not open syslog: $!\n";
		$RetGen= 1;
		return 1;
	}
	for my $item (@_) {
		syslog('info', $item);
	}
	closelog();
} else {
	unless (open(LOGFILE,">>$LogFile")) {
		print STDERR "Error: Could not append to $LogFile: $!\n";
		$RetGen= 1;
		return 1;
	}
	for my $item (@_) {
		print LOGFILE "$now: $item\n";
	}
	close(LOGFILE);
}

return 0
}

######################
# Get local RPM list
######################

sub Get_Local_List
{
my ($item);

for $item (@_) {
	next unless ($item);
	print "Getting local rpms for comparison: $item\n" if $Debug;
	chdir($item) || fatal("Could not cd to: $item ($!)");
	AddLocalRPMs("f", Get_RPMs($item) );
}

}

######################
# Get a list of all local rpms
######################

sub Get_RPMs
{
my @dirs = @_;
my ($dir, $item);
my @rpms = ();

print "  Getting list of local rpms:\n" if ($Debug>1);

foreach $dir (@dirs) {
	print "    $dir\n" if ($Debug>1);
	unless ( -d $dir ) {
		my $warn= 1;
		foreach $item (@NoWarnDirs) {
			if ($dir eq $item) {
				$warn= 0;
				last;
			}
		}
		next unless ($Debug or $warn);
		print STDERR "Error: No such directory: $dir\n";
		$RetGen= 1;
		next;
	}
	Strip_Dir($dir,"1");
	if ($Recursive) {
		# Recursively descent through all of the sub-directories
		foreach $item (glob("$dir/*")) {
			if ( -d $item ) {
				if ( -l $item ) {
					$item= readlink($item);
					$item= "$dir/$item" unless ($item =~ /^\//);
					Strip_Dir($item,"1");
				}
				# Count the number of subdirs
				if ( $item =~ tr@/@@ > $MaxRecursive ) {
					print STDERR "Warning: Maximum number of subdirs exceeded for: $item\n" if $Warnings;
				} elsif (! IsElement($item, @dirs) ) {
					push(@dirs, $item);
				}
			} elsif ( $item =~ /\.rpm$/ ) {
				push (@rpms,$item);
			} elsif ( $item =~ /autoprovides[^\/]*-[^\/\-]+\.db$/ ) {
				push (@DldDataBase,$item);
			}			
		}
	} else {
		push(@rpms,glob("$dir/*.rpm"));
		push(@DldDataBase,glob("$dir/autoprovides*.db"));
	}
}
	
return @rpms;
}

######################
# Get a list of all remote rpms (ftp)
######################

sub Get_RPMs_ftp
{

my $ftp = shift;
my @dirs = @_;
my ($dir, $item);
my @rpms = ();


foreach $item (@dirs) {
	if ($item =~ /^\.(\/.*)$/) {
		$item= $ftp->pwd() . $1;
	}
	if ($item eq ".") {
		$item= $ftp->pwd();
	}
}		

print "  Getting list of remote rpms (ftp):\n" if $Debug;

foreach $dir (@dirs) {
	print "    $dir\n" if $Debug;
	unless ( $ftp->cwd($dir) ) {
		my $warn= 1;
		foreach $item (@NoWarnDirs) {
			if ($dir eq $item) {
				$warn= 0;
				last;
			}
		}
		next unless ($Debug or $warn);
		print STDERR "Error: Failed to check directory at $Host: ";
		$RetDld= 1;
		if ($ftp->message !~ /\Q$dir/) {
			print STDERR "$dir: ";
		}
		if ($ftp->message) {
			print STDERR $ftp->message;
		} else {
			print STDERR "Timeout (?)\n";
		}
		next;
	}
	if ($dir =~ /^(.*)\/$/) {
		$dir=$1;
	}
	if ($DldRecursive) {
		# Recursively descent through all of the sub-directories
		foreach $item ( $ftp->dir() ) {
			# |Attributes|HLs|Owner|Group|Size|Date|Name|
			my ($attr,$filename) = (split(/\s+/,$item))[0,-1];
			next if ($filename eq "." or $filename eq "..");
			if ($filename =~ /^autoprovides[^\/]*-[^\/\-]+\.db$/ and $attr !~ /^d/) {
				# database
				push(@DldDataBase, "$dir/$filename");
				next;
			}
			if ($filename =~ /\.rpm$/ and $attr !~ /^d/) {
				# RPM file
				push(@rpms, "$dir/$filename");
				next;
			}
			if ( $attr =~ /^d/ ) {
				# Directory
			} elsif ( $attr =~ /^l/ ) {
				# Link
				next unless ($ftp->cwd($filename));
				$ftp->cwd($dir);
			} else {
				next;
			}
			$filename="$dir/$filename";
			# Count the number of subdirs
			if ( $filename =~ tr@/@@ > $MaxRecursive ) {
				print STDERR "Warning: Maximum number of ftp subdirs exceeded for: $item\n" if $Warnings;
			} else {
				push(@dirs,$filename);
			}
		}
	} else {
		foreach $item ( $ftp->ls() ) {
			if ($item =~ /^autoprovides[^\/]*-[^\/\-]+\.db$/) {
				# database
				push(@DldDataBase, "$dir/$item");
			} elsif ($item and $item =~ /\.rpm$/) {
				push(@rpms, "$dir/$item");
			}	
		}
	}
}

return @rpms;
}

######################
# Get a list of all remote rpms (sftp)
######################

sub Get_RPMs_sftp
{

my $ftp = shift;
my @dirs = @_;
my ($dir, $item, $stat);
my @rpms = ();


print "  Getting list of remote rpms (sftp):\n" if $Debug;

foreach $dir (@dirs) {
	print "    $dir\n" if $Debug;
	$stat= $ftp->do_stat($dir);
	unless ( $stat ) {
		my $warn= 1;
		foreach $item (@NoWarnDirs) {
			if ($dir eq $item) {
				$warn= 0;
				last;
			}
		}
		next unless ($Debug or $warn);
		print STDERR "Error: Failed to check directory at $Host: ";
		$RetDld= 1;
		next;
	}
	if ($dir =~ /^(.*)\/$/) {
		$dir=$1;
	}
	if ($DldRecursive) {
		# Recursively descent through all of the sub-directories
		
		foreach $item ( $ftp->ls($dir) ) {
			$item= $item->{'longname'};
			# |Attributes|HLs|Owner|Group|Size|Date|Name|
			my ($attr,$filename) = (split(/\s+/,$item))[0,-1];
			next if ($filename eq "." or $filename eq "..");
			if ($filename =~ /^autoprovides[^\/]*-[^\/\-]+\.db$/ and $attr !~ /^d/) {
				# database
				push(@DldDataBase, "$dir/$filename");
				next;
			}
			if ($filename =~ /\.rpm$/ and $attr !~ /^d/) {
				# RPM file
				push(@rpms, "$dir/$filename");
				next;
			}
			if ( $attr =~ /^d/ ) {
				# Directory
			} elsif ( $attr =~ /^l/ ) {
				next unless( $stat= $ftp->do_stat($item) );
				$stat= $stat->{'perm'} >> 12;
				next unless( $stat == "4");
			} else {
				next;
			}
			$filename="$dir/$filename";
			# Count the number of subdirs
			if ( $filename =~ tr@/@@ > $MaxRecursive ) {
				print STDERR "Warning: Maximum number of ftp subdirs exceeded for: $item\n" if $Warnings;
			} else {
				push(@dirs,$filename);
			}
		}
	} else {
		foreach $item ( $ftp->ls($dir) ) {
			$item= $item->{'filename'};
			if ($item =~ /^autoprovides[^\/]*-[^\/\-]+\.db$/) {
				# database
				push(@DldDataBase, "$dir/$item");
			} elsif ($item and $item =~ /\.rpm$/) {
				push(@rpms, "$dir/$item");
			}	
		}
	}
}

return @rpms;
}

######################
# Get a list of all remote rpms (http/lwp)
######################

sub Get_RPMs_http
{

my @files = @_;
my ($dir, $file, $rfile, $item, @filelist);
my @rpms = ();

print "  Getting list of remote rpms ($Protocol):\n" if $Debug;

foreach $file (@files) {
	$file="/$file" unless ($file =~ /^\//);
	print "    $file\n" if $Debug;
	if ($UseLWP) {
		@filelist= Get_LWP($file, "p");
	} else {
		@filelist= Get_HTTP($file, "p");
	}
	$rfile= shift(@filelist);
	unless ($rfile) {
		my $warn= 1;
		foreach $item (@NoWarnDirs) {
			if ($file eq $item) {
				$warn= 0;
				last;
			}
		}
		next unless ($Debug or $warn);
		print STDERR "Error: Failed to check $file at $Host: $DldResult\n";
		$RetDld= 1;
		next;	
	}
	$file= $rfile;
	if ($file =~ /^(.*\/)[^\/]*$/) {
		$dir=$1;
	}
	for $item (@filelist) {
		next unless ($item);
		next if ($item =~ "^mailto:");
		$item =~ s/^$Protocol:\/\/$Host//;
		$item =~ s/#.*$//; #Remove labels
		$item =~ s/\/\?.*$/\//; #Remove queries
		if ($item =~ /^\w+:\/\//) {
			next unless ($UseLWP and $item =~ /^(ftp|http|https):\/\/.*\.rpm$/);
			next if ($DldRpmInclude and $item !~ /$DldRpmInclude/);
			push(@rpms,$item) unless (IsElement($item, @rpms));
			next;
		}
		$item= "$dir$item" unless ($item =~ /^\//);
		Strip_Dir($item);
		if ($item =~ /autoprovides[^\/]*-[^\/\-]+\.db$/) {
			push(@DldDataBase,$item) unless (IsElement($item, @DldDataBase));
			next;
		}
		if ($item =~ /\.rpm$/) {
			push(@rpms,$item) unless (IsElement($item, @rpms));
			next;
		}
		next unless $DldRecursive;
		next if ($item !~ /^$dir/);
		next if ($DldDirInclude and $item !~ /$DldDirInclude/);
		my $maxrecursive = $MaxRecursive + ($dir =~ tr@/@@);
		if ($item =~ /\/$/) {
			# Count the number of subdirs
			if ( $item =~ tr@/@@ > $maxrecursive ) {
				print STDERR "Warning: Maximum number of subdirs exceeded for: $item\n" if $Warnings;
			} else {
				push(@files, $item) unless (IsElement($item, @files));
			}
			next;
		}
		foreach my $ext (split(/,/,$HTTPExt)) {
			if ($item =~ /\.$ext$/) {
				if ( $item =~ tr@/@@ > $maxrecursive ) {
					print STDERR "Warning: Maximum number of subdirs exceeded for: $item\n" if $Warnings;
				} else {
					push(@files, $item) unless (IsElement($item, @files));
				}
				last;
			}					
		}
	}
}

return @rpms;
}

######################
# Get a file via LWP
# Saves a file to disk resp. parses html files for links
######################

sub Get_LWP
{
my $location= shift;
my $action= shift; # s=save p=parse
my ($file, $res, $url);
$DldResult= "";
if ( $location =~ /^\w+:\/\// ) {
	$url= $location;
} else {
	unless ($location =~ /^\//) {
		$location= "/" . $location;
	}
	$url = "$Protocol://$Host:$Port$location";
}

print "LWP: Getting url: $url\n" if ($DebugLWP);
my $req = HTTP::Request->new( GET => $url );
if ($User) {
	$req->authorization_basic($User, $Pass);
}

if ($action eq "s") {  # Store rpms
	return undef unless ($location =~ /([^\/]+)$/);
	$file = $1;
	print "\nLWP: Saving data: $file\n" if ($DebugLWP);
	$res = $LWPua->request( $req, $file );
} else {
	$file= undef;
	$req->header(Accept => "text/html");
	$res = $LWPua->request( $req );
}

if( $res->is_success ) {
	print "LWP: Received url: $url\n" if ($DebugLWP);
	return $file if (defined $file);
} else {
	print "LWP: $url: " . $res->status_line . "\n" if ($DebugLWP);
	$DldResult= $res->status_line;
	return undef;
}

# Parse data
print "\nLWP: Parsing data:\n" if ($DebugLWP);
   
$file = $res->base->as_string;
$file =~ s/^[a-z]+:\/\/[^\/]+(\/.*)$/$1/i;
my @files = ($file);
my($data, $hrefMask) = $res->content;
   
foreach $hrefMask ('<a [^>]*href="([^"]+)"', '<a [^>]*href=([^" >]+)' ) {
	while( $data =~ /$hrefMask/ig ) {
		push @files, $1;
		print "LWP: HREF: $1\n" if ($DebugLWP);
	}
}  
print "LWP: Found " . (scalar(@files)-1) . " links.\n" if ($DebugLWP);

return @files;
}

######################
# Get a file via http
# Saves an rpm to disk resp. parses html files for links
######################

sub Get_HTTP
{
my $location= shift;
my $action= shift; # s=save p=parse
my ($file, $result, $host, $port, $header, $type, $length, $socket, $line, $buf, $buff, $n);
my $maxredirect= $MaxRedirect;
my $EOL = "\015\012";
$DldResult= "";

if ($Protocol eq "http") {
	$host=$Host;
	$port=$Port;
} else { #https
	$host="localhost";
	$port=$StunnelPort;
}

do { 
	$file= $location;
	my %args = ( PeerAddr => $host, 
		    PeerPort => $port,
		    Proto    => 'tcp');
	if ($Timeout) {
		$args{'Timeout'}= $Timeout;
	}
	$socket = IO::Socket::INET->new(%args);
	unless ($socket) {
		print STDERR "Error: Failed to connect to $host.\n";
		$@ =~ s/IO::Socket::INET/HTTP errror/;
		print STDERR "$@\n";
		return undef;
	}
	$socket->autoflush(1);
	#$socket->sockopt(MSG_WAITALL,1);

	# Send header
	$header= "GET $file HTTP/1.0" . $EOL;
	if ($HTTPSendHost) {
		$header= $header . "Host: $host" . $EOL;
	}
	$header= $header . $EOL;
	$n= length($header);
	unless (syswrite($socket,$header,$n) == $n) {
		close($socket);
		print STDERR "Error: Sending http request: $file ($!)\n";
		return undef;
	}

	# Receive data
	unless ( sysread($socket,$buf,$BufSize) ) {
		close($socket);
		print STDERR "Error: Reading http data: $file ($!)\n";
		return undef;
	}

	if ($buf =~ /^HTTP\S+\s+(\d+[^\n]*)\r\n(.*)$/s ) {
		$result= $1;
		$buf= $2;
	} else {
		close($socket);
		print STDERR "Error: Malformed http header: $file\n";
		return undef;
	}

	if ($result !~ "^(200|30)") {
		close($socket);
		$DldResult= $result;
		print "HTTP: $Protocol://$host$file: $result\n"  if ($DebugHTTP);
		return undef;
	}

	print "HTTP:Result: $result\n" if ($DebugHTTP);
	my $max= 100;
	my $done= 0;
	do {
		$max--;
		if ($buf =~ /^([^\n]*)\r\n(.*)$/s) {
			$line= $1;
			$buf= $2;
			print "HTTP:Head: $line\n" if ($line and $DebugHTTP);
			if ($line =~ /^Content-Type:\s+(\S+)/) {
				$type=$1;
			}
			if ($line =~ /^Content-Length:\s+(\d+)/) {
				$length=$1;
			}
			if ($line =~ /^Location:\s+(\S+)/) {
				$location=$1;
			}
			unless ($buf) {
				$max=0 unless (sysread($socket,$buff,$BufSize));
				$buf= $buf . $buff;
			}
			$done= 1 unless ($line);
		} else {
			$max =0;
		}
	} until ($done or $max<=0);

	if ($max <=0) {
		print STDERR "Error: Reading http header: $file\n";
		$@ =~ s/IO::Socket::INET/HTTP errror/;
		print STDERR "$@\n" if $@;
		close($socket);
		return undef;
	}
	unless ($type) {
		print STDERR "Warning: No content type: $file\n" if $Warnings;
		$type="unkown";
	}

	if ($result =~ "^30") {
		close($socket);
		$maxredirect--;
		unless ($maxredirect >= 0) {
			print STDERR "Warning: Maximum number of redirects exceeded at $host\n";
			return undef;	
		}
		if ($location !~ /^$Protocol:\/\/$Host[^\/]*(\/.*)$/) {
			print STDERR "Warning: Not following redirect: $location\n";
			return undef;
		}
		$location=$1;
		print "HTTP: Redirect: $Host:$Port$location\n" if ($DebugHTTP);		
	} else {
		$location= "";
	}

} until (! $location);

if ($action eq "s") {
	# Store rpms
	print "\nHTTP:Saving data: $file\n" if ($DebugHTTP);
	$file= $1;
	if ($type =~ /^text/) {
		print "Error: Wrong content type: $type\n";
		close($socket);
		return undef;
	}
	unless (open(FILE,">$file")) {
		print STDERR "Error: Opening file ($!)\n";
		close($socket);
		return undef;
	}
	my $received= length($buf);
	print FILE $buf;
	do {
		$n= sysread($socket,$buf,$BufSize);
		unless (defined($n)) {
			print STDERR "Error: Reading http data ($!).\n";
			close(FILE);
			close($socket);
			return undef;
		}
		$received+= $n;
		print FILE $buf;
	} until ($n == 0);
	close(FILE);
	close($socket);
	print "HTTP: Received $received bytes of data.\n" if ($DebugHTTP);
	if (defined $length and $length ne $received) {
		print STDERR "Warning: Received bytes ($received) does not match content length ($length)!\n" if $Warnings;
	}
	return $file;
}

# Parse data

print "\nHTTP:Parsing data:\n" if ($DebugHTTP);
if ($type !~ /^text\/html/) {
	print "Error: Wrong content type: $type\n";
	close($socket);
	return undef;
}

my $received= length($buf);
my @files= ($file);
do {
	# We look only for <a href="...">
	if ($buf =~ /(<A\s+.*)/si) {
		$buf = $1;
	} elsif ($buf =~ /(<A|<)$/i ) {
		$buf = $1
	} else {
		$buf ="";
	}
	if ($buf =~ /^<A\s+HREF="([^"]+)"(.*)$/is) {
		push(@files, $1);
		$buf= $2;
		$buf= " " unless ($buf);
		print "HTTP:HREF:$1\n" if ($DebugHTTP);
	} else {
		$n= sysread($socket,$buff,$BufSize);
		if (defined($n)) {
			if ($n>0) {
				$received+= $n;
				$buf= $buf . $buff;
			} else {
				$buf= "";
				print "HTTP: Finished reading data.\n" if ($DebugHTTP);

			}
		} else {
			print "Error: Reading http data ($!).\n";
			close($socket);
			return undef;
		}
	}
} until (! $buf);

close($socket);
print "Received $received bytes of data.\n" if ($DebugHTTP);
print "HTTP: Found " . (scalar(@files)-1) . " links.\n" if ($DebugHTTP);

if (defined $length and $length ne $received) {
	print STDERR "Warning: Received bytes ($received) does not match content length ($length)!\n" if $Warnings;
}

return @files;
}

######################
# Substitute distribution data.
######################

sub Substitute_Dist_Data {

	$_[0] =~ s/#DistVersion#/$DistVersion/g if ($DistVersion);
	$_[0] =~ s/#DistVersionNoDot#/$DistVersionNoDot/g if ($DistVersionNoDot);
	$_[0] =~ s/#DistLang#/$DistLang/g if ($DistLang);
	$_[0] =~ s/#DistArch#/$DistArch/g if ($DistArch);
}


######################
# Read in configuration.
######################

sub Read_Config
{
my ($val, $var);
my $file = shift;
local *FILE;

return 0 unless $file;
print "Reading config file: $file\n" if ($Debug >1);

unless( $file =~ /^\// or -f $file) {
	$file= "$ConfigDir/$file";
}

$val=(stat($file))[2];
if ( $val and ($val >> 1)%2 ) {
	fatal("Configuration file '$file' is world writeable!");
}

unless (open(FILE,"<$file")) {
	print STDERR "Error: Could not open: $file ($!)\n";
	$RetGen= 1;
	return 1;
}

while (<FILE>) {
	chomp;
	next if ( $_ =~ /^\s*#/ );
	if ( $_ =~ /Reset(Include|Exclude|Patterns)/ ) {
		@UpdInExclude = ();
		next;
	}
	next unless ($_ =~ /^\s*(\w+)\s*=(.*)$/);
	($var, $val)= ($1, $2);
	if ($val =~ /^\s+(\S.*)$/) {
		$val= $1;
	}
	if ($val =~ /^(.*\S)\s+$/) {
		$val= $1;
	}
	if ($val =~ /^\s+$/) {
		$val= "";
	}
	$var =~ s/Recurse/Recursive/; # Old syntax
	$var =~ s/^Dir(.+)$/$1Dir/; # Old syntax
	$val="" unless (defined($val));
	if ($val =~ /^`(.+)`$/) {
		if ($ShellEscapes) {
			chomp($val = `$1`);
		} else {
			print STDERR "Warning: Shell escapes disabled ('$var').\n" if $Warnings;
			next;
		}
	} 
	if ( $var eq "ShellEscapes") {
		$ShellEscapes = 0 unless ($val);
	} elsif ( $var eq "Debug" and $val =~/^[0-9]$/ ) {
		$Debug = $val;
	} elsif ( $var eq "Verbose" ) {
		$Verbose = $val;
	} elsif ( $var eq "Quiet" ) {
		$Quiet = $val;
	} elsif ( $var eq "Warnings" ) {
		$Warnings = $val;
	} elsif ( $var eq "RPMNameWarnings" ) {
		$DefaultRPMNameWarnings = $val;
	} elsif ( $var eq "FixRPMNames" ) {
		$DefaultFixRPMNames = $val;
	} elsif ( $var eq "DoDld" ) {
		$DoDld = $val;
	} elsif ( $var eq "DoUpdate" ) {
		$DoUpdate = $val;
	} elsif ( $var eq "DoInstall" ) {
		$DoInstall = $val;
	} elsif ( $var eq "DoKernel" ) {
		$DoKernel = $val;
	} elsif ( $var eq "DoInitRD" ) {
		$DoInitRD = $val;
	} elsif ( $var eq "DoBoot" ) {
		$DoBoot = $val;
	} elsif ( $var eq "DoMerge" ) {
		$DoMerge = $val;
	} elsif ( $var eq "MergeAll" ) {
		$MergeAll = $val;
	} elsif ( $var eq "DoPurge" ) {
		$DoPurge = $val;
	} elsif ( $var eq "MergeMatch" ) {
		$MergeMatch = $val;
	} elsif ( $var eq "DldMatch" ) {
		$DldMatch = $val;
	} elsif ( $var eq "HTTPExt" and $val =~ /^\w+(,\w+)*$/) {
		$HTTPExt = $val;
	} elsif ( $var eq "RPMBindings" and $val =~/^[0-3]$/ ) {
		$RpmBindings = $val;
	} elsif ( $var eq "QueryHeaders" ) {
		print STDERR "Deprecated option: QueryHeaders\n";
	} elsif ( $var eq "QueryDatabase" ) {
		print STDERR "Deprecated option: QueryDatabase\n";
	} elsif ( $var eq "CleanUp" ) {
		$CleanUp = $val;
	} elsif ( $var eq "CleanUpKernel" ) {
		$CleanUpKernel = $val;
	} elsif ( $var eq "BootManager" ) {
		$BootManager = $val;
	} elsif ( $var eq "BootConf" ) {
		$BootConf = $val;
	} elsif ( $var eq "BootScript" ) {
		$BootScript = $val;
	} elsif ( $var eq "BootAddAsNew" ) {
		$BootAddAsNew = $val;
	} elsif ( $var eq "BootTag" ) {
		$BootTag = $val;
	} elsif ( $var eq "UseBootTag" ) {
		$UseBootTag = $val;
	} elsif ( $var eq "KernelExt" and $val =~ /^\w+(,\w+)*$/) {
		$KernelExt = $val;
	} elsif ( $var eq "RemoveBad" ) {
		$RemoveBad = $val;
	} elsif ( $var eq "BestMatch" ) {
		$BestMatch = $val;
	} elsif ( $var eq "GetAll" ) {
		$GetAll = $val;
	} elsif ( $var eq "Repackage" ) {
		$Repackage = $val;
	} elsif ( $var eq "CheckSize" ) {
		$CheckSize = $val;
	} elsif ( $var eq "CheckSig" ) {
		$CheckSig = $val;
	} elsif ( $var eq "CheckVendor" ) {
		$CheckVendor = $val;
	} elsif ( $var eq "CheckGPG" ) {
		$CheckGPG = $val;
	} elsif ( $var eq "GPGHome" ) {
		$GPGHome = $val;
	} elsif ( $var eq "Resolve" ) {
		$Resolve = $val;
	} elsif ( $var eq "ResolveUseRPMDB" ) {
		$ResolveUseRPMDB = $val;
	} elsif ( $var eq "DataBase" ) {
		$DataBase = $val;
	} elsif ( $var eq "DataBaseName" ) {
		$DataBaseName = $val;
	} elsif ( $var eq "DataBaseRO" ) {
		$DataBaseRO = $val;
	} elsif ( $var eq "DoLog" ) {
		$DoLog = $val;
	} elsif ( $var eq "DoLock" ) {
		$DoLock = $val;
	} elsif ( $var eq "LogFile" ) {
		$LogFile = $val;
	} elsif ( $var eq "UpdateDir" ) {
		$UpdateDir = $val;
	} elsif ( $var eq "InstallDir" ) {
		$InstallDir = $val;
	} elsif ( $var eq "RPMDir" ) {
		$RPMDir = $val;
	} elsif ( $var eq "Recursive") {
		$Recursive = $val;
	} elsif ( $var eq "PostUpdateScript" ) {
		$PostUpdateScript = $val;
	} elsif ( $var eq "PostDldScript" ) {
		$PostDldScript = $val;
	} elsif ( $var eq "DistVersion" ) {
		$DistVersion = $val;
	} elsif ( $var eq "DistLang" ) {
		$DistLang = $val;
	} elsif ( $var eq "DistArch" ) {
		SetDistArch($val);
	} elsif ( $var eq "DefaultArch" ) {
		SetArch($val);
	} elsif ( $var eq "DefaultUser" ) {
		$DefaultUser = $val;
	} elsif ( $var eq "DefaultPass" ) {
		$DefaultPass = $val;
	} elsif ( $var eq "Timeout" and $val =~ /^[0-9]+$/) {
		$Timeout = $val;
	} elsif ( $var eq "UseLWP" ) {
		$DefaultUseLWP = $val;
	} elsif ( $var eq "LWPProtocols" ) {
		$LWPProtocols = $val;
	} elsif ( $var eq "LWPProxy" ) {
		$Proxy = $val;
	} elsif ( $var eq "LWPProxyUser" ) {
		$ProxyUser = $val;
	} elsif ( $var eq "LWPProxyPass" ) {
		$ProxyPass = $val;
	} elsif ( $var eq "LWPNoProxy" ) {
		push(@NoProxy, $val);
	} elsif ( $var eq "HTTPSendHost" ) {
		$DefaultHTTPSendHost = $val;
	} elsif ( $var eq "SSHCipher" ) {
		$SSHCipher = $val;
	} elsif ( $var eq "Passive" ) {
		$DefaultPassive = $val;
	} elsif ( $var eq "FTPRetry" and $val =~/^[0-9]+$/) {
		$DefaultFTPRetry = $val;
	} elsif ( $var eq "FTPWait" and $val =~/^[0-9]+$/) {
		$DefaultFTPWait = $val;
	} elsif ( $var eq "FTPFirewall") {
		$Firewall = $val;
	} elsif ( $var eq "FTPFirewallType"  and $val =~/^[0-7]$/) {
		$FirewallType = $val;
	} elsif ( $var eq "DldAddInstalled" ) {
		$DldAddInstalled = $val;
	} elsif ( $var eq "DldUseDB" ) {
		$DldUseDB = $val;
	} elsif ( $var eq "DldConfigDir" ) {
		$DldConfigDir = $val;
	} elsif ( $var eq "DldConfig" ) {
		push(@DldConfig, $val) if $val;
	} elsif ( $var eq "Comps" ) {
		$Comps = $val;
	} elsif ( $var eq "CompsDefault" and $val =~/^[0-2]$/) {
		$CompsDefault = $val;
	} elsif ( $var eq "Languages" and $val =~ /^\w+(,\w+)*$/) {
		@DistLanguages = split(/,/, $val);
	} elsif ( $var eq "Exclude" ) {
		push(@UpdInExclude, "1" . $val) if $val;
	} elsif ( $var eq "Include" ) {
		push(@UpdInExclude, "0" . $val) if $val;
	} elsif ( $var eq "IncludeFile") {
		Read_Config($val);
	} else {
		print STDERR "Warning: Unknown variable '$var' or bad value '$val' in $file.\n" if $Warnings;
	}
}
close (FILE);

if ($Firewall and ! $FirewallType) {
	$FirewallType= 1;
}

return 0;
}

######################
# Read in dld configuration.
######################

sub Read_Dld_Config
{
my $file = shift;

if (-d $DldConfigDir) {
	chdir($DldConfigDir) || fatal("Could not cd to: $DldConfigDir ($!)");
}
if ( -f $file ) {
	# fine
} elsif ( -f "$file.dld") {
	$file = "$file.dld";
} elsif ( -f "$file.get") {
	$file = "$file.get";
} elsif ( -f "$file.ftp") {
	$file = "$file.ftp";
} elsif ( $cwd and -f "$cwd/$file") {
	$file = "$cwd/$file";
}

$Host= "";
$User= "";
$Pass= "";
$Protocol= "ftp";
$DldAll= 0;
$DldRecursive= 0;
$DldDirInclude= "";
$DldRpmInclude= "";
$BaseDir= "";
$FTPRetry= $DefaultFTPRetry;
$FTPWait= $DefaultFTPWait;
$HTTPSendHost= $DefaultHTTPSendHost;
$UseLWP= $DefaultUseLWP;
$Passive= $DefaultPassive;
$StoreDir= $UpdateDir;
$RPMNameWarnings= $DefaultRPMNameWarnings;
$FixRPMNames= $DefaultFixRPMNames;
$DirWarnings = 1;
@DldDirs= ();
@NoWarnDirs= ();
if ( $DldMatch ) {
	@InExclude= @UpdInExclude;
} else {
	@InExclude= ();
}

return 1 unless ( Read_Dld_ConfigFile($file) == 0);

$RPMNameWarnings =1 if $Debug;

if ($Protocol eq "file") {
	$Host= "localhost";
	$Port= "0";
}

unless ($Host) {
	print STDERR "Error: No host given in $file.\n";
	return 1;
}
unless (@DldDirs) {
	print STDERR "Error: No dld directories given in $file.\n";
	return 1;
}

return 0;
}

######################
# Read in dld configuration file.
######################

sub Read_Dld_ConfigFile
{
my ($item, $val, $var);
my $file = shift;
local *FILE;

return 0 unless $file;
print "\nReading dld config file: $file\n" if $Debug;

$val=(stat($file))[2];
if ( $val and ($val >> 1)%2 ) {
	print STDERR "Warning: Skipping world writeable configuration file: $file\n" if $Warnings;
	return 1;
}

unless (open(FILE,"<$file")) {
	print STDERR "Error: Could not open: $file ($!)\n";
	$RetGen= 1;
	return 1;
}

while (<FILE>) {
	chomp;
	next if ( $_ =~ /^\s*#/ );
	if ( $_ =~ /Reset(Include|Exclude|Patterns)/ ) {
		@InExclude = ();
		next;
	}
	next unless ($_ =~/\s*(\w+)\s*=\s*(.*)/);
	($var, $val)= ($1, $2);
	$val="" unless (defined($val));
	if ($val =~ /^`(.+)`$/) {
		if ($ShellEscapes) {
			chomp($val = `$1`);
		} else {
			print STDERR "Warning: Shell escapes disabled ('$var') in $file.\n" if $Warnings;
			next;
		}
	}
	$var =~ s/DldRecurse/DldRecursive/; # Old syntax
	if ( $var eq "Host" ) {
		$Host = $val;
	} elsif ( $var eq "Protocol" ) {
		$Protocol = lc($val);
	} elsif ( $var eq "User" ) {
		$User = $val;
	} elsif ( $var eq "Pass" ) {
		$Pass = $val;
	} elsif ( $var eq "Passive" ) {
		$Passive = $val;
	} elsif ( $var eq "DirWarnings") {
		$DirWarnings = $val;
	} elsif ( $var eq "RPMNameWarnings" ) {
		$RPMNameWarnings = $val;
	} elsif ( $var eq "FixRPMNames" ) {
		$FixRPMNames = $val;
	} elsif ( $var eq "DldAll" and $val =~/^[0-2]$/) {
		$DldAll = $val;
 	} elsif ( $var eq "DldRecursive" ) {
 		$DldRecursive = $val;
 	} elsif ( $var eq "DldDirInclude" ) {
		Substitute_Dist_Data($val);
 		$DldDirInclude = $val;
 	} elsif ( $var eq "DldRpmInclude" ) {
		Substitute_Dist_Data($val);
 		$DldRpmInclude = $val;
	} elsif ( $var eq "UseLWP") {
		$UseLWP = $val;
	} elsif ( $var eq "HTTPSendHost") {
		$HTTPSendHost = $val;
	} elsif ( $var eq "FTPRetry" and $val =~/^[0-9]+$/) {
		$FTPRetry = $val;
	} elsif ( $var eq "FTPWait" and $val =~/^[0-9]+$/) {
		$FTPWait = $val;
	} elsif ( $var eq "StoreDir" ) {
		Substitute_Dist_Data($val);
		$StoreDir = $val if $val;
	} elsif ( $var eq "BaseDir" ) {
		if ($val =~ /^(.*)\/$/) {
			$BaseDir = $1;
		} else {
			$BaseDir= $val;
		}
	} elsif ( $var eq "Dir" ) {
		if ($BaseDir and $val !~ /^\//) {
			$val= "$BaseDir/$val";
		}
		Substitute_Dist_Data($val);
		if ( $val=~ /^(.+\/)\/$/ ) {
			$val=$1;
			for $item (@AllowedArch) {
				push(@NoWarnDirs, "$val$item/") unless ($DirWarnings and $item eq $DistArch);
				push(@DldDirs, "$val$item/");
			}
		} else {
			push(@NoWarnDirs, $val) unless ($DirWarnings);
			push(@DldDirs, $val);
		}
	} elsif ( $var eq "Exclude" ) {
		push(@InExclude, "1" . $val) if $val;
	} elsif ( $var eq "Include" ) {
		push(@InExclude, "0" . $val) if $val;
	} elsif ( $var eq "IncludeFile" and $val) {
		Read_Dld_Config_File($val);
	} else {
		print STDERR "Warning: Unknown variable '$var' or bad value '$val' in $file.\n" if $Warnings;
	}
}
close (FILE);

return 0;
}

######################
# Purge old RPMs from updates.
######################

sub Purge_RPMs
{
my ($item, @newrpms, @oldrpms);
my $cl= $CheckLocal;
$cl= 2 if ($CleanUp and !$cl);

print "\nDoing purge part.\n" if $Debug;

if ($cl) {
	# Get a list of rpms we already have
	if ($RPMDir) {
		SetLocalRPMs();
		Get_Local_List($RPMDir);
	} else {
		SetLocalRPMs("l");
	}
	if ($Resolve and $cl==1) {
		# Don't remove rpms which are needed by other rpms
		# This will set up a fake local entry for each requirement
		print "Checking requirements for rpms.\n" if $Debug;
		Get_Requirements(SelectRPMs(\@AllRPMs, 1, 1));
	}
} else {
	SetLocalRPMs();
}

@newrpms= SelectRPMs(\@AllRPMs, $BestMatch, $cl);
@oldrpms= ();

foreach $item (@AllRPMs) {
	push(@oldrpms,$item) unless (IsElement($item, @newrpms));
}
return 0 unless (@oldrpms);

print "Purging rpms from $UpdateDir:\n" unless $Quiet;
chdir($UpdateDir) || fatal("Could not cd to: $UpdateDir ($!)");
foreach $item (@oldrpms) {
	print "  ". $item->rpmname . "\n" unless $Quiet;
	unless ($Test) {
		unless (unlink($item->filename)) {
			print STDERR "Error: Failed to remove: " . $item->filename . "($!)\n";
			$RetGen= 1;
		}
	}
}

@AllRPMs=@newrpms;

}

######################
# Merge new RPMs into dist.
######################

sub Merge_RPMs
{
my ($item, $name, $cl, @newrpms, @oldrpms);

print "\nMerging rpms:\n" if $Debug;

unless ( $RPMDir ) {
	print STDERR "Error: No distribution directory.\n";
	$RetGen= 1;
	return 1;
	}
unless ( -d $RPMDir ) { fatal("No such directory: $RPMDir"); }
if ($UpdateDir eq $RPMDir) {
	print STDERR "Error: Update and distribution directory are equal!\n";
	$RetGen= 1;
	return 1;
}

if ( $MergeMatch ) {
	@InExclude= @UpdInExclude;
} else {
	@InExclude= ();
}

unless (!$DoUpdate and $DoPurge and ($CheckLocal or $CleanUp)) { #Already have it from purge if CleanUp
	# Get a list of rpms we already have
	SetLocalRPMs();
	Get_Local_List($RPMDir);
	if ($Resolve and !($MergeAll or $CleanUp or $CheckLocal==2)) {
		print "Checking requirements for rpms.\n" if $Debug;
		Get_Requirements(SelectRPMs(\@AllRPMs, 1, 1));
	}
}

if ($MergeAll) {
        $cl= 0;
} else {
        $cl= 1;
}

@newrpms= ();
foreach $item ( SelectRPMs(\@AllRPMs, 0, $cl) ) {
	push(@newrpms, $item) unless IsExcluded($item);
}

return 0 unless (@newrpms);

print "Merging rpms into $RPMDir:\n" unless $Quiet;
chdir($UpdateDir) || fatal("Could not cd to: $UpdateDir ($!)");
foreach $item ( @newrpms ) {
	print "  " . $item->rpmname . "\n" unless $Quiet;
	next unless (Check_Vendor($item)==0);
	next unless (Check_Sig($item)==0);
	foreach $name ($item->obsoletes) {
		next unless ($name);
		next if ( $name =~ /^\s*$/); # Don't care about blank lines.
		next if ( $name =~ /^\s*error:/i); # Don't care about this line.
		if ( $name =~ /^(\S+)\s/ ) {
			$name= $1;
		}
		if (CheckLocal($name)) {
			print "  Removing obsolete rpm: $name.\n" if $Verbose;
			unlink glob("$RPMDir/$name-" . CheckLocal($name) . ".*.rpm") unless ($Test);
		}
	}
	next if ($Test);
	$name=$item->name;
	if (CheckLocal($name) and CheckLocal($name) ne $item->version) {
		unlink glob("$RPMDir/$name-" . CheckLocal($name) . ".*.rpm");
	}
	if ($CleanUp) {
		unless (move($item->filename, $RPMDir)) {
			print STDERR "Error: Failed to move: " . $item->filename . " ($!)\n";
			$RetGen= 1;
		}
	} else {
		unless (copy($item->filename, $RPMDir)) {
			print STDERR "Error: Failed to copy: " . $item->filename . " ($!)\n";
			$RetGen= 1;
		}
	}
}

if ($CleanUp) {
	@oldrpms= ();
	foreach $item (@AllRPMs) {
		push(@oldrpms,$item) unless (IsElement($item, @newrpms));
	}
	@AllRPMs= @oldrpms
}

}

######################
# Get RPMs from remote site
######################

sub Get_Remote_RPMs
{
my ($ftp, $dir, $subdir, $item, $file, $name, $base, $tmp, $StoreSubDir, @AllDldRPMs, @DldRPMs);
my @requirements= ();
my $StoreByArch= $DefaultStoreByArch;
my $retval= 0;

if ($Protocol eq "file") {
	$Port= 0;
} elsif ($Protocol eq "sftp") {
	$Port= getservbyname("ssh","tcp");
} else {
	$Port= getservbyname($Protocol,"tcp");
}

# Check for port
unless ($Port) { #Just to be sure
	$Port= 0;
	$Port= 80 if ($Protocol eq "http");
	$Port= 443 if ($Protocol eq "https");
	$Port= 21 if ($Protocol eq "ftp");
	$Port= 22 if ($Protocol eq "sftp");
}
if ( $Host =~ /^(.*):(\d+)$/ ) {
	$Host=$1;
	$Port=$2;
}

#Get all new rpms from the dld site
if ($User) {
	print "Fetching rpms from '$Protocol://$User\@$Host'.\n" if $Debug;
} else {
	print "Fetching rpms from '$Protocol://$Host'.\n" if $Debug;
	if ($Protocol eq "ftp") {
		($User, $Pass) = ($DefaultUser, $DefaultPass);
		print "User='$User' Pass='$Pass'.\n" if ($Debug >1);
	}
}
print "Using port: $Port. Passive=$Passive.\n" if ($Debug >1);

@DldDataBase= ();

if ($StoreDir =~ /^(.*\/)\/$/) {
	$StoreDir = $1;
	$StoreByArch = 1;
}
Strip_Dir($StoreDir);
print "Will store them in '$StoreDir'.\n" if ($Debug >1);
chdir($StoreDir) || fatal("Could not cd to: $StoreDir ($!)");
$StoreDir .= "/" unless ($StoreDir =~ /\/$/);
$StoreSubDir = $StoreDir;
# Add rpms from StoreDir to local list unless it is a subdir of UpdateDir
if ($StoreDir ne $UpdateDir and $StoreDir !~ /^\Q$UpdateDir\//) {
	if ($StoreByArch) {
		foreach $item (@AllowedArch) {
			$tmp = $StoreDir . $item;
			Get_Local_List($tmp) if ( -d $tmp );
		}
	} else {
		Get_Local_List($StoreDir);
	}
}

unless ( IsElement($Protocol, split(/,/,$LWPProtocols)) ) {
	$UseLWP =0;
}

if ($UseLWP) {
	# Set up LWP
	unless ($LWPua) {

		##############################
		# Subpackage for LWP proxy support
		##############################

		{
			package AutoUpdateUserAgent;
			use vars qw(@ISA $VERSION);
			require LWP::UserAgent;

			@ISA = qw(LWP::UserAgent);
			$VERSION = "1.0";

			sub new
			{
			my $self = LWP::UserAgent::new(@_);
			$self->agent($VERSION);
			$self->{ProxyUser} = "";
			$self->{ProxyPass} = "";

			$self;
			}

			sub set_proxy_credentials
			{
			my $self = shift;
			$self->{ProxyUser} = shift;
			$self->{ProxyPass} = shift;
			}

			sub get_basic_credentials
			{
			my $self = shift;
			return ($self->{ProxyUser}, $self->{ProxyPass});
			}
		}

		$LWPua = AutoUpdateUserAgent->new;
		if ($Proxy) {
			my @tmp= split(/,/,$LWPProtocols);
			$LWPua->proxy(\@tmp, $Proxy);
			$LWPua->no_proxy(@NoProxy);

			if ($ProxyUser && $ProxyPass) {
				$LWPua->set_proxy_credentials($ProxyUser, $ProxyPass);
			}
		} else {
			$LWPua->env_proxy();
		}
		if ($Timeout) {
			$LWPua->timeout($Timeout);
		}
	}
	@AllDldRPMs= GetRPMHeaders("r", Get_RPMs_http(@DldDirs) );
} elsif ($Protocol eq "file") {
	$Host="localhost";
	$Port="0";
	my $RecursiveSave= $Recursive;
	$Recursive= $DldRecursive;
	@AllDldRPMs= GetRPMHeaders("f", Get_RPMs( @DldDirs) );
	$Recursive= $RecursiveSave;
} elsif ($Protocol eq "ftp") {
	require Net::FTP;
	unless ($FTPRetry >= 0) {
		$FTPRetry= 0;
	}
	$FTPRetry++;
	my %args = (Debug => $DebugFTP, Passive => $Passive, Firewall => $Firewall, FirewallType => $FirewallType, Port => $Port);
	if ($Timeout) {
		$args{''}= $Timeout;
	}
	while ( ! ($ftp = Net::FTP->new($Host, %args)) ) {
		$FTPRetry--;
		if ($FTPRetry <= 0) {
			print STDERR "Error: Failed to connect to $Host.\n";
			$@ =~ s/Net::FTP/FTP errror/;
			print STDERR "$@\n";
			return 1;
		} else {
			print "Could not connect to $Host. Will try ". $FTPRetry ." more time(s).\n" if $Verbose;
			$@ =~ s/Net::FTP/FTP errror/;
			print "$@\n" if $Debug;
			sleep $FTPWait;
		}
	}

	while ( ! $ftp->login($User,$Pass) ) {
		$FTPRetry--;
		if ($FTPRetry <= 0) {
			print STDERR "Error: Failed to login at $Host: ", $ftp->message;
			return 1;
		} else {
			print "Could not login to $Host. Will try ". $FTPRetry ." more time(s).\n" if $Verbose;
			print "FTP message: ", $ftp->message if $Debug;
			sleep $FTPWait;
		}
	}

	$ftp->binary();
	@AllDldRPMs= GetRPMHeaders("r", Get_RPMs_ftp($ftp, @DldDirs) );
} elsif ( $Protocol eq "sftp" ) {
	require Net::SFTP;
	my @ssh_args= ();
	my @sftp_args= ($Host);
	if ($User) {
		push(@sftp_args, user => $User);
	}
	if ($Pass) {
		push(@sftp_args, password => $Pass);
	}
	if ($Port) {
		push(@ssh_args, port => $Port);
	}
	if ($SSHCipher) {
		push(@ssh_args, cipher => $SSHCipher);
	}
	if (@ssh_args) {
		push(@sftp_args, ssh_args => \@ssh_args);
	}
	$ftp= Net::SFTP->new(@ssh_args);
	unless ($ftp) {
		print STDERR "Error: Failed to connect to $Host\n";
		return 1;
	}
	@AllDldRPMs= GetRPMHeaders("r", Get_RPMs_sftp($ftp, @DldDirs) );
} elsif ( $Protocol eq "http" ) {
	@AllDldRPMs= GetRPMHeaders("r", Get_RPMs_http(@DldDirs) );
} elsif ( $Protocol eq "https" ) {
	# We use stunnel
	unless ( -x $STUNNEL ) {
		print STDERR "Error: stunnel is needed for https ($STUNNEL)\n";
		return 1;
	}
	$StunnelPid=0;
	my @args= @StunnelArgs;
	push(@args, "$Host:$Port");

	unless ( system($STUNNEL, @args) ==0 ) {
		print STDERR "Error: Could not execute $STUNNEL ($!)\n";
		return 1;
	}
	print STDERR "Waiting for stunnel.\n" if ($Debug>1);
	my $n= 10;
	$file="$Host.$Port.pid";
	$file=~ s/-/./;
	$file="$StunnelPidFile.$file";
	while ( $n >0 ) {
		$n--;
		if ( -f $file ) {
			$n=0;
			if ( open(FILE,"<$file") ) {
				$StunnelPid=<FILE>;
				close(FILE);
			} else {
				print STDERR "Error: Could not read $file\n";
			}
		} else {
			sleep 1;
		}
	}
	if ($StunnelPid) {
		print STDERR "Started stunnel (pid=$StunnelPid,port=$StunnelPort)\n" if ($Debug>1);
	} else {
		print "Error: Failed to start stunnel for $Host!\n";
		return 1;
	}
	@AllDldRPMs= GetRPMHeaders("r", Get_RPMs_http(@DldDirs) );
} else {
	print STDERR "Error: Unsupported protocol: $Protocol://$Host\n";
	return 1;
}

unless (!$Verbose or !$Warnings or @DldDataBase or @AllDldRPMs) {
	print "Warning: No files found at $Host. Please check remote directory path";
	print " or try Passive=1" unless($Passive or $Protocol ne "ftp");
	print ".\n";
}

# select new provides databases
if ($DldUseDB and @DldDataBase) {
	print "Selecting provides databases:\n" if $Debug;
	my @db= ();
	for $MergeDB (@DldDataBase) {
		print "  $MergeDB " if ($Debug>1);
		unless ( $MergeDB =~ /^(.*autoprovides[^\/]*)-([^\/\-]+)\.db$/) {
			print "(ignored)\n" if ($Debug>1);
			next;
		}
		my ($name, $ver) = ($1, $2);
		$name= "$Host:$Port:$name";
		my $dbver= $Provides{"##$name##"};
		unless ($dbver){
			print "(new)\n" if ($Debug>1);
			push(@db,$MergeDB);
			next;
		}
		my $tmp1= Header->new("foo-$ver-1.noarch.rpm", "r");
		my $tmp2= Header->new("foo-$dbver-1.noarch.rpm", "r");
		if ( $tmp1->compare($tmp2) ==1 ){
			print "(newer)\n" if ($Debug>1);
			push(@db,$MergeDB);
		} else {
			print "(old)\n" if ($Debug>1);
		}
	}
	@DldDataBase= @db;
} else {
	@DldDataBase= ();
}


if (@DldDataBase) {
	if (OpenProvides("rw")) {
		print "Merging provides database:\n" unless $Quiet;
		for $MergeDB (@DldDataBase) {
			if ($MergeDB =~ /(autoprovides[^\/]*-[^\/\-]+\.db)$/) {
				$name= $1;
			}
			print "  $name\n" unless $Quiet;
			next if ($Test);
			if ($UseLWP) {
				unless (Get_LWP($MergeDB, "s")) {
					print STDERR "Error: Failed to get: $MergeDB ($DldResult)\n";
					$retval=1;
				}
			} elsif ($Protocol eq "file") {
				unless (copy($MergeDB, ".")) {
					print STDERR "Error: Failed to copy: $MergeDB ($?)\n";
					$retval+=1;
				}
			} elsif ($Protocol eq "ftp") {
				unless ($ftp->get($MergeDB)) {
					print STDERR "Error: Failed to ftp: ";
					if ($ftp->message !~ /\Q$MergeDB/) {
						print STDERR "$MergeDB: ";
					}
					if ($ftp->message) {
						print STDERR $ftp->message;
					} else {
						print STDERR "Timeout (?)\n";
					}
					$retval=1;
				}
			} elsif ($Protocol eq "sftp") {
				unless ($ftp->get($MergeDB, $name)) {
					print STDERR "Error: Failed to get: $MergeDB\n";
					$retval=1;
				}
			} elsif ($Protocol eq "http" or $Protocol eq "https") {
				unless (Get_HTTP($MergeDB, "s")) {
					print STDERR "Error: Failed to get: $MergeDB ($DldResult)\n";
					$retval=1;
				}
			}
			next unless ( $MergeDB =~ /^(.*autoprovides[^\/]*)-([^\/\-]+)\.db$/);
			my ($dbname, $ver) = ($1, $2);
			$dbname= "$Host:$Port:$dbname";
			if (MergeProvides($name) == 0) {
				$Provides{"##$dbname##"}= $ver;
				unlink($MergeDB);
			}
		}
		OpenProvides("ro");
	}
}

# Download new rpms
$DldAll=0 if (!$Resolve and $DldAll<=1);
my $cl= 1;
if ($DldAll) {
	$cl= 2;
}

do {
  @DldRPMs=();
  for $item (SelectRPMs(\@AllDldRPMs, $BestMatch, $cl)) {
	unless ($DldAll >1 or CheckLocal($item->name)) {
		# Only download rpm if not in provides database
		if ($Provides{$item->name}) {
			print "Not downloading " . $item->rpmname . " (is in provides database).\n" if $Debug;
			next;
		}
	}
	# Should we store by arch
	if ($StoreByArch) {
		$tmp= $item->{ARCH};
		$tmp= "SRPMS" if ($tmp eq "src");
		$tmp= $StoreDir . $tmp . "/";
		if ( $StoreSubDir ne $tmp ) {
			$StoreSubDir = $tmp;
			unless( -d $StoreSubDir ) {
				mkdir($StoreSubDir, 0755) || fatal("Could not create: $StoreSubDir ($!)");
			}
			chdir($StoreSubDir) || fatal("Could not cd to: $StoreSubDir ($!)");
		}
	}
	# Version comparison for remote rpms might give wrong results
	# since we cannot query the rpm header, so make sure we do not
	# download such rpms again
	$file= $item->filename;
	if ($file =~ /\/([^\/]+)$/ ) {
		$file = $1;
	}
	$file= $StoreSubDir . $file;
	if ( -f $file ) {
		# If it is a bad one, try to resume
		next unless ($BadRPMs{$file});
	}
	unless ($GetAll) {
		my $found= 0;
		foreach $name (@GetList) {
			if ( $name =~ /^([^-]+-)/ ) {
				$base= $1;
			} else {
				$base= $name;
			}
	   		if ( substr($item->name, 0, length($base)) eq $base ) {
	   			$found= 1;
	   			last;
	   		}
	   	}
	   	unless ($found) {
			print "Not downloading " . $item->rpmname . " (not in get list).\n" if $Debug;
			next;
		}
	}
	push(@DldRPMs,$item)
  }

  if (!@requirements) {
	if (@DldRPMs) {
		print "New rpms from $Host:\n" unless $Quiet;
	} else {
		print "Found no new rpms at $Host.\n" if $Verbose;
	}
  }
  @requirements=();
  foreach $item (@DldRPMs) {
	$file= $item->filename;
	if ($file =~ /\/([^\/]+)$/ ) {
		$file = $1;
	}
	print "  $file\n" unless $Quiet;
	if ($Test) {
		AddLocalRPMs("r", $item->rpmname);
		next;
	}
	# Should we store by arch
	if ($StoreByArch) {
		$tmp= $item->{ARCH};
		$tmp= "SRPMS" if ($tmp eq "src");
		$tmp= $StoreDir . $tmp . "/";
		if ( $StoreSubDir ne $tmp ) {
			$StoreSubDir = $tmp;
			chdir($StoreSubDir) || fatal("Could not cd to: $StoreSubDir ($!)");
		}
	}
	if ($UseLWP) {
		unless (Get_LWP($item->filename, "s")) {
			print STDERR "Error: Failed to get: " . $item->filename. " ($DldResult)\n";
			$retval=1;
		}
	} elsif ($Protocol eq "file") {
		unless (copy($item->filename, ".")) {
			print STDERR "Error: Failed to copy: " . $item->filename. " ($?)\n";
			$retval=1;
		}
	} elsif ($Protocol eq "ftp") {
		my $lsize= 0;
		my $rsize= 0;
		if ( -f $file ) {
			$lsize= (stat($file))[7];
			$rsize= $ftp->size($item->filename);
			if ( $lsize < $rsize ) {
				print "  Resuming incomplete download.\n" if ($Verbose);
			} else {
				unlink($file);
				$lsize= 0;
			}	
		}	
		unless ($ftp->get($item->filename, $file, $lsize)) {
			print STDERR "Error: Failed to ftp: ";
			if ($ftp->message !~ /\Q$file/) {
				print STDERR $item->filename . ": ";
			}
			if ($ftp->message) {
				print STDERR $ftp->message;
			} else {
				print STDERR "Timeout (?)\n";
			}
			$retval=1;
		}
	} elsif ($Protocol eq "sftp") {
		$ftp->get($item->filename, $file);
		unless (-f $file ) {
			print STDERR "Error: Failed to get: " . $item->filename. "\n";
			$retval=1;
		}
	} elsif ($Protocol eq "http" or $Protocol eq "https") {
		unless (Get_HTTP($item->filename, "s")) {
			print STDERR "Error: Failed to get: " . $item->filename. " ($DldResult)\n";
			$retval=1;
		}
	}
	if (-f $file ) {
		$tmp= (GetRPMHeaders("c", "$StoreSubDir$file"))[0];
		if ($tmp) {
			AddLocalRPMs("f", $tmp->filename);
			push(@DownloadedRPMs, $tmp);
			if ($Resolve) {
				Get_Provides($tmp);
				push(@requirements, Get_Requirements($tmp));
			}
		}
	}
  }
  print "  Searching for: @requirements\n" if ($Debug and @requirements);
} until (! @requirements);

if ($UseLWP) {
	# do nothing
} elsif ($Protocol eq "ftp") {
	$ftp->quit;
} elsif ($Protocol eq "sftp") {
	$ftp= undef;
} elsif ($Protocol eq "https") {
	kill(1,$StunnelPid);
	$StunnelPid= "0";
}

OpenProvides("ro") if ($Resolve);

return $retval;
}

######################
# Update given rpms and resolve dependencies
# Rpm names are passed in @GetList
######################

sub Do_Update
{
my ($AddedRPM, $rpm, $item, $name, $base, $reqmnt);
my @flags = ("-U");
my @RPMs = ();
my @NewRPMs = ();
my @OldGetList= ();
$GetAll=0;

push(@flags,"--repackage") if ($Repackage);
push(@flags,"--test") if ($Test);


while (@GetList ne @OldGetList) { # until we succeed or can't find rpms to resolve deps
	$AddedRPM=0; # So far we haven't added anything to satisfy deps
	foreach $name (@GetList) {
		next if (IsElement($name, @DepList));
		next if (IsElement($name, @OldGetList));
		# Search for the corresponding rpm
		$rpm= $ResolveRPMs{$name};
		if ($rpm) {
			next if (IsElement($rpm, @RPMs));
			next if (IsElement($rpm, @UpdatedRPMs));
			if ( $rpm->name =~ /^([^-]+-)/ ) {
				$base= $1;
			} else {
				$base= $rpm->name;
			}
			for $rpm (@SelectedRPMs) {
	   			if ( substr($rpm->name, 0, length($base)) eq $base ) {
					next if (IsElement($rpm, @RPMs));
					next if (IsElement($rpm, @UpdatedRPMs));
					print "Adding " . $rpm->rpmname . ".\n" if $Debug;
					push(@RPMs, $rpm);
					#Get_Requirements($rpm) if $Resolve;
				}
			}
			$AddedRPM=1;
			next;
		}
		if ($Resolve and $NewResolveRPMs{$name}) {
			my @tmp= SelectRPMs($NewResolveRPMs{$name},1,2);
			if (@tmp) {
				$rpm= $tmp[0];
				$NewResolveRPMs{$name} = [ $rpm ];
			} else {
				$rpm= undef;
				delete $NewResolveRPMs{$name};
			}
		}
		unless ($rpm) {
			print "Warning: $name not available to satisfy dependencies.\n" if $Debug;
			push(@DepList, $name);
			next;
		}
		next if (IsElement($rpm, @NewRPMs));
		next if (IsElement($rpm, @UpdatedRPMs));
		next unless (Check_Sig($rpm)==0 );
		print "Adding " . $rpm->rpmname . " to satisfy dependencies.\n" if $Debug;
		push(@NewRPMs, $rpm);
		#Get_Requirements($rpm) if $Resolve;
		$AddedRPM=1;
	}
	@OldGetList=@GetList;
	last unless($AddedRPM);
	#Let's try to upgrade the rpms we found
	print "Trying to upgrade:" if $Debug;
	my @rpms= ();
	foreach $item (@RPMs, @NewRPMs) {
		push (@rpms, $item->filename);
   		print " " . $item->rpmname if $Debug;
	}			 
	print "\n" if $Debug;
	TieRpmDatabase(0);
	if ( CallRPM(\@RPMOutput, @flags, @rpms) ==0 ) {
		push(@UpdatedRPMs, @NewRPMs);
		push(@UpdatedRPMs, @RPMs);
		return 0 if ($Quiet);
		foreach $rpm (@NewRPMs) {
			print "  " . $rpm->rpmname . " (new)\n";
		}
		foreach $rpm (@RPMs) {
			print "  " . $rpm->rpmname . "\n";
		}
		if (@RPMOutput) {
			print "  " . join("\n  ", @RPMOutput) . "\n";
		}
		return 0;
	}

	# Couldn't upgrade. Lets find out what we need
	print join("\n", @RPMOutput) . "\n" if (@RPMOutput and $Debug >1);
	foreach $item (@RPMOutput) {
		next if ( $item =~ /^\s*$/); # Don't care about blank lines.
		next if ( $item =~ /^\s*error:/i); # Don't care about this line.
		# Try to figure out what we need
		$reqmnt="";
		if ($item =~ /^\s*file .* from install of\s+\S+\s+conflicts with file from package\s+(\S+)-[^-]+-[^-]+\s*$/) {
			$reqmnt= $1;
		} elsif ($item =~ /^\s*(\S+)\s+.*conflicts with/) {
			$reqmnt= $1;
		} elsif ($item =~ /^\s*(\S+)\s+.*is needed by/) {
			$reqmnt= $1;
			my $tmp= "";
			if ($item =~ /^\s*(\S+)\s+is needed by\s+(\S+)-([^-]+-[^-]+)\s*$/) {
				$tmp= $2 if (CheckLocal($2) eq $3); 
			} elsif ($item =~ /^\s*(\S+)\s+is needed by \(installed\)\s+(\S+)-([^-]+-[^-]+)\s*$/) {
				$tmp= $2; 
			} elsif ($item =~ /^\s*(\S+)\s+\S+\s+(\S+)\s+is needed by\s+(\S+)-[^-]+-[^-]+\s*$/) {
	 			$tmp= $3 if (substr(CheckLocal($1), 0, length($2)) eq $2);
			} elsif ($item =~ /^\s*(\S+)\s+\S+\s+(\S+)\s+is needed by \(installed\)\s+(\S+)-[^-]+-[^-]+\s*$/) {
	 			$tmp= $3 if (substr(CheckLocal($1), 0, length($2)) eq $2);
			}
			# Prefere a newer version over compat libs
			if ($tmp and $ResolveRPMs{$tmp}) {
				$reqmnt= $tmp;
			} elsif ($tmp and $NewResolveRPMs{$tmp}) {
				my @tmpp= SelectRPMs($NewResolveRPMs{$tmp},1,2);
				if (@tmpp) {
					$reqmnt= $tmp;
					$NewResolveRPMs{$tmp} = [ $tmpp[0] ];
				} else {
					delete $NewResolveRPMs{$tmp};
				}
			}
		} else {
			print "Warning: Unknown line: \"$item\"\n" if ($Debug >1);
			next;
		}
		$name= Requirement2Name($reqmnt);
		next unless ($name);
		if (IsElement($name, @GetList)) {
			print "Requirement: $reqmnt ($name)\n" if ($Debug > 1);
			next;
		}
		print "Requirement: $reqmnt ($name)\n" if $Debug;
		push(@GetList, $name);

   }
}

#Check if it makes sense to try again by passing all remaining rpms to rpm
my @MissingRPMs= ();
foreach $rpm ( @SelectedRPMs ) {
	push(@MissingRPMs, $rpm) unless (IsElement($rpm, @UpdatedRPMs));
}
$LastTry=1 if (@MissingRPMs eq @RPMs);

if ($Debug or $LastTry) {
	print "Failed to upgrade:";
	foreach $item (@RPMs) {
		print " " . $item->rpmname;
	}
	print "\n";
	if (@NewRPMs and ! $Quiet) {
		print "Tried to add:";
		foreach $item (@NewRPMs) {
			print " " . $item->rpmname;
		}
		print "\n";
	}
	print "Would need: @DepList\n" if (@DepList and ! $Quiet);
	print "RPM Output:\n" unless $Quiet;
	print join("\n", @RPMOutput) . "\n" unless $Quiet;
}

return 1;
}

######################
# Update given kernel rpms and resolve dependencies
######################

sub Do_Update_Kernel
{
my ($item, $name, $version, $oldversion);
my $disabledgrubby= 0;
my $Ret=0;
my @SelectedKernelRPMs= @_;

print "Installing kernel rpms.\n" if ($Debug);

if ($DoBoot) {
	unless ($BootManager) {
		if ( -x $LILO and -f $LiloConf) {
			$BootManager= "lilo";
		} elsif ( -x $GRUB and -f $GrubConf) {
			$BootManager= "grub";
		}
	} else {
		$BootManager= lc($BootManager);
	}
	if ($BootManager eq "lilo") {
		# Determine config file
		unless ($BootConf) {
			$BootConf= $LiloConf;
		}
		print "Using lilo as boot manager ($BootConf).\n" if $Debug;
	} elsif ($BootManager eq "grub") {
		# Determine config file
		unless ($BootConf) {
			$BootConf= $GrubConf;
		}
		print "Using grub as boot manager ($BootConf).\n" if $Debug;
                # Is BootDir on a boot partition?
                if (-d $BootDir and ! $BootMnt) {
                        if (open(DF, "$DF $BootDir |") ) {
                                for my $line (<DF>) {
                                        if ($line =~ /^\/dev\/\S+(\s+\S+){4}\s+(\/\S*)$/) {
                                                $BootMnt= $2;
                                                $BootMnt= "" if ($BootMnt eq "/");
                                                last;
                                        }
                                }
                                close(DF);
                        } else {
                                print STDERR "Warning: Could not determine boot partition ($!).\n" if $Warnings;
                        }
                }
		print "Boot directory $BootDir mounted on $BootMnt.\n" if ($Debug and $BootMnt);
	} else {
		print "Warning: No supported boot manager found!\n" if $Warnings;
		$DoBoot= 0;
	}
	unless ( -f $BootConf ) {
		print STDERR "Error: Configuration file '$BootConf' does not exist!?\n";
		$RetGen= 1;
		$DoBoot= 0;
	}
	print "Warning: Boot manager part disabled.\n" if ($Warnings and ! $DoBoot);

	#Disable grubby
	if ( $GRUBBY and -x $GRUBBY) {
		print "Disabling $GRUBBY.\n" if ($Debug);
		if ( -f $GRUBBY ) {
			if ( rename ($GRUBBY, "$GRUBBY.save") == 1 ) {
				symlink ("/bin/true", $GRUBBY);
				$disabledgrubby= 1;
			} else {
				print STDERR "Error: Failed to disable $GRUBBY ($!).\n";
			}
		} elsif ( -l $GRUBBY and -f "$GRUBBY.save" ) {
			$disabledgrubby= 1;
			print "Warning: Grubby already disabled!?\n" if $Warnings;
		} else {
			print "Warning: $GRUBBY is no regular file!?\n" if $Warnings;
		}
	}
}

foreach $item (@SelectedKernelRPMs) {
	$name=$item->name;
	$version=$item->version;
	$oldversion= CheckLocal($name);
	$oldversion= "0.0-0" unless ($oldversion);
	if ($name =~ /^kernel-(\w+)$/) {
		$oldversion=$oldversion . $1;
		$version=$version . $1;
	}
	unless ( Do_KernelUpdate($oldversion,$version,$item) ==0) {
		$Ret =1;
	}
}

#Restore grubby
if ($disabledgrubby) {
	print "Enabling $GRUBBY.\n" if ($Debug);
	rename ("$GRUBBY.save", $GRUBBY);
}

return $Ret unless ($DoBoot and $ChangedBoot);

print "Installing $BootManager.\n" if $Verbose;
$CurrentKernel=(uname())[2];
print "Current kernel: $CurrentKernel\n" if ($Debug>1);

unless ($Test) {
	unless ( Boot_Manager()==0 ) {
		$ChangedBoot= 0;
		print STDERR "Error: Installation of $BootManager failed.\n";
		print STDERR "Restoring old configuration.\n";
		print STDERR "Saving new one as $BootConf.test.\n";
		rename($BootConf,"$BootConf.test");
		rename("$BootConf.bak",$BootConf);
		unless ( Boot_Manager()==0 ) {
			print STDERR "Failed to restore old configuration.\n";
		}
		$Ret=1;
		return $Ret;
	} else {
		print "Please check the contents of $BootConf before rebooting!\n" if $Verbose;
	}
}

return $Ret unless ($CleanUpKernel and @RemovedKernel);

my $image;
my @flags=("-e");
push(@flags,"--repackage") if ($Repackage);
push(@flags,"--test") if ($Test);

print "Uninstalling old kernels:\n" if $Verbose;

foreach $image (@RemovedKernel) {
	if ($CurrentKernel eq $image) {
		print STDERR "Error: Cannot remove running kernel.\n";
		$Ret=1;
		next;
	}
	unless (CallRPM(\@RPMOutput, "-qf","$BootDir/vmlinuz-$image")==0 and $RPMOutput[0] =~ /^kernel-/) {
		print STDERR "Error: Could not determine rpm owning $BootDir/vmlinuz-$image.\n";
		$Ret=1;
		next;
	}
	$name=$RPMOutput[0];
	if (CallRPM(\@RPMOutput, @flags,$name)==0) {
		print "  $name\n" if $Verbose;
		if (-f "$BootDir/initrd-$image.img") {
			unlink("$BootDir/initrd-$image.img");
		}
	} else {
		print STDERR "Error: Failed to uninstall $name.\n";
		print STDERR "@RPMOutput";
		$Ret=1;
	}
}

return $Ret;
}

######################
# Update all RPMs
######################

sub Update_RPMs
{

my ($item, $rpm, $name, @UpdateRPMs, @rpms);
my $Ret = 0;
%ResolveRPMs = ();
%NewResolveRPMs = ();
@DepList = ();
@SelectedRPMs = ();
@SelectedKernelRPMs = ();
$DldAddInstalled = 1; #Check installed rpms during Get_Requirements
$ResolveUseRPMDB = 1; #Use the rpm data base for resolving


# Set include/exclude patterns
@InExclude= @UpdInExclude;

# Get a list of all installed rpms
SetLocalRPMs("l");

if ($DoGet) {
	if ( SetGetList() ) {
		print "All requested rpms are already installed.\n" if ($Verbose);
	} elsif ($RPMDir) {
		print "Also adding rpms from $RPMDir.\n" if $Debug;
		foreach $rpm ( GetRPMHeaders("f", Get_RPMs($RPMDir)) ) {
			$name= $rpm->name;
			foreach $item (@GetList) {
				next unless ($name eq $item);
				push(@AllRPMs, $rpm);
			} 
		}
	}
}

# Create a list of latest version of all available rpms
# except those already installed
print "Selecting rpms for upgrade.\n" if $Debug;
@UpdateRPMs= ();
foreach $item ( SelectRPMs(\@AllRPMs, 1, 2) ) {
	next if (IsExcluded($item));
	push(@UpdateRPMs, $item);
}

if ($DoInstall) {
	@rpms= @UpdateRPMs;
} else {
	print "Selecting new rpms.\n" if $Debug;
	@rpms=SelectRPMs(\@UpdateRPMs, 1, 1);
}

# Check rpms and split off kernel rpms.
print "Checking selected rpms.\n" if $Debug;

foreach $item (@UpdateRPMs) {
	unless ( IsElement($item, @rpms) ) {
		my $obsoletes=0;
		foreach $name ($item->obsoletes) {
			next unless ($name);
			next if ( $name =~ /^\s*$/); # Don't care about blank lines.
			next if ( $name =~ /^\s*error:/i); # Don't care about this line.
			if ( $name =~ /^(\S+)\s/ ) {
				$name= $1;
			}
			if (CheckLocal($name)) {
				print "  " . $item->rpmname . " obsoletes $name.\n" if ($Debug);
				$obsoletes= 1;
				$ResolveRPMs{$name}= $item;
				last;
			}
		}
		unless ($obsoletes) {
			next if (IsElement($item->name,@KernelNames));
			$NewResolveRPMs{$item->name}= [ $item ] if ($Resolve);
			next;
		}
	}
	if (IsElement($item->name,@KernelNames)) {
		next unless ( $DoKernel );
		next unless ( Check_Vendor($item)==0 );
		next unless ( Check_Sig($item)==0 );
		push(@SelectedKernelRPMs, $item);
	} else {
		next unless ( Check_Vendor($item)==0 );
		next unless ( Check_Sig($item)==0 );
		$ResolveRPMs{$item->name}= $item;
		push(@SelectedRPMs, $item);
	}
}
# Free some space
@rpms=();
@UpdateRPMs= ();

unless (@SelectedRPMs or @SelectedKernelRPMs) {
	if ($DoInstall or $DoGet) {
		print "Found no rpms to install.\n" if $Verbose;
	} else {
		print "Found no rpms to upgrade.\n" if $Verbose;
	}
	return $Ret;
}

if ($Resolve) {
	my @dirs= ();
	push(@dirs, $RPMDir) if ($RPMDir);
	push(@dirs, $UpdateDir) if ($RPMsFromCL);
	foreach my $dir (@dirs) {
		print "Getting rpms for resolving dependencies: $dir\n" if $Debug;
		foreach $item ( GetRPMHeaders("f", Get_RPMs($dir) ) ) {
			if ($ResolveRPMs{$item->name}) {
				delete $NewResolveRPMs{$item->name};
				next;
			}
			next if (IsElement($item->name,@KernelNames));
			if ($NewResolveRPMs{$item->name}) {
				push(@{$NewResolveRPMs{$item->name}},$item);
			} else {
				$NewResolveRPMs{$item->name}= [ $item ];
			}
		}
	}
}
print "\n" if $Debug;

if ($DoInstall or $DoGet) {
	print "Installing rpms:\n" unless $Quiet;
} else {
	print "Upgrading rpms:\n" unless $Quiet;
}

# First, upgrade selected kernel rpms
if ($DoKernel and @SelectedKernelRPMs) {
	$Ret= Do_Update_Kernel(@SelectedKernelRPMs);
}

# Maybe some rpms already got upgraded
foreach $item (@SelectedRPMs) {
	push(@rpms, $item) unless (IsElement($item, @UpdatedRPMs));
}
return $Ret unless ( @rpms );
@SelectedRPMs= @rpms;
@rpms= ();
$LastTry= 0;

# Now upgrade selected rpms
foreach $rpm (@SelectedRPMs) {
	next if (IsElement($rpm, @UpdatedRPMs));
	@GetList= ($rpm->name);
	$Ret= Do_Update();
	last if ($LastTry);
}

# This is our status right now.
@GetList= ();
foreach $rpm ( @SelectedRPMs ) {
	push(@GetList, $rpm->name) unless (IsElement($rpm, @UpdatedRPMs));
}

if (!$LastTry and @GetList) {
	# Finally, pass all rpms which could not be upgraded to rpm.
	# This should not help, but I guess it doesn't hurt.
	print "Upgrading remaining rpms (all at once).\n" if $Debug;

	$LastTry=1;
	$Ret= Do_Update();
}

return $Ret;
}

######################
# Install Boot Manager
######################

sub Boot_Manager
{

if ( $BootScript ) {
	if ( -x $BootScript ) {
		print "Calling boot script ($BootScript):\n" if $Debug;
		return system($BootScript);
	} else {
		print STDERR "Error: Cannot execute: $BootScript\n";
	}
} elsif ( $BootManager eq "lilo" ) {
	if ( -x $LILO ) {
		print "Calling lilo ($LILO):\n" if $Debug;
		return system($LILO);
	} else {
		print STDERR "Error: Cannot execute lilo: $LILO\n";
	}
} elsif ( $BootManager eq "grub" ) {
	# No need to reinstall grub.
	return 0;
}

return 1;
}

######################
# Create initial ram disk if needed
######################

sub Create_InitRD
{
my $InitRD = shift;
my $NewVersion = shift;
my $ifneeded = shift;

unless(-f $InitRD) {
	print "Creating initial ram disk: $InitRD\n" if $Verbose;
	unless ( -x $MKINITRD ) {
		print STDERR "Error: Cannot execute mkinitrd ($MKINITRD).\n";
		return 1;
	}
	my @args= ($MKINITRD);
	push(@args, "--ifneeded") unless ($ifneeded);
	unless ($Test) {
		unless ( system(@args, $InitRD, $NewVersion) ==0 ) {
			print STDERR "Error: Failed to create $InitRD.\n";
			return 1;
		}
	}
}
return 0
}

#####################
# Upgrade the kernel
#####################

sub Do_KernelUpdate
{
my $OldVersion= shift;
my $NewVersion= shift;
my $RPM= shift;
my @flags=("-i");
my ($line, $item, $name, $entry, $newlabel);
my @Conf =();
my @NewConf =();
my @Entries =();
my @Image =();
my @Label =();
my %Entry =();
my $InitRD;
my $link="";

print "Upgrading kernel: $OldVersion -> $NewVersion\n" if $Debug;

push(@flags,"--test") if ($Test);

# Set default for BootTag
unless ($BootTag) {
	if ($BootAddAsNew) {
		$BootTag="-new";
	} else {
		$BootTag="-old";
	}
}

# Check link
if ( -l "$BootDir/vmlinuz" ) {
	if ( readlink("$BootDir/vmlinuz") =~ /vmlinuz(.+)$/ ) {
		$link=$1;
		print "  Link 'vmlinuz' points to 'vmlinuz$link'.\n" if ($Debug >1);
	}
}

# Install the new kernel.
TieRpmDatabase(0);
unless ( CallRPM(\@RPMOutput, @flags, $RPM->filename) ==0 ) {
	my $failed= 1;
	# Couldn't install. Lets find out what we need
	print join("\n", @RPMOutput) . "\n" if (@RPMOutput and $Debug >1);
	@GetList= ();
	foreach $item (@RPMOutput) {
		next if ( $item =~ /^\s*$/); # Don't care about blank lines.
		next if ( $item =~ /^\s*error:/i); # Don't care about this line.
		# Try to figure out what we need
		if ($item =~ /^\s*(\S+)\s+.*conflicts with kernel-$NewVersion/) {
			$name= $1;
		} elsif ($item =~ /^\s*(\S+)\s+.*is needed by kernel-$NewVersion/) {
			$name= $1;
		} else {
			print "Warning: Unknown line: \"$item\"\n" if ($Debug >1);
			next;
		}
		next if (IsElement($name, @GetList));
		print "Requirement: $name\n" if $Debug;
		push(@GetList, $name);
	}
	if (@GetList) {
		my @rpms= @UpdatedRPMs;
		Do_Update();
		if (@rpms ne @UpdatedRPMs) {
			$failed= CallRPM(\@RPMOutput, @flags, $RPM->filename);
		}
	}
	if ($failed) {
		print "Failed to install new kernel: " . $RPM->rpmname . "\n";
		print "  " . join("\n  ", @RPMOutput) . "\n" if (@RPMOutput and ! $Quiet);
		return 1;
	}
}

$Kernel+=1;
push(@UpdatedRPMs, $RPM);
print "  " unless $Quiet;
print join("\n  ", @RPMOutput) . "\n  " if (@RPMOutput and ! $Quiet);
print $RPM->rpmname . "\n" unless $Quiet;

unless ($DoBoot) {
	return 0 unless ($DoInitRD);
	$InitRD= "$BootDir/initrd-$NewVersion.img";
	return Create_InitRD($InitRD, $NewVersion, 1)
}

if ($OldVersion =~ /^0.0-0/) {
	print STDERR "Warning: Cannot change boot manager since no old version is present!\n" if $Warnings;	
	return 0;
}

# Parse boot manager configuration

unless (open(FILE,"<$BootConf")) {
  print STDERR "Error: Could not open: $BootConf ($!)\n";
  return 1;
}

print "Parsing $BootConf.\n" if $Debug;
$entry= 0;
if ($BootManager eq "lilo") {
	my $nr= -1;
	foreach $line ( <FILE> ) {
		if ( $link and $line =~ /^\s*image\s*=.*\/vmlinuz\s*$/) {
			print "Changing 'vmlinuz' to 'vmlinuz$link'!\n" if $Debug;
			$line =~ s/vmlinuz\s*$/vmlinuz$link\n/;
		}
		if ($line =~ /^\s*image\s*=.*\/vmlinuz-(\S+)/) {
			print "  Found lilo entry: $1.\n" if ($Debug >1);
			$entry= 1;
			$nr++;
			$Image[$nr]=$1;
			$Entries[$nr]= [];
			$Label[$nr]= "";
			push(@Conf,"--entry=$nr\n");
		} elsif ($line =~ /^\s*image\s*=/) {
			$entry = 0;
		} elsif ($line =~ /^\s*other\s*=/) {
			$entry = 0;
		}
		if ($entry) {
			if ($line =~ /^\s*label\s*=\s*(\S.*)$/) {
				if ($Label[$nr]) {
					print STDERR "Error: Kernel '$Image[$nr]' has more than one label!?\n";
					return 1;
				}
				$Label[$nr]=$1;
			}

			push(@{$Entries[$nr]}, $line);
		} else {
			push(@Conf,$line);
		}
	}
} elsif ($BootManager eq "grub") {
	my $nr= -1;
	foreach $line ( <FILE> ) {
		if ($line =~ /^\s*title\s+(.+)$/) {
			print "  Found grub entry: $1.\n" if ($Debug >1);
			$entry= 1;
			$nr++;
			$Image[$nr]= "";
			$Entries[$nr]= [];
			$Label[$nr]= $1;
			push(@Conf,"--entry=$nr\n");
		}
		if ($entry) {
			if ( $link and $line =~ /^\s*kernel\s.*\/vmlinuz(\s+|$)/) {
				print "Changing 'vmlinuz' to 'vmlinuz$link'!\n" if $Debug;
				$line =~ s/\/vmlinuz(\s+|$)/\/vmlinuz$link$1/;
			}
			if ($line =~ /^\s*kernel\s.*\/vmlinuz-(\S+)/) {
				if ( $Image[$nr] ) {
					print STDERR "Error: Entry '$Label[$nr]' has more than one kernel!?.\n";
					return 1;
				}
				$Image[$nr]= $1;
			}
			push(@{$Entries[$nr]}, $line);
		} else {
			push(@Conf,$line);
		}
	}
}
close(FILE);

# Investigate entries
$entry= 0;
$InitRD= "";
my $default= "";
my $newdefault= "";

for $line (@Conf) {
	unless ($line =~ /^--entry=(\d+)$/) {
		if ($line =~ /^\s*default\s*=\s*(\S+)\s*$/) {
			$default = $1;
			}
		push(@NewConf,$line);
		next;
	}
	my $i= $1;
	if ($Image[$i] eq $NewVersion) {
		print STDERR "Warning: New image '$NewVersion' already present in $BootConf!\n";
		return 0;
	}
	unless ($Image[$i] eq $OldVersion) {
		if ($Label[$i] =~ /$BootTag$/) {
			# Remove old versions
			if ($Image[$i] eq $CurrentKernel) {
				print STDERR "Warning: Cannot remove running kernel from boot manager ($Label[$i]).\n" if $Warnings;
				if ($Entry{$Label[$i]}) {
					print STDERR "Error: Label '$Label[$i]' appears more than once!\n";
					return 1;
				}
				$Entry{$Label[$i]}= 1;
			} else {
				print "Removing boot entry: '$Label[$i]' ($Image[$i])\n" if $Verbose;
				push(@RemovedKernel,$Image[$i]);
				next;
			}
		}
		push(@NewConf,@{$Entries[$i]});
		next;
	}
	$entry=1;
	my $label= $Label[$i];
	my $oldlabel=$label;
	if ( ! $BootAddAsNew and ($UseBootTag or $oldlabel !~ /\Q$OldVersion/) ) {
		$oldlabel="$label$BootTag";
	}
	if ($Entry{$oldlabel}) {
		print STDERR "Error: Label '$oldlabel' appears more than once!?\n";
		return 1;
	}
	@{$Entry{$oldlabel}}= @{$Entries[$i]};
	if ($oldlabel ne $label) {
		# Need to rewrite old entry
		for $item (@{$Entry{$oldlabel}}) {
			if ($item =~ /^\s*label\s*=/ or $item =~ /^\s*title\s+/) {
				$item =~ s/\Q$label/$oldlabel/;
			} elsif ($item =~ /^(\s*alias\s*=\s*)(\S+)/) {
				my $tmp1 = $1;
				my $tmp2 = $2;
				if ( ! $BootAddAsNew and ($UseBootTag or $tmp2 !~ /\Q$OldVersion/) ) {
					$item="$tmp1$tmp2$BootTag";
				}
			}
		}
	}
	$newlabel=$label;
	$newlabel =~ s/\Q$OldVersion/$NewVersion/;
	if ($BootAddAsNew and ($UseBootTag or $newlabel !~ /$NewVersion/) ) {
		$newlabel= "$newlabel$BootTag" unless ($newlabel =~ /$BootTag$/);
	}
	if ($newlabel eq $oldlabel) {
		if ($Image[$i] eq $CurrentKernel) {
			print STDERR "Error: Cannot remove running kernel from boot manager ($Label[$i])!\n" if $Warnings;
			return 1;
		}
		print "Removing boot entry: '$Label[$i]' ($Image[$i])\n" if $Verbose;
		push(@RemovedKernel,$Image[$i]);
		$oldlabel= "";
	} else {
		if ($Entry{$newlabel}) {
			print STDERR "Error: Label '$newlabel' appears more than once!?\n";
			return 1;
		}
	}
	if ($default and $default eq $label) {
		$newdefault=$newlabel;
	}
	@{$Entry{$newlabel}}= @{$Entries[$i]};
	for $item (@{$Entry{$newlabel}}) {
		if ($item =~ /^\s*label\s*=/ or $item =~ /^\s*title\s+/) {
			$item =~ s/\Q$label/$newlabel/;
			next;
		}
		$item =~ s/\Q$OldVersion/$NewVersion/;
		if ($item =~ /^(\s*initrd\s*[=\s]\s*)(\S+)/) {
				$InitRD=$2;
				unless ($InitRD =~ /$NewVersion/) {
					$InitRD= "$BootDir/initrd-$NewVersion.img";
					$item = "$1$InitRD\n";
				}
		} elsif ($item =~ /^(\s*alias\s*=\s*)(\S+)/) {
			my $tmp1 = $1;
			my $tmp2 = $2;
			if ( $BootAddAsNew and ($UseBootTag or $tmp2 !~ /$NewVersion/) ) {
					$item="$tmp1$tmp2$BootTag";
			}
		} 
	}
	if ($BootAddAsNew) {
		push(@NewConf,"--entry=$oldlabel\n") if $oldlabel; 
		push(@NewConf,"--entry=$newlabel\n");
	} else {
		push(@NewConf,"--entry=$newlabel\n");
		push(@NewConf,"--entry=$oldlabel\n") if $oldlabel;
	}
}

unless ($entry) {
	print STDERR "Warning: Old image '$OldVersion' not present in $BootConf!\n" if $Warnings;
	return 0;
}

# Change default entry
if ($newdefault) {
	for $line (@NewConf) {
		if ($line =~ /^(\s*default\s*=\s*)\S+(\s*)$/) {
			$line = "$1$newdefault$2";
			}
	}
}

# Did the old kernel have an initrd?
my $oldinitrd;
if ($InitRD) {
	$oldinitrd= 1;
} else {
	$oldinitrd= 0;
	$InitRD= "$BootDir/initrd-$NewVersion.img";
}
unless ( Create_InitRD($InitRD, $NewVersion, $oldinitrd) == 0) {
	return 1;
}

return 0 unless ($DoBoot);

if ( !$oldinitrd and -f $InitRD) {
	# mkinitrd thinks we need one, so lets add it
	print STDERR "Warning: Adding initrd to new boot image.\n" if $Warnings;
	$InitRD= "$BootDir/initrd-$NewVersion.img";
	if ($BootManager eq "lilo") {
		push(@{$Entry{$newlabel}},"\tinitrd=$InitRD\n");
	} elsif ($BootManager eq "grub") {
		if ($BootMnt) {
			$InitRD =~ s/^$BootMnt//;
		}
		push(@{$Entry{$newlabel}},"\tinitrd $InitRD\n");
	}
}

# Backup boot manager config

my $mode;
unless ( $mode=(stat($BootConf))[2] ) {
	$mode= "0600";
}

unless ($ChangedBoot) {
	print "Writing backup: $BootConf.bak.\n" if $Debug;
	unless ( copy($BootConf, "$BootConf.bak") ) {
		print STDERR "Error: Failed to backup $BootConf ($!)\n";
		return 1;
	}
	chmod($mode,"$BootConf.bak");
}

# Write new boot manager config

if ($Test and ($BootConf !~ /\.test$/)) {
	$BootConf= $BootConf . ".test";
}

print "Writing new $BootConf.\n" if $Verbose;
unless (open(FILE,">$BootConf")) {
  print STDERR "Error: Could not open: $BootConf ($!)\n";
  return 1;
}
chmod($mode, $BootConf);
$ChangedBoot=1;

foreach $line ( @NewConf ) {
	if ($line =~ /^--entry=(.+)$/) {
		print FILE @{$Entry{$1}};
	} else {
		print FILE $line;
	}
}
close(FILE);

return 0;
}


######################
# Main
######################

my ($item, $tmp);

if ( -d $ConfigDir ) {
	if ( ((stat($ConfigDir))[2] >> 1)%2 ) {
		fatal("Configuration directory '$ConfigDir' is world writeable!");
	}
} else {
	print STDERR "Warning: No configuration directory: $ConfigDir\n" if $Warnings;
}

Getopt::Long::Configure('pass_through');
GetOptions('debug=i' => \$Debug,
	'config=s' => \$ConfigFile);
@UpdInExclude= ();
@DldConfig=();
Read_Config($ConfigFile);

if ( $0 =~ /autoupd$/) {
	$DoDld=0;
	$DoUpdate=1;
	$DoMerge=0;
	$DoPurge=0;
	$RemoveBad=0;
	$CleanUp=0;
	$BestMatch=1;
	$CheckLocal=1;
} elsif ( $0 =~ /autoins$/) {
	$DoDld=0;
	$DoInstall=1;
	$DoMerge=0;
	$DoPurge=0;
	$RemoveBad=0;
	$CleanUp=0;
	$BestMatch=1;
	$CheckLocal=2;
} elsif ( $0 =~ /automrg$/) {
	$DoDld=0;
	$DoUpdate=0;
	$DoInstall=0;
	$DoMerge=1;
	$CheckSize=1;
} elsif ( $0 =~ /autoprg$/) {
	$DoDld=0;
	$DoUpdate=0;
	$DoInstall=0;
	$DoMerge=0;
	$DoPurge=1;
	$CheckSize=1;
	$RemoveBad=1;
	$CheckLocal=0;
} elsif ( $0 =~ /autoget$/) {
	$DoGet=1;
	$DoDld=2 if $DoDld;
	$DoInstall=0;
	$DoMerge=0;
	$DoPurge=0;
	$CheckSize=1;
} elsif ( $0 =~ /autodld$/) {
	$DoDld=1;
	$GetAll=1;
	$CheckSize=1;
}
@InExclude= ();
my $SaveUpdateDir= $UpdateDir;
my @SaveDldConfig= @DldConfig;
@DldConfig= ();

Getopt::Long::Configure('default');
$item= GetOptions('dld!' => \$DoDld,
	'ftp!' => \$DoDld, #Old syntax
	'get!' => \$DoGet,
	'url=s' => \$URL,
	'dldconfig=s' => \@DldConfig,
	'dldall+' => \$DldAll,
 	'dldrecursive!' => \$DldRecursive,
 	'dldrecurse!' => \$DldRecursive, #Old syntax
 	'dlddirinclude=s' => \$DldDirInclude,
 	'dldrpminclude=s' => \$DldRpmInclude,
	'dldaddinstalled!' => \$DldAddInstalled,
	'dldmatch!' => \$DldMatch,
	'getall!' => \$GetAll,
	'bestmatch!' => \$BestMatch,
	'removebad!' => \$RemoveBad,
	'passive!' => \$DefaultPassive,
	'httpsendhost!' => \$DefaultHTTPSendHost,
	'addtodb!' => \$DoAddToDB,
	'adddbentry=s' => \$AddDBEntry,
	'mergedb=s' => \$MergeDB,
	'createdb=s' => \$CreateDB,
	'dldusedb!' => \$DldUseDB,
	'update!' => \$DoUpdate,
	'install!' => \$DoInstall,
	'kernel!' => \$DoKernel,
	'initrd!' => \$DoInitRD,
	'boot!' => \$DoBoot,
	'checksize!' => \$CheckSize,
	'checksig!' => \$CheckSig,
	'checkvendor!' => \$CheckVendor,
	'checkgpg!' => \$CheckGPG,
	'gpghomes' => \$GPGHome,
	'repackage!' => \$Repackage,
	'resolve!' => \$Resolve,
	'resolveuserpmdb!' => \$ResolveUseRPMDB,
	'recursive!' => \$Recursive,
 	'recurse!' => \$Recursive, #Old syntax
	'updatedir=s' => \$UpdateDir,
	'installdir=s' => \$InstallDir,
	'rpmdir=s' => \$RPMDir,
	'postupdatescript=s' => \$PostUpdateScript,
	'postupdldscript=s' => \$PostDldScript,
	'distversion=s' => \$DistVersion,
	'distlang=s' => \$DistLang,
	'distarch=s' => sub { SetDistArch($_[1]) },
	'database=s' => \$DataBase,
	'databasero!' => \$DataBaseRO,
	'log!' => \$DoLog,
	'lock!' => \$DoLock,
	'logfile=s' => \$LogFile,
	'cleanup!' => \$CleanUp,
	'cleanupkernel!' => \$CleanUpKernel,
	'liloaddasnew!' => \$BootAddAsNew,
	'bootaddasnew!' => \$BootAddAsNew,
	'include=s' => sub { push(@InExclude, "0" . $_[1]) },
	'exclude=s' => sub { push(@InExclude, "1" . $_[1]) },
	'merge!' => \$DoMerge,
	'mergeall!' => \$MergeAll,
	'mergematch!' => \$MergeMatch,
	'purge!' => \$DoPurge,
	'client' => sub { $DoDld=0; $DoUpdate=1; $DoMerge=0; $DoPurge=0; $RemoveBad=0; $CleanUp=0; $DataBaseRO=1}, #Old syntax
	'arch=s' => sub { SetArch($_[1]) },
	'compare=s' => \$Compare,
	'whatprovides=s' => \$WhatProvides,
	'requires' => sub { $PrintRequires=1 },
	'requiresxml' => sub { $PrintRequires=1; $XML=1 },
	'test!' => \$Test,
	'verbose' => sub { $Verbose=1; $Quiet=0 },
	'noverbose' => sub { $Verbose=0; },
	'quiet!' => \$Quiet,
	'warnings!' => \$Warnings,
	'rpmnamewarnings!' => \$DefaultRPMNameWarnings,
	'fixrpmnames!' => \$DefaultFixRPMNames,
	'uselwp!' => \$DefaultUseLWP,
	'comps=s' => \$Comps,
	'compsdefault=i' => \$CompsDefault,
	'languages=s' => sub { @DistLanguages = split(/,/, $_[1])},
	'debugftp!' => \$DebugFTP,
	'debuglwp!' => \$DebugLWP,
	'debughttp!' => \$DebugHTTP,
	'debugaupm!' => \$DebugAUPM,
	'debugcomps!' => \$DebugCOMPS,
	'showlatest!' => \$showlatest,
	'showget!' => \$showget,
	'checklocal=i' => \$CheckLocal,
	'nochecklocal' => sub { $CheckLocal=0 },
	'version!' => \$showversion,
	'help!' => \$showhelp );

unless ($item) {
    print STDERR "Try '$0 --help'.\n";
    exit 1;
}

if ($showhelp or $showversion)
{
	print "autoupdate version $Version <autoupdate\@mat.univie.ac.at>.\n";
	print $USAGE if $showhelp;
	exit 0;
}

if ($DebugAUPM) {
	$autoupdate::DEBUG= $DebugAUPM;
}
if (($DebugFTP or $DebugHTTP or $DebugLWP or $DebugAUPM or $DebugCOMPS) and !$Debug) {
	$Debug= 1;
}

print "autoupdate version $Version\n" if $Debug;

if (@InExclude) {
	# Use in-/exclude patterns from command line
	@UpdInExclude= @InExclude;
}

# Array of all kernel rpm names
@KernelNames=qw(kernel);
foreach $item ( split(/,/,$KernelExt) ) {
	push(@KernelNames,"kernel-$item");
}		

# Set up defaults

$Verbose = 1 if ($Debug or $Test);
if ($Debug) {
	$DefaultRPMNameWarnings = 1;
	$Warnings = 1;
	$Quiet = 0;
}
$Verbose = 0 if $Quiet;
#$Warnings = 1 if $Verbose;
$DefaultRPMNameWarnings = 0 unless ($Warnings);
$RPMNameWarnings = $DefaultRPMNameWarnings;

$RpmBindings= LoadRpmBindings($RpmBindings);

$DoGet= 1 if ($showget);
$DoPurge= 1 if $CleanUp;
unless ($RPMDir) {
	$DldAddInstalled = 1;
	$ResolveUseRPMDB = 1;
}
if ($RemoveBad) {
	$CheckSize = 1;
}
if ($DoInstall) {
	$DoUpdate = 1;
}
if ($DoGet) {
	$DoInstall = 0;
	$DoDld = 1 unless ($DoUpdate);
}
if (@DldConfig or $URL) {
	$DoDld = 1;
}
if (@SaveDldConfig and ! @DldConfig) {
	@DldConfig= @SaveDldConfig;
	@SaveDldConfig= ();
}
if ($PrintRequires) {
	$Resolve = 1;
	$DoGet = 0;
	$DoDld = 0;
}

# Compare version strings
if ($Compare)
{
	my $vera="";
	my $verb="";
	if( $Compare =~ /^([^,]*),([^,]*)$/ ) {
		$vera=$1;
		$verb=$2;
	} else {
		print STDERR "Usage: --compare 1.1-1,1.0-1\n";
    	print STDERR "Try '$0 --help'.\n";
		exit 1;
	}
	if ($vera =~ /^(.+)\.\w+\.rpm$/) {
		$vera = $1;
	}
	if ($vera =~ /^(.*-)?([^-]+-[^-]+)$/) {
		$vera = $2;
	}
	if ($verb =~ /^(.+)\.\w+\.rpm$/) {
		$verb = $1;
	}
	if ($verb =~ /^(.*-)?([^-]+-[^-]+)$/) {
		$verb = $2;
	}
	print "Comparing: \"$vera\" and \"$verb\"\n" if $Debug;
	$vera= Header->new("foo-$vera.noarch.rpm", "r");
	$verb= Header->new("foo-$verb.noarch.rpm", "r");
	if ($vera and $verb) {
		print $vera->compare($verb)."\n";
		exit 0;
	} else {
		print STDERR "Error: Illegal version string\n";
		exit 1;
	}
} 

&TestArch;
print "Using architecture: " . &GetArch . "\n" if $Debug;
$DistArch= &GetDistArch;

# Use InstallDir for DoInstall
if ($DoInstall and $InstallDir) {
	$UpdateDir=$InstallDir;
}
if ($UpdateDir =~ /(^.*\/)\/$/) {
	$UpdateDir = $1;
	$DefaultStoreByArch = 1;
}

#

# Make sure all paths are absolute
$cwd = getcwd();
$cwd = "" if ($cwd eq "/");
$Comps= "$cwd/$Comps" if ($Comps and $Comps !~ /^\//);
$DataBase= "$cwd/$DataBase" if ($DataBase and $DataBase !~ /^\//);
$RPMDir= "$cwd/$RPMDir" if ($RPMDir and $RPMDir !~ /^\//);
$UpdateDir= "$cwd/$UpdateDir" if ($UpdateDir and $UpdateDir !~ /^\//);

# Substitute DistVersion and DistArch
$DistVersionNoDot= $DistVersion;
$DistVersionNoDot =~ tr/.//d;

Substitute_Dist_Data($RPMDir);
Substitute_Dist_Data($UpdateDir);
Substitute_Dist_Data($SaveUpdateDir);
Substitute_Dist_Data($DataBase);
Substitute_Dist_Data($Comps);

Strip_Dir($RPMDir,"1");
Strip_Dir($UpdateDir,"1");
Strip_Dir($SaveUpdateDir,"1");

# Does the UpdateDir contain subdirs according to arch?
if ( !$DefaultStoreByArch and -d "$UpdateDir/$DistArch" ) {
	print "Warning: $UpdateDir contains subdirs according to arch.\n" if $Warnings;
	print "         Please make sure it has two trailing slashes (in the config file)\n" if $Warnings;
	print "         if you want to check these subdirs.\n" if $Warnings;
}

#Show latest version
if ($showlatest) {
	if ($CheckLocal) {
		if ($DoDld) {
			SetLocalRPMs();
			Get_Local_List($RPMDir) if ($RPMDir);
			Get_Local_List($UpdateDir) if ($UpdateDir);
		} else {
			SetLocalRPMs("l");
		}
	}
	@InExclude= @UpdInExclude;
	@AllRPMs= GetRPMHeaders("t", @ARGV );
	@ARGV= ();
	foreach $item (SelectRPMs(\@AllRPMs, $BestMatch, $CheckLocal)) {
		print $item->filename . "\n";
	}
	exit 0;
}

$DoLog= 0 unless ($LogFile);
if ($DoLog and $LogFile eq "syslog") {
	eval "use Sys::Syslog";
	if ($@) { 
		warn('Failed to load Sys::Syslog');
		$DoLog=0;
	}
}

# Set up default for provides database
# Use database in update/config dir if available
unless ($DataBase) {
	if ( -f "$ConfigDir/$DataBaseName" ) {
		$DataBase="$ConfigDir/$DataBaseName";
	} elsif ( -f "$UpdateDir/$DataBaseName" ) {
		$DataBase="$UpdateDir/$DataBaseName";
	} elsif ( -f "$SaveUpdateDir/$DataBaseName" ) {
		$DataBase="$SaveUpdateDir/$DataBaseName";
	} else {
		$DataBase="$UpdateDir/$DataBaseName";
	}
}

if ($WhatProvides) {
	unless (OpenProvides("ro")) {
 		exit 1;
	}
	if ($Provides{$WhatProvides}) {
		print "$Provides{$WhatProvides}\n";
	}
	OpenProvides();
	exit 0;
}

fatal("Cannot execute RPM ($RPM)") unless ( -x $RPM );
CallRPM(\@RPMOutput, "-q", "rpm" );
foreach $item (@RPMOutput) {
	next unless ($item =~ /^rpm-(\d+\.\d+)/);
	if ($1 >= "4.1") {
		print "rpm is 4.1 or newer.\n" if $Debug;
		$RPM_nogpg= 0;
	}
	last;
}
# Make sure the gpg keyring is found if run from cron
if ($CheckGPG) {
	if ($GPGHome) {
		$ENV{'GNUPGHOME'} = $GPGHome;
	} elsif (! $ENV{'HOME'} or ! -d "$ENV{'HOME'}/.gnupg/") {
		$ENV{'HOME'} = "/root" if (-d "/root/.gnupg/");
	}
}

#
# Create a lock file
#

if ($DoLock) {
	if ( -e $LockFile ) {
		open(LOCK,"<$LockFile") || fatal("Could not read the lock file: $LockFile ($!).");
		my $pid= <LOCK>;
		close(LOCK);
		fatal("Illegal lock file ($LockFile)!?") unless ($pid =~ /^(\d+)$/);
		$pid =$1;
		fatal("Cannot execute PS ($PS)") unless ( -x $PS );
		if ( system("$PS --no-heading $pid >/dev/null") == 0 ) {
			print STDERR "Error: autoupdate already running under pid $pid!\n";
			exit(1);
		}
		print "Warning: Found stale lock file.\n" if ($Warnings);
		$CheckOldRequirements= 1;
	}

	open(LOCK,">$LockFile") || fatal("Could not create the lock file: $LockFile  ($!)");
	print LOCK $$;
	close(LOCK);
	$HaveLock= 1;
}

#
# Determine all available rpms
#

# Don't exclude any rpms at this point
@InExclude= ();

# Determine allowed archs
if ($BestMatch and $DoDld) {
	@AllowedArch= &GetAllowedArch;
} else {
	@AllowedArch= &GetAllowedDistArch;
}
if ($DistArch eq "SRPMS") {
	@AllowedArch= qw(SRPMS);
}

@AllRPMs= ();
if ($DoDld or $DoGet) {
	if (@ARGV) {
		print "Setting up rpm names to get.\n" if $Debug;
		$GetAll=0 unless ($DoGet);
		foreach $item (@ARGV) {
			if ($item =~ /^(.*)-([^-]+)-([^-]+)\.([^.]+)\.rpm/) {
				#print "Warning: $item is no rpm name. Taking $1 instead.\n" if $Warnings;
				$item= $1;
			}
			push(@GetList,$item);
		}
		@ARGV= ();
	} elsif ($DoGet) {
		if ($GetAll) {
			print "Warning: No rpms given to get. Searching for updates only.\n" if $Warnings;
		} else {
			fatal("No rpms given to get.");
		}
	} else {
		$GetAll= 1;
	}
}


my $type= "f";
if ($CheckSize) {
	print "Checking size of all rpms.\n" if $Debug;
	$type= "c";
}
if (@ARGV) {
	print "Using rpms from the command line.\n" if $Debug;
	$RPMsFromCL= 1;
	$type= "t" if ($type eq "f");
	@AllRPMs= GetRPMHeaders($type, @ARGV );
	@ARGV= ();
} else {
	print "Using rpms from $UpdateDir.\n" if $Debug;
	if ($DefaultStoreByArch) {
		foreach $item (@AllowedArch) {
			$tmp = "$UpdateDir/$item";
			push(@AllRPMs, GetRPMHeaders($type, Get_RPMs($tmp)) ) if ( -d $tmp );
		}
	} else {
		@AllRPMs= GetRPMHeaders($type, Get_RPMs($UpdateDir) );
	}
}


#
# Creat new provides database
#

if ($CreateDB) {
	OpenProvides();
	unlink($CreateDB) if ( -f $CreateDB );
	$DataBase= $CreateDB;
	if (OpenProvides("rw")) {
		my @rpms= SelectRPMs(\@AllRPMs, 1, 0);
		Get_Provides(@rpms) if (@rpms);
		OpenProvides();
	} else {
		fatal("Could not create $CreateDB: $!\n");
	}
	unlink($LockFile) if ($HaveLock);
	exit(0);
}

#
# Provides database
#

if ($Resolve or $DoAddToDB or $MergeDB) {
	# Make sure the database exists
	unless ( -f $DataBase ) {
		if (OpenProvides("rw")) {
			my @rpms= ();
			if ($RPMDir) {
				print "Adding $RPMDir to provides database.\n" if $Debug;
				@rpms= GetRPMHeaders("f", Get_RPMs($RPMDir) );
			} else {
				print "Adding installed rpms to provides database.\n" if $Debug;
				Get_Provides();
			}
			print "Adding $UpdateDir to provides database.\n" if $Debug;
			push(@rpms, GetRPMHeaders("f", Get_RPMs($UpdateDir) ));
			@rpms= SelectRPMs(\@rpms, 1, 0);
			Get_Provides(@rpms) if (@rpms and ! $Test);
		}
	}
	if ($MergeDB) {
		print "Merging provides database: $MergeDB\n" if $Verbose;
		MergeProvides($MergeDB);
	}
	if ($AddDBEntry) {
		print "Adding entry to provides database: $DataBase\n" if $Verbose;
		# syntax is name:capability
		if (OpenProvides("rw") and $AddDBEntry =~ /^\s*(\S+):(\S+)\s*$/) {
			$Provides{$2} = $1;
		} else {
			print STDERR "Error: Could not add entry to $DataBase: $!\n";
			$RetGen= 1;
		}
		unlink($LockFile) if ($HaveLock);
		exit $RetGen;
	}
	if ($DoAddToDB) {
		print "Adding to provides database: $DataBase\n" if $Verbose;
		if (OpenProvides("rw")) {
			my @rpms= SelectRPMs(\@AllRPMs, 1, 0);
			Get_Provides(@rpms) if (@rpms and ! $Test);
		} else {
			print STDERR "Error: Could not add to $DataBase: $!\n";
			$RetGen= 1;
		}
	} elsif (OpenProvides("ro")) { # add rpms we do not know
		foreach $item (@AllRPMs) {
			Get_Provides($item) unless ($DataBaseRO or $Provides{$item->name});
		}
	}
	if ($Resolve) {
		OpenProvides("ro");
	} else {
		OpenProvides();
	}
	if (@GetList) {
		foreach $item (@GetList) {
			if ( $item !~ /^@/ and $Provides{$item} ) {
				$item= $Provides{$item};
			}
		}
	}
}

# Print requirements for given files

if ($PrintRequires) {
	my $rpm;
	my @requirements = ();
	unless ($XML) {
		foreach $rpm (@AllRPMs) {
			push(@requirements, $rpm->name);
		}
	}
	foreach $rpm (SelectRPMs(\@AllRPMs, 1, 0)) {
		if ($XML) {
			print "  <package>\n";
			print "    <name>" . $rpm->name ."</name>\n";
			print "    <dependencylist>\n";
			@requirements = ($rpm->name);
		}
		foreach $item ($rpm->requires) {
			chomp($item);
			next if ($item =~ /^\s*$/);
			next if ($item =~ /^\s*warning:/i);
			next if ($item =~ /^\s*error:/i);
			if ($item =~ /^(\S+)\s+[>=]+\s+(\S+)\s*$/) {
				$item= $1;
			} elsif ($item =~ /^(\S+)\s/) {
				$item= $1;
			}
			next if ($item =~ /^rpmlib\(.*\)/); #RPM stuff
			my $name= $Provides{$item};
			if (! $name and $ResolveUseRPMDB) {
				$name= Requirement2NameRPMDB($item);
			}
			unless ($name) {
				print STDERR "Error: $item is not in provides data base.\n";
				$name= $item;
			}
			next if (IsElement($name, @requirements));
			push (@requirements, $name);
			if ($XML) {
					print "      <dependency>$name</dependency>\n";
			} else {
				print "$name\n";
			}
		}
		if ($XML) {
			print "    </dependencylist>\n";
			print "  </package>\n";
		}
	}
	OpenProvides();
	unlink($LockFile) if ($HaveLock);
	exit $RetGen;
}

@SaveGetList= @GetList;

#Show get list
if ($showget) {
	if ($CheckLocal) {
		if ($DoDld) {
			SetLocalRPMs();
			Get_Local_List($RPMDir) if ($RPMDir);
			Get_Local_List($UpdateDir) if ($UpdateDir);
		} else {
			SetLocalRPMs("l");
		}
	}
	SetGetList();
	foreach $item (@GetList) {
		print "$item\n";
	}
	unlink($LockFile) if ($HaveLock); 
	exit $RetGen;
}

#
# Dld Part
#

if ($DoDld) {
  # Get a list of rpms we already have
  if ($DldAddInstalled) {
	  SetLocalRPMs("l");
  } else {
	  SetLocalRPMs();
  }
  if ( $RPMDir ) {
	Get_Local_List($RPMDir);
  }
  foreach $item (@AllRPMs) {
	AddLocalRPMs("f", $item->filename);
  }
  if ($DoGet) {
	my $haveall= SetGetList();
	$DoDld= 0 if ($haveall and $DoDld == 2);
  }
}

if ($DoDld) {
  print "\nDoing download part.\n\n" if $Debug;
  my @OrigGetList= @GetList;

  # See if the rpms we have already require some new ones
  if ($Resolve and $CheckOldRequirements) {
	print "Checking requirements for old rpms.\n\n" if $Debug;
	Get_Requirements(SelectRPMs(\@AllRPMs, 1, 0));
  }
  if ($URL) {
	print "Fetching rpms from URL passed on the command line:\n" if $Debug;
	my $dir;
	$Protocol="ftp"; #default
	if ($URL=~ /^(\w+):\/\/(.*)$/) {
		$Protocol=lc($1);
		$URL=$2;
	} elsif ($URL=~ /^file:(.*)$/i) {
		$Protocol="file";
		$URL=$1;
	}
	if ($Protocol eq "file") {
		$Host= "localhost";
		$dir= $URL;
	} else {
		($Host, $dir)= ($URL=~/([^\/]+)(.*)/);
		if ($Host=~/^(.*)\@(.+)$/) {
			($User, $Host)= ($1, $2);
		}
		if ($User and $User=~/^(\w*):(.*)$/) {
			($User, $Pass)= ($1, $2);
		}
	}
	$StoreDir= $UpdateDir;
	$Passive= $DefaultPassive;
	$HTTPSendHost= $DefaultHTTPSendHost;
	$FixRPMNames= $DefaultFixRPMNames;
	$UseLWP= $DefaultUseLWP;
	$dir= "." unless $dir;
	if ($Host) {
		@DldDirs= ();
		if ( $dir=~ /^(.+\/)\/$/ ) {
			$dir=$1;
			@NoWarnDirs= ();
			for $item (@AllowedArch) {
				push(@NoWarnDirs, "$dir$item/") unless ($item eq $DistArch);
				push(@DldDirs, "$dir$item/");
			}
		} else {
			@DldDirs= ($dir);
		}
		$RetDld= Get_Remote_RPMs();
	} else {
		print STDERR "Error: No host given!\n";
		$RetGen= 1;
	}
  } else {
	print "Processing download config files:\n" if $Debug;
	my $file;
	unless ($DldConfigDir) {
		$DldConfigDir = $ConfigDir;
	}
	unless (@DldConfig) {
		if ( -d $DldConfigDir ) {
			if ($DoGet) {
				@DldConfig= glob("$DldConfigDir/*.get");
			} else {
				@DldConfig= glob("$DldConfigDir/*.dld $DldConfigDir/*.ftp");
			}
		} else {
			print STDERR "Error: No dld configuration directory: $DldConfigDir\n";
			$RetGen=1;
		}
	}
	unless (@DldConfig) {
		print STDERR "Warning: No download configuration files found.\n" if $Warnings;
	}
	foreach $file ( @DldConfig ) {
		if ( Read_Dld_Config($file)==0 ) {
			unless (Get_Remote_RPMs() ==0) {
				$RetDld= 1;
			}
		} else {
			$RetGen= 1;
		}
		print "\n" if $Debug;
	}
  }

  @NoWarnDirs= ();

  for $item (@OrigGetList) {
	next unless (CheckLocal($item) eq "0.0-0");
	print STDERR "Warning: $item not found during download.\n" if $Warnings;
  }
  
  my $total = @DownloadedRPMs;
  if ($total) {
	push(@AllRPMs, @DownloadedRPMs);
	print "Downloaded a total of $total rpms.\n" if ($Verbose and $total>1);
	Do_PostScript($PostDldScript);
  }

  if ($DoLog) {
	my @log= ();
	push(@log, "sum: Downloaded a total of $total rpm(s).");
	for $item (@DownloadedRPMs) {
		push(@log, "dld: " . $item->rpmname);
	}
	Do_Log(@log);
  }

@DownloadedRPMs= ();
}


# Remove any remaining bad rpms
if ($RemoveBad and keys %BadRPMs) {
	print "\n" if $Debug;
	print "Removing bad rpms:\n" if $Verbose;
	foreach $item (keys %BadRPMs) {
		print "  " . basename($item) . "\n" if $Verbose;
		next if ($Test);
		unless ( unlink($item) ) {
			print STDERR "Error: Failed to remove: ". $item . " ($!).\n";
			$RetGen= 1;
		}
	}
}


#
# Purge part
#

@InExclude= ();
Purge_RPMs() if ($DoPurge);

#
# Update part
#

@UpdatedRPMs= ();
@RemovedKernel= ();

if ($DoUpdate) {
	print "\nDoing update/install part.\n\n" if $Debug;
	# Upgrade all rpms
	$RetUpd= Update_RPMs();
	Do_PostScript($PostUpdateScript) if (@UpdatedRPMs);
	OpenProvides() if ($Resolve);
	my $total = @UpdatedRPMs;
	my $status = "Upgraded";
	$status = "Installed" if ($DoInstall or $DoGet);
	print "$status a total of $total rpms.\n" if ($Verbose and $total>1);
	if ($DoLog) {
		my @log= ();
		push(@log, "sum: $status a total of $total rpm(s).");
			push(@log, "sum: Installed boot manager.") unless (! $ChangedBoot);
			my $tmp;
			for $item (@UpdatedRPMs) {
				$tmp= CheckLocal($item->name);
				if (!$tmp or $tmp eq "0.0-0") {
					$status="ins";
				} else {
					$status="upd";
				}
				push(@log, "$status: " . $item->rpmname);
			}
		Do_Log(@log);
	}
}


#
# Merge part
#

Merge_RPMs() if ($DoMerge and $RetUpd==0);

#
# Finally clean up
#

TieRpmDatabase(0);

if ($CleanUp and @UpdatedRPMs) {
	print "\n" if $Debug;
	print "Removing updated rpms:\n" if $Verbose;
	foreach $item (@UpdatedRPMs) {
		next unless (IsElement($item, @AllRPMs)); #Maybe added during get from rpmdir
		print "  " . $item->rpmname . "\n" if $Verbose;
		next if ($Test);
		unless ( unlink($item->filename) ) {
			print STDERR "Error: Failed to remove: ". $item->filename . " ($!).\n";
			$RetGen= 1;
		}
	}
}

unlink($LockFile) if ($HaveLock);

exit ($RetGen + 2*$RetUpd + 4*$RetDld);

