|
|
@@ -1,6 +1,3 @@
|
|
|
-#
|
|
|
-# Dpkg::Changelog
|
|
|
-#
|
|
|
# Copyright © 2005, 2007 Frank Lichtenheld <frank@lichtenheld.de>
|
|
|
# Copyright © 2009 Raphaël Hertzog <hertzog@debian.org>
|
|
|
#
|
|
|
@@ -21,13 +18,16 @@
|
|
|
|
|
|
=head1 NAME
|
|
|
|
|
|
-Dpkg::Changelog
|
|
|
+Dpkg::Changelog - base class to implement a changelog parser
|
|
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
|
|
-FIXME: to be written
|
|
|
+Dpkg::Changelog is a class representing a changelog file
|
|
|
+as an array of changelog entries (Dpkg::Changelog::Entry).
|
|
|
+By deriving this object and implementing its parse method, you
|
|
|
+add the ability to fill this object with changelog entries.
|
|
|
|
|
|
-=head2 Functions
|
|
|
+=head2 FUNCTIONS
|
|
|
|
|
|
=cut
|
|
|
|
|
|
@@ -39,90 +39,102 @@ use warnings;
|
|
|
use Dpkg;
|
|
|
use Dpkg::Gettext;
|
|
|
use Dpkg::ErrorHandling qw(:DEFAULT report);
|
|
|
+use Dpkg::Control;
|
|
|
use Dpkg::Control::Changelog;
|
|
|
use Dpkg::Control::Fields;
|
|
|
+use Dpkg::Index;
|
|
|
use Dpkg::Version;
|
|
|
use Dpkg::Vendor qw(run_vendor_hook);
|
|
|
|
|
|
-use base qw(Exporter);
|
|
|
+use overload
|
|
|
+ '""' => sub { return $_[0]->output() },
|
|
|
+ '@{}' => sub { return $_[0]->{data} };
|
|
|
+
|
|
|
+=over 4
|
|
|
+
|
|
|
+=item my $c = Dpkg::Changelog->new(%options)
|
|
|
|
|
|
-our %EXPORT_TAGS = ( 'util' => [ qw(
|
|
|
- data2rfc822
|
|
|
- data2rfc822_mult
|
|
|
- get_dpkg_changes
|
|
|
-) ] );
|
|
|
-our @EXPORT_OK = @{$EXPORT_TAGS{util}};
|
|
|
+Creates a new changelog object.
|
|
|
|
|
|
-=pod
|
|
|
+=cut
|
|
|
|
|
|
-=head3 init
|
|
|
+sub new {
|
|
|
+ my ($this, %opts) = @_;
|
|
|
+ my $class = ref($this) || $this;
|
|
|
+ my $self = {
|
|
|
+ verbose => 1,
|
|
|
+ parse_errors => []
|
|
|
+ };
|
|
|
+ bless $self, $class;
|
|
|
+ $self->set_options(%opts);
|
|
|
+ return $self;
|
|
|
+}
|
|
|
|
|
|
-Creates a new object instance. Takes a reference to a hash as
|
|
|
-optional argument, which is interpreted as configuration options.
|
|
|
-There are currently no supported general configuration options, but
|
|
|
-see the other methods for more specific configuration options which
|
|
|
-can also specified to C<init>.
|
|
|
+=item $c->load($filename)
|
|
|
|
|
|
-If C<infile>, C<inhandle>, or C<instring> are specified, C<parse()>
|
|
|
-is called from C<init>. If a fatal error is encountered during parsing
|
|
|
-(e.g. the file can't be opened), C<init> will not return a
|
|
|
-valid object but C<undef>!
|
|
|
+Parse $filename as a changelog.
|
|
|
|
|
|
=cut
|
|
|
|
|
|
-sub init {
|
|
|
- my $classname = shift;
|
|
|
- my $config = shift || {};
|
|
|
- my $self = {};
|
|
|
- bless( $self, $classname );
|
|
|
+sub load {
|
|
|
+ my ($self, $file) = @_;
|
|
|
+ open(my $fh, "<", $file) or syserr(_g("cannot read %s"), $file);
|
|
|
+ my $ret = $self->parse($fh, $file);
|
|
|
+ close($fh);
|
|
|
+ return $ret;
|
|
|
+}
|
|
|
|
|
|
- $config->{verbose} = 1 if $config->{debug};
|
|
|
- $self->{config} = $config;
|
|
|
+=item $c->set_options(%opts)
|
|
|
|
|
|
- $self->reset_parse_errors;
|
|
|
+Change the value of some options. "verbose" (defaults to 1) defines
|
|
|
+whether parse errors are displayed as warnings by default. "reportfile"
|
|
|
+is a string to use instead of the name of the file parsed, in particular
|
|
|
+in error messages. "range" defines the range of entries that we want to
|
|
|
+parse, the parser will stop as soon as it has parsed enough data to
|
|
|
+satisfy $c->get_range($opts{'range'}).
|
|
|
|
|
|
- if ($self->{config}{infile}
|
|
|
- || $self->{config}{inhandle}
|
|
|
- || $self->{config}{instring}) {
|
|
|
- defined($self->parse) or return undef;
|
|
|
- }
|
|
|
+=cut
|
|
|
|
|
|
- return $self;
|
|
|
+sub set_options {
|
|
|
+ my ($self, %opts) = @_;
|
|
|
+ $self->{$_} = $opts{$_} foreach keys %opts;
|
|
|
}
|
|
|
|
|
|
-=pod
|
|
|
-
|
|
|
-=head3 reset_parse_errors
|
|
|
+=item $c->reset_parse_errors()
|
|
|
|
|
|
Can be used to delete all information about errors ocurred during
|
|
|
-previous L<parse> runs. Note that C<parse()> also calls this method.
|
|
|
+previous L<parse> runs.
|
|
|
|
|
|
=cut
|
|
|
|
|
|
sub reset_parse_errors {
|
|
|
my ($self) = @_;
|
|
|
-
|
|
|
- $self->{errors}{parser} = [];
|
|
|
+ $self->{parse_errors} = [];
|
|
|
}
|
|
|
|
|
|
-sub _do_parse_error {
|
|
|
+=item $c->parse_error($line_nr, $error, [$line])
|
|
|
+
|
|
|
+Record a new parse error at line $line_nr. The error message is specified
|
|
|
+with $error and a copy of the line can be recorded in $line.
|
|
|
+
|
|
|
+=cut
|
|
|
+
|
|
|
+sub parse_error {
|
|
|
my ($self, $file, $line_nr, $error, $line) = @_;
|
|
|
shift;
|
|
|
|
|
|
- push @{$self->{errors}{parser}}, [ @_ ];
|
|
|
+ push @{$self->{parse_errors}}, [ @_ ];
|
|
|
|
|
|
- unless ($self->{config}{quiet}) {
|
|
|
+ if ($self->{verbose}) {
|
|
|
if ($line) {
|
|
|
- warning("%20s(l$.): $error\nLINE: $line", $file);
|
|
|
+ warning("%20s(l$line_nr): $error\nLINE: $line", $file);
|
|
|
} else {
|
|
|
- warning("%20s(l$.): $error", $file);
|
|
|
+ warning("%20s(l$line_nr): $error", $file);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-=pod
|
|
|
-
|
|
|
-=head3 get_parse_errors
|
|
|
+=item $c->get_parse_errors()
|
|
|
|
|
|
Returns all error messages from the last L<parse> run.
|
|
|
If called in scalar context returns a human readable
|
|
|
@@ -133,10 +145,8 @@ an array of arrays. Each of these arrays contains
|
|
|
|
|
|
=item 1.
|
|
|
|
|
|
-the filename of the parsed file or C<FileHandle> or C<String>
|
|
|
-if the input came from a file handle or a string. If the
|
|
|
-reportfile configuration option was given, its value will be
|
|
|
-used instead
|
|
|
+a string describing the origin of the data (a filename usually). If the
|
|
|
+reportfile configuration option was given, its value will be used instead.
|
|
|
|
|
|
=item 2.
|
|
|
|
|
|
@@ -152,19 +162,16 @@ the original line
|
|
|
|
|
|
=back
|
|
|
|
|
|
-NOTE: This format isn't stable yet and may change in later versions
|
|
|
-of this module.
|
|
|
-
|
|
|
=cut
|
|
|
|
|
|
sub get_parse_errors {
|
|
|
my ($self) = @_;
|
|
|
|
|
|
if (wantarray) {
|
|
|
- return @{$self->{errors}{parser}};
|
|
|
+ return @{$self->{parse_errors}};
|
|
|
} else {
|
|
|
my $res = "";
|
|
|
- foreach my $e (@{$self->{errors}{parser}}) {
|
|
|
+ foreach my $e (@{$self->{parse_errors}}) {
|
|
|
if ($e->[3]) {
|
|
|
$res .= report(_g('warning'),_g("%s(l%s): %s\nLINE: %s"), @$e );
|
|
|
} else {
|
|
|
@@ -175,74 +182,72 @@ sub get_parse_errors {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-sub _do_fatal_error {
|
|
|
- my ($self, $msg, @msg) = @_;
|
|
|
-
|
|
|
- $self->{errors}{fatal} = report(_g('fatal error'), $msg, @msg);
|
|
|
- warning($msg, @msg) unless $self->{config}{quiet};
|
|
|
-}
|
|
|
+=item $c->set_unparsed_tail($tail)
|
|
|
|
|
|
-=pod
|
|
|
+Add a string representing unparsed lines after the changelog entries.
|
|
|
+Use undef as $tail to remove the unparsed lines currently set.
|
|
|
|
|
|
-=head3 get_error
|
|
|
+=item $c->get_unparsed_tail()
|
|
|
|
|
|
-Get the last non-parser error (e.g. the file to parse couldn't be opened).
|
|
|
+Return a string representing the unparsed lines after the changelog
|
|
|
+entries. Returns undef if there's no such thing.
|
|
|
|
|
|
=cut
|
|
|
|
|
|
-sub get_error {
|
|
|
- my ($self) = @_;
|
|
|
+sub set_unparsed_tail {
|
|
|
+ my ($self, $tail) = @_;
|
|
|
+ $self->{'unparsed_tail'} = $tail;
|
|
|
+}
|
|
|
|
|
|
- return $self->{errors}{fatal};
|
|
|
+sub get_unparsed_tail {
|
|
|
+ my ($self) = @_;
|
|
|
+ return $self->{'unparsed_tail'};
|
|
|
}
|
|
|
|
|
|
-=pod
|
|
|
+=item @{$c}
|
|
|
|
|
|
-=head3 data
|
|
|
+Returns all the Dpkg::Changelog::Entry objects contained in this changelog
|
|
|
+in the order in which they have been parsed.
|
|
|
|
|
|
-C<data> returns an array (if called in list context) or a reference
|
|
|
-to an array of Dpkg::Changelog::Entry objects which each
|
|
|
-represent one entry of the changelog.
|
|
|
+=item $c->get_range($range)
|
|
|
|
|
|
-This method supports the common output options described in
|
|
|
-section L<"COMMON OUTPUT OPTIONS">.
|
|
|
+Returns an array (if called in list context) or a reference to an array of
|
|
|
+Dpkg::Changelog::Entry objects which each represent one entry of the
|
|
|
+changelog. $range is a hash reference describing the range of entries
|
|
|
+to return. See section L<"RANGE SELECTION">.
|
|
|
|
|
|
=cut
|
|
|
|
|
|
-sub data {
|
|
|
- my ($self, $config) = @_;
|
|
|
-
|
|
|
+sub __sanity_check_range {
|
|
|
+ my ($self, $r) = @_;
|
|
|
my $data = $self->{data};
|
|
|
- if ($config) {
|
|
|
- $self->{config}{DATA} = $config if $config;
|
|
|
- $data = $self->_data_range( $config ) or return undef;
|
|
|
- }
|
|
|
- return @$data if wantarray;
|
|
|
- return $data;
|
|
|
-}
|
|
|
|
|
|
-sub __sanity_check_range {
|
|
|
- my ( $data, $from, $to, $since, $until, $start, $end ) = @_;
|
|
|
+ if (defined($r->{offset}) and not defined($r->{count})) {
|
|
|
+ warning(_g("'offset' without 'count' has no effect")) if $self->{verbose};
|
|
|
+ delete $r->{offset};
|
|
|
+ }
|
|
|
|
|
|
- if (($$start || $$end) &&
|
|
|
- (length($$from) || length($$since) || length($$to) || length($$until)))
|
|
|
+ if ((defined($r->{count}) || defined($r->{offset})) &&
|
|
|
+ (defined($r->{from}) || defined($r->{since}) ||
|
|
|
+ defined($r->{to}) || defined($r->{'until'})))
|
|
|
{
|
|
|
- warning(_g( "you can't combine 'count' or 'offset' with any other range option" ));
|
|
|
- $$from = $$since = $$to = $$until = '';
|
|
|
+ warning(_g("you can't combine 'count' or 'offset' with any other " .
|
|
|
+ "range option")) if $self->{verbose};
|
|
|
+ delete $r->{from};
|
|
|
+ delete $r->{since};
|
|
|
+ delete $r->{to};
|
|
|
+ delete $r->{'until'};
|
|
|
}
|
|
|
- if (length($$from) && length($$since)) {
|
|
|
- warning(_g( "you can only specify one of 'from' and 'since', using 'since'" ));
|
|
|
- $$from = '';
|
|
|
+ if (defined($r->{from}) && defined($r->{since})) {
|
|
|
+ warning(_g("you can only specify one of 'from' and 'since', using " .
|
|
|
+ "'since'")) if $self->{verbose};
|
|
|
+ delete $r->{from};
|
|
|
}
|
|
|
- if (length($$to) && length($$until)) {
|
|
|
- warning(_g( "you can only specify one of 'to' and 'until', using 'until'" ));
|
|
|
- $$to = '';
|
|
|
+ if (defined($r->{to}) && defined($r->{'until'})) {
|
|
|
+ warning(_g("you can only specify one of 'to' and 'until', using " .
|
|
|
+ "'until'")) if $self->{verbose};
|
|
|
+ delete $r->{to};
|
|
|
}
|
|
|
- $$start = 0 if $$start < 0;
|
|
|
- return if $$start > $#$data;
|
|
|
- $$end = $#$data if $$end > $#$data;
|
|
|
- return if $$end < 0;
|
|
|
- $$end = $$start if $$end < $$start;
|
|
|
|
|
|
# Handle non-existing versions
|
|
|
my (%versions, @versions);
|
|
|
@@ -250,198 +255,217 @@ sub __sanity_check_range {
|
|
|
$versions{$entry->get_version()->as_string()} = 1;
|
|
|
push @versions, $entry->get_version()->as_string();
|
|
|
}
|
|
|
- if ((length($$since) and not exists $versions{$$since})) {
|
|
|
+ if ((defined($r->{since}) and not exists $versions{$r->{since}})) {
|
|
|
warning(_g("'%s' option specifies non-existing version"), "since");
|
|
|
warning(_g("use newest entry that is smaller than the one specified"));
|
|
|
foreach my $v (@versions) {
|
|
|
- if (version_compare_relation($v, REL_LT, $$since)) {
|
|
|
- $$since = $v;
|
|
|
+ if (version_compare_relation($v, REL_LT, $r->{since})) {
|
|
|
+ $r->{since} = $v;
|
|
|
last;
|
|
|
}
|
|
|
}
|
|
|
- if (not exists $versions{$$since}) {
|
|
|
+ if (not exists $versions{$r->{since}}) {
|
|
|
# No version was smaller, include all
|
|
|
warning(_g("none found, starting from the oldest entry"));
|
|
|
- $$since = '';
|
|
|
- $$from = $versions[-1];
|
|
|
+ delete $r->{since};
|
|
|
+ $r->{from} = $versions[-1];
|
|
|
}
|
|
|
}
|
|
|
- if ((length($$from) and not exists $versions{$$from})) {
|
|
|
+ if ((defined($r->{from}) and not exists $versions{$r->{from}})) {
|
|
|
warning(_g("'%s' option specifies non-existing version"), "from");
|
|
|
warning(_g("use oldest entry that is bigger than the one specified"));
|
|
|
my $oldest;
|
|
|
foreach my $v (@versions) {
|
|
|
- if (version_compare_relation($v, REL_GT, $$from)) {
|
|
|
+ if (version_compare_relation($v, REL_GT, $r->{from})) {
|
|
|
$oldest = $v;
|
|
|
}
|
|
|
}
|
|
|
if (defined($oldest)) {
|
|
|
- $$from = $oldest;
|
|
|
+ $r->{from} = $oldest;
|
|
|
} else {
|
|
|
warning(_g("no such entry found, ignoring '%s' parameter"), "from");
|
|
|
- $$from = ''; # No version was bigger
|
|
|
+ delete $r->{from}; # No version was bigger
|
|
|
}
|
|
|
}
|
|
|
- if ((length($$until) and not exists $versions{$$until})) {
|
|
|
+ if (defined($r->{'until'}) and not exists $versions{$r->{'until'}}) {
|
|
|
warning(_g("'%s' option specifies non-existing version"), "until");
|
|
|
warning(_g("use oldest entry that is bigger than the one specified"));
|
|
|
my $oldest;
|
|
|
foreach my $v (@versions) {
|
|
|
- if (version_compare_relation($v, REL_GT, $$until)) {
|
|
|
+ if (version_compare_relation($v, REL_GT, $r->{'until'})) {
|
|
|
$oldest = $v;
|
|
|
}
|
|
|
}
|
|
|
if (defined($oldest)) {
|
|
|
- $$until = $oldest;
|
|
|
+ $r->{'until'} = $oldest;
|
|
|
} else {
|
|
|
warning(_g("no such entry found, ignoring '%s' parameter"), "until");
|
|
|
- $$until = ''; # No version was bigger
|
|
|
+ delete $r->{'until'}; # No version was bigger
|
|
|
}
|
|
|
}
|
|
|
- if ((length($$to) and not exists $versions{$$to})) {
|
|
|
+ if (defined($r->{to}) and not exists $versions{$r->{to}}) {
|
|
|
warning(_g("'%s' option specifies non-existing version"), "to");
|
|
|
warning(_g("use newest entry that is smaller than the one specified"));
|
|
|
foreach my $v (@versions) {
|
|
|
- if (version_compare_relation($v, REL_LT, $$to)) {
|
|
|
- $$to = $v;
|
|
|
+ if (version_compare_relation($v, REL_LT, $r->{to})) {
|
|
|
+ $r->{to} = $v;
|
|
|
last;
|
|
|
}
|
|
|
}
|
|
|
- if (not exists $versions{$$to}) {
|
|
|
+ if (not exists $versions{$r->{to}}) {
|
|
|
# No version was smaller
|
|
|
warning(_g("no such entry found, ignoring '%s' parameter"), "to");
|
|
|
- $$to = '';
|
|
|
+ delete $r->{to};
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (length($$since) && ($data->[0]->get_version() eq $$since)) {
|
|
|
- warning(_g( "'since' option specifies most recent version, ignoring" ));
|
|
|
- $$since = '';
|
|
|
+ if (defined($r->{since}) and $data->[0]->get_version() eq $r->{since}) {
|
|
|
+ warning(_g("'since' option specifies most recent version, ignoring"));
|
|
|
+ delete $r->{since};
|
|
|
}
|
|
|
- if (length($$until) && ($data->[$#{$data}]->get_version() eq $$until)) {
|
|
|
- warning(_g( "'until' option specifies oldest version, ignoring" ));
|
|
|
- $$until = '';
|
|
|
+ if (defined($r->{'until'}) and $data->[-1]->get_version() eq $r->{'until'}) {
|
|
|
+ warning(_g("'until' option specifies oldest version, ignoring"));
|
|
|
+ delete $r->{'until'};
|
|
|
}
|
|
|
- return 1;
|
|
|
}
|
|
|
|
|
|
-sub _data_range {
|
|
|
- my ($self, $config) = @_;
|
|
|
+sub get_range {
|
|
|
+ my ($self, $range) = @_;
|
|
|
+ $range = {} unless defined $range;
|
|
|
+ my $res = $self->_data_range($range);
|
|
|
+ return undef unless defined $res;
|
|
|
+ return @$res if wantarray;
|
|
|
+ return $res;
|
|
|
+}
|
|
|
|
|
|
- my $data = $self->data or return undef;
|
|
|
+sub _data_range {
|
|
|
+ my ($self, $range) = @_;
|
|
|
|
|
|
- return [ @$data ] if $config->{all};
|
|
|
+ my $data = $self->{data} or return undef;
|
|
|
|
|
|
- my ($since, $until, $from, $to, $count, $offset) = ('', '', '', '', 0, 0);
|
|
|
- $since = $config->{since} if defined($config->{since});
|
|
|
- $until = $config->{until} if defined($config->{until});
|
|
|
- $from = $config->{from} if defined($config->{from});
|
|
|
- $to = $config->{to} if defined($config->{to});
|
|
|
- $count = $config->{count} if defined($config->{count});
|
|
|
- $offset = $config->{offset} if defined($config->{offset});
|
|
|
+ return [ @$data ] if $range->{all};
|
|
|
|
|
|
- return if $offset and not $count;
|
|
|
- if ($offset > 0) {
|
|
|
- $offset -= ($count < 0);
|
|
|
- } elsif ($offset < 0) {
|
|
|
- $offset = $#$data + ($count > 0) + $offset;
|
|
|
- } else {
|
|
|
- $offset = $#$data if $count < 0;
|
|
|
+ unless (grep { m/^(since|until|from|to|count|offset)$/ } keys %$range) {
|
|
|
+ return [ @$data ];
|
|
|
}
|
|
|
- my $start = my $end = $offset;
|
|
|
- $start += $count+1 if $count < 0;
|
|
|
- $end += $count-1 if $count > 0;
|
|
|
-
|
|
|
- return unless __sanity_check_range( $data, \$from, \$to,
|
|
|
- \$since, \$until,
|
|
|
- \$start, \$end );
|
|
|
|
|
|
-
|
|
|
- unless (length($from) or length($to) or length($since) or length($until)
|
|
|
- or $start or $end)
|
|
|
- {
|
|
|
- return [ @$data ] if $config->{default_all} and not $count;
|
|
|
- return [ $data->[0] ];
|
|
|
+ $self->__sanity_check_range($range);
|
|
|
+
|
|
|
+ my ($start, $end);
|
|
|
+ if (defined($range->{count})) {
|
|
|
+ my $offset = $range->{offset} || 0;
|
|
|
+ my $count = $range->{count};
|
|
|
+ # Convert count/offset in start/end
|
|
|
+ if ($offset > 0) {
|
|
|
+ $offset -= ($count < 0);
|
|
|
+ } elsif ($offset < 0) {
|
|
|
+ $offset = $#$data + ($count > 0) + $offset;
|
|
|
+ } else {
|
|
|
+ $offset = $#$data if $count < 0;
|
|
|
+ }
|
|
|
+ $start = $end = $offset;
|
|
|
+ $start += $count+1 if $count < 0;
|
|
|
+ $end += $count-1 if $count > 0;
|
|
|
+ # Check limits
|
|
|
+ $start = 0 if $start < 0;
|
|
|
+ return if $start > $#$data;
|
|
|
+ $end = $#$data if $end > $#$data;
|
|
|
+ return if $end < 0;
|
|
|
+ $end = $start if $end < $start;
|
|
|
+ return [ @{$data}[$start .. $end] ];
|
|
|
}
|
|
|
|
|
|
- return [ @{$data}[$start .. $end] ] if $start or $end;
|
|
|
-
|
|
|
my @result;
|
|
|
-
|
|
|
my $include = 1;
|
|
|
- $include = 0 if length($to) or length($until);
|
|
|
+ $include = 0 if defined($range->{to}) or defined($range->{'until'});
|
|
|
foreach (@$data) {
|
|
|
my $v = $_->get_version();
|
|
|
- $include = 1 if $to and $v eq $to;
|
|
|
- last if $since and $v eq $since;
|
|
|
+ $include = 1 if defined($range->{to}) and $v eq $range->{to};
|
|
|
+ last if defined($range->{since}) and $v eq $range->{since};
|
|
|
|
|
|
push @result, $_ if $include;
|
|
|
|
|
|
- $include = 1 if $until and $v eq $until;
|
|
|
- last if $from and $v eq $from;
|
|
|
+ $include = 1 if defined($range->{'until'}) and $v eq $range->{'until'};
|
|
|
+ last if defined($range->{from}) and $v eq $range->{from};
|
|
|
}
|
|
|
|
|
|
return \@result if scalar(@result);
|
|
|
return undef;
|
|
|
}
|
|
|
|
|
|
-sub _abort_early {
|
|
|
- my ($self) = @_;
|
|
|
+=item $c->abort_early()
|
|
|
|
|
|
- my $data = $self->data or return;
|
|
|
- my $config = $self->{config} or return;
|
|
|
+Returns true if enough data have been parsed to be able to return all
|
|
|
+entries selected by the range set at creation (or with set_options).
|
|
|
|
|
|
-# use Data::Dumper;
|
|
|
-# warn "Abort early? (\$# = $#$data)\n".Dumper($config);
|
|
|
+=cut
|
|
|
|
|
|
- return if $config->{all};
|
|
|
+sub abort_early {
|
|
|
+ my ($self) = @_;
|
|
|
|
|
|
- my ($since, $until, $from, $to, $count, $offset) = ('', '', '', '', 0, 0);
|
|
|
- $since = $config->{since} if defined($config->{since});
|
|
|
- $until = $config->{until} if defined($config->{until});
|
|
|
- $from = $config->{from} if defined($config->{from});
|
|
|
- $to = $config->{to} if defined($config->{to});
|
|
|
- $count = $config->{count} if defined($config->{count});
|
|
|
- $offset = $config->{offset} if defined($config->{offset});
|
|
|
+ my $data = $self->{data} or return;
|
|
|
+ my $r = $self->{range} or return;
|
|
|
+ my $count = $r->{count} || 0;
|
|
|
+ my $offset = $r->{offset} || 0;
|
|
|
|
|
|
- return if $offset and not $count;
|
|
|
+ return if $r->{all};
|
|
|
+ return unless grep { m/^(since|until|from|to|count|offset)$/ } keys %$r;
|
|
|
return if $offset < 0 or $count < 0;
|
|
|
- if ($offset > 0) {
|
|
|
- $offset -= ($count < 0);
|
|
|
- }
|
|
|
- my $start = my $end = $offset;
|
|
|
- $end += $count-1 if $count > 0;
|
|
|
-
|
|
|
- unless (length($from) or length($to) or length($since) or length($until)
|
|
|
- or $start or $end)
|
|
|
- {
|
|
|
- return if not $count;
|
|
|
- return 1 if @$data;
|
|
|
+ if (defined($r->{count})) {
|
|
|
+ if ($offset > 0) {
|
|
|
+ $offset -= ($count < 0);
|
|
|
+ }
|
|
|
+ my $start = my $end = $offset;
|
|
|
+ $end += $count-1 if $count > 0;
|
|
|
+ return ($start < @$data and $end < @$data);
|
|
|
}
|
|
|
|
|
|
- return 1 if ($start or $end)
|
|
|
- and $start < @$data and $end < @$data;
|
|
|
-
|
|
|
- return unless length($since) or length($from);
|
|
|
+ return unless defined($r->{since}) or defined($r->{from});
|
|
|
foreach (@$data) {
|
|
|
my $v = $_->get_version();
|
|
|
-
|
|
|
- return 1 if $v eq $since;
|
|
|
- return 1 if $v eq $from;
|
|
|
+ return 1 if defined($r->{since}) and $v eq $r->{since};
|
|
|
+ return 1 if defined($r->{from}) and $v eq $r->{from};
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
-=pod
|
|
|
+=item $c->output()
|
|
|
+
|
|
|
+=item "$c"
|
|
|
+
|
|
|
+Returns a string representation of the changelog (it's a concatenation of
|
|
|
+the string representation of the individual changelog entries).
|
|
|
+
|
|
|
+=item $c->output($fh)
|
|
|
+
|
|
|
+Output the changelog to the given filehandle.
|
|
|
+
|
|
|
+=cut
|
|
|
+
|
|
|
+sub output {
|
|
|
+ my ($self, $fh) = @_;
|
|
|
+ my $str = "";
|
|
|
+ foreach my $entry (@{$self}) {
|
|
|
+ my $text = $entry->output();
|
|
|
+ print $fh $text if defined $fh;
|
|
|
+ $str .= $text if defined wantarray;
|
|
|
+ }
|
|
|
+ my $text = $self->get_unparsed_tail();
|
|
|
+ if (defined $text) {
|
|
|
+ print $fh $text if defined $fh;
|
|
|
+ $str .= $text if defined wantarray;
|
|
|
+ }
|
|
|
+ return $str;
|
|
|
+}
|
|
|
|
|
|
-=head3 dpkg
|
|
|
+=item my $control = $c->dpkg($range)
|
|
|
|
|
|
-(and B<dpkg_str>)
|
|
|
+Returns a Dpkg::Control::Changelog object representing the entries selected
|
|
|
+by the optional range specifier (see L<"RANGE SELECTION"> for details).
|
|
|
+Returns undef in no entries are matched.
|
|
|
|
|
|
-C<dpkg> returns a hash (in list context) or a hash reference
|
|
|
-(in scalar context) where the keys are field names and the values are
|
|
|
-field values. The following fields are given:
|
|
|
+The following fields are contained in the object:
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
@@ -479,16 +503,6 @@ content of the the entry/entries
|
|
|
|
|
|
=back
|
|
|
|
|
|
-C<dpkg_str> returns a stringified version of this hash. The fields are
|
|
|
-ordered like in the list above.
|
|
|
-
|
|
|
-Both methods support the common output options described in
|
|
|
-section L<"COMMON OUTPUT OPTIONS">.
|
|
|
-
|
|
|
-=head3 dpkg_str
|
|
|
-
|
|
|
-See L<dpkg>.
|
|
|
-
|
|
|
=cut
|
|
|
|
|
|
our ( @URGENCIES, %URGENCIES );
|
|
|
@@ -499,24 +513,22 @@ BEGIN {
|
|
|
}
|
|
|
|
|
|
sub dpkg {
|
|
|
- my ($self, $config) = @_;
|
|
|
+ my ($self, $range) = @_;
|
|
|
|
|
|
- $self->{config}{DPKG} = $config if $config;
|
|
|
-
|
|
|
- $config = $self->{config}{DPKG} || {};
|
|
|
- my $data = $self->_data_range( $config ) or return undef;
|
|
|
+ my @data = $self->get_range($range) or return undef;
|
|
|
+ my $entry = shift @data;
|
|
|
|
|
|
my $f = Dpkg::Control::Changelog->new();
|
|
|
- $f->{Urgency} = $data->[0]->get_urgency() || "unknown";
|
|
|
- $f->{Source} = $data->[0]->get_source() || "unknown";
|
|
|
- $f->{Version} = $data->[0]->get_version() || "unknown";
|
|
|
- $f->{Distribution} = join(" ", $data->[0]->get_distributions());
|
|
|
- $f->{Maintainer} = $data->[0]->get_maintainer() || '';
|
|
|
- $f->{Date} = $data->[0]->get_timestamp() || '';
|
|
|
- $f->{Changes} = get_dpkg_changes($data->[0]);
|
|
|
+ $f->{Urgency} = $entry->get_urgency() || "unknown";
|
|
|
+ $f->{Source} = $entry->get_source() || "unknown";
|
|
|
+ $f->{Version} = $entry->get_version() || "unknown";
|
|
|
+ $f->{Distribution} = join(" ", $entry->get_distributions());
|
|
|
+ $f->{Maintainer} = $entry->get_maintainer() || '';
|
|
|
+ $f->{Date} = $entry->get_timestamp() || '';
|
|
|
+ $f->{Changes} = $entry->get_dpkg_changes();
|
|
|
|
|
|
# handle optional fields
|
|
|
- my $opts = $data->[0]->get_optional_fields();
|
|
|
+ my $opts = $entry->get_optional_fields();
|
|
|
my %closes;
|
|
|
foreach (keys %$opts) {
|
|
|
if (/^Urgency$/i) { # Already dealt
|
|
|
@@ -527,17 +539,13 @@ sub dpkg {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- my $first = 1; my $urg_comment = '';
|
|
|
- foreach my $entry (@$data) {
|
|
|
- $first = 0, next if $first;
|
|
|
-
|
|
|
+ foreach $entry (@data) {
|
|
|
my $oldurg = $f->{Urgency} || '';
|
|
|
my $oldurgn = $URGENCIES{$f->{Urgency}} || -1;
|
|
|
my $newurg = $entry->get_urgency() || '';
|
|
|
my $newurgn = $URGENCIES{$newurg} || -1;
|
|
|
$f->{Urgency} = ($newurgn > $oldurgn) ? $newurg : $oldurg;
|
|
|
-
|
|
|
- $f->{Changes} .= "\n ." . get_dpkg_changes($entry);
|
|
|
+ $f->{Changes} .= "\n ." . $entry->get_dpkg_changes();
|
|
|
|
|
|
# handle optional fields
|
|
|
$opts = $entry->get_optional_fields();
|
|
|
@@ -555,47 +563,26 @@ sub dpkg {
|
|
|
}
|
|
|
run_vendor_hook("post-process-changelog-entry", $f);
|
|
|
|
|
|
- return %$f if wantarray;
|
|
|
return $f;
|
|
|
}
|
|
|
|
|
|
-sub dpkg_str {
|
|
|
- return data2rfc822(scalar dpkg(@_));
|
|
|
-}
|
|
|
-
|
|
|
-=pod
|
|
|
-
|
|
|
-=head3 rfc822
|
|
|
-
|
|
|
-(and B<rfc822_str>)
|
|
|
+=item my @controls = $c->rfc822($range)
|
|
|
|
|
|
-C<rfc822> returns an array of hashes (in list context) or a reference
|
|
|
-to this array (in scalar context) where each hash represents one entry
|
|
|
-in the changelog. For the format of such a hash see the description
|
|
|
-of the L<"dpkg"> method (while ignoring the remarks about which
|
|
|
-values are taken from the first entry).
|
|
|
-
|
|
|
-C<rfc822_str> returns a stringified version of this array.
|
|
|
-
|
|
|
-Both methods support the common output options described in
|
|
|
-section L<"COMMON OUTPUT OPTIONS">.
|
|
|
-
|
|
|
-=head3 rfc822_str
|
|
|
-
|
|
|
-See L<rfc822>.
|
|
|
+Returns a Dpkg::Index containing Dpkg::Control::Changelog objects where
|
|
|
+each object represents one entry in the changelog that is part of the
|
|
|
+range requested (see L<"RANGE SELECTION"> for details). For the format of
|
|
|
+such an object see the description of the L<"dpkg"> method (while ignoring
|
|
|
+the remarks about which values are taken from the first entry).
|
|
|
|
|
|
=cut
|
|
|
|
|
|
sub rfc822 {
|
|
|
- my ($self, $config) = @_;
|
|
|
+ my ($self, $range) = @_;
|
|
|
|
|
|
- $self->{config}{RFC822} = $config if $config;
|
|
|
+ my @data = $self->get_range($range) or return undef;
|
|
|
+ my $index = Dpkg::Index->new(type => CTRL_CHANGELOG);
|
|
|
|
|
|
- $config = $self->{config}{RFC822} || {};
|
|
|
- my $data = $self->_data_range( $config ) or return undef;
|
|
|
- my @out_data;
|
|
|
-
|
|
|
- foreach my $entry (@$data) {
|
|
|
+ foreach my $entry (@data) {
|
|
|
my $f = Dpkg::Control::Changelog->new();
|
|
|
$f->{Urgency} = $entry->get_urgency() || "unknown";
|
|
|
$f->{Source} = $entry->get_source() || "unknown";
|
|
|
@@ -603,7 +590,7 @@ sub rfc822 {
|
|
|
$f->{Distribution} = join(" ", $entry->get_distributions());
|
|
|
$f->{Maintainer} = $entry->get_maintainer() || "";
|
|
|
$f->{Date} = $entry->get_timestamp() || "";
|
|
|
- $f->{Changes} = get_dpkg_changes($entry);
|
|
|
+ $f->{Changes} = $entry->get_dpkg_changes();
|
|
|
|
|
|
# handle optional fields
|
|
|
my $opts = $entry->get_optional_fields();
|
|
|
@@ -613,23 +600,19 @@ sub rfc822 {
|
|
|
|
|
|
run_vendor_hook("post-process-changelog-entry", $f);
|
|
|
|
|
|
- push @out_data, $f;
|
|
|
+ $index->add($f);
|
|
|
}
|
|
|
-
|
|
|
- return @out_data if wantarray;
|
|
|
- return \@out_data;
|
|
|
+ return $index;
|
|
|
}
|
|
|
|
|
|
-sub rfc822_str {
|
|
|
- return data2rfc822(scalar rfc822(@_));
|
|
|
-}
|
|
|
+=back
|
|
|
|
|
|
-=pod
|
|
|
+=head1 RANGE SELECTION
|
|
|
|
|
|
-=head1 COMMON OUTPUT OPTIONS
|
|
|
+A range selection is described by a hash reference where
|
|
|
+the allowed keys and values are described below.
|
|
|
|
|
|
-The following options are supported by all output methods,
|
|
|
-all take a version number as value:
|
|
|
+The following options take a version number as value.
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
@@ -655,8 +638,7 @@ specified B<version> itself.
|
|
|
|
|
|
=back
|
|
|
|
|
|
-The following options are also supported by all output methods but
|
|
|
-don't take version numbers as values:
|
|
|
+The following options don't take version numbers as values:
|
|
|
|
|
|
=over 4
|
|
|
|
|
|
@@ -683,109 +665,26 @@ wasn't given as well.
|
|
|
Some examples for the above options. Imagine an example changelog with
|
|
|
entries for the versions 1.2, 1.3, 2.0, 2.1, 2.2, 3.0 and 3.1.
|
|
|
|
|
|
- Call Included entries
|
|
|
- C<E<lt>formatE<gt>({ since =E<gt> '2.0' })> 3.1, 3.0, 2.2
|
|
|
- C<E<lt>formatE<gt>({ until =E<gt> '2.0' })> 1.3, 1.2
|
|
|
- C<E<lt>formatE<gt>({ from =E<gt> '2.0' })> 3.1, 3.0, 2.2, 2.1, 2.0
|
|
|
- C<E<lt>formatE<gt>({ to =E<gt> '2.0' })> 2.0, 1.3, 1.2
|
|
|
- C<E<lt>formatE<gt>({ count =E<gt> 2 }>> 3.1, 3.0
|
|
|
- C<E<lt>formatE<gt>({ count =E<gt> -2 }>> 1.3, 1.2
|
|
|
- C<E<lt>formatE<gt>({ count =E<gt> 3,
|
|
|
- offset=E<gt> 2 }>> 2.2, 2.1, 2.0
|
|
|
- C<E<lt>formatE<gt>({ count =E<gt> 2,
|
|
|
- offset=E<gt> -3 }>> 2.0, 1.3
|
|
|
- C<E<lt>formatE<gt>({ count =E<gt> -2,
|
|
|
- offset=E<gt> 3 }>> 3.0, 2.2
|
|
|
- C<E<lt>formatE<gt>({ count =E<gt> -2,
|
|
|
- offset=E<gt> -3 }>> 2.2, 2.1
|
|
|
+ Range Included entries
|
|
|
+ C<{ since =E<gt> '2.0' }> 3.1, 3.0, 2.2
|
|
|
+ C<{ until =E<gt> '2.0' }> 1.3, 1.2
|
|
|
+ C<{ from =E<gt> '2.0' }> 3.1, 3.0, 2.2, 2.1, 2.0
|
|
|
+ C<{ to =E<gt> '2.0' }> 2.0, 1.3, 1.2
|
|
|
+ C<{ count =E<gt> 2 }> 3.1, 3.0
|
|
|
+ C<{ count =E<gt> -2 }> 1.3, 1.2
|
|
|
+ C<{ count =E<gt> 3, offset=E<gt> 2 }> 2.2, 2.1, 2.0
|
|
|
+ C<{ count =E<gt> 2, offset=E<gt> -3 }> 2.0, 1.3
|
|
|
+ C<{ count =E<gt> -2, offset=E<gt> 3 }> 3.0, 2.2
|
|
|
+ C<{ count =E<gt> -2, offset=E<gt> -3 }> 2.2, 2.1
|
|
|
|
|
|
Any combination of one option of C<since> and C<from> and one of
|
|
|
C<until> and C<to> returns the intersection of the two results
|
|
|
with only one of the options specified.
|
|
|
|
|
|
-=head1 UTILITY FUNCTIONS
|
|
|
-
|
|
|
-=head3 data2rfc822
|
|
|
-
|
|
|
-Takes a single argument, either a Dpkg::Changelog::Entry object
|
|
|
-or a reference to an array of such objects.
|
|
|
-
|
|
|
-Returns the data in RFC822 format as string.
|
|
|
-
|
|
|
-=cut
|
|
|
-
|
|
|
-sub data2rfc822 {
|
|
|
- my ($data) = @_;
|
|
|
-
|
|
|
- if (ref($data) eq "ARRAY") {
|
|
|
- my @rfc822 = ();
|
|
|
-
|
|
|
- foreach my $entry (@$data) {
|
|
|
- push @rfc822, data2rfc822($entry);
|
|
|
- }
|
|
|
-
|
|
|
- return join "\n", @rfc822;
|
|
|
- } elsif (ref($data)) {
|
|
|
- my $rfc822_str = $data->output;
|
|
|
-
|
|
|
- return $rfc822_str;
|
|
|
- } else {
|
|
|
- return;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-=pod
|
|
|
-
|
|
|
-=head3 get_dpkg_changes
|
|
|
-
|
|
|
-Takes a Dpkg::Changelog::Entry object as first argument.
|
|
|
-
|
|
|
-Returns a string that is suitable for using it in a C<Changes> field
|
|
|
-in the output format of C<dpkg-parsechangelog>.
|
|
|
-
|
|
|
-=cut
|
|
|
-
|
|
|
-sub get_dpkg_changes {
|
|
|
- my $entry = shift;
|
|
|
- my $header = $entry->get_part("header") || "";
|
|
|
- $header =~ s/\s+$//;
|
|
|
- my $changes = "\n $header\n .\n";
|
|
|
- foreach my $line (@{$entry->get_part("changes")}) {
|
|
|
- $line =~ s/\s+$//;
|
|
|
- if ($line eq "") {
|
|
|
- $changes .= " .\n";
|
|
|
- } else {
|
|
|
- $changes .= " $line\n";
|
|
|
- }
|
|
|
- }
|
|
|
- chomp $changes;
|
|
|
- return $changes;
|
|
|
-}
|
|
|
-
|
|
|
-1;
|
|
|
-__END__
|
|
|
-
|
|
|
=head1 AUTHOR
|
|
|
|
|
|
Frank Lichtenheld, E<lt>frank@lichtenheld.deE<gt>
|
|
|
-
|
|
|
-=head1 COPYRIGHT AND LICENSE
|
|
|
-
|
|
|
-Copyright E<copy> 2005, 2007 by Frank Lichtenheld
|
|
|
-Copyright E<copy> 2009 by Raphael Hertzog
|
|
|
-
|
|
|
-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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
+Raphael Hertzog, E<lt>hertzog@debian.orgE<gt>
|
|
|
|
|
|
=cut
|
|
|
+1;
|