#!/usr/bin/perl
# vim:ft=perl:cindent:ts=8
#
# Find all docs related to one program or find matching entries in Debian Doc. Menu
# "$Id: dwww-find,v 1.38 2003/09/04 06:32:12 robert Exp $"
#

use strict;

use Debian::Dwww::Utils;
use Debian::Dwww::Initialize;


my $dwwwvars = &DwwwInitialize("/etc/dwww/dwww.conf");

my $dwww_quickfind_db = $dwwwvars->{'DWWW_QUICKFIND_DB'};
my $dwww_menu_dir     = $dwwwvars->{'DWWW_HTMLDIR'} . "/menu";
my $dwww_swish_index  = "/var/lib/dwww/dwww.swish++.index";
my $dwww_swish_conf   = "/usr/share/dwww/swish++.conf";

undef %{$dwwwvars};

my $templates_dir  = "/usr/share/dwww";
my $template_start = "$templates_dir/dwww-find.start";
my $template_end   = "$templates_dir/dwww-find.end";

my %hrefs = (  
	    'man'       => '/cgi-bin/dwww?type=man&amp;location=', 
            'runman'    => '/cgi-bin/dwww?type=runman&amp;location=',
            'dir'       => '/cgi-bin/dwww?type=dir&amp;location=',
            'info'      => '/cgi-bin/info2www?file=',
            'file'      => '/cgi-bin/dwww?type=file&amp;location=',
            'menu'      => '/dwww/menu/',
            'search'    => '/cgi-bin/dwww?search=',
            'dpkg'      => '/cgi-bin/dpkg?query='
        );

my $dpkgwwwcgi = "/usr/lib/cgi-bin/dpkg";


my $dpkg="dpkg";
if ( -x "/usr/bin/dlocate" && -s "/var/lib/dlocate/dlocatedb" 
    && -s "/var/lib/dlocate/dpkg-list" ) {
        $dpkg="dlocate";
} else {
        $dpkg="dpkg";
}

#########################################################################
#
# Main program
#

if (! defined $ARGV[0]) {
    print STDERR "usage: $0 [--package|--menu|--documentation] searcharg\n";
    exit(1);
}

my $mode="p";
if ($ARGV[0] =~ m/^--(.*)$/) {
        if ($1 eq "package") { $mode = "p"; }
        elsif ($1 eq "menu") { $mode = "m"; }
        elsif ($1 eq "documentation") { $mode = "d"; }
        else {
                print STDERR "usage: $0 [--package|--menu|--documentation] searcharg\n";
                exit(1);
        }
        shift @ARGV;
}

my $skip=0;
if ($ARGV[0] =~ m/^--skip=([0-9]+)$/) {
	$skip=$1;
        shift @ARGV;
}




my $f_cnt = 0;

print &TemplateFile($template_start, { 'TITLE' => 'Search results',
				     'VALUE'   => &HTMLEncode(join(" ", @ARGV)), 	
                                     'MCHECKED' => $mode eq "m" ? 'checked' : '',
                                     'PCHECKED' => $mode eq "p" ? 'checked' : '',
                                     'DCHECKED' => $mode eq "d" ? 'checked' : ''
                                 });

if ($mode eq "p") {
        $f_cnt = &SearchPackage(@ARGV);
} elsif ($mode eq "m") {
        $f_cnt = &SearchMenus($dwww_menu_dir, $ARGV[0]);
} elsif ($mode eq "d") {
        $f_cnt = &SearchRegisteredDocumentation($ARGV[0], $skip, 50);
}

print "<strong>Not found!</strong>\n" unless $f_cnt;


print &TemplateFile($template_end, { } );



#########################################################################
#
# Local functions
#

sub FindPkg {
        my $searchfor = shift;
        my @ret = ();

        if ( -f "$dwww_quickfind_db" ) {
                open (FINDPKG, "dwww-quickfind \"$searchfor\" \"$dwww_quickfind_db\"|");
                while (<FINDPKG>) {
                    chomp();    
                    push(@ret, $_);
                }   
        } else {
                open (FINDPKG, "$dpkg \"$searchfor\"|");
                while (<FINDPKG>) {
                    chomp();    
                    if (m/^([^:]+):.*bin.*\/$searchfor\$/o) {
                            push(@ret, $1);
                    }
                }
        }
        close FINDPKG;
        return @ret;
}

sub SearchPackage {
	my @args  = @_;
        my $f_cnt = 0;
	
        foreach my $arg (@args) {
                print "<h2>Documentation related to <em>" . &HTMLEncode("$arg") . "</em></h2>\n";
                foreach my $pkg (&FindPkg("$arg")) {
                        print "<h3><strong>Package:</strong> ";
			if ( -x "$dpkgwwwcgi" ) {
				print "<a href=" . $hrefs{'dpkg'} . &URLEncode("$pkg"). ">"
					. &HTMLEncode("$pkg") . "</a>";
			} else {
				print &HTMLEncode("$pkg");
			}
			print "</h3>\n";
			
                        $f_cnt += &PkgDescription("$pkg");
                        my @filelist = sort &GetPkgFileList("$pkg");
                        $f_cnt += &MansInPkg(@filelist);
                        $f_cnt += &InfosInPkg (@filelist);
                        $f_cnt += &DocsInPkg (@filelist);
                }
        	$f_cnt += &Apropos ("$arg");
        }
        return $f_cnt;
}


sub GetPkgFileList {
        my $pkg = shift;
        my @ret = ();
        
        open (FILELIST, "$dpkg -L \"$pkg\"|");
        while (<FILELIST>) {
                chomp();    
                push(@ret, $_);
        }
        close FILELIST;
        return @ret;
}


sub MansInPkg {
	my @files 	= @_;
        my $res		= 0;
        my $table 	= undef;

        foreach $_ (@files) {
                next unless m/\/man\/man[1-9n]\//;
                next unless ( -f "$_");
                if (!$res) {
                        $res = 1;
                        $table = &BeginTable(\*STDOUT, "Manual pages:", 3);
                }
                my $uri = &URLEncode($_);
        	s/^.*\///;
	        s/\.(gz|bz2)$//;
	        s/\.([^.]*)$/($1)/;
                &AddToTable(\*STDOUT, $table, 
                        "<a href=\"" . $hrefs{'man'} . "$uri\">$_</a>");
        }
        if ($res) {
                &EndTable(\*STDOUT, $table);
        }
        return $res;
}

sub InfosInPkg() {
       	my @files 	= @_;
        my $res		= 0;
        my $table 	= undef;

        foreach $_ (@files) {
                next unless m/\/info\/.*\.info(\.gz)?$/;
                next unless ( -f "$_");
                if (!$res) {
                        $res = 1;
                        $table = &BeginTable(\*STDOUT, "Info files:", 3);
                }
                my $uri = &URLEncode($_);
                s/^.*\///;
                s/^\..*//;
                &AddToTable(\*STDOUT, $table, 
                        "<a href=\"" . $hrefs{'info'} . "$uri\">$_</a>");
        }
        if ($res) {
                &EndTable(\*STDOUT, $table);
        }
        return $res;
}

### sub RegisteredDocsInPkg {
###     my @files  = @_;
### 	my $res = 0;
### 	my $table = undef;
### 	my $entry = undef;
### 
###         foreach $_ (@files) {
###                 next unless m/^\/usr\/share\/doc-base\//;
###                 next unless ( -f "$_");
### 
### 		$entry = &ParseDocBaseFile("$_");
### 		next unless defined $entry;
### 		
### 		if (!$res) {
### 			$res = 1;
###                         $table = &BeginTable(\*STDOUT, "Registered documents:", 1);
### 		}
###                 my $uri = &URLEncode($_);
###                 &AddToTable(\*STDOUT, $table, 
###                         "<a href=\"" . $hrefs{'dir'} . "$uri\">$_</a>");
### 		undef %{$entry};
###         }
###         if ($res) {
###                 &EndTable(\*STDOUT, $table);
###         }
###         return $res;
### }

sub DocsInPkg {
	my @files	= @_;
        my $res 	= 0;
        my $table 	= undef;

        foreach $_ (@files) {
                next unless m/^\/usr(\/share)?\/doc\//;
                next unless ( -d "$_");
                if (!$res) {
                        $res = 1;
                        $table = &BeginTable(\*STDOUT, "Other documents:", 2);
                }
                my $uri = &URLEncode($_);
                &AddToTable(\*STDOUT, $table, 
                        "<a href=\"" . $hrefs{'dir'} . "$uri\">$_</a>");
        }
        if ($res) {
                &EndTable(\*STDOUT, $table);
        }
        return $res;
}

sub Apropos {
        my $searchfor 	= shift;
        my @apropos 	= ();
        my $res 	= 0;
        my $table 	= undef;

        open (APROPOS, "man -k \"$searchfor\"|");
        while (<APROPOS>) {
                chomp();
                push (@apropos, $_);
        }
        close APROPOS;

        foreach $_ (sort @apropos) {
                chomp();
## Example "man -k" output that we are trying to parse:
## a2p (1)              - Awk to Perl translator
## #include <qslider.h> (3qt) [qslider] - Vertical or horizontal slider
##
                next unless (/^(.*?)\s\(([1-9]\S*)\)(\s*\[.*\])?\s+- .*$/);
                my $man  = "$1";
                my $sect = "$2";
                my $tmp  = defined $3 ? "$3" : "";
                if ($tmp =~  /\s*\[(.*)\]\s*/) {
                        $_ = "$1/$sect";
                } else {
                        $_ = "$man/$sect";
                }
                if (!$res) {
                        $res = 1;
                        $table = &BeginTable(\*STDOUT, "Manual page search:", 3);
                }

                my $uri = &URLEncode($_);
                $_ = &HTMLEncode("$man($sect)");
                &AddToTable(\*STDOUT, $table, 
                        "<a href=\"" . $hrefs{'runman'} . "$uri\">$_</a>");
        }
        
        if ($res) {
                &EndTable(\*STDOUT, $table);
        }
        return $res;
}

sub PkgDescription () {
        my $pkg 	= shift;
        my $descr 	= '';
        my $synopsis	= undef;
        my $fdescr 	= 0;     
        my $res	 	= 0;
        my $table 	= undef;
        
        return 0 unless ( -x "/usr/bin/apt-cache" );

        open PKGDESC, ("apt-cache show -o 'APT::Cache::AllVersions=0' \"$pkg\" 2>/dev/null |");
        while (<PKGDESC>) {
                if (!$fdescr && s/^Description:\s+//) {
                        chomp();
                        $synopsis = $_;
                        $fdescr = 1;
                } elsif ($fdescr) {
                    $descr .= $_;
                }
        }
        close PKGDESC;

        return 0 unless defined $synopsis;
        print '<strong>Description:</strong> ' . &HTMLEncode("$synopsis");
        print "\n<br>";
        print &HTMLEncodeAbstract($descr);
        return 1;
}


sub SearchMenus {
        my $dir        = shift;
        my $searchfor  = shift;
        my $match_cnt  = 0;

        print "<h2>Menu entries related to <em>" . &HTMLEncode("$searchfor") . "</em></h2>\n";

        if (not opendir DOCBASEDIR, $dir) {
                print "Can't open directory $dir: $!\n";
                return $match_cnt;
        }   
        
    
        while (my $f = readdir(DOCBASEDIR)) {
                next if -d $f;
                next unless $f =~ /^s.*\.html$/;

                $match_cnt += &SearchinMenuFile($dir, $f, $searchfor);
        }
        return $match_cnt;
}



sub SearchinMenuFile() {
        my $dir       = shift;
        my $file      = shift;
        my $searchfor = shift;
        my $sec       = undef;
        my $res       = undef;
        my $entry     = undef;
        my $inentry   = 0;
        my $found     = 0; 
        my $match_cnt = 0;

        open FILE, "<$dir/$file" or die "Can't open file";
    
        while (<FILE>) {
                if (!defined $sec) {
                        $sec = $1 if m/^<!-- Section: (.*) -->$/;
                } elsif (m/^<!-- begin entry -->/) {
                        $inentry = 1;
                        $entry = '';
                        $found = 0;
                } elsif (m/^<!-- end entry -->/) {
                        $inentry = 0;
                        $res .= $entry if ($found);
                        $match_cnt++ if ($found);
                } elsif ($inentry) {
                        $entry .= $_;
                        next if s/^<br><b>Formats:.*//;
                        s/<[^>]*>//g;
                        $found = 1 if (m/$searchfor/io);
                }
        }


        if (defined $res) {
                print "<h2>Section: <a href=\"" . $hrefs{'menu'} . 
                                        &URLEncode($file) ."\">$sec</a></h2>\n";
                print "<dl>\n";
                print $res;
                print "</dl>\n";
        }
        return $match_cnt;
}   


sub PrintRegDocsPages {
	my $searchfor  = shift;
	my $startwith  = shift;
	my $maxperpage = shift;
	my $resultcnt  = shift;
	my $max        = 10;
	my ($first, $last);

	return unless (defined $resultcnt and defined $startwith and defined $maxperpage
			and $resultcnt > $maxperpage and $maxperpage > 0);
	
	my $pagescnt = int ($resultcnt / $maxperpage);
	my $pageno   = int ($startwith / $maxperpage);
	$first       = 0;
	$last 	     = $pagescnt + 1;
	
	
	print "<center>\n";
	
	if ($pagescnt > $max) {
		$first = (int ($pageno / $max)) * $max - 1;
		$last  = $first +  $max + 1;
		$last  = $pagescnt + 1 if $last > $pagescnt;
	}

#	print STDERR '$f, $l, $pn,$pc,$res = ' . "$first,$last,$pageno,$pagescnt,$resultcnt\n";

	for (my ($i, $skip) = ($first, $first * $maxperpage); 
			$i <= $last; 
			$i++, $skip += $maxperpage) {
		next if $i < 0 or $i > $pagescnt;
		if ($i == $pageno) {
			print "[<strong>" . ($i + 1) . "</strong>]\n"
		}
		else {
			print "[<a href=\"" .  $hrefs{'search'} . $searchfor . "&amp;skip=" .
				$skip . "&amp;searchtype=d\">";
			
			if ($i == $first) {
				print "&lt;&lt;";
			} 
			elsif ($i == $last) {
				print "&gt;&gt;";
			}
			else {
				print ($i + 1);
			}
			
			print "</a>]\n";
		}
	}
	print "</center>\n";
}
	
sub SearchRegisteredDocumentation {
	my $searchfor  = shift;
	my $startwith  = shift;
	my $maxperpage = shift;
	my @searchargs = ("/usr/bin/search++");
	my $resultcnt  = undef;
	my $desc       = '';

	if (not -x $searchargs[0]) {
		print "<strong>Error:</strong> Can't find <em>search++</em> program.\n";
		print "<br>Please install <a href=\"http://packages.debian.org/swish%2b%2b\">"
			. "swish++</a> package.\n";
		return 1 ;
	}
	
	if (not -r $dwww_swish_index) {
		print "<strong>Error:</strong> Can't find generated index file\n";
		print "<br>Please check if <a href=\"" . $hrefs{'runman'} . "dwww-index%2b%2b/8\">"
			. "dwww-index++(8)</a> has been run.\n";
		return 1;
	}
	
	if (defined $startwith and defined $maxperpage) {
		$startwith = $maxperpage * int ($startwith/$maxperpage);
	}

	push(@searchargs, "--config-file=$dwww_swish_conf");
	push(@searchargs, "--index-file=$dwww_swish_index");
	push(@searchargs, "--skip-results=$startwith") if defined ($startwith);
	push(@searchargs, ("-m", "$maxperpage")) if defined ($maxperpage);
	push(@searchargs, "\"$searchfor\"");

	# Swish++ WWW module
	use lib '/usr/lib/swish++';
	my $use_www = eval "require WWW";

	
	open (SEARCH, "@searchargs |")
		or die "can't open search++: $!\n";

	print "Search results: " . &HTMLEncode($searchfor) . "\n";
	print "<dl>\n";

	while (<SEARCH>) {
		if (/^# ignored: /) {
			print "Following words were ignored: " . &HTMLEncode($') . "<br>\n";
			next;
		}
		if (/^# not found: /) {
			print "Following words weren't found: " . &HTMLEncode ($') . "<br>\n";
			next;
		}
		if (/^# results: /) {
			$resultcnt = $';
			# print "Result count: $'<br>\n";
			&PrintRegDocsPages($searchfor, $startwith, $maxperpage, $resultcnt);
			next;
		}
		next if (/^#/);

		my($rank, $file, $size, $title ) = split( /__--__/, $_, 4);
		print "<dt><a href=\"" . $hrefs{'file'} . &URLEncode($file) 
		       . "\">" . &HTMLEncode("$title") . "</a> <em>($rank%)</em></dt>\n";
		
		if ($use_www and -r "$file") {
			$desc = &WWW::extract_description("$file");
			&WWW::hyperlink($desc);
		}
		$desc = &HTMLEncode($title) if $desc eq '';
		
		print "<dd>" . $desc . "</dd>\n";
	}
	print "</dl>\n";

	&PrintRegDocsPages($searchfor, $startwith, $maxperpage, $resultcnt);

	close SEARCH;

	return $resultcnt;
	
}
