3 # Generate a man page from sections of a Texinfo manual.
6 # The Free Software Foundation,
8 # & Ximbiot <http://ximbiot.com>
10 # This program is free software; you can redistribute it and/or modify
11 # it under the terms of the GNU General Public License as published by
12 # the Free Software Foundation; either version 2, or (at your option)
15 # This program is distributed in the hope that it will be useful,
16 # but WITHOUT ANY WARRANTY; without even the implied warranty of
17 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 # GNU General Public License for more details.
20 # You should have received a copy of the GNU General Public License
21 # along with this program; if not, write to the Free Software Foundation,
22 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
26 # Need Perl 5.005 or greater for re 'eval'.
38 my $texi_num = 0; # Keep track of how many texinfo files have been encountered.
39 my @parent; # This needs to be global to be used inside of a regex later.
41 my $ret; # The RE match Type, used in debug prints.
42 my $debug = 0; # Debug mode?
58 my ($keyword, $file) = @_;
61 if $keyword =~ /^(|r|t)$/;
63 if $keyword =~ /^(strong|sc|code|file|samp)$/;
65 if $keyword =~ /^(emph|var|dfn)$/;
66 die "no handler for keyword \`$keyword', found at line $. of file \`$file'\n";
71 # Return replacement for \@$keyword{$content}.
74 my ($file, $parent, $keyword, $content) = @_;
76 return "`$content\\(aq in the CVS manual"
78 return "see node `$content\\(aq in the CVS manual"
79 if $keyword =~ /^p?xref$/;
80 return "\\fP\\fP$content"
81 if $keyword =~ /^splitrcskeyword$/;
83 my $endmode = keyword_mode $parent;
84 my $startmode = keyword_mode $keyword, $file;
86 return "$startmode$content$endmode";
96 my $fh = new IO::File "< $file"
97 or die "Failed to open file \`$file': $!";
99 if ($file !~ /\.(texinfo|texi|txi)$/)
101 print stderr "Passing \`$file' through unprocessed.\n";
102 # Just cat any file that doesn't look like a Texinfo source.
103 while (my $line = $fh->getline)
110 print stderr "Processing \`$file'.\n";
118 my $last_header = "";
121 my $table_header = "";
122 my $table_footer = "";
124 while ($_ = $fh->getline)
126 if (!$gotone && /^\@c ----- START MAN $texi_num -----$/)
132 # Skip ahead until our man section.
135 # If we find the end tag we are done.
136 last if /^\@c ----- END MAN $texi_num -----$/;
138 # Need to do this everywhere. i.e., before we print example
139 # lines, since literal back slashes can appear there too.
145 s/(?<!-)---(?!-)/\\(em/g;
146 s/\@bullet({}|\b)/\\(bu/g;
147 s/\@dots({}|\b)/\\&.../g;
149 # Examples should be indented and otherwise untouched
153 print qq{.SP\n.PD 0\n};
159 if (/^\@end example$/)
162 print qq{\n.PD\n.IP "" $indent\n};
172 # Preserve the newline.
173 $_ = qq{.IP "" $indent\n} . $_;
176 # Compress blank lines into a single line. This and its
177 # corresponding skip purposely bracket the @menu and comment
178 # removal so that blanks on either side of a menu are
179 # compressed after the menu is removed.
187 if (/^\@(ignore|menu)$/)
192 # Delete menu contents.
195 next unless /^\@end (ignore|menu)$/;
201 next if /^\@c(omment)?\b/;
204 next if /^\@include\b/;
206 # It's okay to ignore this keyword - we're not using any
207 # first-line indent commands at all.
208 next if s/^\@noindent\s*$//;
210 # @need is only significant in printed manuals.
211 next if s/^\@need\s+.*$//;
213 # If we didn't hit the previous check and $inblank is set, then
214 # we just finished with some number of blanks. Print the man
215 # page blank symbol before continuing processing of this line.
223 $last_header = $1 if s/^\@node\s+(.*)$/.SH "$1"/;
224 if (/^\@appendix\w*\s+(.*)$/)
227 $content =~ s/^$last_header(\\\(em|\s+)?//;
228 next if $content =~ /^\s*$/;
229 s/^\@appendix\w*\s+.*$/.SS "$content"/;
232 # Tables are similar to examples, except we need to handle the
234 if (/^\@(itemize|table)(\s+(.*))?$/)
237 push @table_headers, $table_header;
238 push @table_footers, $table_footer;
242 my $bullet = $content;
243 $table_header = qq{.IP "$bullet" $indent\n};
248 my $hi = $indent - 2;
249 $table_header = qq{.IP "" $hi\n};
250 $table_footer = qq{\n.IP "" $indent};
253 $table_header .= "$content\{";
254 $table_footer = "\}$table_footer";
263 if (/^\@end (itemize|table)$/)
265 $table_header = pop @table_headers;
266 $table_footer = pop @table_footers;
271 s/^\@itemx?(\s+(.*))?$/$table_header$2$table_footer/;
272 # Fall through so the rest of the table lines are
273 # processed normally.
277 s/^\@cindex\s+(.*)$/.IX "$1"/;
279 $_ = "$last$_" if $last;
285 (?{ debug_print "$ret MATCHED $&\nPUSHING $1\n";
286 push @parent, $1; }) # Keep track of the last keyword
287 # keyword we encountered.
289 [^{}]|(?<=\@)[{}] # Non-braces...
291 (??{ $nk }) # ...nested keywords...
292 )*) # ...without backtracking.
294 (?{ debug_print "$ret MATCHED $&\nPOPPING ",
295 pop (@parent), "\n"; }) # Lose track of the current keyword.
299 if (/\@\w+\{(?:[^{}]|(?<=\@)[{}]|(??{ $nk }))*$/)
301 # If there is an opening keyword on this line without a
302 # close bracket, we need to find the close bracket
303 # before processing the line. Set $last to append the
304 # next line in the next pass.
309 # Okay, the following works somewhat counter-intuitively. $nk
310 # processes the whole line, so @parent gets loaded properly,
311 # then, since no closing brackets have been found for the
312 # outermost matches, the innermost matches match and get
317 # Processing the line:
319 # yadda yadda @code{yadda @var{foo} yadda @var{bar} yadda}
321 # Happens something like this:
323 # 1. Ignores "yadda yadda "
324 # 2. Sees "@code{" and pushes "code" onto @parent.
325 # 3. Ignores "yadda " (backtracks and ignores "yadda yadda
327 # 4. Sees "@var{" and pushes "var" onto @parent.
328 # 5. Sees "foo}", pops "var", and realizes that "@var{foo}"
329 # matches the overall pattern ($nk).
330 # 6. Replaces "@var{foo}" with the result of:
332 # do_keyword $file, $parent[$#parent], $1, $2;
334 # which would be "\Ifoo\B", in this case, because "var"
335 # signals a request for italics, or "\I", and "code" is
336 # still on the stack, which means the previous style was
339 # Then the while loop restarts and a similar series of events
340 # replaces "@var{bar}" with "\Ibar\B".
342 # Then the while loop restarts and a similar series of events
343 # replaces "@code{yadda \Ifoo\B yadda \Ibar\B yadda}" with
344 # "\Byadda \Ifoo\B yadda \Ibar\B yadda\R".
348 while (s/$nk/do_keyword $file, $parent[$#parent], $1, $2/e)
350 # Do nothing except reset our last-replacement
351 # tracker - the replacement regex above is handling
353 debug_print "FINAL MATCH $&\n";
357 # Finally, unprotect texinfo special characters.
361 # Verify we haven't left commands unprocessed.
362 die "Unprocessed command at line $. of file \`$file': "
363 . ($1 ? "$1\n" : "<EOL>\n")
364 if /^(?>(?:[^\@]|\@\@)*)\@(\w+|.|$)/;
369 # And print whatever's left.