#!/usr/bin/perl -w
#

use strict;
use LWP::UserAgent;
use Getopt::Long;
use Digest::MD5 qw(md5_hex);

###########################################################################
## Config
###########################################################################
my $SOURCES = "/etc/apt/sources.list";
my $PACKAGEDIR = "/var/lib/apt/lists";
my $PDIFFBASE = "http://people.debian.org/%7Eblade/du";
my $VERBOSE = 0;
my $DRYRUN = 0;
my $DSELNORUN = 0;
my $tdir = `mktemp -d`;

my %optctl = ("sources=s" => \$SOURCES,
              "packagedir=s" => \$PACKAGEDIR,
              "pdiffbase=s" => \$PDIFFBASE,
              "verbose!" => \$VERBOSE,
              "dryrun!" => \$DRYRUN,
              "no-dselect!" => \$DSELNORUN,
	      "help" => \&usage);

###########################################################################
## Program
###########################################################################

## Path security.
$ENV{PATH} = "/bin:/usr/bin:/sbin:/usr/sbin";

sub main {
   my $failed;
   chomp $tdir;
   chdir $tdir;
   #print "WO? $tdir\n";

   GetOptions(%optctl) or &usage;

   die "must have write access to packages $PACKAGEDIR" if !( (-w $PACKAGEDIR) || $DRYRUN);
   my $ARCH = `dpkg --print-architecture`;
   chomp $ARCH;

   ##
   ## Read sources.list
   open(SOURCES, $SOURCES) or die("open($SOURCES): $!");
   my @sources;
   my $ua = LWP::UserAgent->new;
   $ua->timeout(10);
   $ua->env_proxy;
   my $md5 = Digest::MD5->new;

   while(<SOURCES>) {
      chomp;
      s/#.*//;
      s/^\s+//;
      next unless /^deb(-src)?\s+(\S+?)\/?\s+(\S+)\s+(.*)$/;
      my $type = $1 ? "Sources" : "Packages";
      my $arch = $1 ? "source" : "binary-$ARCH";

      for(split /\s+/, $4) {
         my $source = "$2/dists/$3/$_/$arch/$type";
         push @sources, $source;
      }
   }

   ## Iterate over sources
   SOURCE:
   for my $source (@sources) {
      my $pkgfile = $source;
      $pkgfile =~ s[/][_]g;
      $pkgfile =~ s[^\w+:__][$PACKAGEDIR/];
      ($pkgfile) = $pkgfile =~ m/^([^;|&<>{}\[\]]+)$/;
#      my $pdiffdir = $source;
#      $pdiffdir =~ s/\/(Packages|Sources)$// or next;
#      my $type = $1;
#      $pdiffdir =~ s[^.*/dists/][/dists/];
#      $pdiffdir = $PDIFFBASE.$pdiffdir;
#      print "Considering $pdiffdir/$type\n" if $VERBOSE; 

      ##  if file exists
      if(-e $pkgfile) {
         $md5->reset;
         open(my $fh, "<$pkgfile");
         $md5->addfile($fh);
         close($fh);
         my $curSum=$md5->hexdigest();

#         print "$pkgfile\n" if $VERBOSE;
#	 print "$PDIFFBASE/$curSum.diff.bz2\n" if $VERBOSE;
         my $response = $ua->get("$PDIFFBASE/$curSum.diff.bz2",
         ":content_file"   => "$tdir/$curSum.diff.bz2" );

         if ($response->is_success) {
            my $mtime;
#	    print "$PDIFFBASE/$curSum.diff.bz2 downloaded\n" if $VERBOSE;
	    print "hit\n" if $VERBOSE;
            unlink("$tdir/tmp");
            link($pkgfile, "$tdir/tmp") 
            || symlink($pkgfile, "$tdir/tmp") 
            || die "Oi, filesystem problems...\n";
            my $ret=0;
            # provisoric
            $ret += system "bunzip2 $curSum.diff.bz2";
            $ret += system "perl", "-pe", 's/^(\+\+\+|---)\s+(\S+)/$1 tmp/;', "-i", "$tdir/$curSum.diff";
            $ret += system "patch -p0 ".($DRYRUN?" --dry-run":"")." < $tdir/$curSum.diff >/dev/null 2>&1";
            if($ret) { print STDERR "Error patching $pkgfile.\n" if
	    $VERBOSE; next;} else { print "patched ok\n" if
	    $VERBOSE;}

            # чтото там не то, но хватит мозги ебать
#            open(my $patch, "| patch -p0 ".($DRYRUN?" --dry-run":""));
#            open(my $diff, "bzcat $tdir/$curSum.diff.bz2 |");
#            open(my $copy, ">/tmp/copy");
#            while(<$diff>) {
#               s/^(\+\+\+|---)\s+(\S+)/$1 tmp/;
#               print $copy $_;
#               print $patch $_;
#            }
#            my $ret=0;
#            close($diff);
#            $ret += ($? >> 8);
#            if($ret) { print STDERR "Error reading the diff file.\n" if $VERBOSE; next;}
#            close($patch);
#            $ret += ($? >> 8);
#            if($ret) { print STDERR "Error patching $pkgfile.\n" if $VERBOSE; next;}
#            next if $ret;
            if(!$DRYRUN) {
               system "cp", "$tdir/tmp", $pkgfile;
               $mtime = HTTP::Date::str2time($response->header('Last-Modified'));
#               print "touch $pkgfile (date ".(localtime $mtime).")\n" if $VERBOSE;
               utime($mtime, $mtime, $pkgfile) or die if $mtime;
            }
         } else {
#	    print "$PDIFFBASE/$curSum.diff.bz2 not on server.\n" if $VERBOSE;
	    print "miss (no delta file)\n" if $VERBOSE;
	 }
      } else {
#         print "$pkgfile doesn't exist.\n" if $VERBOSE;
	 print "miss (no local file)\n" if $VERBOSE;
         ##   retrieve Packages.gz
         $failed = 1;
      }
   }

   if($failed || (!$DSELNORUN && !$DRYRUN)) {
#      system("dselect update");
   }

   system "rm", "-rf", $tdir;
}

sub usage {
   print <<EOT;
   Usage: $0 [options]
   Options:
   --sources=$SOURCES
   --packagedir=$PACKAGEDIR
   --pdiffbase=$PDIFFBASE
   --[no]-dselect (runs "dselect update" when done)
   --[no]-dse
   --[no]-verbose
   --help
EOT
   exit;
}

&main;

