Import the old changelog extraction stuff
authorFrank Lichtenheld <frank@lichtenheld.de>
Mon, 20 Feb 2006 01:54:09 +0000 (01:54 +0000)
committerFrank Lichtenheld <frank@lichtenheld.de>
Mon, 20 Feb 2006 01:54:09 +0000 (01:54 +0000)
bin/extract_files [new file with mode: 0755]
conf/tmpl/content.tmpl [new file with mode: 0644]
conf/tmpl/default.tmpl [new file with mode: 0644]
conf/tmpl/footer.tmpl [new file with mode: 0644]
conf/tmpl/header.tmpl [new file with mode: 0644]
conf/tmpl/html_head.tmpl [new file with mode: 0644]
cron.d/300extract_changelogs [new file with mode: 0755]

diff --git a/bin/extract_files b/bin/extract_files
new file mode 100755 (executable)
index 0000000..2d4dbdc
--- /dev/null
@@ -0,0 +1,683 @@
+#!/usr/bin/perl
+#
+# Script to extract files from Debian packages
+# Copyright 2004 Frank Lichtenheld
+#
+# based on a shell script which was
+# Copyright 2003 Noel K├Âthe
+# Copyright 2004 Martin Schulze <joey@debian.org>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program; if not, write to the Free Software
+#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use lib "$FindBin::Bin";
+
+use Getopt::Long;
+use File::Temp qw( tempdir );
+use File::Path;
+use File::Copy;
+use File::Basename;
+#use Digest::SHA1;
+use Deb::Versions;
+use Parse::DebControl;
+use Parse::DebianChangelog;
+use Cwd;
+use Fcntl qw(:DEFAULT :flock);
+use Data::Dumper;
+use DB_File;
+
+use constant PKGPOOL => 1;
+use constant DIGESTPOOL => 2;
+
+my $PROGNAME = 'extract_files';
+my $MAXWARN = 100;
+my $TEMPDIR;
+
+my $directory = cwd()."/pool";
+my $dumpfile = '';
+my $cachefile = '';
+my $target = cwd()."/extracted_files";
+my $workdir = '';
+my $configdir = cwd()."/etc";
+my ( $verbose, $version, $help, $debug, $force, $use_dump );
+
+my %opthash = (
+              'verbose|v' => \$verbose,
+              'force|f' => \$force,
+              'directory|d=s' => \$directory,
+              'config|c=s' => \$configdir,
+              'target|t=s' => \$target,
+              'workdir|w=s' => \$workdir,
+              'cachefile=s' => \$cachefile,
+              'dumpfile=s' => \$dumpfile,
+              'use_dump' => \$use_dump,
+              'version' => \$version,
+              'debug' => \$debug,
+              'help' => \$help,
+              );
+
+my (%src_packages, %bin_packages, %cache, %stats);
+
+Getopt::Long::config('no_getopt_compat', 'no_auto_abbrev');
+
+GetOptions(%opthash) or do_error( "couldn't parse commandline parameters" );
+
+$verbose ||= $debug;
+$directory =~ s,/+$,,o;
+if ($workdir) {
+    $TEMPDIR = tempdir( 'pdo_extract_file.XXXXXX',
+                       DIR => $workdir, CLEANUP => 1 );
+} else {
+    $TEMPDIR = tempdir( 'pdo_extract_file.XXXXXX',
+                       CLEANUP => 1 );
+}
+
+##################################################
+# OUTPUT/LOGGING
+
+sub do_error {
+    die "$PROGNAME: FATAL: @_\n";
+}
+
+my $no_warnings = 0;
+sub do_warning {
+    warn "$PROGNAME: WARNING: @_\n";
+    if (++$no_warnings > $MAXWARN) {
+       do_error( "too many warnings ($MAXWARN)" );
+    }
+}
+
+sub do_info {
+    if ($verbose) {
+       print "$PROGNAME: INFO: @_\n";
+    }
+}
+
+sub do_debug {
+    if ($debug) {
+       print "$PROGNAME: DEBUG: @_\n";
+    }
+}
+
+sub add_log {
+    my $log  = shift;
+
+    do_debug( @_ );
+    $$log .= localtime().": @_\n";
+}
+
+##################################################
+# PACKAGE HANDLING (UNPACKING/CLEANUP)
+
+sub unpack_srcpkg {
+    my ( $dscname, $log ) = @_;
+
+    chdir( $TEMPDIR ) or do_error( "couldn't change working directory to $TEMPDIR" );
+
+    add_log( $log, "dpkg-source -sn -x $dscname"  );
+
+    my $out = qx/dpkg-source -sn -x "$dscname" 2>&1/;
+    my ($dir) = ($out =~ /(\S+)$/mo);
+
+    add_log( $log, "dpkg-source output: ", $out );
+
+#Bug#246802
+#    system("dpkg-source", "-x", $dscname ) == 0
+#      or do {
+#          do_warning( "couldn't unpack $dscname" );
+#          add_log( $log, "couldn't unpack $dscname" );
+#          return;
+#      };
+
+    return $dir;
+}
+
+sub unpack_binpkg {
+    my ( $pkgname, $debname, $log ) = @_;
+
+    add_log( $log, "unpacking binary package $pkgname" );
+
+    mkdir( "$TEMPDIR/$pkgname" ) or do {
+       do_warning( "couldn't create directory $TEMPDIR/$pkgname" );
+       add_log( $log, "couldn't create directory $TEMPDIR/$pkgname" );
+       return;
+    };
+
+    add_log( $log, "dpkg-deb --extract $debname $TEMPDIR/$pkgname" );
+
+    system("dpkg-deb", "--extract", $debname, "$TEMPDIR/$pkgname" ) == 0
+       or do {
+           do_warning( "couldn't unpack $debname" );
+           add_log( $log, "couldn't unpack $debname" );
+           return;
+       };
+
+    return 1;
+}
+
+sub unpack_allbinpkg {
+    my ($pkg_data, $log) = @_;
+
+    my %already_seen;
+
+    foreach my $pkg (@{$pkg_data->{bins}}) {
+       next if $already_seen{$pkg->{bin_name}}; # some assumptions about sane version numbers included
+
+       unpack_binpkg($pkg->{bin_name}, $pkg->{deb}, $log );
+
+       $already_seen{$pkg->{bin_name}}++;
+    }
+}
+
+sub cleanup_binpkg {
+    my ($pkg_data) = @_;
+
+    foreach my $pkg (keys %{$pkg_data->{bin_list}}) {
+       # rmtree should do that itself, but there seems to be a bug somewhere
+       system( "chmod", "-R", "u+rwx", "$TEMPDIR/$pkg" );
+       rmtree( "$TEMPDIR/$pkg" );
+    }
+}
+
+##################################################
+# POOL HANDLING
+
+sub pkg_pool_directory {
+    my ($pkg_data) = @_;
+
+    my $name = $pkg_data->{src_name};
+    my $version = $pkg_data->{src_version};
+    my $dscname = $pkg_data->{dsc};
+
+    my $dir = "";
+
+# I would prefer $name_$version but lets be backward compatible
+# in case someone depends on the naming
+    if ($name =~ /^(lib.)/o) {
+#      $dir .= "$1/$name/${name}_$version";
+       $dir .= "$1/$name/".basename($dscname, '.dsc');
+    } else {
+#      $dir .= substr($name,0,1)."/$name/${name}_$version";
+       $dir .= substr($name,0,1)."/$name/".basename($dscname, '.dsc');
+    }
+
+    return $dir;
+}
+
+sub to_update {
+    my ($pkg_data, $config_data, $log) = @_;
+
+    if ($config_data->{structure} == PKGPOOL) {
+       my $dir = "$target/".pkg_pool_directory( $pkg_data );
+       if (!$force && -d $dir && -f "$dir/log") {
+           (system( "touch", "$dir/log" ) == 0)
+               or do_warning( "touch of $dir/log failed" );
+           return 0;
+       } else {
+           rmtree( $dir );
+           return 1;
+       }
+    } elsif ($config_data->{structure} == DIGESTPOOL) {
+       die "UNIMPLEMENTED!";
+    } else {
+       do_error( "unknown pool structure $config_data->{structure}" );
+    }
+}
+
+sub write_log ($$) {
+    my ($dir, $log) = @_;
+
+    open my $logfh, ">$dir/log" or do_error( "couldn't open log file $dir/log.\n$log" );
+    flock $logfh, LOCK_EX or do_error( "couldn't lock log file $dir/log" );;
+
+    print $logfh $log;
+
+    close $logfh or do_warning( "couldn't close log file $dir/log" );
+}
+
+##################################################
+# EXTRACTION
+
+sub extract_copyright_to_pkgpool {
+    my ($pkg_data, $config_data, $log, $source_dir, $target_dir) = @_;
+
+    add_log( $log, "copy copyright file from source package" );
+
+    my $src_tgt = "$target_dir/copyright";
+    copy( "$source_dir/debian/copyright", $src_tgt )
+       or add_log( $log, "seems to have failed: $!" );
+
+    foreach my $bin_pkg (keys %{$pkg_data->{bin_list}}) {
+
+       my $usd = "$TEMPDIR/$bin_pkg/usr/share/doc/$bin_pkg";
+       my $cpy = "$usd/copyright";
+       my $tgt = "$target_dir/$bin_pkg.copyright";
+
+       if (-f $cpy) {
+           add_log( $log, "copy copyright file from binary package $bin_pkg" );
+           copy( $cpy, $tgt )
+               or add_log( $log, "seems to have failed: $!" );
+       } elsif (-l $cpy ) {
+           add_log( $log, "copyright file $cpy is symlink, I can't handle that" );
+       } elsif (-l $usd) {
+           add_log( $log, "doc directory $usd is symlink" );
+           my $link = readlink($usd) or add_log( $log, "readlink $usd failed" );
+           if ($link && $link =~ m,^(?:\./)?(\S+)/?$,o) { # do a little sanity check
+               my $pkg2 = $1;
+               if ($pkg_data->{bin_list}{$pkg2}) {
+                   add_log( $log, "symlink points to $pkg2, make symlink to copyright file" );
+                   (system("ln", "-s", "$pkg2.copyright", $tgt ) == 0)
+                       or add_log( $log, "symlink creation failed" );
+               } else {
+                   add_log( $log, "symlink points to $pkg2, don't know what to do with that" );
+               }
+           } else {
+               add_log( $log, "link seems fishy, not using" );
+           }
+       }
+
+       unless (-e $tgt || -l $tgt) { # if it is a link, we can't be sure that the target package was already processed
+           add_log( $log, "copyright file $tgt still doesn't exist" );
+           if (-e $src_tgt) {
+               add_log( $log, "copyright file of the source package exists, make symlink" );
+               (system("ln", "-s", "copyright", $tgt ) == 0)
+                   or add_log( $log, "symlink generation failed" );
+           } else {
+               add_log( $log, "give up on $bin_pkg" );
+               (system( "touch", "$tgt.ERROR" ) == 0)
+                   or add_log( $log, "even the touch of $tgt.ERROR failed :(" );
+           }
+       }
+
+    } #foreach $bin_pkg
+
+    unless (-e $src_tgt) {
+       add_log( $log, "copyright file $src_tgt still doesn't exist" );
+       # take one of the binary packages, prefering one that has
+       # the same name as the source package
+       foreach my $bin_pkg (($pkg_data->{src_name},
+                             keys %{$pkg_data->{bin_list}})) {
+           if (-e "$target_dir/$bin_pkg.copyright") {
+               add_log( $log, "copyright file $target_dir/$bin_pkg.copyright seems like a good guess to me, make a symlink" );
+               (system("ln", "-s", "$bin_pkg.copyright", $src_tgt ) == 0)
+                   or do {
+                       add_log( $log, "symlink generation failed" );
+                       next;
+                   };
+               last;
+           }
+       }
+       unless (-e $src_tgt) {
+           add_log( $log, "give up" );
+           (system( "touch", "$src_tgt.ERROR" ) == 0) or
+               add_log( $log, "even the touch of $src_tgt.ERROR failed :(" );
+       }
+    }
+}
+
+sub extract_changelog_to_pkgpool {
+    my ($pkg_data, $config_data, $log, $source_dir, $target_dir) = @_;
+
+    add_log( $log, "copy changelog file from source package" );
+
+    my $src_changelog = copy( "$source_dir/debian/changelog",
+                             "$target_dir/changelog.txt" );
+
+    if ($src_changelog) {
+       add_log( $log, "changelog file sucessfully copied" );
+    } else {
+       add_log( $log, "seems to have failed: $!" );
+    }
+
+    add_log( $log, "create enhanced HTML version" );
+    my $chg = Parse::DebianChangelog->init;
+    my $parsed = $chg->parse( { infile => "$source_dir/debian/changelog" } );
+    if ($parsed) {
+       $chg->html( { outfile => "$target_dir/changelog.html",
+                     template => "$configdir/tmpl/default.tmpl" } );
+       add_log( $log, scalar $chg->get_parse_errors )
+           if $chg->get_parse_errors;
+    } else {
+       do_warning( $chg->get_error );
+       add_log( $log, $chg->get_error );
+    }
+}
+
+sub manage_current_link {
+    my ($pkg_data, $config_data, $log, $source_dir, $target_dir) = @_;
+
+    my $parent_dir = dirname($target_dir);
+    my $dirname = basename($target_dir);
+    my $current_link = "$parent_dir/current";
+    add_log( $log, "parent_dir=$parent_dir; dirname=$dirname" );
+    unless (-l $current_link) {
+       add_log( $log, "create new current link" );
+       (chdir( $parent_dir ) and
+        not system( 'ln', '-s', $dirname, 'current' )) or
+        add_log( $log, "creating new current link failed: $!" );
+    } else {
+       my $old_target = readlink( $current_link );
+       (my $old_version = $old_target) =~ s/^[^_]*_//o;
+       if (version_cmp( $pkg_data->{src_version},
+                        $old_version) > 0) {
+           add_log( $log,
+                    "old_version=$old_version; overwriting current link" );
+           (chdir( $parent_dir ) and
+            unlink( 'current' ) and
+            not system( 'ln', '-s', $dirname, 'current' )) or
+            add_log( $log, "overwriting current link failed: $!" );
+       } else {
+           add_log( $log,
+                    "old_version=$old_version; not touching current link" );
+       }
+    }
+}
+
+sub extract_files {
+    my ($pkg_data, $config_data) = @_;
+    my $log = "";
+
+    add_log( \$log, "process source package $pkg_data->{src_name} ($pkg_data->{src_version})" );
+
+    unless (to_update( $pkg_data, $config_data, \$log )) {
+       $stats{already_extracted}++;
+       do_debug( "source package $pkg_data->{src_name} ($pkg_data->{src_version}) doesn't need extracting" );
+       return;
+    }
+
+    if (my $source_dir = unpack_srcpkg( $pkg_data->{dsc}, \$log )) {
+
+       $source_dir = "$TEMPDIR/$source_dir";
+
+       unpack_allbinpkg($pkg_data, \$log);
+
+       my $target_dir = "$target/".pkg_pool_directory($pkg_data);
+       add_log( \$log, "source_dir=$source_dir; target_dir=$target_dir" );
+
+       mkpath( $target_dir );
+
+       if ($config_data->{structure} == PKGPOOL) {
+           extract_copyright_to_pkgpool( $pkg_data, $config_data, \$log,
+                                         $source_dir, $target_dir );
+           extract_changelog_to_pkgpool( $pkg_data, $config_data, \$log,
+                                         $source_dir, $target_dir );
+           manage_current_link( $pkg_data, $config_data, \$log,
+                                $source_dir, $target_dir );
+       } elsif ($config_data->{structure} == DIGESTPOOL) {
+           die "UNIMPLEMENTED!";
+       } else {
+           do_error( "unknown pool structure $config_data->{structure}" );
+       }
+
+       # rmtree should do that itself, but there seems to be a bug somewhere
+       system( "chmod", "-R", "u+rwx", "$source_dir" );
+       rmtree( $source_dir );
+       cleanup_binpkg($pkg_data);
+       write_log( $target_dir, $log );
+    }
+}
+
+sub extract_from_all {
+    my ( $src_packages ) = @_;
+
+    unless (-d $target) {
+       mkpath( $target ) or do_error( "couldn't create target directory" );
+    }
+
+    # TODO: make configurable
+    my %config = (
+                 structure => PKGPOOL,
+                 );
+
+    do_info( scalar(keys(%$src_packages))." source packages to process" );
+    foreach my $p (keys %$src_packages) {
+       foreach my $v (keys %{$src_packages->{$p}}) {
+           extract_files( $src_packages->{$p}{$v}, \%config );
+       }
+    }
+}
+
+##################################################
+# COLLECTING INFORMATION
+
+sub merge_src_bin_packages {
+    my ( $src_packages, $bin_packages ) = @_;
+
+    foreach my $p (keys %$bin_packages) { # packages
+       foreach my $v (keys %{$bin_packages->{$p}}) { # versions
+           foreach my $a (keys %{$bin_packages->{$p}{$v}}) { # architectures
+               my %bin_data = %{$bin_packages->{$p}{$v}{$a}};
+
+               if (exists $src_packages->{$bin_data{bin_src}}{$bin_data{bin_src_version}}) {
+                   $src_packages->{$bin_data{bin_src}}{$bin_data{bin_src_version}}{bins} ||= [];
+                   push @{$src_packages->{$bin_data{bin_src}}{$bin_data{bin_src_version}}{bins}}, \%bin_data;
+                   $src_packages->{$bin_data{bin_src}}{$bin_data{bin_src_version}}{bin_list}{$p}++;
+               }
+           }
+       }
+    }
+
+    return $src_packages;
+}
+
+sub read_dsc {
+    my ( $dscname ) = @_;
+
+    my $parser = Parse::DebControl->new();
+    my ( $raw_data, $pkg_data );
+
+    my $dsccontent = $cache{$dscname};
+    unless ($dsccontent) {
+       open my $dscfh, "<", $dscname or do {
+           do_warning( "reading file $dscname failed" );
+           return;
+       };
+
+       $dsccontent = "";
+       while (<$dscfh>) {
+           next if /^\#/o;
+           if (/^-----BEGIN PGP SIGNED MESSAGE/o) {
+               while (<$dscfh> =~ /\S/) {}; # skip Hash: line and similar
+               next;
+           }
+           if (/^-----BEGIN PGP SIGNATURE/o) {
+               last; # stop reading
+           }
+           $dsccontent .= $_;
+       }
+
+       $cache{$dscname} = $dsccontent;
+    } else {
+       $stats{src_cache}++;
+       if ($debug) {
+           (my $begin = substr($dsccontent,0,20)) =~ s/\n/\\n/go;
+           do_debug( "CACHE HIT: $dscname ($begin)" );
+       }
+    }
+
+    unless ( $raw_data = $parser->parse_mem( $dsccontent,
+                                            { discardCase => 1 } ) ) {
+       do_warning( "parsing file $dscname failed.\n$dsccontent" );
+       return;
+    }
+
+    my $no_chunks = @$raw_data;
+    if ($no_chunks != 1) {
+       do_warning( "expected exactly one chunk in .dsc file, got $no_chunks" );
+       return;
+    }
+
+    $pkg_data = {
+       src_name => $raw_data->[0]{source},
+       src_version => $raw_data->[0]{version},
+       dsc => $dscname,
+    };
+
+    unless( $pkg_data->{src_name} && $pkg_data->{src_version}
+       && $pkg_data->{dsc} ) {
+       use Data::Dumper;
+       do_error( "something fishy happened.\n", Dumper( $pkg_data ) );
+    }
+
+    do_debug( "found source package $pkg_data->{src_name}, version $pkg_data->{src_version}" );
+    $stats{src_pkgs}++;
+
+    return $pkg_data;
+}
+
+sub read_deb {
+    my ( $debname ) = @_;
+
+    my $parser = Parse::DebControl->new();
+    my ( $raw_data, $pkg_data );
+
+    if ($cache{$debname}) {
+       $stats{bin_cache}++;
+       if ($debug) {
+           (my $begin = substr($cache{$debname},0,20)) =~ s/\n/\\n/go;
+           do_debug( "CACHE HIT: $debname ($begin)" );
+       }
+    }
+    $cache{$debname} ||= qx/dpkg-deb --info "$debname" control/;
+    my $control = $cache{$debname};
+
+    unless ( $raw_data = $parser->parse_mem( $control,
+                                            { discardCase => 1 } ) ) {
+       do_warning( "parsing control information <<$control>> of file $debname failed" );
+       return;
+    }
+
+    my $no_chunks = @$raw_data;
+    if ($no_chunks != 1) {
+       do_warning( "expected exactly one chunk in .deb control information, got $no_chunks" );
+       return;
+    }
+
+    $pkg_data = {
+       bin_name => $raw_data->[0]{package},
+       bin_version => $raw_data->[0]{version},
+       bin_arch => $raw_data->[0]{architecture},
+       bin_src => $raw_data->[0]{source} || $raw_data->[0]{package},,
+       bin_src_version => $raw_data->[0]{version},
+       deb => $debname,
+    };
+
+    if ($pkg_data->{bin_src} =~ /^([\w.+-]+)\s*\(\s*=\s*([^\s\)])\s*\)\s*$/) {
+       $pkg_data->{bin_src} = $1;
+       $pkg_data->{bin_src_version} = $2;
+    }
+
+    do_debug( "found binary package $pkg_data->{bin_name}, version $pkg_data->{bin_version}, architecture $pkg_data->{bin_arch}" );
+    $stats{bin_pkgs}++;
+
+    return $pkg_data;
+}
+
+sub collect_deb {
+    my ( $debname ) = @_;
+
+    do_debug( "processing deb file $debname" );
+
+    my $pkg_data = read_deb( $debname );
+    return unless $pkg_data;
+
+    if (exists $bin_packages{$pkg_data->{bin_name}}{$pkg_data->{bin_version}}{$pkg_data->{bin_arch}}) {
+       do_warning( "duplicated package $pkg_data->{bin_name}, version {$pkg_data->{bin_version}{$pkg_data->{bin_arch}}" );
+       return;
+    } else {
+       $bin_packages{$pkg_data->{bin_name}}{$pkg_data->{bin_version}}{$pkg_data->{bin_arch}} = $pkg_data;
+    }
+}
+
+sub collect_dsc {
+    my ( $dscname ) = @_;
+
+    do_debug( "processing dsc file $dscname" );
+
+    my $pkg_data = read_dsc( $dscname );
+    return unless $pkg_data;
+
+    if (exists $src_packages{$pkg_data->{src_name}}{$pkg_data->{src_version}}) {
+       do_warning( "duplicated package $pkg_data->{src_name}, version {$pkg_data->{src_version}" );
+       return;
+    } else {
+       $src_packages{$pkg_data->{src_name}}{$pkg_data->{src_version}} = $pkg_data;
+    }
+}
+
+sub read_sub {
+    my ( $dir ) = @_;
+
+    do_debug( "processing directory $dir" );
+
+    opendir my $dh, $dir or do_error( "couldn't open directory $dir" );
+    while( my $entry = readdir $dh ) {
+       chomp $entry;
+       next if $entry =~ /^\.\.?$/o;
+
+       my $fullname = "$dir/$entry";
+
+       read_sub( $fullname ) if -d $fullname;
+       collect_dsc( $fullname ) if -f _ && ( $fullname =~ /\.dsc$/o );
+       collect_deb( $fullname ) if -f _ && ( $fullname =~ /\..?deb$/o );
+    }
+    closedir $dh or do_warning( "couldn't close directory $dir" );
+}
+
+##################################################
+# MAIN PROGRAM
+
+do_info( "Using working directory $TEMPDIR" );
+if ($use_dump) {
+    do_info( "load information from dump file" );
+    open DUMP, '<', $dumpfile
+       or do_error( "couldn't open dump file $dumpfile: $!" );
+    my $info = join "", <DUMP>;
+    eval $info;
+    close DUMP or do_warning( "couldn't close dump file: $!" );
+} else {
+    do_info( "collect information (in $directory)" );
+    if ($cachefile) {
+       tie %cache, 'DB_File', $cachefile, O_CREAT|O_RDWR, 0640 
+           or die "E: tie with file $cachefile failed: $!";
+    }
+    read_sub( $directory );
+#FIXME: "untie attempted while 1 inner references still exist"
+#    untie %cache if tied %cache;
+    do_info( "postprocess collected information" );
+    merge_src_bin_packages( \%src_packages, \%bin_packages );
+    if ($dumpfile) {
+       do_info( "dump backup of collected information" );
+       open DUMP, '>', $dumpfile
+           or do_error( "couldn't open dump file $dumpfile: $!" );
+       print DUMP Data::Dumper->Dump( [ \%src_packages ],
+                                      [ '*src_packages' ] );
+       close DUMP or do_warning( "couldn't close dump file: $!" );
+    }
+}
+do_info( "begin extracting files" );
+extract_from_all( \%src_packages );
+do_info( <<STATS );
+Statistics:
+ Source Packages:   $stats{src_pkgs}
+ Cached Info:       $stats{src_cache}
+ Already Extracted: $stats{already_extracted}
+ Binary Packages:   $stats{bin_pkgs}
+ Cached Info:       $stats{bin_cache}
+STATS
diff --git a/conf/tmpl/content.tmpl b/conf/tmpl/content.tmpl
new file mode 100644 (file)
index 0000000..693b9d4
--- /dev/null
@@ -0,0 +1,27 @@
+<div id="content">
+<TMPL_LOOP NAME="CONTENT_YEARS">
+<h2 class="year_header" id="year<TMPL_VAR NAME="CONTENT_YEAR">">
+<TMPL_VAR NAME="CONTENT_YEAR">
+</h2>
+<TMPL_LOOP NAME="CONTENT_VERSIONS">
+<h3 class="entry_header" id="version<TMPL_VAR NAME="CONTENT_VERSION_ID">">
+       <a class="packagelink" href="http://packages.debian.org/src:<TMPL_VAR ESCAPE="URL" NAME="CONTENT_SOURCE">"><TMPL_VAR NAME="CONTENT_SOURCE"></a>
+        (<TMPL_VAR NAME="CONTENT_VERSION">)
+       <span class="<TMPL_VAR NAME="CONTENT_DISTRIBUTION_NORM">"><TMPL_VAR NAME="CONTENT_DISTRIBUTION"></span>;
+       urgency=<span class="<TMPL_VAR NAME="CONTENT_URGENCY_NORM">"><TMPL_VAR NAME="CONTENT_URGENCY"></span>
+</h3>
+<!-- NOTE: CONTENT_CHANGES can contain HTML -->
+<pre><TMPL_VAR NAME="CONTENT_CHANGES"></pre>
+<p class="trailer">&nbsp;-- <TMPL_VAR NAME="CONTENT_MAINTAINER_NAME"> &lt;<a href="http://qa.debian.org/developer.php?login=<TMPL_VAR NAME="CONTENT_MAINTAINER_EMAIL">"><TMPL_VAR NAME="CONTENT_MAINTAINER_EMAIL"></a>&gt;&nbsp;&nbsp;<TMPL_VAR NAME="CONTENT_DATE">
+</p>
+<!-- NOTE: CONTENT_PARSE_ERROR can contain HTML -->
+<TMPL_VAR NAME="CONTENT_PARSE_ERROR">
+</TMPL_LOOP>
+</TMPL_LOOP>
+<TMPL_IF NAME="OLDFORMAT">
+<h2 class="year_header" id="oldformat">Old changelog format(s), not parsed</h2>
+<pre class="oldformat">
+<TMPL_VAR ESCAPE="HTML" NAME="OLDFORMAT_CHANGES">
+</pre>
+</TMPL_IF>
+</div>
diff --git a/conf/tmpl/default.tmpl b/conf/tmpl/default.tmpl
new file mode 100644 (file)
index 0000000..d10303f
--- /dev/null
@@ -0,0 +1,10 @@
+<!--
+       All parts of the default template are
+       Copyright (C) 2005 Frank Lichtenheld <frank@lichtenheld.de>
+       And may be used under the same license as Parse::DebianChangelog
+       itself
+-->
+<TMPL_INCLUDE NAME="html_head.tmpl">
+<TMPL_INCLUDE NAME="header.tmpl">
+<TMPL_INCLUDE NAME="content.tmpl">
+<TMPL_INCLUDE NAME="footer.tmpl">
diff --git a/conf/tmpl/footer.tmpl b/conf/tmpl/footer.tmpl
new file mode 100644 (file)
index 0000000..2d7f13e
--- /dev/null
@@ -0,0 +1,14 @@
+<div class="footer">
+       <hr class="hide">
+       <address>
+               Generated <TMPL_VAR NAME="GENERATED_DATE"> by <tt>
+                       <TMPL_VAR NAME="MODULE_NAME"> (v<TMPL_VAR NAME="MODULE_VERSION">)
+               </tt>
+               <br>
+               Contact <a href="mailto:debian-www@lists.debian.org">debian-www@lists.debian.org</a>
+                in case of problems.
+       </address>
+</div>
+
+</body>
+</html>
diff --git a/conf/tmpl/header.tmpl b/conf/tmpl/header.tmpl
new file mode 100644 (file)
index 0000000..73ea5b4
--- /dev/null
@@ -0,0 +1,39 @@
+<p class="hide">
+       <a href="#content">Skip to content</a>
+</p>
+
+<!-- Links to usefull Debian websites providing more information about
+       this package -->
+<ul class="navbar">
+       <li>
+               <a href="http://packages.debian.org/src:<TMPL_VAR NAME="SOURCE_NEWEST">">Package Information</a>
+       </li>
+       <li>
+               <a href="http://packages.qa.debian.org/<TMPL_VAR NAME="SOURCE_NEWEST">">Package Developer Information</a>
+       </li>
+       <li>
+               <a href="http://bugs.debian.org/src:<TMPL_VAR NAME="SOURCE_NEWEST">">Bug Information</a>
+       </li>
+</ul>
+<h1 class="document_header">
+       Debian Changelog <TMPL_VAR NAME="SOURCE_NEWEST"> (<TMPL_VAR NAME="VERSION_NEWEST">)
+</h1>
+
+<!-- Outline of the changelog with links to all versions -->
+<ul class="outline">
+<TMPL_LOOP NAME="NAV_YEARS">
+<li>
+       <a href="#year<TMPL_VAR NAME="NAV_YEAR">"><TMPL_VAR NAME="NAV_YEAR"></a>:
+       <ul>
+       <TMPL_LOOP NAME="NAV_VERSIONS">
+               <li>
+                       <a title="<TMPL_VAR ESCAPE="HTML" NAME="NAV_MAINTAINER"> <TMPL_VAR NAME="NAV_DATE">" href="#version<TMPL_VAR NAME="NAV_VERSION_ID">"><TMPL_VAR NAME="NAV_VERSION"></a>
+               </li>
+       </TMPL_LOOP>
+       </ul>
+</li>
+</TMPL_LOOP>
+<TMPL_IF NAME="OLDFORMAT">
+<li><a href="#oldformat">old format</a></li>
+</TMPL_IF>
+</ul>
diff --git a/conf/tmpl/html_head.tmpl b/conf/tmpl/html_head.tmpl
new file mode 100644 (file)
index 0000000..4399040
--- /dev/null
@@ -0,0 +1,15 @@
+<!DOCTYPE html
+       PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
+        "http://www.w3.org/TR/html4/loose.dtd">
+<html lang="en-US"><head><title>Debian Changelog <TMPL_VAR NAME="SOURCE_NEWEST"> (<TMPL_VAR NAME="VERSION_NEWEST">)</title>
+<link rev="made" href="mailto:<TMPL_VAR ESCAPE="URL" NAME="MAINTAINER_NEWEST">">
+<meta name="keywords" content="Debian Changelog <TMPL_VAR NAME="SOURCE_NEWEST"> <TMPL_VAR NAME="VERSION_NEWEST">">
+<meta name="generator" content="<TMPL_VAR NAME="MODULE_NAME"> (<TMPL_VAR NAME="MODULE_VERSION">)">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+
+<link type="text/css" media="screen" rel="stylesheet" href="<TMPL_VAR NAME="CSS_FILE_SCREEN" ESCAPE="URL" DEFAULT="/changelogs.css">">
+
+<link type="text/css" media="print" rel="stylesheet" href="<TMPL_VAR NAME="CSS_FILE_PRINT" ESCAPE="URL" DEFAULT="/changelogs-print.css">">
+
+</head>
+<body>
diff --git a/cron.d/300extract_changelogs b/cron.d/300extract_changelogs
new file mode 100755 (executable)
index 0000000..08be087
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+. `dirname $0`/../config.sh
+if test -z "${localdir}"; then
+    echo skipping due to missing local archive
+    exit 1
+fi
+
+#set -e
+
+NOCPY_TEMPLATE=$topdir/etc/copyright_error
+NOCPY_BIN_TEMPLATE=$topdir/etc/copyright_error_bin
+
+logs="$topdir/files/logs"
+log="${logs}/changelogs.log"
+
+test -d "$logs" || mkdir -p "$logs"
+test -d "$tmpdir" || mkdir -p "$tmpdir"
+
+if [ -s "$log" ]
+then
+    savelog -c 14 "$log" > /dev/null
+fi
+
+(
+date
+
+for part in $parts; do
+    time "${bindir}/extract_files" -v -d "${localdir}/pool/$part/" \
+       -t "${htmldir}/changelogs/pool/$part/" \
+       -c "$configdir" -w "$tmpdir" \
+       --dumpfile "${filesdir}/changelogs.$part.dump" \
+       --cachefile "${filesdir}/changelogs.cache"
+done
+
+find "${htmldir}/changelogs/" -name log -cmin +7200 \
+ | while read logfile; do
+       dir=$(dirname "$logfile")
+       echo deleting $dir
+       rm -r "$dir"
+       rmdir --ignore-fail-on-non-empty $(dirname "$dir")
+done
+
+date
+) > $log 2>&1