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