#
# htgrep.pl    --- generic perl package to query HTML files
#
# Copyright (c) 1995 Oscar Nierstrasz

# 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 (as the file COPYING in the main directory of
# the distribution); if not, write to the Free Software Foundation,
# Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

# For details about how to install and use this package, please visit:
#
# http://iamwww.unibe.ch/~scg/Src/Doc/
#
# Author: Oscar Nierstrasz (oscar\@iam.unibe.ch)

# ---------------------------------------------------------------------------

require "html.pl";
require "bib.pl";

# ---------------------------------------------------------------------------

package htgrep;

# ---------------------------------------------------------------------------

# => VERSIONS

# Author: Oscar Nierstrasz 2/9/93
# 2/9/93 -- wrote parscan package for Plexus
# 8/9/93 -- extended parscan to handle pre-formatted text
# 27/10/93 -- converted parscan to htbin script
#v = 'htgrep v1.0'; # 11/5/94 -- converted `parscan' into cgi-bin script
#v = 'htgrep v1.1'; # 22/5/94 -- moved &FixTags from &settags to &CheckTags
	# added sanity check for grab=yes
#v = 'htgrep v1.2'; # 24/5/94 -- added call to html'esc in &query
#v = 'htgrep v1.3'; # 25/5/94 -- added timeout feature
#v = 'htgrep v1.4'; # 31/5/94 -- Ignore files in directories that have a
	# .htaccess file (NCSA httpd) M.J.Cox@bradford.ac.uk
#v = 'htgrep v1.5'; # 19/8/94 -- added support for ~user in URLs
	# 24/8/94 -- allow file to have arbitrary extension
	# 2/9/94  -- allow URLs in refer files (see bib.pl)
	# 6/9/94  -- added "alt" and "filter" tags
#$v = 'htgrep v1.6'; # 9/10/94 -- added "abstract" tag
#v = 'htgrep v1.6a'; # 20/2/95 -- patch: expanding user directories
#v = 'htgrep v1.7'; # 8/7/95 -- added $safeopen variable
#v = 'htgrep v1.7a'; # 7/9/95 -- two minor patches for Perl5
#v = 'htgrep v1.8'; # 2/8/95 -- Modified by Brian Exelbierd (bex@ncsu.edu)
	# Addition of a footer file for the header and query returns
	# Moved @USER_HOMES up to the user settable variables
	# Added support for URL Aliases or pseudo urls, i.e. ones
	#  redirected by the server
	# Added support for specifying the directory ~user expands to
	#  under user's $HOME in a variable
	# HTML now ends in </HTML> instead of just </BODY>
	# Changed the default form slightly moving the submit and reset
	#  buttons below the input field.
#v = 'htgrep v2.0'; # 8/9/95 -- general reorganization
	# made all locals/globals explicit
	# record separator is now configurable in $separator
	# multiple files are supported in @files, or comma-separated in $tag{'file'}
	# case sensitivity is configurable in tag case=yes
	# added &htgrep'PageVersion
	# merged in Boolean query support from P.C.Sutton@bradford.ac.uk
	# added &ThisCGI -- 'htgrep' tag and $htgrep variable now obsolete
#v = 'htgrep v2.0a'; # 21/9/95 -- fixed <TITLE> in &Makeprint
#v = 'htgrep v2.1'; # 27/9/95 -- various cosmetic fixes
	# Factored out &PageHeader
	# Simplified lookup of pseudo-URLs and expansion of ~user
#v = 'htgrep v2.2'; # 16/10/95 -- added various sanity checks
	# query must not contain "/"
	# filter must be subroutine name
$v = 'htgrep v2.3'; # 4/6/96 -- modified ParseTags to interpret
	# non-tags as isindex (query) strings
$v = 'htgrep v2.4'; # 6/5/97 -- added LinkURLs; handles multiple bib URLs

# ---------------------------------------------------------------------------

# => THESE MUST BE CONFIGURED!

# NB: you may prefer to configure these variables directly from your
# wrapper scripts.  This has the advantage that you can then install new
# versions of htgrep.pl without having to reconfigure it.  If you do
# this, set the necessary variables after the line `require "htgrep.pl";',
# and be sure to prefix each variable with "htgrep'",
# i.e., set $htgrep'www_home, not $www_home.

# path to root of the WWW tree
$www_home = "/home/www/WWW";	# moved 17/3/95

#  expands user user directories specified with '~user'.
#  This is a local path for IAM, University of Berne !
#
#  kg/ 20.2.95
@USER_HOMES = ("/home");

# ~user expands to what directory under the users $HOME
$tilde_dir = "public_html";

# A list of pseudo urls on your system.
# (pairs of pseudo-locations under WWW home and their actual locations)

%pseudo_urls = (
	'cgi-bin', '/home/www/httpd-1.3/cgi-bin'
);

# ---------------------------------------------------------------------------

# => OTHER CONFIGURABLE VARIABLES

# These may also be configured, or overridden from a wrapper script,
# if necessary:

# the default record separator is a blank line
$separator = "";

# Normally directories containing a .htaccess file are not searchable.
# It can be useful to override this if you want a file to be *only*
# searchable, but not accessible otherwise.
# To override, set $htgrep'safeopen = 0
$safeopen = 1; # check for presence of .htaccess files

# Place to point users to if they give invalid regexes:
#perlexp = "http://www.cis.ohio-state.edu/htbin/info/info/perl.info,Regular%20Expressions";
$perlexp = "http://www.cs.cmu.edu/Web/People/rgs/pl-regex.html";

# Documentation for multiple keyword searches:
$search = "http://iamwww.unibe.ch/~scg/Src/Doc/search.html";

# Signature at bottom of output page
# You probably should change this if you modify the implementation
# of this package.
$doc = "http://iamwww.unibe.ch/~scg/Src/Doc/htgrep.html";
$sig = "<i>This page was generated by\n<a href=\"$doc\">$v</a>.</i>\n<p>\n";

# Default MAX records to retrieve
# This will normally be overriden be setting $htgrep'tags{'max'} = <value>
$maxcount = 250;

# set $htgrep'timeout = 0 to inhibit timeouts
$timeout = 60;

# Alternative names for $tags{'style'}:
# (For backwards compatibility only)
%styletags = (
	"HTML paragraphs", "NONE",
	"Numbered list", "OL",
	"Bullet list", "UL",
	"Description list", "DL",
	"Plain ascii text", "PRE"
	);

# ---------------------------------------------------------------------------

# => INTERFACE ROUTINES

# Globals introduced:
# %tags		-- associative array of URL tags

# Generic routine to set tags from $ENV{'PATH_INFO'} or $ENV{'QUERY_STRING'}
# Optionally called by the main wrapper script to initialize %tags
# Argument is a string of tag=value&tag=value& ...
#
# Patched 4/6/96 so that tags of the form "file=isindex.html&stuff"
# will interpret "stuff" as an isindex (query) string.
# (Apparently some browsers generate this kind of URL.)
#
sub ParseTags {
	local($_) = @_;
	local(@terms, $tag, $val);
	s|^/||;
	@terms = split('&');
	foreach $term (@terms) {
		if ($term =~ /=/) {
			($tag,$val) = split('=',$term,2);
			# may override previous value
			$tags{$tag} = $val;
		}
		# No '=', so the whole string must be a query:
		else { $tags{'isindex'} = $term; }
	}
}

# This does the actual work.
# %tags must be initialized before being called:
sub DoSearch {
	if ($timeout) {
		# Timeout after $timeout seconds
		$main'SIG{'ALRM'} = "htgrep'PageTimeout";
		alarm($timeout);
	}
	print "Content-type: text/html\n\n";
	chdir($www_home) || &PageAbort("chdir", "Can't chdir to $www_home");
	if ($tags{'version'} eq "yes") {
		&PageVersion; # only report versions; nothing else
	}
	else {
		&CheckTags(%tags);
		if ($query) { &PageWithQuery; }
		else { &PageNoQuery; }
	}
}

# backwards compatibility:
sub settags { &ParseTags; }
sub doit { &DoSearch; }

# ---------------------------------------------------------------------------

# => TAGS INITIALIZATION

# Globals introduced:

# $query		-- the query string
# $maxcount		-- max records to return
# $filter		-- optional filter to apply to records matched
# $caseSensitivity	-- normally "i" (insensitive)
# $pre			-- start tag for search output (e.g., "<ol>")
# $post			-- end tag for search output
# @files		-- list of files to search
# $mainfile		-- the first file

# Additional tags used but not processed:
# $tags{'ftpstyle'}	-- "dir"
# $tags{'hdr'}		-- header file
# $tags{'qry'}		-- header file to use with queries only
# $tags{'hdr_footer'}	-- footer file
# $tags{'qry_footer'}	-- footer file with queries only

# check %tags and initialize some variables
sub CheckTags {
	# Convert special chars (+ for space etc.) back to plain text
	foreach $tag (keys(%tags)) {
		$tags{$tag} =~ s/\+/ /g;
		$tags{$tag} =~ s/%([\da-f]{1,2})/pack(C,hex($1))/eig;
	}
	@files = split(/,/, $tags{'file'}) unless(defined(@files));
	$mainfile = &ExpandPath($files[0]);
	if ($#files < $[) {
		&PageAbort('bad request', "Missing filename.");
	}
	#
	# set the query string:
	$query = $tags{'isindex'};
	#
	# max records to return (must be a number):
	($tags{'max'} =~ /^\d+$/) && ($maxcount = $tags{'max'});
	#
	# optional filter (must be a subroutine name):
	if ($tags{'filter'} =~ /^[\w']+$/) { $filter = "&$tags{'filter'};"; }
	#
	$caseSensitivity = "i" unless ($tags{'case'} eq "yes");
	#
	# normally records are separated by blank lines
	# if linemode is set, there is one record per line
	if ($tags{'linemode'} =~ /yes/i) { $/ = "\n"; }
	else { $/ = $separator; }
	#
	# set pre and post tags:
	&SetStyle;
	#
	# refer bibliography mode (overrides $filter):
	if ($tags{'refer'} =~ /yes/i) { &SetRefMode; }
	# Backwards compatibility:
	elsif ($tags{'refer'} =~ /plain/i) { &SetRefMode; }
	elsif ($tags{'refer'} =~ /abstract/i) { &SetAbstractMode; }
	#
	# abstract=yes overrides refer=yes etc.
	if ($tags{'abstract'} =~ /yes/i) { &SetAbstractMode; }
	# pre-formatted text should be escaped:
	if ($pre =~ /<pre>/i) { $filter = "&html'esc;"; }
	# Only recognize and hypertextify URLs within plain text!
	if (($tags{'grab'} =~ /yes/i) && ($pre =~ /pre/i)) {
		$filter = "&html'esc; &html'href;";
	}
}

# set the appropriate pre and post tags for whatever
# paragraph style wil be returned/generated for queries:
sub SetStyle {
	local($pretag, $fmttag);
	# $pre and $post are printed before and after the query results
	# They can be given directly, or retrieved from %styletags.
	# Should be pre, dl, ol or ul.
	if (($pretag = $styletags{($fmttag = $tags{'style'})}) =~ /./) {
		$pre = "<$pretag>";
		$post = "</$pretag>";
	}
	elsif ($fmttag =~ /./) {
		$pre = "<$fmttag>";
		$post = "</$fmttag>";
	}
	else { $pre = $post = ""; }
	if ($pre =~ /none/i) { $pre = $post = ""; }
}

# ---------------------------------------------------------------------------

# => FILE EXPANSION/OPENING

# Expand the file argument to a real path
sub ExpandPath {
	local($path) = @_;
	local($prefix, $exp);
	($path eq "") && &PageAbort('bad request', "Missing filename.");
	(($path =~ m#\.+/#) || ($path =~ m#/\.+$#))
		&& &PageAbort('bad request',
		"No backward directory references permitted: $path");
	$path =~ s|^/||; # relative to www_home only!
	#
	# EXPAND ~user to ~user/$tilde_dir:
	if ($path =~ s|^(~[^/]+)||) {
		$path = &ExpandUser($1) . $path;
	}
	#
	# EXPAND pseudo urls, such as those created in srm.conf with NCSA HTTPD
	# i.e. ones without a sub-directory under $www_home
	# simplified expansion -- on 27/9.95
	($prefix = $path) =~ s|/.*||;
	if (($exp = $pseudo_urls{$prefix}) =~ /./) {
		# if there is an expansion for the prefix, swap it in:
		$path =~ s|^$prefix|$exp|;
	}
	#
	(-e $path) || &PageAbort('not found', "Can't find $path\n");
	(-d $path) && ($path = "$path/index.html");
	(-f $path) || &PageAbort('not found', "Can't find $path\n");
	return($path);
}

# Karl Guggisberg / 20.2.95
sub ExpandUser {
	local($user) = @_;
	local($dir, $udir);
	$user =~ s|~(.*)|\1|;
	# simplified expansion -- on 27/9.95
	foreach $dir (@USER_HOMES){
		$udir = "$dir/$user/$tilde_dir";
		return $udir if -d $udir;
	}
	return "~$user";
}

# Adapted from Plexus.
sub SafeOpen {
	local($fh, $_) = @_;
	local($facc);
	s#^\s#./$&#;                # protect leading spaces
	(m#/\.+/# || m#/\.+$#) && &PageAbort('bad request',
		"No backward directory references permitted: $_");
	# Modification from M.J.Cox@bradford.ac.uk:
	($facc = $_) =~ s|[^/]*$|.htaccess|; # For NCSA httpd
	!$safeopen || !(-e $facc)
		|| &PageAbort('Access Denied',"Access denied to $_\n");
	open($fh, "< $_\0");
}

# ---------------------------------------------------------------------------

# => PAGE CONTROL

# Responsible for generating a valid HTML page

# Globals introduced:
$headprinted = 0;	# this flag tells &PageAbort not to generate a HEAD

# What to do if there is no query --
# Output the appropriate user-supplied header (if any):
# (formerly called "noquery")
sub PageNoQuery {
	local($mainfile, $base, $dir, $hdr, $ftr);
	$mainfile = &ExpandPath($files[0]);
	($base = $mainfile) =~ s/\.[^.]+$//; # accept arbitrary extensions
	# look for a header file:
	($dir = $mainfile) =~ s|[^/]*$||;
	(-f ($hdr = "$dir/$tags{'hdr'}"))
		|| (-f ($hdr = $tags{'hdr'}))
		|| (-f ($hdr = "$mainfile.hdr"))
		|| (-f ($hdr = "$base.hdr"));
	if (&SafeOpen(HDR,$hdr)) {
		local($/) = undef; # gobble the whole input
		print <HDR>;
		close(HDR);
	}
	else { &MakeForm; }
	print "$sig\n";
	(-f ($ftr = "$dir/$tags{'hdr_footer'}"))
		|| (-f ($ftr = $tags{'hdr_footer'}))
		|| (-f ($ftr = "$mainfile.hdr_footer"))
		|| (-f ($ftr = "$base.hdr_footer"));
	if (&SafeOpen(FTR,$ftr)) {
		local($/) = undef; # gobble the whole input
		print <FTR>;
		close(FTR);
	}
	print "</BODY></HTML>\n\n"; # should be an else clause?
}

# What to do if there is a query --
sub PageWithQuery {
	&PageHeader;
	&HandleQuery;
	&PageFooter;
}

# Output the appropriate user-supplied header (if any):
sub PageHeader {
	local($base, $dir, $hdr);
	# ($base = $mainfile) =~ s/\.html$//;
	($base = $mainfile) =~ s/\.[^.]+$//; # accept arbitrary extensions
	($dir = $mainfile) =~ s|[^/]*$||;
	# look for a header file:
	(-f ($hdr = "$dir/$tags{'qry'}"))
		|| (-f ($hdr = "$tags{'qry'}"))
		|| (-f ($hdr = "$mainfile.qry"))
		|| (-f ($hdr = "$base.qry"))
		|| (-f ($hdr = "$base.query"))
		|| (-f ($hdr = "$dir/$tags{'hdr'}"))
		|| (-f ($hdr = $tags{'hdr'}))
		|| (-f ($hdr = "$mainfile.hdr"))
		|| (-f ($hdr = "$base.hdr"));
	if (&SafeOpen(HDR,$hdr)) {
		local($/) = undef; # gobble the whole input
		print <HDR>;
	}
	else { &MakeForm; }
	$headprinted = 1; # make sure that &PageAbort does not generate a HEAD
}

# Output the user-supplied footer (if any):
sub PageFooter {
	local($mainfile, $base, $dir, $ftr);
	$mainfile = &ExpandPath($files[0]);
	print "<hr>\n$sig\n";
	($base = $mainfile) =~ s/\.[^.]+$//; # accept arbitrary extensions
	($dir = $mainfile) =~ s|[^/]*$||;
	# look for a footer file:
	(-f ($ftr = "$dir/$tags{'qry_footer'}"))
		|| (-f ($ftr = "$tags{'qry_footer'}"))
		|| (-f ($ftr = "$mainfile.qry_footer"))
		|| (-f ($ftr = "$base.qry_footer"))
		|| (-f ($ftr = "$base.query_footer"));
	if (&SafeOpen(FTR,$ftr)) {
		local($/) = undef; # gobble the whole input
		print <FTR>;
	}
	print "</BODY></HTML>\n\n"; # should be an else clause?
}

# Report which versions of packages are being used.
# Should normally be called if tag version=yes.
sub PageVersion {
	local($av, $bv, $hv, $no);
	$no = "<i>no version number</i>";
	$av = &DefaultString($accent'v, $no);
	$bv = &DefaultString($bib'v, $no);
	$hv = &DefaultString($html'v, $no);
	print <<EndOfPage;
<HTML>
<HEAD>
<TITLE>Htgrep version</TITLE>
</HEAD>
<BODY>
<H1>Htgrep version</H1>
The following package versions are being used from
this CGI script:<p>
<ul>
<li><b>htgrep.pl:</b> $v
<li><b>html.pl:</b> $hv
<li><b>bib.pl:</b> $bv
<li><b>accent.pl:</b> $av
</ul>
Please consult the <a href=$doc>htgrep documentation</a>
to obtain the current versions.
<p>
<HR>
$sig
</BODY>
</HTML>
EndOfPage
	exit;
}

#
# construct a new call to htgrep if there is no user-supplied header
#
sub MakeForm {
	local($cgi) = &ThisCGI;
	print "<HTML>\n<HEAD>\n<TITLE>Htgrep of @files</TITLE>\n";
	print "</HEAD>\n<BODY>\n<H1>Htgrep of @files</H1>\n";
	print "<FORM ACTION=\"$cgi\">\n";
	print "Provide a\n";
	if ($tags{'boolean'} eq "no") {
		print "<a href=$perlexp>Perl regular expression</a>\n";
	}
	elsif ($tags{'boolean'} eq "yes") {
		print "<a href=$search>boolean keyword expression</a>\n";
	}
	else {
		print "<a href=$search>boolean keyword expression</a>\n";
		print "or a\n";
		print "<a href=$perlexp>Perl regular expression</a>\n";
	}
	print "as a search pattern.\n<p>\n";
	print "<INPUT NAME=\"isindex\">\n",
		"<INPUT TYPE=\"submit\" VALUE=\"Submit\">\n",
		"<INPUT TYPE=\"reset\" VALUE=\"Reset\">\n",
		"<\/FORM>\n";
	print "<hr>\n";
}

sub PageAbort {
	local($kind, $msg) = @_;
	unless ($headprinted) {
		print "<HTML>\n",
			"<HEAD><TITLE>Htgrep error: $kind</TITLE></HEAD>\n",
			"<BODY>";
	}
	print "<H2>Htgrep error: $kind</H2>\n",
		"<i>$msg</i>\n<p>\n<hr>\n$sig\n</BODY>\n</HTML>\n";
	exit;
}

sub PageTimeout {
	$main'SIG{'ALRM'} = "htgrep'HardTimeout";
	alarm(2);
	&PageAbort('timeout', "Sorry. Htgrep timed out after $timeout seconds!");
	exit(1);
}

# Just exit, in case &PageAbort hangs ...
sub HardTimeout {
	exit(1);
}

# ---------------------------------------------------------------------------

# => QUERY ROUTINES

# do the query
sub HandleQuery {
	local($q, $eval);
	$q = &Escape($query); # escape special chars in query
	print "<b>Result of search for \"$q\":</b><p>\n";
	$count = 0;
	$eval = &MakeQuery;
	&Debug($eval); # print the eval script, if in debug mode
	print "$pre\n";
	unless (eval $eval) {
		print "$post\n<i>Garbled search pattern:</i>",
			"\"$query\"\n<br>\nBe sure to use a valid\n",
			"<a href=\"$search\">search expression</a>.<p>\n";
		if ($query =~ /\+/) {
			$q =~ s/\+/\\+/g;
			print "Maybe you should try: \"$q\"?<p>\n";
		}
	}
	$@ = undef; # clean up error msg
	print "$post\n";
	if ($count == $maxcount) {
		print "<b>Too many matching records (> $maxcount)!\n",
			"Try a more restrictive pattern.</b><p>";
	}
	elsif ($count == 0) {
		print "<b>No matching entries found.</b><p>\n";
	}
}

# This constructs the query loop as an eval string
sub MakeQuery {
	local ($eval, $queryCommand);
	$queryCommand = &ParseQuery($query);
	$eval = <<EOM;
	foreach \$thisfile (\@files) {
		local(\$path) = &ExpandPath(\$thisfile);
		&SafeOpen(FILE,\$path)
			|| &PageAbort("couldn't open file", "\$thisfile \$!");
		while (<FILE>) {
			$queryCommand
			$filter
			print;
			last if (++\$count == $maxcount);
		}
		close(FILE);
		last if (\$count == $maxcount);
	}
	1;
EOM
	return($eval);
}

# Given a query as entered by the user, return a perl expression
# which does a "next" if the search record (to be in $_) is not matched by
# the user's input search string.
#
# If the query looks like a perl regexp (if it has a backslash)
# then match as a perl regexp.
#
# Otherwise assume it is a word or set of words, possibly joined
# by `and', `or' and `not' (the default is `and'). In this mode, words
#  are assumed to be complete; partial matching can be forced with *
# at any point (eg work* to match work, workers, working).
#
# Can also set tag boolean=yes or no to override automatic choice.
#
# Originally from P.C.Sutton@bradford.ac.uk (Aug 1994)
# Finally (!) adapted and integrated Sept 1995 (!!)

sub ParseQuery
{
	local($query) = @_;
	# Sanity check: query must not contain unescaped "/"!
	$query =~ s|^/|\\/|;
	$query =~ s|([^\\])/|\\/|g;
	if (	($tags{'boolean'} eq "no")
		|| (($tags{'boolean'} ne "yes") && $query =~ /\\/)) {
		# perl expression, return as is.
		return "/$query/$caseSensitivity || next;\n";
	}
	# boolean expression (or single word)
	local($not, $join);
	local($qrycmd) = "next unless (";
	# $query =~ tr/A-Z/a-z/;
	$query =~ s/\(/ ( /g;	# make sure brackets are separate "words"
	$query =~ s/\)/ ) /g;
	$query =~ s/["'`]//g;   # quotes don't work (yet?)
	# for (split(/[+ \t]+/, $query)) {
	# Splitting on "+" is bad for queries like "C++"!
	$query =~ s/\+/\\+/g;
	for (split(/[ \t]+/, $query)) {
		# for each "word", do ...
		next if /^$/;
		if ($_ eq ")") { $qrycmd .= $_; next; }
		if ($_ eq "(") { $qrycmd .= "$join$_"; $join = ""; next; }
		if ($_ eq "not") { $not = "!"; next; }
		if ($_ eq "or")  { $join = " || "; next; }
		if ($_ eq "and") { $join = " && "; next; }
		if (/\*/) {
			s/\*/\\w*/g;
		}
		# match word boundaries only if fully alphanumeric
		# (for queries like "C++")
		elsif (/^\w+$/) {
			s/(.*)/\\b\1\\b/;
		}
		$qrycmd .= "$join $not/$_/$caseSensitivity";
		$join = " && ";	# default to AND joins
		$not = "";
	}
	$qrycmd .= ");";
}

sub Escape {
	local($_) = @_;
	# s/\\/&&/g;
	&html'esc;
	return($_);
}


# ---------------------------------------------------------------------------

# => REFER FILTERS

# initialize the environment to handle refer bibliography files
sub SetRefMode {
	&bib'html_init;
	# default style is numbered, unless set to bullet list:
	unless ($pre =~ /ul/i) {
		$pre = "<ol>\n"; $post = "</ol>\n";
	}
	open(STDERR,">/dev/null"); # ignore errors from bib'getref
	$filter = "&ReferFilter;";
}

# prepare to format abstracts together with bibliography entries
sub SetAbstractMode {
	&bib'html_init;
	$pre = ""; $post = ""; # default style
	open(STDERR,">/dev/null"); # ignore errors from bib'getref
	$filter = "&AbstractFilter;";
}

# the filter that formats bibliography records
sub ReferFilter {
	local($what, $cgi);
	&accent'html; # expand accents
	&bib'getref; # set variables to field values of the record
	$_ = "<li>$bib'ref";
	if ($bib'abstract ne "") {
		$what = "abstract";
		# if ($bib'url ne "") { $what .= "+file"; }
		$cgi = &ThisCGI;
		# If multi-file search, abstract is in *this* file!
		$cgi =~ s/file=[^&]*/file=$thisfile/;
		# replace tag refer=yes by abstract=yes
		$cgi =~ s/refer=yes//i;
		$cgi =~ s/abstract=yes//i;
		$cgi .= "&abstract=yes";
		$cgi =~ s/&+/&/g; # collapse resulting empty tags
		$cgi =~ s|^([^=]*/)&|$1|;
		$_ .= "<a href=\"$cgi?$bib'lbl\"><i>$what</i></a>\n";
	}
	# elsif ($bib'url ne "") { &LinkFile; }
	&LinkURLs;
	$_ .= "\n";
}

sub AbstractFilter {
	&accent'html;
	&bib'getref;
	$_ = "<hr>\n<H2>$bib'title</H2>\n$bib'ref<p>\n";
	if ($bib'abstract ne "") {
		$_ .= "<H3>Abstract</H3>\n$bib'abstract<p>\n";
	}
	# if ($bib'url ne "") { &LinkFile; }
	&LinkURLs;
}

# Generate a link to the ftpable file:
# Obsolete -- replaced by LinkURLs ...
sub LinkFile {
	local($ftpfile);
	if ($tags{'ftpstyle'} eq "dir") {
		$bib'url =~ s|[^/]*$||;
		$ftpfile = $&;
	}
	$_ .= "<b>File:</b> <a href=\"$bib'url\"><i>$bib'url</i></a>$ftpfile\n";
}

# Generate links to remote URLs:
sub LinkURLs {
	local($url);
	foreach $url (@bib'urlList) {
		$_ .= "<a href=\"$url\"><i>";
		if ($url =~ /\.ps/) { $_ .= "postscript"; }
		elsif ($url =~ /\.pdf/) { $_ .= "PDF"; }
		elsif ($url =~ /\.html/) { $_ .= "HTML"; }
		elsif ($url =~ /\/$/) { $_ .= "directory"; }
		else { $_ .= "file"; }
		$_ .= "</i></a>\n";
	}
}

# ---------------------------------------------------------------------------

# => MISCELLANEOUS STUFF

# return a string or its default value
sub DefaultString {
	local($string, $default) = @_;
	return ($string) if ($string =~ /./);
	return ($default);
}

# reconstruct the URL path of this CGI script
sub ThisCGI {
	local($cgi);
	$cgi = $ENV{'SCRIPT_NAME'} . "/";
	while (($key, $val) = each %tags) {
		# Need to pack special chars?!
		$cgi .= "&" . $key . "=" . $val
			unless $key =~ /isindex/i;
	}
	$cgi =~ s|/&|/|; # eliminate spurious first "&"
	return($cgi);
}

# optionally print a message if the debug flag is on
sub Debug {
	local($msg) = @_;
	if ($htgrep'tags{'debug'} eq "yes") {
		$msg = &Escape($msg);
		print "<b>DEBUG:</b>\n<pre>\n$msg\n</pre>\n";
	}
}

# ---------------------------------------------------------------------------

1;

