1*0Sstevel@tonic-gate# Pod::PlainText -- Convert POD data to formatted ASCII text. 2*0Sstevel@tonic-gate# $Id: Text.pm,v 2.1 1999/09/20 11:53:33 eagle Exp $ 3*0Sstevel@tonic-gate# 4*0Sstevel@tonic-gate# Copyright 1999-2000 by Russ Allbery <rra@stanford.edu> 5*0Sstevel@tonic-gate# 6*0Sstevel@tonic-gate# This program is free software; you can redistribute it and/or modify it 7*0Sstevel@tonic-gate# under the same terms as Perl itself. 8*0Sstevel@tonic-gate# 9*0Sstevel@tonic-gate# This module is intended to be a replacement for Pod::Text, and attempts to 10*0Sstevel@tonic-gate# match its output except for some specific circumstances where other 11*0Sstevel@tonic-gate# decisions seemed to produce better output. It uses Pod::Parser and is 12*0Sstevel@tonic-gate# designed to be very easy to subclass. 13*0Sstevel@tonic-gate 14*0Sstevel@tonic-gate############################################################################ 15*0Sstevel@tonic-gate# Modules and declarations 16*0Sstevel@tonic-gate############################################################################ 17*0Sstevel@tonic-gate 18*0Sstevel@tonic-gatepackage Pod::PlainText; 19*0Sstevel@tonic-gate 20*0Sstevel@tonic-gaterequire 5.005; 21*0Sstevel@tonic-gate 22*0Sstevel@tonic-gateuse Carp qw(carp croak); 23*0Sstevel@tonic-gateuse Pod::Select (); 24*0Sstevel@tonic-gate 25*0Sstevel@tonic-gateuse strict; 26*0Sstevel@tonic-gateuse vars qw(@ISA %ESCAPES $VERSION); 27*0Sstevel@tonic-gate 28*0Sstevel@tonic-gate# We inherit from Pod::Select instead of Pod::Parser so that we can be used 29*0Sstevel@tonic-gate# by Pod::Usage. 30*0Sstevel@tonic-gate@ISA = qw(Pod::Select); 31*0Sstevel@tonic-gate 32*0Sstevel@tonic-gate$VERSION = '2.02'; 33*0Sstevel@tonic-gate 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate############################################################################ 36*0Sstevel@tonic-gate# Table of supported E<> escapes 37*0Sstevel@tonic-gate############################################################################ 38*0Sstevel@tonic-gate 39*0Sstevel@tonic-gate# This table is taken near verbatim from Pod::PlainText in Pod::Parser, 40*0Sstevel@tonic-gate# which got it near verbatim from the original Pod::Text. It is therefore 41*0Sstevel@tonic-gate# credited to Tom Christiansen, and I'm glad I didn't have to write it. :) 42*0Sstevel@tonic-gate%ESCAPES = ( 43*0Sstevel@tonic-gate 'amp' => '&', # ampersand 44*0Sstevel@tonic-gate 'lt' => '<', # left chevron, less-than 45*0Sstevel@tonic-gate 'gt' => '>', # right chevron, greater-than 46*0Sstevel@tonic-gate 'quot' => '"', # double quote 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate "Aacute" => "\xC1", # capital A, acute accent 49*0Sstevel@tonic-gate "aacute" => "\xE1", # small a, acute accent 50*0Sstevel@tonic-gate "Acirc" => "\xC2", # capital A, circumflex accent 51*0Sstevel@tonic-gate "acirc" => "\xE2", # small a, circumflex accent 52*0Sstevel@tonic-gate "AElig" => "\xC6", # capital AE diphthong (ligature) 53*0Sstevel@tonic-gate "aelig" => "\xE6", # small ae diphthong (ligature) 54*0Sstevel@tonic-gate "Agrave" => "\xC0", # capital A, grave accent 55*0Sstevel@tonic-gate "agrave" => "\xE0", # small a, grave accent 56*0Sstevel@tonic-gate "Aring" => "\xC5", # capital A, ring 57*0Sstevel@tonic-gate "aring" => "\xE5", # small a, ring 58*0Sstevel@tonic-gate "Atilde" => "\xC3", # capital A, tilde 59*0Sstevel@tonic-gate "atilde" => "\xE3", # small a, tilde 60*0Sstevel@tonic-gate "Auml" => "\xC4", # capital A, dieresis or umlaut mark 61*0Sstevel@tonic-gate "auml" => "\xE4", # small a, dieresis or umlaut mark 62*0Sstevel@tonic-gate "Ccedil" => "\xC7", # capital C, cedilla 63*0Sstevel@tonic-gate "ccedil" => "\xE7", # small c, cedilla 64*0Sstevel@tonic-gate "Eacute" => "\xC9", # capital E, acute accent 65*0Sstevel@tonic-gate "eacute" => "\xE9", # small e, acute accent 66*0Sstevel@tonic-gate "Ecirc" => "\xCA", # capital E, circumflex accent 67*0Sstevel@tonic-gate "ecirc" => "\xEA", # small e, circumflex accent 68*0Sstevel@tonic-gate "Egrave" => "\xC8", # capital E, grave accent 69*0Sstevel@tonic-gate "egrave" => "\xE8", # small e, grave accent 70*0Sstevel@tonic-gate "ETH" => "\xD0", # capital Eth, Icelandic 71*0Sstevel@tonic-gate "eth" => "\xF0", # small eth, Icelandic 72*0Sstevel@tonic-gate "Euml" => "\xCB", # capital E, dieresis or umlaut mark 73*0Sstevel@tonic-gate "euml" => "\xEB", # small e, dieresis or umlaut mark 74*0Sstevel@tonic-gate "Iacute" => "\xCD", # capital I, acute accent 75*0Sstevel@tonic-gate "iacute" => "\xED", # small i, acute accent 76*0Sstevel@tonic-gate "Icirc" => "\xCE", # capital I, circumflex accent 77*0Sstevel@tonic-gate "icirc" => "\xEE", # small i, circumflex accent 78*0Sstevel@tonic-gate "Igrave" => "\xCD", # capital I, grave accent 79*0Sstevel@tonic-gate "igrave" => "\xED", # small i, grave accent 80*0Sstevel@tonic-gate "Iuml" => "\xCF", # capital I, dieresis or umlaut mark 81*0Sstevel@tonic-gate "iuml" => "\xEF", # small i, dieresis or umlaut mark 82*0Sstevel@tonic-gate "Ntilde" => "\xD1", # capital N, tilde 83*0Sstevel@tonic-gate "ntilde" => "\xF1", # small n, tilde 84*0Sstevel@tonic-gate "Oacute" => "\xD3", # capital O, acute accent 85*0Sstevel@tonic-gate "oacute" => "\xF3", # small o, acute accent 86*0Sstevel@tonic-gate "Ocirc" => "\xD4", # capital O, circumflex accent 87*0Sstevel@tonic-gate "ocirc" => "\xF4", # small o, circumflex accent 88*0Sstevel@tonic-gate "Ograve" => "\xD2", # capital O, grave accent 89*0Sstevel@tonic-gate "ograve" => "\xF2", # small o, grave accent 90*0Sstevel@tonic-gate "Oslash" => "\xD8", # capital O, slash 91*0Sstevel@tonic-gate "oslash" => "\xF8", # small o, slash 92*0Sstevel@tonic-gate "Otilde" => "\xD5", # capital O, tilde 93*0Sstevel@tonic-gate "otilde" => "\xF5", # small o, tilde 94*0Sstevel@tonic-gate "Ouml" => "\xD6", # capital O, dieresis or umlaut mark 95*0Sstevel@tonic-gate "ouml" => "\xF6", # small o, dieresis or umlaut mark 96*0Sstevel@tonic-gate "szlig" => "\xDF", # small sharp s, German (sz ligature) 97*0Sstevel@tonic-gate "THORN" => "\xDE", # capital THORN, Icelandic 98*0Sstevel@tonic-gate "thorn" => "\xFE", # small thorn, Icelandic 99*0Sstevel@tonic-gate "Uacute" => "\xDA", # capital U, acute accent 100*0Sstevel@tonic-gate "uacute" => "\xFA", # small u, acute accent 101*0Sstevel@tonic-gate "Ucirc" => "\xDB", # capital U, circumflex accent 102*0Sstevel@tonic-gate "ucirc" => "\xFB", # small u, circumflex accent 103*0Sstevel@tonic-gate "Ugrave" => "\xD9", # capital U, grave accent 104*0Sstevel@tonic-gate "ugrave" => "\xF9", # small u, grave accent 105*0Sstevel@tonic-gate "Uuml" => "\xDC", # capital U, dieresis or umlaut mark 106*0Sstevel@tonic-gate "uuml" => "\xFC", # small u, dieresis or umlaut mark 107*0Sstevel@tonic-gate "Yacute" => "\xDD", # capital Y, acute accent 108*0Sstevel@tonic-gate "yacute" => "\xFD", # small y, acute accent 109*0Sstevel@tonic-gate "yuml" => "\xFF", # small y, dieresis or umlaut mark 110*0Sstevel@tonic-gate 111*0Sstevel@tonic-gate "lchevron" => "\xAB", # left chevron (double less than) 112*0Sstevel@tonic-gate "rchevron" => "\xBB", # right chevron (double greater than) 113*0Sstevel@tonic-gate); 114*0Sstevel@tonic-gate 115*0Sstevel@tonic-gate 116*0Sstevel@tonic-gate############################################################################ 117*0Sstevel@tonic-gate# Initialization 118*0Sstevel@tonic-gate############################################################################ 119*0Sstevel@tonic-gate 120*0Sstevel@tonic-gate# Initialize the object. Must be sure to call our parent initializer. 121*0Sstevel@tonic-gatesub initialize { 122*0Sstevel@tonic-gate my $self = shift; 123*0Sstevel@tonic-gate 124*0Sstevel@tonic-gate $$self{alt} = 0 unless defined $$self{alt}; 125*0Sstevel@tonic-gate $$self{indent} = 4 unless defined $$self{indent}; 126*0Sstevel@tonic-gate $$self{loose} = 0 unless defined $$self{loose}; 127*0Sstevel@tonic-gate $$self{sentence} = 0 unless defined $$self{sentence}; 128*0Sstevel@tonic-gate $$self{width} = 76 unless defined $$self{width}; 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate $$self{INDENTS} = []; # Stack of indentations. 131*0Sstevel@tonic-gate $$self{MARGIN} = $$self{indent}; # Current left margin in spaces. 132*0Sstevel@tonic-gate 133*0Sstevel@tonic-gate $self->SUPER::initialize; 134*0Sstevel@tonic-gate} 135*0Sstevel@tonic-gate 136*0Sstevel@tonic-gate 137*0Sstevel@tonic-gate############################################################################ 138*0Sstevel@tonic-gate# Core overrides 139*0Sstevel@tonic-gate############################################################################ 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate# Called for each command paragraph. Gets the command, the associated 142*0Sstevel@tonic-gate# paragraph, the line number, and a Pod::Paragraph object. Just dispatches 143*0Sstevel@tonic-gate# the command to a method named the same as the command. =cut is handled 144*0Sstevel@tonic-gate# internally by Pod::Parser. 145*0Sstevel@tonic-gatesub command { 146*0Sstevel@tonic-gate my $self = shift; 147*0Sstevel@tonic-gate my $command = shift; 148*0Sstevel@tonic-gate return if $command eq 'pod'; 149*0Sstevel@tonic-gate return if ($$self{EXCLUDE} && $command ne 'end'); 150*0Sstevel@tonic-gate $self->item ("\n") if defined $$self{ITEM}; 151*0Sstevel@tonic-gate $command = 'cmd_' . $command; 152*0Sstevel@tonic-gate $self->$command (@_); 153*0Sstevel@tonic-gate} 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate# Called for a verbatim paragraph. Gets the paragraph, the line number, and 156*0Sstevel@tonic-gate# a Pod::Paragraph object. Just output it verbatim, but with tabs converted 157*0Sstevel@tonic-gate# to spaces. 158*0Sstevel@tonic-gatesub verbatim { 159*0Sstevel@tonic-gate my $self = shift; 160*0Sstevel@tonic-gate return if $$self{EXCLUDE}; 161*0Sstevel@tonic-gate $self->item if defined $$self{ITEM}; 162*0Sstevel@tonic-gate local $_ = shift; 163*0Sstevel@tonic-gate return if /^\s*$/; 164*0Sstevel@tonic-gate s/^(\s*\S+)/(' ' x $$self{MARGIN}) . $1/gme; 165*0Sstevel@tonic-gate $self->output ($_); 166*0Sstevel@tonic-gate} 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate# Called for a regular text block. Gets the paragraph, the line number, and 169*0Sstevel@tonic-gate# a Pod::Paragraph object. Perform interpolation and output the results. 170*0Sstevel@tonic-gatesub textblock { 171*0Sstevel@tonic-gate my $self = shift; 172*0Sstevel@tonic-gate return if $$self{EXCLUDE}; 173*0Sstevel@tonic-gate $self->output ($_[0]), return if $$self{VERBATIM}; 174*0Sstevel@tonic-gate local $_ = shift; 175*0Sstevel@tonic-gate my $line = shift; 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate # Perform a little magic to collapse multiple L<> references. This is 178*0Sstevel@tonic-gate # here mostly for backwards-compatibility. We'll just rewrite the whole 179*0Sstevel@tonic-gate # thing into actual text at this part, bypassing the whole internal 180*0Sstevel@tonic-gate # sequence parsing thing. 181*0Sstevel@tonic-gate s{ 182*0Sstevel@tonic-gate ( 183*0Sstevel@tonic-gate L< # A link of the form L</something>. 184*0Sstevel@tonic-gate / 185*0Sstevel@tonic-gate ( 186*0Sstevel@tonic-gate [:\w]+ # The item has to be a simple word... 187*0Sstevel@tonic-gate (\(\))? # ...or simple function. 188*0Sstevel@tonic-gate ) 189*0Sstevel@tonic-gate > 190*0Sstevel@tonic-gate ( 191*0Sstevel@tonic-gate ,?\s+(and\s+)? # Allow lots of them, conjuncted. 192*0Sstevel@tonic-gate L< 193*0Sstevel@tonic-gate / 194*0Sstevel@tonic-gate ( 195*0Sstevel@tonic-gate [:\w]+ 196*0Sstevel@tonic-gate (\(\))? 197*0Sstevel@tonic-gate ) 198*0Sstevel@tonic-gate > 199*0Sstevel@tonic-gate )+ 200*0Sstevel@tonic-gate ) 201*0Sstevel@tonic-gate } { 202*0Sstevel@tonic-gate local $_ = $1; 203*0Sstevel@tonic-gate s%L</([^>]+)>%$1%g; 204*0Sstevel@tonic-gate my @items = split /(?:,?\s+(?:and\s+)?)/; 205*0Sstevel@tonic-gate my $string = "the "; 206*0Sstevel@tonic-gate my $i; 207*0Sstevel@tonic-gate for ($i = 0; $i < @items; $i++) { 208*0Sstevel@tonic-gate $string .= $items[$i]; 209*0Sstevel@tonic-gate $string .= ", " if @items > 2 && $i != $#items; 210*0Sstevel@tonic-gate $string .= " and " if ($i == $#items - 1); 211*0Sstevel@tonic-gate } 212*0Sstevel@tonic-gate $string .= " entries elsewhere in this document"; 213*0Sstevel@tonic-gate $string; 214*0Sstevel@tonic-gate }gex; 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate # Now actually interpolate and output the paragraph. 217*0Sstevel@tonic-gate $_ = $self->interpolate ($_, $line); 218*0Sstevel@tonic-gate s/\s+$/\n/; 219*0Sstevel@tonic-gate if (defined $$self{ITEM}) { 220*0Sstevel@tonic-gate $self->item ($_ . "\n"); 221*0Sstevel@tonic-gate } else { 222*0Sstevel@tonic-gate $self->output ($self->reformat ($_ . "\n")); 223*0Sstevel@tonic-gate } 224*0Sstevel@tonic-gate} 225*0Sstevel@tonic-gate 226*0Sstevel@tonic-gate# Called for an interior sequence. Gets the command, argument, and a 227*0Sstevel@tonic-gate# Pod::InteriorSequence object and is expected to return the resulting text. 228*0Sstevel@tonic-gate# Calls code, bold, italic, file, and link to handle those types of 229*0Sstevel@tonic-gate# sequences, and handles S<>, E<>, X<>, and Z<> directly. 230*0Sstevel@tonic-gatesub interior_sequence { 231*0Sstevel@tonic-gate my $self = shift; 232*0Sstevel@tonic-gate my $command = shift; 233*0Sstevel@tonic-gate local $_ = shift; 234*0Sstevel@tonic-gate return '' if ($command eq 'X' || $command eq 'Z'); 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate # Expand escapes into the actual character now, carping if invalid. 237*0Sstevel@tonic-gate if ($command eq 'E') { 238*0Sstevel@tonic-gate return $ESCAPES{$_} if defined $ESCAPES{$_}; 239*0Sstevel@tonic-gate carp "Unknown escape: E<$_>"; 240*0Sstevel@tonic-gate return "E<$_>"; 241*0Sstevel@tonic-gate } 242*0Sstevel@tonic-gate 243*0Sstevel@tonic-gate # For all the other sequences, empty content produces no output. 244*0Sstevel@tonic-gate return if $_ eq ''; 245*0Sstevel@tonic-gate 246*0Sstevel@tonic-gate # For S<>, compress all internal whitespace and then map spaces to \01. 247*0Sstevel@tonic-gate # When we output the text, we'll map this back. 248*0Sstevel@tonic-gate if ($command eq 'S') { 249*0Sstevel@tonic-gate s/\s{2,}/ /g; 250*0Sstevel@tonic-gate tr/ /\01/; 251*0Sstevel@tonic-gate return $_; 252*0Sstevel@tonic-gate } 253*0Sstevel@tonic-gate 254*0Sstevel@tonic-gate # Anything else needs to get dispatched to another method. 255*0Sstevel@tonic-gate if ($command eq 'B') { return $self->seq_b ($_) } 256*0Sstevel@tonic-gate elsif ($command eq 'C') { return $self->seq_c ($_) } 257*0Sstevel@tonic-gate elsif ($command eq 'F') { return $self->seq_f ($_) } 258*0Sstevel@tonic-gate elsif ($command eq 'I') { return $self->seq_i ($_) } 259*0Sstevel@tonic-gate elsif ($command eq 'L') { return $self->seq_l ($_) } 260*0Sstevel@tonic-gate else { carp "Unknown sequence $command<$_>" } 261*0Sstevel@tonic-gate} 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate# Called for each paragraph that's actually part of the POD. We take 264*0Sstevel@tonic-gate# advantage of this opportunity to untabify the input. 265*0Sstevel@tonic-gatesub preprocess_paragraph { 266*0Sstevel@tonic-gate my $self = shift; 267*0Sstevel@tonic-gate local $_ = shift; 268*0Sstevel@tonic-gate 1 while s/^(.*?)(\t+)/$1 . ' ' x (length ($2) * 8 - length ($1) % 8)/me; 269*0Sstevel@tonic-gate $_; 270*0Sstevel@tonic-gate} 271*0Sstevel@tonic-gate 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate############################################################################ 274*0Sstevel@tonic-gate# Command paragraphs 275*0Sstevel@tonic-gate############################################################################ 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate# All command paragraphs take the paragraph and the line number. 278*0Sstevel@tonic-gate 279*0Sstevel@tonic-gate# First level heading. 280*0Sstevel@tonic-gatesub cmd_head1 { 281*0Sstevel@tonic-gate my $self = shift; 282*0Sstevel@tonic-gate local $_ = shift; 283*0Sstevel@tonic-gate s/\s+$//; 284*0Sstevel@tonic-gate $_ = $self->interpolate ($_, shift); 285*0Sstevel@tonic-gate if ($$self{alt}) { 286*0Sstevel@tonic-gate $self->output ("\n==== $_ ====\n\n"); 287*0Sstevel@tonic-gate } else { 288*0Sstevel@tonic-gate $_ .= "\n" if $$self{loose}; 289*0Sstevel@tonic-gate $self->output ($_ . "\n"); 290*0Sstevel@tonic-gate } 291*0Sstevel@tonic-gate} 292*0Sstevel@tonic-gate 293*0Sstevel@tonic-gate# Second level heading. 294*0Sstevel@tonic-gatesub cmd_head2 { 295*0Sstevel@tonic-gate my $self = shift; 296*0Sstevel@tonic-gate local $_ = shift; 297*0Sstevel@tonic-gate s/\s+$//; 298*0Sstevel@tonic-gate $_ = $self->interpolate ($_, shift); 299*0Sstevel@tonic-gate if ($$self{alt}) { 300*0Sstevel@tonic-gate $self->output ("\n== $_ ==\n\n"); 301*0Sstevel@tonic-gate } else { 302*0Sstevel@tonic-gate $self->output (' ' x ($$self{indent} / 2) . $_ . "\n\n"); 303*0Sstevel@tonic-gate } 304*0Sstevel@tonic-gate} 305*0Sstevel@tonic-gate 306*0Sstevel@tonic-gate# Start a list. 307*0Sstevel@tonic-gatesub cmd_over { 308*0Sstevel@tonic-gate my $self = shift; 309*0Sstevel@tonic-gate local $_ = shift; 310*0Sstevel@tonic-gate unless (/^[-+]?\d+\s+$/) { $_ = $$self{indent} } 311*0Sstevel@tonic-gate push (@{ $$self{INDENTS} }, $$self{MARGIN}); 312*0Sstevel@tonic-gate $$self{MARGIN} += ($_ + 0); 313*0Sstevel@tonic-gate} 314*0Sstevel@tonic-gate 315*0Sstevel@tonic-gate# End a list. 316*0Sstevel@tonic-gatesub cmd_back { 317*0Sstevel@tonic-gate my $self = shift; 318*0Sstevel@tonic-gate $$self{MARGIN} = pop @{ $$self{INDENTS} }; 319*0Sstevel@tonic-gate unless (defined $$self{MARGIN}) { 320*0Sstevel@tonic-gate carp "Unmatched =back"; 321*0Sstevel@tonic-gate $$self{MARGIN} = $$self{indent}; 322*0Sstevel@tonic-gate } 323*0Sstevel@tonic-gate} 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate# An individual list item. 326*0Sstevel@tonic-gatesub cmd_item { 327*0Sstevel@tonic-gate my $self = shift; 328*0Sstevel@tonic-gate if (defined $$self{ITEM}) { $self->item } 329*0Sstevel@tonic-gate local $_ = shift; 330*0Sstevel@tonic-gate s/\s+$//; 331*0Sstevel@tonic-gate $$self{ITEM} = $self->interpolate ($_); 332*0Sstevel@tonic-gate} 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate# Begin a block for a particular translator. Setting VERBATIM triggers 335*0Sstevel@tonic-gate# special handling in textblock(). 336*0Sstevel@tonic-gatesub cmd_begin { 337*0Sstevel@tonic-gate my $self = shift; 338*0Sstevel@tonic-gate local $_ = shift; 339*0Sstevel@tonic-gate my ($kind) = /^(\S+)/ or return; 340*0Sstevel@tonic-gate if ($kind eq 'text') { 341*0Sstevel@tonic-gate $$self{VERBATIM} = 1; 342*0Sstevel@tonic-gate } else { 343*0Sstevel@tonic-gate $$self{EXCLUDE} = 1; 344*0Sstevel@tonic-gate } 345*0Sstevel@tonic-gate} 346*0Sstevel@tonic-gate 347*0Sstevel@tonic-gate# End a block for a particular translator. We assume that all =begin/=end 348*0Sstevel@tonic-gate# pairs are properly closed. 349*0Sstevel@tonic-gatesub cmd_end { 350*0Sstevel@tonic-gate my $self = shift; 351*0Sstevel@tonic-gate $$self{EXCLUDE} = 0; 352*0Sstevel@tonic-gate $$self{VERBATIM} = 0; 353*0Sstevel@tonic-gate} 354*0Sstevel@tonic-gate 355*0Sstevel@tonic-gate# One paragraph for a particular translator. Ignore it unless it's intended 356*0Sstevel@tonic-gate# for text, in which case we treat it as a verbatim text block. 357*0Sstevel@tonic-gatesub cmd_for { 358*0Sstevel@tonic-gate my $self = shift; 359*0Sstevel@tonic-gate local $_ = shift; 360*0Sstevel@tonic-gate my $line = shift; 361*0Sstevel@tonic-gate return unless s/^text\b[ \t]*\n?//; 362*0Sstevel@tonic-gate $self->verbatim ($_, $line); 363*0Sstevel@tonic-gate} 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate 366*0Sstevel@tonic-gate############################################################################ 367*0Sstevel@tonic-gate# Interior sequences 368*0Sstevel@tonic-gate############################################################################ 369*0Sstevel@tonic-gate 370*0Sstevel@tonic-gate# The simple formatting ones. These are here mostly so that subclasses can 371*0Sstevel@tonic-gate# override them and do more complicated things. 372*0Sstevel@tonic-gatesub seq_b { return $_[0]{alt} ? "``$_[1]''" : $_[1] } 373*0Sstevel@tonic-gatesub seq_c { return $_[0]{alt} ? "``$_[1]''" : "`$_[1]'" } 374*0Sstevel@tonic-gatesub seq_f { return $_[0]{alt} ? "\"$_[1]\"" : $_[1] } 375*0Sstevel@tonic-gatesub seq_i { return '*' . $_[1] . '*' } 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gate# The complicated one. Handle links. Since this is plain text, we can't 378*0Sstevel@tonic-gate# actually make any real links, so this is all to figure out what text we 379*0Sstevel@tonic-gate# print out. 380*0Sstevel@tonic-gatesub seq_l { 381*0Sstevel@tonic-gate my $self = shift; 382*0Sstevel@tonic-gate local $_ = shift; 383*0Sstevel@tonic-gate 384*0Sstevel@tonic-gate # Smash whitespace in case we were split across multiple lines. 385*0Sstevel@tonic-gate s/\s+/ /g; 386*0Sstevel@tonic-gate 387*0Sstevel@tonic-gate # If we were given any explicit text, just output it. 388*0Sstevel@tonic-gate if (/^([^|]+)\|/) { return $1 } 389*0Sstevel@tonic-gate 390*0Sstevel@tonic-gate # Okay, leading and trailing whitespace isn't important; get rid of it. 391*0Sstevel@tonic-gate s/^\s+//; 392*0Sstevel@tonic-gate s/\s+$//; 393*0Sstevel@tonic-gate 394*0Sstevel@tonic-gate # Default to using the whole content of the link entry as a section 395*0Sstevel@tonic-gate # name. Note that L<manpage/> forces a manpage interpretation, as does 396*0Sstevel@tonic-gate # something looking like L<manpage(section)>. The latter is an 397*0Sstevel@tonic-gate # enhancement over the original Pod::Text. 398*0Sstevel@tonic-gate my ($manpage, $section) = ('', $_); 399*0Sstevel@tonic-gate if (/^(?:https?|ftp|news):/) { 400*0Sstevel@tonic-gate # a URL 401*0Sstevel@tonic-gate return $_; 402*0Sstevel@tonic-gate } elsif (/^"\s*(.*?)\s*"$/) { 403*0Sstevel@tonic-gate $section = '"' . $1 . '"'; 404*0Sstevel@tonic-gate } elsif (m/^[-:.\w]+(?:\(\S+\))?$/) { 405*0Sstevel@tonic-gate ($manpage, $section) = ($_, ''); 406*0Sstevel@tonic-gate } elsif (m%/%) { 407*0Sstevel@tonic-gate ($manpage, $section) = split (/\s*\/\s*/, $_, 2); 408*0Sstevel@tonic-gate } 409*0Sstevel@tonic-gate 410*0Sstevel@tonic-gate my $text = ''; 411*0Sstevel@tonic-gate # Now build the actual output text. 412*0Sstevel@tonic-gate if (!length $section) { 413*0Sstevel@tonic-gate $text = "the $manpage manpage" if length $manpage; 414*0Sstevel@tonic-gate } elsif ($section =~ /^[:\w]+(?:\(\))?/) { 415*0Sstevel@tonic-gate $text .= 'the ' . $section . ' entry'; 416*0Sstevel@tonic-gate $text .= (length $manpage) ? " in the $manpage manpage" 417*0Sstevel@tonic-gate : " elsewhere in this document"; 418*0Sstevel@tonic-gate } else { 419*0Sstevel@tonic-gate $section =~ s/^\"\s*//; 420*0Sstevel@tonic-gate $section =~ s/\s*\"$//; 421*0Sstevel@tonic-gate $text .= 'the section on "' . $section . '"'; 422*0Sstevel@tonic-gate $text .= " in the $manpage manpage" if length $manpage; 423*0Sstevel@tonic-gate } 424*0Sstevel@tonic-gate $text; 425*0Sstevel@tonic-gate} 426*0Sstevel@tonic-gate 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate############################################################################ 429*0Sstevel@tonic-gate# List handling 430*0Sstevel@tonic-gate############################################################################ 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate# This method is called whenever an =item command is complete (in other 433*0Sstevel@tonic-gate# words, we've seen its associated paragraph or know for certain that it 434*0Sstevel@tonic-gate# doesn't have one). It gets the paragraph associated with the item as an 435*0Sstevel@tonic-gate# argument. If that argument is empty, just output the item tag; if it 436*0Sstevel@tonic-gate# contains a newline, output the item tag followed by the newline. 437*0Sstevel@tonic-gate# Otherwise, see if there's enough room for us to output the item tag in the 438*0Sstevel@tonic-gate# margin of the text or if we have to put it on a separate line. 439*0Sstevel@tonic-gatesub item { 440*0Sstevel@tonic-gate my $self = shift; 441*0Sstevel@tonic-gate local $_ = shift; 442*0Sstevel@tonic-gate my $tag = $$self{ITEM}; 443*0Sstevel@tonic-gate unless (defined $tag) { 444*0Sstevel@tonic-gate carp "item called without tag"; 445*0Sstevel@tonic-gate return; 446*0Sstevel@tonic-gate } 447*0Sstevel@tonic-gate undef $$self{ITEM}; 448*0Sstevel@tonic-gate my $indent = $$self{INDENTS}[-1]; 449*0Sstevel@tonic-gate unless (defined $indent) { $indent = $$self{indent} } 450*0Sstevel@tonic-gate my $space = ' ' x $indent; 451*0Sstevel@tonic-gate $space =~ s/^ /:/ if $$self{alt}; 452*0Sstevel@tonic-gate if (!$_ || /^\s+$/ || ($$self{MARGIN} - $indent < length ($tag) + 1)) { 453*0Sstevel@tonic-gate my $margin = $$self{MARGIN}; 454*0Sstevel@tonic-gate $$self{MARGIN} = $indent; 455*0Sstevel@tonic-gate my $output = $self->reformat ($tag); 456*0Sstevel@tonic-gate $output =~ s/\n*$/\n/; 457*0Sstevel@tonic-gate $self->output ($output); 458*0Sstevel@tonic-gate $$self{MARGIN} = $margin; 459*0Sstevel@tonic-gate $self->output ($self->reformat ($_)) if /\S/; 460*0Sstevel@tonic-gate } else { 461*0Sstevel@tonic-gate $_ = $self->reformat ($_); 462*0Sstevel@tonic-gate s/^ /:/ if ($$self{alt} && $indent > 0); 463*0Sstevel@tonic-gate my $tagspace = ' ' x length $tag; 464*0Sstevel@tonic-gate s/^($space)$tagspace/$1$tag/ or warn "Bizarre space in item"; 465*0Sstevel@tonic-gate $self->output ($_); 466*0Sstevel@tonic-gate } 467*0Sstevel@tonic-gate} 468*0Sstevel@tonic-gate 469*0Sstevel@tonic-gate 470*0Sstevel@tonic-gate############################################################################ 471*0Sstevel@tonic-gate# Output formatting 472*0Sstevel@tonic-gate############################################################################ 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate# Wrap a line, indenting by the current left margin. We can't use 475*0Sstevel@tonic-gate# Text::Wrap because it plays games with tabs. We can't use formline, even 476*0Sstevel@tonic-gate# though we'd really like to, because it screws up non-printing characters. 477*0Sstevel@tonic-gate# So we have to do the wrapping ourselves. 478*0Sstevel@tonic-gatesub wrap { 479*0Sstevel@tonic-gate my $self = shift; 480*0Sstevel@tonic-gate local $_ = shift; 481*0Sstevel@tonic-gate my $output = ''; 482*0Sstevel@tonic-gate my $spaces = ' ' x $$self{MARGIN}; 483*0Sstevel@tonic-gate my $width = $$self{width} - $$self{MARGIN}; 484*0Sstevel@tonic-gate while (length > $width) { 485*0Sstevel@tonic-gate if (s/^([^\n]{0,$width})\s+// || s/^([^\n]{$width})//) { 486*0Sstevel@tonic-gate $output .= $spaces . $1 . "\n"; 487*0Sstevel@tonic-gate } else { 488*0Sstevel@tonic-gate last; 489*0Sstevel@tonic-gate } 490*0Sstevel@tonic-gate } 491*0Sstevel@tonic-gate $output .= $spaces . $_; 492*0Sstevel@tonic-gate $output =~ s/\s+$/\n\n/; 493*0Sstevel@tonic-gate $output; 494*0Sstevel@tonic-gate} 495*0Sstevel@tonic-gate 496*0Sstevel@tonic-gate# Reformat a paragraph of text for the current margin. Takes the text to 497*0Sstevel@tonic-gate# reformat and returns the formatted text. 498*0Sstevel@tonic-gatesub reformat { 499*0Sstevel@tonic-gate my $self = shift; 500*0Sstevel@tonic-gate local $_ = shift; 501*0Sstevel@tonic-gate 502*0Sstevel@tonic-gate # If we're trying to preserve two spaces after sentences, do some 503*0Sstevel@tonic-gate # munging to support that. Otherwise, smash all repeated whitespace. 504*0Sstevel@tonic-gate if ($$self{sentence}) { 505*0Sstevel@tonic-gate s/ +$//mg; 506*0Sstevel@tonic-gate s/\.\n/. \n/g; 507*0Sstevel@tonic-gate s/\n/ /g; 508*0Sstevel@tonic-gate s/ +/ /g; 509*0Sstevel@tonic-gate } else { 510*0Sstevel@tonic-gate s/\s+/ /g; 511*0Sstevel@tonic-gate } 512*0Sstevel@tonic-gate $self->wrap ($_); 513*0Sstevel@tonic-gate} 514*0Sstevel@tonic-gate 515*0Sstevel@tonic-gate# Output text to the output device. 516*0Sstevel@tonic-gatesub output { $_[1] =~ tr/\01/ /; print { $_[0]->output_handle } $_[1] } 517*0Sstevel@tonic-gate 518*0Sstevel@tonic-gate 519*0Sstevel@tonic-gate############################################################################ 520*0Sstevel@tonic-gate# Backwards compatibility 521*0Sstevel@tonic-gate############################################################################ 522*0Sstevel@tonic-gate 523*0Sstevel@tonic-gate# The old Pod::Text module did everything in a pod2text() function. This 524*0Sstevel@tonic-gate# tries to provide the same interface for legacy applications. 525*0Sstevel@tonic-gatesub pod2text { 526*0Sstevel@tonic-gate my @args; 527*0Sstevel@tonic-gate 528*0Sstevel@tonic-gate # This is really ugly; I hate doing option parsing in the middle of a 529*0Sstevel@tonic-gate # module. But the old Pod::Text module supported passing flags to its 530*0Sstevel@tonic-gate # entry function, so handle -a and -<number>. 531*0Sstevel@tonic-gate while ($_[0] =~ /^-/) { 532*0Sstevel@tonic-gate my $flag = shift; 533*0Sstevel@tonic-gate if ($flag eq '-a') { push (@args, alt => 1) } 534*0Sstevel@tonic-gate elsif ($flag =~ /^-(\d+)$/) { push (@args, width => $1) } 535*0Sstevel@tonic-gate else { 536*0Sstevel@tonic-gate unshift (@_, $flag); 537*0Sstevel@tonic-gate last; 538*0Sstevel@tonic-gate } 539*0Sstevel@tonic-gate } 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate # Now that we know what arguments we're using, create the parser. 542*0Sstevel@tonic-gate my $parser = Pod::PlainText->new (@args); 543*0Sstevel@tonic-gate 544*0Sstevel@tonic-gate # If two arguments were given, the second argument is going to be a file 545*0Sstevel@tonic-gate # handle. That means we want to call parse_from_filehandle(), which 546*0Sstevel@tonic-gate # means we need to turn the first argument into a file handle. Magic 547*0Sstevel@tonic-gate # open will handle the <&STDIN case automagically. 548*0Sstevel@tonic-gate if (defined $_[1]) { 549*0Sstevel@tonic-gate local *IN; 550*0Sstevel@tonic-gate unless (open (IN, $_[0])) { 551*0Sstevel@tonic-gate croak ("Can't open $_[0] for reading: $!\n"); 552*0Sstevel@tonic-gate return; 553*0Sstevel@tonic-gate } 554*0Sstevel@tonic-gate $_[0] = \*IN; 555*0Sstevel@tonic-gate return $parser->parse_from_filehandle (@_); 556*0Sstevel@tonic-gate } else { 557*0Sstevel@tonic-gate return $parser->parse_from_file (@_); 558*0Sstevel@tonic-gate } 559*0Sstevel@tonic-gate} 560*0Sstevel@tonic-gate 561*0Sstevel@tonic-gate 562*0Sstevel@tonic-gate############################################################################ 563*0Sstevel@tonic-gate# Module return value and documentation 564*0Sstevel@tonic-gate############################################################################ 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate1; 567*0Sstevel@tonic-gate__END__ 568*0Sstevel@tonic-gate 569*0Sstevel@tonic-gate=head1 NAME 570*0Sstevel@tonic-gate 571*0Sstevel@tonic-gatePod::PlainText - Convert POD data to formatted ASCII text 572*0Sstevel@tonic-gate 573*0Sstevel@tonic-gate=head1 SYNOPSIS 574*0Sstevel@tonic-gate 575*0Sstevel@tonic-gate use Pod::PlainText; 576*0Sstevel@tonic-gate my $parser = Pod::PlainText->new (sentence => 0, width => 78); 577*0Sstevel@tonic-gate 578*0Sstevel@tonic-gate # Read POD from STDIN and write to STDOUT. 579*0Sstevel@tonic-gate $parser->parse_from_filehandle; 580*0Sstevel@tonic-gate 581*0Sstevel@tonic-gate # Read POD from file.pod and write to file.txt. 582*0Sstevel@tonic-gate $parser->parse_from_file ('file.pod', 'file.txt'); 583*0Sstevel@tonic-gate 584*0Sstevel@tonic-gate=head1 DESCRIPTION 585*0Sstevel@tonic-gate 586*0Sstevel@tonic-gatePod::PlainText is a module that can convert documentation in the POD format (the 587*0Sstevel@tonic-gatepreferred language for documenting Perl) into formatted ASCII. It uses no 588*0Sstevel@tonic-gatespecial formatting controls or codes whatsoever, and its output is therefore 589*0Sstevel@tonic-gatesuitable for nearly any device. 590*0Sstevel@tonic-gate 591*0Sstevel@tonic-gateAs a derived class from Pod::Parser, Pod::PlainText supports the same methods and 592*0Sstevel@tonic-gateinterfaces. See L<Pod::Parser> for all the details; briefly, one creates a 593*0Sstevel@tonic-gatenew parser with C<Pod::PlainText-E<gt>new()> and then calls either 594*0Sstevel@tonic-gateparse_from_filehandle() or parse_from_file(). 595*0Sstevel@tonic-gate 596*0Sstevel@tonic-gatenew() can take options, in the form of key/value pairs, that control the 597*0Sstevel@tonic-gatebehavior of the parser. The currently recognized options are: 598*0Sstevel@tonic-gate 599*0Sstevel@tonic-gate=over 4 600*0Sstevel@tonic-gate 601*0Sstevel@tonic-gate=item alt 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gateIf set to a true value, selects an alternate output format that, among other 604*0Sstevel@tonic-gatethings, uses a different heading style and marks C<=item> entries with a 605*0Sstevel@tonic-gatecolon in the left margin. Defaults to false. 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate=item indent 608*0Sstevel@tonic-gate 609*0Sstevel@tonic-gateThe number of spaces to indent regular text, and the default indentation for 610*0Sstevel@tonic-gateC<=over> blocks. Defaults to 4. 611*0Sstevel@tonic-gate 612*0Sstevel@tonic-gate=item loose 613*0Sstevel@tonic-gate 614*0Sstevel@tonic-gateIf set to a true value, a blank line is printed after a C<=head1> heading. 615*0Sstevel@tonic-gateIf set to false (the default), no blank line is printed after C<=head1>, 616*0Sstevel@tonic-gatealthough one is still printed after C<=head2>. This is the default because 617*0Sstevel@tonic-gateit's the expected formatting for manual pages; if you're formatting 618*0Sstevel@tonic-gatearbitrary text documents, setting this to true may result in more pleasing 619*0Sstevel@tonic-gateoutput. 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate=item sentence 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gateIf set to a true value, Pod::PlainText will assume that each sentence ends in two 624*0Sstevel@tonic-gatespaces, and will try to preserve that spacing. If set to false, all 625*0Sstevel@tonic-gateconsecutive whitespace in non-verbatim paragraphs is compressed into a 626*0Sstevel@tonic-gatesingle space. Defaults to true. 627*0Sstevel@tonic-gate 628*0Sstevel@tonic-gate=item width 629*0Sstevel@tonic-gate 630*0Sstevel@tonic-gateThe column at which to wrap text on the right-hand side. Defaults to 76. 631*0Sstevel@tonic-gate 632*0Sstevel@tonic-gate=back 633*0Sstevel@tonic-gate 634*0Sstevel@tonic-gateThe standard Pod::Parser method parse_from_filehandle() takes up to two 635*0Sstevel@tonic-gatearguments, the first being the file handle to read POD from and the second 636*0Sstevel@tonic-gatebeing the file handle to write the formatted output to. The first defaults 637*0Sstevel@tonic-gateto STDIN if not given, and the second defaults to STDOUT. The method 638*0Sstevel@tonic-gateparse_from_file() is almost identical, except that its two arguments are the 639*0Sstevel@tonic-gateinput and output disk files instead. See L<Pod::Parser> for the specific 640*0Sstevel@tonic-gatedetails. 641*0Sstevel@tonic-gate 642*0Sstevel@tonic-gate=head1 DIAGNOSTICS 643*0Sstevel@tonic-gate 644*0Sstevel@tonic-gate=over 4 645*0Sstevel@tonic-gate 646*0Sstevel@tonic-gate=item Bizarre space in item 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate(W) Something has gone wrong in internal C<=item> processing. This message 649*0Sstevel@tonic-gateindicates a bug in Pod::PlainText; you should never see it. 650*0Sstevel@tonic-gate 651*0Sstevel@tonic-gate=item Can't open %s for reading: %s 652*0Sstevel@tonic-gate 653*0Sstevel@tonic-gate(F) Pod::PlainText was invoked via the compatibility mode pod2text() interface 654*0Sstevel@tonic-gateand the input file it was given could not be opened. 655*0Sstevel@tonic-gate 656*0Sstevel@tonic-gate=item Unknown escape: %s 657*0Sstevel@tonic-gate 658*0Sstevel@tonic-gate(W) The POD source contained an C<EE<lt>E<gt>> escape that Pod::PlainText didn't 659*0Sstevel@tonic-gateknow about. 660*0Sstevel@tonic-gate 661*0Sstevel@tonic-gate=item Unknown sequence: %s 662*0Sstevel@tonic-gate 663*0Sstevel@tonic-gate(W) The POD source contained a non-standard internal sequence (something of 664*0Sstevel@tonic-gatethe form C<XE<lt>E<gt>>) that Pod::PlainText didn't know about. 665*0Sstevel@tonic-gate 666*0Sstevel@tonic-gate=item Unmatched =back 667*0Sstevel@tonic-gate 668*0Sstevel@tonic-gate(W) Pod::PlainText encountered a C<=back> command that didn't correspond to an 669*0Sstevel@tonic-gateC<=over> command. 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gate=back 672*0Sstevel@tonic-gate 673*0Sstevel@tonic-gate=head1 RESTRICTIONS 674*0Sstevel@tonic-gate 675*0Sstevel@tonic-gateEmbedded Ctrl-As (octal 001) in the input will be mapped to spaces on 676*0Sstevel@tonic-gateoutput, due to an internal implementation detail. 677*0Sstevel@tonic-gate 678*0Sstevel@tonic-gate=head1 NOTES 679*0Sstevel@tonic-gate 680*0Sstevel@tonic-gateThis is a replacement for an earlier Pod::Text module written by Tom 681*0Sstevel@tonic-gateChristiansen. It has a revamped interface, since it now uses Pod::Parser, 682*0Sstevel@tonic-gatebut an interface roughly compatible with the old Pod::Text::pod2text() 683*0Sstevel@tonic-gatefunction is still available. Please change to the new calling convention, 684*0Sstevel@tonic-gatethough. 685*0Sstevel@tonic-gate 686*0Sstevel@tonic-gateThe original Pod::Text contained code to do formatting via termcap 687*0Sstevel@tonic-gatesequences, although it wasn't turned on by default and it was problematic to 688*0Sstevel@tonic-gateget it to work at all. This rewrite doesn't even try to do that, but a 689*0Sstevel@tonic-gatesubclass of it does. Look for L<Pod::Text::Termcap|Pod::Text::Termcap>. 690*0Sstevel@tonic-gate 691*0Sstevel@tonic-gate=head1 SEE ALSO 692*0Sstevel@tonic-gate 693*0Sstevel@tonic-gateL<Pod::Parser|Pod::Parser>, L<Pod::Text::Termcap|Pod::Text::Termcap>, 694*0Sstevel@tonic-gatepod2text(1) 695*0Sstevel@tonic-gate 696*0Sstevel@tonic-gate=head1 AUTHOR 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gatePlease report bugs using L<http://rt.cpan.org>. 699*0Sstevel@tonic-gate 700*0Sstevel@tonic-gateRuss Allbery E<lt>rra@stanford.eduE<gt>, based I<very> heavily on the 701*0Sstevel@tonic-gateoriginal Pod::Text by Tom Christiansen E<lt>tchrist@mox.perl.comE<gt> and 702*0Sstevel@tonic-gateits conversion to Pod::Parser by Brad Appleton 703*0Sstevel@tonic-gateE<lt>bradapp@enteract.comE<gt>. 704*0Sstevel@tonic-gate 705*0Sstevel@tonic-gate=cut 706