/* -*- 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. * * Contributor(s): */ #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 "; static void CSubsume(nsStr& aDest,nsStr& aSource){ if(aSource.mStr && aSource.mLength) { if(aSource.mOwnsBuffer){ nsStr::Destroy(aDest); aDest.mStr=aSource.mStr; aDest.mLength=aSource.mLength; aDest.mCharSize=aSource.mCharSize; aDest.mCapacity=aSource.mCapacity; aDest.mOwnsBuffer=aSource.mOwnsBuffer; aSource.mOwnsBuffer=PR_FALSE; aSource.mStr=0; } else{ nsStr::Assign(aDest,aSource,0,aSource.mLength); } } else nsStr::Truncate(aDest,0); } /** * Default constructor. */ nsCString::nsCString() { nsStr::Initialize(*this,eOneByte); } /** * 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) { nsStr::Initialize(*this,eOneByte); Assign(aCString,aLength); } /** * 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) { nsStr::Initialize(*this,eOneByte); Assign(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) { nsStr::Initialize(*this,eOneByte); nsStr::Assign(*this,aString,0,aString.mLength); } /** * This is our copy constructor * @update gess 1/4/99 * @param reference to another nsCString */ nsCString::nsCString(const nsCString& aString) { nsStr::Initialize(*this,aString.mCharSize); nsStr::Assign(*this,aString,0,aString.mLength); } /** * construct from subsumeable string * @update gess 1/4/99 * @param reference to a subsumeString */ nsCString::nsCString(nsSubsumeCStr& aSubsumeStr) { CSubsume(*this,aSubsumeStr); } /** * Destructor */ nsCString::~nsCString() { nsStr::Destroy(*this); } void nsCString::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { if (aResult) { *aResult = sizeof(*this) + mCapacity * mCharSize; } } /** * This method truncates this string to given length. * * @update gess 01/04/99 * @param anIndex -- new length of string * @return nada */ void nsCString::Truncate(PRUint32 anIndex) { nsStr::Truncate(*this,anIndex); } /** * Call this method if you want to force the string to a certain capacity * @update gess 1/4/99 * @param aLength -- contains new length for mStr */ void nsCString::SetCapacity(PRUint32 aLength) { if(aLength>mCapacity) { GrowCapacity(*this,aLength); } AddNullTerminator(*this); } /********************************************************************** Accessor methods... *********************************************************************/ /** * Retrieves internal (1-byte) buffer ptr; * @update gess1/4/99 * @return ptr to internal buffer */ const char* nsCString::GetBuffer(void) const { return mStr; } /** * Get nth character. */ PRUnichar nsCString::operator[](PRUint32 anIndex) const { return GetCharAt(*this,anIndex); } /** * Get nth character. */ PRUnichar nsCString::CharAt(PRUint32 anIndex) const { return GetCharAt(*this,anIndex); } /** * Get 1st character. */ PRUnichar nsCString::First(void) const{ return GetCharAt(*this,0); } /** * Get last character. */ PRUnichar nsCString::Last(void) const{ return (char)GetCharAt(*this,mLength-1); } /** * 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) { Insert(theFirstChar,0); Append(theLastChar); } } return *this; } /** * 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 */ nsCString& nsCString::CompressSet(const char* aSet, PRUnichar aChar,PRBool aEliminateLeading,PRBool aEliminateTrailing){ if(aSet){ ReplaceChar(aSet,aChar); nsStr::CompressSet(*this,aSet,aEliminateLeading,aEliminateTrailing); } return *this; } /** * 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 */ nsCString& nsCString::CompressWhitespace( PRBool aEliminateLeading,PRBool aEliminateTrailing){ CompressSet(kWhitespace,' ',aEliminateLeading,aEliminateTrailing); return *this; } /********************************************************************** 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 Recycle(). * @update gess 02/24/00 * @return ptr to new ascii string */ char* nsCString::ToNewCString() const { nsCString temp(*this); //construct nsCString with alloc on heap (which we'll steal in a moment) temp.SetCapacity(8); //force it to have an allocated buffer, even if this is empty. char* result=temp.mStr; //steal temp's buffer temp.mStr=0; //clear temp's buffer to prevent deallocation return result; //and return the char* } /** * Creates an unicode clone of this string * Note that calls to this method should be matched with calls to Recycle(). * @update gess 01/04/99 * @return ptr to new ascii string */ PRUnichar* nsCString::ToNewUnicode() const { nsString temp(*this); //construct nsCString with alloc on heap (which we'll steal in a moment) temp.SetCapacity(8); //force temp to have an allocated buffer, even if this is empty. PRUnichar* result=temp.mUStr; //steal temp's buffer temp.mStr=0; //now clear temp's buffer to prevent deallocation temp.mOwnsBuffer=PR_FALSE; //and return the PRUnichar* 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(*this,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 = (kAutoDetect==aRadix) ? 10 : aRadix; PRInt32 result=0; PRBool negate=PR_FALSE; char theChar=0; *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 nsStr (or derivative) to this one * @update gess 01/04/99 * @param aString: string to be appended * @return this */ nsCString& nsCString::Assign(const nsStr& aString,PRInt32 aCount) { if(this!=&aString){ nsStr::Truncate(*this,0); if(aCount<0) aCount=aString.mLength; else aCount=MinInt(aCount,aString.mLength); nsStr::Assign(*this,aString,0,aCount); } return *this; } /** * assign given char* 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 */ nsCString& nsCString::Assign(const char* aCString,PRInt32 aCount) { nsStr::Truncate(*this,0); if(aCString){ Append(aCString,aCount); } return *this; } /** * 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 */ nsCString& nsCString::Assign(const PRUnichar* aString,PRInt32 aCount) { nsStr::Truncate(*this,0); if(aString && aCount){ nsStr temp; Initialize(temp,eTwoByte); temp.mUStr=(PRUnichar*)aString; if(0=1) { PRInt32 theDiv=theInt/mask1; if((theDiv) || (!isfirst)) { buf[charpos++]="0123456789abcdef"[theDiv]; isfirst=PR_FALSE; } theInt-=theDiv*mask1; mask1/=aRadix; } return Append(buf); } /** * Append the given float to this string * @update gess 01/04/99 * @param aFloat: * @return */ nsCString& nsCString::Append(float aFloat){ char buf[40]; // *** XX UNCOMMENT THIS LINE //PR_snprintf(buf, sizeof(buf), "%g", aFloat); sprintf(buf,"%g",aFloat); return Append(buf); } /* * Copies n characters from this left of this string to given string, * * @update gess 4/1/98 * @param aDest -- Receiving string * @param aCount -- number of chars to copy * @return number of chars copied */ PRUint32 nsCString::Left(nsCString& aDest,PRInt32 aCount) const{ if(aCount<0) aCount=mLength; else aCount=MinInt(aCount,mLength); nsStr::Assign(aDest,*this,0,aCount); return aDest.mLength; } /* * Copies n characters from this string to from given offset * * @update gess 4/1/98 * @param aDest -- Receiving string * @param anOffset -- where copying should begin * @param aCount -- number of chars to copy * @return number of chars copied */ PRUint32 nsCString::Mid(nsCString& aDest,PRUint32 anOffset,PRInt32 aCount) const{ if(aCount<0) aCount=mLength; else aCount=MinInt(aCount,mLength); nsStr::Assign(aDest,*this,anOffset,aCount); return aDest.mLength; } /* * Copies last n characters from this string to given string, * * @update gess 4/1/98 * @param aDest -- Receiving string * @param aCount -- number of chars to copy * @return number of chars copied */ PRUint32 nsCString::Right(nsCString& aDest,PRInt32 aCount) const{ PRInt32 offset=MaxInt(mLength-aCount,0); return Mid(aDest,offset,aCount); } /* * This method inserts n chars from given string into this * string at str[anOffset]. * * @update gess 4/1/98 * @param aString -- source String to be inserted into this * @param anOffset -- insertion position within this str * @param aCount -- number of chars to be copied from aCopy * @return this */ nsCString& nsCString::Insert(const nsCString& aString,PRUint32 anOffset,PRInt32 aCount) { nsStr::Insert(*this,anOffset,aString,0,aCount); return *this; } /** * Insert a char* into this string at a specified offset. * * @update gess4/22/98 * @param char* aCString to be inserted into this string * @param anOffset is insert pos in str * @param aCounttells us how many chars to insert * @return this */ nsCString& nsCString::Insert(const char* aCString,PRUint32 anOffset,PRInt32 aCount){ if(aCString){ nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mStr=(char*)aCString; if(0(const nsStr& S) const {return PRBool(Compare(S)>0);} PRBool nsCString::operator>(const char* s) const {return PRBool(Compare(s)>0);} PRBool nsCString::operator>(const PRUnichar* s) const {return PRBool(Compare(s)>0);} PRBool nsCString::operator<=(const nsStr& S) const {return PRBool(Compare(S)<=0);} PRBool nsCString::operator<=(const char* s) const {return PRBool(Compare(s)<=0);} PRBool nsCString::operator<=(const PRUnichar* s) const {return PRBool(Compare(s)<=0);} PRBool nsCString::operator>=(const nsStr& S) const {return PRBool(Compare(S)>=0);} PRBool nsCString::operator>=(const char* s) const {return PRBool(Compare(s)>=0);} PRBool nsCString::operator>=(const PRUnichar* s) const {return PRBool(Compare(s)>=0);} PRBool nsCString::EqualsIgnoreCase(const nsStr& aString) const { return Equals(aString,PR_TRUE); } PRBool nsCString::EqualsIgnoreCase(const char* aString,PRInt32 aLength) const { return Equals(aString,PR_TRUE,aLength); } PRBool nsCString::EqualsIgnoreCase(const PRUnichar* aString,PRInt32 aLength) const { return Equals(aString,PR_TRUE,aLength); } /** * Compare this to given string; note that we compare full strings here. * * @update gess 01/04/99 * @param aString is the other nsCString to be compared to * @param aIgnoreCase tells us how to treat case * @param aCount tells us how many chars to test; -1 implies full length * @return TRUE if equal */ PRBool nsCString::Equals(const nsStr& aString,PRBool aIgnoreCase,PRInt32 aCount) const { PRInt32 theAnswer=nsStr::Compare(*this,aString,aCount,aIgnoreCase); PRBool result=PRBool(0==theAnswer); return result; } /** * Compare this to given string; note that we compare full strings here. * * @param aString is the CString to be compared * @param aCount tells us how many chars you want to compare starting with start of string * @param aIgnorecase tells us whether to be case sensitive * @param aCount tells us how many chars to test; -1 implies full length * @return TRUE if equal */ PRBool nsCString::Equals(const char* aCString,PRBool aIgnoreCase,PRInt32 aCount) const{ PRInt32 theAnswer=Compare(aCString,aIgnoreCase,aCount); PRBool result=PRBool(0==theAnswer); return result; } PRBool nsCString::Equals(const PRUnichar* aString,PRBool aIgnoreCase,PRInt32 aCount) const { PRInt32 theAnswer=Compare(aString,aIgnoreCase,aCount); PRBool result=PRBool(0==theAnswer); return result; } /************************************************************** Define the string deallocator class... **************************************************************/ #ifndef RICKG_TESTBED class nsCStringDeallocator: public nsDequeFunctor{ public: virtual void* operator()(void* anObject) { nsCString* aString= (nsCString*)anObject; if(aString){ delete aString; } return 0; } }; #endif /**************************************************************************** * This class, appropriately enough, creates and recycles nsCString objects.. ****************************************************************************/ #ifndef RICKG_TESTBED class nsCStringRecycler { public: nsCStringRecycler() : mDeque(0) { } ~nsCStringRecycler() { nsCStringDeallocator theDeallocator; mDeque.ForEach(theDeallocator); //now delete the strings } void Recycle(nsCString* aString) { mDeque.Push(aString); } nsCString* CreateString(void){ nsCString* result=(nsCString*)mDeque.Pop(); if(!result) result=new nsCString(); return result; } nsDeque mDeque; }; static nsCStringRecycler& GetRecycler(void); /** * * @update gess 01/04/99 * @param * @return */ nsCStringRecycler& GetRecycler(void){ static nsCStringRecycler gCRecycler; return gCRecycler; } #endif /** * Call this mehod when you're done * @update gess 01/04/99 * @param * @return */ nsCString* nsCString::CreateString(void){ nsCString* result=0; #ifndef RICKG_TESTBED result=GetRecycler().CreateString(); #endif return result; } /** * Call this mehod when you're done * @update gess 01/04/99 * @param * @return */ void nsCString::Recycle(nsCString* aString){ #ifndef RICKG_TESTBED GetRecycler().Recycle(aString); #endif } #if 0 /** * * @update gess8/8/98 * @param * @return */ ostream& operator<<(ostream& aStream,const nsCString& aString){ if(eOneByte==aString.mCharSize) { aStream<= 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 { if(mStr && (eOneByte==mCharSize)) { printf("\n%s",mStr); } } /*********************************************************************** IMPLEMENTATION NOTES: AUTOSTRING... ***********************************************************************/ /** * Default constructor * */ nsCAutoString::nsCAutoString() : nsCString(){ nsStr::Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); } /** * Default constructor */ nsCAutoString::nsCAutoString(const nsCAutoString& aString) : nsCString() { nsStr::Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } /** * Copy construct from ascii c-string * @param aCString is a ptr to a 1-byte cstr */ nsCAutoString::nsCAutoString(const char* aCString,PRInt32 aLength) : nsCString() { nsStr::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) { nsStr::Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); } else { nsStr::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. } /** * Copy construct from uni-string * @param aString is a ptr to a unistr */ nsCAutoString::nsCAutoString(const PRUnichar* aString,PRInt32 aLength) : nsCString() { nsStr::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() { nsStr::Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aString); } /** * construct from a char * @param */ nsCAutoString::nsCAutoString(PRUnichar aChar) : nsCString(){ nsStr::Initialize(*this,mBuffer,sizeof(mBuffer)-1,0,eOneByte,PR_FALSE); AddNullTerminator(*this); Append(aChar); } /** * construct from a subsumeable string * @update gess 1/4/99 * @param reference to a subsumeString */ #ifdef AIX nsCAutoString::nsCAutoString(const nsSubsumeCStr& aSubsumeStr) :nsCString() { nsSubsumeCStr temp(aSubsumeStr); // a temp is needed for the AIX compiler CSubsume(*this,temp); #else nsCAutoString::nsCAutoString( nsSubsumeCStr& aSubsumeStr) :nsCString() { CSubsume(*this,aSubsumeStr); #endif // AIX } /** * deconstructor * @param */ nsCAutoString::~nsCAutoString(){ } void nsCAutoString::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const { if (aResult) { *aResult = sizeof(*this) + mCapacity * mCharSize; } } nsSubsumeCStr::nsSubsumeCStr(nsStr& aString) : nsCString() { CSubsume(*this,aString); } nsSubsumeCStr::nsSubsumeCStr(PRUnichar* aString,PRBool assumeOwnership,PRInt32 aLength) : nsCString() { mUStr=aString; mCapacity=mLength=(-1==aLength) ? nsCRT::strlen(aString) : aLength; mOwnsBuffer=assumeOwnership; } nsSubsumeCStr::nsSubsumeCStr(char* aString,PRBool assumeOwnership,PRInt32 aLength) : nsCString() { mStr=aString; mCapacity=mLength=(-1==aLength) ? strlen(aString) : aLength; mOwnsBuffer=assumeOwnership; }