xref: /freebsd/contrib/tcsh/tcsh.man2html (revision 1669d8afc64812c8d2d1d147ae1fd42ff441e1b1)
1: # -*- perl -*-
2# $tcsh: tcsh.man2html,v 1.14 2006/03/02 18:46:45 christos Exp $
3
4# tcsh.man2html, Dave Schweisguth <dcs@proton.chem.yale.edu>
5#
6# Notes:
7#
8# Always puts all files in the directory tcsh.html, creating it if necessary.
9# tcsh.html/top.html is the entry point, and tcsh.html/index.html is a symlink
10# to tcsh.html/top.html so one needn't specify a file at all if working through
11# a typically configured server.
12#
13# Designed for tcsh manpage. Guaranteed not to work on manpages not written
14# in the exact same style of nroff -man, i.e. any other manpage.
15#
16# Makes links FROM items which are both a) in particular sections (see
17# Configuration) and b) marked with .B or .I. Makes links TO items which
18# are marked with \fB ... \fR or \fI ... \fR.
19#
20# Designed with X Mosaic in mind and tested lightly with lynx. I've punted on
21# HTML's lack of a .PD equivalent and lynx's different <menu> handling.
22
23# Emulate #!/usr/local/bin/perl on systems without #!
24
25eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
26& eval 'exec perl -S $0 $argv:q' if 0;
27
28### Constants
29
30# Setup
31
32($whatami = $0)	=~ s|.*/||;	# `basename $0`
33$isatty		= -t STDIN;
34
35# Configuration
36
37$index		= 0;		# Don't make a searchable index CGI script
38$cgibin		= 0;		# Look for $cgifile in $dir, not $cgibindir
39$shortfiles	= 0;		# Use long filenames
40$single		= 0;		# Make single page instead of top and sections
41
42$host		= '';		# host:port part of server URL ***
43$updir		= '';		# Directories between $host and $dir ***
44$dir		= 'tcsh';	# Directory in which to put the pieces *
45$cgifile	= 'tcsh.cgi';	# CGI script name **
46$cgibindir	= 'cgi-bin';	# CGI directory ***
47$headerfile	= 'header';	# HTML file for initial comments *
48$indexfile	= 'index';	# Symlink to $topfile *
49$listsfile	= 'lists';	# Mailing list description HTML file *
50$outfile	= 'tcsh.man';	# Default input file and copy of input file
51$script		= $whatami;	# Copy of script; filename length must be OK
52$topfile	= 'top';	# Top-level HTML file *
53
54# *   .htm or .html suffix added later
55# **  Only used with -i or -c
56# *** Only used with -c
57
58# Sections to inline in the top page
59
60%inline_me	= ('NAME',	1,
61		   'SYNOPSIS',	1);
62
63# Sections in which to put name anchors and the font in which to look for
64# links to those anchors
65
66%link_me	= ('Editor commands',		'I',
67		   'Builtin commands',		'I',
68		   'Special aliases',		'I',
69		   'Special shell variables',	'B',
70		   'ENVIRONMENT',		'B',
71		   'FILES',			'I');
72
73### Arguments and error-checking
74
75# Parse args
76
77while ($#ARGV > -1 && (($first, $rest) = ($ARGV[0] =~ /^-(.)(.*)/))) {
78    # Perl 5 lossage alert
79    if ($first =~ /[CdDGh]/) {	# Switches with arguments
80    	shift;
81    	$arg = $rest ne '' ? $rest : $ARGV[0] ne '' ? shift :
82      	    &usage("$whatami: -$first requires an argument.\n");
83    } elsif ($rest ne '') {
84    	$ARGV[0] = "-$rest";
85    } else {
86	shift;
87    }
88    if	  ($first eq '1')   { $single = 1; }
89    elsif ($first eq 'c')   { $cgibin = 1; }
90    elsif ($first eq 'C')   { $cgibindir = $arg; }
91    elsif ($first eq 'd')   { $updir = $arg; }
92    elsif ($first eq 'D')   { $dir = $arg; }
93    elsif ($first eq 'G')   { $cgifile = $arg; }
94    elsif ($first eq 'h')   { $host = $arg; }
95    elsif ($first eq 'i')   { $index = 1; }
96    elsif ($first eq 's')   { $shortfiles = 1; }
97    elsif ($first eq 'u')   { &usage(0); }
98    else		    { &usage("$whatami: -$first is not an option.\n"); }
99}
100
101if (@ARGV == 0) {
102    if ($isatty) {
103        $infile = $outfile;		# Default input file if interactive
104    } else {
105	$infile = 'STDIN';		# Read STDIN if no args and not a tty
106    }
107} elsif (@ARGV == 1) {
108    $infile = $ARGV[0];
109} else {
110    &usage("$whatami: Please specify one and only one file.\n");
111}
112
113$index = $index || $cgibin;		# $index is true if $cgibin is true
114
115if ($cgibin && ! $host) {
116    die "$whatami: Must specify host with -h if using -c.\n";
117}
118
119# Decide on HTML suffix and append it to filenames
120
121$html = $shortfiles ? 'htm' : 'html';	# Max 3-character extension
122$dir		.= ".$html";		# Directory in which to put the pieces
123$headerfile	.= ".$html";		# HTML file for initial comments
124$topfile	.= ".$html";		# Top-level HTML file (or moved notice)
125$indexfile	.= ".$html";		# Symlink to $topfile
126$listsfile	.= ".$html";		# Mailing list description HTML file
127
128# Check for input file
129
130unless ($infile eq 'STDIN') {
131    die "$whatami: $infile doesn't exist!\n"	unless -e $infile;
132    die "$whatami: $infile is unreadable!\n"	unless -r _;
133    die "$whatami: $infile is empty!\n"		unless -s _;
134}
135
136# Check for output directory and create if necessary
137
138if (-e $dir) {
139    -d _ || die "$whatami: $dir is not a directory!\n";
140    -r _ && -w _ && -x _ || die "$whatami: $dir is inaccessible!\n"
141} else {
142    mkdir($dir, 0755) || die "$whatami: Can't create $dir!\n";
143}
144
145# Slurp manpage
146
147if ($infile eq 'STDIN') {
148    @man = <STDIN>;
149} else {
150    open(MAN, $infile) || die "$whatami: Error opening $infile!\n";
151    @man = <MAN>;
152    close MAN;
153}
154
155# Print manpage to HTML directory (can't use cp if we're reading from STDIN)
156
157open(MAN, ">$dir/$outfile") || die "$whatami: Can't open $dir/$outfile!\n";
158print MAN @man;
159close MAN;
160
161# Copy script to HTML directory
162
163(system("cp $0 $dir") >> 8) && die "$whatami: Can't copy $0 to $dir!\n";
164
165# Link top.html to index.html in case someone looks at tcsh.html/
166
167system("rm -f $dir/$indexfile");    # Some systems can't ln -sf
168(system("ln -s $topfile $dir/$indexfile") >> 8)
169    && die "$whatami: Can't link $topfile to $dir/$indexfile!\n";
170
171### Get title and section headings
172
173$comment = 0;			    # 0 for text, 1 for ignored text
174@sectionlines = (0);		    # First line of section
175@sectiontypes = (0);		    # H or S
176@sectiontexts = ('Header');	    # Text of section heading
177@sectionfiles = ($headerfile);	    # Filename in which to store section
178%name = ();			    # Array of name anchors
179@name = () if $index;		    # Ordered array of name anchors
180$font = '';		    	    # '' to not make names, 'B' or 'I' to do so
181
182$line = 0;
183foreach (@man) {
184    if (/^\.ig/) {		    # Start ignoring
185	$comment = 1;
186    } elsif (/^\.\./) {		    # Stop ignoring
187	$comment = 0;
188    } elsif (! $comment) {	    # Not in .ig'ed section; do stuff
189
190	# nroff special characters
191
192	s/\\-/-/g;		    # \-
193	s/\\^//g;		    # \^
194	s/^\\'/'/;		    # leading ' escape
195	s/^\\(\s)/$1/;		    # leading space escape
196	s/\\(e|\\)/\\/g;	    # \e, \\; must do this after other escapes
197
198	# HTML special characters; deal with these before adding more
199
200	s/&/&amp\;/g;
201	s/>/&gt\;/g;
202	s/</&lt\;/g;
203
204	# Get title
205
206	if (/^\.TH\s+(\w+)\s+(\w+)\s+\"([^\"]*)\"\s+\"([^\"]*)\"/) {
207	    $title = "$1($2) $4 ($3) $1($2)";
208	}
209
210	# Build per-section info arrays
211
212	if (($type, $text) = /^\.S([HS])\s+\"?([^\"]*)\"?/) {
213
214	    push(@sectionlines, $line);	    # Index of first line of section
215	    push(@sectiontypes, $type eq 'H' ? 0 : 1);	# Type of section
216	    $text =~ s/\s*$//;		    # Remove trailing whitespace
217	    push(@sectiontexts, $text);	    # Title of section (key for href)
218	    $text =~ s/\s*\(\+\)$//;	    # Remove (+)
219	    if ($shortfiles) {
220		$file = $#sectionlines;	    # Short filenames; use number
221	    } else {
222		$file = $text;		    # Long filenames; use title
223		$file =~ s/[\s\/]+/_/g;	    # Replace whitespace and / with _
224	    }
225	    $file .= ".$html" unless $single;
226	    push(@sectionfiles, $file);	    # File in which to store section
227	    $name{"$text B"} = ($single ? '#' : '') . $file;
228					    # Index entry for &make_hrefs
229	    push(@name, "$text\t" . $name{"$text B"}) if $index;
230					    # Index entry for CGI script
231	    # Look for anchors in the rest of this section if $link_me{$text}
232	    # is non-null, and mark them with the font which is its value
233
234	    $font = $link_me{$text};
235    	}
236	&make_name(*name, *font, *file, *index, *_) if $font;
237    }
238    $line++;
239}
240
241### Make top page
242
243open(TOP, ">$dir/$topfile");
244select TOP;
245
246# Top page header
247
248print <<EOP;
249<HEAD>
250<TITLE>$title</TITLE>
251</HEAD>
252<BODY>
253<A NAME="top"></A>
254<H1>$title</H1>
255<HR>
256EOP
257
258# FORM block, if we're making an index
259
260$action = $cgibin ? "http://$host/$cgibindir/$cgifile" : $cgifile;
261
262print <<EOP if $index;
263<FORM METHOD="GET" ACTION="$action">
264Go directly to a section, command or variable: <INPUT NAME="input">
265</FORM>
266EOP
267
268# Table of contents
269
270print <<EOP;
271<H2>
272EOP
273
274foreach $section (1 .. $#sectionlines) {
275    if ($sectiontypes[$section - 1] < $sectiontypes[$section]) {
276	print "</H2> <menu>\n";	    # Indent, smaller font
277    } elsif ($sectiontypes[$section - 1] > $sectiontypes[$section]) {
278	print "</menu> <H2>\n";	    # Outdent, larger font
279    }
280    if ($inline_me{$sectiontexts[$section]}) {    # Section is in %inline_me
281
282	# Print section inline
283
284	print "$sectiontexts[$section]\n";
285	print "</H2> <menu>\n";	    # Indent, smaller font
286	&printsectionbody(*man, *sectionlines, *section, *name);
287	print "</menu> <H2>\n";	    # Outdent, larger font
288    } else {
289
290	# Print link to section
291
292	print "<A HREF=\"", $single ? '#' : '',
293	    "$sectionfiles[$section]\">$sectiontexts[$section]</A><BR>\n";
294    }
295}
296
297print <<EOP;
298</H2>
299EOP
300
301print "<HR>\n" if $single;
302
303### Make sections
304
305foreach $section (0 .. $#sectionlines) {
306
307    # Skip inlined sections
308
309    next if $inline_me{$sectiontexts[$section]};
310
311    if ($single) {
312
313	# Header
314
315	print <<EOP if $section;	# Skip header section
316<H2><A NAME="$sectionfiles[$section]">$sectiontexts[$section]</A></H2>
317<menu>
318EOP
319	&printsectionbody(*man, *sectionlines, *section, *name);
320	print <<EOP if $section;	# Skip header section
321<A HREF="#top">Table of Contents</A>
322</menu>
323EOP
324
325    } else {
326
327	# Make pointer line for header and trailer
328
329	$pointers  = "<A HREF=\"$topfile\">Up</A>";
330	$pointers .= "\n<A HREF=\"$sectionfiles[$section + 1]\">Next</A>"
331	    if ($section < $#sectionlines) &&
332	    ! $inline_me{$sectiontexts[$section + 1]};
333	$pointers .= "\n<A HREF=\"$sectionfiles[$section - 1]\">Previous</A>"
334	    if ($section > 1) &&		# section 0 is initial comments
335	    ! $inline_me{$sectiontexts[$section - 1]};
336
337	# Header
338
339	open(OUT, ">$dir/$sectionfiles[$section]");
340	select OUT;
341	print <<EOP;
342<HEAD>
343<TITLE>$sectiontexts[$section]</TITLE>
344</HEAD>
345<BODY>
346$pointers
347<H2>$sectiontexts[$section]</H2>
348EOP
349	&printsectionbody(*man, *sectionlines, *section, *name);
350
351	# Trailer
352
353	print <<EOP;
354$pointers
355</BODY>
356EOP
357
358    }
359}
360
361select TOP unless $single;
362
363# Top page trailer
364
365print <<EOP;
366</H2>
367<HR>
368Here are the <A HREF="$outfile">nroff manpage</A> (175K)
369from which this HTML version was generated,
370the <A HREF="$script">Perl script</A> which did the conversion
371and the <A HREF="ftp://ftp.astron.com/pub/tcsh/">
372complete source code</A> for <I>tcsh</I>.
373<HR>
374<I>tcsh</I> is maintained by
375Christos Zoulas <A HREF="mailto:christos\@gw.com">&lt;christos\@gw.com&gt;</A>
376and the <A HREF="$listsfile"><I>tcsh</I> maintainers' mailing list</A>.
377Dave Schweisguth <A HREF="mailto:dcs\@proton.chem.yale.edu">&lt;dcs\@proton.chem.yale.edu&gt;</A>
378wrote the manpage and the HTML conversion script.
379</BODY>
380EOP
381
382close TOP;
383
384### Make lists page
385
386open(LISTS, ">$dir/$listsfile");
387select LISTS;
388while(($_ = <DATA>) ne "END\n") {   # Text stored after __END__
389    s/TOPFILEHERE/$topfile/;
390    print;
391}
392close LISTS;
393
394### Make search script
395
396if ($index) {
397
398    # URL of $dir; see comments in search script
399
400    $root = $cgibin
401	? "'http://$host/" . ($updir ? "$updir/" : '') . "$dir/'"
402	: '"http://$ENV{\'SERVER_NAME\'}:$ENV{\'SERVER_PORT\'}" . (($_ = $ENV{\'SCRIPT_NAME\'}) =~ s|[^/]*$||, $_)';
403
404    # String for passing @name to search script
405
406    $name = join("',\n'", @name);
407
408    open(TOP, ">$dir/$cgifile");
409    select TOP;
410    while(($_ = <DATA>) ne "END\n") {   # Text stored after __END__
411	s/ROOTHERE/$root/;
412	s/NAMEHERE/$name/;
413	s/TOPFILEHERE/$topfile/;
414	print;
415    }
416    close TOP;
417    chmod(0755, "$dir/$cgifile") ||
418	die "$whatami: Can't chmod 0755 $dir/$cgifile!\n";
419    warn "$whatami: Don't forget to move $dir/$cgifile to /$cgibindir.\n"
420	if $cgibin;
421}
422
423### That's all, folks
424
425exit;
426
427### Subroutines
428
429# Process and print the body of a section
430
431sub printsectionbody {
432
433    local(*man, *sectionlines, *sline, *name) = @_;	# Number of section
434    local($sfirst, $slast, @paralines, @paratypes, $comment, $dl, $pline,
435	  $comment, $pfirst, $plast, @para, @tag, $changeindent);
436
437    # Define section boundaries
438
439    $sfirst = $sectionlines[$sline] + 1;
440    if ($sline == $#sectionlines) {
441	$slast = $#man;
442    } else {
443	$slast = $sectionlines[$sline + 1] - 1;
444    }
445
446    # Find paragraph markers, ignoring those between '.ig' and '..'
447
448    if ($man[$sfirst] =~ /^\.[PIT]P/) {
449	@paralines = ();
450	@paratypes = ();
451    } else {
452	@paralines = ($sfirst - 1);		# .P follows .S[HS] by default
453	@paratypes = ('P');
454    }
455    $comment = 0;
456    foreach ($sfirst .. $slast) {
457	if ($man[$_] =~ /^\.ig/) {		# Start ignoring
458	    $comment = 1;
459	} elsif ($man[$_] =~ /^\.\./) {		# Stop ignoring
460	    $comment = 0;
461	} elsif (! $comment && $man[$_] =~ /^\.([PIT])P/) {
462	    push(@paralines, $_);
463	    push(@paratypes, $1);
464	}
465    }
466
467    # Process paragraphs
468
469    $changeindent = 0;
470    $dl = 0;
471    foreach $pline (0 .. $#paralines) {
472
473	@para = ();
474	$comment = 0;
475
476	# Define para boundaries
477
478	$pfirst = $paralines[$pline] + 1;
479	if ($pline == $#paralines) {
480	    $plast = $slast;
481	} else {
482	    $plast = $paralines[$pline + 1] - 1;
483	}
484
485	foreach (@man[$pfirst .. $plast]) {
486	    if (/^\.ig/) {		    # nroff begin ignore
487		if ($comment == 0) {
488		    $comment = 2;
489		    push(@para, "<!--\n");
490		} elsif ($comment == 1) {
491		    $comment = 2;
492		} elsif ($comment == 2) {
493		    s/--/-/g;		    # Remove double-dashes in comments
494		    push(@para, $_);
495		}
496	    } elsif (/^\.\./) {		    # nroff end ignore
497		if ($comment == 0) {
498		    ;
499		} elsif ($comment == 1) {
500		    ;
501		} elsif ($comment == 2) {
502		    $comment = 1;
503		}
504	    } elsif (/^\.\\\"/) {	    # nroff comment
505		if ($comment == 0) {
506		    $comment = 1;
507		    push(@para, "<!--\n");
508		    s/^\.\\\"//;
509		} elsif ($comment == 1) {
510		    s/^\.\\\"//;
511		} elsif ($comment == 2) {
512		    ;
513		}
514		s/--/-/g;		    # Remove double-dashes in comments
515		push(@para, $_);
516	    } else {			    # Nothing to do with comments
517		if ($comment == 0) {
518		    ;
519    		} elsif ($comment == 1) {
520		    $comment = 0;
521		    push(@para, "-->\n");
522		} elsif ($comment == 2) {
523		    s/--/-/g;		    # Remove double-dashes in comments
524		}
525
526		unless ($comment) {
527
528		    if (/^\.TH/) {	    # Title; got this already
529			next;
530		    } elsif (/^\.PD/) {	    # Para spacing; unimplemented
531			next;
532		    } elsif (/^\.RS/) {	    # Indent (one width only)
533			$changeindent++;
534			next;
535		    } elsif (/^\.RE/) {	    # Outdent
536			$changeindent--;
537			next;
538		    }
539
540		    # Line break
541		    s/^\.br.*/<BR>/;
542
543		    # More nroff special characters
544
545		    s/^\\&amp\;//;	    # leading dot escape; save until
546					    #   now so leading dots aren't
547					    #   confused with ends of .igs
548
549		    &make_hrefs(*name, *_);
550		}
551		push(@para, $_);
552	    }
553	}
554
555	push(@para, "-->\n") if $comment;   # Close open comment
556
557    	# Print paragraph
558
559	if ($paratypes[$pline] eq 'P') {
560	    &font(*para);
561	    print   @para;
562	} elsif ($paratypes[$pline] eq 'I') {
563	    &font(*para);
564	    print   "<menu>\n",
565		    @para,
566		    "</menu>\n";
567	} else {			# T
568	    @tag = shift(@para);
569	    &font(*tag);
570	    &font(*para);
571	    print   "<DL compact>\n" unless $dl;
572	    print   "<DT>\n",
573		    @tag,
574		    "<DD>\n",
575		    @para;
576	    if ($pline == $#paratypes || $paratypes[$pline + 1] ne 'T') {
577		# Perl 5 lossage alert
578		# Next para is not a definition list
579		$dl = 0;		    # Close open definition list
580		print "</DL>\n";
581	    } else {
582		$dl = 1;		    # Leave definition list open
583	    }
584	}
585	print "<P>\n";
586
587	# Indent/outdent the *next* para
588
589	while ($changeindent > 0) {
590	    print "<menu>\n";
591	    $changeindent--;
592	}
593	while ($changeindent < 0) {
594	    print "</menu>\n";
595	    $changeindent++;
596	}
597    }
598    1;
599}
600
601# Make one name anchor in a line; cue on fonts (.B or .I) but leave them alone
602
603sub make_name {
604
605    local(*name, *font, *file, *index, *line) = @_;
606    local($text);
607
608    if (($text) = ($line =~ /^\.[BI]\s+([^\s\\]+)/)) {	# Found pattern
609
610	if (
611	    $text !~ /^-/		    # Avoid lists of options
612	    && (length($text) > 1	    # and history escapes
613		||  $text =~ /^[%:@]$/)	    # Special pleading for %, :, @
614	    && ! $name{"$text $font"}	    # Skip if there's one already
615	) {
616	    # Record link
617
618	    $name{"$text $font"} = ($single ? '' : $file) . "#$text";
619	    push(@name, "$text\t" . $name{"$text $font"}) if $index;
620
621	    # Put in the name anchor
622
623	    $line =~ s/^(\.[BI]\s+)([^\s\\]+)/$1<A NAME=\"$text\">$2<\/A>/;
624	}
625    }
626    $line;
627}
628
629# Make all the href anchors in a line; cue on fonts (\fB ... \fR or
630# \fI ... \fR) but leave them alone
631
632sub make_hrefs {
633
634    local(*name, *line) = @_;
635    local(@pieces, $piece);
636
637    @pieces = split(/(\\f[BI][^\\]*\\fR)/, $line);
638
639    $piece = 0;
640    foreach (@pieces) {
641	if (/\\f([BI])([^\\]*)\\fR/	# Found a possibility
642
643	# It's not followed by (, i.e. it's not a manpage reference
644
645	&& substr($pieces[$piece + 1], 0, 1) ne '(') {
646	    $key = "$2 $1";
647	    if ($name{$key}) {			# If there's a matching name
648		s/(\\f[BI])([^\\]*)(\\fR)/$1<A HREF=\"$name{$key}\">$2<\/A>$3/;
649	    }
650	}
651	$piece++;
652    }
653    $line = join('', @pieces);
654}
655
656# Convert nroff font escapes to HTML
657# Expects comments and breaks to be in HTML form already
658
659sub font {
660
661    local(*para) = @_;
662    local($i, $j, @begin, @end, $part, @pieces, $bold, $italic);
663
664    return 0 if $#para == -1;   # Ignore empty paragraphs
665				# Perl 5 lossage alert
666
667    # Find beginning and end of each part between HTML comments
668
669    $i = 0;
670    @begin = ();
671    @end = ();
672    foreach (@para) {
673	push(@begin, $i + 1) if /^-->/ || /^<BR>/;
674	push(@end, $i - 1) if /^<!--/ || /^<BR>/;
675	$i++;
676    }
677    if ($para[0] =~ /^<!--/ || $para[0] =~ /^<BR>/) {
678	shift(@end);
679    } else {
680	unshift(@begin, 0);	# Begin at the beginning
681    }
682    if ($para[$#para] =~ /^-->/ || $para[$#para] =~ /^<BR>/) {
683	pop(@begin);
684    } else {
685	push(@end, $#para);	# End at the end
686    }
687
688    # Fontify each part
689
690    $bold = $italic = 0;
691    foreach $i (0 .. $#begin) {
692	$* = 1;
693	$part = join('', @para[$begin[$i] .. $end[$i]]);
694	$part =~ s/^\.([BI])\s+(.*)$/\\f$1$2\\fR/g;	    # .B, .I
695	@pieces = split(/(\\f[BIR])/, $part);
696	$part = '';
697	foreach $j (@pieces) {
698	    if ($j eq '\fB') {
699		if ($italic) {
700		    $italic = 0;
701		    $part .= '</I>';
702		}
703		unless ($bold) {
704		    $bold = 1;
705		    $part .= '<B>';
706		}
707	    } elsif ($j eq '\fI') {
708		if ($bold) {
709		    $bold = 0;
710		    $part .= '</B>';
711		}
712		unless ($italic) {
713		    $italic = 1;
714		    $part .= '<I>';
715		}
716	    } elsif ($j eq '\fR') {
717		if ($bold) {
718		    $bold = 0;
719		    $part .= '</B>';
720		} elsif ($italic) {
721		    $italic = 0;
722		    $part .= '</I>';
723		}
724	    } else {
725		$part .= $j;
726	    }
727	}
728	$* = 0;
729
730	# Close bold/italic before break
731
732	if ($end[$i] == $#para || $para[$end[$i] + 1] =~ /^<BR>/) {
733	    # Perl 5 lossage alert
734	    if ($bold) {
735		$bold = 0;
736		$part =~ s/(\n)?$/<\/B>$1\n/;
737	    } elsif ($italic) {
738		$italic = 0;
739		$part =~ s/(\n)?$/<\/I>$1\n/;
740	    }
741	}
742
743	# Rebuild this section of @para
744
745	foreach $j ($begin[$i] .. $end[$i]) {
746	    $part =~ s/^([^\n]*(\n|$))//;
747	    $para[$j] = $1;
748	}
749    }
750
751    # Close bold/italic on last non-comment line
752    # Do this only here because fonts pass through comments
753
754    $para[$end[$#end]] =~ s/(\n)?$/<\/B>$1/ if $bold;
755    $para[$end[$#end]] =~ s/(\n)?$/<\/I>$1/ if $italic;
756}
757
758sub usage {
759    local ($message) = $_[0];
760
761    warn $message if $message;
762    warn <<EOP;
763Usage: $whatami [-1icsu] [-C dir] [-d dir] [-h host] [file]
764Without [file], reads from tcsh.man or stdin.
765-1	    Makes a single page instead of a table of contents and sections
766-i	    Makes a CGI searchable index script, tcsh.html/tcsh.cgi, intended
767	    for a server which respects the .cgi extension in any directory.
768-c	    Like -i,  but the CGI script is intended for a server which wants
769	    scripts in /cgi-bin (or some other privileged directory separate
770	    from the rest of the HTML) and must be moved there by hand.
771-C dir	    Uses /dir instead of /cgi-bin as the CGI bin dir.
772	    Meaningless without -c.
773-d dir	    Uses /dir/tcsh.html instead of /tcsh.html as the HTML dir.
774	    Meaningless without -c.
775-D dir	    Uses /dir.html instead of /tcsh.html as the HTML dir.
776	    Meaningless without -c.
777-G name	    Uses name instead of tcsh.cgi as the name of the CGI script.
778	    Meaningless without -c or -i.
779-h host	    Uses host as the host:port part of the URL to the entry point.
780	    Meaningless without -c.
781-s	    Filenames are shorter (max 8 + 3) but less descriptive.
782-u	    This message
783EOP
784    exit !! $message;
785}
786
787### Inlined documents. Watch for *HERE tokens.
788
789__END__
790<HEAD>
791<TITLE>The tcsh mailing lists</TITLE>
792</HEAD>
793<BODY>
794<A HREF="TOPFILEHERE">Up</A>
795<H2>The <I>tcsh</I> mailing lists</H2>
796There are three <I>tcsh</I> mailing lists:
797<DL>
798<DT>
799<I>tcsh@mx.gw.com</I>
800<DD>
801The <I>tcsh</I> maintainers and testers' mailing list.
802<DT>
803<I>tcsh-bugs@mx.gw.com</I>
804<DD>
805Open bug and user comment discussion.
806</DL>
807You can subscribe to either of these lists by visiting
808<I><A HREF="http://mx.gw.com/">http://mx.gw.com/</A></I>
809<P>
810To file a bug report or a feature suggestion (preferably
811with code), please visit
812<I><A HREF="http://bugs.gw.com/">http://bugs.gw.com/</A></I>
813<P>
814<A HREF="TOPFILEHERE">Up</A>
815</BODY>
816END
817: # -*- perl -*-
818
819# Emulate #!/usr/local/bin/perl on systems without #!
820
821eval '(exit $?0)' && eval 'exec perl -S $0 ${1+"$@"}'
822& eval 'exec perl -S $0 $argv:q' if 0;
823
824# Setup
825
826# Location: doesn't work with relative URLs, so we need to know where to find
827#   the top and section files.
828# If the search engine is in /cgi-bin, we need a hard-coded URL.
829# If the search engine is in the same directory, we can figure it out from CGI
830#   environment variables.
831
832$root = ROOTHERE;
833$topfile = 'TOPFILEHERE';
834@name = (
835'NAMEHERE'
836);
837
838# Do the search
839
840$input = $ENV{'QUERY_STRING'};
841$input =~ s/^input=//;
842$input =~ s/\+/ /g;
843print "Status: 302 Found\n";
844if ($input ne '' && ($key = (grep(/^$input/,  @name))[0] ||
845			    (grep(/^$input/i, @name))[0] ||
846			    (grep( /$input/i, @name))[0]   )) {
847    $key =~ /\t([^\t]*)$/;
848    print "Location: $root$1\n\n";
849} else {
850    print "Location: $root$topfile\n\n";
851}
852END
853