]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - contrib/cvs/doc/mkman.in
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / contrib / cvs / doc / mkman.in
1 #! @PERL@
2 #
3 # Generate a man page from sections of a Texinfo manual.
4 #
5 # Copyright 2004 The Free Software Foundation,
6 #                Derek R. Price,
7 #                & Ximbiot <http://ximbiot.com>
8 #
9 # This program is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2, or (at your option)
12 # any later version.
13 #
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 # GNU General Public License for more details.
18 #
19 # You should have received a copy of the GNU General Public License
20 # along with this program; if not, write to the Free Software Foundation,
21 # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23
24
25 # Need Perl 5.005 or greater for re 'eval'.
26 require 5.005;
27
28 # The usual.
29 use strict;
30 use IO::File;
31
32
33
34 ###
35 ### GLOBALS
36 ###
37 my $texi_num = 0; # Keep track of how many texinfo files have been encountered.
38 my @parent;       # This needs to be global to be used inside of a regex later.
39
40
41
42 ###
43 ### FUNCTIONS
44 ###
45 sub keyword_mode
46 {
47         my ($keyword, $file) = @_;
48
49         return "\\fR"
50                 if $keyword =~ /^(|r|t)$/;
51         return "\\fB"
52                 if $keyword =~ /^(strong|sc|code|file|samp)$/;
53         return "\\fI"
54                 if $keyword =~ /^(emph|var|dfn)$/;
55         die "no handler for keyword \`$keyword', found at line $. of file \`$file'\n";
56 }
57
58
59
60 # Return replacement for \@$keyword{$content}.
61 sub do_keyword
62 {
63         my ($file, $parent, $keyword, $content) = @_;
64
65         return "see node \`$content\\(aq in the CVS manual"
66                 if $keyword =~ /^(p?x)?ref$/;
67         return "\\fP\\fP$content"
68                 if $keyword =~ /^splitrcskeyword$/;
69
70         my $endmode = keyword_mode $parent;
71         my $startmode = keyword_mode $keyword, $file;
72
73         return "$startmode$content$endmode";
74 }
75
76
77
78 ###
79 ### MAIN
80 ###
81 for my $file (@ARGV)
82 {
83         my $fh = new IO::File "< $file"
84                 or die "Failed to open file \`$file': $!";
85
86         if ($file !~ /\.(texinfo|texi|txi)$/)
87         {
88                 print stderr "Passing \`$file' through unprocessed.\n";
89                 # Just cat any file that doesn't look like a Texinfo source.
90                 while (my $line = $fh->getline)
91                 {
92                         print $line;
93                 }
94                 next;
95         }
96
97         print stderr "Processing \`$file'.\n";
98         $texi_num++;
99         my $gotone = 0;
100         my $inblank = 0;
101         my $indent = 0;
102         my $inexample = 0;
103         my $inmenu = 0;
104         my $intable = 0;
105         my $last_header = "";
106         my @table_headers;
107         my @table_footers;
108         my $table_header = "";
109         my $table_footer = "";
110         my $last;
111         while ($_ = $fh->getline)
112         {
113                 if (!$gotone && /^\@c ----- START MAN $texi_num -----$/)
114                 {
115                         $gotone = 1;
116                         next;
117                 }
118
119                 # Skip ahead until our man section.
120                 next unless $gotone;
121
122                 # If we find the end tag we are done.
123                 last if /^\@c ----- END MAN $texi_num -----$/;
124
125                 # Need to do this everywhere.  i.e., before we print example
126                 # lines, since literal back slashes can appear there too.
127                 s/\\/\\\\/g;
128                 s/^\./\\&./;
129                 s/([\s])\./$1\\&./;
130                 s/'/\\(aq/g;
131                 s/`/\\`/g;
132                 s/(?<!-)---(?!-)/\\(em/g;
133                 s/\@bullet({}|\b)/\\(bu/g;
134                 s/\@dots({}|\b)/\\&.../g;
135
136                 # Examples should be indented and otherwise untouched
137                 if (/^\@example$/)
138                 {
139                         $indent += 2;
140                         print qq{.SP\n.PD 0\n};
141                         $inexample = 1;
142                         next;
143                 }
144                 if ($inexample)
145                 {
146                         if (/^\@end example$/)
147                         {
148                                 $indent -= 2;
149                                 print qq{\n.PD\n.IP "" $indent\n};
150                                 $inexample = 0;
151                                 next;
152                         }
153                         if (/^[         ]*$/)
154                         {
155                                 print ".SP\n";
156                                 next;
157                         }
158
159                         # Preserve the newline.
160                         $_ = qq{.IP "" $indent\n} . $_;
161                 }
162
163                 # Compress blank lines into a single line.  This and its
164                 # corresponding skip purposely bracket the @menu and comment
165                 # removal so that blanks on either side of a menu are
166                 # compressed after the menu is removed.
167                 if (/^[         ]*$/)
168                 {
169                         $inblank = 1;
170                         next;
171                 }
172
173                 # Not used
174                 if (/^\@(ignore|menu)$/)
175                 {
176                         $inmenu++;
177                         next;
178                 }
179                 # Delete menu contents.
180                 if ($inmenu)
181                 {
182                         next unless /^\@end (ignore|menu)$/;
183                         $inmenu--;
184                         next;
185                 }
186
187                 # Remove comments
188                 next if /^\@c(omment)?\b/;
189
190                 # It's okay to ignore this keyword - we're not using any
191                 # first-line indent commands at all.
192                 next if s/^\@noindent\s*$//;
193
194                 # @need is only significant in printed manuals.
195                 next if s/^\@need\s+.*$//;
196
197                 # If we didn't hit the previous check and $inblank is set, then
198                 # we just finished with some number of blanks.  Print the man
199                 # page blank symbol before continuing processing of this line.
200                 if ($inblank)
201                 {
202                         print ".SP\n";
203                         $inblank = 0;
204                 }
205
206                 # Chapter headers.
207                 $last_header = $1 if s/^\@node\s+(.*)$/.SH "$1"/;
208                 if (/^\@appendix\w*\s+(.*)$/)
209                 {
210                         my $content = $1;
211                         $content =~ s/^$last_header(\\\(em|\s+)?//;
212                         next if $content =~ /^\s*$/;
213                         s/^\@appendix\w*\s+.*$/.SS "$content"/;
214                 }
215
216                 # Tables are similar to examples, except we need to handle the
217                 # keywords.
218                 if (/^\@(itemize|table)(\s+(.*))?$/)
219                 {
220                         $indent += 2;
221                         push @table_headers, $table_header;
222                         push @table_footers, $table_footer;
223                         my $content = $3;
224                         if (/^\@itemize/)
225                         {
226                                 my $bullet = $content;
227                                 $table_header = qq{.IP "$bullet" $indent\n};
228                                 $table_footer = "";
229                         }
230                         else
231                         {
232                                 my $hi = $indent - 2;
233                                 $table_header = qq{.IP "" $hi\n};
234                                 $table_footer = qq{\n.IP "" $indent};
235                                 if ($content)
236                                 {
237                                         $table_header .= "$content\{";
238                                         $table_footer = "\}$table_footer";
239                                 }
240                         }
241                         $intable++;
242                         next;
243                 }
244
245                 if ($intable)
246                 {
247                         if (/^\@end (itemize|table)$/)
248                         {
249                                 $table_header = pop @table_headers;
250                                 $table_footer = pop @table_footers;
251                                 $indent -= 2;
252                                 $intable--;
253                                 next;
254                         }
255                         s/^\@itemx?(\s+(.*))?$/$table_header$2$table_footer/;
256                         # Fall through so the rest of the table lines are
257                         # processed normally.
258                 }
259
260                 # Index entries.
261                 s/^\@cindex\s+(.*)$/.IX "$1"/;
262
263                 $_ = "$last$_" if $last;
264                 undef $last;
265
266                 # Trap keywords
267                 my $nk = qr/
268                                 \@(\w+)\{
269                                 (?{ push @parent, $1 })       # Keep track of the last keyword
270                                                               # keyword we encountered.
271                                 ((?:
272                                         (?> (?:[^{}]|(?<=\@)[{}])*) # Non-braces without backtracking
273                                                 |
274                                         (??{ $nk })                 # Nested keywords
275                                 )*)
276                                 \}
277                                 (?{ pop (@parent) })            # Lose track of the current keyword.
278                         /x;
279
280                 @parent = ("");
281                 while (s/$nk/do_keyword $file, $parent[$#parent], $1, $2/e)
282                 {
283                         # Do nothing except replace our last-replacement
284                         # tracker - the replacement regex above is handling
285                         # everything else.
286                         @parent = ("");
287                 }
288                 s/$nk/do_keyword $file, $parent[$#parent], $1, $2/ge;
289
290                 if (/\@\w+\{/)
291                 {
292                         # If there is still an opening keyword left, we need to
293                         # find the close bracket.  Set $last to append the next
294                         # line in the next pass.
295                         $last = $_;
296                         next;
297                 }
298
299                 # Finally, unprotect texinfo special characters.
300                 s/\@://g;
301                 s/\@([{}])/$1/g;
302
303                 # Verify we haven't left commands unprocessed.
304                 die "Unprocessed command at line $. of file \`$file': "
305                     . ($1 ? "$1\n" : "<EOL>\n")
306                         if /^(?>(?:[^\@]|\@\@)*)\@(\w+|.|$)/;
307
308                 # Unprotect @@.
309                 s/\@\@/\@/g;
310
311                 # And print whatever's left.
312                 print $_;
313         }
314 }