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