183098Smp: # -*- perl -*-
2232633Smp# $tcsh: tcsh.man2html,v 1.15 2011/02/05 16:15:56 christos Exp $
359243Sobrien
459243Sobrien# tcsh.man2html, Dave Schweisguth <dcs@proton.chem.yale.edu>
559243Sobrien#
659243Sobrien# Notes:
759243Sobrien#
859243Sobrien# Always puts all files in the directory tcsh.html, creating it if necessary.
959243Sobrien# tcsh.html/top.html is the entry point, and tcsh.html/index.html is a symlink
1059243Sobrien# to tcsh.html/top.html so one needn't specify a file at all if working through
1159243Sobrien# a typically configured server.
1259243Sobrien#
1359243Sobrien# Designed for tcsh manpage. Guaranteed not to work on manpages not written
1459243Sobrien# in the exact same style of nroff -man, i.e. any other manpage.
1559243Sobrien#
1659243Sobrien# Makes links FROM items which are both a) in particular sections (see
1759243Sobrien# Configuration) and b) marked with .B or .I. Makes links TO items which
1859243Sobrien# are marked with \fB ... \fR or \fI ... \fR.
1959243Sobrien#
2059243Sobrien# Designed with X Mosaic in mind and tested lightly with lynx. I've punted on
2159243Sobrien# HTML's lack of a .PD equivalent and lynx's different <menu> handling.
2259243Sobrien
2359243Sobrien# Emulate #!/usr/local/bin/perl on systems without #!
2459243Sobrien
2561515Sobrieneval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
2661515Sobrien& eval 'exec perl -S $0 $argv:q' if 0;
2759243Sobrien
2859243Sobrien### Constants
2959243Sobrien
3059243Sobrien# Setup
3159243Sobrien
3259243Sobrien($whatami = $0)	=~ s|.*/||;	# `basename $0`
3359243Sobrien$isatty		= -t STDIN;
3459243Sobrien
3559243Sobrien# Configuration
3659243Sobrien
3759243Sobrien$index		= 0;		# Don't make a searchable index CGI script
3859243Sobrien$cgibin		= 0;		# Look for $cgifile in $dir, not $cgibindir
3959243Sobrien$shortfiles	= 0;		# Use long filenames
4059243Sobrien$single		= 0;		# Make single page instead of top and sections
4159243Sobrien
4259243Sobrien$host		= '';		# host:port part of server URL ***
4359243Sobrien$updir		= '';		# Directories between $host and $dir ***
4459243Sobrien$dir		= 'tcsh';	# Directory in which to put the pieces *
4559243Sobrien$cgifile	= 'tcsh.cgi';	# CGI script name **
4659243Sobrien$cgibindir	= 'cgi-bin';	# CGI directory ***
4759243Sobrien$headerfile	= 'header';	# HTML file for initial comments *
4859243Sobrien$indexfile	= 'index';	# Symlink to $topfile *
4959243Sobrien$listsfile	= 'lists';	# Mailing list description HTML file *	
5059243Sobrien$outfile	= 'tcsh.man';	# Default input file and copy of input file
5159243Sobrien$script		= $whatami;	# Copy of script; filename length must be OK
5259243Sobrien$topfile	= 'top';	# Top-level HTML file *
5359243Sobrien
5459243Sobrien# *   .htm or .html suffix added later
5559243Sobrien# **  Only used with -i or -c
5659243Sobrien# *** Only used with -c
5759243Sobrien
5859243Sobrien# Sections to inline in the top page
5959243Sobrien
6059243Sobrien%inline_me	= ('NAME',	1,
6159243Sobrien		   'SYNOPSIS',	1);
6259243Sobrien
6359243Sobrien# Sections in which to put name anchors and the font in which to look for
6459243Sobrien# links to those anchors
6559243Sobrien
6659243Sobrien%link_me	= ('Editor commands',		'I',
6759243Sobrien		   'Builtin commands',		'I',
6859243Sobrien		   'Special aliases',		'I',
6959243Sobrien		   'Special shell variables',	'B',
7059243Sobrien		   'ENVIRONMENT',		'B',
7159243Sobrien		   'FILES',			'I');
7259243Sobrien
7359243Sobrien### Arguments and error-checking
7459243Sobrien
7559243Sobrien# Parse args
7659243Sobrien
7759243Sobrienwhile ($#ARGV > -1 && (($first, $rest) = ($ARGV[0] =~ /^-(.)(.*)/))) {
7859243Sobrien    # Perl 5 lossage alert
7959243Sobrien    if ($first =~ /[CdDGh]/) {	# Switches with arguments
8059243Sobrien    	shift;
8159243Sobrien    	$arg = $rest ne '' ? $rest : $ARGV[0] ne '' ? shift :
8259243Sobrien      	    &usage("$whatami: -$first requires an argument.\n");
8359243Sobrien    } elsif ($rest ne '') {
8459243Sobrien    	$ARGV[0] = "-$rest";
8559243Sobrien    } else {
8659243Sobrien	shift;
8759243Sobrien    }
8859243Sobrien    if	  ($first eq '1')   { $single = 1; }
8959243Sobrien    elsif ($first eq 'c')   { $cgibin = 1; }
9059243Sobrien    elsif ($first eq 'C')   { $cgibindir = $arg; }
9159243Sobrien    elsif ($first eq 'd')   { $updir = $arg; }
9259243Sobrien    elsif ($first eq 'D')   { $dir = $arg; }
9359243Sobrien    elsif ($first eq 'G')   { $cgifile = $arg; }
9459243Sobrien    elsif ($first eq 'h')   { $host = $arg; }
9559243Sobrien    elsif ($first eq 'i')   { $index = 1; }
9659243Sobrien    elsif ($first eq 's')   { $shortfiles = 1; }
9759243Sobrien    elsif ($first eq 'u')   { &usage(0); }
9859243Sobrien    else		    { &usage("$whatami: -$first is not an option.\n"); }
9959243Sobrien}
10059243Sobrien
10159243Sobrienif (@ARGV == 0) {
10259243Sobrien    if ($isatty) {
10359243Sobrien        $infile = $outfile;		# Default input file if interactive
10459243Sobrien    } else {
10559243Sobrien	$infile = 'STDIN';		# Read STDIN if no args and not a tty
10659243Sobrien    }
10759243Sobrien} elsif (@ARGV == 1) {
10859243Sobrien    $infile = $ARGV[0];
10959243Sobrien} else {
11059243Sobrien    &usage("$whatami: Please specify one and only one file.\n");
11159243Sobrien}
11259243Sobrien
11359243Sobrien$index = $index || $cgibin;		# $index is true if $cgibin is true
11459243Sobrien
11559243Sobrienif ($cgibin && ! $host) {
11659243Sobrien    die "$whatami: Must specify host with -h if using -c.\n";
11759243Sobrien}
11859243Sobrien
11959243Sobrien# Decide on HTML suffix and append it to filenames
12059243Sobrien
12159243Sobrien$html = $shortfiles ? 'htm' : 'html';	# Max 3-character extension
12259243Sobrien$dir		.= ".$html";		# Directory in which to put the pieces
12359243Sobrien$headerfile	.= ".$html";		# HTML file for initial comments
12459243Sobrien$topfile	.= ".$html";		# Top-level HTML file (or moved notice)
12559243Sobrien$indexfile	.= ".$html";		# Symlink to $topfile
12659243Sobrien$listsfile	.= ".$html";		# Mailing list description HTML file
12759243Sobrien
12859243Sobrien# Check for input file
12959243Sobrien
13059243Sobrienunless ($infile eq 'STDIN') {
13159243Sobrien    die "$whatami: $infile doesn't exist!\n"	unless -e $infile;
13259243Sobrien    die "$whatami: $infile is unreadable!\n"	unless -r _;
13359243Sobrien    die "$whatami: $infile is empty!\n"		unless -s _;
13459243Sobrien}
13559243Sobrien
13659243Sobrien# Check for output directory and create if necessary
13759243Sobrien
13859243Sobrienif (-e $dir) {
13959243Sobrien    -d _ || die "$whatami: $dir is not a directory!\n";
14059243Sobrien    -r _ && -w _ && -x _ || die "$whatami: $dir is inaccessible!\n"
14159243Sobrien} else {
14259243Sobrien    mkdir($dir, 0755) || die "$whatami: Can't create $dir!\n";
14359243Sobrien}
14459243Sobrien
14559243Sobrien# Slurp manpage
14659243Sobrien
14759243Sobrienif ($infile eq 'STDIN') {
14859243Sobrien    @man = <STDIN>;
14959243Sobrien} else {
15059243Sobrien    open(MAN, $infile) || die "$whatami: Error opening $infile!\n";
15159243Sobrien    @man = <MAN>;
15259243Sobrien    close MAN;
15359243Sobrien}
15459243Sobrien
15559243Sobrien# Print manpage to HTML directory (can't use cp if we're reading from STDIN)
15659243Sobrien
15759243Sobrienopen(MAN, ">$dir/$outfile") || die "$whatami: Can't open $dir/$outfile!\n";
15859243Sobrienprint MAN @man;
15959243Sobrienclose MAN;
16059243Sobrien
16159243Sobrien# Copy script to HTML directory
16259243Sobrien
16359243Sobrien(system("cp $0 $dir") >> 8) && die "$whatami: Can't copy $0 to $dir!\n";
16459243Sobrien
16559243Sobrien# Link top.html to index.html in case someone looks at tcsh.html/
16659243Sobrien
16759243Sobriensystem("rm -f $dir/$indexfile");    # Some systems can't ln -sf
16859243Sobrien(system("ln -s $topfile $dir/$indexfile") >> 8)
16959243Sobrien    && die "$whatami: Can't link $topfile to $dir/$indexfile!\n";
17059243Sobrien
17159243Sobrien### Get title and section headings
17259243Sobrien
17359243Sobrien$comment = 0;			    # 0 for text, 1 for ignored text
17459243Sobrien@sectionlines = (0);		    # First line of section
17559243Sobrien@sectiontypes = (0);		    # H or S
17659243Sobrien@sectiontexts = ('Header');	    # Text of section heading
17759243Sobrien@sectionfiles = ($headerfile);	    # Filename in which to store section
17859243Sobrien%name = ();			    # Array of name anchors
17959243Sobrien@name = () if $index;		    # Ordered array of name anchors
18059243Sobrien$font = '';		    	    # '' to not make names, 'B' or 'I' to do so
18159243Sobrien
18259243Sobrien$line = 0;
18359243Sobrienforeach (@man) {
18459243Sobrien    if (/^\.ig/) {		    # Start ignoring
18559243Sobrien	$comment = 1;
18659243Sobrien    } elsif (/^\.\./) {		    # Stop ignoring
18759243Sobrien	$comment = 0;
18859243Sobrien    } elsif (! $comment) {	    # Not in .ig'ed section; do stuff
18959243Sobrien	
19059243Sobrien	# nroff special characters
19159243Sobrien	
19259243Sobrien	s/\\-/-/g;		    # \-
19359243Sobrien	s/\\^//g;		    # \^
19459243Sobrien	s/^\\'/'/;		    # leading ' escape
19559243Sobrien	s/^\\(\s)/$1/;		    # leading space escape
19659243Sobrien	s/\\(e|\\)/\\/g;	    # \e, \\; must do this after other escapes
19759243Sobrien
19859243Sobrien	# HTML special characters; deal with these before adding more
19959243Sobrien	
20059243Sobrien	s/&/&amp\;/g;
20159243Sobrien	s/>/&gt\;/g;
20259243Sobrien	s/</&lt\;/g;
20359243Sobrien	
20459243Sobrien	# Get title
20559243Sobrien	
20659243Sobrien	if (/^\.TH\s+(\w+)\s+(\w+)\s+\"([^\"]*)\"\s+\"([^\"]*)\"/) {
20759243Sobrien	    $title = "$1($2) $4 ($3) $1($2)";
20859243Sobrien	}
20959243Sobrien	
21059243Sobrien	# Build per-section info arrays
21159243Sobrien	
21259243Sobrien	if (($type, $text) = /^\.S([HS])\s+\"?([^\"]*)\"?/) {
21359243Sobrien
21459243Sobrien	    push(@sectionlines, $line);	    # Index of first line of section
21559243Sobrien	    push(@sectiontypes, $type eq 'H' ? 0 : 1);	# Type of section
21659243Sobrien	    $text =~ s/\s*$//;		    # Remove trailing whitespace
21759243Sobrien	    push(@sectiontexts, $text);	    # Title of section (key for href)
21859243Sobrien	    $text =~ s/\s*\(\+\)$//;	    # Remove (+)
21959243Sobrien	    if ($shortfiles) {
22059243Sobrien		$file = $#sectionlines;	    # Short filenames; use number
22159243Sobrien	    } else {
22259243Sobrien		$file = $text;		    # Long filenames; use title
22359243Sobrien		$file =~ s/[\s\/]+/_/g;	    # Replace whitespace and / with _
22459243Sobrien	    }
22559243Sobrien	    $file .= ".$html" unless $single;
22659243Sobrien	    push(@sectionfiles, $file);	    # File in which to store section
22759243Sobrien	    $name{"$text B"} = ($single ? '#' : '') . $file;
22859243Sobrien					    # Index entry for &make_hrefs
22959243Sobrien	    push(@name, "$text\t" . $name{"$text B"}) if $index;
23059243Sobrien					    # Index entry for CGI script
23159243Sobrien	    # Look for anchors in the rest of this section if $link_me{$text}
23259243Sobrien	    # is non-null, and mark them with the font which is its value
23359243Sobrien
23459243Sobrien	    $font = $link_me{$text};
23559243Sobrien    	}
23659243Sobrien	&make_name(*name, *font, *file, *index, *_) if $font;
23759243Sobrien    }
23859243Sobrien    $line++;
23959243Sobrien}
24059243Sobrien
24159243Sobrien### Make top page
24259243Sobrien
24359243Sobrienopen(TOP, ">$dir/$topfile");
24459243Sobrienselect TOP;
24559243Sobrien
24659243Sobrien# Top page header
24759243Sobrien
24859243Sobrienprint <<EOP;
24959243Sobrien<HEAD>
25059243Sobrien<TITLE>$title</TITLE>
25159243Sobrien</HEAD>
25259243Sobrien<BODY>
25359243Sobrien<A NAME="top"></A>
25459243Sobrien<H1>$title</H1>
25559243Sobrien<HR>
25659243SobrienEOP
25759243Sobrien
25859243Sobrien# FORM block, if we're making an index
25959243Sobrien
26059243Sobrien$action = $cgibin ? "http://$host/$cgibindir/$cgifile" : $cgifile;
26159243Sobrien
26259243Sobrienprint <<EOP if $index;
26359243Sobrien<FORM METHOD="GET" ACTION="$action">
26459243SobrienGo directly to a section, command or variable: <INPUT NAME="input">
26559243Sobrien</FORM>
26659243SobrienEOP
26759243Sobrien
26859243Sobrien# Table of contents
26959243Sobrien
27059243Sobrienprint <<EOP;
27159243Sobrien<H2>
27259243SobrienEOP
27359243Sobrien
27459243Sobrienforeach $section (1 .. $#sectionlines) {
27559243Sobrien    if ($sectiontypes[$section - 1] < $sectiontypes[$section]) {
27659243Sobrien	print "</H2> <menu>\n";	    # Indent, smaller font
27759243Sobrien    } elsif ($sectiontypes[$section - 1] > $sectiontypes[$section]) {
27859243Sobrien	print "</menu> <H2>\n";	    # Outdent, larger font
27959243Sobrien    }
28059243Sobrien    if ($inline_me{$sectiontexts[$section]}) {    # Section is in %inline_me
28159243Sobrien	
28259243Sobrien	# Print section inline
28359243Sobrien	
28459243Sobrien	print "$sectiontexts[$section]\n";
28559243Sobrien	print "</H2> <menu>\n";	    # Indent, smaller font
28659243Sobrien	&printsectionbody(*man, *sectionlines, *section, *name);
28759243Sobrien	print "</menu> <H2>\n";	    # Outdent, larger font
28859243Sobrien    } else {
28959243Sobrien	
29059243Sobrien	# Print link to section
29159243Sobrien	
29259243Sobrien	print "<A HREF=\"", $single ? '#' : '',
29359243Sobrien	    "$sectionfiles[$section]\">$sectiontexts[$section]</A><BR>\n";
29459243Sobrien    }
29559243Sobrien}
29659243Sobrien
29759243Sobrienprint <<EOP;
29859243Sobrien</H2>
29959243SobrienEOP
30059243Sobrien
30159243Sobrienprint "<HR>\n" if $single;
30259243Sobrien
30359243Sobrien### Make sections
30459243Sobrien
30559243Sobrienforeach $section (0 .. $#sectionlines) {
30659243Sobrien
30759243Sobrien    # Skip inlined sections
30859243Sobrien
30959243Sobrien    next if $inline_me{$sectiontexts[$section]};
31059243Sobrien    
31159243Sobrien    if ($single) {
31259243Sobrien
31359243Sobrien	# Header
31459243Sobrien    
31559243Sobrien	print <<EOP if $section;	# Skip header section
31659243Sobrien<H2><A NAME="$sectionfiles[$section]">$sectiontexts[$section]</A></H2>
31759243Sobrien<menu>
31859243SobrienEOP
31959243Sobrien	&printsectionbody(*man, *sectionlines, *section, *name);
32059243Sobrien	print <<EOP if $section;	# Skip header section
32159243Sobrien<A HREF="#top">Table of Contents</A>
32259243Sobrien</menu>
32359243SobrienEOP
32459243Sobrien
32559243Sobrien    } else {
32659243Sobrien
32759243Sobrien	# Make pointer line for header and trailer
32859243Sobrien	
32959243Sobrien	$pointers  = "<A HREF=\"$topfile\">Up</A>";
33059243Sobrien	$pointers .= "\n<A HREF=\"$sectionfiles[$section + 1]\">Next</A>"
33159243Sobrien	    if ($section < $#sectionlines) &&
33259243Sobrien	    ! $inline_me{$sectiontexts[$section + 1]};
33359243Sobrien	$pointers .= "\n<A HREF=\"$sectionfiles[$section - 1]\">Previous</A>"
33459243Sobrien	    if ($section > 1) &&		# section 0 is initial comments
33559243Sobrien	    ! $inline_me{$sectiontexts[$section - 1]};
33659243Sobrien    
33759243Sobrien	# Header
33859243Sobrien
33959243Sobrien	open(OUT, ">$dir/$sectionfiles[$section]");
34059243Sobrien	select OUT;
34159243Sobrien	print <<EOP;
34259243Sobrien<HEAD>
34359243Sobrien<TITLE>$sectiontexts[$section]</TITLE>
34459243Sobrien</HEAD>
34559243Sobrien<BODY>
34659243Sobrien$pointers
34759243Sobrien<H2>$sectiontexts[$section]</H2>
34859243SobrienEOP
34959243Sobrien	&printsectionbody(*man, *sectionlines, *section, *name);
35059243Sobrien
35159243Sobrien	# Trailer
35259243Sobrien
35359243Sobrien	print <<EOP;
35459243Sobrien$pointers
35559243Sobrien</BODY>
35659243SobrienEOP
35759243Sobrien
35859243Sobrien    }
35959243Sobrien}
36059243Sobrien
36159243Sobrienselect TOP unless $single;
36259243Sobrien
36359243Sobrien# Top page trailer
36459243Sobrien
36559243Sobrienprint <<EOP;
36659243Sobrien</H2>
36759243Sobrien<HR>
36859243SobrienHere are the <A HREF="$outfile">nroff manpage</A> (175K)
36959243Sobrienfrom which this HTML version was generated,
37059243Sobrienthe <A HREF="$script">Perl script</A> which did the conversion
37161515Sobrienand the <A HREF="ftp://ftp.astron.com/pub/tcsh/">
37259243Sobriencomplete source code</A> for <I>tcsh</I>.
37359243Sobrien<HR>
37459243Sobrien<I>tcsh</I> is maintained by
37559243SobrienChristos Zoulas <A HREF="mailto:christos\@gw.com">&lt;christos\@gw.com&gt;</A>
37659243Sobrienand the <A HREF="$listsfile"><I>tcsh</I> maintainers' mailing list</A>.
37759243SobrienDave Schweisguth <A HREF="mailto:dcs\@proton.chem.yale.edu">&lt;dcs\@proton.chem.yale.edu&gt;</A>
37859243Sobrienwrote the manpage and the HTML conversion script.
37959243Sobrien</BODY>
38059243SobrienEOP
38159243Sobrien
38259243Sobrienclose TOP;
38359243Sobrien
38459243Sobrien### Make lists page
38559243Sobrien
38659243Sobrienopen(LISTS, ">$dir/$listsfile");
38759243Sobrienselect LISTS;
38859243Sobrienwhile(($_ = <DATA>) ne "END\n") {   # Text stored after __END__
38959243Sobrien    s/TOPFILEHERE/$topfile/;
39059243Sobrien    print;
39159243Sobrien}
39259243Sobrienclose LISTS;
39359243Sobrien
39459243Sobrien### Make search script
39559243Sobrien
39659243Sobrienif ($index) {
39759243Sobrien
39859243Sobrien    # URL of $dir; see comments in search script
39959243Sobrien
40059243Sobrien    $root = $cgibin
40159243Sobrien	? "'http://$host/" . ($updir ? "$updir/" : '') . "$dir/'"
40259243Sobrien	: '"http://$ENV{\'SERVER_NAME\'}:$ENV{\'SERVER_PORT\'}" . (($_ = $ENV{\'SCRIPT_NAME\'}) =~ s|[^/]*$||, $_)';
40359243Sobrien
40459243Sobrien    # String for passing @name to search script
40559243Sobrien
40659243Sobrien    $name = join("',\n'", @name);
40759243Sobrien
40859243Sobrien    open(TOP, ">$dir/$cgifile");
40959243Sobrien    select TOP;
41059243Sobrien    while(($_ = <DATA>) ne "END\n") {   # Text stored after __END__
41159243Sobrien	s/ROOTHERE/$root/;
41259243Sobrien	s/NAMEHERE/$name/;
41359243Sobrien	s/TOPFILEHERE/$topfile/;
41459243Sobrien	print;
41559243Sobrien    }
41659243Sobrien    close TOP;
41759243Sobrien    chmod(0755, "$dir/$cgifile") ||
41859243Sobrien	die "$whatami: Can't chmod 0755 $dir/$cgifile!\n";
41959243Sobrien    warn "$whatami: Don't forget to move $dir/$cgifile to /$cgibindir.\n"
42059243Sobrien	if $cgibin;
42159243Sobrien}
42259243Sobrien
42359243Sobrien### That's all, folks
42459243Sobrien
42559243Sobrienexit;
42659243Sobrien
42759243Sobrien### Subroutines
42859243Sobrien
42959243Sobrien# Process and print the body of a section
43059243Sobrien
43159243Sobriensub printsectionbody {
43259243Sobrien
43359243Sobrien    local(*man, *sectionlines, *sline, *name) = @_;	# Number of section
43459243Sobrien    local($sfirst, $slast, @paralines, @paratypes, $comment, $dl, $pline,
43559243Sobrien	  $comment, $pfirst, $plast, @para, @tag, $changeindent);
43659243Sobrien
43759243Sobrien    # Define section boundaries
43859243Sobrien
43959243Sobrien    $sfirst = $sectionlines[$sline] + 1;
44059243Sobrien    if ($sline == $#sectionlines) {
44159243Sobrien	$slast = $#man;
44259243Sobrien    } else {
44359243Sobrien	$slast = $sectionlines[$sline + 1] - 1;
44459243Sobrien    }
44559243Sobrien
44659243Sobrien    # Find paragraph markers, ignoring those between '.ig' and '..'
44759243Sobrien
44859243Sobrien    if ($man[$sfirst] =~ /^\.[PIT]P/) {
44959243Sobrien	@paralines = ();
45059243Sobrien	@paratypes = ();
45159243Sobrien    } else {
45259243Sobrien	@paralines = ($sfirst - 1);		# .P follows .S[HS] by default
45359243Sobrien	@paratypes = ('P');
45459243Sobrien    }
45559243Sobrien    $comment = 0;
45659243Sobrien    foreach ($sfirst .. $slast) {
45759243Sobrien	if ($man[$_] =~ /^\.ig/) {		# Start ignoring
45859243Sobrien	    $comment = 1;
45959243Sobrien	} elsif ($man[$_] =~ /^\.\./) {		# Stop ignoring
46059243Sobrien	    $comment = 0;
46159243Sobrien	} elsif (! $comment && $man[$_] =~ /^\.([PIT])P/) {
46259243Sobrien	    push(@paralines, $_);
46359243Sobrien	    push(@paratypes, $1);
46459243Sobrien	}
46559243Sobrien    }
46659243Sobrien
46759243Sobrien    # Process paragraphs
46859243Sobrien
46959243Sobrien    $changeindent = 0;
47059243Sobrien    $dl = 0;
47159243Sobrien    foreach $pline (0 .. $#paralines) {
47259243Sobrien
47359243Sobrien	@para = ();
47459243Sobrien	$comment = 0;
47559243Sobrien
47659243Sobrien	# Define para boundaries
47759243Sobrien
47859243Sobrien	$pfirst = $paralines[$pline] + 1;
47959243Sobrien	if ($pline == $#paralines) {
48059243Sobrien	    $plast = $slast;
48159243Sobrien	} else {
48259243Sobrien	    $plast = $paralines[$pline + 1] - 1;
48359243Sobrien	}
48459243Sobrien
48559243Sobrien	foreach (@man[$pfirst .. $plast]) {
48659243Sobrien	    if (/^\.ig/) {		    # nroff begin ignore
48759243Sobrien		if ($comment == 0) {
48859243Sobrien		    $comment = 2;
48959243Sobrien		    push(@para, "<!--\n");
49059243Sobrien		} elsif ($comment == 1) {
49159243Sobrien		    $comment = 2;
49259243Sobrien		} elsif ($comment == 2) {
49359243Sobrien		    s/--/-/g;		    # Remove double-dashes in comments
49459243Sobrien		    push(@para, $_);
49559243Sobrien		}
49659243Sobrien	    } elsif (/^\.\./) {		    # nroff end ignore
49759243Sobrien		if ($comment == 0) {
49859243Sobrien		    ;
49959243Sobrien		} elsif ($comment == 1) {
50059243Sobrien		    ;
50159243Sobrien		} elsif ($comment == 2) {
50259243Sobrien		    $comment = 1;
50359243Sobrien		}
50459243Sobrien	    } elsif (/^\.\\\"/) {	    # nroff comment
50559243Sobrien		if ($comment == 0) {
50659243Sobrien		    $comment = 1;
50759243Sobrien		    push(@para, "<!--\n");
50859243Sobrien		    s/^\.\\\"//;
50959243Sobrien		} elsif ($comment == 1) {
51059243Sobrien		    s/^\.\\\"//;
51159243Sobrien		} elsif ($comment == 2) {
51259243Sobrien		    ;
51359243Sobrien		}
51459243Sobrien		s/--/-/g;		    # Remove double-dashes in comments
51559243Sobrien		push(@para, $_);
51659243Sobrien	    } else {			    # Nothing to do with comments
51759243Sobrien		if ($comment == 0) {
51859243Sobrien		    ;
51959243Sobrien    		} elsif ($comment == 1) {
52059243Sobrien		    $comment = 0;
52159243Sobrien		    push(@para, "-->\n");
52259243Sobrien		} elsif ($comment == 2) {
52359243Sobrien		    s/--/-/g;		    # Remove double-dashes in comments
52459243Sobrien		}
52559243Sobrien
52659243Sobrien		unless ($comment) {
52759243Sobrien		
52859243Sobrien		    if (/^\.TH/) {	    # Title; got this already
52959243Sobrien			next;
53059243Sobrien		    } elsif (/^\.PD/) {	    # Para spacing; unimplemented
53159243Sobrien			next;
53259243Sobrien		    } elsif (/^\.RS/) {	    # Indent (one width only)
53359243Sobrien			$changeindent++;
53459243Sobrien			next;
53559243Sobrien		    } elsif (/^\.RE/) {	    # Outdent
53659243Sobrien			$changeindent--;
53759243Sobrien			next;
53859243Sobrien		    }
53959243Sobrien
54059243Sobrien		    # Line break
54159243Sobrien		    s/^\.br.*/<BR>/;
54259243Sobrien
54359243Sobrien		    # More nroff special characters
54459243Sobrien
54559243Sobrien		    s/^\\&amp\;//;	    # leading dot escape; save until
54659243Sobrien					    #   now so leading dots aren't
54759243Sobrien					    #   confused with ends of .igs
54859243Sobrien
54959243Sobrien		    &make_hrefs(*name, *_);			
55059243Sobrien		}
55159243Sobrien		push(@para, $_);
55259243Sobrien	    }
55359243Sobrien	}
55459243Sobrien	
55559243Sobrien	push(@para, "-->\n") if $comment;   # Close open comment
55659243Sobrien	
55759243Sobrien    	# Print paragraph
55859243Sobrien
55959243Sobrien	if ($paratypes[$pline] eq 'P') {
56059243Sobrien	    &font(*para);
56159243Sobrien	    print   @para;
56259243Sobrien	} elsif ($paratypes[$pline] eq 'I') {
56359243Sobrien	    &font(*para);
56459243Sobrien	    print   "<menu>\n",
56559243Sobrien		    @para,
56659243Sobrien		    "</menu>\n";
56759243Sobrien	} else {			# T
56859243Sobrien	    @tag = shift(@para);
56959243Sobrien	    &font(*tag);
57059243Sobrien	    &font(*para);
57159243Sobrien	    print   "<DL compact>\n" unless $dl;
57259243Sobrien	    print   "<DT>\n",
57359243Sobrien		    @tag,
57459243Sobrien		    "<DD>\n",
57559243Sobrien		    @para;
57659243Sobrien	    if ($pline == $#paratypes || $paratypes[$pline + 1] ne 'T') {
57759243Sobrien		# Perl 5 lossage alert
57859243Sobrien		# Next para is not a definition list
57959243Sobrien		$dl = 0;		    # Close open definition list
58059243Sobrien		print "</DL>\n";
58159243Sobrien	    } else {
58259243Sobrien		$dl = 1;		    # Leave definition list open
58359243Sobrien	    }
58459243Sobrien	}
58559243Sobrien	print "<P>\n";
58659243Sobrien	
58759243Sobrien	# Indent/outdent the *next* para
58859243Sobrien	
58959243Sobrien	while ($changeindent > 0) {
59059243Sobrien	    print "<menu>\n";
59159243Sobrien	    $changeindent--;
59259243Sobrien	}
59359243Sobrien	while ($changeindent < 0) {
59459243Sobrien	    print "</menu>\n";
59559243Sobrien	    $changeindent++;
59659243Sobrien	}
59759243Sobrien    }
59859243Sobrien    1;
59959243Sobrien}
60059243Sobrien
60159243Sobrien# Make one name anchor in a line; cue on fonts (.B or .I) but leave them alone
60259243Sobrien
60359243Sobriensub make_name {
60459243Sobrien
60559243Sobrien    local(*name, *font, *file, *index, *line) = @_;
60659243Sobrien    local($text);
60759243Sobrien
60859243Sobrien    if (($text) = ($line =~ /^\.[BI]\s+([^\s\\]+)/)) {	# Found pattern
60959243Sobrien
61059243Sobrien	if (
61159243Sobrien	    $text !~ /^-/		    # Avoid lists of options
61259243Sobrien	    && (length($text) > 1	    # and history escapes
61359243Sobrien		||  $text =~ /^[%:@]$/)	    # Special pleading for %, :, @
61459243Sobrien	    && ! $name{"$text $font"}	    # Skip if there's one already
61559243Sobrien	) {
61659243Sobrien	    # Record link
61759243Sobrien	    
61859243Sobrien	    $name{"$text $font"} = ($single ? '' : $file) . "#$text";
61959243Sobrien	    push(@name, "$text\t" . $name{"$text $font"}) if $index;
62059243Sobrien	    
62159243Sobrien	    # Put in the name anchor
62259243Sobrien    
62359243Sobrien	    $line =~ s/^(\.[BI]\s+)([^\s\\]+)/$1<A NAME=\"$text\">$2<\/A>/;
62459243Sobrien	}
62559243Sobrien    }
62659243Sobrien    $line;
62759243Sobrien}
62859243Sobrien
62959243Sobrien# Make all the href anchors in a line; cue on fonts (\fB ... \fR or
63059243Sobrien# \fI ... \fR) but leave them alone
63159243Sobrien
63259243Sobriensub make_hrefs {
63359243Sobrien
63459243Sobrien    local(*name, *line) = @_;
63559243Sobrien    local(@pieces, $piece);
63659243Sobrien
63759243Sobrien    @pieces = split(/(\\f[BI][^\\]*\\fR)/, $line);
63859243Sobrien    
63959243Sobrien    $piece = 0;
64059243Sobrien    foreach (@pieces) {
64159243Sobrien	if (/\\f([BI])([^\\]*)\\fR/	# Found a possibility
64259243Sobrien
64359243Sobrien	# It's not followed by (, i.e. it's not a manpage reference
64459243Sobrien
64559243Sobrien	&& substr($pieces[$piece + 1], 0, 1) ne '(') {
64659243Sobrien	    $key = "$2 $1";
64759243Sobrien	    if ($name{$key}) {			# If there's a matching name
64859243Sobrien		s/(\\f[BI])([^\\]*)(\\fR)/$1<A HREF=\"$name{$key}\">$2<\/A>$3/;
64959243Sobrien	    }
65059243Sobrien	}
65159243Sobrien	$piece++;
65259243Sobrien    }
65359243Sobrien    $line = join('', @pieces);
65459243Sobrien}
65559243Sobrien
65659243Sobrien# Convert nroff font escapes to HTML
65759243Sobrien# Expects comments and breaks to be in HTML form already
65859243Sobrien
65959243Sobriensub font {
66059243Sobrien
66159243Sobrien    local(*para) = @_;
66259243Sobrien    local($i, $j, @begin, @end, $part, @pieces, $bold, $italic);
66359243Sobrien
66459243Sobrien    return 0 if $#para == -1;   # Ignore empty paragraphs
66559243Sobrien				# Perl 5 lossage alert
66659243Sobrien
66759243Sobrien    # Find beginning and end of each part between HTML comments
66859243Sobrien
66959243Sobrien    $i = 0;
67059243Sobrien    @begin = ();
67159243Sobrien    @end = ();
67259243Sobrien    foreach (@para) {
67359243Sobrien	push(@begin, $i + 1) if /^-->/ || /^<BR>/;
67459243Sobrien	push(@end, $i - 1) if /^<!--/ || /^<BR>/;
67559243Sobrien	$i++;
67659243Sobrien    }
67759243Sobrien    if ($para[0] =~ /^<!--/ || $para[0] =~ /^<BR>/) {
67859243Sobrien	shift(@end);
67959243Sobrien    } else {
68059243Sobrien	unshift(@begin, 0);	# Begin at the beginning
68159243Sobrien    }
68259243Sobrien    if ($para[$#para] =~ /^-->/ || $para[$#para] =~ /^<BR>/) {
68359243Sobrien	pop(@begin);
68459243Sobrien    } else {
68559243Sobrien	push(@end, $#para);	# End at the end
68659243Sobrien    }
68759243Sobrien
68859243Sobrien    # Fontify each part
68959243Sobrien
69059243Sobrien    $bold = $italic = 0;
69159243Sobrien    foreach $i (0 .. $#begin) {
69259243Sobrien	$part = join('', @para[$begin[$i] .. $end[$i]]);
693232633Smp	$part =~ s/^\.([BI])\s+(.*)$/\\f$1$2\\fR/gm;	    # .B, .I
694232633Smp	@pieces = split(/(\\f[BIR])/m, $part);
69559243Sobrien	$part = '';
69659243Sobrien	foreach $j (@pieces) {
69759243Sobrien	    if ($j eq '\fB') {
69859243Sobrien		if ($italic) {
69959243Sobrien		    $italic = 0;
70059243Sobrien		    $part .= '</I>';
70159243Sobrien		}
70259243Sobrien		unless ($bold) {
70359243Sobrien		    $bold = 1;
70459243Sobrien		    $part .= '<B>';
70559243Sobrien		}
70659243Sobrien	    } elsif ($j eq '\fI') {
70759243Sobrien		if ($bold) {
70859243Sobrien		    $bold = 0;
70959243Sobrien		    $part .= '</B>';
71059243Sobrien		}
71159243Sobrien		unless ($italic) {
71259243Sobrien		    $italic = 1;
71359243Sobrien		    $part .= '<I>';
71459243Sobrien		}
71559243Sobrien	    } elsif ($j eq '\fR') {
71659243Sobrien		if ($bold) {
71759243Sobrien		    $bold = 0;
71859243Sobrien		    $part .= '</B>';
71959243Sobrien		} elsif ($italic) {
72059243Sobrien		    $italic = 0;
72159243Sobrien		    $part .= '</I>';
72259243Sobrien		}
72359243Sobrien	    } else {
72459243Sobrien		$part .= $j;	
72559243Sobrien	    }
72659243Sobrien	}
72759243Sobrien
72859243Sobrien	# Close bold/italic before break
72959243Sobrien
73059243Sobrien	if ($end[$i] == $#para || $para[$end[$i] + 1] =~ /^<BR>/) {
73159243Sobrien	    # Perl 5 lossage alert
73259243Sobrien	    if ($bold) {
73359243Sobrien		$bold = 0;
73459243Sobrien		$part =~ s/(\n)?$/<\/B>$1\n/;
73559243Sobrien	    } elsif ($italic) {
73659243Sobrien		$italic = 0;
73759243Sobrien		$part =~ s/(\n)?$/<\/I>$1\n/;
73859243Sobrien	    }
73959243Sobrien	}
74059243Sobrien
74159243Sobrien	# Rebuild this section of @para
74259243Sobrien
74359243Sobrien	foreach $j ($begin[$i] .. $end[$i]) {
74459243Sobrien	    $part =~ s/^([^\n]*(\n|$))//;
74559243Sobrien	    $para[$j] = $1;
74659243Sobrien	}
74759243Sobrien    }
74859243Sobrien
74959243Sobrien    # Close bold/italic on last non-comment line
75059243Sobrien    # Do this only here because fonts pass through comments
75159243Sobrien
75259243Sobrien    $para[$end[$#end]] =~ s/(\n)?$/<\/B>$1/ if $bold;
75359243Sobrien    $para[$end[$#end]] =~ s/(\n)?$/<\/I>$1/ if $italic;
75459243Sobrien}
75559243Sobrien
75659243Sobriensub usage {
75759243Sobrien    local ($message) = $_[0];
75859243Sobrien
75959243Sobrien    warn $message if $message;
76059243Sobrien    warn <<EOP;
76159243SobrienUsage: $whatami [-1icsu] [-C dir] [-d dir] [-h host] [file]
76259243SobrienWithout [file], reads from tcsh.man or stdin.
76359243Sobrien-1	    Makes a single page instead of a table of contents and sections
76459243Sobrien-i	    Makes a CGI searchable index script, tcsh.html/tcsh.cgi, intended
76559243Sobrien	    for a server which respects the .cgi extension in any directory.
76659243Sobrien-c	    Like -i,  but the CGI script is intended for a server which wants
76759243Sobrien	    scripts in /cgi-bin (or some other privileged directory separate
76859243Sobrien	    from the rest of the HTML) and must be moved there by hand.
76959243Sobrien-C dir	    Uses /dir instead of /cgi-bin as the CGI bin dir.
77059243Sobrien	    Meaningless without -c.
77159243Sobrien-d dir	    Uses /dir/tcsh.html instead of /tcsh.html as the HTML dir.
77259243Sobrien	    Meaningless without -c.
77359243Sobrien-D dir	    Uses /dir.html instead of /tcsh.html as the HTML dir.
77459243Sobrien	    Meaningless without -c.
77559243Sobrien-G name	    Uses name instead of tcsh.cgi as the name of the CGI script.
77659243Sobrien	    Meaningless without -c or -i.
77759243Sobrien-h host	    Uses host as the host:port part of the URL to the entry point.
77859243Sobrien	    Meaningless without -c.
77959243Sobrien-s	    Filenames are shorter (max 8 + 3) but less descriptive.
78059243Sobrien-u	    This message
78159243SobrienEOP
78259243Sobrien    exit !! $message;
78359243Sobrien}
78459243Sobrien
78559243Sobrien### Inlined documents. Watch for *HERE tokens.
78659243Sobrien
78759243Sobrien__END__
78859243Sobrien<HEAD>
78959243Sobrien<TITLE>The tcsh mailing lists</TITLE>
79059243Sobrien</HEAD>
79159243Sobrien<BODY>
79259243Sobrien<A HREF="TOPFILEHERE">Up</A>
79359243Sobrien<H2>The <I>tcsh</I> mailing lists</H2>
79459243SobrienThere are three <I>tcsh</I> mailing lists:
79559243Sobrien<DL>
79659243Sobrien<DT>
79759243Sobrien<I>tcsh@mx.gw.com</I>
79859243Sobrien<DD>
79959243SobrienThe <I>tcsh</I> maintainers and testers' mailing list.
80059243Sobrien<DT>
80159243Sobrien<I>tcsh-bugs@mx.gw.com</I>
80259243Sobrien<DD>
803145479SmpOpen bug and user comment discussion.
80459243Sobrien</DL>
805145479SmpYou can subscribe to either of these lists by visiting
806145479Smp<I><A HREF="http://mx.gw.com/">http://mx.gw.com/</A></I>
80759243Sobrien<P>
808145479SmpTo file a bug report or a feature suggestion (preferably
809145479Smpwith code), please visit
810145479Smp<I><A HREF="http://bugs.gw.com/">http://bugs.gw.com/</A></I>
811145479Smp<P>
81259243Sobrien<A HREF="TOPFILEHERE">Up</A>
81359243Sobrien</BODY>
81459243SobrienEND
81583098Smp: # -*- perl -*-
81659243Sobrien
81759243Sobrien# Emulate #!/usr/local/bin/perl on systems without #!
81859243Sobrien
81961515Sobrieneval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
82061515Sobrien& eval 'exec perl -S $0 $argv:q' if 0;
82159243Sobrien
82259243Sobrien# Setup
82359243Sobrien
82459243Sobrien# Location: doesn't work with relative URLs, so we need to know where to find
82559243Sobrien#   the top and section files.
82659243Sobrien# If the search engine is in /cgi-bin, we need a hard-coded URL.
82759243Sobrien# If the search engine is in the same directory, we can figure it out from CGI
82859243Sobrien#   environment variables.
82959243Sobrien
83059243Sobrien$root = ROOTHERE;
83159243Sobrien$topfile = 'TOPFILEHERE';
83259243Sobrien@name = (
83359243Sobrien'NAMEHERE'
83459243Sobrien);
83559243Sobrien
83659243Sobrien# Do the search
83759243Sobrien
83859243Sobrien$input = $ENV{'QUERY_STRING'};
83959243Sobrien$input =~ s/^input=//;
84059243Sobrien$input =~ s/\+/ /g;
84159243Sobrienprint "Status: 302 Found\n";
84259243Sobrienif ($input ne '' && ($key = (grep(/^$input/,  @name))[0] ||
84359243Sobrien			    (grep(/^$input/i, @name))[0] ||
84459243Sobrien			    (grep( /$input/i, @name))[0]   )) {
84559243Sobrien    $key =~ /\t([^\t]*)$/;
84659243Sobrien    print "Location: $root$1\n\n";
84759243Sobrien} else {
84859243Sobrien    print "Location: $root$topfile\n\n";
84959243Sobrien}
85059243SobrienEND
851