#!/usr/bin/perl
##############################################################################
# $Id: autorpm.pl,v 1.82 2001/04/05 22:16:04 kirk Exp $
##############################################################################
# Created by Kirk Bauer <kirk@kaybee.org>
# http://www.kaybee.org/~kirk
#
# AutoRPM mailing list:
#    autorpm@kaybee.org
#
# Most current version can always be found at:
# ftp://ftp.kaybee.org/pub/redhat/RPMS
###############################################################################

# Little kludge to make perl-libnet RPM for RHL5.X work under RHL6.0
BEGIN {
   unshift (@INC, "/usr/lib/perl5/site_perl");
}

use Getopt::Long;
use Net::FTP;
use File::Copy;
use integer;

##############################################################################
# Variables
##############################################################################

# $Apply tells us if we are in --apply mode...
my $Apply = 0;

# Default config file
my $ConfigFile = '/etc/autorpm.d/autorpm.conf';

# Default "spool" directory
my $TempDir = '/var/spool/autorpm/';

# Default FTP pool definition directory
my $PoolDir = Check_Dir ("/etc/autorpm.d/pools");

# Default RPM location
my $RPMLocation = '/bin/rpm';

# Dialog or Whiptail program
my $MenuProg = '/usr/bin/dialog';
if (-x '/usr/bin/whiptail') {
   $MenuProg = '/usr/bin/whiptail --fullbuttons';
}
my $DialogProg = '/usr/bin/dialog';

my $MailProg = '/bin/mail';
if (-x '/usr/bin/mailx') {
   $MailProg = '/usr/bin/mailx';
}

my $Version = '1.9.9';
my $VDate = '04/05/01';
my $CurrFTPSite = '';
my $LastFTPSite = '';

umask(0077);

# Don't wait for carriage return to display STDOUT...
$| = 1;

$FirstBlock = 1;
$FTPRetries = 2;
$FTPRetryMinDelay = 5;
$FTPRetryMaxDelay = 600;
$DontReportInteractive = 0;

# Set when we recurse on a remote source
my $Recursing = 0;

##############################################################################
# Small functions
##############################################################################

# Sets permissions on file
sub TouchFile ($$) {
   # Filename
   my $File = $_[0];
   # Set to 1 to overwrite
   my $Write = $_[1];
   if (! -f $File) {
      if ($Write) {
         # If we are going to write to the file, unlink
         # whatever might be there in its place (i.e. symlink?)
         unlink ($File);
      }
      # Create file
      system ("touch $File >/dev/null 2>&1");
   }
   # Set ownership and permissions
   chmod 0600, $File;
   chown 0, 0, $File;
}

sub Usage {
   print "\nUsage: $0 [--apply] [--ftp <ftp-site> ]\n";
   print "[--dir <dir-name>] [--config <filename>] [--check]\n";
   print "   --config <filename> which config file to use.  The default is\n";
   print "                       /etc/autorpm.d/autorpm.conf.\n";
   print "   --ftp <ftp-site>    Compares the remote FTP site to the locally\n";
   print "                       installed RPMs and prompts for actions.\n";
   print "   --dir <dir-name>    Compares the directory to the locally installed\n";
   print "                       RPMs and prompts for actions.\n";
   print "   --apply or\n";
   print "       --interactive   applies interactive RPM installs/updates.\n";
   print "   --delay=XX          Wait up to XX seconds before starting.\n";
   print "   --debug             Turn on debug mode.\n";
   print "   --print             Force output to screen regardless of what\n";
   print "                       the config file says to do...\n";
   print "   --help or --usage   Displays this help message.\n";
   print "   --version           Displays the version of AutoRPM.\n\n";
   exit (255);
}

sub WaitEnter($) {
   print $_[0] . "\n";
   until (<STDIN> eq "\n") {
   }
}

sub Check_Dir($) {
   my $ThisDir = $_[0];
   # Add / to $ThisDir
   unless ($ThisDir =~ m=/$=) {
      $ThisDir = $ThisDir . '/';
   }
   # Create directory if necessary
   unless (-d $ThisDir) {
      #mkdir ($ThisDir,0770) or die "ERROR: Can't create directory " . $ThisDir . "\n";
      mkdir ($ThisDir,0770);
      Report ("\nMaking directory: " . $ThisDir . "\n");
   }
   return ($ThisDir);
}

##############################################################################
# RPM Version comparison code
##############################################################################

# rpm_split_name - split an RPM name into the base name and the version
# Arguments:
#    $_[0]	RPM file name (with or without directory prefix)
# Returns:	RPM base name, RPM version string, and Arch or undef on error
sub RPM_Split_Name($) {
   chomp($_[0]);
   if (-e $_[0] || $_[0] =~ 'ftp://') {
       $real_name = `rpm -q --qf '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\n' -p $_[0]`;
       if ($real_name =~ m#(.*/)?([^/]+)-([^-]+-[^-]+)\.([^.]+)\.rpm$#) {
         return ($2,$3,$4);
      }
   }

   #if ($_[0] =~ m#(.*/)?([^/]*)-([^-]*-[^-]*)\.(.*)\.rpm$#) {
   if ($_[0] =~ m#(.*/)?([^/]+)-([^-]+-[^-]+)\.([^.]+)\.rpm$#) {
       return ($2,$3,$4);
    }
   Report ("Bad RPM name: $_[0] - skipping it" . "\n");
   return undef;
}

sub BaseName($) {
   my $Temp = $_[0];
   #$Temp =~ s#(.*/)?([^/]*-[^-]*-[^-]*)\..*\.rpm$#$2#;
   $Temp =~ s#(.*/)?([^/]+-[^-]+-[^-.]*)\..*\.rpm$#$2#;
   return ($Temp);
}

sub StripVersion($) {
   my $Temp = $_[0];
   $Temp =~ s#^([^/]*)-[^-]*-[^-]*$#$1#;
   return ($Temp);
}

# This version comparison code was sent in by Robert Mitchell and, although
# not yet perfect, is better than the original one I had. He took the code
# from freshrpms and did some mods to it. Further mods by Simon Liddington
# <sjl96v@ecs.soton.ac.uk>.
#
# Splits string into minors on . and change from numeric to non-numeric
# characters. Minors are compared from the beginning of the string. If the
# minors are both numeric then they are numerically compared. If both minors
# are non-numeric and a single character they are alphabetically compared, if
# they are not a single character they are checked to be the same if the are not
# the result is unknown (currently we say the first is newer so that we have
# a choice to upgrade). If one minor is numeric and one non-numeric then the
# numeric one is newer as it has a longer version string.
# We also assume that (for example) .15 is equivalent to 0.15
sub cmp_vers_part($$) {
   my($va, $vb) = @_;
   my(@va_dots, @vb_dots);
   my($a, $b);
   my($i);
   
   @va_dots = split(/\./, $va);
   @vb_dots = split(/\./, $vb);

   $a = shift(@va_dots);
   $b = shift(@vb_dots);
   # We also assume that (for example) .15 is equivalent to 0.15
   if ($a eq '' && $va ne '') { $a = "0"; }
   if ($b eq '' && $vb ne '') { $b = "0"; }
   while ((defined($a) && $a ne '') || (defined($b) && $b ne '')) {
      # compare each minor from left to right
      if ($a eq '') { return -1; }        # the longer version is newer
      if ($b eq '') { return 1; }
      if ($a =~ /^\d+$/ && $b =~ /^\d+$/) {
	 # I have changed this so that when the two strings are numeric, but one or both
	 # of them start with a 0, then do a string compare - Kirk Bauer - 5/28/99
	 if ($a =~ /^0/ or $b =~ /^0/) {
	    # We better string-compare so that netscape-4.6 is newer than netscape-4.08
	    if ($a ne $b) {return ($a cmp $b);}
	 }

	 # numeric compare
	 if ($a != $b) { return $a <=> $b; }
      }
      elsif ($a =~ /^\D+$/ && $b =~ /^\D+$/) {
	 # string compare
	 if (length($a) == 1 && length($b) == 1) {
	    # only minors with one letter seem to be useful for versioning
	    if ($a ne $b) { return $a cmp $b; }
	 }
	 elsif (($a cmp $b) != 0) {
	    # otherwise we should at least check they are the same and if not say unknown
	    # say newer for now so at least we get choice whether to upgrade or not
	    return -1;
	 }
      }
      elsif ( ($a =~ /^\D+$/ && $b =~ /^\d+$/) || ($a =~ /^\d+$/ && $b =~ /^\D+$/) )
      {
	 # if we get a number in one and a word in another the one with a number
	 # has a longer version string
	 if ($a =~ /^\d+$/) { return 1; }
	 if ($b =~ /^\d+$/) { return -1; }
      }
      else {
	 # minor needs splitting
	 $a =~ /\d+/ || $a =~ /\D+/;
	 # split the $a minor into numbers and non-numbers
	 my @va_bits = ($`, $&, $');
	 $b =~ /\d+/ || $b =~ /\D+/;
	 # split the $b minor into numbers and non-numbers
	 my @vb_bits = ($`, $&, $');
	 
	 for ($j=2; $j >= 0; $j--) {
	    if ($va_bits[$j] ne '') { unshift(@va_dots,$va_bits[$j]); }
	    if ($vb_bits[$j] ne '') { unshift(@vb_dots,$vb_bits[$j]); }
	 }
      }
      $a = shift(@va_dots);
      $b = shift(@vb_dots);
   }
   return 0;
}

# RPM_Compare_Version - compare RPM version strings
# Arguments:
#    $_[0]		version string of "a"
#    $_[1]		version string of "b"
# Returns:		"a" <=> "b"
#        -1 = a < b, 0 = a==b, 1 = a > b
sub RPM_Compare_Version($$) {
   my ($Version1,$Release1,$Version2,$Release2,$Result);
   ($Version1,$Release1) = split (/-/,$_[0]);
   ($Version2,$Release2) = split (/-/,$_[1]);
   if ($VersionCheckMode) {
      print "Version 1: $Version1\n";
      print "Release 1: $Release1\n";
      print "Version 2: $Version2\n";
      print "Release 2: $Release2\n";
   }
   $Result = cmp_vers_part ($Version1,$Version2);
   if ($Result) {
      return ($Result);
   }
   return (cmp_vers_part ($Release1,$Release2));
}

##############################################################################
# Process_Local
##############################################################################

# Process_Local gets a list of local files ready and then calls the appropriate
# Source-Files function...
sub Process_Local {
   $LocalSource = '';  # keeps an ID for local source caching
   my @TempLocalFiles = ();
   unless (@AutoIgnore) {
      Read_Auto_Ignore();
   }
   if ( ( (not(@{$Data->{'compare_to_dir'}} )) and
	  (not(@{$Data->{'recursive_compare_to_dir'}}) ) ) or
	( (@{$Data->{'compare_to_installed'}}) and ($Data->{'compare_to_installed'}->[0]) ) ) {
      # We are comparing to the installed RPMs.
      $LocalSource = 'installed';
      Inform ("Comparing to locally installed RPMs\n");
      unless (defined(@{$LocalFiles{$LocalSource}})) {
	 @TempLocalFiles = `$RPMLocation -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}.%{ARCH}.rpm\n'`;
	 chomp(@TempLocalFiles);
      }
   }
   if ( (@{$Data->{'compare_to_dir'}} ) or
	(@{$Data->{'recursive_compare_to_dir'}} )
	) {
      # We are comparing to one or more local directories
      $LocalSource = '';
      foreach $ThisDir ( @{$Data->{'compare_to_dir'}},
			 @{$Data->{'recursive_compare_to_dir'}} ) {

	 Inform ("\nComparing to directory: " . $ThisDir . "\n");

	 $LocalSource .= $ThisDir;
      }

      unless (defined(@{$LocalFiles{$LocalSource}})) {
	 foreach $ThisDir ( @{$Data->{'compare_to_dir'}},
			    @{$Data->{'recursive_compare_to_dir'}} ) {
	    
	    # Add / to end of directory
	    unless ($ThisDir =~ m=/$=) {
	       $ThisDir = $ThisDir . '/';
	    }

	    # Create Directory...
	    unless (-d $ThisDir) {
	       mkdir ($ThisDir,0770) or die "ERROR: Can't create directory " . $ThisDir . "\n";
	       Report ("\nMaking directory: " . $ThisDir . "\n");
	    }
	    
	    opendir (LOCALDIR,$ThisDir) or die "ERROR: Can't open local directory " . $ThisDir . "\n";
	    while (defined($ThisFile = readdir(LOCALDIR))) {
	       unless (-d $ThisDir . $ThisFile) {
		  if ($ThisFile =~ m/\.rpm$/) {
		     push @TempLocalFiles, $ThisDir . $ThisFile;
		     $LocalSizes{$ThisDir . $ThisFile} = (-s $ThisDir . $ThisFile);
		     $HaveLocalSizes{$ThisDir . $ThisFile} = 1;
		  }
	       }
	    }
	    closedir(LOCALDIR);
	 }
      }
   }

   # Okay, if we have multiple local versions of one package, we need to ignore all but the newest one
   # Move from @TempLocalFiles to @LocalFiles or @OldLocalFiles...
   unless (defined(@{$LocalFiles{$LocalSource}})) {
      foreach $ThisRPM (@TempLocalFiles) {
	 if (($LocalName, $LocalVersion, $LocalArch) = RPM_Split_Name($ThisRPM)) {
	    @LocalMatches = grep (/(.*\/|^)\Q$LocalName\E-[^-]+-[^-]+\.[^.]+\.rpm$/, @TempLocalFiles);
	    if ($#LocalMatches > 0) {
	       $isNewest = 1;	        
	       # There are other local matches besides this one... find the newest one and keep it
	       foreach $Match (@LocalMatches) {
		  if (($isNewest) and ($MatchName, $MatchVersion, $MatchArch) = RPM_Split_Name($Match)) {
		     if ($LocalPackageName eq $SourcePackageName) {
			$Result = RPM_Compare_Version ($MatchVersion,$LocalVersion);
			if ($Result == 1) {
			   if ($Debug) {
			      Report ("DEBUG: Local File $ThisRPM is older than local file $Match\n");
			   }
			   $isNewest = 0;
			}
		     }
		  }
	       }
	       # We now know which one is the newest...
	       if ($isNewest)
	       {
		  push @{$LocalFiles{$LocalSource}}, $ThisRPM;
	       }
	       else
	       {
		  push @{$OldLocalFiles{$LocalSource}}, $ThisRPM;
	       }
	    }
	    elsif ($#LocalMatches == 0) {
	       # This is the only local package of this name...
	       push @{$LocalFiles{$LocalSource}}, $ThisRPM;
	    }
	 }
	 
      }
      
      if ($Debug) {
	 Report ('DEBUG: Local Files' . "\n");
	 foreach $DebugTemp (@{$LocalFiles{$LocalSource}}) {
	    Report ('   DEBUG: ' . $DebugTemp . "\n");
	 }
      }
   }

   # Okay, local file-list is done, now lets get the remote one...
   Process_Remote();
   # Blank line
   Inform ("\n");
}

##############################################################################
# Do_Actions (process the Action Block)
##############################################################################

sub Do_Actions($$$$$) {
   my ($ActionType, $SourceFile, $LocalFile, $OldLocalFlag, $AutoIgnoreString) = @_;
   my ($Msg,$ReportCommand,$i,$ThisAction,$Delete,$QueueFile,$Cached,$CopyTo,$bn,$ForceInstallChange, $line, $file, $RPMOpts, $IgnoreOnFailure, $a, @CurrQueueFile);
   if ( $LocalFile =~ m/^\// ) {
      $Msg = ' local file ';
   }
   else {
      $Msg = ' installed RPM ';
   }
   if ( keys %{$Data->{$ActionType}} ) {
      $ReportCommand = 1;
      if ( ( @{$Data->{$ActionType}->{'report'}} ) and
	   ( $Data->{$ActionType}->{'report'}->[0] == 0 ) ) {
	 $ReportCommand = 0;
      }
      $ForceInstallChange = 0;
      # This next block used for both pgp_require and install
      $Cached = '';
      if ($FTP) {
	 $QueueFile = $SourceFile;
	 $QueueFile =~ s=^.*/([^/]+)$=$1=;
	 $QueueFile = $TempDir . $QueueFile;
	 if ($FTPUser eq 'anonymous') {
	    $Cached = 'ftp://' . $FTPSiteName . $SourceFile;
	 }
	 else {
	    $Cached = 'ftp://' . $FTPUser . ':' . $FTPPasswd . '@' . $FTPSiteName . $SourceFile;
	 }
      }
      else {
	 $QueueFile = $SourceFile;
      }
      foreach $ThisAction ( keys %{$Data->{$ActionType}} ) {
	 if(not ($OldLocalFlag)) {
	    if ($ThisAction eq 'recursive_store') {
	       for $i ( 0 .. $#{$Data->{$ActionType}->{'recursive_store'}} ) {
		  # Create Directory...
		  unless (-d $Data->{$ActionType}->{'recursive_store'}->[$i]) {
		     if ($ReportCommand) {
			mkdir ($Data->{$ActionType}->{'recursive_store'}->[$i],0770) or
			    die "ERROR: Can't create directory " . $Data->{$ActionType}->{'recursive_store'}->[$i] . "\n";
		     }
		     Report ("\nMaking directory: " . $Data->{$ActionType}->{'recursive_store'}->[$i] . "\n\n");
		  }
		  unless ( $Data->{$ActionType}->{'recursive_store'}->[$i] =~ m=/$= ) {
		     $Data->{$ActionType}->{'recursive_store'}->[$i] .= '/';
		  }
		  if ($ReportCommand) {
		     Report ('   Storing ' . $SourceFile . "\n      into " . $Data->{$ActionType}->{'recursive_store'}->[$i] . '... ');
		  }
		  if ($a = VFS_Store($SourceFile,$Data->{$ActionType}->{'recursive_store'}->[$i])) {
		     if ($ReportCommand) {
			if ($a == 3) {
			   Report ("Resumed Transfer Done.\n");
			}
			elsif ($a == 2) {
			   Report ("Already there.\n");
			}
			else {
			   Report ("Done.\n");
			}
		     }
		     if ($FTP) {
			# Queue File is now wherever this was just stored...
			$QueueFile = $SourceFile;
			$QueueFile =~ s=^.*/([^/]+)$=$1=;
			$QueueFile = $Data->{$ActionType}->{'recursive_store'}->[$i] . '/' . $QueueFile;
		     }
		  }
		  else {
		     Report ("Error.\n");
		     print STDERR "Couldn't place " . $SourceFile . ' into ' . $Data->{$ActionType}->{'recursive_store'}->[$i] . "!\n";
		  }
	       }
	    }
	    if ($ThisAction eq 'rpm_opt') {
	       $RPMOpts = $Data->{$ActionType}->{$ThisAction}->[0];
	    }
	    if ($ThisAction eq 'store') {
	       for $i ( 0 .. $#{$Data->{$ActionType}->{'store'}} ) {
		  # Create Directory...
		  unless (-d $Data->{$ActionType}->{'store'}->[$i]) {
		     mkdir ($Data->{$ActionType}->{'store'}->[$i],0770) or Report ("\n\nERROR: Can't create directory " . $Data->{$ActionType}->{'store'}->[$i] . "\n\n");
		     if ($ReportCommand) {
			Report ("\nMaking directory: " . $Data->{$ActionType}->{'store'}->[$i] . "\n\n");
		     }
		  }
		  if ($ReportCommand) {
		     Report ('   Storing ' . $SourceFile . "\n      into " . $Data->{$ActionType}->{'store'}->[$i] . '... ');
		  }
		  if ($a = VFS_Store($SourceFile,$Data->{$ActionType}->{'store'}->[$i])) {
		     if ($ReportCommand) {
			if ($a == 3) {
			   Report ("Resumed Transfer Done.\n");
			}
			elsif ($a == 2) {
			   Report ("Already there.\n");
			}
			else {
			   Report ("Done.\n");
			}
		     }
		     if ($FTP) {
			# Queue File is now wherever this was just stored...
			$QueueFile = $SourceFile;
			$QueueFile =~ s=^.*/([^/]+)$=$1=;
			$QueueFile = $Data->{$ActionType}->{'store'}->[$i] . '/' . $QueueFile;
		     }
		  }
		  else {
		     Report ("Error.\n");
		     print STDERR "Couldn't place " . $SourceFile . ' into ' . $Data->{$ActionType}->{'store'}->[$i] . "!\n";
		  }
	       }
	    }
	 }
	 if (($ThisAction eq 'delete_old_version') and
	     ($Data->{$ActionType}->{$ThisAction}->[0] == 1) and
	     ($ActionType eq 'updated') and ($LocalFile) ) {
	    if (unlink($LocalFile)) {
	       if ($ReportCommand) {
		  Report ('   Deleted old local version: ' . $LocalFile . "\n");
	       }
	    }
	    else {
	       print STDERR "Couldn't remove " . $LocalFile . "!\n";
	    }
	 }
	 if(not $OldLocalFlag) {
	    if (($ThisAction eq 'pgp_require') and
		($Data->{$ActionType}->{'install'}->[0] != 0) and
		($Data->{$ActionType}->{'pgp_require'}->[0] ne "0") ) {
	       # Only applies for Auto-Installs or Interactive-Installs...
	       $bn = BaseName($QueueFile);
	       Inform ('   Checking PGP Signature for ' . $bn . '... ');
	       unless (@{$Data->{$ActionType}->{'pgp_fail_install'}} ) {
		  $Data->{$ActionType}->{'pgp_fail_install'}->[0] = 1;
	       }
	       Get_Remote_File ($Cached,$QueueFile);
	       @Output = `$RPMLocation --checksig $QueueFile 2>&1`;
	       chomp (@Output);
	       foreach $i (@Output) {
		  if ( ( $i =~ /^\Q$QueueFile\E: size .*pgp.* md5 OK.*$/ ) or
		       ( $i =~ /^\Q$QueueFile\E:.* md5 gpg OK.*$/ ) ) {
		     Inform ("Good Signature.\n");
		  }
		  else {
		     Report ("\n   PGP Check Failed for " . $bn . ", switching from Auto to Interactive Install mode.\n");
		     $ForceInstallChange = 1;
		  }
	       }
	       if ( ($ForceInstallChange) and ($Data->{$ActionType}->{'pgp_fail_install'}->[0] == 0) and ($QueueFile)) {
		  Report ('   ' . $bn . " won't be installed, deleting file cached from FTP site.\n");
	       }
	    }
	    if ( ($ThisAction eq 'report_always') and
		 ($Data->{$ActionType}->{$ThisAction}->[0] != 0)) {
	       if ($ActionType eq 'new') {
		  Report ('   ' . BaseName($SourceFile) . " is a new RPM and could be installed.\n");
	       }
	       elsif ($ActionType eq 'same') {
		  #Report ('   ' . BaseName($SourceFile) . " is the same as the local RPM.\n");
	       }
	       elsif ($ActionType eq 'old') {
		  Inform ('   ' . BaseName($SourceFile) . " is older than the local RPM.\n");
	       }
	       else {
		  Report ('   ' . BaseName($SourceFile) . " is an updated RPM and could be upgraded.\n");
	       }
	    }
	    if ( ($ThisAction eq 'install') and
		 ($Data->{$ActionType}->{$ThisAction}->[0] != 0) and
		 ( ($ActionType eq 'new') or ($ActionType eq 'updated') ) and
		 ( not ( ($ForceInstallChange) and ($Data->{$ActionType}->{'pgp_fail_install'}->[0] == 0) ))) {
	       $Delete = 0;
	       $IgnoreOnFailure = 0;
	       $CopyTo = '';
	       if ($ReportCommand) {
		  if ($ActionType eq 'new') {
		     Report ('   ' . BaseName($SourceFile) . " is a new RPM and could be installed.\n");
		  }
		  else {
		     Report ('   ' . BaseName($SourceFile) . " is an updated RPM and could be upgraded.\n");
		  }
	       }
	       if ( ( @{$Data->{$ActionType}->{'delete_after_install'}} ) and
		    ($Data->{$ActionType}->{'delete_after_install'}->[0] == 1) ) {
		  $Delete = 2;
	       }
	       if (($FTP) and ( not ( (@{$Data->{$ActionType}->{'delete_after_install'}}) and ( $Data->{$ActionType}->{'delete_after_install'}->[0] == 0 )) ) ) {
		  $Delete = 1;
	       }
	       if ( ( @{$Data->{$ActionType}->{'ignore_on_failure'}} ) and
		    ($Data->{$ActionType}->{'ignore_on_failure'}->[0] == 1) ) {
		  $IgnoreOnFailure = 1;
	       }
	       if ( @{$Data->{$ActionType}->{'copy_after_install'}} ) {
		  foreach $i ( @{$Data->{$ActionType}->{'copy_after_install'}} ) {
		     if ($CopyTo) {
			$CopyTo .= ':';
		     }
		     $CopyTo .= $Data->{$ActionType}->{'copy_after_install'}->[$i];
		  }
	       }
	       if ( @{$Data->{$ActionType}->{'recursive_copy_after_install'}} ) {
		  foreach $i ( @{$Data->{$ActionType}->{'recursive_copy_after_install'}} ) {
		     if ($CopyTo) {
			$CopyTo .= ':';
		     }
		     $CopyTo .= $Data->{$ActionType}->{'recursive_copy_after_install'}->[$i];
		  }
	       }
	       if ( @{$Data->{$ActionType}->{'copy_on_failure'}} ) {
		  foreach $i ( @{$Data->{$ActionType}->{'copy_on_failure'}} ) {
		     if ($CopyTo) {
			$CopyTo .= ':';
		     }
		     $CopyTo .= ('FAIL' . $Data->{$ActionType}->{'copy_on_failure'}->[$i]);
		  }
	       }
	       if ( @{$Data->{$ActionType}->{'recursive_copy_on_failure'}} ) {
		  foreach $i ( @{$Data->{$ActionType}->{'recursive_copy_on_failure'}} ) {
		     if ($CopyTo) {
			$CopyTo .= ':';
		     }
		     $CopyTo .= ('FAIL' . $Data->{$ActionType}->{'recursive_copy_on_failure'}->[$i]);
		  }
	       }
	       # Determine the source name for the auto-ignore file...
	       if (($Data->{$ActionType}->{'install'}->[0] == 1) or
		   ( ($ForceInstallChange) and ($Data->{$ActionType}->{'pgp_fail_install'}->[0] == 1) ) ) {
		  # Interactive install...
		  $file = $TempDir . 'interactive.queue';
	       }
	       else {
		  $file = $TempDir . 'auto.queue';
	       }
	       open (QUEUE, $file);
	       @CurrQueueLines = <QUEUE>;
	       close QUEUE;
	       TouchFile ($file, 0);
	       open (QUEUE,'>>' . $file) or die "ERROR: Can't open $file\n";
	       $line = $QueueFile . ';' . $Delete . ';' . $Cached . ';0:;' . $LocalFile . ';' . $CopyTo . ';' . $RPMOpts . ';' .
		   $IgnoreOnFailure . ';' . $AutoIgnoreString . "\n";
	       unless (grep /;\Q$AutoIgnoreString\E$/, @CurrQueueLines) {
		  print QUEUE $line;
	       }
	       close (QUEUE);
	    }
	 }
      }
   }
   else {
      if ( $ActionType eq 'new' ) {

	 Report ( 'Source File ' . $SourceFile . " is brand new.\n" );
	 Report ( "   But nothing done as no action block was defined.\n");
      }
      elsif ( $ActionType eq 'updated' ) {
	 Report ( 'Source File ' . $SourceFile . ' is newer than' . $Msg . $LocalFile . "\n");
	 Report ( "   But nothing done as no action block was defined.\n");
      }
   }
}

##############################################################################
# Process_Remote and Process_RPM_File
##############################################################################

sub Process_Remote {
   my (@DirList,$ThisDir,$ThisRegex,$Okay,$SubDir,$Temp,$ThisAction,$MySourceLocation);
   $RetriesLeft = $FTPRetries;
   VFS_Open();
   while ($ThisFile = VFS_Read()) {
      if ($ThisFile eq "NODIR") {
	 $ThisFile = "";
	 # The source is an FTP pool, but the site we are currently connected to
	 # isn't allowing us into the directory for whatever reason...
	 # The VFS_Read has lowered that site's score by quite a bit... but now
	 # we are going to call VFS_Open again to get a new site.
	 return if ($Recursing);
	 $RetriesLeft--;
	 VFS_Open();
      }
      if ( $ThisFile =~ m=/$= ) {
	 # The file is a directory...
	 unless ( ($ThisFile =~ m=\./$=) or ($ThisFile =~ m=\.\./$=) ) {
	    push @DirList, $ThisFile;
	 }
      }
      elsif ($ThisFile =~ /\.rpm$/ ) {
	 # Okay, now we have a "Remote" RPM... lets figure out if we care about it
	 Process_RPM_File ($ThisFile);
      }
   }
   # Process recursion here...
   if ( (@DirList) and
	( @{$Data->{'recursive'}} ) and
	( $Data->{'recursive'}->[0] = 1) ) {
      unless ($SourceLocation =~ m=/$=) {
	 $SourceLocation .= '/';
      }
      foreach $ThisDir (@DirList) {
	 $Okay = 1;
	 $SubDir = $ThisDir;
	 $MySourceLocation = $SourceLocation;
	 if ($FTP) {
	    $MySourceLocation =~ s=^ftp://[^/]+==;
	 }
	 $SubDir =~ s=^\Q$MySourceLocation\E==;
	 if ( @{$Data->{'regex_dir_ignore'}} ) {
	    # Apply Regex_Dir_Ignores...
	    foreach $ThisRegex (@{$Data->{'regex_dir_ignore'}}) {
	       if ( $SubDir =~ m/$ThisRegex/ ) {
		  $Okay = 0;
	       }
	    }
	 }
	 if (( @{$Data->{'regex_dir_accept'}} ) and
	     ( $Okay ) ) {
	    # Apply Regex_Dir_Accepts only if dir hasn't been denied already...
	    # By specifying any Regex_Dir_Accepts, every dir must match at least
	    # one of them...
	    $Okay = 0;
	    foreach $ThisRegex (@{$Data->{'regex_dir_accept'}}) {
	       if ( $SubDir =~ m/$ThisRegex/ ) {
		  $Okay = 1;
	       }
	    }
	 }
	 if ($Okay) {
	    # Looks like we have to recurse into this directory...
	    $Recursing = 1;
	    $SourceLocation .= $SubDir;
	    if ( @{$Data->{'recursive_compare_to_dir'}} ) {
	       for $Temp ( 0 .. $#{@{$Data->{'recursive_compare_to_dir'}}} ) {
		  unless ($Data->{'recursive_compare_to_dir'}->[$Temp] =~ m=/$=) {
		     $Data->{'recursive_compare_to_dir'}->[$Temp] .= '/';
		  }
		  $Data->{'recursive_compare_to_dir'}->[$Temp] .= $SubDir;
	       }
	    }
	    foreach $ThisAction ( 'new', 'updated', 'old', 'same' ) {
	       if ( @{$Data->{$ThisAction}->{'recursive_store'}} ) {
		  for $Temp ( 0 .. $#{@{$Data->{$ThisAction}->{'recursive_store'}}} ) {
		     unless ( $Data->{$ThisAction}->{'recursive_store'}->[$i] =~ m=/$= ) {
			$Data->{$ThisAction}->{'recursive_store'}->[$i] .= '/';
		     }
		     $Data->{$ThisAction}->{'recursive_store'}->[$Temp] .= $SubDir;
		  }
	       }
	       if ( @{$Data->{$ThisAction}->{'recursive_copy_after_install'}} ) {
		  for $Temp ( 0 .. $#{@{$Data->{$ThisAction}->{'recursive_copy_after_install'}}} ) {
		     unless ( $Data->{$ThisAction}->{'recursive_copy_after_install'}->[$i] =~ m=/$= ) {
			$Data->{$ThisAction}->{'recursive_copy_after_install'}->[$i] .= '/';
		     }
		     $Data->{$ThisAction}->{'recursive_copy_after_install'}->[$Temp] .= $SubDir;
		  }
	       }
	       if ( @{$Data->{$ThisAction}->{'recursive_copy_on_failure'}} ) {
		  for $Temp ( 0 .. $#{@{$Data->{$ThisAction}->{'recursive_copy_on_failure'}}} ) {
		     unless ( $Data->{$ThisAction}->{'recursive_copy_on_failure'}->[$i] =~ m=/$= ) {
			$Data->{$ThisAction}->{'recursive_copy_on_failure'}->[$i] .= '/';
		     }
		     $Data->{$ThisAction}->{'recursive_copy_on_failure'}->[$Temp] .= $SubDir;
		  }
	       }
	    }
	    Process_Local();
	    # Go up a directory on everything now...
	    $SourceLocation =~ s/\Q$SubDir\E$//;
	    if ( @{$Data->{'recursive_compare_to_dir'}} ) {
	       for $Temp ( 0 .. $#{@{$Data->{'recursive_compare_to_dir'}}} ) {
		  $Data->{'recursive_compare_to_dir'}->[$Temp] =~ s/\Q$SubDir\E$//;
	       }
	    }
	    foreach $ThisAction ( 'new', 'updated', 'old', 'same' ) {
	       if ( @{$Data->{$ThisAction}->{'recursive_store'}} ) {
		  for $Temp ( 0 .. $#{@{$Data->{$ThisAction}->{'recursive_store'}}} ) {
		     $Data->{$ThisAction}->{'recursive_store'}->[$Temp] =~ s/\Q$SubDir\E$//;
		  }
	       }
	       if ( @{$Data->{$ThisAction}->{'recursive_copy_after_install'}} ) {
		  for $Temp ( 0 .. $#{@{$Data->{$ThisAction}->{'recursive_copy_after_install'}}} ) {
		     $Data->{$ThisAction}->{'recursive_copy_after_install'}->[$Temp] =~ s/\Q$SubDir\E$//;
		  }
	       }
	       if ( @{$Data->{$ThisAction}->{'recursive_copy_on_failure'}} ) {
		  for $Temp ( 0 .. $#{@{$Data->{$ThisAction}->{'recursive_copy_on_failure'}}} ) {
		     $Data->{$ThisAction}->{'recursive_copy_on_failure'}->[$Temp] =~ s/\Q$SubDir\E$//;
		  }
	       }
	    }
	 }
      }
   }
   VFS_Close();
}

sub Process_RPM_File($) {
   my (@LocalMatches, $Temp, $Result, $ThisRegex, $TestArch,
       $SourcePackageName,$SourceVersionString,$SourceArch,
       $LocalPackageName, $LocalVersionString, $LocalArch, $AutoIgnoreString);
   my $ThisFile = $_[0];
   my $Okay = 1;
   my $bn = BaseName ($ThisFile);
   if (defined(@{$Data->{'regex_ignore'}}) and
       ( @{$Data->{'regex_ignore'}} )) {
      # Apply Regex_Ignores...
      foreach $ThisRegex (@{$Data->{'regex_ignore'}}) {
	 if ( $bn =~ m/$ThisRegex/ ) {
	    $Okay = 0;
	 }
      }
   }
   if (defined(@{$Data->{'regex_accept'}}) and
       ( @{$Data->{'regex_accept'}} ) and
       ( $Okay ) ) {
      # Apply Regex_Accepts only if file hasn't been denied already...
      # By specifying any Regex_Accepts, every file must match at least
      # one of them...
      $Okay = 0;
      foreach $ThisRegex (@{$Data->{'regex_accept'}}) {
	 if ( $bn =~ m/$ThisRegex/ ) {
	    $Okay = 1;
	 }
      }
   }
   if (defined(@{$Data->{'ignore_arch'}}) and
       ( @{$Data->{'ignore_arch'}} ) and
       ( $Okay ) ) {
      # If the file is still okay, but there are some archs to ignore
      foreach $ThisArch (@{$Data->{'ignore_arch'}}) {
	 if ( $ThisFile =~ m/$ThisArch\.rpm$/ ) {
	    $Okay = 0;
	 }
      }
   }
   if (defined(@{$Data->{'accept_arch'}}) and
       ( @{$Data->{'accept_arch'}} ) and
       ( $Okay ) ) {
      # Apply Accept_Archs only if file hasn't been denied already...
      # By specifying any Accept_Archs, every file must match at least
      # one of them...
      $Okay = 0;
      foreach $ThisArch (@{$Data->{'accept_arch'}}) {
	 if ( $ThisFile =~ m/$ThisArch\.rpm$/ ) {
	    $Okay = 1;
	 }
      }
   }
   # Determine the source name for the auto-ignore file...
   if ($FTPPool) {
      $AutoIgnoreString = $ThisFile;
      $AutoIgnoreString =~ s=^.*/([^/]+)$=$1=;
      $AutoIgnoreString = "$FTPPool:$AutoIgnoreString";
   }
   else {
      $AutoIgnoreString = "$CurrFTPSite:$ThisFile";
   }
   if (($Okay) and (not Test_Auto_Ignore($AutoIgnoreString))) {
      # Well, the file looks good if we are here...
      # Now, we are ready to find out if the file is older, newer, the
      # same, or brand-new, and take the appropriate action.
            if (($FTP && $CurrFTPSite && (($SourcePackageName,$SourceVersionString,$SourceArch) =
               RPM_Split_Name('ftp://' . $CurrFTPSite . $ThisFile))) ||
              (($SourcePackageName,$SourceVersionString,$SourceArch) = 
               RPM_Split_Name($ThisFile)) ) {
	 if ($Debug) {
	    Report ('DEBUG: Comparing File ' . $ThisFile . ' (' . $SourcePackageName . ")\n");
	 }
	 # Use grep to pull out any local packages of the same name...
	 # If source architecture is noarch accept any architecture in local files
	 if ($SourceArch eq 'noarch') {
	    $TestArch = "([^\.]*";
	 }
	 else {
	    $TestArch = "(\Q$SourceArch\E|noarch";
	 }
	 foreach $ThisArch (@{$ReplaceArch{$SourceArch}}) {
	    $TestArch .= "|\Q$ThisArch\E";
	 }
	 $TestArch .= ")";
	 
	 # Delete any old versions if necessary
	 @OldLocalMatches = grep (/(.*\/|^)\Q$SourcePackageName\E-[^-]+-[^-]+\.$TestArch\.rpm$/, @{$OldLocalFiles{$LocalSource}});
	 if ($Debug) {
	    foreach $DebugTemp (@OldLocalMatches) {
	       Report ('   DEBUG: Old Local Match: ' . $DebugTemp . "\n");
	    }
	 }


	 if (@OldLocalMatches) {
	    # There is at least one local package by that name...
	    foreach $Temp (@OldLocalMatches) {
	       if (($OldLocalPackageName,$OldLocalVersionString,$OldLocalArch) =
		   RPM_Split_Name($Temp) ) {
		  if ($OldLocalPackageName eq $SourcePackageName) {
		     $Result = RPM_Compare_Version ($SourceVersionString,$OldLocalVersionString);
		     if ($Result == 1) {
			# 'updated'
			Do_Actions('updated',$ThisFile,$Temp,1,$AutoIgnoreString);
		     }
		  }
	       }
	    }
	 }

	 # on to the newest local files
	 @LocalMatches = grep (/(.*\/|^)\Q$SourcePackageName\E-[^-]+-[^-]+\.$TestArch\.rpm$/, @{$LocalFiles{$LocalSource}});
	 if ($Debug) {
	    foreach $DebugTemp (@LocalMatches) {
	       Report ('   DEBUG: Local Match: ' . $DebugTemp . "\n");
	    }
	 }
	 if (@LocalMatches) {
	    # There is at least one local package by that name...
	    foreach $Temp (@LocalMatches) {
	       if (($LocalPackageName,$LocalVersionString,$LocalArch) =
		   RPM_Split_Name($Temp) ) {
		  if ($LocalPackageName eq $SourcePackageName) {
		     $Result = RPM_Compare_Version ($SourceVersionString,$LocalVersionString);
		     if ($Result == -1) {
			# 'old'
			Do_Actions('old',$ThisFile,$Temp,0,$AutoIgnoreString);
		     }
		     elsif ($Result == 0) {
			# 'same'
			Do_Actions('same',$ThisFile,$Temp,0,$AutoIgnoreString);
			if ($HaveLocalSizes{$Temp}) {
			   if ($LocalSizes{$Temp} != $RemoteSizes{$ThisFile}) {
			      # Local file is not the same size... re-download...
			      Do_Actions('updated',$ThisFile,"",0,$AutoIgnoreString);
			   }
			}
		     }
		     elsif ($Result == 1) {
			# 'updated'
			Do_Actions('updated',$ThisFile,$Temp,0,$AutoIgnoreString);
		     }
		  }
	       }
	    }
	 }
	 else {
	    # There are no local packages by that name...
	    # Therefore, remote package is 'new'
	    Do_Actions('new',$ThisFile,'',0,$AutoIgnoreString);
	 }
      }
   }
}

##############################################################################
# Report commands
##############################################################################

# There are now 2 commands, on a per-source-block-basis
# Report_To ('email_address') or Report_To ('PRINT')
#    In $Data->{'report_to'}->[0]
#    Any unique email address will have its own file that will be mailed
#    after AutoRPM is done running...
# Report_All (Yes or No)
#    In $Data->{'report_all'}->[0]

# The global variable, $ForcePrint will force the output to the screen.

# Reporting has its own data structure,
#    $Report{'email_address'}, the file...
# Internal Variables:
#    CURRMAILFILE
#    $SendCurrReport
#    $PrintReport
#    $TempMailFile

sub Start_Report {
   $NoReport = 0;
   if (($ForcePrint) or
       (not @{$Data->{'report_to'}} ) or
       ($Data->{'report_to'}->[0] eq 'PRINT') ) {
      # Print Report
      $PrintReport = 1;
   }
   elsif ( (@{$Data->{'report_to'}} ) and
	   (not $Data->{'report_to'}->[0]) ) {
      # Null Report_To, so don't report...
      $NoReport = 1;
   }
   else {
      # Mail Report
      $PrintReport = 0;
      $SendCurrReport = 0;
      if ( ( @{$Data->{'report_all'}} ) and
	   ( $Data->{'report_all'}->[0] == 1 ) ) {
	 $SendCurrReport = 1;
      }
      $TempMailFile = $TempDir . 'mail.report.tmp';
      open (CURRMAILFILE,'>' . $TempMailFile);
   }
   unless ($NoReport) {
      my $Date = `date`;
      chomp($Date);
      Inform ("\n***********************************************\nAutoRPM $Version on $HostName started $Date\n\n");
   }
   if ($Debug) {
      Report ("\nDEBUG: If you are having problems, send the output of this to <autorpm\@kaybee.org>\n");
   }
}

# Submit a message to report and note that this report contains an action
sub Report($) {
   my $Msg = $_[0];
   unless ($NoReport) {
      if ($PrintReport) {
	 print $Msg;
      }
      else {
	 print CURRMAILFILE $Msg;
	 $SendCurrReport = 1;
      }
   }
}

# Submit a message to report but do NOT note that this report contains an action
sub Inform {
   my $Msg = $_[0];
   unless ($NoReport) {
      if ($PrintReport) {
	 print $Msg;
      }
      else {
	 print CURRMAILFILE $Msg;
      }
   }
}

sub End_Report {
   my $Temp = $Data->{'report_to'}->[0];
   my $Temp2;
   my $Date = `date`;
   unless ($NoReport) {
      chomp($Date);
      Inform ("\n*************************************\nFinished $Date\n\n");
      unless ($PrintReport) {
	 if ($SendCurrReport) {
	    unless ( $Report{$Temp} ) {
	       # This is the file that the message will be placed into...
	       $Report{$Temp} = $TempDir . $Temp;
	       $Report{$Temp} =~ s/\@/_/g;
	    }
	    close (CURRMAILFILE);
	    $Temp2 = $Report{$Temp};
	    `cat $TempMailFile >> $Temp2`;
	    unlink ($TempMailFile);
	 }
      }
   }
}

sub Mail_Reports {
   my $Temp;
   foreach my $ThisAddress (keys %Report) {
      $Temp = $Report{$ThisAddress};
      if ( -s $Temp ) {
	 # The file exists and is non-empty, so mail it...
	 `$MailProg -s "AutoRPM on $HostName" $ThisAddress < $Temp`;
	 unlink ($Temp);
      }
   }
}

##############################################################################
# FTP Pool Functions
##############################################################################

# This is the maximum value of the "Tries" part of the score...
$MaxTries = 20;

# This routine uses $FTPPool to keep track of the name of the current FTP Pool
# If a non-null parameter is passed in, then it is assumed that an attempt had
# been made to connect to the given FTP and it failed.  This will then be
# recorded as such in the score file.

# Regardless of the parameter, this function will then return the site in the
# FTP pool with the highest score.
sub GetFTPPool ($) {
   my $BadSite = $_[0];
   my ($ThisSite,$MaxScore, @BestSites, $num, $this);

   # Load FTP pool if necessary
   unless ( keys %{$FTPPools{$FTPPool}} ) {
      LoadFTPPool ($FTPPool) or die "FTP Source Pool not defined: $FTPPool\n";
   }

   if ($BadSite) {
      # A site that failed to be connected to was passed in...
      $FTPPools{$FTPPool}{$BadSite}{'tries'}++;
   }

   # Find a new site to use...
   # Step one, determine the highest score...
   $MaxScore = 0;
   foreach $ThisSite ( keys %{$FTPPools{$FTPPool}} ) {
      if ($FTPPools{$FTPPool}{$ThisSite}{'tries'} <= 0) {
	 # Don't divide by zero... I don't know how it would become 0, but re-initialize it if it does
    $FTPPools{$FTPPool}{$ThisSite}{'successes'} = 1;
	 $FTPPools{$FTPPool}{$ThisSite}{'tries'} = 2;
      }
      $FTPPools{$FTPPool}{$ThisSite}{'score'} = ($FTPPools{$FTPPool}{$ThisSite}{'successes'}*1000) / $FTPPools{$FTPPool}{$ThisSite}{'tries'};
      if ($FTPPools{$FTPPool}{$ThisSite}{'score'} > $MaxScore) {
        $MaxScore = $FTPPools{$FTPPool}{$ThisSite}{'score'};
      }
   }
   
   # Step two, pick a random host out of all the hosts with this highest score
   # (of which there may only be one)
   foreach $ThisSite ( keys %{$FTPPools{$FTPPool}} ) {
      if ($FTPPools{$FTPPool}{$ThisSite}{'score'} == $MaxScore) {
	 push @BestSites, $ThisSite;
      }
   }

   # Step three: Pick a random site from the @BestSites list
   $num = $#BestSites;
   $this = rand ($num+1);
   $this = $this / 1;

   # Return the selected site
   return ($BestSites[$this]);
   
}

# This will load the list of FTP sites and directories associated with
# the given FTP pool, and also load their scores.
sub LoadFTPPool ($) {
   my $PoolName = $_[0];
   my $ScoreDir = Check_Dir ("$TempDir/scores");
   my ($ThisLine, $Site, $Successes, $Tries, $Name);
   
   # Read pool file
   open (POOLFILE, "$PoolDir/$PoolName") or return (0);
   while ($ThisLine = <POOLFILE>) {
      chomp($ThisLine);
      # Ignore comments
      $ThisLine =~ s/^#.*//;
      # Get rid of white space
      $ThisLine =~ s/\s+//;
      # Process any variable substitutions in here...
      while (($Name) = ($ThisLine =~ /\${([^\}]+)}/)) {
	 if (defined($Variables{$Name})) {
	    $ThisLine =~ s/\${[^\}]+}/$Variables{$Name}/;
	 }
	 else {
	    die 'ERROR: Error parsing pool $PoolName.  No such variable defined: $Name\n';
	 }    
      }
      if ($ThisLine) {
         $ThisLine = StandardizeFTP($ThisLine);
         $FTPPools{$FTPPool}{$ThisLine}{'tries'} = 1;
         $FTPPools{$FTPPool}{$ThisLine}{'successes'} = 1;
      }
   }
   close (POOLFILE);
   
   # Read score file
   if (-f "$ScoreDir/$PoolName") {
      open (SCOREFILE, "$ScoreDir/$PoolName") or return (0);
      while ($ThisLine = <SCOREFILE>) {
	 chomp ($ThisLine);
	 ($Site, $Successes, $Tries) = split (/ /, $ThisLine);
	 if ( grep (/\Q$Site\E/, keys %{$FTPPools{$FTPPool}}) ) {
	    $FTPPools{$FTPPool}{$Site}{'successes'} = $Successes;
	    $FTPPools{$FTPPool}{$Site}{'tries'} = $Tries;
	 }
      }
      close (SCOREFILE);
   }

   # Return success
   return (1);
}

# This will save the scores for the given FTP pool
sub SaveFTPPoolScores ($) {
   my $PoolName = $_[0];
   my $ScoreDir = Check_Dir ("$TempDir/scores");
   
   TouchFile ("$ScoreDir/$PoolName", 1);
   open (SCOREFILE, ">$ScoreDir/$PoolName");
   foreach $ThisSite ( keys %{$FTPPools{$PoolName}} ) {
      if ($FTPPools{$PoolName}{$ThisSite}{'tries'} >= $MaxTries) {
	 # Keep scores in a reasonable range... (i.e. keep tries < $MaxTries)
	 $FTPPools{$PoolName}{$ThisSite}{'tries'} = $FTPPools{$PoolName}{$ThisSite}{'tries'} / 2;
	 $FTPPools{$PoolName}{$ThisSite}{'successes'} = $FTPPools{$PoolName}{$ThisSite}{'successes'} / 2;
      }
      print SCOREFILE $ThisSite . ' ' . $FTPPools{$PoolName}{$ThisSite}{'successes'} . ' ' . $FTPPools{$PoolName}{$ThisSite}{'tries'} . "\n";
   }
   close (SCOREFILE);
}

# This is called to report a successful connection...
sub TellSuccess ($) {
   my $GoodSite = $_[0];
   $FTPPools{$FTPPool}{$GoodSite}{'tries'}++;
   $FTPPools{$FTPPool}{$GoodSite}{'successes'}++;
}

# This is called when the directory doesn't even exist on the server.
# The server is then ranked very low
sub TellNoDir ($) {
   my $VeryBadSite = $_[0];
   $FTPPools{$FTPPool}{$VeryBadSite}{'tries'} = $MaxTries-1;
   $FTPPools{$FTPPool}{$VeryBadSite}{'successes'} = 0;
}

##############################################################################
# VFS Functions
##############################################################################

sub StandardizeFTP ($) {
   my $SourceFTP = $_[0];
   unless ( $SourceFTP =~ m=^ftp://=) {
      if ( ($FTPHost, $FTPDir) = ($SourceFTP =~ m=^([^:]+):(.+)$= ) ) {
	 $SourceFTP = 'ftp://' . $FTPHost . $FTPDir;
      }
   }
   return ($SourceFTP);
}

# This takes the $SourceLocation variable and splits it into the various FTP
# variables
sub ProcessFTPSite {
   unless ( ($FTPUser, $FTPPasswd, $FTPSiteName, $CurrFTPDir) = ($SourceLocation =~ m=^ftp://([^:]+):([^@]+)@([^/]+)(/.+)$=) ) {
      ($FTPSiteName, $CurrFTPDir) = ($SourceLocation =~ m=^ftp://([^/]+)(/.+)$=);
      $FTPUser = 'anonymous';
      $FTPPasswd = 'AutoRPM@' . $HostName;
   }
   unless ($CurrFTPDir =~ m=/$=) {
      $CurrFTPDir = $CurrFTPDir . '/';
   }
}

sub VFS_Open {
   if ($FTP) {
      if (($FTPPool) and (not $Recursing)) {
	 $SourceLocation = GetFTPPool("");
	 Inform ('RPM Source (FTP Pool): ' . $FTPPool . "\n\n");
      }
      else {
	 $SourceLocation = StandardizeFTP($SourceLocation);
      }
      %FTPOptions = ();
      if ($FTPPassiveMode) {
	 $FTPOptions{'Passive'} = 1;
      }
      if ($FTPTimeOut) {
	 $FTPOptions{'Timeout'} = $FTPTimeOut;
      }
      if ($FTPFirewall) {
	 $FTPOptions{'Firewall'} = $FTPFirewall;
      }
      if ($FTPPort) {
	 $FTPOptions{'Port'} = $FTPPort;
      }
      ProcessFTPSite();
      if ($RetriesLeft <= 0) {
	 $CurrFTPSite = '';
      }
      unless ($FTPSiteName eq $CurrFTPSite) {
	 if (($LastFTPSite eq $FTPSiteName) and ($LastFTPUser eq $FTPUser)) {
	    unless ($DoingCache) {
	       Inform ('Remaining Connected to ' . $FTPSiteName . ' as ' . $FTPUser . "...\n");
	    }
	    $CurrFTPSite = $FTPSiteName;
	 }
	 else {
	    $Success = 0;
	    while (($RetriesLeft > 0) && ($Success == 0)) {
	       $RetriesLeft--;
	       $FTPRetryDelay = ((rand ($FTPRetryMaxDelay)) / 1) + $FTPRetryMinDelay;
	       unless ($DoingCache) {
		  Inform ('Connecting to ' . $FTPSiteName . "...\n");
	       }
	       if ($FTP_Object = Net::FTP->new($FTPSiteName, %FTPOptions)) {
		  unless ($DoingCache) {
		     Inform ('Logging in as ' . $FTPUser . "\n");
		  }
		  unless ($Success = $FTP_Object->login($FTPUser,$FTPPasswd)) {
		     Inform "ERROR: Can't login as " . $FTPUser . ' on ' . $FTPSiteName . ' (' . $RetriesLeft . " Retries Left)\n";
		     Inform "       Sleeping $FTPRetryDelay second(s)...\n";
		     if ($FTPPool) {
			$SourceLocation = GetFTPPool($SourceLocation);
			ProcessFTPSite();
		     }
		     sleep ($FTPRetryDelay);
		  }
	       }
	       else {
		  Inform "ERROR: Can't connect to " . $FTPSiteName . ' (' . $RetriesLeft . " Retries Left)\n";
		  Inform "       Sleeping $FTPRetryDelay second(s)...\n";
		  if (($FTPPool) and (not $Recursing)) {
		     $SourceLocation = GetFTPPool($SourceLocation);
		     ProcessFTPSite();
		  }
		  sleep ($FTPRetryDelay);
	       }
	    }
	    if ($Success == 0) {
	       $CurrFTPSite = '';
	    }
	    else {
	       if (($FTPPool) and (not $Recursing)) {
		  TellSuccess($SourceLocation);
	       }
	       $CurrFTPSite = $FTPSiteName;
	    }
	 }
      }
      unless ($DoingCache) {
	 $FTP_First = 1;
      }
   }
   else {
      # Add / to end of directory
      unless ($DoingCache) {
	 Inform ('RPM Source: ' . $SourceLocation . "\n\n");
      }
      unless ($SourceLocation =~ m=/$=) {
	 $SourceLocation = $SourceLocation . '/';
      }
      opendir (SOURCEDIR,$SourceLocation) or Report ("ERROR: Can't open directory " . $SourceLocation . "\n");
   }
}

sub VFS_Store($$) {
   my ($SourceFile, $DestDir) = @_;
   my ($RemoteDir,$RemoteFile,$Temp,$RemoteSize,$LocalSize,$Okay);
   unless ($DestDir =~ m=/$= ) {
      $DestDir .= '/';
   }
   if ($FTP) {
      $FTP_Object->binary();
      ($RemoteDir,$RemoteFile) = ( $SourceFile =~ m=(^.*/)([^/]+)$=);
      $FTP_Object->cwd($RemoteDir);
      $Okay = 1;
      if (-e $DestDir . $RemoteFile) {
	 $RemoteSize = $FTP_Object->size($RemoteFile);
	 $RemoteSizeKb = $RemoteSize / 1000;
	 $LocalSize = (-s $DestDir . $RemoteFile);
	 if ($RemoteSize == $LocalSize) {
	    $Okay = 0;
	    $Temp = 2;
	 }
	 elsif ($LocalSize > 0) {
	    # Resume transfer...
	    Report ($RemoteSizeKb . 'KB... ');
	    if ($FTP_Object->get($RemoteFile, $DestDir . $RemoteFile, $LocalSize)) {
	       $Temp = 3;
	       $Okay = 0;
	    }
	 }
      }
      if ($Okay == 1) {
	 $Temp =  $FTP_Object->get($RemoteFile,$DestDir . $RemoteFile);
	 `chmod a+r $DestDir/$RemoteFile`;
      }
      return ($Temp);
   }
   else {
      $Temp = copy($SourceFile,$DestDir);
      ($RemoteDir,$RemoteFile) = ( $SourceFile =~ m=(^.*/)([^/]+)$=);
      `chmod a+r $DestDir/$RemoteFile`;
      return ($Temp);
   }
}

sub VFS_Read {
   my ($ThisFile,$Temp,$Temp2,$LinkName,$LinkDest,@DirListing,@TempListing,$IsDir,$FileName);
   # Any directories returned need to end in a /...
   if ($FTP) {
      if ($CurrFTPSite) {
	 if ($FTP_First) {
	    @SourceList = ();
	    $FTP_First = 0;
	    Inform ('Listing Directory: ' . $CurrFTPDir . "\n\n");
	    unless (@DirListing = $FTP_Object->dir($CurrFTPDir)) {
	       Inform("ERROR: Can't get FTP file listing...\n");
	       if ($FTPPool) {
		  TellNoDir($SourceLocation);
		  return ("NODIR");
	       }
	    }
	    foreach $Temp (@DirListing) {
	       if ( ($LinkName,$LinkDest) = ($Temp =~ /^l[rwxst-]{9}(?:\s+\S+)+\s+[0123456789]+ ... .. [ ]?..[:]?..\s+(.*) -> (.*)$/ ) ) {
		  if ($LinkName eq $CurrFTPDir) {
		     # The actual directory we are looking at is a symlink, so look at the directory it points to
		     $CurrFTPDir =~ s=/[^/]+/*$=/=;
		     $CurrFTPDir .= "$LinkDest/";
		     $FTP_First = 1;
		     return (VFS_Read());
		  }
		  # Symbolic link...
		  unless ($LinkDest =~ m=^/= ) {
		     # Relative Link...
		     $LinkDest = $CurrFTPDir . $LinkDest;
		  }
		  @TempListing = $FTP_Object->dir($LinkDest);# or print STDERR "ERROR: Can't get FTP file listing of " . $LinkDest . "\n";
		  $IsDir = 0;
		  foreach $Temp2 (@TempListing) {
		     if ( $Temp2 =~ /^d[rwxst-]{9}(?:\s+\S+)+\s+[0123456789]+ ... .. [ ]?..[:]?.. \.$/ ) {
			# Well, we now know that the sybolic link points to a directory.
			#push @SourceList, $LinkDest . '/';
			push @SourceList, $CurrFTPDir . $LinkName . '/';
			$IsDir = 1;
		     }
		     else {
			# Get file size
			($FileSize) = ($Temp2 =~ /^d[rwxst-]{9}(?:\s+\S+)+\s+([0123456789]+) ... .. [ ]?..[:]?.. \.$/);
		     }
		  }
		  unless ($IsDir) {
		     # Points to a file...
		     push @SourceList, $LinkDest;
		     $RemoteSizes{$LinkDest} = $FileSize;
		  }
	       }
	       elsif ( ($FileName) = ($Temp =~ /^d[rwxst-]{9}(?:\s+\S+)+\s+[0123456789]+ ... .. [ ]?..[:]?..\s+(.*)$/ ) ) {
		  # Directory...
		  push @SourceList, $CurrFTPDir . $FileName . '/';
	       }
	       elsif ( ($FileSize, $FileName) = ($Temp =~ /^[rwxst-]{10}(?:\s+\S+)+\s+([0123456789]+) ... .. [ ]?..[:]?..\s+(.*)$/ ) ) {
		  # Regular file...
		  push @SourceList, $CurrFTPDir . $FileName;
		  $RemoteSizes{$CurrFTPDir . $FileName} = $FileSize;
	       }
	    }
	    $SourceList = sort (@SourceList);
	 }
	 if (@SourceList) {
	    return (shift(@SourceList));
	 }
	 else {
	    return ('');
	 }
      }
      else {
	 return ('');
      }
   }
   else {
      if (defined($ThisFile = readdir(SOURCEDIR))) {
	 $ThisFile = $SourceLocation . $ThisFile;
	 if ( -d $ThisFile ) {
	    unless ($ThisFile =~ m=/$=) {
	       $ThisFile = $ThisFile . '/';
	    }
	 }
	 return ($ThisFile);
      }
      else
      {
	 return ('');
      }
   }
}

sub VFS_Close {
   if ($FTP) {
      if ($FTPPool) {
	 SaveFTPPoolScores ($FTPPool);
      }
      $LastFTPSite = $CurrFTPSite;
      $LastFTPUser = $FTPUser;
      $CurrFTPDir = '';
      $CurrFTPSite = '';
      #$FTP_Object->quit;
   }
   else {
      closedir (SOURCEDIR);
   }
}

##############################################################################
# Read_Config
##############################################################################

sub Config_Trim($) {
   my $Line = $_[0];
   $Line =~ s/^([^\#\"]*)#.*$/$1/;
   $Line =~ s/^(\".*\")#.*$/$1/;
   $Line =~ s/^\s+//;
   $Line =~ s/\s+$//;
   chomp ($Line);
   return ($Line);
}

sub Reset_Config_Vars {
   %$Data = ();
   $NeedFuncName = 1;
   $NeedOParen = 0;
   $NeedCParen = 0;
   $NeedSemiColon = 0;
   $NeedOCurly = 0;
   $NeedCCurly = 0;
   $InSourceBlock = 0;
   $InActionBlock = 0;
   $ActionName = 0;
   $ProcessCurr = 0;
   $Dirty = 0;
   $LastDirty = 1;
   $FTP = 0;
   $FTPPool = "";
   $SourceLocation = 0;
}

sub Read_Config ($) {
   my $CurrConfigFile = $_[0];
   # Enforce permissions on config file and any backups, etc...
   # In case there are passwords in this file
   system ('chmod 0600 ' . $CurrConfigFile . '*');
   system ('chown root ' . $CurrConfigFile . '*');

   # Open the config file
   open (CONFFILE,$CurrConfigFile) or print STDERR 'ERROR: Cannot open config file: ' . $CurrConfigFile . "\n";
   $LineNum = 0;

   Reset_Config_Vars();

   while (defined($ThisLine = <CONFFILE>)) {

      $LineNum++;

      $ThisLine = Config_Trim($ThisLine);

      until ($ThisLine eq '') {

	 # Looks for a new function name...
	 if ($NeedFuncName) {
	    $FuncName = $ThisLine;
	    $FuncName =~ s/\(.*$//;
	    $FuncName = Config_Trim ($FuncName);
	    if ($FuncName) {
	       if ($FuncName =~ m/ /) {
		  die 'ERROR: Error parsing ' . $CurrConfigFile . "\nSpace not allowed in function name on line " . $LineNum . "\n";
	       }
	       unless ($FuncName =~ m/[\{\}\(\)\;\#\"]/ ) {
		  @ParamList = ();
		  $FuncName = lc($FuncName);
		  $NeedFuncName = 0;
		  $Dirty++;
		  $LastDirty = $LineNum;
		  $NeedOParen = 1;
		  unless ($ThisLine =~ s/^.*?\(/\(/ ) {
		     $ThisLine = '';
		  }
		  $ThisLine = Config_Trim($ThisLine);
	       }
	    }
	 }

	 # Looks for an opening parentesis...
	 if ($ThisLine =~ s/^\(//) {
	    if ($NeedOParen) {
	       $NeedOParen = 0;
	       $NeedParam = 1;
	       $ThisLine = Config_Trim($ThisLine);
	    }
	    else {
	       die 'ERROR: Error Parsing ' . $CurrConfigFile . ', ( not expected, Line ' . $LineNum . "\n";
	    }
	 }

	 # Looks for an opening curly-brace...
	 if ($ThisLine =~ s/^\{//) {
	    if ($NeedOCurly) {
	       $NeedOCurly = 0;
	       $NeedCCurly = 1;
	       $NeedFuncName = 1;
	       if ($InSourceBlock) {
		  if ($InActionBlock) {
		     die 'ERROR: Error Parsing ' . $CurrConfigFile . ', line ' . $LineNum . "\n";
		  }
		  else {
		     $InActionBlock = 1;
		  }
	       }
	       else {
		  $InSourceBlock = 1;
	       }
	       $ThisLine = Config_Trim($ThisLine);
	    }
	    else {
	       die 'ERROR: Error Parsing ' . $CurrConfigFile . ', { not expected, Line ' . $LineNum . "\n";
	    }
	 }

	 # Looks for an closing curly-brace...
	 if ($ThisLine =~ s/^\}// ) {
	    if ( ($NeedCCurly) and
		 ($NeedOParen == 0) and
		 ($NeedCParen == 0) and
		 ($NeedSemiColon == 0) and
		 ($NeedParam == 0)
		 ) {
	       if ($InActionBlock) {
		  $InActionBlock = 0;
		  $Dirty--;
	       }
	       elsif ($InSourceBlock) {
		  unless ($GetTempDirOnly) {
		     Start_Report();
		     if ($FirstBlock) {
			if ($StartDelay) {
			   $StartDelay = ((rand ($StartDelay)) / 1);
			   Inform ("\nDelaying $StartDelay seconds...\n");
			   sleep ($StartDelay);
			}
		     }
		     Process_Local();
		     End_Report();
		  }
		  Reset_Config_Vars();
	       }
	       else {
		  die 'ERROR: Error Parsing ' . $CurrConfigFile . ', line ' . $LineNum . "\n";
	       }
	    }
	    else {
	       die 'ERROR: Error Parsing ' . $CurrConfigFile . ', } not expected, Line ' . $LineNum . "\n";
	    }
	 }

	 # Looks for the actual parameter...
	 if ($NeedParam) {
	    $Param = $ThisLine;
	    $Param =~ s/,[^\"]*\)/\)/;
	    $Param =~ s/^([^\"]*),.*\)/$1\)/;
	    $Param =~ s/^(\".*\"),.*\)/$1\)/;
	    $Param =~ s/\)[^\)]*$//;
	    $Param = Config_Trim ($Param);
	    if ($Param) {
	       # Do variable substitution
	       while (($Name) = ($Param =~ /\${([^\}]+)}/)) {
		  if (defined($Variables{$Name})) {
		     $Param =~ s/\${[^}]+}/$Variables{$Name}/;
	       }
	       else {
		  die 'ERROR: Error Parsing ' . $CurrConfigFile . "\nNo such variable defined: $Name - Line " . $LineNum . "\n";
	       }
	    }
	    # Process Parameter
	    if ($Param =~ s/^\"// ) {
	       unless ($Param =~ s/\"$// ) {
		  die 'ERROR: Error Parsing ' . $CurrConfigFile . "\nNo Closing quote - Line " . $LineNum . "\n";
	       }
	    }
	    else {
	       $Param = lc ($Param);
	       if ($Param eq 'yes') {
		  $Param = 1;
	       }
	       elsif ($Param eq 'no') {
		  $Param = 0;
	       }
	       elsif ($Param eq 'interactive') {
		  $Param = 1;
	       }
	       elsif ($Param eq 'auto') {
		  $Param = 2;
	       }
	       elsif ($FuncName eq 'action') {
		  # Don't process action's arguments
	       }
	       else {
		  die 'ERROR: Error Parsing ' . $CurrConfigFile . "\nInvalid Parameter Value ($Param) - Line " . $LineNum . "\n";
	       }
	    }

	    push @ParamList, $Param;
	    $NeedParam = 0;
	    $NeedCParen = 1;
	    $ThisLine =~ s/^\"[^\"]*\"([,\)])/$1/;
	    unless ($ThisLine =~ s/^[^,]*([,\)])/$1/) {
	       $ThisLine = '';
	    }
	    $ThisLine = Config_Trim($ThisLine);
	 }
      }
      
      # If we have a quote at the beginning of the line at this point, something is wrong.
      if ($ThisLine =~ /^\"/ ) {
	 die 'ERROR: Error Parsing ' . $CurrConfigFile . ', " not expected, Line ' . $LineNum . "\n";
      }
      
      # Look for closing parenthesis...
      if ($ThisLine =~ s/^\)//) {
	 if ($NeedCParen) {
	    $ThisLine = Config_Trim($ThisLine);
	    $NeedCParen = 0;
	    $NeedSemiColon = 1;
	 }
	 else {
	    die 'ERROR: Error Parsing ' . $CurrConfigFile . ', ) not expected, Line ' . $LineNum . "\n";
	 }
      }
      
      # Look for possible comma...
      if ($ThisLine =~ s/^,//) {
	 if ($NeedCParen) {
	    $ThisLine = Config_Trim($ThisLine);
	    $NeedCParen = 0;
	    $NeedParam = 1;
	 }
	 else {
	    die 'ERROR: Error Parsing ' . $CurrConfigFile . ', comma not expected, Line ' . $LineNum . "\n";
	 }
      }
      
      # Look for semi-colon (signals the end of a function call).
      if ($ThisLine =~ s/^;//) {
	 if ($NeedSemiColon) {
	    $ThisLine = Config_Trim($ThisLine);
	    $NeedSemiColon = 0;
	    $ProcessCurr = 1;
	 }
	 else {
	    die 'ERROR: Error Parsing ' . $CurrConfigFile . ', ; not expected, Line ' . $LineNum . "\n";
	 }
      }
      
      # Even if we still need a semi-colon, we may be starting a block instead.
      if ($NeedSemiColon) {
	 if ($InSourceBlock) {
	    if ($FuncName eq 'action') {
	       $NeedSemiColon = 0;
	       $NeedOCurly = 1;
	       $LastDirty = $LineNum;
	       $ActionName = $ParamList[0];
	    }
	 }
	 else {
	    if ($FuncName eq 'ftp') {
	       $NeedSemiColon = 0;
	       $NeedOCurly = 1;
	       $LastDirty = $LineNum;
	       $FTP = 1;
	       $SourceLocation = $ParamList[0];
	    }
	    elsif ($FuncName eq 'ftppool') {
	       $NeedSemiColon = 0;
	       $NeedOCurly = 1;
	       $LastDirty = $LineNum;
	       $FTPPool = $ParamList[0];
	       $Recursing = 0;
	       $FTP = 1;
	    }
	    elsif ($FuncName eq 'directory') {
	       $NeedSemiColon = 0;
	       $NeedOCurly = 1;
	       $LastDirty = $LineNum;
	       $FTP = 0;
	       $SourceLocation = $ParamList[0];
	    }
	 }
      }
      
      # If we are ready to process the current fuction/parameter pair, let's do it.
      if ($ProcessCurr) {
	 if ($InSourceBlock) {
	    if ($InActionBlock) {
	       if (keys %{$Data->{$ActionName}}) {
		  if (@{$Data->{$ActionName}->{$FuncName}}) {
		     $Pos = $#{$Data->{$ActionName}->{$FuncName}} + 1;
		  }
		  else {
		     $Pos = 0;
		  }
	       }
	       else {
		  $Pos = 0;
	       }
	       $Data->{$ActionName}->{$FuncName}->[$Pos] = $ParamList[0];
	    }
	    else {
	       if (defined($Data->{$FuncName}) and
		   @{$Data->{$FuncName}})
	       {
		  $Pos = $#{$Data->{$FuncName}} + 1;
	       }
	       else {
		  $Pos = 0;
	       }
	       $Data->{$FuncName}->[$Pos] = $ParamList[0];
	    }
	 }
	 else {
	    if ($FuncName eq 'temp_dir') {
	       $TempDir = $ParamList[0];
	       $TempDir = Check_Dir($TempDir);
	    }
	    elsif ($FuncName eq 'pool_dir') {
	       $PoolDir = $ParamList[0];
	       $PoolDir = Check_Dir($PoolDir);
	    }
	    elsif ($FuncName eq 'rpm_location') {
	       $RPMLocation = $ParamList[0];
	    }
	    elsif ($FuncName eq 'config_dir') {
	       push (@ConfigDirs, $ParamList[0]);
	    }
	    elsif ($FuncName eq 'config_file') {
	       push (@ConfigFiles, $ParamList[0]);
	    }
	    elsif ($FuncName eq 'ftp_passive') {
	       $FTPPassiveMode = $ParamList[0];
	    }
	    elsif ($FuncName eq 'ftp_timeout') {
	       $FTPTimeOut = $ParamList[0];
	    }
	    elsif ($FuncName eq 'ftp_retries') {
	       $FTPRetries = $ParamList[0];
	    }
	    elsif ($FuncName eq 'start_delay') {
	       print STDERR "Warning: antiquated command Start_Delay() in $CurrConfigFile on Line $LineNum\nUse the --delay=XXX command line option instead\n";
	    }
	    elsif ($FuncName eq 'ftp_retry_delay') {
	       $FTPRetryMinDelay = $ParamList[0];
	       $FTPRetryMaxDelay = $ParamList[1];
	    }
	    elsif ($FuncName eq 'replace_arch') {
	       push @{$ReplaceArch{$ParamList[1]}}, $ParamList[0];
	    }
	    elsif ($FuncName eq 'ftp_retry_max_delay') {
	       # This command has been replaced by FTPRetryDelay
	       print STDERR "Warning: antiquated command FTP_Retry_Max_Delay() in $CurrConfigFile on Line $LineNum\nUse FTP_Retry_Delay(min,max) instead\n";
	       $FTPRetryMaxDelay = $ParamList[0];
	    }
	    elsif ($FuncName eq 'ftp_retry_min_delay') {
	       # This command has been replaced by FTPRetryDelay
	       print STDERR "Warning: antiquated command FTP_Retry_Min_Delay() in $CurrConfigFile on Line $LineNum\nUse FTP_Retry_Delay(min,max) instead\n";
	       $FTPRetryMinDelay = $ParamList[0];
	    }
	    elsif ($FuncName eq 'set_var') {
	       $Variables{$ParamList[0]} = $ParamList[1];
	    }
	    elsif ($FuncName eq 'eval_var') {
	       $Variables{$ParamList[0]} = `$ParamList[1]`;
	       chomp($Variables{$ParamList[0]});
	    }
	    elsif ($FuncName eq 'read_var') {
	       if (open (TFILE, $ParamList[1])) {
		  $Variables{$ParamList[0]} = <TFILE>;
		  close (TFILE);
		  chomp($Variables{$ParamList[0]});
	       }
	    }
	    elsif ($FuncName eq 'ftp_firewall') {
	       $FTPFirewall = $ParamList[0];
	    }
	    elsif ($FuncName eq 'ftp_port') {
	       $FTPPort = $ParamList[0];
	    }
	    elsif ($FuncName eq 'debug') {
	       $Debug = $ParamList[0];
	    }
	    elsif ($FuncName eq 'hostname') {
	       $HostName = $ParamList[0];
	    }
	    elsif ($FuncName eq 'umask') {
	       umask($ParamList[0]);
	    }
	    elsif ($FuncName eq 'report_queues_to') {
	       $AutoQueueReport = $ParamList[0];
	    }
	    else {
	       die 'ERROR: ' . $FuncName . ' not expected in Main File on line ' . $LineNum . "\n";
	    }
	 }
	 $ProcessCurr = 0;
	 $NeedFuncName = 1;
	 $Dirty--;
      }

      $ThisLine = Config_Trim ($ThisLine);

   }

}

close (CONFFILE);
unlink ($TempConfig);

# If $Dirty > 0, the file wasn't correct.
if ($Dirty) {
   if ($NeedParam) {
      $Msg = 'Parameter Expected for ' . $FuncName;
   }
   elsif ($NeedOParen) {
      $Msg = '\( Expected';
   }
   elsif ($NeedCParen) {
      $Msg = '\) Expected';
   }
   elsif ($NeedSemiColon) {
      $Msg = '\; Expected';
   }
   elsif ($NeedOCurly) {
      $Msg = '\{ Expected';
   }
   elsif ($NeedCCurly) {
      $Msg = '\} Expected';
   }
   else {
      $Msg = 'Parsing Error';
   }
   die 'ERROR: Error parsing ' . $CurrConfigFile . "\n" . $Msg . ' - Possibly Started on Line ' . $LastDirty . ".\n";
}

}

##############################################################################
# Setup_Auto_Mode ()
##############################################################################

sub Setup_Auto_Mode {
   if ($AutoFTP) {
      # Set FTP Mode
      $FTP=1;
      if ( ($FTPHost, $FTPDir) = ($AutoFTP =~ m=^([^:]+):(.+)$= ) ) {
	 $AutoFTP = 'ftp://' . $FTPHost . $FTPDir;
      }
      $SourceLocation = $AutoFTP;
      $Data->{'updated'}->{'delete_after_install'}->[0] = 0;
      $Data->{'new'}->{'delete_after_install'}->[0] = 0;
   }
   else {
      # Set Dir Mode
      $FTP=0;
      unless ($AutoDir =~ m=^/=) {
	 # Turn into absolute directory
	 @TArray = `pwd`;
	 chomp (@TArray);
	 $Temp = pop @TArray;
	 $AutoDir = $Temp . '/' . $AutoDir;
      }
      $SourceLocation = $AutoDir;
   }
   $Data->{'display_report'}->[0] = 1;   #Display Report
   $Data->{'updated'}->{'install'}->[0] = 1;   #Interactive mode
   $Data->{'new'}->{'install'}->[0] = 1;   #Interactive mode
}

##############################################################################
# Check_Queues
##############################################################################

sub Check_Queues {
   my ($Source,$Delete,$Orig);
   my ($ThisLine,$InterTotal,%TempArray);

   # First, start a report section for the install queues.
   # Of course, if $ForcePrint is on, this won't matter...
   $Data->{'report_to'}->[0] = $AutoQueueReport;
   # Only report if something happened
   $Data->{'report_all'}->[0] = 0;
   Start_Report;

   Inform ("\nProcessing Install Queues:\n");

   Do_Auto_Queue();

   # Tell about the Interactive Queue
   $InterTotal = 0;
   open (QUEUEFILE,$TempDir . 'interactive.queue');
   while (defined($ThisLine = <QUEUEFILE>)) {
      $ThisLine =~ s/^([^;]+);.*$/$1/;
      unless ($TempArray{$ThisLine}) {
	 $TempArray{$ThisLine}++;
	 $InterTotal++;
      }
   }
   close (QUEUEFILE);
   if ($InterTotal) {
      Inform ("\nInteractive-Install Queue:\n");
      Report ("\n   " . $InterTotal . " RPM(s) waiting to be installed/updated Interactively\n");
      Report ("      To install/upgrade, run 'autorpm --apply' as root...\n");
   }
   End_Report;
}

sub Get_Remote_File($$) {
   my ($Remote, $Local) = @_;
   my ($FTPSiteName,$FTPFile,$a,$ctmp);
   if ($Debug) {
      Report ('DEBUG: Get_Remote_File (' . $Remote . ', ' . $Local . ")\n");
   }
   unless ((-s $Local) or
	   (not $Remote) ) {
      # Only do anything if the original ($Remote) string
      # is non-null (i.e. came from an FTP site).
      $FTP = 1;
      $DoingCache = 1;
      $SourceLocation = $Remote;
      # Strip the filename off the end of the SourceLocation.
      $SourceLocation =~ s=/[^/]+$=/=;
      if ($Debug) {
	 Report ('DEBUG: Inside Get_Remote_File, SourceLocation is now: ' . $SourceLocation . "\n");
      }
      $RetriesLeft = $FTPRetries;
      VFS_Open();
      unless ( ($FTPUser, $FTPPasswd, $FTPSiteName, $FTPFile) = ($Remote =~ m=^ftp://([^:]+):([^@]+)@([^/]+)(/.+)$=) ) {
	 ($FTPSiteName, $FTPFile) = ($Remote =~ m=^ftp://([^/]+)(/.+)$=);
	 $FTPUser = 'anonymous';
	 $FTPPasswd = 'AutoRPM@' . $HostName;
      }
      $Local =~ s=^(.*/)[^/]+$=$1= ;
      if ($Apply) {
	 print 'Transferring ' . $Remote . "\n   to " . $Local . '... ';
      }
      else {
	 $ctmp = BaseName ($Remote);
	 Inform ("Downloading $ctmp... ");
      }
      if ($a = VFS_Store($FTPFile,$Local)) {
	 if ($a == 3) {
	    Inform ("Resumed Transfer Done.\n");
	 }
	 elsif ($a == 2) {
	    Inform ("Already There.\n");
	 }
	 else {
	    Inform ("Done.\n");
	 }
      }
      else {
	 Report ("Error.\n");
	 #print STDERR "ERROR: Couldn't transfer " . $Remote . ' to ' . $Local . "\n";
      }
   }
   $DoingCache = 0;
}

sub Do_Auto_Queue {
   my ($ThisLine,$ThisPackage,$TFile,$TFile2,$Temp,$NumInstalledThisLoop,$Success,$Error);
   my (@DepsList,$ThisConflict,$ThisOther,@ConflictsFileList,@ConflictsPackageList,@RenameList,@NoRemoveList);
   my ($Source,$Delete,$Orig,$Flags,$Local,$CopyTo,$RPMOpts,$WierdError,$IgnoreOnFailure,$AutoIgnoreString);
   my $Date = `date`;
   chomp ($Date);
   Inform ("\nAuto-Install Queue:\n");
   if ( -s $TempDir . 'auto.queue.backup' ) {
      Report ("AutoInstall didn't complete successfully last time, appending backup queue.\n");
      TouchFile ("$TempDir/auto.queue",0);
      system ("cat $TempDir/auto.queue.backup >> $TempDir/auto.queue");
   }
   if ($ProcessDelayedQueue) {
      if ( -s $TempDir . 'delayed.queue' ) {
	 Report ("Appending RPMs marked (inside Interactive Mode) to be installed later.\n");
	 TouchFile ("$TempDir/auto.queue",0);
	 system ("cat $TempDir/delayed.queue >> $TempDir/auto.queue");
	 unlink ($TempDir . 'delayed.queue');
      }
   }
   if ( -s $TempDir . 'auto.queue' ) {
      open (QUEUEFILE,$TempDir . 'auto.queue');
      while (defined($ThisLine = <QUEUEFILE>)) {
	 chomp ($ThisLine);
	 ($Source,$Delete,$Orig,$Flags,$Local,$CopyTo,$RPMOpts,$IgnoreOnFailure,$AutoIgnoreString) = split (/;/,$ThisLine);
	 #Trim any special first-characters if they are there...
	 $Source =~ s/^\*//;
	 $Source =~ s/^\!//;
	 $bn = BaseName ($Source);
	 $AutoData->{$bn}->{'Active'} = 1;
	 $AutoData->{$bn}->{'CopyTo'} = $CopyTo;
	 $AutoData->{$bn}->{'File'} = $Source;
	 $AutoData->{$bn}->{'Delete'} = $Delete;
	 $AutoData->{$bn}->{'Source'} = $Orig;
	 $AutoData->{$bn}->{'Local'} = $Local;
	 $AutoData->{$bn}->{'ErrorString'} = $Flags;
	 $AutoData->{$bn}->{'RPMOpts'} = $RPMOpts;
	 $AutoData->{$bn}->{'IgnoreOnFailure'} = $IgnoreOnFailure;
	 $AutoData->{$bn}->{'AutoIgnoreString'} = $AutoIgnoreString;
      }
      close (QUEUEFILE);
      TouchFile ("$TempDir/auto.queue.backup", 1);
      system ("mv $TempDir/auto.queue $TempDir/auto.queue.backup");
   }
   $NumInstalledThisLoop = 1;
   while ($NumInstalledThisLoop) {
      $NumInstalledThisLoop = 0;
      foreach $ThisPackage (keys %{$AutoData}) {
	 if ($AutoData->{$ThisPackage}->{'Active'}) {
	    $TFile = $TempDir . 'rpm.tmp';
	    $TFile2 = $TempDir . 'rpmout.tmp';
	    $Temp = $AutoData->{$ThisPackage}->{'File'};
	    Get_Remote_File ($AutoData->{$ThisPackage}->{'Source'},$Temp);
	    $RPMOpts = '';
	    $RPMOpts = $AutoData->{$ThisPackage}->{'RPMOpts'};
	    if ($AutoData->{$ThisPackage}->{'ErrorString'} =~ /^1\*/) {
	       $RPMOpts .= ' --nodeps ';
	       Report ("   Using --nodeps...\n");
	    }
	    elsif ($AutoData->{$ThisPackage}->{'ErrorString'} =~ /^2\*/) {
	       $RPMOpts .= ' --force ';
	       Report ("   Using --force...\n");
	    }
	    $Success = '.';
	    Report ('   ' . $ThisPackage . '... ');
	    @NoRemoveList = ();
	    @RenameList = ();
	    `$RPMLocation -U $RPMOpts $Temp > $TFile2 2> $TFile`;
	    if ( -s $TFile) {
	       # If Success is non-null, the RPM was installed successfully.
	       $Success = '';
	       # There were errors...
	       # If error is set to 1, that means that an error occured and the RPM should be placed in
	       # the interactive queue...
	       $Error = 0;
	       @DepsList = ();
	       @ConflictsFileList = ();
	       @ConflictsPackageList = ();
	       open (RPMRUN, $TFile);
	       while (defined($ThisLine = <RPMRUN>)) {
		  chomp ($ThisLine);
		  $ThisLine =~ s/^error: //;
		  if ($Debug) {
		     Report ('DEBUG: STDERR: ' . $ThisLine . "\n");
		  }
		  if ($ThisLine =~ /^failed dependencies:/ ) {
		     $Error = 1;
		  }
		  elsif ($ThisLine =~ /^execution of script failed$/ ) {
		     $Success = ', but script failed.';
		     $Error = 0;
		  }
		  #elsif ($ThisLine =~ /installing package .+ needs .+ on the .+ filesystem/) {
		  # TBD - not enough space error
		  #}
		  elsif (($ThisLine =~ /^package .+ \(which is newer\) is already installed$/ ) or
			 ($ThisLine =~ /^package .+ \(which is newer th.n .+\) is already installed$/ ) ) {
		     Report ("is older than the currently installed one.\n");
		     $AutoData->{$ThisPackage}->{'Active'} = 0;
		     $Error = 0;
		     #Delete File if necessary
		     if ($AutoData->{$ThisPackage}->{'Delete'} ) {
			if (unlink ($AutoData->{$ThisPackage}->{'File'})) {
			   Report ('      Deleted ' . $AutoData->{$ThisPackage}->{'File'} . "\n");
			}
			else {
			   Report ("      Couldn't Delete " . $AutoData->{$ThisPackage}->{'File'} . "\n");
			}
		     }
		  }
		  elsif ($ThisLine =~ /^package .+ is already installed$/ ) {
		     Report ('is already installed.' . "\n");
		     $AutoData->{$ThisPackage}->{'Active'} = 0;
		     $Error = 0;
		     #Delete File if necessary
		     if ($AutoData->{$ThisPackage}->{'Delete'} ) {
			if (unlink ($AutoData->{$ThisPackage}->{'File'})) {
			   Report ('      Deleted ' . $AutoData->{$ThisPackage}->{'File'} . "\n");
			}
			else {
			   Report ("      Couldn't Delete " . $AutoData->{$ThisPackage}->{'File'} . "\n");
			}
		     }
		  }
		  elsif ( ($ThisLine =~ /^failed to open/ ) or
			  ($ThisLine =~ /^cannot open \// ) ) {
		     die "\nERROR: RPM can not open RPM database...\n";
		  }
		  elsif ( $ThisLine =~ s/^cannot open file (.+)$/$1/ ) {
		     $Error = 1;
		     Report ("\nERROR: RPM can not open " . $ThisLine . "\n");
		  }
		  elsif ( $ThisLine =~ s/^warning: (.+ saved as .+)$/      $1/) {
		     $Success = ', but file(s) renamed:';
		     push @RenameList, $ThisLine . "\n";
		     $Error = 0;
		  }
		  elsif ( $ThisLine =~ s/^r[^ ]+ of (.+) failed: No such file or directory$/      $1/) {
		     $Success = ", but file(s) couldn't be removed:";
		     push @NoRemoveList, $ThisLine . "\n";
		     $Error = 0;
		  }
		  elsif ( $ThisLine =~ s/^cannot remove (.+) - directory not empty$/      $1/) {
		     $Success = ", but file(s) couldn't be removed:";
		     push @NoRemoveList, $ThisLine . "\n";
		     $Error = 0;
		  }
		  elsif ($ThisLine =~ /^$/ ) {
		     # Blank Line
		  }
		  elsif ( $ThisLine =~ s/^\s+(.+) is needed by .+$/$1/ ) {
		     push @DepsList,$ThisLine;
		  }
		  elsif ( ($ThisConflict, $ThisOther) = ($ThisLine =~ /^(.+) conflicts with file from (.+)$/)) {
		     $Error = 1;
		     push @ConflictsFileList,$ThisConflict;
		     push @ConflictsPackageList,$ThisOther;
		  }
		  elsif ( ($ThisConflict, $ThisOther) = ($ThisLine =~ /^file (.+) from install of .+ conflicts with file from package (.+)$/)) {
		     $Error = 1;
		     push @ConflictsFileList,$ThisConflict;
		     push @ConflictsPackageList,$ThisOther;
		  }
		  elsif ( $ThisLine =~ /^.+ cannot be installed$/ ) {
		     # Couldn't be installed, but we don't need to do anything here...
		  }
		  else {
		     $Error = 1;
		     $WierdError = $ThisLine;
		     Report ("\nUnknown Error Message From RPM:\n" . $ThisLine . "\n");
		  }
	       }
	       close (RPMRUN);
	       if ($Error) {
		  Report ("failed, but will try again...\n");
		  if (@DepsList) {
		     $AutoData->{$ThisPackage}->{'ErrorString'} = '1';
		     foreach $Temp (@DepsList) {
			$AutoData->{$ThisPackage}->{'ErrorString'} .= ':' . $Temp;
		     }
		  }
		  elsif (@ConflictsFileList) {
		     $AutoData->{$ThisPackage}->{'ErrorString'} = '2';
		     while ($TFile = pop (@ConflictsFileList) ) {
			$Temp = pop (@ConflictsPackageList);
			$AutoData->{$ThisPackage}->{'ErrorString'} .= ':' . $TFile . '(' . $Temp . ')';
		     }
		  }
		  else {
		     $AutoData->{$ThisPackage}->{'ErrorString'} = '3:' . $WierdError;
		     Report ('Unknown Error for ' . $ThisPackage . "\n");
		  }
	       }
	    }
	    unlink ($TFile);
	    if ($Success) {
	       # Installation successful
	       $NumInstalledThisLoop++;
	       $AutoData->{$ThisPackage}->{'Active'} = 0;
	       $AutoData->{$ThisPackage}->{'ErrorString'} = '';
	       if ($AutoData->{$ThisPackage}->{'Local'}) {
		  $Temp = 'Upgraded';
	       }
	       else {
		  $Temp = 'Installed';
	       }
	       Report ($Temp . ' Successfully' . $Success . "\n");
	       foreach $ThisOne (@RenameList, @NoRemoveList) {
		  Report($ThisOne);
	       }
	       if ( -s $TFile2) {
		  Report ('   OUTPUT FROM RPM:' . "\n");
		  open (OUTPUTFILE,$TFile2);
		  while (defined($Temp = <OUTPUTFILE>)) {
		     Report '      ' . $Temp
		      }
		  close (OUTPUTFILE);
	       }
	       # Copy file if necessary...
	       if ($AutoData->{$ThisPackage}->{'CopyTo'} ) {
		  foreach $TFile (split(/:/,$AutoData->{$ThisPackage}->{'CopyTo'})) {
		     unless ($TFile =~ /^FAIL/) {
			$Temp = $AutoData->{$ThisPackage}->{'File'} . ' ' . $TFile;
			unless (`cp $Temp`) {
			   `chmod a+r $Temp`;
			   Report ( '      Copied to ' . $AutoData->{$ThisPackage}->{'CopyTo'} . "\n");
			}
			else {
			   Report ( "      Couldn't copy to " . $AutoData->{$ThisPackage}->{'CopyTo'} . "\n");
			}
		     }
		  }
	       }
	       #Delete File if necessary
	       if ($AutoData->{$ThisPackage}->{'Delete'} ) {
		  if (unlink ($AutoData->{$ThisPackage}->{'File'})) {
		     Report ('      Deleted ' . $AutoData->{$ThisPackage}->{'File'} . "\n");
		  }
		  else {
		     Report ("      Couldn't Delete " . $AutoData->{$ThisPackage}->{'File'} . "\n");
		  }
	       }
	       # Log the installation...
	       open (LOGFILE,'>>' . $TempDir . 'install.log');
	       if ($AutoData->{$ThisPackage}->{'Local'}) {
		  $Temp = BaseName ($AutoData->{$ThisPackage}->{'Local'});
		  print LOGFILE $Date . ' - ' . $Temp . ' -> ';
		  $Temp = BaseName ($AutoData->{$ThisPackage}->{'File'});
		  print LOGFILE $Temp . "\n";
	       }
	       else {
		  $Temp = BaseName ($AutoData->{$ThisPackage}->{'File'});
		  print LOGFILE $Date . ' - Installed ' . $Temp . "\n";
	       }
	       close (LOGFILE);
	    }
	    unlink ($TFile2);
	 }
      }
   }

   # Count the number of packages that still need to be installed
   $LeftCount = 0;
   foreach $ThisPackage (keys %{$AutoData}) {
      if ($AutoData->{$ThisPackage}->{'Active'}) {
	 $LeftCount++;
      }
   }
   if ($LeftCount) {
      Report ( "\n** Failed RPMs **\n\n");
   }

   my $LastTry = "";
   TouchFile ("$TempDir/interactive.queue", 0);
   open (QUEUEFILE,'>>' . $TempDir . 'interactive.queue');
   foreach $ThisPackage (keys %{$AutoData}) {
      if ($AutoData->{$ThisPackage}->{'Active'}) {
	 if ($AutoData->{$ThisPackage}->{'IgnoreOnFailure'}) {
	    # Instead of going into the interactive queue, auto-ignore this package...
	    Add_Auto_Ignore($AutoData->{$ThisPackage}->{'AutoIgnoreString'});
	 }
	 else {
	    print QUEUEFILE $AutoData->{$ThisPackage}->{'File'} . ';' . $AutoData->{$ThisPackage}->{'Delete'} . ';' .
		$AutoData->{$ThisPackage}->{'Source'} . ';' . $AutoData->{$ThisPackage}->{'ErrorString'} . ';' .
		    $AutoData->{$ThisPackage}->{'Local'} . ';' . $AutoData->{$ThisPackage}->{'CopyTo'} . ';' .
			$AutoData->{$ThisPackage}->{'RPMOpts'} . ';' . $AutoData->{$ThisPackage}->{'IgnoreOnFailure'} .
			    ';' . $AutoData->{$ThisPackage}->{'AutoIgnoreString'} . "\n";
	 }
         # Copy file if necessary...
	 if ($AutoData->{$ThisPackage}->{'CopyTo'} ) {
	    foreach $TFile (split(/:/,$AutoData->{$ThisPackage}->{'CopyTo'})) {
	       if ($TFile =~ s/^FAIL//) {
                  # Copy on failure...
		  $Temp = $AutoData->{$ThisPackage}->{'File'} . ' ' . $TFile;
		  unless (`cp $Temp`) {
		     `chmod a+r $Temp`;
		     Report ( '      Copied to ' . $AutoData->{$ThisPackage}->{'CopyTo'} . "\n");
		  }
		  else {
		     Report ( "      Couldn't copy to " . $AutoData->{$ThisPackage}->{'CopyTo'} . "\n");
		  }
	       }
	    }
	 }
	 if ( ($AutoData->{$ThisPackage}->{'ErrorString'}) =~ /^1:/ ) {
	    # Missing Dependencies...
	    Report ( '   ' . $ThisPackage . ' is missing dependencies:' . "\n");
           # One last try - put all failed dependencies on one line to rpm -U
           $LastTry = $LastTry." $AutoData->{$ThisPackage}->{'File'} ";
	    foreach $Temp ( split (/:/, $AutoData->{$ThisPackage}->{'ErrorString'})) {
	       unless ($Temp eq '1') {
		  Report ( '      ' . $Temp . "\n");
	       }
	    }
           
	 }
	 elsif ( ($AutoData->{$ThisPackage}->{'ErrorString'}) =~ /^2:/ ) {
	    # Conflicts...
	    Report ( '   ' . $ThisPackage . ' has conflicting files:' . "\n");
	    foreach $Temp ( split (/:/, $AutoData->{$ThisPackage}->{'ErrorString'})) {
	       unless ($Temp eq '2') {
		  $Temp =~ s/^(.+)\((.+)\)$/      $1 is owned by $2/;
		  Report ($Temp . "\n");
	       }
	    }
	 }
      }
   }
   if ($LastTry ne "") {
     Report ('One Last Try for ' . $LastTry . "\n");
    `$RPMLocation -U $RPMOpts $LastTry `;
   # Check for success & do logging here...
      }

   close (QUEUEFILE);
   unlink ($TempDir . 'auto.queue.backup');
}

##############################################################################
# Apply Mode (autorpm --apply)
##############################################################################

sub Apply_Mode {
   my ($StillMore,$Source,$Delete,$Orig,$Flags,$ThisLine,$bn,$Temp,$ThisOne,$CurrItem,$MessageText,@Output,$Local);
   my ($SaveChanges,$DoneInstallOnce,$DoneMenuLoop,%LocalPackages,$SubMenuDone,$ShowBox,$DoRPMString,@TempList,$CopyTo,$RPMOpts,$IgnoreOnFailure,$AutoIgnoreString);

   # Backup queue in case program abnormally exits...
   if ( -s $TempDir . 'interactive.queue.backup' ) {
      print "\nInteractive Install didn't complete successfully last time\n";
      WaitEnter ('Appending backup queue.  Press ENTER to continue.');
      TouchFile ("$TempDir/interactive.queue", 0);
      system ("cat $TempDir/interactive.queue.backup >> $TempDir/interactive.queue");
   }
   TouchFile ("$TempDir/interactive.queue.backup", 1);
   system ("cp $TempDir/interactive.queue $TempDir/interactive.queue.backup");

   # Get installed package list
   print "\nGetting list of installed packages...";
   @TempList = `$RPMLocation -qa --queryformat '%{NAME}-%{VERSION}-%{RELEASE}\n'`;
   die ('ERROR: Error executing ' . $RPMLocation . "\n") unless ($? == 0);
   chomp(@TempList);
   foreach $Temp (@TempList) {
      $bn = StripVersion ($Temp);
      $LocalPackages{$bn} = $Temp;
   }
   print " Done.\n\n";

   # Loop while there is more to do...
   $SaveChanges = 1;
   $StillMore = 1;
   $DoneInstallOnce = 0;
   $TryAuto = 1;
   while ($StillMore) {
      open (QUEUEFILE,$TempDir . 'interactive.queue');
      while (defined($ThisLine = <QUEUEFILE>)) {
	 chomp ($ThisLine);
	 ($Source,$Delete,$Orig,$Flags,$Local,$CopyTo,$RPMOpts,$IgnoreOnFailure,$AutoIgnoreString) = split (/;/,$ThisLine);
	 $bn = BaseName ($Source);
	 print 'Processing queue file: ' . $bn . "\n";
	 # If a local file, make sure it exists first...
	 if ( (-s $Source) or ($Orig) ) {
	    $InterData->{$bn}->{'Active'} = 1;
	    $InterData->{$bn}->{'CopyTo'} = $CopyTo;
	    $InterData->{$bn}->{'Queue_Later'} = 0;
	    if ($Source =~ s/^\*//) {
	       $InterData->{$bn}->{'Install'} = 1;
	    }
	    $InterData->{$bn}->{'File'} = $Source;
	    $InterData->{$bn}->{'Delete'} = $Delete;
	    $InterData->{$bn}->{'Source'} = $Orig;
	    $InterData->{$bn}->{'Local'} = $Local;
	    $InterData->{$bn}->{'RPMOpts'} = $RPMOpts;
	    $InterData->{$bn}->{'IgnoreOnFailure'} = $IgnoreOnFailure;
	    $InterData->{$bn}->{'AutoIgnoreString'} = $AutoIgnoreString;
	    $Temp = $Flags;
	    $Temp =~ s/^([0123456789]\**):.*$/$1/;
	    $InterData->{$bn}->{'Error_Code'} = $Temp;
	    $Temp = $Flags;
	    $Temp =~ s/^[0123456789]\**:(.*)$/$1/;
	    $InterData->{$bn}->{'Error_Data'} = $Temp;
	    unless ($InterData->{$bn}->{'Error_Data'}) {
	       $InterData->{$bn}->{'Error_Data'} = '';
	    }
	    $Temp = StripVersion ($bn);
	    @{$InterData->{$bn}->{'Local_Installed'}} = ();
	    foreach $ThisOne (keys %LocalPackages) {
	       if ($Temp eq $ThisOne) {
		  push @{$InterData->{$bn}->{'Local_Installed'}}, $LocalPackages{$ThisOne};
	       }
	    }
	 }
	 else {
	    $InterData->{$bn}->{'Active'} = 1;
	 }
      }
      close (QUEUEFILE);
      unlink ($TempDir . 'interactive.queue');

      $DoneMenuLoop = 0;
      unless ($DoneMenuLoop) {
	 # This is the loop to do the UI menu stuff
	 # on exit, these need to be set:
	 # $StillMore > 0 if you want to re-enter the menu...
	 # $TryAuto > 0 if you want to install progs markes as such...
	 # $SaveChanges > 0 if you want to save changes.  NOTE: This should
	 #                  only be zero if $DoneInstallOnce = 0 too...
	 # This loop will be re-entered *only* if $StillMore > 0 and if there
	 # are still some RPMs that need to be installed or if there were errors.

	 $MenuText = '';
	 #print "\nGenerating Menu...\n";
	 foreach $ThisOne (sort keys %{$InterData}) {
	    if ($InterData->{$ThisOne}->{'Active'}) {
	       if (@{$InterData->{$ThisOne}->{'Local_Installed'}}) {
		  # Upgrading...
		  if ($InterData->{$ThisOne}->{'Install'}) {
		     if ($InterData->{$ThisOne}->{'Delete'}) {
			$Temp = 'Upgrade Now and Delete';
		     }
		     else {
			$Temp = 'Upgrade Now';
		     }
		  }
		  elsif ($InterData->{$ThisOne}->{'Queue_Later'}) {
		     if ($InterData->{$ThisOne}->{'Delete'}) {
			$Temp = 'Upgrade Later and Delete';
		     }
		     else {
			$Temp = 'Upgrade Later';
		     }
		  }
		  else {
		     $Temp = 'Do not Upgrade';
		  }
	       }
	       else {
		  # Installing...
		  if ($InterData->{$ThisOne}->{'Install'}) {
		     if ($InterData->{$ThisOne}->{'Delete'}) {
			$Temp = 'Install Now and Delete';
		     }
		     else {
			$Temp = 'Install Now';
		     }
		  }
		  elsif ($InterData->{$ThisOne}->{'Queue_Later'}) {
		     if ($InterData->{$ThisOne}->{'Delete'}) {
			$Temp = 'Install Later and Delete';
		     }
		     else {
			$Temp = 'Install Later';
		     }
		  }
		  else {
		     $Temp = 'Do not Install';
		  }
	       }
	       if ($InterData->{$ThisOne}->{'Error_Code'} =~ /^1\*$/) {
		  $Temp .= ', use --nodeps';
	       }
	       elsif ($InterData->{$ThisOne}->{'Error_Code'} =~ /^2\*$/) {
		  $Temp .= ', use --force';
	       }
	       elsif ($InterData->{$CurrItem}->{'Error_Code'}) {
		  $Temp .= ', Previous Errors';
	       }
	       $MenuText .= ' ' . $ThisOne . ' "(' . $Temp . ')" ';
	    }
	 }

	 $MenuText .= ' Apply "(Perform Actions Now)" Exit "(Save Changes)" ';
	 unless ($DoneInstallOnce) {
	    $MenuText .= ' Quit "(Discard Changes)" ';
	 }

	 #system('/usr/bin/dialog --menu "AutoRPM Interactive Mode" 22 70 15 ' . $MenuText . ' 2>' . $TempDir . 'menu.tmp');
	 system($MenuProg . ' --menu "AutoRPM Interactive Mode" 22 70 13 ' . $MenuText . ' 2>' . $TempDir . 'menu.tmp');
	 if ($?) {
	    # Exit...
	    $StillMore = 0;
	    $TryAuto = 0;
	    unless ($DoneInstallOnce) {
	       $SaveChanges = 0;
	    }
	    unlink ($TempDir . 'menu.tmp');
	 }
	 else {
	    # Process results of menu...
	    open (MENUFILE, $TempDir . 'menu.tmp');
	    $CurrItem = <MENUFILE>;
	    chomp($CurrItem);
	    close (MENUFILE);
	    unlink ($TempDir . 'menu.tmp');
	    if ($CurrItem eq 'Apply') {
	       # Don't do anything here...
	       # Let things pan out below...
	       $TryAuto = 1;
	    }
	    elsif ($CurrItem eq 'Quit') {
	       # Quit and don't save changes...
	       $StillMore = 0;
	       $TryAuto = 0;
	       unless ($DoneInstallOnce) {
		  $SaveChanges = 0;
	       }
	    }
	    elsif ($CurrItem eq 'Exit') {
	       # Quit and save changes...
	       $StillMore = 0;
	       $TryAuto = 0;
	       $SaveChanges = 1;
	    }
	    else {
	       # Go to a submenu...
	       $TryAuto = 0;
	       $SubMenuDone = 0;
	       until ($SubMenuDone) {
		  $MenuText = ' Return "(To Main Menu)" ';
		  unless ($InterData->{$CurrItem}->{'Queue_Later'}) {
		     if (@{$InterData->{$CurrItem}->{'Local_Installed'}}) {
			# Upgrading
			if ($InterData->{$CurrItem}->{'Install'}) {
			   $MenuText .= ' Toggle-Install "(Currently Yes, Upgrade Now)" ';
			}
			else {
			   $MenuText .= ' Toggle-Install "(Currently No, Do Not Upgrade Now)" ';
			}
		     }
		     else {
			# Installing
			if ($InterData->{$CurrItem}->{'Install'}) {
			   $MenuText .= ' Toggle-Install "(Currently Yes, Install Now)" ';
			}
			else {
			   $MenuText .= ' Toggle-Install "(Currently No, Do Not Install Now)" ';
			}
		     }
		  }
		  if ($InterData->{$CurrItem}->{'Error_Code'}) {
		     # There have been errors w/ this package...
		     $MenuText .= ' View-Errors "(View Previous Installation Errors)" ';
		     if ($InterData->{$CurrItem}->{'Error_Code'} =~ /^1\*$/) {
			$MenuText .= ' Use-NoDeps "(Currently Yes, use --nodeps to install)" ';
		     }
		     elsif ($InterData->{$CurrItem}->{'Error_Code'} =~ /^1$/) {
			$MenuText .= ' Use-NoDeps "(Currently No, don\'t use --nodeps to install)" ';
		     }
		     elsif ($InterData->{$CurrItem}->{'Error_Code'} =~ /^2\*$/) {
			$MenuText .= ' Use-Force "(Currently Yes, use --force to install)" ';
		     }
		     elsif ($InterData->{$CurrItem}->{'Error_Code'} =~ /^2$/) {
			$MenuText .= " Use-Force \"(Currently No, don\'t use --force to install)\" ";
		     }
		  }
		  else {
		     unless ($InterData->{$CurrItem}->{'Install'}) {
			if (@{$InterData->{$CurrItem}->{'Local_Installed'}}) {
			   # Upgrading
			   if ($InterData->{$CurrItem}->{'Queue_Later'}) {
			      $MenuText .= ' Toggle-Later "(Currently Yes, Upgrade Later)" ';
			   }
			   else {
			      $MenuText .= ' Toggle-Later "(Currently No, Do Not Upgrade Later)" ';
			   }
			}
			else {
			   # Installing
			   if ($InterData->{$CurrItem}->{'Queue_Later'}) {
			      $MenuText .= ' Toggle-Later "(Currently Yes, Install Later)" ';
			   }
			   else {
			      $MenuText .= ' Toggle-Later "(Currently No, Do Not Install Later)" ';
			   }
			}
		     }
		  }

		  $MenuText .= ' Remove "(Remove From Queue)" ';
		  $MenuText .= ' AutoRPM-Info "(Internal Info)" Package-Info "(Info From RPM)" List-Files "(Show RPM File List)" List-Deps "(Show Dependencies)" ';
		  $MenuText .= ' Show-Provides "(What RPM Provides)" Show-Scripts "(Show Install Scripts)" ';
		  $MenuText .= ' Check-Sig "(Check PGP Signature)" ';
		  system("$MenuProg --menu $CurrItem 22 70 13 " . $MenuText . ' 2>' . $TempDir . 'menu.tmp');
		  if ($?) {
		     $DoRPMString = '';
		     $ShowBox = 0;
		     $SubMenuDone = 1;
		  }
		  else {
		     open (MENUFILE, $TempDir . 'menu.tmp');
		     $ThisLine = <MENUFILE>;
		     chomp($ThisLine);
		     close (MENUFILE);
		     unlink ($TempDir . 'menu.tmp');

		     $ShowBox = 1;
		     $MessageText = '';
		     if ($ThisLine eq 'Return') {
			$DoRPMString = '';
			$ShowBox = 0;
			$SubMenuDone = 1;
		     }
		     elsif ($ThisLine eq 'AutoRPM-Info') {
			$DoRPMString = '';
			$MessageText = 'Internal AutoRPM Info for ' . $CurrItem . "\n\n";
			$MessageText .= "Filename:\n" . $InterData->{$CurrItem}->{'File'} . "\n\n";
			if ($InterData->{$CurrItem}->{'Local'}) {
			   if ($InterData->{$CurrItem}->{'Local'} =~ m=^/=) {
			      # Local file was compared against...
			      $MessageText .= "Compared Against File:\n" . $InterData->{$CurrItem}->{'Local'} . "\n\n";
			   }
			   else {
			      # Local installed package was compared against...
			      $MessageText .= 'Compared Against Installed Package: ' . $InterData->{$CurrItem}->{'Local'} . "\n\n";
			   }
			}
			if ($InterData->{$CurrItem}->{'Source'}) {
			   $MessageText .= "\nOriginal Location:\n" . $InterData->{$CurrItem}->{'Source'} . "\n\n";
			}
			if (@{$InterData->{$CurrItem}->{'Local_Installed'}}) {
			   $MessageText .= "\nVersion(s) Already Installed:\n";
			   foreach $Temp (@{$InterData->{$CurrItem}->{'Local_Installed'}}) {
			      $MessageText .= '   ' . $Temp . "\n";
			   }
			}
		     }
		     elsif ($ThisLine eq 'View-Errors') {
			$DoRPMString = '';
			if ($InterData->{$CurrItem}->{'Error_Code'} =~ m/^1/ ) {
			   # Missing Dependencies...
			   $MessageText = "Missing Dependencies:\n";
			   foreach $Temp ( split (/:/, $InterData->{$CurrItem}->{'Error_Data'})) {
			      $MessageText .= ( '   ' . $Temp . "\n" );
			   }
			}
			elsif ($InterData->{$CurrItem}->{'Error_Code'} =~ m/^2/ ) {
			   # Conflicts...
			   $MessageText = "Conflicting Files:\n";
			   foreach $Temp ( split (/:/, $InterData->{$CurrItem}->{'Error_Data'})) {
			      $Temp =~ s/^(.+)\((.+)\)$/   $1 is owned by $2/;
			      $MessageText .= ( $Temp . "\n" );
			   }
			}
			elsif ($InterData->{$CurrItem}->{'Error_Code'} =~ m/^3/ ) {
			   # Unknown Error...
			   $MessageText = "Unknown Error:\n";
			   print $InterData->{$CurrItem}->{'Error_Data'} . "\n";
			}
		     }
		     elsif ($ThisLine eq 'Use-NoDeps') {
			$DoRPMString = '';
			$ShowBox = 0;
			if ($InterData->{$CurrItem}->{'Error_Code'} =~ /^1\*$/) {
			   $InterData->{$CurrItem}->{'Error_Code'} = '1';
			}
			elsif ($InterData->{$CurrItem}->{'Error_Code'} =~ /^1$/) {
			   $InterData->{$CurrItem}->{'Error_Code'} = "1*";
			}
		     }
		     elsif ($ThisLine eq 'Use-Force') {
			$DoRPMString = '';
			$ShowBox = 0;
			if ($InterData->{$CurrItem}->{'Error_Code'} =~ /^2\*$/) {
			   $InterData->{$CurrItem}->{'Error_Code'} = '2';
			}
			elsif ($InterData->{$CurrItem}->{'Error_Code'} =~ /^2$/) {
			   $InterData->{$CurrItem}->{'Error_Code'} = "2*";
			}
		     }
		     elsif ($ThisLine eq 'Toggle-Install') {
			$DoRPMString = '';
			$ShowBox = 0;
			$InterData->{$CurrItem}->{'Install'} = not $InterData->{$CurrItem}->{'Install'};
			$SubMenuDone = 1;
		     }
		     elsif ($ThisLine eq 'Toggle-Later') {
			$DoRPMString = '';
			$ShowBox = 0;
			$InterData->{$CurrItem}->{'Queue_Later'} = not $InterData->{$CurrItem}->{'Queue_Later'};
		     }
		     elsif ($ThisLine eq 'Remove') {
			Add_Auto_Ignore($InterData->{$CurrItem}->{'AutoIgnoreString'});
			$InterData->{$CurrItem}->{'Active'} = 0;
			$DoRPMString = '';
			$ShowBox = 0;
			$SubMenuDone = 1;
			if ($InterData->{$CurrItem}->{'Delete'} == 1) {
			   unlink ($InterData->{$CurrItem}->{'File'});
			}
		     }
		     elsif ($ThisLine eq 'Package-Info') {
			$DoRPMString = ' -i ';
		     }
		     elsif ($ThisLine eq 'List-Files') {
			$DoRPMString = ' -l ';
		     }
		     elsif ($ThisLine eq 'List-Deps') {
			$DoRPMString = ' --requires ';
		     }
		     elsif ($ThisLine eq 'Show-Provides') {
			$DoRPMString = ' --provides ';
		     }
		     elsif ($ThisLine eq 'Show-Scripts') {
			$DoRPMString = ' --scripts ';
		     }
		     elsif ($ThisLine eq 'Check-Sig') {
			Get_Remote_File ($InterData->{$CurrItem}->{'Source'},$InterData->{$CurrItem}->{'File'});
			$DoRPMString = '';
			$Temp = $InterData->{$CurrItem}->{'File'};
			@Output = `$RPMLocation --checksig $Temp`;
			if ($?) {
			   die "ERROR: Command Failed:\n" . $RPMLocation . ' --checksig ' . $Temp . "\n";
			}
			else {
			   $ShowBox = 1;
			   $MessageText = join('', @Output);
			}
		     }
		     if ($DoRPMString) {
			Get_Remote_File ($InterData->{$CurrItem}->{'Source'},$InterData->{$CurrItem}->{'File'});
			$Temp = $InterData->{$CurrItem}->{'File'};
			@Output = `$RPMLocation -q $DoRPMString -p $Temp`;
			if ($?) {
			   die "ERROR: Command Failed:\n" . $RPMLocation . ' -q ' . $DoRPMString . ' -p ' . $Temp . "\n";
			}
			else {
			   $ShowBox = 1;
			   $MessageText = join('', @Output);
			}
		     }
		     if ($ShowBox) {
			#system("/usr/bin/dialog --msgbox '" . $MessageText . "' 22 80" );
			open (MSGFILE,'>' . $TempDir . 'tmp.msg');
			print MSGFILE $MessageText;
			close (MSGFILE);
			system("$DialogProg --textbox " . $TempDir . 'tmp.msg 22 80' );
			unlink ($TempDir . 'tmp.msg');
		     }
		  }
	       }
	    }
	 }
      }

      # Fill Delayed Queue and execute...
      TouchFile ("$TempDir/delayed.queue", 0);
      open (QUEUEFILE,'>>' . $TempDir . 'delayed.queue');
      foreach $ThisOne (keys %{$InterData}) {
	 if ( ($InterData->{$ThisOne}->{'Queue_Later'}) and
	      ($InterData->{$ThisOne}->{'Active'}) ) {
	    print QUEUEFILE $InterData->{$ThisOne}->{'File'} . ';' . $InterData->{$ThisOne}->{'Delete'} .
		';' . $InterData->{$ThisOne}->{'Source'} . ';' . $InterData->{$ThisOne}->{'Error_Code'} .
		    ':' . $InterData->{$ThisOne}->{'Error_Data'} . ';' . $InterData->{$ThisOne}->{'Local'} . 
			';' . $InterData->{$ThisOne}->{'CopyTo'} . ';' . $InterData->{$ThisOne}->{'RPMOpts'} .
			    ';' . $InterData->{$ThisOne}->{'IgnoreOnFailure'} . ';' . 
				$InterData->{$ThisOne}->{'AutoIgnoreString'} . "\n";
	    $InterData->{$ThisOne}->{'Active'} = 0;
	    $DoneInstallOnce++;
	 }
      }
      close (QUEUEFILE);

      if ($TryAuto) {
	 # Fill Auto Queue and execute...
	 TouchFile ("$TempDir/auto.queue", 0);
	 open (QUEUEFILE,'>>' . $TempDir . 'auto.queue');
	 foreach $ThisOne (keys %{$InterData}) {
	    if ( ($InterData->{$ThisOne}->{'Install'}) and
		 ($InterData->{$ThisOne}->{'Active'}) ) {
	       print QUEUEFILE $InterData->{$ThisOne}->{'File'} . ';' . $InterData->{$ThisOne}->{'Delete'} .
		   ';' . $InterData->{$ThisOne}->{'Source'} . ';' . $InterData->{$ThisOne}->{'Error_Code'} .
		       ':' . $InterData->{$ThisOne}->{'Error_Data'} . ';' . $InterData->{$ThisOne}->{'Local'} .
			   ';' . $InterData->{$ThisOne}->{'CopyTo'} . ';' . $InterData->{$ThisOne}->{'RPMOpts'} .
			       ';' . $InterData->{$ThisOne}->{'IgnoreOnFailure'} . ';' . 
				   $InterData->{$ThisOne}->{'AutoIgnoreString'} . "\n";
	       $InterData->{$ThisOne}->{'Active'} = 0;
	       $DoneInstallOnce++;
	    }
	 }
	 close (QUEUEFILE);
	 $ForcePrint = 1;
	 Start_Report();
	 Do_Auto_Queue();
	 End_Report();
	 WaitEnter ("\nPress ENTER to continue...");
      }

      if ($StillMore) {
	 # Check to see if there are any more active items in the queue
	 $StillMore = 0;
	 foreach $ThisOne (keys %{$InterData}) {
	    if ($InterData->{$ThisOne}->{'Active'}) {
	       $StillMore++;
	    }
	 }
	 if ( -s $TempDir . 'interactive.queue') {
	    $StillMore++;
	 }
      }
   }
   if ($SaveChanges) {
      print "\nSaving changes...\n";
      TouchFile ("$TempDir/interactive.queue", 0);
      open (QUEUEFILE,'>>' . $TempDir . 'interactive.queue');
      foreach $ThisOne (keys %{$InterData}) {
	 if ($InterData->{$ThisOne}->{'Active'}) {
	    if ($InterData->{$ThisOne}->{'Install'}) {
	       $InterData->{$ThisOne}->{'File'} = "*" . $InterData->{$ThisOne}->{'File'};
	    }
	    print QUEUEFILE $InterData->{$ThisOne}->{'File'} . ';' . $InterData->{$ThisOne}->{'Delete'} .
		';' . $InterData->{$ThisOne}->{'Source'} . ';' . $InterData->{$ThisOne}->{'Error_Code'} .
		    ':' . $InterData->{$ThisOne}->{'Error_Data'} . ';' . $InterData->{$ThisOne}->{'Local'} .
			';' . $InterData->{$ThisOne}->{'CopyTo'} . ';' . $InterData->{$ThisOne}->{'RPMOpts'} . ';' . 
			    $InterData->{$ThisOne}->{'IgnoreOnFailure'} . ';' .
				$InterData->{$ThisOne}->{'AutoIgnoreString'} . "\n";
	 }
      }
      close (QUEUEFILE);
      unlink ($TempDir . 'interactive.queue.backup');
   }
   else {
      print "\nNot saving changes...\n";
      TouchFile ("$TempDir/interactive.queue", 1);
      system ("mv $TempDir/interactive.queue.backup $TempDir/interactive.queue");
   }
}

##############################################################################
# Auto-Ignore Functions
##############################################################################

sub Read_Auto_Ignore {
   if (open (IGNOREFILE,$TempDir . 'auto-ignore')) {
      # Get Auto-Ignore list...
      #Inform ('Getting Auto-Ignore list... ');
      @AutoIgnore = <IGNOREFILE>;
      close (IGNOREFILE);
      chomp(@AutoIgnore);
      #Inform ("Done.\n\n");
   }
}

sub Test_Auto_Ignore($$) {
   my $RPM1 = $_[0];
   if ($Debug) {
      @DebugTempList = (grep (/^\Q$RPM1\E$/,@AutoIgnore));
      if (@DebugTempList) {
	 Report ('DEBUG: Auto-Ignoring ' . $RPM1 . "\n");
	 return (1);
      }
      return (0);
   }
   return (grep (/^\Q$RPM1\E$/,@AutoIgnore));
}

sub Add_Auto_Ignore($$) {
   my $RPM1 = $_[0];
   open (IGNOREFILE,'>>' . $TempDir . 'auto-ignore');
   print IGNOREFILE $RPM1 . "\n";
   close (IGNOREFILE);
}

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

# Get arguments
$Usage = 0;
$Help = 0;
$AutoFTP = '';
$AutoDir = '';
$Inter = 0;
$OldInter = 0;
$GetVersion = 0;
$ForcePrint = 0;
$FlushMode = 0;
$ApplyOnly = 0;
$VersionCheckMode = 0;
$StartDelay = 0;
GetOptions ( 'c|config=s' => \$ConfigFile,
	     'a|apply'    => \$Apply,
	     'i|interactive'    => \$Inter,
	     'o|oldapply'    => \$OldInter,
	     'e|debug'    => \$Debug,
	     'p|print'    => \$ForcePrint,
	     'l|flush'    => \$FlushMode,
	     'v|version'    => \$GetVersion,
	     't|vtest'    => \$VersionCheckMode,
	     'applyonly' => \$ApplyOnly,
	     'f|ftp=s'    => \$AutoFTP,
	     'd|dir=s'    => \$AutoDir,
	     'delay=i'  => \$StartDelay,
	     'h|help'     => \$Help,
	     'u|usage'    => \$Usage
	     ) or Usage();
($Usage or $Help) and Usage();

sub StartInteractiveMode {
   if ($Apply) {
      if (-x '/usr/sbin/autorpm-apply') {
	 # Go to new apply mode
	 system("/usr/sbin/autorpm-apply $ConfigFile");
      }
      else {
	 # Didn't find the new apply mode, so run old one...
	 $OldInter = 1;
      }
   }
   if ($OldInter) {
      # Go to old Apply_Mode
      $ProcessDelayedQueue = 0;
      $GetTempDirOnly = 1;
      Read_Config ($ConfigFile);
      Apply_Mode();
   }
}

if ($GetVersion) {
   print 'AutoRPM Version ' . $Version . ', created ' . $VDate . "\n";
}
elsif ($FlushMode) {
   $TempDir = Check_Dir($TempDir);
   $GetTempDirOnly = 1;
   Read_Config ($ConfigFile);
   `rm $TempDir/*`;
}
elsif ($VersionCheckMode) {
   $Debug = 1;
   print "This mode will test the version comparison code.\n";
   print "Just type CTRL-C to quit...\n\n";
   while (1) {
      print 'Type in filename of package one: ';
      $RPM1 = <STDIN>;
      print 'Type in filename of package two: ';
      $RPM2 = <STDIN>;
      ($Name1, $Version1, $Arch1) = RPM_Split_Name($RPM1);
      ($Name2, $Version2, $Arch2) = RPM_Split_Name($RPM2);
      print "Package #1:\n   Name: $Name1\n   Version: $Version1\n   Arch: $Arch1\n";
	  print "Package #2:\n   Name: $Name2\n   Version: $Version2\n   Arch: $Arch2\n";
	      $Result = RPM_Compare_Version ($Version1, $Version2);
      if ($Result < 0) {
	 print "Package two is newer.\n\n";
      }
      if ($Result > 0) {
	 print "Package one is newer.\n\n";
      }
      if ($Result == 0) {
	 print "Package one is the same as package two.\n\n";
      }
   }
}
else {
   if ($Apply or $Inter) {
      $Apply = 1;
   }

   # This tells Read_Config to only read the tempdir, debug, hostname, etc...
   $GetTempDirOnly = 0;

   $HostName = `hostname`;
   chomp ($HostName);

   # By default, display the AutoQueueReport to the screen
   $AutoQueueReport = 'PRINT';

   if ( ($AutoFTP) or ($AutoDir) ) {
      $ProcessDelayedQueue = 0;
      $GetTempDirOnly = 1;
      Read_Config ($ConfigFile);
      $TempDir = Check_Dir($TempDir);
      if ( -s $TempDir . 'interactive.queue' ) {
	 print "\n\nThere are currently files in the interactive queue.\n";
	 print "You should probably clear out the queue before you proceed.\n";
	 print "You can quit by hitting CTRL-C and running 'autorpm --apply'\n\n";
	 WaitEnter ('Hit ENTER to continue or CTRL-C to cancel');
      }
      Setup_Auto_Mode();
      $ForcePrint = 1;
      Start_Report();
      Process_Local();
      End_Report();
      $InterTotal = 0;
      open (QUEUEFILE,$TempDir . 'interactive.queue');
      while (defined($ThisLine = <QUEUEFILE>)) {
	 $InterTotal++;
      }
      close (QUEUEFILE);
      if ($InterTotal) {
	 WaitEnter ('Hit ENTER to start Interactive Mode');
	 $Apply = 1;
	 StartInteractiveMode();
      }
   }
   else {
      $TempDir = Check_Dir($TempDir);
      if ($Apply) {
	 StartInteractiveMode();
      }
      elsif ($OldInter) {
	 StartInteractiveMode();
      }
      elsif ($ApplyOnly) {
	 # Only do auto queue...
	 $ProcessDelayedQueue = 0;
	 $GetTempDirOnly = 1;
	 Read_Config ($ConfigFile);
	 $ForcePrint = 1;
	 Start_Report();
	 Do_Auto_Queue();
	 End_Report();
      }
      else {
	 $ProcessDelayedQueue = 1;
	 Read_Config ($ConfigFile);
	 foreach $ConfigDir (@ConfigDirs) {
	    if (-d $ConfigDir) {
	       # If defined, a configuration directory has been specified and
	       # those files need to be processed
	       opendir (CONFIGDIR, $ConfigDir) or die "Directory $ConfigDir not found!\n";
	       while ($ThisDir = readdir(CONFIGDIR)) {
		  unless ((-d $ConfigDir . '/' . $ThisDir) or ($ThisDir =~ /~$/)) {
		     push (@MoreConfigs, "$ConfigDir/$ThisDir");
		  }
	       }
	       closedir (CONFIGDIR);
	    }
	 }
	 foreach $MoreConfig (@ConfigFiles, @MoreConfigs) {
	    if (-f $MoreConfig) {
	       Read_Config ($MoreConfig);
	    }
	 }
	 Check_Queues;
	 Mail_Reports;
      }
   }
}

exit (0);
