test: Move test_data_file() to test.h
[dpkg.git] / scripts / Dpkg / Dist / Files.pm
blob1a9fa1f451881948de67964fc054155e165cc0b3
1 # Copyright © 2014-2015 Guillem Jover <guillem@debian.org>
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <https://www.gnu.org/licenses/>.
16 =encoding utf8
18 =head1 NAME
20 Dpkg::Dist::Files - handle built artifacts to distribute
22 =head1 DESCRIPTION
24 This module provides a class used to parse and write the F<debian/files>
25 file, as part of the list of built artifacts to include in an upload.
27 B<Note>: This is a private module, its API can change at any time.
29 =cut
31 package Dpkg::Dist::Files 0.01;
33 use strict;
34 use warnings;
36 use IO::Dir;
38 use Dpkg::Gettext;
39 use Dpkg::ErrorHandling;
41 use parent qw(Dpkg::Interface::Storable);
43 sub new {
44 my ($this, %opts) = @_;
45 my $class = ref($this) || $this;
47 my $self = {
48 options => [],
49 files => {},
51 foreach my $opt (keys %opts) {
52 $self->{$opt} = $opts{$opt};
54 bless $self, $class;
56 return $self;
59 sub reset {
60 my $self = shift;
62 $self->{files} = {};
65 sub parse_filename {
66 my ($self, $fn) = @_;
68 my $file;
70 if ($fn =~ m/^(([-+:.0-9a-z]+)_([^_]+)_([-\w]+)\.([a-z0-9.]+))$/) {
71 # Artifact using the common <name>_<version>_<arch>.<type> pattern.
72 $file->{filename} = $1;
73 $file->{package} = $2;
74 $file->{version} = $3;
75 $file->{arch} = $4;
76 $file->{package_type} = $5;
77 } elsif ($fn =~ m/^([-+:.,_0-9a-zA-Z~]+)$/) {
78 # Artifact with no common pattern, usually called byhand or raw, as
79 # they might require manual processing on the server side, or custom
80 # actions per file type.
81 $file->{filename} = $1;
82 } else {
83 $file = undef;
86 return $file;
89 sub parse {
90 my ($self, $fh, $desc) = @_;
91 my $count = 0;
93 local $_;
94 binmode $fh;
96 while (<$fh>) {
97 chomp;
99 my $file;
101 if (m/^(\S+) (\S+) (\S+)((?:\s+[0-9a-z-]+=\S+)*)$/) {
102 $file = $self->parse_filename($1);
103 error(g_('badly formed file name in files list file, line %d'), $.)
104 unless defined $file;
105 $file->{section} = $2;
106 $file->{priority} = $3;
107 my $attrs = $4;
108 $file->{attrs} = { map { split /=/ } split ' ', $attrs };
109 } else {
110 error(g_('badly formed line in files list file, line %d'), $.);
113 if (defined $self->{files}->{$file->{filename}}) {
114 warning(g_('duplicate files list entry for file %s (line %d)'),
115 $file->{filename}, $.);
116 } else {
117 $count++;
118 $self->{files}->{$file->{filename}} = $file;
122 return $count;
125 sub load_dir {
126 my ($self, $dir) = @_;
128 my $count = 0;
129 my $dh = IO::Dir->new($dir) or syserr(g_('cannot open directory %s'), $dir);
131 while (defined(my $file = $dh->read)) {
132 my $pathname = "$dir/$file";
133 next unless -f $pathname;
134 $count += $self->load($pathname);
137 return $count;
140 sub get_files {
141 my $self = shift;
143 return map { $self->{files}->{$_} } sort keys %{$self->{files}};
146 sub get_file {
147 my ($self, $filename) = @_;
149 return $self->{files}->{$filename};
152 sub add_file {
153 my ($self, $filename, $section, $priority, %attrs) = @_;
155 my $file = $self->parse_filename($filename);
156 error(g_('invalid filename %s'), $filename) unless defined $file;
157 $file->{section} = $section;
158 $file->{priority} = $priority;
159 $file->{attrs} = \%attrs;
161 $self->{files}->{$filename} = $file;
163 return $file;
166 sub del_file {
167 my ($self, $filename) = @_;
169 delete $self->{files}->{$filename};
172 sub filter {
173 my ($self, %opts) = @_;
174 my $remove = $opts{remove} // sub { 0 };
175 my $keep = $opts{keep} // sub { 1 };
177 foreach my $filename (keys %{$self->{files}}) {
178 my $file = $self->{files}->{$filename};
180 if (not $keep->($file) or $remove->($file)) {
181 delete $self->{files}->{$filename};
186 sub output {
187 my ($self, $fh) = @_;
188 my $str = '';
190 binmode $fh if defined $fh;
192 foreach my $filename (sort keys %{$self->{files}}) {
193 my $file = $self->{files}->{$filename};
194 my $entry = "$filename $file->{section} $file->{priority}";
196 if (exists $file->{attrs}) {
197 foreach my $attr (sort keys %{$file->{attrs}}) {
198 $entry .= " $attr=$file->{attrs}->{$attr}";
202 $entry .= "\n";
204 print { $fh } $entry if defined $fh;
205 $str .= $entry;
208 return $str;
211 =head1 CHANGES
213 =head2 Version 0.xx
215 This is a private module.
217 =cut