// NeL - MMORPG Framework
// 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 .
#include "stdmisc.h"
#include "nel/misc/sstring.h"
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' 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;i32) || 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 (uint32 i=(uint32)size()-1; --i;)
if (!isValidFileNameChar((*this)[i]) && (*this)[i]!=' ')
return false;
}
else
{
// iterate from size-1 to 0
for (uint32 i=(uint32)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 (uint32 i=(uint32)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' as brackets
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
bool useRepeatQuoteStringEscape) // treat """" as '"'
{
// skip white space
uint32 startPos;
for (startPos=0;startPos=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' as brackets
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
bool useRepeatQuoteStringEscape) const // treat """" as '"'
{
return const_cast(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' as brackets
bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
bool useRepeatQuoteStringEscape) const // treat """" as '"'
{
return const_cast(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())
{
uint32 pre=(uint32)s.size();
result.push_back(s.firstWord(true));
uint32 post=(uint32)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())
{
uint32 pre=(uint32)s.size();
result.push_back(s.firstWordOrWords(true,useSlashStringEscape,useRepeatQuoteStringEscape));
uint32 post=(uint32)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","");
uint32 it=0;
uint32 len= (uint32)s.size();
while(it' 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())
{
uint32 pre=(uint32)s.size();
result.push_back(s.splitToSeparator(separator,true,useAngleBrace,useSlashStringEscape,
useRepeatQuoteStringEscape,true));
if (skipBlankEntries && result.back().empty())
result.pop_back();
uint32 post=(uint32)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())
{
uint32 pre=(uint32)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);
}
}
uint32 post=(uint32)s.size();
if (post>=pre)
return false;
}
return true;
}
const CSString& CSString::join(const std::vector& strings, const CSString& separator)
{
for (uint32 i=0;i& strings, char separator)
{
for (uint32 i=0;i=0 && isWhiteSpace((*this)[j]); --j) {}
for (i=0; i=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='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(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(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(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='0' && (*this)[0]<='9')||(*this)[0]=='-')
{
for (i=1;i'9')
break;
}
else if ( CSString::isValidKeywordFirstChar((*this)[0]) )
{
for (i=1;i='0' && result[i]<='7')
{
hold=8*hold+(result[i]-'0');
++i;
if (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='0' && result[i]<='7')
{
hold=8*hold+(result[i]-'0');
++i;
}
result[j]=hold;
continue;
}
break;
case 'x':
if (i+1': 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+="";
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'; 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 ""[0-9|a-f]*
uint32 j; for (j=i+3;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 (uint32 i=(uint32)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 '' 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 '' and trailing ';' are hex digits
for (k=j+3;k':
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 '' 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 '' and trailing ';' are hex digits
for (k=j+3;k127 || (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())
return std::string::npos;
std::string::size_type i,j;
for (i=startLocation;i=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()) || 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
{
return ::atof(c_str());
}
bool CSString::readFromFile(const CSString& fileName)
{
FILE* file;
file=fopen(fileName.c_str(),"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(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=fopen(fileName.c_str(),"wb");
if (file==NULL)
{
nlwarning("Failed to open file for writing: %s",fileName.c_str());
return false;
}
uint32 recordsWritten=(uint32)fwrite(const_cast(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