/***************************************************************************** # 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 using namespace std; #include "mbt_exception.h" #ifndef WALRUS_BUILD #include #endif #include "rwcsubstring.h" #include "rwcregexp.h" #include #include "assert.h" #include #include #include #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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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().c_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, ®m, 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().c_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( ®Pattern.regexPreg_, sz_, 1, ®m, 0 ); if ( nError == 0 ) return true; else if ( nError == REG_NOMATCH ) return false; else { char szErrorMessage[ nBUFFERSIZE ]; regerror( nError, ®Pattern.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( ®Pattern.regexPreg_, sz_, 1, ®m, 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, ®Pattern.regexPreg_, szErrorMessage, nBUFFERSIZE ); ost << szErrorMessage << ends; throw RWInternalError( ost.str().c_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().c_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().c_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().c_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().c_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 ); } }