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