line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# Copyright © 2009 Raphaël Hertzog |
2
|
|
|
|
|
|
|
# Copyright © 2012-2013 Guillem Jover |
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
|
|
|
|
|
|
|
package Dpkg::Changelog::Entry::Debian; |
18
|
|
|
|
|
|
|
|
19
|
2
|
|
|
2
|
|
15
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
57
|
|
20
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
106
|
|
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
our $VERSION = '2.00'; |
23
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
24
|
|
|
|
|
|
|
match_header |
25
|
|
|
|
|
|
|
match_trailer |
26
|
|
|
|
|
|
|
find_closes |
27
|
|
|
|
|
|
|
); |
28
|
|
|
|
|
|
|
|
29
|
2
|
|
|
2
|
|
12
|
use Exporter qw(import); |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
49
|
|
30
|
2
|
|
|
2
|
|
1187
|
use Time::Piece; |
|
2
|
|
|
|
|
20037
|
|
|
2
|
|
|
|
|
8
|
|
31
|
|
|
|
|
|
|
|
32
|
2
|
|
|
2
|
|
156
|
use Dpkg::Gettext; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
114
|
|
33
|
2
|
|
|
2
|
|
13
|
use Dpkg::Control::Fields; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
180
|
|
34
|
2
|
|
|
2
|
|
12
|
use Dpkg::Control::Changelog; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
82
|
|
35
|
2
|
|
|
2
|
|
899
|
use Dpkg::Changelog::Entry; |
|
2
|
|
|
|
|
7
|
|
|
2
|
|
|
|
|
63
|
|
36
|
2
|
|
|
2
|
|
14
|
use Dpkg::Version; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
181
|
|
37
|
|
|
|
|
|
|
|
38
|
2
|
|
|
2
|
|
13
|
use parent qw(Dpkg::Changelog::Entry); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
11
|
|
39
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
=encoding utf8 |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
=head1 NAME |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
Dpkg::Changelog::Entry::Debian - represents a Debian changelog entry |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
=head1 DESCRIPTION |
47
|
|
|
|
|
|
|
|
48
|
|
|
|
|
|
|
This class represents a Debian changelog entry. |
49
|
|
|
|
|
|
|
It implements the generic interface Dpkg::Changelog::Entry. |
50
|
|
|
|
|
|
|
Only functions specific to this implementation are described below, |
51
|
|
|
|
|
|
|
the rest are inherited. |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
=cut |
54
|
|
|
|
|
|
|
|
55
|
|
|
|
|
|
|
my $name_chars = qr/[-+0-9a-z.]/i; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
# The matched content is the source package name ($1), the version ($2), |
58
|
|
|
|
|
|
|
# the target distributions ($3) and the options on the rest of the line ($4). |
59
|
|
|
|
|
|
|
my $regex_header = qr{ |
60
|
|
|
|
|
|
|
^ |
61
|
|
|
|
|
|
|
(\w$name_chars*) # Package name |
62
|
|
|
|
|
|
|
\ \(([^\(\) \t]+)\) # Package version |
63
|
|
|
|
|
|
|
((?:\s+$name_chars+)+) # Target distribution |
64
|
|
|
|
|
|
|
\; # Separator |
65
|
|
|
|
|
|
|
(.*?) # Key=Value options |
66
|
|
|
|
|
|
|
\s*$ # Trailing space |
67
|
|
|
|
|
|
|
}xi; |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
# The matched content is the maintainer name ($1), its email ($2), |
70
|
|
|
|
|
|
|
# some blanks ($3) and the timestamp ($4), which is decomposed into |
71
|
|
|
|
|
|
|
# day of week ($6), date-time ($7) and this into month name ($8). |
72
|
|
|
|
|
|
|
my $regex_trailer = qr< |
73
|
|
|
|
|
|
|
^ |
74
|
|
|
|
|
|
|
\ \-\- # Trailer marker |
75
|
|
|
|
|
|
|
\ (.*) # Maintainer name |
76
|
|
|
|
|
|
|
\ \<(.*)\> # Maintainer email |
77
|
|
|
|
|
|
|
(\ \ ?) # Blanks |
78
|
|
|
|
|
|
|
( |
79
|
|
|
|
|
|
|
((\w+)\,\s*)? # Day of week (abbreviated) |
80
|
|
|
|
|
|
|
( |
81
|
|
|
|
|
|
|
\d{1,2}\s+ # Day of month |
82
|
|
|
|
|
|
|
(\w+)\s+ # Month name (abbreviated) |
83
|
|
|
|
|
|
|
\d{4}\s+ # Year |
84
|
|
|
|
|
|
|
\d{1,2}:\d\d:\d\d\s+[-+]\d{4} # ISO 8601 date |
85
|
|
|
|
|
|
|
) |
86
|
|
|
|
|
|
|
) |
87
|
|
|
|
|
|
|
\s*$ # Trailing space |
88
|
|
|
|
|
|
|
>xo; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
my %week_day = map { $_ => 1 } qw(Mon Tue Wed Thu Fri Sat Sun); |
91
|
|
|
|
|
|
|
my %month_abbrev = map { $_ => 1 } qw( |
92
|
|
|
|
|
|
|
Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec |
93
|
|
|
|
|
|
|
); |
94
|
|
|
|
|
|
|
my %month_name = map { $_ => } qw( |
95
|
|
|
|
|
|
|
January February March April May June July |
96
|
|
|
|
|
|
|
August September October November December |
97
|
|
|
|
|
|
|
); |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
=head1 METHODS |
100
|
|
|
|
|
|
|
|
101
|
|
|
|
|
|
|
=over 4 |
102
|
|
|
|
|
|
|
|
103
|
|
|
|
|
|
|
=item @items = $entry->get_change_items() |
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
Return a list of change items. Each item contains at least one line. |
106
|
|
|
|
|
|
|
A change line starting with an asterisk denotes the start of a new item. |
107
|
|
|
|
|
|
|
Any change line like "C<[ Raphaël Hertzog ]>" is treated like an item of its |
108
|
|
|
|
|
|
|
own even if it starts a set of items attributed to this person (the |
109
|
|
|
|
|
|
|
following line necessarily starts a new item). |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
=cut |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
sub get_change_items { |
114
|
2
|
|
|
2
|
1
|
7
|
my $self = shift; |
115
|
2
|
|
|
|
|
5
|
my (@items, @blanks, $item); |
116
|
2
|
|
|
|
|
6
|
foreach my $line (@{$self->get_part('changes')}) { |
|
2
|
|
|
|
|
8
|
|
117
|
18
|
100
|
|
|
|
78
|
if ($line =~ /^\s*\*/) { |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
118
|
8
|
100
|
|
|
|
21
|
push @items, $item if defined $item; |
119
|
8
|
|
|
|
|
21
|
$item = "$line\n"; |
120
|
|
|
|
|
|
|
} elsif ($line =~ /^\s*\[\s[^\]]+\s\]\s*$/) { |
121
|
4
|
100
|
|
|
|
16
|
push @items, $item if defined $item; |
122
|
4
|
|
|
|
|
14
|
push @items, "$line\n"; |
123
|
4
|
|
|
|
|
7
|
$item = undef; |
124
|
4
|
|
|
|
|
10
|
@blanks = (); |
125
|
|
|
|
|
|
|
} elsif ($line =~ /^\s*$/) { |
126
|
2
|
|
|
|
|
5
|
push @blanks, "$line\n"; |
127
|
|
|
|
|
|
|
} else { |
128
|
4
|
50
|
|
|
|
12
|
if (defined $item) { |
129
|
4
|
|
|
|
|
13
|
$item .= "@blanks$line\n"; |
130
|
|
|
|
|
|
|
} else { |
131
|
0
|
|
|
|
|
0
|
$item = "$line\n"; |
132
|
|
|
|
|
|
|
} |
133
|
4
|
|
|
|
|
7
|
@blanks = (); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
} |
136
|
2
|
50
|
|
|
|
10
|
push @items, $item if defined $item; |
137
|
2
|
|
|
|
|
11
|
return @items; |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=item @errors = $entry->parse_header() |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
=item @errors = $entry->parse_trailer() |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
Return a list of errors. Each item in the list is an error message |
145
|
|
|
|
|
|
|
describing the problem. If the empty list is returned, no errors |
146
|
|
|
|
|
|
|
have been found. |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
=cut |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
sub parse_header { |
151
|
250
|
|
|
250
|
1
|
408
|
my $self = shift; |
152
|
250
|
|
|
|
|
400
|
my @errors; |
153
|
250
|
50
|
33
|
|
|
2460
|
if (defined($self->{header}) and $self->{header} =~ $regex_header) { |
154
|
250
|
|
|
|
|
787
|
$self->{header_source} = $1; |
155
|
|
|
|
|
|
|
|
156
|
250
|
|
|
|
|
938
|
my $version = Dpkg::Version->new($2); |
157
|
250
|
|
|
|
|
663
|
my ($ok, $msg) = version_check($version); |
158
|
250
|
50
|
|
|
|
577
|
if ($ok) { |
159
|
250
|
|
|
|
|
778
|
$self->{header_version} = $version; |
160
|
|
|
|
|
|
|
} else { |
161
|
0
|
|
|
|
|
0
|
push @errors, sprintf(g_("version '%s' is invalid: %s"), $version, $msg); |
162
|
|
|
|
|
|
|
} |
163
|
|
|
|
|
|
|
|
164
|
250
|
|
|
|
|
378
|
@{$self->{header_dists}} = split ' ', $3; |
|
250
|
|
|
|
|
1289
|
|
165
|
|
|
|
|
|
|
|
166
|
250
|
|
|
|
|
677
|
my $options = $4; |
167
|
250
|
|
|
|
|
876
|
$options =~ s/^\s+//; |
168
|
250
|
|
|
|
|
946
|
my $f = Dpkg::Control::Changelog->new(); |
169
|
250
|
|
|
|
|
934
|
foreach my $opt (split(/\s*,\s*/, $options)) { |
170
|
254
|
50
|
|
|
|
1487
|
unless ($opt =~ m/^([-0-9a-z]+)\=\s*(.*\S)$/i) { |
171
|
0
|
|
|
|
|
0
|
push @errors, sprintf(g_("bad key-value after ';': '%s'"), $opt); |
172
|
0
|
|
|
|
|
0
|
next; |
173
|
|
|
|
|
|
|
} |
174
|
254
|
|
|
|
|
772
|
my ($k, $v) = (field_capitalize($1), $2); |
175
|
254
|
50
|
|
|
|
856
|
if (exists $f->{$k}) { |
176
|
0
|
|
|
|
|
0
|
push @errors, sprintf(g_('repeated key-value %s'), $k); |
177
|
|
|
|
|
|
|
} else { |
178
|
254
|
|
|
|
|
545
|
$f->{$k} = $v; |
179
|
|
|
|
|
|
|
} |
180
|
254
|
100
|
|
|
|
877
|
if ($k eq 'Urgency') { |
|
|
50
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
181
|
250
|
50
|
|
|
|
1335
|
push @errors, sprintf(g_('badly formatted urgency value: %s'), $v) |
182
|
|
|
|
|
|
|
unless ($v =~ m/^([-0-9a-z]+)((\s+.*)?)$/i); |
183
|
|
|
|
|
|
|
} elsif ($k eq 'Binary-Only') { |
184
|
0
|
0
|
|
|
|
0
|
push @errors, sprintf(g_('bad binary-only value: %s'), $v) |
185
|
|
|
|
|
|
|
unless ($v eq 'yes'); |
186
|
|
|
|
|
|
|
} elsif ($k =~ m/^X[BCS]+-/i) { |
187
|
|
|
|
|
|
|
} else { |
188
|
0
|
|
|
|
|
0
|
push @errors, sprintf(g_('unknown key-value %s'), $k); |
189
|
|
|
|
|
|
|
} |
190
|
|
|
|
|
|
|
} |
191
|
250
|
|
|
|
|
671
|
$self->{header_fields} = $f; |
192
|
|
|
|
|
|
|
} else { |
193
|
0
|
|
|
|
|
0
|
push @errors, g_("the header doesn't match the expected regex"); |
194
|
|
|
|
|
|
|
} |
195
|
250
|
|
|
|
|
790
|
return @errors; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub parse_trailer { |
199
|
248
|
|
|
248
|
1
|
398
|
my $self = shift; |
200
|
248
|
|
|
|
|
353
|
my @errors; |
201
|
248
|
50
|
33
|
|
|
2325
|
if (defined($self->{trailer}) and $self->{trailer} =~ $regex_trailer) { |
202
|
248
|
|
|
|
|
1297
|
$self->{trailer_maintainer} = "$1 <$2>"; |
203
|
|
|
|
|
|
|
|
204
|
248
|
50
|
|
|
|
646
|
if ($3 ne ' ') { |
205
|
0
|
|
|
|
|
0
|
push @errors, g_('badly formatted trailer line'); |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
# Validate the week day. Date::Parse used to ignore it, but Time::Piece |
209
|
|
|
|
|
|
|
# is much more strict and it does not gracefully handle bogus values. |
210
|
248
|
50
|
66
|
|
|
1051
|
if (defined $5 and not exists $week_day{$6}) { |
211
|
0
|
|
|
|
|
0
|
push @errors, sprintf(g_('ignoring invalid week day \'%s\''), $6); |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# Ignore the week day ('%a, '), as we have validated it above. |
215
|
248
|
|
|
|
|
1481
|
local $ENV{LC_ALL} = 'C'; |
216
|
|
|
|
|
|
|
eval { |
217
|
248
|
|
|
|
|
925
|
my $tp = Time::Piece->strptime($7, '%d %b %Y %T %z'); |
218
|
248
|
|
|
|
|
17715
|
$self->{trailer_timepiece} = $tp; |
219
|
248
|
50
|
|
|
|
472
|
} or do { |
220
|
|
|
|
|
|
|
# Validate the month. Date::Parse used to accept both abbreviated |
221
|
|
|
|
|
|
|
# and full months, but Time::Piece strptime() implementation only |
222
|
|
|
|
|
|
|
# matches the abbreviated one with %b, which is what we want anyway. |
223
|
0
|
0
|
|
|
|
0
|
if (not exists $month_abbrev{$8}) { |
224
|
|
|
|
|
|
|
# We have to nest the conditionals because May is the same in |
225
|
|
|
|
|
|
|
# full and abbreviated forms! |
226
|
0
|
0
|
|
|
|
0
|
if (exists $month_name{$8}) { |
227
|
|
|
|
|
|
|
push @errors, sprintf(g_('uses full \'%s\' instead of abbreviated month name \'%s\''), |
228
|
0
|
|
|
|
|
0
|
$8, $month_name{$8}); |
229
|
|
|
|
|
|
|
} else { |
230
|
0
|
|
|
|
|
0
|
push @errors, sprintf(g_('invalid abbreviated month name \'%s\''), $8); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
} |
233
|
0
|
|
|
|
|
0
|
push @errors, sprintf(g_("cannot parse non-conformant date '%s'"), $7); |
234
|
|
|
|
|
|
|
}; |
235
|
248
|
|
|
|
|
8199
|
$self->{trailer_timestamp_date} = $4; |
236
|
|
|
|
|
|
|
} else { |
237
|
0
|
|
|
|
|
0
|
push @errors, g_("the trailer doesn't match the expected regex"); |
238
|
|
|
|
|
|
|
} |
239
|
248
|
|
|
|
|
847
|
return @errors; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
=item $entry->normalize() |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
Normalize the content. Strip whitespaces at end of lines, use a single |
245
|
|
|
|
|
|
|
empty line to separate each part. |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
=cut |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
sub normalize { |
250
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
251
|
0
|
|
|
|
|
0
|
$self->SUPER::normalize(); |
252
|
|
|
|
|
|
|
#XXX: recreate header/trailer |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=item $src = $entry->get_source() |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
Return the name of the source package associated to the changelog entry. |
258
|
|
|
|
|
|
|
|
259
|
|
|
|
|
|
|
=cut |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
sub get_source { |
262
|
502
|
|
|
502
|
1
|
776
|
my $self = shift; |
263
|
|
|
|
|
|
|
|
264
|
502
|
|
|
|
|
1897
|
return $self->{header_source}; |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
|
267
|
|
|
|
|
|
|
=item $ver = $entry->get_version() |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
Return the version associated to the changelog entry. |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
=cut |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
sub get_version { |
274
|
1966
|
|
|
1966
|
1
|
23188
|
my $self = shift; |
275
|
|
|
|
|
|
|
|
276
|
1966
|
|
|
|
|
4524
|
return $self->{header_version}; |
277
|
|
|
|
|
|
|
} |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=item @dists = $entry->get_distributions() |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
Return a list of target distributions for this version. |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=cut |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub get_distributions { |
286
|
504
|
|
|
504
|
1
|
771
|
my $self = shift; |
287
|
|
|
|
|
|
|
|
288
|
504
|
50
|
|
|
|
1128
|
if (defined $self->{header_dists}) { |
289
|
504
|
100
|
|
|
|
984
|
return @{$self->{header_dists}} if wantarray; |
|
502
|
|
|
|
|
1905
|
|
290
|
2
|
|
|
|
|
13
|
return $self->{header_dists}[0]; |
291
|
|
|
|
|
|
|
} |
292
|
0
|
|
|
|
|
0
|
return; |
293
|
|
|
|
|
|
|
} |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
=item $fields = $entry->get_optional_fields() |
296
|
|
|
|
|
|
|
|
297
|
|
|
|
|
|
|
Return a set of optional fields exposed by the changelog entry. |
298
|
|
|
|
|
|
|
It always returns a Dpkg::Control object (possibly empty though). |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=cut |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
sub get_optional_fields { |
303
|
1478
|
|
|
1478
|
1
|
2207
|
my $self = shift; |
304
|
1478
|
|
|
|
|
2017
|
my $f; |
305
|
|
|
|
|
|
|
|
306
|
1478
|
50
|
|
|
|
3472
|
if (defined $self->{header_fields}) { |
307
|
1478
|
|
|
|
|
2418
|
$f = $self->{header_fields}; |
308
|
|
|
|
|
|
|
} else { |
309
|
0
|
|
|
|
|
0
|
$f = Dpkg::Control::Changelog->new(); |
310
|
|
|
|
|
|
|
} |
311
|
|
|
|
|
|
|
|
312
|
1478
|
|
|
|
|
2136
|
my @closes = find_closes(join("\n", @{$self->{changes}})); |
|
1478
|
|
|
|
|
7532
|
|
313
|
1478
|
100
|
|
|
|
3362
|
if (@closes) { |
314
|
1034
|
|
|
|
|
4067
|
$f->{Closes} = join(' ', @closes); |
315
|
|
|
|
|
|
|
} |
316
|
|
|
|
|
|
|
|
317
|
1478
|
|
|
|
|
4069
|
return $f; |
318
|
|
|
|
|
|
|
} |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=item $urgency = $entry->get_urgency() |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
Return the urgency of the associated upload. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
=cut |
325
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
sub get_urgency { |
327
|
738
|
|
|
738
|
1
|
1221
|
my $self = shift; |
328
|
738
|
|
|
|
|
1386
|
my $f = $self->get_optional_fields(); |
329
|
738
|
50
|
|
|
|
1729
|
if (exists $f->{Urgency}) { |
330
|
738
|
|
|
|
|
1423
|
$f->{Urgency} =~ s/\s.*$//; |
331
|
738
|
|
|
|
|
2036
|
return lc($f->{Urgency}); |
332
|
|
|
|
|
|
|
} |
333
|
0
|
|
|
|
|
0
|
return; |
334
|
|
|
|
|
|
|
} |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
=item $maint = $entry->get_maintainer() |
337
|
|
|
|
|
|
|
|
338
|
|
|
|
|
|
|
Return the string identifying the person who signed this changelog entry. |
339
|
|
|
|
|
|
|
|
340
|
|
|
|
|
|
|
=cut |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
sub get_maintainer { |
343
|
502
|
|
|
502
|
1
|
787
|
my $self = shift; |
344
|
|
|
|
|
|
|
|
345
|
502
|
|
|
|
|
1691
|
return $self->{trailer_maintainer}; |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
=item $time = $entry->get_timestamp() |
349
|
|
|
|
|
|
|
|
350
|
|
|
|
|
|
|
Return the timestamp of the changelog entry. |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
=cut |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
sub get_timestamp { |
355
|
508
|
|
|
508
|
1
|
1496
|
my $self = shift; |
356
|
|
|
|
|
|
|
|
357
|
508
|
|
|
|
|
1641
|
return $self->{trailer_timestamp_date}; |
358
|
|
|
|
|
|
|
} |
359
|
|
|
|
|
|
|
|
360
|
|
|
|
|
|
|
=item $time = $entry->get_timepiece() |
361
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
Return the timestamp of the changelog entry as a Time::Piece object. |
363
|
|
|
|
|
|
|
|
364
|
|
|
|
|
|
|
This function might return undef if there was no timestamp. |
365
|
|
|
|
|
|
|
|
366
|
|
|
|
|
|
|
=cut |
367
|
|
|
|
|
|
|
|
368
|
|
|
|
|
|
|
sub get_timepiece { |
369
|
1000
|
|
|
1000
|
1
|
14814
|
my $self = shift; |
370
|
|
|
|
|
|
|
|
371
|
1000
|
|
|
|
|
2906
|
return $self->{trailer_timepiece}; |
372
|
|
|
|
|
|
|
} |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
=back |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=head1 UTILITY FUNCTIONS |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
=over 4 |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=item $bool = match_header($line) |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
Checks if the line matches a valid changelog header line. |
383
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
=cut |
385
|
|
|
|
|
|
|
|
386
|
|
|
|
|
|
|
sub match_header { |
387
|
3780
|
|
|
3780
|
1
|
5826
|
my $line = shift; |
388
|
|
|
|
|
|
|
|
389
|
3780
|
|
|
|
|
38143
|
return $line =~ /$regex_header/; |
390
|
|
|
|
|
|
|
} |
391
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
=item $bool = match_trailer($line) |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
Checks if the line matches a valid changelog trailing line. |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
=cut |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
sub match_trailer { |
399
|
3524
|
|
|
3524
|
1
|
6386
|
my $line = shift; |
400
|
|
|
|
|
|
|
|
401
|
3524
|
|
|
|
|
19608
|
return $line =~ /$regex_trailer/; |
402
|
|
|
|
|
|
|
} |
403
|
|
|
|
|
|
|
|
404
|
|
|
|
|
|
|
=item @closed_bugs = find_closes($changes) |
405
|
|
|
|
|
|
|
|
406
|
|
|
|
|
|
|
Takes one string as argument and finds "Closes: #123456, #654321" statements |
407
|
|
|
|
|
|
|
as supported by the Debian Archive software in it. Returns all closed bug |
408
|
|
|
|
|
|
|
numbers in an array. |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
=cut |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
sub find_closes { |
413
|
1478
|
|
|
1478
|
1
|
2469
|
my $changes = shift; |
414
|
1478
|
|
|
|
|
2074
|
my %closes; |
415
|
|
|
|
|
|
|
|
416
|
1478
|
|
66
|
|
|
10231
|
while ($changes && ($changes =~ m{ |
417
|
|
|
|
|
|
|
closes:\s* |
418
|
|
|
|
|
|
|
(?:bug)?\#?\s?\d+ |
419
|
|
|
|
|
|
|
(?:,\s*(?:bug)?\#?\s?\d+)* |
420
|
|
|
|
|
|
|
}pigx)) { |
421
|
5822
|
|
|
|
|
55610
|
$closes{$_} = 1 foreach (${^MATCH} =~ /\#?\s?(\d+)/g); |
422
|
|
|
|
|
|
|
} |
423
|
|
|
|
|
|
|
|
424
|
1478
|
|
|
|
|
5855
|
my @closes = sort { $a <=> $b } keys %closes; |
|
19779
|
|
|
|
|
27489
|
|
425
|
1478
|
|
|
|
|
5552
|
return @closes; |
426
|
|
|
|
|
|
|
} |
427
|
|
|
|
|
|
|
|
428
|
|
|
|
|
|
|
=back |
429
|
|
|
|
|
|
|
|
430
|
|
|
|
|
|
|
=head1 CHANGES |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
=head2 Version 2.00 (dpkg 1.20.0) |
433
|
|
|
|
|
|
|
|
434
|
|
|
|
|
|
|
Remove methods: $entry->check_header(), $entry->check_trailer(). |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
Hide variables: $regex_header, $regex_trailer. |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
=head2 Version 1.03 (dpkg 1.18.8) |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
New methods: $entry->get_timepiece(). |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
=head2 Version 1.02 (dpkg 1.18.5) |
443
|
|
|
|
|
|
|
|
444
|
|
|
|
|
|
|
New methods: $entry->parse_header(), $entry->parse_trailer(). |
445
|
|
|
|
|
|
|
|
446
|
|
|
|
|
|
|
Deprecated methods: $entry->check_header(), $entry->check_trailer(). |
447
|
|
|
|
|
|
|
|
448
|
|
|
|
|
|
|
=head2 Version 1.01 (dpkg 1.17.2) |
449
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
New functions: match_header(), match_trailer() |
451
|
|
|
|
|
|
|
|
452
|
|
|
|
|
|
|
Deprecated variables: $regex_header, $regex_trailer |
453
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
=head2 Version 1.00 (dpkg 1.15.6) |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
Mark the module as public. |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=cut |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
1; |