From: Simon Wistow Date: Tue, 22 Apr 2008 17:48:48 +0000 (+0000) Subject: Load XML-Feed-0.01 into trunk. X-Git-Tag: v0.01~2 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=commitdiff_plain;h=5ec68b3b962d9e6575a03e46de074e04ce0541cd;p=catagits%2FXML-Feed.git Load XML-Feed-0.01 into trunk. --- diff --git a/Build.PL b/Build.PL new file mode 100644 index 0000000..289c881 --- /dev/null +++ b/Build.PL @@ -0,0 +1,3 @@ +# $Id: Build.PL,v 1.1.1.1 2004/05/29 17:29:56 btrott Exp $ + +require 'Makefile.PL'; diff --git a/Changes b/Changes new file mode 100644 index 0000000..237bc0b --- /dev/null +++ b/Changes @@ -0,0 +1,2 @@ +0.01 2004.06.01 + - Initial distribution. diff --git a/MANIFEST b/MANIFEST new file mode 100644 index 0000000..82a9d15 --- /dev/null +++ b/MANIFEST @@ -0,0 +1,28 @@ +Build.PL +Changes +inc/ExtUtils/AutoInstall.pm +inc/Module/Install.pm +inc/Module/Install/AutoInstall.pm +inc/Module/Install/Base.pm +inc/Module/Install/Build.pm +inc/Module/Install/Can.pm +inc/Module/Install/Fetch.pm +inc/Module/Install/Include.pm +inc/Module/Install/Makefile.pm +inc/Module/Install/Metadata.pm +inc/Module/Install/Win32.pm +inc/Module/Install/WriteAll.pm +lib/XML/Feed.pm +lib/XML/Feed/Atom.pm +lib/XML/Feed/Entry.pm +lib/XML/Feed/ErrorHandler.pm +lib/XML/Feed/RSS.pm +Makefile.PL +MANIFEST This list of files +META.yml +README +t/00-compile.t +t/01-parse.t +t/samples/atom.xml +t/samples/rss10.xml +t/samples/rss20.xml diff --git a/META.yml b/META.yml new file mode 100644 index 0000000..1ddf0fb --- /dev/null +++ b/META.yml @@ -0,0 +1,21 @@ +name: XML-Feed +version: 0.01 +abstract: XML Syndication Feed Support +author: Benjamin Trott +license: perl +distribution_type: module +requires: + XML::RSS: 1.01 + XML::Atom: 0.08 + LWP: 0 + HTML::Parser: 0 + URI: 0 + DateTime: 0 + DateTime::Format::Mail: 0 + DateTime::Format::W3CDTF: 0 + List::Util: 0 +no_index: + directory: + - t + - inc +generated_by: Module::Install version 0.33 diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..d3c3b67 --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,28 @@ +# $Id: Makefile.PL,v 1.1.1.1 2004/05/29 17:29:56 btrott Exp $ + +use inc::Module::Install; + +name('XML-Feed'); +abstract('XML Syndication Feed Support'); +author('Benjamin Trott '); +version_from('lib/XML/Feed.pm'); +license('perl'); +no_index(directory => 't'); +sign(1); + +include('ExtUtils::AutoInstall'); + +requires('XML::RSS' => 1.01); +requires('XML::Atom' => 0.08); +requires('LWP'); +requires('HTML::Parser'); +requires('URI'); +requires('DateTime'); +requires('DateTime::Format::Mail'); +requires('DateTime::Format::W3CDTF'); +requires('List::Util'); + +auto_include(); +auto_install(); + +&WriteAll; diff --git a/README b/README new file mode 100644 index 0000000..533a462 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +$Id: README,v 1.1.1.1 2004/05/29 17:29:56 btrott Exp $ + +This is XML::Feed, an abstraction above the RSS and Atom syndication +feed formats. It supports both parsing and autodiscovery of feeds. + +PREREQUISITES + + * XML::RSS + * XML::Atom + * LWP + * HTML::Parser + * URI + * DateTime + * DateTime::Format::Mail + * DateTime::Format::W3CDTF + * List::Util + +INSTALLATION + +XML::Feed installation is straightforward. If your CPAN shell +is set up, you should just be able to do + + % perl -MCPAN -e 'install XML::Feed' + +Download it, unpack it, then build it as per the usual: + + % perl Makefile.PL + % make && make test + +Then install it: + + % make install + +Benjamin Trott / cpan@stupidfool.org diff --git a/inc/ExtUtils/AutoInstall.pm b/inc/ExtUtils/AutoInstall.pm new file mode 100644 index 0000000..5e43c13 --- /dev/null +++ b/inc/ExtUtils/AutoInstall.pm @@ -0,0 +1,631 @@ +#line 1 "inc/ExtUtils/AutoInstall.pm - /Library/Perl/5.8.1/ExtUtils/AutoInstall.pm" +# $File: //member/autrijus/ExtUtils-AutoInstall/lib/ExtUtils/AutoInstall.pm $ +# $Revision: #9 $ $Change: 9532 $ $DateTime: 2004/01/01 06:47:30 $ vim: expandtab shiftwidth=4 + +package ExtUtils::AutoInstall; +$ExtUtils::AutoInstall::VERSION = '0.56'; + +use strict; +use Cwd (); +use ExtUtils::MakeMaker (); + +#line 282 + +# special map on pre-defined feature sets +my %FeatureMap = ( + '' => 'Core Features', # XXX: deprecated + '-core' => 'Core Features', +); + +# various lexical flags +my (@Missing, @Existing, %DisabledTests, $UnderCPAN, $HasCPANPLUS); +my ($Config, $CheckOnly, $SkipInstall, $AcceptDefault, $TestOnly); +my ($PostambleActions, $PostambleUsed); + +$AcceptDefault = 1 unless -t STDIN; # non-interactive session +_init(); + +sub missing_modules { + return @Missing; +} + +sub do_install { + __PACKAGE__->install( + [ UNIVERSAL::isa($Config, 'HASH') ? %{$Config} : @{$Config}], + @Missing, + ); +} + +# initialize various flags, and/or perform install +sub _init { + foreach my $arg (@ARGV, split(/[\s\t]+/, $ENV{PERL_EXTUTILS_AUTOINSTALL} || '')) { + if ($arg =~ /^--config=(.*)$/) { + $Config = [ split(',', $1) ]; + } + elsif ($arg =~ /^--installdeps=(.*)$/) { + __PACKAGE__->install($Config, @Missing = split(/,/, $1)); + exit 0; + } + elsif ($arg =~ /^--default(?:deps)?$/) { + $AcceptDefault = 1; + } + elsif ($arg =~ /^--check(?:deps)?$/) { + $CheckOnly = 1; + } + elsif ($arg =~ /^--skip(?:deps)?$/) { + $SkipInstall = 1; + } + elsif ($arg =~ /^--test(?:only)?$/) { + $TestOnly = 1; + } + } +} + +# overrides MakeMaker's prompt() to automatically accept the default choice +sub _prompt { + goto &ExtUtils::MakeMaker::prompt unless $AcceptDefault; + + my ($prompt, $default) = @_; + my $y = ($default =~ /^[Yy]/); + + print $prompt, ' [', ($y ? 'Y' : 'y'), '/', ($y ? 'n' : 'N'), '] '; + print "$default\n"; + return $default; +} + +# the workhorse +sub import { + my $class = shift; + my @args = @_ or return; + my $core_all; + + print "*** $class version ".$class->VERSION."\n"; + print "*** Checking for dependencies...\n"; + + my $cwd = Cwd::cwd(); + + $Config = []; + + my $maxlen = length((sort { length($b) <=> length($a) } + grep { /^[^\-]/ } + map { ref($_) ? keys %{ref($_) eq 'HASH' ? $_ : +{@{$_}}} : '' } + map { +{@args}->{$_} } + grep { /^[^\-]/ or /^-core$/i } keys %{+{@args}})[0]); + + while (my ($feature, $modules) = splice(@args, 0, 2)) { + my (@required, @tests, @skiptests); + my $default = 1; + my $conflict = 0; + + if ($feature =~ m/^-(\w+)$/) { + my $option = lc($1); + + # check for a newer version of myself + _update_to($modules, @_) and return if $option eq 'version'; + + # sets CPAN configuration options + $Config = $modules if $option eq 'config'; + + # promote every features to core status + $core_all = ($modules =~ /^all$/i) and next + if $option eq 'core'; + + next unless $option eq 'core'; + } + + print "[".($FeatureMap{lc($feature)} || $feature)."]\n"; + + $modules = [ %{$modules} ] if UNIVERSAL::isa($modules, 'HASH'); + + unshift @$modules, -default => &{shift(@$modules)} + if (ref($modules->[0]) eq 'CODE'); # XXX: bugward combatability + + while (my ($mod, $arg) = splice(@$modules, 0, 2)) { + if ($mod =~ m/^-(\w+)$/) { + my $option = lc($1); + + $default = $arg if ($option eq 'default'); + $conflict = $arg if ($option eq 'conflict'); + @tests = @{$arg} if ($option eq 'tests'); + @skiptests = @{$arg} if ($option eq 'skiptests'); + + next; + } + + printf("- %-${maxlen}s ...", $mod); + + # XXX: check for conflicts and uninstalls(!) them. + if (defined(my $cur = _version_check(_load($mod), $arg ||= 0))) { + print "loaded. ($cur".($arg ? " >= $arg" : '').")\n"; + push @Existing, $mod => $arg; + $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; + } + else { + print "missing." . ($arg ? " (would need $arg)" : '') . "\n"; + push @required, $mod => $arg; + } + } + + next unless @required; + + my $mandatory = ($feature eq '-core' or $core_all); + + if (!$SkipInstall and ($CheckOnly or _prompt( + qq{==> Auto-install the }. (@required / 2). + ($mandatory ? ' mandatory' : ' optional'). + qq{ module(s) from CPAN?}, $default ? 'y' : 'n', + ) =~ /^[Yy]/)) { + push (@Missing, @required); + $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; + } + + elsif (!$SkipInstall and $default and $mandatory and _prompt( + qq{==> The module(s) are mandatory! Really skip?}, 'n', + ) =~ /^[Nn]/) { + push (@Missing, @required); + $DisabledTests{$_} = 1 for map { glob($_) } @skiptests; + } + + else { + $DisabledTests{$_} = 1 for map { glob($_) } @tests; + } + } + + _check_lock(); # check for $UnderCPAN + + if (@Missing and not ($CheckOnly or $UnderCPAN)) { + require Config; + print "*** Dependencies will be installed the next time you type '$Config::Config{make}'.\n"; + # make an educated guess of whether we'll need root permission. + print " (You may need to do that as the 'root' user.)\n" if eval '$>'; + } + print "*** $class configuration finished.\n"; + + chdir $cwd; + + # import to main:: + no strict 'refs'; + *{'main::WriteMakefile'} = \&Write if caller(0) eq 'main'; +} + +# CPAN.pm is non-reentrant, so check if we're under it and have no CPANPLUS +sub _check_lock { + return unless @Missing; + return if _has_cpanplus(); + + require CPAN; CPAN::Config->load; + my $lock = MM->catfile($CPAN::Config->{cpan_home}, ".lock"); + + if (-f $lock and open(LOCK, $lock) + and ($^O eq 'MSWin32' ? _under_cpan() : == getppid()) + and ($CPAN::Config->{prerequisites_policy} || '') ne 'ignore' + ) { + print << '.'; + +*** Since we're running under CPAN, I'll just let it take care + of the dependency's installation later. +. + $UnderCPAN = 1; + } + + close LOCK; +} + +sub install { + my $class = shift; + + my $i; # used below to strip leading '-' from config keys + my @config = (map { s/^-// if ++$i; $_ } @{+shift}); + + my (@modules, @installed); + while (my ($pkg, $ver) = splice(@_, 0, 2)) { + # grep out those already installed + if (defined(_version_check(_load($pkg), $ver))) { + push @installed, $pkg; + } + else { + push @modules, $pkg, $ver; + } + } + + return @installed unless @modules; # nothing to do + + print "*** Installing dependencies...\n"; + + return unless _connected_to('cpan.org'); + + my %args = @config; + my %failed; + local *FAILED; + if ($args{do_once} and open(FAILED, '.#autoinstall.failed')) { + while () { chomp; $failed{$_}++ } + close FAILED; + + my @newmod; + while (my ($k, $v) = splice(@modules, 0, 2)) { + push @newmod, ($k => $v) unless $failed{$k}; + } + @modules = @newmod; + } + + if (_has_cpanplus()) { + _install_cpanplus(\@modules, \@config); + } + else { + _install_cpan(\@modules, \@config); + } + + print "*** $class installation finished.\n"; + + # see if we have successfully installed them + while (my ($pkg, $ver) = splice(@modules, 0, 2)) { + if (defined(_version_check(_load($pkg), $ver))) { + push @installed, $pkg; + } + elsif ($args{do_once} and open(FAILED, '>> .#autoinstall.failed')) { + print FAILED "$pkg\n"; + } + } + + close FAILED if $args{do_once}; + + return @installed; +} + +sub _install_cpanplus { + my @modules = @{+shift}; + my @config = @{+shift}; + my $installed = 0; + + require CPANPLUS::Backend; + my $cp = CPANPLUS::Backend->new; + my $conf = $cp->configure_object; + + return unless _can_write($conf->_get_build('base')); + + # if we're root, set UNINST=1 to avoid trouble unless user asked for it. + my $makeflags = $conf->get_conf('makeflags') || ''; + if (UNIVERSAL::isa($makeflags, 'HASH')) { + # 0.03+ uses a hashref here + $makeflags->{UNINST} = 1 unless exists $makeflags->{UNINST}; + } + else { + # 0.02 and below uses a scalar + $makeflags = join(' ', split(' ', $makeflags), 'UNINST=1') + if ($makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' }); + } + $conf->set_conf(makeflags => $makeflags); + + while (my ($key, $val) = splice(@config, 0, 2)) { + eval { $conf->set_conf($key, $val) }; + } + + my $modtree = $cp->module_tree; + while (my ($pkg, $ver) = splice(@modules, 0, 2)) { + print "*** Installing $pkg...\n"; + + MY::preinstall($pkg, $ver) or next if defined &MY::preinstall; + + my $success; + my $obj = $modtree->{$pkg}; + + if ($obj and defined(_version_check($obj->{version}, $ver))) { + my $pathname = $pkg; $pathname =~ s/::/\\W/; + + foreach my $inc (grep { m/$pathname.pm/i } keys(%INC)) { + delete $INC{$inc}; + } + + my $rv = $cp->install( modules => [ $obj->{module} ]); + + if ($rv and ($rv->{$obj->{module}} or $rv->{ok})) { + print "*** $pkg successfully installed.\n"; + $success = 1; + } + else { + print "*** $pkg installation cancelled.\n"; + $success = 0; + } + + $installed += $success; + } + else { + print << "."; +*** Could not find a version $ver or above for $pkg; skipping. +. + } + + MY::postinstall($pkg, $ver, $success) if defined &MY::postinstall; + } + + return $installed; +} + +sub _install_cpan { + my @modules = @{+shift}; + my @config = @{+shift}; + my $installed = 0; + my %args; + + require CPAN; CPAN::Config->load; + + return unless _can_write(MM->catfile($CPAN::Config->{cpan_home}, 'sources')); + + # if we're root, set UNINST=1 to avoid trouble unless user asked for it. + my $makeflags = $CPAN::Config->{make_install_arg} || ''; + $CPAN::Config->{make_install_arg} = join(' ', split(' ', $makeflags), 'UNINST=1') + if ($makeflags !~ /\bUNINST\b/ and eval qq{ $> eq '0' }); + + # don't show start-up info + $CPAN::Config->{inhibit_startup_message} = 1; + + # set additional options + while (my ($opt, $arg) = splice(@config, 0, 2)) { + ($args{$opt} = $arg, next) + if $opt =~ /^force$/; # pseudo-option + $CPAN::Config->{$opt} = $arg; + } + + while (my ($pkg, $ver) = splice(@modules, 0, 2)) { + MY::preinstall($pkg, $ver) or next if defined &MY::preinstall; + + print "*** Installing $pkg...\n"; + + my $obj = CPAN::Shell->expand(Module => $pkg); + my $success = 0; + + if ($obj and defined(_version_check($obj->cpan_version, $ver))) { + my $pathname = $pkg; $pathname =~ s/::/\\W/; + + foreach my $inc (grep { m/$pathname.pm/i } keys(%INC)) { + delete $INC{$inc}; + } + + $obj->force('install') if $args{force}; + + if ($obj->install eq 'YES') { + print "*** $pkg successfully installed.\n"; + $success = 1; + } + else { + print "*** $pkg installation failed.\n"; + $success = 0; + } + + $installed += $success; + } + else { + print << "."; +*** Could not find a version $ver or above for $pkg; skipping. +. + } + + MY::postinstall($pkg, $ver, $success) if defined &MY::postinstall; + } + + return $installed; +} + +sub _has_cpanplus { + return ( + $HasCPANPLUS = ( + $INC{'CPANPLUS/Config.pm'} or + _load('CPANPLUS::Shell::Default') + ) + ); +} + +# make guesses on whether we're under the CPAN installation directory +sub _under_cpan { + require Cwd; + require File::Spec; + + my $cwd = File::Spec->canonpath(Cwd::cwd()); + my $cpan = File::Spec->canonpath($CPAN::Config->{cpan_home}); + + return (index($cwd, $cpan) > -1); +} + +sub _update_to { + my $class = __PACKAGE__; + my $ver = shift; + + return if defined(_version_check(_load($class), $ver)); # no need to upgrade + + if (_prompt( + "==> A newer version of $class ($ver) is required. Install?", 'y' + ) =~ /^[Nn]/) { + die "*** Please install $class $ver manually.\n"; + } + + print << "."; +*** Trying to fetch it from CPAN... +. + + # install ourselves + _load($class) and return $class->import(@_) + if $class->install([], $class, $ver); + + print << '.'; exit 1; + +*** Cannot bootstrap myself. :-( Installation terminated. +. +} + +# check if we're connected to some host, using inet_aton +sub _connected_to { + my $site = shift; + + return ( + ( _load('Socket') and Socket::inet_aton($site) ) or _prompt(qq( +*** Your host cannot resolve the domain name '$site', which + probably means the Internet connections are unavailable. +==> Should we try to install the required module(s) anyway?), 'n' + ) =~ /^[Yy]/ + ); +} + +# check if a directory is writable; may create it on demand +sub _can_write { + my $path = shift; + mkdir ($path, 0755) unless -e $path; + + require Config; + return 1 if -w $path and -w $Config::Config{sitelib}; + + print << "."; +*** You are not allowed to write to the directory '$path'; + the installation may fail due to insufficient permissions. +. + + if (eval '$>' and lc(`sudo -V`) =~ /version/ and _prompt(qq( +==> Should we try to re-execute the autoinstall process with 'sudo'?), 'y' + ) =~ /^[Yy]/) { + # try to bootstrap ourselves from sudo + print << "."; +*** Trying to re-execute the autoinstall process with 'sudo'... +. + my $missing = join(',', @Missing); + my $config = join(',', + UNIVERSAL::isa($Config, 'HASH') ? %{$Config} : @{$Config} + ) if $Config; + + return unless system('sudo', $^X, $0, "--config=$config", "--installdeps=$missing"); + + print << "."; +*** The 'sudo' command exited with error! Resuming... +. + } + + return _prompt(qq( +==> Should we try to install the required module(s) anyway?), 'n' + ) =~ /^[Yy]/ +} + +# load a module and return the version it reports +sub _load { + my $mod = pop; # class/instance doesn't matter + my $file = $mod; + + $file =~ s|::|/|g; + $file .= '.pm'; + + local $@; + return eval { require $file; $mod->VERSION } || ($@ ? undef : 0); +} + +# compare two versions, either use Sort::Versions or plain comparison +sub _version_check { + my ($cur, $min) = @_; + return unless defined $cur; + + $cur =~ s/\s+$//; + + # check for version numbers that are not in decimal format + if (ref($cur) or ref($min) or $cur =~ /v|\..*\./ or $min =~ /v|\..*\./) { + if ($version::VERSION or defined(_load('version'))) { + # use version.pm if it is installed. + return ((version->new($cur) >= version->new($min)) ? $cur : undef); + } + elsif ($Sort::Versions::VERSION or defined(_load('Sort::Versions'))) { + # use Sort::Versions as the sorting algorithm for a.b.c versions + return ((Sort::Versions::versioncmp($cur, $min) != -1) ? $cur : undef); + } + + warn "Cannot reliably compare non-decimal formatted versions.\n". + "Please install version.pm or Sort::Versions.\n"; + } + + # plain comparison + local $^W = 0; # shuts off 'not numeric' bugs + return ($cur >= $min ? $cur : undef); +} + +# nothing; this usage is deprecated. +sub main::PREREQ_PM { return {}; } + +sub _make_args { + my %args = @_; + + $args{PREREQ_PM} = { %{$args{PREREQ_PM} || {} }, @Existing, @Missing } + if $UnderCPAN or $TestOnly; + + if ($args{EXE_FILES}) { + require ExtUtils::Manifest; + my $manifest = ExtUtils::Manifest::maniread('MANIFEST'); + + $args{EXE_FILES} = [ + grep { exists $manifest->{$_} } @{$args{EXE_FILES}} + ]; + } + + $args{test}{TESTS} ||= 't/*.t'; + $args{test}{TESTS} = join(' ', grep { + !exists($DisabledTests{$_}) + } map { glob($_) } split(/\s+/, $args{test}{TESTS})); + + my $missing = join(',', @Missing); + my $config = join(',', + UNIVERSAL::isa($Config, 'HASH') ? %{$Config} : @{$Config} + ) if $Config; + + $PostambleActions = ( + $missing ? "\$(PERL) $0 --config=$config --installdeps=$missing" + : "\@\$(NOOP)" + ); + + return %args; +} + +# a wrapper to ExtUtils::MakeMaker::WriteMakefile +sub Write { + require Carp; + Carp::croak "WriteMakefile: Need even number of args" if @_ % 2; + + if ($CheckOnly) { + print << "."; +*** Makefile not written in check-only mode. +. + return; + } + + my %args = _make_args(@_); + + no strict 'refs'; + + $PostambleUsed = 0; + local *MY::postamble = \&postamble unless defined &MY::postamble; + ExtUtils::MakeMaker::WriteMakefile(%args); + + print << "." unless $PostambleUsed; +*** WARNING: Makefile written with customized MY::postamble() without + including contents from ExtUtils::AutoInstall::postamble() -- + auto installation features disabled. Please contact the author. +. + + return 1; +} + +sub postamble { + $PostambleUsed = 1; + + return << "."; + +config :: installdeps +\t\@\$(NOOP) + +checkdeps :: +\t\$(PERL) $0 --checkdeps + +installdeps :: +\t$PostambleActions + +. + +} + +1; + +__END__ + +#line 929 diff --git a/inc/Module/Install.pm b/inc/Module/Install.pm new file mode 100644 index 0000000..eeb4c1e --- /dev/null +++ b/inc/Module/Install.pm @@ -0,0 +1,171 @@ +#line 1 "inc/Module/Install.pm - /Library/Perl/5.8.1/Module/Install.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install.pm $ $Author: autrijus $ +# $Revision: #67 $ $Change: 1885 $ $DateTime: 2004/03/11 05:55:27 $ vim: expandtab shiftwidth=4 + +package Module::Install; +$VERSION = '0.33'; + +die << "." unless $INC{join('/', inc => split(/::/, __PACKAGE__)).'.pm'}; +Please invoke ${\__PACKAGE__} with: + + use inc::${\__PACKAGE__}; + +not: + + use ${\__PACKAGE__}; + +. + +use strict 'vars'; +use Cwd (); +use File::Find (); +use File::Path (); + +@inc::Module::Install::ISA = 'Module::Install'; + +#line 129 + +sub import { + my $class = shift; + my $self = $class->new(@_); + + if (not -f $self->{file}) { + require "$self->{path}/$self->{dispatch}.pm"; + File::Path::mkpath("$self->{prefix}/$self->{author}"); + $self->{admin} = + "$self->{name}::$self->{dispatch}"->new(_top => $self); + $self->{admin}->init; + @_ = ($class, _self => $self); + goto &{"$self->{name}::import"}; + } + + *{caller(0) . "::AUTOLOAD"} = $self->autoload; + + # Unregister loader and worker packages so subdirs can use them again + delete $INC{"$self->{file}"}; + delete $INC{"$self->{path}.pm"}; +} + +#line 156 + +sub autoload { + my $self = shift; + my $caller = caller; + + my $cwd = Cwd::cwd(); + my $sym = "$caller\::AUTOLOAD"; + + $sym->{$cwd} = sub { + my $pwd = Cwd::cwd(); + if (my $code = $sym->{$pwd}) { + goto &$code unless $cwd eq $pwd; # delegate back to parent dirs + } + $$sym =~ /([^:]+)$/ or die "Cannot autoload $caller"; + unshift @_, ($self, $1); + goto &{$self->can('call')} unless uc($1) eq $1; + }; +} + +#line 181 + +sub new { + my ($class, %args) = @_; + + return $args{_self} if $args{_self}; + + $args{dispatch} ||= 'Admin'; + $args{prefix} ||= 'inc'; + $args{author} ||= '.author'; + $args{bundle} ||= 'inc/BUNDLES'; + + $class =~ s/^\Q$args{prefix}\E:://; + $args{name} ||= $class; + $args{version} ||= $class->VERSION; + + unless ($args{path}) { + $args{path} = $args{name}; + $args{path} =~ s!::!/!g; + } + $args{file} ||= "$args{prefix}/$args{path}.pm"; + + bless(\%args, $class); +} + +#line 210 + +sub call { + my $self = shift; + my $method = shift; + my $obj = $self->load($method) or return; + + unshift @_, $obj; + goto &{$obj->can($method)}; +} + +#line 225 + +sub load { + my ($self, $method) = @_; + + $self->load_extensions( + "$self->{prefix}/$self->{path}", $self + ) unless $self->{extensions}; + + foreach my $obj (@{$self->{extensions}}) { + return $obj if $obj->can($method); + } + + my $admin = $self->{admin} or die << "END"; +The '$method' method does not exist in the '$self->{prefix}' path! +Please remove the '$self->{prefix}' directory and run $0 again to load it. +END + + my $obj = $admin->load($method, 1); + push @{$self->{extensions}}, $obj; + + $obj; +} + +#line 255 + +sub load_extensions { + my ($self, $path, $top_obj) = @_; + + unshift @INC, $self->{prefix} + unless grep { $_ eq $self->{prefix} } @INC; + + local @INC = ($path, @INC); + foreach my $rv ($self->find_extensions($path)) { + my ($file, $pkg) = @{$rv}; + next if $self->{pathnames}{$pkg}; + + eval { require $file; 1 } or (warn($@), next); + $self->{pathnames}{$pkg} = delete $INC{$file}; + push @{$self->{extensions}}, $pkg->new( _top => $top_obj ); + } +} + +#line 279 + +sub find_extensions { + my ($self, $path) = @_; + my @found; + + File::Find::find(sub { + my $file = $File::Find::name; + return unless $file =~ m!^\Q$path\E/(.+)\.pm\Z!is; + return if $1 eq $self->{dispatch}; + + $file = "$self->{path}/$1.pm"; + my $pkg = "$self->{name}::$1"; $pkg =~ s!/!::!g; + push @found, [$file, $pkg]; + }, $path) if -d $path; + + @found; +} + +1; + +__END__ + +#line 614 diff --git a/inc/Module/Install/AutoInstall.pm b/inc/Module/Install/AutoInstall.pm new file mode 100644 index 0000000..c22821c --- /dev/null +++ b/inc/Module/Install/AutoInstall.pm @@ -0,0 +1,65 @@ +#line 1 "inc/Module/Install/AutoInstall.pm - /Library/Perl/5.8.1/Module/Install/AutoInstall.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/AutoInstall.pm $ $Author: autrijus $ +# $Revision: #13 $ $Change: 1846 $ $DateTime: 2003/12/31 22:57:12 $ vim: expandtab shiftwidth=4 + +package Module::Install::AutoInstall; +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +sub AutoInstall { $_[0] } + +sub run { + my $self = shift; + $self->auto_install_now(@_); +} + +sub write { + my $self = shift; + $self->auto_install(@_); +} + +sub auto_install { + my $self = shift; + return if $self->{done}++; + +# ExtUtils::AutoInstall Bootstrap Code, version 7. +AUTO:{my$p='ExtUtils::AutoInstall';my$v=0.49;$p->VERSION||0>=$v +or+eval"use $p $v;1"or+do{my$e=$ENV{PERL_EXTUTILS_AUTOINSTALL}; +(!defined($e)||$e!~m/--(?:default|skip|testonly)/and-t STDIN or +eval"use ExtUtils::MakeMaker;WriteMakefile(PREREQ_PM=>{'$p',$v} +);1"and exit)and print"==> $p $v required. Install it from CP". +"AN? [Y/n] "and!~/^n/i and print"*** Installing $p\n"and +do{if (eval '$>' and lc(`sudo -V`) =~ /version/){system('sudo', +$^X,"-MCPANPLUS","-e","CPANPLUS::install $p");eval"use $p $v;1" +||system('sudo', $^X, "-MCPAN", "-e", "CPAN::install $p")}eval{ +require CPANPLUS;CPANPLUS::install$p};eval"use $p $v;1"or eval{ +require CPAN;CPAN::install$p};eval"use $p $v;1"||die"*** Please +manually install $p $v from cpan.org first...\n"}}} + + # Flatten array of arrays into a single array + my @core = map @$_, map @$_, grep ref, + $self->build_requires, $self->requires; + + while ( @core and @_ > 1 and $_[0] =~ /^-\w+$/ ) { + push @core, splice(@_, 0, 2); + } + + ExtUtils::AutoInstall->import( + (@core ? (-core => \@core) : ()), @_, $self->features + ); + + $self->makemaker_args( ExtUtils::AutoInstall::_make_args() ); + + my $class = ref($self); + $self->postamble( + "# --- $class section:\n" . + ExtUtils::AutoInstall::postamble() + ); +} + +sub auto_install_now { + my $self = shift; + $self->auto_install; + ExtUtils::AutoInstall::do_install(); +} + +1; diff --git a/inc/Module/Install/Base.pm b/inc/Module/Install/Base.pm new file mode 100644 index 0000000..ac43208 --- /dev/null +++ b/inc/Module/Install/Base.pm @@ -0,0 +1,57 @@ +#line 1 "inc/Module/Install/Base.pm - /Library/Perl/5.8.1/Module/Install/Base.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Base.pm $ $Author: autrijus $ +# $Revision: #10 $ $Change: 1847 $ $DateTime: 2003/12/31 23:14:54 $ vim: expandtab shiftwidth=4 + +package Module::Install::Base; + +#line 31 + +sub new { + my ($class, %args) = @_; + + foreach my $method (qw(call load)) { + *{"$class\::$method"} = sub { + +shift->_top->$method(@_); + } unless defined &{"$class\::$method"}; + } + + bless(\%args, $class); +} + +#line 49 + +sub AUTOLOAD { + my $self = shift; + goto &{$self->_top->autoload}; +} + +#line 60 + +sub _top { $_[0]->{_top} } + +#line 71 + +sub admin { + my $self = shift; + $self->_top->{admin} or Module::Install::Base::FakeAdmin->new; +} + +sub is_admin { + my $self = shift; + $self->admin->VERSION; +} + +sub DESTROY {} + +package Module::Install::Base::FakeAdmin; + +my $Fake; +sub new { $Fake ||= bless(\@_, $_[0]) } +sub AUTOLOAD {} +sub DESTROY {} + +1; + +__END__ + +#line 115 diff --git a/inc/Module/Install/Build.pm b/inc/Module/Install/Build.pm new file mode 100644 index 0000000..6fb595e --- /dev/null +++ b/inc/Module/Install/Build.pm @@ -0,0 +1,66 @@ +#line 1 "inc/Module/Install/Build.pm - /Library/Perl/5.8.1/Module/Install/Build.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Build.pm $ $Author: ingy $ +# $Revision: #23 $ $Change: 1255 $ $DateTime: 2003/03/05 13:23:32 $ vim: expandtab shiftwidth=4 + +package Module::Install::Build; +$VERSION = '0.01'; +use strict; +use vars qw(@ISA); +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +sub Build { $_[0] } + +sub write { + my $self = shift; + die "Build->write() takes no arguments\n" if @_; + + my %args; + my $build; + + $args{dist_name} = $self->name || $self->determine_NAME($self->{args}); + $args{license} = $self->license; + $args{dist_version} = $self->version || $self->determine_VERSION($self->{args}); + $args{dist_abstract} = $self->abstract; + $args{dist_author} = $self->author; + $args{sign} = $self->sign; + $args{no_index} = $self->no_index; + + foreach my $key (qw(build_requires requires recommends conflicts)) { + my $val = eval "\$self->$key" or next; + $args{$key} = { map @$_, @$val }; + } + + %args = map {($_, $args{$_})} grep {defined($args{$_})} keys %args; + + require Module::Build; + $build = Module::Build->new(%args); + $build->add_to_cleanup(split /\s+/, $self->clean_files); + $build->create_build_script; +} + +sub ACTION_reset { + my ($self) = @_; + die "XXX - Can't get this working yet"; + require File::Path; + warn "Removing inc\n"; + rmpath('inc'); +} + +sub ACTION_dist { + my ($self) = @_; + die "XXX - Can't get this working yet"; +} + +# DrMath: is there an OO way to add actions to Module::Build?? +# ingy: yeah +# ingy: package MyBuilder; use w(Module::Build; @ISA = qw(w(Module::Build); sub ACTION_ingy +# {...} +# ingy: then my $build = new MyBuilder( ...parameters... ); +# $build->write_build_script; + + +1; + +__END__ + +#line 178 diff --git a/inc/Module/Install/Can.pm b/inc/Module/Install/Can.pm new file mode 100644 index 0000000..fe6ac24 --- /dev/null +++ b/inc/Module/Install/Can.pm @@ -0,0 +1,41 @@ +#line 1 "inc/Module/Install/Can.pm - /Library/Perl/5.8.1/Module/Install/Can.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Can.pm $ $Author: autrijus $ +# $Revision: #6 $ $Change: 1840 $ $DateTime: 2003/12/28 19:42:02 $ vim: expandtab shiftwidth=4 + +package Module::Install::Can; +use Module::Install::Base; @ISA = qw(Module::Install::Base); +$VERSION = '0.01'; + +use strict; +use Config (); +use File::Spec (); +use ExtUtils::MakeMaker (); + +# check if we can run some command +sub can_run { + my ($self, $cmd) = @_; + + my $_cmd = $cmd; + return $_cmd if (-x $_cmd or $_cmd = MM->maybe_command($_cmd)); + + for my $dir ((split /$Config::Config{path_sep}/, $ENV{PATH}), '.') { + my $abs = File::Spec->catfile($dir, $_[1]); + return $abs if (-x $abs or $abs = MM->maybe_command($abs)); + } + + return; +} + +sub can_cc { + my $self = shift; + my @chunks = split(/ /, $Config::Config{cc}) or return; + + # $Config{cc} may contain args; try to find out the program part + while (@chunks) { + return $self->can_run("@chunks") || (pop(@chunks), next); + } + + return; +} + +1; diff --git a/inc/Module/Install/Fetch.pm b/inc/Module/Install/Fetch.pm new file mode 100644 index 0000000..6c9a1d7 --- /dev/null +++ b/inc/Module/Install/Fetch.pm @@ -0,0 +1,89 @@ +#line 1 "inc/Module/Install/Fetch.pm - /Library/Perl/5.8.1/Module/Install/Fetch.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Fetch.pm $ $Author: autrijus $ +# $Revision: #8 $ $Change: 1374 $ $DateTime: 2003/03/18 11:50:15 $ vim: expandtab shiftwidth=4 + +package Module::Install::Fetch; +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +$VERSION = '0.01'; + +sub get_file { + my ($self, %args) = @_; + my ($scheme, $host, $path, $file) = + $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; + + if ($scheme eq 'http' and !eval { require LWP::Simple; 1 }) { + $args{url} = $args{ftp_url} + or (warn("LWP support unavailable!\n"), return); + ($scheme, $host, $path, $file) = + $args{url} =~ m|^(\w+)://([^/]+)(.+)/(.+)| or return; + } + + $|++; + print "Fetching '$file' from $host... "; + + unless (eval { require Socket; Socket::inet_aton($host) }) { + warn "'$host' resolve failed!\n"; + return; + } + + return unless $scheme eq 'ftp' or $scheme eq 'http'; + + require Cwd; + my $dir = Cwd::getcwd(); + chdir $args{local_dir} or return if exists $args{local_dir}; + + if (eval { require LWP::Simple; 1 }) { + LWP::Simple::mirror($args{url}, $file); + } + elsif (eval { require Net::FTP; 1 }) { eval { + # use Net::FTP to get past firewall + my $ftp = Net::FTP->new($host, Passive => 1, Timeout => 600); + $ftp->login("anonymous", 'anonymous@example.com'); + $ftp->cwd($path); + $ftp->binary; + $ftp->get($file) or (warn("$!\n"), return); + $ftp->quit; + } } + elsif (my $ftp = $self->can_run('ftp')) { eval { + # no Net::FTP, fallback to ftp.exe + require FileHandle; + my $fh = FileHandle->new; + + local $SIG{CHLD} = 'IGNORE'; + unless ($fh->open("|$ftp -n")) { + warn "Couldn't open ftp: $!\n"; + chdir $dir; return; + } + + my @dialog = split(/\n/, << "."); +open $host +user anonymous anonymous\@example.com +cd $path +binary +get $file $file +quit +. + foreach (@dialog) { $fh->print("$_\n") } + $fh->close; + } } + else { + warn "No working 'ftp' program available!\n"; + chdir $dir; return; + } + + unless (-f $file) { + warn "Fetching failed: $@\n"; + chdir $dir; return; + } + + return if exists $args{size} and -s $file != $args{size}; + system($args{run}) if exists $args{run}; + unlink($file) if $args{remove}; + + print(((!exists $args{check_for} or -e $args{check_for}) + ? "done!" : "failed! ($!)"), "\n"); + chdir $dir; return !$?; +} + +1; diff --git a/inc/Module/Install/Include.pm b/inc/Module/Install/Include.pm new file mode 100644 index 0000000..6adef92 --- /dev/null +++ b/inc/Module/Install/Include.pm @@ -0,0 +1,12 @@ +#line 1 "inc/Module/Install/Include.pm - /Library/Perl/5.8.1/Module/Install/Include.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Include.pm $ $Author: autrijus $ +# $Revision: #8 $ $Change: 1811 $ $DateTime: 2003/12/14 18:52:33 $ vim: expandtab shiftwidth=4 + +package Module::Install::Include; +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +sub include { +shift->admin->include(@_) }; +sub include_deps { +shift->admin->include_deps(@_) }; +sub auto_include { +shift->admin->auto_include(@_) }; + +1; diff --git a/inc/Module/Install/Makefile.pm b/inc/Module/Install/Makefile.pm new file mode 100644 index 0000000..41dff0c --- /dev/null +++ b/inc/Module/Install/Makefile.pm @@ -0,0 +1,146 @@ +#line 1 "inc/Module/Install/Makefile.pm - /Library/Perl/5.8.1/Module/Install/Makefile.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Makefile.pm $ $Author: autrijus $ +# $Revision: #53 $ $Change: 1847 $ $DateTime: 2003/12/31 23:14:54 $ vim: expandtab shiftwidth=4 + +package Module::Install::Makefile; +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +$VERSION = '0.01'; + +use strict 'vars'; +use vars '$VERSION'; + +use ExtUtils::MakeMaker (); + +sub Makefile { $_[0] } + +sub prompt { + shift; + goto &ExtUtils::MakeMaker::prompt; +} + +sub makemaker_args { + my $self = shift; + my $args = ($self->{makemaker_args} ||= {}); + %$args = ( %$args, @_ ) if @_; + $args; +} + +sub clean_files { + my $self = shift; + my $clean = $self->makemaker_args->{clean} ||= {}; + %$clean = ( + %$clean, + FILES => join(" ", grep length, $clean->{FILES}, @_), + ); +} + +sub libs { + my $self = shift; + my $libs = ref $_[0] ? shift : [shift]; + $self->makemaker_args( LIBS => $libs ); +} + +sub inc { + my $self = shift; + $self->makemaker_args( INC => shift ); +} + +sub write { + my $self = shift; + die "&Makefile->write() takes no arguments\n" if @_; + + my $args = $self->makemaker_args; + + $args->{DISTNAME} = $self->name; + $args->{NAME} = $self->module_name || $self->name || $self->determine_NAME($args); + $args->{VERSION} = $self->version || $self->determine_VERSION($args); + $args->{NAME} =~ s/-/::/g; + + if ($] >= 5.005) { + $args->{ABSTRACT} = $self->abstract; + $args->{AUTHOR} = $self->author; + } + if ( eval($ExtUtils::MakeMaker::VERSION) >= 6.10 ) { + $args->{NO_META} = 1; + } + if ( eval($ExtUtils::MakeMaker::VERSION) > 6.17 ) { + $args->{SIGN} = 1 if $self->sign; + } + delete $args->{SIGN} unless $self->is_admin; + + # merge both kinds of requires into prereq_pm + my $prereq = ($args->{PREREQ_PM} ||= {}); + %$prereq = ( %$prereq, map { @$_ } map { @$_ } grep $_, + ($self->build_requires, $self->requires) ); + + # merge both kinds of requires into prereq_pm + my $dir = ($args->{DIR} ||= []); + if ($self->bundles) { + push @$dir, map "$_->[1]", @{$self->bundles}; + delete $prereq->{$_->[0]} for @{$self->bundles}; + } + + if (my $perl_version = $self->perl_version) { + eval "use $perl_version; 1" + or die "ERROR: perl: Version $] is installed, ". + "but we need version >= $perl_version"; + } + + my %args = map {($_ => $args->{$_})} grep {defined($args->{$_})} keys %$args; + + if ($self->admin->preop) { + $args{dist} = $self->admin->preop; + } + + ExtUtils::MakeMaker::WriteMakefile(%args); + + $self->fix_up_makefile(); +} + +sub fix_up_makefile { + my $self = shift; + my $top_class = ref($self->_top) || ''; + my $top_version = $self->_top->VERSION || ''; + + my $preamble = $self->preamble + ? "# Preamble by $top_class $top_version\n" . $self->preamble + : ''; + my $postamble = "# Postamble by $top_class $top_version\n" . + ($self->postamble || ''); + + open MAKEFILE, '< Makefile' or die $!; + my $makefile = do { local $/; }; + close MAKEFILE; + + $makefile =~ s/\b(test_harness\(\$\(TEST_VERBOSE\), )/$1'inc', /; + $makefile =~ s/( -I\$\(INST_ARCHLIB\))/ -Iinc$1/g; + $makefile =~ s/( "-I\$\(INST_LIB\)")/ "-Iinc"$1/g; + + $makefile =~ s/^(FULLPERL = .*)/$1 -Iinc/m; + $makefile =~ s/^(PERL = .*)/$1 -Iinc/m; + + open MAKEFILE, '> Makefile' or die $!; + print MAKEFILE "$preamble$makefile$postamble"; + close MAKEFILE; +} + +sub preamble { + my ($self, $text) = @_; + $self->{preamble} = $text . $self->{preamble} if defined $text; + $self->{preamble}; +} + +sub postamble { + my ($self, $text) = @_; + + $self->{postamble} ||= $self->admin->postamble; + $self->{postamble} .= $text if defined $text; + $self->{postamble} +} + +1; + +__END__ + +#line 276 diff --git a/inc/Module/Install/Metadata.pm b/inc/Module/Install/Metadata.pm new file mode 100644 index 0000000..b966264 --- /dev/null +++ b/inc/Module/Install/Metadata.pm @@ -0,0 +1,190 @@ +#line 1 "inc/Module/Install/Metadata.pm - /Library/Perl/5.8.1/Module/Install/Metadata.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Metadata.pm $ $Author: autrijus $ +# $Revision: #32 $ $Change: 1885 $ $DateTime: 2004/03/11 05:55:27 $ vim: expandtab shiftwidth=4 + +package Module::Install::Metadata; +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +$VERSION = '0.04'; + +use strict 'vars'; +use vars qw($VERSION); + +sub Meta { shift } + +my @scalar_keys = qw( + name module_name version abstract author license + distribution_type sign perl_version +); +my @tuple_keys = qw(build_requires requires recommends bundles); + +foreach my $key (@scalar_keys) { + *$key = sub { + my $self = shift; + return $self->{'values'}{$key} unless @_; + $self->{'values'}{$key} = shift; + return $self; + }; +} + +foreach my $key (@tuple_keys) { + *$key = sub { + my $self = shift; + return $self->{'values'}{$key} unless @_; + my @rv; + while (@_) { + my $module = shift or last; + my $version = shift || 0; + if ($module eq 'perl') { + $version =~ s{^(\d+)\.(\d+)\.(\d+)} + {$1 + $2/1_000 + $3/1_000_000}e; + $self->perl_version($version); + next; + } + my $rv = [$module, $version]; + push @{$self->{'values'}{$key}}, $rv; + push @rv, $rv; + } + return @rv; + }; +} + +sub features { + my $self = shift; + while (my ($name, $mods) = splice(@_, 0, 2)) { + my $count = 0; + push @{$self->{'values'}{'features'}}, ($name => [ + map { (++$count % 2 and ref($_) and ($count += $#$_)) ? @$_ : $_ } @$mods + ] ); + } + return @{$self->{'values'}{'features'}}; +} + +sub no_index { + my $self = shift; + my $type = shift; + push @{$self->{'values'}{'no_index'}{$type}}, @_ if $type; + return $self->{'values'}{'no_index'}; +} + +sub _dump { + my $self = shift; + my $package = ref($self->_top); + my $version = $self->_top->VERSION; + my %values = %{$self->{'values'}}; + + delete $values{sign}; + if (my $perl_version = delete $values{perl_version}) { + # Always canonical to three-dot version + $perl_version =~ s{^(\d+)\.(\d\d\d)(\d*)}{join('.', $1, int($2), int($3))}e + if $perl_version >= 5.006; + $values{requires} = [ + [perl => $perl_version], + @{$values{requires}||[]}, + ]; + } + + warn "No license specified, setting license = 'unknown'\n" + unless $values{license}; + + $values{license} ||= 'unknown'; + $values{distribution_type} ||= 'module'; + $values{name} ||= do { + my $name = $values{module_name}; + $name =~ s/::/-/g; + $name; + } if $values{module_name}; + + if ($values{name} =~ /::/) { + my $name = $values{name}; + $name =~ s/::/-/g; + die "Error in name(): '$values{name}' should be '$name'!\n"; + } + + my $dump = ''; + foreach my $key (@scalar_keys) { + $dump .= "$key: $values{$key}\n" if exists $values{$key}; + } + foreach my $key (@tuple_keys) { + next unless exists $values{$key}; + $dump .= "$key:\n"; + foreach (@{$values{$key}}) { + $dump .= " $_->[0]: $_->[1]\n"; + } + } + + if (my $no_index = $values{no_index}) { + push @{$no_index->{'directory'}}, 'inc'; + require YAML; + local $YAML::UseHeader = 0; + $dump .= YAML::Dump({ no_index => $no_index}); + } + else { + $dump .= << "META"; +no_index: + directory: + - inc +META + } + + $dump .= "generated_by: $package version $version\n"; + return $dump; +} + +sub read { + my $self = shift; + $self->include_deps( 'YAML', 0 ); + require YAML; + my $data = YAML::LoadFile( 'META.yml' ); + # Call methods explicitly in case user has already set some values. + while ( my ($key, $value) = each %$data ) { + next unless $self->can( $key ); + if (ref $value eq 'HASH') { + while (my ($module, $version) = each %$value) { + $self->$key( $module => $version ); + } + } + else { + $self->$key( $value ); + } + } + return $self; +} + +sub write { + my $self = shift; + return $self unless $self->is_admin; + + META_NOT_OURS: { + local *FH; + if (open FH, "META.yml") { + while () { + last META_NOT_OURS if /^generated_by: Module::Install\b/; + } + return $self if -s FH; + } + } + + warn "Writing META.yml\n"; + open META, "> META.yml" or warn "Cannot write to META.yml: $!"; + print META $self->_dump; + close META; + return $self; +} + +sub version_from { + my ($self, $version_from) = @_; + require ExtUtils::MM_Unix; + $self->version(ExtUtils::MM_Unix->parse_version($version_from)); +} + +sub abstract_from { + my ($self, $abstract_from) = @_; + require ExtUtils::MM_Unix; + $self->abstract( + bless( { DISTNAME => $self->name }, 'ExtUtils::MM_Unix') + ->parse_abstract($abstract_from) + ); +} + +1; diff --git a/inc/Module/Install/Win32.pm b/inc/Module/Install/Win32.pm new file mode 100644 index 0000000..a72d8ed --- /dev/null +++ b/inc/Module/Install/Win32.pm @@ -0,0 +1,66 @@ +#line 1 "inc/Module/Install/Win32.pm - /Library/Perl/5.8.1/Module/Install/Win32.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/Win32.pm $ $Author: autrijus $ +# $Revision: #9 $ $Change: 1789 $ $DateTime: 2003/11/11 01:22:54 $ vim: expandtab shiftwidth=4 + +package Module::Install::Win32; +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +$VERSION = '0.02'; + +use strict; + +# determine if the user needs nmake, and download it if needed +sub check_nmake { + my $self = shift; + $self->load('can_run'); + $self->load('get_file'); + + require Config; + return unless ( + $Config::Config{make} and + $Config::Config{make} =~ /^nmake\b/i and + $^O eq 'MSWin32' and + !$self->can_run('nmake') + ); + + print "The required 'nmake' executable not found, fetching it...\n"; + + require File::Basename; + my $rv = $self->get_file( + url => 'http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe', + ftp_url => 'ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe', + local_dir => File::Basename::dirname($^X), + size => 51928, + run => 'Nmake15.exe /o > nul', + check_for => 'Nmake.exe', + remove => 1, + ); + + if (!$rv) { + die << '.'; + +------------------------------------------------------------------------------- + +Since you are using Microsoft Windows, you will need the 'nmake' utility +before installation. It's available at: + + http://download.microsoft.com/download/vc15/Patch/1.52/W95/EN-US/Nmake15.exe + or + ftp://ftp.microsoft.com/Softlib/MSLFILES/Nmake15.exe + +Please download the file manually, save it to a directory in %PATH% (e.g. +C:\WINDOWS\COMMAND\), then launch the MS-DOS command line shell, "cd" to +that directory, and run "Nmake15.exe" from there; that will create the +'nmake.exe' file needed by this module. + +You may then resume the installation process described in README. + +------------------------------------------------------------------------------- +. + } +} + +1; + +__END__ + diff --git a/inc/Module/Install/WriteAll.pm b/inc/Module/Install/WriteAll.pm new file mode 100644 index 0000000..8418d98 --- /dev/null +++ b/inc/Module/Install/WriteAll.pm @@ -0,0 +1,39 @@ +#line 1 "inc/Module/Install/WriteAll.pm - /Library/Perl/5.8.1/Module/Install/WriteAll.pm" +# $File: //depot/cpan/Module-Install/lib/Module/Install/WriteAll.pm $ $Author: autrijus $ +# $Revision: #3 $ $Change: 1885 $ $DateTime: 2004/03/11 05:55:27 $ vim: expandtab shiftwidth=4 + +package Module::Install::WriteAll; +use Module::Install::Base; @ISA = qw(Module::Install::Base); + +sub WriteAll { + my $self = shift; + my %args = ( + meta => 1, + sign => 0, + inline => 0, + check_nmake => 1, + @_ + ); + + $self->sign(1) if $args{sign}; + $self->Meta->write if $args{meta}; + $self->admin->WriteAll(%args) if $self->is_admin; + + if ($0 =~ /Build.PL$/i) { + $self->Build->write; + } + else { + $self->check_nmake if $args{check_nmake}; + $self->makemaker_args( PL_FILES => {} ) + unless $self->makemaker_args->{'PL_FILES'}; + + if ($args{inline}) { + $self->Inline->write; + } + else { + $self->Makefile->write; + } + } +} + +1; diff --git a/lib/XML/Feed.pm b/lib/XML/Feed.pm new file mode 100644 index 0000000..7621bbb --- /dev/null +++ b/lib/XML/Feed.pm @@ -0,0 +1,250 @@ +# $Id: Feed.pm,v 1.6 2004/05/30 16:59:02 btrott Exp $ + +package XML::Feed; +use strict; + +use base qw( XML::Feed::ErrorHandler ); +use LWP::UserAgent; +use HTML::Parser; + +use vars qw( $VERSION ); +$VERSION = '0.01'; + +use constant FEED_MIME_TYPES => [ + 'application/x.atom+xml', + 'application/atom+xml', + 'text/xml', + 'application/rss+xml', + 'application/rdf+xml', +]; + +sub parse { + my $class = shift; + my($stream) = @_; + return $class->error("Stream parameter is required") unless $stream; + my $feed = bless {}, $class; + my $xml = ''; + if (UNIVERSAL::isa($stream, 'URI')) { + my $ua = LWP::UserAgent->new; + my $req = HTTP::Request->new(GET => $stream); + my $res = $ua->request($req); + if ($res->is_success) { + $xml = $res->content; + } + } elsif (ref($stream) eq 'SCALAR') { + $xml = $$stream; + } elsif (ref($stream)) { + while (read($stream, my($chunk), 8192)) { + $xml .= $chunk; + } + } else { + open my $fh, $stream + or return $class->error("Can't open $stream: $!"); + while (read $fh, my($chunk), 8192) { + $xml .= $chunk; + } + close $fh; + } + return $class->error("Can't get feed XML content from $stream") + unless $xml; + ## Auto-detect feed type based on first element. This is prone + ## to breakage, but then again we don't want to parse the whole + ## feed ourselves. + my($tag) = $xml =~ /<([a-zA-Z]\S+)/s; + $tag =~ s/^.*://; + if ($tag eq 'rss' || $tag eq 'RDF') { + require XML::Feed::RSS; + bless $feed, 'XML::Feed::RSS'; + } elsif ($tag eq 'feed') { + require XML::Feed::Atom; + bless $feed, 'XML::Feed::Atom'; + } else { + return $class->error("Cannot detect feed type"); + } + $feed->init_string($xml) or return; + $feed; +} + +sub find_feeds { + my $class = shift; + my($uri) = @_; + my $ua = LWP::UserAgent->new; + my $req = HTTP::Request->new(GET => $uri); + my $res = $ua->request($req); + return unless $res->is_success; + my @feeds; + my %is_feed = map { $_ => 1 } @{ FEED_MIME_TYPES() }; + my $ct = $res->content_type; + if ($is_feed{$ct}) { + @feeds = ($uri); + } elsif ($ct eq 'text/html' || $ct eq 'application/xhtml+xml') { + my $base_uri = $uri; + my $find_links = sub { + my($tag, $attr) = @_; + if ($tag eq 'link') { + return unless $attr->{rel}; + my %rel = map { $_ => 1 } split /\s+/, lc($attr->{rel}); + (my $type = lc $attr->{type}) =~ s/^\s*//; + $type =~ s/\s*$//; + push @feeds, URI->new_abs($attr->{href}, $base_uri)->as_string + if $is_feed{$type} && + ($rel{alternate} || $rel{'service.feed'}); + } elsif ($tag eq 'base') { + $base_uri = $attr->{href}; + } + }; + my $p = HTML::Parser->new(api_version => 3, + start_h => [ $find_links, "tagname, attr" ]); + $p->parse($res->content); + } + @feeds; +} + +sub format; +sub title; +sub link; +sub description; +sub language; +sub copyright; +sub modified; +sub generator; +sub entries; + +sub tagline { $_[0]->description } +sub items { $_[0]->entries } + +1; +__END__ + +=head1 NAME + +XML::Feed - Syndication feed parser and auto-discovery + +=head1 SYNOPSIS + + use XML::Feed; + my $feed = XML::Feed->parse(URI->new('http://example.com/atom.xml')) + or die XML::Feed->errstr; + print $feed->title, "\n"; + for my $entry ($feed->entries) { + } + + ## Find all of the syndication feeds on a given page, using + ## auto-discovery. + my @feeds = XML::Feed->find_feeds('http://example.com/'); + +=head1 DESCRIPTION + +I is a syndication feed parser for both RSS and Atom feeds. It +also implements feed auto-discovery for finding feeds, given a URI. + +I supports the following syndication feed formats: + +=over 4 + +=item * RSS 0.91 + +=item * RSS 1.0 + +=item * RSS 2.0 + +=item * Atom + +=back + +The goal of I is to provide a unified API for parsing and using +the various syndication formats. The different flavors of RSS and Atom +handle data in different ways: date handling; summaries and content; +escaping and quoting; etc. This module attempts to remove those differences +by providing a wrapper around the formats and the classes implementing +those formats (I and I). For example, dates are +handled differently in each of the above formats. To provide a unified API for +date handling, I converts all date formats transparently into +I objects, which it then returns to the caller. + +=head1 USAGE + +=head2 XML::Feed->parse($stream) + +Parses a syndication feed identified by I<$stream>. I<$stream> can be any +one of the following: + +=over 4 + +=item * Scalar reference + +A reference to string containing the XML body of the feed. + +=item * Filehandle + +An open filehandle from which the feed XML will be read. + +=item * File name + +The name of a file containing the feed XML. + +=item * URI object + +A URI from which the feed XML will be retrieved. + +=back + +=head2 XML::Feed->find_feeds($uri) + +Given a URI I<$uri>, use auto-discovery to find all of the feeds linked +from that page (using IlinkE> tags). + +Returns a list of feed URIs. + +=head2 $feed->format + +Returns the format of the feed (C, or some version of C). + +=head2 $feed->title + +The title of the feed/channel. + +=head2 $feed->link + +The permalink of the feed/channel. + +=head2 $feed->tagline + +The description or tagline of the feed/channel. + +=head2 $feed->description + +Alias for I<$feed-Etagline>. + +=head2 $feed->language + +The language of the feed. + +=head2 $feed->copyright + +The copyright notice of the feed. + +=head2 $feed->modified + +A I object representing the last-modified date of the feed. + +=head2 $feed->generator + +The generator of the feed. + +=head2 $feed->entries + +A list of the entries/items in the feed. Returns an array containing +I objects. + +=head1 LICENSE + +I is free software; you may redistribute it and/or modify it +under the same terms as Perl itself. + +=head1 AUTHOR & COPYRIGHT + +Except where otherwise noted, I is Copyright 2004 Benjamin +Trott, cpan@stupidfool.org. All rights reserved. + +=cut diff --git a/lib/XML/Feed/Atom.pm b/lib/XML/Feed/Atom.pm new file mode 100644 index 0000000..1ba03d1 --- /dev/null +++ b/lib/XML/Feed/Atom.pm @@ -0,0 +1,66 @@ +# $Id: Atom.pm,v 1.1.1.1 2004/05/29 17:29:56 btrott Exp $ + +package XML::Feed::Atom; +use strict; + +use base qw( XML::Feed ); +use XML::Atom::Feed; +use XML::Atom::Util qw( iso2dt ); +use List::Util qw( first ); + +sub init_string { + my $feed = shift; + my($str) = @_; + $feed->{atom} = XML::Atom::Feed->new(Stream => \$str) + or return $feed->error(XML::Atom::Feed->errstr); + $feed; +} + +sub format { 'Atom' } + +sub title { $_[0]->{atom}->title } +sub link { + my $l = first { $_->rel eq 'alternate' } $_[0]->{atom}->link; + $l ? $l->href : undef; +} +sub description { $_[0]->{atom}->tagline } +sub copyright { $_[0]->{atom}->copyright } +sub language { $_[0]->{atom}->language } +sub generator { $_[0]->{atom}->generator } +sub author { $_[0]->{atom}->author ? $_[0]->{atom}->author->name : undef } +sub modified { iso2dt($_[0]->{atom}->modified) } + +sub entries { + my @entries; + for my $entry ($_[0]->{atom}->entries) { + push @entries, XML::Feed::Atom::Entry->wrap($entry); + } + @entries; +} + +package XML::Feed::Atom::Entry; +use strict; + +use base qw( XML::Feed::Entry ); +use XML::Atom::Util qw( iso2dt ); +use List::Util qw( first ); + +sub title { $_[0]->{entry}->title } +sub link { + my $l = first { $_->rel eq 'alternate' } $_[0]->{entry}->link; + $l ? $l->href : undef; +} +sub summary { $_[0]->{entry}->summary } +sub content { $_[0]->{entry}->content ? $_[0]->{entry}->content->body : undef } + +sub category { + my $ns = XML::Atom::Namespace->new(dc => 'http://purl.org/dc/elements/1.1/'); + $_[0]->{entry}->get($ns, 'subject'); +} + +sub author { $_[0]->{entry}->author ? $_[0]->{entry}->author->name : undef } +sub id { $_[0]->{entry}->id } +sub issued { iso2dt($_[0]->{entry}->issued) } +sub modified { iso2dt($_[0]->{entry}->modified) } + +1; diff --git a/lib/XML/Feed/Entry.pm b/lib/XML/Feed/Entry.pm new file mode 100644 index 0000000..3e91332 --- /dev/null +++ b/lib/XML/Feed/Entry.pm @@ -0,0 +1,98 @@ +# $Id: Entry.pm,v 1.1.1.1 2004/05/29 17:29:56 btrott Exp $ + +package XML::Feed::Entry; +use strict; + +sub wrap { + my $class = shift; + my($item) = @_; + bless { entry => $item }, $class; +} + +sub title; +sub link; +sub content; +sub summary; +sub category; +sub author; +sub id; +sub issued; +sub modified; + +1; +__END__ + +=head1 NAME + +XML::Feed::Entry - Entry/item in a syndication feed + +=head1 SYNOPSIS + + ## $feed is an XML::Feed object. + for my $entry ($feed->entries) { + print $entry->title, "\n", $entry->summary, "\n\n"; + } + +=head1 DESCRIPTION + +I represents an entry/item in an I syndication +feed. + +=head1 USAGE + +=head2 $entry->title + +The title of the entry. + +=head2 $entry->link + +The permalink of the entry, in most cases, except in cases where it points +instead of an offsite URI referenced in the entry. + +=head2 $entry->content + +The full entry body, or as much as is available in the feed. + +In RSS feeds, this method will look first for +I and +I elements, then fall back to a +IdescriptionE> element. + +=head2 $entry->summary + +A short summary of the entry. Possibly. + +Since RSS feeds do not have the idea of a summary separate from the entry +body, this may return the same value as the I<$entry-Econtent> method. +But it won't always, even with RSS feeds. For example, a number of RSS feeds +use an element like I +for the entry body and put an excerpt in the IdescriptionE> element; +in those cases, this method will return the excerpt. + +=head2 $entry->category + +The category in which the entry was posted. + +=head2 $entry->author + +The name or email address of the person who posted the entry. + +=head2 $entry->id + +The unique ID of the entry. + +=head2 $entry->issued + +A I object representing the date and time at which the entry +was posted. + +=head2 $entry->modified + +A I object representing the last-modified date of the entry. + +=head1 AUTHOR & COPYRIGHT + +Please see the I manpage for author, copyright, and license +information. + +=cut diff --git a/lib/XML/Feed/ErrorHandler.pm b/lib/XML/Feed/ErrorHandler.pm new file mode 100644 index 0000000..44cc5da --- /dev/null +++ b/lib/XML/Feed/ErrorHandler.pm @@ -0,0 +1,20 @@ +# $Id: ErrorHandler.pm,v 1.2 2004/05/29 18:19:50 btrott Exp $ + +package XML::Feed::ErrorHandler; +use strict; + +use vars qw( $ERROR ); + +sub error { + my $msg = $_[1] || ''; + $msg .= "\n" unless $msg =~ /\n$/; + if (ref($_[0])) { + $_[0]->{_errstr} = $msg; + } else { + $ERROR = $msg; + } + return; + } +sub errstr { ref($_[0]) ? $_[0]->{_errstr} : $ERROR } + +1; diff --git a/lib/XML/Feed/RSS.pm b/lib/XML/Feed/RSS.pm new file mode 100644 index 0000000..a678d17 --- /dev/null +++ b/lib/XML/Feed/RSS.pm @@ -0,0 +1,109 @@ +# $Id: RSS.pm,v 1.3 2004/05/30 09:39:52 btrott Exp $ + +package XML::Feed::RSS; +use strict; + +use base qw( XML::Feed ); +use XML::RSS; +use DateTime::Format::Mail; +use DateTime::Format::W3CDTF; + +sub init_string { + my $feed = shift; + my($str) = @_; + my $rss = $feed->{rss} = XML::RSS->new; + if ($str) { + $rss->parse($str); + } + $feed; +} + +sub format { 'RSS ' . $_[0]->{rss}->{'version'} } + +## The following elements are the same in all versions of RSS. +sub title { $_[0]->{rss}->channel('title') } +sub link { $_[0]->{rss}->channel('link') } +sub description { $_[0]->{rss}->channel('description') } + +## This is RSS 2.0 only--what's the equivalent in RSS 1.0? +sub copyright { $_[0]->{rss}->channel('copyright') } + +## The following all work transparently in any RSS version. +sub language { + $_[0]->{rss}->channel('language') || + $_[0]->{rss}->channel->{dc}{language} +} + +sub generator { + $_[0]->{rss}->channel('generator') || + $_[0]->{rss}->channel->{'http://webns.net/mvcb/'}{generatorAgent}; +} + +sub author { + $_[0]->{rss}->channel('webMaster') || + $_[0]->{rss}->channel->{dc}{creator}; +} + +sub modified { + my $rss = $_[0]->{rss}; + if (my $ts = $rss->channel('pubDate')) { + return DateTime::Format::Mail->parse_datetime($ts); + } elsif ($ts = $rss->channel->{dc}{date}) { + return DateTime::Format::W3CDTF->parse_datetime($ts); + } +} + +sub entries { + my $rss = $_[0]->{rss}; + my @entries; + for my $item (@{ $rss->{items} }) { + push @entries, XML::Feed::RSS::Entry->wrap($item); + } + @entries; +} + +package XML::Feed::RSS::Entry; +use strict; + +use base qw( XML::Feed::Entry ); + +sub title { $_[0]->{entry}{title} } +sub link { $_[0]->{entry}{link} } +sub summary { $_[0]->{entry}{description} } + +sub content { + my $item = $_[0]->{entry}; + $_[0]->{entry}{'http://purl.org/rss/1.0/modules/content/'}{encoded} || + $_[0]->{entry}{'http://www.w3.org/1999/xhtml'}{body} || + $_[0]->{entry}{description}; +} + +sub category { + $_[0]->{entry}{category} || $_[0]->{entry}{dc}{subject}; +} + +sub author { + $_[0]->{entry}{author} || $_[0]->{entry}{dc}{creator}; +} + +## XML::RSS doesn't give us access to the rdf:about for the , +## so we have to fall back to the element in RSS 1.0 feeds. +sub id { + $_[0]->{entry}{guid} || $_[0]->{entry}{link}; +} + +sub issued { + if (my $ts = $_[0]->{entry}{pubDate}) { + return DateTime::Format::Mail->parse_datetime($ts); + } elsif ($ts = $_[0]->{entry}{dc}{date}) { + return DateTime::Format::W3CDTF->parse_datetime($ts); + } +} + +sub modified { + if (my $ts = $_[0]->{entry}{'http://purl.org/rss/1.0/modules/dcterms/'}{modified}) { + return DateTime::Format::W3CDTF->parse_datetime($ts); + } +} + +1; diff --git a/t/00-compile.t b/t/00-compile.t new file mode 100644 index 0000000..ec6105d --- /dev/null +++ b/t/00-compile.t @@ -0,0 +1,11 @@ +# $Id: 00-compile.t,v 1.1 2004/05/29 18:19:50 btrott Exp $ + +my $loaded; +BEGIN { print "1..1\n" } +use XML::Feed; +use XML::Feed::Entry; +use XML::Feed::RSS; +use XML::Feed::Atom; +$loaded++; +print "ok 1\n"; +END { print "not ok 1\n" unless $loaded } diff --git a/t/01-parse.t b/t/01-parse.t new file mode 100644 index 0000000..2ed1b35 --- /dev/null +++ b/t/01-parse.t @@ -0,0 +1,61 @@ +# $Id: 01-parse.t,v 1.2 2004/05/30 09:39:52 btrott Exp $ + +use strict; +use Test; +use XML::Feed; +use URI; + +BEGIN { plan tests => 68 } + +my %Feeds = ( + 't/samples/atom.xml' => 'Atom', + 't/samples/rss10.xml' => 'RSS 1.0', + 't/samples/rss20.xml' => 'RSS 2.0', +); + +## First, test all of the various ways of calling parse. +my $feed; +my $file = 't/samples/atom.xml'; +ok($feed = XML::Feed->parse($file)); +ok($feed->title, 'First Weblog'); +open my $fh, $file or die "Can't open $file: $!"; +ok($feed = XML::Feed->parse($fh)); +ok($feed->title, 'First Weblog'); +seek $fh, 0, 0; +my $xml = do { local $/; <$fh> }; +ok($feed = XML::Feed->parse(\$xml)); +ok($feed->title, 'First Weblog'); +ok($feed = XML::Feed->parse(URI->new("file:$file"))); +ok($feed->title, 'First Weblog'); + +## Then try calling all of the unified API methods. +for my $file (sort keys %Feeds) { + my $feed = XML::Feed->parse($file) or die XML::Feed->errstr; + ok($feed); + ok($feed->format, $Feeds{$file}); + ok($feed->language, 'en-us'); + ok($feed->title, 'First Weblog'); + ok($feed->link, 'http://localhost/weblog/'); + ok($feed->tagline, 'This is a test weblog.'); + ok($feed->description, 'This is a test weblog.'); + my $dt = $feed->modified; + ok(ref($dt), 'DateTime'); + $dt->set_time_zone('UTC'); + ok($dt->iso8601, '2004-05-30T07:39:57'); + ok($feed->author, 'Melody'); + + my @entries = $feed->entries; + ok(scalar @entries, 2); + my $entry = $entries[0]; + ok($entry->title, 'Entry Two'); + ok($entry->link, 'http://localhost/weblog/2004/05/entry_two.html'); + $dt = $entry->issued; + ok(ref($dt), 'DateTime'); + $dt->set_time_zone('UTC'); + ok($dt->iso8601, '2004-05-30T07:39:25'); + ok($entry->content =~ /

Hello!<\/p>/); + ok($entry->summary, 'Hello!...'); + ok($entry->category, 'Travel'); + ok($entry->author, 'Melody'); + ok($entry->id); +} diff --git a/t/samples/atom.xml b/t/samples/atom.xml new file mode 100644 index 0000000..49b6b47 --- /dev/null +++ b/t/samples/atom.xml @@ -0,0 +1,51 @@ + + +First Weblog + +2004-05-30T07:39:57Z +This is a test weblog. +tag:localhost,2004:/weblog//1 +Movable Type +Copyright (c) 2004, Melody + +Entry Two + +2004-05-30T07:39:53Z +2004-05-30T07:39:25Z +tag:localhost,2004:/weblog//1.28 +2004-05-30T07:39:25Z +

Hello!... + +Melody + +melody@example.com + +Travel + +Hello!

]]> + +
+ + +Test + +2004-05-30T06:24:08Z +2004-05-09T07:03:28Z +tag:localhost,2004:/weblog//1.1 +2004-05-09T07:03:28Z +This is a test. Why don't you come down to our place for a coffee and a chat?... + +Melody + +melody@example.com + +Sports + +This is a test.

+ +

Why don't you come down to our place for a coffee and a chat?

]]> + +
+
+ + diff --git a/t/samples/rss10.xml b/t/samples/rss10.xml new file mode 100644 index 0000000..ce0d1c8 --- /dev/null +++ b/t/samples/rss10.xml @@ -0,0 +1,52 @@ + + + + + +First Weblog +http://localhost/weblog/ +This is a test weblog. +en-us +Melody +2004-05-29T23:39:57-08:00 + + + + + + + + + + + + +Entry Two +http://localhost/weblog/2004/05/entry_two.html +Hello!... +Hello!

]]>
+Travel +Melody +2004-05-29T23:39:25-08:00 +
+ +Test +http://localhost/weblog/2004/05/test.html +This is a test. Why don't you come down to our place for a coffee and a chat?... +This is a test.

+ +

Why don't you come down to our place for a coffee and a chat?

]]>
+Sports +Melody +2004-05-08T23:03:28-08:00 +
+ + +
diff --git a/t/samples/rss20.xml b/t/samples/rss20.xml new file mode 100644 index 0000000..061b88c --- /dev/null +++ b/t/samples/rss20.xml @@ -0,0 +1,39 @@ + + + +First Weblog +http://localhost/weblog/ +This is a test weblog. +en-us +Copyright 2004 +Sat, 29 May 2004 23:39:25 -0800 +Sat, 29 May 2004 23:39:57 -0800 +http://www.movabletype.org/?v=3.0D +http://blogs.law.harvard.edu/tech/rss +Melody + + +Entry Two +Hello!... +Hello!

]]>
+http://localhost/weblog/2004/05/entry_two.html +Melody +http://localhost/weblog/2004/05/entry_two.html +Travel +Sat, 29 May 2004 23:39:25 -0800 +
+ +Test +This is a test. Why don't you come down to our place for a coffee and a chat?... +This is a test.

+ +

Why don't you come down to our place for a coffee and a chat?

]]>
+http://localhost/weblog/2004/05/test.html +http://localhost/weblog/2004/05/test.html +Sports +Sat, 08 May 2004 23:03:28 -0800 +
+ + +
+