]> git.deb.at Git - deb/packages.git/blob - lib/Packages/Search.pm
These changes make the search_packages.pl script mod_perl ready
[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 CGI qw( -oldstyle_urls );
47 use POSIX;
48 use HTML::Entities;
49 use DB_File;
50
51 use Deb::Versions;
52 use Packages::CGI;
53 use Exporter;
54
55 our @ISA = qw( Exporter );
56
57 our @EXPORT_OK = qw( nextlink prevlink indexline
58                      resperpagelink
59                      read_entry read_src_entry find_binaries
60                      do_names_search do_fulltext_search
61                      printindexline multipageheader );
62 our %EXPORT_TAGS = ( all => [ @EXPORT_OK ] );
63
64 our $VERSION = 0.01;
65
66 our $USE_PAGED_MODE = 1;
67 use constant DEFAULT_PAGE => 1;
68 use constant DEFAULT_RES_PER_PAGE => 50;
69 our %page_params = ( page => { default => DEFAULT_PAGE,
70                                match => '(\d+)' },
71                      number => { default => DEFAULT_RES_PER_PAGE,
72                                  match => '(\d+)' } );
73
74 our $too_many_hits = 0;
75
76 sub parse_params {
77     my ( $cgi, $params_def, $opts ) = @_;
78
79     my %params_ret = ( values => {}, errors => {} );
80     my %params;
81     if ($USE_PAGED_MODE) {
82         debug( "Use PAGED_MODE", 2 );
83         %params = %$params_def;
84         foreach (keys %page_params) {
85             delete $params{$_};
86         }
87         %params = ( %params, %page_params );
88     } else {
89         %params = %$params_def;
90     }
91
92     foreach my $param ( keys %params ) {
93         
94         debug( "Param <strong>$param</strong>", 2 );
95
96         my $p_value_orig = $cgi->param($param);
97
98         if (!defined($p_value_orig)
99             && defined $params_def->{$param}{alias}
100             && defined $cgi->param($params_def->{$param}{alias})) {
101             $p_value_orig = $cgi->param($params_def->{$param}{alias});
102             debug( "Used alias <strong>$params_def->{$param}{alias}</strong>",
103                    2 );
104         }
105
106         my @p_value = ($p_value_orig);
107
108         debug( "Value (Orig) ".($p_value_orig||""), 2 );
109
110         if ($params_def->{$param}{array} && defined $p_value_orig) {
111             @p_value = split /$params_def->{$param}{array}/, $p_value_orig;
112             debug( "Value (Array Split) ". join('##',@p_value), 2 );
113         }
114
115         if ($params_def->{$param}{match} && defined $p_value_orig) {
116             @p_value = map
117             { $_ =~ m/$params_def->{$param}{match}/; $_ = $1 }
118             @p_value;
119         }
120         @p_value = grep { defined $_ } @p_value;
121
122         debug( "Value (Match) ". join('##',@p_value), 2 );
123
124         unless (@p_value) {
125             if (defined $params{$param}{default}) {
126                 @p_value = ($params{$param}{default});
127             } else {
128                 @p_value = undef;
129                 $params_ret{errors}{$param} = "undef";
130                 next;
131             }
132         }
133
134         debug( "Value (Default) ". join('##',@p_value), 2 );
135         my @p_value_no_replace = @p_value;
136
137         if ($params{$param}{replace} && @p_value) {
138             @p_value = ();
139             foreach my $pattern (keys %{$params{$param}{replace}}) {
140                 foreach (@p_value_no_replace) {
141                     if ($_ eq $pattern) {
142                         my $replacement = $params{$param}{replace}{$_};
143                         if (ref $replacement) {
144                             push @p_value, @$replacement;
145                         } else {
146                             push @p_value, $replacement;
147                         }
148                     } else {
149                         push @p_value, $_;
150                     }
151                 }
152             }
153         }
154         
155         debug( "Value (Final) ". join('##',@p_value), 2 );
156
157         if ($params_def->{$param}{array}) {
158             $params_ret{values}{$param} = {
159                 orig => $p_value_orig,
160                 no_replace => \@p_value_no_replace,
161                 final => \@p_value,
162             };
163             @{$params_def->{$param}{var}} = @p_value
164                 if $params_def->{$param}{var};
165         } else {
166             $params_ret{values}{$param} = {
167                 orig => $p_value_orig,
168                 no_replace => $p_value_no_replace[0],
169                 final => $p_value[0],
170             };
171             ${$params_def->{$param}{var}} = $p_value[0]
172                 if $params_def->{$param}{var};
173         }
174         $opts->{$param} = $params_ret{values}{$param}{final} if $opts;
175     }
176
177     if ($USE_PAGED_MODE) {
178         $cgi->delete( "page" );
179         $cgi->delete( "number" );
180     }
181
182     return %params_ret;
183 }
184
185 sub start { 
186     my $params = shift;
187
188     my $page = $params->{values}{page}{final}
189     || DEFAULT_PAGE;
190     my $res_per_page = $params->{values}{number}{final}
191     || DEFAULT_RES_PER_PAGE;
192
193     return 1 if $res_per_page =~ /^all$/i;
194     return $res_per_page * ($page - 1) + 1;
195 }
196
197 sub end {
198     my $params = shift;
199
200     use Data::Dumper;
201     debug( "end: ".Dumper($params) );
202     my $page = $params->{page}
203     || DEFAULT_PAGE;
204     my $res_per_page = $params->{number}
205     || DEFAULT_RES_PER_PAGE;
206
207     return $page * $res_per_page;
208 }
209
210 sub indexline {
211     my ($cgi, $params, $num_res) = @_;
212
213     my $index_line = "";
214     my $page = $params->{page}
215     || DEFAULT_PAGE;
216     my $res_per_page = $params->{number}
217     || DEFAULT_RES_PER_PAGE;
218     my $numpages = ceil($num_res /
219                         $res_per_page);
220     for (my $i = 1; $i <= $numpages; $i++) {
221         if ($i == $page) {
222             $index_line .= $i;
223         } else {
224             $index_line .= "<a href=\"".encode_entities($cgi->self_url).
225                 "&amp;page=$i&amp;number=$res_per_page\">".
226                 "$i</a>";
227         }
228         if ($i < $numpages) {
229            $index_line .= " | ";
230         }
231     }
232     return $index_line;
233 }
234
235 sub nextlink {
236     my ($cgi, $params, $no_results ) = @_;
237
238     my $page = $params->{page}
239     || DEFAULT_PAGE;
240     $page++;
241     my $res_per_page = $params->{number}
242     || DEFAULT_RES_PER_PAGE;
243
244     if ((($page-1)*$res_per_page + 1) > $no_results) {
245         return "&gt;&gt;";
246     }
247
248     return "<a href=\"".encode_entities($cgi->self_url).
249         "&amp;page=$page&amp;number=$res_per_page\">&gt;&gt;</a>";
250 }
251
252 sub prevlink {
253     my ($cgi, $params ) = @_;
254
255     my $page = $params->{page}
256     || DEFAULT_PAGE;
257     $page--;
258     if (!$page) {
259         return "&lt;&lt;";
260     }
261
262     my $res_per_page = $params->{number}
263     || DEFAULT_RES_PER_PAGE;
264
265     return "<a href=\"".encode_entities($cgi->self_url).
266         "&amp;page=$page&amp;number=$res_per_page\">&lt;&lt;</a>";
267 }
268
269 sub resperpagelink {
270     my ($cgi, $params, $res_per_page ) = @_;
271
272     my $page;
273     if ($res_per_page =~ /^all$/i) {
274         $page = 1;
275     } else {
276         $page = ceil(start( $params ) / $res_per_page);
277     }
278
279     return "<a href=\"".encode_entities($cgi->self_url).
280         "&amp;page=$page&amp;number=$res_per_page\">$res_per_page</a>";
281 }
282
283 sub printindexline {
284     my ( $input, $no_results, $opts ) = @_;
285
286     my $index_line;
287     if ($no_results > $opts->{number}) {
288         
289         $index_line = prevlink( $input, $opts)." | ".
290             indexline( $input, $opts, $no_results)." | ".
291             nextlink( $input, $opts, $no_results);
292         
293         print "<p style=\"text-align:center\">$index_line</p>";
294     }
295 }
296
297 sub multipageheader {
298     my ( $input, $no_results, $opts ) = @_;
299
300     my ($start, $end);
301     if ($opts->{number} =~ /^all$/i) {
302         $start = 1;
303         $end = $no_results;
304         $opts->{number} = $no_results;
305         $opts->{number_all}++;
306     } else {
307         $start = Packages::Search::start( $opts );
308         $end = Packages::Search::end( $opts );
309         if ($end > $no_results) { $end = $no_results; }
310     }
311
312     print "<p>Found <em>$no_results</em> matching packages,";
313     if ($end == $start) {
314         print " displaying package $end.</p>";
315     } else {
316         print " displaying packages $start to $end.</p>";
317     }
318
319     printindexline( $input, $no_results, $opts );
320
321     if ($no_results > 100) {
322         print "<p>Results per page: ";
323         my @resperpagelinks;
324         for (50, 100, 200) {
325             if ($opts->{number} == $_) {
326                 push @resperpagelinks, $_;
327             } else {
328                 push @resperpagelinks, resperpagelink($input,$opts,$_);
329             }
330         }
331         if ($opts->{number_all}) {
332             push @resperpagelinks, "all";
333         } else {
334             push @resperpagelinks, resperpagelink($input, $opts, "all");
335         }
336         print join( " | ", @resperpagelinks )."</p>";
337     }
338     return ( $start, $end );
339 }
340
341 sub read_entry {
342     my ($hash, $key, $results, $opts) = @_;
343     my $result = $hash->{$key} || '';
344     foreach (split /\000/, $result) {
345         my @data = split ( /\s/, $_, 7 );
346         debug( "Considering entry ".join( ':', @data), 2);
347         if ($opts->{h_suites}{$data[0]}
348             && ($opts->{h_archs}{$data[1]} || $data[1] eq 'all')
349             && $opts->{h_sections}{$data[2]}) {
350             debug( "Using entry ".join( ':', @data), 2);
351             push @$results, [ $key, @data ];
352         }
353     }
354 }
355 sub read_src_entry {
356     my ($hash, $key, $results, $opts) = @_;
357     my $result = $hash->{$key} || '';
358     foreach (split /\000/, $result) {
359         my @data = split ( /\s/, $_, 5 );
360         debug( "Considering entry ".join( ':', @data), 2);
361         if ($opts->{h_suites}{$data[0]} && $opts->{h_sections}{$data[1]}) {
362             debug( "Using entry ".join( ':', @data), 2);
363             push @$results, [ $key, @data ];
364         }
365     }
366 }
367 sub do_names_search {
368     my ($keyword, $packages, $postfixes, $read_entry, $opts) = @_;
369     my @results;
370
371     $keyword = lc $keyword unless $opts->{case_bool};
372         
373     if ($opts->{exact}) {
374         &$read_entry( $packages, $keyword, \@results, $opts );
375     } else {
376         my ($key, $prefixes) = ($keyword, '');
377         my %pkgs;
378         $postfixes->seq( $key, $prefixes, R_CURSOR );
379         while (index($key, $keyword) >= 0) {
380             if ($prefixes =~ /^\001(\d+)/o) {
381                 $too_many_hits += $1;
382             } else {
383                 foreach (split /\000/o, $prefixes) {
384                     $_ = '' if $_ eq '^';
385                     debug( "add word $_$key", 2);
386                     $pkgs{$_.$key}++;
387                 }
388             }
389             last if $postfixes->seq( $key, $prefixes, R_NEXT ) != 0;
390             last if $too_many_hits or keys %pkgs >= 100;
391         }
392         
393         my $no_results = keys %pkgs;
394         if ($too_many_hits || ($no_results >= 100)) {
395             $too_many_hits += $no_results;
396             %pkgs = ( $keyword => 1 );
397         }
398         foreach my $pkg (sort keys %pkgs) {
399             &$read_entry( $packages, $pkg, \@results, $opts );
400         }
401     }
402     return \@results;
403 }
404 sub do_fulltext_search {
405     my ($keyword, $file, $did2pkg, $packages, $read_entry, $opts) = @_;
406     my @results;
407
408     my @lines;
409     my $regex;
410     if ($opts->{case_bool}) {
411         if ($opts->{exact}) {
412             $regex = qr/\b\Q$keyword\E\b/o;
413         } else {
414             $regex = qr/\Q$keyword\E/o;
415         }
416     } else {
417         if ($opts->{exact}) {
418             $regex = qr/\b\Q$keyword\E\b/io;
419         } else {
420             $regex = qr/\Q$keyword\E/io;
421         }
422     }
423
424     open DESC, '<', "$file"
425         or die "couldn't open $file: $!";
426     while (<DESC>) {
427         $_ =~ $regex or next;
428         debug( "Matched line $.", 2);
429         push @lines, $.;
430     }
431     close DESC;
432
433     my %tmp_results;
434     foreach my $l (@lines) {
435         my $result = $did2pkg->{$l};
436         foreach (split /\000/o, $result) {
437             my @data = split /\s/, $_, 3;
438             next unless $opts->{h_archs}{$data[2]};
439             $tmp_results{$data[0]}++;
440         }
441     }
442     foreach my $pkg (keys %tmp_results) {
443         &$read_entry( $packages, $pkg, \@results, $opts );
444     }
445     return \@results;
446 }
447
448 sub find_binaries {
449     my ($pkg, $suite, $src2bin) = @_;
450
451     my $bins = $src2bin->{$pkg} || '';
452     my %bins;
453     foreach (split /\000/o, $bins) {
454         my @data = split /\s/, $_, 4;
455
456         if ($data[0] eq $suite) {
457             $bins{$data[1]}++;
458         }
459     }
460
461     return [ keys %bins ];
462 }
463
464
465 1;