| File: | Dpkg/Dist/Files.pm | 
| Coverage: | 85.7% | 
| line | stmt | bran | cond | sub | pod | time | code | 
|---|---|---|---|---|---|---|---|
| 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 | |||||||
| 31 | package 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 | |||||||
| 43 | sub 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 | |||||||
| 59 | sub reset { | ||||||
| 60 | 15 | 0 | 12 | my $self = shift; | |||
| 61 | |||||||
| 62 | 15 | 67 | $self->{files} = {}; | ||||
| 63 | } | ||||||
| 64 | |||||||
| 65 | sub 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 | |||||||
| 89 | sub 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 | |||||||
| 125 | sub 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 | |||||||
| 140 | sub 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 | |||||||
| 146 | sub get_file { | ||||||
| 147 | 36 | 0 | 38 | my ($self, $filename) = @_; | |||
| 148 | |||||||
| 149 | 36 | 51 | return $self->{files}->{$filename}; | ||||
| 150 | } | ||||||
| 151 | |||||||
| 152 | sub 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 | |||||||
| 166 | sub del_file { | ||||||
| 167 | 3 | 0 | 4 | my ($self, $filename) = @_; | |||
| 168 | |||||||
| 169 | 3 | 4 | delete $self->{files}->{$filename}; | ||||
| 170 | } | ||||||
| 171 | |||||||
| 172 | sub 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 | |||||||
| 186 | sub 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 | |||||||
| 219 | 1; | ||||||