our
$VERSION
=
'1.00'
;
FIRST_HEADING
=> g_(
'first heading'
),
NEXT_OR_EOF
=> g_(
'next heading or end of file'
),
START_CHANGES
=> g_(
'start of change data'
),
CHANGES_OR_TRAILER
=> g_(
'more change data or trailer'
),
};
my
$ancient_delimiter_re
=
qr{
^
(?: # Ancient GNU style changelog entry with expanded date
(?:
\w+\s+ # Day of week (abbreviated)
\w+\s+ # Month name (abbreviated)
\d{1,2}
\Q \E
\d{1,2}:\d{1,2}:\d{1,2}\s+
[\w\s]*
\d{4}
)
\s+
(?:.*)
\s+
[<\(]
(?:.*)
[\)>]
|
(?:
\w+\s+
\w+\s+
\d{1,2},?\s*
\d{4}
)
\s+
(?:.*)
\s+
[<\(]
(?:.*)
[\)>]
|
(?:\w[-+0-9a-z.]*)
\Q \E
\(
(?:[^\(\) \t]+)
\)
\;?
|
(?:[\w.+-]+)
[- ]
(?:\S+)
\ Debian
\ (?:\S+)
|
Changes\ from\ version\ (?:.*)\ to\ (?:.*):
|
Changes\
for
\ [\w.+-]+-[\w.+-]+:?\s*$
|
Old\ Changelog:\s*$
|
(?:\d+:)?
\w[\w.+~-]*:?
\s*$
)
}xi;
sub
parse {
my
(
$self
,
$fh
,
$file
) =
@_
;
$file
=
$self
->{reportfile}
if
exists
$self
->{reportfile};
$self
->reset_parse_errors;
$self
->{data} = [];
$self
->set_unparsed_tail(
undef
);
my
$expect
= FIRST_HEADING;
my
$entry
= Dpkg::Changelog::Entry::Debian->new();
my
@blanklines
= ();
my
$unknowncounter
= 1;
local
$_
;
while
(<
$fh
>) {
chomp
;
if
(match_header(
$_
)) {
unless
(
$expect
eq FIRST_HEADING ||
$expect
eq NEXT_OR_EOF) {
$self
->parse_error(
$file
, $.,
sprintf
(g_(
'found start of entry where expected %s'
),
$expect
),
"$_"
);
}
unless
(
$entry
->is_empty) {
push
@{
$self
->{data}},
$entry
;
$entry
= Dpkg::Changelog::Entry::Debian->new();
last
if
$self
->abort_early();
}
$entry
->set_part(
'header'
,
$_
);
foreach
my
$error
(
$entry
->parse_header()) {
$self
->parse_error(
$file
, $.,
$error
,
$_
);
}
$expect
= START_CHANGES;
@blanklines
= ();
}
elsif
(m/^(?:;;\s*)?Local variables:/io) {
$self
->set_unparsed_tail(
"$_\n"
. (file_slurp(
$fh
) //
''
));
last
;
}
elsif
(m/^vim:/io) {
$self
->set_unparsed_tail(
"$_\n"
. (file_slurp(
$fh
) //
''
));
last
;
}
elsif
(m/^\$\w+:.*\$/o) {
next
;
}
elsif
(m/^\
next
;
}
elsif
(m{^/\*.*\*/}o) {
next
;
}
elsif
(m/
$ancient_delimiter_re
/) {
$self
->set_unparsed_tail(
"$_\n"
. file_slurp(
$fh
));
}
elsif
(m/^\S/) {
$self
->parse_error(
$file
, $., g_(
'badly formatted heading line'
),
"$_"
);
}
elsif
(match_trailer(
$_
)) {
unless
(
$expect
eq CHANGES_OR_TRAILER) {
$self
->parse_error(
$file
, $.,
sprintf
(g_(
'found trailer where expected %s'
),
$expect
),
"$_"
);
}
$entry
->set_part(
'trailer'
,
$_
);
$entry
->extend_part(
'blank_after_changes'
, [
@blanklines
]);
@blanklines
= ();
foreach
my
$error
(
$entry
->parse_trailer()) {
$self
->parse_error(
$file
, $.,
$error
,
$_
);
}
$expect
= NEXT_OR_EOF;
}
elsif
(m/^ \-\-/) {
$self
->parse_error(
$file
, $., g_(
'badly formatted trailer line'
),
"$_"
);
}
elsif
(m/^\s{2,}(?:\S)/) {
unless
(
$expect
eq START_CHANGES or
$expect
eq CHANGES_OR_TRAILER) {
$self
->parse_error(
$file
, $.,
sprintf
(g_(
'found change data'
.
' where expected %s'
),
$expect
),
"$_"
);
if
(
$expect
eq NEXT_OR_EOF and not
$entry
->is_empty) {
push
@{
$self
->{data}},
$entry
;
$entry
= Dpkg::Changelog::Entry::Debian->new();
$entry
->set_part(
'header'
,
'unknown (unknown'
. (
$unknowncounter
++) .
') unknown; urgency=unknown'
);
}
}
$entry
->extend_part(
'changes'
, [
@blanklines
,
$_
]);
@blanklines
= ();
$expect
= CHANGES_OR_TRAILER;
}
elsif
(!m/\S/) {
if
(
$expect
eq START_CHANGES) {
$entry
->extend_part(
'blank_after_header'
,
$_
);
next
;
}
elsif
(
$expect
eq NEXT_OR_EOF) {
$entry
->extend_part(
'blank_after_trailer'
,
$_
);
next
;
}
elsif
(
$expect
ne CHANGES_OR_TRAILER) {
$self
->parse_error(
$file
, $.,
sprintf
(g_(
'found blank line where expected %s'
),
$expect
));
}
push
@blanklines
,
$_
;
}
else
{
$self
->parse_error(
$file
, $., g_(
'unrecognized line'
),
"$_"
);
unless
(
$expect
eq START_CHANGES or
$expect
eq CHANGES_OR_TRAILER) {
$entry
->extend_part(
'changes'
, [
@blanklines
,
$_
]);
@blanklines
= ();
$expect
= CHANGES_OR_TRAILER;
}
}
}
unless
(
$expect
eq NEXT_OR_EOF) {
$self
->parse_error(
$file
, $.,
sprintf
(g_(
'found end of file where expected %s'
),
$expect
));
}
unless
(
$entry
->is_empty) {
push
@{
$self
->{data}},
$entry
;
}
return
scalar
@{
$self
->{data}};
}
1;