X-Git-Url: https://git.deb.at/?p=deb%2Fpackages.git;a=blobdiff_plain;f=lib%2FPackages%2FDoSearchContents.pm;h=debbbb2a93e28539aef61f66556ae17871080c85;hp=b1057f83bd51a084baed256bb4eac4b2630e96e9;hb=e2881c2279414d845cb0b21b5e2661924194cb77;hpb=a430a7f2c477de0e9952384e896e7c78cea3f32f diff --git a/lib/Packages/DoSearchContents.pm b/lib/Packages/DoSearchContents.pm index b1057f8..debbbb2 100644 --- a/lib/Packages/DoSearchContents.pm +++ b/lib/Packages/DoSearchContents.pm @@ -14,17 +14,17 @@ our @EXPORT = qw( do_search_contents ); use Deb::Versions; use Packages::I18N::Locale; use Packages::Search qw( :all ); -use Packages::CGI; +use Packages::CGI qw( :DEFAULT error ); use Packages::DB; -use Packages::Config qw( $DBDIR $SEARCH_URL $SEARCH_PAGE - @SUITES @ARCHIVES @ARCHITECTURES $ROOT ); +use Packages::Config qw( $DBDIR @SUITES @ARCHIVES @ARCHITECTURES $ROOT ); sub do_search_contents { - my ($params, $opts, $html_header, $menu, $page_content) = @_; + my ($params, $opts, $page_content) = @_; if ($params->{errors}{keywords}) { fatal_error( _g( "keyword not valid or missing" ) ); - } elsif (length($opts->{keywords}) < 2) { + $opts->{keywords} = []; + } elsif (grep { length($_) < 2 } @{$opts->{keywords}}) { fatal_error( _g( "keyword too short (keywords need to have at least two characters)" ) ); } if ($params->{errors}{suite}) { @@ -34,33 +34,19 @@ sub do_search_contents { #FIXME: that's extremely hacky atm if ($params->{values}{suite}{no_replace}[0] eq 'default') { $params->{values}{suite}{no_replace} = - $params->{values}{suite}{final} = $opts->{suite} = [ 'stable' ]; + $params->{values}{suite}{final} = $opts->{suite} = [ 'etch' ]; } if (@{$opts->{suite}} > 1) { fatal_error( sprintf( _g( "more than one suite specified for contents search (%s)" ), "@{$opts->{suite}}" ) ); } - $$menu = ""; - - my $keyword = $opts->{keywords}; + my @keywords = @{$opts->{keywords}}; my $mode = $opts->{mode} || ''; my $suite = $opts->{suite}[0]; my $archive = $opts->{archive}[0] ||''; $Packages::Search::too_many_hits = 0; - # for URL construction - my $keyword_esc = uri_escape( $keyword ); - my $suites_param = join ',', @{$params->{values}{suite}{no_replace}}; - my $sections_param = join ',', @{$params->{values}{section}{no_replace}}; - my $archs_param = join ',', @{$params->{values}{arch}{no_replace}}; - - # for output - my $keyword_enc = encode_entities $keyword || ''; - my $suites_enc = encode_entities( join( ', ', @{$params->{values}{suite}{no_replace}} ), '&<>"' ); - my $sections_enc = encode_entities( join( ', ', @{$params->{values}{section}{no_replace}} ), '&<>"' ); - my $archs_enc = encode_entities( join( ', ', @{$params->{values}{arch}{no_replace}} ), '&<>"' ); - my $st0 = new Benchmark; my (@results); @@ -68,7 +54,7 @@ sub do_search_contents { my $nres = 0; - my $kw = lc $keyword; + my $first_kw = lc shift @keywords; # full filename search is tricky my $ffn = $mode eq 'filename'; @@ -77,19 +63,28 @@ sub do_search_contents { or die "Failed opening reverse DB: $!"; if ($ffn) { - open FILENAMES, '-|', 'fgrep', '--', $kw, "$DBDIR/contents/filenames_$suite.txt" + open FILENAMES, '-|', 'fgrep', '--', $first_kw, "$DBDIR/contents/filenames_$suite.txt" or die "Failed opening filename table: $!"; + FILENAME: while () { chomp; + foreach my $kw (@keywords) { + next FILENAME unless /\Q$kw\E/; + } &searchfile(\@results, reverse($_)."/", \$nres, $reverses); last if $Packages::Search::too_many_hits; } + while () {}; close FILENAMES or warn "fgrep error: $!\n"; } else { - $kw = reverse $kw; - + error(_g("The search mode you selected doesn't support more than one keyword.")) + if @keywords; + + my $kw = reverse $first_kw; + $kw =~ s{/+$}{}; + # exact filename searching follows trivially: $kw = "$kw/" if $mode eq 'exactfilename'; @@ -98,65 +93,12 @@ sub do_search_contents { $reverses = undef; untie %reverses; - + my $st1 = new Benchmark; my $std = timediff($st1, $st0); debug( "Search took ".timestr($std) ) if DEBUG; } - - my $suite_wording = sprintf(_g("suite %s"), $suites_enc ); - my $section_wording = $sections_enc eq 'all' ? _g("all sections") - : sprintf(_g("section(s) %s"), $sections_enc ); - my $arch_wording = $archs_enc eq 'any' ? _g("all architectures") - : sprintf(_g("architecture(s) %s"), $archs_enc ); - my $wording = _g("paths that end with"); - if ($mode eq 'filename') { - $wording = _g("files named"); - } elsif ($mode eq 'exactfilename') { - $wording = _g("filenames that contain"); - } - msg( sprintf( _g("You have searched for %s %s in %s, %s, and %s." ), - $wording, $keyword_enc, - $suite_wording, $section_wording, $arch_wording ) ); - if ($mode ne 'filename') { - msg( ''filename'}). - "\">"._g("Search within filenames").""); - } - if ($mode ne 'exactfilename') { - msg( ''exactfilename'}). - "\">"._g("Search exact filename").""); - } - if ($mode eq 'exactfilename' || $mode eq 'filename') { - msg( 'undef}). - "\">"._g("Search for paths ending with").""); - } - - msg( _g("Search in other suite:")." ". - join( ' ', map { '[$_}). - "\">$_]" } @SUITES ) ); - - if ($Packages::Search::too_many_hits) { - error( _g( "Your search was too wide so we will only display only the first about 100 matches. Please consider using a longer keyword or more keywords." ) ); - } - - %$html_header = ( title => _g( 'Package Contents Search Results' ), - lang => $opts->{lang}, - title_tag => _g( 'Debian Package Contents Search Results' ), - print_title => 1, - print_search_field => 'packages', - search_field_values => { - keywords => $keyword_enc, - searchon => 'contents', - arch => $archs_enc, - suite => $suites_enc, - section => $sections_enc, - exact => $opts->{exact}, - debug => $opts->{debug}, - }, - ); - - $$page_content = ''; my (%results,%archs); foreach my $result (sort { $a->[0] cmp $b->[0] } @results) { my $file = shift @$result; @@ -170,51 +112,49 @@ sub do_search_contents { next unless keys %pkgs; $results{$file} = \%pkgs; } - my @all_archs = keys %archs; - @all_archs = @ARCHITECTURES unless @all_archs; + my @all_archs = sort keys %archs; + @all_archs = sort @ARCHITECTURES unless @all_archs; + $page_content->{suite} = $suite; + $page_content->{archive} = $archive; + $page_content->{all_architectures} = \@all_archs; + $page_content->{all_suites} = \@SUITES; + $page_content->{mode} = $mode; + $page_content->{search_architectures} = $opts->{arch}; + $page_content->{search_keywords} = $opts->{keywords}; + $page_content->{sections} = $opts->{section}; + $page_content->{too_many_hits} = $Packages::Search::too_many_hits; + debug( "all_archs = @all_archs", 1 ) if DEBUG; - msg(_g("Limit search to a specific architecture:")." ". - join( ' ', map { '[$_}). - "\">$_]" } @all_archs ) ) - unless (@{$opts->{arch}} == 1) || (@all_archs == 1); - msg(sprintf(_g('Search in all architectures'), - make_search_url('',"keywords=$keyword_esc",{arch=>undef}))) - if @{$opts->{arch}} == 1; - - if (!@Packages::CGI::fatal_errors && !keys(%results)) { - error( _g( "Nothing found" ) ); - } if (keys %results) { - $$page_content .= "

".sprintf( _g( 'Found %s results' ), - scalar keys %results )."

"; - $$page_content .= '
\n"; - foreach my $file (sort keys %results) { - my $file_enc = encode_entities($file); - $file_enc =~ s#(\Q$keyword_enc\E)#$1#g; - $$page_content .= "\n"; + push @{$page_content->{results}}, \%result; } - $$page_content .= '\n" if @results > 20; - $$page_content .= '
'._g('File').''._g('Packages') - ."
/$file_enc"; - my @pkgs; + my $sort_func = sub { $_[0] cmp $_[1] }; + $sort_func = sub { (sort keys %{$results{$_[0]}})[0] + cmp + (sort keys %{$results{$_[1]}})[0] + } if $opts->{sort_by} eq 'pkg'; + + $page_content->{results} = []; + foreach my $file (sort {&$sort_func($a,$b)} keys %results) { + my %result; + $result{file} = "/$file"; + $result{packages} = []; foreach my $pkg (sort keys %{$results{$file}}) { my $arch_str = ''; my @archs = keys %{$results{$file}{$pkg}}; + my $arch_neg = 0; unless ($results{$file}{$pkg}{all} || (@archs == @all_archs)) { - if (@archs < @all_archs/2) { - $arch_str = ' ['.join(' ',sort @archs).']'; - } else { - $arch_str = ' ['._g('not').' '. - join(' ', grep { !$results{$file}{$pkg}{$_} } @all_archs).']'; + if (@archs >= @all_archs/2) { + @archs = grep { !$results{$file}{$pkg}{$_} } @all_archs; + $arch_neg = 1; } + } else { + @archs = (); } - push @pkgs, "$suite})."\">$pkg$arch_str"; + push @{$result{packages}}, { pkg => $pkg, architectures => \@archs, architectures_are_rev => $arch_neg }; } - $$page_content .= join( ", ", @pkgs); - $$page_content .= "
'._g('File').''._g('Packages')."
'; } } # sub do_search_contents @@ -226,15 +166,22 @@ sub searchfile debug( "searchfile: kw=$kw", 1 ) if DEBUG; for (my $status = $reverses->seq($key, $value, R_CURSOR); $status == 0; - $status = $reverses->seq( $key, $value, R_NEXT)) { + $status = $reverses->seq( $key, $value, R_NEXT)) { # FIXME: what's the most efficient "is prefix of" thingy? We only want to know # whether $kw is or is not a prefix of $key last unless index($key, $kw) == 0; debug( "found $key", 2 ) if DEBUG; - my @hits = split /\0/o, $value; - push @$results, [ scalar reverse($key), @hits ]; + my @files = split /\001/o, $value; + foreach my $f (@files) { + my @hits = split /\0/o, $f; + my $file = shift @hits; + if ($file eq '-') { + $file = reverse($key); + } + push @$results, [ $file, @hits ]; + } last if ($$nres)++ > 100; }