# ManViewer - A web-enabled Manual Reader for Linux and Irix

# Copyright (C) 2000 Steve Horsburgh, shorsburgh@horsburgh.com.

# 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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

    print &PrintHeader();
    &ReadParse();

    $in{'action'}  =~ s/[^A-Za-z0-9_ -]//g;
    $in{'section'} =~ s/[^A-Za-z0-9_ \+-]//g;
    $in{'search'}  =~ s/[^A-Za-z0-9_: \+-]//g;
    $in{'s'}       =~ s/[^A-Za-z0-9_ \+-]//g;

    &CreateDB() if $in{'action'} eq 'createdb';
    &CreateDB() unless -r "$CGI/allcommands";

    open(F, "<$CGI/allcommands");
    while (<F>) {
      chomp;
      ($arg1,$desc) = split (/\s+\-\s+/,$_,2);
      $desc = &Encode($desc);
      ($names,$sec) = $arg1 =~ /^(.*)\s+\((.*)\)$/;
      undef @name;
      @name = split(/\s*,\s*/,$names);
      foreach $n (@name) {
        $commands{$sec}{$n}{'desc'} = $desc;
        $commands{$sec}{$n}{'base'} = $name[0];
      }
    }
    close(F);

    &Header();
    &Search()         if $in{'action'} =~ /find/i;
    &DisplaySection() if $in{'section'};
    &DisplayCmd()     if $in{'cmd'};
    &DisplayTOC();
    exit;

sub DisplayTOC {
    my $title = $IRIX ? "IRIX" : "LINUX";
    print <<ETOC;
    <p><font size=5 color="#666684">&nbsp;Welcome to the Manpage Viewer for $title.</font></p>
    <p style="margin-left:14pt"><font size=4>Using this utility you can browse 
     and search the <em>man</em> pages.<br>
     Choose a section to browse from the list at the left, or 
     enter a keyword or phrase in the search box.</font></p>
    <p style="margin-left:14pt">If you want to search just the command
     names only, check the 'Command Only' check box, otherwise the command names 
     <strong>and</strong> descriptions will be searched.
    </p>
    <p style="margin-left:14pt">
     Note: this viewer assumes the $title 'whatis' database is up-to-date. 
     To ensure this, issue the 'makewhatis' command as root and then click 
     the 'Update Database' link at the left.<br>
     &nbsp;
    </p>
ETOC
    &Footer();
}

sub DisplayCmd {
    my($file,@files,$output,$cmd,$f);
    my $myurl = &MyBaseUrl();
    my $sec = substr($in{'s'},0,1);

    @files = `$MAN_W $sec $in{'cmd'}`;
    if (($files[0] eq "") or ($files[0] =~ /^No /i)) {
      undef @files;
      @files = `$MAN_W $sec \L$in{'cmd'}\E`;
    }
    if (($files[0] eq "") or ($files[0] =~ /^No /i)) {
      undef @files;
      @files = `$MAN_W $in{'cmd'}`;
    }

    foreach (@files) {
      chomp;
      $f = (split /\s+/,$_)[0];
      if ( -r "$f" ) { $file = $f; last; }
    }

    &Error("Cannot find man page for ".$in{'cmd'}."(".$sec.")") unless $file;

    if ($IRIX) {
      $cmd = $MAN2HTML." ".$file;
    } else {
      if ($file =~ /\.gz$/) {
        $cmd = $ZCAT." ".$file."|".$MAN2HTML." -H ".$ENV{'HTTP_HOST'}." -M ".$myurl;
      } else {
        $cmd = $MAN2HTML." -H ".$ENV{'HTTP_HOST'}." -M ".$myurl." ".$file;
      }
    }
    $output = `$cmd`;

    $output =~ s/\007//g;
    $output =~ s/<A HREF=.?file:[^>]+>//ig;
    $output =~ s/^\s*Content-type: text\/html\s*$//m;

    if ($IRIX) {
      $output =~ s/infosrch.cgi\?cmd=getdoc\&db=man\&fname=([^\%]+)\%20([^\"]+)/$myurl\?s=$1\&cmd=$2/ig;
    } else {
      $output =~ s/$myurl\?([^\+]+)\+([^>]+)/$myurl\?s=$1\&cmd=$2/ig;
    }

    print $output;
    &Footer();
    exit;
}

sub Search {
    my($sec, $n);
    foreach $sec (sort keys(%commands)) {
      foreach $n (sort keys(%{$commands{$sec}})) {
        delete $commands{$sec}{$n} unless (($n =~ /$in{'search'}/i) or 
                                           (!$in{'cmdonly'} and
                                            ($commands{$sec}{$n}{'desc'} =~ /$in{'search'}/i)
                                            )
                                           );
      }
    }
    &Display();
}

sub DisplaySection {
    my($sec);
    foreach $sec (keys(%commands)) {
      delete $commands{$sec} unless $sec eq $in{'section'};
    }
    &Display();
} 

sub Display {
      my($sec, $output, $lc, $c, $n, $nn, $m, $bgcolor);
      print <<EOD;
<table border=0 width="100%">
EOD

    my $foundsomething = 0;
    foreach $sec (sort keys(%commands)) {
      next unless keys(%{$commands{$sec}});
      $foundsomething = 1;
      $output = "";
      $lc = $c = 0;
      print <<EOF2;
 <tr>
  <td colspan=6 bgcolor="#000042">
   <font size=4 color="#ffffff"><strong><a name="$sec">Section $sec</a></strong></font>
  </td>
 </tr>
EOF2
      foreach $n (sort keys(%{$commands{$sec}})) {
        $nn = $commands{$sec}{$n}{'base'};
        $m = &Encode($nn); $m =~ s/ /\+/g;
        $bgcolor = "#ffffff";
        $bgcolor = "#e7e7e7" if $lc;
        $output .= "<tr valign=\"top\">" if $c++ == 0;
        $output .= "<td bgcolor=\"".$bgcolor."\"><font size=2><a ".
                   "href=\"".&MyBaseUrl()."?".
                   "s=".$sec."\&cmd=".$m."\">".$n."</a></font></td>".
                   "<td bgcolor=\"".$bgcolor."\"><font size=2>".
                   $commands{$sec}{$n}{'desc'}."</font></td>";
        if ($c > 1) {
          $output .= "</tr>\n";
          $c = 0;
          $lc++;
          $lc = 0 if $lc > 1;
        }
      }
      print $output;
    }
    print <<EOF3;
 </table>
EOF3

    print <<EOE unless $foundsomething;
    <p><font size=5 color="#666684"><strong>Oops!</strong></p>
    <p><font size=4><strong>Nothing was found that matched your search criteria.</strong></font></p>
    <p>Try another search keyword or shorter phrase.</p>
    <p>You can also browse sections by choosing a section number and clicking the 'Go' button.</p>
    <p>&nbsp;</p>
EOE
    &Footer();
    exit;
}

sub Encode {
  my $v = shift;
  $v =~ s/\&/\&amp\;/g;
  $v =~ s/>/\&gt\;/g;
  $v =~ s/</\&lt\;/g;
  return $v;
}

sub CreateDB {
    local(*F,*G);
    my($file, $line);

    open(G, ">$CGI/allcommands") || &Error("Unable to create database.");

    foreach $file (split(/:/,$MANPATH)) {
      next unless $file;
      next unless -r "$file/whatis";
      open(F, "<$file/whatis");
      foreach $line (<F>) {
        next unless $line =~ "-";
        print G $line;
      }
      close(F);
    }
    close(G);
}

sub Header {
    my($sec, $selected);
    my $myurl = &MyBaseUrl();
    my $title = $IRIX ? "Irix" : "Linux";

    my $toc = "<form method=get action=".$myurl.">".
           "<input type=hidden name=action value=Go>".
           "&nbsp;Browse Section:<br>".
           "&nbsp;<select name=section>";

    my $csec = $in{'section'} || $in{'s'} || 1;

    foreach $sec (sort keys(%commands)) {
      $selected = "";
      $selected = "selected" if $sec eq $csec;
      $toc .= "<option $selected value=\"".&Encode($sec)."\">$sec</option>";
    }

    $toc .= "</select> <input type=image src=\"".$IMGDIR."/mv_go.gif\" alt=\"Browse\" border=0></form>\n";
    
    my $searchbx = "<form method=get action=".$myurl.">".
                "<input type=hidden name=action value=\"Find\">".
                "&nbsp;Search For:<br>".
                "&nbsp;<input type=text name=search size=10 value=\"".$in{'search'}."\"> ".
                "<input type=image src=\"".$IMGDIR."/mv_find.gif\" border=0 alt=\"Search\"><br>".
                "<input type=checkbox name=cmdonly value=1><font size=2>Command Only</font>".
                "</form>\n";
    
    my $createdb = "<a href=\"".$myurl."?action=createdb\"><font size=2 color=\"#ffffff\">Update Database</font></a>";

    print <<EOH;
<html>
<head>
 <title>$title Manual Reader</title>
 <meta name=Author content="Steve Horsburgh">
</head>
<body bgcolor="#ffffff" style="margins:0" marginwidth=0 marginheight=0 topmargin=0 leftmargin=0>
<table border=0 cellspacing=0 cellpadding=0 width="100%">
 <tr>
  <td background="$IMGDIR/mv_h_img.gif" colspan=3 height=9><img src="$IMGDIR/mv_ul_img.gif" width=9 height=9 border=0 alt=""></td>
  <td width=9 height=9><img src="$IMGDIR/mv_ur_img.gif" width=9 height=9 border=0 alt=""></td>
 </tr>
 <tr valign=top>
  <td background="$IMGDIR/mv_v_img.gif" rowspan=4 width=9><img src="$IMGDIR/mv_dot.gif" width=9 height=9 border=0 alt=""></td>
  <td background="$IMGDIR/mv_lines.gif" bgcolor="#000042" width=100><a href="$myurl"><img src="$IMGDIR/mv_logo.gif" border=0 width=76 height=45 alt="Home"></a></td>
  <td background="$IMGDIR/mv_lines.gif" bgcolor="#000042" align=right><img src="$IMGDIR/mv_text.gif" height=45 width=346 border=0 alt="ManPage Viewer"></td>
  <td background="$IMGDIR/mv_vr_img.gif" rowspan=4 width=9><img src="$IMGDIR/mv_dot.gif" width=9 height=9 border=0 alt=""></td>
 </tr>
 <tr valign=top>
  <td bgcolor="#606084" align=right colspan=2><table border=0 cellspacing=0 cellpadding=0><tr><td bgcolor="#ffff00"><font size=3 color="#660000"><strong>&nbsp;Optimized for&nbsp;</strong></font></td><td bgcolor="#ffffff"><font size=3 color="#000000"><strong>&nbsp;$title&nbsp;</strong></font></td></tr></table></td>
 </tr>
 <tr valign=top>
   <td bgcolor="#606084" nowrap><font size=2 color="#ffffff">$searchbx&nbsp;<b>OR</b>&nbsp;$toc<br>$createdb</font></td>
   <td style="border:ridge 3pt" width="100%">
EOH
}

sub Footer {
    print <<EOF;
  </td>
 </tr>
 <tr valign=bottom>
  <td colspan=2 align="right" background="$IMGDIR/mv_lines.gif"><font size=1 color="#ffff00">Designed by </font><a href="http://www.horsburgh.com/" style="text-decoration:none"><font size=1 color="#ffffff">Horsburgh.com</font></a>&nbsp;
  </td>
 </tr>
 <tr>
  <td background="$IMGDIR/mv_hl_img.gif" colspan=3 height=9><img src="$IMGDIR/mv_ll_img.gif" width=9 height=9 border=0 alt=""></td>
  <td width=9 height=9><img src="$IMGDIR/mv_lr_img.gif" width=9 height=9 border=0 alt=""></td>
 </tr>
</table>
</body>
</html>
EOF
}

sub Error {
    my $msg = shift;
    print "<h2>ERROR</h2>\n<p><strong>".$msg."</strong></p>\n";
    &Footer();
    exit;
}

sub ReadParse {
  local (*in) = shift if @_;    # CGI input
  local ($meth, $errflag, $cmdflag);
	
  $meth = $ENV{'REQUEST_METHOD'};
  
  if (!defined $meth || $meth eq '' || $meth eq 'GET') {
    local ($key, $val, $i);
	
    if (!defined $meth || $meth eq '') {
      $in = $ENV{'QUERY_STRING'};
      $cmdflag = 1;
    } elsif($meth eq 'GET') {
      $in = $ENV{'QUERY_STRING'};
    } else {
      $errflag = 1;
    }

    @in = split(/[&;]/,$in); 
    push(@in, @ARGV) if $cmdflag;

    foreach $i (0 .. $#in) {
      $in[$i] =~ s/\+/ /g;

      ($key, $val) = split(/=/,$in[$i],2);

      $key =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;
      $val =~ s/%([A-Fa-f0-9]{2})/pack("c",hex($1))/ge;

      $in{$key} .= "\0" if (defined($in{$key}));
      $in{$key} .= $val;
    }

  } else {
    $errflag = 1;
  }

  return ($errflag ? undef : scalar(@in)); 
}

sub PrintHeader {
  return "Content-type: text/html\n\n";
}

# MyBaseUrl
# Returns the base URL (without the host part) to the script (i.e., no extra path or query string)
sub MyBaseUrl {
  local ($ret);
#  $ret = 'http://' . $ENV{'HTTP_HOST'} . $ENV{'SCRIPT_NAME'};
  $ret = $ENV{'SCRIPT_NAME'};
  return $ret;
}


