🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Article View: comp.infosystems.www
Article #9

[Web Filter]: bbc_man2html v0.2 (was: Man Page Filter of the Week)

#9
From: Brooks Cutter
Date: Sun, 13 Feb 1994 18:40
421 lines
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/&/\&amp;/g;
		s/</\&lt;/g;
		s/>/\&gt;/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!bcutter
References: <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