File Coverage

blib/lib/Pod/DocBook.pm
Criterion Covered Total %
statement 326 398 81.9
branch 166 216 76.8
condition 40 66 60.6
subroutine 28 28 100.0
pod 1 8 12.5
total 561 716 78.3


line stmt bran cond sub pod time code
1             package Pod::DocBook;
2              
3 1     1   19765 use 5.006001;
  1         4  
  1         46  
4 1     1   7 use strict;
  1         3  
  1         45  
5 1     1   6 use warnings;
  1         7  
  1         45  
6              
7 1     1   5 use Digest::MD5 'md5_hex';
  1         2  
  1         67  
8 1     1   5 use Pod::Parser;
  1         1  
  1         47  
9 1     1   1283 use Pod::ParseLink;
  1         1391  
  1         78  
10 1     1   1041 use Text::ParseWords;
  1         2034  
  1         284  
11 1     1   3254 use Text::Wrap;
  1         9761  
  1         12004  
12              
13             our @ISA = qw(Pod::Parser);
14             our $VERSION = '1.2';
15              
16             #----------------------------------------------------------------------
17             # overridden Pod::Parser methods
18             #----------------------------------------------------------------------
19              
20             sub initialize
21             {
22 17     17 0 24350 $_[0]->errorsub ('error_msg');
23 17         223 $_[0]->{'Pod::DocBook::errors'} = [];
24             }
25              
26             sub begin_pod
27             {
28 17     17 0 2584 my ($parser) = @_;
29 17         57 my $out_fh = $parser->output_handle ();
30              
31 17 100       174 print $out_fh "{doctype} ",
32             qq#PUBLIC "-//OASIS//DTD DocBook V4.2//EN">\n# if $parser->{header};
33              
34 17         292 print $out_fh join ("\n",
35             '"), "\n";
43              
44 17         57 $parser->{indentlevel} = 1;
45              
46 17 50       44 if ($parser->{doctype} eq 'refentry') {
47 0         0 print $out_fh join ('',
48             "\n",
49             $parser->_indent (),
50             "\n",
51             $parser->_current_indent (),
52             "$parser->{title}",
53             "\n",
54             $parser->_outdent (),
55             "\n");
56             }
57              
58             else {
59 17         1262 print $out_fh "<$parser->{doctype}>$parser->{title}\n";
60             }
61             }
62              
63             sub end_pod
64             {
65 17     17 0 28 my ($parser) = @_;
66 17         58 my $out_fh = $parser->output_handle ();
67              
68 17         39 $parser->_transition ('THE END');
69              
70             # end document
71 17         41 print $out_fh "{doctype}>\n";
72 17 100       15 if (@{$parser->{'Pod::DocBook::errors'}}) {
  17         740  
73 9         12 print $out_fh "\n\n";
81             }
82             }
83              
84             sub command {
85 96     96 1 154 my ($parser, $command, $paragraph, $line_num) = @_;
86 96         306 my $out_fh = $parser->output_handle ();
87              
88 96 100       623 return if $command eq 'pod';
89              
90 85         165 $paragraph =~ s/\s+$//s;
91 85         3468 $paragraph = $parser->interpolate ($paragraph, $line_num);
92 85         165 $paragraph = _fix_chars ($paragraph);
93              
94 85 100       368 if ($command =~ /^head[1-4]/) {
    100          
    100          
    50          
    100          
    100          
    100          
95 9         23 $parser->_transition ($command);
96 9         23 $parser->_handle_head ($command, $paragraph, $line_num);
97             }
98              
99             elsif ($command eq 'begin') {
100 5         20 $parser->_transition ("begin $paragraph");
101 5         7 push (@{$parser->{'Pod::DocBook::state'}}, "begin $paragraph");
  5         333  
102             }
103              
104             elsif ($command eq 'end') {
105 4         56 $parser->_transition ("end $paragraph");
106             }
107              
108             elsif ($command eq 'for') {
109 0         0 $parser->_transition ('for');
110 0 0       0 if ($paragraph =~ /^(:\S+|docbook)/) {
111 0         0 $paragraph =~ s/$1\s+//;
112 0         0 print $out_fh $paragraph, "\n";
113             }
114             }
115              
116             elsif ($command eq 'over') {
117 15         27 $parser->_transition ('over');
118 15         19 push @{$parser->{'Pod::DocBook::state'}}, 'over';
  15         670  
119             }
120              
121             elsif ($command eq 'item') {
122 36         60 $parser->_transition ('item');
123 36         74 $parser->_handle_item ($paragraph, $line_num);
124             }
125              
126             elsif ($command =~ /^back/) {
127 15         34 $parser->_transition ('back');
128             }
129              
130             else {
131 1         6 my $file = $parser->input_file ();
132 1         9 $parser->error_msg ("unknown command `$command' at",
133             "line $line_num in file $file");
134             }
135             }
136              
137             sub textblock {
138 78     78 0 113 my ($parser, $paragraph, $line_num) = @_;
139 78         227 my $out_fh = $parser->output_handle ();
140 78         85 my $state = pop @{$parser->{'Pod::DocBook::state'}};
  78         138  
141 78         90 my $para_out = '';
142              
143 78 100       167 $state = '' unless defined $state;
144 78 100       354 $paragraph =~ s/\s+$//s unless $state eq 'begin docbook';
145              
146 78 100 100     325 unless ($state eq 'begin docbook' || $state eq 'begin table') {
147 75         4266 $paragraph = $parser->interpolate ($paragraph, $line_num);
148 75         149 $paragraph = _fix_chars ($paragraph);
149             }
150              
151 78 50       401 if ($state eq 'name') {
    50          
    50          
    100          
    100          
    100          
    100          
152 0         0 my ($name, $purpose) = split (/\s*-\s*/, $paragraph, 2);
153              
154 0         0 $para_out = join ('',
155             $parser->_indent (),
156             "\n",
157             $parser->_current_indent (),
158             "$name\n",
159             "$purpose\n",
160             $parser->_outdent (),
161             "\n");
162             }
163              
164             elsif ($state eq 'synopsis+') {
165 0         0 $para_out = join ('',
166             $parser->_indent (),
167             "\n",
168             "$paragraph\n");
169              
170 0         0 push @{$parser->{'Pod::DocBook::state'}}, 'synopsis';
  0         0  
171             }
172              
173             elsif ($state eq 'synopsis') {
174 0         0 $para_out = "$paragraph\n";
175 0         0 push @{$parser->{'Pod::DocBook::state'}}, $state;
  0         0  
176             }
177              
178             elsif ($state eq 'begin docbook') {
179 1         2 push @{$parser->{'Pod::DocBook::dbpara'}}, $paragraph;
  1         5  
180 1         2 push @{$parser->{'Pod::DocBook::state'}}, $state;
  1         2  
181             }
182              
183             elsif ($state eq 'begin table') {
184 2         8 $parser->_handle_table ($paragraph, $line_num);
185 2         4 push @{$parser->{'Pod::DocBook::state'}}, $state;
  2         6  
186             }
187              
188             elsif ($state =~ /^begin [^:]/) {
189 2         3 push @{$parser->{'Pod::DocBook::state'}}, $state;
  2         5  
190             }
191              
192             elsif ($state eq 'over') {
193 1         1 local $Text::Wrap::huge = 'overflow'; # don't break tags
194              
195 1         3 $paragraph =~ s/\s*\n\s*/ /g; # don't just wrap, fill
196              
197 1         5 $para_out = join ('',
198             $parser->_indent (),
199             "
\n",
200             $parser->_indent (),
201             "\n",
202             wrap (' ' x ($parser->{spaces} *
203             $parser->{indentlevel}),
204             ' ' x ($parser->{spaces} *
205             $parser->{indentlevel}),
206             $paragraph),
207             "\n",
208             $parser->_outdent (),
209             "\n");
210              
211 1         3 push @{$parser->{'Pod::DocBook::state'}}, 'indent';
  1         3  
212             }
213              
214             else {
215 72         89 local $Text::Wrap::huge = 'overflow'; # don't break tags
216              
217 72 100       125 print $out_fh "]]>\n" if $state eq 'verbatim';
218              
219 72         115 $paragraph =~ s/\s*\n\s*/ /g; # don't just wrap, fill
220              
221 72         136 $para_out = join ('',
222             $parser->_indent (),
223             "\n",
224             wrap (' ' x ($parser->{spaces} *
225             $parser->{indentlevel}),
226             ' ' x ($parser->{spaces} *
227             $parser->{indentlevel}),
228             $paragraph),
229             "\n",
230             $parser->_outdent (),
231             "\n");
232              
233 72         190 $state =~ s/\+$//;
234 72 100 100     300 push @{$parser->{'Pod::DocBook::state'}}, $state
  42         92  
235             unless ($state eq 'verbatim' || $state eq '');
236             }
237              
238             # fix double quotes in ordinary paragraphs if asked to
239 78 100 66     519 if ($state !~ /^begin/ &&
      100        
240             $parser->{fix_double_quotes} && $para_out =~ /"/) {
241 6         9 my @protected;
242 6         35 while ($para_out =~ m#(<[^>"]*".+?>)#s) {
243             # don't modify things that look like tags with quotes inside
244 6   33     20 my $protect = $1 || $2;
245 6         13 my $replace = quotemeta ($protect);
246              
247 6         68 $para_out =~ s/$replace/\376/;
248 6         24 push @protected, $protect;
249             }
250              
251 6         9 $para_out =~ s!"(.+?)"!$1!sg;
252 6         9 foreach my $protect (@protected) {
253 6         27 $para_out =~ s/\376/$protect/;
254             }
255             }
256              
257 78         3923 print $out_fh $para_out;
258             }
259              
260             sub verbatim {
261 6     6 0 10 my ($parser, $paragraph, $line_num) = @_;
262 6         22 my $out_fh = $parser->output_handle ();
263 6   100     7 my $state = pop @{$parser->{'Pod::DocBook::state'}} || '';
264 6         8 my (@lines, $min_leader);
265              
266 6 50       39 $paragraph =~ s/\s+$//s unless $state eq 'begin docbook';
267              
268 6         16 @lines = split (/\n/, $paragraph);
269 6         9 foreach my $line (@lines) {
270             # expand tabs (see perldoc -q 'expand tabs')
271 6         17 1 while $line =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
  0         0  
272              
273             # find the minimum-length whitespace leader for this paragraph
274 6         18 my ($leader) = $line =~ /^( +)/;
275 6   50     12 $leader ||= '';
276 6 50 33     25 $min_leader = $leader
277             if (!defined $min_leader ||
278             length ($leader) < length ($min_leader));
279             }
280              
281 6         12 $paragraph = join ("\n", @lines);
282              
283             # strip the minimum-length whitespace leader from every line
284 6   50     11 $min_leader ||= '';
285 6         37 $paragraph =~ s/^$min_leader//mg;
286              
287 6 50       567 if (!defined $state) {
    50          
    50          
    50          
    50          
    50          
    50          
    100          
288 0         0 print $out_fh $parser->_current_indent (),
289             "
290 0         0 push @{$parser->{'Pod::DocBook::state'}}, 'verbatim';
  0         0  
291             }
292              
293             elsif ($state eq 'name') {
294 0         0 my ($name, $purpose) = split (/\s*-\s*/, $paragraph, 2);
295              
296 0         0 print $out_fh join ('',
297             $parser->_indent (),
298             "refnamediv>\n",
299             $parser->_current_indent (),
300             "$name\n",
301             $parser->_current_indent (),
302             "$purpose\n",
303             $parser->_outdent (),
304             "\n");
305             }
306              
307             elsif ($state eq 'synopsis+') {
308 0         0 print $out_fh join ('',
309             $parser->_indent (),
310             "\n",
311             "$paragraph\n");
312              
313 0         0 push @{$parser->{'Pod::DocBook::state'}}, 'synopsis';
  0         0  
314             }
315              
316             elsif ($state eq 'synopsis') {
317 0         0 print $out_fh "$paragraph\n";
318 0         0 push @{$parser->{'Pod::DocBook::state'}}, $state;
  0         0  
319             }
320              
321             elsif ($state eq 'begin docbook') {
322 0         0 push @{$parser->{'Pod::DocBook::dbpara'}}, $paragraph;
  0         0  
323 0         0 push @{$parser->{'Pod::DocBook::state'}}, $state;
  0         0  
324             }
325              
326             elsif ($state =~ /^begin [^:]/) {
327 0         0 push @{$parser->{'Pod::DocBook::state'}}, $state;
  0         0  
328             }
329              
330             elsif ($state eq 'over') {
331 0         0 print $out_fh join ('',
332             $parser->_indent (),
333             "
\n",
334             $parser->_current_indent (),
335             "
336              
337 0         0 push @{$parser->{'Pod::DocBook::state'}}, 'indent';
  0         0  
338 0         0 push @{$parser->{'Pod::DocBook::state'}}, 'verbatim';
  0         0  
339             }
340              
341             elsif ($state eq 'verbatim') {
342 2         6 print $out_fh "\n\n$paragraph";
343 2         2 push @{$parser->{'Pod::DocBook::state'}}, $state;
  2         160  
344             }
345              
346             else {
347 4         12 print $out_fh $parser->_current_indent (),
348             "
349 4         6 $state =~ s/\+$//;
350 4         5 push @{$parser->{'Pod::DocBook::state'}}, $state;
  4         8  
351 4         6 push @{$parser->{'Pod::DocBook::state'}}, 'verbatim';
  4         168  
352             }
353             }
354              
355             sub interior_sequence {
356 26     26 0 52 my ($parser, $command, $argument, $seq) = @_;
357 26         98 my $out_fh = $parser->output_handle ();
358 26         29 my ($string, @parents);
359              
360             # nothing is ever allowed to be nested inside of E<>, or Z<>
361 26 100       116 if (my $parent = $seq->nested ()) {
362 2 50 33     21 if ($parent->cmd_name () eq 'E' || $parent->cmd_name () eq 'Z') {
363 2         14 my ($file, $line) = $seq->file_line ();
364 2         17 $parser->error_msg ("formatting code `$command' nested within",
365             "`" . $parent->cmd_name () . "'",
366             "at line $line in file $file");
367 2         159 return $seq->raw_text ();
368             }
369             }
370              
371 24 50       46 $argument = '' unless defined $argument;
372              
373             # the substring "\37632\377" is a space character protected
374             # against translation in S<>; other characters are protected at
375             # the end of this function, and all protected characters are
376             # de-protected in _fix_chars ()
377              
378 24 100       107 if ($command eq 'I') {
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    50          
379 1         4 $string = qq!$argument!;
380             }
381              
382             elsif ($command eq 'B') {
383 1         4 $string = qq!$argument!;
384             }
385              
386             elsif ($command eq 'C') {
387 1         4 $string = qq!! .
388             "";
389             }
390              
391             elsif ($command eq 'L') {
392 10         53 $string = $parser->_handle_L ($argument, $seq);
393             }
394              
395             elsif ($command eq 'E') {
396 8         19 $string = $parser->_handle_E ($argument, $seq);
397             }
398              
399             elsif ($command eq 'F') {
400 1         4 $string = "$argument";
401             }
402              
403             elsif ($command eq 'S') {
404 0         0 $argument =~ s/\s(?![^<]*>)/ /g;
405 0         0 $string = $argument;
406             }
407              
408             elsif ($command eq 'X') {
409 1         4 $string = "$argument";
410             }
411              
412             elsif ($command eq 'Z') {
413 0         0 $string = '';
414             }
415              
416             else {
417 1         7 my ($file, $line) = $seq->file_line ();
418 1         6 $parser->error_msg ("unknown formatting code `$command' at line",
419             "in file $file");
420 1         9 $string = $seq->raw_text ();
421             }
422              
423             # protect &, <, and > characters from later processing
424             # I got this from the first edition Camel Book
425 24 50       99 unless ($seq->nested ()) {
426             # just do this once, at the top of a subtree so we can
427             # report more meaningful errors along the way
428 24         39 foreach my $char ('&', '<', '>') {
429 72         618 $string =~ s/$char/"\376" . ord ($char) . "\377"/eg;
  110         297  
430             }
431             }
432              
433 24         6993 return $string;
434             }
435              
436             #----------------------------------------------------------------------
437             # other public methods
438             #----------------------------------------------------------------------
439              
440             sub error_msg
441             {
442 11     11 0 17 my $parser = shift;
443              
444 11         13 push (@{$parser->{'Pod::DocBook::errors'}}, join (' ', @_));
  11         55  
445             }
446              
447             #----------------------------------------------------------------------
448             # private methods and helper functions
449             #----------------------------------------------------------------------
450              
451             sub _indent
452             {
453 171     171   170 my ($parser) = @_;
454 171         1211 return (' ' x ($parser->{spaces} * $parser->{indentlevel}++));
455             }
456              
457             sub _outdent
458             {
459 171     171   11982 my ($parser) = @_;
460 171         606 return (' ' x (--$parser->{indentlevel} * $parser->{spaces}));
461             }
462              
463             sub _current_indent
464             {
465 54     54   61 my $parser = shift;
466              
467 54         183 return ' ' x ($parser->{spaces} * $parser->{indentlevel});
468             }
469              
470             sub _make_id
471             {
472 16     16   17 my $parser = shift;
473 16         39 my $string = join ('-', $parser->{doctype}, $parser->{title}, $_[0]);
474              
475 16         58 $string =~ s//$1/g;
476 16         25 $string =~ s/<.+?>//g;
477              
478 16         139 return 'ID-' . md5_hex ($string);
479             }
480              
481             sub _handle_L
482             {
483 10     10   17 my ($parser, $argument, $seq) = @_;
484 10         11 my $node = $seq;
485              
486             # look all the way up the subtree to see if any ancestor is an 'L'
487 10         43 while ($node = $node->nested ()) {
488 0 0       0 if ($node->cmd_name () eq 'L') {
489 0         0 my ($file, $line) = $seq->file_line ();
490 0         0 $parser->error_msg ("formatting code `L' nested within `L' at",
491             "line $line in file $file");
492 0         0 return $seq->raw_text ();
493             }
494             }
495              
496             # the substring "\37632\377" is a space character protected
497             # against translation in S<>; other characters are protected at
498             # the end of interior_sequence (), and all protected characters
499             # are de-protected in _fix_chars ()
500              
501 10         30 my ($text, $inferred, $name, $section, $type) = parselink ($argument);
502              
503 10 100       302 if ($type eq 'url') {
504 1         4 return qq!$inferred!;
505             }
506              
507             else {
508             # types 'man' and 'pod' are handled the same way
509 9 100 100     77 if (defined $section && ! defined $name) {
    100          
    100          
510 2         6 my $id = $parser->_make_id ($section);
511              
512 2 100       5 $section = $text if defined $text;
513 2         9 return (qq!$section! .
514             "");
515             }
516              
517             elsif (defined $text) {
518 2         7 return $text;
519             }
520              
521             elsif (defined $name) {
522 4         5 my $string;
523 4 100       14 if ($name =~ /(.+?)\((.+)\)/) {
524 2         6 $string = $parser->_manpage ($1, $2);
525             }
526              
527             else {
528 2         7 $string = $parser->_manpage ($name);
529             }
530              
531 4 100       8 if (defined $section) {
532 2         9 return "$section in $string";
533             }
534              
535             else {
536 2         5 return $string;
537             }
538             }
539              
540             else {
541 1         8 my ($file, $line) = $seq->file_line ();
542 1         5 $parser->error_msg ("empty L<> at line",
543             "$line in file $file\n");
544 1         16 return $seq->raw_text ();
545             }
546             }
547             }
548              
549             sub _handle_E
550             {
551 8     8   14 my ($parser, $argument, $seq) = @_;
552              
553 8 100 33     101 if ($argument !~ /\A\w+\z/) {
    100 33        
    100          
    50          
    50          
    100          
    100          
    100          
554 2         12 my ($file, $line) = $seq->file_line ();
555 2         12 $parser->error_msg ("invalid escape `$argument'",
556             "at line $line in file $file\n");
557 2         19 return $seq->raw_text ();
558             }
559              
560             elsif ($argument eq 'verbar') {
561 1         8 return '|';
562             }
563              
564             elsif ($argument eq 'sol') {
565 1         2 return '/';
566             }
567              
568             elsif ($argument eq 'lchevron' || $argument eq 'laquo') {
569 0         0 return '«';
570             }
571              
572             elsif ($argument eq 'rchevron' || $argument eq 'raquo') {
573 0         0 return '»';
574             }
575              
576             elsif ($argument =~ /^0x/) {
577 1         5 return ('&#' . hex ($argument) . ';');
578             }
579              
580             elsif ($argument =~ /^0/) {
581 1         6 return ('&#' . oct ($argument) . ';');
582             }
583              
584             elsif ($argument =~ /^\d+$/) {
585 1         4 return "&#$argument;";
586             }
587              
588             else {
589 1         6 return "&$argument;";
590             }
591             }
592              
593             sub _handle_head
594             {
595 9     9   15 my ($parser, $command, $paragraph, $line_num) = @_;
596 9         26 my $out_fh = $parser->output_handle ();
597              
598 9 50 33     53 if ($parser->{doctype} eq 'refentry' &&
    50 33        
      33        
      33        
599             $command eq 'head1' && $paragraph eq 'NAME') {
600 0         0 push @{$parser->{'Pod::DocBook::state'}}, 'name';
  0         0  
601             }
602              
603             elsif ($parser->{doctype} eq 'refentry' &&
604             $command eq 'head1' && $paragraph eq 'SYNOPSIS') {
605 0         0 push @{$parser->{'Pod::DocBook::state'}}, 'synopsis+';
  0         0  
606             }
607              
608             else {
609 9         9 push @{$parser->{'Pod::DocBook::state'}}, "$command+";
  9         23  
610 9         19 my $id = $parser->_make_id ($paragraph);
611              
612 9 50       24 if ($parser->{doctype} eq 'refentry') {
613 0         0 print $out_fh $parser->_indent (),
614             qq!$paragraph\n!;
615             }
616              
617             else {
618 9         37 print $out_fh $parser->_indent (),
619             qq!
$paragraph\n!;
620             }
621             }
622             }
623              
624             sub _handle_item
625             {
626 36     36   46 my ($parser, $paragraph, $line_num) = @_;
627 36         76 my $out_fh = $parser->output_handle ();
628 36         35 my $state = pop @{$parser->{'Pod::DocBook::state'}};
  36         53  
629              
630 36 100       68 $state = '' unless defined $state;
631              
632 36 50       58 if ($state eq 'verbatim') {
633 0         0 print $out_fh "]]>\n";
634 0         0 $state = pop @{$parser->{'Pod::DocBook::state'}};
  0         0  
635 0 0       0 $state = '' unless defined $state;
636             }
637              
638 36 50       56 if ($state =~ /list\+$/) {
639 0         0 print $out_fh $parser->_current_indent (), "\n";
640             }
641              
642 36 100       104 if ($state eq 'over') {
    100          
    100          
643             # first item
644 14 100 33     108 if (!defined ($paragraph) ||
    100 66        
645             $paragraph =~ /^\s*$/ ||
646             $paragraph eq '*') {
647 2         8 print $out_fh join ('',
648             $parser->_indent (),
649             "\n",
650             $parser->_indent (),
651             "\n",
652             $parser->_indent (),
653             "\n");
654 2         5 $state = 'list+';
655             }
656              
657             elsif ($paragraph =~ /^([1aAiI])\.?$/) {
658 10         47 my $numeration = { 1 => 'arabic',
659             a => 'loweralpha',
660             A => 'upperalpha',
661             i => 'lowerroman',
662             I => 'upperroman' }->{$1};
663              
664 10         33 print $out_fh join ('',
665             $parser->_indent (),
666             "\n",
667             $parser->_indent (),
668             qq!\n!,
669             $parser->_indent (),
670             "\n");
671 10         16 $state = 'olist+';
672             }
673              
674             else {
675 2         7 my $id = $parser->_make_id ($paragraph);
676 2         7 print $out_fh join ('',
677             $parser->_indent (),
678             "\n",
679             $parser->_indent (),
680             "\n",
681             $parser->_indent (),
682             "\n",
683             $parser->_current_indent (),
684             qq!$paragraph\n!,
685             $parser->_indent (),
686             qq!\n!);
687 2         5 $state = 'vlist+';
688             }
689             }
690              
691             elsif ($state =~ /^o?list/) {
692 18         34 print $out_fh join ('',
693             $parser->_outdent (),
694             "\n",
695             $parser->_indent (),
696             "\n");
697 18 50       56 $state = "$state+" unless $state =~ /\+$/;
698             }
699              
700             elsif ($state =~ /^vlist/) {
701 3         6 my $id = $parser->_make_id ($paragraph);
702 3         8 print $out_fh join ('',
703             $parser->_outdent (),
704             "\n",
705             $parser->_outdent (),
706             "\n",
707             $parser->_indent (),
708             "\n",
709             $parser->_current_indent (),
710             qq!$paragraph\n!,
711             $parser->_indent (),
712             "\n");
713 3         5 $state = 'vlist+';
714             }
715              
716             else {
717 1         8 $parser->error_msg ('=item must be inside an',
718             '=over ... =back region',
719             "at line $line_num in file",
720             $parser->input_file ());
721             }
722              
723 36         34 push @{$parser->{'Pod::DocBook::state'}}, $state;
  36         1501  
724             }
725              
726             sub _transition
727             {
728 101     101   130 my ($parser, $what) = @_;
729 101         354 my $out_fh = $parser->output_handle ();
730 101         109 my ($level);
731              
732             # $level helps us determine what to do when we see =head
733             # 1-4 are the valid numbers after '=head', so 0 and 5
734             # are safe to use to mark out-of-bounds on either side
735 101 100       225 if ($what eq 'THE END') {
    100          
736 17         21 $level = 0;
737             }
738              
739             elsif ($what =~ /^head(\d)/) {
740 9         14 $level = $1;
741             }
742              
743             else {
744 75         86 $level = 5;
745             }
746              
747 101         91 while (my $state = pop @{$parser->{'Pod::DocBook::state'}}) {
  114         366  
748 79 50 100     430 if (($what eq 'item' || $what eq 'over') &&
      66        
      66        
749             ($state eq 'over' || $state =~ /^(o|v)?list/)) {
750             # these are treated specially in _handle_item ()
751 42         39 push @{$parser->{'Pod::DocBook::state'}}, $state;
  42         69  
752 42         78 last;
753             }
754              
755 37 50       76 if ($state =~ /list\+$/) {
756 0         0 print $out_fh $parser->_current_indent (), "\n";
757 0         0 $state =~ s/\+$//;
758             }
759              
760 37 100       165 if ($state =~ /^head(\d)/) {
    100          
    100          
    100          
    100          
    50          
    50          
    100          
    50          
    0          
761 15         21 my $prev_level = $1;
762              
763 15 100       25 if ($level > $prev_level) {
764             # embed in a previously opened section (i.e. restore
765             # state and continue processing the document)
766              
767             # the enclosing section is no longer empty
768 6         10 $state =~ s/\+$//;
769 6         6 push @{$parser->{'Pod::DocBook::state'}}, $state;
  6         12  
770 6         15 last;
771             }
772              
773             else {
774 9 100       19 if ($state =~ /\+$/) {
775             # prevent empty sections
776 1         4 print $out_fh $parser->_current_indent (),
777             "\n";
778             }
779              
780             # close the previous section and continue with the stack
781 9 50       16 if ($parser->{doctype} eq 'refentry') {
782 0         0 print $out_fh $parser->_outdent (), "\n";
783             }
784              
785             else {
786 9         15 print $out_fh $parser->_outdent (), "\n";
787             }
788             }
789             }
790              
791             elsif ($state eq 'indent') {
792 1         3 print $out_fh $parser->_outdent (), "\n";
793              
794 1 50       3 push @{$parser->{'Pod::DocBook::state'}}, 'over'
  0         0  
795             if ($what eq 'item');
796              
797 1 50       14 last if $what eq 'back';
798             }
799              
800             elsif ($state eq 'list') {
801 2         11 print $out_fh join ('',
802             $parser->_outdent (),
803             "\n",
804             $parser->_outdent (),
805             "\n",
806             $parser->_outdent (),
807             "\n");
808              
809 2 50       99 last if $what eq 'back';
810             }
811              
812             elsif ($state eq 'olist') {
813 10         19 print $out_fh join ('',
814             $parser->_outdent (),
815             "\n",
816             $parser->_outdent (),
817             "\n",
818             $parser->_outdent (),
819             "\n");
820              
821 10 50       499 last if $what eq 'back';
822             }
823              
824             elsif ($state eq 'vlist') {
825 2         5 print $out_fh join ('',
826             $parser->_outdent (),
827             "\n",
828             $parser->_outdent (),
829             "\n",
830             $parser->_outdent (),
831             "\n",
832             $parser->_outdent (),
833             "\n");
834              
835 2 50       64 last if $what eq 'back';
836             }
837              
838             elsif ($state =~ /^synopsis/) {
839 0 0       0 print $out_fh join ('',
840             $parser->_indent (),
841             "\n",
842             $parser->_current_indent (),
843             "\n")
844             if $state eq 'synopsis+';
845              
846 0         0 print $out_fh $parser->_outdent (), "\n";
847             }
848              
849             elsif ($state eq 'name') {
850 0         0 print $out_fh join ('',
851             $parser->_indent (),
852             "\n",
853             $parser->_indent (),
854             "\n",
855             $parser->_current_indent (),
856             "\n",
857             $parser->_outdent (),
858             "\n");
859             }
860              
861             elsif ($state eq 'verbatim') {
862 2         5 print $out_fh "]]>\n";
863             }
864              
865             elsif ($state =~ /^begin (.+)/) {
866 5         11 my $begin_format = $1;
867 5 100       19 if ($what =~ /^end (.+)/) {
    50          
868 4         7 my $end_format = $1;
869              
870 4 100       10 if ($end_format eq $begin_format) {
871 3 100       8 if ($end_format eq 'docbook') {
872 1         4 my $paragraph =
873             join ('',
874 1         2 @{$parser->{'Pod::DocBook::dbpara'}});
875 1         9 $paragraph =~ s/\s+$//;
876 1         2 print $out_fh $paragraph, "\n";
877 1         3 $parser->{'Pod::DocBook::dbpara'} = [];
878             }
879              
880 3         96 last;
881             }
882              
883             else {
884             # this is bad POD, but we do what we can
885             # (maybe we'll find the begin we're looking for
886             # deeper in the stack)
887 1         7 $parser->error_msg ("`=end $end_format' found",
888             'but current region opened with',
889             "`=begin $begin_format'");
890             }
891             }
892              
893             elsif ($what eq 'THE END') {
894             # this is bad POD, but we do what we can
895 1         5 $parser->error_msg ("no matching `=end' for",
896             "`=begin $begin_format'");
897              
898             # we've got the data stored; might as well use it
899 1 50       5 if ($begin_format eq 'docbook') {
900 0         0 my $paragraph =
901             join ('',
902 0         0 @{$parser->{'Pod::DocBook::dbpara'}});
903 0         0 $paragraph =~ s/\s+$//;
904 0         0 print $out_fh $paragraph, "\n";
905 0         0 $parser->{'Pod::DocBook::dbpara'} = [];
906             }
907             }
908              
909             else {
910 0         0 push @{$parser->{'Pod::DocBook::state'}}, $state;
  0         0  
911 0         0 last;
912             }
913             }
914              
915             elsif ($state eq 'over') {
916 0         0 next;
917             }
918              
919             else {
920 0         0 $parser->error_msg ("encountered unknown state `$state'",
921             '(this should never happen)');
922             }
923              
924             }
925             }
926              
927             sub _handle_table
928             {
929 2     2   4 my ($parser, $paragraph, $line_num) = @_;
930 2         9 my $out_fh = $parser->output_handle ();
931 2         4 my (@rows, $columns, $title);
932              
933 2         10 foreach my $row (split (/\n/, $paragraph)) {
934 16         47 my @fields = quotewords (',', 0, $row);
935              
936 16 100 100     1404 $columns = @fields
937             if (!defined $columns || @fields > $columns);
938 16         49 push @rows, [@fields];
939             }
940              
941             # the first row specifies the title
942 2         6 $title = $rows[0]->[0];
943              
944 2         7 print $out_fh join ('',
945             $parser->_indent (),
946             "\n", \n", \n"); \n"; \n",
947             $parser->_current_indent (),
948             "$title\n",
949             $parser->_indent (),
950             qq!\n!);
951              
952             # the second row specifies column alignments
953 2         6 foreach my $spec (@{$rows[1]}) {
  2         5  
954 6         11 print $out_fh $parser->_current_indent (), '
955              
956 6 100       10 if (grep { $_ eq $spec } qw(left right center justify)) {
  24         47  
957 5         13 print $out_fh qq!align="$spec">\n!;
958             }
959              
960             else {
961 1         2 print $out_fh qq!align="left">\n!;
962 1         12 $parser->error_msg ("unknown colspec `$spec' in table",
963             $title, "at line $line_num in file",
964             $parser->input_file ());
965             }
966             }
967              
968             # the third row (first row of data) is the table header
969 2         7 print $out_fh join ('',
970             $parser->_indent (),
971             "
972             $parser->_indent (),
973             "\n");
974              
975 2         5 foreach my $field (@{$rows[2]}) {
  2         5  
976 6         10 print $out_fh $parser->_current_indent (),
977             "$field\n";
978             }
979              
980 2         7 print $out_fh join ('',
981             $parser->_outdent (),
982             "\n",
983             $parser->_outdent (),
984             "
985              
986             # the remaining rows are the table body
987 2         6 print $out_fh $parser->_indent (), "
988              
989 2         8 foreach my $row (@rows[3..$#rows]) {
990 10         19 print $out_fh $parser->_indent (), "\n";
991              
992 10         18 foreach my $field (@$row) {
993 30         52 print $out_fh $parser->_current_indent (),
994             "$field\n";
995             }
996              
997 10         21 print $out_fh $parser->_outdent (), "\n";
998             }
999              
1000 2         7 print $out_fh join ('',
1001             $parser->_outdent (),
1002             "
1003             $parser->_outdent (),
1004             "\n",
1005             $parser->_outdent (),
1006             "
\n");
1007             }
1008              
1009             sub _manpage
1010             {
1011 4     4   7 my ($parser, $title, $volnum) = @_;
1012              
1013             # the substring "\37632\377" is a space character protected
1014             # against translation in S<>; other characters are protected at
1015             # the end of interior_sequence (), and all protected characters
1016             # are de-protected in _fix_chars ()
1017              
1018 4 100       8 if (defined $volnum) {
1019 2         15 return join ("\n",
1020             '',
1021             "\37632\377" x $parser->{spaces} .
1022             "$title",
1023             "\37632\377" x $parser->{spaces} .
1024             "$volnum",
1025             '');
1026             }
1027              
1028             else {
1029 2         13 return join ("\n",
1030             '',
1031             "\37632\377" x $parser->{spaces} .
1032             "$title",
1033             '');
1034             }
1035             }
1036              
1037             #----------------------------------------------------------------------
1038             # helper functions
1039             #----------------------------------------------------------------------
1040              
1041             sub _fix_chars
1042             {
1043 160     160   193 my ($paragraph) = @_;
1044              
1045             # fix characters that might annoy an SGML parser
1046 160         208 $paragraph =~ s/&/&/g;
1047 160         161 $paragraph =~ s/
1048 160         144 $paragraph =~ s/>/>/g;
1049              
1050             # finally, de-protect any characters that were protected
1051             # from the previous step
1052 160         211 $paragraph =~ s!\376(\d+)\377!pack ('C', $1)!eg;
  128         496  
1053              
1054 160         283 return $paragraph;
1055             }
1056              
1057             1;
1058              
1059             __END__