1 // NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
2 // Copyright (C) 2010 Winch Gate Property Limited
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.
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/>.
18 #include "nel/misc/sstring.h"
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
)
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);
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
58 // copy out everything up to the next separator character
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
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
81 // delete the treated bit from this string
88 CSString
CSString::splitToOneOfSeparators( const CSString
& separators
,
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
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
))
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
))
111 // we are not at beginning of the string, delimiter is considered as separator
112 if (splitStringAtBrackets
)
116 i
=findMatchingDelimiterPos(useAngleBrace
,useSlashStringEscape
,useRepeatQuoteStringEscape
,i
);
117 // if there was a problem then break here
124 // build the return string
125 CSString result
=left(i
);
127 // if need be truncate '*this' before returning
130 if (truncateSeparatorCharacter
&& separators
.contains((*this)[i
]))
138 CSString
CSString::splitToLineComment(bool truncateThis
, bool useSlashStringEscape
)
140 bool quoteOpen
= false;
142 for (uint32 i
=0;i
<size();++i
)
144 // if we're escaped then accept the next character blindly
151 // do we have an escape?
152 if (useSlashStringEscape
&& operator[](i
)=='\\')
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
167 CSString result
= left(i
);
179 CSString result
= *this;
189 bool CSString::isValidText()
191 // setup a handy static lookup table for differentiating valid and invalid text characters
192 static bool* tbl
=NULL
;
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])
210 if (!tbl
[(uint8
)operator[](i
)])
212 nldebug("string is not valid text due to character: %u at index: %u",(uint8
)operator[](i
),i
);
217 // no binary characters found so return true
221 bool CSString::isValidFileName() const
228 if (!isDelimitedMonoBlock(false,false,false))
231 // iterate from size-2 to 1
232 for (uint i
=(uint
)size()-1; --i
;)
233 if (!isValidFileNameChar((*this)[i
]) && (*this)[i
]!=' ')
238 // iterate from size-1 to 0
239 for (uint i
=(uint
)size(); i
--;)
240 if (!isValidFileNameChar((*this)[i
]))
246 bool CSString::isValidUnquotedFileName() const
248 return (CSString('\"'+*this+'\"')).isValidFileName();
251 bool CSString::isValidKeyword() const
256 if (!isValidKeywordFirstChar((*this)[0]))
259 // iterate from size-1 to 1
260 for (uint i
=(uint
)size(); --i
;)
261 if (!isValidKeywordChar((*this)[i
]))
267 uint32
CSString::findMatchingDelimiterPos( bool useAngleBrace
,
268 bool useSlashStringEscape
,
269 bool useRepeatQuoteStringEscape
,
270 uint32 startPos
) const
273 char openingDelimiter
= (*this)[i
];
274 if (isOpeningDelimiter(openingDelimiter
,useAngleBrace
))
276 // deal with (), [], {} or <> type delimiters
280 if(isMatchingDelimiter(openingDelimiter
,(*this)[i
]))
282 // this is it! we've found the matching quote so we're done
285 if (isOpeningDelimiter((*this)[i
],useAngleBrace
) || isStringDelimiter((*this)[i
]))
288 i
=findMatchingDelimiterPos(useAngleBrace
,useSlashStringEscape
,useRepeatQuoteStringEscape
,i
);
293 if (isClosingDelimiter((*this)[i
],useAngleBrace
))
295 // we've found a closing delimiter that doesn't match our opening delimiter so give up
300 else if (isStringDelimiter(openingDelimiter
))
302 // deal with "..." or '...' type delimiters
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
316 // this is it! we've found the matching quote so we're done
320 if (useSlashStringEscape
&& (*this)[i
]=='\\')
322 // we've found a '\' character so skip the next character, whatever it is
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 '"'
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())
350 // build the return string
351 CSString result
=left(matchPos
+1);
353 // if need be truncate '*this' before returning
356 *this=leftCrop(matchPos
+1);
362 CSString
CSString::splitToStringSeparator(
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
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
)
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
))
383 i
=findMatchingDelimiterPos(false,useSlashStringEscape
,useRepeatQuoteStringEscape
,i
);
384 // if there was a problem then break here
391 // build the return string
392 CSString result
=left(i
);
394 // if need be truncate '*this' before returning
397 if (truncateSeparatorCharacter
&& separator
==(*this)[i
])
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
,
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
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
)
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
))
434 i
=findMatchingDelimiterPos(useAngleBrace
,useSlashStringEscape
,useRepeatQuoteStringEscape
,i
);
435 // if there was a problem then break here
442 // build the return string
443 CSString result
=left(i
);
445 // if need be truncate '*this' before returning
448 if (truncateSeparatorCharacter
&& separator
==(*this)[i
])
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 '"'
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 '"'
481 if (isDelimitedMonoBlock(useAngleBrace
,useSlashStringEscape
,useRepeatQuoteStringEscape
))
483 return substr(1,size()-2);
491 bool CSString::splitWords(CVectorSString
& result
) const
496 uint pre
=(uint
)s
.size();
497 result
.push_back(s
.firstWord(true));
498 uint post
=(uint
)s
.size();
505 bool CSString::splitWordOrWords(CVectorSString
& result
,bool useSlashStringEscape
,bool useRepeatQuoteStringEscape
) const
510 uint pre
=(uint
)s
.size();
511 result
.push_back(s
.firstWordOrWords(true,useSlashStringEscape
,useRepeatQuoteStringEscape
));
512 uint post
=(uint
)s
.size();
519 bool CSString::splitLines(CVectorSString
& result
) const
523 // make sure we deal with '\n\r' style carriage returns cleanly
524 if (s
.contains('\r'))
525 s
=s
.replace("\r","");
528 uint len
= (uint
)s
.size();
531 // extract the text up to the next '\n'character
532 result
.push_back(s
.splitToWithIterator('\n',it
));
533 // skip the '\n' character
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
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())
554 uint post
=(uint
)s
.size();
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
570 while (!s
.empty() && skipBlankEntries
&& !retainSeparators
&& separators
.contains(s
[0]))
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())
587 if (retainSeparators
)
593 uint post
=(uint
)s
.size();
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
606 operator+=(separator
);
607 // append next string
608 operator+=(strings
[i
]);
611 // return a ref to ourselves
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
621 operator+=(separator
);
622 // append next string
623 operator+=(strings
[i
]);
626 // return a ref to ourselves
630 CSString
CSString::strip() const
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);
640 CSString
CSString::leftStrip() const
643 int i
,j
=(int)size()-1;
644 for (i
=0; i
<j
&& isWhiteSpace((*this)[i
]); ++i
) {}
645 result
=substr(i
,j
-i
+1);
649 CSString
CSString::rightStrip() const
653 for (j
=(int)size()-1; j
>=0 && isWhiteSpace((*this)[j
]); --j
) {}
654 result
=substr(i
,j
-i
+1);
658 CSString
CSString::toUpper() const
661 std::string::const_iterator it
;
662 for (it
=begin();it
!=end();++it
)
665 if (c
>='a' && c
<='z')
672 CSString
CSString::toLower() const
675 std::string::const_iterator it
;
676 for (it
=begin();it
!=end();++it
)
679 if (c
>='A' && c
<='Z')
686 CSString
CSString::splitTo(char c
) const
690 for (i
=0;i
<size() && (*this)[i
]!=c
;++i
)
695 CSString
CSString::splitTo(char c
,bool truncateThis
,bool absorbSeparator
)
699 for (i
=0;i
<size() && (*this)[i
]!=c
;++i
)
702 // remove the result string from the input string if so desired
716 CSString
CSString::splitTo(const char *s
,bool truncateThis
)
720 for (i
=0;i
<size();++i
)
722 // perform a quick string compare
724 for (j
=0;s
[j
]!=0 && s
[j
]==(&((*this)[i
]))[j
];++j
)
727 // if string compare matched then return result so far
730 // remove the result string from the input string if so desired
734 (*this)=substr(i
+1); // +1 to skip the separator character
743 // we didn't find the separator string so we're returning a copy of the whole string
749 CSString
CSString::splitFrom(char c
) const
752 std::string::const_iterator it
;
753 for (it
=begin();it
!=end() && *it
!=c
;++it
)
758 for (;it
!=end();++it
)
764 CSString
CSString::splitFrom(const char *s
) const
768 for (i
=0;i
<size();++i
)
770 // perform a quick string compare
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
786 CSString
CSString::firstWord(bool truncateThis
)
788 // idiot test to avoid accessing index 0 in empty strings
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
]=='_')
810 // just take the first character of the input
815 // remove the result string from the input string if so desired
827 CSString
CSString::firstWordConst() const
829 return const_cast<CSString
*>(this)->firstWord();
832 CSString
CSString::tailFromFirstWord() const
835 hold
.firstWord(true);
839 unsigned CSString::countWords() const
842 CSString hold
=strip();
843 while (!hold
.empty())
845 hold
=hold
.tailFromFirstWord().strip();
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
)
864 for (startPos
=0;startPos
<size();++startPos
)
865 if (!isWhiteSpace((*this)[startPos
]))
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
);
874 *this=leftCrop(endPos
+1);
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
889 hold
.firstWordOrWords(true,useSlashStringEscape
,useRepeatQuoteStringEscape
);
893 unsigned CSString::countWordOrWords(bool useSlashStringEscape
,bool useRepeatQuoteStringEscape
) const
896 CSString hold
=strip();
897 while (!hold
.empty())
899 hold
=hold
.tailFromFirstWordOrWords(useSlashStringEscape
,useRepeatQuoteStringEscape
).strip();
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
929 hold
.firstLine(true);
933 unsigned CSString::countLines() const
936 CSString hold
=strip();
937 while (!hold
.empty())
939 hold
=hold
.tailFromFirstLine().strip();
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
961 for (uint32 i
=0;i
<size();++i
)
966 if (useSlashStringEscape
)
971 else if (useRepeatQuoteStringEscape
)
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;
987 if ((signed char)(*this)[i
]<32 && useSlashStringEscape
)
989 result
+=NLMISC::toString("\\x%02x",(unsigned char)(*this)[i
]);
1001 CSString
CSString::quoteIfNotQuoted( bool useSlashStringEscape
, bool useRepeatQuoteStringEscape
) const
1003 if (empty()||(*this)[0]!='\"'||!isDelimitedMonoBlock(false,useSlashStringEscape
,useRepeatQuoteStringEscape
))
1004 return quote(useSlashStringEscape
,useRepeatQuoteStringEscape
);
1009 CSString
CSString::quoteIfNotAtomic( bool useSlashStringEscape
, bool useRepeatQuoteStringEscape
) const
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')
1021 else if ( CSString::isValidKeywordFirstChar((*this)[0]) )
1023 for (i
=1;i
<size();++i
)
1024 if (!CSString::isValidFileNameChar((*this)[i
]))
1027 else if ((*this)[0]=='\"' && isDelimitedMonoBlock(false,useSlashStringEscape
,useRepeatQuoteStringEscape
))
1032 return quote(useSlashStringEscape
,useRepeatQuoteStringEscape
);
1037 CSString
CSString::unquote(bool useSlashStringEscape
,bool useRepeatQuoteStringEscape
) const
1039 CSString result
=stripBlockDelimiters(false,useSlashStringEscape
,useRepeatQuoteStringEscape
);
1041 for (i
=0,j
=0;i
<result
.size();++i
,++j
)
1043 if (useSlashStringEscape
&& result
[i
]=='\\')
1046 if (i
<result
.size())
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;
1063 char hold
=result
[i
]-'0';
1065 if (i
<result
.size() && result
[i
]>='0' && result
[i
]<='7')
1067 hold
=8*hold
+(result
[i
]-'0');
1069 if (i
<result
.size() && result
[i
]>='0' && result
[i
]<='7')
1071 hold
=8*hold
+(result
[i
]-'0');
1085 char hold
=result
[i
]-'0';
1087 if (i
<result
.size() && result
[i
]>='0' && result
[i
]<='7')
1089 hold
=8*hold
+(result
[i
]-'0');
1098 if (i
+1<result
.size() && isHexDigit(result
[i
+1]))
1100 char hold
=convertHexDigit(result
[i
+1]);
1102 if (i
<result
.size() && isHexDigit(result
[i
]))
1104 hold
=16*hold
+convertHexDigit(result
[i
]);
1114 else if (useRepeatQuoteStringEscape
&& (i
+1<result
.size()) && result
[i
]=='\"' && result
[i
+1]=='\"')
1116 if (i
<result
.size())
1117 result
[j
]=result
[i
];
1125 CSString
CSString::unquoteIfQuoted(bool useSlashStringEscape
,bool useRepeatQuoteStringEscape
) const
1128 return unquote(useSlashStringEscape
,useRepeatQuoteStringEscape
);
1133 CSString
CSString::encodeXML(bool isParameter
) const
1137 for (uint32 i
=0;i
<size();++i
)
1139 unsigned char c
= (*this)[i
];
1142 // special xml characters
1143 case '\"': result
+="""; continue;
1144 case '&': result
+="&"; continue;
1145 case '<': result
+="<"; continue;
1146 case '>': result
+=">"; continue;
1148 // characters that are not allowed inside a parameter block
1152 if (!isParameter
) { result
+=c
; continue; }
1155 // hex coding for extended characters
1159 result
+= ((c
>>4)>=10? 'A'+(c
>>4)-10: '0'+(c
>>4));
1160 result
+= ((c
&15)>=10? 'A'+(c
&15)-10: '0'+(c
&15));
1165 // all the special cases are catered for... treat this as a normal character
1172 CSString
CSString::decodeXML() const
1176 for (uint32 i
=0;i
<size();++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
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
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
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
1213 // move 'i' forward to point at the ';' .. the for(...) will increment i to point to next char
1220 // all the special cases are catered for... treat this as a normal character
1227 bool CSString::isEncodedXML() const
1229 bool foundToken
= false;
1231 for (uint i
=(uint
)size();i
--;)
1235 // decoded special xml characters
1243 // encoded special xml characters
1244 if (i
>=5 && substr(i
-5,5)==""") { foundToken
= true; i
-=5; break; }
1245 if (i
>=4 && substr(i
-4,4)=="&") { foundToken
= true; i
-=4; break; }
1246 if (i
>=3 && substr(i
-3,3)=="<") { foundToken
= true; i
-=3; break; }
1247 if (i
>=3 && substr(i
-3,3)==">") { foundToken
= true; i
-=3; break; }
1249 // hex coding for extended characters
1250 if (i
>=3 && isHexDigit((*this)[i
-1]))
1253 // locate the start of a potential hex string
1255 if ((*this)[j
]=='&')
1257 // make sure that at least 5 chars were found for: �
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
1265 if (!isHexDigit((*this)[k
]))
1268 // skip the characters that were matched - i now refs the opening '&'
1279 bool CSString::isXMLCompatible(bool isParameter
) const
1281 for (uint i
=(uint
)size();i
--;)
1285 // decoded special xml characters
1293 // encoded special xml characters
1294 if (i
>=5 && substr(i
-5,5)==""") { i
-=5; continue; }
1295 if (i
>=4 && substr(i
-4,4)=="&") { i
-=4; continue; }
1296 if (i
>=3 && substr(i
-3,3)=="<") { i
-=3; continue; }
1297 if (i
>=3 && substr(i
-3,3)==">") { i
-=3; continue; }
1299 // hex coding for extended characters
1300 if (i
>=3 && isHexDigit((*this)[i
-1]))
1303 // locate the start of a potential hex string
1305 if ((*this)[j
]=='&')
1307 // make sure that at least 5 chars were found for: �
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
1315 if (!isHexDigit((*this)[k
]))
1318 // skip the characters that were matched - i now refs the opening '&'
1323 // characters that are not allowed inside a parameter block
1327 if (!isParameter
) continue;
1330 if ((uint8
)((*this)[i
])>127 || (uint8
)((*this))[i
]<32)
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)
1343 std::string::size_type i
,j
;
1347 // string compare toFind against (*this)+i ...
1348 for (j
=0;toFind
[j
];++j
)
1349 if ((*this)[i
+j
]!=toFind
[j
])
1351 // if strings were identical then j reffers to ASCIIZ terminator at end of 'toFind'
1354 if (replacement
!=NULL
)
1355 result
+=replacement
;
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
])
1382 // if strings were identical then we're done
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
]))
1405 // if strings were identical then we're done
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
)
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
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;
1462 for (const_iterator it
=begin()+1;it
!=end();++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;
1481 if (result
>=MaxUint32PreLastDigit
/*~0u/10*/)
1483 if (result
>MaxUint32PreLastDigit
|| offset
>MaxUint32LastDigit
)
1489 if (result
>=MaxNegSint32PreLastDigit
/*~0u/20+1*/)
1491 if (result
>MaxNegSint32PreLastDigit
|| offset
>MaxNegSint32LastDigit
)
1495 result
=10*result
+offset
;
1497 return neg
? -(sint32
)result
: (sint32
)result
;
1500 sint32
CSString::atosi() const
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;
1524 for (const_iterator it
=begin()+1;it
!=end();++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;
1541 if (result
>=MaxPosSint32PreLastDigit
/*~0u/20*/)
1543 if (result
>MaxPosSint32PreLastDigit
|| offset
>(neg
?MaxNegSint32LastDigit
:MaxPosSint32LastDigit
))
1546 result
=10*result
+offset
;
1548 return neg
? -(sint32
)result
: (sint32
)result
;
1551 uint32
CSString::atoui() const
1554 for (const_iterator it
=begin();it
!=end();++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;
1571 if (result
>=MaxUint32PreLastDigit
/*~0u/10*/)
1573 if (result
>MaxUint32PreLastDigit
|| offset
>MaxUint32LastDigit
)
1576 result
=10*result
+offset
;
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
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;
1617 for (const_iterator it
=begin()+1;it
!=end();++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;
1636 if (result
>=MaxUint64PreLastDigit
/*~0u/10*/)
1638 if (result
>MaxUint64PreLastDigit
|| offset
>MaxUint64LastDigit
)
1644 if (result
>=MaxNegSint64PreLastDigit
/*~0u/20+1*/)
1646 if (result
>MaxNegSint64PreLastDigit
|| offset
>MaxNegSint64LastDigit
)
1650 result
=10*result
+offset
;
1652 return neg
? -(sint64
)result
: (sint64
)result
;
1655 sint64
CSString::atosi64() const
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;
1679 for (const_iterator it
=begin()+1;it
!=end();++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;
1696 if (result
>=MaxPosSint64PreLastDigit
/*~0u/20*/)
1698 if (result
>MaxPosSint64PreLastDigit
|| offset
>(neg
?MaxNegSint64LastDigit
:MaxPosSint64LastDigit
))
1701 result
=10*result
+offset
;
1703 return neg
? -(sint64
)result
: (sint64
)result
;
1706 uint64
CSString::atoui64() const
1709 for (const_iterator it
=begin();it
!=end();++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;
1726 if (result
>=MaxUint64PreLastDigit
/*~0u/10*/)
1728 if (result
>MaxUint64PreLastDigit
|| offset
>MaxUint64LastDigit
)
1731 result
=10*result
+offset
;
1736 double CSString::atof() const
1739 NLMISC::fromString(*this, val
);
1743 bool CSString::readFromFile(const CSString
& fileName
)
1746 file
= nlfopen(fileName
, "rb");
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());
1754 resize(NLMISC::CFile::getFileSize(file
));
1755 uint32 bytesRead
=(uint32
)fread(const_cast<char*>(data()),1,size(),file
);
1757 if (bytesRead
!=size())
1760 nlwarning("Failed to read file contents (requested %u bytes but fread returned %u) for file:%s",size(),bytesRead
,fileName
.c_str());
1766 bool CSString::writeToFile(const CSString
& fileName
) const
1769 file
= nlfopen(fileName
, "wb");
1772 nlwarning("Failed to open file for writing: %s",fileName
.c_str());
1775 uint32 recordsWritten
=(uint32
)fwrite(const_cast<char*>(data()),size(),1,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());
1782 nldebug("CSSWTF Wrote %u bytes to file %s",size(),fileName
.c_str());
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...
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());
1807 // the file didn't already exist or content
1808 return writeToFile(fileName
);
1811 } // namespace NLMISC