| 1 |
operz |
11 |
#!/bin/env perl -w |
| 2 |
|
|
# |
| 3 |
|
|
# $Id: cpan_check 1091 2007-03-07 07:00:12Z comand $ |
| 4 |
|
|
# |
| 5 |
|
|
# Copyright 2006 Cory Omand <comand@blastwave.org> |
| 6 |
|
|
# All rights reserved. Use is subject to license terms. |
| 7 |
|
|
# |
| 8 |
|
|
# Redistribution and/or use, with or without modification, is |
| 9 |
|
|
# permitted. This software is without warranty of any kind. The |
| 10 |
|
|
# author(s) shall not be liable in the event that use of the |
| 11 |
|
|
# software causes damage. |
| 12 |
|
|
# |
| 13 |
|
|
# cpan_check - Check CPAN for module updates. |
| 14 |
|
|
# |
| 15 |
|
|
|
| 16 |
|
|
use strict; |
| 17 |
|
|
use Data::Dumper; |
| 18 |
|
|
use File::Basename; |
| 19 |
|
|
use Time::Local qw/timelocal/; |
| 20 |
|
|
use vars qw/$HAS_LWP $MODULE_EXT/; |
| 21 |
|
|
|
| 22 |
|
|
# Check for LWP support |
| 23 |
|
|
$HAS_LWP = 0; |
| 24 |
|
|
eval { |
| 25 |
|
|
require LWP::UserAgent; |
| 26 |
|
|
import LWP::UserAgent; |
| 27 |
|
|
$HAS_LWP = 1; |
| 28 |
|
|
}; |
| 29 |
|
|
|
| 30 |
|
|
# Standard module extension |
| 31 |
|
|
$MODULE_EXT = qr/\.(tar\.(gz|bz2)|tgz)/; |
| 32 |
|
|
|
| 33 |
|
|
# Month lookup table |
| 34 |
|
|
my @MONTHS = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; |
| 35 |
|
|
my %MONTHS; |
| 36 |
|
|
for (my $i = 0; $i < @MONTHS; $i++) { $MONTHS{$MONTHS[$i]} = $i } |
| 37 |
|
|
|
| 38 |
|
|
# Parse a module filename to get version information |
| 39 |
|
|
sub fileinfo { |
| 40 |
|
|
my $file = shift; |
| 41 |
|
|
return unless $file; |
| 42 |
|
|
|
| 43 |
|
|
$file = basename $file; |
| 44 |
|
|
|
| 45 |
|
|
my ($mname, $mvers) = $file =~ /^(.+?)[-\.\_](\d.*?)$MODULE_EXT$/; |
| 46 |
|
|
return unless $mvers; |
| 47 |
|
|
return ($mname, $mvers); |
| 48 |
|
|
} |
| 49 |
|
|
|
| 50 |
|
|
# Get a directory listing |
| 51 |
|
|
sub get_listing { |
| 52 |
|
|
my $dir = shift; |
| 53 |
|
|
|
| 54 |
|
|
my @listing; |
| 55 |
|
|
if ($HAS_LWP) { |
| 56 |
|
|
my $ua = LWP::UserAgent->new(env_proxy => 1); |
| 57 |
|
|
my $rsp = $ua->get($dir); |
| 58 |
|
|
die "failed to list $dir: [" . $rsp->code . "] " . $rsp->message |
| 59 |
|
|
unless $rsp->is_success; |
| 60 |
|
|
|
| 61 |
|
|
@listing = split(/\n/, $rsp->content); |
| 62 |
|
|
} |
| 63 |
|
|
else { |
| 64 |
|
|
@listing = `curl -s $dir`; |
| 65 |
|
|
} |
| 66 |
|
|
|
| 67 |
|
|
return @listing; |
| 68 |
|
|
} |
| 69 |
|
|
|
| 70 |
|
|
# Parse a HTML directory listing |
| 71 |
|
|
sub parse_listing { |
| 72 |
|
|
my $line = shift; |
| 73 |
|
|
my ($file, $d, $m, $y, $H, $M); |
| 74 |
|
|
|
| 75 |
|
|
($file, $d, $m, $y, $H, $M) = $line =~ |
| 76 |
|
|
/<a\s+href="([^"]+)">.+<\/a>.+? # file |
| 77 |
|
|
(\d{2})-(\w{3})-(\d{4})\s+ # date (DD-MMM-YYYY) |
| 78 |
|
|
(\d{2}):(\d{2}) # time (HH:MM) |
| 79 |
|
|
/ix; |
| 80 |
|
|
|
| 81 |
|
|
unless ($file and $d and $m and $y) { |
| 82 |
|
|
($file, $y, $m, $d, $H, $M) = $line =~ |
| 83 |
|
|
/<a\s+href=\"([^\"]+)\">.+<\/a>.+? # file |
| 84 |
|
|
(\d{4})-(\w{3})-(\d{2})\s+ # date (YYYY-MMM-DD) |
| 85 |
|
|
(\d{2}):(\d{2}) # time (HH:MM) |
| 86 |
|
|
/ix; |
| 87 |
|
|
} |
| 88 |
|
|
|
| 89 |
|
|
return ($file, $d, $m, $y, $H, $M); |
| 90 |
|
|
} |
| 91 |
|
|
|
| 92 |
|
|
# Get module information |
| 93 |
|
|
sub get_modinfo { |
| 94 |
|
|
my $moddir = shift; |
| 95 |
|
|
my $modname = shift; |
| 96 |
|
|
return unless $moddir and $modname; |
| 97 |
|
|
|
| 98 |
|
|
my @listing = get_listing($moddir); |
| 99 |
|
|
|
| 100 |
|
|
# Parse the listing... |
| 101 |
|
|
my $vinfo; |
| 102 |
|
|
foreach my $line (@listing) { |
| 103 |
|
|
my ($file, $d, $m, $y, $H, $M) = parse_listing($line); |
| 104 |
|
|
|
| 105 |
|
|
next unless $file && $file =~ /$MODULE_EXT$/; |
| 106 |
|
|
my ($mname, $mver) = fileinfo($file); |
| 107 |
|
|
next unless $mver and $mname; |
| 108 |
|
|
next if $mname ne $modname; |
| 109 |
|
|
|
| 110 |
|
|
$vinfo->{$mver} = timelocal(0, $M, $H, $d, $MONTHS{$m}, $y - 1900); |
| 111 |
|
|
} |
| 112 |
|
|
|
| 113 |
|
|
return $vinfo; |
| 114 |
|
|
} |
| 115 |
|
|
|
| 116 |
|
|
# |
| 117 |
|
|
# MAIN EXECUTION |
| 118 |
|
|
# |
| 119 |
|
|
|
| 120 |
|
|
my $distfile = shift @ARGV; |
| 121 |
|
|
die "Usage: $0 module_file" unless $distfile; |
| 122 |
|
|
|
| 123 |
|
|
my $outfh; |
| 124 |
|
|
my $outfile = shift @ARGV; |
| 125 |
|
|
if ($outfile) { |
| 126 |
|
|
open $outfh, ">>$outfile" |
| 127 |
|
|
or die "Failed to open update file '$outfile': $!\n"; |
| 128 |
|
|
} else { |
| 129 |
|
|
$outfh = \*STDERR; |
| 130 |
|
|
} |
| 131 |
|
|
|
| 132 |
|
|
my ($modname, $modvers) = fileinfo($distfile); |
| 133 |
|
|
die "Unable to parse module file name: $distfile" |
| 134 |
|
|
unless $modname && defined $modvers; |
| 135 |
|
|
|
| 136 |
|
|
# Get the distribution directory |
| 137 |
|
|
my $distdir = dirname($distfile) . "/"; |
| 138 |
|
|
|
| 139 |
|
|
# Get a list of modules... |
| 140 |
|
|
my $info = get_modinfo($distdir, $modname) |
| 141 |
|
|
or die "Unable to retrieve module information for $modname!\n"; |
| 142 |
|
|
|
| 143 |
|
|
# Remove the current version. If the module has been pulled |
| 144 |
|
|
# from CPAN, assume no current release (update to latest). |
| 145 |
|
|
my $currel = delete $info->{$modvers} || 0; |
| 146 |
|
|
|
| 147 |
|
|
# Find the versions which are newer than that one |
| 148 |
|
|
my @uprev; |
| 149 |
|
|
foreach my $ver (sort { $info->{$a} <=> $info->{$b} } keys %$info) { |
| 150 |
|
|
push @uprev, $ver if $info->{$ver} >= $currel; |
| 151 |
|
|
} |
| 152 |
|
|
|
| 153 |
|
|
# Print version info |
| 154 |
|
|
if (@uprev) { |
| 155 |
|
|
my @ndev = grep(!/_/, @uprev); |
| 156 |
|
|
my @dev = grep( /_/, @uprev); |
| 157 |
|
|
|
| 158 |
|
|
if (@ndev) { |
| 159 |
|
|
print "$modname $modvers:\n"; |
| 160 |
|
|
|
| 161 |
|
|
my $rec = pop @ndev; |
| 162 |
|
|
print " Recommended: ", $rec, "\n"; |
| 163 |
|
|
print " Older: ", join(", ", @ndev), "\n" if @ndev; |
| 164 |
|
|
print " Development: ", join(", ", @dev), "\n" if @dev; |
| 165 |
|
|
|
| 166 |
|
|
# Output info to automate udpates |
| 167 |
|
|
print $outfh "$modname|$rec", "\n"; |
| 168 |
|
|
} |
| 169 |
|
|
} |
| 170 |
|
|
|
| 171 |
|
|
exit 0; |
| 172 |
|
|
|