Import Upstream version 1.8.5
[hcoop/debian/openafs.git] / doc / xml / AdminRef / pod2refentry
CommitLineData
805e021f
CE
1#!/usr/bin/perl
2 eval 'exec /usr/bin/perl -S $0 ${1+"$@"}'
3 if $running_under_some_shell;
4
5# Pod::RefEntry -- Convert POD data to DocBook RefEntry
6#
7# Copyright 2005, 2006 by Chas Williams <chas@cmf.nrl.navy.mil>
8#
9# This program is free software; you can redistribute it and/or modify it
10# under the same terms as Perl itself.
11#
12# based on:
13#
14# Pod::PlainText -- Convert POD data to formatted ASCII text.
15# $Id: Text.pm,v 2.1 1999/09/20 11:53:33 eagle Exp $
16#
17# Copyright 1999-2000 by Russ Allbery <rra@stanford.edu>
18#
19# This program is free software; you can redistribute it and/or modify it
20# under the same terms as Perl itself.
21
22package Pod::RefEntry;
23
24require 5.005;
25
26use Carp qw(carp);
27use Pod::Select ();
28
29use strict;
30use vars qw(@ISA %ESCAPES $VERSION);
31
32# We inherit from Pod::Select instead of Pod::Parser so that we can be used
33# by Pod::Usage.
34@ISA = qw(Pod::Select);
35
36$VERSION = '0.06';
37
38# This table is taken near verbatim from Pod::PlainText in Pod::Parser,
39# which got it near verbatim from the original Pod::Text. It is therefore
40# credited to Tom Christiansen, and I'm glad I didn't have to write it. :)
41%ESCAPES = (
42 'amp' => '&amp;', # ampersand
43 'lt' => '&lt;', # left chevron, less-than
44 'gt' => '&gt;', # right chevron, greater-than
45 'quot' => '"', # double quote
46);
47
48# Initialize the object. Must be sure to call our parent initializer.
49sub initialize {
50 my $self = shift;
51
52 $$self{hlevel} = 0 unless defined $$self{hlevel};
53 $$self{ltype} = 0 unless defined $$self{ltype};
54 $$self{lopen} = 0 unless defined $$self{lopen};
55 $$self{indent} = 2 unless defined $$self{indent};
56 $$self{width} = 76 unless defined $$self{width};
57 $$self{refnamediv} = 0;
58
59 $$self{LSTATE} = [];
60 $$self{MARGIN} = 0; # Current left margin in spaces.
61
62 $self->SUPER::initialize;
63}
64
65sub begin_pod {
66 my $self = shift;
67
68 $self->output ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
69}
70
71sub end_pod {
72 my $self = shift;
73 my $i;
74
75 for($i = 4; $i > 0; --$i) {
76 if ($$self{hlevel} >= $i) {
77 $self->{MARGIN} -= 2;
78 #$self->output ("</refsection>\n");
79 $self->output (sprintf "</refsect%d>\n", $i);
80 };
81 };
82
83 $self->{MARGIN} -= 2;
84 $self->output ("</refentry>\n");
85}
86
87# Called for each command paragraph. Gets the command, the associated
88# paragraph, the line number, and a Pod::Paragraph object. Just dispatches
89# the command to a method named the same as the command. =cut is handled
90# internally by Pod::Parser.
91sub command {
92 my $self = shift;
93 my $command = shift;
94 return if $command eq 'pod';
95 return if ($$self{EXCLUDE} && $command ne 'end');
96 $self->item ("\n") if defined $$self{ITEM};
97 $command = 'cmd_' . $command;
98 $self->$command (@_);
99}
100
101# Called for a verbatim paragraph. Gets the paragraph, the line number, and
102# a Pod::Paragraph object. Just output it verbatim, but with tabs converted
103# to spaces.
104sub verbatim {
105 my $self = shift;
106 return if $$self{EXCLUDE};
107 $self->item if defined $$self{ITEM};
108 local $_ = shift;
109 return if /^\s*$/;
110 $$self{MARGIN} += 2;
111 s/&/&amp;/g; # do &amp; first to avoid "fixing" the & in &lt;
112 s/</&lt;/g;
113 s/>/&gt;/g;
114 my $saved = $$self{MARGIN};
115 $$self{MARGIN} = 0;
116 $self->output ("<programlisting>\n");
117 $self->output ($_);
118 $self->output ("</programlisting>\n");
119 $$self{MARGIN} = $saved;
120}
121
122sub escapes {
123 (undef, local $_) = @_;
124 s/(&)/\&amp;/g;
125 s/(<)/\&lt;/g;
126 s/(>)/\&gt;/g;
127 $_;
128}
129
130# Called for interior sequences. Gets a Pod::InteriorSequence object
131# and is expected to return the resulting text.
132sub sequence {
133 my ($self, $seq) = @_;
134
135 my $cmd_name = $seq->cmd_name;
136
137 $seq->left_delimiter( '' );
138 $seq->right_delimiter( '' );
139 $seq->cmd_name( '' );
140 $_ = $seq->raw_text;
141
142 if ($cmd_name eq 'B') {
143 $_ = sprintf "<emphasis role=\"bold\">%s</emphasis>", $_;
144 } elsif ($cmd_name eq 'C') {
145 $_ = sprintf "<computeroutput>%s</computeroutput>", $_;
146 } elsif ($cmd_name eq 'F') {
147 $_ = sprintf "<replaceable>%s</replaceable>", $_;
148 } elsif ($cmd_name eq 'I') {
149 $_ = sprintf "<emphasis>%s</emphasis>", $_;
150 } elsif ($cmd_name eq 'S') {
151 # perhaps translate ' ' to &nbsp;
152 $_ = sprintf "%s", $_;
153 } elsif ($cmd_name eq 'L') {
154 $_ = $self->seq_l ($seq);
155 } elsif ($cmd_name eq 'E') {
156 if (defined $ESCAPES{$_}) {
157 $_ = $ESCAPES{$_} if defined $ESCAPES{$_};
158 } else {
159 carp "Unknown escape: E<$_>";
160 }
161 } else {
162 carp "\nUnknown sequence $cmd_name<$_>\n";
163 }
164
165 my $parent = $seq->nested;
166 if (defined $parent) {
167
168 if ($parent->cmd_name eq 'B') {
169 $_ = sprintf "</emphasis>%s<emphasis role=\"bold\">", $_;
170 } elsif ($parent->cmd_name eq 'C') {
171 $_ = sprintf "</computeroutput>%s<computeroutput>", $_;
172 } elsif ($parent->cmd_name eq 'F') {
173 $_ = sprintf "</replaceable>%s<replaceable>", $_;
174 } elsif ($parent->cmd_name eq 'I') {
175 $_ = sprintf "</emphasis>%s<emphasis>", $_;
176 }
177 }
178
179 return $_;
180}
181
182# Called for a regular text block. Gets the paragraph, the line number, and
183# a Pod::Paragraph object. Perform parse_text and output the results.
184sub textblock {
185 my $self = shift;
186 return if $$self{EXCLUDE};
187 $self->output ($_[0]), return if $$self{VERBATIM};
188 local $_ = shift;
189 my $line = shift;
190 my $name;
191 my $purpose;
192
193# /<http:.*>/ && do {
194# s/<http:([^>]+)\>/<ulink url=\"http:\1\">http:\1<\/ulink>/;
195# };
196#
197# /<.*@.*>/ && do {
198# s/<([^>]+@[^>]+)>/<email>\1<\/email>/g;
199# };
200
201 $_ = $self->parse_text(
202 { -expand_text => q(escapes),
203 -expand_seq => q(sequence) },
204 $_, $line ) -> raw_text();
205
206 if (defined $$self{ITEM}) {
207 $self->item ($_ . "\n");
208 } elsif ($self->{refnamediv}) {
209 ($name, $purpose) = /(.+)\s+\-\s+(.+)/;
210 my $id = $name;
211 $id =~ s/,.*$//; # only reference by first entry?
212 $id =~ s/[ \.,\(\)]/_/g;
213 if (defined $$self{section}) {
214 $id = sprintf "%s%d", $id, $$self{section};
215 }
216 $self->output ("<refentry id=\"$id\">\n");
217 $self->{MARGIN} += 2;
218 if (defined $$self{section}) {
219 $self->output ("<refmeta>\n");
220 $self->{MARGIN} += 2;
221 $self->output (sprintf "<refentrytitle>%s</refentrytitle>\n", $name);
222 $self->output (sprintf "<manvolnum>%d</manvolnum>\n", $$self{section});
223 $self->{MARGIN} -= 2;
224 $self->output ("</refmeta>\n");
225 }
226 $self->output ("<refnamediv>\n");
227 $self->{MARGIN} += 2;
228 $self->output ("<refname>$name</refname>\n");
229 $self->output ("<refpurpose>$purpose</refpurpose>\n");
230 $self->{MARGIN} -= 2;
231 $self->output ("</refnamediv>\n");
232 $self->{refnamediv} = 0;
233 } else {
234 s/\n+$//;
235 $self->output ("<para>" . $_ . "<\/para>" . "\n\n");
236 }
237}
238
239# Level headings.
240sub cmd_head {
241 my $self = shift;
242 local $_ = shift;
243 my $line = shift;
244 my $level = $self->{level};
245 my $i;
246
247 for($i = 4; $i > 0; --$i) {
248 if ($level <= $i) {
249 if ($$self{hlevel} >= $i) {
250 $$self{MARGIN} -= 2;
251 #$self->output (sprintf "</refsection>\n", $i);
252 $self->output (sprintf "</refsect%d>\n", $i);
253 }
254 }
255 }
256
257 # special, output next <para> as <refnamediv>
258 if ($level == 1 && $_ =~ /NAME/) {
259 $self->{refnamediv} = 1;
260 return;
261 }
262
263 #$self->output (sprintf "<refsection>\n", $level);
264 $self->output (sprintf "<refsect%d>\n", $level);
265 $$self{MARGIN} += 2;
266 s/\s+$//;
267 $_ = $self->parse_text(
268 { -expand_text => q(escapes),
269 -expand_seq => q(sequence) },
270 $_, $line ) -> raw_text();
271 if (/^[A-Z ]+$/) {
272 s/(\w+)/\u\L$1/g if $level == 1; # kill capitalization
273 }
274 $self->output ("<title>" . $_ . "<\/title>" . "\n");
275 $$self{hlevel} = $level;
276}
277
278# First level heading.
279sub cmd_head1 {
280 my $self = shift;
281 $self->{level} = 1;
282 $self->cmd_head (@_);
283}
284
285# Second level heading.
286sub cmd_head2 {
287 my $self = shift;
288 $self->{level} = 2;
289 $self->cmd_head (@_);
290}
291
292# Third level heading.
293sub cmd_head3 {
294 my $self = shift;
295 $self->{level} = 3;
296 $self->cmd_head (@_);
297}
298
299sub cmd_head4 {
300 my $self = shift;
301 # <refsect4> doesnt exist -- we would use <refsection>
302 # when it becomes available in 4.4
303 printf STDERR "=head4 being rendered as <refsect3>\n";
304 $self->{level} = 3;
305 $self->cmd_head (@_);
306}
307
308# Start a list.
309sub cmd_over {
310 my $self = shift;
311 local $_ = shift;
312 unless (/^[-+]?\d+\s+$/) { $_ = $$self{indent} }
313 push (@{ $$self{LSTATE} }, $$self{lopen});
314 push (@{ $$self{LSTATE} }, $$self{ltype});
315 undef $self->{ltype};
316 $$self{lopen} = 0;
317}
318
319# End a list.
320sub cmd_back {
321 my $self = shift;
322 if ($self->{ltype} == 2) {
323 $self->{MARGIN} -= 2;
324 $self->output ("</listitem>\n");
325 $self->{MARGIN} -= 2;
326 $self->output ("</orderedlist>\n");
327 } elsif ($self->{ltype} == 1) {
328 $self->{MARGIN} -= 2;
329 $self->output ("</listitem>\n");
330 $self->{MARGIN} -= 2;
331 $self->output ("</itemizedlist>\n");
332 } else {
333 $self->{MARGIN} -= 2;
334 $self->output ("</listitem>\n");
335 $self->{MARGIN} -= 2;
336 $self->output ("</varlistentry>\n");
337 $self->{MARGIN} -= 2;
338 $self->output ("</variablelist>\n");
339 }
340 $$self{ltype} = pop @{ $$self{LSTATE} };
341 $$self{lopen} = pop @{ $$self{LSTATE} };
342 unless (defined $$self{LSTATE}) {
343 carp "Unmatched =back";
344 $$self{MARGIN} = $$self{indent};
345 }
346}
347
348# An individual list item.
349sub cmd_item {
350 my $self = shift;
351 if (defined $$self{ITEM}) { $self->item }
352 local $_ = shift;
353 my $line = shift;
354 s/\s+$//;
355 $$self{ITEM} = $self->parse_text(
356 { -expand_text => q(escapes),
357 -expand_seq => q(sequence) },
358 $_, $line ) -> raw_text();
359}
360
361# Begin a block for a particular translator. Setting VERBATIM triggers
362# special handling in textblock().
363sub cmd_begin {
364 my $self = shift;
365 local $_ = shift;
366 my ($kind) = /^(\S+)/ or return;
367 if ($kind eq 'text') {
368 $$self{VERBATIM} = 1;
369 } else {
370 $$self{EXCLUDE} = 1;
371 }
372}
373
374# End a block for a particular translator. We assume that all =begin/=end
375# pairs are properly closed.
376sub cmd_end {
377 my $self = shift;
378 $$self{EXCLUDE} = 0;
379 $$self{VERBATIM} = 0;
380}
381
382# One paragraph for a particular translator. Ignore it unless it's intended
383# for text, in which case we treat it as a verbatim text block.
384sub cmd_for {
385 my $self = shift;
386 local $_ = shift;
387 my $line = shift;
388 return unless s/^text\b[ \t]*\n?//;
389 $self->verbatim ($_, $line);
390}
391
392# The complicated one. Handle links. Since this is plain text, we can't
393# actually make any real links, so this is all to figure out what text we
394# print out.
395sub seq_l {
396 my ($self, $seq) = @_;
397
398 s/>$//; # remove trailing >
399
400 # Smash whitespace in case we were split across multiple lines.
401 s/\s+/ /g;
402
403 # If we were given any explicit text, just output it.
404 if (/^([^|]+)\|/) { return $1 }
405
406 # Okay, leading and trailing whitespace isn't important; get rid of it.
407 s/^\s+//;
408 s/\s+$//;
409
410 # Default to using the whole content of the link entry as a section
411 # name. Note that L<manpage/> forces a manpage interpretation, as does
412 # something looking like L<manpage(section)>. The latter is an
413 # enhancement over the original Pod::Text.
414 my ($manpage, $section) = ('', $_);
415 if (/^(?:https?|ftp|news):/) {
416 # a URL
417 return $_;
418 } elsif (/^"\s*(.*?)\s*"$/) {
419 $section = '"' . $1 . '"';
420 } elsif (m/^[-:.\w]+(?:\(\S+\))?$/) {
421 ($manpage, $section) = ($_, '');
422 } elsif (m%/%) {
423 ($manpage, $section) = split (/\s*\/\s*/, $_, 2);
424 }
425
426 $seq->cmd_name("");
427
428 # Now build the actual output text.
429 if (length $section) {
430 $section =~ s/^\"\s*//;
431 $section =~ s/\s*\"$//;
432 $_ = $section;
433 $_ .= " in $manpage" if length $manpage;
434 }
435 if (length $manpage) {
436 my $linkend = $manpage;
437 $linkend =~ s/[\(\)]//g;
438 $linkend =~ s/[ ,\.]/_/g; # this needs to match <refentry id=
439 $seq->prepend("<link linkend=\"$linkend\">");
440 $seq->append("</link>");
441 return $seq;
442 } else {
443 return $_;
444 }
445}
446
447# This method is called whenever an =item command is complete (in other
448# words, we've seen its associated paragraph or know for certain that it
449# doesn't have one). It gets the paragraph associated with the item as an
450# argument. If that argument is empty, just output the item tag; if it
451# contains a newline, output the item tag followed by the newline.
452# Otherwise, see if there's enough room for us to output the item tag in the
453# margin of the text or if we have to put it on a separate line.
454sub item {
455 my $self = shift;
456 local $_ = shift;
457 my $tag = $$self{ITEM};
458 unless (defined $tag) {
459 carp "item called without tag";
460 return;
461 }
462 undef $$self{ITEM};
463 if ($$self{lopen}) {
464 if ($self->{ltype} == 1 || $self->{ltype} == 2) {
465 $self->{MARGIN} -= 2;
466 $self->output ("</listitem>\n");
467 } else {
468 $self->{MARGIN} -= 2;
469 $self->output ("</listitem>\n");
470 $self->{MARGIN} -= 2;
471 $self->output ("</varlistentry>\n");
472 }
473 }
474 my $output = $_;
475 $output =~ s/\n*$/\n/;
476 if (!defined $self->{ltype}) {
477 if ($tag =~ /[0-9]+\./) {
478 $self->{ltype} = 2;
479 $self->output ("<orderedlist>\n");
480 } elsif ($tag =~ /^\*$/) {
481 $self->{ltype} = 1;
482 $self->output ("<itemizedlist>\n");
483 } else {
484 $self->{ltype} = 0;
485 $self->output ("<variablelist>\n");
486 }
487 $self->{MARGIN} += 2;
488 }
489 if ($self->{ltype} == 1 || $self->{ltype} == 2) {
490 $self->output ("<listitem>\n");
491 $self->{MARGIN} += 2;
492 s/\n+$//;
493 $self->output ("<para>" . $_ . "<\/para>" . "\n\n");
494 } else {
495 $self->output ("<varlistentry>\n");
496 $self->{MARGIN} += 2;
497 $self->output ("<term>" . $tag . "</term>" . "\n");
498 $self->output ("<listitem>\n");
499 $self->{MARGIN} += 2;
500 s/\n+$//;
501 $self->output ("<para>" . $_ . "<\/para>" . "\n\n");
502 }
503 $$self{lopen} = 1;
504}
505
506# Output text to the output device.
507sub output {
508 my $self = shift;
509 local $_ = shift;
510 s/^(\s*\S+)/(' ' x $$self{MARGIN}) . $1/gme;
511 print { $self->output_handle } $_;
512}
513
5141;
515
516
517# pod2refentry -- Convert POD data to DocBook RefEntry
518#
519# Copyright 2005, 2006 by Chas Williams <chas@cmf.nrl.navy.mil>
520#
521# This program is free software; you may redistribute it and/or modify it
522# under the same terms as Perl itself.
523#
524# based on:
525#
526# pod2text -- Convert POD data to formatted ASCII text.
527#
528# Copyright 1999, 2000, 2001 by Russ Allbery <rra@stanford.edu>
529#
530# This program is free software; you may redistribute it and/or modify it
531# under the same terms as Perl itself.
532
533package main;
534
535require 5.004;
536
537use Getopt::Long qw(GetOptions);
538use Pod::Usage qw(pod2usage);
539
540use strict;
541
542# Silence -w warnings.
543use vars qw($running_under_some_shell);
544
545# Insert -- into @ARGV before any single dash argument to hide it from
546# Getopt::Long; we want to interpret it as meaning stdin (which Pod::Parser
547# does correctly).
548my $stdin;
549@ARGV = map { $_ eq '-' && !$stdin++ ? ('--', $_) : $_ } @ARGV;
550
551# Parse our options.
552my %options;
553GetOptions (\%options, 'help|h', 'indent|i=i', 'section|s=i' ) or exit 1;
554pod2usage (1) if $options{help};
555
556# Initialize and run the formatter.
557my $parser = Pod::RefEntry->new (%options);
558$parser->parse_from_file (@ARGV);