Merge branch 'fixes' into main/rendor-staging
[ryzomcore.git] / nel / src / misc / sstring.cpp
bloba429814215aae2906b68ce028372f8210b608378
1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU Affero General Public License as
6 // published by the Free Software Foundation, either version 3 of the
7 // License, or (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU Affero General Public License for more details.
14 // You should have received a copy of the GNU Affero General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
17 #include "stdmisc.h"
18 #include "nel/misc/sstring.h"
20 #ifdef DEBUG_NEW
21 #define new DEBUG_NEW
22 #endif
24 namespace NLMISC
27 CSString CSString::strtok( const char *separators,
28 bool useSmartExtensions, // if true then match brackets etc (and refine with following args)
29 bool useAngleBrace, // - treat '<' and '>' as brackets
30 bool useSlashStringEscape, // - treat '\' as escape char so "\"" == '"'
31 bool useRepeatQuoteStringEscape) // - treat """" as '"')
33 if (useSmartExtensions)
35 CSString token;
37 // split to the first non empty token, or until the this string is empty
38 while (!empty() && token.empty())
39 token = splitToOneOfSeparators(separators,true,useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,true);
41 return token;
44 uint i, j;
45 CSString result;
47 // skip leading junk
48 for (i=0;i<size();++i)
50 // look for the next character in the 'separator' character list supplied
51 for (j=0;separators[j] && (*this)[i]!=separators[j];++j)
53 // if not found then we're at end of leading junk
54 if (!separators[j])
55 break;
58 // copy out everything up to the next separator character
59 for (;i<size();++i)
61 // look for the next character in the 'separator' character list supplied
62 for (j=0;separators[j] && (*this)[i]!=separators[j];++j)
64 // if not found then we're at end of text chunk
65 if (separators[j])
66 break;
67 result+=(*this)[i];
70 // skip trailing junk
71 for (;i<size();++i)
73 // look for the next character in the 'separator' character list supplied
74 for (j=0;separators[j] && (*this)[i]!=separators[j];++j)
76 // if not found then we're at end of leading junk
77 if (!separators[j])
78 break;
81 // delete the treated bit from this string
82 (*this)=substr(i);
84 return result;
88 CSString CSString::splitToOneOfSeparators( const CSString& separators,
89 bool truncateThis,
90 bool useAngleBrace, // treat '<' and '>' as brackets
91 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
92 bool useRepeatQuoteStringEscape, // treat """" as '"'
93 bool truncateSeparatorCharacter, // if true tail begins after separator char
94 bool splitStringAtBrackets)
96 // iterate over our string
97 uint32 i;
98 for (i=0;i<size();++i)
100 char thisChar=(*this)[i];
102 // if we've found the separator character then all's cool so break out of the loop
103 if (separators.contains(thisChar))
104 break;
106 // if we have a bracket or quote of any type then match to it's matching bracket, quote or whatever
107 if (isOpeningDelimiter(thisChar,useAngleBrace) || isStringDelimiter(thisChar))
109 if (i != 0)
111 // we are not at beginning of the string, delimiter is considered as separator
112 if (splitStringAtBrackets)
113 break;
115 uint32 j=i;
116 i=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,i);
117 // if there was a problem then break here
118 if (j==i)
119 break;
120 continue;
124 // build the return string
125 CSString result=left(i);
127 // if need be truncate '*this' before returning
128 if (truncateThis)
130 if (truncateSeparatorCharacter && separators.contains((*this)[i]))
131 ++i;
132 *this=leftCrop(i);
135 return result;
138 CSString CSString::splitToLineComment(bool truncateThis, bool useSlashStringEscape)
140 bool quoteOpen= false;
141 bool escape= false;
142 for (uint32 i=0;i<size();++i)
144 // if we're escaped then accept the next character blindly
145 if (escape)
147 escape= false;
148 continue;
151 // do we have an escape?
152 if (useSlashStringEscape && operator[](i)=='\\')
154 escape= true;
156 // do we have a quote (it is not escaped by definition here)
157 else if (operator[](i)=='\"')
159 quoteOpen= !quoteOpen;
161 // do we have a comment (not in quotes)
162 else if (!quoteOpen && i<size()-1 && operator[](i)=='/' && operator[](i+1)=='/')
164 // we found a comment so strip string down here
165 if (truncateThis)
167 CSString result= left(i);
168 *this= leftCrop(i);
169 return result;
171 else
173 return left(i);
177 if (truncateThis)
179 CSString result= *this;
180 clear();
181 return result;
183 else
185 return *this;
189 bool CSString::isValidText()
191 // setup a handy static lookup table for differentiating valid and invalid text characters
192 static bool* tbl=NULL;
193 if (tbl==NULL)
195 tbl= new bool[256];
196 for (uint32 i=0;i<256;++i)
198 tbl[i]= ((i>32) || isWhiteSpace((char)i));
202 // scan the string for binary characters
203 uint32 i=(uint32)size();
204 // while (i && !tbl[i-1])
205 // {
206 // i--;
207 // }
208 while (i--)
210 if (!tbl[(uint8)operator[](i)])
212 nldebug("string is not valid text due to character: %u at index: %u",(uint8)operator[](i),i);
213 return false;
217 // no binary characters found so return true
218 return true;
221 bool CSString::isValidFileName() const
223 if (empty())
224 return false;
226 if ((*this)[0]=='"')
228 if (!isDelimitedMonoBlock(false,false,false))
229 return false;
231 // iterate from size-2 to 1
232 for (uint i=(uint)size()-1; --i;)
233 if (!isValidFileNameChar((*this)[i]) && (*this)[i]!=' ')
234 return false;
236 else
238 // iterate from size-1 to 0
239 for (uint i=(uint)size(); i--;)
240 if (!isValidFileNameChar((*this)[i]))
241 return false;
243 return true;
246 bool CSString::isValidUnquotedFileName() const
248 return (CSString('\"'+*this+'\"')).isValidFileName();
251 bool CSString::isValidKeyword() const
253 if (empty())
254 return false;
256 if (!isValidKeywordFirstChar((*this)[0]))
257 return false;
259 // iterate from size-1 to 1
260 for (uint i=(uint)size(); --i;)
261 if (!isValidKeywordChar((*this)[i]))
262 return false;
264 return true;
267 uint32 CSString::findMatchingDelimiterPos( bool useAngleBrace,
268 bool useSlashStringEscape,
269 bool useRepeatQuoteStringEscape,
270 uint32 startPos ) const
272 uint32 i=startPos;
273 char openingDelimiter= (*this)[i];
274 if (isOpeningDelimiter(openingDelimiter,useAngleBrace))
276 // deal with (), [], {} or <> type delimiters
277 while (i<size())
279 ++i;
280 if(isMatchingDelimiter(openingDelimiter,(*this)[i]))
282 // this is it! we've found the matching quote so we're done
283 break;
285 if (isOpeningDelimiter((*this)[i],useAngleBrace) || isStringDelimiter((*this)[i]))
287 uint32 j=i;
288 i=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,i);
289 if (j==i)
290 return startPos;
291 continue;
293 if (isClosingDelimiter((*this)[i],useAngleBrace))
295 // we've found a closing delimiter that doesn't match our opening delimiter so give up
296 return startPos;
300 else if (isStringDelimiter(openingDelimiter))
302 // deal with "..." or '...' type delimiters
303 while (i<size())
305 ++i;
306 if ((*this)[i]==openingDelimiter)
308 if (useRepeatQuoteStringEscape && (*this)[i+1]==openingDelimiter)
310 // we've found a "" pair and we're treating it as \" equivalent so skip an extra character
311 ++i;
312 continue;
314 else
316 // this is it! we've found the matching quote so we're done
317 break;
320 if (useSlashStringEscape && (*this)[i]=='\\')
322 // we've found a '\' character so skip the next character, whatever it is
323 ++i;
324 continue;
329 return i;
332 CSString CSString::matchDelimiters( bool truncateThis,
333 bool useAngleBrace, // treat '<' and '>' as brackets
334 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
335 bool useRepeatQuoteStringEscape) // treat """" as '"'
337 // skip white space
338 uint32 startPos;
339 for (startPos=0;startPos<size() && isWhiteSpace((*this)[startPos]);++startPos) {}
341 // locate the matching brace
342 uint32 matchPos=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,startPos);
344 // if not found give up
345 if (matchPos>=size())
347 return CSString();
350 // build the return string
351 CSString result=left(matchPos+1);
353 // if need be truncate '*this' before returning
354 if (truncateThis)
356 *this=leftCrop(matchPos+1);
359 return result;
362 CSString CSString::splitToStringSeparator(
363 char separator,
364 bool truncateThis,
365 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
366 bool useRepeatQuoteStringEscape, // treat """" as '"'
367 bool truncateSeparatorCharacter) // if true tail begins after separator char
369 // iterate over our string
370 uint32 i;
371 for (i=0;i<size();++i)
373 char thisChar=(*this)[i];
375 // if we've found the separator character then all's cool so break out of the loop
376 if (thisChar==separator)
377 break;
379 // if we have a bracket or quote of any type then match to it's matching bracket, quote or whatever
380 if (isStringDelimiter(thisChar))
382 uint32 j=i;
383 i=findMatchingDelimiterPos(false,useSlashStringEscape,useRepeatQuoteStringEscape,i);
384 // if there was a problem then break here
385 if (j==i)
386 break;
387 continue;
391 // build the return string
392 CSString result=left(i);
394 // if need be truncate '*this' before returning
395 if (truncateThis)
397 if (truncateSeparatorCharacter && separator==(*this)[i])
398 ++i;
399 *this=leftCrop(i);
402 return result;
405 CSString CSString::splitToSeparator( char separator,
406 bool useAngleBrace, // treat '<' and '>' as brackets
407 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
408 bool useRepeatQuoteStringEscape) const // treat """" as '"'
410 return const_cast<CSString*>(this)->splitToSeparator(separator,false,useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,false);
413 CSString CSString::splitToSeparator( char separator,
414 bool truncateThis,
415 bool useAngleBrace, // treat '<' and '>' as brackets
416 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
417 bool useRepeatQuoteStringEscape, // treat """" as '"'
418 bool truncateSeparatorCharacter) // if true tail begins after separator char
420 // iterate over our string
421 uint32 i;
422 for (i=0;i<size();++i)
424 char thisChar=(*this)[i];
426 // if we've found the separator character then all's cool so break out of the loop
427 if (thisChar==separator)
428 break;
430 // if we have a bracket or quote of any type then match to it's matching bracket, quote or whatever
431 if (isOpeningDelimiter(thisChar,useAngleBrace) || isStringDelimiter(thisChar))
433 uint32 j=i;
434 i=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,i);
435 // if there was a problem then break here
436 if (j==i)
437 break;
438 continue;
442 // build the return string
443 CSString result=left(i);
445 // if need be truncate '*this' before returning
446 if (truncateThis)
448 if (truncateSeparatorCharacter && separator==(*this)[i])
449 ++i;
450 *this=leftCrop(i);
453 return result;
456 CSString CSString::splitToOneOfSeparators( const CSString& separators,
457 bool useAngleBrace, // treat '<' and '>' as brackets
458 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
459 bool useRepeatQuoteStringEscape) const // treat """" as '"'
461 return const_cast<CSString*>(this)->splitToOneOfSeparators(separators,false,useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape,false);
465 bool CSString::isDelimitedMonoBlock( bool useAngleBrace, // treat '<' and '>' as brackets
466 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
467 bool useRepeatQuoteStringEscape // treat """" as '"'
468 ) const
470 if (empty())
471 return false;
472 uint32 matchPos=findMatchingDelimiterPos(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape);
473 return (matchPos==size()-1 && isMatchingDelimiter((*this)[0],(*this)[matchPos]));
476 CSString CSString::stripBlockDelimiters( bool useAngleBrace, // treat '<' and '>' as brackets
477 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
478 bool useRepeatQuoteStringEscape // treat """" as '"'
479 ) const
481 if (isDelimitedMonoBlock(useAngleBrace,useSlashStringEscape,useRepeatQuoteStringEscape))
483 return substr(1,size()-2);
485 else
487 return *this;
491 bool CSString::splitWords(CVectorSString& result) const
493 CSString s=strip();
494 while(!s.empty())
496 uint pre=(uint)s.size();
497 result.push_back(s.firstWord(true));
498 uint post=(uint)s.size();
499 if (post>=pre)
500 return false;
502 return true;
505 bool CSString::splitWordOrWords(CVectorSString& result,bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
507 CSString s=*this;
508 while(!s.empty())
510 uint pre=(uint)s.size();
511 result.push_back(s.firstWordOrWords(true,useSlashStringEscape,useRepeatQuoteStringEscape));
512 uint post=(uint)s.size();
513 if (post>=pre)
514 return false;
516 return true;
519 bool CSString::splitLines(CVectorSString& result) const
521 CSString s=*this;
523 // make sure we deal with '\n\r' style carriage returns cleanly
524 if (s.contains('\r'))
525 s=s.replace("\r","");
527 uint it=0;
528 uint len= (uint)s.size();
529 while(it<len)
531 // extract the text up to the next '\n'character
532 result.push_back(s.splitToWithIterator('\n',it));
533 // skip the '\n' character
534 ++it;
536 return true;
539 bool CSString::splitBySeparator( char separator, CVectorSString& result,
540 bool useAngleBrace, // treat '<' and '>' as brackets
541 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
542 bool useRepeatQuoteStringEscape, // treat """" as '"'
543 bool skipBlankEntries // dont add blank entries to the result vector
544 ) const
546 CSString s=*this;
547 while(!s.empty())
549 uint pre=(uint)s.size();
550 result.push_back(s.splitToSeparator(separator,true,useAngleBrace,useSlashStringEscape,
551 useRepeatQuoteStringEscape,true));
552 if (skipBlankEntries && result.back().empty())
553 result.pop_back();
554 uint post=(uint)s.size();
555 if (post>=pre)
556 return false;
558 return true;
561 bool CSString::splitByOneOfSeparators( const CSString& separators, CVectorSString& result,
562 bool useAngleBrace, // treat '<' and '>' as brackets
563 bool useSlashStringEscape, // treat '\' as escape char so "\"" == '"'
564 bool useRepeatQuoteStringEscape,// treat """" as '"'
565 bool retainSeparators, // have the separators turn up in the result vector
566 bool skipBlankEntries // dont add blank entries to the result vector
567 ) const
569 CSString s=*this;
570 while (!s.empty() && skipBlankEntries && !retainSeparators && separators.contains(s[0]))
571 s= s.leftCrop(1);
573 while(!s.empty())
575 uint pre=(uint)s.size();
576 result.push_back(s.splitToOneOfSeparators( separators,true,useAngleBrace,useSlashStringEscape,
577 useRepeatQuoteStringEscape,!retainSeparators ));
579 // if we failed to extract a string segment then we must be looking at a separator
580 if (result.back().empty())
582 if (skipBlankEntries && result.back().empty())
583 result.pop_back();
585 if (!s.empty())
587 if (retainSeparators)
588 result.back()=s[0];
589 s=s.leftCrop(1);
593 uint post=(uint)s.size();
594 if (post>=pre)
595 return false;
597 return true;
600 const CSString& CSString::join(const std::vector<CSString>& strings, const CSString& separator)
602 for (uint32 i=0;i<strings.size();++i)
604 // add in separators before all but the first string
605 if (!empty())
606 operator+=(separator);
607 // append next string
608 operator+=(strings[i]);
611 // return a ref to ourselves
612 return *this;
615 const CSString& CSString::join(const std::vector<CSString>& strings, char separator)
617 for (uint32 i=0;i<strings.size();++i)
619 // add in separators before all but the first string
620 if (!empty())
621 operator+=(separator);
622 // append next string
623 operator+=(strings[i]);
626 // return a ref to ourselves
627 return *this;
630 CSString CSString::strip() const
632 CSString result;
633 int i,j;
634 for (j=(int)size()-1; j>=0 && isWhiteSpace((*this)[j]); --j) {}
635 for (i=0; i<j && isWhiteSpace((*this)[i]); ++i) {}
636 result=substr(i,j-i+1);
637 return result;
640 CSString CSString::leftStrip() const
642 CSString result;
643 int i,j=(int)size()-1;
644 for (i=0; i<j && isWhiteSpace((*this)[i]); ++i) {}
645 result=substr(i,j-i+1);
646 return result;
649 CSString CSString::rightStrip() const
651 CSString result;
652 int i=0,j;
653 for (j=(int)size()-1; j>=0 && isWhiteSpace((*this)[j]); --j) {}
654 result=substr(i,j-i+1);
655 return result;
658 CSString CSString::toUpper() const
660 CSString result;
661 std::string::const_iterator it;
662 for (it=begin();it!=end();++it)
664 char c=(*it);
665 if (c>='a' && c<='z')
666 c^=('a'^'A');
667 result+=c;
669 return result;
672 CSString CSString::toLower() const
674 CSString result;
675 std::string::const_iterator it;
676 for (it=begin();it!=end();++it)
678 char c=(*it);
679 if (c>='A' && c<='Z')
680 c^=('a'^'A');
681 result+=c;
683 return result;
686 CSString CSString::splitTo(char c) const
688 uint i;
689 CSString result;
690 for (i=0;i<size() && (*this)[i]!=c;++i)
691 result+=(*this)[i];
692 return result;
695 CSString CSString::splitTo(char c,bool truncateThis,bool absorbSeparator)
697 uint i;
698 CSString result;
699 for (i=0;i<size() && (*this)[i]!=c;++i)
700 result+=(*this)[i];
702 // remove the result string from the input string if so desired
703 if (truncateThis)
705 if (absorbSeparator)
706 ++i;
707 if (i<size())
708 (*this)=substr(i);
709 else
710 clear();
713 return result;
716 CSString CSString::splitTo(const char *s,bool truncateThis)
718 uint i;
719 CSString result;
720 for (i=0;i<size();++i)
722 // perform a quick string compare
723 uint j;
724 for (j=0;s[j]!=0 && s[j]==(&((*this)[i]))[j];++j)
727 // if string compare matched then return result so far
728 if (s[j]==0)
730 // remove the result string from the input string if so desired
731 if (truncateThis)
733 if (i+1<size())
734 (*this)=substr(i+1); // +1 to skip the separator character
735 else
736 clear();
739 return result;
741 result+=(*this)[i];
743 // we didn't find the separator string so we're returning a copy of the whole string
744 if (truncateThis)
745 clear();
746 return result;
749 CSString CSString::splitFrom(char c) const
751 CSString result;
752 std::string::const_iterator it;
753 for (it=begin();it!=end() && *it!=c;++it)
755 if (it!=end())
757 ++it;
758 for (;it!=end();++it)
759 result+=*it;
761 return result;
764 CSString CSString::splitFrom(const char *s) const
766 uint i;
767 CSString result;
768 for (i=0;i<size();++i)
770 // perform a quick string compare
771 uint j;
772 for (j=0;i+j<size() && s[j]!=0 && s[j]==(*this)[i+j];++j)
775 // if string compare matched then build and return a result
776 if (s[j]==0)
778 result=substr(i+j);
779 return result;
782 return result;
786 CSString CSString::firstWord(bool truncateThis)
788 // idiot test to avoid accessing index 0 in empty strings
789 if (empty())
790 return CSString();
792 CSString result;
793 uint i=0;
794 // skip white space
795 for (i=0;i<size() && isWhiteSpace((*this)[i]);++i)
798 if ( ((*this)[i]>='A' && (*this)[i]<='Z') || ((*this)[i]>='a' && (*this)[i]<='z') ||
799 ((*this)[i]>='0' && (*this)[i]<='9') || (*this)[i]=='_')
801 // copy out an alpha-numeric string
802 for (;i<(*this).size() &&
803 ( ((*this)[i]>='A' && (*this)[i]<='Z') || ((*this)[i]>='a' && (*this)[i]<='z') ||
804 ((*this)[i]>='0' && (*this)[i]<='9') || (*this)[i]=='_')
805 ;++i)
806 result+=(*this)[i];
808 else
810 // just take the first character of the input
811 result=(*this)[i];
812 ++i;
815 // remove the result string from the input string if so desired
816 if (truncateThis)
818 if (i<size())
819 (*this)=substr(i);
820 else
821 clear();
824 return result;
827 CSString CSString::firstWordConst() const
829 return const_cast<CSString *>(this)->firstWord();
832 CSString CSString::tailFromFirstWord() const
834 CSString hold=*this;
835 hold.firstWord(true);
836 return hold;
839 unsigned CSString::countWords() const
841 unsigned count=0;
842 CSString hold=strip();
843 while (!hold.empty())
845 hold=hold.tailFromFirstWord().strip();
846 ++count;
848 return count;
851 CSString CSString::word(uint32 idx) const
853 CSString hold=strip();
855 for (unsigned count=0;count<idx;++count)
856 hold=hold.tailFromFirstWord().strip();
858 return hold.firstWord();
861 CSString CSString::firstWordOrWords(bool truncateThis,bool useSlashStringEscape,bool useRepeatQuoteStringEscape)
863 uint32 startPos;
864 for (startPos=0;startPos<size();++startPos)
865 if (!isWhiteSpace((*this)[startPos]))
866 break;
868 if (isStringDelimiter((*this)[startPos]))
870 uint32 endPos= findMatchingDelimiterPos(false,useSlashStringEscape,useRepeatQuoteStringEscape,startPos);
871 CSString result=substr(startPos,endPos-startPos+1);
872 result=result.unquote(useSlashStringEscape,useRepeatQuoteStringEscape);
873 if (truncateThis)
874 *this=leftCrop(endPos+1);
875 return result;
877 else
878 return firstWord(truncateThis);
881 CSString CSString::firstWordOrWordsConst(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
883 return const_cast<CSString *>(this)->firstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape);
886 CSString CSString::tailFromFirstWordOrWords(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
888 CSString hold=*this;
889 hold.firstWordOrWords(true,useSlashStringEscape,useRepeatQuoteStringEscape);
890 return hold;
893 unsigned CSString::countWordOrWords(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
895 unsigned count=0;
896 CSString hold=strip();
897 while (!hold.empty())
899 hold=hold.tailFromFirstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape).strip();
900 ++count;
902 return count;
905 CSString CSString::wordOrWords(uint32 idx,bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
907 CSString hold=strip();
909 for (unsigned count=0;count<idx;++count)
910 hold=hold.tailFromFirstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape).strip();
912 return hold.firstWordOrWords(useSlashStringEscape,useRepeatQuoteStringEscape);
916 CSString CSString::firstLine(bool truncateThis)
918 return splitTo('\n',truncateThis);
921 CSString CSString::firstLineConst() const
923 return const_cast<CSString *>(this)->firstLine();
926 CSString CSString::tailFromFirstLine() const
928 CSString hold=*this;
929 hold.firstLine(true);
930 return hold;
933 unsigned CSString::countLines() const
935 unsigned count=0;
936 CSString hold=strip();
937 while (!hold.empty())
939 hold=hold.tailFromFirstLine().strip();
940 ++count;
942 return count;
945 CSString CSString::line(uint32 idx) const
947 CSString hold=strip();
949 for (unsigned count=0;count<idx;++count)
950 hold= hold.tailFromFirstLine().strip();
952 return hold.firstLine();
956 CSString CSString::quote(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
958 CSString result;
960 result+='\"';
961 for (uint32 i=0;i<size();++i)
963 switch ((*this)[i])
965 case '\"':
966 if (useSlashStringEscape)
968 result+="\\\"";
969 continue;
971 else if (useRepeatQuoteStringEscape)
973 result+="\"\"";
974 continue;
976 break;
977 case '\\': if (useSlashStringEscape) { result+="\\\\"; continue; } break;
978 case '\a': if (useSlashStringEscape) { result+="\\a"; continue; } break;
979 case '\b': if (useSlashStringEscape) { result+="\\b"; continue; } break;
980 case '\f': if (useSlashStringEscape) { result+="\\f"; continue; } break;
981 case '\n': if (useSlashStringEscape) { result+="\\n"; continue; } break;
982 case '\r': if (useSlashStringEscape) { result+="\\r"; continue; } break;
983 case '\t': if (useSlashStringEscape) { result+="\\t"; continue; } break;
984 case '\v': if (useSlashStringEscape) { result+="\\v"; continue; } break;
985 break;
986 default:
987 if ((signed char)(*this)[i]<32 && useSlashStringEscape)
989 result+=NLMISC::toString("\\x%02x",(unsigned char)(*this)[i]);
990 continue;
992 break;
994 result+=(*this)[i];
996 result+='\"';
998 return result;
1001 CSString CSString::quoteIfNotQuoted( bool useSlashStringEscape, bool useRepeatQuoteStringEscape ) const
1003 if (empty()||(*this)[0]!='\"'||!isDelimitedMonoBlock(false,useSlashStringEscape,useRepeatQuoteStringEscape))
1004 return quote(useSlashStringEscape,useRepeatQuoteStringEscape);
1006 return *this;
1009 CSString CSString::quoteIfNotAtomic( bool useSlashStringEscape, bool useRepeatQuoteStringEscape ) const
1011 if (empty())
1012 return "\"\"";
1014 uint32 i=1;
1015 if (((*this)[0]>='0' && (*this)[0]<='9')||(*this)[0]=='-')
1017 for (i=1;i<size();++i)
1018 if ((*this)[i]<'0' || (*this)[i]>'9')
1019 break;
1021 else if ( CSString::isValidKeywordFirstChar((*this)[0]) )
1023 for (i=1;i<size();++i)
1024 if (!CSString::isValidFileNameChar((*this)[i]))
1025 break;
1027 else if ((*this)[0]=='\"' && isDelimitedMonoBlock(false,useSlashStringEscape,useRepeatQuoteStringEscape))
1029 i=(uint)size();
1031 if (i!=size())
1032 return quote(useSlashStringEscape,useRepeatQuoteStringEscape);
1034 return *this;
1037 CSString CSString::unquote(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
1039 CSString result=stripBlockDelimiters(false,useSlashStringEscape,useRepeatQuoteStringEscape);
1040 uint32 i,j;
1041 for (i=0,j=0;i<result.size();++i,++j)
1043 if (useSlashStringEscape && result[i]=='\\')
1045 ++i;
1046 if (i<result.size())
1048 switch(result[i])
1050 case 'a': result[i]='\a'; break;
1051 case 'b': result[i]='\b'; break;
1052 case 'f': result[i]='\f'; break;
1053 case 'n': result[i]='\n'; break;
1054 case 'r': result[i]='\r'; break;
1055 case 't': result[i]='\t'; break;
1056 case 'v': result[i]='\v'; break;
1058 case '0':
1059 case '1':
1060 case '2':
1061 case '3':
1063 char hold=result[i]-'0';
1064 ++i;
1065 if (i<result.size() && result[i]>='0' && result[i]<='7')
1067 hold=8*hold+(result[i]-'0');
1068 ++i;
1069 if (i<result.size() && result[i]>='0' && result[i]<='7')
1071 hold=8*hold+(result[i]-'0');
1072 ++i;
1075 result[j]=hold;
1076 continue;
1078 break;
1080 case '4':
1081 case '5':
1082 case '6':
1083 case '7':
1085 char hold=result[i]-'0';
1086 ++i;
1087 if (i<result.size() && result[i]>='0' && result[i]<='7')
1089 hold=8*hold+(result[i]-'0');
1090 ++i;
1092 result[j]=hold;
1093 continue;
1095 break;
1097 case 'x':
1098 if (i+1<result.size() && isHexDigit(result[i+1]))
1100 char hold=convertHexDigit(result[i+1]);
1101 i+=2;
1102 if (i<result.size() && isHexDigit(result[i]))
1104 hold=16*hold+convertHexDigit(result[i]);
1105 ++i;
1107 result[j]=hold;
1108 continue;
1110 break;
1114 else if (useRepeatQuoteStringEscape && (i+1<result.size()) && result[i]=='\"' && result[i+1]=='\"')
1115 ++i;
1116 if (i<result.size())
1117 result[j]=result[i];
1119 if (i!=j)
1120 result.resize(j);
1122 return result;
1125 CSString CSString::unquoteIfQuoted(bool useSlashStringEscape,bool useRepeatQuoteStringEscape) const
1127 if (isQuoted())
1128 return unquote(useSlashStringEscape,useRepeatQuoteStringEscape);
1129 else
1130 return *this;
1133 CSString CSString::encodeXML(bool isParameter) const
1135 CSString result;
1137 for (uint32 i=0;i<size();++i)
1139 unsigned char c= (*this)[i];
1140 switch(c)
1142 // special xml characters
1143 case '\"': result+="&quot;"; continue;
1144 case '&': result+="&amp;"; continue;
1145 case '<': result+="&lt;"; continue;
1146 case '>': result+="&gt;"; continue;
1148 // characters that are not allowed inside a parameter block
1149 case '\n':
1150 case '\r':
1151 case '\t':
1152 if (!isParameter) { result+=c; continue; }
1155 // hex coding for extended characters
1156 if (c<32 || c>127)
1158 result+="&#x";
1159 result+= ((c>>4)>=10? 'A'+(c>>4)-10: '0'+(c>>4));
1160 result+= ((c&15)>=10? 'A'+(c&15)-10: '0'+(c&15));
1161 result+=";";
1162 continue;
1165 // all the special cases are catered for... treat this as a normal character
1166 result+=c;
1169 return result;
1172 CSString CSString::decodeXML() const
1174 CSString result;
1176 for (uint32 i=0;i<size();++i)
1178 breakable
1180 if((*this)[i]=='&')
1182 // special xml characters
1183 if (substr(i+1,5)=="quot;") { i+=5; result+='\"'; continue; }
1184 if (substr(i+1,4)=="amp;") { i+=4; result+='&'; continue; }
1185 if (substr(i+1,3)=="lt;") { i+=3; result+='<'; continue; }
1186 if (substr(i+1,3)=="gt;") { i+=3; result+='>'; continue; }
1188 // hex coding for extended characters
1189 if ((size()-i)>=5)
1191 if ((*this)[i+1]!='#') break;
1192 if (((*this)[i+2]|('a'-'A'))!='x') break;
1193 // setup j to point at the first character following "&#x"[0-9|a-f]*
1194 uint32 j; for (j=i+3;j<size();++j) if (!isHexDigit((*this)[j])) break;
1195 // make sure that at least 1 hex character was found
1196 if (j==i+3) break;
1197 // make sure have the terminating ';' to complete the token: "&#x"[0-9|a-f]*";"
1198 if (j>=size() || (*this)[j]!=';') break;
1199 // make sure that the value we have is only one or 2 hex digits
1200 if (j>=i+6) nlwarning("Truncating extended char code '%s",(substr(i,j-i+1)+"' => using "+substr(j-2,2)).c_str());
1202 // convert the 1 or 2 last hex digits to a char value
1203 char c=0;
1204 if (j>=i+5)
1206 // if we have 2 or more digits then grab the high digit
1207 c+= convertHexDigit( (*this)[j-2] )*16;
1209 c+= convertHexDigit( (*this)[j-1] );
1211 // append our new character to the result string
1212 result+=c;
1213 // move 'i' forward to point at the ';' .. the for(...) will increment i to point to next char
1214 i=j;
1215 continue;
1220 // all the special cases are catered for... treat this as a normal character
1221 result+=(*this)[i];
1224 return result;
1227 bool CSString::isEncodedXML() const
1229 bool foundToken= false;
1231 for (uint i=(uint)size();i--;)
1233 switch((*this)[i])
1235 // decoded special xml characters
1236 case '\"':
1237 case '<':
1238 case '>':
1239 case '&':
1240 return false;
1242 case ';':
1243 // encoded special xml characters
1244 if (i>=5 && substr(i-5,5)=="&quot") { foundToken= true; i-=5; break; }
1245 if (i>=4 && substr(i-4,4)=="&amp") { foundToken= true; i-=4; break; }
1246 if (i>=3 && substr(i-3,3)=="&lt") { foundToken= true; i-=3; break; }
1247 if (i>=3 && substr(i-3,3)=="&gt") { foundToken= true; i-=3; break; }
1249 // hex coding for extended characters
1250 if (i>=3 && isHexDigit((*this)[i-1]))
1252 uint32 j,k;
1253 // locate the start of a potential hex string
1254 for (j=i;j--;)
1255 if ((*this)[j]=='&')
1256 break;
1257 // make sure that at least 5 chars were found for: &#x0;
1258 if (i-j<4) continue;
1259 // make sure we have '&#x' at the start
1260 if ((*this)[j]!='&') continue;
1261 if ((*this)[j+1]!='#') continue;
1262 if ((*this)[j+2]!='x') continue;
1263 // ensure that the remainder between the leading '&#x' and trailing ';' are hex digits
1264 for (k=j+3;k<i;++k)
1265 if (!isHexDigit((*this)[k]))
1266 break;
1267 if (k!=i) continue;
1268 // skip the characters that were matched - i now refs the opening '&'
1269 i=j;
1270 foundToken= true;
1271 break;
1276 return foundToken;
1279 bool CSString::isXMLCompatible(bool isParameter) const
1281 for (uint i=(uint)size();i--;)
1283 switch((*this)[i])
1285 // decoded special xml characters
1286 case '\"':
1287 case '<':
1288 case '>':
1289 case '&':
1290 return false;
1292 case ';':
1293 // encoded special xml characters
1294 if (i>=5 && substr(i-5,5)=="&quot") { i-=5; continue; }
1295 if (i>=4 && substr(i-4,4)=="&amp") { i-=4; continue; }
1296 if (i>=3 && substr(i-3,3)=="&lt") { i-=3; continue; }
1297 if (i>=3 && substr(i-3,3)=="&gt") { i-=3; continue; }
1299 // hex coding for extended characters
1300 if (i>=3 && isHexDigit((*this)[i-1]))
1302 uint32 j,k;
1303 // locate the start of a potential hex string
1304 for (j=i;j--;)
1305 if ((*this)[j]=='&')
1306 break;
1307 // make sure that at least 5 chars were found for: &#x0;
1308 if (i-j<4) continue;
1309 // make sure we have '&#x' at the start
1310 if ((*this)[j]!='&') continue;
1311 if ((*this)[j+1]!='#') continue;
1312 if ((*this)[j+2]!='x') continue;
1313 // ensure that the remainder between the leading '&#x' and trailing ';' are hex digits
1314 for (k=j+3;k<i;++k)
1315 if (!isHexDigit((*this)[k]))
1316 break;
1317 if (k!=i) continue;
1318 // skip the characters that were matched - i now refs the opening '&'
1319 i=j;
1320 continue;
1323 // characters that are not allowed inside a parameter block
1324 case '\n':
1325 case '\r':
1326 case '\t':
1327 if (!isParameter) continue;
1330 if ((uint8)((*this)[i])>127 || (uint8)((*this))[i]<32)
1331 return false;
1334 return true;
1337 CSString CSString::replace(const char *toFind,const char *replacement) const
1339 // just bypass the problems that can cause a crash...
1340 if (toFind==NULL || *toFind==0)
1341 return *this;
1343 std::string::size_type i,j;
1344 CSString result;
1345 for (i=0;i<size();)
1347 // string compare toFind against (*this)+i ...
1348 for (j=0;toFind[j];++j)
1349 if ((*this)[i+j]!=toFind[j])
1350 break;
1351 // if strings were identical then j reffers to ASCIIZ terminator at end of 'toFind'
1352 if (toFind[j]==0)
1354 if (replacement!=NULL)
1355 result+=replacement;
1356 i+=j;
1358 else
1360 result+=(*this)[i];
1361 ++i;
1364 return result;
1367 std::string::size_type CSString::find(const char *toFind, std::string::size_type startLocation) const
1369 // const char *constStr = c_str();
1371 // just bypass the problems that can cause a crash...
1372 if (toFind==NULL || *toFind==0 || startLocation>=size())
1373 return std::string::npos;
1375 std::string::size_type i,j;
1376 for (i=startLocation;i<size();++i)
1378 // string compare toFind against (*this)+i ...
1379 for (j=0;toFind[j];++j)
1380 if ((i+j>=size()) || (*this)[i+j]!=toFind[j])
1381 break;
1382 // if strings were identical then we're done
1383 if (toFind[j]==0)
1384 return i;
1386 return std::string::npos;
1389 /// Find index at which a sub-string starts (case NOT sensitive) - if sub-string not found then returns string::npos
1390 std::string::size_type CSString::findNS(const char *toFind, std::string::size_type startLocation) const
1392 const char *constStr = c_str();
1394 // just bypass the problems that can cause a crash...
1395 if (toFind==NULL || *toFind==0 || startLocation>=size())
1396 return std::string::npos;
1398 std::string::size_type i,j;
1399 for (i=startLocation;i<size();++i)
1401 // string compare toFind against (*this)+i ...
1402 for (j=0;toFind[j];++j)
1403 if ((i+j>=size()) || tolower(constStr[i+j])!=tolower(toFind[j]))
1404 break;
1405 // if strings were identical then we're done
1406 if (toFind[j]==0)
1407 return i;
1409 return std::string::npos;
1412 bool CSString::contains(const char *toFind) const
1414 return find(toFind)!=std::string::npos;
1417 bool CSString::contains(int character) const
1419 for (const_iterator it=begin();it!=end();++it)
1420 if ((*it)==character)
1421 return true;
1423 return false;
1426 static const uint32 MaxUint32= ~0u;
1427 static const uint32 MaxUint32LastDigit= MaxUint32-(MaxUint32/10)*10;
1428 static const uint32 MaxUint32PreLastDigit= (MaxUint32/10);
1430 static const uint32 MaxNegSint32= ~0u/2+1;
1431 static const uint32 MaxNegSint32LastDigit= MaxNegSint32-(MaxNegSint32/10)*10;
1432 static const uint32 MaxNegSint32PreLastDigit= (MaxNegSint32/10);
1434 static const uint32 MaxPosSint32= ~0u/2;
1435 static const uint32 MaxPosSint32LastDigit= MaxPosSint32-(MaxPosSint32/10)*10;
1436 static const uint32 MaxPosSint32PreLastDigit= (MaxPosSint32/10);
1438 int CSString::atoi() const
1440 if (empty())
1441 return 0;
1443 bool neg= false;
1444 uint32 result;
1445 switch (*begin())
1447 case '+': result=0; break;
1448 case '-': result=0; neg=true; break;
1449 case '0': result=0; break;
1450 case '1': result=1; break;
1451 case '2': result=2; break;
1452 case '3': result=3; break;
1453 case '4': result=4; break;
1454 case '5': result=5; break;
1455 case '6': result=6; break;
1456 case '7': result=7; break;
1457 case '8': result=8; break;
1458 case '9': result=9; break;
1459 default: return 0;
1462 for (const_iterator it=begin()+1;it!=end();++it)
1464 uint32 offset;
1465 switch (*it)
1467 case '0': offset=0; break;
1468 case '1': offset=1; break;
1469 case '2': offset=2; break;
1470 case '3': offset=3; break;
1471 case '4': offset=4; break;
1472 case '5': offset=5; break;
1473 case '6': offset=6; break;
1474 case '7': offset=7; break;
1475 case '8': offset=8; break;
1476 case '9': offset=9; break;
1477 default: return 0;
1479 if (!neg)
1481 if (result>=MaxUint32PreLastDigit/*~0u/10*/)
1483 if (result>MaxUint32PreLastDigit || offset>MaxUint32LastDigit)
1484 return 0;
1487 else
1489 if (result>=MaxNegSint32PreLastDigit /*~0u/20+1*/)
1491 if (result>MaxNegSint32PreLastDigit || offset>MaxNegSint32LastDigit)
1492 return 0;
1495 result=10*result+offset;
1497 return neg? -(sint32)result: (sint32)result;
1500 sint32 CSString::atosi() const
1502 if (empty())
1503 return 0;
1505 bool neg= false;
1506 uint32 result;
1507 switch (*begin())
1509 case '+': result=0; break;
1510 case '-': result=0; neg=true; break;
1511 case '0': result=0; break;
1512 case '1': result=1; break;
1513 case '2': result=2; break;
1514 case '3': result=3; break;
1515 case '4': result=4; break;
1516 case '5': result=5; break;
1517 case '6': result=6; break;
1518 case '7': result=7; break;
1519 case '8': result=8; break;
1520 case '9': result=9; break;
1521 default: return 0;
1524 for (const_iterator it=begin()+1;it!=end();++it)
1526 uint32 offset;
1527 switch (*it)
1529 case '0': offset=0; break;
1530 case '1': offset=1; break;
1531 case '2': offset=2; break;
1532 case '3': offset=3; break;
1533 case '4': offset=4; break;
1534 case '5': offset=5; break;
1535 case '6': offset=6; break;
1536 case '7': offset=7; break;
1537 case '8': offset=8; break;
1538 case '9': offset=9; break;
1539 default: return 0;
1541 if (result>=MaxPosSint32PreLastDigit /*~0u/20*/)
1543 if (result>MaxPosSint32PreLastDigit || offset>(neg?MaxNegSint32LastDigit:MaxPosSint32LastDigit))
1544 return 0;
1546 result=10*result+offset;
1548 return neg? -(sint32)result: (sint32)result;
1551 uint32 CSString::atoui() const
1553 uint32 result=0;
1554 for (const_iterator it=begin();it!=end();++it)
1556 uint32 offset;
1557 switch (*it)
1559 case '0': offset=0; break;
1560 case '1': offset=1; break;
1561 case '2': offset=2; break;
1562 case '3': offset=3; break;
1563 case '4': offset=4; break;
1564 case '5': offset=5; break;
1565 case '6': offset=6; break;
1566 case '7': offset=7; break;
1567 case '8': offset=8; break;
1568 case '9': offset=9; break;
1569 default: return 0;
1571 if (result>=MaxUint32PreLastDigit/*~0u/10*/)
1573 if (result>MaxUint32PreLastDigit || offset>MaxUint32LastDigit)
1574 return 0;
1576 result=10*result+offset;
1578 return result;
1581 static const uint64 MaxUint64= (uint64)0-(uint64)1;
1582 static const uint64 MaxUint64LastDigit= MaxUint64-(MaxUint64/10)*10;
1583 static const uint64 MaxUint64PreLastDigit= (MaxUint64/10);
1585 static const uint64 MaxNegSint64= ((uint64)0-(uint64)1)/2+1;
1586 static const uint64 MaxNegSint64LastDigit= MaxNegSint64-(MaxNegSint64/10)*10;
1587 static const uint64 MaxNegSint64PreLastDigit= (MaxNegSint64/10);
1589 static const uint64 MaxPosSint64= ((uint64)0-(uint64)1)/2;
1590 static const uint64 MaxPosSint64LastDigit= MaxPosSint64-(MaxPosSint64/10)*10;
1591 static const uint64 MaxPosSint64PreLastDigit= (MaxPosSint64/10);
1593 sint64 CSString::atoi64() const
1595 if (empty())
1596 return 0;
1598 bool neg= false;
1599 uint64 result;
1600 switch (*begin())
1602 case '+': result=0; break;
1603 case '-': result=0; neg=true; break;
1604 case '0': result=0; break;
1605 case '1': result=1; break;
1606 case '2': result=2; break;
1607 case '3': result=3; break;
1608 case '4': result=4; break;
1609 case '5': result=5; break;
1610 case '6': result=6; break;
1611 case '7': result=7; break;
1612 case '8': result=8; break;
1613 case '9': result=9; break;
1614 default: return 0;
1617 for (const_iterator it=begin()+1;it!=end();++it)
1619 uint64 offset;
1620 switch (*it)
1622 case '0': offset=0; break;
1623 case '1': offset=1; break;
1624 case '2': offset=2; break;
1625 case '3': offset=3; break;
1626 case '4': offset=4; break;
1627 case '5': offset=5; break;
1628 case '6': offset=6; break;
1629 case '7': offset=7; break;
1630 case '8': offset=8; break;
1631 case '9': offset=9; break;
1632 default: return 0;
1634 if (!neg)
1636 if (result>=MaxUint64PreLastDigit/*~0u/10*/)
1638 if (result>MaxUint64PreLastDigit || offset>MaxUint64LastDigit)
1639 return 0;
1642 else
1644 if (result>=MaxNegSint64PreLastDigit /*~0u/20+1*/)
1646 if (result>MaxNegSint64PreLastDigit || offset>MaxNegSint64LastDigit)
1647 return 0;
1650 result=10*result+offset;
1652 return neg? -(sint64)result: (sint64)result;
1655 sint64 CSString::atosi64() const
1657 if (empty())
1658 return 0;
1660 bool neg= false;
1661 uint64 result;
1662 switch (*begin())
1664 case '+': result=0; break;
1665 case '-': result=0; neg=true; break;
1666 case '0': result=0; break;
1667 case '1': result=1; break;
1668 case '2': result=2; break;
1669 case '3': result=3; break;
1670 case '4': result=4; break;
1671 case '5': result=5; break;
1672 case '6': result=6; break;
1673 case '7': result=7; break;
1674 case '8': result=8; break;
1675 case '9': result=9; break;
1676 default: return 0;
1679 for (const_iterator it=begin()+1;it!=end();++it)
1681 uint64 offset;
1682 switch (*it)
1684 case '0': offset=0; break;
1685 case '1': offset=1; break;
1686 case '2': offset=2; break;
1687 case '3': offset=3; break;
1688 case '4': offset=4; break;
1689 case '5': offset=5; break;
1690 case '6': offset=6; break;
1691 case '7': offset=7; break;
1692 case '8': offset=8; break;
1693 case '9': offset=9; break;
1694 default: return 0;
1696 if (result>=MaxPosSint64PreLastDigit /*~0u/20*/)
1698 if (result>MaxPosSint64PreLastDigit || offset>(neg?MaxNegSint64LastDigit:MaxPosSint64LastDigit))
1699 return 0;
1701 result=10*result+offset;
1703 return neg? -(sint64)result: (sint64)result;
1706 uint64 CSString::atoui64() const
1708 uint64 result=0;
1709 for (const_iterator it=begin();it!=end();++it)
1711 uint64 offset;
1712 switch (*it)
1714 case '0': offset=0; break;
1715 case '1': offset=1; break;
1716 case '2': offset=2; break;
1717 case '3': offset=3; break;
1718 case '4': offset=4; break;
1719 case '5': offset=5; break;
1720 case '6': offset=6; break;
1721 case '7': offset=7; break;
1722 case '8': offset=8; break;
1723 case '9': offset=9; break;
1724 default: return 0;
1726 if (result>=MaxUint64PreLastDigit/*~0u/10*/)
1728 if (result>MaxUint64PreLastDigit || offset>MaxUint64LastDigit)
1729 return 0;
1731 result=10*result+offset;
1733 return result;
1736 double CSString::atof() const
1738 double val;
1739 NLMISC::fromString(*this, val);
1740 return val;
1743 bool CSString::readFromFile(const CSString& fileName)
1745 FILE* file;
1746 file = nlfopen(fileName, "rb");
1747 if (file==NULL)
1749 clear();
1750 // 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
1751 // nlwarning("Failed to open file for reading: %s",fileName.c_str());
1752 return false;
1754 resize(NLMISC::CFile::getFileSize(file));
1755 uint32 bytesRead=(uint32)fread(const_cast<char*>(data()),1,size(),file);
1756 fclose(file);
1757 if (bytesRead!=size())
1759 resize(bytesRead);
1760 nlwarning("Failed to read file contents (requested %u bytes but fread returned %u) for file:%s",size(),bytesRead,fileName.c_str());
1761 return false;
1763 return true;
1766 bool CSString::writeToFile(const CSString& fileName) const
1768 FILE* file;
1769 file = nlfopen(fileName, "wb");
1770 if (file==NULL)
1772 nlwarning("Failed to open file for writing: %s",fileName.c_str());
1773 return false;
1775 uint32 recordsWritten=(uint32)fwrite(const_cast<char*>(data()),size(),1,file);
1776 fclose(file);
1777 if (recordsWritten!=1)
1779 nlwarning("Failed to write file contents (requested %u bytes but fwrite returned %u) for file:%s",size(),recordsWritten,fileName.c_str());
1780 return false;
1782 nldebug("CSSWTF Wrote %u bytes to file %s",size(),fileName.c_str());
1783 return true;
1786 bool CSString::writeToFileIfDifferent(const CSString& fileName) const
1788 // if the file exists...
1789 if (NLMISC::CFile::fileExists(fileName))
1791 // the file exists so check it's the right size
1792 if (NLMISC::CFile::getFileSize(fileName)==size())
1794 // the file is the right size so read its data from disk...
1795 CSString hold;
1796 hold.readFromFile(fileName);
1797 // check whether data read from file and our own data are identical
1798 if (hold.size()==size() && memcmp(&hold[0],&(*this)[0],size())==0)
1800 // data is identical so drop out
1801 nldebug("CSSWTF Request to write data to file %s IGNORED because file already contains correct data",fileName.c_str());
1802 return true;
1807 // the file didn't already exist or content
1808 return writeToFile(fileName);
1811 } // namespace NLMISC