Expunge revision histories in files.
[sw-tools] / perl / SWMan.pm
CommitLineData
961ce1c2 1# -*-perl-*-
2#
9796a787 3# $Id: SWMan.pm,v 1.5 2004/04/08 01:52:19 mdw Exp $
961ce1c2 4#
5# Display and other fiddling of manual pages
6#
7# (c) 1999 EBI
8#
9
10#----- Licensing notice -----------------------------------------------------
11#
12# This file is part of sw-tools.
13#
14# sw-tools is free software; you can redistribute it and/or modify
15# it under the terms of the GNU General Public License as published by
16# the Free Software Foundation; either version 2 of the License, or
17# (at your option) any later version.
18#
19# sw-tools is distributed in the hope that it will be useful,
20# but WITHOUT ANY WARRANTY; without even the implied warranty of
21# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22# GNU General Public License for more details.
23#
24# You should have received a copy of the GNU General Public License
25# along with sw-tools; if not, write to the Free Software Foundation,
26# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27
961ce1c2 28#----- Package preamble -----------------------------------------------------
29
30package SWMan;
31
32use IO;
33use POSIX;
34use DirHandle;
35use Exporter;
36
37use SWConfig;
38use SWCGI qw(:DEFAULT :layout);
39
40@ISA = qw(Exporter);
fae2108b 41@EXPORT_OK = qw(subst urlsubst check);
961ce1c2 42
43#----- Useful functions -----------------------------------------------------
44
45%mandb = ();
46
47# --- @mans(SECTION)@ ---
48#
49# Returns a reference to a list of manual pages in the given section.
50
51sub mans($) {
52 my ($sec) = @_;
53 $mandb{$sec} and return $mandb{sec};
54
55 my $d = DirHandle->new("$C{prefix}/man/man$sec") or return undef;
56 my @f;
57 while (my $f = $d->read()) {
58 push(@f, $f);
59 }
60 $mandb{$sec} = \@f;
61 return \@f;
62}
63
64# --- @check(NAME, SECTION)@ ---
65#
66# See whether there's a manpage called NAME with section SECTION.
67
68sub check($$) {
69 my $pre = "$C{prefix}/man/man";
70 my ($man, $sec) = @_;
71 my $f;
72
73 # --- Quick check for obvious things ---
74
75 my ($base) = ($sec =~ /^(\d+)/);
76 $f = "$pre$base/$man.$sec";
77 -r $f and return $f; $f .= ".gz"; -r $f and return $f;
78
79 # --- Snarf the appropriate filename list ---
80
81 my $fs = mans($base) or return undef;
82 foreach my $f (@$fs) {
83 $f =~ /^$man\.$sec\w+(\.gz)?$/ and return "$C{prefix}/man/man$base/$f";
84 }
85 return undef;
86}
87
88# --- @subst(STRING, NAME, SECTION)@ ---
89#
90# If NAME(SECTION) is a manual page, return the STRING appropriately wrapped
91# in an anchor element; otherwise return it unmolested.
92
93sub subst($$$) {
94 my ($s, $n, $sec) = @_;
95 check($n, $sec) and
fef14233 96 return sprintf("<a href=\"$ref?act=man&man=%s&sec=$sec\">$s</a>",
97 SWCGI::sanitize($n));
98 return $s;
961ce1c2 99}
100
fae2108b 101# --- @urlsubst(URL, STRING)@ ---
102#
103# Substitutes in a URL reference. The important bit is that embedded `&'
104# characters are un-entitied from `&amp;'. This doesn't seem to upset
105# Netscape or Lynx as much as I'd expect (or, in fact, at all), but it's
106# slightly untidy.
107
108sub urlsubst($$) {
109 my ($url, $name) = @_;
110 $url =~ s/\&amp\;/&/;
111 return "<a href=\"$url\">$name</a>";
112}
113
961ce1c2 114# --- @sections()@ ---
115#
116# Return a list of manual sections.
117
118@sectionlist = ();
119
120sub sections() {
121 return @sectionlist if @sectionlist;
122 my @s = ();
123 my $d = DirHandle->new("$C{prefix}/man") or
124 barf("couldn't open man directory: $!");
125 while ($f = $d->read()) {
126 next if $f !~ /^man/ || !-d "$C{prefix}/man/$f";
127 push(@s, $');
128 }
129 return (@sectionlist = sort(@s));
130}
131
132#----- Display a quick section index ----------------------------------------
133
134sub quickie {
135 print "Quick section index:\n";
136 foreach $s (sections()) {
137 print "<a href=\"$ref?act=man&sec=$s\">$s</a>\n";
138 }
139}
140
141#----- Display indices for manual sections ----------------------------------
142
143sub dosection($) {
144 my ($sec) = @_;
145 my @m = ();
146
147 barf("illegal section `$sec'") if $sec =~ m:/:;
148
149 # --- Snarf the list of manual pages in this section ---
150
151 {
152 my $d = DirHandle->new("$C{prefix}/man/man$sec") or
153 barf("couldn't read directory `$C{prefix}/man/man$sec': $!");
154 while (my $f = $d->read()) {
155 my ($man, $sec) = split(/\./, $f, 3);
156 push(@m, "$man($sec)") if $sec;
157 }
158 }
159
160 # --- Sort and emit the index ---
161
162 print("<h4>Section $sec</h4>\n<table>");
163
164 {
165 my $col = 0;
166 foreach my $m (sort(@m)) {
167 my ($man, $sec) = $m =~ /^(.*)\((.*)\)$/;
168 $col or print("<tr>\n");
169 print("<td><a href=\"$ref?act=man&man=$man&sec=$sec\">$m</a>\n");
170 $col = ($col + 1) % 5;
171 }
172 }
173
174 print("</table>\n");
175}
176
177sub section {
178 my $sec = $Q{"sec"};
179 header("Index of manual section $sec");
180 quickie(); print "<hr>\n";
181 dosection($sec);
182 print "<hr>\n"; quickie();;
183 footer();
184}
185
186sub index {
187 header("Manual page index");
188 print("<h3>Manual page index</h3>\n");
189 foreach my $s (sections()) { dosection($s); }
190 footer();
191}
192
193#----- Display a manual page ------------------------------------------------
194
195sub man {
196 my ($man, $sec) = ($Q{"man"}, $Q{"sec"});
197
198 $sec or &index(), return;
199 $man or &section(), return;
200
201 my $file = check($man, $sec) or
202 barf("no manual page $man($sec)");
203 barf("illegal filename `$file'") if $file =~ m:\./:;
204
205 # --- Read the manual page ---
206
207 my $p = IO::Pipe->new();
208 my $kid = fork();
209 defined($kid) or barf("fork failed: $!");
210 if ($kid == 0) {
211 $p->writer();
212 dup2($p->fileno(), 1);
213 chdir("$C{prefix}/man");
214 if ($file =~ /\.gz$/) {
215 $pp = IO::Pipe->new;
216 $kkid = fork();
217 defined($kid) or exit(127);
218 if ($kkid == 0) {
219 $pp->writer();
220 dup2($pp->fileno, 1);
221 exec("gzip", "-dc", $file);
222 exit(127);
223 }
224 exec("nroff", "-man");
225 } else {
226 exec("nroff", "-man", $file);
227 }
228 exit(127);
229 }
230 $p->reader();
231
232 # --- Spit out the manual page now ---
233
234 header("Manual page $Q{man}($Q{sec})");
235 quickie(); print "<hr>\n";
236 print "<pre>\n";
237 while (my $line = $p->getline()) {
238 chomp $line;
239
240 # --- Grind through the line turning it into HTML ---
241
242 {
243 my $state = "";
244 my $l = "";
245
246 for (my $i = 0; $i < length($line); $i++) {
247 my $ch = substr($line, $i, 1);
248 my $nstate = "";
249
250 # --- Sort out overstriking ---
251
252 if (substr($line, $i + 1, 1) eq "\b") {
253 my ($italic, $bold) = (0, 0);
254 $ch eq "_" and $italic = 1;
255 $ch eq substr($line, $i + 2, 1) and $bold = 1;
256 $ch = substr($line, $i + 2, 1);
257 while (substr($line, $i + 1, 1) eq "\b") { $i += 2; }
258 if ($italic && $bold) {
259 $nstate = $state ? $state : "b";
260 } elsif ($italic) {
261 $nstate = "i";
262 } elsif ($bold) {
263 $nstate = "b";
264 }
265 }
266 $state ne $nstate and
267 $l .= ($state && "</$state>") . ($nstate && "<$nstate>");
268 $state = $nstate;
269
270 # --- Translate the character if it's magical ---
271
272 $ch eq "&" and $ch = "&amp;";
fae2108b 273 $ch eq "<" and $ch = "&lt;<";
274 $ch eq ">" and $ch = ">&gt;";
961ce1c2 275 $l .= $ch;
276 }
277 $state and $l .= "</$state>";
278
279 # --- Now find manual references in there ---
280 #
281 # I don't use /x regexps very often, but I think this is a good excuse.
282
283 $l =~ s! ((?:\<[bi]\>)*) # Leading highlighting tags
284 ([-_.\w]+) # Various plausible manual name chars
285 ((?:\</[bi]\>)* # Closing highlighting tags
286 (?:\<[bi]\>)* # And opening ones again
287 \( # An open parenthesis
288 (?:\<[bi]\>)*) # More opening highlights
289 (\d+\w*) # The section number
290 ((?:\</[bi]\>)* # Close highlights
291 \) # Close parens
292 (?:\</[bi]\>)*) # Finally more closing tags
293 ! subst($&, $2, $4) !egx;
294
295 # --- And email and hypertext references too ---
296
297 $l =~ s! ((?:\<[bi]\>)*) # Leading highlighting
fae2108b 298 ( \b (?: http s? | ftp | file | news ) # A protocol name
44b3c589 299 : # The important and obvious bit
fae2108b 300 [^]<>)\s<>\'\"]+ # Most characters are allowed
301 [^]<>).,\s<>\'\"]) # Don't end on punctuation
961ce1c2 302 ((?:\</[bi]\>)*) # Closing tags, optional
fae2108b 303 !urlsubst($2, $&)!egx;
961ce1c2 304
fae2108b 305 $l =~ s! ( (?:\<[bi]\>)* (?:\bmailto:)? )
306 ( [^\s()<>;:{}&<>,.\`\'\"] [^\s()<>;:{}&<>\`\'\"]* \@
307 [^\s()<>;:{&}<>\'\"]* [^\s()<>;:{}&<>.,\'\"])
961ce1c2 308 ((?:\</[bi]\>)*)
fae2108b 309 !<a href="mailto:$2">$&</a>!gx;
310
311 # --- Fix up the HTML ---
312
313 $l =~ s/\&lt\;\</&lt;/g;
314 $l =~ s/\>\&gt\;/&gt;/g;
961ce1c2 315
316 # --- Done! ---
317
318 print $l, "\n";
319 }
320 }
321
322 # --- Done all of that ---
323
324 print "</pre>\n";
325 $p->close();
326 waitpid($kid, 0);
327 barf("nroff failed (exit status $?)") if $?;
328 print "<hr>\n"; quickie();;
329 footer();
330}
331
332#----- Register actions -----------------------------------------------------
333
334$main::ACT{"man"} = \&man;
335
336#----- That's all, folks ----------------------------------------------------
337
3381;