File Coverage

blib/lib/Dpkg/Changelog.pm
Criterion Covered Total %
statement 224 299 74.9
branch 88 158 55.7
condition 64 115 55.6
subroutine 31 31 100.0
pod 11 11 100.0
total 418 614 68.0


line stmt bran cond sub pod time code
1             # Copyright © 2005, 2007 Frank Lichtenheld
2             # Copyright © 2009 Raphaël Hertzog
3             #
4             # This program is free software; you can redistribute it and/or modify
5             # it under the terms of the GNU General Public License as published by
6             # the Free Software Foundation; either version 2 of the License, or
7             # (at your option) any later version.
8             #
9             # This program is distributed in the hope that it will be useful,
10             # but WITHOUT ANY WARRANTY; without even the implied warranty of
11             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12             # GNU General Public License for more details.
13             #
14             # You should have received a copy of the GNU General Public License
15             # along with this program. If not, see .
16              
17             =encoding utf8
18              
19             =head1 NAME
20              
21             Dpkg::Changelog - base class to implement a changelog parser
22              
23             =head1 DESCRIPTION
24              
25             Dpkg::Changelog is a class representing a changelog file
26             as an array of changelog entries (Dpkg::Changelog::Entry).
27             By deriving this class and implementing its parse method, you
28             add the ability to fill this object with changelog entries.
29              
30             =cut
31              
32             package Dpkg::Changelog;
33              
34 2     2   1481 use strict;
  2         3  
  2         60  
35 2     2   10 use warnings;
  2         4  
  2         76  
36              
37             our $VERSION = '2.00';
38              
39 2     2   10 use Carp;
  2         3  
  2         98  
40              
41 2     2   14 use Dpkg::Gettext;
  2         3  
  2         129  
42 2     2   13 use Dpkg::ErrorHandling qw(:DEFAULT report REPORT_WARN);
  2         3  
  2         343  
43 2     2   912 use Dpkg::Control;
  2         6  
  2         207  
44 2     2   903 use Dpkg::Control::Changelog;
  2         5  
  2         87  
45 2     2   14 use Dpkg::Control::Fields;
  2         4  
  2         157  
46 2     2   893 use Dpkg::Index;
  2         6  
  2         56  
47 2     2   961 use Dpkg::Version;
  2         7  
  2         199  
48 2     2   16 use Dpkg::Vendor qw(run_vendor_hook);
  2         5  
  2         86  
49              
50 2     2   12 use parent qw(Dpkg::Interface::Storable);
  2         4  
  2         14  
51              
52             use overload
53 2     2   184 '@{}' => sub { return $_[0]->{data} };
  2     24   4  
  2         15  
  24         18583  
54              
55             =head1 METHODS
56              
57             =over 4
58              
59             =item $c = Dpkg::Changelog->new(%options)
60              
61             Creates a new changelog object.
62              
63             =cut
64              
65             sub new {
66 16     16 1 250 my ($this, %opts) = @_;
67 16   33     94 my $class = ref($this) || $this;
68 16         67 my $self = {
69             verbose => 1,
70             parse_errors => []
71             };
72 16         41 bless $self, $class;
73 16         81 $self->set_options(%opts);
74 16         47 return $self;
75             }
76              
77             =item $c->set_options(%opts)
78              
79             Change the value of some options. "verbose" (defaults to 1) defines
80             whether parse errors are displayed as warnings by default. "reportfile"
81             is a string to use instead of the name of the file parsed, in particular
82             in error messages. "range" defines the range of entries that we want to
83             parse, the parser will stop as soon as it has parsed enough data to
84             satisfy $c->get_range($opts{range}).
85              
86             =cut
87              
88             sub set_options {
89 16     16 1 48 my ($self, %opts) = @_;
90 16         167 $self->{$_} = $opts{$_} foreach keys %opts;
91             }
92              
93             =item $count = $c->parse($fh, $description)
94              
95             Read the filehandle and parse a changelog in it. The data in the object is
96             reset before parsing new data.
97              
98             Returns the number of changelog entries that have been parsed with success.
99              
100             This method needs to be implemented by one of the specialized changelog
101             format subclasses.
102              
103             =item $count = $c->load($filename)
104              
105             Parse $filename contents for a changelog.
106              
107             Returns the number of changelog entries that have been parsed with success.
108              
109             =item $c->reset_parse_errors()
110              
111             Can be used to delete all information about errors occurred during
112             previous L runs.
113              
114             =cut
115              
116             sub reset_parse_errors {
117 16     16 1 34 my $self = shift;
118 16         56 $self->{parse_errors} = [];
119             }
120              
121             =item $c->parse_error($file, $line_nr, $error, [$line])
122              
123             Record a new parse error in $file at line $line_nr. The error message is
124             specified with $error and a copy of the line can be recorded in $line.
125              
126             =cut
127              
128             sub parse_error {
129 6     6 1 25 my ($self, $file, $line_nr, $error, $line) = @_;
130              
131 6         13 push @{$self->{parse_errors}}, [ $file, $line_nr, $error, $line ];
  6         23  
132              
133 6 50       29 if ($self->{verbose}) {
134 0 0       0 if ($line) {
135 0         0 warning("%20s(l$line_nr): $error\nLINE: $line", $file);
136             } else {
137 0         0 warning("%20s(l$line_nr): $error", $file);
138             }
139             }
140             }
141              
142             =item $c->get_parse_errors()
143              
144             Returns all error messages from the last L run.
145             If called in scalar context returns a human readable
146             string representation. If called in list context returns
147             an array of arrays. Each of these arrays contains
148              
149             =over 4
150              
151             =item 1.
152              
153             a string describing the origin of the data (a filename usually). If the
154             reportfile configuration option was given, its value will be used instead.
155              
156             =item 2.
157              
158             the line number where the error occurred
159              
160             =item 3.
161              
162             an error description
163              
164             =item 4.
165              
166             the original line
167              
168             =back
169              
170             =cut
171              
172             sub get_parse_errors {
173 16     16 1 50 my $self = shift;
174              
175 16 100       48 if (wantarray) {
176 4         8 return @{$self->{parse_errors}};
  4         17  
177             } else {
178 12         24 my $res = '';
179 12         26 foreach my $e (@{$self->{parse_errors}}) {
  12         53  
180 0 0       0 if ($e->[3]) {
181 0         0 $res .= report(REPORT_WARN, g_("%s(l%s): %s\nLINE: %s"), @$e);
182             } else {
183 0         0 $res .= report(REPORT_WARN, g_('%s(l%s): %s'), @$e);
184             }
185             }
186 12         38 return $res;
187             }
188             }
189              
190             =item $c->set_unparsed_tail($tail)
191              
192             Add a string representing unparsed lines after the changelog entries.
193             Use undef as $tail to remove the unparsed lines currently set.
194              
195             =item $c->get_unparsed_tail()
196              
197             Return a string representing the unparsed lines after the changelog
198             entries. Returns undef if there's no such thing.
199              
200             =cut
201              
202             sub set_unparsed_tail {
203 20     20 1 51 my ($self, $tail) = @_;
204 20         66 $self->{unparsed_tail} = $tail;
205             }
206              
207             sub get_unparsed_tail {
208 14     14 1 739 my $self = shift;
209 14         41 return $self->{unparsed_tail};
210             }
211              
212             =item @{$c}
213              
214             Returns all the Dpkg::Changelog::Entry objects contained in this changelog
215             in the order in which they have been parsed.
216              
217             =item $c->get_range($range)
218              
219             Returns an array (if called in list context) or a reference to an array of
220             Dpkg::Changelog::Entry objects which each represent one entry of the
221             changelog. $range is a hash reference describing the range of entries
222             to return. See section L<"RANGE SELECTION">.
223              
224             =cut
225              
226             sub __sanity_check_range {
227 70     70   119 my ($self, $r) = @_;
228 70         148 my $data = $self->{data};
229              
230 70 50 66     222 if (defined($r->{offset}) and not defined($r->{count})) {
231 0 0       0 warning(g_("'offset' without 'count' has no effect")) if $self->{verbose};
232 0         0 delete $r->{offset};
233             }
234              
235             ## no critic (ControlStructures::ProhibitUntilBlocks)
236 70 50 66     469 if ((defined($r->{count}) || defined($r->{offset})) &&
      33        
      66        
237             (defined($r->{from}) || defined($r->{since}) ||
238             defined($r->{to}) || defined($r->{until})))
239             {
240             warning(g_("you can't combine 'count' or 'offset' with any other " .
241 0 0       0 'range option')) if $self->{verbose};
242 0         0 delete $r->{from};
243 0         0 delete $r->{since};
244 0         0 delete $r->{to};
245 0         0 delete $r->{until};
246             }
247 70 50 66     190 if (defined($r->{from}) && defined($r->{since})) {
248             warning(g_("you can only specify one of 'from' and 'since', using " .
249 0 0       0 "'since'")) if $self->{verbose};
250 0         0 delete $r->{from};
251             }
252 70 50 66     169 if (defined($r->{to}) && defined($r->{until})) {
253             warning(g_("you can only specify one of 'to' and 'until', using " .
254 0 0       0 "'until'")) if $self->{verbose};
255 0         0 delete $r->{to};
256             }
257              
258             # Handle non-existing versions
259 70         112 my (%versions, @versions);
260 70         94 foreach my $entry (@{$data}) {
  70         130  
261 818         1669 my $version = $entry->get_version();
262 818 50       1441 next unless defined $version;
263 818         1512 $versions{$version->as_string()} = 1;
264 818         1676 push @versions, $version->as_string();
265             }
266 70 100 100     231 if ((defined($r->{since}) and not exists $versions{$r->{since}})) {
267 2         14 warning(g_("'%s' option specifies non-existing version '%s'"), 'since', $r->{since});
268 2         17 warning(g_('use newest entry that is earlier than the one specified'));
269 2         11 foreach my $v (@versions) {
270 14 50       41 if (version_compare_relation($v, REL_LT, $r->{since})) {
271 0         0 $r->{since} = $v;
272 0         0 last;
273             }
274             }
275 2 50       10 if (not exists $versions{$r->{since}}) {
276             # No version was earlier, include all
277 2         9 warning(g_('none found, starting from the oldest entry'));
278 2         10 delete $r->{since};
279 2         18 $r->{from} = $versions[-1];
280             }
281             }
282 70 50 66     207 if ((defined($r->{from}) and not exists $versions{$r->{from}})) {
283 0         0 warning(g_("'%s' option specifies non-existing version '%s'"), 'from', $r->{from});
284 0         0 warning(g_('use oldest entry that is later than the one specified'));
285 0         0 my $oldest;
286 0         0 foreach my $v (@versions) {
287 0 0       0 if (version_compare_relation($v, REL_GT, $r->{from})) {
288 0         0 $oldest = $v;
289             }
290             }
291 0 0       0 if (defined($oldest)) {
292 0         0 $r->{from} = $oldest;
293             } else {
294 0         0 warning(g_("no such entry found, ignoring '%s' parameter '%s'"), 'from', $r->{from});
295 0         0 delete $r->{from}; # No version was oldest
296             }
297             }
298 70 50 66     176 if (defined($r->{until}) and not exists $versions{$r->{until}}) {
299 0         0 warning(g_("'%s' option specifies non-existing version '%s'"), 'until', $r->{until});
300 0         0 warning(g_('use oldest entry that is later than the one specified'));
301 0         0 my $oldest;
302 0         0 foreach my $v (@versions) {
303 0 0       0 if (version_compare_relation($v, REL_GT, $r->{until})) {
304 0         0 $oldest = $v;
305             }
306             }
307 0 0       0 if (defined($oldest)) {
308 0         0 $r->{until} = $oldest;
309             } else {
310 0         0 warning(g_("no such entry found, ignoring '%s' parameter '%s'"), 'until', $r->{until});
311 0         0 delete $r->{until}; # No version was oldest
312             }
313             }
314 70 50 66     170 if (defined($r->{to}) and not exists $versions{$r->{to}}) {
315 0         0 warning(g_("'%s' option specifies non-existing version '%s'"), 'to', $r->{to});
316 0         0 warning(g_('use newest entry that is earlier than the one specified'));
317 0         0 foreach my $v (@versions) {
318 0 0       0 if (version_compare_relation($v, REL_LT, $r->{to})) {
319 0         0 $r->{to} = $v;
320 0         0 last;
321             }
322             }
323 0 0       0 if (not exists $versions{$r->{to}}) {
324             # No version was earlier
325 0         0 warning(g_("no such entry found, ignoring '%s' parameter '%s'"), 'to', $r->{to});
326 0         0 delete $r->{to};
327             }
328             }
329              
330 70 50 66     148 if (defined($r->{since}) and $data->[0]->get_version() eq $r->{since}) {
331 0         0 warning(g_("'since' option specifies most recent version '%s', ignoring"), $r->{since});
332 0         0 delete $r->{since};
333             }
334 70 50 66     337 if (defined($r->{until}) and $data->[-1]->get_version() eq $r->{until}) {
335 0         0 warning(g_("'until' option specifies oldest version '%s', ignoring"), $r->{until});
336 0         0 delete $r->{until};
337             }
338             ## use critic
339             }
340              
341             sub get_range {
342 84     84 1 20272 my ($self, $range) = @_;
343 84   100     267 $range //= {};
344 84         199 my $res = $self->_data_range($range);
345 84 50       203 return unless defined $res;
346 84 50       203 if (wantarray) {
347 84 100       183 return reverse @{$res} if $range->{reverse};
  2         8  
348 82         106 return @{$res};
  82         375  
349             } else {
350 0         0 return $res;
351             }
352             }
353              
354             sub _is_full_range {
355 84     84   132 my ($self, $range) = @_;
356              
357 84 100       191 return 1 if $range->{all};
358              
359             # If no range delimiter is specified, we want everything.
360 82         170 foreach my $delim (qw(since until from to count offset)) {
361 314 100       646 return 0 if exists $range->{$delim};
362             }
363              
364 12         71 return 1;
365             }
366              
367             sub _data_range {
368 84     84   141 my ($self, $range) = @_;
369              
370 84 50       276 my $data = $self->{data} or return;
371              
372 84 100       188 return [ @$data ] if $self->_is_full_range($range);
373              
374 70         202 $self->__sanity_check_range($range);
375              
376 70         150 my ($start, $end);
377 70 100       176 if (defined($range->{count})) {
378 40   100     107 my $offset = $range->{offset} // 0;
379 40         58 my $count = $range->{count};
380             # Convert count/offset in start/end
381 40 100       104 if ($offset > 0) {
    100          
382 12         23 $offset -= ($count < 0);
383             } elsif ($offset < 0) {
384 8         17 $offset = $#$data + ($count > 0) + $offset;
385             } else {
386 20 100       40 $offset = $#$data if $count < 0;
387             }
388 40         72 $start = $end = $offset;
389 40 100       87 $start += $count+1 if $count < 0;
390 40 100       76 $end += $count-1 if $count > 0;
391             # Check limits
392 40 100       73 $start = 0 if $start < 0;
393 40 50       81 return if $start > $#$data;
394 40 100       72 $end = $#$data if $end > $#$data;
395 40 50       72 return if $end < 0;
396 40 50       72 $end = $start if $end < $start;
397 40         79 return [ @{$data}[$start .. $end] ];
  40         125  
398             }
399              
400             ## no critic (ControlStructures::ProhibitUntilBlocks)
401 30         51 my @result;
402 30         61 my $include = 1;
403 30 100 100     147 $include = 0 if defined($range->{to}) or defined($range->{until});
404 30         47 foreach my $entry (@{$data}) {
  30         67  
405 538         953 my $v = $entry->get_version();
406 538 100 100     996 $include = 1 if defined($range->{to}) and $v eq $range->{to};
407 538 100 100     948 last if defined($range->{since}) and $v eq $range->{since};
408              
409 536 100       896 push @result, $entry if $include;
410              
411 536 100 100     920 $include = 1 if defined($range->{until}) and $v eq $range->{until};
412 536 100 100     1074 last if defined($range->{from}) and $v eq $range->{from};
413             }
414             ## use critic
415              
416 30 50       124 return \@result if scalar(@result);
417 0         0 return;
418             }
419              
420             =item $c->abort_early()
421              
422             Returns true if enough data have been parsed to be able to return all
423             entries selected by the range set at creation (or with set_options).
424              
425             =cut
426              
427             sub abort_early {
428 234     234 1 362 my $self = shift;
429              
430 234 50       631 my $data = $self->{data} or return;
431 234 50       829 my $r = $self->{range} or return;
432 0   0     0 my $count = $r->{count} // 0;
433 0   0     0 my $offset = $r->{offset} // 0;
434              
435 0 0       0 return if $self->_is_full_range($r);
436 0 0 0     0 return if $offset < 0 or $count < 0;
437 0 0       0 if (defined($r->{count})) {
438 0 0       0 if ($offset > 0) {
439 0         0 $offset -= ($count < 0);
440             }
441 0         0 my $start = my $end = $offset;
442 0 0       0 $end += $count-1 if $count > 0;
443 0   0     0 return ($start < @$data and $end < @$data);
444             }
445              
446 0 0 0     0 return unless defined($r->{since}) or defined($r->{from});
447 0         0 foreach my $entry (@{$data}) {
  0         0  
448 0         0 my $v = $entry->get_version();
449 0 0 0     0 return 1 if defined($r->{since}) and $v eq $r->{since};
450 0 0 0     0 return 1 if defined($r->{from}) and $v eq $r->{from};
451             }
452              
453 0         0 return;
454             }
455              
456             =item $str = $c->output()
457              
458             =item "$c"
459              
460             Returns a string representation of the changelog (it's a concatenation of
461             the string representation of the individual changelog entries).
462              
463             =item $c->output($fh)
464              
465             Output the changelog to the given filehandle.
466              
467             =cut
468              
469             sub output {
470 12     12 1 36 my ($self, $fh) = @_;
471 12         23 my $str = '';
472 12         19 foreach my $entry (@{$self}) {
  12         59  
473 242         601 my $text = $entry->output();
474 242 50       413 print { $fh } $text if defined $fh;
  0         0  
475 242 50       947 $str .= $text if defined wantarray;
476             }
477 12         46 my $text = $self->get_unparsed_tail();
478 12 100       44 if (defined $text) {
479 4 50       13 print { $fh } $text if defined $fh;
  0         0  
480 4 50       29 $str .= $text if defined wantarray;
481             }
482 12         385 return $str;
483             }
484              
485             =item $c->save($filename)
486              
487             Save the changelog in the given file.
488              
489             =cut
490              
491             our ( @URGENCIES, %URGENCIES );
492             BEGIN {
493 2     2   4407 @URGENCIES = qw(low medium high critical emergency);
494 2         11 my $i = 1;
495 2         7 %URGENCIES = map { $_ => $i++ } @URGENCIES;
  10         1912  
496             }
497              
498             sub _format_dpkg {
499 16     16   44 my ($self, $range) = @_;
500              
501 16 50       61 my @data = $self->get_range($range) or return;
502 16         38 my $src = shift @data;
503              
504 16         124 my $f = Dpkg::Control::Changelog->new();
505 16   50     72 $f->{Urgency} = $src->get_urgency() || 'unknown';
506 16   50     71 $f->{Source} = $src->get_source() || 'unknown';
507 16   50     65 $f->{Version} = $src->get_version() // 'unknown';
508 16         69 $f->{Distribution} = join(' ', $src->get_distributions());
509 16   50     77 $f->{Maintainer} = $src->get_maintainer() // '';
510 16   50     53 $f->{Date} = $src->get_timestamp() // '';
511 16   66     63 $f->{Timestamp} = $src->get_timepiece && $src->get_timepiece->epoch // '';
      50        
512 16         102 $f->{Changes} = $src->get_dpkg_changes();
513              
514             # handle optional fields
515 16         67 my $opts = $src->get_optional_fields();
516 16         27 my %closes;
517 16         40 foreach (keys %$opts) {
518 26 100       109 if (/^Urgency$/i) { # Already dealt
    50          
519             } elsif (/^Closes$/i) {
520 10         29 $closes{$_} = 1 foreach (split(/\s+/, $opts->{Closes}));
521             } else {
522 0         0 field_transfer_single($opts, $f);
523             }
524             }
525              
526 16         45 foreach my $bin (@data) {
527 238   50     551 my $oldurg = $f->{Urgency} // '';
528 238   50     621 my $oldurgn = $URGENCIES{$f->{Urgency}} // -1;
529 238   50     716 my $newurg = $bin->get_urgency() // '';
530 238   50     617 my $newurgn = $URGENCIES{$newurg} // -1;
531 238 100       647 $f->{Urgency} = ($newurgn > $oldurgn) ? $newurg : $oldurg;
532 238         544 $f->{Changes} .= "\n" . $bin->get_dpkg_changes();
533              
534             # handle optional fields
535 238         1728 $opts = $bin->get_optional_fields();
536 238         569 foreach (keys %$opts) {
537 416 100       1338 if (/^Closes$/i) {
    100          
538 168         365 $closes{$_} = 1 foreach (split(/\s+/, $opts->{Closes}));
539             } elsif (not exists $f->{$_}) { # Don't overwrite an existing field
540 10         39 field_transfer_single($opts, $f);
541             }
542             }
543             }
544              
545 16 100       62 if (scalar keys %closes) {
546 10         231 $f->{Closes} = join ' ', sort { $a <=> $b } keys %closes;
  8501         10030  
547             }
548 16         131 run_vendor_hook('post-process-changelog-entry', $f);
549              
550 16         275 return $f;
551             }
552              
553             sub _format_rfc822 {
554 22     22   45 my ($self, $range) = @_;
555              
556 22 50       75 my @data = $self->get_range($range) or return;
557 22         43 my @ctrl;
558              
559 22         44 foreach my $entry (@data) {
560 484         1351 my $f = Dpkg::Control::Changelog->new();
561 484   50     1542 $f->{Urgency} = $entry->get_urgency() || 'unknown';
562 484   50     1533 $f->{Source} = $entry->get_source() || 'unknown';
563 484   50     1400 $f->{Version} = $entry->get_version() // 'unknown';
564 484         1464 $f->{Distribution} = join(' ', $entry->get_distributions());
565 484   50     1291 $f->{Maintainer} = $entry->get_maintainer() // '';
566 484   50     1326 $f->{Date} = $entry->get_timestamp() // '';
567 484   66     1275 $f->{Timestamp} = $entry->get_timepiece && $entry->get_timepiece->epoch // '';
      50        
568 484         1667 $f->{Changes} = $entry->get_dpkg_changes();
569              
570             # handle optional fields
571 484         1446 my $opts = $entry->get_optional_fields();
572 484         1023 foreach (keys %$opts) {
573 834 100       1662 field_transfer_single($opts, $f) unless exists $f->{$_};
574             }
575              
576 484         1655 run_vendor_hook('post-process-changelog-entry', $f);
577              
578 484         1188 push @ctrl, $f;
579             }
580              
581 22         306 return @ctrl;
582             }
583              
584             =item $control = $c->format_range($format, $range)
585              
586             Formats the changelog into Dpkg::Control::Changelog objects representing the
587             entries selected by the optional range specifier (see L<"RANGE SELECTION">
588             for details). In scalar context returns a Dpkg::Index object containing the
589             selected entries, in list context returns an array of Dpkg::Control::Changelog
590             objects.
591              
592             With format B the returned Dpkg::Control::Changelog object is coalesced
593             from the entries in the changelog that are part of the range requested,
594             with the fields described below, but considering that "selected entry"
595             means the first entry of the selected range.
596              
597             With format B each returned Dpkg::Control::Changelog objects
598             represents one entry in the changelog that is part of the range requested,
599             with the fields described below, but considering that "selected entry"
600             means for each entry.
601              
602             The different formats return undef if no entries are matched. The following
603             fields are contained in the object(s) returned:
604              
605             =over 4
606              
607             =item Source
608              
609             package name (selected entry)
610              
611             =item Version
612              
613             packages' version (selected entry)
614              
615             =item Distribution
616              
617             target distribution (selected entry)
618              
619             =item Urgency
620              
621             urgency (highest of all entries in range)
622              
623             =item Maintainer
624              
625             person that created the (selected) entry
626              
627             =item Date
628              
629             date of the (selected) entry
630              
631             =item Timestamp
632              
633             date of the (selected) entry as a timestamp in seconds since the epoch
634              
635             =item Closes
636              
637             bugs closed by the (selected) entry/entries, sorted by bug number
638              
639             =item Changes
640              
641             content of the (selected) entry/entries
642              
643             =back
644              
645             =cut
646              
647             sub format_range {
648 38     38 1 27407 my ($self, $format, $range) = @_;
649              
650 38         84 my @ctrl;
651              
652 38 100       143 if ($format eq 'dpkg') {
    50          
653 16         64 @ctrl = $self->_format_dpkg($range);
654             } elsif ($format eq 'rfc822') {
655 22         72 @ctrl = $self->_format_rfc822($range);
656             } else {
657 0         0 croak "unknown changelog output format $format";
658             }
659              
660 38 100       2978 if (wantarray) {
661 2         10 return @ctrl;
662             } else {
663 36         215 my $index = Dpkg::Index->new(type => CTRL_CHANGELOG);
664              
665 36         79 foreach my $f (@ctrl) {
666 498         979 $index->add($f);
667             }
668              
669 36         328 return $index;
670             }
671             }
672              
673             =back
674              
675             =head1 RANGE SELECTION
676              
677             A range selection is described by a hash reference where
678             the allowed keys and values are described below.
679              
680             The following options take a version number as value.
681              
682             =over 4
683              
684             =item since
685              
686             Causes changelog information from all versions strictly
687             later than B to be used.
688              
689             =item until
690              
691             Causes changelog information from all versions strictly
692             earlier than B to be used.
693              
694             =item from
695              
696             Similar to C but also includes the information for the
697             specified B itself.
698              
699             =item to
700              
701             Similar to C but also includes the information for the
702             specified B itself.
703              
704             =back
705              
706             The following options don't take version numbers as values:
707              
708             =over 4
709              
710             =item all
711              
712             If set to a true value, all entries of the changelog are returned,
713             this overrides all other options.
714              
715             =item count
716              
717             Expects a signed integer as value. Returns C entries from the
718             top of the changelog if set to a positive integer, and C
719             entries from the tail if set to a negative integer.
720              
721             =item offset
722              
723             Expects a signed integer as value. Changes the starting point for
724             C, either counted from the top (positive integer) or from
725             the tail (negative integer). C has no effect if C
726             wasn't given as well.
727              
728             =back
729              
730             Some examples for the above options. Imagine an example changelog with
731             entries for the versions 1.2, 1.3, 2.0, 2.1, 2.2, 3.0 and 3.1.
732              
733             Range Included entries
734             ----- ----------------
735             since => '2.0' 3.1, 3.0, 2.2
736             until => '2.0' 1.3, 1.2
737             from => '2.0' 3.1, 3.0, 2.2, 2.1, 2.0
738             to => '2.0' 2.0, 1.3, 1.2
739             count => 2 3.1, 3.0
740             count => -2 1.3, 1.2
741             count => 3, offset => 2 2.2, 2.1, 2.0
742             count => 2, offset => -3 2.0, 1.3
743             count => -2, offset => 3 3.0, 2.2
744             count => -2, offset => -3 2.2, 2.1
745              
746             Any combination of one option of C and C and one of
747             C and C returns the intersection of the two results
748             with only one of the options specified.
749              
750             =head1 CHANGES
751              
752             =head2 Version 2.00 (dpkg 1.20.0)
753              
754             Remove methods: $c->dpkg(), $c->rfc822().
755              
756             =head2 Version 1.01 (dpkg 1.18.8)
757              
758             New method: $c->format_range().
759              
760             Deprecated methods: $c->dpkg(), $c->rfc822().
761              
762             New field Timestamp in output formats.
763              
764             =head2 Version 1.00 (dpkg 1.15.6)
765              
766             Mark the module as public.
767              
768             =cut
769             1;