// 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" #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' 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 (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' 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()) { 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' 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& 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+="&#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=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() || (*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': 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;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 { double val; NLMISC::fromString(*this, val); return val; } 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