xref: /openbsd-src/gnu/usr.bin/binutils/etc/texi2pod.pl (revision d2201f2f89f0be1a0be6f7568000ed297414a06d)
1*d2201f2fSdrahn#! /usr/bin/perl -w
2*d2201f2fSdrahn
3*d2201f2fSdrahn#   Copyright (C) 1999, 2000, 2001, 200 Free Software Foundation, Inc.
4*d2201f2fSdrahn
5*d2201f2fSdrahn# This file is part of GNU CC.
6*d2201f2fSdrahn
7*d2201f2fSdrahn# GNU CC is free software; you can redistribute it and/or modify
8*d2201f2fSdrahn# it under the terms of the GNU General Public License as published by
9*d2201f2fSdrahn# the Free Software Foundation; either version 2, or (at your option)
10*d2201f2fSdrahn# any later version.
11*d2201f2fSdrahn
12*d2201f2fSdrahn# GNU CC is distributed in the hope that it will be useful,
13*d2201f2fSdrahn# but WITHOUT ANY WARRANTY; without even the implied warranty of
14*d2201f2fSdrahn# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15*d2201f2fSdrahn# GNU General Public License for more details.
16*d2201f2fSdrahn
17*d2201f2fSdrahn# You should have received a copy of the GNU General Public License
18*d2201f2fSdrahn# along with GNU CC; see the file COPYING.  If not, write to
19*d2201f2fSdrahn# the Free Software Foundation, 59 Temple Place - Suite 330,
20*d2201f2fSdrahn# Boston MA 02111-1307, USA.
21*d2201f2fSdrahn
22*d2201f2fSdrahn# This does trivial (and I mean _trivial_) conversion of Texinfo
23*d2201f2fSdrahn# markup to Perl POD format.  It's intended to be used to extract
24*d2201f2fSdrahn# something suitable for a manpage from a Texinfo document.
25*d2201f2fSdrahn
26*d2201f2fSdrahn$output = 0;
27*d2201f2fSdrahn$skipping = 0;
28*d2201f2fSdrahn%sects = ();
29*d2201f2fSdrahn$section = "";
30*d2201f2fSdrahn@icstack = ();
31*d2201f2fSdrahn@endwstack = ();
32*d2201f2fSdrahn@skstack = ();
33*d2201f2fSdrahn@instack = ();
34*d2201f2fSdrahn$shift = "";
35*d2201f2fSdrahn%defs = ();
36*d2201f2fSdrahn$fnno = 1;
37*d2201f2fSdrahn$inf = "";
38*d2201f2fSdrahn$ibase = "";
39*d2201f2fSdrahn
40*d2201f2fSdrahnwhile ($_ = shift) {
41*d2201f2fSdrahn    if (/^-D(.*)$/) {
42*d2201f2fSdrahn	if ($1 ne "") {
43*d2201f2fSdrahn	    $flag = $1;
44*d2201f2fSdrahn	} else {
45*d2201f2fSdrahn	    $flag = shift;
46*d2201f2fSdrahn	}
47*d2201f2fSdrahn	$value = "";
48*d2201f2fSdrahn	($flag, $value) = ($flag =~ /^([^=]+)(?:=(.+))?/);
49*d2201f2fSdrahn	die "no flag specified for -D\n"
50*d2201f2fSdrahn	    unless $flag ne "";
51*d2201f2fSdrahn	die "flags may only contain letters, digits, hyphens, dashes and underscores\n"
52*d2201f2fSdrahn	    unless $flag =~ /^[a-zA-Z0-9_-]+$/;
53*d2201f2fSdrahn	$defs{$flag} = $value;
54*d2201f2fSdrahn    } elsif (/^-/) {
55*d2201f2fSdrahn	usage();
56*d2201f2fSdrahn    } else {
57*d2201f2fSdrahn	$in = $_, next unless defined $in;
58*d2201f2fSdrahn	$out = $_, next unless defined $out;
59*d2201f2fSdrahn	usage();
60*d2201f2fSdrahn    }
61*d2201f2fSdrahn}
62*d2201f2fSdrahn
63*d2201f2fSdrahnif (defined $in) {
64*d2201f2fSdrahn    $inf = gensym();
65*d2201f2fSdrahn    open($inf, "<$in") or die "opening \"$in\": $!\n";
66*d2201f2fSdrahn    $ibase = $1 if $in =~ m|^(.+)/[^/]+$|;
67*d2201f2fSdrahn} else {
68*d2201f2fSdrahn    $inf = \*STDIN;
69*d2201f2fSdrahn}
70*d2201f2fSdrahn
71*d2201f2fSdrahnif (defined $out) {
72*d2201f2fSdrahn    open(STDOUT, ">$out") or die "opening \"$out\": $!\n";
73*d2201f2fSdrahn}
74*d2201f2fSdrahn
75*d2201f2fSdrahnwhile(defined $inf) {
76*d2201f2fSdrahnwhile(<$inf>) {
77*d2201f2fSdrahn    # Certain commands are discarded without further processing.
78*d2201f2fSdrahn    /^\@(?:
79*d2201f2fSdrahn	 [a-z]+index		# @*index: useful only in complete manual
80*d2201f2fSdrahn	 |need			# @need: useful only in printed manual
81*d2201f2fSdrahn	 |(?:end\s+)?group	# @group .. @end group: ditto
82*d2201f2fSdrahn	 |page			# @page: ditto
83*d2201f2fSdrahn	 |node			# @node: useful only in .info file
84*d2201f2fSdrahn	 |(?:end\s+)?ifnottex   # @ifnottex .. @end ifnottex: use contents
85*d2201f2fSdrahn	)\b/x and next;
86*d2201f2fSdrahn
87*d2201f2fSdrahn    chomp;
88*d2201f2fSdrahn
89*d2201f2fSdrahn    # Look for filename and title markers.
90*d2201f2fSdrahn    /^\@setfilename\s+([^.]+)/ and $fn = $1, next;
91*d2201f2fSdrahn    /^\@settitle\s+([^.]+)/ and $tl = postprocess($1), next;
92*d2201f2fSdrahn
93*d2201f2fSdrahn    # Identify a man title but keep only the one we are interested in.
94*d2201f2fSdrahn    /^\@c\s+man\s+title\s+([A-Za-z0-9-]+)\s+(.+)/ and do {
95*d2201f2fSdrahn	if (exists $defs{$1}) {
96*d2201f2fSdrahn	    $fn = $1;
97*d2201f2fSdrahn	    $tl = postprocess($2);
98*d2201f2fSdrahn	}
99*d2201f2fSdrahn	next;
100*d2201f2fSdrahn    };
101*d2201f2fSdrahn
102*d2201f2fSdrahn    # Look for blocks surrounded by @c man begin SECTION ... @c man end.
103*d2201f2fSdrahn    # This really oughta be @ifman ... @end ifman and the like, but such
104*d2201f2fSdrahn    # would require rev'ing all other Texinfo translators.
105*d2201f2fSdrahn    /^\@c\s+man\s+begin\s+([A-Z]+)\s+([A-Za-z0-9-]+)/ and do {
106*d2201f2fSdrahn	$output = 1 if exists $defs{$2};
107*d2201f2fSdrahn        $sect = $1;
108*d2201f2fSdrahn	next;
109*d2201f2fSdrahn    };
110*d2201f2fSdrahn    /^\@c\s+man\s+begin\s+([A-Z]+)/ and $sect = $1, $output = 1, next;
111*d2201f2fSdrahn    /^\@c\s+man\s+end/ and do {
112*d2201f2fSdrahn	$sects{$sect} = "" unless exists $sects{$sect};
113*d2201f2fSdrahn	$sects{$sect} .= postprocess($section);
114*d2201f2fSdrahn	$section = "";
115*d2201f2fSdrahn	$output = 0;
116*d2201f2fSdrahn	next;
117*d2201f2fSdrahn    };
118*d2201f2fSdrahn
119*d2201f2fSdrahn    # handle variables
120*d2201f2fSdrahn    /^\@set\s+([a-zA-Z0-9_-]+)\s*(.*)$/ and do {
121*d2201f2fSdrahn	$defs{$1} = $2;
122*d2201f2fSdrahn	next;
123*d2201f2fSdrahn    };
124*d2201f2fSdrahn    /^\@clear\s+([a-zA-Z0-9_-]+)/ and do {
125*d2201f2fSdrahn	delete $defs{$1};
126*d2201f2fSdrahn	next;
127*d2201f2fSdrahn    };
128*d2201f2fSdrahn
129*d2201f2fSdrahn    next unless $output;
130*d2201f2fSdrahn
131*d2201f2fSdrahn    # Discard comments.  (Can't do it above, because then we'd never see
132*d2201f2fSdrahn    # @c man lines.)
133*d2201f2fSdrahn    /^\@c\b/ and next;
134*d2201f2fSdrahn
135*d2201f2fSdrahn    # End-block handler goes up here because it needs to operate even
136*d2201f2fSdrahn    # if we are skipping.
137*d2201f2fSdrahn    /^\@end\s+([a-z]+)/ and do {
138*d2201f2fSdrahn	# Ignore @end foo, where foo is not an operation which may
139*d2201f2fSdrahn	# cause us to skip, if we are presently skipping.
140*d2201f2fSdrahn	my $ended = $1;
141*d2201f2fSdrahn	next if $skipping && $ended !~ /^(?:ifset|ifclear|ignore|menu|iftex)$/;
142*d2201f2fSdrahn
143*d2201f2fSdrahn	die "\@end $ended without \@$ended at line $.\n" unless defined $endw;
144*d2201f2fSdrahn	die "\@$endw ended by \@end $ended at line $.\n" unless $ended eq $endw;
145*d2201f2fSdrahn
146*d2201f2fSdrahn	$endw = pop @endwstack;
147*d2201f2fSdrahn
148*d2201f2fSdrahn	if ($ended =~ /^(?:ifset|ifclear|ignore|menu|iftex)$/) {
149*d2201f2fSdrahn	    $skipping = pop @skstack;
150*d2201f2fSdrahn	    next;
151*d2201f2fSdrahn	} elsif ($ended =~ /^(?:example|smallexample|display)$/) {
152*d2201f2fSdrahn	    $shift = "";
153*d2201f2fSdrahn	    $_ = "";	# need a paragraph break
154*d2201f2fSdrahn	} elsif ($ended =~ /^(?:itemize|enumerate|[fv]?table)$/) {
155*d2201f2fSdrahn	    $_ = "\n=back\n";
156*d2201f2fSdrahn	    $ic = pop @icstack;
157*d2201f2fSdrahn	} else {
158*d2201f2fSdrahn	    die "unknown command \@end $ended at line $.\n";
159*d2201f2fSdrahn	}
160*d2201f2fSdrahn    };
161*d2201f2fSdrahn
162*d2201f2fSdrahn    # We must handle commands which can cause skipping even while we
163*d2201f2fSdrahn    # are skipping, otherwise we will not process nested conditionals
164*d2201f2fSdrahn    # correctly.
165*d2201f2fSdrahn    /^\@ifset\s+([a-zA-Z0-9_-]+)/ and do {
166*d2201f2fSdrahn	push @endwstack, $endw;
167*d2201f2fSdrahn	push @skstack, $skipping;
168*d2201f2fSdrahn	$endw = "ifset";
169*d2201f2fSdrahn	$skipping = 1 unless exists $defs{$1};
170*d2201f2fSdrahn	next;
171*d2201f2fSdrahn    };
172*d2201f2fSdrahn
173*d2201f2fSdrahn    /^\@ifclear\s+([a-zA-Z0-9_-]+)/ and do {
174*d2201f2fSdrahn	push @endwstack, $endw;
175*d2201f2fSdrahn	push @skstack, $skipping;
176*d2201f2fSdrahn	$endw = "ifclear";
177*d2201f2fSdrahn	$skipping = 1 if exists $defs{$1};
178*d2201f2fSdrahn	next;
179*d2201f2fSdrahn    };
180*d2201f2fSdrahn
181*d2201f2fSdrahn    /^\@(ignore|menu|iftex)\b/ and do {
182*d2201f2fSdrahn	push @endwstack, $endw;
183*d2201f2fSdrahn	push @skstack, $skipping;
184*d2201f2fSdrahn	$endw = $1;
185*d2201f2fSdrahn	$skipping = 1;
186*d2201f2fSdrahn	next;
187*d2201f2fSdrahn    };
188*d2201f2fSdrahn
189*d2201f2fSdrahn    next if $skipping;
190*d2201f2fSdrahn
191*d2201f2fSdrahn    # Character entities.  First the ones that can be replaced by raw text
192*d2201f2fSdrahn    # or discarded outright:
193*d2201f2fSdrahn    s/\@copyright\{\}/(c)/g;
194*d2201f2fSdrahn    s/\@dots\{\}/.../g;
195*d2201f2fSdrahn    s/\@enddots\{\}/..../g;
196*d2201f2fSdrahn    s/\@([.!? ])/$1/g;
197*d2201f2fSdrahn    s/\@[:-]//g;
198*d2201f2fSdrahn    s/\@bullet(?:\{\})?/*/g;
199*d2201f2fSdrahn    s/\@TeX\{\}/TeX/g;
200*d2201f2fSdrahn    s/\@pounds\{\}/\#/g;
201*d2201f2fSdrahn    s/\@minus(?:\{\})?/-/g;
202*d2201f2fSdrahn    s/\\,/,/g;
203*d2201f2fSdrahn
204*d2201f2fSdrahn    # Now the ones that have to be replaced by special escapes
205*d2201f2fSdrahn    # (which will be turned back into text by unmunge())
206*d2201f2fSdrahn    s/&/&amp;/g;
207*d2201f2fSdrahn    s/\@\{/&lbrace;/g;
208*d2201f2fSdrahn    s/\@\}/&rbrace;/g;
209*d2201f2fSdrahn    s/\@\@/&at;/g;
210*d2201f2fSdrahn
211*d2201f2fSdrahn    # Inside a verbatim block, handle @var specially.
212*d2201f2fSdrahn    if ($shift ne "") {
213*d2201f2fSdrahn	s/\@var\{([^\}]*)\}/<$1>/g;
214*d2201f2fSdrahn    }
215*d2201f2fSdrahn
216*d2201f2fSdrahn    # POD doesn't interpret E<> inside a verbatim block.
217*d2201f2fSdrahn    if ($shift eq "") {
218*d2201f2fSdrahn	s/</&lt;/g;
219*d2201f2fSdrahn	s/>/&gt;/g;
220*d2201f2fSdrahn    } else {
221*d2201f2fSdrahn	s/</&LT;/g;
222*d2201f2fSdrahn	s/>/&GT;/g;
223*d2201f2fSdrahn    }
224*d2201f2fSdrahn
225*d2201f2fSdrahn    # Single line command handlers.
226*d2201f2fSdrahn
227*d2201f2fSdrahn    /^\@include\s+(.+)$/ and do {
228*d2201f2fSdrahn	push @instack, $inf;
229*d2201f2fSdrahn	$inf = gensym();
230*d2201f2fSdrahn
231*d2201f2fSdrahn	# Try cwd and $ibase.
232*d2201f2fSdrahn	open($inf, "<" . $1)
233*d2201f2fSdrahn	    or open($inf, "<" . $ibase . "/" . $1)
234*d2201f2fSdrahn		or die "cannot open $1 or $ibase/$1: $!\n";
235*d2201f2fSdrahn	next;
236*d2201f2fSdrahn    };
237*d2201f2fSdrahn
238*d2201f2fSdrahn    /^\@(?:section|unnumbered|unnumberedsec|center)\s+(.+)$/
239*d2201f2fSdrahn	and $_ = "\n=head2 $1\n";
240*d2201f2fSdrahn    /^\@subsection\s+(.+)$/
241*d2201f2fSdrahn	and $_ = "\n=head3 $1\n";
242*d2201f2fSdrahn
243*d2201f2fSdrahn    # Block command handlers:
244*d2201f2fSdrahn    /^\@itemize\s+(\@[a-z]+|\*|-)/ and do {
245*d2201f2fSdrahn	push @endwstack, $endw;
246*d2201f2fSdrahn	push @icstack, $ic;
247*d2201f2fSdrahn	$ic = $1;
248*d2201f2fSdrahn	$_ = "\n=over 4\n";
249*d2201f2fSdrahn	$endw = "itemize";
250*d2201f2fSdrahn    };
251*d2201f2fSdrahn
252*d2201f2fSdrahn    /^\@enumerate(?:\s+([a-zA-Z0-9]+))?/ and do {
253*d2201f2fSdrahn	push @endwstack, $endw;
254*d2201f2fSdrahn	push @icstack, $ic;
255*d2201f2fSdrahn	if (defined $1) {
256*d2201f2fSdrahn	    $ic = $1 . ".";
257*d2201f2fSdrahn	} else {
258*d2201f2fSdrahn	    $ic = "1.";
259*d2201f2fSdrahn	}
260*d2201f2fSdrahn	$_ = "\n=over 4\n";
261*d2201f2fSdrahn	$endw = "enumerate";
262*d2201f2fSdrahn    };
263*d2201f2fSdrahn
264*d2201f2fSdrahn    /^\@([fv]?table)\s+(\@[a-z]+)/ and do {
265*d2201f2fSdrahn	push @endwstack, $endw;
266*d2201f2fSdrahn	push @icstack, $ic;
267*d2201f2fSdrahn	$endw = $1;
268*d2201f2fSdrahn	$ic = $2;
269*d2201f2fSdrahn	$ic =~ s/\@(?:samp|strong|key|gcctabopt|env)/B/;
270*d2201f2fSdrahn	$ic =~ s/\@(?:code|kbd)/C/;
271*d2201f2fSdrahn	$ic =~ s/\@(?:dfn|var|emph|cite|i)/I/;
272*d2201f2fSdrahn	$ic =~ s/\@(?:file)/F/;
273*d2201f2fSdrahn	$_ = "\n=over 4\n";
274*d2201f2fSdrahn    };
275*d2201f2fSdrahn
276*d2201f2fSdrahn    /^\@((?:small)?example|display)/ and do {
277*d2201f2fSdrahn	push @endwstack, $endw;
278*d2201f2fSdrahn	$endw = $1;
279*d2201f2fSdrahn	$shift = "\t";
280*d2201f2fSdrahn	$_ = "";	# need a paragraph break
281*d2201f2fSdrahn    };
282*d2201f2fSdrahn
283*d2201f2fSdrahn    /^\@itemx?\s*(.+)?$/ and do {
284*d2201f2fSdrahn	if (defined $1) {
285*d2201f2fSdrahn	    # Entity escapes prevent munging by the <> processing below.
286*d2201f2fSdrahn	    $_ = "\n=item $ic\&LT;$1\&GT;\n";
287*d2201f2fSdrahn	} else {
288*d2201f2fSdrahn	    $_ = "\n=item $ic\n";
289*d2201f2fSdrahn	    $ic =~ y/A-Ya-y/B-Zb-z/;
290*d2201f2fSdrahn	    $ic =~ s/(\d+)/$1 + 1/eg;
291*d2201f2fSdrahn	}
292*d2201f2fSdrahn    };
293*d2201f2fSdrahn
294*d2201f2fSdrahn    $section .= $shift.$_."\n";
295*d2201f2fSdrahn}
296*d2201f2fSdrahn# End of current file.
297*d2201f2fSdrahnclose($inf);
298*d2201f2fSdrahn$inf = pop @instack;
299*d2201f2fSdrahn}
300*d2201f2fSdrahn
301*d2201f2fSdrahndie "No filename or title\n" unless defined $fn && defined $tl;
302*d2201f2fSdrahn
303*d2201f2fSdrahn$sects{NAME} = "$fn \- $tl\n";
304*d2201f2fSdrahn$sects{FOOTNOTES} .= "=back\n" if exists $sects{FOOTNOTES};
305*d2201f2fSdrahn
306*d2201f2fSdrahnfor $sect (qw(NAME SYNOPSIS DESCRIPTION OPTIONS ENVIRONMENT FILES
307*d2201f2fSdrahn	      BUGS NOTES FOOTNOTES SEEALSO AUTHOR COPYRIGHT)) {
308*d2201f2fSdrahn    if(exists $sects{$sect}) {
309*d2201f2fSdrahn	$head = $sect;
310*d2201f2fSdrahn	$head =~ s/SEEALSO/SEE ALSO/;
311*d2201f2fSdrahn	print "=head1 $head\n\n";
312*d2201f2fSdrahn	print scalar unmunge ($sects{$sect});
313*d2201f2fSdrahn	print "\n";
314*d2201f2fSdrahn    }
315*d2201f2fSdrahn}
316*d2201f2fSdrahn
317*d2201f2fSdrahnsub usage
318*d2201f2fSdrahn{
319*d2201f2fSdrahn    die "usage: $0 [-D toggle...] [infile [outfile]]\n";
320*d2201f2fSdrahn}
321*d2201f2fSdrahn
322*d2201f2fSdrahnsub postprocess
323*d2201f2fSdrahn{
324*d2201f2fSdrahn    local $_ = $_[0];
325*d2201f2fSdrahn
326*d2201f2fSdrahn    # @value{foo} is replaced by whatever 'foo' is defined as.
327*d2201f2fSdrahn    while (m/(\@value\{([a-zA-Z0-9_-]+)\})/g) {
328*d2201f2fSdrahn	if (! exists $defs{$2}) {
329*d2201f2fSdrahn	    print STDERR "Option $2 not defined\n";
330*d2201f2fSdrahn	    s/\Q$1\E//;
331*d2201f2fSdrahn	} else {
332*d2201f2fSdrahn	    $value = $defs{$2};
333*d2201f2fSdrahn	    s/\Q$1\E/$value/;
334*d2201f2fSdrahn	}
335*d2201f2fSdrahn    }
336*d2201f2fSdrahn
337*d2201f2fSdrahn    # Formatting commands.
338*d2201f2fSdrahn    # Temporary escape for @r.
339*d2201f2fSdrahn    s/\@r\{([^\}]*)\}/R<$1>/g;
340*d2201f2fSdrahn    s/\@(?:dfn|var|emph|cite|i)\{([^\}]*)\}/I<$1>/g;
341*d2201f2fSdrahn    s/\@(?:code|kbd)\{([^\}]*)\}/C<$1>/g;
342*d2201f2fSdrahn    s/\@(?:gccoptlist|samp|strong|key|option|env|command|b)\{([^\}]*)\}/B<$1>/g;
343*d2201f2fSdrahn    s/\@sc\{([^\}]*)\}/\U$1/g;
344*d2201f2fSdrahn    s/\@file\{([^\}]*)\}/F<$1>/g;
345*d2201f2fSdrahn    s/\@w\{([^\}]*)\}/S<$1>/g;
346*d2201f2fSdrahn    s/\@(?:dmn|math)\{([^\}]*)\}/$1/g;
347*d2201f2fSdrahn
348*d2201f2fSdrahn    # Cross references are thrown away, as are @noindent and @refill.
349*d2201f2fSdrahn    # (@noindent is impossible in .pod, and @refill is unnecessary.)
350*d2201f2fSdrahn    # @* is also impossible in .pod; we discard it and any newline that
351*d2201f2fSdrahn    # follows it.  Similarly, our macro @gol must be discarded.
352*d2201f2fSdrahn
353*d2201f2fSdrahn    s/\(?\@xref\{(?:[^\}]*)\}(?:[^.<]|(?:<[^<>]*>))*\.\)?//g;
354*d2201f2fSdrahn    s/\s+\(\@pxref\{(?:[^\}]*)\}\)//g;
355*d2201f2fSdrahn    s/;\s+\@pxref\{(?:[^\}]*)\}//g;
356*d2201f2fSdrahn    s/\@noindent\s*//g;
357*d2201f2fSdrahn    s/\@refill//g;
358*d2201f2fSdrahn    s/\@gol//g;
359*d2201f2fSdrahn    s/\@\*\s*\n?//g;
360*d2201f2fSdrahn
361*d2201f2fSdrahn    # @uref can take one, two, or three arguments, with different
362*d2201f2fSdrahn    # semantics each time.  @url and @email are just like @uref with
363*d2201f2fSdrahn    # one argument, for our purposes.
364*d2201f2fSdrahn    s/\@(?:uref|url|email)\{([^\},]*)\}/&lt;B<$1>&gt;/g;
365*d2201f2fSdrahn    s/\@uref\{([^\},]*),([^\},]*)\}/$2 (C<$1>)/g;
366*d2201f2fSdrahn    s/\@uref\{([^\},]*),([^\},]*),([^\},]*)\}/$3/g;
367*d2201f2fSdrahn
368*d2201f2fSdrahn    # Turn B<blah I<blah> blah> into B<blah> I<blah> B<blah> to
369*d2201f2fSdrahn    # match Texinfo semantics of @emph inside @samp.  Also handle @r
370*d2201f2fSdrahn    # inside bold.
371*d2201f2fSdrahn    s/&LT;/</g;
372*d2201f2fSdrahn    s/&GT;/>/g;
373*d2201f2fSdrahn    1 while s/B<((?:[^<>]|I<[^<>]*>)*)R<([^>]*)>/B<$1>${2}B</g;
374*d2201f2fSdrahn    1 while (s/B<([^<>]*)I<([^>]+)>/B<$1>I<$2>B</g);
375*d2201f2fSdrahn    1 while (s/I<([^<>]*)B<([^>]+)>/I<$1>B<$2>I</g);
376*d2201f2fSdrahn    s/[BI]<>//g;
377*d2201f2fSdrahn    s/([BI])<(\s+)([^>]+)>/$2$1<$3>/g;
378*d2201f2fSdrahn    s/([BI])<([^>]+?)(\s+)>/$1<$2>$3/g;
379*d2201f2fSdrahn
380*d2201f2fSdrahn    # Extract footnotes.  This has to be done after all other
381*d2201f2fSdrahn    # processing because otherwise the regexp will choke on formatting
382*d2201f2fSdrahn    # inside @footnote.
383*d2201f2fSdrahn    while (/\@footnote/g) {
384*d2201f2fSdrahn	s/\@footnote\{([^\}]+)\}/[$fnno]/;
385*d2201f2fSdrahn	add_footnote($1, $fnno);
386*d2201f2fSdrahn	$fnno++;
387*d2201f2fSdrahn    }
388*d2201f2fSdrahn
389*d2201f2fSdrahn    return $_;
390*d2201f2fSdrahn}
391*d2201f2fSdrahn
392*d2201f2fSdrahnsub unmunge
393*d2201f2fSdrahn{
394*d2201f2fSdrahn    # Replace escaped symbols with their equivalents.
395*d2201f2fSdrahn    local $_ = $_[0];
396*d2201f2fSdrahn
397*d2201f2fSdrahn    s/&lt;/E<lt>/g;
398*d2201f2fSdrahn    s/&gt;/E<gt>/g;
399*d2201f2fSdrahn    s/&lbrace;/\{/g;
400*d2201f2fSdrahn    s/&rbrace;/\}/g;
401*d2201f2fSdrahn    s/&at;/\@/g;
402*d2201f2fSdrahn    s/&amp;/&/g;
403*d2201f2fSdrahn    return $_;
404*d2201f2fSdrahn}
405*d2201f2fSdrahn
406*d2201f2fSdrahnsub add_footnote
407*d2201f2fSdrahn{
408*d2201f2fSdrahn    unless (exists $sects{FOOTNOTES}) {
409*d2201f2fSdrahn	$sects{FOOTNOTES} = "\n=over 4\n\n";
410*d2201f2fSdrahn    }
411*d2201f2fSdrahn
412*d2201f2fSdrahn    $sects{FOOTNOTES} .= "=item $fnno.\n\n"; $fnno++;
413*d2201f2fSdrahn    $sects{FOOTNOTES} .= $_[0];
414*d2201f2fSdrahn    $sects{FOOTNOTES} .= "\n\n";
415*d2201f2fSdrahn}
416*d2201f2fSdrahn
417*d2201f2fSdrahn# stolen from Symbol.pm
418*d2201f2fSdrahn{
419*d2201f2fSdrahn    my $genseq = 0;
420*d2201f2fSdrahn    sub gensym
421*d2201f2fSdrahn    {
422*d2201f2fSdrahn	my $name = "GEN" . $genseq++;
423*d2201f2fSdrahn	my $ref = \*{$name};
424*d2201f2fSdrahn	delete $::{$name};
425*d2201f2fSdrahn	return $ref;
426*d2201f2fSdrahn    }
427*d2201f2fSdrahn}
428