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

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

use strict;
use vars qw($showhelp $showversion $showlatest $showget $StoreDir $Host $Port $User $Pass
	$Protocol $LWPua $DldResult $BaseDir $DirWarnings $DldUseAutoProvides
	$DldUseAutoInfo $DldUseSynthesis $DldUseHeaderInfo $DldConfigDisabled $FTPWait $Passive $FTPRetry
	@DldDirInclude @DldRpmInclude @DldDbInclude @DldDataBase %DldDataBaseType %DldDataBaseDir
	@AllowedArch @SubDirs @DldDirs @DldConfig @InExclude @UpdInExclude @AllRPMs
	@SelectedRPMs @SelectedKernelRPMs @SelectedKernelModulesRPMs %ResolveRPMs %NewResolveRPMs %BadRPMs
	@UpdatedRPMs @DownloadedRPMs @DepList @GetList @SaveGetList @KernelNames @KernelModules @RemovedKernel
	@RPMOutput %TmpProvides %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 $DldNew = 0;
my $DldUnknown = 0;
my $DldRecursive = 0;
my $DldMatch = 0;
my $BestMatch = 1;
my $RemoveBad = 0;
my $GetAll = 0;
my $UseTimeStamps = 1;
my $DefaultPassive = 1;
my $DefaultFTPRetry = 0;
my $DefaultFTPWait = 10;
my $Firewall = "";
my $FirewallType = 0;
my @LWPProtocols = qw( http https );
my $Proxy = "";
my $ProxyUser = "";
my $ProxyPass = "";
my @NoProxy = ();
my $Timeout = 0;
my $SSHCipher;
my $DldAddInstalled = 0;
my $DefaultDldUseAutoProvides = 1;
my $DefaultDldUseAutoInfo = 0;
my $DefaultDldUseSynthesis = 0;
my $DefaultDldUseHeaderInfo = 0;
my $DefaultDirWarnings = 1;
my $DoAddToDB = 0;
my $MergeDB = "";
my $CreateDB = "";
my $DoUpdate = 0;
my $DoInstall = 0;
my $DoKernel = 0;
my $KernelExt = "smp,enterprise,bigmem,debug";
my $KernelMod = "unsupported,modules-alsa,ntfs";
my $DoInitRD = 1;
my $DoBoot = 0;
my $CheckSize = 0;
my $CheckSig = 0;
my $CheckVendor = 0;
my $CheckModified = 0;
my $CheckGPG = 0;
my $GPGHome = "";
my $Repackage = 0;
my $RpmRoot = "";
my @RpmOptions = ();
my $RpmBindings = "";
my $Resolve = 1;
my $ResolveUseRPMDB = 1;
my $ResolveAvoidNew = 1;
my $ResolveAllAtOnce = 0;
my $ResolveKernelNodeps = 0;
my $ResolveUnInstall = 0;
my $Recursive = 0;
my $DefaultStoreByArch = 0;
my $GenerateAutoInfo = 0;
my $AutoInfoProvides= 1;
my $AutoInfoFile = "";
my $AutoInfoBaseDir = "";
my $MaxRecursive = 100;
my $PostUpdateScript = "";
my $PostDldScript = "";
my $DistArch;
my %DistVars = ( DistName => "",  DistNameLC => "", DistVersion => "", DistVersionNoDot => "", DistLang => "en", DistArch => "");
my @DistLanguages = ( $DistVars{'DistLang'} );
if ($ENV{'LANG'}) {
	my $tmp= $ENV{'LANG'};
	if ($tmp =~ /^(\w\w_\w\w)\./) { # Strip encoding
		$tmp= $1;
	}
	@DistLanguages = ( $tmp );
}
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 $UseDataBase = 1;
my $UseRpmVerCmp = 0;
my $DoLog = 0;
my @HTTPExt= qw( html htm php asp );
my $ShellEscapes = 1;
my $Test = 0;
my $Verbose = 0;
my $HashMarks = 0;
my $Quiet = 0;
my $Warnings = 1;
my $DefaultRPMNameWarnings = $Warnings;
my $RPMNameWarnings = $DefaultRPMNameWarnings;
my $DefaultFixRPMNames = 1;
my $FixRPMNames = 0;
my $Debug = 0;
my $DebugLWP = 0;
my $DebugFTP = 0;
my $DebugSFTP = 0;
my $DebugRPM = 0;
my $DebugCOMPS = 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 $LiloConf = "/etc/lilo.conf";
my $BootMnt = "";
my $GrubConf = "/boot/grub/grub.conf";
my $BootDir = "/boot";
my $LogFile= "autoupdate.log";
if ( -d "/var/log" ) {
	$LogFile= "/var/log/" . $LogFile;
} elsif ( -d "/var/adm") {
	$LogFile= "/var/adm/" . $LogFile;
} else {
	$LogFile= "";
}
my $LockFile= "autoupdate.pid";
if ( -d "/var/lock/" ) {
	$LockFile= "/var/lock/" . $LockFile;
} elsif ( -d "/var/run") {
	$LockFile= "/var/run/" . $LockFile;
} else {
	$LockFile= "/tmp/" . $LockFile;
}

# 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 $GZIP = "/bin/gzip";
my $GUNZIP = "/bin/gunzip";
my $BZIP = "/usr/bin/bzip2";
my $BUNZIP = "/usr/bin/bunzip2";
my $DF = "/bin/df";
my $PS = "/bin/ps";

# Internal variables (do not change).
my $Version = "5.3";
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.
  --dldconfig file1[,file2[,..]] Download configuration files to include.
  --(no)dldnew                   Download all new rpms from remote site.
  --dldunknown                   Download unknown 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.
  --dlddbinclude regexp          Include only databases whose filename plus path match regexp.
  --dlddatabase path             Location of remote data base file to use.
  --(no)dldaddinstalled          Add installed rpms for comparison during download.
  --(no)dldmatch                 Apply Include/Exclude match during download.
  --(no)dlduseautoprovides       Search for provides database at dld site.
  --(no)dlduseautoinfo           Search for autoinfo file at dld site.
  --(no)dldusesynthesis          Use urpmi synthesis database from dld site.
  --(no)dlduseheaderinfo         Use header.info files 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.
  --lwpproxy url                 URL which will be passed as a proxy to LWP. 
  --lwpprotocols pro1[,pro2,..]  Use the LWP modules for these protocols.
  --(no)timestamps               Use timstamps when checking for new files.
  --(no)addtodb                  Add rpms to the provides database.
  --adddbentry name:capability   Add an entry to the provides database.
  --deldbentry name              Delete an entry from the 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)checkmodified            Check that the local rpm is unmodified before upgrading.
  --(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.
  --root dir                     Use this directory as root directory for rpm.
  --rpmoption                    Additional (long) rpm option to use during upgrade.
  --distmode                     Same as: kernel,resolve{allatonce,uninstall,kernelnodeps}
  --(no)resolve                  Try to resolve dependencies.
  --(no)resolveuserpmdb          Try to resolve dependencies using the rpm data base.
  --(no)resolveavoidnew          Try to avoid adding new rpms to resolve dependencies.
  --(no)resolveuninstall         Try to uninstall packages which have unresolvable dependencies.
  --(no)resolveallatonce         Try to upgrade all packages at once.
  --(no)resolvekernelnodeps      Install kernel rpms with nodeps option.
  --(no)checkoldrequirements     Check old packages for missing requirements
  --(no)recursive                Recursively descent through all local subdirectories.
  --updatedir dir                Directory containing the rpms to be updated.
  --installdir dir               Directory containing the rpms to be installed.
  --rpmdir dir                   Directory containing the distribution rpms.
  --postupdatescript             Script to execute after upgrading rpms.
  --postdldscript                Script to execute after downloading rpms.
  --distversion                  Distribution version.
  --distname                     Distribution name.
  --distlang                     Distribution language.
  --distarch                     Distribution architecture.
  --define var=val               Define the variable var to have value val.
  --database                     Provides database to use.
  --(no)usedb                    Use the provides database.
  --(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.
  --(no)autoinfo                 Generate autoinfo file.
  --autoinfofile file            Create given autoinfo file.
  --autoinfobasedir dir          Add this basedir to the autoinfo file.
  --(no)autoinfoprovides         Include provides information in autoinfo list.
  --test                         Test mode.
  --(no)verbose                  Be verbose.
  --(no)hash                     Print hash marks when installing rpms.
  --(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)debugftp                 Show debugging info for ftp actions.
  --(no)debugsftp                Show debugging info for sftp actions.
  --(no)debuglwp                 Show debugging info for lwp actions.
  --(no)debugrpm                 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 @UnknownRequirements= ();
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 $DelDBEntry = "";
my $HaveDB_File= 0;
my $DataBaseMode = "";
my $CurrentKernel = "";
my $RPMBaseDir= "";
my @NoWarnDirs = ();
my $RPM_41 = 0;
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);
}

######################
# 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, $incomment, $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;
$incomment= 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
		$line =~ s/<!--.*-->//g;
		if ($incomment == 1 and $line =~ /-->(.*)$/) {
			$incomment=0;
			$line= $1;
		}
		next if $incomment;
		if ($incomment == 0 and $line =~ /^(.*)<!--/) {
			$incomment=1;
			$line= $1;
		}
		# Ignore empty lines
		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 STDERR "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}= 1; #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 STDERR "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 STDERR "Warning: Already have $item.\n" if $Debug;
	} else {
		$haveall = 0;
		AddLocalRPMs("r", "$item-0-0.noarch.rpm"); #Pretend we have an older version
	}
}

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";
}

$DataBaseMode= $mode unless ($UseDataBase);

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

unless ($HaveDB_File) {
	# load the DB_File module
	eval "use DB_File";
	if ($@) {
		print STDERR "Error: Failed to load the \'DB_File\' module. Dependency resolution will not work.\n";
		$UseDataBase= 0;
		$DataBaseMode= "";
		$RetGen= 1;
		return $DataBaseMode;
	}
	$HaveDB_File= 1;
}


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_RDWR|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 {
	if ($DataBaseMode eq "ro") {
		print STDERR "Error: Failed to open provides database in rw mode ($!).\n";
		$DataBaseRO= 1; # Do not try again
	} else {
		print STDERR "Error: Failed to open provides database ($!).\n";
		$UseDataBase= 0;
		$DataBaseMode= $mode;
	}
	$RetGen= 1;
}

return $DataBaseMode;
}

######################
# 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) {
			if ($PrintRequires) {
				$rpm= GetInstalled($item);
				if ($rpm) {
					push(@rpms, $rpm);
					next;
				}
			}
			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 STDERR "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= 1;
				$asize= 1;
				next;
			} elsif ($line =~ /^D:.*\s+signature:\s+OK/) {
				$esize= 1;
				$asize= 1;
				next;
			} elsif ($line =~ /^D:\s/) {
				next;
			} elsif ($line =~ /^[Ee]rror:\s/) {
				next;
			} elsif ($line =~ /^[Ww]arning:\s/) {
				next;
			} elsif ($line =~ / failed/) {
				next;
			} elsif ($line !~ /\.rpm$/) {
				next;
			}
			$rpm= $line;
			last;
		}
		chomp($rpm);
		if (defined $esize and defined $asize and $asize != $esize) {
			print STDERR "Warning: " . basename($item) . ": Incorrect size.\n" if $Warnings;
			$BadRPMs{$item}= "1";
			next;
		}
		unless (defined $esize and defined $asize and defined $rpm) {
			print STDERR "Warning: Could not check $item\n";
			$BadRPMs{$item}= "1";
			next;				
		}
		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");
unless ($CheckGPG) {
	if ($RPM_41) {
		push(@flags, "--nosignature");
	} else {
		push(@flags, "--nogpg", "--nopgp");
	}
}

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

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

print "  Good signature: ". $item->rpmname . "\n" if ($Debug >1);
return 0;
}
######################
# Check if the local RPM is modified
######################

sub Check_Modified
{
return 0 unless ($CheckModified);

my $item = shift;

return 0 unless (CheckLocal($item->name));

if ( CallRPM(\@RPMOutput, "-V", $item->name) != 0) {
	print "Excluding modified rpm: ". $item->name ."\n" if $Verbose;
	return 1;
}

print "  Umodified: ". $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;
}

$oldvendor= $hdr->gettag("VENDOR");
return 0 unless ($oldvendor);
# Simplify vendor of old rpm
# "Inc." seems to come and go with vendors.  Also remove leading commas,
# spaces and remove trailing dots
$oldvendor =~ s/\s*\,\s*Inc\..*$//i;

$newvendor= $item->gettag("VENDOR");  

unless ($newvendor and $newvendor =~ /^$oldvendor/i) {
	print STDERR "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 if ($DataBaseRO);
	OpenProvides("rw");
	$Provides{$reqmnt}= $name;
	OpenProvides("ro");

	return $name;
}

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

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

my $name= $TmpProvides{$reqmnt};
return $name if ($name);

$name= $Provides{$reqmnt};
return $name if ($name);

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

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

print STDERR "Warning: Requirement \"$reqmnt\" is not in provides database.\n" if $Debug;
push(@UnknownRequirements, $reqmnt);

# 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);
		if ($item =~ /^(\S+)\s+[>=]+\s+(\S+)\s*$/) {
			$item= $1;
			$version= $2;
			if ($Provides{$item}) {
				$name= $Provides{$item};
			} else {
				$name= $item;
			}
			unless ( $GetAll or IsElement($name, @GetList) or !CheckLocal($name) ) {
				$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);
					push(@requirements, $name);
				}
			}
		} elsif ($item =~ /^(\S+)\s/) {
			$item= $1;
		}
		$name= Requirement2Name($item);
		next unless ($name);
		next if (IsElement($name, @GetList));
		if ($DldAddInstalled) {
			next if ( CallRPM(\@RPMOut, "-q", "--whatprovides", $item)==0 );
			push(@GetList, $name);
			if (CheckLocal($name)) {
				print"      Requirement: Newer version of $item ($name)\n" if $Debug;
				push(@requirements, $name) unless ($GetAll);
				next;
			}
		}
		next if (CheckLocal($name)); #Already have item
		AddLocalRPMs("r", "$name-0-0.noarch.rpm"); #Pretend we have an older version
		push(@GetList, $name);
		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);
		next if ($rpm =~ /^gpg-pubkey-/);
		$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);
		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
{

my $string = shift;

return 0 unless $string;

my @script = ();
foreach my $item ( split(/\s+/,$string) ) {
	if ( $item eq "#RPMS#") {
		foreach my $rpm (@_) {
			push(@script, $rpm->filename);
		}
	} else {
		push(@script, $item);
	}
}
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 unless ($DoLog);
return 0 if ($Test and $LogFile ne "-");

unless ($LogFile) {
	print STDERR "Error: No log filename set.\n";
	$DoLog= 0;
	$RetGen= 1;
	return 1;
}

my $now= localtime();
if ($now =~ /^\S+\s+(.*)\s+\S+$/) {
	$now= $1;
}
print "Writing log file: $LogFile ($now)\n" if ( $Debug > 2);
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 ) {
		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);
			}			
		}
	} else {
		push(@rpms,glob("$dir/*.rpm"));
	}
}

return @rpms;
}

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

sub Substitute_Dist_Data {

foreach my $key (keys(%DistVars)) {
	$_[0] =~ s/#$key#/$DistVars{$key}/g if ($DistVars{$key});
}

}


######################
# 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;
	$_ =~ s/\r$//; # fix dos type line endings
	next if ( $_ =~ /^\s*#/ );
	next if ( $_ =~ /^\s*$/ );
	if ( $_ =~ /Reset(Include|Exclude|Patterns)/ ) {
		@UpdInExclude = ();
		next;
	}
	unless ($_ =~ /^\s*(\w+)\s*=(.*)$/) {
		print STDERR "Warning: Unknown line '$_' in $file.\n" if $Warnings;
		next;
	}
	($var, $val)= ($1, $2);
	if ($val =~ /^\s+(\S.*)$/) {
		$val= $1;
	}
	if ($val =~ /^(.*\S)\s+$/) {
		$val= $1;
	}
	if ($val =~ /^\s+$/) {
		$val= "";
	}
	$var =~ s/^Dir(.+)$/$1Dir/; # Old syntax
	$var =~ s/^DldUseDB$/DldUseAutoProvides/; # 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 = split(/,/,$val);
	} elsif ( $var eq "RPMBindings" and $val =~/^[0-3]$/ ) {
		$RpmBindings = $val;
	} 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 "KernelMod" and $val =~ /^\w+(,\w+)*$/) {
		$KernelMod = $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 "RpmOption" ) {
		push(@RpmOptions, $val);
	} elsif ( $var eq "CheckSize" ) {
		$CheckSize = $val;
	} elsif ( $var eq "CheckSig" ) {
		$CheckSig = $val;
	} elsif ( $var eq "CheckVendor" ) {
		$CheckVendor = $val;
	} elsif ( $var eq "CheckModified" ) {
		$CheckModified = $val;
	} elsif ( $var eq "CheckGPG" ) {
		$CheckGPG = $val;
	} elsif ( $var eq "CheckOldRequirements" ) {
		$CheckOldRequirements = $val;
	} elsif ( $var eq "GPGHome" ) {
		$GPGHome = $val;
	} elsif ( $var eq "Resolve" ) {
		$Resolve = $val;
	} elsif ( $var eq "ResolveUseRPMDB" ) {
		$ResolveUseRPMDB = $val;
	} elsif ( $var eq "ResolveAvoidNew" ) {
		$ResolveAvoidNew = $val;
	} elsif ( $var eq "ResolveAllAtOnce" ) {
		$ResolveAllAtOnce = $val;
	} elsif ( $var eq "DataBase" ) {
		$DataBase = $val;
	} elsif ( $var eq "DataBaseName" ) {
		$DataBaseName = $val;
	} elsif ( $var eq "UseDataBase" ) {
		$UseDataBase = $val;
	} elsif ( $var eq "DataBaseRO" ) {
		$DataBaseRO = $val;
	} elsif ( $var eq "UseRpmVerCmp" ) {
		$UseRpmVerCmp = $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 "Define" and $val =~ /^\s*(\w+)\s*$/) {
		$DistVars{$1} = "";
	} elsif ( $var eq "Define" and $val =~ /^\s*(\w+)\s*=(.*)$/) {
		$DistVars{$1} = $2;
	} 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 "LWPProtocols" ) {
		@LWPProtocols = split(/,/,$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 "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 "DldUseAutoProvides" ) {
		$DefaultDldUseAutoProvides = $val;
	} elsif ( $var eq "DldUseAutoInfo" ) {
		$DefaultDldUseAutoInfo = $val;
	} elsif ( $var eq "DldUseSynthesis" ) {
		$DefaultDldUseSynthesis = $val;
	} elsif ( $var eq "DldUseHeaderInfo" ) {
		$DefaultDldUseHeaderInfo = $val;
	} elsif ( $var eq "GenerateAutoInfo" ) {
		$GenerateAutoInfo = $val;
	} elsif ( $var eq "AutoInfoFile" ) {
		Substitute_Dist_Data($val);
		$AutoInfoFile = $val;
	} elsif ( $var eq "AutoInfoBaseDir" ) {
		Substitute_Dist_Data($val);
		$AutoInfoBaseDir = $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);
	} elsif ( defined $DistVars{$var} ) {
		$DistVars{$var} = $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;
}

######################
# Set dld default values
######################

sub Set_Dld_Defaults
{

$FTPRetry= $DefaultFTPRetry;
$FTPWait= $DefaultFTPWait;
$Passive= $DefaultPassive;
$RPMNameWarnings= $DefaultRPMNameWarnings;
$FixRPMNames= $DefaultFixRPMNames;
$DldUseAutoProvides= $DefaultDldUseAutoProvides;
$DldUseAutoInfo= $DefaultDldUseAutoInfo;
$DldUseSynthesis= $DefaultDldUseSynthesis;
$DldUseHeaderInfo= $DefaultDldUseHeaderInfo;
$DirWarnings = $DefaultDirWarnings;

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

$StoreDir= $UpdateDir;

}


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

sub Read_Dld_Config
{
my $file = shift;
my $ignoredisabled = 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";
}

$URL= "";
$Host= "";
$User= "";
$Pass= "";
$Protocol= "ftp";
$Port= "";
$DldNew= 0;
$DldUnknown= 0;
$DldRecursive= 0;
@DldDirInclude= ();
@DldRpmInclude= ();
@DldDbInclude= ();
$BaseDir= "";
$RPMBaseDir= "";
@DldDirs= ();
@DldDataBase= ();
@NoWarnDirs= ();
$DldConfigDisabled= 0;
Set_Dld_Defaults();

return 1 unless ( Read_Dld_ConfigFile($file, $ignoredisabled) == 0);
return 0 if ($DldConfigDisabled);

$RPMNameWarnings =1 if $Debug;

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

unless ($Host or $URL) {
	print STDERR "Error: No host given in $file.\n";
	return 1;
}
unless ($URL or @DldDirs or @DldDataBase) {
	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;
my $ignoredisabled = 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;
	$_ =~ s/\r$//; # fix dos type line endings
	next if ( $_ =~ /^\s*#/ );
	next if ( $_ =~ /^\s*$/ );
	if ( $_ =~ /Reset(Include|Exclude|Patterns)/ ) {
		@InExclude = ();
		next;
	}
	unless ($_ =~ /^\s*(\w+)\s*=(.*)$/) {
		print STDERR "Warning: Unknown line '$_' in $file.\n" if $Warnings;
		next;
	}
	($var, $val)= ($1, $2);
	$var =~ s/DldAll/DldUnknown/; #OldSyntax
	$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;
		}
	}
	if ( $var eq "URL" ) {
		Substitute_Dist_Data($val);
		$URL = $val;
	} elsif ( $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 "DldNew" ) {
 		$DldNew = $val;
	} elsif ( $var eq "DldUnknown" and $val =~/^[0-2]$/) {
		$DldUnknown = $val;
 	} elsif ( $var eq "DldRecursive" ) {
 		$DldRecursive = $val;
	} elsif ( $var eq "DldUseAutoProvides" ) {
		$DldUseAutoProvides = $val;
	} elsif ( $var eq "DldUseAutoInfo" ) {
		$DldUseAutoInfo = $val;
	} elsif ( $var eq "DldUseSynthesis" ) {
		$DldUseSynthesis = $val;
	} elsif ( $var eq "DldUseHeaderInfo" ) {
		$DldUseHeaderInfo = $val;
	} elsif ( $var eq "DldDirInclude" ) {
		Substitute_Dist_Data($val);
 		push(@DldDirInclude, $val);
 	} elsif ( $var eq "DldRpmInclude" ) {
		Substitute_Dist_Data($val);
 		push(@DldRpmInclude, $val);
 	} elsif ( $var eq "DldDbInclude" ) {
		Substitute_Dist_Data($val);
 		push(@DldDbInclude, $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 "RPMBaseDir" ) {
		$RPMBaseDir = $val;
	} elsif ( $var eq "AutoProvides" or
			$var eq "AutoInfo" or
			$var eq "Synthesis" or
			$var eq "HeaderInfo" ) {
		if ($BaseDir and $val !~ /^\//) {
			$val= "$BaseDir/$val";
		}
		Substitute_Dist_Data($val);
		if ($val) {
			push(@DldDataBase, $val);
			$DldDataBaseType{$val}= $var;
			$DldDataBaseDir{$val}= $RPMBaseDir;
		}
	} elsif ( $var eq "BaseDir" ) {
		Substitute_Dist_Data($val);
		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 "Disabled") {
		if ($val and ! $ignoredisabled) {
			$DldConfigDisabled= 1;
			print "  Skipping config file (disabled).\n" if $Debug;
			last;
		}
	} elsif ( $var eq "MatchDistName") {
		if ($DistVars{'DistName'} !~ /$val/) {
			$DldConfigDisabled= 1;
			print "  Skipping config file (\'$DistVars{'DistName'}\' does not match \'$val\').\n" if $Debug;
			last;
		}
	} elsif ( $var eq "IncludeFile" and $val) {
		Read_Dld_ConfigFile($val, $ignoredisabled);
		last if $DldConfigDisabled;
	} 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:\n" unless $Quiet;
$GenerateAutoInfo= 1 if ($GenerateAutoInfo eq 2);

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= 2;
} else {
        $cl= 1;
}

@newrpms= ();
foreach $item ( SelectRPMs(\@AllRPMs, 0, $cl) ) {
	if ( IsExcluded($item) ) {
		print "Excluded: " . $item->rpmname . "\n" if $Debug;
		next;
	}
	push(@newrpms, $item);
}

return 0 unless (@newrpms);

print "Merging rpms into $RPMDir:\n" unless $Quiet;
$GenerateAutoInfo= 1 if ($CleanUp and $GenerateAutoInfo eq 2);
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);
		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;
		} else {
			my $time= (stat($item->filename))[9];
			utime (time, $time, "$RPMDir/" . basename($item->filename) ) if $time;
		}
	}
}

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 %timestamps= ();
my $StoreByArch= $DefaultStoreByArch;
my $retval= 0;
%TmpProvides= ();

#Parse URL
if ($URL) {
	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);
		}
	}
	$dir= "." unless ($dir or @DldDataBase);
	if ($Host) {
		@DldDirs= ();
		if ($dir=~ /autoprovides[^\/]*$/ or
			$dir=~ /autoinfo[^\/]*$/ or
			$dir=~ /\.(gz|bz2)$/ or
			$dir=~ /synthesis[^\/]*\.cz$/ or
			$dir=~ /header\.info$/) {
			push(@DldDataBase, $dir)
		} elsif ( $dir=~ /^(.+\/)\/$/ ) {
			$dir=$1;
			@NoWarnDirs= ();
			for $item (@AllowedArch) {
				push(@NoWarnDirs, "$dir$item/") unless ($item eq $DistArch);
				push(@DldDirs, "$dir$item/");
			}
		} else {
			@DldDirs= ($dir);
		}
	} else {
		print STDERR "Error: Malformed URL: $URL\n";
		return 1;
	}
}

#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);
	}
}

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);
	}
}

my $site= autoupdate::dld->new(
	Debug => $Debug,
	DebugFTP => $DebugFTP,
	DebugSFTP => $DebugSFTP,
	DebugLWP => $DebugLWP,
	Verbose => $Verbose,
	Warnings => $Warnings,
	Protocol => $Protocol,
	User => $User,
	Pass => $Pass,
	Host => $Host,
	Port => $Port,
	Dirs => \@DldDirs,
	Passive => $Passive,
	FTPRetry => $FTPRetry,
	FTPWait => $FTPWait,
	Firewall => $Firewall,
	FirewallType => $FirewallType,
	LWPProtocols => \@LWPProtocols,
	Proxy => $Proxy,
	ProxyUser => $ProxyUser,
	ProxyPass => $ProxyPass,
	NoProxy => \@NoProxy,
	HTTPExt => \@HTTPExt,
	SSHCipher => $SSHCipher,
	Recursive => $DldRecursive,
	MaxRecursive => $MaxRecursive,
	Timeout => $Timeout,
	NoWarnDirs => \@NoWarnDirs,
	DirInclude => \@DldDirInclude,
	FileInclude => [ '\.rpm$' ] );

return 1 unless ($site);
$Port= $site->{'Port'};

if (@DldRpmInclude) {
	$site->{'FileInclude'}= [ ];
	foreach my $item (@DldRpmInclude) {
		$item .= '$' if ($item =~ /\.rpm$/);
		$item .= '.*\.rpm$' unless ($item =~ /\$$/);
		push(@{$site->{'FileInclude'}}, $item);
	}
}

if (@DldDbInclude) {
	foreach my $item (@DldDbInclude) {
		$item .= '(\.gz|\.bz2)?$' if ($item =~ /\.db$/);
		push(@{$site->{'FileInclude'}}, $item);
	}
} else {
	push(@{$site->{'FileInclude'}}, 'autoprovides.*\.db(\.gz|\.bz2)?$') if ($DldUseAutoProvides);
	push(@{$site->{'FileInclude'}}, 'autoinfo.*(\.gz|\.bz2)?$') if ($DldUseAutoInfo);
	push(@{$site->{'FileInclude'}}, 'synthesis.*\.cz$') if ($DldUseSynthesis);
	push(@{$site->{'FileInclude'}}, 'header\.info$') if ($DldUseHeaderInfo);
}

unless ($site->open) {
	return 1;
}

if (@DldDirs) {
	# Parse directories
	foreach my $rfile ($site->list()) {
		if ($rfile =~ /\.rpm$/) {
			push(@AllDldRPMs, GetRPMHeaders("r", $rfile));
		} else {
			push(@DldDataBase, $rfile);
		}
	}
}

foreach my $rfile (@DldDataBase) {
	print "Parsing $rfile\n" if $Debug;
	my $storebyarch= 0;
	my $type= $DldDataBaseType{$rfile};
	$base= "";
	if ($rfile =~ /^(.*\/)([^\/]+)$/ ) {
		($base, $file)= ($1, $2);
	}
	unless ($type) {
		if ($file =~ /^autoprovides/) {
			$type= "AutoProvides";
		} elsif ($file =~ /^synthesis.*\.cz$/) {
			$type= "Synthesis";
		} elsif ($file =~ /^header\.info$/) {
			$type= "HeaderInfo";
		} else {
			$type= "AutoInfo";
		}
	}
	if ($DldDataBaseDir{$rfile}) {
		$base= $DldDataBaseDir{$rfile}
	} else {
		if ($type eq "Synthesis" or $type eq "AutoInfo") {
			if ($base =~ /^(.*\/)base\/$/ ) {
				$base= $1 . "RPMS/";
			}
		} elsif ($type eq "HeaderInfo") {
			if ($base =~ /^(.*\/)headers\/$/ ) {
				$base= $1;
			}
		}
	}
	if ($base =~ /^(.*\/)\/$/) {
		$base=$1;
		$storebyarch= 1;
	}

	my $time;
	if ($UseDataBase and $UseTimeStamps) { # Get old time stamp
		OpenProvides('ro');
		$time= $Provides{"##$Host:$Port:$rfile##"};
	}

	$file= "tmp" . $$ . "-" . $file;
	unless ($site->get($rfile, $file, $time) == 0) {
		$retval= 1;
		next;
	}
	next unless ( -f $file);

	if ($UseDataBase and $UseTimeStamps) { # Remember time stamp
		$time= (stat($file))[9];
		$timestamps{"##$Host:$Port:$rfile##"}= $time;
	}	

	if ($file =~ /\.cz$/) {
		$file= $1;
		rename("$file.cz", "$file.gz");
		$file .= ".gz";
	}

	if ($file =~ /^(.+)(\.gz|\.bz2)$/) {
		$file= $1;
		my $ext= $2;
		my $prog= $GUNZIP;			
		$prog= $BUNZIP if ($ext eq '.bz2');
		unlink($file);
		unless (system($prog, $file . $ext) ==0) {
			print STDERR "Error: Failed to uncompress $file$ext\n";
			unlink($file . $ext);
			$retval=1;
			next;
		}
	}

	if ( $type eq "AutoProvides") {
		print "Merging provides database:\n" if $Verbose;
		if (!$Test and MergeProvides($file) == 0) {
			$Provides{"##$Host:$Port:$rfile##"}= time;
		}
		unlink($file);
		next;
	}

	unless (open(FILE, $file)) {
		print STDERR "Error: Failed to open $file ($!)\n";
		unlink($file);
		$retval=1;
		next;
	}

	if ($type eq "HeaderInfo" ) {
		foreach my $line (<FILE>) {
			next unless ($line =~ /^(\d+):([^=]+)=(.+)$/);
			my ($epoch, $name, $file)= ($1, $2, $3);
			$file= $base . $file unless ($file =~ /^\//);
			my $rpm= Header->new($file, "r");
        		unless ($rpm) {
				$name .= ".rpm";
				$rpm= Header->new($name, "r");
				unless ($rpm) {
                			print STDERR "Warning: Illegal rpm name: $name\n" if $RPMNameWarnings;
                			next;
				}
				$rpm->{'FILENAME'}= $file;
        		}
       			 if ( IsExcluded($rpm) ) {
                		print "Excluded: " . $rpm->rpmname . "\n" if $Debug;
               			 next;
        		}
			$rpm->{'EPOCH'}= $epoch if ($epoch);
			push (@AllDldRPMs, $rpm);
		}
	} else { # Synthesis or AutoInfo
		my %data= ();
		foreach my $line (<FILE>) {
			chomp $line;
			$line =~ s/^@//;
			my @tmp= split(/@/,$line);
			my $item= shift(@tmp);
			if ($item eq "basedir") {
				$base= $tmp[0];
				if ($base =~ /^(.*\/)\/$/) {
					$base= $1;
					$storebyarch= 1;
				} elsif ($base !~ /\/$/) {
					$base .= "/";
				}
				next;
			} elsif ($item eq "storebyarch") {
				$storebyarch= $tmp[0];
				next;
			} elsif ($item ne "info") {
				$data{$item}= \@tmp;
				next;
			}
			next unless ($tmp[0]);
			$item= $tmp[0] . ".rpm";
			if ($item !~ /^\//) {
				if ($storebyarch and $item =~ /\.([^.]+)\.rpm$/) {
					$item= $base . $1 . $item;
				} else {
					$item= $base . $item;
				}
			}
			my $rpm= Header->new($item, "r");
			next unless ($rpm);
			$name= $rpm->name;
			$rpm->{'EPOCH'}= $tmp[1] if ($tmp[1]);
			push (@AllDldRPMs, $rpm);
			# Obsoletes
			foreach my $obs (@{$data{'obsoletes'}}) {
				$obs =~ s/\[[^[]*\]$//;
				next unless ($obs);
				if (CheckLocal($obs)) {
					print "  " . $name . " obsoletes $obs.\n" if $Debug;
					AddLocalRPMs("r", "$name-0-0.noarch.rpm"); #Pretend we have an older version
					last;
				}
			}
			# Provides
			if (!$Test and $Resolve and $data{'provides'} and OpenProvides("rw") eq "rw") {
				if ($Provides{$name}) {
					$TmpProvides{$name}= $name;
				} else {
					$Provides{$name}= $name;
				}
				foreach my $cap (@{$data{'provides'}}) {
					$cap =~ s/\[[^[]*\]$//;
					next unless ($cap);
					if ($Provides{$cap}) {
						$TmpProvides{$cap}= $name;
					} else {
						$Provides{$cap}= $name;
					}
				}
			}
			%data= ();
		}
	}
	unlink($file);
}

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

# Download new rpms
$DldUnknown=0 if (!$Resolve or $DldNew);
my $cl= 1;
if ($DldNew or $DldUnknown) {
	$cl= 2;
}

print "Selecting new rpms.\n" if $Debug;
@UnknownRequirements= ();

do {
  @DldRPMs=();
  for $item (SelectRPMs(\@AllDldRPMs, $BestMatch, $cl)) {
	if ($DldUnknown and !$DldNew and !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> 1);
			next;
		}
		# or if there are unknown requirements
		unless (@UnknownRequirements) {
			print "Not downloading " . $item->rpmname . " (no unknown requirements).\n" if ($Debug>1);
			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 ($!)");
		}
	}
	
	unless ($site->get($item->filename) == 0) {
		if ($site->{'Connected'}) {
			# We are still connected, maybe just a problem with this file
			$retval=1;
			next;
		}
		print "Reconnecting...\n";
		unless ($site->open) {
			print STDERR "Error: Failed to reconnect.\n";
			return 1;
		}
		# Try again
		unless ($site->get($item->filename) == 0) {
			$retval=1;
			next;
		}
	}
	$tmp= (GetRPMHeaders("c", "$StoreSubDir$file"))[0];
	if ($tmp) {
		AddLocalRPMs("f", $tmp->filename);
		Do_Log("dld: " . $tmp->rpmname);
		push(@DownloadedRPMs, $tmp);
		if ($Resolve) {
			Get_Provides($tmp) unless ($DataBaseRO or !$UseDataBase or $tmp->arch eq "src");
			push(@requirements, Get_Requirements($tmp));
		}
	}
  }
  if (@requirements) {
  	print "  Searching for: @requirements\n" if $Debug;
	my @tmp= ();
	# Check if there are still unknown requirements 
	foreach my $item (@UnknownRequirements) {
		my $name =  undef;
		if ($Resolve and $Provides{$item}) {
			my $name= $Provides{$item};
		} else {
			# Is there an rpm with the same name
			foreach my $rpm (@AllDldRPMs) {
				if ($item eq $rpm->name) {
					$name= $item;
					last;
				} 
			}
		}
		if ($name) {
			next if (CheckLocal($name)); #Already have item
			AddLocalRPMs("r", "$name-0-0.noarch.rpm"); #Pretend we have an older version
			push(@GetList, $name);
			print "      Requirement(unknown): $item ($name)\n" if $Debug;
		} else {
			# Still unknown
			push (@tmp, $item);
		}
	}
	@UnknownRequirements= @tmp;
  }
} until (! @requirements);

$site->close;

# Update time stamps
if (!$Test and !$retval and $UseDataBase and %timestamps and OpenProvides('rw') eq 'rw') {
	foreach my $key (keys %timestamps) {
		$Provides{$key}= $timestamps{$key};
	}
	OpenProvides("ro");
} elsif ($Resolve) {	
	OpenProvides("ro");
}

return $retval;
}

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

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

my $status= "upgrade";
$status= "install" if ($DoInstall or $DoGet);

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


while (@GetList ne @OldGetList or $ResolveUnInstall) { # until we succeed or can't find rpms to resolve deps
	$AddedRPM=0; # So far we haven't added anything to satisfy deps
	my @tmpGetList= @GetList;
	@tmpGetList= () if (@GetList eq @OldGetList);
	foreach $name (@tmpGetList) {
		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));
			$base= $rpm->basename;
			for $rpm (@SelectedRPMs) {
	   			if ( $rpm->basename eq $base ) {
					next if (IsElement($rpm, @RPMs));
					next if (IsElement($rpm, @UpdatedRPMs));
					print "Adding " . $rpm->rpmname . ".\n" if $Debug;
					push(@RPMs, $rpm);
					push(@GetList, $rpm->name) unless (IsElement($rpm->name, @GetList));
					#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 STDERR "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 );
		next unless (Check_Modified($rpm)==0 );
		print "Adding " . $rpm->rpmname . " to satisfy dependencies.\n" if $Debug;
		push(@NewRPMs, $rpm);
		#Get_Requirements($rpm) if $Resolve;
		$AddedRPM=1;
	}
	@tmpGetList= ();
	@OldGetList=@GetList;
	unless($AddedRPM) {
		last unless ($ResolveUnInstall);
		$ResolveUnInstall= 0; #Don't try again
		my %rpms= ();
		my @newDepList= ();
		SetLocalRPMs("l");
		for $item (@DepList) {
			unless (CheckLocal($item)) {
				push (@newDepList, $item);
				next;
			}
			next if ($rpms{$item});
			my $tmp= GetInstalled($item);
			$rpms{$item}= $tmp->rpmname;
		}
		@DepList= @newDepList;
		TieRpmDatabase(0);
		last unless (keys %rpms);
		print "Trying to uninstall rpms.\n" if $Debug;
		CallRPM(\@RPMOutput, "-e", (keys %rpms));
		SetLocalRPMs("l");
		my $failed= 0;
		my @log= ();
		for $item (keys %rpms) {
			if (GetInstalled($item)) {
				$failed= 1;
				print STDERR "Error: Failed to uninstall: $item\n";
				$RetGen= 1;
				next;
			}
			print "  Uninstalled: " . $rpms{$item} . "\n" unless ($Quiet);
			push(@log, "exd: " . $rpms{$item});
		}
		if (@RPMOutput) {
			print "  " . join("\n  ", @RPMOutput) . "\n";
		}
		Do_Log(@log);
		last if ($failed);
	}
	#Let's try to upgrade the rpms we found
	print "Trying to $status:" if $Debug;
	my @rpms= ();
	foreach $item (@RPMs, @NewRPMs) {
		push (@rpms, $item->filename);
   		print " " . $item->rpmname if $Debug;
	}			 
	print "\n" if $Debug;
	TieRpmDatabase(0);
	my $retval;
	if ( $HashMarks ) {
		print "Checking dependencies.\n" if ($ResolveAllAtOnce);
		if ($RPM_41) {
			$retval= CallRPM(\@RPMOutput, "--test", "--nosignature", "--nodigest", @flags, @rpms);
		} else {
			$retval= CallRPM(\@RPMOutput, "--test", @flags, @rpms);
		}
		if ($retval == 0) {
			@RPMOutput= ();
			# we could save some time by using --nodeps here, but then packages
			# don't get ordered
			$retval= system($RPM, "--verbose", "--hash", @flags, @rpms);
		}
	} else {
		$retval= CallRPM(\@RPMOutput, @flags, @rpms);
	}	
	unless ( $retval == 0 or $Test) {
		#Check if the install went ok
		if (@RPMs) {
			$rpm= $RPMs[0];
		} else {
			$rpm= $NewRPMs[0];
		}
		SetLocalRPMs("l");
		if ( CheckLocal($rpm->name) eq $rpm->version ) {
			#At least the rpms are installed
			$retval= 0;
		}
	}
	if ( $retval == 0 ) {
		foreach $rpm (@RPMs, @NewRPMs) {
			#Free some space
			$rpm->{HDR}= "";
		}
		push(@UpdatedRPMs, @NewRPMs);
		push(@UpdatedRPMs, @RPMs);
		my @log= ();
		foreach $rpm (@NewRPMs) {
			print "  " . $rpm->rpmname . " (new)\n" unless ($Quiet);
			push(@log, "ins: " . $rpm->rpmname);
		}
		$status= "upd";
		$status= "ins" if ($DoInstall or $DoGet);
		foreach $rpm (@RPMs) {
			print "  " . $rpm->rpmname . "\n" unless ($Quiet);
			push(@log, "$status: " . $rpm->rpmname);
		}
		if (@RPMOutput) {
			print "  " . join("\n  ", @RPMOutput) . "\n";
		}
		Do_Log(@log);
		return 0;
	}

	# Couldn't upgrade. Lets find out what we need
	print join("\n", @RPMOutput) . "\n" if (@RPMOutput and $Debug >1);

	my %AvoidNew= ();
	my $SuggestedResolution= 0;
	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.
		next if ( $item =~ /^\s*warning:/i); # Don't care about this line.
		if ($item =~ /^\s*Suggested resolutions:/i) {
			$SuggestedResolution= 1;
			next;
		} elsif ($item =~ /package\s+(\S+)\s+is already installed/) {
			#Something went wrong!
			$name= $1;
			for $rpm (@RPMs, @NewRPMs) {
	   			if ( $rpm->name . "-" . $rpm->version eq $name) {
					print "  " . $rpm->rpmname . " (already installed)\n" unless ($Quiet);
					push(@UpdatedRPMs, $rpm);
					last;
				}
			}
			next;
		} elsif ($SuggestedResolution) {
			if ($item =~ /^\s*(\S+)-[^-]+-[^-]+\.[^.]+\.rpm\s*$/) {
				print "Suggestion: $1\n" if ($Debug >1);
				#Let's ignore suggestions from rpm for now
				next;
			} else {
				$SuggestedResolution= 0;
			}
		}
		# Try to figure out what we need
		$reqmnt="";
		$lrpm="";
		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;
			if ($item =~ /conflicts with\s+(\S+)-([^-]+-[^-]+)\s*$/) {
				$lrpm= $1 if (CheckLocal($1) eq $2); 
			} elsif ($item =~ /conflicts with \(installed\)\s+(\S+)-([^-]+-[^-]+)\s*$/) {
				$lrpm= $1; 
			}
		} elsif ($item =~ /^\s*(\S+)\s+.*is needed by/) {
			$reqmnt= $1;
			if ($item =~ /is needed by\s+(\S+)-([^-]+-[^-]+)\s*$/) {
				$lrpm= $1 if (CheckLocal($1) eq $2); 
			} elsif ($item =~ /is needed by \(installed\)\s+(\S+)-([^-]+-[^-]+)\s*$/) {
				$lrpm= $1; 
			}
		} else {
			print STDERR "Warning: Unknown line: \"$item\"\n" if ($Debug >1);
			next;
		}
		$name= Requirement2Name($reqmnt);
		if ($lrpm) {
			# The installed rpm $lrpm requires $rqmnt. There are two ways to resolve this:
			# - either a package providing $reqmnt (typically compat libs)
			# - or a newer version of $lrpm (which no longer needs $reqmnt)
			if (!$name or IsElement($name, @OldGetList)) {
				# Makes no sense to try $name again
				$name= $lrpm;
				$lrpm= "";
                	} elsif ($ResolveRPMs{$name}) {
				$lrpm= "";
			} else {
				my $havename= 0;
				if ($NewResolveRPMs{$name}) {
                        		my @tmp= SelectRPMs($NewResolveRPMs{$name},1,2);
                        		if (@tmp) {
                                		$NewResolveRPMs{$name} = [ $tmp[0] ];
						$havename= 1;
                        		} else {
                               			delete $NewResolveRPMs{$name};
                        		}
				}
				if ($havename and !($ResolveAvoidNew and $ResolveRPMs{$lrpm})) {
					$lrpm= "";
				}
			}
			if ($lrpm) {
				# We'll add them at the end unless $name is needed anyway
				unless ($AvoidNew{$name}) {
					$AvoidNew{$name} = [ $lrpm ];
				} else {
					push (@{$AvoidNew{$name}}, $lrpm);
				}
				print "Requirement: $reqmnt -> $lrpm\n" if $Debug;
				next;
			}
		}
		next unless ($name);
		if (IsElement($name, @GetList)) {
			print "Requirement(old): $reqmnt ($name)\n" if ($Debug > 2);
			next;
		}
		print "Requirement: $reqmnt ($name)\n" if $Debug;
		push(@GetList, $name);
	}
	foreach $item (keys %AvoidNew) {
		if (IsElement($item, @GetList)) {
			print "Already have $item. Skipping AvoidNew list.\n" if ($Debug >1);
			next;
		}
		print "AvoidNew list for $item.\n" if ($Debug >1);
		foreach $name (@{$AvoidNew{$item}}) {
			next if (IsElement($name, @GetList));
			print "Requirement: $item ($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 $status:";
	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 STDERR "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 STDERR "Warning: Boot manager part disabled.\n" if ($Warnings and ! $DoBoot);

	#Disable grubby
	if ( $DoBoot and $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 STDERR "Warning: Grubby already disabled!?\n" if $Warnings;
		} else {
			print STDERR "Warning: $GRUBBY is no regular file!?\n" if $Warnings;
		}
	}
}

foreach $item (@SelectedKernelRPMs) {
	$name=$item->name;
	$version=$item->version;
	$oldversion= CheckLocal($name);
	$oldversion= "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, "--root", $RpmRoot) if ($RpmRoot);
push(@flags, "--test") if ($Test);

print "Uninstalling old kernels.\n" if $Debug;

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= $1;
	$version= $2;
	my @rpms= ( $RPMOutput[0] );
	foreach my $item (split(/,/, $KernelMod)) {
		if ( CallRPM(\@RPMOutput, "-q", "$name-$item-$version")==0 ) {
			push (@rpms, "$name-$item-$version");
		}
	}
	
	if (CallRPM(\@RPMOutput, @flags, @rpms)==0) {
		print "Removed: @rpms\n" if $Verbose;
		if (-f "$BootDir/initrd-$image.img") {
			unlink("$BootDir/initrd-$image.img");
		}
		my $arch= &GetArch;
		foreach $item (@rpms) {
			Do_Log("exd: " . $item . ".$arch.rpm");
		}
	} 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 = ();
@SelectedKernelModulesRPMs = ();
$DldAddInstalled = 1; #Check installed rpms during Get_Requirements
$BestMatch = 1; #Used in SetGetList
$ResolveUseRPMDB = 1; #Use the rpm data base for resolving
$CheckSize = 0;

# 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 ($Verbose and $HashMarks);
@UpdateRPMs= ();
foreach $item ( SelectRPMs(\@AllRPMs, 1, 2) ) {
	if ( IsExcluded($item) ) {
		print "Excluded: " . $item->rpmname . "\n" if $Debug;
		next;
	}
	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 ($Verbose and @UpdateRPMs);

foreach $item (@UpdateRPMs) {
	unless ( IsElement($item, @rpms) ) {
		my $obsoletes=0;
		foreach $name ($item->obsoletes) {
			next unless ($name);
			if ( $name =~ /^(\S+)\s/ ) {
				$name= $1;
			}
			if (CheckLocal($name)) {
				print "  " . $item->rpmname . " obsoletes $name.\n" if $Debug;
				$ResolveRPMs{$name}= $item;
				$obsoletes= 1;
			}
		}
		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);
	} elsif (IsElement($item->name,@KernelModules)) {
		next unless ( $DoKernel );
		next unless ( Check_Vendor($item)==0 );
		next unless ( Check_Sig($item)==0 );
		push(@SelectedKernelModulesRPMs, $item);
	} else {
		next unless ( Check_Vendor($item)==0 );
		next unless ( Check_Modified($item)==0 );
		next unless ( Check_Sig($item)==0 );
		$ResolveRPMs{$item->name}= $item;
		push(@SelectedRPMs, $item);
	}
}
# Free some space
@UpdateRPMs= ();
@rpms= ();

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);
	@SelectedKernelRPMs= ();
	# Maybe some rpms already got upgraded
	if (@UpdatedRPMs) {
		@rpms=();
		foreach $rpm (@SelectedRPMs) {
			next if (IsElement($rpm, @UpdatedRPMs));
			push(@rpms, $rpm);
		}
		return $Ret unless ( @rpms );
		@SelectedRPMs= @rpms;
		@rpms=();
	}
}

if ($ResolveAllAtOnce or $Test) {
	@GetList= ();
	foreach $rpm ( @SelectedRPMs ) {
		push(@GetList, $rpm->name) unless (IsElement($rpm, @UpdatedRPMs));
	}
	$Ret= Do_Update();
	@GetList= ();

	return $Ret;
}

# Try to find some minimal sets which can be upgraded
# Make sure some core rpms get upgraded first
my @CoreRPMs= qw(rpm pam sshd);
@SelectedRPMs= sort {
	my $aa= IsElement($a->basename, @CoreRPMs);
	my $bb= IsElement($b->basename, @CoreRPMs);
	return 1  if (!$aa and $bb);
	return -1 if (!$bb and $aa);
	if ($a->basename eq $b->basename) {
		return $a->name cmp $b->name;
	}
	return $a->basename cmp $b->basename;
	} @SelectedRPMs;

$LastTry= 0;
my $base= "";

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

# 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();
}

@GetList= ();

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 $ret= 0;
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, "--repackage") if ($Repackage);
push(@flags, "--root", $RpmRoot) if ($RpmRoot);
push(@flags, "--test") if ($Test);
push(@flags, "--nodeps") if ($ResolveKernelNodeps);
push(@flags, @RpmOptions);

# 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.
		next if ( $item =~ /^\s*warning:/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 STDERR "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;
Do_Log("ins: " . $RPM->rpmname);

# Install additional kernel modules
my @kernelmodules= ();
foreach my $item ( split(/,/, $KernelMod) ) {
	push(@kernelmodules, $RPM->name . "-" . $item);
}

foreach my $item (@SelectedKernelModulesRPMs) {
	next unless ($item->version eq $NewVersion);
	next unless (IsElement($item->name, @kernelmodules));
	if ( CallRPM(\@RPMOutput, @flags, $item->filename) == 0 ) {
		push(@UpdatedRPMs, $item);
		print "  " unless $Quiet;
		print join("\n  ", @RPMOutput) . "\n  " if (@RPMOutput and ! $Quiet);
		print $item->rpmname . "\n" unless $Quiet;
		Do_Log("ins: " . $item->rpmname);
	} else {
		$ret= 1;
		print "Failed to install new kernel modules: " . $item->rpmname . "\n";
		print "  " . join("\n  ", @RPMOutput) . "\n" if (@RPMOutput and ! $Quiet);
		return 1;
	}
}


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

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

# 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 $ret;
	}
	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 $ret;
}

# 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 $ret 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");
	my $time= (stat($BootConf))[9];
	utime (time, $time, "$BootConf.bak") if $time;
}

# 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 $ret;
}

######################
# Generate autoinfo
######################

sub GenerateAutoInfo {
	print "Writing autoinfo file: $AutoInfoFile\n" if $Verbose;

	if ($AutoInfoFile and $AutoInfoFile ne "-") {
		my $open = ">$AutoInfoFile";
		if ($AutoInfoFile =~ /\.bz2/) {
			$open = "| $BZIP " . $open;
		} elsif ($AutoInfoFile =~ /\.gz/) {
			$open = "| $GZIP " . $open;
		}
		unless ( open(FILE, $open) ) {
			print STDERR "Error: Could not write autoinfo: $AutoInfoFile ($!)\n";
			return 1;
		}
	} else {
		*FILE= *STDOUT;
	}

	if ($DefaultStoreByArch) {
		print FILE "storebyarch@" . "1\n";
	}
	if ($AutoInfoBaseDir) {
		print FILE "basedir@" . $AutoInfoBaseDir . "\n";
	}
	foreach my $rpm (SelectRPMs(\@AllRPMs, 0, 0)) {
		my $rpmname= $rpm->name;
		if ($AutoInfoProvides) {
			my @tmp= ();
			foreach my $item ($rpm->provides) {
				if ($item =~ /^(\S+)/) {
					$item= $1;
				}
				next if ($item eq $rpmname);
				next if (IsElement($item, @tmp));
				push (@tmp, $item);
			}
			# Add executables as well
			foreach my $item ($rpm->files) {
				chomp($item);
				next unless ($item =~ /^\/./);
				next unless ($item =~ /\/s?bin\/./ or $item =~ /^\/usr\/games\/[^\/]+$/ or $item =~ /^\/etc\/./);
				next if (IsElement($item, @tmp));
				push (@tmp, $item);
			}
			if (@tmp) {
				print FILE "provides@";
				print FILE join("@", @tmp);
				print FILE "\n";
			}
		}
		my @tmp= ();
		foreach my $item ($rpm->obsoletes) {
			if ($item =~ /^(\S+)/) {
				push(@tmp, $1);
			}
		}
		if (@tmp) {
			print FILE "obsoletes@";
			print FILE join("@", @tmp);
			print FILE "\n";
		}
		print FILE "info\@$rpmname" . "-" . $rpm->version . "." .$rpm->{'ARCH'};
		print FILE "@" . $rpm->{'EPOCH'} if ($rpm->{'EPOCH'});
		print FILE "\n";
	}
	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);
$GenerateAutoInfo=2 if $GenerateAutoInfo;

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;
	$UseTimeStamps= 0;
} elsif ( $0 =~ /autodld$/) {
	$DoDld=1;
	$GetAll=1;
}
@InExclude= ();
my $SaveUpdateDir= $UpdateDir;
my @SaveDldConfig= @DldConfig;
my @SaveRpmOptions= @RpmOptions;
my $SaveCheckSize= $CheckSize;
@DldConfig= ();
@RpmOptions= ();

Getopt::Long::Configure('default');
$item= GetOptions('dld!' => \$DoDld,
	'ftp!' => \$DoDld, #Old syntax
	'get!' => \$DoGet,
	'url=s' => \$URL,
	'dlddatabase' => \@DldDataBase,
	'dldconfig=s' =>  sub { @DldConfig = split(/,/,$_[1]); },
	'dldnew!' => \$DldNew,
	'dldall+' => \$DldUnknown, #Old syntax
	'dldunknown+' => \$DldUnknown,
 	'dldrecursive!' => \$DldRecursive,
 	'dlddirinclude=s' => \@DldDirInclude,
 	'dldrpminclude=s' => \@DldRpmInclude,
 	'dlddbinclude=s' => \@DldDbInclude,
	'dldaddinstalled!' => \$DldAddInstalled,
	'dldmatch!' => \$DldMatch,
	'getall!' => \$GetAll,
	'bestmatch!' => \$BestMatch,
	'removebad!' => \$RemoveBad,
	'timestamps!' => \$UseTimeStamps,
	'passive!' => \$DefaultPassive,
	'lwpproxy=s' => \$Proxy,
	'lwpprotocols=s' => sub { @LWPProtocols = split(/,/,$_[1]); },
	'addtodb!' => \$DoAddToDB,
	'usedb!' => \$UseDataBase,
	'adddbentry=s' => \$AddDBEntry,
	'deldbentry=s' => \$DelDBEntry,
	'mergedb=s' => \$MergeDB,
	'createdb=s' => \$CreateDB,
	'dldusedb!' => \$DldUseAutoProvides,
	'dlduseautoprovides!' => \$DefaultDldUseAutoProvides,
	'dlduseautoinfo!' => \$DefaultDldUseAutoInfo,
	'dldusesynthesis!' => \$DefaultDldUseSynthesis,
	'dlduseheaderinfo!' => \$DefaultDldUseHeaderInfo,
	'update!' => \$DoUpdate,
	'install!' => \$DoInstall,
	'kernel!' => \$DoKernel,
	'initrd!' => \$DoInitRD,
	'boot!' => \$DoBoot,
	'checksize!' => \$CheckSize,
	'checksig!' => \$CheckSig,
	'checkmodified!' => \$CheckModified,
	'checkvendor!' => \$CheckVendor,
	'checkgpg!' => \$CheckGPG,
	'gpghome=s' => \$GPGHome,
	'repackage!' => \$Repackage,
	'root=s' => \$RpmRoot,
	'rpmoption=s' => \@RpmOptions,
	'distmode' => sub { $Resolve=1; $DoKernel=1; $ResolveKernelNodeps=1; $ResolveAllAtOnce=1; $ResolveUnInstall=1; },
	'resolve!' => \$Resolve,
	'resolveuserpmdb!' => \$ResolveUseRPMDB,
	'resolveavoidnew!' => \$ResolveAvoidNew,
	'resolveallatonce!' => \$ResolveAllAtOnce,
	'resolvekernelnodeps!' => \$ResolveKernelNodeps,
	'resolveuninstall!' => \$ResolveUnInstall,
	'checkoldrequirements!' => \$CheckOldRequirements,
	'recursive!' => \$Recursive,
	'updatedir=s' => \$UpdateDir,
	'installdir=s' => \$InstallDir,
	'rpmdir=s' => \$RPMDir,
	'postupdatescript=s' => \$PostUpdateScript,
	'postdldscript=s' => \$PostDldScript,
	'distversion=s' => sub { $DistVars{'DistVersion'} = $_[1] },
	'distname=s' => sub { $DistVars{'DistName'} = $_[1] },
	'distlang=s' => sub { $DistVars{'DistLang'} = $_[1] },
	'define=s' => sub { if ($_[1] =~ /^(\w+)=(.+)$/) {$DistVars{$1}=$2} },
	'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 },
	'autoinfo!' => \$GenerateAutoInfo,
	'autoinfofile=s' => \$AutoInfoFile,
	'autoinfoprovides!' => \$AutoInfoProvides,
	'autoinfobasedir=s' => \$AutoInfoBaseDir,
	'test!' => \$Test,
	'verbose' => sub { $Verbose=1; $Quiet=0 },
	'noverbose' => sub { $Verbose=0 },
	'hash!' => \$HashMarks,
	'quiet!' => \$Quiet,
	'warnings!' => \$Warnings,
	'rpmnamewarnings!' => \$DefaultRPMNameWarnings,
	'fixrpmnames!' => \$DefaultFixRPMNames,
	'comps=s' => \$Comps,
	'compsdefault=i' => \$CompsDefault,
	'languages=s' => sub { @DistLanguages = split(/,/, $_[1])},
	'debugftp!' => \$DebugFTP,
	'debugsftp!' => \$DebugSFTP,
	'debuglwp!' => \$DebugLWP,
	'debugrpm!' => \$DebugRPM,
	'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 ($DebugRPM) {
	$autoupdate::rpm::DEBUG= $DebugRPM;
}
if (($DebugFTP or $DebugSFTP or $DebugLWP or $DebugRPM 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 my $item ( split(/,/,$KernelExt) ) {
	push(@KernelNames,"kernel-$item");
}
@KernelModules= ();
foreach $item ( @KernelNames ) {
	foreach my $moditem ( split(/,/,$KernelMod) ) {
		push(@KernelModules, "$item-$moditem");
	}
}
		

# Set up defaults

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

$RpmBindings= LoadRpmBindings($RpmBindings);
$autoupdate::rpm::UseRpmVerCmp= $UseRpmVerCmp;
if ($RpmRoot) {
	$autoupdate::rpm::RpmRoot= $RpmRoot;
}

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

$DoGet= 1 if ($showget);
$DoPurge= 1 if $CleanUp;
unless ($RPMDir) {
	$DldAddInstalled = 1 unless ($DistArch eq "src");
	$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 ($DoDld and $CheckSize eq $SaveCheckSize) {
	$CheckSize = 1;
}
if (@SaveDldConfig and ! @DldConfig) {
	@DldConfig= @SaveDldConfig;
	@SaveDldConfig= ();
}
if (@SaveRpmOptions and ! @RpmOptions) {
	@RpmOptions= @SaveRpmOptions;
	@SaveRpmOptions= ();
}
foreach my $opt (@RpmOptions) {
		unless ($opt =~ /^--/) {
			$opt = "--" . $opt;
		}
}
unless ($Resolve) {
	$UseDataBase = 0;
}
if ($CreateDB or $DoAddToDB or $MergeDB or $AddDBEntry or $DelDBEntry) {
	$UseDataBase = 1;
}
if ($PrintRequires) {
	$Resolve = 1;
	$UseDataBase = 1;
	$DoGet = 0;
	$DoDld = 0;
}

push (@LWPProtocols, "http") unless (IsElement("http", @LWPProtocols));
push (@LWPProtocols, "https") unless (IsElement("https", @LWPProtocols));

# 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;
	}
} 

# 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 !~ /^\//);

# Generate DistnameLC and DistVersionNoDot
$DistVars{'DistNameLC'} = lc($DistVars{'DistName'});
$DistVars{'DistVersionNoDot'} = $DistVars{'DistVersion'};
$DistVars{'DistVersionNoDot'} =~ tr/.//d;
$DistVars{'DistVersionNoDot2'} = $DistVars{'DistVersion'};
$DistVars{'DistVersionNoDot2'} =~ s/\.0$//;
$DistVars{'DistVersionNoDot2'} =~ 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" and $Warnings) {
	print STDERR "Warning: $UpdateDir contains subdirs according to arch.\n";
	print STDERR "         Please make sure it has two trailing slashes (in the config file)\n";
	print STDERR "         if you want to check these subdirs.\n";
}

#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 ($@) { 
		print STDERR "Error: Failed to load \'Sys::Syslog\'. Disabling logging.\n";
		$DoLog=0;
		$RetGen= 1;
	}
}

# 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";
	}
}

unless ($AutoInfoFile) {
	$AutoInfoFile="$UpdateDir/autoinfo";
	if (-x $BZIP) {
		$AutoInfoFile .= ".bz2";
	} elsif (-x $GZIP) {
		$AutoInfoFile .= ".gz";
	} 
}

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 );
if (CheckRPMVersion("4.1") ge 0) {
	print "rpm is 4.1 or newer.\n" if $Debug;
	$RPM_41= 1;
}
# 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 and $LockFile) {
	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 $pid >/dev/null") == 0 ) {
			print STDERR "Error: autoupdate already running under pid $pid!\n";
			exit(1);
		}
		print STDERR "Warning: Found stale lock file.\n" if $Warnings;
		$CheckOldRequirements= 1;
		# Just to be sure
		unlink($LockFile) || fatal("Could not remove $LockFile: $!");
	}

	sysopen(LOCK, $LockFile, O_WRONLY|O_CREAT|O_EXCL, 00600) || 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;
}
$DistArch= "src" if ($DistArch eq "SRPMS");
if ($DistArch eq "src") {
	@AllowedArch= qw(SRPMS);
	$Resolve= 0;
}

@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 STDERR "Warning: $item is no rpm name. Taking $1 instead.\n" if $Warnings;
				$item= $1;
			}
			push(@GetList,$item);
		}
		@ARGV= ();
	} elsif ($DoGet) {
		if ($GetAll) {
			print STDERR "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") eq "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 ($UseDataBase) {
	# Make sure the database exists
	unless ( -f $DataBase ) {
		if (OpenProvides("rw") eq "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") eq "rw" and $AddDBEntry =~ /^\s*(\S+):(\S+)\s*$/) {
			$Provides{$2} = $1;
		} else {
			print STDERR "Error: Could not add entry to provides database.\n";
			$RetGen= 1;
		}
		unlink($LockFile) if ($HaveLock);
		exit $RetGen;
	}
	if ($DelDBEntry) {
		print "Deleting entry to provides database: $DataBase\n" if $Verbose;
		unless (OpenProvides("rw") eq "rw") {
			print STDERR "Error: Could not delete entry from provides database.\n";
			$RetGen= 1;
		}
		delete $Provides{$DelDBEntry};
		unlink($LockFile) if ($HaveLock);
		exit $RetGen;
	}
}

# Print requirements for given files

if ($PrintRequires) {
	my $rpm;
	my @requirements = ();
	OpenProvides("ro");
	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) {
			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;
}

# Print autoinfo for given files

if ($GenerateAutoInfo eq 1 and !$DoDld) {
	GenerateAutoInfo();
	unlink($LockFile) if ($HaveLock);
	exit $RetGen;
}

if ($UseDataBase) {
	if ($DoAddToDB) {
		print "Adding to provides database: $DataBase\n" if $Verbose;
		if (OpenProvides("rw") eq "rw") {
			my @rpms= SelectRPMs(\@AllRPMs, 1, 0);
			Get_Provides(@rpms) if (@rpms and ! $Test);
		} else {
			print STDERR "Error: Could not add rpms to provides database.\n";
			$RetGen= 1;
		}
	} elsif (OpenProvides("ro")) {
		unless ($DataBaseRO) {
			print "Adding unknown rpms to provides database.\n" if ($Debug>1);
			foreach $item (@AllRPMs) {
				next if ($item->arch eq "src");
				Get_Provides($item) unless ($Provides{$item->name});
			}
		}
	}
	if (@GetList) {
		foreach $item (@GetList) {
			if ( $item !~ /^@/ and $Provides{$item} ) {
				$item= $Provides{$item};
			}
		}
	}
}


@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 ($UseDataBase 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;
	Set_Dld_Defaults();
	$RetDld= Get_Remote_RPMs();
  } else {
	print "Processing download config files:\n" if $Debug;
	my $file;
	my $ignoredisabled= 1;
	unless ($DldConfigDir) {
		$DldConfigDir = $ConfigDir;
	}
	unless (@DldConfig) {
		$ignoredisabled= 0;
		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, $ignoredisabled)==0 ) {
			next if ($DldConfigDisabled);
			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");
	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_Log("sum: Downloaded a total of $total rpm(s).");
	Do_PostScript($PostDldScript, @DownloadedRPMs);
	$GenerateAutoInfo= 1 if ($GenerateAutoInfo eq 2);
  }
@DownloadedRPMs= ();
}


# Remove any remaining bad rpms
if ($RemoveBad and keys %BadRPMs) {
	print "\n" if $Debug;
	print "Removing bad rpms:\n" if $Verbose;
	$GenerateAutoInfo= 1 if ($GenerateAutoInfo eq 2);
	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();
	OpenProvides() if ($Resolve);
	Do_PostScript($PostUpdateScript, @UpdatedRPMs) if (@UpdatedRPMs);
	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);
	Do_Log("sum: Updated boot manager.") unless (! $ChangedBoot);
	Do_Log("sum: $status a total of $total rpm(s).");
}


#
# 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;
	$GenerateAutoInfo= 1 if ($GenerateAutoInfo eq 2);
	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;
		}
	}
}

#
# Generate AutoInfo
#

$GenerateAutoInfo=0 if ($GenerateAutoInfo eq 2);
GenerateAutoInfo() if ($GenerateAutoInfo);

unlink($LockFile) if ($HaveLock);

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

