/*****************************************************************************
#   Copyright (C) 1994-2008 by David Gordon.
#   All rights reserved.                           
#                                                                           
#   This software is part of a beta-test version of the Consed/Autofinish
#   package.  It should not be redistributed or
#   used for any commercial purpose, including commercially funded
#   sequencing, without written permission from the author and the
#   University of Washington.
#   
#   This software is provided ``AS IS'' and any express or implied
#   warranties, including, but not limited to, the implied warranties of
#   merchantability and fitness for a particular purpose, are disclaimed.
#   In no event shall the authors or the University of Washington be
#   liable for any direct, indirect, incidental, special, exemplary, or
#   consequential damages (including, but not limited to, procurement of
#   substitute goods or services; loss of use, data, or profits; or
#   business interruption) however caused and on any theory of liability,
#   whether in contract, strict liability, or tort (including negligence
#   or otherwise) arising in any way out of the use of this software, even
#   if advised of the possibility of such damage.
#
#   Building Consed from source is error prone and not simple which is
#   why I provide executables.  Due to time limitations I cannot
#   provide any assistance in building Consed.  Even if you do not
#   modify the source, you may introduce errors due to using a
#   different version of the compiler, a different version of motif,
#   different versions of other libraries than I used, etc.  For this
#   reason, if you discover Consed bugs, I can only offer help with
#   those bugs if you first reproduce those bugs with an executable
#   provided by me--not an executable you have built.
# 
#   Modifying Consed is also difficult.  Although Consed is modular,
#   some modules are used by many other modules.  Thus making a change
#   in one place can have unforeseen effects on many other features.
#   It may takes months for you to notice these other side-effects
#   which may not seen connected at all.  It is not feasable for me to
#   provide help with modifying Consed sources because of the
#   potentially huge amount of time involved.
#
#*****************************************************************************/
#include    "rwcstring.h"
#include    <iostream.h>
#include    "mbt_exception.h"

#ifndef WALRUS_BUILD
#include    <malloc.h>
#endif

#include    "rwcsubstring.h"
#include    "rwcregexp.h"
#include    <ctype.h>
#include    "assert.h"
#include    <stdio.h>
#include    <stdlib.h>
#include    <stdarg.h>
#include    "max.h"
#include    "soEmptyString.h"



RWCString:: ~RWCString() {
   free( sz_ );
}



RWCString :: RWCString() {
   sz_ = 0;
   nMaxLength_ = 0;
   nCurrentLength_ = 0;
}


// RWCString :: RWCString() {
//    sz_ = (char*) malloc( ( nDefaultMaxLength + 1) * sizeof( char ) );

//    if ( !sz_ ) {
//       PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::RWCString() amount: " << nDefaultMaxLength + 1 << ends;
//       throw RWInternalError( ost.str() );
//    }
   
//    nMaxLength_ = nDefaultMaxLength;
//    nCurrentLength_ = 0;
//    sz_[0] = '\0';
// }




RWCString :: RWCString( const char c ) {

   sz_ = (char*) malloc( ( nDefaultMaxLength + 1 ) * sizeof( char ) );

   if ( !sz_ ) {
      PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::RWCString( char c ) amount: " << nDefaultMaxLength + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   nMaxLength_ = nDefaultMaxLength;
   nCurrentLength_ = 1;
   sz_[0] = c;
   sz_[1] = '\0';
}

RWCString :: RWCString( const char c, size_t nNumberOfCopies ) {
   nMaxLength_ = (int) ( nNumberOfCopies * fDefaultInitialAdditionalFactor );
   
   sz_ = (char*) malloc( ( nMaxLength_ + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << 
         "cannot malloc enough memory for RWCString::RWCString( " << 
         c << ", " << nNumberOfCopies << " ).  amount = " << 
         nMaxLength_ + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   nCurrentLength_ = nNumberOfCopies;
   for( int n = 0; n < nNumberOfCopies; ++n )
      sz_[ n ] = c;

   sz_[ nCurrentLength_ ] = '\0';
}


RWCString :: RWCString( const char* sz ) {

   nCurrentLength_ = strlen( sz );
   int nTemp = nCurrentLength_;
   if ( nTemp == 0 )
      nTemp = nDefaultMaxLength;

   nMaxLength_ = (int) (nTemp * fDefaultInitialAdditionalFactor );

   sz_ = (char*) malloc( ( nMaxLength_ + 1 ) * sizeof( char ) );

   if ( !sz_ ) {
      PANIC_OST( ost ) << "cannot malloc enough memory for RWCString::RWCString( char* sz ) amount: " << nMaxLength_ + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   memcpy( sz_, sz, nCurrentLength_ + 1 );
}
      

RWCString :: RWCString( const char* sz, size_t nNumberOfBytes ) {
   nCurrentLength_ = nNumberOfBytes;
   int nTemp = nCurrentLength_;
   if ( nTemp == 0 )
      nTemp = nDefaultMaxLength;

   nMaxLength_ = (int) ( nTemp * fDefaultInitialAdditionalFactor );
   
   sz_ = (char*) malloc( ( nMaxLength_ + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << 
         "cannot malloc enough memory for RWCString::RWCString( char* sz, " << 
         nNumberOfBytes << " ) " << ends;

      throw RWInternalError( ost.str() );
   }

   memcpy( sz_, sz, nNumberOfBytes );
   sz_[ nCurrentLength_ ] = '\0';
}





RWCString :: RWCString( const RWCString& so ) {
   nCurrentLength_ = so.nCurrentLength_;

   // special case for null so
   if ( nCurrentLength_ == 0 ) {
      nMaxLength_ = 0;
      sz_ = 0;
      return;
   }
      
   nMaxLength_ = 
         (int) (so.nCurrentLength_ * fDefaultInitialAdditionalFactor);

   sz_ = (char*) malloc( (nMaxLength_ + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << "cannot malloc enough memory for RWCString::RWCString( const RWCString& so ).  Amount: " << nMaxLength_ + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   memcpy( sz_, so.sz_, nCurrentLength_ + 1 );
}



RWCString :: RWCString( const RWCSubString& sub ) {

   nCurrentLength_ = sub.nLength_;

   // special case when assigned from a null substring
   if ( nCurrentLength_ == 0 ) {
      nMaxLength_ = 0;
      sz_ = 0;
      return;
   }

   nMaxLength_ = 
         (int) ( nCurrentLength_ * fDefaultInitialAdditionalFactor );

   sz_ = (char*) malloc( ( nMaxLength_ + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::RWCString( const RWCSubString& sub ).  Amount: " << nMaxLength_ + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   memcpy( sz_, sub.pRWCString_->sz_ + sub.nStart_, sub.nLength_ );

   // where it was just copied from is not necessarily null-terminated,
   // so must add a null.

   sz_[ nCurrentLength_ ] = '\0';
}

const size_t nDefaultLengthForConversions = 15;

RWCString :: RWCString( const long lNumberToConvert ) {

   sz_ = (char*) malloc( ( nDefaultLengthForConversions + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::RWCString( const long lNumberToConvert ) amount: " << 
         nDefaultLengthForConversions + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   nMaxLength_ = nDefaultLengthForConversions;

   nCurrentLength_ = sprintf( sz_, "%d", lNumberToConvert );

   if ( nCurrentLength_ > nMaxLength_ ) {
      // big trouble--might not even make it here--might segmentation fault
      PANIC_OST( ost ) << "number was too large for memory in RWCString::RWCString( const long " << lNumberToConvert << ends;
      throw RWInternalError( ost.str() );
   }

}




RWCString :: RWCString( const double dNumberToConvert, const int nPrecision ) {

   sz_ = (char*) malloc( ( nDefaultLengthForConversions + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << 
         "cannot allocate enough memory for RWCString::RWCString( const double " << 
         dNumberToConvert << ", const int " << nPrecision <<
         " ) amount: " << 
         nDefaultLengthForConversions + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   nMaxLength_ = nDefaultLengthForConversions;

   char szFormat[100];
   sprintf( szFormat, "%%%dg", nPrecision );
   nCurrentLength_ = sprintf( sz_, szFormat, dNumberToConvert );

   if ( nCurrentLength_ > nMaxLength_ ) {
      // big trouble--might not even make it here--might segmentation fault
      PANIC_OST( ost ) << 
         "number was too large for memory in RWCString::RWCString( const double " << 
         dNumberToConvert << ", const int " << nPrecision << 
         " but nMaxLength_ = " << nMaxLength_ << ends;

      throw RWInternalError( ost.str() );
   }
}


void RWCString :: operator=( char* sz ) {

   int nNewLength = strlen( sz );

   if ( nNewLength == 0 ) {
      if ( sz_ )
         sz_[0] = '\0';

      nCurrentLength_ = 0;
      return;
   }

   if ( nMaxLength_ < nNewLength + 1 ) {
      
      free( sz_ );
      nMaxLength_ = (int) ( nNewLength * fDefaultInitialAdditionalFactor );

      sz_ = (char*) malloc( (nMaxLength_ + 1 ) * sizeof( char ) );
      if ( !sz_ ) {
         PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::operator=( char* sz ), amount: " << nMaxLength_ + 1 << ends;
         throw RWInternalError( ost.str() );
      }
   }
         
   // +1 so that null byte is also copied
   memcpy( sz_, sz, nNewLength + 1 );

   nCurrentLength_ = nNewLength;
}

      
char RWCString :: cGetLastChar( const int nChar ) {

   // this is to be like perl, where -1 is the last char, -2 is the
   // next to last, etc.

   // 0               1        2          ... length-2  length-1
   //-length    -(length-1)  -(length-2)  ...    -2       -1

   //   assert( -nCurrentLength_ <= nChar && nChar <= -1 );

   int nCharPos = nCurrentLength_ + nChar;
   assert( 0 <= nCharPos && nCharPos < nCurrentLength_ );
   return( sz_[ nCharPos ] );
}



void RWCString :: operator=( const RWCString& so ) {
   // special case in which the r-value has length 0
   if ( so.nCurrentLength_ == 0 ) {
      if ( sz_ )
         sz_[0] = '\0';

      nCurrentLength_ = 0;
      return;
   }


   if ( nMaxLength_ < ( so.nCurrentLength_ + 1)  ) {
      free( sz_ );
      nMaxLength_ = 
         (int) (so.nCurrentLength_ * fDefaultInitialAdditionalFactor);

      sz_ = (char*) malloc( (nMaxLength_ + 1 ) * sizeof( char ) );
      if ( !sz_ ) {
         PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::operator=( const RWCString& so ), amount: " << nMaxLength_ + 1 << ends;
         throw RWInternalError( ost.str() );
      }
   }

   // + 1 so that null byte is also copied
   memcpy( sz_, so.sz_, so.nCurrentLength_ + 1 );

   nCurrentLength_ = so.nCurrentLength_;
}



RWCString& RWCString :: operator=( const RWCSubString& sub ) {
   // special case when setting the string to null
   if ( sub.nLength_ == 0 ) {
      nCurrentLength_ = 0;
      if ( sz_ )
         sz_[0] = '\0';
      return( *this );
   }

   if ( nMaxLength_ < sub.nLength_ ) {
      free( sz_ );
      nMaxLength_ = 
         (int) (sub.nLength_ * fDefaultInitialAdditionalFactor);
      
      sz_ = (char*) malloc( (nMaxLength_ + 1 ) * sizeof( char ) );
      if ( !sz_ ) {
         PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::operator=( const RWCSubString& sub ), amount: " << nMaxLength_ + 1 << ends;
         throw RWInternalError( ost.str() );
      }
   }

   nCurrentLength_ = sub.nLength_;

   memcpy( sz_, sub.pRWCString_->sz_ + sub.nStart_, nCurrentLength_ );

   // where it was just copied from is not necessarily null-terminated,
   // so must add a null

   sz_[ nCurrentLength_ ] = '\0';
   return( *this );
}





// RWCString& RWCString :: operator+=( const RWCString& so ) {
void RWCString :: operator+=( const RWCString& so ) {
   
   // special case.  This is not just to improve performance.  It may be
   // that the string being added has sz_ == NULL, in which case 
   // the memcpy below might segmentation fault.  It both strings are 
   // null, then the sz_[0] = '\0' below would segmentation fault.
   
   if ( so.nCurrentLength_ == 0 )
      return;


   int nNewStringLength = nCurrentLength_ + so.nCurrentLength_;
   if ( nNewStringLength > nMaxLength_ ) {
      int nNewMaxLength = 
         (int) (nNewStringLength * fDefaultResizeIncrement);
      increaseMaxLength( nNewMaxLength );
   }

   // there was a bug here--I tried to copy the null byte as well,
   // but if the source and destination were the same, then the
   // null byte had already been overwritten by the time memcpy got to
   // it.  So explicitly set the null byte.

   memcpy( sz_ + nCurrentLength_, so.sz_, so.nCurrentLength_ );
   nCurrentLength_ += so.nCurrentLength_;
   sz_[ nCurrentLength_ ] = '\0';
   //   return( *this );
}


RWCString operator+( const RWCString& so1, const RWCString& so2 ) {
   int nNewStringLength = so1.nCurrentLength_ + so2.nCurrentLength_;
   RWCString soNew( (size_t) ( nNewStringLength * fDefaultInitialAdditionalFactor + 1 ) );
   soNew = so1;
   soNew += so2;
   return( soNew );
}



RWCString operator+( const RWCString& so1, const char* sz2 ) {
   
   RWCString so2( sz2 );
   return( so1 + so2 );
}


RWCString operator+( const char* sz1, const RWCString& so2 ) {
   RWCString so1( sz1 );
   return( so1 + so2 );
}



RWCString operator+( const RWCString& so1, const int n ) {
   RWCString so2( (long) n );
   return( so1 + so2 );
}


char RWCString :: operator[]( const size_t nPos ) const {
   if ( nPos >= nCurrentLength_ ) {
      PANIC_OST( ost ) << "past end of array in RWCString[].  array length = " << 
         nCurrentLength_ << 
         " position requested: " << 
         nPos << ends;
      throw RWInternalError( ost.str() );
   }

   return( sz_[ nPos ] );
}


char& RWCString :: operator[]( size_t nPos ) {
   if ( nPos >= nCurrentLength_ ) {
      PANIC_OST( ost ) << "past end of array in RWCString[]&.  array length = " << 
         nCurrentLength_ << 
         " position requested: " << 
         nPos << ends;
      throw RWInternalError( ost.str() );
   }

   return( sz_[ nPos ] );
}

#ifdef INT_CHAR_OPERATOR

char RWCString :: operator[]( const int nPos ) const {
  return (*this)[ (size_t) nPos ];
}

char& RWCString :: operator[]( int nPos ) {
  return (*this)[ (size_t) nPos ];
}

#endif


const RWCSubString RWCString :: operator()( size_t nStart, size_t nLength ) const {
   
   if ( nStart + nLength > nCurrentLength_ ) {
      PANIC_OST( ost ) << " RWCString :: operator()( size_t nStart, size_t nLength ) had out of bounds arguments.  nStart = " << nStart << " and nLength = " <<
         nLength << " while current length = " << nCurrentLength_ << ends;
      throw RWInternalError( ost.str() );
   }

   RWCSubString sub( (RWCString*) this, nStart, nLength );

   return( sub );
}





RWCString :: RWCString( const size_t nMaxLength ) {
   nMaxLength_ = nMaxLength;
   sz_ = (char*) malloc( ( nMaxLength_ + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::RWCString( size_t nMaxLength).  amount requested: " << nMaxLength_ + 1 << ends;
      throw RWInternalError( ost.str() );
   }

   sz_[0] = '\0';
   nCurrentLength_ = 0;
}



ostream& operator<<( ostream& oStream, const RWCString& so ) {
   if ( so.sz_ )
      return( oStream << so.sz_ );
   else
      return( oStream );
}   


istream& operator>>( istream& iStream, RWCString& so ) {
   return( so.readToken( iStream ) );
}

istream& RWCString :: readToken( istream& iStream ) {
   
   (*this) = "";

   // get rid of leading whitespace

   char c;

   while( isspace( c = iStream.peek() ) && iStream.good() &&
          c != EOF ) {
      iStream.get();
   }

   while( !isspace( c = iStream.peek())  && iStream.good() &&
          c != EOF ) {
      (*this) += c;
   }
   return( iStream );
}
   



istream& RWCString :: readToDelim( istream& iStream, char cDelim ) {

   (*this) = "";

   char c;
   
   while( ( c = iStream.peek() ) && iStream.good() && c!= EOF ) {
      iStream.get();
      if ( c == cDelim ) break;
      else
         (*this) += c;
      // in case you are interested, the compiler handles this by
      // doing a RWCString::RWCString( const char c ) for c, and then
      // using operator+=( const RWCString& so )
   }

   if ( c == EOF ) {
      iStream.get();
      // for some reason, even though iStream.eof() returns true at this point,
      // if ( iStream ) still returns true, until I call get again.
      iStream.get();
   }

   return( iStream );
}


istream& RWCString :: readLine( istream& iStream, bool bSkipWhite ) {
   // I'm not implementing the bSkipWhite

   return( readToDelim( iStream ) );
}


void RWCString :: removeAllCommas() {

   if ( nCurrentLength_ == 0 )
      return;

   for( int nPos = nCurrentLength_ - 1;
        nPos >= 0; --nPos ) {
      if ( sz_[ nPos ] == ',' ) {
         remove( nPos, 1 );
      }
   }
}


void RWCString :: removeSingleCharacterAllOccurrences( char cToRemove ) {

   if ( nCurrentLength_ == 0 )
      return;

   for( int nPos = nCurrentLength_ - 1;
        nPos >= 0; --nPos ) {
      if ( sz_[ nPos ] == cToRemove ) {
         remove( nPos, 1 );
      }
   }
}



RWCString :: operator char*() const {
   return( data() );
}




RWCString :: operator const char*() const {
   return( data() );
}
   

      
RWCSubString RWCString :: operator()( const RWCRegexp& re ) const {

   // special case for null strings
   if ( !sz_ ) {
      RWCSubString sub( (RWCString*) this, 0, 0 );
      return( sub );
   }


   regmatch_t regm;

   // just look for the first match
   int nError = regexec( &re.regexPreg_, sz_, 1, &regm, 0 );

   if ( nError == 0 ) {

      RWCSubString sub( (RWCString*) this, (size_t) regm.rm_so, 
                        (size_t) ( regm.rm_eo - regm.rm_so ) );
      return( sub );
   }
   else if ( nError == REG_NOMATCH ) {
      RWCSubString sub( (RWCString*) this, 0, 0 );
      return( sub );
   }
   else {

      PANIC_OST( ost ) << "RWCString( RWCRegexp) failed in regexec. ";

#define nBUFFERSIZE 2000
      char szErrorMessage[ nBUFFERSIZE ];
      regerror( nError, &re.regexPreg_, szErrorMessage, nBUFFERSIZE );

      ost << szErrorMessage << ends;

      throw RWInternalError( ost.str() );
   }
}
   

void RWCString :: toLower() {
   for( int nPos = 0; nPos < nCurrentLength_; ++nPos )
      sz_[nPos] = tolower( sz_[nPos] );
}


void RWCString :: toUpper() {
   for( int nPos = 0; nPos < nCurrentLength_; ++nPos )
      sz_[nPos] = toupper( sz_[nPos] );
}

RWCString RWCString :: returnToLower() {
   RWCString so( *this );
   so.toLower();
   return( so );
}


RWCString RWCString :: returnToUpper() {
   RWCString so( *this );
   so.toUpper();
   return( so );
}


RWCSubString RWCString :: strip( stripType s, char c ) {

   // handle special case of null string
   if ( nCurrentLength_ == 0 )
      return( RWCSubString( NULL, 0, 0 ) );




   int nStart = 0;
   int nEnd = nCurrentLength_ - 1;

   
   if ( s & TRAILING ) {
      int nPos;
      for( nPos = nCurrentLength_ - 1; 
           nPos >= 0 && ( sz_[ nPos ] == c );
           --nPos );

      nEnd = nPos;
   }


   if ( s & LEADING ) {
      int nPos;
      for( nPos = 0; nPos < nCurrentLength_ && ( sz_[ nPos ] == c );
           ++nPos );

      nStart = nPos;
   }


   if ( nStart > nEnd ) 
      return( RWCSubString( NULL, 0, 0 ) );
   else {
      return( RWCSubString( this, nStart, nEnd - nStart + 1 ) );
   }
}


void RWCString :: stripTrailingWhitespaceFast() {

   if ( nCurrentLength_ == 0 )
      return;

   int nPos;
   for( nPos = nCurrentLength_ - 1;
        nPos >= 0 && isspace( sz_[ nPos ] );
        --nPos );

   // loop above can end in 2 ways:  either nPos == -1, or
   // else nPos points to a non-space character.  If nPos == -1,
   // then all the string was whitespace, so put a null at 0 and 
   // the new string is length 0.  If the loop ended at a non-space
   // character, then put a null at the next character and the string
   // is the length of characters.  Is ++nPos within the space allocated
   // for sz_?  If the loop terminated when nPos == -1, then ++nPos is 0,
   // but since nCurrentLength_ originally was > 0 (since we returned if it 
   // was 0), sz_[0] will not give segmentation fault.  If we ended on 
   // a non-space character, then ++nPos could be the previous null character
   // or a previous blank character.  In either case, sz_[ nPos] will
   // not give a segmentation fault.

   ++nPos;
   sz_[ nPos ] = '\0';
   nCurrentLength_ = nPos;
}


void RWCString :: stripAllWhitespaceIncludingInternal() {

   if ( nCurrentLength_ == 0 )
      return;

   for( int nPos = nCurrentLength_ - 1;
        nPos >= 0; --nPos ) {
      if ( isspace( sz_[ nPos ] ) ) {
         remove( nPos, 1 );
      }
   }
}


void RWCString :: stripAllWhitespaceExceptInternal() {

   stripTrailingWhitespaceFast();

   (*this) = stripWhitespace( LEADING );
}




// I tried to make this const, but the problem is that it is theoretically
// possible for someone to go:
// a.stripWhitespace() = "new"
// which would violate the const


RWCSubString RWCString :: stripWhitespace( stripType s ) {

   // handle special case of null string

   if ( nCurrentLength_ == 0 )
      return( RWCSubString( NULL, 0, 0 ) );

   int nStart = 0;
   int nEnd = nCurrentLength_ - 1;
   
   if ( s & TRAILING ) {
      int nPos;
      for( nPos = nCurrentLength_ - 1;
           nPos >= 0 && isspace( sz_[ nPos ] );
           --nPos );

      nEnd = nPos;
   }


   if ( s & LEADING ) {
      int nPos;
      for( nPos = 0; 
           nPos < nCurrentLength_ && isspace( sz_[ nPos ] );
           ++nPos );

      nStart = nPos;
   }

   // if the string is all whitespace, then nStart > nEnd 

   if ( nStart > nEnd )
      return( RWCSubString( NULL, 0, 0 ) );
   else
      return( RWCSubString( this, nStart, nEnd - nStart + 1 ) );
}





bool operator==( const RWCString& so1, const RWCString& so2 ) {

   if ( so1.nCurrentLength_ != so2.nCurrentLength_ )
      return( false );
   else {
      // to cover special case in which either is a null string
      if ( so1.nCurrentLength_ == 0 ) 
         return( true );

      if ( memcmp( so1.sz_, so2.sz_, so1.nCurrentLength_ ) == 0 )
         return( true );
      else 
         return( false );
   }
}


bool operator==( const RWCString& so1, const char* sz2 ) {
   // handle special case of null string
   if ( !so1.sz_ ) {
      if ( sz2[0] == 0 )
         return( true );
      else
         return( false );
   }

   if ( strcmp( so1.sz_, sz2 ) == 0 ) 
      return( true );
   else
      return( false );
}


bool operator==( const char* sz1, const RWCString& so2 ) {
   return( so2 == sz1 );
}


bool operator%( const RWCString& so1, const RWCString& so2 ) {

   if ( so1.nCurrentLength_ != so2.nCurrentLength_ )
      return false;
   else {

      // to cover special case in which either is a null string
      if ( so1.nCurrentLength_ == 0 )
         return true;

      if ( strncasecmp( so1.sz_, so2.sz_, so1.nCurrentLength_ ) == 0 )
         return true;
      else
         return false;
   }
}


bool operator%( const RWCString& so1, const char* sz2 ) {

   // handle special case of null string
   if ( so1.nCurrentLength_ == 0 ) {
      if ( sz2[0] == 0 )
         return true;
      else
         return false;
   }

   if ( strcasecmp( so1.sz_, sz2 ) == 0 )
      return true;
   else
      return false;
}


bool operator%( const char* sz1, const RWCString& so2 ) {
   return( so2 % sz1 );
}


bool operator!=( const RWCString& so1, const RWCString& so2 ) {
   return( !( so1 == so2 ) );
}


bool operator!=( const RWCString& so1, const char* sz2 ) {
   return( ! ( so1 == sz2 ) );
}

bool operator!=( const char* sz1, const RWCString& so2 ) {
   return( ! ( sz1 == so2 ) );
}







bool operator<( const RWCString& so1, const RWCString& so2 ) {
   
   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 || so2.nCurrentLength_ == 0 ) {
      if ( so2.nCurrentLength_ != 0 )
         return( true );
      else
         return( false );
   }

   // warning--this method of comparing cannot handle imbedded nulls
   if ( strcmp( so1.sz_, so2.sz_ ) < 0 )
      return( true );
   else
      return( false );
}


bool operator<( const char* sz1, const RWCString& so2 ) {

   // handle possibly null strings
   if ( so2.nCurrentLength_ == 0 )
      return( false );

   if ( strcmp( sz1, so2.sz_ ) < 0 )
      return( true );
   else
      return( false );
}


bool operator<( const RWCString& so1, const char* sz2 ) {

   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 ) {
      if ( sz2[0] )
         return( true );
      else
         return( false );
   }

   if ( strcmp( so1.sz_, sz2 ) < 0 )
      return( true );
   else
      return( false );
}


bool operator>( const RWCString& so1, const RWCString& so2 ) {

   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 || so2.nCurrentLength_ == 0 ) {
      if ( so1.nCurrentLength_ != 0 )
         return( true );
      else
         return( false );
   }

   // warning--this method of comparing cannot handle embedded nulls
   if ( strcmp( so1.sz_, so2.sz_ ) > 0 ) 
      return( true );
   else
      return( false );
}

bool operator>( const char* sz1, const RWCString& so2 ) {
   

   // handle possibly null strings
   if ( so2.nCurrentLength_ == 0 ) {
      if ( sz1[0] )
         return( true );
      else
         return( false );
   }



   if ( strcmp( sz1, so2.sz_ ) > 0 )
      return( true );
   else
      return( false );
}


bool operator>( const RWCString& so1, const char* sz2 ) {
   
   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 ) 
      return( false );
      

   if ( strcmp( so1.sz_, sz2 ) > 0 )
      return( true );
   else
      return( false );
}



bool operator<=( const RWCString& so1, const RWCString& so2 ) {
   
   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 || so2.nCurrentLength_ == 0 ) {
      if ( so1.nCurrentLength_ == 0 ) 
         return( true );
      else
         return( false );
   }



   if ( strcmp( so1.sz_, so2.sz_ ) <= 0 )
      return( true );
   else
      return( false );
}



bool operator<=( const char* sz1, const RWCString& so2 ) {

   // handle possibly null strings
   if ( so2.nCurrentLength_ == 0 ) {
      if ( sz1[0] )
         return( false );
      else
         return( true );
   }


   if ( strcmp( sz1, so2.sz_ ) <= 0 )
      return( true );
   else
      return( false );
}



bool operator<=( const RWCString& so1, const char* sz2 ) {

   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 )
      return( true );




   if ( strcmp( so1.sz_, sz2 ) <= 0 )
      return( true );
   else
      return( false );
}



bool operator>=( const RWCString& so1, const RWCString& so2 ) {

   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 || so2.nCurrentLength_ == 0 ) {
      if ( so2.nCurrentLength_ == 0 )
         return( true );
      else
         return( false );
   }

   if ( strcmp( so1.sz_, so2.sz_ ) >= 0 )
      return( true );
   else
      return( false );
}



bool operator>=( const char* sz1, const RWCString& so2 ) {

   // handle possibly null strings
   if ( so2.nCurrentLength_ == 0 ) 
      return( true );


   if ( strcmp( sz1, so2.sz_ ) >= 0 )
      return( true );
   else
      return( false );
}



bool operator>=( const RWCString& so1, const char* sz2 ) {

   // handle possibly null strings
   if ( so1.nCurrentLength_ == 0 ) {
      if ( sz2[0] )
         return( false );
      else
         return( true );
   }


   if ( strcmp( so1.sz_, sz2 ) >= 0 )
      return( true );
   else
      return( false );
}






size_t RWCString :: index( const char* szPattern, const size_t nStartPosition )
 const  {


   int nPatternLength = strlen( szPattern );

   return( index( szPattern, nPatternLength, nStartPosition ) );
}


size_t RWCString :: index( const char* szPattern, 
                           const size_t nPatternLength,
                           const size_t nStartPosition ) const {

   // What shall be our definition of a match if the pattern is null?
   // memcmp says that it is found at the first position
   
   // this isn't needed because size_t assures that the number
   // is >= 0
   //   assert( nStartPosition >= 0 );
   
   //                      nCurrentLength_ - 1
   //                        |
   // 0                      v 
   // ------------------------
   // ---                     ^ 
   //  ---                    nCurrentLength_
   //   ---
   // . 
   //                   ---  
   //                    ---
   //                      --- 
   //                      ^
   //                      nRightMostPlaceToLook

   // handle case of null string.
   if ( nCurrentLength_ == 0 )
      return( RW_NPOS );

   int nRightMostPlaceToLook = nCurrentLength_ - nPatternLength;

   if ( nRightMostPlaceToLook < 0 ) 
      return( RW_NPOS );

   for( int nPos = nStartPosition; nPos <= nRightMostPlaceToLook; ++nPos ) {

      if (memcmp( sz_ + nPos, szPattern, nPatternLength ) == 0 )
         return( nPos );

   }

   return( RW_NPOS );
}



size_t RWCString :: index( const RWCString& soPattern, 
                           const size_t nStartPosition ) const {

   return( index( soPattern.sz_, soPattern.nCurrentLength_, nStartPosition ) );
}


size_t RWCString :: index2( const RWCString& soPattern,
                           const size_t nStartPosition,
                           const char cCaseSensitivity ) const {

   if ( cCaseSensitivity == cCaseInsensitive ) {

      RWCString soPattern2 = soPattern;
      soPattern2.toLower();
      RWCString soThisString = (*this);
      soThisString.toLower();

      return( soThisString.index( soPattern2, nStartPosition ) );
   }
   else {
      return( index( soPattern, nStartPosition ) );
   }
}



bool RWCString :: bContains( const RWCRegexp& regPattern ) const {
   
   // special case for null strings
   if ( !sz_ )
      return( false );

   regmatch_t regm;

   int nError = regexec( &regPattern.regexPreg_, sz_, 1, &regm, 0 );
   if ( nError == 0 )
      return true;
   else if ( nError == REG_NOMATCH )
      return false;
   else {
      char szErrorMessage[ nBUFFERSIZE ];
      regerror( nError, &regPattern.regexPreg_, szErrorMessage, nBUFFERSIZE );
      RWCString soErrorMessage = "RWCString::bContains RWCString = ";
      soErrorMessage += *this;
      soErrorMessage += " error: ";
      soErrorMessage += szErrorMessage;
      THROW_ERROR( soErrorMessage );
   }
}
         

bool RWCString :: bContains( const RWCString& soPattern, 
                             const char cCaseSensitivity ) const {


   if ( index2( soPattern, 0, cCaseSensitivity ) == RW_NPOS )
      return false;
   else
      return true;
}



bool RWCString :: bContains( const char* sz ) const {

   return( bContains( RWCString( sz ) ) );
}


bool RWCString :: bContainsChar( const char c ) const {
   
   for( int n = 0; n < nCurrentLength_; ++n ) {
      if ( sz_[n] == c ) 
         return true;
   }

   return false;
}



size_t RWCString :: index( const RWCRegexp& regPattern ) const {

   // special case for null strings
   if ( !sz_ ) 
      return( RW_NPOS );

   regmatch_t regm;

   // just look for the first match
   int nError = regexec( &regPattern.regexPreg_, sz_, 1, &regm, 0 );

   if ( nError == 0 ) 
      return( regm.rm_so );
   else if ( nError == REG_NOMATCH )
      return( RW_NPOS );
   else {

      PANIC_OST( ost ) << "RWCString::index( const RWCRegexp& regPattern ) gave a strange error from regexec: ";
      char szErrorMessage[ nBUFFERSIZE ];
      regerror( nError, &regPattern.regexPreg_, szErrorMessage, nBUFFERSIZE );

      ost << szErrorMessage << ends;
      throw RWInternalError( ost.str() );
   }
}



// remove from nPos to the end of the string
RWCString& RWCString :: remove( size_t nPos ) {

   // I decided to do nothing rather than throw an exception if
   // trying to remove past the end of the string.
   if ( nPos >= nCurrentLength_ ) return( *this );


//    if ( nPos >= nCurrentLength_ ) {
//       PANIC_OST( ost ) << "RWCString::remove failed because nPos = " <<
//          nPos << " while nCurrentLength_ = " << nCurrentLength_ << ends;
//       throw RWInternalError( ost.str() );
//    }
      
   nCurrentLength_ = nPos;
   sz_[ nPos ] = '\0';
   return( *this );
}



RWCString& RWCString :: remove( size_t nPositionToStartRemoving, 
                                size_t nLengthToRemove ) {

   if ( nPositionToStartRemoving >= nCurrentLength_ ) return( *this );

   // 2 cases:
   //   1) remove to end of the string
   //        ------------
   //              ^.....
   //   2) remove an internal portion of the string
   //        ------------
   //              ^..
   
   if ( nPositionToStartRemoving + nLengthToRemove < nCurrentLength_ ) {
      // this is case 2


      // -------------
      //       ^..
      //       ^... (move)
      //        ^... (move)
      //         ^... (move)
      //          ^... (move)
      //           ^... (null to move)

      // The last character to move is the null at the end, which is at
      // nCurrentLength_.  That is, when
      // nPos + nLengthToRemove == nCurrentLength_
      // So stop when nPos + nLengthToRemove > nCurrentLength_
      // and continue when nPos + nLengthToRemove <= nCurrentLength_
      // equivalently continue when nPos <= nCurrentLength_ - nLengthToRemove

      for( int nPos = nPositionToStartRemoving; 
           nPos <= nCurrentLength_ - nLengthToRemove;
           ++nPos ) {
         sz_[ nPos ] = sz_[ nPos + nLengthToRemove ];
      }

      nCurrentLength_ -= nLengthToRemove;

      return( *this );
   }
   else {
      return( remove( nPositionToStartRemoving ) );
   }
}


void RWCString :: removeLastChar() {
   if ( nCurrentLength_ == 0 ) return;

   --nCurrentLength_;
   sz_[ nCurrentLength_ ] = '\0';
}


RWCString RWCString :: soRemoveFinalBases( const int nBasesToRemove ) {

   assert( nBasesToRemove >= 0 );

   int nNewLength = nCurrentLength_ - nBasesToRemove;

   // no need because subsumed by the check below
   // if ( nCurrentLength_ == 0 ) return soEmptyString;
   
   if ( nNewLength <= 0 ) return soEmptyString;

   return (*this)( 0, nNewLength );
}
   


void RWCString :: append( const char* szStringToAppend ) {
   RWCString soStringToAppend( szStringToAppend );
   (*this) += soStringToAppend;
}


void RWCString :: append( const RWCString& soStringToAppend ) {
   (*this) += soStringToAppend;
}


void RWCString :: append( const char cCharacterToAppend ) {
   // written out to be fast--this could have been done by creating a 
   // length 1 RWCString and += it to this one, but that would have
   // been less efficient

   int nNewStringLength = nCurrentLength_ + 1;
   if ( nNewStringLength > nMaxLength_ ) {
      int nNewMaxLength = 
         (int) ( nNewStringLength * fDefaultResizeIncrement);
      increaseMaxLength( nNewMaxLength );
   }

   sz_[ nCurrentLength_ ] = cCharacterToAppend;
   ++nCurrentLength_;
   sz_[ nCurrentLength_ ] = '\0';
}



void RWCString :: appendLotsOfCopies( const char cCharacterToAppend,
                                      const size_t nNumberOfCopies ) {

   int nNewStringLength = nCurrentLength_ + nNumberOfCopies + 1;
   if ( nNewStringLength > nMaxLength_ ) {
      int nNewMaxLength = 
         (int) ( nNewStringLength * fDefaultResizeIncrement );
      increaseMaxLength( nNewMaxLength );
   }

   for( int n = 0; n < nNumberOfCopies; ++n ) {
      sz_[ nCurrentLength_ + n ] = cCharacterToAppend;
   }

   nCurrentLength_ += nNumberOfCopies;
   sz_[ nCurrentLength_ ] = '\0';
}



size_t RWCString :: first( char cCharToFind ) const {

   // special case for null strings
   if ( nCurrentLength_ == 0 )
      return( RW_NPOS );
   
   char* pChar = strchr( sz_, cCharToFind );
   if ( pChar ) 
      return( pChar - sz_ );
   else
      return( RW_NPOS );
}


size_t RWCString :: last( char cCharToFind ) const {

   // special case for null strings
   if ( nCurrentLength_ == 0 )
      return( RW_NPOS );

   // I believe it is less efficient to use strrchr since that 
   // first has to find the end of the string, and we already
   // know where the end of the string is.
   
   for( int nPos = nCurrentLength_ - 1; nPos >= 0; --nPos ) {
      if ( sz_[ nPos ] == cCharToFind )
         return( nPos );
   }

   return( RW_NPOS );
}


void RWCString :: increaseMaxLength( const size_t nRequestedMaxLength ) {

   // this allows the size to either increase or decrease, but it can't
   // decrease below the current size
   if ( nRequestedMaxLength < nCurrentLength_ ) {
      PANIC_OST( ost ) << "in RWCString::increaseMaxLength, cannot decrease memory to " 
                       << nRequestedMaxLength << 
         " since this is below the current size of " <<
         nCurrentLength_ << ends;
      throw RWInternalError( ost.str() );
   }

   sz_ = (char*) realloc( sz_, 
                             ( nRequestedMaxLength + 1 ) * sizeof( char ) );
   if ( !sz_ ) {
      PANIC_OST( ost ) << "cannot allocate enough memory for RWCString::capacity( const size_t nRequestedMaxLength ), amount: " << 
         nRequestedMaxLength + 1 << ends;

      throw RWInternalError( ost.str() );
   }
      
   nMaxLength_ = nRequestedMaxLength;
}


void RWCString :: increaseButNotDecreaseMaxLength( const size_t 
                                                   nRequestedMaxLength ) {
   if ( nRequestedMaxLength <= nMaxLength_ )
      return;

   increaseMaxLength( nRequestedMaxLength );
}



void RWCString :: increaseMaxLengthIfNecessary( const size_t nExtraRoomNeeded ) {

   // the +1 is to have room for a null byte at the end of the string
   int nMaxLengthNeeded = nCurrentLength_ + nExtraRoomNeeded + 1;

   if ( nMaxLengthNeeded > nMaxLength_ )
      increaseMaxLength( nMaxLengthNeeded );

}


void RWCString :: insert( size_t nInsertBeforeThisPosition, const char* szToInsert ) {
   
   // shall I allow inserting just after the last character?
   if ( nInsertBeforeThisPosition > nCurrentLength_ ) {
      PANIC_OST( ost ) << " RWCString::insert(" << nInsertBeforeThisPosition << ", " <<
         szToInsert << ") gave error since nCurrentLength_ is " <<
         nCurrentLength_ << " for string " << (*this) << ends;
      throw RWInternalError( ost.str() );
   }

   int nLengthToInsert = strlen( szToInsert );
   if ( nLengthToInsert == 0 ) return;

   int nNewStringLength = nCurrentLength_ + nLengthToInsert;
   if ( nNewStringLength > nMaxLength_ ) {
      int nNewMaxLength = 
         (int) (nNewStringLength * fDefaultResizeIncrement);
      increaseMaxLength( nNewMaxLength );
   }
   
   // move bytes over
   // ----abcdef  (old string)
   //     ^--    (string to insert)
   //        abcdef 
   //     ^ nInsertBeforeThisPosition
   //             ^ nNewStringLength - 1
   //        ^ nInsertBeforeThisPosition + nLengthToInsert

   for( int nPos = nNewStringLength - 1; 
        nPos >= nInsertBeforeThisPosition + nLengthToInsert;
        --nPos )
      sz_[ nPos ] = sz_[ nPos - nLengthToInsert ];

   memcpy( sz_ + nInsertBeforeThisPosition, szToInsert, nLengthToInsert );
      
   nCurrentLength_ = nNewStringLength;

   sz_[ nCurrentLength_ ] = '\0';
}


// void RWCString :: resize( const size_t nRequestedMaxLength ) {
   
//    increaseMaxLength( nRequestedMaxLength );

//    // I don't see any reason to put in pads--we are just going to 
//    // overwrite them anyway

//    nCurrentLength_ = nMaxLength_;
// }


RWCString RWCString :: soPadOrTruncate( const size_t nLength ) const {
   
   if ( nCurrentLength_ >= nLength ) {
      if ( nLength > 3 ) 
         return( (*this)( 0, nLength - 3 ) + "..." );
      else
         return( (*this)( 0, nLength ) );
   }
   else {
      int nPadsToAdd = nLength - nCurrentLength_;

      RWCString soPads( ' ', (size_t) nPadsToAdd );
      return( (*this) + soPads );
   }
}



void RWCString :: pad( const size_t nLength ) {
   
   if ( nCurrentLength_ < nLength ) {
      int nPadsToAdd = nLength - nCurrentLength_;

      RWCString soPads( ' ', (size_t) nPadsToAdd );
      append( soPads );
   }

}


void RWCString :: padOnLeft( const size_t nLength ) {

   if ( nCurrentLength_ < nLength ) {
      int nPadsToAdd = nLength - nCurrentLength_;

      RWCString soPads( ' ', (size_t) nPadsToAdd );
      RWCString soTemp = soPads + (*this);
      (*this) = soTemp;
   }
}



void RWCString :: truncate( const size_t nLength ) {

   if ( nCurrentLength_ <= nLength )
      return;
   
   nCurrentLength_ = nLength;
   sz_[ nCurrentLength_ ] = '\0';
}



RWCString RWCString :: soGetRestOfString( const size_t nStart ) const {

   if ( nStart < nCurrentLength_ )
      return( (*this)( nStart, nCurrentLength_ - nStart ) );
   else
      return( RWCSubString( NULL, 0, 0 ) );
}


RWCString RWCString :: soGetLastPart( const size_t nNumberOfChars ) const {

   if ( nNumberOfChars >= nCurrentLength_ )
      return( (*this) );
   else {

      // last char is nCurrentLength_ - 1
      // start at last char - nNumberOfChars + 1 which is 
      // the expression below.

      int nStart = nCurrentLength_ - nNumberOfChars;

      return( soGetRestOfString( nStart ) );
   }
}
      


void RWCString :: keepOnlyLastPartOfString( const size_t nNumberOfChars ) {

   if ( nNumberOfChars >= nCurrentLength_ )
      nCurrentLength_ = 0;
   else {
      // this is made with high efficiency in mind--not
      // to avoid rewriting similar code

      int nStart = nCurrentLength_ - nNumberOfChars;

      if ( nStart == 0 ) return;

      // the reason for the +1 is so that the trailing null will also
      // be moved
      memcpy( sz_, sz_ + nStart, nNumberOfChars + 1 );

      nCurrentLength_ = nNumberOfChars;
   }
}




void RWCString :: appendFormat( const char* szFormat, ... ) {

   va_list args;
   va_start( args, szFormat );
   int nLengthAdded = vsprintf( sz_ + nCurrentLength_, 
                                szFormat,
                                args );
   va_end( args );

   nCurrentLength_ += nLengthAdded;

   if ( nCurrentLength_ > nMaxLength_ ) {
      THROW_ERROR( "appendFormat nCurrentLength_ > nMaxLength_" );
   }
}


bool RWCString :: bStartsWith( const RWCString& soStartsWithThis ) {
   
   if ( length() < soStartsWithThis.length()  )
      return( false );

   for( int nPos = 0; nPos < soStartsWithThis.length(); ++nPos ) {
      if ( (*this)[ nPos ] != soStartsWithThis[ nPos ] )
         return( false );
   }

   return( true );
}

bool RWCString :: bStartsWithAndRemove( const RWCString& soStartsWithThis ) {

   if ( !bStartsWith( soStartsWithThis ) )
      return false;

   keepOnlyLastPartOfString( nCurrentLength_ - soStartsWithThis.length() );
   
   return true;
}



bool RWCString :: bEndsWith( const RWCString& soEndsWithThis ) {

   if ( length() < soEndsWithThis.length() ) 
      return( false );

   for( int nPos1 = length() - 1, nPos2 = soEndsWithThis.length() - 1;
        nPos2 >= 0; --nPos1, --nPos2 ) {
      if ( (*this)[ nPos1 ] != soEndsWithThis[ nPos2 ] )
         return( false );
   }

   return( true );
}


bool RWCString :: bEndsWithAndRemove( const RWCString& soEndsWithThis ) {
   
   if ( !bEndsWith( soEndsWithThis ) ) 
      return false;

   // if reached here, it ends with soEndsWithThis.  What position is 
   // it found?

   // if this is "xxx:" (length 4) and soEndsWithThis is ":", it is
   // found at position 3, which is 4 - 1.
   
   int nPositionFound = nCurrentLength_ - soEndsWithThis.length();

   sz_[ nPositionFound ] = '\0';
   nCurrentLength_ = nPositionFound;

   return true;
}


bool RWCString :: bStartsWithCaseInsensitive( 
    const RWCString& soStartsWithThis ) const {

   if ( length() < soStartsWithThis.length() )
      return( false );

   for( int nPos = 0; nPos < soStartsWithThis.length(); ++nPos ) {
      if ( toupper( (*this)[ nPos ] ) != toupper( soStartsWithThis[ nPos ] ) )
           return( false );
   }


   return( true );
}





void RWCString :: getField( char* pCharacterField, const int nLength ) {
   
   increaseMaxLength( nLength + 1 );

   memcpy( sz_, pCharacterField, nLength );
   nCurrentLength_ = nLength + 1;  // note that this will include the
   // entire field, including any embedded nulls, in the string
   sz_[ nLength ] = '\0';

   
}



int nCompareRWCStringsForQSort( const RWCString* pString1, const RWCString* pString2 ) {

   // handle possibly null strings
   if ( pString1->nCurrentLength_ == 0 || pString2->nCurrentLength_ == 0 ) {
      if ( pString2->nCurrentLength_ != 0 )
         return -1;
      else {
         if ( pString1->nCurrentLength_ != 0 )
            return 1;
         else // both 0 length
            return 0;
      }
   }

   return strcmp( pString1->sz_, pString2->sz_ );
}


void RWCString :: reverse() {

   // last position in string:
   // 0  1  2  3  4  5  6  7  8
   // end of index that we want:
   // NA 0  0  1  1  2  2  3  3
   // length:
   // 1  2  3  4  5  6  7  8  9
   // length/2:
   // 0  1  1  2  2  3  3  4  4
   // length/2 - 1 and we've got it:
   // -1 0  0  1  1  2  2  3  3


   int nMaxIndex = nCurrentLength_ / 2 - 1;
   for( int n = 0; n <= nMaxIndex; ++n ) {
      char cTemp = sz_[n];
      sz_[n] = sz_[ nCurrentLength_ - 1 - n ];
      sz_[ nCurrentLength_ - 1 - n ] = cTemp;
   }
}

   
void RWCString :: replace( const size_t nPositionToStartReplacing,
                           const RWCString& soReplacementString ) {

   // if position 0, length will be at least soReplacementString.length()
   // if position 1, length will be at least soReplacementString.length() + 1
   // etc.

   if ( soReplacementString.length() + nPositionToStartReplacing >
        nMaxLength_ ) {
      // not big enough to handle new length

      increaseMaxLength( soReplacementString.length() + nPositionToStartReplacing );
   }

   for( int n = 0; n < soReplacementString.length(); ++n ) {
      sz_[ n + nPositionToStartReplacing ] = soReplacementString( n );
   }

   nCurrentLength_ = MAX( nCurrentLength_, 
                          nPositionToStartReplacing + 
                          soReplacementString.length() );

}



void RWCString :: removeFinalCR() {

   if ( nCurrentLength_ > 0 ) {
      if ( cGetLastChar() == '\n' ) {
         removeLastChar();
      }
   }

}


void RWCString :: setBaseUnsafe( const int nPos, const char c ) {
   sz_[ nPos ] = c;
}



int RWCString :: nGetNumberOfLinesOfText() {

   return( nGetNumberOfCopiesOfChar('\n' ) + 1 );

}



int RWCString :: nGetNumberOfCopiesOfChar( const char c ) {

   int nCopies = 0;
   for( int n = 0; n < nCurrentLength_; ++n ) {
      if ( sz_[n] == c )
         ++nCopies;
   }

   return nCopies;
}

      
bool  RWCString :: bIsWhitespace() {
   
   if ( isNull() ) return true;

   for( int n = 0; n < nCurrentLength_; ++n ) {
      if ( ! isspace( sz_[n] ) ) return( false );
   }

   return( true );
}


// This differs from RWCTokenizer in several ways:
// 1)  It will only split on whitespace (but any whitespace)
// 2)  It can be given a position within the string to find
// a token--it need not start at the beginning.
// It is very efficient--good if the string is very large and 
// we are not interested in all tokens.

// can start pointing at whitespace.  Will end either pointing again
// to whitespace just after a token, or pointing after the last byte
// of the string (hence to the null).  If there are no tokens after
// the current position, it will return the empty string.

RWCString RWCString :: soGetNextToken( size_t& nCurrentStringPosition ) {

   if ( nCurrentStringPosition >= nCurrentLength_ )
      return( soEmptyString );

   // skip over leading whitespace
   while( nCurrentStringPosition < nCurrentLength_ &&
          isspace( sz_[ nCurrentStringPosition ] ) )
      ++nCurrentStringPosition;

   if ( nCurrentStringPosition >= nCurrentLength_ )
      return( soEmptyString );

   RWCString soToken;
   while( nCurrentStringPosition < nCurrentLength_ &&
          !isspace( sz_[ nCurrentStringPosition ] ) ) {
      soToken.append( sz_[ nCurrentStringPosition ] );
      ++nCurrentStringPosition;
   }

   // if reached here, either end of string, or else have hit 
   // whitespace.

   return( soToken );

}



// this has the advantage over the above in that you can preallocate
// the size of soToken based on what you believe its maximum size to be


void RWCString :: getNextToken( size_t& nCurrentStringPosition,
                                RWCString& soToken ) {

   soToken = "";
   if ( nCurrentStringPosition >= nCurrentLength_ )
      return;

   // skip over leading whitespace
   while( nCurrentStringPosition < nCurrentLength_ &&
          isspace( sz_[ nCurrentStringPosition ] ) )
      ++nCurrentStringPosition;

   if ( nCurrentStringPosition >= nCurrentLength_ )
      return;

   while( nCurrentStringPosition < nCurrentLength_ &&
          !isspace( sz_[ nCurrentStringPosition ] ) ) {
      soToken.append( sz_[ nCurrentStringPosition ] );
      ++nCurrentStringPosition;
   }

   // if reached here, either end of string, or else have hit
   // whitespace

   return;
}

      





void RWCString :: removeLeadingKeyword( const RWCString& soKeyword ) {
   
   if ( bStartsWith( soKeyword ) ) {
      keepOnlyLastPartOfString( nCurrentLength_ - soKeyword.length() );
   }
}


bool RWCString :: bContainsAndRemoveToEnd( const RWCString& soLookForThis ) {

   int nIndex = index( soLookForThis );
   if ( nIndex == RW_NPOS ) {
      return false;
   }

   sz_[ nIndex ] = '\0';
   nCurrentLength_ = nIndex;

   return true;
}



// not sure if this works, but its a start   
void RWCString :: substitute( const RWCRegexp& regexp, 
                              const RWCString& soReplacement,
                              const bool bAll ) {

   
   while( true ) {
      if ( bContains( regexp ) ) {
         (*this)( regexp ) = soReplacement;
      }
      else {
         break;
      }
      if ( !bAll )
         break;
   }


}



void RWCString :: translate( const RWCString& soOldCharacters,
                             const RWCString& soNewCharacters ) {

   assert( soOldCharacters.length() == soNewCharacters.length() );

   for( int n = 0; n < nCurrentLength_; ++n ) {
      for( int nOldChar = 0; nOldChar < soOldCharacters.length(); ++nOldChar ) {
         if ( sz_[n] == soOldCharacters[nOldChar] ) {
            sz_[n] = soNewCharacters[nOldChar];
            break;  // this is necessary to prevent the
            // character just changed from matching another old character
            // Notice that n will be incremented.  Thus 
            // "aaaaaa".translate("a", "t" ) will be changed to
            // "tttttt" since all original characters will be processed--it
            // won't stop at the first character that matches one of
            // soOldCharacters
         }
      }
   }

}

bool RWCString :: bContainsOnlyTheseCharacters( const RWCString& soCharSet ) const {

   size_t nLength = strspn( sz_, soCharSet.data() );

   if ( nLength == nCurrentLength_ )
      return true;
   else
      return false;
}


static int nCompareLetters( char* pc1, char* pc2 ) {
   if ( *pc1 < *pc2 ) {
      return -1;
   }
   else if ( *pc1 > *pc2 ) {
      return 1;
   }
   else
      return 0;
}



void RWCString :: sortLetters() {

   void* pArray = sz_;
   size_t nNumberOfElements = nCurrentLength_;
   size_t nSizeOfAnElement = sizeof( char );

   qsort( pArray, nNumberOfElements, nSizeOfAnElement,
          ( int(*) ( const void*, const void*) ) nCompareLetters );


   bool bSorted = true;
   for( int n = 1; n < nCurrentLength_; ++n ) {
      if ( sz_[n-1] > sz_[n] ) {
         bSorted = false;
      }
   }

   if ( !bSorted ) {
      RWCString soError = (*this) + " is not sorted";
      THROW_ERROR2( soError );
   }
}