/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/NPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Original Author: * Rick Gessner * * Contributor(s): * Scott Collins */ #include #include #include #include #include "nsString.h" #include "nsDebug.h" #include "nsCRT.h" #include "nsDeque.h" #ifndef RICKG_TESTBED #include "prdtoa.h" #include "nsISizeOfHandler.h" #endif static const char* kPossibleNull = "Error: possible unintended null in string"; static const char* kNullPointerError = "Error: unexpected null ptr"; static const char* kWhitespace="\b\t\r\n "; const nsBufferHandle* nsCString::GetFlatBufferHandle() const { return NS_REINTERPRET_CAST(const nsBufferHandle*, 1); } /** * Default constructor. */ nsCString::nsCString() { Initialize(*this,eOneByte); } nsCString::nsCString(const char* aCString) { Initialize(*this,eOneByte); Assign(aCString); } /** * This constructor accepts an ascii string * @update gess 1/4/99 * @param aCString is a ptr to a 1-byte cstr * @param aLength tells us how many chars to copy from given CString */ nsCString::nsCString(const char* aCString,PRInt32 aLength) { Initialize(*this,eOneByte); Assign(aCString,aLength); } #if 0 /** * This constructor accepts a unicode string * @update gess 1/4/99 * @param aString is a ptr to a unichar string * @param aLength tells us how many chars to copy from given aString */ nsCString::nsCString(const PRUnichar* aString,PRInt32 aLength) { Initialize(*this,eOneByte); AssignWithConversion(aString,aLength); } /** * This constructor works for all other nsSTr derivatives * @update gess 1/4/99 * @param reference to another nsCString */ nsCString::nsCString(const nsStr &aString) { Initialize(*this,eOneByte); StrAssign(*this,aString,0,aString.mLength); } #endif /** * This is our copy constructor * @update gess 1/4/99 * @param reference to another nsCString */ nsCString::nsCString(const nsCString& aString) { Initialize(*this,aString.mCharSize); StrAssign(*this,aString,0,aString.mLength); } /** * Destructor */ nsCString::~nsCString() { Destroy(*this); } const char* nsCString::GetReadableFragment( nsReadableFragment& aFragment, nsFragmentRequest aRequest, PRUint32 aOffset ) const { switch ( aRequest ) { case kFirstFragment: case kLastFragment: case kFragmentAt: aFragment.mEnd = (aFragment.mStart = mStr) + mLength; return aFragment.mStart + aOffset; case kPrevFragment: case kNextFragment: default: return 0; } } char* nsCString::GetWritableFragment( nsWritableFragment& aFragment, nsFragmentRequest aRequest, PRUint32 aOffset ) { switch ( aRequest ) { case kFirstFragment: case kLastFragment: case kFragmentAt: aFragment.mEnd = (aFragment.mStart = mStr) + mLength; return aFragment.mStart + aOffset; case kPrevFragment: case kNextFragment: default: return 0; } } nsCString::nsCString( const nsACString& aReadable ) { Initialize(*this,eOneByte); Assign(aReadable); } void nsCString::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { if (aResult) { *aResult = sizeof(*this) + mCapacity * mCharSize; } } /** * This method truncates this string to given length. * * @update rickg 03.23.2000 * @param anIndex -- new length of string * @return nada */ void nsCString::SetLength(PRUint32 anIndex) { if ( anIndex > mCapacity ) SetCapacity(anIndex); // |SetCapacity| normally doesn't guarantee the use we are putting it to here (see its interface comment in nsAWritableString.h), // we can only use it since our local implementation, |nsCString::SetCapacity|, is known to do what we want nsStr::StrTruncate(*this,anIndex); } /** * Call this method if you want to force the string to a certain capacity; * |SetCapacity(0)| discards associated storage. * * @param aNewCapacity -- desired minimum capacity */ void nsCString::SetCapacity( PRUint32 aNewCapacity ) { if ( aNewCapacity ) { if( aNewCapacity > mCapacity ) GrowCapacity(*this,aNewCapacity); AddNullTerminator(*this); } else { nsStr::Destroy(*this); nsStr::Initialize(*this, eOneByte); } } /********************************************************************** Accessor methods... *********************************************************************/ /** * set a char inside this string at given index * @param aChar is the char you want to write into this string * @param anIndex is the ofs where you want to write the given char * @return TRUE if successful */ PRBool nsCString::SetCharAt(PRUnichar aChar,PRUint32 anIndex){ PRBool result=PR_FALSE; if(anIndex2)) { theFirstChar=First(); theLastChar=Last(); if(theFirstChar==theLastChar) { if(('\''==theFirstChar) || ('"'==theFirstChar)) { Cut(0,1); Truncate(mLength-1); theQuotesAreNeeded=PR_TRUE; } else theFirstChar=0; } } nsStr::Trim(*this,aTrimSet,aEliminateLeading,aEliminateTrailing); if(aIgnoreQuotes && theQuotesAreNeeded) { InsertWithConversion(theFirstChar,0); AppendWithConversion(theLastChar); } } } /** * This method strips chars in given set from string. * You can control whether chars are yanked from * start and end of string as well. * * @update gess 3/31/98 * @param aEliminateLeading controls stripping of leading ws * @param aEliminateTrailing controls stripping of trailing ws * @return this */ void nsCString::CompressSet(const char* aSet, PRUnichar aChar,PRBool aEliminateLeading,PRBool aEliminateTrailing){ if(aSet){ ReplaceChar(aSet,aChar); nsStr::CompressSet(*this,aSet,aEliminateLeading,aEliminateTrailing); } } /** * This method strips whitespace from string. * You can control whether whitespace is yanked from * start and end of string as well. * * @update gess 3/31/98 * @param aEliminateLeading controls stripping of leading ws * @param aEliminateTrailing controls stripping of trailing ws * @return this */ void nsCString::CompressWhitespace( PRBool aEliminateLeading,PRBool aEliminateTrailing){ CompressSet(kWhitespace,' ',aEliminateLeading,aEliminateTrailing); } /********************************************************************** string conversion methods... *********************************************************************/ /** * Creates a duplicate clone (ptr) of this string. * @update gess 01/04/99 * @return ptr to clone of this string */ nsCString* nsCString::ToNewString() const { return new nsCString(*this); } /** * Creates an ascii clone of this string * Note that calls to this method should be matched with calls to * |nsMemory::Free|. * @update gess 02/24/00 * @return ptr to new ascii string */ char* nsCString::ToNewCString() const { return nsCRT::strdup(mStr); } /** * Creates an unicode clone of this string * Note that calls to this method should be matched with calls to * |nsMemory::Free|. * @update gess 01/04/99 * @return ptr to new ascii string */ PRUnichar* nsCString::ToNewUnicode() const { PRUnichar* result = NS_STATIC_CAST(PRUnichar*, nsMemory::Alloc(sizeof(PRUnichar) * (mLength + 1))); if (result) { CBufDescriptor desc(result, PR_TRUE, mLength + 1, 0); nsAutoString temp(desc); temp.AssignWithConversion(mStr); } return result; } /** * Copies contents of this string into he given buffer * Note that if you provide me a buffer that is smaller than the length of * this string, only the number of bytes that will fit are copied. * * @update gess 01/04/99 * @param aBuf * @param aBufLength * @param anOffset * @return */ char* nsCString::ToCString(char* aBuf, PRUint32 aBufLength,PRUint32 anOffset) const{ if(aBuf) { // NS_ASSERTION(mLength<=aBufLength,"buffer smaller than string"); CBufDescriptor theDescr(aBuf,PR_TRUE,aBufLength,0); nsCAutoString temp(theDescr); temp.Assign(mStr, PR_MIN(mLength, aBufLength-1)); temp.mStr=0; } return aBuf; } /** * Perform string to float conversion. * @update gess 01/04/99 * @param aErrorCode will contain error if one occurs * @return float rep of string value */ float nsCString::ToFloat(PRInt32* aErrorCode) const { char buf[100]; if (mLength > PRInt32(sizeof(buf)-1)) { *aErrorCode = (PRInt32) NS_ERROR_ILLEGAL_VALUE; return 0.0f; } char* cp = ToCString(buf, sizeof(buf)); float f = (float) PR_strtod(cp, &cp); if (*cp != 0) { *aErrorCode = (PRInt32) NS_ERROR_ILLEGAL_VALUE; } *aErrorCode = (PRInt32) NS_OK; return f; } /** * Perform decimal numeric string to int conversion. * NOTE: In this version, we use the radix you give, even if it's wrong. * @update gess 02/14/00 * @param aErrorCode will contain error if one occurs * @param aRadix tells us what base to expect the given string in. kAutoDetect tells us to determine the radix. * @return int rep of string value */ PRInt32 nsCString::ToInteger(PRInt32* anErrorCode,PRUint32 aRadix) const { char* cp=mStr; PRInt32 theRadix=10; // base 10 unless base 16 detected, or overriden (aRadix != kAutoDetect) PRInt32 result=0; PRBool negate=PR_FALSE; char theChar=0; //initial value, override if we find an integer *anErrorCode=NS_ERROR_ILLEGAL_VALUE; if(cp) { //begin by skipping over leading chars that shouldn't be part of the number... char* endcp=cp+mLength; PRBool done=PR_FALSE; while((cp='A') && (theChar<='F')) { if(10==theRadix) { if(kAutoDetect==aRadix){ theRadix=16; cp=first; //backup result=0; } else { *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } } else { result = (theRadix * result) + ((theChar-'A')+10); } } else if((theChar>='a') && (theChar<='f')) { if(10==theRadix) { if(kAutoDetect==aRadix){ theRadix=16; cp=first; //backup result=0; } else { *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } } else { result = (theRadix * result) + ((theChar-'a')+10); } } else if(('X'==theChar) || ('x'==theChar) || ('#'==theChar) || ('+'==theChar)) { continue; } else { //we've encountered a char that's not a legal number or sign break; } } //while if(negate) result=-result; } //if } return result; } /********************************************************************** String manipulation methods... *********************************************************************/ /** * assign given unichar* to this string * @update gess 01/04/99 * @param aCString: buffer to be assigned to this * @param aCount -- length of given buffer or -1 if you want me to compute length. * NOTE: IFF you pass -1 as aCount, then your buffer must be null terminated. * * @return this */ void nsCString::AssignWithConversion(const PRUnichar* aString,PRInt32 aCount) { nsStr::StrTruncate(*this,0); if(aString && aCount){ nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mUStr=(PRUnichar*)aString; if(0 start; aString.BeginReading(start); nsReadingIterator end; aString.EndReading(end); while (start != end) { PRUint32 fraglen = start.size_forward(); nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mUStr=(PRUnichar*)start.get(); temp.mLength=fraglen; StrAppend(*this,temp,0,fraglen); start.advance(fraglen); } } } void nsCString::AppendWithConversion( const nsAString& aString ) { PRInt32 count = aString.Length(); if(count){ nsReadingIterator start; aString.BeginReading(start); nsReadingIterator end; aString.EndReading(end); while (start != end) { PRUint32 fraglen = start.size_forward(); nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mUStr=(PRUnichar*)start.get(); temp.mLength=fraglen; StrAppend(*this,temp,0,fraglen); start.advance(fraglen); } } } /** * assign given unichar to this string * @update gess 01/04/99 * @param aChar: char to be assignd to this * @return this */ void nsCString::AssignWithConversion(PRUnichar aChar) { nsStr::StrTruncate(*this,0); AppendWithConversion(aChar); } /** * append given char to this string * @update gess 01/04/99 * @param aChar: char to be appended to this * @return this */ void nsCString::AppendWithConversion(PRUnichar aChar) { PRUnichar buf[2]={0,0}; buf[0]=aChar; nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mUStr=buf; temp.mLength=1; StrAppend(*this,temp,0,1); } void nsCString::AppendWithConversion( const PRUnichar* aBuffer, PRInt32 aLength ) { nsStr temp; nsStr::Initialize(temp, eTwoByte); temp.mUStr = NS_CONST_CAST(PRUnichar*, aBuffer); if ( aLength < 0 ) aLength = nsCharTraits::length(aBuffer); if ( aLength > 0 ) { temp.mLength = aLength; StrAppend(*this, temp, 0, aLength); } } /** * Append the given integer to this string * @update gess 01/04/99 * @param aInteger: * @param aRadix: * @return */ void nsCString::AppendInt(PRInt32 anInteger,PRInt32 aRadix) { PRUint32 theInt=(PRUint32)anInteger; char buf[]={'0',0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; PRInt32 radices[] = {1000000000,268435456}; PRInt32 mask1=radices[16==aRadix]; PRInt32 charpos=0; if(anInteger<0) { theInt*=-1; if(10==aRadix) { buf[charpos++]='-'; } else theInt=(int)~(theInt-1); } PRBool isfirst=PR_TRUE; while(mask1>=1) { PRInt32 theDiv=theInt/mask1; if((theDiv) || (!isfirst)) { buf[charpos++]="0123456789abcdef"[theDiv]; isfirst=PR_FALSE; } theInt-=theDiv*mask1; mask1/=aRadix; } Append(buf); } /** * Append the given float to this string * @update gess 01/04/99 * @param aFloat: * @return */ void nsCString::AppendFloat( double aFloat ){ char buf[40]; // *** XX UNCOMMENT THIS LINE //PR_snprintf(buf, sizeof(buf), "%g", aFloat); sprintf(buf,"%g",aFloat); Append(buf); } /** * append given string to this string; * @update gess 01/04/99 * @param aString : string to be appended to this * @return this */ void nsCString::AppendWithConversion(const nsString& aString,PRInt32 aCount) { if(aCount<0) aCount=aString.mLength; else aCount=MinInt(aCount,aString.mLength); if(0= PRInt32(sizeof(buf))) { cp = aString.ToNewCString(); } else { aString.ToCString(cp, len + 1); } if(len>0) ::fwrite(cp, 1, len, out); if (cp != buf) { delete[] cp; } return (int) len; } /** * Dumps the contents of the string to stdout * @update gess 11/15/99 */ void nsCString::DebugDump(void) const { #ifdef DEBUG if(mStr && (eOneByte==mCharSize)) { printf("\n%s",mStr); } #endif } //---------------------------------------------------------------------- NS_ConvertUCS2toUTF8::NS_ConvertUCS2toUTF8( const nsAString& aString ) { nsReadingIterator start; aString.BeginReading(start); nsReadingIterator end; aString.EndReading(end); while (start != end) { nsReadableFragment frag(start.fragment()); Append(frag.mStart, frag.mEnd - frag.mStart); start.advance(start.size_forward()); } } void NS_ConvertUCS2toUTF8::Append( const PRUnichar* aString, PRUint32 aLength ) { // Handle null string by just leaving us as a brand-new // uninitialized nsCAutoString. if (! aString) return; // Caculate how many bytes we need const PRUnichar* p; PRInt32 count, utf8len; for (p = aString, utf8len = 0, count = aLength; 0 != count && 0 != (*p); count--, p++) { if (! ((*p) & 0xFF80)) utf8len += 1; // 0000 0000 - 0000 007F else if (! ((*p) & 0xF800)) utf8len += 2; // 0000 0080 - 0000 07FF else utf8len += 3; // 0000 0800 - 0000 FFFF // Note: Surrogate pair needs 4 bytes, but in this calcuation // we count it as 6 bytes. It will waste 2 bytes per surrogate pair } // Make sure our buffer's big enough, so we don't need to do // multiple allocations. if(mLength+PRUint32(utf8len+1) > sizeof(mBuffer)) SetCapacity(mLength+utf8len+1); // |SetCapacity| normally doesn't guarantee the use we are putting it to here (see its interface comment in nsAWritableString.h), // we can only use it since our local implementation, |nsCString::SetCapacity|, is known to do what we want char* out = mStr+mLength; PRUint32 ucs4=0; for (p = aString, count = aLength; 0 != count && 0 != (*p); count--, p++) { if (0 == ucs4) { if (! ((*p) & 0xFF80)) { *out++ = (char)*p; } else if (! ((*p) & 0xF800)) { *out++ = 0xC0 | (char)((*p) >> 6); *out++ = 0x80 | (char)(0x003F & (*p)); } else { if (0xD800 == (0xFC00 & (*p))) { // D800- DBFF - High Surrogate // N = (H- D800) *400 + 10000 + ... ucs4 = 0x10000 | ((0x03FF & (*p)) << 10); } else if (0xDC00 == (0xFC00 & (*p))) { // DC00- DFFF - Low Surrogate // error here. We should hit High Surrogate first // Do not output any thing in this case } else { *out++ = 0xE0 | (char)((*p) >> 12); *out++ = 0x80 | (char)(0x003F & (*p >> 6)); *out++ = 0x80 | (char)(0x003F & (*p) ); } } } else { if (0xDC00 == (0xFC00 & (*p))) { // DC00- DFFF - Low Surrogate // N += ( L - DC00 ) ucs4 |= (0x03FF & (*p)); // 0001 0000-001F FFFF *out++ = 0xF0 | (char)(ucs4 >> 18); *out++ = 0x80 | (char)(0x003F & (ucs4 >> 12)); *out++ = 0x80 | (char)(0x003F & (ucs4 >> 6)); *out++ = 0x80 | (char)(0x003F & ucs4) ; } else { // Got a High Surrogate but no low surrogate // output nothing. } ucs4 = 0; } } *out = '\0'; // null terminate mLength += utf8len; } /*********************************************************************** IMPLEMENTATION NOTES: AUTOSTRING... ***********************************************************************/ /** * Default constructor * */ nsCAutoString::nsCAutoString() : nsCString(){ Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); } nsCAutoString::nsCAutoString( const nsCString& aString ) : nsCString(){ Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } nsCAutoString::nsCAutoString( const nsACString& aString ) : nsCString(){ Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } nsCAutoString::nsCAutoString(const char* aCString) : nsCString() { Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aCString); } /** * Copy construct from ascii c-string * @param aCString is a ptr to a 1-byte cstr */ nsCAutoString::nsCAutoString(const char* aCString,PRInt32 aLength) : nsCString() { Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aCString,aLength); } /** * Copy construct using an external buffer descriptor * @param aBuffer -- descibes external buffer */ nsCAutoString::nsCAutoString(const CBufDescriptor& aBuffer) : nsCString() { if(!aBuffer.mBuffer) { Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); } else { Initialize(*this,aBuffer.mBuffer,aBuffer.mCapacity,aBuffer.mLength,aBuffer.mCharSize,!aBuffer.mStackBased); } if(!aBuffer.mIsConst) AddNullTerminator(*this); //this isn't really needed, but it guarantees that folks don't pass string constants. } #if 0 /** * Copy construct from uni-string * @param aString is a ptr to a unistr */ nsCAutoString::nsCAutoString(const PRUnichar* aString,PRInt32 aLength) : nsCString() { Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aString,aLength); } /** * construct from an nsStr * @param */ nsCAutoString::nsCAutoString(const nsStr& aString) : nsCString() { Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } /** * construct from a char * @param */ nsCAutoString::nsCAutoString(PRUnichar aChar) : nsCString(){ Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aChar); } #endif /** * deconstructor * @param */ nsCAutoString::~nsCAutoString(){ } void nsCAutoString::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { if (aResult) { *aResult = sizeof(*this) + mCapacity * mCharSize; } }