diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:42:30 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-14 13:42:30 +0000 |
commit | 75808db17caf8b960b351e3408e74142f4c85aac (patch) | |
tree | 7989e9c09a4240248bf4658a22208a0a52d991c4 /lib/Lintian/Deb822/Section.pm | |
parent | Initial commit. (diff) | |
download | lintian-upstream.tar.xz lintian-upstream.zip |
Adding upstream version 2.117.0.upstream/2.117.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/Lintian/Deb822/Section.pm')
-rw-r--r-- | lib/Lintian/Deb822/Section.pm | 373 |
1 files changed, 373 insertions, 0 deletions
diff --git a/lib/Lintian/Deb822/Section.pm b/lib/Lintian/Deb822/Section.pm new file mode 100644 index 0000000..5950ee4 --- /dev/null +++ b/lib/Lintian/Deb822/Section.pm @@ -0,0 +1,373 @@ +# Copyright (C) 2020 Felix Lechner +# +# 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, you can find it on the World Wide +# Web at https://www.gnu.org/copyleft/gpl.html, or write to the Free +# Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, +# MA 02110-1301, USA. + +package Lintian::Deb822::Section; + +use v5.20; +use warnings; +use utf8; + +use Const::Fast; +use List::Compare; + +const my $EMPTY => q{}; + +const my $UNKNOWN_POSITION => -1; + +use Moo; +use namespace::clean; + +=encoding utf-8 + +=head1 NAME + +Lintian::Deb822::Section -- A paragraph in a control file + +=head1 SYNOPSIS + + use Lintian::Deb822::Section; + +=head1 DESCRIPTION + +Represents a paragraph in a Deb822 control file. + +=head1 INSTANCE METHODS + +=over 4 + +=item legend + +Returns exact field names for their lowercase versions. + +=item verbatim + +Returns a hash to the raw, unedited and verbatim field values. + +=item unfolded + +Returns a hash to unfolded field values. Continuations lines +have been connected. + +=item positions + +The original line positions. + +=cut + +has legend => ( + is => 'rw', + lazy => 1, + default => sub { + my ($self) = @_; + + my %legend; + + for my $key (keys %{$self->verbatim}) { + + my $lowercase = lc $key; + $legend{$lowercase} = $key; + } + + return \%legend; + } +); + +has verbatim => (is => 'rw', default => sub { {} }); +has unfolded => (is => 'rw', default => sub { {} }); +has positions => (is => 'rw', default => sub { {} }); + +=item trimmed_list(FIELD [, SEPARATOR]) + +=cut + +sub trimmed_list { + my ($self, $name, $regex) = @_; + + $regex //= qr/\s+/; + + my $value = $self->value($name); + + # trim both ends + $value =~ s/^\s+|\s+$//g; + + my @list = split($regex, $value); + + # trim both ends of each element + s/^\s+|\s+$//g for @list; + + return grep { length } @list; +} + +=item unfolded_value (FIELD) + +This method returns the unfolded value of the control field FIELD in +the control file for the package. For a source package, this is the +*.dsc file; for a binary package, this is the control file in the +control section of the package. + +If FIELD is passed but not present, then this method returns undef. + +=cut + +sub unfolded_value { + my ($self, $name) = @_; + + return $EMPTY + unless length $name; + + my $lowercase = lc $name; + + my $unfolded = $self->unfolded->{$lowercase}; + return $unfolded + if defined $unfolded; + + my $value = $self->value($name); + + # will also replace a newline at the very end + $value =~ s/\n//g; + + # Remove leading space as it confuses some of the other checks + # that are anchored. This happens if the field starts with a + # space and a newline, i.e ($ marks line end): + # + # Vcs-Browser: $ + # http://somewhere.com/$ + $value =~ s/^\s*+//; + + $self->unfolded->{$lowercase} = $value; + + return $value; +} + +=item value (FIELD) + +If FIELD is given, this method returns the value of the control field +FIELD. + +=cut + +sub value { + my ($self, $name) = @_; + + return $EMPTY + unless length $name; + + my $exact = $self->legend->{lc $name}; + return $EMPTY + unless length $exact; + + my $trimmed = $self->verbatim->{$exact} // $EMPTY; + + # trim both ends + $trimmed =~ s/^\s+|\s+$//g; + + return $trimmed; +} + +=item untrimmed_value (FIELD) + +If FIELD is given, this method returns the value of the control field +FIELD. + +=cut + +sub untrimmed_value { + my ($self, $name) = @_; + + return $EMPTY + unless length $name; + + my $exact = $self->legend->{lc $name}; + return $EMPTY + unless length $exact; + + return $self->verbatim->{$exact} // $EMPTY; +} + +=item text (FIELD) + +=cut + +sub text { + my ($self, $name) = @_; + + my $text = $self->untrimmed_value($name); + + # remove leading space in each line + $text =~ s/^[ \t]//mg; + + # remove dot place holder for empty lines + $text =~ s/^\.$//mg; + + return $text; +} + +=item store (FIELD, VALUE) + +=cut + +sub store { + my ($self, $name, $value) = @_; + + $value //= $EMPTY; + + return + unless length $name; + + my $exact = $self->legend->{lc $name}; + + # add new value if key not found + unless (defined $exact) { + + $exact = $name; + + # update legend with exact spelling + $self->legend->{lc $exact} = $exact; + + # remove any old position + $self->positions->{$exact} = $UNKNOWN_POSITION; + } + + $self->verbatim->{$exact} = $value; + + # remove old unfolded value, if any + delete $self->unfolded->{$exact}; + + return; +} + +=item drop (FIELD) + +=cut + +sub drop { + my ($self, $name) = @_; + + return + unless length $name; + + my $exact = $self->legend->{lc $name}; + return + unless length $exact; + + delete $self->legend->{lc $exact}; + + delete $self->verbatim->{$exact}; + delete $self->unfolded->{$exact}; + delete $self->positions->{$exact}; + + return; +} + +=item declares (NAME) + +Returns a boolean for whether the named field exists. + +=cut + +sub declares { + my ($self, $name) = @_; + + return 1 + if defined $self->legend->{lc $name}; + + return 0; +} + +=item names + +Returns an array with the literal field names. + +=cut + +sub names { + my ($self) = @_; + + return keys %{$self->verbatim}; +} + +=item literal_name + +Returns an array with the literal, true case field names. + +=cut + +sub literal_name { + my ($self, $anycase) = @_; + + return $self->legend->{ lc $anycase }; +} + +=item position + +With an argument, returns the starting line of the named field. + +Without an argument, return the starting line of the paragraph. + +=cut + +sub position { + my ($self, $field) = @_; + + return $self->positions->{'START-OF-PARAGRAPH'} + unless length $field; + + my $exact = $self->legend->{lc $field}; + return undef + unless length $exact; + + return $self->positions->{$exact}; +} + +=item extra + +=cut + +sub extra { + my ($self, @reference) = @_; + + my @lowercase = map { lc } @reference; + + my $extra_lc + = List::Compare->new([keys %{$self->legend}], \@lowercase); + my @extra_lowercase = $extra_lc->get_Lonly; + + my @extra = map { $self->literal_name($_) } @extra_lowercase; + + return @extra; +} + +=back + +=head1 AUTHOR + +Originally written by Felix Lechner <felix.lechner@lease-up.com> for Lintian. + +=head1 SEE ALSO + +lintian(1) + +=cut + +1; + +# Local Variables: +# indent-tabs-mode: nil +# cperl-indent-level: 4 +# End: +# vim: syntax=perl sw=4 sts=4 sr et |