]> git.deb.at Git - deb/packages.git/blob - lib/Packages/Page.pm
bc7fdc03f4faf54d4815ae6755ed5201af91818c
[deb/packages.git] / lib / Packages / Page.pm
1 package Packages::Page;
2
3 use strict;
4 use warnings;
5
6 use Data::Dumper;
7 use Deb::Versions;
8 use Packages::CGI;
9 use IO::String;
10
11 our $ARCHIVE_DEFAULT = '';
12 our $SECTION_DEFAULT = 'main';
13 our $SUBSECTION_DEFAULT = 'unknown';
14 our $PRIORITY_DEFAULT = 'unknown';
15 our $ESSENTIAL_DEFAULT = 'no';
16 our $MAINTAINER_DEFAULT = 'unknown <unknown@email.invalid>';
17
18 sub new {
19     my $classname = shift;
20     my $name = shift || '';
21     my $config = shift || {};
22
23     my $self = {};
24     bless( $self, $classname );
25
26     $self->{package} = $name;
27     $self->{config} = $config;
28
29     return $self;
30 }
31
32 sub merge_data {
33     my ($self, $pkg, $version, $architecture, $data) = @_;
34
35     my %data = ( package => $pkg,
36                      version => $version,
37                      architecture => $architecture );
38     chomp($data);
39     while ($data =~ /^(\S+):\s*(.*)\s*$/mg) {
40         my ($key, $value) = ($1, $2);
41         $key =~ tr [A-Z] [a-z];
42         $data{$key} = $value;
43     }
44 #       debug( "Merge package:\n".Dumper(\%data), 3 );
45     return $self->merge_package( \%data );
46 }
47
48 sub gettext { return $_[0]; }
49 sub split_name_mail {
50     my $string = shift;
51     my ( $name, $email );
52     if ($string =~ /(.*?)\s*<(.*)>/o) {
53         $name =  $1;
54         $email = $2;
55     } elsif ($string =~ /^[\w.-]*@[\w.-]*$/o) {
56         $name =  $string;
57         $email = $string;
58     } else {
59         $name = gettext( 'package has bad maintainer field' );
60         $email = '';
61     }
62     $name =~ s/\s+$//o;
63     return ($name, $email);
64 }
65
66 sub add_src_data {
67     my ($self, $src, $version, $data) = @_;
68
69     chomp($data);
70     my %data = ();
71     while ($data =~ /^(\S+):\s*(.*)\s*$/mg) {
72         my ($key, $value) = ($1, $2);
73         $key =~ tr [A-Z] [a-z];
74         $data{$key} = $value;
75     }
76
77     $self->{src}{name} = $src;
78     $self->{src}{version} = $version;
79     if ($data{files}) {
80         $data{files} =~ s/\A\s*//o; # remove leading spaces
81         $self->{src}{files} = [];
82         foreach my $sf ( split( /\n\s*/, $data{files} ) ) {
83             # md5, size, name
84             push @{$self->{src}{files}}, [ split( /\s+/, $sf) ];
85         }
86     }
87     $self->{src}{directory} = $data{directory};
88     my @uploaders;
89     if ($data{maintainer} ||= '') {
90         push @uploaders, [ split_name_mail( $data{maintainer} ) ];
91     }
92     if ($data{uploaders}) {
93         my @up_tmp = split( /\s*,\s*/,
94                             $data{uploaders} );
95         foreach my $up (@up_tmp) {
96             if ($up ne $data{maintainer}) { # weed out duplicates
97                 push @uploaders, [ split_name_mail( $up ) ];
98             }
99         }
100     }
101     $self->{src}{uploaders} = \@uploaders;
102
103     return 1;
104 }
105
106 our @TAKE_NEWEST = qw( description essential priority section subsection tag
107                        archive source source-version );
108 our @STORE_ALL = qw( version source source-version installed-size size
109                      filename md5sum
110                      origin bugs suite archive section );
111 our @DEP_FIELDS = qw( depends pre-depends recommends suggests enhances
112                       provides conflicts );
113 sub merge_package {
114     my ($self, $data) = @_;
115
116     ($data->{package} && $data->{version} && $data->{architecture}) || return;
117     $self->{package} ||= $data->{package};
118     ($self->{package} eq $data->{package}) || return;
119     debug( "merge package $data->{package}/$data->{version}/$data->{architecture} into $self (".($self->{newest}||'').")", 2 );
120
121     unless ($self->{newest}) {
122         debug( "package $data->{package}/$data->{version}/$data->{architecture} is first to merge", 3 );
123         foreach my $key (@TAKE_NEWEST) {
124             $self->{data}{$key} = $data->{$key};
125         }
126         foreach my $key (@STORE_ALL) {
127             $self->{versions}{$data->{architecture}}{$key}
128             = $data->{$key};
129         }
130         foreach my $key (@DEP_FIELDS) {
131             $self->normalize_dependencies($key, $data);
132         }
133         $self->{newest} = $data->{version};
134         
135         return 1;
136     }
137
138     debug( "package $data->{package}/$data->{version}/$data->{architecture} is subsequent merge", 3 );
139     my $is_newest;
140     if ($is_newest =
141         (version_cmp( $data->{version}, $self->{newest} ) > 0)) {
142         $self->{newest} = $data->{version};
143         foreach my $key (@TAKE_NEWEST) {
144             $self->{data}{$key} = $data->{$key};
145         }
146     }
147     debug( "is_newest= ".($is_newest||0), 3 );
148     if (!$self->{versions}{$data->{architecture}}
149         || $is_newest
150         || (version_cmp( $data->{version},
151                          $self->{versions}{$data->{architecture}} ) > 0)) {
152         foreach my $key (@STORE_ALL) {
153             $self->{versions}{$data->{architecture}}{$key}
154             = $data->{$key};
155         }
156         foreach my $key (@DEP_FIELDS) {
157             $self->normalize_dependencies($key, $data);
158         }
159     }
160     
161     return 1;
162 }
163
164 sub normalize_dependencies {
165     my ($self, $dep_field, $data) = @_;
166
167     my ($deps_norm, $deps) = parse_deps( $data->{$dep_field}||'' );
168     $self->{dep_fields}{$data->{architecture}}{$dep_field} =
169         [ $deps_norm, $deps ];
170 }
171
172 sub parse_deps {
173     my ($dep_str) = @_;
174
175     my (@dep_and_norm, @dep_and);
176     foreach my $dep_and (split( /\s*,\s*/m, $dep_str )) {
177         next if $dep_and =~ /^\s*$/;
178         my (@dep_or_norm, @dep_or);
179         foreach my $dep_or (split( /\s*\|\s*/m, $dep_and )) {
180             my ($pkg, $relation, $version, @arches) = ('','','');
181             $pkg = $1 if $dep_or =~ s/^([a-zA-Z0-9][a-zA-Z0-9+._-]*)\s*//m;
182             ($relation, $version) = ($1, $2)
183                 if $dep_or =~ s/^\(\s*(=|<=|>=|<<?|>>?)\s*([^\)]+).*\)\s*//m;
184             @arches = split(/\s+/m, $1) if $dep_or =~ s/^\[([^\]]+)\]\s*//m;
185             push @dep_or_norm, "$pkg($relation$version)[".
186                 join(" ",sort(@arches))."]";
187             push @dep_or, [ $pkg, $relation, $version, \@arches ];
188         }
189         push @dep_and_norm, join('|',@dep_or_norm);
190         push @dep_and, \@dep_or;
191     }
192     return (\@dep_and_norm, \@dep_and);
193 }
194
195 sub get_newest {
196     my ($self, $field) = @_;
197
198     return $self->{data}{$field};
199 }
200 sub get_src {
201     my ($self, $field) = @_;
202     
203     return $self->{src}{$field};
204 }
205
206 sub get_architectures {
207     my ($self) = @_;
208
209     return keys %{$self->{versions}};
210 }
211
212 sub get_arch_field {
213     my ($self, $field) = @_;
214
215     my %result;
216     foreach (sort keys %{$self->{versions}}) {
217         $result{$_} = $self->{versions}{$_}{$field}
218         if $self->{versions}{$_}{$field};
219     }
220
221     return \%result;
222 }
223
224 sub get_versions {
225     my ($self) = @_;
226
227     my %versions;
228     foreach (keys %{$self->{versions}}) {
229         my $version = $self->{versions}{$_}{version};
230         $versions{$version} ||= [];
231         push @{$versions{$version}}, $_;
232     }
233
234     return \%versions;
235 }
236
237 sub get_version_string {
238     my ($self) = @_;
239
240     my $versions = $self->get_versions;
241     my @versions = version_sort keys %$versions;
242     my (@v_str, $v_str, $v_str_arch);
243     if ( scalar @versions == 1 ) {
244         @v_str = ( [ $versions[0], undef ] );
245         $v_str = $versions[0];
246         $v_str_arch = $versions[0];
247     } else {
248         my @v_str_arch;
249         foreach ( @versions ) {
250             push @v_str, [ $_, $versions->{$_} ];
251             push @v_str_arch, "$_ [".join(', ', @{$versions->{$_}})."]";
252         }
253         $v_str_arch = join( ", ", @v_str_arch );
254         $v_str = join( ", ",  @versions );
255     }
256
257     return ($v_str, $v_str_arch, \@v_str);
258 }
259
260 sub get_dep_field {
261     my ($self, $dep_field) = @_;
262
263     my @architectures = $self->get_architectures;
264
265     my ( %dep_pkgs, %arch_deps );
266     foreach my $a ( @architectures ) {
267         next unless exists $self->{dep_fields}{$a}{$dep_field};
268         my ($a_deps_norm, $a_deps) = @{$self->{dep_fields}{$a}{$dep_field}};
269 #       debug( "get_dep_field: $dep_field/$a: ".Dumper($a_deps_norm,$a_deps), 3 );
270         for ( my $i=0; $i < @$a_deps; $i++ ) { # splitted by ,      
271             $dep_pkgs{$a_deps_norm->[$i]} = $a_deps->[$i];
272             $arch_deps{$a}{$a_deps_norm->[$i]}++;
273         }
274     }
275     @architectures = sort keys %arch_deps;
276  #   debug( "get_dep_field called:\n ".Dumper( \%dep_pkgs, \%arch_deps ), 3 );
277     
278     my @deps;
279     if ( %dep_pkgs ) {
280         my $old_pkgs = '';
281         my $is_old_pkgs = 0;
282         foreach my $dp ( sort keys %dep_pkgs ) {
283             my @dp_alts = @{$dep_pkgs{$dp}};
284             my ( @pkgs, $pkgs );
285             foreach (@dp_alts) { push @pkgs, $_->[0]; }
286             $pkgs = "@pkgs";
287
288             unless ( $is_old_pkgs = ($pkgs eq $old_pkgs) ) {
289                 $old_pkgs = $pkgs;
290             }
291             
292             my ($arch_neg, $arch_str) = _compute_arch_str ( $dp, \%arch_deps,
293                                                             \@architectures );
294
295             my @res_pkgs; my $pkg_ix = 0;
296             foreach my $p_name ( @pkgs ) {
297                 if ( $pkg_ix > 0 ) { $arch_str = ""; }
298                 
299                 my $pkg_version = "";
300                 $pkg_version = "$dep_pkgs{$dp}[$pkg_ix][1] $dep_pkgs{$dp}[$pkg_ix][2]"
301                     if $dep_pkgs{$dp}[$pkg_ix][1];
302
303
304                 push @res_pkgs, [ $p_name, $pkg_version, $arch_neg,
305                                   $arch_str ];
306                 $pkg_ix++;
307             }
308             push @deps, [ $is_old_pkgs, @res_pkgs ];
309         }
310     }
311     return \@deps;
312 }
313
314 sub _compute_arch_str {
315     my ( $dp, $arch_deps, $all_archs, $is_src_dep ) = @_;
316
317     my ( @dependend_archs, @not_dependend_archs );
318     my $arch_str;
319     foreach my $a ( @$all_archs ) {
320         if ( exists $arch_deps->{$a}{$dp} ) {
321             push @dependend_archs, $a;
322         } else {
323             push @not_dependend_archs, $a;
324         }
325     }
326     my $arch_neg = 0;
327     if ( @dependend_archs == @$all_archs ) {
328         $arch_str = "";
329     } else {
330         if ( @dependend_archs > (@$all_archs/2) ) {
331             $arch_neg = 1;
332             $arch_str = join( ", ", @not_dependend_archs);
333         } else {
334             $arch_str = join( ", ", @dependend_archs);
335         }
336     }
337     return my @ret = ( $arch_neg, $arch_str );
338 }
339
340 1;