# -*- Perl -*-
#
# File:  PTools/WordWrap.pm
# Desc:  Format text strings
# Date:  Wed Oct 16 15:20:15 PDT 1996
# Mods:  Fri Jul 19 01:31:57 PDT 2002
# Lang:  Perl
# Stat:  Production
#
# Synopsis:
#
#     use PTools::WordWrap;
#     
#     $textBlock = PTools::WordWrap->parse( 72, $textBlock );
#
# 
# Abstract:
#
#   "Given an arbitrary string ... look for long lines ^M"
#    ^                                         ^        ^
#    curPos                              nextPos        crlfPos
#
#  Where:
#    curPos = current position in string; this index "leap frogs"
#             over the "crlfPos" index as string segments are
#             processed
#
#   nextPos = one of a)  71 chars out from start of string
#                 or b)  71 chars out from last CRLF found
#                 or c)  71 chars out from a CRLF we just inserted
#                 or d)  at the next CRLF position in the text
#                 or e)  at the end position of the string
#
#             If there are more than 70 chars between this
#             index and the "curPos" index, then we need to
#             insert one line break in the current segment.
#
#             Also, we may need to insert multiple line breaks
#             before we reach the current value of "crlfPos"index.
#
#   crlfPos = either a)  the position of next CRLF characters
#                 or b)  end position of the string
#
#  Note that some text may have both Carriage Return and Newline
#  characters at the end of each line and other text may just have
#  Newline characters; this routine handles both cases.
#
#  And, of course, this routine is designed so that any text can be
#  sent back through this routine at any time. After the first time
#  through, no further changes will be made (unless the text changes
#  or the specified margin is less than in the previous iteration).
#

package PTools::WordWrap;
use strict;

use vars qw( $VERSION );
$VERSION = 0.04;

*wrapLongLines = \&parse;                  # retain original method name

sub parse {
  my($class)      = shift;
  my($hardMargin) = shift;                 # If < 10 will be set to 70
  my($text)       = shift;                 # Text to be parsed

  #___________________________________
  # Initialize some vars
  #
  my($CRLF,$softMargin,$crlfPos,$curPos,$nextPos,$strLen);
  my($done,$check,$pos);

# $CRLF = "\r\n";         # (each of          # Carriage Return and Newline
# $CRLF = "\cM\n";        # these are         # Carriage Return and Newline
  $CRLF = "\015\012";     # equivalent)       # Carriage Return and Newline

  $crlfPos= index($text,$CRLF,0);             # Do we have "\r\n" chars?
  if ($crlfPos == -1) {                       # if none found then just
     $CRLF = "\012";                          # look for "\n" chars.
  } else {
  }

  if ($hardMargin < 10) {
     $hardMargin= 70;                         # Hard margin
  }
  $softMargin = $hardMargin + 1;              # Soft margin
  $curPos = 0;                                # Start at beginning of string
  $nextPos=$softMargin;                       # and look for long lines

  $strLen = length($text);                    # Check to make sure that
  if ($strLen < $hardMargin) {                # we even need to process
    return($text);                            # this string.
  }

  $crlfPos= index($text,$CRLF,$curPos);       # Find first CRLF in string
  if ($crlfPos == -1) {                       # if none, then use
     $crlfPos = $strLen;                      # the end of the string
  }

  if ($nextPos > $crlfPos) {                  # Do a sanity check and adjust
     $nextPos=$crlfPos;                       # if first line is a short one
  }
  #___________________________________
  # Loop through entire text block
  #
  $done="";
  while (!$done) {

#$check = $nextPos - $curPos;
#printf(" DEBUG: crlfPos = %d \t curPos = %d \t nextPos = %d \t (diff = %s)\n",
#                  $crlfPos,        $curPos,      $nextPos,       $check );
    #___________________________________
    # Loop through current section of text
    # up to the next CRLF character adding
    # line breaks as necessary
    #
    $check = $nextPos - $curPos;                  # Do we have a long line??

    while ($check > $hardMargin) {                # Yup ...

       $pos = _replaceSpace($hardMargin,$curPos,
			    \$text,$CRLF);        # Force a CR/LF

       $curPos = $pos + 1;                        # Set up for another loop if
       $nextPos= $curPos + $softMargin;           # line is still too long ...

       if ($nextPos > $crlfPos) {
          $check = 0;                             # All done w/this "paragraph"
       } else {
          $check = $nextPos - $curPos;            # Line is still too long!
       }
    }

    #___________________________________
    # Find next occurance of CRLF if any
    #
    $curPos = $crlfPos + 1;                       # Leap frog to next segment
    $crlfPos= index($text,$CRLF,$curPos);         # and find next existing CRLF

    if ($crlfPos == -1 && $curPos < $strLen) {    # Short segment w/out CRLF so
       $crlfPos = $strLen;                        # go to end of string (EOS)

    } elsif ($crlfPos == -1) {                    # We're outta' here!
       $done = "True";
    }

    #___________________________________
    # Set up for the next paragraph
    #
    $nextPos= $curPos  + $softMargin;             # Mark off the next segment
    if ($nextPos > $crlfPos) {                    # which cannot be longer than
       $nextPos= $crlfPos;                        # the next CRLF char (or EOS)
    }
  }
  return($text);                                  # We're outta' here.
}

  #___________________________________________________
  #
  # Look backwards through the text string starting at position 
  # "$start" and ending with position "$end" ... replace the first 
  # space found with a CR/LF and return the position in the string.
  #
  sub _replaceSpace {

    my($hardMargin,$end,$text,$CRLF) = @_;
    my($start) = $end + $hardMargin;

    while ($start > $end) {
      if (substr($$text,$start,1) eq " ") {
         substr($$text,$start,1) = "$CRLF";        # Retain space & add CR/LF
         return($start);                          # and return the offset
      }
      $start--;
    }
    return($end);                                 # No replacement done.
  }
#_________________________
1; # Required by require()

__END__

=head1 NAME

PTools::WordWrap - Format text strings

=head1 VERSION

=head1 AUTHOR

Chris Cobb [no dot spam at ccobb dot net]
