home *** CD-ROM | disk | FTP | other *** search
/ Australian Personal Computer 2004 July / APC0407D2.iso / workshop / apache / files / ActivePerl-5.6.1.638-MSWin32-x86.msi / _a21e093b44458bab6e563fa98932e043 < prev    next >
Encoding:
Text File  |  2004-04-13  |  70.2 KB  |  2,407 lines

  1. package Text::Autoformat;
  2.  
  3. use strict; use vars qw($VERSION @ISA @EXPORT @EXPORT_OK); use Carp;
  4. use 5.005;
  5. $VERSION = '1.04';
  6.  
  7. require Exporter;
  8.  
  9. @ISA = qw(Exporter);
  10. @EXPORT = qw( autoformat );
  11. @EXPORT_OK = qw( form tag break_with break_wrap break_TeX );
  12.  
  13.  
  14. my $IGNORABLES = join "|", qw {
  15.     a an at as and are
  16.     but by 
  17.     for from
  18.     in is
  19.     of on or
  20.     the to that 
  21.     with while whilst with without
  22. };
  23.  
  24.  
  25. my @bspecials = qw( [ | ] );
  26. my @lspecials = qw( < ^ > );
  27. my $ljustified = '[<]{2,}[>]{2,}';
  28. my $bjustified = '[[]{2,}[]]{2,}';
  29. my $bsingle    = '~+';
  30. my @specials = (@bspecials, @lspecials);
  31. my $fixed_fieldpat = join('|', ($ljustified, $bjustified,
  32.                 $bsingle,
  33.                 map { "\\$_\{2,}" } @specials));
  34. my ($lfieldmark, $bfieldmark, $fieldmark, $fieldpat, $decimal);
  35. my $emptyref = '';
  36.  
  37. sub import
  38. {
  39. #    use POSIX qw( localeconv );
  40. #    $decimal = localeconv()->{decimal_point} || '.';
  41.     $decimal = '.';
  42.  
  43.     my $lnumerical = '[>]+(?:'.quotemeta($decimal).'[<]{1,})';
  44.     my $bnumerical = '[]]+(?:'.quotemeta($decimal).'[[]{1,})';
  45.  
  46.     $fieldpat = join('|', ($lnumerical, $bnumerical,$fixed_fieldpat));
  47.  
  48.     $lfieldmark = join '|', ($lnumerical, $ljustified, map { "\\$_\{2}" } @lspecials);
  49.     $bfieldmark = join '|', ($bnumerical, $bjustified, $bsingle, map { "\\$_\{2}" } @bspecials);
  50.     $fieldmark  = join '|', ($lnumerical, $bnumerical,
  51.                  $bsingle,
  52.                  $ljustified, $bjustified,
  53.                  $lfieldmark, $bfieldmark);
  54.  
  55.     Text::Autoformat->export_to_level(1, @_);
  56. }
  57.  
  58. ###### USEFUL TOOLS ######################################
  59.  
  60. #===== form =============================================#
  61.  
  62. sub BAD_CONFIG { 'Configuration hash not allowed between format and data' }
  63.  
  64. sub break_with
  65. {
  66.     my $hyphen = $_[0];
  67.     my $hylen = length($hyphen);
  68.     my @ret;
  69.     sub
  70.     {
  71.         if ($_[2]<=$hylen)
  72.         {
  73.             @ret = (substr($_[0],0,1), substr($_[0],1))
  74.         }
  75.         else
  76.         {
  77.             @ret = (substr($_[0],0,$_[1]-length($hyphen)),
  78.                 substr($_[0],$_[1]-length($hyphen)))
  79.         }
  80.         if ($ret[0] =~ /\A\s*\Z/) { return ("",$_[0]); }
  81.         else { return ($ret[0].$hyphen,$ret[1]); }
  82.     }
  83.  
  84. }
  85.  
  86. sub break_wrap
  87. {
  88.     return \&break_wrap unless @_;
  89.     my ($text, $reqlen, $fldlen) = @_;
  90.     if ($reqlen==$fldlen) { $text =~ m/\A(\s*\S*)(.*)/s }
  91.     else                  { ("", $text) }
  92. }
  93.  
  94. my %hyp;
  95. sub break_TeX
  96. {
  97.     my $file = $_[0] || "";
  98.  
  99.     croak "Can't find TeX::Hypen module"
  100.         unless require "TeX/Hyphen.pm";
  101.  
  102.     $hyp{$file} = TeX::Hyphen->new($file||undef)
  103.             || croak "Can't open hyphenation file $file"
  104.         unless $hyp{$file};
  105.  
  106.     return sub {
  107.         for (reverse $hyp{$file}->hyphenate($_[0])) {
  108.             if ($_ < $_[1]) {
  109.                 return (substr($_[0],0,$_).'-',
  110.                     substr($_[0],$_) );
  111.             }
  112.         }
  113.         return ("",$_[0]);
  114.     }
  115. }
  116.  
  117. sub debug { print STDERR @_, "\n" if $::DEBUG || $::DEBUG }
  118.  
  119. sub notempty
  120. {
  121.     my $ne = ${$_[0]} =~ /\S/;
  122.     debug("\tnotempty('${$_[0]}') = $ne\n");
  123.     return $ne;
  124. }
  125.  
  126. sub replace($$$$)   # ($fmt, $len, $argref, $config)
  127. {
  128.     my $ref = $_[2];
  129.     my $text = '';
  130.     my $rem = $_[1];
  131.     my $config = $_[3];
  132.  
  133.     $$ref =~ s/\A\s*//;
  134.     my $fmtnum = length $_[0];
  135.  
  136.     if ($$ref =~ /\S/ && $fmtnum>2)
  137.     {
  138.     NUMERICAL:{
  139.         #use POSIX qw( strtod );
  140.         my ($ilen,$dlen) = map {length} $_[0] =~ m/([]>]+)\Q$decimal\E([[<]+)/;
  141.         my ($num,$unconsumed) = strtod($$ref);
  142.         if ($unconsumed == length $$ref)
  143.         {
  144.             $$ref =~ s/\s*\S*//;
  145.             redo NUMERICAL if $config->{numeric} =~ m/\bSkipNaN\b/i
  146.                        && $$ref =~ m/\S/;
  147.             $text = '?' x $ilen . $decimal . '?' x $dlen;
  148.             $rem = 0;
  149.             return $text;
  150.         }
  151.         my $formatted = sprintf "%$fmtnum.${dlen}lf", $num;
  152.         $text = (length $formatted > $fmtnum)
  153.             ? '#' x $ilen . $decimal . '#' x $dlen
  154.             : $formatted;
  155.         $text =~ s/(\Q$decimal\E\d+?)(0+)$/$1 . " " x length $2/e
  156.             unless $config->{numeric} =~ m/\bAllPlaces\b/i
  157.                 || $num =~ /\Q$decimal\E\d\d{$dlen,}$/;
  158.         if ($unconsumed)
  159.         {
  160.             if ($unconsumed == length $$ref)
  161.                 { $$ref =~ s/\A.[^0-9.+-]*// }
  162.             else
  163.                 { substr($$ref,0,-$unconsumed) = ""}
  164.         }
  165.         else            { $$ref = "" }
  166.         $rem = 0;
  167.         }
  168.     }
  169.     else
  170.     {
  171.         while ($$ref =~ /\S/)
  172.         {
  173.             last if !$config->{fill} && $$ref=~s/\A\n//;
  174.             last unless $$ref =~ /\A(\s*)(\S+)(.*)\z/s;
  175.             my ($ws, $word, $extra) = ($1,$2,$3);
  176.             my $nonnl = $ws =~ /[^\n]/;
  177.             $ws =~ s/\n/$nonnl? "" : " "/ge if $config->{fill};
  178.             my $lead = ($config->{squeeze} ? ($ws ? " " : "") : $ws);
  179.             my $match = $lead . $word;
  180.             last if $text && $match =~ /\n/;
  181.             my $len1 = length($match);
  182.             if ($len1 <= $rem)
  183.             {
  184.                 $text .= $match;
  185.                 $rem  -= $len1;
  186.                 $$ref = $extra;
  187.             }
  188.             else
  189.             {
  190.                 if ($len1 > $_[1] and $rem-length($lead)>$config->{minbreak})
  191.                 {
  192.                     my ($broken,$left) =
  193.                         $config->{break}->($match,$rem,$_[1]);    
  194.                     $text .= $broken;
  195.                     $$ref = $left.$extra;
  196.                     $rem -= length $broken;
  197.                 }
  198.                 last;
  199.             }
  200.         }
  201.     }
  202.  
  203.     unless (length $text)
  204.     {
  205.         $text = substr($$ref,0,$rem);
  206.         substr($$ref,0,$rem) = "";
  207.         $rem -= length $text;
  208.     }
  209.  
  210.     if ( $_[0] eq 'J' && $text =~ / / && length($$ref))     # FULLY JUSTIFIED
  211.     {
  212.         $text = reverse $text;
  213.         $text =~ s/( +)/($rem-->0?" ":"").$1/ge while $rem>0;
  214.         $text = reverse $text;
  215.     }
  216.     elsif ( $_[0] =~ /[~<[J]/ )             # LEFT JUSTIFIED
  217.     {
  218.         $text .= ' ' x $rem
  219.     }
  220.     elsif ( $_[0] =~ /\>|\]/ )            # RIGHT JUSTIFIED
  221.     {
  222.         substr($text,0,0) = ' ' x $rem;
  223.     }
  224.     elsif ( $_[0] =~ /\^|\|/ )            # CENTRE JUSTIFIED
  225.     {
  226.         my $halfrem = int($rem/2);
  227.         substr($text,0,0) = ' ' x $halfrem;
  228.         $text .= ' ' x ($rem-$halfrem);
  229.     }
  230.  
  231.     return $text;
  232. }
  233.  
  234. my %std_config =
  235. (
  236.     header     => sub{""},
  237.     footer     => sub{""},
  238.     pagefeed => sub{""},
  239.     pagelen     => 0,
  240.     pagenum     => do { \(my $pagenum = 1 )},
  241.     break     => break_with('-'),
  242.     minbreak => 2,
  243.     squeeze     => 0,
  244.     numeric     => "",
  245.     _used    => 1,
  246. );
  247.  
  248. sub lcr {
  249.     my ($data) = @_;
  250.     $data->{width}  ||= 72;
  251.     $data->{left}   ||= "";
  252.     $data->{centre} ||= $data->{center}||"";
  253.     $data->{right}  ||= "";
  254.     return sub {
  255.         my $l = ref $data->{left} eq 'CODE'
  256.                 ? $data->{left}->(@_) : $data->{left};
  257.         my $c = ref $data->{centre} eq 'CODE'
  258.                 ? $data->{centre}->(@_) : $data->{centre};
  259.         my $r = ref $data->{right} eq 'CODE'
  260.                 ? $data->{right}->(@_) : $data->{right};
  261.         my $gap = int(($data->{width}-length($c))/2-length($l));
  262.         return $l . " " x $gap
  263.              . $c . " " x ($data->{width}-length($l)-length($c)-$gap-length($r))
  264.              . $r;
  265.     }
  266. }
  267.  
  268. sub fix_config(\%)
  269. {
  270.     my ($config) = @_;
  271.     if (ref $config->{header} eq 'HASH') {
  272.         $config->{header} = lcr $config->{header};
  273.     }
  274.     unless (ref $config->{header} eq 'CODE') {
  275.         my $tmp = $config->{header}; $config->{header} = sub { $tmp }
  276.     }
  277.     if (ref $config->{footer} eq 'HASH') {
  278.         $config->{footer} = lcr $config->{footer};
  279.     }
  280.     unless (ref $config->{footer} eq 'CODE') {
  281.         my $tmp = $config->{footer}; $config->{footer} = sub { $tmp }
  282.     }
  283.     unless (ref $config->{pagefeed} eq 'CODE')
  284.         { my $tmp = $config->{pagefeed}; $config->{pagefeed} = sub { $tmp } }
  285.     unless (ref $config->{break} eq 'CODE')
  286.         { $config->{break} = break_with($config->{break}) }
  287.     unless (ref $config->{pagenum} eq 'SCALAR') 
  288.         { my $tmp = $config->{pagenum}; $config->{pagenum} = \$tmp }
  289. }
  290.  
  291. sub FormOpt::DESTROY
  292. {
  293.     carp "Configuration specified at $std_config{_line} was not used before it went out of scope"
  294.         if $^W && !$std_config{_used};
  295.     %std_config = %{$std_config{_prev}};
  296. }
  297.  
  298. sub form
  299. {
  300.     my $config = {%std_config};
  301.     my $startidx = 0;
  302.     if (@_ && ref($_[0]) eq 'HASH')        # RESETTING CONFIG
  303.     {
  304.         if (@_ > 1)            # TEMPORARY RESET
  305.         {
  306.             $config = {%$config, %{$_[$startidx++]}};
  307.             fix_config(%$config);
  308.             $startidx = 1;
  309.         }
  310.         elsif (defined wantarray)    # CONTEXT BEING CAPTURED
  311.         {
  312.             $_[0]->{_prev} = { %std_config };
  313.             $_[0]->{_used} = 0;
  314.             $_[0]->{_line} = join " line ", (caller)[1..2];;
  315.             %{$_[0]} = %std_config = (%std_config, %{$_[0]});
  316.             fix_config(%std_config);
  317.             return bless $_[0], 'FormOpt';
  318.         }
  319.         else                # PERMANENT RESET
  320.         {
  321.             $_[0]->{_used} = 1;
  322.             $_[0]->{_line} = join " line ", (caller)[1..2];;
  323.             %std_config = (%std_config, %{$_[0]});
  324.             fix_config(%std_config);
  325.             return;
  326.         }
  327.     }
  328.     $std_config{_used}++;
  329.     my @ref = map { ref } @_;
  330.     my @orig = @_;
  331.     my $caller = caller;
  332.     no strict;
  333.  
  334.     for my $nextarg (0..$#_)
  335.     {
  336.         my $next = $_[$nextarg];
  337.         if (!defined $next)
  338.         {
  339.             splice @_, $nextarg, 1, '';
  340.         }
  341.         elsif ($ref[$nextarg] eq 'ARRAY')
  342.         {
  343.             splice @_, $nextarg, 1, \join("\n", @$next)
  344.         }
  345.         elsif (!defined eval { local $SIG{__DIE__};
  346.                        $_[$nextarg] = $next;
  347.                        debug "writeable: [$_[$nextarg]]";
  348.                        1})
  349.         {
  350.                 debug "unwriteable: [$_[$nextarg]]";
  351.             my $arg = $_[$nextarg];
  352.             splice @_, $nextarg, 1, \$arg;
  353.         }
  354.         elsif (!$ref[$nextarg])
  355.         {
  356.             splice @_, $nextarg, 1, \$_[$nextarg];
  357.         }
  358.                 elsif ($ref[$nextarg] ne 'HASH' and $ref[$nextarg] ne 'SCALAR')
  359.                 {
  360.                        splice @_, $nextarg, 1, \"$next";
  361.                 }
  362.     }
  363.  
  364.     my $header = $config->{header}->(${$config->{pagenum}});
  365.     $header.="\n" if $header && substr($header,-1,1) ne "\n";
  366.  
  367.     my $footer = $config->{footer}->(${$config->{pagenum}});
  368.     $footer.="\n" if $footer && substr($footer,-1,1) ne "\n";
  369.  
  370.     my $prevfooter = $footer;
  371.  
  372.     my $linecount = $header=~tr/\n/\n/ + $footer=~tr/\n/\n/;
  373.     my $hfcount = $linecount;
  374.  
  375.     my $text = $header;
  376.  
  377.     while ($startidx < @_)
  378.     {
  379.         if ($ref[$startidx]||'' eq 'HASH')
  380.         {
  381.             $config = {%$config, %{$_[$startidx++]}};
  382.             fix_config(%$config);
  383.             next;
  384.         }
  385.         my $format = ${$_[$startidx++]}||"";
  386.         debug("format: [$format]");
  387.     
  388.         my @parts = split /(\n|(?:\\.)+|$fieldpat)/, $format;
  389.         push @parts, "\n" unless @parts && $parts[-1] eq "\n";
  390.         my $fieldcount = 0;
  391.         my $filled = 0;
  392.         my $firstline = 1;
  393.         while (!$filled)
  394.         {
  395.             my $nextarg = $startidx;
  396.             my @data;
  397.             foreach my $part ( @parts )
  398.             {
  399.                 if ($part =~ /\A(?:\\.)+/)
  400.                 {
  401.                     debug("esc literal: [$part]");
  402.                     my $tmp = $part;
  403.                     $tmp =~ s/\\(.)/$1/g;
  404.                     $text .= $tmp;
  405.                 }
  406.                 elsif ($part =~ /($lfieldmark)/)
  407.                 {
  408.                     if ($firstline)
  409.                     {
  410.                         $fieldcount++;
  411.                         if ($nextarg > $#_)
  412.                             { push @_,\$emptyref; push @ref, '' }
  413.                         my $type = $1;
  414.                         $type = 'J' if $part =~ /$ljustified/;
  415.                         croak BAD_CONFIG if ($ref[$startidx] eq 'HASH');
  416.                         debug("once field: [$part]");
  417.                         debug("data was: [${$_[$nextarg]}]");
  418.                         $text .= replace($type,length($part),$_[$nextarg],$config);
  419.                         debug("data now: [${$_[$nextarg]}]");
  420.                     }
  421.                     else
  422.                     {
  423.                         $text .= ' ' x length($part);
  424.                         debug("missing once field: [$part]");
  425.                     }
  426.                     $nextarg++;
  427.                 }
  428.                 elsif ($part =~ /($fieldmark)/ and substr($part,0,2) ne '~~')
  429.                 {
  430.                     $fieldcount++ if $firstline;
  431.                     if ($nextarg > $#_)
  432.                         { push @_,\$emptyref; push @ref, '' }
  433.                     my $type = $1;
  434.                     $type = 'J' if $part =~ /$bjustified/;
  435.                     croak BAD_CONFIG if ($ref[$startidx] eq 'HASH');
  436.                     debug("multi field: [$part]");
  437.                     debug("data was: [${$_[$nextarg]}]");
  438.                     $text .= replace($type,length($part),$_[$nextarg],$config);
  439.                     debug("data now: [${$_[$nextarg]}]");
  440.                     push @data, $_[$nextarg];
  441.                     $nextarg++;
  442.                 }
  443.                 else
  444.                 {
  445.                     debug("literal: [$part]");
  446.                     my $tmp = $part;
  447.                     $tmp =~ s/\0(\0*)/$1/g;
  448.                     $text .= $tmp;
  449.                     if ($part eq "\n")
  450.                     {
  451.                         $linecount++;
  452.                         if ($linecount>=$config->{pagelen})
  453.                         {
  454.                             ${$config->{pagenum}}++;
  455.                             my $pagefeed = $config->{pagefeed}->(${$config->{pagenum}});
  456.                             $header = $config->{header}->(${$config->{pagenum}});
  457.                             $header.="\n" if $header && substr($header,-1,1) ne "\n";
  458.                             $text .= $footer
  459.                                    . $pagefeed
  460.                                    . $header;
  461.                             $prevfooter = $footer;
  462.                             $footer = $config->{footer}->(${$config->{pagenum}});
  463.                             $footer.="\n" if $footer && substr($footer,-1,1) ne "\n";
  464.                             $linecount = $hfcount =
  465.                                 $header=~tr/\n/\n/ + $footer=~tr/\n/\n/;
  466.                             $header = $pagefeed
  467.                                 . $header;
  468.                         }
  469.                     }
  470.                 }
  471.                 debug("\tnextarg now:  $nextarg");
  472.                 debug("\tstartidx now: $startidx");
  473.             }
  474.             $firstline = 0;
  475.             $filled = ! grep { notempty $_ } @data;
  476.         }
  477.         $startidx += $fieldcount;
  478.     }
  479.  
  480.     # ADJUST FINAL PAGE HEADER OR FOOTER AS REQUIRED
  481.     if ($hfcount && $linecount == $hfcount)        # UNNEEDED HEADER
  482.     {
  483.         $text =~ s/\Q$header\E\Z//;
  484.     }
  485.     elsif ($linecount && $config->{pagelen})    # MISSING FOOTER
  486.     {
  487.         $text .= "\n" x ($config->{pagelen}-$linecount)
  488.                . $footer;
  489.         $prevfooter = $footer;
  490.     }
  491.  
  492.     # REPLACE LAST FOOTER
  493.     
  494.     if ($prevfooter) {
  495.         my $lastfooter = $config->{footer}->(${$config->{pagenum}},1);
  496.         $lastfooter.="\n"
  497.             if $lastfooter && substr($lastfooter,-1,1) ne "\n";
  498.         substr($text, -length($prevfooter)) = $lastfooter;
  499.     }
  500.  
  501.     # RESTORE ARG LIST
  502.     for my $i (0..$#orig)
  503.     {
  504.         if ($ref[$i] eq 'ARRAY')
  505.             { eval { @{$orig[$i]} = map "$_\n", split /\n/, ${$_[$i]} } }
  506.         elsif (!$ref[$i])
  507.             { eval { debug("restoring $i (".$_[$i].") to $orig[$i]");
  508.                  ${$_[$i]} = $orig[$i] } }
  509.     }
  510.  
  511.     ${$config->{pagenum}}++;
  512.     $text =~ s/[ ]+$//gm if $config->{trim};
  513.     return $text unless wantarray;
  514.     return map "$_\n", split /\n/, $text;
  515. }
  516.  
  517.  
  518. #==== tag ============================================#
  519.  
  520. sub invert($)
  521. {
  522.     my $inversion = reverse $_[0];
  523.     $inversion =~ tr/{[<(/}]>)/;
  524.     return $inversion;
  525. }
  526.  
  527. sub tag        # ($tag, $text; $opt_endtag)
  528. {
  529.     my ($tagleader,$tagindent,$ldelim,$tag,$tagargs,$tagtrailer) = 
  530.         ( $_[0] =~ /\A((?:[ \t]*\n)*)([ \t]*)(\W*)(\w+)(.*?)(\s*)\Z/ );
  531.  
  532.     $ldelim = '<' unless $ldelim;
  533.     $tagtrailer =~ s/([ \t]*)\Z//;
  534.     my $textindent = $1||"";
  535.  
  536.     my $rdelim = invert $ldelim;
  537.  
  538.     my $i;
  539.     for ($i = -1; -1-$i < length $rdelim && -1-$i < length $tagargs; $i--)
  540.     {
  541.         last unless substr($tagargs,$i,1) eq substr($rdelim,$i,1);
  542.     }
  543.     if ($i < -1)
  544.     {
  545.         $i++;
  546.         $tagargs = substr($tagargs,0,$i);
  547.         $rdelim = substr($rdelim,$i);
  548.     }
  549.  
  550.     my $endtag = $_[2] || "$ldelim/$tag$rdelim";
  551.  
  552.     return "$tagleader$tagindent$ldelim$tag$tagargs$rdelim$tagtrailer".
  553.         join("\n",map { "$tagindent$textindent$_" } split /\n/, $_[1]).
  554.         "$tagtrailer$tagindent$endtag$tagleader";
  555.  
  556. }
  557.  
  558.  
  559. ###### AUTOFORMATTING ####################################
  560.  
  561. my $default_margin = 72;
  562. my $default_widow  = 10;
  563.  
  564. $Text::Autoformat::widow_slack = 0.1;
  565.  
  566.  
  567.  
  568.  
  569. sub defn($)
  570. {
  571.     return $_[0] if defined $_[0];
  572.     return "";
  573. }
  574.  
  575. # BITS OF A TEXT LINE
  576.  
  577. my $quotechar = qq{[!#%=|:]};
  578. my $quotechunk = qq{(?:$quotechar(?![a-z])|[a-z]*>+)};
  579. my $quoter = qq{(?:(?i)(?:$quotechunk(?:[ \\t]*$quotechunk)*))};
  580.  
  581. my $separator = q/(?:[-_]{2,}|[=#*]{3,}|[+~]{4,})/;
  582.  
  583.  
  584. sub autoformat    # ($text, %args)
  585. {
  586.     my ($text,%args,$toSTDOUT);
  587.  
  588.     foreach ( @_ )
  589.     {
  590.         unless (ref || defined $text) { $text = $_ }
  591.         elsif (ref eq 'HASH') { %args = (%args, %$_) }
  592.         else { croak q{Usage: autoformat([text],[{options}])} }
  593.     }
  594.  
  595.     unless (defined $text) {
  596.         $text = join("",<STDIN>);
  597.         $toSTDOUT = !defined wantarray();
  598.     }
  599.  
  600.     $args{right}   = $default_margin unless exists $args{right};
  601.     $args{justify} = "" unless exists $args{justify};
  602.     $args{widow}   = 0 if $args{justify}||"" =~ /full/;
  603.     $args{widow}   = $default_widow unless exists $args{widow};
  604.     $args{case}    = '' unless exists $args{case};
  605.     $args{squeeze} = 1 unless exists $args{squeeze};
  606.     $args{gap}     = 0 unless exists $args{gap};
  607.     $args{impfill} = ! exists $args{fill};
  608.     $args{expfill} = $args{fill};
  609.     $args{_centred} = 1 if $args{justify} =~ /cent(er(ed)?|red?)/;
  610.  
  611.     # DETABIFY
  612.     my @rawlines = split /\n/, $text;
  613.     use Text::Tabs;
  614.     @rawlines = expand(@rawlines);
  615.  
  616.     # PARSE EACH LINE
  617.  
  618.     my $pre = 0;
  619.     my @lines;
  620.     foreach (@rawlines)
  621.     {
  622.             push @lines, { raw       => $_ };
  623.             s/\A([ \t]*)($quoter?)([ \t]*)//
  624.                 or die "Internal Error ($@) on '$_'";
  625.             $lines[-1]{presig} =  $lines[-1]{prespace}   = defn $1;
  626.             $lines[-1]{presig} .= $lines[-1]{quoter}     = defn $2;
  627.             $lines[-1]{presig} .= $lines[-1]{quotespace} = defn $3;
  628.  
  629.             $lines[-1]{hang}       = defn Hang->new($_);
  630.  
  631.             s/([ \t]*)(.*?)(\s*)$//
  632.                 or die "Internal Error ($@) on '$_'";
  633.             $lines[-1]{hangspace} = defn $1;
  634.             $lines[-1]{text} = defn $2;
  635.             $lines[-1]{empty} = $lines[-1]{hang}->empty() && $2 !~ /\S/;
  636.             $lines[-1]{separator} = $lines[-1]{text} =~ /^$separator$/;
  637.     }
  638.  
  639.     # SUBDIVIDE DOCUMENT INTO COHERENT SUBSECTIONS
  640.  
  641.     my @chunks;
  642.     push @chunks, [shift @lines];
  643.     foreach my $line (@lines)
  644.     {
  645.         if ($line->{separator} ||
  646.             $line->{quoter} ne $chunks[-1][-1]->{quoter} ||
  647.             $line->{empty} ||
  648.             @chunks && $chunks[-1][-1]->{empty})
  649.         {
  650.             push @chunks, [$line];
  651.         }
  652.         else
  653.         {
  654.             push @{$chunks[-1]}, $line;
  655.         }
  656.     }
  657.  
  658.  
  659.  
  660.  # DETECT CENTRED PARAS
  661.  
  662.     CHUNK: foreach my $chunk ( @chunks )
  663.     {
  664.         next CHUNK if @$chunk < 2;
  665.         my @length;
  666.         my $ave = 0;
  667.         foreach my $line (@$chunk)
  668.         {
  669.             my $prespace = $line->{quoter}  ? $line->{quotespace}
  670.                             : $line->{prespace};
  671.             my $pagewidth = 
  672.                 2*length($prespace) + length($line->{text});
  673.             push @length, [length $prespace,$pagewidth];
  674.             $ave += $pagewidth;
  675.         }
  676.         $ave /= @length;
  677.         my $diffpre = 0;
  678.         foreach my $l (0..$#length)
  679.         {
  680.             next CHUNK unless abs($length[$l][1]-$ave) <= 2;
  681.             $diffpre ||= $length[$l-1][0] != $length[$l][0]
  682.                 if $l > 0;
  683.         }
  684.         next CHUNK unless $diffpre;
  685.         foreach my $line (@$chunk)
  686.         {
  687.             $line->{centred} = 1;
  688.             ($line->{quoter} ? $line->{quotespace}
  689.                      : $line->{prespace}) = "";
  690.         }
  691.     }
  692.  
  693.     # REDIVIDE INTO PARAGRAPHS
  694.  
  695.     my @paras;
  696.     foreach my $chunk ( @chunks )
  697.     {
  698.         my $first = 1;
  699.         my $firstfrom;
  700.         foreach my $line ( @{$chunk} )
  701.         {
  702.             if ($first ||
  703.                 $line->{quoter} ne $paras[-1]->{quoter} ||
  704.                 $paras[-1]->{separator} ||
  705.                 !$line->{hang}->empty
  706.                )
  707.             {
  708.                 push @paras, $line;
  709.                 $first = 0;
  710.                 $firstfrom = length($line->{raw}) - length($line->{text});
  711.             }
  712.             else
  713.             {
  714.     my $extraspace = length($line->{raw}) - length($line->{text}) - $firstfrom;
  715.                 $paras[-1]->{text} .= "\n" . q{ }x$extraspace . $line->{text};
  716.                 $paras[-1]->{raw} .= "\n" . $line->{raw};
  717.             }
  718.         }
  719.     }
  720.  
  721.     # HANDLE FIRST PARA UNLESS $args{all}
  722.  
  723.     my $remainder = "";
  724.     unless ($args{all})
  725.     {
  726.         $remainder = join "\n", map { $_->{raw} } @paras[1..$#paras];
  727.         @paras = ( $paras[0] );
  728.     }
  729.  
  730.     # RE-CASE TEXT
  731.     if ($args{case}) {
  732.         foreach my $para ( @paras ) {
  733.             if ($args{case} =~ /upper/i) {
  734.                 $para->{text} =~ tr/a-z/A-Z/;
  735.             }
  736.             if ($args{case} =~ /lower/i) {
  737.                 $para->{text} =~ tr/A-Z/a-z/;
  738.             }
  739.             if ($args{case} =~ /title/i) {
  740.                 $para->{text} =~ s/(\S+)/entitle($1)/ge;
  741.             }
  742.             if ($args{case} =~ /highlight/i) {
  743.                 $para->{text} =~ s/(\S+)/entitle($1,1)/ge;
  744.                 $para->{text} =~ s/([a-z])/\U$1/i;
  745.             }
  746.             if ($args{case} =~ /sentence(\s*)/i) {
  747.                 my $trailer = $1;
  748.                 $args{squeeze}=0 if $trailer && $trailer ne " ";
  749.                 ensentence();
  750.                 $para->{text} =~ s/(\S+(\s+|$))/ensentence($1, $trailer)/ge;
  751.             }
  752.         }
  753.     }
  754.  
  755.     # ALIGN QUOTERS
  756.     # DETERMINE HANGING MARKER TYPE (BULLET, ALPHA, ROMAN, ETC.)
  757.  
  758.     my %sigs;
  759.     my $lastquoted = 0;
  760.     my $lastprespace = 0;
  761.     for my $i ( 0..$#paras )
  762.     {
  763.         my $para = $paras[$i];
  764.  
  765.      if ($para->{quoter})
  766.         {
  767.             if ($lastquoted) { $para->{prespace} = $lastprespace }
  768.             else         { $lastquoted = 1; $lastprespace = $para->{prespace} }
  769.         }
  770.         else
  771.         {
  772.             $lastquoted = 0;
  773.         }
  774.     }
  775.  
  776. # RENUMBER PARAGRAPHS
  777.  
  778.     for my $para ( @paras )
  779.     {
  780.         my $sig = $para->{presig} . $para->{hang}->signature();
  781.         push @{$sigs{$sig}{hangref}}, $para;
  782.         $sigs{$sig}{hangfields} = $para->{hang}->fields()-1
  783.             unless defined $sigs{$sig}{hangfields};
  784.     }
  785.  
  786.     while (my ($sig,$val) = each %sigs)
  787.     {
  788.         next unless $sig =~ /rom/;
  789.         field: for my $field ( 0..$val->{hangfields} )
  790.         {
  791.             my $romlen = 0;
  792.             foreach my $para ( @{$val->{hangref}} )
  793.             {
  794.                 my $hang = $para->{hang};
  795.                 my $fieldtype = $hang->field($field);
  796.                 next field 
  797.                     unless $fieldtype && $fieldtype =~ /rom|let/;
  798.                 if ($fieldtype eq 'let') {
  799.                     foreach my $para ( @{$val->{hangref}} ) {
  800.                         $hang->field($field=>'let')
  801.                     }
  802.                 }
  803.                 else {
  804.                     $romlen += length $hang->val($field);
  805.                 }
  806.             }
  807.             # NO ROMAN LETTER > 1 CHAR -> ALPHABETICS
  808.             if ($romlen <= @{$val->{hangref}}) {
  809.                 foreach my $para ( @{$val->{hangref}} ) {
  810.                     $para->{hang}->field($field=>'let')
  811.                 }
  812.             }
  813.         }
  814.     }
  815.  
  816.     my %prev;
  817.     for my $para ( @paras )
  818.     {
  819.         my $sig = $para->{presig} . $para->{hang}->signature();
  820.         unless ($para->{quoter}) {
  821.             $para->{hang}->incr($prev{""}, $prev{$sig});
  822.             $prev{""} = $prev{$sig} = $para->{hang}
  823.                 unless $para->{hang}->empty;
  824.         }
  825.             
  826.         # COLLECT MAXIMAL HANG LENGTHS BY SIGNATURE
  827.  
  828.         my $siglen = $para->{hang}->length();
  829.         $sigs{$sig}{hanglen} = $siglen
  830.             if ! $sigs{$sig}{hanglen} ||
  831.                $sigs{$sig}{hanglen} < $siglen;
  832.     }
  833.  
  834.     # PROPAGATE MAXIMAL HANG LENGTH
  835.  
  836.     while (my ($sig,$val) = each %sigs)
  837.     {
  838.         foreach (@{$val->{hangref}}) {
  839.             $_->{hanglen} = $val->{hanglen};
  840.         }
  841.     }
  842.  
  843.     # BUILD FORMAT FOR EACH PARA THEN FILL IT 
  844.  
  845.     $text = "";
  846.     my $gap = $paras[0]->{empty} ? 0 : $args{gap};
  847.     for my $para ( @paras )
  848.     {
  849.         if ($para->{empty}) {
  850.         $gap += 1 + ($para->{text} =~ tr/\n/\n/);
  851.         }
  852.         my $leftmargin = $args{left} ? " "x($args{left}-1)
  853.                      : $para->{prespace};
  854.         my $hlen = $para->{hanglen} || $para->{hang}->length;
  855.         my $hfield = ($hlen==1 ? '~' : '>'x$hlen);
  856.         my @hang;
  857.         push @hang, $para->{hang}->stringify if $hlen;
  858.         my $format = $leftmargin
  859.                . quotemeta($para->{quoter})
  860.                . $para->{quotespace}
  861.                . $hfield
  862.                . $para->{hangspace};
  863.         my $rightslack = int (($args{right}-length $leftmargin)*$Text::Autoformat::widow_slack);
  864.         my ($widow_okay, $rightindent, $firsttext, $newtext) = (0,0);
  865.         do {
  866.             my $tlen = $args{right}-$rightindent-length($leftmargin
  867.                          . $para->{quoter}
  868.                          . $para->{quotespace}
  869.                          . $hfield
  870.                          . $para->{hangspace});
  871.             next if blockquote($text,$para, $format, $tlen, \@hang, \%args);
  872.             my $tfield = ( $tlen==1                          ? '~'
  873.                  : $para->{centred}||$args{_centred} ? '|'x$tlen
  874.                  : $args{justify} eq 'right'         ? ']'x$tlen
  875.                  : $args{justify} eq 'full'          ? '['x($tlen-2) . ']]'
  876.                  : $para->{centred}||$args{_centred} ? '|'x$tlen
  877.                  :                                     '['x$tlen
  878.                      );
  879.         my $tryformat = "$format$tfield";
  880.         $newtext = (!$para->{empty} ? "\n"x($args{gap}-$gap) : "") 
  881.                  . form( { squeeze=>$args{squeeze}, trim=>1,
  882.                    fill => !(!($args{expfill}
  883.                     || $args{impfill} &&
  884.                        !$para->{centred}))
  885.                    },
  886.                 $tryformat, @hang,
  887.                 $para->{text});
  888.         $firsttext ||= $newtext;
  889.         $newtext =~ /\s*([^\n]*)$/;
  890.         $widow_okay = $para->{empty} || length($1) >= $args{widow};
  891.         # print "[$rightindent <= $rightslack : $widow_okay : $1]\n";
  892.         # print $tryformat;
  893.         # print $newtext;
  894.         } until $widow_okay || ++$rightindent > $rightslack;
  895.  
  896.         $text .= $widow_okay ? $newtext : $firsttext;
  897.         $gap = 0 unless $para->{empty};
  898.     }
  899.  
  900.  
  901.     # RETURN FORMATTED TEXT
  902.  
  903.     if ($toSTDOUT) { print STDOUT $text . $remainder; return }
  904.     return $text . $remainder;
  905. }
  906.  
  907. sub entitle {
  908.     my ($str,$ignore) = @_;
  909.     my $mixedcase = $str =~ /[a-z].*[A-Z]|[A-Z].*[a-z]/;
  910.     my $ignorable = $ignore && $str =~ /^[^a-z]*($IGNORABLES)[^a-z]*$/i;
  911.     $str = lc $str if $ignorable || ! $mixedcase ;
  912.     $str =~ s/([a-z])/\U$1/i unless $ignorable;
  913.     return $str;
  914. }
  915.  
  916. my $abbrev = join '|', qw{
  917.     etc[.]    pp[.]    ph[.]?d[.]    U[.]S[.]
  918. };
  919.  
  920. my $gen_abbrev = join '|', $abbrev, qw{
  921.      (^[^a-z]*([a-z][.])+)
  922. };
  923.  
  924. my $term = q{(?:[.]|[!?]+)};
  925.  
  926. my $eos = 1;
  927. my $brsent = 0;
  928.  
  929. sub ensentence {
  930.     do { $eos = 1; return } unless @_;
  931.     my ($str, $trailer) = @_;
  932.     if ($str =~ /^([^a-z]*)I[^a-z]*?($term?)[^a-z]*$/i) {
  933.         $eos = $2;
  934.         $brsent = $1 =~ /^[[(]/;
  935.         return uc $str
  936.     }
  937.     unless ($str =~ /[a-z].*[A-Z]|[A-Z].*[a-z]/) {
  938.         $str = lc $str;
  939.     }
  940.     if ($eos) {
  941.         $str =~ s/([a-z])/uc $1/ie;
  942.         $brsent = $str =~ /^[[(]/;
  943.     }
  944.     $eos = $str !~ /($gen_abbrev)[^a-z]*\s/i
  945.         && $str =~ /[a-z][^a-z]*$term([^a-z]*)\s/
  946.         && !($1=~/[])]/ && !$brsent);
  947.     $str =~ s/\s+$/$trailer/ if $eos && $trailer;
  948.     return $str;
  949. }
  950.  
  951. # blockquote($text,$para, $format, $tlen, \@hang, \%args);
  952. sub blockquote {
  953.     my ($dummy, $para, $format, $tlen, $hang, $args) = @_;
  954. =begin other
  955.     print STDERR "[", join("|", $para->{raw} =~
  956. / \A(\s*)        # $1 - leading whitespace (quotation)
  957.        (["']|``)        # $2 - opening quotemark
  958.        (.*)            # $3 - quotation
  959.        (''|\2)        # $4 closing quotemark
  960.        \s*?\n        # trailing whitespace
  961.        (\1[ ]+)        # $5 - leading whitespace (attribution)
  962.        (--|-)        # $6 - attribution introducer
  963.        ([^\n]*?$)        # $7 - attribution line 1
  964.        ((\5[^\n]*?$)*)        # $8 - attributions lines 2-N
  965.        \s*\Z
  966.      /xsm
  967. ), "]\n";
  968. =cut
  969.     $para->{text} =~
  970.         / \A(\s*)        # $1 - leading whitespace (quotation)
  971.        (["']|``)        # $2 - opening quotemark
  972.        (.*)            # $3 - quotation
  973.        (''|\2)        # $4 closing quotemark
  974.        \s*?\n        # trailing whitespace
  975.        (\1[ ]+)        # $5 - leading whitespace (attribution)
  976.        (--|-)        # $6 - attribution introducer
  977.        (.*?$)        # $7 - attribution line 1
  978.        ((\5.*?$)*)        # $8 - attributions lines 2-N
  979.        \s*\Z
  980.      /xsm
  981.      or return;
  982.  
  983.     #print "[$1][$2][$3][$4][$5][$6][$7]\n";
  984.     my $indent = length $1;
  985.     my $text = $2.$3.$4;
  986.     my $qindent = length $2;
  987.     my $aindent = length $5;
  988.     my $attribintro = $6;
  989.     my $attrib = $7.$8;
  990.     $text =~ s/\n/ /g;
  991.  
  992.     $_[0] .= 
  993.  
  994.                 form {squeeze=>$args->{squeeze}, trim=>1,
  995.           fill => $args->{expfill}
  996.                    },
  997.        $format . q{ }x$indent . q{<}x$tlen,
  998.              @$hang, $text,
  999.        $format . q{ }x($qindent) . q{[}x($tlen-$qindent), 
  1000.              @$hang, $text,
  1001.        {squeeze=>0},
  1002.        $format . q{ } x $aindent . q{>> } . q{[}x($tlen-$aindent-3),
  1003.              @$hang, $attribintro, $attrib;
  1004.     return 1;
  1005. }
  1006.  
  1007. # Emulation to avoid including POSIX:
  1008. sub strtod {
  1009.     my $str = $_[0]; # preserve the original.
  1010.     my ($num, $sign);
  1011.  
  1012.     # From the strtod manpage:
  1013.     # The expected form of the (initial portion of the) string
  1014.     # is optional leading white space as recognized by isspace(3),
  1015.     $str =~ s/^\s*//;
  1016.  
  1017.     # an optional plus (``+'') or minus sign (``-'')
  1018.     $sign = $str =~ s/^([-+])//;
  1019.     $sign ||= '+';
  1020.  
  1021.     # NOTE: do hex first...
  1022.     # or (ii) a hexadecimal number,
  1023.     if ($str =~ s/^0x([0-9a-f]+(?:\.[0-9a-f]*)?)(?:p([-+]?)([0-9a-f]+))?//i) {
  1024.     my $exp = hex($3) || 0;
  1025.     $exp *= -1 if ($2 and $2 eq '-');
  1026.     $num = hex($1) * (2 ** $exp);
  1027.     }
  1028.  
  1029.     # NOTE: and then decimal second...
  1030.     # and then either (i) a decimal number, [decimal assumed to be '.']
  1031.     elsif ($str =~ s/^([0-9]+(?:\.[0-9]*)?)(?:[Ee]([-+]?)([0-9]+))?//) {
  1032.     my $exp = $3 || 0;
  1033.     $exp *= -1 if ($2 and $2 eq '-');
  1034.     $num = $1 * (10 ** $exp);
  1035.     }
  1036.  
  1037.     # or (iii) an infinity,
  1038.     elsif ($str =~ s/^(?:infinity|inf)//i) {
  1039.     $num = 'inf';
  1040.     }
  1041.  
  1042.     # or (iv) a NAN (not-a-number).
  1043.     elsif ($str =~ s/^NAN(?:\([^)]*\))?//i) {
  1044.     $num = 'nan';
  1045.     }
  1046.  
  1047.     $num = 0 unless defined $num;
  1048.     $num *= -1 if ($sign eq '-');
  1049.  
  1050.     return wantarray ? ($num, length($str)) : $num;
  1051. }
  1052.  
  1053. package Hang;
  1054.  
  1055. # ROMAN NUMERALS
  1056.  
  1057. sub inv($@) { my ($k, %inv)=shift; for(0..$#_) {$inv{$_[$_]}=$_*$k} %inv } 
  1058. my @unit= ( "" , qw ( I II III IV V VI VII VIII IX ));
  1059. my @ten = ( "" , qw ( X XX XXX XL L LX LXX LXXX XC ));
  1060. my @hund= ( "" , qw ( C CC CCC CD D DC DCC DCCC CM ));
  1061. my @thou= ( "" , qw ( M MM MMM ));
  1062. my %rval= (inv(1,@unit),inv(10,@ten),inv(100,@hund),inv(1000,@thou));
  1063. my $rbpat= join ")(",join("|",reverse @thou), join("|",reverse @hund), join("|",reverse @ten), join("|",reverse @unit);
  1064. my $rpat= join ")(?:",join("|",reverse @thou), join("|",reverse @hund), join("|",reverse @ten), join("|",reverse @unit);
  1065.  
  1066. sub fromRoman($)
  1067. {
  1068.     return 0 unless $_[0] =~ /^.*?($rbpat).*$/i;
  1069.     return $rval{uc $1} + $rval{uc $2} + $rval{uc $3} + $rval{uc $4};
  1070. }
  1071.  
  1072. sub toRoman($$)
  1073. {
  1074.     my ($num,$example) = @_;
  1075.     return '' unless $num =~ /^([0-3]??)(\d??)(\d??)(\d)$/;
  1076.     my $roman = $thou[$1||0] . $hund[$2||0] . $ten[$3||0] . $unit[$4||0];
  1077.     return $example=~/[A-Z]/ ? uc $roman : lc $roman;
  1078. }
  1079.  
  1080. # BITS OF A NUMERIC VALUE
  1081.  
  1082. my $num = q/(?:\d+)/;
  1083. my $rom = qq/(?:(?=[MDCLXVI])(?:$rpat))/;
  1084. my $let = q/[A-Za-z]/;
  1085. my $pbr = q/[[(<]/;
  1086. my $sbr = q/])>/;
  1087. my $ows = q/[ \t]*/;
  1088. my %close = ( '[' => ']', '(' => ')', '<' => '>', "" => '' );
  1089.  
  1090. my $hangPS      = qq{(?i:ps:|(?:p\\.?)+s\\.?(?:[ \\t]*:)?)};
  1091. my $hangNB      = qq{(?i:n\\.?b\\.?(?:[ \\t]*:)?)};
  1092. my $hangword    = qq{(?:(?:Note)[ \\t]*:)};
  1093. my $hangbullet  = qq{[*.+-]};
  1094. my $hang        = qq{(?:(?i)(?:$hangNB|$hangword|$hangbullet)(?=[ \t]))};
  1095.  
  1096. # IMPLEMENTATION
  1097.  
  1098. sub new { 
  1099.     my ($class, $orig) = @_;
  1100.     my $origlen = length $orig;
  1101.     my @vals;
  1102.     if ($_[1] =~ s#\A($hangPS)##) {
  1103.         @vals = { type => 'ps', val => $1 }
  1104.     }
  1105.     elsif ($_[1] =~ s#\A($hang)##) {
  1106.         @vals = { type => 'bul', val => $1 }
  1107.     }
  1108.     else {
  1109.         local $^W;
  1110.         my $cut;
  1111.         while (length $_[1]) {
  1112.             last if $_[1] =~ m#\A($ows)($abbrev)#
  1113.                  && (length $1 || !@vals);    # ws-separated or first
  1114.  
  1115.             $cut = $origlen - length $_[1];
  1116.             my $pre = $_[1] =~ s#\A($ows$pbr$ows)## ? $1 : "";
  1117.             my $val =  $_[1] =~ s#\A($num)##  && { type=>'num', val=>$1 }
  1118.                    || $_[1] =~ s#\A($rom)##i && { type=>'rom', val=>$1, nval=>fromRoman($1) }
  1119.                    || $_[1] =~ s#\A($let(?!$let))##i && { type=>'let', val=>$1 }
  1120.                    || { val => "", type => "" };
  1121.             $_[1] = $pre.$_[1] and last unless $val->{val};
  1122.             $val->{post} = $pre && $_[1] =~ s#\A($ows()[.:/]?[$close{$pre}][.:/]?)## && $1
  1123.                              || $_[1] =~ s#\A($ows()[$sbr.:/])## && $1
  1124.                              || "";
  1125.             $val->{pre}  = $pre;
  1126.             $val->{cut}  = $cut;
  1127.             push @vals, $val;
  1128.         }
  1129.         while (@vals && !$vals[-1]{post}) {
  1130.             $_[1] = substr($orig,pop(@vals)->{cut});
  1131.         }
  1132.     }
  1133.     # check for orphaned years...
  1134.     if (@vals==1 && $vals[0]->{type} eq 'num'
  1135.              && $vals[0]->{val} >= 1000
  1136.              && $vals[0]->{post} eq '.')  {
  1137.         $_[1] = substr($orig,pop(@vals)->{cut});
  1138.  
  1139.         }
  1140.     return NullHang->new if !@vals;
  1141.     bless \@vals, $class;
  1142.  
  1143. sub incr {
  1144.     local $^W;
  1145.     my ($self, $prev, $prevsig) = @_;
  1146.     my $level;
  1147.     # check compatibility
  1148.  
  1149.     return unless $prev && !$prev->empty;
  1150.  
  1151.     for $level (0..(@$self<@$prev ? $#$self : $#$prev)) {
  1152.         if ($self->[$level]{type} ne $prev->[$level]{type}) {
  1153.             return if @$self<=@$prev;    # no incr if going up
  1154.             $prev = $prevsig;
  1155.             last;
  1156.         }
  1157.     }
  1158.     return unless $prev && !$prev->empty;
  1159.     if ($self->[0]{type} eq 'ps') {
  1160.         my $count = 1 + $prev->[0]{val} =~ s/(p[.]?)/$1/gi;
  1161.         $prev->[0]{val} =~ /^(p[.]?).*(s[.]?[:]?)/;
  1162.         $self->[0]{val} = $1  x $count . $2;
  1163.     }
  1164.     elsif ($self->[0]{type} eq 'bul') {
  1165.         # do nothing
  1166.     }
  1167.     elsif (@$self>@$prev) {    # going down level(s)
  1168.         for $level (0..$#$prev) {
  1169.                 @{$self->[$level]}{'val','nval'} = @{$prev->[$level]}{'val','nval'};
  1170.         }
  1171.         for $level (@$prev..$#$self) {
  1172.                 _reset($self->[$level]);
  1173.         }
  1174.     }
  1175.     else    # same level or going up
  1176.     {
  1177.         for $level (0..$#$self) {
  1178.             @{$self->[$level]}{'val','nval'} = @{$prev->[$level]}{'val','nval'};
  1179.         }
  1180.         _incr($self->[-1])
  1181.     }
  1182. }
  1183.  
  1184. sub _incr {
  1185.     local $^W;
  1186.     if ($_[0]{type} eq 'rom') {
  1187.         $_[0]{val} = toRoman(++$_[0]{nval},$_[0]{val});
  1188.     }
  1189.     else {
  1190.         $_[0]{val}++ unless $_[0]{type} eq 'let' && $_[0]{val}=~/Z/i;
  1191.     }
  1192. }
  1193.  
  1194. sub _reset {
  1195.     local $^W;
  1196.     if ($_[0]{type} eq 'rom') {
  1197.         $_[0]{val} = toRoman($_[0]{nval}=1,$_[0]{val});
  1198.     }
  1199.     elsif ($_[0]{type} eq 'let') {
  1200.         $_[0]{val} = $_[0]{val} =~ /[A-Z]/ ? 'A' : 'a';
  1201.     }
  1202.     else {
  1203.         $_[0]{val} = 1;
  1204.     }
  1205. }
  1206.  
  1207. sub stringify {
  1208.     my ($self) = @_;
  1209.     my ($str, $level) = ("");
  1210.     for $level (@$self) {
  1211.         local $^W;
  1212.         $str .= join "", @{$level}{'pre','val','post'};
  1213.     }
  1214.     return $str;
  1215.  
  1216. sub val {
  1217.     my ($self, $i) = @_;
  1218.     return $self->[$i]{val};
  1219. }
  1220.  
  1221. sub fields { return scalar @{$_[0]} }
  1222.  
  1223. sub field {
  1224.     my ($self, $i, $newval) = @_;
  1225.     $self->[$i]{type} = $newval if @_>2;
  1226.     return $self->[$i]{type};
  1227. }
  1228.  
  1229. sub signature {
  1230.     local $^W;
  1231.     my ($self) = @_;
  1232.     my ($str, $level) = ("");
  1233.     for $level (@$self) {
  1234.         $level->{type} ||= "";
  1235.         $str .= join "", $level->{pre},
  1236.                          ($level->{type} =~ /rom|let/ ? "romlet" : $level->{type}),
  1237.                          $level->{post};
  1238.     }
  1239.     return $str;
  1240.  
  1241. sub length {
  1242.     length $_[0]->stringify
  1243. }
  1244.  
  1245. sub empty { 0 }
  1246.  
  1247. package NullHang;
  1248.  
  1249. sub new       { bless {}, $_[0] }
  1250. sub stringify { "" }
  1251. sub length    { 0 }
  1252. sub incr      {}
  1253. sub empty     { 1 }
  1254. sub signature     { "" }
  1255. sub fields { return 0 }
  1256. sub field { return "" }
  1257. sub val { return "" }
  1258. 1;
  1259.  
  1260. __END__
  1261.  
  1262. =head1 NAME
  1263.  
  1264. Text::Autoformat - Automatic and manual text wrapping and reformating formatting
  1265.  
  1266. =head1 VERSION
  1267.  
  1268. This document describes version 1.04 of Text::Autoformat,
  1269. released December  5, 2000.
  1270.  
  1271. =head1 SYNOPSIS
  1272.  
  1273.  # Minimal use: read from STDIN, format to STDOUT...
  1274.  
  1275.     use Text::Autoformat;
  1276.     autoformat;
  1277.  
  1278.  # In-memory formatting...
  1279.  
  1280.     $formatted = autoformat $rawtext;
  1281.  
  1282.  # Configuration...
  1283.  
  1284.     $formatted = autoformat $rawtext, { %options };
  1285.  
  1286.  # Margins (1..72 by default)...
  1287.  
  1288.     $formatted = autoformat $rawtext, { left=>8, right=>70 };
  1289.  
  1290.  # Justification (left by default)...
  1291.  
  1292.     $formatted = autoformat $rawtext, { justify => 'left' };
  1293.     $formatted = autoformat $rawtext, { justify => 'right' };
  1294.     $formatted = autoformat $rawtext, { justify => 'full' };
  1295.     $formatted = autoformat $rawtext, { justify => 'centre' };
  1296.  
  1297.  # Filling (does so by default)...
  1298.  
  1299.     $formatted = autoformat $rawtext, { fill=>0 };
  1300.  
  1301.  # Squeezing whitespace (does so by default)...
  1302.  
  1303.     $formatted = autoformat $rawtext, { squeeze=>0 };
  1304.  
  1305.  # Case conversions...
  1306.  
  1307.     $formatted = autoformat $rawtext, { case => 'lower' };
  1308.     $formatted = autoformat $rawtext, { case => 'upper' };
  1309.     $formatted = autoformat $rawtext, { case => 'sentence' };
  1310.     $formatted = autoformat $rawtext, { case => 'title' };
  1311.     $formatted = autoformat $rawtext, { case => 'highlight' };
  1312.  
  1313.  
  1314. =head1 BACKGROUND
  1315.  
  1316. =head2 The problem
  1317.  
  1318. Perl plaintext formatters just aren't smart enough. Given a typical
  1319. piece of plaintext in need of formatting:
  1320.  
  1321.         In comp.lang.perl.misc you wrote:
  1322.         : > <CN = Clooless Noobie> writes:
  1323.         : > CN> PERL sux because:
  1324.         : > CN>    * It doesn't have a switch statement and you have to put $
  1325.         : > CN>signs in front of everything
  1326.         : > CN>    * There are too many OR operators: having |, || and 'or'
  1327.         : > CN>operators is confusing
  1328.         : > CN>    * VB rools, yeah!!!!!!!!!
  1329.         : > CN> So anyway, how can I stop reloads on a web page?
  1330.         : > CN> Email replies only, thanks - I don't read this newsgroup.
  1331.         : >
  1332.         : > Begone, sirrah! You are a pathetic, Bill-loving, microcephalic
  1333.         : > script-infant.
  1334.         : Sheesh, what's with this group - ask a question, get toasted! And how
  1335.         : *dare* you accuse me of Ianuphilia!
  1336.  
  1337. both the venerable Unix L<fmt> tool and Perl's standard Text::Wrap module
  1338. produce:
  1339.  
  1340.         In comp.lang.perl.misc you wrote:  : > <CN = Clooless Noobie>
  1341.         writes:  : > CN> PERL sux because:  : > CN>    * It doesn't
  1342.         have a switch statement and you have to put $ : > CN>signs in
  1343.         front of everything : > CN>    * There are too many OR
  1344.         operators: having |, || and 'or' : > CN>operators is confusing
  1345.         : > CN>    * VB rools, yeah!!!!!!!!!  : > CN> So anyway, how
  1346.         can I stop reloads on a web page?  : > CN> Email replies only,
  1347.         thanks - I don't read this newsgroup.  : > : > Begone, sirrah!
  1348.         You are a pathetic, Bill-loving, microcephalic : >
  1349.         script-infant.  : Sheesh, what's with this group - ask a
  1350.         question, get toasted! And how : *dare* you accuse me of
  1351.         Ianuphilia!
  1352.  
  1353. Other formatting modules -- such as Text::Correct and Text::Format --
  1354. provide more control over their output, but produce equally poor results
  1355. when applied to arbitrary input. They simply don't understand the
  1356. structural conventions of the text they're reformatting.
  1357.  
  1358. =head2 The solution
  1359.  
  1360. The Text::Autoformat module provides a subroutine named C<autoformat> that
  1361. wraps text to specified margins. However, C<autoformat> reformats its
  1362. input by analysing the text's structure, so it wraps the above example
  1363. like so:
  1364.  
  1365.         In comp.lang.perl.misc you wrote:
  1366.         : > <CN = Clooless Noobie> writes:
  1367.         : > CN> PERL sux because:
  1368.         : > CN>    * It doesn't have a switch statement and you
  1369.         : > CN>      have to put $ signs in front of everything
  1370.         : > CN>    * There are too many OR operators: having |, ||
  1371.         : > CN>      and 'or' operators is confusing
  1372.         : > CN>    * VB rools, yeah!!!!!!!!! So anyway, how can I
  1373.         : > CN>      stop reloads on a web page? Email replies
  1374.         : > CN>      only, thanks - I don't read this newsgroup.
  1375.         : >
  1376.         : > Begone, sirrah! You are a pathetic, Bill-loving,
  1377.         : > microcephalic script-infant.
  1378.         : Sheesh, what's with this group - ask a question, get toasted!
  1379.         : And how *dare* you accuse me of Ianuphilia!
  1380.  
  1381. Note that the various quoting conventions have been observed. In fact,
  1382. their structure has been used to determine where some paragraphs begin.
  1383. Furthermore C<autoformat> correctly distinguished between the leading
  1384. '*' bullets of the nested list (which were outdented) and the leading
  1385. emphatic '*' of "*dare*" (which was inlined).
  1386.  
  1387. =head1 DESCRIPTION
  1388.  
  1389. =head2 Paragraphs
  1390.  
  1391. The fundamental task of the C<autoformat> subroutine is to identify and
  1392. rearrange independent paragraphs in a text. Paragraphs typically consist
  1393. of a series of lines containing at least one non-whitespace character,
  1394. followed by one or more lines containing only optional whitespace.
  1395. This is a more liberal definition than many other formatters
  1396. use: most require an empty line to terminate a paragraph. Paragraphs may
  1397. also be denoted by bulleting, numbering, or quoting (see the following
  1398. sections).
  1399.  
  1400. Once a paragraph has been isolated, C<autoformat> fills and re-wraps its
  1401. lines according to the margins that are specified in its argument list.
  1402. These are placed after the text to be formatted, in a hash reference:
  1403.  
  1404.         $tidied = autoformat($messy, {left=>20, right=>60});
  1405.  
  1406. By default, C<autoformat> uses a left margin of 1 (first column) and a
  1407. right margin of 72.
  1408.  
  1409. Normally, C<autoformat> only reformats the first paragraph it encounters,
  1410. and leaves the remainder of the text unaltered. This behaviour is useful
  1411. because it allows a one-liner invoking the subroutine to be mapped
  1412. onto a convenient keystroke in a text editor, to provide 
  1413. one-paragraph-at-a-time reformatting:
  1414.  
  1415.         % cat .exrc
  1416.  
  1417.         map f !Gperl -MText::Autoformat -e'autoformat'
  1418.  
  1419. (Note that to facilitate such one-liners, if C<autoformat> is called
  1420. in a void context without any text data, it takes its text from
  1421. C<STDIN> and writes its result to C<STDOUT>).
  1422.  
  1423. To enable C<autoformat> to rearrange the entire input text at once, the
  1424. C<all> argument is used:
  1425.  
  1426.         $tidied_all = autoformat($messy, {left=>20, right=>60, all=>1});
  1427.  
  1428.  
  1429. =head2 Bulleting and (re-)numbering
  1430.  
  1431. Often plaintext will include lists that are either:
  1432.  
  1433.         * bulleted,
  1434.         * simply numbered (i.e. 1., 2., 3., etc.), or
  1435.         * hierarchically numbered (1, 1.1, 1.2, 1.3, 2, 2.1. and so forth).
  1436.  
  1437. In such lists, each bulleted item is implicitly a separate paragraph,
  1438. and is formatted individually, with the appropriate indentation:
  1439.  
  1440.         * bulleted,
  1441.         * simply numbered (i.e. 1., 2., 3.,
  1442.           etc.), or
  1443.         * hierarchically numbered (1, 1.1,
  1444.           1.2, 1.3, 2, 2.1. and so forth).
  1445.  
  1446. More importantly, if the points are numbered, the numbering is
  1447. checked and reordered. For example, a list whose points have been
  1448. rearranged:
  1449.  
  1450.         2. Analyze problem
  1451.         3. Design algorithm
  1452.         1. Code solution
  1453.         5. Test
  1454.         4. Ship
  1455.  
  1456. would be renumbered automatically by C<autoformat>:
  1457.  
  1458.         1. Analyze problem
  1459.         2. Design algorithm
  1460.         3. Code solution
  1461.         4. Ship
  1462.         5. Test
  1463.  
  1464. The same reordering would be performed if the "numbering" was by letters
  1465. (C<a.> C<b.> C<c.> etc.) or Roman numerals (C<i.> C<ii.> C<iii.)> or by
  1466. some combination of these (C<1a.> C<1b.> C<2a.> C<2b.> etc.) Handling
  1467. disordered lists of letters and Roman numerals presents an interesting
  1468. challenge. A list such as:
  1469.  
  1470.         C. Put cat in box.
  1471.         D. Close lid.
  1472.         E. Activate Geiger counter.
  1473.  
  1474. should be reordered as C<A.> C<B.> C<C.,> whereas:
  1475.  
  1476.         C. Put cat in box.
  1477.         D. Close lid.
  1478.         XLI. Activate Geiger counter.
  1479.  
  1480. should be reordered C<I.> C<II.> C<III.> 
  1481.  
  1482. The C<autoformat> subroutine solves this problem by always interpreting 
  1483. alphabetic bullets as being letters, unless the full list consists
  1484. only of valid Roman numerals, at least one of which is two or
  1485. more characters long.
  1486.  
  1487. =head2 Quoting
  1488.  
  1489. Another case in which contiguous lines may be interpreted as belonging
  1490. to different paragraphs, is where they are quoted with distinct
  1491. quoters. For example:
  1492.  
  1493.         : > CN> So anyway, how can I stop reloads on a web page?
  1494.         : > CN> Email replies only, thanks - I don't read this newsgroup.
  1495.         : > Begone, sirrah! You are a pathetic, Bill-loving,
  1496.         : > microcephalic script-infant.
  1497.         : Sheesh, what's with this group - ask a question, get toasted!
  1498.         : And how *dare* you accuse me of Ianuphilia!
  1499.  
  1500. C<autoformat> recognizes the various quoting conventions used in this example
  1501. and treats it as three paragraphs to be independently reformatted.
  1502.  
  1503. Block quotations present a different challenge. A typical formatter would
  1504. render the following quotation:
  1505.  
  1506.         "We are all of us in the gutter,
  1507.          but some of us are looking at the stars"
  1508.                                 -- Oscar Wilde
  1509.  
  1510. like so:
  1511.  
  1512.         "We are all of us in the gutter, but some of us are looking at
  1513.         the stars" -- Oscar Wilde
  1514.  
  1515. C<autoformat> recognizes the quotation structure by matching the following regular
  1516. expression against the text component of each paragraph:
  1517.  
  1518.         / \A(\s*)               # leading whitespace for quotation
  1519.           (["']|``)             # opening quotemark
  1520.           (.*)                  # quotation
  1521.           (''|\2)               # closing quotemark
  1522.           \s*?\n                # trailing whitespace after quotation
  1523.           (\1[ ]+)              # leading whitespace for attribution
  1524.                                 #   (must be indented more than quotation)
  1525.           (--|-)                # attribution introducer
  1526.           ([^\n]*?\n)           # first attribution line
  1527.           ((\5[^\n]*?$)*)       # other attribution lines 
  1528.                                 #   (indented no less than first line)
  1529.           \s*\Z                 # optional whitespace to end of paragraph
  1530.         /xsm
  1531.  
  1532. When reformatted (see below), the indentation and the attribution
  1533. structure will be preserved:
  1534.  
  1535.         "We are all of us in the gutter, but some of us are looking
  1536.          at the stars"
  1537.                                 -- Oscar Wilde
  1538.  
  1539. =head2 Widow control
  1540.  
  1541. Note that in the last example, C<autoformat> broke the line at column
  1542. 68, four characters earlier than it should have. It did so because, if
  1543. the full margin width had been used, the formatting would have left the
  1544. last two words by themselves on an oddly short last line:
  1545.  
  1546.         "We are all of us in the gutter, but some of us are looking at
  1547.          the stars"
  1548.  
  1549. This phenomenon is known as "widowing" and is heavily frowned upon in
  1550. typesetting circles. It looks ugly in plaintext too, so C<autoformat> 
  1551. avoids it by stealing extra words from earlier lines in a
  1552. paragraph, so as to leave enough for a reasonable last line. The heuristic
  1553. used is that final lines must be at least 10 characters long (though
  1554. this number may be adjusted by passing a C<widow =E<gt> I<minlength>>
  1555. argument to C<autoformat>).
  1556.  
  1557. If the last line is too short,
  1558. the paragraph's right margin is reduced by one column, and the paragraph
  1559. is reformatted. This process iterates until either the last line exceeds
  1560. nine characters or the margins have been narrowed by 10% of their
  1561. original separation. In the latter case, the reformatter gives up and uses its
  1562. original formatting.
  1563.  
  1564.  
  1565. =head2 Justification
  1566.  
  1567. The C<autoformat> subroutine also takes a named argument: C<{justify
  1568. =E<gt> I<type>}>, which specifies how each paragraph is to be justified.
  1569. The options are: C<'left'> (the default), C<'right',> C<'centre'> (or
  1570. C<'center'>), and C<'full'>. These act on the complete paragraph text
  1571. (but I<not> on any quoters before that text). For example, with C<'right'>
  1572. justification:
  1573.  
  1574.         R3>     Now is the Winter of our discontent made
  1575.         R3> glorious Summer by this son of York. And all
  1576.         R3> the clouds that lour'd upon our house In the
  1577.         R3>              deep bosom of the ocean buried.
  1578.  
  1579. Full justification is interesting in a fixed-width medium like plaintext
  1580. because it usually results in uneven spacing between words. Typically,
  1581. formatters provide this by distributing the extra spaces into the first
  1582. available gaps of each line:
  1583.  
  1584.         R3> Now  is  the  Winter  of our discontent made
  1585.         R3> glorious Summer by this son of York. And all
  1586.         R3> the  clouds  that  lour'd  upon our house In
  1587.         R3> the deep bosom of the ocean buried.
  1588.  
  1589. This produces a rather jarring visual effect, so C<autoformat> reverses
  1590. the strategy and inserts extra spaces at the end of lines:
  1591.  
  1592.         R3> Now is the Winter  of  our  discontent  made
  1593.         R3> glorious Summer by this son of York. And all
  1594.         R3> the clouds that lour'd  upon  our  house  In
  1595.         R3> the deep bosom of the ocean buried.
  1596.  
  1597. Most readers find this less disconcerting.
  1598.  
  1599. =head2 Implicit centring
  1600.  
  1601. Even if explicit centring is not specified, C<autoformat> will attempt
  1602. to automatically detect centred paragraphs and preserve their
  1603. justification. It does this by examining each line of the paragraph and
  1604. asking: "if this line were part of a centred paragraph, where would the
  1605. centre line have been?"
  1606.  
  1607. The answer can be determined by adding the length of leading whitespace
  1608. before the first word, plus half the length of the full set of words
  1609. on the line. That is, for a single line:
  1610.  
  1611.         $line =~ /^(\s*)(.*?)(\s*)$/
  1612.         $centre = length($1)+0.5*length($2);
  1613.  
  1614. By making the same estimate for every line, and then comparing the
  1615. estimates, it is possible to deduce whether all the lines are centred
  1616. with respect to the same axis of symmetry (with an allowance of
  1617. E<plusminus>1 to cater for the inevitable rounding when the centre
  1618. positions of even-length rows were originally computed). If a common
  1619. axis of symmetry is detected, C<autoformat> assumes that the lines are
  1620. supposed to be centred, and switches to centre-justification mode for
  1621. that paragraph.
  1622.  
  1623. =head2 Case transformations
  1624.  
  1625. The C<autoformat> subroutine can also optionally perform case conversions
  1626. on the text it processes. The C<{case =E<gt> I<type>}> argument allows the
  1627. user to specify five different conversions:
  1628.  
  1629. =over 4
  1630.  
  1631. =item C<'upper'>
  1632.  
  1633. This mode unconditionally converts every letter in the reformatted text to upper-case;
  1634.  
  1635. =item C<'lower'>
  1636.  
  1637. This mode unconditionally converts every letter in the reformatted text to lower-case;
  1638.  
  1639. =item C<'sentence'>
  1640.  
  1641. This mode attempts to generate correctly-cased sentences from the input text.
  1642. That is, the first letter after a sentence-terminating punctuator is converted
  1643. to upper-case. Then, each subsequent word in the sentence is converted to
  1644. lower-case, unless that word is originally mixed-case or contains punctuation.
  1645. For example, under C<{case =E<gt> 'sentence'}>:
  1646.  
  1647.         'POVERTY, MISERY, ETC. are the lot of the PhD candidate. alas!'
  1648.  
  1649. becomes:
  1650.  
  1651.         'Poverty, misery, etc. are the lot of the PhD candidate. Alas!'
  1652.  
  1653. Note that C<autoformat> is clever enough to recognize that the period after abbreviations such as C<etc.> is not a sentence terminator.
  1654.  
  1655. If the argument is specified as C<'sentence  '> (with one or more trailing
  1656. whitespace characters) those characters are used to replace the single space
  1657. that appears at the end of the sentence. For example,
  1658. C<autoformat($text, {case=E<gt>'sentence  '}>) would produce:
  1659.  
  1660.         'Poverty, misery, etc. are the lot of the PhD candidate.  Alas!'
  1661.  
  1662. =item C<'title'>
  1663.  
  1664. This mode behaves like C<'sentence'> except that the first letter of
  1665. I<every> word is capitalized:
  1666.  
  1667.         'What I Did On My Summer Vacation In Monterey'
  1668.  
  1669. =item C<'highlight'>
  1670.  
  1671. This mode behaves like C<'title'> except that trivial words are not
  1672. capitalized:
  1673.  
  1674.         'What I Did on my Summer Vacation in Monterey'
  1675.  
  1676. =back
  1677.  
  1678. =head1 OTHER FEATURES
  1679.  
  1680. =head2 The C<form> sub
  1681.  
  1682. The C<form()> subroutine may be exported from the module.
  1683. It takes a series of format (or "picture") strings followed by
  1684. replacement values, interpolates those values into each picture string,
  1685. and returns the result. The effect is similar to the inbuilt perl
  1686. C<format> mechanism, although the field specification syntax is
  1687. simpler and some of the formatting behaviour is more sophisticated.
  1688.  
  1689. A picture string consists of sequences of the following characters:
  1690.  
  1691. =over 8
  1692.  
  1693. =item <
  1694.  
  1695. Left-justified field indicator.
  1696. A series of sequential <'s specify
  1697. a left-justified field to be filled by a subsequent value.
  1698.  
  1699. =item >
  1700.  
  1701. Right-justified field indicator.
  1702. A series of sequential >'s specify
  1703. a right-justified field to be filled by a subsequent value.
  1704.  
  1705. =item ^
  1706.  
  1707. Centre-justified field indicator.
  1708. A series of sequential ^'s specify
  1709. a centred field to be filled by a subsequent value.
  1710.  
  1711. =item >>>.<<<<
  1712.  
  1713. A numerically formatted field with the specified number of digits to
  1714. either side of the decimal place. See L<Numerical formatting> below.
  1715.  
  1716.  
  1717. =item [
  1718.  
  1719. Left-justified block field indicator.
  1720. Just like a < field, except it repeats as required on subsequent lines. See
  1721. below.
  1722.  
  1723. =item ]
  1724.  
  1725. Right-justified block field indicator.
  1726. Just like a > field, except it repeats as required on subsequent lines. See
  1727. below.
  1728.  
  1729. =item |
  1730.  
  1731. Centre-justified block field indicator.
  1732. Just like a ^ field, except it repeats as required on subsequent lines. See
  1733. below.
  1734.  
  1735. =item ]]].[[[[
  1736.  
  1737. A numerically formatted block field with the specified number of digits to
  1738. either side of the decimal place.
  1739. Just like a >>>.<<<< field, except it repeats as required on
  1740. subsequent lines. See below. 
  1741.  
  1742. =item \
  1743.  
  1744. Literal escape of next character (e.g. C<\|> is formatted as '|', not a one
  1745. character wide centre-justified block field).
  1746.  
  1747. =item Any other character
  1748.  
  1749. That literal character.
  1750.  
  1751. =back
  1752.  
  1753. Any substitution value which is C<undef> (either explicitly so, or because it
  1754. is missing) is replaced by an empty string.
  1755.  
  1756.  
  1757.  
  1758. =head2 Controlling line filling.
  1759.  
  1760. Note that, unlike the a perl C<format>, C<form> preserves whitespace
  1761. (including newlines) unless called with certain options.
  1762.  
  1763. The "squeeze" option (when specified with a true value) causes any sequence
  1764. of spaces and/or tabs (but not newlines) in an interpolated string to be
  1765. replaced with a single space.
  1766.  
  1767. The "fill" option causes newlines to also be squeezed.
  1768.  
  1769. Hence:
  1770.  
  1771.     $frmt = "# [[[[[[[[[[[[[[[[[[[[[";
  1772.     $data = "h  e\t \tl lo\nworld\t\t\t\t\t";
  1773.  
  1774.     print form $frmt, $data;
  1775.     # h  e            l lo
  1776.     # world
  1777.  
  1778.     print form {squeeze=>1}, $frmt, $data;
  1779.     # h e l lo
  1780.     # world
  1781.  
  1782.     print form {fill=>1}, $frmt, $data;
  1783.     # h  e            l lo world
  1784.  
  1785.     print form {squeeze=>1, fill=>1}, $frmt, $data;
  1786.     # h e l lo world
  1787.  
  1788.  
  1789. Whether or not filling or squeezing is in effect, C<form> can also be
  1790. directed to trim any extra whitespace from the end of each line it
  1791. formats, using the "trim" option. If this option is specified with a
  1792. true value, every line returned by C<form> will automatically have the
  1793. substitution C<s/[ \t]+$//gm> applied to it.
  1794.  
  1795. Hence:
  1796.  
  1797.     print length form "[[[[[[[[[[", "short";
  1798.     # 11
  1799.  
  1800.     print length form {trim=>1}, "[[[[[[[[[[", "short";
  1801.     # 6
  1802.  
  1803.  
  1804.  
  1805. =head2 Temporary and permanent default options
  1806.  
  1807. If C<form> is called with options, but no template string or data, it resets
  1808. it's defaults to the options specified. If called in a void context:
  1809.  
  1810.         form { squeeze => 1, trim => 1 };
  1811.  
  1812. the options become permanent defaults.
  1813.  
  1814. However, when called with only options in non-void context, C<form>
  1815. resets its defaults to those options and returns an object. The reset
  1816. default values persist only until that returned object is destroyed.
  1817. Hence to temporarily reset C<form>'s defaults within a single subroutine:
  1818.  
  1819.         sub single {
  1820.                 my $tmp = form { squeeze => 1, trim => 1 };
  1821.  
  1822.                 # do formatting with the obove defaults
  1823.  
  1824.         } # form's defaults revert to previous values as $tmp object destroyed
  1825.  
  1826.  
  1827. =head2 How C<form> hyphenates
  1828.  
  1829. Any line with a block field repeats on subsequent lines until all block fields
  1830. on that line have consumed all their data. Non-block fields on these lines are
  1831. replaced by the appropriate number of spaces.
  1832.  
  1833. Words are wrapped whole, unless they will not fit into the field at
  1834. all, in which case they are broken and (by default) hyphenated. Simple
  1835. hyphenation is used (i.e. break at the I<N-1>th character and insert a
  1836. '-'), unless a suitable alternative subroutine is specified instead.
  1837.  
  1838. Words will not be broken if the break would leave less than 2 characters on
  1839. the current line. This minimum can be varied by setting the 'minbreak' option
  1840. to a numeric value indicating the minumum total broken characters (including
  1841. hyphens) required on the current line. Note that, for very narrow fields,
  1842. words will still be broken (but I<unhyphenated>). For example:
  1843.  
  1844.         print form '|', 'split';
  1845.  
  1846. would print:
  1847.  
  1848.         s
  1849.         p
  1850.         l
  1851.         i
  1852.         t
  1853.  
  1854. whilst:
  1855.  
  1856.         print form {minbreak=>1}, '|', 'split';
  1857.  
  1858. would print:
  1859.  
  1860.         s-
  1861.         p-
  1862.         l-
  1863.         i-
  1864.         t
  1865.  
  1866. Alternative breaking subroutines can be specified using the "break" option in a
  1867. configuration hash. For example:
  1868.  
  1869.         form { break => \&my_line_breaker }
  1870.              $format_str,
  1871.              @data;
  1872.  
  1873. C<form> expects any user-defined line-breaking subroutine to take three
  1874. arguments (the string to be broken, the maximum permissible length of
  1875. the initial section, and the total width of the field being filled).
  1876. The C<hypenate> sub must return a list of two strings: the initial
  1877. (broken) section of the word, and the remainder of the string
  1878. respectively).
  1879.  
  1880. For example:
  1881.  
  1882.         sub tilde_break = sub($$$)
  1883.         {
  1884.                 (substr($_[0],0,$_[1]-1).'~', substr($_[0],$_[1]-1));
  1885.         }
  1886.  
  1887.         form { break => \&tilde_break }
  1888.              $format_str,
  1889.              @data;
  1890.  
  1891.  
  1892. makes '~' the hyphenation character, whilst:
  1893.  
  1894.         sub wrap_and_slop = sub($$$)
  1895.         {
  1896.                 my ($text, $reqlen, $fldlen) = @_;
  1897.                 if ($reqlen==$fldlen) { $text =~ m/\A(\s*\S*)(.*)/s }
  1898.                 else                  { ("", $text) }
  1899.         }
  1900.  
  1901.         form { break => \&wrap_and_slop }
  1902.              $format_str,
  1903.              @data;
  1904.  
  1905. wraps excessively long words to the next line and "slops" them over
  1906. the right margin if necessary.
  1907.  
  1908. The Text::Autoformat package provides three functions to simplify the use
  1909. of variant hyphenation schemes. The exportable subroutine
  1910. C<Text::Autoformat::break_wrap> generates a reference to a subroutine
  1911. implementing the "wrap-and-slop" algorithm shown in the last example,
  1912. which could therefore be rewritten:
  1913.  
  1914.         use Text::Autoformat qw( form break_wrap );
  1915.  
  1916.         form { break => break_wrap }
  1917.              $format_str,
  1918.              @data;
  1919.  
  1920. The subroutine C<Text::Autoformat::break_with> takes a single string
  1921. argument and returns a reference to a sub which hyphenates with that
  1922. string. Hence the first of the two examples could be rewritten:
  1923.  
  1924.         use Text::Autoformat qw( form break_wrap );
  1925.  
  1926.         form { break => break_with('~') }
  1927.              $format_str,
  1928.              @data;
  1929.  
  1930. The subroutine C<Text::Autoformat::break_TeX> 
  1931. returns a reference to a sub which hyphenates using 
  1932. Jan Pazdziora's TeX::Hyphen module. For example:
  1933.  
  1934.         use Text::Autoformat qw( form break_wrap );
  1935.  
  1936.         form { break => break_TeX }
  1937.              $format_str,
  1938.              @data;
  1939.  
  1940. Note that in the previous examples there is no leading '\&' before
  1941. C<break_wrap>, C<break_with>, or C<break_TeX>, since each is being
  1942. directly I<called> (and returns a reference to some other suitable
  1943. subroutine);
  1944.  
  1945.  
  1946. =head2 The C<form> formatting algorithm
  1947.  
  1948. The algorithm C<form> uses is:
  1949.  
  1950.         1. split the first string in the argument list
  1951.            into individual format lines and add a terminating
  1952.            newline (unless one is already present).
  1953.  
  1954.         2. for each format line...
  1955.  
  1956.                 2.1. determine the number of fields and shift
  1957.                      that many values off the argument list and
  1958.                      into the filling list. If insufficient
  1959.                      arguments are available, generate as many 
  1960.                      empty strings as are required.
  1961.  
  1962.                 2.2. generate a text line by filling each field
  1963.                      in the format line with the initial contents
  1964.                      of the corresponding arg in the filling list
  1965.                      (and remove those initial contents from the arg).
  1966.  
  1967.                 2.3. replace any <,>, or ^ fields by an equivalent
  1968.                      number of spaces. Splice out the corresponding
  1969.                      args from the filling list.
  1970.  
  1971.                 2.4. Repeat from step 2.2 until all args in the
  1972.                      filling list are empty.
  1973.  
  1974.         3. concatenate the text lines generated in step 2
  1975.  
  1976.         4. repeat from step 1 until the argument list is empty
  1977.  
  1978.  
  1979. =head2 C<form> examples
  1980.  
  1981. As an example of the use of C<form>, the following:
  1982.  
  1983.         $count = 1;
  1984.         $text = "A big long piece of text to be formatted exquisitely";
  1985.  
  1986.         print form q
  1987.         {
  1988.                 ||||  <<<<<<<<<<
  1989.                 ----------------
  1990.                 ^^^^  ]]]]]]]]]]\|
  1991.                                 =
  1992.                 ]]].[[[
  1993.                 
  1994.         }, $count, $text, $count+11, $text, "123 123.4\n123.456789";
  1995.  
  1996. produces the following output:
  1997.  
  1998.                  1    A big long
  1999.                 ----------------
  2000.                  12     piece of|
  2001.                       text to be|
  2002.                        formatted|
  2003.                       exquisite-|
  2004.                               ly|
  2005.                                 =
  2006.                 123.0
  2007.                 123.4
  2008.                 123.456
  2009.  
  2010. Picture strings and replacement values can be interleaved in the
  2011. traditional C<format> format, but care is needed to ensure that the
  2012. correct number of substitution values are provided. For example:
  2013.  
  2014.         $report = form
  2015.                 'Name           Rank    Serial Number',
  2016.                 '====           ====    =============',
  2017.                 '<<<<<<<<<<<<<  ^^^^    <<<<<<<<<<<<<',
  2018.                  $name,         $rank,  $serial_number,
  2019.                 ''
  2020.                 'Age    Sex     Description',
  2021.                 '===    ===     ===========',
  2022.                 '^^^    ^^^     [[[[[[[[[[[',
  2023.                  $age,  $sex,   $description;
  2024.  
  2025.  
  2026. =head2 How C<form> consumes strings
  2027.  
  2028. Unlike C<format>, within C<form> non-block fields I<do> consume the text
  2029. they format, so the following:
  2030.  
  2031.         $text = "a line of text to be formatted over three lines";
  2032.         print form "<<<<<<<<<<\n  <<<<<<<<\n    <<<<<<\n",
  2033.                     $text,        $text,        $text;
  2034.  
  2035. produces:
  2036.  
  2037.         a line of
  2038.           text to
  2039.             be fo-
  2040.  
  2041. not:
  2042.  
  2043.         a line of
  2044.           a line 
  2045.             a line
  2046.  
  2047. To achieve the latter effect, convert the variable arguments
  2048. to independent literals (by double-quoted interpolation):
  2049.  
  2050.         $text = "a line of text to be formatted over three lines";
  2051.         print form "<<<<<<<<<<\n  <<<<<<<<\n    <<<<<<\n",
  2052.                    "$text",      "$text",      "$text";
  2053.  
  2054. Although values passed from variable arguments are progressively consumed
  2055. I<within> C<form>, the values of the original variables passed to C<form>
  2056. are I<not> altered.  Hence:
  2057.  
  2058.         $text = "a line of text to be formatted over three lines";
  2059.         print form "<<<<<<<<<<\n  <<<<<<<<\n    <<<<<<\n",
  2060.                     $text,        $text,        $text;
  2061.         print $text, "\n";
  2062.  
  2063. will print:
  2064.  
  2065.         a line of
  2066.           text to
  2067.             be fo-
  2068.         a line of text to be formatted over three lines
  2069.  
  2070. To cause C<form> to consume the values of the original variables passed to
  2071. it, pass them as references. Thus:
  2072.  
  2073.         $text = "a line of text to be formatted over three lines";
  2074.         print form "<<<<<<<<<<\n  <<<<<<<<\n    <<<<<<\n",
  2075.                     \$text,       \$text,       \$text;
  2076.         print $text, "\n";
  2077.  
  2078. will print:
  2079.  
  2080.         a line of
  2081.           text to
  2082.             be fo-
  2083.         rmatted over three lines
  2084.  
  2085. Note that, for safety, the "non-consuming" behaviour takes precedence,
  2086. so if a variable is passed to C<form> both by reference I<and> by value,
  2087. its final value will be unchanged.
  2088.  
  2089. =head2 Numerical formatting
  2090.  
  2091. The ">>>.<<<" and "]]].[[[" field specifiers may be used to format
  2092. numeric values about a fixed decimal place marker. For example:
  2093.  
  2094.         print form '(]]]]].[[)', <<EONUMS;
  2095.                    1
  2096.                    1.0
  2097.                    1.001
  2098.                    1.009
  2099.                    123.456
  2100.                    1234567
  2101.                    one two
  2102.         EONUMS
  2103.  
  2104. would print:
  2105.                    
  2106.         (    1.0 )
  2107.         (    1.0 )
  2108.         (    1.00)
  2109.         (    1.01)
  2110.         (  123.46)
  2111.         (#####.##)
  2112.         (?????.??)
  2113.         (?????.??)
  2114.  
  2115. Fractions are rounded to the specified number of places after the
  2116. decimal, but only significant digits are shown. That's why, in the
  2117. above example, 1 and 1.0 are formatted as "1.0", whilst 1.001 is
  2118. formatted as "1.00".
  2119.  
  2120. You can specify that the maximal number of decimal places always be used
  2121. by giving the configuration option 'numeric' a value that matches
  2122. /\bAllPlaces\b/i. For example:
  2123.  
  2124.         print form { numeric => AllPlaces },
  2125.                    '(]]]]].[[)', <<'EONUMS';
  2126.                    1
  2127.                    1.0
  2128.         EONUMS
  2129.  
  2130. would print:
  2131.                    
  2132.         (    1.00)
  2133.         (    1.00)
  2134.  
  2135. Note that although decimal digits are rounded to fit the specified width, the
  2136. integral part of a number is never modified. If there are not enough places
  2137. before the decimal place to represent the number, the entire number is 
  2138. replaced with hashes.
  2139.  
  2140. If a non-numeric sequence is passed as data for a numeric field, it is
  2141. formatted as a series of question marks. This querulous behaviour can be
  2142. changed by giving the configuration option 'numeric' a value that
  2143. matches /\bSkipNaN\b/i in which case, any invalid numeric data is simply
  2144. ignored. For example:
  2145.  
  2146.  
  2147.         print form { numeric => 'SkipNaN' }
  2148.                    '(]]]]].[[)',
  2149.                    <<EONUMS;
  2150.                    1
  2151.                    two three
  2152.                    4
  2153.         EONUMS
  2154.  
  2155. would print:
  2156.                    
  2157.         (    1.0 )
  2158.         (    4.0 )
  2159.  
  2160.  
  2161. =head2 Filling block fields with lists of values
  2162.  
  2163. If an argument corresponding to a field is an array reference, then C<form>
  2164. automatically joins the elements of the array into a single string, separating
  2165. each element with a newline character. As a result, a call like this:
  2166.  
  2167.         @values = qw( 1 10 100 1000 );
  2168.         print form "(]]]].[[)", \@values;
  2169.  
  2170. will print out
  2171.  
  2172.          (   1.00)
  2173.          (  10.00)
  2174.          ( 100.00)
  2175.          (1000.00)
  2176.  
  2177. as might be expected.
  2178.  
  2179. Note however that arrays must be passed by reference (so that C<form>
  2180. knows that the entire array holds data for a single field). If the previous
  2181. example had not passed @values by reference:
  2182.  
  2183.         @values = qw( 1 10 100 1000 );
  2184.         print form "(]]]].[[)", @values;
  2185.  
  2186. the output would have been:
  2187.  
  2188.          (   1.00)
  2189.          10
  2190.          100
  2191.          1000
  2192.  
  2193. This is because @values would have been interpolated into C<form>'s
  2194. argument list, so only $value[0] would have been used as the data for
  2195. the initial format string. The remaining elements of @value would have
  2196. been treated as separate format strings, and printed out "verbatim".
  2197.  
  2198. Note too that, because arrays must be passed using a reference, their
  2199. original contents are consumed by C<form>, just like the contents of
  2200. scalars passed by reference.
  2201.  
  2202. To avoid having an array consumed by C<form>, pass it as an anonymous
  2203. array:
  2204.  
  2205.         print form "(]]]].[[)", [@values];
  2206.  
  2207.  
  2208. =head2 Headers, footers, and pages
  2209.  
  2210. The C<form> subroutine can also insert headers, footers, and page-feeds
  2211. as it formats. These features are controlled by the "header", "footer",
  2212. "pagefeed", "pagelen", and "pagenum" options.
  2213.  
  2214. The "pagenum" option takes a scalar value or a reference to a scalar
  2215. variable and starts page numbering at that value. If a reference to a
  2216. scalar variable is specified, the value of that variable is updated as
  2217. the formatting proceeds, so that the final page number is available in
  2218. it after formatting. This can be useful for multi-part reports.
  2219.  
  2220. The "pagelen" option specifies the total number of lines in a page (including
  2221. headers, footers, and page-feeds).
  2222.  
  2223. If the "header" option is specified with a string value, that string is
  2224. used as the header of every page generated. If it is specified as a reference
  2225. to a subroutine, that subroutine is called at the start of every page and
  2226. its return value used as the header string. When called, the subroutine is
  2227. passed the current page number.
  2228.  
  2229. Likewise, if the "footer" option is specified with a string value, that
  2230. string is used as the footer of every page generated. If it is specified
  2231. as a reference to a subroutine, that subroutine is called at the I<start>
  2232. of every page and its return value used as the footer string. When called,
  2233. the footer subroutine is passed the current page number. If the option is
  2234. specified as a hash, it acts as described above for the "header" option.
  2235.  
  2236. Both the header and footer options can also be specified as hash references.
  2237. In this case the hash entires for keys "left", "centre" (or "center"), and
  2238. "right" specify what is to appear on the left, centre, and right of the
  2239. header/footer. The entry for the key "width" specifies how wide the
  2240. footer is to be. The  "left", "centre", and "right" values may be literal
  2241. strings, or subroutines (just as a normal header/footer specification may
  2242. be.) See the second example, below.
  2243.  
  2244. The "pagefeed" option acts in exactly the same way, to produce a
  2245. pagefeed which is appended after the footer. But note that the pagefeed
  2246. is not counted as part of the page length.
  2247.  
  2248. All three of these page components are recomputed at the start of each
  2249. new page, before the page contents are formatted (recomputing the header
  2250. and footer makes it possible to determine how many lines of data to
  2251. format so as to adhere to the specified page length).
  2252.  
  2253. When the call to C<form> is complete and the data has been fully formatted,
  2254. the footer subroutine is called one last time, with an extra argument of 1.
  2255. The string returned by this final call is used as the final footer.
  2256.  
  2257. So for example, a 60-line per page report, starting at page 7,
  2258. with appropriate headers and footers might be set up like so:
  2259.  
  2260.         $page = 7;
  2261.  
  2262.         form { header => sub { "Page $_[0]\n\n" },
  2263.                footer => sub { return "" if $_[1];
  2264.                                "-"x50 . "\n" . form ">"x50", "...".($_[0]+1);
  2265.                              },
  2266.                pagefeed => "\n\n",
  2267.                pagelen  => 60
  2268.                pagenum  => \$page,
  2269.              },
  2270.              $template,
  2271.              @data;
  2272.  
  2273. Note the recursive use of C<form> within the "footer" option.
  2274.  
  2275. Alternatively, to set up headers and footers such that the running
  2276. head is right justified in the header and the page number is centred
  2277. in the footer:
  2278.  
  2279.         form { header => { right => "Running head" },
  2280.                footer => { centre => sub { "Page $_[0]" } },
  2281.                pagelen  => 60
  2282.              },
  2283.              $template,
  2284.              @data;
  2285.  
  2286.  
  2287.  
  2288. =head2 The C<tag> sub
  2289.  
  2290. The C<tag> subroutine may be exported from the module.
  2291. It takes two arguments: a tag specifier and a text to be
  2292. entagged. The tag specifier indicates the indenting of the tag, and of the
  2293. text. The sub generates an end-tag (using the usual "/I<tag>" variant),
  2294. unless an explicit end-tag is provided as the third argument.
  2295.  
  2296. The tag specifier consists of the following components (in order):
  2297.  
  2298. =over 4
  2299.  
  2300. =item An optional vertical spacer (zero or more whitespace-separated newlines)
  2301.  
  2302. One or more whitespace characters up to a final mandatory newline. This
  2303. vertical space is inserted before the tag and after the end-tag
  2304.  
  2305. =item An optional tag indent
  2306.  
  2307. Zero or more whitespace characters. Both the tag and the end-tag are indented
  2308. by this whitespace.
  2309.  
  2310. =item An optional left (opening) tag delimiter
  2311.  
  2312. Zero or more non-"word" characters (not alphanumeric or '_').
  2313. If the opening delimiter is omitted, the character '<' is used.
  2314.  
  2315. =item A tag
  2316.  
  2317. One or more "word" characters (alphanumeric or '_').
  2318.  
  2319. =item Optional tag arguments
  2320.  
  2321. Any number of any characters
  2322.  
  2323. =item An optional right (closing) tag delimiter
  2324.  
  2325. Zero or more non-"word" characters which balance some sequential portion
  2326. of the opening tag delimiter. For example, if the opening delimiter
  2327. is "<-(" then any of the following are acceptible closing delimiters:
  2328. ")->", "->", or ">".
  2329. If the closing delimiter is omitted, the "inverse" of the opening delimiter 
  2330. is used (for example, ")->"),
  2331.  
  2332. =item An optional vertical spacer (zero or more newlines)
  2333.  
  2334. One or more whitespace characters up to a mandatory newline. This
  2335. vertical space is inserted before and after the complete text.
  2336.  
  2337. =item An optional text indent
  2338.  
  2339. Zero or more space of tab characters. Each line of text is indented
  2340. by this whitespace (in addition to the tag indent).
  2341.  
  2342.  
  2343. =back
  2344.  
  2345. For example:
  2346.  
  2347.         $text = "three lines\nof tagged\ntext";
  2348.  
  2349.         print tag "A HREF=#nextsection", $text;
  2350.  
  2351. prints:
  2352.  
  2353.         <A HREF=#nextsection>three lines
  2354.         of tagged
  2355.         text</A>
  2356.  
  2357. whereas:
  2358.  
  2359.         print tag "[-:GRIN>>>\n", $text;
  2360.  
  2361. prints:
  2362.  
  2363.         [-:GRIN>>>:-]
  2364.         three lines
  2365.         of tagged
  2366.         text
  2367.         [-:/GRIN>>>:-]
  2368.  
  2369. and:
  2370.  
  2371.         print tag "\n\n   <BOLD>\n\n   ", $text, "<END BOLD>";
  2372.  
  2373. prints:
  2374.  
  2375. S< >
  2376.  
  2377.            <BOLD>
  2378.  
  2379.               three lines
  2380.               of tagged
  2381.               text
  2382.  
  2383.            <END BOLD>
  2384.  
  2385. S< >
  2386.  
  2387. (with the indicated spacing fore and aft).
  2388.  
  2389. =head1 AUTHOR
  2390.  
  2391. Damian Conway (damian@conway.org)
  2392.  
  2393. =head1 BUGS
  2394.  
  2395. There are undoubtedly serious bugs lurking somewhere in code this funky :-)
  2396. Bug reports and other feedback are most welcome.
  2397.  
  2398. =head1 COPYRIGHT
  2399.  
  2400. Copyright (c) 1997-2000, Damian Conway. All Rights Reserved.
  2401. This module is free software. It may be used, redistributed
  2402. and/or modified under the terms of the Perl Artistic License
  2403.   (see http://www.perl.com/perl/misc/Artistic.html)
  2404.