File Coverage

File:Dpkg/Dist/Files.pm
Coverage:85.7%

linestmtbrancondsubpodtimecode
1# Copyright © 2014-2015 Guillem Jover <guillem@debian.org>
2#
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.
7#
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.
12#
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/>.
15
16=encoding utf8
17
18 - 29
=head1 NAME

Dpkg::Dist::Files - handle built artifacts to distribute

=head1 DESCRIPTION

This module provides a class used to parse and write the F<debian/files>
file, as part of the list of built artifacts to include in an upload.

B<Note>: This is a private module, its API can change at any time.

=cut
30
31package Dpkg::Dist::Files 0.01;
32
33
3
3
3
10
3
52
use strict;
34
3
3
3
7
3
87
use warnings;
35
36
3
3
3
4375
16436
107
use IO::Dir;
37
38
3
3
3
943
17
90
use Dpkg::Gettext;
39
3
3
3
754
3
106
use Dpkg::ErrorHandling;
40
41
3
3
3
6
2
6
use parent qw(Dpkg::Interface::Storable);
42
43sub new {
44
3
0
2
    my ($this, %opts) = @_;
45
3
13
    my $class = ref($this) || $this;
46
47
3
4
    my $self = {
48        options => [],
49        files => {},
50    };
51
3
4
    foreach my $opt (keys %opts) {
52
0
0
        $self->{$opt} = $opts{$opt};
53    }
54
3
2
    bless $self, $class;
55
56
3
4
    return $self;
57}
58
59sub reset {
60
15
0
12
    my $self = shift;
61
62
15
67
    $self->{files} = {};
63}
64
65sub parse_filename {
66
99
0
96
    my ($self, $fn) = @_;
67
68
99
55
    my $file;
69
70
99
175
    if ($fn =~ m/^(([-+:.0-9a-z]+)_([^_]+)_([-\w]+)\.([a-z0-9.]+))$/) {
71        # Artifact using the common <name>_<version>_<arch>.<type> pattern.
72
54
42
        $file->{filename} = $1;
73
54
41
        $file->{package} = $2;
74
54
39
        $file->{version} = $3;
75
54
35
        $file->{arch} = $4;
76
54
46
        $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
42
37
        $file->{filename} = $1;
82    } else {
83
3
2
        $file = undef;
84    }
85
86
99
54
    return $file;
87}
88
89sub parse {
90
21
1
21
    my ($self, $fh, $desc) = @_;
91
21
8
    my $count = 0;
92
93
21
15
    local $_;
94
21
27
    binmode $fh;
95
96
21
92
    while (<$fh>) {
97
81
58
        chomp;
98
99
81
31
        my $file;
100
101
81
163
        if (m/^(\S+) (\S+) (\S+)((?:\s+[0-9a-z-]+=\S+)*)$/) {
102
81
49
            $file = $self->parse_filename($1);
103
81
64
            error(g_('badly formed file name in files list file, line %d'), $.)
104                unless defined $file;
105
81
58
            $file->{section} = $2;
106
81
56
            $file->{priority} = $3;
107
81
45
            my $attrs = $4;
108
81
30
73
49
            $file->{attrs} = { map { split /=/ } split ' ', $attrs };
109        } else {
110
0
0
            error(g_('badly formed line in files list file, line %d'), $.);
111        }
112
113
81
119
        if (defined $self->{files}->{$file->{filename}}) {
114            warning(g_('duplicate files list entry for file %s (line %d)'),
115
0
0
                    $file->{filename}, $.);
116        } else {
117
81
40
            $count++;
118
81
87
            $self->{files}->{$file->{filename}} = $file;
119        }
120    }
121
122
21
28
    return $count;
123}
124
125sub load_dir {
126
3
0
4
    my ($self, $dir) = @_;
127
128
3
1
    my $count = 0;
129
3
13
    my $dh = IO::Dir->new($dir) or syserr(g_('cannot open directory %s'), $dir);
130
131
3
120
    while (defined(my $file = $dh->read)) {
132
15
82
        my $pathname = "$dir/$file";
133
15
40
        next unless -f $pathname;
134
9
11
        $count += $self->load($pathname);
135    }
136
137
3
16
    return $count;
138}
139
140sub get_files {
141
3
0
3
    my $self = shift;
142
143
3
15
3
2
13
6
    return map { $self->{files}->{$_} } sort keys %{$self->{files}};
144}
145
146sub get_file {
147
36
0
38
    my ($self, $filename) = @_;
148
149
36
51
    return $self->{files}->{$filename};
150}
151
152sub add_file {
153
15
0
19
    my ($self, $filename, $section, $priority, %attrs) = @_;
154
155
15
12
    my $file = $self->parse_filename($filename);
156
15
17
    error(g_('invalid filename %s'), $filename) unless defined $file;
157
15
13
    $file->{section} = $section;
158
15
9
    $file->{priority} = $priority;
159
15
13
    $file->{attrs} = \%attrs;
160
161
15
18
    $self->{files}->{$filename} = $file;
162
163
15
13
    return $file;
164}
165
166sub del_file {
167
3
0
4
    my ($self, $filename) = @_;
168
169
3
4
    delete $self->{files}->{$filename};
170}
171
172sub filter {
173
9
0
13
    my ($self, %opts) = @_;
174
9
6
14
9
    my $remove = $opts{remove} // sub { 0 };
175
9
15
20
18
    my $keep = $opts{keep} // sub { 1 };
176
177
9
9
6
12
    foreach my $filename (keys %{$self->{files}}) {
178
45
24
        my $file = $self->{files}->{$filename};
179
180
45
32
        if (not $keep->($file) or $remove->($file)) {
181
27
40
            delete $self->{files}->{$filename};
182        }
183    }
184}
185
186sub output {
187
21
1
18
    my ($self, $fh) = @_;
188
21
11
    my $str = '';
189
190
21
19
    binmode $fh if defined $fh;
191
192
21
21
12
43
    foreach my $filename (sort keys %{$self->{files}}) {
193
78
37
        my $file = $self->{files}->{$filename};
194
78
51
        my $entry = "$filename $file->{section} $file->{priority}";
195
196
78
59
        if (exists $file->{attrs}) {
197
78
78
32
65
            foreach my $attr (sort keys %{$file->{attrs}}) {
198
27
22
                $entry .= " $attr=$file->{attrs}->{$attr}";
199            }
200        }
201
202
78
40
        $entry .= "\n";
203
204
78
0
51
0
        print { $fh } $entry if defined $fh;
205
78
55
        $str .= $entry;
206    }
207
208
21
39
    return $str;
209}
210
211 - 217
=head1 CHANGES

=head2 Version 0.xx

This is a private module.

=cut
218
2191;