Article View: comp.infosystems.www
Article #9[Web Filter]: bbc_man2html v0.2 (was: Man Page Filter of the Week)
From: Brooks Cutter
Date: Sun, 13 Feb 1994 18:40
Date: Sun, 13 Feb 1994 18:40
421 lines
12106 bytes
12106 bytes
Here is the latest version of my man2html script with the bug fixes
previously mentioned on this group. Please send any questions or
comments to bcutter@paradyne.com
#!/usr/local/bin/perl
#
# bbc_man2html v0.2 by Brooks Cutter (bcutter@paradyne.com)
# (Sun Feb 13 13:24:59 EST 1994)
#
# Changes from v0.1:
#
# A bit more documentation..
#
# Alan Coopersmith (alanc@ocf.berkeley.edu) pointed out a major
# security hole - any file from the file system could be accessed with
# the script. oops.
#
# Wes Barris (wes@msc.edu) pointed out that I had embedded control
# characters in the script
#
# Some notes about man2html:
#
# This script parses the output of 'nroff -man' on the man page you
# desire. I should parse the roff source itself, but that isn't
# guaranteed to be available, and parsing the formatted output was easier..
#
# Set the man path array to the paths to search...
@manpath = ('/usr/share/man','/usr/openwin/man','/usr/local/man');
# Set this link to somebody that can be contacted...
# Unless you are on the AT&T R&D Internet (Behind the AT&T Firewall)
# you won't be able to access this link...
$contact = <<EOF;
<a href="http://wwwhome.paradyne.com/users/bcutter/intro.html">Brooks Cutter</a>
EOF
# This message describes the current status - feel free to change
# to suite your environment. Good place for disclaimers, etc
$filter_status = <<EOF;
This converter is still in development. I intend to
improve the handling of multiple matches, and add
a interface to apropos (or man -k (or whatis...))
EOF
# Must support the -L option - if a symlink stat the file itself
$file = '/usr/bin/file';
$ls = '/bin/ls';
$nroff = '/usr/local/bin/nroff';
# Must support the -r option
$uname = '/usr/bin/uname';
# There has to be a blank line after this...
print "Content-type:text/html\n\n";
if (!$ARGV[0]) {
print "<isindex>\n";
chop($os = `$uname`);
chop($ver = `$uname -r`);
print "
<title> $os $ver Manual Pages </title>
<h1> $os $ver Manual Pages </h1>
Enter the name of the man page, optionally surrounded
by parenthesis with the number. For example:
<p>
<ul>
<li> stat to find one or more man pages for ls
<li> stat(2) for the system call stat
</ul>
$filter_status
<p>
$contact
";
exit(0);
}
$_ = &check_man_path($ARGV[0],@manpath);
if ((/^-$/)) {
$manpages[0] = $_;
} elsif ((m!^/!)) {
$manpages[0] = $_;
} elsif (($name, $sect) = /(\S+)\((\d.*)\)/) {
@manpages = &findman($name, $sect, @manpath);
} elsif (($name, $sect) = /(\S+)<(\d.*)>/) {
@manpages = &findman($name, $sect, @manpath);
} elsif (($name, $sect) = /(\S+)\[(\d.*)\]/) {
@manpages = &findman($name, $sect, @manpath);
} else {
@manpages = &findman($_, '', @manpath);
}
if (!scalar @manpages) {
print "Sorry, I was unable to find a match for <b>$_</b>\n";
exit(0);
} elsif (scalar @manpages > 1) {
&which_manpage(@manpages);
} else {
if (!-e $manpages[0]) {
die "man2html: Error, Can't locate file '$manpages[0]'\n";
}
chop($type=`$file -L $manpages[0]`);
if ($type =~ /roff/i) {
$manpages[0] = "$nroff -man $manpages[0]|";
} elsif ($type =~ /text/i) {
#$manpages[0] = $manpages[0];
; # NOP (No Operation)
} else {
print "
<title>Man2HTML: An Error has occurred</title>
<h1>Man2HTML: An Error has occurred</h1>
man2html found the following match for your query:</hr>
$manpages[0]
<p>
When '$file -L $manpages[0]' was run
(which should follow symbolic links)
it returned the following value '$type'
<p>
";
if ($type =~ /link/i) {
print "
This problem appears to be that there is a symbolic link
for a man page that is pointing to a file that doesn't exist.
<p>
";
}
print "
Please report this problem to someone who can do something about it.
<i>(Assuming you aren't that person...)</i>
If you don't know who that is, try emailing 'root' or 'postmaster'.
<p>
There was only one match for your query - and it can't currently
be accessed.
";
exit(0);
#die "Unknown type '$type' for manpage '$manpages[0]'";
}
&print_manpage($manpages[0]);
}
exit(0);
sub findman {
# Take a argument like 'ls' or 'vi(1)' or 'tip(1c)' and return
# a list of one or more manpages.
# Arguments 2- are the directories to search in
local($lookfor) = shift(@_);
local($section) = shift(@_);
local($file, @files, @return, $return);
local(%men,%man);
die "lookfor($lookfor) is null\n" unless($lookfor);
for (@_) {
# I'm... too lazy... for... opendir()... too lazy for readdir()...
# too lazy for closedir() ... I'm too lazy!
if (!$section) {
@files = `$ls $_/*/$lookfor.* 2> /dev/null`;
} else {
# if the section is like '1b' then just search *1b
# otherwise if '1' search *1* (to catch all sub-sections)
# Reason for wildcards: ($_/*$section*/$lookfor.*)
# (given $section = '2')
# 1st: So it catches cat2 and man2
# 2nd: So it catches man2 and man2v
# (This should make it compatiable with HP/UX's man2.Z - not tested)
# 3rd: So it catches stat.2 and stat.2v
#
if (length($section) == 1) {
@files = `$ls $_/*$section*/$lookfor.* 2> /dev/null`;
} else {
local($section_num) = substr($section, 0, 1); # Just the number...
@files = `$ls $_/*$section_num*/$lookfor.* $_/*$section/$lookfor.* 2> /dev/null`;
}
}
next if (!scalar @files);
# This part checks the files that were found...
for $file (@files) {
chop($file);
local(@dirs) = split(/\//,$file);
local($fn) = pop(@dirs);
local($catman) = pop(@dirs);
local($dir) = join('/',@dirs);
local($key) = "$dir/$fn";
next if ($man{$key}); # forces unique
if (!$men{$key}) {
$men{$key} = $catman;
$man{$key} = $file;
} else {
# pre-formatted man pages always take precedence unless zero bytes...
next if (($men{$key} =~ /^cat/i) && (!(-z $man{$key})));
$men{$key} = $catman;
$man{$key} = $file;
}
}
}
return(values %man);
}
sub which_manpage {
# Print a list of manpages...
print "
There were multiple matches for the argument '$ARGV[0]'.
Below are the fully qualified pathnames of the matches, please
click on the appropriate one.
<ul>
";
for (@_) {
print "<li><a href=\"/htbin/man2html?$_\">$_</a>\n";
}
print "</ul>\n";
return;
}
sub print_manpage {
local($page) = @_;
local($label, $before, $after, $begtag, $endtag, $blanks, $begtag2, $endtag2);
local($pre);
local($standard_indent) = 0;
if ($page eq '-') {
open(MAN, '-');
} elsif (index($page,'|') == length($page)) {
# A Pipe
local($eval) =
'open(MAN, "'.$page.'") || die "Can'."'t open pipe to '$page' for reading: ".'$!";';
eval($eval);
die "Eval error line $. : '$eval' returned '$@' : $!\n";
} else {
open(MAN, $page) || die "Can't open '$page' for reading: $!";
}
while (<MAN>) {
if (/^\s*$/) {
$blanks++;
#if ($pre) { print "</pre>\n"; $pre = 0; }
if (($. != 1) && ($blanks == 1)) {
if (($pre) || ($section_pre)) {
print "\n";
} else {
print "<p>\n";
}
}
next;
}
#next if (!/^[A-Z]{2,}\(.*\).*/);
if (/\ch/) { s/.\ch//g; }
# Escape & < and >
s/&/\&/g;
s/</\</g;
s/>/\>/g;
#
if (/^(\w+.*)\s*$/) {
$label = $1;
$next_action = '';
if (/^[A-Z ]{2,}\s*$/) {
if (($pre) || ($section_pre)) { print "</pre>\n"; }
$pre = $section_pre = $section_fmt = 0;
if (!$standard_indent) { $next_action = 'check_indent'; }
}
if ($label eq 'NAME') {
$begtag = '<title>';
$endtag = '</title>';
$begtag2 = '<h1>';
$endtag2 = '</h1>';
$next_action = 'check_indent';
next;
}
if ($label eq 'SYNOPSIS') {
$section_fmt = 1;
}
if ($label eq 'SEE ALSO') {
$next_action = 'create_links';
}
if (($label =~ /OPTIONS$/) || ($label eq 'FILES')) {
$section_pre = 1;
print "</pre>\n";
} elsif (/^[A-Z ]+\s*$/) {
print "</pre>\n" if (($pre) || ($section_pre));
$section_pre = 0;
}
if (/^[A-Z ]+\s*$/) {
print "<h2>$label</h2>\n";
$blanks = 0;
print "<pre>\n" if ($section_pre);
next;
}
next;
}
if ($section_fmt) { print; $blanks = 0; next; }
if ($next_action eq 'create_links') {
# Parse see also looking for man page links. Make it
# call this program. use '+' notation for spaces
local($page);
local($first) = 1;
for $page (split(/,/)) {
$page =~ tr/\x00-\x20//d; # Delete all control chars, spaces
if ($page =~ /.+\(\d.*\).*$/) {
$url_page = $page;
$url_page =~ tr/()/[]/;
print "," if (!$first);
$first = 0;
print "<a href=\"/htbin/man2html?$url_page\">$page</a>\n";
} else {
print "," if (!$first);
$first = 0;
print "$page";
}
}
next;
}
# This is to detect preformatted blocks. I look at the first
# line after header 'DESCRIPTION' and count the leading white
# space as the "standard indent". If I encounter a line with
# a indent greater than the value of standard_indent then
# surround it with <pre> and </pre>
if ($next_action eq 'check_indent') {
if (/^(\s+)\S+.*/) {
$standard_indent = length($1);
$next_action = '';
}
}
#
$before = length($_);
$saved = $_;
s/^[ ][ ]*//; # Delete leading whitespace
$after = length($_);
s/[ ][ ]*$//; # Delete trailing whitespace
if ($begtag) {
chop;
print "$begtag$_$endtag\n";
print "$begtag2$_$endtag2\n" if ($begtag2);
$blanks = 0;
$begtag2 = $endtag2 = $begtag = $endtag = '';
next;
}
if ((!$section_fmt) && (!$section_pre) && ($standard_indent)) {
if (($blanks == 1) && (!$pre) && ($after + $standard_indent) < $before) {
$pre = 1;
print "<pre>\n";
} elsif (($pre) && ($after + $standard_indent) >= $before) {
$pre = 0;
print "</pre>\n";
}
}
if (($section_pre) || ($pre)) {
print "$saved";
$blanks = 0;
next;
}
# Handle word cont-
# inuations
if ($prefix) {
print $prefix;
$prefix = '';
}
if (/^(.+)\s+(\w+)\-\s*$/) {
$prefix = $2;
print "$1\n";
$blanks = 0;
next;
}
print;
$blanks = 0;
}
close(MAN);
}
sub check_man_path {
local($path) = shift(@_);
local($ndx);
return($path) if (($ndx = rindex($path,'/')) == -1); # No '/' in path
local($remainder,$length);
for (@_) {
$length = length($_);
if (substr($path,0,$length) eq $_) {
if (substr($_,($length-1),1) eq '/') {
# Man path is terminated with a '/', skip the '/'
$remainder = substr($path,$length);
} else {
$remainder = substr($path,($length+1));
}
# Check the remaining path for things like '..'
local($p,@p);
@p = split('/',$remainder);
for $p (0..($#p-1)) {
&access_denied($path) if ($p[$p] eq '..');
}
# It's ok - return it as so
return($path);
}
}
&access_denied($path);
}
sub access_denied {
local($path) = shift(@_);
print <<EOF;
An attempt was made to access the explicit path:<br>
$path
<p>
This path was not specified in the manpath list.<br>
If you believe this request should be valid, please contact
the system administrator.
EOF
exit;
}
# EOF
--
Brooks Cutter (I: bcutter@paradyne.com) (U: ...uunet!pdn!bcutter)
Unix System Administrator, Engineering Services
AT&T Paradyne, Largo Florida
(Opinions expressed are my own and do not reflect those of my employer)
Technical Details
Message-ID:
<2jls73$4lg@pdn.paradyne.com>Path:
arc1-ca.pugleaf.net!wupost!cs.utexas.edu!uunet!sybus.sybus.com!myrddin!pdn!bcutterReferences:
<66Yk03Zle1qZ00@amdahl.uts.amdahl.com> <2jc2m7$g51@pdn.paradyne.com> <2jjqgj$ied@agate.berkeley.edu>Show Full Headers
Complete Article Headers
Path: arc1-ca.pugleaf.net!wupost!cs.utexas.edu!uunet!sybus.sybus.com!myrddin!pdn!bcutter From: bcutter@paradyne.com (Brooks Cutter) Newsgroups: comp.infosystems.www,pdn.comp.www,alt.sources Subject: [Web Filter]: bbc_man2html v0.2 (was: Man Page Filter of the Week) Date: 13 Feb 1994 18:40:35 GMT Organization: AT&T Paradyne, Largo Florida Lines: 421 Message-ID: <2jls73$4lg@pdn.paradyne.com> References: <66Yk03Zle1qZ00@amdahl.uts.amdahl.com> <2jc2m7$g51@pdn.paradyne.com> <2jjqgj$ied@agate.berkeley.edu> NNTP-Posting-Host: natas.paradyne.com