1814 lines
45 KiB
C++
1814 lines
45 KiB
C++
// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
|
|
// Copyright (C) 2010 Winch Gate Property Limited
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as
|
|
// published by the Free Software Foundation, either version 3 of the
|
|
// License, or (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
#include "stdmisc.h"
|
|
#include "nel/misc/sstring.h"
|
|
|
|
#ifdef DEBUG_NEW
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
namespace NLMISC
|
|
{
|
|
|
|
CSString CSString::strtok( const char *separators,
|
|
bool useSmartExtensions, // if true then match brackets etc (and refine with following args)
|
|
bool useAngleBrace, // - treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // - treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape) // - treat """" as '"')
|
|
{
|
|
if (useSmartExtensions)
|
|
{
|
|
CSString token;
|
|
|
|
// split to the first non empty token, or until the this string is empty
|
|
while (!empty() && token.empty())
|
|
token = splitToOneOfSeparators(separators,true,useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,true);
|
|
|
|
return token;
|
|
}
|
|
|
|
uint i, j;
|
|
CSString result;
|
|
|
|
// skip leading junk
|
|
for (i=0;i<size();++i)
|
|
{
|
|
// look for the next character in the 'separator' character list supplied
|
|
for (j=0;separators[j] && (*this)[i]!=separators[j];++j)
|
|
{}
|
|
// if not found then we're at end of leading junk
|
|
if (!separators[j])
|
|
break;
|
|
}
|
|
|
|
// copy out everything up to the next separator character
|
|
for (;i<size();++i)
|
|
{
|
|
// look for the next character in the 'separator' character list supplied
|
|
for (j=0;separators[j] && (*this)[i]!=separators[j];++j)
|
|
{}
|
|
// if not found then we're at end of text chunk
|
|
if (separators[j])
|
|
break;
|
|
result+=(*this)[i];
|
|
}
|
|
|
|
// skip trailing junk
|
|
for (;i<size();++i)
|
|
{
|
|
// look for the next character in the 'separator' character list supplied
|
|
for (j=0;separators[j] && (*this)[i]!=separators[j];++j)
|
|
{}
|
|
// if not found then we're at end of leading junk
|
|
if (!separators[j])
|
|
break;
|
|
}
|
|
|
|
// delete the treated bit from this string
|
|
(*this)=substr(i);
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
CSString CSString::splitToOneOfSeparators( const CSString& separators,
|
|
bool truncateThis,
|
|
bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape, // treat """" as '"'
|
|
bool truncateSeparatorCharacter, // if true tail begins after separator char
|
|
bool splitStringAtBrackets)
|
|
{
|
|
// iterate over our string
|
|
uint32 i;
|
|
for (i=0;i<size();++i)
|
|
{
|
|
char thisChar=(*this)[i];
|
|
|
|
// if we've found the separator character then all's cool so break out of the loop
|
|
if (separators.contains(thisChar))
|
|
break;
|
|
|
|
// if we have a bracket or quote of any type then match to it's matching bracket, quote or whatever
|
|
if (isOpeningDelimiter(thisChar,useAngleBrace) || isStringDelimiter(thisChar))
|
|
{
|
|
if (i != 0)
|
|
{
|
|
// we are not at beginning of the string, delimiter is considered as separator
|
|
if (splitStringAtBrackets)
|
|
break;
|
|
}
|
|
uint32 j=i;
|
|
i=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,i);
|
|
// if there was a problem then break here
|
|
if (j==i)
|
|
break;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// build the return string
|
|
CSString result=left(i);
|
|
|
|
// if need be truncate '*this' before returning
|
|
if (truncateThis)
|
|
{
|
|
if (truncateSeparatorCharacter && separators.contains((*this)[i]))
|
|
++i;
|
|
*this=leftCrop(i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitToLineComment(bool truncateThis, bool useSlashStringEscape)
|
|
{
|
|
bool quoteOpen= false;
|
|
bool escape= false;
|
|
for (uint32 i=0;i<size();++i)
|
|
{
|
|
// if we're escaped then accept the next character blindly
|
|
if (escape)
|
|
{
|
|
escape= false;
|
|
continue;
|
|
}
|
|
|
|
// do we have an escape?
|
|
if (useSlashStringEscape && operator[](i)=='\\')
|
|
{
|
|
escape= true;
|
|
}
|
|
// do we have a quote (it is not escaped by definition here)
|
|
else if (operator[](i)=='\"')
|
|
{
|
|
quoteOpen= !quoteOpen;
|
|
}
|
|
// do we have a comment (not in quotes)
|
|
else if (!quoteOpen && i<size()-1 && operator[](i)=='/' && operator[](i+1)=='/')
|
|
{
|
|
// we found a comment so strip string down here
|
|
if (truncateThis)
|
|
{
|
|
CSString result= left(i);
|
|
*this= leftCrop(i);
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return left(i);
|
|
}
|
|
}
|
|
}
|
|
if (truncateThis)
|
|
{
|
|
CSString result= *this;
|
|
clear();
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
return *this;
|
|
}
|
|
}
|
|
|
|
bool CSString::isValidText()
|
|
{
|
|
// setup a handy static lookup table for differentiating valid and invalid text characters
|
|
static bool* tbl=NULL;
|
|
if (tbl==NULL)
|
|
{
|
|
tbl= new bool[256];
|
|
for (uint32 i=0;i<256;++i)
|
|
{
|
|
tbl[i]= ((i>32) || isWhiteSpace((char)i));
|
|
}
|
|
}
|
|
|
|
// scan the string for binary characters
|
|
uint32 i=(uint32)size();
|
|
// while (i && !tbl[i-1])
|
|
// {
|
|
// i--;
|
|
// }
|
|
while (i--)
|
|
{
|
|
if (!tbl[(uint8)operator[](i)])
|
|
{
|
|
nldebug("string is not valid text due to character: %u at index: %u",(uint8)operator[](i),i);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// no binary characters found so return true
|
|
return true;
|
|
}
|
|
|
|
bool CSString::isValidFileName() const
|
|
{
|
|
if (empty())
|
|
return false;
|
|
|
|
if ((*this)[0]=='"')
|
|
{
|
|
if (!isDelimitedMonoBlock(false,false,false))
|
|
return false;
|
|
|
|
// iterate from size-2 to 1
|
|
for (uint i=(uint)size()-1; --i;)
|
|
if (!isValidFileNameChar((*this)[i]) && (*this)[i]!=' ')
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
// iterate from size-1 to 0
|
|
for (uint i=(uint)size(); i--;)
|
|
if (!isValidFileNameChar((*this)[i]))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSString::isValidUnquotedFileName() const
|
|
{
|
|
return (CSString('\"'+*this+'\"')).isValidFileName();
|
|
}
|
|
|
|
bool CSString::isValidKeyword() const
|
|
{
|
|
if (empty())
|
|
return false;
|
|
|
|
if (!isValidKeywordFirstChar((*this)[0]))
|
|
return false;
|
|
|
|
// iterate from size-1 to 1
|
|
for (uint i=(uint)size(); --i;)
|
|
if (!isValidKeywordChar((*this)[i]))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
uint32 CSString::findMatchingDelimiterPos( bool useAngleBrace,
|
|
bool useSlashStringEscape,
|
|
bool useRepeatQuoteStringEscape,
|
|
uint32 startPos ) const
|
|
{
|
|
uint32 i=startPos;
|
|
char openingDelimiter= (*this)[i];
|
|
if (isOpeningDelimiter(openingDelimiter,useAngleBrace))
|
|
{
|
|
// deal with (), [], {} or <> type delimiters
|
|
while (i<size())
|
|
{
|
|
++i;
|
|
if(isMatchingDelimiter(openingDelimiter,(*this)[i]))
|
|
{
|
|
// this is it! we've found the matching quote so we're done
|
|
break;
|
|
}
|
|
if (isOpeningDelimiter((*this)[i],useAngleBrace) || isStringDelimiter((*this)[i]))
|
|
{
|
|
uint32 j=i;
|
|
i=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,i);
|
|
if (j==i)
|
|
return startPos;
|
|
continue;
|
|
}
|
|
if (isClosingDelimiter((*this)[i],useAngleBrace))
|
|
{
|
|
// we've found a closing delimiter that doesn't match our opening delimiter so give up
|
|
return startPos;
|
|
}
|
|
}
|
|
}
|
|
else if (isStringDelimiter(openingDelimiter))
|
|
{
|
|
// deal with "..." or '...' type delimiters
|
|
while (i<size())
|
|
{
|
|
++i;
|
|
if ((*this)[i]==openingDelimiter)
|
|
{
|
|
if (useRepeatQuoteStringEscape && (*this)[i+1]==openingDelimiter)
|
|
{
|
|
// we've found a "" pair and we're treating it as \" equivalent so skip an extra character
|
|
++i;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// this is it! we've found the matching quote so we're done
|
|
break;
|
|
}
|
|
}
|
|
if (useSlashStringEscape && (*this)[i]=='\\')
|
|
{
|
|
// we've found a '\' character so skip the next character, whatever it is
|
|
++i;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
|
|
CSString CSString::matchDelimiters( bool truncateThis,
|
|
bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape) // treat """" as '"'
|
|
{
|
|
// skip white space
|
|
uint32 startPos;
|
|
for (startPos=0;startPos<size() && isWhiteSpace((*this)[startPos]);++startPos) {}
|
|
|
|
// locate the matching brace
|
|
uint32 matchPos=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,startPos);
|
|
|
|
// if not found give up
|
|
if (matchPos>=size())
|
|
{
|
|
return CSString();
|
|
}
|
|
|
|
// build the return string
|
|
CSString result=left(matchPos+1);
|
|
|
|
// if need be truncate '*this' before returning
|
|
if (truncateThis)
|
|
{
|
|
*this=leftCrop(matchPos+1);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitToStringSeparator(
|
|
char separator,
|
|
bool truncateThis,
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape, // treat """" as '"'
|
|
bool truncateSeparatorCharacter) // if true tail begins after separator char
|
|
{
|
|
// iterate over our string
|
|
uint32 i;
|
|
for (i=0;i<size();++i)
|
|
{
|
|
char thisChar=(*this)[i];
|
|
|
|
// if we've found the separator character then all's cool so break out of the loop
|
|
if (thisChar==separator)
|
|
break;
|
|
|
|
// if we have a bracket or quote of any type then match to it's matching bracket, quote or whatever
|
|
if (isStringDelimiter(thisChar))
|
|
{
|
|
uint32 j=i;
|
|
i=findMatchingDelimiterPos(false,useSlashStringEscape,useRepeatQuoteStringEscape,i);
|
|
// if there was a problem then break here
|
|
if (j==i)
|
|
break;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// build the return string
|
|
CSString result=left(i);
|
|
|
|
// if need be truncate '*this' before returning
|
|
if (truncateThis)
|
|
{
|
|
if (truncateSeparatorCharacter && separator==(*this)[i])
|
|
++i;
|
|
*this=leftCrop(i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitToSeparator( char separator,
|
|
bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape) const // treat """" as '"'
|
|
{
|
|
return const_cast<CSString*>(this)->splitToSeparator(separator,false,useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,false);
|
|
}
|
|
|
|
CSString CSString::splitToSeparator( char separator,
|
|
bool truncateThis,
|
|
bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape, // treat """" as '"'
|
|
bool truncateSeparatorCharacter) // if true tail begins after separator char
|
|
{
|
|
// iterate over our string
|
|
uint32 i;
|
|
for (i=0;i<size();++i)
|
|
{
|
|
char thisChar=(*this)[i];
|
|
|
|
// if we've found the separator character then all's cool so break out of the loop
|
|
if (thisChar==separator)
|
|
break;
|
|
|
|
// if we have a bracket or quote of any type then match to it's matching bracket, quote or whatever
|
|
if (isOpeningDelimiter(thisChar,useAngleBrace) || isStringDelimiter(thisChar))
|
|
{
|
|
uint32 j=i;
|
|
i=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,i);
|
|
// if there was a problem then break here
|
|
if (j==i)
|
|
break;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// build the return string
|
|
CSString result=left(i);
|
|
|
|
// if need be truncate '*this' before returning
|
|
if (truncateThis)
|
|
{
|
|
if (truncateSeparatorCharacter && separator==(*this)[i])
|
|
++i;
|
|
*this=leftCrop(i);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitToOneOfSeparators( const CSString& separators,
|
|
bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape) const // treat """" as '"'
|
|
{
|
|
return const_cast<CSString*>(this)->splitToOneOfSeparators(separators,false,useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,false);
|
|
}
|
|
|
|
|
|
bool CSString::isDelimitedMonoBlock( bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape // treat """" as '"'
|
|
) const
|
|
{
|
|
if (empty())
|
|
return false;
|
|
uint32 matchPos=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
return (matchPos==size()-1 && isMatchingDelimiter((*this)[0],(*this)[matchPos]));
|
|
}
|
|
|
|
CSString CSString::stripBlockDelimiters( bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape // treat """" as '"'
|
|
) const
|
|
{
|
|
if (isDelimitedMonoBlock(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape))
|
|
{
|
|
return substr(1,size()-2);
|
|
}
|
|
else
|
|
{
|
|
return *this;
|
|
}
|
|
}
|
|
|
|
bool CSString::splitWords(CVectorSString& result) const
|
|
{
|
|
CSString s=strip();
|
|
while(!s.empty())
|
|
{
|
|
uint pre=(uint)s.size();
|
|
result.push_back(s.firstWord(true));
|
|
uint post=(uint)s.size();
|
|
if (post>=pre)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSString::splitWordOrWords(CVectorSString& result,bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
CSString s=*this;
|
|
while(!s.empty())
|
|
{
|
|
uint pre=(uint)s.size();
|
|
result.push_back(s.firstWordOrWords(true,useSlashStringEscape,useRepeatQuoteStringEscape));
|
|
uint post=(uint)s.size();
|
|
if (post>=pre)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSString::splitLines(CVectorSString& result) const
|
|
{
|
|
CSString s=*this;
|
|
|
|
// make sure we deal with '\n\r' style carriage returns cleanly
|
|
if (s.contains('\r'))
|
|
s=s.replace("\r","");
|
|
|
|
uint it=0;
|
|
uint len= (uint)s.size();
|
|
while(it<len)
|
|
{
|
|
// extract the text up to the next '\n'character
|
|
result.push_back(s.splitToWithIterator('\n',it));
|
|
// skip the '\n' character
|
|
++it;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSString::splitBySeparator( char separator, CVectorSString& result,
|
|
bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape, // treat """" as '"'
|
|
bool skipBlankEntries // dont add blank entries to the result vector
|
|
) const
|
|
{
|
|
CSString s=*this;
|
|
while(!s.empty())
|
|
{
|
|
uint pre=(uint)s.size();
|
|
result.push_back(s.splitToSeparator(separator,true,useAngleBrace,useSlashStringEscape,
|
|
useRepeatQuoteStringEscape,true));
|
|
if (skipBlankEntries && result.back().empty())
|
|
result.pop_back();
|
|
uint post=(uint)s.size();
|
|
if (post>=pre)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSString::splitByOneOfSeparators( const CSString& separators, CVectorSString& result,
|
|
bool useAngleBrace, // treat '<' and '>' as brackets
|
|
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
|
|
bool useRepeatQuoteStringEscape,// treat """" as '"'
|
|
bool retainSeparators, // have the separators turn up in the result vector
|
|
bool skipBlankEntries // dont add blank entries to the result vector
|
|
) const
|
|
{
|
|
CSString s=*this;
|
|
while (!s.empty() && skipBlankEntries && !retainSeparators && separators.contains(s[0]))
|
|
s= s.leftCrop(1);
|
|
|
|
while(!s.empty())
|
|
{
|
|
uint pre=(uint)s.size();
|
|
result.push_back(s.splitToOneOfSeparators( separators,true,useAngleBrace,useSlashStringEscape,
|
|
useRepeatQuoteStringEscape,!retainSeparators ));
|
|
|
|
// if we failed to extract a string segment then we must be looking at a separator
|
|
if (result.back().empty())
|
|
{
|
|
if (skipBlankEntries && result.back().empty())
|
|
result.pop_back();
|
|
|
|
if (!s.empty())
|
|
{
|
|
if (retainSeparators)
|
|
result.back()=s[0];
|
|
s=s.leftCrop(1);
|
|
}
|
|
}
|
|
|
|
uint post=(uint)s.size();
|
|
if (post>=pre)
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
const CSString& CSString::join(const std::vector<CSString>& strings, const CSString& separator)
|
|
{
|
|
for (uint32 i=0;i<strings.size();++i)
|
|
{
|
|
// add in separators before all but the first string
|
|
if (!empty())
|
|
operator+=(separator);
|
|
// append next string
|
|
operator+=(strings[i]);
|
|
}
|
|
|
|
// return a ref to ourselves
|
|
return *this;
|
|
}
|
|
|
|
const CSString& CSString::join(const std::vector<CSString>& strings, char separator)
|
|
{
|
|
for (uint32 i=0;i<strings.size();++i)
|
|
{
|
|
// add in separators before all but the first string
|
|
if (!empty())
|
|
operator+=(separator);
|
|
// append next string
|
|
operator+=(strings[i]);
|
|
}
|
|
|
|
// return a ref to ourselves
|
|
return *this;
|
|
}
|
|
|
|
CSString CSString::strip() const
|
|
{
|
|
CSString result;
|
|
int i,j;
|
|
for (j=(int)size()-1; j>=0 && isWhiteSpace((*this)[j]); --j) {}
|
|
for (i=0; i<j && isWhiteSpace((*this)[i]); ++i) {}
|
|
result=substr(i,j-i+1);
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::leftStrip() const
|
|
{
|
|
CSString result;
|
|
int i,j=(int)size()-1;
|
|
for (i=0; i<j && isWhiteSpace((*this)[i]); ++i) {}
|
|
result=substr(i,j-i+1);
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::rightStrip() const
|
|
{
|
|
CSString result;
|
|
int i=0,j;
|
|
for (j=(int)size()-1; j>=0 && isWhiteSpace((*this)[j]); --j) {}
|
|
result=substr(i,j-i+1);
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::toUpper() const
|
|
{
|
|
CSString result;
|
|
std::string::const_iterator it;
|
|
for (it=begin();it!=end();++it)
|
|
{
|
|
char c=(*it);
|
|
if (c>='a' && c<='z')
|
|
c^=('a'^'A');
|
|
result+=c;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::toLower() const
|
|
{
|
|
CSString result;
|
|
std::string::const_iterator it;
|
|
for (it=begin();it!=end();++it)
|
|
{
|
|
char c=(*it);
|
|
if (c>='A' && c<='Z')
|
|
c^=('a'^'A');
|
|
result+=c;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitTo(char c) const
|
|
{
|
|
uint i;
|
|
CSString result;
|
|
for (i=0;i<size() && (*this)[i]!=c;++i)
|
|
result+=(*this)[i];
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitTo(char c,bool truncateThis,bool absorbSeparator)
|
|
{
|
|
uint i;
|
|
CSString result;
|
|
for (i=0;i<size() && (*this)[i]!=c;++i)
|
|
result+=(*this)[i];
|
|
|
|
// remove the result string from the input string if so desired
|
|
if (truncateThis)
|
|
{
|
|
if (absorbSeparator)
|
|
++i;
|
|
if (i<size())
|
|
(*this)=substr(i);
|
|
else
|
|
clear();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitTo(const char *s,bool truncateThis)
|
|
{
|
|
uint i;
|
|
CSString result;
|
|
for (i=0;i<size();++i)
|
|
{
|
|
// perform a quick string compare
|
|
uint j;
|
|
for (j=0;s[j]!=0 && s[j]==(&((*this)[i]))[j];++j)
|
|
{
|
|
}
|
|
// if string compare matched then return result so far
|
|
if (s[j]==0)
|
|
{
|
|
// remove the result string from the input string if so desired
|
|
if (truncateThis)
|
|
{
|
|
if (i+1<size())
|
|
(*this)=substr(i+1); // +1 to skip the separator character
|
|
else
|
|
clear();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
result+=(*this)[i];
|
|
}
|
|
// we didn't find the separator string so we're returning a copy of the whole string
|
|
if (truncateThis)
|
|
clear();
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitFrom(char c) const
|
|
{
|
|
CSString result;
|
|
std::string::const_iterator it;
|
|
for (it=begin();it!=end() && *it!=c;++it)
|
|
{}
|
|
if (it!=end())
|
|
{
|
|
++it;
|
|
for (;it!=end();++it)
|
|
result+=*it;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::splitFrom(const char *s) const
|
|
{
|
|
uint i;
|
|
CSString result;
|
|
for (i=0;i<size();++i)
|
|
{
|
|
// perform a quick string compare
|
|
uint j;
|
|
for (j=0;i+j<size() && s[j]!=0 && s[j]==(*this)[i+j];++j)
|
|
{
|
|
}
|
|
// if string compare matched then build and return a result
|
|
if (s[j]==0)
|
|
{
|
|
result=substr(i+j);
|
|
return result;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
CSString CSString::firstWord(bool truncateThis)
|
|
{
|
|
// idiot test to avoid accessing index 0 in empty strings
|
|
if (empty())
|
|
return CSString();
|
|
|
|
CSString result;
|
|
uint i=0;
|
|
// skip white space
|
|
for (i=0;i<size() && isWhiteSpace((*this)[i]);++i)
|
|
{}
|
|
|
|
if ( ((*this)[i]>='A' && (*this)[i]<='Z') || ((*this)[i]>='a' && (*this)[i]<='z') ||
|
|
((*this)[i]>='0' && (*this)[i]<='9') || (*this)[i]=='_')
|
|
{
|
|
// copy out an alpha-numeric string
|
|
for (;i<(*this).size() &&
|
|
( ((*this)[i]>='A' && (*this)[i]<='Z') || ((*this)[i]>='a' && (*this)[i]<='z') ||
|
|
((*this)[i]>='0' && (*this)[i]<='9') || (*this)[i]=='_')
|
|
;++i)
|
|
result+=(*this)[i];
|
|
}
|
|
else
|
|
{
|
|
// just take the first character of the input
|
|
result=(*this)[i];
|
|
++i;
|
|
}
|
|
|
|
// remove the result string from the input string if so desired
|
|
if (truncateThis)
|
|
{
|
|
if (i<size())
|
|
(*this)=substr(i);
|
|
else
|
|
clear();
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::firstWordConst() const
|
|
{
|
|
return const_cast<CSString *>(this)->firstWord();
|
|
}
|
|
|
|
CSString CSString::tailFromFirstWord() const
|
|
{
|
|
CSString hold=*this;
|
|
hold.firstWord(true);
|
|
return hold;
|
|
}
|
|
|
|
unsigned CSString::countWords() const
|
|
{
|
|
unsigned count=0;
|
|
CSString hold=strip();
|
|
while (!hold.empty())
|
|
{
|
|
hold=hold.tailFromFirstWord().strip();
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
CSString CSString::word(uint32 idx) const
|
|
{
|
|
CSString hold=strip();
|
|
|
|
for (unsigned count=0;count<idx;++count)
|
|
hold=hold.tailFromFirstWord().strip();
|
|
|
|
return hold.firstWord();
|
|
}
|
|
|
|
CSString CSString::firstWordOrWords(bool truncateThis,bool useSlashStringEscape,bool useRepeatQuoteStringEscape)
|
|
{
|
|
uint32 startPos;
|
|
for (startPos=0;startPos<size();++startPos)
|
|
if (!isWhiteSpace((*this)[startPos]))
|
|
break;
|
|
|
|
if (isStringDelimiter((*this)[startPos]))
|
|
{
|
|
uint32 endPos= findMatchingDelimiterPos(false,useSlashStringEscape,useRepeatQuoteStringEscape,startPos);
|
|
CSString result=substr(startPos,endPos-startPos+1);
|
|
result=result.unquote(useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
if (truncateThis)
|
|
*this=leftCrop(endPos+1);
|
|
return result;
|
|
}
|
|
else
|
|
return firstWord(truncateThis);
|
|
}
|
|
|
|
CSString CSString::firstWordOrWordsConst(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
return const_cast<CSString *>(this)->firstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
}
|
|
|
|
CSString CSString::tailFromFirstWordOrWords(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
CSString hold=*this;
|
|
hold.firstWordOrWords(true,useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
return hold;
|
|
}
|
|
|
|
unsigned CSString::countWordOrWords(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
unsigned count=0;
|
|
CSString hold=strip();
|
|
while (!hold.empty())
|
|
{
|
|
hold=hold.tailFromFirstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape).strip();
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
CSString CSString::wordOrWords(uint32 idx,bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
CSString hold=strip();
|
|
|
|
for (unsigned count=0;count<idx;++count)
|
|
hold=hold.tailFromFirstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape).strip();
|
|
|
|
return hold.firstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
}
|
|
|
|
|
|
CSString CSString::firstLine(bool truncateThis)
|
|
{
|
|
return splitTo('\n',truncateThis);
|
|
}
|
|
|
|
CSString CSString::firstLineConst() const
|
|
{
|
|
return const_cast<CSString *>(this)->firstLine();
|
|
}
|
|
|
|
CSString CSString::tailFromFirstLine() const
|
|
{
|
|
CSString hold=*this;
|
|
hold.firstLine(true);
|
|
return hold;
|
|
}
|
|
|
|
unsigned CSString::countLines() const
|
|
{
|
|
unsigned count=0;
|
|
CSString hold=strip();
|
|
while (!hold.empty())
|
|
{
|
|
hold=hold.tailFromFirstLine().strip();
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
CSString CSString::line(uint32 idx) const
|
|
{
|
|
CSString hold=strip();
|
|
|
|
for (unsigned count=0;count<idx;++count)
|
|
hold= hold.tailFromFirstLine().strip();
|
|
|
|
return hold.firstLine();
|
|
}
|
|
|
|
|
|
CSString CSString::quote(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
CSString result;
|
|
|
|
result+='\"';
|
|
for (uint32 i=0;i<size();++i)
|
|
{
|
|
switch ((*this)[i])
|
|
{
|
|
case '\"':
|
|
if (useSlashStringEscape)
|
|
{
|
|
result+="\\\"";
|
|
continue;
|
|
}
|
|
else if (useRepeatQuoteStringEscape)
|
|
{
|
|
result+="\"\"";
|
|
continue;
|
|
}
|
|
break;
|
|
case '\\': if (useSlashStringEscape) { result+="\\\\"; continue; } break;
|
|
case '\a': if (useSlashStringEscape) { result+="\\a"; continue; } break;
|
|
case '\b': if (useSlashStringEscape) { result+="\\b"; continue; } break;
|
|
case '\f': if (useSlashStringEscape) { result+="\\f"; continue; } break;
|
|
case '\n': if (useSlashStringEscape) { result+="\\n"; continue; } break;
|
|
case '\r': if (useSlashStringEscape) { result+="\\r"; continue; } break;
|
|
case '\t': if (useSlashStringEscape) { result+="\\t"; continue; } break;
|
|
case '\v': if (useSlashStringEscape) { result+="\\v"; continue; } break;
|
|
break;
|
|
default:
|
|
if ((signed char)(*this)[i]<32 && useSlashStringEscape)
|
|
{
|
|
result+=NLMISC::toString("\\x%02x",(unsigned char)(*this)[i]);
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
result+=(*this)[i];
|
|
}
|
|
result+='\"';
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::quoteIfNotQuoted( bool useSlashStringEscape, bool useRepeatQuoteStringEscape ) const
|
|
{
|
|
if (empty()||(*this)[0]!='\"'||!isDelimitedMonoBlock(false,useSlashStringEscape,useRepeatQuoteStringEscape))
|
|
return quote(useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
|
|
return *this;
|
|
}
|
|
|
|
CSString CSString::quoteIfNotAtomic( bool useSlashStringEscape, bool useRepeatQuoteStringEscape ) const
|
|
{
|
|
if (empty())
|
|
return "\"\"";
|
|
|
|
uint32 i=1;
|
|
if (((*this)[0]>='0' && (*this)[0]<='9')||(*this)[0]=='-')
|
|
{
|
|
for (i=1;i<size();++i)
|
|
if ((*this)[i]<'0' || (*this)[i]>'9')
|
|
break;
|
|
}
|
|
else if ( CSString::isValidKeywordFirstChar((*this)[0]) )
|
|
{
|
|
for (i=1;i<size();++i)
|
|
if (!CSString::isValidFileNameChar((*this)[i]))
|
|
break;
|
|
}
|
|
else if ((*this)[0]=='\"' && isDelimitedMonoBlock(false,useSlashStringEscape,useRepeatQuoteStringEscape))
|
|
{
|
|
i=(uint)size();
|
|
}
|
|
if (i!=size())
|
|
return quote(useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
|
|
return *this;
|
|
}
|
|
|
|
CSString CSString::unquote(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
CSString result=stripBlockDelimiters(false,useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
uint32 i,j;
|
|
for (i=0,j=0;i<result.size();++i,++j)
|
|
{
|
|
if (useSlashStringEscape && result[i]=='\\')
|
|
{
|
|
++i;
|
|
if (i<result.size())
|
|
{
|
|
switch(result[i])
|
|
{
|
|
case 'a': result[i]='\a'; break;
|
|
case 'b': result[i]='\b'; break;
|
|
case 'f': result[i]='\f'; break;
|
|
case 'n': result[i]='\n'; break;
|
|
case 'r': result[i]='\r'; break;
|
|
case 't': result[i]='\t'; break;
|
|
case 'v': result[i]='\v'; break;
|
|
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
{
|
|
char hold=result[i]-'0';
|
|
++i;
|
|
if (i<result.size() && result[i]>='0' && result[i]<='7')
|
|
{
|
|
hold=8*hold+(result[i]-'0');
|
|
++i;
|
|
if (i<result.size() && result[i]>='0' && result[i]<='7')
|
|
{
|
|
hold=8*hold+(result[i]-'0');
|
|
++i;
|
|
}
|
|
}
|
|
result[j]=hold;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
{
|
|
char hold=result[i]-'0';
|
|
++i;
|
|
if (i<result.size() && result[i]>='0' && result[i]<='7')
|
|
{
|
|
hold=8*hold+(result[i]-'0');
|
|
++i;
|
|
}
|
|
result[j]=hold;
|
|
continue;
|
|
}
|
|
break;
|
|
|
|
case 'x':
|
|
if (i+1<result.size() && isHexDigit(result[i+1]))
|
|
{
|
|
char hold=convertHexDigit(result[i+1]);
|
|
i+=2;
|
|
if (i<result.size() && isHexDigit(result[i]))
|
|
{
|
|
hold=16*hold+convertHexDigit(result[i]);
|
|
++i;
|
|
}
|
|
result[j]=hold;
|
|
continue;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (useRepeatQuoteStringEscape && (i+1<result.size()) && result[i]=='\"' && result[i+1]=='\"')
|
|
++i;
|
|
if (i<result.size())
|
|
result[j]=result[i];
|
|
}
|
|
if (i!=j)
|
|
result.resize(j);
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::unquoteIfQuoted(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
|
|
{
|
|
if (isQuoted())
|
|
return unquote(useSlashStringEscape,useRepeatQuoteStringEscape);
|
|
else
|
|
return *this;
|
|
}
|
|
|
|
CSString CSString::encodeXML(bool isParameter) const
|
|
{
|
|
CSString result;
|
|
|
|
for (uint32 i=0;i<size();++i)
|
|
{
|
|
unsigned char c= (*this)[i];
|
|
switch(c)
|
|
{
|
|
// special xml characters
|
|
case '\"': result+="""; continue;
|
|
case '&': result+="&"; continue;
|
|
case '<': result+="<"; continue;
|
|
case '>': result+=">"; continue;
|
|
|
|
// characters that are not allowed inside a parameter block
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
if (!isParameter) { result+=c; continue; }
|
|
}
|
|
|
|
// hex coding for extended characters
|
|
if (c<32 || c>127)
|
|
{
|
|
result+="&#x";
|
|
result+= ((c>>4)>=10? 'A'+(c>>4)-10: '0'+(c>>4));
|
|
result+= ((c&15)>=10? 'A'+(c&15)-10: '0'+(c&15));
|
|
result+=";";
|
|
continue;
|
|
}
|
|
|
|
// all the special cases are catered for... treat this as a normal character
|
|
result+=c;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
CSString CSString::decodeXML() const
|
|
{
|
|
CSString result;
|
|
|
|
for (uint32 i=0;i<size();++i)
|
|
{
|
|
breakable
|
|
{
|
|
if((*this)[i]=='&')
|
|
{
|
|
// special xml characters
|
|
if (substr(i+1,5)=="quot;") { i+=5; result+='\"'; continue; }
|
|
if (substr(i+1,4)=="amp;") { i+=4; result+='&'; continue; }
|
|
if (substr(i+1,3)=="lt;") { i+=3; result+='<'; continue; }
|
|
if (substr(i+1,3)=="gt;") { i+=3; result+='>'; continue; }
|
|
|
|
// hex coding for extended characters
|
|
if ((size()-i)>=5)
|
|
{
|
|
if ((*this)[i+1]!='#') break;
|
|
if (((*this)[i+2]|('a'-'A'))!='x') break;
|
|
// setup j to point at the first character following "&#x"[0-9|a-f]*
|
|
uint32 j; for (j=i+3;j<size();++j) if (!isHexDigit((*this)[j])) break;
|
|
// make sure that at least 1 hex character was found
|
|
if (j==i+3) break;
|
|
// make sure have the terminating ';' to complete the token: "&#x"[0-9|a-f]*";"
|
|
if (j>=size() || (*this)[j]!=';') break;
|
|
// make sure that the value we have is only one or 2 hex digits
|
|
if (j>=i+6) nlwarning("Truncating extended char code '%s",(substr(i,j-i+1)+"' => using "+substr(j-2,2)).c_str());
|
|
|
|
// convert the 1 or 2 last hex digits to a char value
|
|
char c=0;
|
|
if (j>=i+5)
|
|
{
|
|
// if we have 2 or more digits then grab the high digit
|
|
c+= convertHexDigit( (*this)[j-2] )*16;
|
|
}
|
|
c+= convertHexDigit( (*this)[j-1] );
|
|
|
|
// append our new character to the result string
|
|
result+=c;
|
|
// move 'i' forward to point at the ';' .. the for(...) will increment i to point to next char
|
|
i=j;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
// all the special cases are catered for... treat this as a normal character
|
|
result+=(*this)[i];
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
bool CSString::isEncodedXML() const
|
|
{
|
|
bool foundToken= false;
|
|
|
|
for (uint i=(uint)size();i--;)
|
|
{
|
|
switch((*this)[i])
|
|
{
|
|
// decoded special xml characters
|
|
case '\"':
|
|
case '<':
|
|
case '>':
|
|
case '&':
|
|
return false;
|
|
|
|
case ';':
|
|
// encoded special xml characters
|
|
if (i>=5 && substr(i-5,5)==""") { foundToken= true; i-=5; break; }
|
|
if (i>=4 && substr(i-4,4)=="&") { foundToken= true; i-=4; break; }
|
|
if (i>=3 && substr(i-3,3)=="<") { foundToken= true; i-=3; break; }
|
|
if (i>=3 && substr(i-3,3)==">") { foundToken= true; i-=3; break; }
|
|
|
|
// hex coding for extended characters
|
|
if (i>=3 && isHexDigit((*this)[i-1]))
|
|
{
|
|
uint32 j,k;
|
|
// locate the start of a potential hex string
|
|
for (j=i;j--;)
|
|
if ((*this)[j]=='&')
|
|
break;
|
|
// make sure that at least 5 chars were found for: �
|
|
if (i-j<4) continue;
|
|
// make sure we have '&#x' at the start
|
|
if ((*this)[j]!='&') continue;
|
|
if ((*this)[j+1]!='#') continue;
|
|
if ((*this)[j+2]!='x') continue;
|
|
// ensure that the remainder between the leading '&#x' and trailing ';' are hex digits
|
|
for (k=j+3;k<i;++k)
|
|
if (!isHexDigit((*this)[k]))
|
|
break;
|
|
if (k!=i) continue;
|
|
// skip the characters that were matched - i now refs the opening '&'
|
|
i=j;
|
|
foundToken= true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return foundToken;
|
|
}
|
|
|
|
bool CSString::isXMLCompatible(bool isParameter) const
|
|
{
|
|
for (uint i=(uint)size();i--;)
|
|
{
|
|
switch((*this)[i])
|
|
{
|
|
// decoded special xml characters
|
|
case '\"':
|
|
case '<':
|
|
case '>':
|
|
case '&':
|
|
return false;
|
|
|
|
case ';':
|
|
// encoded special xml characters
|
|
if (i>=5 && substr(i-5,5)==""") { i-=5; continue; }
|
|
if (i>=4 && substr(i-4,4)=="&") { i-=4; continue; }
|
|
if (i>=3 && substr(i-3,3)=="<") { i-=3; continue; }
|
|
if (i>=3 && substr(i-3,3)==">") { i-=3; continue; }
|
|
|
|
// hex coding for extended characters
|
|
if (i>=3 && isHexDigit((*this)[i-1]))
|
|
{
|
|
uint32 j,k;
|
|
// locate the start of a potential hex string
|
|
for (j=i;j--;)
|
|
if ((*this)[j]=='&')
|
|
break;
|
|
// make sure that at least 5 chars were found for: �
|
|
if (i-j<4) continue;
|
|
// make sure we have '&#x' at the start
|
|
if ((*this)[j]!='&') continue;
|
|
if ((*this)[j+1]!='#') continue;
|
|
if ((*this)[j+2]!='x') continue;
|
|
// ensure that the remainder between the leading '&#x' and trailing ';' are hex digits
|
|
for (k=j+3;k<i;++k)
|
|
if (!isHexDigit((*this)[k]))
|
|
break;
|
|
if (k!=i) continue;
|
|
// skip the characters that were matched - i now refs the opening '&'
|
|
i=j;
|
|
continue;
|
|
}
|
|
|
|
// characters that are not allowed inside a parameter block
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
if (!isParameter) continue;
|
|
}
|
|
|
|
if ((uint8)((*this)[i])>127 || (uint8)((*this))[i]<32)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
CSString CSString::replace(const char *toFind,const char *replacement) const
|
|
{
|
|
// just bypass the problems that can cause a crash...
|
|
if (toFind==NULL || *toFind==0)
|
|
return *this;
|
|
|
|
std::string::size_type i,j;
|
|
CSString result;
|
|
for (i=0;i<size();)
|
|
{
|
|
// string compare toFind against (*this)+i ...
|
|
for (j=0;toFind[j];++j)
|
|
if ((*this)[i+j]!=toFind[j])
|
|
break;
|
|
// if strings were identical then j reffers to ASCIIZ terminator at end of 'toFind'
|
|
if (toFind[j]==0)
|
|
{
|
|
if (replacement!=NULL)
|
|
result+=replacement;
|
|
i+=j;
|
|
}
|
|
else
|
|
{
|
|
result+=(*this)[i];
|
|
++i;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
std::string::size_type CSString::find(const char *toFind, std::string::size_type startLocation) const
|
|
{
|
|
// const char *constStr = c_str();
|
|
|
|
// just bypass the problems that can cause a crash...
|
|
if (toFind==NULL || *toFind==0 || startLocation>=size())
|
|
return std::string::npos;
|
|
|
|
std::string::size_type i,j;
|
|
for (i=startLocation;i<size();++i)
|
|
{
|
|
// string compare toFind against (*this)+i ...
|
|
for (j=0;toFind[j];++j)
|
|
if ((i+j>=size()) || (*this)[i+j]!=toFind[j])
|
|
break;
|
|
// if strings were identical then we're done
|
|
if (toFind[j]==0)
|
|
return i;
|
|
}
|
|
return std::string::npos;
|
|
}
|
|
|
|
/// Find index at which a sub-string starts (case NOT sensitive) - if sub-string not found then returns string::npos
|
|
std::string::size_type CSString::findNS(const char *toFind, std::string::size_type startLocation) const
|
|
{
|
|
const char *constStr = c_str();
|
|
|
|
// just bypass the problems that can cause a crash...
|
|
if (toFind==NULL || *toFind==0 || startLocation>=size())
|
|
return std::string::npos;
|
|
|
|
std::string::size_type i,j;
|
|
for (i=startLocation;i<size();++i)
|
|
{
|
|
// string compare toFind against (*this)+i ...
|
|
for (j=0;toFind[j];++j)
|
|
if ((i+j>=size()) || tolower(constStr[i+j])!=tolower(toFind[j]))
|
|
break;
|
|
// if strings were identical then we're done
|
|
if (toFind[j]==0)
|
|
return i;
|
|
}
|
|
return std::string::npos;
|
|
}
|
|
|
|
bool CSString::contains(const char *toFind) const
|
|
{
|
|
return find(toFind)!=std::string::npos;
|
|
}
|
|
|
|
bool CSString::contains(int character) const
|
|
{
|
|
for (const_iterator it=begin();it!=end();++it)
|
|
if ((*it)==character)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static const uint32 MaxUint32= ~0u;
|
|
static const uint32 MaxUint32LastDigit= MaxUint32-(MaxUint32/10)*10;
|
|
static const uint32 MaxUint32PreLastDigit= (MaxUint32/10);
|
|
|
|
static const uint32 MaxNegSint32= ~0u/2+1;
|
|
static const uint32 MaxNegSint32LastDigit= MaxNegSint32-(MaxNegSint32/10)*10;
|
|
static const uint32 MaxNegSint32PreLastDigit= (MaxNegSint32/10);
|
|
|
|
static const uint32 MaxPosSint32= ~0u/2;
|
|
static const uint32 MaxPosSint32LastDigit= MaxPosSint32-(MaxPosSint32/10)*10;
|
|
static const uint32 MaxPosSint32PreLastDigit= (MaxPosSint32/10);
|
|
|
|
int CSString::atoi() const
|
|
{
|
|
if (empty())
|
|
return 0;
|
|
|
|
bool neg= false;
|
|
uint32 result;
|
|
switch (*begin())
|
|
{
|
|
case '+': result=0; break;
|
|
case '-': result=0; neg=true; break;
|
|
case '0': result=0; break;
|
|
case '1': result=1; break;
|
|
case '2': result=2; break;
|
|
case '3': result=3; break;
|
|
case '4': result=4; break;
|
|
case '5': result=5; break;
|
|
case '6': result=6; break;
|
|
case '7': result=7; break;
|
|
case '8': result=8; break;
|
|
case '9': result=9; break;
|
|
default: return 0;
|
|
}
|
|
|
|
for (const_iterator it=begin()+1;it!=end();++it)
|
|
{
|
|
uint32 offset;
|
|
switch (*it)
|
|
{
|
|
case '0': offset=0; break;
|
|
case '1': offset=1; break;
|
|
case '2': offset=2; break;
|
|
case '3': offset=3; break;
|
|
case '4': offset=4; break;
|
|
case '5': offset=5; break;
|
|
case '6': offset=6; break;
|
|
case '7': offset=7; break;
|
|
case '8': offset=8; break;
|
|
case '9': offset=9; break;
|
|
default: return 0;
|
|
}
|
|
if (!neg)
|
|
{
|
|
if (result>=MaxUint32PreLastDigit/*~0u/10*/)
|
|
{
|
|
if (result>MaxUint32PreLastDigit || offset>MaxUint32LastDigit)
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (result>=MaxNegSint32PreLastDigit /*~0u/20+1*/)
|
|
{
|
|
if (result>MaxNegSint32PreLastDigit || offset>MaxNegSint32LastDigit)
|
|
return 0;
|
|
}
|
|
}
|
|
result=10*result+offset;
|
|
}
|
|
return neg? -(sint32)result: (sint32)result;
|
|
}
|
|
|
|
sint32 CSString::atosi() const
|
|
{
|
|
if (empty())
|
|
return 0;
|
|
|
|
bool neg= false;
|
|
uint32 result;
|
|
switch (*begin())
|
|
{
|
|
case '+': result=0; break;
|
|
case '-': result=0; neg=true; break;
|
|
case '0': result=0; break;
|
|
case '1': result=1; break;
|
|
case '2': result=2; break;
|
|
case '3': result=3; break;
|
|
case '4': result=4; break;
|
|
case '5': result=5; break;
|
|
case '6': result=6; break;
|
|
case '7': result=7; break;
|
|
case '8': result=8; break;
|
|
case '9': result=9; break;
|
|
default: return 0;
|
|
}
|
|
|
|
for (const_iterator it=begin()+1;it!=end();++it)
|
|
{
|
|
uint32 offset;
|
|
switch (*it)
|
|
{
|
|
case '0': offset=0; break;
|
|
case '1': offset=1; break;
|
|
case '2': offset=2; break;
|
|
case '3': offset=3; break;
|
|
case '4': offset=4; break;
|
|
case '5': offset=5; break;
|
|
case '6': offset=6; break;
|
|
case '7': offset=7; break;
|
|
case '8': offset=8; break;
|
|
case '9': offset=9; break;
|
|
default: return 0;
|
|
}
|
|
if (result>=MaxPosSint32PreLastDigit /*~0u/20*/)
|
|
{
|
|
if (result>MaxPosSint32PreLastDigit || offset>(neg?MaxNegSint32LastDigit:MaxPosSint32LastDigit))
|
|
return 0;
|
|
}
|
|
result=10*result+offset;
|
|
}
|
|
return neg? -(sint32)result: (sint32)result;
|
|
}
|
|
|
|
uint32 CSString::atoui() const
|
|
{
|
|
uint32 result=0;
|
|
for (const_iterator it=begin();it!=end();++it)
|
|
{
|
|
uint32 offset;
|
|
switch (*it)
|
|
{
|
|
case '0': offset=0; break;
|
|
case '1': offset=1; break;
|
|
case '2': offset=2; break;
|
|
case '3': offset=3; break;
|
|
case '4': offset=4; break;
|
|
case '5': offset=5; break;
|
|
case '6': offset=6; break;
|
|
case '7': offset=7; break;
|
|
case '8': offset=8; break;
|
|
case '9': offset=9; break;
|
|
default: return 0;
|
|
}
|
|
if (result>=MaxUint32PreLastDigit/*~0u/10*/)
|
|
{
|
|
if (result>MaxUint32PreLastDigit || offset>MaxUint32LastDigit)
|
|
return 0;
|
|
}
|
|
result=10*result+offset;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static const uint64 MaxUint64= (uint64)0-(uint64)1;
|
|
static const uint64 MaxUint64LastDigit= MaxUint64-(MaxUint64/10)*10;
|
|
static const uint64 MaxUint64PreLastDigit= (MaxUint64/10);
|
|
|
|
static const uint64 MaxNegSint64= ((uint64)0-(uint64)1)/2+1;
|
|
static const uint64 MaxNegSint64LastDigit= MaxNegSint64-(MaxNegSint64/10)*10;
|
|
static const uint64 MaxNegSint64PreLastDigit= (MaxNegSint64/10);
|
|
|
|
static const uint64 MaxPosSint64= ((uint64)0-(uint64)1)/2;
|
|
static const uint64 MaxPosSint64LastDigit= MaxPosSint64-(MaxPosSint64/10)*10;
|
|
static const uint64 MaxPosSint64PreLastDigit= (MaxPosSint64/10);
|
|
|
|
sint64 CSString::atoi64() const
|
|
{
|
|
if (empty())
|
|
return 0;
|
|
|
|
bool neg= false;
|
|
uint64 result;
|
|
switch (*begin())
|
|
{
|
|
case '+': result=0; break;
|
|
case '-': result=0; neg=true; break;
|
|
case '0': result=0; break;
|
|
case '1': result=1; break;
|
|
case '2': result=2; break;
|
|
case '3': result=3; break;
|
|
case '4': result=4; break;
|
|
case '5': result=5; break;
|
|
case '6': result=6; break;
|
|
case '7': result=7; break;
|
|
case '8': result=8; break;
|
|
case '9': result=9; break;
|
|
default: return 0;
|
|
}
|
|
|
|
for (const_iterator it=begin()+1;it!=end();++it)
|
|
{
|
|
uint64 offset;
|
|
switch (*it)
|
|
{
|
|
case '0': offset=0; break;
|
|
case '1': offset=1; break;
|
|
case '2': offset=2; break;
|
|
case '3': offset=3; break;
|
|
case '4': offset=4; break;
|
|
case '5': offset=5; break;
|
|
case '6': offset=6; break;
|
|
case '7': offset=7; break;
|
|
case '8': offset=8; break;
|
|
case '9': offset=9; break;
|
|
default: return 0;
|
|
}
|
|
if (!neg)
|
|
{
|
|
if (result>=MaxUint64PreLastDigit/*~0u/10*/)
|
|
{
|
|
if (result>MaxUint64PreLastDigit || offset>MaxUint64LastDigit)
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (result>=MaxNegSint64PreLastDigit /*~0u/20+1*/)
|
|
{
|
|
if (result>MaxNegSint64PreLastDigit || offset>MaxNegSint64LastDigit)
|
|
return 0;
|
|
}
|
|
}
|
|
result=10*result+offset;
|
|
}
|
|
return neg? -(sint64)result: (sint64)result;
|
|
}
|
|
|
|
sint64 CSString::atosi64() const
|
|
{
|
|
if (empty())
|
|
return 0;
|
|
|
|
bool neg= false;
|
|
uint64 result;
|
|
switch (*begin())
|
|
{
|
|
case '+': result=0; break;
|
|
case '-': result=0; neg=true; break;
|
|
case '0': result=0; break;
|
|
case '1': result=1; break;
|
|
case '2': result=2; break;
|
|
case '3': result=3; break;
|
|
case '4': result=4; break;
|
|
case '5': result=5; break;
|
|
case '6': result=6; break;
|
|
case '7': result=7; break;
|
|
case '8': result=8; break;
|
|
case '9': result=9; break;
|
|
default: return 0;
|
|
}
|
|
|
|
for (const_iterator it=begin()+1;it!=end();++it)
|
|
{
|
|
uint64 offset;
|
|
switch (*it)
|
|
{
|
|
case '0': offset=0; break;
|
|
case '1': offset=1; break;
|
|
case '2': offset=2; break;
|
|
case '3': offset=3; break;
|
|
case '4': offset=4; break;
|
|
case '5': offset=5; break;
|
|
case '6': offset=6; break;
|
|
case '7': offset=7; break;
|
|
case '8': offset=8; break;
|
|
case '9': offset=9; break;
|
|
default: return 0;
|
|
}
|
|
if (result>=MaxPosSint64PreLastDigit /*~0u/20*/)
|
|
{
|
|
if (result>MaxPosSint64PreLastDigit || offset>(neg?MaxNegSint64LastDigit:MaxPosSint64LastDigit))
|
|
return 0;
|
|
}
|
|
result=10*result+offset;
|
|
}
|
|
return neg? -(sint64)result: (sint64)result;
|
|
}
|
|
|
|
uint64 CSString::atoui64() const
|
|
{
|
|
uint64 result=0;
|
|
for (const_iterator it=begin();it!=end();++it)
|
|
{
|
|
uint64 offset;
|
|
switch (*it)
|
|
{
|
|
case '0': offset=0; break;
|
|
case '1': offset=1; break;
|
|
case '2': offset=2; break;
|
|
case '3': offset=3; break;
|
|
case '4': offset=4; break;
|
|
case '5': offset=5; break;
|
|
case '6': offset=6; break;
|
|
case '7': offset=7; break;
|
|
case '8': offset=8; break;
|
|
case '9': offset=9; break;
|
|
default: return 0;
|
|
}
|
|
if (result>=MaxUint64PreLastDigit/*~0u/10*/)
|
|
{
|
|
if (result>MaxUint64PreLastDigit || offset>MaxUint64LastDigit)
|
|
return 0;
|
|
}
|
|
result=10*result+offset;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
double CSString::atof() const
|
|
{
|
|
double val;
|
|
NLMISC::fromString(*this, val);
|
|
return val;
|
|
}
|
|
|
|
bool CSString::readFromFile(const CSString& fileName)
|
|
{
|
|
FILE* file;
|
|
file = nlfopen(fileName, "rb");
|
|
if (file==NULL)
|
|
{
|
|
clear();
|
|
// There was previously a warning displayed here but that was incorrect as it is defined that refaFromFile returns an empty result if the file is not found
|
|
// nlwarning("Failed to open file for reading: %s",fileName.c_str());
|
|
return false;
|
|
}
|
|
resize(NLMISC::CFile::getFileSize(file));
|
|
uint32 bytesRead=(uint32)fread(const_cast<char*>(data()),1,size(),file);
|
|
fclose(file);
|
|
if (bytesRead!=size())
|
|
{
|
|
resize(bytesRead);
|
|
nlwarning("Failed to read file contents (requested %u bytes but fread returned %u) for file:%s",size(),bytesRead,fileName.c_str());
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool CSString::writeToFile(const CSString& fileName) const
|
|
{
|
|
FILE* file;
|
|
file = nlfopen(fileName, "wb");
|
|
if (file==NULL)
|
|
{
|
|
nlwarning("Failed to open file for writing: %s",fileName.c_str());
|
|
return false;
|
|
}
|
|
uint32 recordsWritten=(uint32)fwrite(const_cast<char*>(data()),size(),1,file);
|
|
fclose(file);
|
|
if (recordsWritten!=1)
|
|
{
|
|
nlwarning("Failed to write file contents (requested %u bytes but fwrite returned %u) for file:%s",size(),recordsWritten,fileName.c_str());
|
|
return false;
|
|
}
|
|
nldebug("CSSWTF Wrote %u bytes to file %s",size(),fileName.c_str());
|
|
return true;
|
|
}
|
|
|
|
bool CSString::writeToFileIfDifferent(const CSString& fileName) const
|
|
{
|
|
// if the file exists...
|
|
if (NLMISC::CFile::fileExists(fileName))
|
|
{
|
|
// the file exists so check it's the right size
|
|
if (NLMISC::CFile::getFileSize(fileName)==size())
|
|
{
|
|
// the file is the right size so read its data from disk...
|
|
CSString hold;
|
|
hold.readFromFile(fileName);
|
|
// check whether data read from file and our own data are identical
|
|
if (hold.size()==size() && memcmp(&hold[0],&(*this)[0],size())==0)
|
|
{
|
|
// data is identical so drop out
|
|
nldebug("CSSWTF Request to write data to file %s IGNORED because file already contains correct data",fileName.c_str());
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// the file didn't already exist or content
|
|
return writeToFile(fileName);
|
|
}
|
|
|
|
} // namespace NLMISC
|
|
|
|
|
|
|