]> git.deb.at Git - deb/packages.git/blob - lib/Packages/Search.pm
* completly overhaul URL generating. Now things like debug or language
[deb/packages.git] / lib / Packages / Search.pm
1 #
2 # Packages::Search
3 #
4 # Copyright (C) 2004-2006 Frank Lichtenheld <frank@lichtenheld.de>
5
6 # The code is based on the old search_packages.pl script that
7 # was:
8 #
9 # Copyright (C) 1998 James Treacy
10 # Copyright (C) 2000, 2001 Josip Rodin
11 # Copyright (C) 2001 Adam Heath
12 # Copyright (C) 2004 Martin Schulze
13 #
14 #    This program is free software; you can redistribute it and/or modify
15 #    it under the terms of the GNU General Public License as published by
16 #    the Free Software Foundation; either version 1 of the License, or
17 #    (at your option) any later version.
18 #
19 #    This program is distributed in the hope that it will be useful,
20 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
21 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 #    GNU General Public License for more details.
23 #
24 #    You should have received a copy of the GNU General Public License
25 #    along with this program; if not, write to the Free Software
26 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
27 #
28
29 =head1 NAME
30
31 Packages::Search - 
32
33 =head1 SYNOPSIS
34
35 =head1 DESCRIPTION
36
37 =over 4
38
39 =cut
40
41 package Packages::Search;
42
43 use strict;
44 use warnings;
45
46 use POSIX;
47 use HTML::Entities;
48 use DB_File;
49
50 use Deb::Versions;
51 use Packages::CGI;
52 use Exporter;
53
54 our @ISA = qw( Exporter );
55
56 our @EXPORT_OK = qw( read_entry read_entry_all read_entry_simple
57                      read_src_entry read_src_entry_all find_binaries
58                      do_names_search do_fulltext_search
59                      );
60 our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
61
62 our $VERSION = 0.01;
63
64 our $too_many_hits = 0;
65
66 sub read_entry_all {
67     my ($hash, $key, $results, $non_results, $opts) = @_;
68     my ($virt, $result) = split /\000/o, $hash->{$key} || "-\01-", 2;
69
70     my %virt = split /\01/o, $virt;
71     while (my ($suite, $provides) = each %virt) {
72         next if $suite eq '-';
73         if ($opts->{h_suites}{$suite}) {
74             push @$results, [ $key, "-", $suite, 'virtual', 'v', 'v', 'v', 'v',
75                               $provides];
76         } else {
77             push @$non_results, [ $key, "-", $suite, 'virtual', 'v', 'v', 'v', 'v',
78                                   $provides];
79         }
80     }
81
82     foreach (split /\000/o, $result) {
83         my @data = split ( /\s/o, $_, 8 );
84         debug( "Considering entry ".join( ':', @data), 2) if DEBUG;
85         if ($opts->{h_suites}{$data[1]}
86             && ($opts->{h_archs}{$data[2]} || $data[2] eq 'all')
87             && $opts->{h_sections}{$data[3]}) {
88             debug( "Using entry ".join( ':', @data), 2) if DEBUG;
89             push @$results, [ $key, @data ];
90         } else {
91             push @$non_results, [ $key, @data ];
92         }
93     }
94 }
95 sub read_entry {
96     my ($hash, $key, $results, $opts) = @_;
97     my @non_results;
98     read_entry_all( $hash, $key, $results, \@non_results, $opts );
99 }
100
101 #FIXME: make configurable
102 my %fallback_suites = (
103                        'stable-backports' => 'stable',
104                        'stable-volatile' => 'stable',
105                        experimental => 'unstable' );
106
107 sub read_entry_simple {
108     my ($hash, $key, $archives, $suite) = @_;
109     # FIXME: drop $archives
110
111     my ($virt, $result) = split /\000/o, $hash->{$key} || "-\01-\0", 2;
112     my %virt = split /\01/o, $virt; 
113     debug( "read_entry_simple: key=$key, archives=".
114            join(" ",(keys %$archives)).", suite=$suite", 1) if DEBUG;
115     debug( "read_entry_simple: virt=".join(" ",(%virt)), 2) if DEBUG;
116     # FIXME: not all of the 2^4=16 combinations of empty(results),
117     # empty(virt{suite}), empty(fb_result), empty(virt{fb_suite}) are dealt
118     # with correctly, but it's adequate enough for now
119     return [ $virt{$suite} ] unless defined $result;
120     foreach (split /\000/o, $result) {
121         my @data = split ( /\s/o, $_, 8 );
122         debug( "use entry: @data", 2 ) if DEBUG && $data[1] eq $suite;
123         return [ $virt{$suite}, @data ] if $data[1] eq $suite;
124     }
125     if (my $fb_suite = $fallback_suites{$suite}) {
126         my $fb_result = read_entry_simple( $hash, $key, $archives, $fb_suite );
127         my $fb_virt = shift(@$fb_result);
128         $virt{$suite} .= $virt{$suite} ? " $fb_virt" : $fb_virt if $fb_virt;
129         return [ $virt{$suite}, @$fb_result ] if @$fb_result;
130     }
131     return [ $virt{$suite} ];
132 }
133
134 sub read_src_entry_all {
135     my ($hash, $key, $results, $non_results, $opts) = @_;
136     my $result = $hash->{$key} || '';
137     debug( "read_src_entry_all: key=$key", 1) if DEBUG;
138     foreach (split /\000/o, $result) {
139         my @data = split ( /\s/o, $_, 6 );
140         debug( "Considering entry ".join( ':', @data), 2) if DEBUG;
141         if ($opts->{h_archives}{$data[0]}
142             && $opts->{h_suites}{$data[1]}
143             && $opts->{h_sections}{$data[2]}) {
144             debug( "Using entry ".join( ':', @data), 2) if DEBUG;
145             push @$results, [ $key, @data ];
146         } else {
147             push @$non_results, [ $key, @data ];
148         }
149     }
150 }
151 sub read_src_entry {
152     my ($hash, $key, $results, $opts) = @_;
153     my @non_results;
154     read_src_entry_all( $hash, $key, $results, \@non_results, $opts );
155 }
156 sub do_names_search {
157     my ($keyword, $packages, $postfixes, $read_entry, $opts,
158         $results, $non_results) = @_;
159
160     $keyword = lc $keyword;
161         
162     my ($key, $prefixes) = ($keyword, '');
163     my %pkgs;
164     $postfixes->seq( $key, $prefixes, R_CURSOR );
165     while (index($key, $keyword) >= 0) {
166         if ($prefixes =~ /^\001(\d+)/o) {
167             debug( "$key has too many hits", 2 ) if DEBUG;
168             $too_many_hits += $1;
169         } else {
170             foreach (split /\000/o, $prefixes) {
171                 $_ = '' if $_ eq '^';
172                 debug( "add word $_$key", 2) if DEBUG;
173                 $pkgs{$_.$key}++;
174             }
175         }
176         last if $postfixes->seq( $key, $prefixes, R_NEXT ) != 0;
177         last if $too_many_hits or keys %pkgs >= 100;
178     }
179     
180     my $no_results = keys %pkgs;
181     if ($too_many_hits || ($no_results >= 100)) {
182         $too_many_hits += $no_results;
183         %pkgs = ( $keyword => 1 );
184     }
185     foreach my $pkg (sort keys %pkgs) {
186         &$read_entry( $packages, $pkg, $results, $non_results, $opts );
187     }
188 }
189 sub do_fulltext_search {
190     my ($keyword, $file, $did2pkg, $packages, $read_entry, $opts,
191         $results, $non_results) = @_;
192
193 # NOTE: this needs to correspond with parse-packages!
194     $keyword =~ tr [A-Z] [a-z];
195     if ($opts->{exact}) {
196         $keyword = " $keyword ";
197     }
198     $keyword =~ s/[(),.-]+//og;
199     $keyword =~ s#[^a-z0-9_/+]+# #og;
200
201     my $numres = 0;
202     my %tmp_results;
203     # fgrep is seriously faster than using perl
204     open DESC, '-|', 'fgrep', '-n', '--', $keyword, $file
205         or die "couldn't open $file: $!";
206     while (<DESC>) {
207         /^(\d+)/;
208         my $nr = $1;
209         debug( "Matched line $_", 2) if DEBUG;
210         my $result = $did2pkg->{$nr};
211         foreach (split /\000/o, $result) {
212             my @data = split /\s/, $_, 3;
213 #           debug ("Considering $data[0], arch = $data[2]", 3) if DEBUG;
214 #           next unless $data[2] eq 'all' || $opts->{h_archs}{$data[2]};
215 #           debug ("Ok", 3) if DEBUG;
216             $numres++ unless $tmp_results{$data[0]}++;
217         }
218         last if $numres > 100;
219     }
220     close DESC;
221     $too_many_hits++ if $numres > 100;
222
223     my @results;
224     foreach my $pkg (keys %tmp_results) {
225         &$read_entry( $packages, $pkg, $results, $non_results, $opts );
226     }
227  }
228
229 sub find_binaries {
230     my ($pkg, $archive, $suite, $src2bin) = @_;
231
232     my $bins = $src2bin->{$pkg} || '';
233     my %bins;
234     foreach (split /\000/o, $bins) {
235         my @data = split /\s/, $_, 5;
236
237         debug( "find_binaries: considering @data", 3 ) if DEBUG;
238         if (($data[0] eq $archive)
239             && ($data[1] eq $suite)) {
240             $bins{$data[2]}++;
241             debug( "find_binaries: using @data", 3 ) if DEBUG;
242         }
243     }
244
245     return [ keys %bins ];
246 }
247
248
249 1;