1use 5; 2package Pod::Simple::JustPod; 3# ABSTRACT: Pod::Simple formatter that extracts POD from a file containing 4# other things as well 5use strict; 6use warnings; 7 8use Pod::Simple::Methody (); 9our @ISA = ('Pod::Simple::Methody'); 10 11sub new { 12 my $self = shift; 13 my $new = $self->SUPER::new(@_); 14 15 $new->accept_targets('*'); 16 $new->keep_encoding_directive(1); 17 $new->preserve_whitespace(1); 18 $new->complain_stderr(1); 19 $new->_output_is_for_JustPod(1); 20 21 return $new; 22} 23 24#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 25 26sub check_that_all_is_closed { 27 28 # Actually checks that the things we depend on being balanced in fact are, 29 # so that we can continue in spit of pod errors 30 31 my $self = shift; 32 while ($self->{inL}) { 33 $self->end_L(@_); 34 } 35 while ($self->{fcode_end} && @{$self->{fcode_end}}) { 36 $self->_end_fcode(@_); 37 } 38} 39 40sub handle_text { 41 42 # Add text to the output buffer. This is skipped if within a L<>, as we use 43 # the 'raw' attribute of that tag instead. 44 45 $_[0]{buffer} .= $_[1] unless $_[0]{inL} ; 46} 47 48sub spacer { 49 50 # Prints the white space following things like =head1. This is normally a 51 # blank, unless BlackBox has told us otherwise. 52 53 my ($self, $arg) = @_; 54 return unless $arg; 55 56 my $spacer = ($arg->{'~orig_spacer'}) 57 ? $arg->{'~orig_spacer'} 58 : " "; 59 $self->handle_text($spacer); 60} 61 62sub _generic_start { 63 64 # Called from tags like =head1, etc. 65 66 my ($self, $text, $arg) = @_; 67 $self->check_that_all_is_closed(); 68 $self->handle_text($text); 69 $self->spacer($arg); 70} 71 72sub start_Document { shift->_generic_start("=pod\n\n"); } 73sub start_head1 { shift->_generic_start('=head1', @_); } 74sub start_head2 { shift->_generic_start('=head2', @_); } 75sub start_head3 { shift->_generic_start('=head3', @_); } 76sub start_head4 { shift->_generic_start('=head4', @_); } 77sub start_encoding { shift->_generic_start('=encoding', @_); } 78# sub start_Para 79# sub start_Verbatim 80 81sub start_item_bullet { # Handle =item * 82 my ($self, $arg) = @_; 83 $self->check_that_all_is_closed(); 84 $self->handle_text('=item'); 85 86 # It can be that they said simply '=item', and it is inferred that it is to 87 # be a bullet. 88 if (! $arg->{'~orig_content'}) { 89 $self->handle_text("\n\n"); 90 } 91 else { 92 $self->spacer($arg); 93 if ($arg->{'~_freaky_para_hack'}) { 94 95 # See Message Id <87y3gtcwa2.fsf@hope.eyrie.org> 96 my $item_text = $arg->{'~orig_content'}; 97 my $trailing = quotemeta $arg->{'~_freaky_para_hack'}; 98 $item_text =~ s/$trailing$//; 99 $self->handle_text($item_text); 100 } 101 else { 102 $self->handle_text("*\n\n"); 103 } 104 } 105} 106 107sub start_item_number { # Handle '=item 2' 108 my ($self, $arg) = @_; 109 $self->check_that_all_is_closed(); 110 $self->handle_text("=item"); 111 $self->spacer($arg); 112 $self->handle_text("$arg->{'~orig_content'}\n\n"); 113} 114 115sub start_item_text { # Handle '=item foo bar baz' 116 my ($self, $arg) = @_; 117 $self->check_that_all_is_closed(); 118 $self->handle_text('=item'); 119 $self->spacer($arg); 120} 121 122sub _end_item { 123 my $self = shift; 124 $self->check_that_all_is_closed(); 125 $self->emit; 126} 127 128*end_item_bullet = *_end_item; 129*end_item_number = *_end_item; 130*end_item_text = *_end_item; 131 132sub _start_over { # Handle =over 133 my ($self, $arg) = @_; 134 $self->check_that_all_is_closed(); 135 $self->handle_text("=over"); 136 137 # The =over amount is optional 138 if ($arg->{'~orig_content'}) { 139 $self->spacer($arg); 140 $self->handle_text("$arg->{'~orig_content'}"); 141 } 142 $self->handle_text("\n\n"); 143} 144 145*start_over_bullet = *_start_over; 146*start_over_number = *_start_over; 147*start_over_text = *_start_over; 148*start_over_block = *_start_over; 149 150sub _end_over { 151 my $self = shift; 152 $self->check_that_all_is_closed(); 153 $self->handle_text('=back'); 154 $self->emit; 155} 156 157*end_over_bullet = *_end_over; 158*end_over_number = *_end_over; 159*end_over_text = *_end_over; 160*end_over_block = *_end_over; 161 162sub end_Document { 163 my $self = shift; 164 $self->emit; # Make sure buffer gets flushed 165 print {$self->{'output_fh'} } "=cut\n" 166} 167 168sub _end_generic { 169 my $self = shift; 170 $self->check_that_all_is_closed(); 171 $self->emit; 172} 173 174*end_head1 = *_end_generic; 175*end_head2 = *_end_generic; 176*end_head3 = *_end_generic; 177*end_head4 = *_end_generic; 178*end_encoding = *_end_generic; 179*end_Para = *_end_generic; 180*end_Verbatim = *_end_generic; 181 182sub _start_fcode { 183 my ($type, $self, $flags) = @_; 184 185 # How many brackets is set by BlackBox unless the count is 1 186 my $bracket_count = (exists $flags->{'~bracket_count'}) 187 ? $flags->{'~bracket_count'} 188 : 1; 189 $self->handle_text($type . ( "<" x $bracket_count)); 190 191 my $rspacer = ""; 192 if ($bracket_count > 1) { 193 my $lspacer = (exists $flags->{'~lspacer'}) 194 ? $flags->{'~lspacer'} 195 : " "; 196 $self->handle_text($lspacer); 197 198 $rspacer = (exists $flags->{'~rspacer'}) 199 ? $flags->{'~rspacer'} 200 : " "; 201 } 202 203 # BlackBox doesn't output things for for the ending code callbacks, so save 204 # what we need. 205 push @{$self->{'fcode_end'}}, [ $bracket_count, $rspacer ]; 206} 207 208sub start_B { _start_fcode('B', @_); } 209sub start_C { _start_fcode('C', @_); } 210sub start_E { _start_fcode('E', @_); } 211sub start_F { _start_fcode('F', @_); } 212sub start_I { _start_fcode('I', @_); } 213sub start_S { _start_fcode('S', @_); } 214sub start_X { _start_fcode('X', @_); } 215sub start_Z { _start_fcode('Z', @_); } 216 217sub _end_fcode { 218 my $self = shift; 219 my $fcode_end = pop @{$self->{'fcode_end'}}; 220 my $bracket_count = 1; 221 my $rspacer = ""; 222 223 if (! defined $fcode_end) { # If BlackBox is working, this shouldn't 224 # happen, but verify 225 $self->whine($self->{line_count}, "Extra '>'"); 226 } 227 else { 228 $bracket_count = $fcode_end->[0]; 229 $rspacer = $fcode_end->[1]; 230 } 231 232 $self->handle_text($rspacer) if $bracket_count > 1; 233 $self->handle_text(">" x $bracket_count); 234} 235 236*end_B = *_end_fcode; 237*end_C = *_end_fcode; 238*end_E = *_end_fcode; 239*end_F = *_end_fcode; 240*end_I = *_end_fcode; 241*end_S = *_end_fcode; 242*end_X = *_end_fcode; 243*end_Z = *_end_fcode; 244 245sub start_L { 246 _start_fcode('L', @_); 247 $_[0]->handle_text($_[1]->{raw}); 248 $_[0]->{inL}++ 249} 250 251sub end_L { 252 my $self = shift; 253 $self->{inL}--; 254 if ($self->{inL} < 0) { # If BlackBox is working, this shouldn't 255 # happen, but verify 256 $self->whine($self->{line_count}, "Extra '>' ending L<>"); 257 $self->{inL} = 0; 258 } 259 260 $self->_end_fcode(@_); 261} 262 263sub emit { 264 my $self = shift; 265 266 if ($self->{buffer} ne "") { 267 print { $self->{'output_fh'} } "",$self->{buffer} ,"\n\n"; 268 269 $self->{buffer} = ""; 270 } 271 272 return; 273} 274 2751; 276 277__END__ 278 279=head1 NAME 280 281Pod::Simple::JustPod -- just the Pod, the whole Pod, and nothing but the Pod 282 283=head1 SYNOPSIS 284 285 my $infile = "mixed_code_and_pod.pm"; 286 my $outfile = "just_the_pod.pod"; 287 open my $fh, ">$outfile" or die "Can't write to $outfile: $!"; 288 289 my $parser = Pod::Simple::JustPod->new(); 290 $parser->output_fh($fh); 291 $parser->parse_file($infile); 292 close $fh or die "Can't close $outfile: $!"; 293 294=head1 DESCRIPTION 295 296This class returns a copy of its input, translated into Perl's internal 297encoding (UTF-8), and with all the non-Pod lines removed. 298 299This is a subclass of L<Pod::Simple::Methody> and inherits all its methods. 300And since, that in turn is a subclass of L<Pod::Simple>, you can use any of 301its methods. This means you can output to a string instead of a file, or 302you can parse from an array. 303 304This class strives to return the Pod lines of the input completely unchanged, 305except for any necessary translation into Perl's internal encoding, and it makes 306no effort to return trailing spaces on lines; these likely will be stripped. 307If the input pod is well-formed with no warnings nor errors generated, the 308extracted pod should generate the same documentation when formatted by a Pod 309formatter as the original file does. 310 311By default, warnings are output to STDERR 312 313=head1 SEE ALSO 314 315L<Pod::Simple>, L<Pod::Simple::Methody> 316 317=head1 SUPPORT 318 319Questions or discussion about POD and Pod::Simple should be sent to the 320L<mailto:pod-people@perl.org> mail list. Send an empty email to 321L<mailto:pod-people-subscribe@perl.org> to subscribe. 322 323This module is managed in an open GitHub repository, 324L<https://github.com/theory/pod-simple/>. Feel free to fork and contribute, or 325to clone L<git://github.com/theory/pod-simple.git> and send patches! 326 327Patches against Pod::Simple are welcome. Please send bug reports to 328L<mailto:<bug-pod-simple@rt.cpan.org>. 329 330=head1 COPYRIGHT AND DISCLAIMERS 331 332Copyright (c) 2002 Sean M. Burke. 333 334This library is free software; you can redistribute it and/or modify it 335under the same terms as Perl itself. 336 337This program is distributed in the hope that it will be useful, but 338without any warranty; without even the implied warranty of 339merchantability or fitness for a particular purpose. 340 341=head1 AUTHOR 342 343Pod::Simple was created by Sean M. Burke <sburke@cpan.org>. 344But don't bother him, he's retired. 345 346Pod::Simple is maintained by: 347 348=over 349 350=item * Allison Randal C<allison@perl.org> 351 352=item * Hans Dieter Pearcey C<hdp@cpan.org> 353 354=item * David E. Wheeler C<dwheeler@cpan.org> 355 356=back 357 358Pod::Simple::JustPod was developed by John SJ Anderson 359C<genehack@genehack.org>, with contributions from Karl Williamson 360C<khw@cpan.org>. 361 362=cut 363