$version = "1.07+ma1";
$manServerUrl = "<A HREF=\"http://www.squarebox.co.uk/users/rolf/download/manServer.shtml\">manServer $version</A>";
use Socket;
$ENV{'PATH'} = "/bin:/usr/bin";
initialise();
$request = shift @ARGV;
$root = "";
$cgiMode = 0;
$bodyTag = "BODY bgcolor=#F0F0F0 text=#000000 link=#0000ff vlink=#C000C0 alink=#ff0000";
if ($ENV{'GATEWAY_INTERFACE'} ne "")
{
*OUT = *STDOUT;
open(LOG, ">>/tmp/manServer.log");
chmod(0666, '/tmp/manServer.log');
$root = $ENV{'SCRIPT_NAME'};
$url = $ENV{'PATH_INFO'};
if ($ENV{'REQUEST_METHOD'} eq "POST")
{ $args = <STDIN>; chop $args; }
else
{ $args = $ENV{'QUERY_STRING'}; }
$url .= "?".$args if ($args);
$cgiMode = 1;
$date = &fmtTime(time);
$remoteHost = $ENV{'REMOTE_HOST'} || $ENV{'REMOTE_ADDR'};
$referer = $ENV{'HTTP_REFERER'};
$userAgent = $ENV{'HTTP_USER_AGENT'};
print LOG "$date\t$remoteHost\t$url\t$referer\t$userAgent\n";
processRequest($url);
}
elsif ($request eq "-s" || $request eq "")
{
*LOG = *STDERR;
startServer();
}
else
{
$cmdLineMode = 1;
if ($request =~ m/^-d(\d)/)
{
$debug = $1;
$request = shift @ARGV;
}
*OUT = *STDOUT;
*LOG = *STDERR;
$file = findPage($request);
man2html($file);
}
exit(0);
sub startServer
{
($port) = @ARGV;
$port = 8888 unless $port;
$sockaddr = 'S n a4 x8';
($name, $aliases, $proto) = getprotobyname('tcp');
($name, $aliases, $port) = getservbyname($port, 'tcp')
unless $port =~ /^\d+$/;
while(1)
{
$this = pack($sockaddr, AF_INET, $port, "\0\0\0\0");
select(NS); $| = 1; select(stdout);
socket(S, PF_INET, SOCK_STREAM, $proto) || die "socket: $!";
if (bind(S, $this))
{
last;
}
else
{
print STDERR "Failed to bind to port $port: $!\n";
++$port;
}
}
listen(S, 5) || die "connect: $!";
select(S); $| = 1; select(stdout);
while(1)
{
print LOG "Waiting for connection on port $port\n";
($addr = accept(NS,S)) || die $!;
($af,$rport,$inetaddr) = unpack($sockaddr,$addr);
@inetaddr = unpack('C4',$inetaddr);
print LOG "Got connection from ", join(".",@inetaddr), "\n";
while (<NS>)
{
if (m/^GET (\S+)/) { $url = $1; }
last if (m/^\s*$/);
}
*OUT = *NS;
processRequest($url);
close NS ;
}
}
sub processRequest
{
$url = $_[0];
print LOG "Request = $url, root = $root\n";
if ( ($url =~ m/^([^?]*)\?(.*)$/) || ($url =~ m/^([^&]*)&(.*)$/) )
{
$request = $1;
$args = $2;
}
else
{
$request = $url;
$args = "";
}
@params = split(/[=&]/, $args);
for ($i=0; $i<=$ {
$params[$i] =~ tr/+/ /;
$params[$i] =~ s/%([0-9A-Fa-f][0-9A-Fa-f])/pack("C",hex($1))/eg;
}
%params = @params;
$request = $params{'q'} if ($params{'q'});
$searchType = $params{'t'};
$debug = $params{'d'};
$processed = 0;
$file = "";
if ($searchType)
{
print OUT "HTTP/1.0 200 Ok\n" unless ($cgiMode);
print OUT "Content-type: text/html\n\n";
print OUT "<H1>Searching not yet implemented</H1>\n";
print LOG "Searching not implemented\n";
$processed = 1;
}
elsif ($request eq "/" || $request eq "")
{
print OUT "HTTP/1.0 200 Ok\n" unless ($cgiMode);
print OUT "Content-type: text/html\n\n";
print LOG "Home page\n";
homePage();
$processed = 1;
}
elsif ($request =~ m,^/.*/$,)
{
print OUT "HTTP/1.0 200 Ok\n" unless ($cgiMode);
print OUT "Content-type: text/html\n\n";
print LOG "List directory\n";
listDir($request);
$processed = 1;
}
elsif (-f $request || -f "$request.gz" || -f "$request.bz2")
{
foreach $md (@manpath)
{
$dir = $md;
if (substr($request,0,length($dir)) eq $dir)
{
print OUT "HTTP/1.0 200 Ok\n" unless ($cgiMode);
print OUT "Content-type: text/html\n\n";
man2html($request);
$processed = 1;
last;
}
}
}
else
{
$file = findPage($request);
if (@multipleMatches)
{
print OUT "HTTP/1.0 200 Ok\n" unless ($cgiMode);
print OUT "Content-type: text/html\n\n";
print LOG "Multiple matches\n";
printMatches();
$processed = 1;
}
elsif ($file)
{
print OUT "HTTP/1.0 301 Redirected\n" unless ($cgiMode);
$file .= "&d=$debug" if ($debug);
print OUT "Location: $root$file\n\n";
print LOG "Redirect to $root$file\n";
$processed = 1;
}
}
unless ($processed)
{
print OUT "HTTP/1.0 404 Not Found\n" unless ($cgiMode);
print OUT "Content-type: text/html\n\n";
print OUT "<HTML><HEAD>\n<TITLE>Not Found</TITLE>\n<$bodyTag>\n";
print OUT "<CENTER><H1><HR>Not Found<HR></H1></CENTER>\nFailed to find man page /$request\n";
print OUT "<P><HR><P><A HREF=\"$root/\">Main Index</A>\n</HTML>\n";
print STDERR "Failed to find /$request\n" unless ($cgiMode);
}
}
sub homePage
{
print OUT "<HTML><HEAD><TITLE>Manual Pages - Main Index</TITLE>
</HEAD><$bodyTag><CENTER><H1><HR><I>Manual Reference Pages</I> - Main Index<HR></H1></CENTER>
<FORM ACTION=\"$root/\" METHOD=get>\n";
$uname = `uname -s -r`;
if (! $?)
{
$hostname = `hostname`;
print OUT "<B>$uname pages on $hostname</B><P>\n";
}
print OUT "Command name: <INPUT name=q size=20> <INPUT type=submit value=\"Show Page\"> </FORM><P>\n";
loadManDirs();
foreach $dir (@mandirs)
{
($section) = ($dir =~ m/man([0-9A-Za-z]+)$/);
print OUT "<A HREF=\"$root$dir/\">$dir" ;
print OUT "- <I>$sectionName{$section}</I>" if ($sectionName{$section});
print OUT "</A><BR>\n";
}
print OUT "<P><HR><P><FONT SIZE=-1>Generated by $manServerUrl from local unix man pages.</FONT>\n</BODY></HTML>\n";
}
sub listDir
{
foreach $md (@manpath)
{
$dir = $md;
if (substr($request,0,length($dir)) eq $dir)
{
$request =~ s,/$,,;
($section) = ($request =~ m/man([0-9A-Za-z]+)$/);
$sectionName = $sectionName{$section};
$sectionName = "Manual Reference Pages" unless ($sectionName);
print OUT "<HTML><HEAD><TITLE>Contents of $request</TITLE></HEAD>\n<$bodyTag>\n";
print OUT "<CENTER><H1><HR><NOBR><I>$sectionName</I></NOBR> - <NOBR>Index of $request</NOBR><HR></H1></CENTER>\n";
print OUT "<FORM ACTION=\"$root/\" METHOD=get>\n";
print OUT "Command name: <INPUT name=q size=20> <INPUT type=submit value=\"Show Page\"> </FORM><P>\n";
if (opendir(DIR, $request))
{
@files = sort readdir DIR;
foreach $f (@files)
{
next if ($f eq "." || $f eq ".." || $f !~ m/\./);
$f =~ s/\.(gz|bz2)$//;
print OUT "<A HREF=\"$root$request/$f\">$f</A> \n";
}
closedir DIR;
}
print OUT "<P><A HREF=\"$root/\">Main Index</A>\n</HTML>\n";
print OUT "<P><HR><P><FONT SIZE=-1>Generated by $manServerUrl from local unix man pages.</FONT>\n</BODY></HTML>\n";
return;
}
}
print OUT "<H1>Directory $request not known</H1>\n";
}
sub printMatches
{
print OUT "<HTML><HEAD><TITLE>Ambiguous Request '$request'</TITLE></HEAD>\n<$bodyTag>\n";
print OUT "<CENTER><H1><HR>Ambiguous Request '$request'<HR></H1></CENTER>\nPlease select one of the following pages:<P><BLOCKQUOTE>";
foreach $f (@multipleMatches)
{
print OUT "<A HREF=\"$root$f\">$f</A><BR>\n";
}
print OUT "</BLOCKQUOTE><HR><P><A HREF=\"$root/\">Main Index</A>\n</HTML>\n";
}
sub man2html
{
$file = $_[0];
$srcfile = $file;
$zfile = $file;
if (! -f $file)
{
if (-f "$file.gz")
{
$zfile = "$file.gz";
$zcat = "/usr/bin/zcat";
$zcat = "/bin/zcat" unless (-x $zcat);
$srcfile = "$zcat $zfile |";
$srcfile =~ m/^(.*)$/;
$srcfile = $1; }
elsif (-f "$file.bz2")
{
$zfile = "$file.bz2";
$srcfile = "/usr/bin/bzcat $zfile |";
$srcfile =~ m/^(.*)$/;
$srcfile = $1; }
}
print LOG "man2html $file\n";
$foundNroffTag = 0;
loadContents($file);
unless (open(SRC, $srcfile))
{
print OUT "<H1>Failed to open $file</H1>\n";
print STDERR "Failed to open $srcfile\n";
return;
}
($dir,$page,$sect) = ($file =~ m,^(.*)/([^/]+)\.([^.]+)$,);
$troffTable = 0;
%macro = ();
%renamedMacro = ();
%deletedMacro = ();
@indent = ();
@tabstops = ();
$indentLevel = 0;
$prevailingIndent = 6;
$trapLine = 0;
$blockquote = 0;
$noSpace = 0;
$firstSection = 0;
$eqnStart = "";
$eqnEnd = "";
$eqnMode = 0;
%eqndefs = ();
$defaultNm = "";
$title = $file;
$title = "Manual Page - $page($sect)" if ($page && $sect);
$_ = getLine();
if (m/^.so (man.*)$/)
{
man2html("$dir/../$1");
return;
}
$perlPattern = "";
if ($file =~ m/perl/)
{
&loadPerlPages();
$perlPattern = join('|', grep($_ ne $page, keys %perlPages));
}
print OUT "<HTML><HEAD>\n<TITLE>$title</TITLE>\n<$bodyTag><A NAME=top></A>\n";
if ($foundNroffTag)
{
do
{
preProcessLine();
processLine();
}
while(getLine());
endNoFill();
endParagraph();
}
else
{
$sectionName = "Manual Reference Pages";
$sectionNumber = $sect;
$left = "Manual Page";
$right = "Manual Page";
$macroPackage = "(preformatted text)";
$pageName = "$page($sect)";
$saveCurrentLine = $_;
outputPageHead();
$_ = $saveCurrentLine;
print OUT "<PRE>\n";
do
{
print OUT $_;
}
while(getLine());
print OUT "</PRE>\n";
}
outputPageFooter();
}
sub outputPageHead
{
plainOutput( "<CENTER>\n" );
outputLine( "<H1><HR><I>$sectionName - </I><NOBR>$pageName</NOBR><HR></H1>\n" );
plainOutput( "</CENTER>\n" );
}
sub outputPageFooter
{
if ($pageName)
{
unless ($cmdLineMode)
{
plainOutput( "<FORM ACTION=\"$root/\" METHOD=get>\n" );
plainOutput( "Jump to page <INPUT name=q size=12> or go to <A HREF=#top>Top of page</A> | \n" );
plainOutput( "<A HREF=\"$root$dir/\">Section $sectionNumber</A> | \n" );
plainOutput( "<A HREF=\"$root/\">Main Index</A>.\n" );
plainOutput( "<FORM>\n" );
}
endBlockquote();
outputLine("<P><HR>\n<TABLE width=100%><TR> <TD width=33%><I>$left</I></TD> <TD width=33% align=center>$pageName</TD> <TD align=right width=33%><I>$right</I></TD> </TR></TABLE>");
}
plainOutput("<FONT SIZE=-1>Generated by $manServerUrl from $zfile $macroPackage.</FONT>\n</BODY></HTML>\n");
}
sub outputContents
{
print OUT "<A name=contents></A><H3>CONTENTS</H3></A>\n";
blockquote();
for ($id=1; $id<=$ {
$name = $contents[$id];
$pre = "";
$pre = " " if ($name =~ m/^ /);
$pre .= " " if ($name =~ m/^ /);
$name =~ s,^\s+,,;
next if ($name eq "" || $name =~ m,^/,);
unless ($name =~ m/[a-z]/)
{
$name = "\u\L$name";
$name =~ s/ (.)/ \u\1/g;
}
outputLine("$pre<A HREF=#$id>$name</A><BR>\n");
}
endBlockquote();
}
sub loadContents
{
@contents = ();
%contents = ();
open(SRC, $srcfile) || return;
while (<SRC>)
{
preProcessLine();
$foundNroffTag = $foundNroffTag || (m/^\.(\\\"|TH|so) /);
if (m/^\.(S[HShs]) ([A-Z].*)\s*$/)
{
$foundNroffTag = 1;
$c = $1;
$t = $2;
$t =~ s/"//g;
$id = @contents;
if ($c eq "SH" || $c eq "Sh")
{
push(@contents, $t);
}
elsif ($t =~ m/\\f/)
{
$t =~ s/\\f.//g;
push(@contents, " $t");
}
else
{
push(@contents, " $t");
}
$contents{"\U$t"} = $id;
}
}
close SRC;
}
# Preprocess $_
sub preProcessLine
{
# Remove spurious white space to canonicise the input
chop;
$origLine = $_;
s, $,,g;
s,^',.,; # treat non breaking requests as if there was a dot
s,^\.\s*,\.,;
if ($eqnMode == 1)
{
if (m/$eqnEnd/)
{
s,^(.*?)$eqnEnd,&processEqnd($1),e;
$eqnMode = 0;
}
else
{
&processEqns($_);
}
}
if ($eqnStart && $eqnMode==0)
{
s,$eqnStart(.*?)$eqnEnd,&processEqnd($1),ge;
if (m/$eqnStart/)
{
s,$eqnStart(.*)$,&processEqns($1),e;
$eqnMode = 1;
}
}
# XXX Note: multiple levels of escaping aren't handled properly, eg. \\*.. as a macro argument
# should get interpolated as string but ends up with a literal '\' being copied through to output.
s,\\\\\*q,",g; # treat mdoc \\*q as special case
s,\\\\,_DBLSLASH_,g;
s,\\ ,_SPACE_,g;
s,\s*\\".*$,,;
s,\\$,,;
s,\\\*\((..),$vars{$1},ge;
s/\\\*([*'`,^,:~].)/$vars{$1}||"\\*$1"/ge;
s,\\\*(.),$vars{$1},ge;
# Expand special characters for the first time (eg. \(<-
s,\\\((..),$special{$1}||"\\($1",ge;
s,<,<,g;
s,>,>,g;
# Interpolate width and number registers
s,\\w(.)(.*?)\1,&width($2),ge;
s,\\n\((..),&numreg($1),ge;
s,\\n(.),&numreg($1),ge;
}
# Undo slash escaping, normally done at output stage, also in macro defn
sub postProcessLine
{
s,_DBLSLASH_,\\,g;
s,_SPACE_, ,g;
}
# Rewrite the line, expanding escapes such as font styles, and output it.
# The line may be a plain text troff line, or it might be the expanded output of a
# macro in which case some HTML tags may already have been inserted into the text.
sub outputLine
{
$_ = $_[0];
print OUT "<!-- Output: \"$_\" -->\n" if ($debug>1);
if ($needBreak)
{
plainOutput("<!-- Need break --><BR>\n");
lineBreak();
}
if ($textSinceBreak && !$noFill && $_ =~ m/^\s/)
{
plainOutput("<BR>\n");
lineBreak();
}
s,\\&\.,.,g; # \&. often used to escape dot at start of line
s,\\\.,.,g;
s,\\\^,,g;
s,\\\|,,g;
s,\\c,,g;
s,\\0, ,g;
s,\\t,\t,g;
s,\\%, ,g;
s,\\{,,g;
s,\\},,g;
s,\\$,,g;
s,\\e,\,g;
s,\\([-+_~#[]),\1,g;
# Can't implement local motion tags
s,\\[hv](.).*?\1,,g;
s,\\z,,g;
s,\\(f[^(]|f\(..|u|d|s[-+]?\d),&inlineStyle($1),ge;
if (m/\\o/)
{
s,\\o(.)([aouAOU])"\1,\\o\1\2:\1,g;
s,\\o(.)(.)\\(.)\1,\\o\1\2\3\1,g;
s;\\o(.)([A-Za-z])(['`:,^~])\1;\\o\1\3\2\1;g;
#s,\\o(.)(.*?)\1,"<BLINK>".($vars{$2}||$2)."</BLINK>",ge;
s,\\o(.)(.*?)\1,$vars{$2}||$2,ge;
}
# Bracket building (ignore)
s,\\b(.)(.*?)\1,\2,g;
s,\\`,`,g;
s,\\',',g;
s,',’,g;
s,`,‘,g;
# Expand special characters introduced by eqn
s,\\\((..),$special{$1}||"\\($1",ge;
s,\\\((..),<BLINK>\\($1</BLINK>,g unless (m,^\.,);
# Don't know how to handle other escapes
s,(\\[^&]),<BLINK>\1</BLINK>,g unless (m,^\.,);
postProcessLine();
# Insert links for http, ftp and mailto URLs
# Recognised URLs are sequence of alphanumerics and special chars like / and ~
# but must finish with an alphanumeric rather than punctuation like "."
s,\b(https?://[-\w/~:@.%#+$?=]+[\w/]),<A HREF=\"\1\">\1</A>,g;
s,\b(ftp://[-\w/~:@.%#+$?=]+),<A HREF=\"\1\">\1</A>,g;
s,([-_A-Za-z0-9.]+@[A-Za-z][-_A-Za-z0-9]*\.[-_A-Za-z0-9.]+),<A HREF=\"mailto:\1\">\1</A>,g;
# special case for things like 'perlre' as it's so useful but the
# pod-generated pages aren't very parser friendly...
if ($perlPattern && ! m/<A HREF/i)
{
s,\b($perlPattern)\b,<A HREF=\"$root$perlPages{$1}\">\1</A>,g;
}
# Do this late so \& can be used to suppress conversion of URLs etc.
s,\\&,,g;
# replace tabs with spaces to next multiple of 8
if (m/\t/)
{
$tmp = $_;
$tmp =~ s/<[^>]*>//g;
$tmp =~ s/&[^;]*;/@/g;
@tmp = split(/\t/, $tmp);
$pos = 0;
for ($i=0; $i<=$#tmp; ++$i)
{
$pos += length($tmp[$i]);
$tab[$i] = 0;
$tab[$i] = 8 - $pos%8 unless (@tabstops);
foreach $ts (@tabstops)
{
if ($pos < $ts)
{
$tab[$i] = $ts-$pos;
last;
}
}
$pos += $tab[$i];
}
while (m/\t/)
{
s,\t," " x (shift @tab),e;
}
}
$textSinceBreak = $_ unless ($textSinceBreak);
print OUT $_;
}
# Output a line consisting purely of HTML tags which shouldn't be regarded as
# a troff output line.
sub plainOutput
{
print OUT $_[0];
}
# Output the original line for debugging
sub outputOrigLine
{
print OUT "<!-- $origLine -->\n";
}
# Use this to read the next input line (buffered to implement lookahead)
sub getLine
{
$lookaheadPtr = 0;
if (@lookahead)
{
$_ = shift @lookahead;
return $_;
}
$_ = <SRC>;
}
# Look ahead to peek at the next input line
sub _lookahead
{
# set lookaheadPtr to 0 to re-read the lines we've looked ahead at
if ($lookaheadPtr>=0 && $lookaheadPtr <= $#lookahead)
{
return $lookahead[$lookaheadPtr++];
}
$lookaheadPtr = -1;
$ll = <SRC>;
push(@lookahead, $ll);
return $ll;
}
# Consume the last line that was returned by lookahead
sub consume
{
--$lookaheadPtr;
if ($lookaheadPtr>=0 && $lookaheadPtr <= $#lookahead)
{
$removed = $lookahead[$lookaheadPtr];
@lookahead = (@lookahead[0..$lookaheadPtr-1],@lookahead[$lookaheadPtr+1..$#lookahead]);
}
else
{
$removed = pop @lookahead;
}
chop $removed;
plainOutput("<!-- Consumed $removed -->\n");
}
# Look ahead skipping comments and other common non-text tags
sub lookahead
{
$ll = _lookahead();
while ($ll =~ m/^\.(\\"|PD|IX|ns)/)
{
$ll = _lookahead();
}
return $ll;
}
sub processLine
{
$doneLine = 1;
s,^\.if t ,,;
s,^\.el ,,;
if ($troffTable)
{
processTable();
}
elsif ($eqnMode == 2)
{
plainOutput("<!-- $_ -->\n");
processEqns($_);
}
elsif (m/^\./)
{
processMacro();
}
else
{
processPlainText();
}
if ($doneLine)
{
if ($trapLine > 0)
{
--$trapLine;
if ($trapLine == 0)
{
&$trapAction;
}
}
}
}
sub processPlainText
{
if ($_ eq "")
{
lineBreak();
plainOutput("<P>\n");
return;
}
s,(\\f[23BI])([A-Z].*?)(\\f.),$1.($contents{"\U$2"}?"<A HREF=#".$contents{"\U$2"}.">$2</A>":$2).$3,ge;
if ($currentSection eq "SEE ALSO" && ! $cmdLineMode)
{
s,(^|\s)([-.A-Za-z_0-9]+)\s?\(([0-9lL][0-9a-zA-Z]*)\),\1<A HREF=\"$root/$2.$3\">$2($3)</A>,g;
}
outputLine("$_\n");
}
sub processMacro
{
outputOrigLine();
@p = grep($_ !~ m/^\s*$/, split(/("[^"]*"|\s+)/) );
grep(s/"//g, @p);
$_ = join(" ", @p);
$p[0] =~ s/^\.//;
$c = $p[0];
$joined = join(" ", @p[1..$ $joined2 = join(" ", @p[2..$ $joined3 = join(" ", @p[3..$
if ($macro{$c}) {
$macro = $macro{$c};
$macro =~ s,\\\$(\d),$p[$1],ge;
foreach $_ (split(/\n/, $macro))
{
$_ .= "\n";
preProcessLine();
processLine();
}
$doneLine = 0;
return;
}
elsif ($renamedMacro{$c})
{
$c = $renamedMacro{$c};
}
if ($c eq "ds") {
$vars{$p[1]} = $joined2;
$doneLine = 0;
}
elsif ($c eq "nr") {
$number{$p[1]} = evalnum($joined2);
$doneLine = 0;
}
elsif ($c eq "ti") {
plainOutput(" ");
}
elsif ($c eq "rm")
{
$macroName = $p[1];
if ($macro{$macroName})
{
delete $macro{$macroName};
}
else
{
$deletedMacro{$macroName} = 1;
}
}
elsif ($c eq "rn")
{
$oldName = $p[1];
$newName = $p[2];
$macro = $macro{$oldName};
if ($macro)
{
if ($newName =~ $reservedMacros && ! $deletedMacro{$newName})
{
plainOutput("<!-- Not overwriting reserved macro '$newName' -->\n");
}
else
{
$macro{$newName} = $macro;
delete $deletedMacro{$newName};
}
delete $macro{$oldName};
}
else
{
plainOutput("<!-- Fake renaming reserved macro '$oldName' -->\n");
$renamedMacro{$newName} = $oldName;
$deletedMacro{$oldName} = 1;
}
}
elsif ($c eq "de" || $c eq "ig") {
$macroName = $p[1];
if ($c eq "ig")
{ $delim = ".$p[1]"; }
else
{ $delim = ".$p[2]"; }
$delim = ".." if ($delim eq ".");
$macro = "";
$_ = getLine();
preProcessLine();
while ($_ ne $delim)
{
postProcessLine();
outputOrigLine();
$macro .= "$_\n";
$_ = getLine();
last if ($_ eq "");
preProcessLine();
}
outputOrigLine();
if ($c eq "de")
{
if ($macroName =~ $reservedMacros && ! $deletedMacro{$macroName})
{
plainOutput("<!-- Not defining reserved macro '$macroName' ! -->\n");
}
else
{
$macro{$macroName} = $macro;
delete $deletedMacro{$macroName};
}
}
}
elsif ($c eq "so") {
plainOutput("<P>[<A HREF=\"$root$dir/../$p[1]\">Include document $p[1]</A>]<P>\n");
}
elsif ($c eq "TH" || $c eq "Dt") {
endParagraph();
$sectionNumber = $p[2];
$sectionName = $sectionName{"\L$sectionNumber"};
$sectionName = "Manual Reference Pages" unless ($sectionName);
$pageName = "$p[1] ($sectionNumber)";
outputPageHead();
if ($c eq "TH")
{
$right = $p[3];
$left = $p[4];
$left = $osver unless ($left);
$macroPackage = "using man macros";
}
else
{
$macroPackage = "using doc macros";
}
}
elsif ($c eq "Nd")
{
outputLine("- $joined\n");
}
elsif ($c eq "SH" || $c eq "SS" || $c eq "Sh" || $c eq "Ss") {
lineBreak();
endNoFill();
endParagraph();
$id = $contents{"\U$joined"};
$currentSection = $joined;
if ($c eq "SH" || $c eq "Sh")
{
endBlockquote();
if ($firstSection++==1) {
outputContents();
}
outputLine( "<A name=$id>\n\n <H3>$joined</H3>\n\n</A>\n" );
blockquote();
}
elsif ($joined =~ m/\\f/)
{
$joined =~ s/\\f.//g;
$id = $contents{"\U$joined"};
outputLine( "<A name=$id>\n<H4><I>$joined</I></H4></A>\n" );
}
else
{
endBlockquote();
outputLine( "<A name=$id>\n\n <H4> $joined</H4>\n</A>\n" );
blockquote();
}
lineBreak();
}
elsif ($c eq "TX" || $c eq "TZ") {
$title = $title{$p[1]};
$title = "Document [$p[1]]" unless ($title);
outputLine( "\\fI$title\\fP$joined2\n" );
}
elsif ($c eq "PD") {
$noSpace = ($p[1] eq "0");
$doneLine = 0;
}
elsif ($c eq "TS") {
unless ($macroPackage =~ /tbl/)
{
if ($macroPackage =~ /eqn/)
{ $macroPackage =~ s/eqn/eqn & tbl/; }
else
{ $macroPackage .= " with tbl support"; }
}
resetStyles();
endNoFill();
$troffTable = 1;
$troffSeparator = "\t";
plainOutput( "<P><BLOCKQUOTE><TABLE bgcolor=#E0E0E0 border=1 cellspacing=0 cellpadding=3>\n" );
}
elsif ($c eq "EQ") {
unless ($macroPackage =~ /eqn/)
{
if ($macroPackage =~ /tbl/)
{ $macroPackage =~ s/tbl/tbl & eqn/; }
else
{ $macroPackage .= " with eqn support"; }
}
$eqnMode = 2;
}
elsif ($c eq "ps") {
plainOutput(&sizeChange($p[1]));
}
elsif ($c eq "ft") {
plainOutput(&fontChange($p[1]));
}
elsif ($c eq "I" || $c eq "B") {
$id = $contents{"\U$joined"};
if ($id && $joined =~ m/^[A-Z]/)
{ $joined = "<A HREF=#$id>$joined</A>"; }
outputLine( "\\f$c$joined\\fP " );
plainOutput("\n") if ($noFill);
}
elsif ($c eq "SM") {
outputLine("\\s-1$joined\\s0 ");
$doneLine = 0 unless ($joined);
}
elsif ($c eq "SB") {
outputLine("\\fB\\s-1$joined\\s0\\fP ");
}
elsif (m/^\.[BI]R (\S+)\s?\(\s?([0-9lL][0-9a-zA-Z]*)\s?\)(.*)$/)
{
for ($i=1; $i<=$ {
$pair = $p[$i]." ".$p[$i+1];
if ($p[$i+1] eq "(")
{
$pair .= $p[$i+2].$p[$i+3];
$i += 2;
}
if ($pair =~ m/^(\S+)\s?\(\s?([0-9lL][0-9a-zA-Z]*)\s?\)(.*)$/)
{
if ($cmdLineMode)
{ outputLine( "\\fB$1\\fR($2)$3\n" ); }
else
{ outputLine( "<A HREF=\"$root/$1.$2\">$1($2)</A>$3\n" ); }
}
else
{ outputLine( "$pair\n" ); }
}
}
elsif ($c eq "BR" || $c eq "BI" || $c eq "IB" ||
$c eq "IR" || $c eq "RI" || $c eq "RB")
{
$f1 = (substr($c ,0,1));
$f2 = (substr($c,1,1));
$id = $contents{"\U$p[1]"};
if ($id && $p[1] =~ m/^[A-Z]/)
{
$p[1] = "<A HREF=#$id>$p[1]</A>";
}
for ($i=1; $i<=$ {
$f = ($i%2 == 1) ? $f1 : $f2;
outputLine("\\f$f$p[$i]");
}
outputLine("\\fP ");
plainOutput("\n") if ($noFill);
}
elsif ($c eq "nf" || $c eq "Bd") {
startNoFill();
}
elsif ($c eq "fi" || $c eq "Ed") {
endNoFill();
}
elsif ($c eq "HP")
{
$indent = evalnum($p[1]);
if ($trapOnBreak)
{
plainOutput("<BR>\n");
}
else
{
$trapOnBreak = 1;
$trapAction = *trapHP;
newParagraph($indent);
plainOutput( "<TD colspan=2>\n" );
$colState = 2;
}
}
elsif ($c eq "IP")
{
$trapOnBreak = 0;
$tag = $p[1];
$indent = evalnum($p[2]);
newParagraph($indent);
outputLine("<TD$width>\n$tag\n</TD><TD>\n");
$colState = 1;
lineBreak();
}
elsif ($c eq "TP")
{
$trapOnBreak = 0;
$trapLine = 1; $doneLine = 0; $trapAction = *trapTP;
$indent = evalnum($p[1]);
$tag = lookahead();
chop $tag;
$i = ($indent ? $indent : $prevailingIndent) ;
$w = width($tag);
if ($w > $i)
{
plainOutput("<!-- Length of tag '$tag' ($w) > indent ($i) -->\n") if ($debug);
newParagraph($indent);
$trapAction = *trapHP;
plainOutput( "<TD colspan=2>\n" );
$colState = 2;
}
else
{
newParagraph($indent);
plainOutput( "<TD$width nowrap>\n" );
$colState = 0;
}
$body = lookahead();
$lookaheadPtr = 0;
if ($body =~ m/^\.[HILP]?P/)
{
chop $body;
plainOutput("<!-- Suppressing TP body due to $body -->\n");
$trapLine = 0;
}
}
elsif ($c eq "LP" || $c eq "PP" || $c eq "P" || $c eq "Pp") {
$trapOnBreak = 0;
$prevailingIndent = 6;
if ($indent[$indentLevel] > 0 && $docListStyle eq "")
{
$line = lookahead();
if ($line =~ m/^\.(TP|IP|HP)/)
{
plainOutput("<!-- suppressed $c before $1 -->\n");
}
elsif ($line =~ m/^\.RS/)
{
plainOutput("<P>\n");
}
else
{
endRow();
$foundTag = "";
$lookaheadPtr = 0;
do
{
$line = lookahead();
if ($line =~ m/^\.(TP|HP|IP|RS)( \d+)?/)
{
$indent = $2;
$indent = $prevailingIndent unless ($2);
if ($indent == $indent[$indentLevel])
{ $foundTag = $1; }
$line = "";
}
}
while ($line ne "" && $line !~ m/^\.(RE|SH|SS|PD)/);
$lookaheadPtr = 0;
if ($foundTag)
{
plainOutput("<!-- Found tag $foundTag -->\n");
plainOutput("<TR><TD colspan=2>\n");
$colState = 2;
}
else
{
plainOutput("<!-- $c ends table -->\n");
setIndent(0);
}
}
}
else
{
plainOutput("<P>\n");
}
lineBreak();
}
elsif ($c eq "br") {
if ($trapOnBreak)
{
$trapOnBreak = 0;
&$trapAction();
}
$needBreak = 1 if ($textSinceBreak);
}
elsif ($c eq "sp") {
lineBreak();
plainOutput("<P>\n");
}
elsif ($c eq "RS") {
if ($indentLevel==0 && $indent[0]==0)
{
blockquote();
}
else
{
$indent = $p[1];
$indent = $prevailingIndent unless ($indent);
if ($indent > $indent[$indentLevel] && !$extraIndent)
{
$extraIndent = 1;
++$indentLevel;
$indent[$indentLevel] = 0;
setIndent($indent-$indent[$indentLevel-1]);
plainOutput("<TR><TD$width> </TD><TD>\n");
$colState = 1;
}
elsif ($indent < $indent[$indentLevel] || $colState==2)
{
endRow();
setIndent($indent);
plainOutput("<TR><TD$width> </TD><TD>\n");
$colState = 1;
}
++$indentLevel;
$indent[$indentLevel] = 0;
}
$prevailingIndent = 6;
}
elsif ($c eq "RE") {
if ($extraIndent)
{
endRow();
setIndent(0);
--$indentLevel;
$extraIndent = 0;
}
if ($indentLevel==0)
{
endParagraph();
if ($blockquote>0)
{
plainOutput("</BLOCKQUOTE>\n");
--$blockquote;
}
}
else
{
endRow();
setIndent(0);
--$indentLevel;
}
$prevailingIndent = $indent[$indentLevel];
$prevailingIndent = 6 unless($prevailingIndent);
}
elsif ($c eq "DT") {
@tabstops = ();
}
elsif ($c eq "ta") {
@tabstops = ();
for ($i=0; $i<$ {
$ts = $p[$i+1];
$tb = 0;
if ($ts =~ m/^\+/)
{
$tb = $tabstops[$i-1];
$ts =~ s/^\+//;
}
$ts = evalnum($ts);
$tabstops[$i] = $tb + $ts;
}
plainOutput("<!-- Tabstops set at ".join(",", @tabstops)." -->\n") if ($debug);
}
elsif ($c eq "It") {
lineBreak();
if ($docListStyle eq "-tag")
{
endRow() unless($multilineIt);
if ($tagWidth)
{
setIndent($tagWidth);
}
else
{
setIndent(6);
$width = ""; }
if ($p[1] eq "Xo")
{
plainOutput("<TR valign=top><TD colspan=2>");
}
else
{
$tag = &mdocStyle(@p[1..$ $body = lookahead();
if ($body =~ m/^\.It/)
{ $multilineItNext = 1; }
else
{ $multilineItNext = 0; }
if ($multilineIt)
{
outputLine("<BR>\n$tag\n");
}
elsif ($multilineItNext || $tagWidth>0 && width($tag)>$tagWidth)
{
outputLine("<TR valign=top><TD colspan=2>$tag\n");
$colState = 2;
}
else
{
outputLine("<TR valign=top><TD>$tag\n");
$colState = 1;
}
if ($multilineItNext)
{
$multilineIt = 1;
}
else
{
$multilineIt = 0;
if ($colState==2)
{ plainOutput("</TD></TR><TR><TD> </TD><TD>\n"); }
else
{ plainOutput("</TD><TD>\n"); }
}
}
}
else
{
plainOutput("<LI>");
}
lineBreak();
}
elsif ($c eq "Xc")
{
if ($docListStyle eq "-tag")
{
plainOutput("</TD></TR><TR><TD> </TD><TD>\n");
}
}
elsif ($c eq "Bl") {
push @docListStyles, $docListStyle;
if ($p[1] eq "-enum")
{
plainOutput("<OL>\n");
$docListStyle = $p[1];
}
elsif($p[1] eq "-bullet")
{
plainOutput("<UL>\n");
$docListStyle = $p[1];
}
else
{
$docListStyle = "-tag";
if ($p[2] eq "-width")
{
$tagWidth = width($p[3]);
if ($tagWidth < 6) { $tagWidth = 6; }
}
else
{
$tagWidth = 0;
}
$multilineIt = 0;
}
}
elsif ($c eq "El") {
if ($docListStyle eq "-tag")
{
endRow();
setIndent(0);
}
elsif ($docListStyle eq "-bullet")
{
plainOutput("</UL>\n");
}
else
{
plainOutput("</OL>\n");
}
$docListStyle = pop @docListStyles;
}
elsif ($c eq "Os")
{
$right = $joined;
}
elsif ($c eq "Dd")
{
$left = $joined;
}
elsif ($c eq "Sx") {
$id = $contents{"\U$joined"};
if ($id && $joined =~ m/^[A-Z]/)
{
outputLine("<A HREF=#$id>".&mdocStyle(@p[1..$ }
else
{
my $x = &mdocStyle(@p[1..$ $x =~ s/^ //;
outputLine($x."\n");
}
}
elsif (&mdocCallable($c))
{
my $x = &mdocStyle(@p);
$x =~ s/^ //;
outputLine($x."\n");
}
elsif ($c eq "Bx")
{
outputLine("<I>BSD $joined</I>\n");
}
elsif ($c eq "Ux")
{
outputLine("<I>Unix $joined</I>\n");
}
elsif ($c eq "At")
{
outputLine("<I>AT&T $joined</I>\n");
}
elsif ($c =~ m/[A-Z][a-z]/) {
outputLine("<BR>.$c $joined\n");
}
elsif ($c eq "") {
$doneLine = 0;
}
else {
$doneLine = 0;
plainOutput("<!-- ignored unsupported tag .$c -->\n");
}
}
sub trapTP
{
$lookaheadPtr = 0;
$body = lookahead();
if ($body =~ m/^\.TP/)
{
consume();
$trapLine = 1; $doneLine = 0; plainOutput("<BR>\n");
}
else
{
plainOutput("</TD><TD valign=bottom>\n");
$colState = 1;
}
lineBreak();
}
sub trapHP
{
$lookaheadPtr = 0;
$body = lookahead();
if ($body =~ m/^\.([TH]P)/)
{
consume();
if ($1 eq "TP")
{
$trapLine = 1;
$doneLine = 0; }
else
{
$trapOnBreak = 1;
}
plainOutput("<BR>\n");
}
else
{
plainOutput("</TD></TR><TR valign=top><TD$width> </TD><TD>\n");
$colState = 1;
}
lineBreak();
}
sub newParagraph
{
$indent = $_[0];
endRow();
startRow($indent);
}
sub startRow
{
$indent = $_[0];
$indent = $prevailingIndent unless ($indent);
$prevailingIndent = $indent;
setIndent($indent);
plainOutput( "<TR valign=top>" );
}
sub endRow
{
if ($indent[$indentLevel] > 0)
{
lineBreak();
plainOutput( "</TD></TR>\n" );
}
}
sub lineBreak
{
$needBreak = 0;
$textSinceBreak = 0;
}
sub endParagraph
{
++$indentLevel;
while ($indentLevel > 0)
{
--$indentLevel;
if ($indent[$indentLevel] > 0)
{
endRow();
setIndent(0);
}
}
}
sub numreg
{
return 0 + $number{$_[0]};
}
sub evalnum
{
$n = $_[0];
return "" if ($n eq "");
if ($n =~ m/i$/) {
$n =~ s/i//;
$n *= 10;
}
return 0+$n;
}
sub setIndent
{
$tsb = $textSinceBreak;
$indent = evalnum($_[0]);
if ($indent==0 && $_[0] !~ m/^0/)
{
$indent = 6;
}
plainOutput("<!-- setIndent $indent, indent[$indentLevel] = $indent[$indentLevel] -->\n") if ($debug);
if ($indent[$indentLevel] != $indent)
{
lineBreak();
if ($indent[$indentLevel] > 0)
{
plainOutput("<TR></TR>") unless ($noSpace);
plainOutput("</TABLE>");
}
if ($indent > 0)
{
endNoFill();
$border = "";
$border = " border=1" if ($debug>2);
plainOutput("<TABLE$border");
plainOutput(" width=100%") if ($indentLevel>0);
if ($noSpace)
{
plainOutput(" cellpadding=0 cellspacing=0>\n");
}
else
{
plainOutput(" cellpadding=3>".($tsb ? "<!-- tsb: $tsb -->\n<TR></TR><TR></TR>\n" : "\n") );
}
$percent = $indent;
if ($indentLevel > 0)
{ $percent = $indent * 100 / (100-$indentLevel[0]); }
$width = " width=$percent%";
}
$indent[$indentLevel] = $indent;
}
}
sub mdocStyle
{
return "" unless @_;
my ($tag, @param) = @_;
my ($rest, $term);
if ($param[$ {
$term = pop @param;
}
if ($param[$ {
$term = (pop @param).$term;
}
if ($param[0] =~ m,\\\\,)
{
print STDERR "$tag: ",join(",", @param),"\n";
}
$rest = &mdocStyle(@param);
if ($tag eq "Op")
{
$rest =~ s/ //; # remove first space
return " \\fP[$rest]$term";
}
elsif ($tag eq "Xr") {
my $p = shift @param;
my $url = $p;
if (@param==1)
{
$url .= ".".$param[0];
$rest = "(".$param[0].")";
}
else
{
$rest = &mdocStyle(@param);
}
if ($cmdLineMode)
{
return " <B>".$p."</B>".$rest.$term;
}
else
{
return " <A HREF=\"".$root."/".$url."\">".$p."</A>".$rest.$term;
}
}
elsif ($tag eq "Fl")
{
my ($sofar);
while (@param)
{
$f = shift @param;
if ($f eq "Ns") {
chop $sofar;
}
elsif (&mdocCallable($f))
{
unshift @param, $f;
return $sofar.&mdocStyle(@param).$term;
}
else
{
$sofar .= "-<B>$f</B> "
}
}
return $sofar.$term;
}
elsif ($tag eq "Pa" || $tag eq "Er" || $tag eq "Fn" || $tag eq "Dv")
{
return "\\fC$rest\\fP$term";
}
elsif ($tag eq "Ad" || $tag eq "Ar" || $tag eq "Em" || $tag eq "Fa" || $tag eq "St" ||
$tag eq "Ft" || $tag eq "Va" || $tag eq "Ev" || $tag eq "Tn" || $tag eq "%T")
{
return "\\fI$rest\\fP$term";
}
elsif ($tag eq "Nm")
{
$defaultNm = $param[0] unless ($defaultNm);
$rest = $defaultNm unless ($param[0]);
return "\\fB$rest\\fP$term";
}
elsif ($tag eq "Ic" || $tag eq "Cm" || $tag eq "Sy")
{
return "\\fB$rest\\fP$term";
}
elsif ($tag eq "Ta") {
return " $rest$term";
}
elsif ($tag eq "Ql")
{
$rest =~ s/ //;
return "`<TT>$rest</TT>'$term";
}
elsif ($tag eq "Dl")
{
return "<P> <TT>$rest</TT>$term<P>\n";
}
elsif ($tag =~ m/^[ABDEOPQS][qoc]$/)
{
$lq = "";
$rq = "";
if ($tag =~ m/^A/)
{ $lq = "<"; $rq = ">"; }
elsif ($tag =~ m/^B/)
{ $lq = "["; $rq = "]"; }
elsif ($tag =~ m/^D/)
{ $lq = "\""; $rq = "\""; }
elsif ($tag =~ m/^P/)
{ $lq = "("; $rq = ")"; }
elsif ($tag =~ m/^Q/)
{ $lq = "\""; $rq = "\""; }
elsif ($tag =~ m/^S/)
{ $lq = "\\'"; $rq = "\\'"; }
elsif ($tag =~ m/^O/)
{ $lq = "["; $rq = "]"; }
if ($tag =~ m/^.o/)
{ $rq = ""; }
if ($tag =~ m/^.c/)
{ $lq = ""; }
$rest =~ s/ //;
return $lq.$rest.$rq.$term ;
}
elsif (&mdocCallable($tag)) {
return $rest.$term;
}
elsif ($tag =~ m/^[.,;:()\[\]]$/) {
return $tag.$rest.$term;
}
elsif ($tag eq "Ns")
{
return $rest.$term;
}
else
{
return " ".$tag.$rest.$term;
}
}
sub mdocCallable
{
return ($_[0] =~ m/^(Op|Fl|Pa|Er|Fn|Ns|No|Ad|Ar|Xr|Em|Fa|Ft|St|Ic|Cm|Va|Sy|Nm|Li|Dv|Ev|Tn|Pf|Dl|%T|Ta|Ql|[ABDEOPQS][qoc])$/);
}
sub width
{
local($word) = $_[0];
$word =~ s,<[/A-Z][^>]*>,,g; $word =~ s/^\.\S+\s//;
$word =~ s/\\..//g;
$x = length($word);
$word =~ s/[ ()|.,!;:"']//g; # width of punctuation is about half a character
return ($x + length($word)) / 2;
}
# Process a tbl table (between TS/TE tags)
sub processTable
{
if ($troffTable == "1")
{
@troffRowDefs = ();
@tableRows = ();
$hadUnderscore = 0;
while(1)
{
outputOrigLine();
if (m/;\s*$/)
{
$troffSeparator = quotemeta($1) if (m/tab\s*\((.)\)/);
}
else
{
s/\.\s*$//;
s/\t/ /g;
s/^[^lrcan^t]*//; # remove any 'modifiers' coming before tag
# delimit on tags excluding s (viewed as modifier of previous column)
s/([lrcan^t])/\t$1/g;
s/^\t//;
push @troffRowDefs, $_;
last if ($origLine =~ m/\.\s*$/);
}
$_ = getLine();
preProcessLine();
}
$troffTable = 2;
return;
}
s/$troffSeparator/\t/g;
if ($_ eq ".TE")
{
endTblRow();
flushTable();
$troffTable = 0;
plainOutput("</TABLE></BLOCKQUOTE>\n");
}
elsif ($_ eq ".T&")
{
endTblRow();
flushTable();
$troffTable = 1;
}
elsif (m/[_=]/ && m/^[_=\t]*$/ && $troffCol==0)
{
if (m/^[_=]$/)
{
flushTable();
plainOutput("<TR></TR><TR></TR>\n");
$hadUnderscore = 1;
}
elsif ($troffCol==0 && @troffRowDefs)
{
# Don't output a row, but this counts as a row as far as row defs go
$rowDef = shift @troffRowDefs;
@troffColDefs = split(/\t/, $rowDef);
}
}
elsif (m/^\.sp/ && $troffCol==0 && !$hadUnderscore)
{
flushTable();
plainOutput("<TR></TR><TR></TR>\n");
}
elsif ($_ eq ".br" && $troffMultiline)
{
$rowref->[$troffCol] .= "<BR>\n";
}
elsif ($_ !~ m/^\./)
{
$rowref = $tableRows[$#tableRows]; # reference to current row (last row in array)
if ($troffCol==0 && @troffRowDefs)
{
$rowDef = shift @troffRowDefs;
if ($rowDef =~ m/^[_=]/)
{
$xxx = $_;
flushTable();
plainOutput("<TR></TR><TR></TR>\n");
$hadUnderscore = 1;
$_ = $xxx;
$rowDef = shift @troffRowDefs;
}
@troffColDefs = split(/\t/, $rowDef);
}
if ($troffCol == 0 && !$troffMultiline)
{
$rowref = [];
push(@tableRows, $rowref);
#plainOutput("<TR valign=top>");
}
#{
if (m/T}/)
{
$troffMultiline = 0;
}
if ($troffMultiline)
{
$rowref->[$troffCol] .= "$_\n";
return;
}
@columns = split(/\t/, $_);
plainOutput("<!-- Adding (".join(",", @columns)."), type (".join(",", @troffColDefs).") -->\n") if ($debug);
while ($troffCol <= $#troffColDefs && @columns > 0)
{
$def = $troffColDefs[$troffCol];
$col = shift @columns;
$col =~ s/\s*$//;
$align = "";
$col = "\\^" if ($col eq "" && $def =~ m/\^/);
$col = " " if ($col eq "");
$style1 = "";
$style2 = "";
if ($col ne "\\^")
{
if ($def =~ m/[bB]/ || $def =~ m/f3/)
{ $style1 = "\\fB"; $style2 = "\\fP"; }
if ($def =~ m/I/ || $def =~ m/f2/)
{ $style1 = "\\fI"; $style2 = "\\fP"; }
}
if ($def =~ m/c/) { $align = " align=center"; }
if ($def =~ m/[rn]/) { $align = " align=right"; }
$span = $def;
$span =~ s/[^s]//g;
if ($span) { $align.= " colspan=".(length($span)+1); }
#{
if ($col =~ m/T}/)
{
$rowref->[$troffCol] .= "$style2</TD>";
++$troffCol;
}
elsif ($col =~ m/T{/) #}
{
$col =~ s/T{//; #}
$rowref->[$troffCol] = "<TD$align>$style1$col";
$troffMultiline = 1;
}
else
{
$rowref->[$troffCol] = "<TD$align>$style1$col$style2</TD>";
++$troffCol;
}
}
endTblRow() unless ($troffMultiline);
}
}
sub endTblRow
{
return if ($troffCol == 0);
while ($troffCol <= $#troffColDefs)
{
$rowref->[$troffCol] = "<TD> </TD>";
#print OUT "<TD> </TD>";
++$troffCol;
}
$troffCol = 0;
#print OUT "</TR>\n"
}
sub flushTable
{
plainOutput("<!-- flushTable $
for($r=0; $r<$ {
$vspans = 0;
for ($c=0; $c<=$ {++$vspans if ($tableRows[$r+1][$c] =~ m,<TD.*?>\\\^</TD>,);}
if ((($vspans>1) || ($tableRows[$r+1][0] =~ m,<TD.*?> </TD>,)) &&
$ {
if ($debug)
{
plainOutput("<!-- merging row $r+1 into previous -->\n");
plainOutput("<!-- row $r: (".join(",", @{$tableRows[$r]}).") -->\n");
plainOutput("<!-- row $r+1: (".join(",", @{$tableRows[$r+1]}).") -->\n");
}
for ($c=0; $c<=$ {
$tableRows[$r][$c] .= $tableRows[$r+1][$c];
$tableRows[$r][$c] =~ s,\\\^,,g; $tableRows[$r][$c] =~ s,</TD><TD.*?>,<BR>,;
}
@tableRows = (@tableRows[0..$r], @tableRows[$r+2 .. $ --$r; }
}
for($r=0; $r<$ {
for ($c=0; $c<=$ {
$r2 = $r+1;
while ( $r2<=$ {
++$r2;
}
$rs = $r2-$r;
if ($rs > 1)
{
plainOutput("<!-- spanning from $r,$c -->\n") if ($debug);
$tableRows[$r][$c] =~ s/<TD/<TD rowspan=$rs/;
}
}
}
for($r=0; $r<=$ {
for ($c=$ {
if ($tableRows[$r][$c] =~ m/<TD rowspan=(\d+)/)
{
for ($r2=$r+1; $r2<$r+$1; ++$r2)
{
$rowref = $tableRows[$r2];
plainOutput("<!-- removing $r2,$c: ".$rowref->[$c]." -->\n") if ($debug);
@$rowref = (@{$rowref}[0..$c-1], @{$rowref}[$c+1..$ }
}
}
}
for($r=0; $r<=$ {
plainOutput("<TR valign=top>\n");
for ($c=0; $c <= $ {
outputLine($tableRows[$r][$c]);
}
plainOutput("</TR>\n");
}
@tableRows = ();
$troffCol = 0;
plainOutput("<!-- flushTable done -->\n") if ($debug);
}
sub pushStyle
{
$result = "";
$type = $_[0];
$tag = $_[1];
print OUT "<!-- pushStyle $type($tag) [".join(",", @styleStack)."] " if ($debug>1);
@oldItems = ();
if (grep(m/^$type/, @styleStack))
{
print OUT "undoing up to old $type " if ($debug>1);
while (@styleStack)
{
$oldItem = pop @styleStack;
($oldTag) = ($oldItem =~ m/^.(\S+)/);
$result .= "</$oldTag>";
if (substr($oldItem,0,1) eq $type)
{
print OUT "found $oldItem " if ($debug>1);
while (@oldItems)
{
$oldItem = shift @oldItems;
push(@styleStack, $oldItem);
$result .= "<".substr($oldItem,1).">";
}
last;
}
else
{
unshift(@oldItems, $oldItem);
}
}
}
print OUT "oldItems=(@oldItems) " if ($debug>1);
push(@styleStack, @oldItems); if ($tag)
{
$result .= "<$tag>";
push(@styleStack, $type.$tag);
}
print OUT "-> '$result' -->\n" if ($debug>1);
return $result;
}
sub resetStyles
{
if (@styleStack)
{
print OUT "<!-- resetStyles [".join(",", @styleStack)."] -->\n";
print OUT "<HR> resetStyles [".join(",", @styleStack)."] <HR>\n" if ($debug);
}
while (@styleStack)
{
$oldItem = pop @styleStack;
($oldTag) = ($oldItem =~ m/^.(\S+)/);
print OUT "</$oldTag>";
}
$currentSize = 0;
$currentShift = 0;
}
sub blockquote
{
print OUT "<BLOCKQUOTE>\n";
++$blockquote;
}
sub endBlockquote
{
resetStyles();
while ($blockquote > 0)
{
print OUT "</BLOCKQUOTE>\n";
--$blockquote;
}
}
sub indent
{
plainOutput(pushStyle("I", "TABLE"));
$width = $_[0];
$width = " width=$width%" if ($width);
plainOutput("<TR><TD$width> </TD><TD>\n");
}
sub outdent
{
plainOutput("</TD></TR>\n");
plainOutput(pushStyle("I"));
}
sub inlineStyle
{
$_[0] =~ m/^(.)(.*)$/;
if ($1 eq "f")
{ fontChange($2); }
elsif ($1 eq "s" && ! $noFill)
{ sizeChange($2); }
else
{ superSub($1); }
}
sub fontChange
{
$fnt = $_[0];
$fnt =~ s/^\(//;
if ($fnt eq "P" || $fnt eq "R" || $fnt eq "1" || $fnt eq "")
{ $font = ""; }
elsif ($fnt eq "B" || $fnt eq "3")
{ $font = "B"; }
elsif ($fnt eq "I" || $fnt eq "2")
{ $font = "I"; }
else
{ $font = "TT"; }
return pushStyle("F", $font);
}
sub sizeChange
{
$size= $_[0];
if ($size =~ m/^[+-]/)
{ $currentSize += $size; }
else
{ $currentSize = $size-10; }
$currentSize = 0 if (! $size);
$sz = $currentSize;
$sz = -2 if ($sz < -2);
$sz = 2 if ($sz > 2);
if ($currentSize eq "0")
{ $size = ""; }
else
{ $size = "FONT size=$sz"; }
return pushStyle("S", $size);
}
sub superSub
{
$sub = $_[0];
++$currentShift if ($sub eq "u");
--$currentShift if ($sub eq "d");
$tag = "";
$tag = "SUP" if ($currentShift > 0);
$tag = "SUB" if ($currentShift < 0);
return pushStyle("D", $tag);
}
sub startNoFill
{
print OUT "<PRE>\n" unless($noFill);
$noFill = 1;
}
sub endNoFill
{
print OUT "</PRE>\n" if ($noFill);
$noFill = 0;
}
sub processEqns
{
if ($eqnMode==2 && $_[0] =~ m/^\.EN/)
{
$eqnMode = 0;
outputLine(flushEqn());
plainOutput("\n");
return;
}
$eqnBuffer .= $_[0]." ";
}
sub processEqnd
{
processEqns(@_);
return flushEqn();
}
sub flushEqn
{
@p = grep($_ !~ m/^ *$/, split(/("[^"]*"|\s+|[{}~^])/, $eqnBuffer) );
$eqnBuffer = "";
#return "[".join(',', @p)." -> ".&doEqn(@p)."]\n";
$res = &doEqn(@p);
#$res =~ s,\\\((..),$special{$1}||"\\($1",ge;
#$res =~ s,<,<,g;
#$res =~ s,>,>,g;
return $res;
}
sub doEqn
{
my @p = @_;
my $result = "";
my $res;
my $c;
while (@p)
{
($res, @p) = doEqn1(@p);
$result .= $res;
}
return $result;
}
sub doEqn1
{
my @p = @_;
my $res = "";
my $c;
$c = shift @p;
if ($eqndefs{$c})
{
@x = split(/\0/, $eqndefs{$c});
unshift @p, @x;
$c = shift @p;
}
if ($c =~ m/^"(.*)"$/)
{
$res = $1;
}
elsif ($c eq "delim")
{
$c = shift @p;
if ($c eq "off")
{
$eqnStart = "";
$eqnEnd = "";
}
else
{
$c =~ m/^(.)(.)/;
$eqnStart = quotemeta($1);
$eqnEnd = quotemeta($2);
}
}
elsif ($c eq "define" || $c eq "tdefine" || $c eq "ndefine")
{
$t = shift @p;
$d = shift @p;
$def = "";
if (length($d) != 1)
{
$def = $d;
$def =~ s/^.(.*)./\1/;
}
else
{
while (@p && $p[0] ne $d)
{
$def .= shift @p;
$def .= "\0";
}
chop $def;
shift @p;
}
$eqndefs{$t} = $def unless ($c eq "ndefine");
}
elsif ($c eq "{")
{
my $level = 1;
my $i;
for ($i=0; $i<=$#p; ++$i)
{
++$level if ($p[$i] eq "{");
--$level if ($p[$i] eq "}");
last if ($level==0);
}
$res = doEqn(@p[0..$i-1]);
@p = @p[$i+1..$#p];
}
elsif ($c eq "sup")
{
($c,@p) = &doEqn1(@p);
$res = "\\u$c\\d";
}
elsif ($c eq "to")
{
($c,@p) = &doEqn1(@p);
$res = "\\u$c\\d ";
}
elsif ($c eq "sub" || $c eq "from")
{
($c,@p) = &doEqn1(@p);
$res = "\\d$c\\u";
}
elsif ($c eq "matrix")
{
($c,@p) = &doEqn1(@p);
$res = "matrix ( $c )";
}
elsif ($c eq "bold")
{
($c,@p) = &doEqn1(@p);
$res = "\\fB$c\\fP";
}
elsif ($c eq "italic")
{
($c,@p) = &doEqn1(@p);
$res = "\\fI$c\\fP";
}
elsif ($c eq "roman")
{
}
elsif ($c eq "font" || $c eq "gfont" || $c eq "size" || $c eq "gsize")
{
shift @p;
}
elsif ($c eq "mark" || $c eq "lineup")
{
}
elsif ($c eq "~" || $c eq "^")
{
$res = " ";
}
elsif ($c eq "over")
{
$res = " / ";
}
elsif ($c eq "half")
{
$res = "\\(12";
}
elsif ($c eq "prime")
{
$res = "\\' ";
}
elsif ($c eq "dot")
{
$res = "\\u.\\d ";
}
elsif ($c eq "dotdot")
{
$res = "\\u..\\d ";
}
elsif ($c eq "tilde")
{
$res = "\\u~\\d ";
}
elsif ($c eq "hat")
{
$res = "\\u^\\d ";
}
elsif ($c eq "bar" || $c eq "vec")
{
$res = "\\(rn ";
}
elsif ($c eq "under")
{
$res = "_ ";
}
elsif ( $c eq "sqrt" || $c eq "lim" || $c eq "sum" || $c eq "pile" || $c eq "lpile" ||
$c eq "rpile" || $c eq "cpile" || $c eq "int" || $c eq "prod" )
{
$res = " $c ";
}
elsif ($c eq "cdot")
{
$res = " . ";
}
elsif ($c eq "inf")
{
$res = "\\(if";
}
elsif ($c eq "above" || $c eq "lcol" || $c eq "ccol")
{
$res = " ";
}
elsif ($c eq "sin" || $c eq "cos" || $c eq "tan" || $c eq "log" || $c eq "ln" )
{
$res = " $c ";
}
elsif ($c eq "left" || $c eq "right" || $c eq "nothing")
{
}
elsif ($c =~ m/^[A-Za-z]/)
{
$res = "\\fI$c\\fP";
}
else
{
$res = $c;
}
return ($res, @p);
}
##### Search manpath and initialise special char array #####
sub initialise
{
# Determine groff version if possible
my $groffver = `groff -v`;
$groffver =~ /^GNU groff version (\S+)/;
$groffver = $1;
# Parse the macro definition file for section names
if (open(MACRO, "/usr/lib/tmac/tmac.an") ||
open(MACRO, "/usr/lib/tmac/an") ||
open(MACRO, "/usr/lib/groff/tmac/tmac.an") ||
open(MACRO, "/usr/lib/groff/tmac/an.tmac") ||
open(MACRO, "/usr/share/tmac/tmac.an") ||
open(MACRO, "/usr/share/groff/tmac/tmac.an") ||
open(MACRO, "/usr/share/groff/tmac/an.tmac") ||
open(MACRO, "/usr/share/groff/$groffver/tmac/an.tmac") )
{
while (<MACRO>)
{
chop;
if (m/\$2'([0-9a-zA-Z]+)' .ds ]D (.*)$/)
{
$sn = $2;
unless ($sn =~ m/[a-z]/)
{
$sn = "\u\L$sn";
$sn =~ s/ (.)/ \u\1/g;
}
$sectionName{"\L$1"} = $sn;
}
if (m/\$1'([^']+)' .ds Tx "?(.*)$/)
{
$title{"$1"} = $2;
}
if (m/^.ds ]W (.*)$/)
{
$osver = $1;
}
}
}
else
{
print STDERR "Failed to read tmac.an definitions\n" unless ($cgiMode);
}
if (open(MACRO, "/usr/lib/tmac/tz.map"))
{
while (<MACRO>)
{
chop;
if (m/\$1'([^']+)' .ds Tz "?(.*)$/)
{
$title{"$1"} = $2;
}
}
}
# Prevent redefinition of macros that have special meaning to us
$reservedMacros = '^(SH|SS|Sh|Ss)$';
# Predefine special number registers
$number{'.l'} = 75;
# String variables defined by man package
$vars{'lq'} = '& $vars{'rq'} = '”';
$vars{'R'} = '\\(rg';
$vars{'S'} = '\\s0';
$vars{'Le'} = '\\(<=';
$vars{'<='} = '\\(<=';
$vars{'Ge'} = '\\(>=';
$vars{'Lt'} = '<';
$vars{'Gt'} = '>';
$vars{'Ne'} = '\\(!=';
$vars{'>='} = '\\(>=';
$vars{'q'} = '"'; $vars{'Lq'} = '“';
$vars{'Rq'} = '”';
$vars{'ua'} = '\\(ua';
$vars{'ga'} = '\\(ga';
$vars{'Pi'} = '\\(*p';
$vars{'Pm'} = '\\(+-';
$vars{'Na'} = 'NaN';
$vars{'If'} = '\\(if';
$vars{'Ba'} = '|';
$vars{'bu'} = '»';
$vars{'66'} = '“';
$vars{'99'} = '”';
$vars{'*!'} = '¡';
$vars{'ct'} = '¢';
$vars{'po'} = '£';
$vars{'gc'} = '¤';
$vars{'ye'} = '¥';
$vars{'sc'} = '§';
$vars{'*:'} = '¨';
$vars{'co'} = '©';
$vars{'_a'} = 'ª';
$vars{'<<'} = '«';
$vars{'no'} = '¬';
$vars{'hy'} = '­';
$vars{'rg'} = '®';
$vars{'ba'} = '¯';
$vars{'de'} = '°';
$vars{'pm'} = '±';
$vars{'aa'} = '´';
$vars{'mu'} = 'µ';
$vars{'pg'} = '¶';
$vars{'c.'} = '·';
$vars{'cd'} = '¸';
$vars{'_o'} = 'º';
$vars{'>>'} = '»';
$vars{'14'} = '¼';
$vars{'12'} = '½';
$vars{'*?'} = '¿';
$vars{'`A'} = 'À';
$vars{"'A"} = 'Á';
$vars{'^A'} = 'Â';
$vars{'~A'} = 'Ã';
$vars{':A'} = 'Ä';
$vars{'oA'} = 'Å';
$vars{'AE'} = 'Æ';
$vars{',C'} = 'Ç';
$vars{'`E'} = 'È';
$vars{"'E"} = 'É';
$vars{'^E'} = 'Ê';
$vars{':E'} = 'Ë';
$vars{'`I'} = 'Ì';
$vars{"'I"} = 'Í';
$vars{'^I'} = 'Î';
$vars{':I'} = 'Ï';
$vars{'-D'} = 'Ð';
$vars{'~N'} = 'Ñ';
$vars{'`O'} = 'Ò';
$vars{"'O"} = 'Ó';
$vars{'^O'} = 'Ô';
$vars{'~O'} = 'Õ';
$vars{':O'} = 'Ö';
$vars{'NU'} = 'Ø';
$vars{'`U'} = 'Ù';
$vars{"'U"} = 'Ú';
$vars{'^U'} = 'Û';
$vars{':U'} = 'Ü';
$vars{'Th'} = 'Þ';
$vars{'*b'} = 'ß';
$vars{'`a'} = 'à';
$vars{"'a"} = 'á';
$vars{'^a'} = 'â';
$vars{'~a'} = 'ã';
$vars{':a'} = 'ä';
$vars{'oa'} = 'å';
$vars{'ae'} = 'æ';
$vars{',c'} = 'ç';
$vars{'`e'} = 'è';
$vars{"'e"} = 'é';
$vars{'^e'} = 'ê';
$vars{':e'} = 'ë';
$vars{'`i'} = 'ì';
$vars{"'i"} = 'í';
$vars{'^i'} = 'î';
$vars{':i'} = 'ï';
$vars{'~n'} = 'ñ';
$vars{'`o'} = 'ò';
$vars{"'o"} = 'ó';
$vars{'^o'} = 'ô';
$vars{'~o'} = 'õ';
$vars{':o'} = 'ö';
$vars{'di'} = '÷';
$vars{'nu'} = 'ø';
$vars{'`u'} = 'ù';
$vars{"'u"} = 'ú';
$vars{'^u'} = 'û';
$vars{':u'} = 'ü';
$vars{'th'} = 'þ';
$vars{':y'} = 'ÿ';
$special{'em'} = '—';
$special{'hy'} = '-';
$special{'\-'} = '–'; $special{'bu'} = 'o';
$special{'sq'} = '[]';
$special{'ru'} = '_';
$special{'14'} = '¼';
$special{'12'} = '½';
$special{'34'} = '¾';
$special{'fi'} = 'fi';
$special{'fl'} = 'fl';
$special{'ff'} = 'ff';
$special{'Fi'} = 'ffi';
$special{'Fl'} = 'ffl';
$special{'de'} = '°';
$special{'dg'} = '†'; $special{'fm'} = "\\'";
$special{'ct'} = '¢';
$special{'rg'} = '®';
$special{'co'} = '©';
$special{'pl'} = '+';
$special{'mi'} = '-';
$special{'eq'} = '=';
$special{'**'} = '*';
$special{'sc'} = '§';
$special{'aa'} = '´'; $special{'ga'} = '`'; $special{'ul'} = '_';
$special{'sl'} = '/';
$special{'*a'} = 'a';
$special{'*b'} = 'ß';
$special{'*g'} = 'y';
$special{'*d'} = 'd';
$special{'*e'} = 'e';
$special{'*z'} = 'z';
$special{'*y'} = 'n';
$special{'*h'} = 'th';
$special{'*i'} = 'i';
$special{'*k'} = 'k';
$special{'*l'} = 'l';
$special{'*m'} = 'µ';
$special{'*n'} = 'v';
$special{'*c'} = '3';
$special{'*o'} = 'o';
$special{'*p'} = 'pi';
$special{'*r'} = 'p';
$special{'*s'} = 's';
$special{'*t'} = 't';
$special{'*u'} = 'u';
$special{'*f'} = 'ph';
$special{'*x'} = 'x';
$special{'*q'} = 'ps';
$special{'*w'} = 'o';
$special{'*A'} = 'A';
$special{'*B'} = 'B';
$special{'*G'} = '|\\u_\\d';
$special{'*D'} = '/\';
$special{'*E'} = 'E';
$special{'*Z'} = 'Z';
$special{'*Y'} = 'H';
$special{'*H'} = 'TH';
$special{'*I'} = 'I';
$special{'*K'} = 'K';
$special{'*L'} = 'L';
$special{'*M'} = 'M';
$special{'*N'} = 'N';
$special{'*C'} = 'Z';
$special{'*O'} = 'O';
$special{'*P'} = '||';
$special{'*R'} = 'P';
$special{'*S'} = 'S';
$special{'*T'} = 'T';
$special{'*U'} = 'Y';
$special{'*F'} = 'PH';
$special{'*X'} = 'X';
$special{'*Q'} = 'PS';
$special{'*W'} = 'O';
$special{'ts'} = 's';
$special{'sr'} = 'v/';
$special{'rn'} = '\\u–\\d'; $special{'>='} = '>=';
$special{'<='} = '<=';
$special{'=='} = '==';
$special{'~='} = '~=';
$special{'ap'} = '~'; $special{'!='} = '!=';
$special{'->'} = '->';
$special{'<-'} = '<-';
$special{'ua'} = '^';
$special{'da'} = 'v';
$special{'mu'} = '×';
$special{'di'} = '÷';
$special{'+-'} = '±';
$special{'cu'} = 'U';
$special{'ca'} = '^';
$special{'sb'} = '(';
$special{'sp'} = ')';
$special{'ib'} = '(=';
$special{'ip'} = '=)';
$special{'if'} = 'oo';
$special{'pd'} = '6';
$special{'gr'} = 'V';
$special{'no'} = '¬';
$special{'is'} = 'I';
$special{'pt'} = '~';
$special{'es'} = 'Ø';
$special{'mo'} = 'e';
$special{'br'} = '|';
$special{'dd'} = '‡'; $special{'rh'} = '=>';
$special{'lh'} = '<=';
$special{'or'} = '|';
$special{'ci'} = 'O';
$special{'lt'} = '(';
$special{'lb'} = '(';
$special{'rt'} = ')';
$special{'rb'} = ')';
$special{'lk'} = '|';
$special{'rk'} = '|';
$special{'bv'} = '|';
$special{'lf'} = '|';
$special{'rf'} = '|';
$special{'lc'} = '|';
$special{'rc'} = '|';
$special{'cp'} = '©';
$special{'tm'} = '®';
$special{'en'} = '-';
@manpath = ();
if (open(MPC, "/etc/manpath.config") || open(MPC, "/etc/man.config"))
{
while (<MPC>)
{
if (m/^(MANDB_MAP|MANPATH)\s+(\S+)/)
{
push(@manpath, $2);
}
}
}
@manpath = split(/:/, $ENV{'MANPATH'}) unless (@manpath);
@manpath = ("/usr/man") unless (@manpath);
}
sub loadManDirs
{
return if (@mandirs);
print STDERR "Searching ",join(":", @manpath)," for mandirs\n" unless($cgiMode);
foreach $tld (@manpath)
{
$tld =~ m/^(.*)$/;
$tld = $1; if (opendir(DIR, $tld))
{
foreach $d (sort readdir(DIR))
{
if ($d =~ m/^man\w/ && -d "$tld/$d")
{
push (@mandirs, "$tld/$d");
}
}
closedir DIR;
}
}
}
sub findPage
{
$request = $_[0];
$request =~ s,^/,,;
@multipleMatches = ();
$file = $_[0];
return $file if (-f $file || -f "$file.gz" || -f "$file.bz2");
($page,$sect) = ($request =~ m/^(.+)\.([^.]+)$/);
$sect = "\L$sect";
if ($sect)
{
foreach $md (@manpath)
{
$dir = $md;
$file = "$dir/man$sect/$page.$sect";
push(@multipleMatches, $file) if (-f $file || -f "$file.gz" || -f "$file.bz2");
}
}
else
{
$page = $request;
}
if (@multipleMatches == 1)
{
return pop @multipleMatches;
}
loadManDirs();
foreach $dir (@mandirs)
{
($s) = ($dir =~ m/man([0-9A-Za-z]+)$/);
$file = "$dir/$page.$s";
push(@multipleMatches, $file) if (-f $file || -f "$file.gz" || -f "$file.bz2");
$file = "$dir/$request";
push(@multipleMatches, $file) if (-f $file || -f "$file.gz" || -f "$file.bz2");
if ($sect && "$page.$sect" ne $request)
{
$file = "$dir/$page.$sect";
push(@multipleMatches, $file) if (-f $file || -f "$file.gz" || -f "$file.bz2");
}
}
if (@multipleMatches == 1)
{
return pop @multipleMatches;
}
if (@multipleMatches > 1)
{
return "";
}
foreach $dir (@mandirs)
{
opendir(DIR, $dir);
foreach $f (readdir DIR)
{
if ($f =~ m/^$page\./)
{
$f =~ s/\.(gz|bz2)$//;
push(@multipleMatches, "$dir/$f");
}
}
}
if (@multipleMatches == 1)
{
return pop @multipleMatches;
}
return "";
}
sub loadPerlPages
{
my ($dir,$f,$name,@files);
loadManDirs();
return if (%perlPages);
foreach $dir (@mandirs)
{
if (opendir(DIR, $dir))
{
@files = sort readdir DIR;
foreach $f (@files)
{
next if ($f eq "." || $f eq ".." || $f !~ m/\./);
next unless ("$dir/$f" =~ m/perl/);
$f =~ s/\.(gz|bz2)$//;
($name) = ($f =~ m,(.+)\.[^.]*$,);
$perlPages{$name} = "$dir/$f";
}
closedir DIR;
}
}
delete $perlPages{'perl'}; }
sub fmtTime
{
my $time = $_[0];
my @days = qw (Sun Mon Tue Wed Thu Fri Sat);
my @months = qw (Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$istdst) = localtime($time);
return sprintf ("%s, %02d %s %4d %02d:%02d:%02d GMT",
$days[$wday],$mday,$months[$mon],1900+$year,$hour,$min,$sec);
}