]> git.deb.at Git - deb/packages.git/blob - lib/Packages/Page.pm
* Move coniguratio stuf to own module
[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     $data =~ s/\n\s+/\377/g;
72     while ($data =~ /^(\S+):\s*(.*)\s*$/mog) {
73         my ($key, $value) = ($1, $2);
74         $key =~ tr [A-Z] [a-z];
75         $data{$key} = $value;
76     }
77
78     $self->{src}{name} = $src;
79     $self->{src}{version} = $version;
80     if ($data{files}) {
81         $self->{src}{files} = [];
82         foreach my $sf ( split( /\377/, $data{files} ) ) {
83             next unless $sf;
84             # md5, size, name
85             push @{$self->{src}{files}}, [ split( /\s+/, $sf) ];
86         }
87     }
88     $self->{src}{directory} = $data{directory};
89     my @uploaders;
90     if ($data{maintainer} ||= '') {
91         push @uploaders, [ split_name_mail( $data{maintainer} ) ];
92     }
93     if ($data{uploaders}) {
94         my @up_tmp = split( /\s*,\s*/,
95                             $data{uploaders} );
96         foreach my $up (@up_tmp) {
97             if ($up ne $data{maintainer}) { # weed out duplicates
98                 push @uploaders, [ split_name_mail( $up ) ];
99             }
100         }
101     }
102     $self->{src}{uploaders} = \@uploaders;
103
104     return 1;
105 }
106
107 our @TAKE_NEWEST = qw( description essential priority section subsection tag
108                        archive source source-version );
109 our @STORE_ALL = qw( version source source-version installed-size size
110                      filename md5sum
111                      origin bugs suite archive section );
112 our @DEP_FIELDS = qw( depends pre-depends recommends suggests enhances
113                       provides conflicts );
114 sub merge_package {
115     my ($self, $data) = @_;
116
117     ($data->{package} && $data->{version} && $data->{architecture}) || return;
118     $self->{package} ||= $data->{package};
119     ($self->{package} eq $data->{package}) || return;
120     debug( "merge package $data->{package}/$data->{version}/$data->{architecture} into $self (".($self->{newest}||'').")", 2 );
121
122     unless ($self->{newest}) {
123         debug( "package $data->{package}/$data->{version}/$data->{architecture} is first to merge", 3 );
124         foreach my $key (@TAKE_NEWEST) {
125             $self->{data}{$key} = $data->{$key};
126         }
127         foreach my $key (@STORE_ALL) {
128             $self->{versions}{$data->{architecture}}{$key}
129             = $data->{$key};
130         }
131         foreach my $key (@DEP_FIELDS) {
132             $self->normalize_dependencies($key, $data);
133         }
134         $self->{newest} = $data->{version};
135         
136         return 1;
137     }
138
139     debug( "package $data->{package}/$data->{version}/$data->{architecture} is subsequent merge", 3 );
140     my $is_newest;
141     if ($is_newest =
142         (version_cmp( $data->{version}, $self->{newest} ) > 0)) {
143         $self->{newest} = $data->{version};
144         foreach my $key (@TAKE_NEWEST) {
145             $self->{data}{$key} = $data->{$key};
146         }
147     }
148     debug( "is_newest= ".($is_newest||0), 3 );
149     if (!$self->{versions}{$data->{architecture}}
150         || $is_newest
151         || (version_cmp( $data->{version},
152                          $self->{versions}{$data->{architecture}}{version} ) > 0)) {
153         foreach my $key (@STORE_ALL) {
154             $self->{versions}{$data->{architecture}}{$key}
155             = $data->{$key};
156         }
157         foreach my $key (@DEP_FIELDS) {
158             $self->normalize_dependencies($key, $data);
159         }
160     }
161     
162     return 1;
163 }
164
165 sub normalize_dependencies {
166     my ($self, $dep_field, $data) = @_;
167
168     my ($deps_norm, $deps) = parse_deps( $data->{$dep_field}||'' );
169     $self->{dep_fields}{$data->{architecture}}{$dep_field} =
170         [ $deps_norm, $deps ];
171 }
172
173 sub parse_deps {
174     my ($dep_str) = @_;
175
176     my (@dep_and_norm, @dep_and);
177     foreach my $dep_and (split( /\s*,\s*/m, $dep_str )) {
178         next if $dep_and =~ /^\s*$/;
179         my (@dep_or_norm, @dep_or);
180         foreach my $dep_or (split( /\s*\|\s*/m, $dep_and )) {
181             my ($pkg, $relation, $version, @arches) = ('','','');
182             $pkg = $1 if $dep_or =~ s/^([a-zA-Z0-9][a-zA-Z0-9+._-]*)\s*//m;
183             ($relation, $version) = ($1, $2)
184                 if $dep_or =~ s/^\(\s*(=|<=|>=|<<?|>>?)\s*([^\)]+).*\)\s*//m;
185             @arches = split(/\s+/m, $1) if $dep_or =~ s/^\[([^\]]+)\]\s*//m;
186             push @dep_or_norm, "$pkg($relation$version)[".
187                 join(" ",sort(@arches))."]";
188             push @dep_or, [ $pkg, $relation, $version, \@arches ];
189         }
190         push @dep_and_norm, join('|',@dep_or_norm);
191         push @dep_and, \@dep_or;
192     }
193     return (\@dep_and_norm, \@dep_and);
194 }
195
196 sub get_newest {
197     my ($self, $field) = @_;
198
199     return $self->{data}{$field};
200 }
201 sub get_src {
202     my ($self, $field) = @_;
203     
204     return $self->{src}{$field};
205 }
206
207 sub get_architectures {
208     my ($self) = @_;
209
210     return keys %{$self->{versions}};
211 }
212
213 sub get_arch_field {
214     my ($self, $field) = @_;
215
216     my %result;
217     foreach (sort keys %{$self->{versions}}) {
218         $result{$_} = $self->{versions}{$_}{$field}
219         if $self->{versions}{$_}{$field};
220     }
221
222     return \%result;
223 }
224
225 sub get_versions {
226     my ($self) = @_;
227
228     my %versions;
229     foreach (keys %{$self->{versions}}) {
230         my $version = $self->{versions}{$_}{version};
231         $versions{$version} ||= [];
232         push @{$versions{$version}}, $_;
233     }
234
235     return \%versions;
236 }
237
238 sub get_version_string {
239     my ($self) = @_;
240
241     my $versions = $self->get_versions;
242     my @versions = version_sort keys %$versions;
243     my (@v_str, $v_str, $v_str_arch);
244     if ( scalar @versions == 1 ) {
245         @v_str = ( [ $versions[0], undef ] );
246         $v_str = $versions[0];
247         $v_str_arch = $versions[0];
248     } else {
249         my @v_str_arch;
250         foreach ( @versions ) {
251             push @v_str, [ $_, $versions->{$_} ];
252             push @v_str_arch, "$_ [".join(', ', @{$versions->{$_}})."]";
253         }
254         $v_str_arch = join( ", ", @v_str_arch );
255         $v_str = join( ", ",  @versions );
256     }
257
258     return ($v_str, $v_str_arch, \@v_str);
259 }
260
261 sub get_dep_field {
262     my ($self, $dep_field) = @_;
263
264     my @architectures = $self->get_architectures;
265
266     my ( %dep_pkgs, %arch_deps );
267     foreach my $a ( @architectures ) {
268         next unless exists $self->{dep_fields}{$a}{$dep_field};
269         my ($a_deps_norm, $a_deps) = @{$self->{dep_fields}{$a}{$dep_field}};
270 #       debug( "get_dep_field: $dep_field/$a: ".Dumper($a_deps_norm,$a_deps), 3 );
271         for ( my $i=0; $i < @$a_deps; $i++ ) { # splitted by ,      
272             $dep_pkgs{$a_deps_norm->[$i]} = $a_deps->[$i];
273             $arch_deps{$a}{$a_deps_norm->[$i]}++;
274         }
275     }
276     @architectures = sort keys %arch_deps;
277  #   debug( "get_dep_field called:\n ".Dumper( \%dep_pkgs, \%arch_deps ), 3 );
278     
279     my @deps;
280     if ( %dep_pkgs ) {
281         my $old_pkgs = '';
282         my $is_old_pkgs = 0;
283         foreach my $dp ( sort keys %dep_pkgs ) {
284             my @dp_alts = @{$dep_pkgs{$dp}};
285             my ( @pkgs, $pkgs );
286             foreach (@dp_alts) { push @pkgs, $_->[0]; }
287             $pkgs = "@pkgs";
288
289             unless ( $is_old_pkgs = ($pkgs eq $old_pkgs) ) {
290                 $old_pkgs = $pkgs;
291             }
292             
293             my ($arch_neg, $arch_str) = _compute_arch_str ( $dp, \%arch_deps,
294                                                             \@architectures );
295
296             my @res_pkgs; my $pkg_ix = 0;
297             foreach my $p_name ( @pkgs ) {
298                 if ( $pkg_ix > 0 ) { $arch_str = ""; }
299                 
300                 my $pkg_version = "";
301                 $pkg_version = "$dep_pkgs{$dp}[$pkg_ix][1] $dep_pkgs{$dp}[$pkg_ix][2]"
302                     if $dep_pkgs{$dp}[$pkg_ix][1];
303
304
305                 push @res_pkgs, [ $p_name, $pkg_version, $arch_neg,
306                                   $arch_str ];
307                 $pkg_ix++;
308             }
309             push @deps, [ $is_old_pkgs, @res_pkgs ];
310         }
311     }
312     return \@deps;
313 }
314
315 sub _compute_arch_str {
316     my ( $dp, $arch_deps, $all_archs, $is_src_dep ) = @_;
317
318     my ( @dependend_archs, @not_dependend_archs );
319     my $arch_str;
320     foreach my $a ( @$all_archs ) {
321         if ( exists $arch_deps->{$a}{$dp} ) {
322             push @dependend_archs, $a;
323         } else {
324             push @not_dependend_archs, $a;
325         }
326     }
327     my $arch_neg = 0;
328     if ( @dependend_archs == @$all_archs ) {
329         $arch_str = "";
330     } else {
331         if ( @dependend_archs > (@$all_archs/2) ) {
332             $arch_neg = 1;
333             $arch_str = join( ", ", @not_dependend_archs);
334         } else {
335             $arch_str = join( ", ", @dependend_archs);
336         }
337     }
338     return my @ret = ( $arch_neg, $arch_str );
339 }
340
341 1;