Menu

[b29b51]: / search  Maximize  Restore  History

Download this file

318 lines (282 with data), 8.7 kB

#!/usr/bin/perl -T
# $Id: search,v 1.28 2004/10/18 20:22:57 brondsem Exp $

# search --	Freetext search
#
#	Arne Georg Gleditsch <argggh@ifi.uio.no>
#	Per Kristian Gjermshus <pergj@ifi.uio.no>
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

######################################################################

$CVSID = '$Id: search,v 1.28 2004/10/18 20:22:57 brondsem Exp $ ';

use strict;
use lib '.'; # for Local.pm
use lib do { $0 =~ m{(.*)/} ? "$1/lib" : "lib" }; # if LXR modules are in ./lib

use LXR::Common qw(:html);
use LXR::Config;

my $maxhits = 1000;

sub varinputs {
	my $templ = shift;
	my $ret   = '';

	foreach ($config->allvariables) {
		if ($config->variable($_) ne $config->vardefault($_)) {
			$ret .= expandtemplate(
				$templ,
				(
					variable => sub { $_ },
					value    => sub { $config->variable($_) },
				)
			);
		}
	}
	return $ret;
}

sub filename_matches {
	my ($filetext, $advanced, $casesensitive, $file) = @_;
	if ($advanced) {
		if ($casesensitive) {
			if ($file =~ /$filetext/) {
				return 1;
			}
		} elsif ($file =~ /$filetext/i) {
			return 1;
		}
	} else {
		if ($casesensitive) {
			if (index($file, $filetext) != -1) {
				return 1;
			}
		} elsif (index(lc($file), lc($filetext)) != -1) {
			return 1;
		}
	}
	return 0;
}

sub glimpsesearch {
	my ($searchtext, $filetext, $advanced, $casesensitive) = @_;

	$ENV{'PATH'} = '/bin:/usr/local/bin:/usr/bin:/usr/sbin';
	unless (open(GLIMPSE, "-|")) {
		open(STDERR, ">&STDOUT");
		$! = '';
		exec($config->glimpsebin, "-i", "-H" . $config->glimpsedir . "/" . $release,
			'-y', '-n', $searchtext);
		print("Glimpse subprocess died unexpextedly: $!\n");
		exit;
	}

	my $numlines     = 0;
	my @glimpselines = ();
	while (<GLIMPSE>) {
		$numlines++;
		push(@glimpselines, $_);
		if ($numlines >= $maxhits) {
			last;
		}
	}

	close(GLIMPSE);

	my $retval = $? >> 8;

	# The manpage for glimpse says that it returns 2 on syntax errors or
	# inaccessible files. It seems this is not the case.
	# We will have to work around it for the time being.

	if ($retval == 0) {
		my @ret;
		my $sourceroot = $config->sourceroot . '/' . $release . '/';
		my $i          = 0;
		foreach my $glimpseline (@glimpselines) {
			last if ($i > $maxhits);

			$glimpseline =~ s/$sourceroot//;
			my ($file, $line, $text) = $glimpseline =~ /(.*?):\s*(\d+)\s*:(.*)/;
			next if $filetext and !filename_matches($filetext, $advanced, $casesensitive, $file);

			$text =~ s/&/&amp;/g;
			$text =~ s/</&lt;/g;
			$text =~ s/>/&gt;/g;

			push @ret, [ $file, $line, $text ];
		} continue {
			$i++;
		}
		return @ret;
	} elsif ($retval == 1) {
		my $glimpsebin = $config->glimpsebin;
		my $glimpseresponse = join("<br>", @glimpselines);
		my $glimpseresponse =~ s/$glimpsebin/Reason/;
		my $glimpseresponse =~ s/glimpse: error in searching index//;
		print("<b>Search failed</b><br>\n$glimpseresponse");
		return;
	} else {
		print("Unexpected returnvalue $retval from Glimpse\n");
		return;
	}
}

sub swishsearch {
	my ($searchtext, $filetext, $advanced, $casesensitive) = @_;

	if (!-e $config->swishdir . "/" . $release . ".index") {
		print
		  "<p align='center'><i>Version '$release' has not been indexed and is unavailable for searching.</i></p>";
		return;
	}
	$ENV{'PATH'} = '/bin:/usr/local/bin:/usr/bin:/usr/sbin';
	unless (open(SWISH, "-|")) {
		exec($config->swishbin, "-f", $config->swishdir . "/" . $release . ".index",
			"-w", "'(" . $searchtext . ")'");
		open(STDERR, ">&STDOUT");
		print(STDERR "Couldn't exec " . $config->swishbin . ": $!\n");
		kill(9, $$);
	}

	my @result = grep { not /^[\#\.]/ } <SWISH>;
	close(SWISH);
	my $retval = $?;
	my @ret;
	if ($retval == 0) {
		my $numlines = 0;
		foreach my $hit (@result) {
			print $hit, "<br>\n" if $hit =~ $hit =~ /No such file or directory/;	# feeble attempt to print possible errors (e.g. incomplete LD_LIBRARY_PATH causes linking errors)
			next if $hit =~ /^err:/;    # skip; only 'no results' errors happen with return value 0

			my ($score, $file) = $hit =~ /^(\d+) \/(.+) "(.+)" \d+/;
			next if $filetext and !filename_matches($filetext, $advanced, $casesensitive, $file);
			push @ret, [ $file, $score ];
			$numlines++;

			last if ($numlines >= $maxhits);
		}
		return @ret;
	} else {
		print("<b><font color='red'>Search failed: internal error</font></b><br>\n@result");
		return;
	}
}

sub printresults {
	my $templ   = shift;
	my @results = @_;
	my $ret     = '';

	foreach (@results) {

		# filename, glimpse and swish-e searches provide different data for each result
		if (!ref) {
			my $file = $_;
			$ret .= expandtemplate(
				$templ,
				(
					text    => sub { return '' },
					fileref => sub { fileref("$file", "find-file", "/$file") },
				)
			);
		} elsif ($config->glimpsebin) {
			my (@params) = @$_;
			my $file     = $params[0];
			my $line     = $params[1];
			my $text     = $params[2];
			$ret .= expandtemplate(
				$templ,
				(
					text    => sub { return $text },
					fileref => sub { fileref("$file, line $line", "find-file", "/$file", $line) },
				)
			);
		} else {
			my (@params) = @$_;
			my $file     = $params[0];
			my $score    = $params[1];
			$ret .= expandtemplate(
				$templ,
				(
					text   => sub { return $score },
					fileref => sub { fileref("$file", "find-file", "/$file") },
				)
			);
		}
	}
	return $ret;
}

sub search {
	my $templ;

	if ($config->htmlsearch) {
		unless (open(TEMPL, $config->htmlsearch)) {
			warning("Template " . $config->htmlsearch . " does not exist.");
		} else {
			local ($/) = undef;
			$templ = <TEMPL>;
			close(TEMPL);
		}
	} else {
		die "'htmlsearch' template not configured";
	}

	my $searchtext    = $HTTP->{'param'}->{'string'};
	my $filetext      = $HTTP->{'param'}->{'filestring'};
	my $advanced      = $HTTP->{'param'}->{'advanced'};
	my $casesensitive = $HTTP->{'param'}->{'casesensitive'};

	my @results;
	if ($searchtext ne "") {
		if ($config->glimpsebin) {
			@results = glimpsesearch($searchtext, $filetext, $advanced, $casesensitive);
		} elsif ($config->swishbin and $config->swishdir) {
			@results = swishsearch($searchtext, $filetext, $advanced, $casesensitive);
		} else {
			warning("No freetext search engine configured.");
		}
	} elsif ($filetext ne "") {
		my $FILELISTING;
		if ($config->swishdir and $config->swishbin) {
			unless ($FILELISTING = new IO::File($config->swishdir . "/$release.filenames")) {
				&warning(
					"Version '$release' has not been indexed and is unavailable for searching<br>Could not open "
					  . $config->swishdir
					  . "/$release.filenames.");
				return;
			}
		} elsif ($config->glimpsedir and $config->glimpsebin) {
			unless ($FILELISTING =
				new IO::File($config->glimpsedir . "/" . $release . "/.glimpse_filenames"))
			{
				&warning(
					"Version '$release' has not been indexed and is unavailable for searching<br>Could not open "
					  . $config->glimpsedir
					  . "/$release/.glimpse_filenames.");
				return;
			}
		} else {
			warning(
				"Freetext search engine required for file search, and no freetext search engine is configured"
			);
			return;
		}
		my $sourceroot = $config->sourceroot . '/' . $release . '/';
		while (<$FILELISTING>) {
			chomp;
			s/^$sourceroot//;
			push @results, $_ if filename_matches($filetext, $advanced, $casesensitive, $_);
		}
		close($FILELISTING);
	}

	print expandtemplate(
		$templ,
		(
			variables          => sub { varinputs(@_) },
			searchtext         => sub { return $searchtext },
			searchtext_escaped => sub { $_ = $searchtext; s/\"/&quot;/g; return $_; },
			filetext_escaped   => sub { $_ = $filetext; s/\"/&quot;/g; return $_; },
			advancedchecked      => sub { return $advanced      ? "checked" : "" },
			casesensitivechecked => sub { return $casesensitive ? "checked" : "" },
			maxhits_message      => sub {
				return @results == $maxhits
				  ? "<b>Too many hits, displaying first $maxhits</b><br>"
				  : "";
			},

			results     => sub { printresults(@_, @results) },
			resultcount => sub { return scalar @results },
		)
	);
}
httpinit;
&makeheader('search');
&search;
&makefooter('search');

httpclean;

Want the latest updates on software, tech news, and AI?
Get latest updates about software, tech news, and AI from SourceForge directly in your inbox once a month.