/* -*- 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 "nsCRT.h" #include "nsDeque.h" #include "nsString.h" 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,0); 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,0); } } else nsStr::Truncate(aDest,0,0); } /** * Default constructor. Note that we actually allocate a small buffer * to begin with. This is because the "philosophy" of the string class * was to allow developers direct access to the underlying buffer for * performance reasons. */ nsCString::nsCString(nsIMemoryAgent* anAgent) : mAgent(anAgent) { 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 */ nsCString::nsCString(const char* aCString,PRInt32 aLength,nsIMemoryAgent* anAgent) : mAgent(anAgent) { nsStr::Initialize(*this,eOneByte); Assign(aCString,aLength); } /** * This constructor accepts an ascii string * @update gess 1/4/99 * @param aCString is a ptr to a 1-byte cstr */ nsCString::nsCString(const PRUnichar* aString,PRInt32 aLength,nsIMemoryAgent* anAgent) : mAgent(anAgent) { nsStr::Initialize(*this,eOneByte); if(aString && aLength){ nsStr temp; Initialize(temp,eTwoByte); temp.mUStr=(PRUnichar*)aString; if(01) { PRUint32 theIndex; for(theIndex=1;theIndexmStr[theIndex]) { result=PR_FALSE; break; } } } return result; } /** * 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 * @return */ void nsCString::SetCapacity(PRUint32 aLength) { if(aLength>mCapacity) { GrowCapacity(*this,aLength,mAgent); } AddNullTerminator(*this); } /********************************************************************** Accessor methods... *********************************************************************/ /** * * @update gess1/4/99 * @return ptr to internal buffer (if 1-byte), otherwise NULL */ const char* nsCString::GetBuffer(void) const { return mStr; } PRUnichar nsCString::CharAt(PRUint32 anIndex) const { return GetCharAt(*this,anIndex); } PRUnichar nsCString::First(void) const{ return GetCharAt(*this,0); } PRUnichar nsCString::Last(void) const{ return (char)GetCharAt(*this,mLength-1); } PRBool nsCString::SetCharAt(PRUnichar aChar,PRUint32 anIndex){ PRBool result=PR_FALSE; if(anIndex 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 numeric string to int conversion with given radix. * NOTE: 1. This method mandates that the string is well formed. * 2. This method will return an error if the string you give contains chars outside the range for the specified radix. * @update gess 10/01/98 * @param aErrorCode will contain error if one occurs * @param aRadix tells us what base to expect the string in. * @return int rep of string value */ PRInt32 _ToInteger(nsCString& aString,PRInt32* anErrorCode,PRUint32 aRadix) { //copy chars to local buffer -- step down from 2 bytes to 1 if necessary... PRInt32 result=0; char* cp = aString.mStr + aString.mLength; PRInt32 theMult=1; *anErrorCode = NS_OK; //now iterate the numeric chars and build our result char theChar=0; char theDigit=0; while(--cp>=aString.mStr){ char theChar=*cp; if((theChar>='0') && (theChar<='9')){ theDigit=theChar-'0'; } else if((theChar>='A') && (theChar<='F')) { if(10==aRadix){ *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } theDigit=(theChar-'A')+10; } else if('-'==theChar) { result=-result; break; } else if(('+'==theChar) || (' '==theChar)) { //stop in a good state if you see this... break; } else { //we've encountered a char that's not a legal number or sign *anErrorCode=NS_ERROR_ILLEGAL_VALUE; result=0; break; } result+=theDigit*theMult; theMult*=aRadix; } return result; } /** * Call this method to extract the rightmost numeric value from the given * 1-byte input string, and simultaneously determine the radix. * NOTE: This method mandates that the string is well formed. * Leading and trailing gunk should be removed, and the case upper. * @update gess 10/01/98 * @param anInputString contains orig string * @param anOutString contains numeric portion copy of input string * @param aRadix (an out parm) tells the caller what base we think the string is in. * @return non-zero error code if this string is non-numeric */ PRInt32 GetNumericSubstring(nsCString& aString,PRUint32& aRadix) { aString.ToUpperCase(); PRInt32 decPt=nsStr::FindChar(aString,'.',PR_TRUE,0); char* cp = (kNotFound==decPt) ? aString.mStr + aString.mLength-1 : aString.mStr+decPt-1; aRadix=kRadixUnknown; //assume for starters... // Skip trailing non-numeric... while (cp >= aString.mStr) { if((*cp>='0') && (*cp<='9')){ if(kRadixUnknown==aRadix) aRadix=kRadix10; break; } else if((*cp>='A') && (*cp<='F')) { aRadix=16; break; } cp--; } aString.Truncate(cp-aString.mStr+1); //ok, now scan through chars until you find the start of this number... //we delimit the number by the presence of: +,-,#,X // Skip trailing non-numeric... cp++; while (--cp >= aString.mStr) { if((*cp>='0') && (*cp<='9')){ continue; } else if((*cp>='A') && (*cp<='F')) { continue; } else if((*cp=='-') || (*cp=='+')){ break; } else { if(('#'==(*cp)) || ('X'==(*cp))) aRadix=kRadix16; cp++; //move back by one break; } } if(cp>aString.mStr) aString.Cut(0,cp-aString.mStr); PRInt32 result=(0==aString.mLength) ? NS_ERROR_ILLEGAL_VALUE : NS_OK; return result; } /** * This method tries to autodetect that radix given a string * @update gess 10/01/98 * @return 10,16,or 0 (meaning I don't know) */ PRUint32 nsCString::DetermineRadix(void) { PRUint32 result=kRadixUnknown; if(0> 1; PRUnichar theChar=GetCharAt(*this,middle); if (theChar==aChar) return middle; if (theChar>aChar) high = middle - 1; else low = middle + 1; } return kNotFound; } /** * Search for given buffer within this string * * @update gess 3/25/98 * @param aCStringBuf - charstr to be found * @return offset in string, or -1 (kNotFound) */ PRInt32 nsCString::Find(const char* aCString,PRBool aIgnoreCase,PRInt32 anOffset) const{ PRInt32 result=kNotFound; if(aCString) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCString); temp.mStr=(char*)aCString; result=nsStr::FindSubstr(*this,temp,aIgnoreCase,anOffset); } return result; } /** * Search for given buffer within this string * * @update gess 3/25/98 * @param aCStringBuf - charstr to be found * @return offset in string, or -1 (kNotFound) */ PRInt32 nsCString::Find(const PRUnichar* aString,PRBool aIgnoreCase,PRInt32 anOffset) const{ PRInt32 result=kNotFound; if(aString) { nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mLength=nsCRT::strlen(aString); temp.mUStr=(PRUnichar*)aString; result=nsStr::FindSubstr(*this,temp,aIgnoreCase,anOffset); } return result; } /** * Search for given buffer within this string * * @update gess 3/25/98 * @param nsCString -- buffer to be found * @return offset in string, or -1 (kNotFound) */ PRInt32 nsCString::Find(const nsStr& aString,PRBool aIgnoreCase,PRInt32 anOffset) const{ PRInt32 result=nsStr::FindSubstr(*this,aString,aIgnoreCase,anOffset); return result; } /** * Search for a given char, starting at given offset * * @update gess 3/25/98 * @param * @return offset of found char, or -1 (kNotFound) */ PRInt32 nsCString::Find(PRUnichar aChar,PRInt32 anOffset,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::FindChar(*this,aChar,aIgnoreCase,anOffset); return result; } /** * Search for a given char, starting at given offset * * @update gess 3/25/98 * @param * @return offset of found char, or -1 (kNotFound) */ PRInt32 nsCString::FindChar(PRUnichar aChar,PRBool aIgnoreCase,PRInt32 anOffset) const{ PRInt32 result=nsStr::FindChar(*this,aChar,aIgnoreCase,anOffset); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::FindCharInSet(const char* aCStringSet,PRInt32 anOffset) const{ PRInt32 result=kNotFound; if(aCStringSet) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCStringSet); temp.mStr=(char*)aCStringSet; result=nsStr::FindCharInSet(*this,temp,PR_FALSE,anOffset); } return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::FindCharInSet(const PRUnichar* aStringSet,PRInt32 anOffset) const{ PRInt32 result=kNotFound; if(aStringSet) { nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mLength=nsCRT::strlen(aStringSet); temp.mStr=(char*)aStringSet; result=nsStr::FindCharInSet(*this,temp,PR_FALSE,anOffset); } return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::FindCharInSet(const nsStr& aSet,PRInt32 anOffset) const{ PRInt32 result=nsStr::FindCharInSet(*this,aSet,PR_FALSE,anOffset); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::RFind(const nsStr& aString,PRBool aIgnoreCase,PRInt32 anOffset) const{ PRInt32 result=nsStr::RFindSubstr(*this,aString,aIgnoreCase,anOffset); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::RFind(const char* aString,PRBool aIgnoreCase,PRInt32 anOffset) const{ PRInt32 result=kNotFound; if(aString) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aString); temp.mStr=(char*)aString; result=nsStr::RFindSubstr(*this,temp,aIgnoreCase,anOffset); } return result; } /** * Search for a given char, starting at given offset * * @update gess 3/25/98 * @param * @return offset of found char, or -1 (kNotFound) */ PRInt32 nsCString::RFind(PRUnichar aChar,PRInt32 anOffset,PRBool aIgnoreCase) const{ PRInt32 result=nsStr::RFindChar(*this,aChar,aIgnoreCase,anOffset); return result; } /** * Search for a given char, starting at given offset * * @update gess 3/25/98 * @param * @return offset of found char, or -1 (kNotFound) */ PRInt32 nsCString::RFindChar(PRUnichar aChar,PRBool aIgnoreCase,PRInt32 anOffset) const{ PRInt32 result=nsStr::RFindChar(*this,aChar,aIgnoreCase,anOffset); return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::RFindCharInSet(const char* aCStringSet,PRInt32 anOffset) const{ PRInt32 result=kNotFound; if(aCStringSet) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCStringSet); temp.mStr=(char*)aCStringSet; result=nsStr::RFindCharInSet(*this,temp,PR_FALSE,anOffset); } return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::RFindCharInSet(const PRUnichar* aStringSet,PRInt32 anOffset) const{ PRInt32 result=kNotFound; if(aStringSet) { nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mLength=nsCRT::strlen(aStringSet); temp.mUStr=(PRUnichar*)aStringSet; result=nsStr::RFindCharInSet(*this,temp,PR_FALSE,anOffset); } return result; } /** * * * @update gess 3/25/98 * @param * @return */ PRInt32 nsCString::RFindCharInSet(const nsStr& aSet,PRInt32 anOffset) const{ PRInt32 result=nsStr::RFindCharInSet(*this,aSet,PR_FALSE,anOffset); return result; } /************************************************************** COMPARISON METHODS... **************************************************************/ /** * Compares given cstring to this string. * @update gess 01/04/99 * @param aCString points to a cstring * @param aIgnoreCase tells us how to treat case * @param aCount tells us how many chars to test; -1 implies full length * @return -1,0,1 */ PRInt32 nsCString::Compare(const char *aCString,PRBool aIgnoreCase,PRInt32 aCount) const { if(aCString) { nsStr temp; nsStr::Initialize(temp,eOneByte); temp.mLength=nsCRT::strlen(aCString); temp.mStr=(char*)aCString; return nsStr::Compare(*this,temp,aCount,aIgnoreCase); } return 0; } /** * Compares given unistring to this string. * @update gess 01/04/99 * @param aString pts to a uni-string * @param aIgnoreCase tells us how to treat case * @param aCount tells us how many chars to test; -1 implies full length * @return -1,0,1 */ PRInt32 nsCString::Compare(const PRUnichar* aString,PRBool aIgnoreCase,PRInt32 aCount) const { if(aString) { nsStr temp; nsStr::Initialize(temp,eTwoByte); temp.mLength=nsCRT::strlen(aString); temp.mUStr=(PRUnichar*)aString; return nsStr::Compare(*this,temp,aCount,aIgnoreCase); } return 0; } /** * Compare given nsStr with this cstring. * * @param aString is an nsStr instance to be compared * @param aIgnoreCase tells us how to treat case * @param aCount tells us how many chars to test; -1 implies full length * @return -1,0,1 */ PRInt32 nsCString::Compare(const nsStr& aString,PRBool aIgnoreCase,PRInt32 aCount) const { return nsStr::Compare(*this,aString,aCount,aIgnoreCase); } PRBool nsCString::operator==(const nsStr& S) const {return Equals(S);} PRBool nsCString::operator==(const char* s) const {return Equals(s);} PRBool nsCString::operator==(const PRUnichar* s) const {return Equals(s);} 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::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::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 TESTBED class nsCStringDeallocator: public nsDequeFunctor{ public: virtual void* operator()(void* anObject) { static nsMemoryAgent theAgent; nsCString* aString= (nsCString*)anObject; if(aString){ aString->mAgent=&theAgent; delete aString; } return 0; } }; #endif /**************************************************************************** * This class, appropriately enough, creates and recycles nsCString objects.. ****************************************************************************/ #ifndef 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 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 TESTBED GetRecycler().Recycle(aString); #endif } #if 0 /** * * @update gess 01/04/99 * @param * @return */ void nsCString::DebugDump(ostream& aStream) const { for(PRUint32 i=0;i