New CGI script for browsing installed software and documentation.
[sw-tools] / perl / SWMan.pm
diff --git a/perl/SWMan.pm b/perl/SWMan.pm
new file mode 100644 (file)
index 0000000..23f0104
--- /dev/null
@@ -0,0 +1,326 @@
+# -*-perl-*-
+#
+# $Id: SWMan.pm,v 1.1 1999/07/30 18:46:37 mdw Exp $
+#
+# Display and other fiddling of manual pages
+#
+# (c) 1999 EBI
+#
+
+#----- Licensing notice -----------------------------------------------------
+#
+# This file is part of sw-tools.
+#
+# sw-tools 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.
+# 
+# sw-tools 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 sw-tools; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+#----- Revision history -----------------------------------------------------
+#
+# $Log: SWMan.pm,v $
+# Revision 1.1  1999/07/30 18:46:37  mdw
+# New CGI script for browsing installed software and documentation.
+#
+
+#----- Package preamble -----------------------------------------------------
+
+package SWMan;
+
+use IO;
+use POSIX;
+use DirHandle;
+use Exporter;
+
+use SWConfig;
+use SWCGI qw(:DEFAULT :layout);
+
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(subst check);
+
+#----- Useful functions -----------------------------------------------------
+
+%mandb = ();
+
+# --- @mans(SECTION)@ ---
+#
+# Returns a reference to a list of manual pages in the given section.
+
+sub mans($) {
+  my ($sec) = @_;
+  $mandb{$sec} and return $mandb{sec};
+
+  my $d = DirHandle->new("$C{prefix}/man/man$sec") or return undef;
+  my @f;
+  while (my $f = $d->read()) {
+    push(@f, $f);
+  }
+  $mandb{$sec} = \@f;
+  return \@f;
+}
+
+# --- @check(NAME, SECTION)@ ---
+#
+# See whether there's a manpage called NAME with section SECTION.
+
+sub check($$) {
+  my $pre = "$C{prefix}/man/man";
+  my ($man, $sec) = @_;
+  my $f;
+
+  # --- Quick check for obvious things ---
+
+  my ($base) = ($sec =~ /^(\d+)/);
+  $f = "$pre$base/$man.$sec";
+  -r $f and return $f; $f .= ".gz"; -r $f and return $f;
+
+  # --- Snarf the appropriate filename list ---
+
+  my $fs = mans($base) or return undef;
+  foreach my $f (@$fs) {
+    $f =~ /^$man\.$sec\w+(\.gz)?$/ and return "$C{prefix}/man/man$base/$f";
+  }
+  return undef;
+}
+
+# --- @subst(STRING, NAME, SECTION)@ ---
+#
+# If NAME(SECTION) is a manual page, return the STRING appropriately wrapped
+# in an anchor element; otherwise return it unmolested.
+
+sub subst($$$) {
+  my ($s, $n, $sec) = @_;
+  check($n, $sec) and
+    return "<a href=\"$ref?act=man&man=$n&sec=$sec\">$s</a>";
+  return "$s";
+}
+
+# --- @sections()@ ---
+#
+# Return a list of manual sections.
+
+@sectionlist = ();
+
+sub sections() {
+  return @sectionlist if @sectionlist;
+  my @s = ();
+  my $d = DirHandle->new("$C{prefix}/man") or
+    barf("couldn't open man directory: $!");
+  while ($f = $d->read()) {
+    next if $f !~ /^man/ || !-d "$C{prefix}/man/$f";
+    push(@s, $');
+  }
+  return (@sectionlist = sort(@s));
+}
+
+#----- Display a quick section index ----------------------------------------
+
+sub quickie {
+  print "Quick section index:\n";
+  foreach $s (sections()) {
+    print "<a href=\"$ref?act=man&sec=$s\">$s</a>\n";
+  }
+}
+
+#----- Display indices for manual sections ----------------------------------
+
+sub dosection($) {
+  my ($sec) = @_;
+  my @m = ();
+
+  barf("illegal section `$sec'") if $sec =~ m:/:;
+
+  # --- Snarf the list of manual pages in this section ---
+
+  {
+    my $d = DirHandle->new("$C{prefix}/man/man$sec") or
+      barf("couldn't read directory `$C{prefix}/man/man$sec': $!");
+    while (my $f = $d->read()) {
+      my ($man, $sec) = split(/\./, $f, 3);
+      push(@m, "$man($sec)") if $sec;
+    }
+  }
+
+  # --- Sort and emit the index ---
+
+  print("<h4>Section $sec</h4>\n<table>");
+
+  {
+    my $col = 0;
+    foreach my $m (sort(@m)) {
+      my ($man, $sec) = $m =~ /^(.*)\((.*)\)$/;
+      $col or print("<tr>\n");
+      print("<td><a href=\"$ref?act=man&man=$man&sec=$sec\">$m</a>\n");
+      $col = ($col + 1) % 5;
+    }
+  }
+
+  print("</table>\n");
+}
+
+sub section {
+  my $sec = $Q{"sec"};
+  header("Index of manual section $sec");
+  quickie(); print "<hr>\n";
+  dosection($sec);
+  print "<hr>\n"; quickie();;
+  footer();
+}
+
+sub index {
+  header("Manual page index");
+  print("<h3>Manual page index</h3>\n");
+  foreach my $s (sections()) { dosection($s); }
+  footer();
+}  
+
+#----- Display a manual page ------------------------------------------------
+
+sub man {
+  my ($man, $sec) = ($Q{"man"}, $Q{"sec"});
+
+  $sec or &index(), return;
+  $man or &section(), return;
+
+  my $file = check($man, $sec) or
+    barf("no manual page $man($sec)");
+  barf("illegal filename `$file'") if $file =~ m:\./:;
+
+  # --- Read the manual page ---
+
+  my $p = IO::Pipe->new();
+  my $kid = fork();
+  defined($kid) or barf("fork failed: $!");
+  if ($kid == 0) {
+    $p->writer();
+    dup2($p->fileno(), 1);
+    chdir("$C{prefix}/man");
+    if ($file =~ /\.gz$/) {
+      $pp = IO::Pipe->new;
+      $kkid = fork();
+      defined($kid) or exit(127);
+      if ($kkid == 0) {
+       $pp->writer();
+       dup2($pp->fileno, 1);
+       exec("gzip", "-dc", $file);
+       exit(127);
+      }
+      exec("nroff", "-man");
+    } else {
+      exec("nroff", "-man", $file);
+    }
+    exit(127);
+  }
+  $p->reader();
+
+  # --- Spit out the manual page now ---
+
+  header("Manual page $Q{man}($Q{sec})");
+  quickie(); print "<hr>\n";
+  print "<pre>\n";
+  while (my $line = $p->getline()) {
+    chomp $line;
+
+    # --- Grind through the line turning it into HTML ---
+
+    {
+      my $state = "";
+      my $l = "";
+
+      for (my $i = 0; $i < length($line); $i++) {
+       my $ch = substr($line, $i, 1);
+       my $nstate = "";
+
+       # --- Sort out overstriking ---
+
+       if (substr($line, $i + 1, 1) eq "\b") {
+         my ($italic, $bold) = (0, 0);
+         $ch eq "_" and $italic = 1;
+         $ch eq substr($line, $i + 2, 1) and $bold = 1;
+         $ch = substr($line, $i + 2, 1);
+         while (substr($line, $i + 1, 1) eq "\b") { $i += 2; }
+         if ($italic && $bold) {
+           $nstate = $state ? $state : "b";
+         } elsif ($italic) {
+           $nstate = "i";
+         } elsif ($bold) {
+           $nstate = "b";
+         }
+       }
+       $state ne $nstate and
+         $l .= ($state && "</$state>") . ($nstate && "<$nstate>");
+       $state = $nstate;
+
+       # --- Translate the character if it's magical ---
+
+       $ch eq "&" and $ch = "&amp;";
+       $ch eq "<" and $ch = "&lt;";
+       $ch eq ">" and $ch = "&gt;";
+       $l .= $ch;
+      }
+      $state and $l .= "</$state>";
+
+      # --- Now find manual references in there ---
+      #
+      # I don't use /x regexps very often, but I think this is a good excuse.
+
+      $l =~ s! ((?:\<[bi]\>)*)         # Leading highlighting tags
+              ([-_.\w]+)               # Various plausible manual name chars
+              ((?:\</[bi]\>)*          # Closing highlighting tags
+               (?:\<[bi]\>)*           # And opening ones again
+               \(                      # An open parenthesis
+               (?:\<[bi]\>)*)          # More opening highlights
+               (\d+\w*)                # The section number
+               ((?:\</[bi]\>)*         # Close highlights
+                \)                     # Close parens
+                 (?:\</[bi]\>)*)       # Finally more closing tags
+            ! subst($&, $2, $4) !egx;
+
+      # --- And email and hypertext references too ---
+
+      $l =~ s! ((?:\<[bi]\>)*)         # Leading highlighting
+              ((?:http|ftp)            # A protocol name
+               ://                     # The important and obvious bit
+               [^]&)\s]+               # Most characters are allowed
+               [^]&).,\s\'\"])         # Don't end on punctuation
+              ((?:\</[bi]\>)*)         # Closing tags, optional
+            !<a href="$2">$&</a>!gx;
+
+      $l =~ s! ((?:\<[bi]\>)*)
+               ( [^\s()&;{}<>,.\`\"] [^\s()&;{}<>\`\"]* \@
+                 [^\s()&;{}<>\'\"]* [^\s()&;{}<>.,\'\"])
+               ((?:\</[bi]\>)*)
+             !<a href="mailto:$2">$&</a>!gx;
+
+      # --- Done! ---
+
+      print $l, "\n";
+    }
+  }
+
+  # --- Done all of that ---
+
+  print "</pre>\n";
+  $p->close();
+  waitpid($kid, 0);
+  barf("nroff failed (exit status $?)") if $?;
+  print "<hr>\n"; quickie();;
+  footer();
+}
+
+#----- Register actions -----------------------------------------------------
+
+$main::ACT{"man"} = \&man;
+
+#----- That's all, folks ----------------------------------------------------
+
+1;