1 /*
2 * Copyright (C) 2004-2020 ZNC, see the NOTICE file for details.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <znc/ZNCString.h>
18 #include <znc/FileUtils.h>
19 #include <znc/Utils.h>
20 #include <znc/MD5.h>
21 #include <znc/SHA256.h>
22 #include <sstream>
23
24 using std::stringstream;
25
CString(char c)26 CString::CString(char c) : string() {
27 stringstream s;
28 s << c;
29 *this = s.str();
30 }
CString(unsigned char c)31 CString::CString(unsigned char c) : string() {
32 stringstream s;
33 s << c;
34 *this = s.str();
35 }
CString(short i)36 CString::CString(short i) : string() {
37 stringstream s;
38 s << i;
39 *this = s.str();
40 }
CString(unsigned short i)41 CString::CString(unsigned short i) : string() {
42 stringstream s;
43 s << i;
44 *this = s.str();
45 }
CString(int i)46 CString::CString(int i) : string() {
47 stringstream s;
48 s << i;
49 *this = s.str();
50 }
CString(unsigned int i)51 CString::CString(unsigned int i) : string() {
52 stringstream s;
53 s << i;
54 *this = s.str();
55 }
CString(long i)56 CString::CString(long i) : string() {
57 stringstream s;
58 s << i;
59 *this = s.str();
60 }
CString(unsigned long i)61 CString::CString(unsigned long i) : string() {
62 stringstream s;
63 s << i;
64 *this = s.str();
65 }
CString(long long i)66 CString::CString(long long i) : string() {
67 stringstream s;
68 s << i;
69 *this = s.str();
70 }
CString(unsigned long long i)71 CString::CString(unsigned long long i) : string() {
72 stringstream s;
73 s << i;
74 *this = s.str();
75 }
CString(double i,int precision)76 CString::CString(double i, int precision) : string() {
77 stringstream s;
78 s.precision(precision);
79 s << std::fixed << i;
80 *this = s.str();
81 }
CString(float i,int precision)82 CString::CString(float i, int precision) : string() {
83 stringstream s;
84 s.precision(precision);
85 s << std::fixed << i;
86 *this = s.str();
87 }
88
strnchr(const unsigned char * src,unsigned char c,unsigned int iMaxBytes,unsigned char * pFill,unsigned int * piCount) const89 unsigned char* CString::strnchr(const unsigned char* src, unsigned char c,
90 unsigned int iMaxBytes, unsigned char* pFill,
91 unsigned int* piCount) const {
92 for (unsigned int a = 0; a < iMaxBytes && *src; a++, src++) {
93 if (pFill) {
94 pFill[a] = *src;
95 }
96
97 if (*src == c) {
98 if (pFill) {
99 pFill[a + 1] = 0;
100 }
101
102 if (piCount) {
103 *piCount = a;
104 }
105
106 return (unsigned char*)src;
107 }
108 }
109
110 if (pFill) {
111 *pFill = 0;
112 }
113
114 if (piCount) {
115 *piCount = 0;
116 }
117
118 return nullptr;
119 }
120
CaseCmp(const CString & s,CString::size_type uLen) const121 int CString::CaseCmp(const CString& s, CString::size_type uLen) const {
122 if (uLen != CString::npos) {
123 return strncasecmp(c_str(), s.c_str(), uLen);
124 }
125 return strcasecmp(c_str(), s.c_str());
126 }
127
StrCmp(const CString & s,CString::size_type uLen) const128 int CString::StrCmp(const CString& s, CString::size_type uLen) const {
129 if (uLen != CString::npos) {
130 return strncmp(c_str(), s.c_str(), uLen);
131 }
132 return strcmp(c_str(), s.c_str());
133 }
134
Equals(const CString & s,CaseSensitivity cs) const135 bool CString::Equals(const CString& s, CaseSensitivity cs) const {
136 if (cs == CaseSensitive) {
137 return (StrCmp(s) == 0);
138 } else {
139 return (CaseCmp(s) == 0);
140 }
141 }
142
Equals(const CString & s,bool bCaseSensitive,CString::size_type uLen) const143 bool CString::Equals(const CString& s, bool bCaseSensitive,
144 CString::size_type uLen) const {
145 if (bCaseSensitive) {
146 return (StrCmp(s, uLen) == 0);
147 } else {
148 return (CaseCmp(s, uLen) == 0);
149 }
150 }
151
WildCmp(const CString & sWild,const CString & sString,CaseSensitivity cs)152 bool CString::WildCmp(const CString& sWild, const CString& sString,
153 CaseSensitivity cs) {
154 // avoid a copy when cs == CaseSensitive (C++ deliberately specifies that
155 // binding a temporary object to a reference to const on the stack
156 // lengthens the lifetime of the temporary to the lifetime of the reference
157 // itself)
158 const CString& sWld = (cs == CaseSensitive ? sWild : sWild.AsLower());
159 const CString& sStr = (cs == CaseSensitive ? sString : sString.AsLower());
160
161 // Written by Jack Handy - jakkhandy@hotmail.com
162 const char* wild = sWld.c_str(), *CString = sStr.c_str();
163 const char* cp = nullptr, *mp = nullptr;
164
165 while ((*CString) && (*wild != '*')) {
166 if ((*wild != *CString) && (*wild != '?')) {
167 return false;
168 }
169
170 wild++;
171 CString++;
172 }
173
174 while (*CString) {
175 if (*wild == '*') {
176 if (!*++wild) {
177 return true;
178 }
179
180 mp = wild;
181 cp = CString + 1;
182 } else if ((*wild == *CString) || (*wild == '?')) {
183 wild++;
184 CString++;
185 } else {
186 wild = mp;
187 CString = cp++;
188 }
189 }
190
191 while (*wild == '*') {
192 wild++;
193 }
194
195 return (*wild == 0);
196 }
197
WildCmp(const CString & sWild,CaseSensitivity cs) const198 bool CString::WildCmp(const CString& sWild, CaseSensitivity cs) const {
199 return CString::WildCmp(sWild, *this, cs);
200 }
201
MakeUpper()202 CString& CString::MakeUpper() {
203 for (char& c : *this) {
204 // TODO use unicode
205 c = (char)toupper(c);
206 }
207
208 return *this;
209 }
210
MakeLower()211 CString& CString::MakeLower() {
212 for (char& c : *this) {
213 // TODO use unicode
214 c = (char)tolower(c);
215 }
216
217 return *this;
218 }
219
AsUpper() const220 CString CString::AsUpper() const {
221 CString sRet = *this;
222 sRet.MakeUpper();
223 return sRet;
224 }
225
AsLower() const226 CString CString::AsLower() const {
227 CString sRet = *this;
228 sRet.MakeLower();
229 return sRet;
230 }
231
ToEscape(const CString & sEsc)232 CString::EEscape CString::ToEscape(const CString& sEsc) {
233 if (sEsc.Equals("ASCII")) {
234 return EASCII;
235 } else if (sEsc.Equals("HTML")) {
236 return EHTML;
237 } else if (sEsc.Equals("URL")) {
238 return EURL;
239 } else if (sEsc.Equals("SQL")) {
240 return ESQL;
241 } else if (sEsc.Equals("NAMEDFMT")) {
242 return ENAMEDFMT;
243 } else if (sEsc.Equals("DEBUG")) {
244 return EDEBUG;
245 } else if (sEsc.Equals("MSGTAG")) {
246 return EMSGTAG;
247 } else if (sEsc.Equals("HEXCOLON")) {
248 return EHEXCOLON;
249 }
250
251 return EASCII;
252 }
253
Escape_n(EEscape eFrom,EEscape eTo) const254 CString CString::Escape_n(EEscape eFrom, EEscape eTo) const {
255 CString sRet;
256 const char szHex[] = "0123456789ABCDEF";
257 const unsigned char* pStart = (const unsigned char*)data();
258 const unsigned char* p = (const unsigned char*)data();
259 size_type iLength = length();
260 sRet.reserve(iLength * 3);
261 unsigned char pTmp[21] = {};
262 unsigned int iCounted = 0;
263
264 for (unsigned int a = 0; a < iLength; a++, p = pStart + a) {
265 unsigned char ch = 0;
266
267 switch (eFrom) {
268 case EHTML:
269 if ((*p == '&') &&
270 (strnchr((unsigned char*)p, ';', sizeof(pTmp) - 1, pTmp,
271 &iCounted))) {
272 // please note that we do not have any Unicode or UTF-8
273 // support here at all.
274
275 if ((iCounted >= 3) &&
276 (pTmp[1] == '#')) { // do XML and HTML a <
277 int base = 10;
278
279 if ((pTmp[2] & 0xDF) == 'X') {
280 base = 16;
281 }
282
283 char* endptr = nullptr;
284 unsigned long int b =
285 strtol((const char*)(pTmp + 2 + (base == 16)),
286 &endptr, base);
287
288 if ((*endptr == ';') && (b <= 255)) {
289 // incase they do something like �
290 ch = (unsigned char)b;
291 a += iCounted;
292 break;
293 }
294 }
295
296 if (ch == 0) {
297 if (!strncasecmp((const char*)&pTmp, "<", 4))
298 ch = '<';
299 else if (!strncasecmp((const char*)&pTmp, ">", 4))
300 ch = '>';
301 else if (!strncasecmp((const char*)&pTmp, """, 6))
302 ch = '"';
303 else if (!strncasecmp((const char*)&pTmp, "&", 5))
304 ch = '&';
305 }
306
307 if (ch > 0) {
308 a += iCounted;
309 } else {
310 ch = *p; // Not a valid escape, just record the &
311 }
312 } else {
313 ch = *p;
314 }
315 break;
316 case EASCII:
317 ch = *p;
318 break;
319 case EURL:
320 if (*p == '%' && (a + 2) < iLength && isxdigit(*(p + 1)) &&
321 isxdigit(*(p + 2))) {
322 p++;
323 if (isdigit(*p)) {
324 ch = (unsigned char)((*p - '0') << 4);
325 } else {
326 ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4);
327 }
328
329 p++;
330 if (isdigit(*p)) {
331 ch |= (unsigned char)(*p - '0');
332 } else {
333 ch |= (unsigned char)(tolower(*p) - 'a' + 10);
334 }
335
336 a += 2;
337 } else if (pStart[a] == '+') {
338 ch = ' ';
339 } else {
340 ch = *p;
341 }
342
343 break;
344 case ESQL:
345 if (*p != '\\' || iLength < (a + 1)) {
346 ch = *p;
347 } else {
348 a++;
349 p++;
350
351 if (*p == 'n') {
352 ch = '\n';
353 } else if (*p == 'r') {
354 ch = '\r';
355 } else if (*p == '0') {
356 ch = '\0';
357 } else if (*p == 't') {
358 ch = '\t';
359 } else if (*p == 'b') {
360 ch = '\b';
361 } else {
362 ch = *p;
363 }
364 }
365
366 break;
367 case ENAMEDFMT:
368 if (*p != '\\' || iLength < (a + 1)) {
369 ch = *p;
370 } else {
371 a++;
372 p++;
373 ch = *p;
374 }
375
376 break;
377 case EDEBUG:
378 if (*p == '\\' && (a + 3) < iLength && *(p + 1) == 'x' &&
379 isxdigit(*(p + 2)) && isxdigit(*(p + 3))) {
380 p += 2;
381 if (isdigit(*p)) {
382 ch = (unsigned char)((*p - '0') << 4);
383 } else {
384 ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4);
385 }
386
387 p++;
388 if (isdigit(*p)) {
389 ch |= (unsigned char)(*p - '0');
390 } else {
391 ch |= (unsigned char)(tolower(*p) - 'a' + 10);
392 }
393
394 a += 3;
395 } else if (*p == '\\' && a + 1 < iLength && *(p + 1) == '.') {
396 a++;
397 p++;
398 ch = '\\';
399 } else {
400 ch = *p;
401 }
402
403 break;
404 case EMSGTAG:
405 if (*p != '\\' || iLength < (a + 1)) {
406 ch = *p;
407 } else {
408 a++;
409 p++;
410
411 if (*p == ':') {
412 ch = ';';
413 } else if (*p == 's') {
414 ch = ' ';
415 } else if (*p == '0') {
416 ch = '\0';
417 } else if (*p == '\\') {
418 ch = '\\';
419 } else if (*p == 'r') {
420 ch = '\r';
421 } else if (*p == 'n') {
422 ch = '\n';
423 } else {
424 ch = *p;
425 }
426 }
427
428 break;
429 case EHEXCOLON: {
430 while (!isxdigit(*p) && a < iLength) {
431 a++;
432 p++;
433 }
434 if (a == iLength) {
435 continue;
436 }
437 if (isdigit(*p)) {
438 ch = (unsigned char)((*p - '0') << 4);
439 } else {
440 ch = (unsigned char)((tolower(*p) - 'a' + 10) << 4);
441 }
442 a++;
443 p++;
444 while (!isxdigit(*p) && a < iLength) {
445 a++;
446 p++;
447 }
448 if (a == iLength) {
449 continue;
450 }
451 if (isdigit(*p)) {
452 ch |= (unsigned char)(*p - '0');
453 } else {
454 ch |= (unsigned char)(tolower(*p) - 'a' + 10);
455 }
456 } break;
457 }
458
459 switch (eTo) {
460 case EHTML:
461 if (ch == '<')
462 sRet += "<";
463 else if (ch == '>')
464 sRet += ">";
465 else if (ch == '"')
466 sRet += """;
467 else if (ch == '&')
468 sRet += "&";
469 else {
470 sRet += ch;
471 }
472
473 break;
474 case EASCII:
475 sRet += ch;
476 break;
477 case EURL:
478 if (isalnum(ch) || ch == '_' || ch == '.' || ch == '-') {
479 sRet += ch;
480 } else if (ch == ' ') {
481 sRet += '+';
482 } else {
483 sRet += '%';
484 sRet += szHex[ch >> 4];
485 sRet += szHex[ch & 0xf];
486 }
487
488 break;
489 case ESQL:
490 if (ch == '\0') {
491 sRet += '\\';
492 sRet += '0';
493 } else if (ch == '\n') {
494 sRet += '\\';
495 sRet += 'n';
496 } else if (ch == '\t') {
497 sRet += '\\';
498 sRet += 't';
499 } else if (ch == '\r') {
500 sRet += '\\';
501 sRet += 'r';
502 } else if (ch == '\b') {
503 sRet += '\\';
504 sRet += 'b';
505 } else if (ch == '\"') {
506 sRet += '\\';
507 sRet += '\"';
508 } else if (ch == '\'') {
509 sRet += '\\';
510 sRet += '\'';
511 } else if (ch == '\\') {
512 sRet += '\\';
513 sRet += '\\';
514 } else {
515 sRet += ch;
516 }
517
518 break;
519 case ENAMEDFMT:
520 if (ch == '\\') {
521 sRet += '\\';
522 sRet += '\\';
523 } else if (ch == '{') {
524 sRet += '\\';
525 sRet += '{';
526 } else if (ch == '}') {
527 sRet += '\\';
528 sRet += '}';
529 } else {
530 sRet += ch;
531 }
532
533 break;
534 case EDEBUG:
535 if (ch < 0x20 || ch == 0x7F) {
536 sRet += "\\x";
537 sRet += szHex[ch >> 4];
538 sRet += szHex[ch & 0xf];
539 } else if (ch == '\\') {
540 sRet += "\\.";
541 } else {
542 sRet += ch;
543 }
544
545 break;
546 case EMSGTAG:
547 if (ch == ';') {
548 sRet += '\\';
549 sRet += ':';
550 } else if (ch == ' ') {
551 sRet += '\\';
552 sRet += 's';
553 } else if (ch == '\0') {
554 sRet += '\\';
555 sRet += '0';
556 } else if (ch == '\\') {
557 sRet += '\\';
558 sRet += '\\';
559 } else if (ch == '\r') {
560 sRet += '\\';
561 sRet += 'r';
562 } else if (ch == '\n') {
563 sRet += '\\';
564 sRet += 'n';
565 } else {
566 sRet += ch;
567 }
568
569 break;
570 case EHEXCOLON: {
571 sRet += tolower(szHex[ch >> 4]);
572 sRet += tolower(szHex[ch & 0xf]);
573 sRet += ":";
574 } break;
575 }
576 }
577
578 if (eTo == EHEXCOLON) {
579 sRet.TrimRight(":");
580 }
581
582 return sRet;
583 }
584
Escape_n(EEscape eTo) const585 CString CString::Escape_n(EEscape eTo) const { return Escape_n(EASCII, eTo); }
586
Escape(EEscape eFrom,EEscape eTo)587 CString& CString::Escape(EEscape eFrom, EEscape eTo) {
588 return (*this = Escape_n(eFrom, eTo));
589 }
590
Escape(EEscape eTo)591 CString& CString::Escape(EEscape eTo) { return (*this = Escape_n(eTo)); }
592
Replace_n(const CString & sReplace,const CString & sWith,const CString & sLeft,const CString & sRight,bool bRemoveDelims) const593 CString CString::Replace_n(const CString& sReplace, const CString& sWith,
594 const CString& sLeft, const CString& sRight,
595 bool bRemoveDelims) const {
596 CString sRet = *this;
597 CString::Replace(sRet, sReplace, sWith, sLeft, sRight, bRemoveDelims);
598 return sRet;
599 }
600
Replace(const CString & sReplace,const CString & sWith,const CString & sLeft,const CString & sRight,bool bRemoveDelims)601 unsigned int CString::Replace(const CString& sReplace, const CString& sWith,
602 const CString& sLeft, const CString& sRight,
603 bool bRemoveDelims) {
604 return CString::Replace(*this, sReplace, sWith, sLeft, sRight,
605 bRemoveDelims);
606 }
607
Replace(CString & sStr,const CString & sReplace,const CString & sWith,const CString & sLeft,const CString & sRight,bool bRemoveDelims)608 unsigned int CString::Replace(CString& sStr, const CString& sReplace,
609 const CString& sWith, const CString& sLeft,
610 const CString& sRight, bool bRemoveDelims) {
611 unsigned int uRet = 0;
612 CString sCopy = sStr;
613 sStr.clear();
614
615 size_type uReplaceWidth = sReplace.length();
616 size_type uLeftWidth = sLeft.length();
617 size_type uRightWidth = sRight.length();
618 const char* p = sCopy.c_str();
619 bool bInside = false;
620
621 while (*p) {
622 if (!bInside && uLeftWidth &&
623 strncmp(p, sLeft.c_str(), uLeftWidth) == 0) {
624 if (!bRemoveDelims) {
625 sStr += sLeft;
626 }
627
628 p += uLeftWidth - 1;
629 bInside = true;
630 } else if (bInside && uRightWidth &&
631 strncmp(p, sRight.c_str(), uRightWidth) == 0) {
632 if (!bRemoveDelims) {
633 sStr += sRight;
634 }
635
636 p += uRightWidth - 1;
637 bInside = false;
638 } else if (!bInside &&
639 strncmp(p, sReplace.c_str(), uReplaceWidth) == 0) {
640 sStr += sWith;
641 p += uReplaceWidth - 1;
642 uRet++;
643 } else {
644 sStr.append(p, 1);
645 }
646
647 p++;
648 }
649
650 return uRet;
651 }
652
Token(size_t uPos,bool bRest,const CString & sSep,bool bAllowEmpty,const CString & sLeft,const CString & sRight,bool bTrimQuotes) const653 CString CString::Token(size_t uPos, bool bRest, const CString& sSep,
654 bool bAllowEmpty, const CString& sLeft,
655 const CString& sRight, bool bTrimQuotes) const {
656 VCString vsTokens;
657 if (Split(sSep, vsTokens, bAllowEmpty, sLeft, sRight, bTrimQuotes) > uPos) {
658 CString sRet;
659
660 for (size_t a = uPos; a < vsTokens.size(); a++) {
661 if (a > uPos) {
662 sRet += sSep;
663 }
664
665 sRet += vsTokens[a];
666
667 if (!bRest) {
668 break;
669 }
670 }
671
672 return sRet;
673 }
674
675 return Token(uPos, bRest, sSep, bAllowEmpty);
676 }
677
Token(size_t uPos,bool bRest,const CString & sSep,bool bAllowEmpty) const678 CString CString::Token(size_t uPos, bool bRest, const CString& sSep,
679 bool bAllowEmpty) const {
680 const char* sep_str = sSep.c_str();
681 size_t sep_len = sSep.length();
682 const char* str = c_str();
683 size_t str_len = length();
684 size_t start_pos = 0;
685 size_t end_pos;
686
687 if (!bAllowEmpty) {
688 while (strncmp(&str[start_pos], sep_str, sep_len) == 0) {
689 start_pos += sep_len;
690 }
691 }
692
693 // First, find the start of our token
694 while (uPos != 0 && start_pos < str_len) {
695 bool bFoundSep = false;
696
697 while (strncmp(&str[start_pos], sep_str, sep_len) == 0 &&
698 (!bFoundSep || !bAllowEmpty)) {
699 start_pos += sep_len;
700 bFoundSep = true;
701 }
702
703 if (bFoundSep) {
704 uPos--;
705 } else {
706 start_pos++;
707 }
708 }
709
710 // String is over?
711 if (start_pos >= str_len) return "";
712
713 // If they want everything from here on, give it to them
714 if (bRest) {
715 return substr(start_pos);
716 }
717
718 // Now look for the end of the token they want
719 end_pos = start_pos;
720 while (end_pos < str_len) {
721 if (strncmp(&str[end_pos], sep_str, sep_len) == 0)
722 return substr(start_pos, end_pos - start_pos);
723
724 end_pos++;
725 }
726
727 // They want the last token in the string, not something in between
728 return substr(start_pos);
729 }
730
Ellipsize(unsigned int uLen) const731 CString CString::Ellipsize(unsigned int uLen) const {
732 if (uLen >= size()) {
733 return *this;
734 }
735
736 string sRet;
737
738 // @todo this looks suspect
739 if (uLen < 4) {
740 for (unsigned int a = 0; a < uLen; a++) {
741 sRet += ".";
742 }
743
744 return sRet;
745 }
746
747 sRet = substr(0, uLen - 3) + "...";
748
749 return sRet;
750 }
751
Left(size_type uCount) const752 CString CString::Left(size_type uCount) const {
753 uCount = (uCount > length()) ? length() : uCount;
754 return substr(0, uCount);
755 }
756
Right(size_type uCount) const757 CString CString::Right(size_type uCount) const {
758 uCount = (uCount > length()) ? length() : uCount;
759 return substr(length() - uCount, uCount);
760 }
761
URLSplit(MCString & msRet) const762 CString::size_type CString::URLSplit(MCString& msRet) const {
763 msRet.clear();
764
765 VCString vsPairs;
766 Split("&", vsPairs);
767
768 for (const CString& sPair : vsPairs) {
769 msRet[sPair.Token(0, false, "=")
770 .Escape(CString::EURL, CString::EASCII)] =
771 sPair.Token(1, true, "=").Escape(CString::EURL, CString::EASCII);
772 }
773
774 return msRet.size();
775 }
776
OptionSplit(MCString & msRet,bool bUpperKeys) const777 CString::size_type CString::OptionSplit(MCString& msRet,
778 bool bUpperKeys) const {
779 CString sName;
780 CString sCopy(*this);
781 msRet.clear();
782
783 while (!sCopy.empty()) {
784 sName = sCopy.Token(0, false, "=", false, "\"", "\"", false).Trim_n();
785 sCopy =
786 sCopy.Token(1, true, "=", false, "\"", "\"", false).TrimLeft_n();
787
788 if (sName.empty()) {
789 continue;
790 }
791
792 VCString vsNames;
793 sName.Split(" ", vsNames, false, "\"", "\"");
794
795 for (unsigned int a = 0; a < vsNames.size(); a++) {
796 CString sKeyName = vsNames[a];
797
798 if (bUpperKeys) {
799 sKeyName.MakeUpper();
800 }
801
802 if ((a + 1) == vsNames.size()) {
803 msRet[sKeyName] = sCopy.Token(0, false, " ", false, "\"", "\"");
804 sCopy = sCopy.Token(1, true, " ", false, "\"", "\"", false);
805 } else {
806 msRet[sKeyName] = "";
807 }
808 }
809 }
810
811 return msRet.size();
812 }
813
QuoteSplit(VCString & vsRet) const814 CString::size_type CString::QuoteSplit(VCString& vsRet) const {
815 vsRet.clear();
816 return Split(" ", vsRet, false, "\"", "\"", true);
817 }
818
Split(const CString & sDelim,VCString & vsRet,bool bAllowEmpty,const CString & sLeft,const CString & sRight,bool bTrimQuotes,bool bTrimWhiteSpace) const819 CString::size_type CString::Split(const CString& sDelim, VCString& vsRet,
820 bool bAllowEmpty, const CString& sLeft,
821 const CString& sRight, bool bTrimQuotes,
822 bool bTrimWhiteSpace) const {
823 vsRet.clear();
824
825 if (empty()) {
826 return 0;
827 }
828
829 CString sTmp;
830 bool bInside = false;
831 size_type uDelimLen = sDelim.length();
832 size_type uLeftLen = sLeft.length();
833 size_type uRightLen = sRight.length();
834 const char* p = c_str();
835
836 if (!bAllowEmpty) {
837 while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
838 p += uDelimLen;
839 }
840 }
841
842 while (*p) {
843 if (uLeftLen && uRightLen && !bInside &&
844 strncasecmp(p, sLeft.c_str(), uLeftLen) == 0) {
845 if (!bTrimQuotes) {
846 sTmp += sLeft;
847 }
848
849 p += uLeftLen;
850 bInside = true;
851 continue;
852 }
853
854 if (uLeftLen && uRightLen && bInside &&
855 strncasecmp(p, sRight.c_str(), uRightLen) == 0) {
856 if (!bTrimQuotes) {
857 sTmp += sRight;
858 }
859
860 p += uRightLen;
861 bInside = false;
862 continue;
863 }
864
865 if (uDelimLen && !bInside &&
866 strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
867 if (bTrimWhiteSpace) {
868 sTmp.Trim();
869 }
870
871 vsRet.push_back(sTmp);
872 sTmp.clear();
873 p += uDelimLen;
874
875 if (!bAllowEmpty) {
876 while (strncasecmp(p, sDelim.c_str(), uDelimLen) == 0) {
877 p += uDelimLen;
878 }
879 }
880
881 bInside = false;
882 continue;
883 } else {
884 sTmp += *p;
885 }
886
887 p++;
888 }
889
890 if (!sTmp.empty()) {
891 if (bTrimWhiteSpace) {
892 sTmp.Trim();
893 }
894
895 vsRet.push_back(sTmp);
896 }
897
898 return vsRet.size();
899 }
900
Split(const CString & sDelim,SCString & ssRet,bool bAllowEmpty,const CString & sLeft,const CString & sRight,bool bTrimQuotes,bool bTrimWhiteSpace) const901 CString::size_type CString::Split(const CString& sDelim, SCString& ssRet,
902 bool bAllowEmpty, const CString& sLeft,
903 const CString& sRight, bool bTrimQuotes,
904 bool bTrimWhiteSpace) const {
905 VCString vsTokens;
906
907 Split(sDelim, vsTokens, bAllowEmpty, sLeft, sRight, bTrimQuotes,
908 bTrimWhiteSpace);
909
910 ssRet.clear();
911
912 for (const CString& sToken : vsTokens) {
913 ssRet.insert(sToken);
914 }
915
916 return ssRet.size();
917 }
918
NamedFormat(const CString & sFormat,const MCString & msValues)919 CString CString::NamedFormat(const CString& sFormat, const MCString& msValues) {
920 CString sRet;
921
922 CString sKey;
923 bool bEscape = false;
924 bool bParam = false;
925 const char* p = sFormat.c_str();
926
927 while (*p) {
928 if (!bParam) {
929 if (bEscape) {
930 sRet += *p;
931 bEscape = false;
932 } else if (*p == '\\') {
933 bEscape = true;
934 } else if (*p == '{') {
935 bParam = true;
936 sKey.clear();
937 } else {
938 sRet += *p;
939 }
940
941 } else {
942 if (bEscape) {
943 sKey += *p;
944 bEscape = false;
945 } else if (*p == '\\') {
946 bEscape = true;
947 } else if (*p == '}') {
948 bParam = false;
949 MCString::const_iterator it = msValues.find(sKey);
950 if (it != msValues.end()) {
951 sRet += (*it).second;
952 }
953 } else {
954 sKey += *p;
955 }
956 }
957
958 p++;
959 }
960
961 return sRet;
962 }
963
RandomString(unsigned int uLength)964 CString CString::RandomString(unsigned int uLength) {
965 const char chars[] =
966 "abcdefghijklmnopqrstuvwxyz"
967 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
968 "0123456789!?.,:;/*-+_()";
969 // -1 because sizeof() includes the trailing '\0' byte
970 const size_t len = sizeof(chars) / sizeof(chars[0]) - 1;
971 size_t p;
972 CString sRet;
973
974 for (unsigned int a = 0; a < uLength; a++) {
975 p = (size_t)(len * (rand() / (RAND_MAX + 1.0)));
976 sRet += chars[p];
977 }
978
979 return sRet;
980 }
981
Base64Encode(unsigned int uWrap)982 bool CString::Base64Encode(unsigned int uWrap) {
983 CString sCopy(*this);
984 return sCopy.Base64Encode(*this, uWrap);
985 }
986
Base64Decode()987 unsigned long CString::Base64Decode() {
988 CString sCopy(*this);
989 return sCopy.Base64Decode(*this);
990 }
991
Base64Encode_n(unsigned int uWrap) const992 CString CString::Base64Encode_n(unsigned int uWrap) const {
993 CString sRet;
994 Base64Encode(sRet, uWrap);
995 return sRet;
996 }
997
Base64Decode_n() const998 CString CString::Base64Decode_n() const {
999 CString sRet;
1000 Base64Decode(sRet);
1001 return sRet;
1002 }
1003
Base64Encode(CString & sRet,unsigned int uWrap) const1004 bool CString::Base64Encode(CString& sRet, unsigned int uWrap) const {
1005 const char b64table[] =
1006 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1007 sRet.clear();
1008 size_t len = size();
1009 const unsigned char* input = (const unsigned char*)c_str();
1010 unsigned char* output, *p;
1011 size_t i = 0, mod = len % 3, toalloc;
1012 toalloc = (len / 3) * 4 + (3 - mod) % 3 + 1 + 8;
1013
1014 if (uWrap) {
1015 toalloc += len / 57;
1016 if (len % 57) {
1017 toalloc++;
1018 }
1019 }
1020
1021 if (toalloc < len) {
1022 return 0;
1023 }
1024
1025 p = output = new unsigned char[toalloc]{};
1026
1027 while (i < len - mod) {
1028 *p++ = b64table[input[i++] >> 2];
1029 *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
1030 *p++ = b64table[((input[i] << 2) | (input[i + 1] >> 6)) & 0x3f];
1031 *p++ = b64table[input[i + 1] & 0x3f];
1032 i += 2;
1033
1034 if (uWrap && !(i % 57)) {
1035 *p++ = '\n';
1036 }
1037 }
1038
1039 if (!mod) {
1040 if (uWrap && i % 57) {
1041 *p++ = '\n';
1042 }
1043 } else {
1044 *p++ = b64table[input[i++] >> 2];
1045 *p++ = b64table[((input[i - 1] << 4) | (input[i] >> 4)) & 0x3f];
1046 if (mod == 1) {
1047 *p++ = '=';
1048 } else {
1049 *p++ = b64table[(input[i] << 2) & 0x3f];
1050 }
1051
1052 *p++ = '=';
1053
1054 if (uWrap) {
1055 *p++ = '\n';
1056 }
1057 }
1058
1059 *p = 0;
1060 sRet = (char*)output;
1061 delete[] output;
1062 return true;
1063 }
1064
Base64Decode(CString & sRet) const1065 unsigned long CString::Base64Decode(CString& sRet) const {
1066 CString sTmp(*this);
1067 // remove new lines
1068 sTmp.Replace("\r", "");
1069 sTmp.Replace("\n", "");
1070
1071 const char* in = sTmp.c_str();
1072 char c, c1, *p;
1073 unsigned long i;
1074 unsigned long uLen = sTmp.size();
1075 char* out = new char[uLen + 1]{};
1076
1077 for (i = 0, p = out; i < uLen; i++) {
1078 c = (char)base64_table[(unsigned char)in[i++]];
1079 c1 = (char)base64_table[(unsigned char)in[i++]];
1080 *p++ = char((c << 2) | ((c1 >> 4) & 0x3));
1081
1082 if (i < uLen) {
1083 if (in[i] == '=') {
1084 break;
1085 }
1086 c = (char)base64_table[(unsigned char)in[i]];
1087 *p++ = char(((c1 << 4) & 0xf0) | ((c >> 2) & 0xf));
1088 }
1089
1090 if (++i < uLen) {
1091 if (in[i] == '=') {
1092 break;
1093 }
1094 *p++ = char(((c << 6) & 0xc0) |
1095 (char)base64_table[(unsigned char)in[i]]);
1096 }
1097 }
1098
1099 *p = '\0';
1100 unsigned long uRet = p - out;
1101 sRet.clear();
1102 sRet.append(out, uRet);
1103 delete[] out;
1104
1105 return uRet;
1106 }
1107
MD5() const1108 CString CString::MD5() const { return (const char*)CMD5(*this); }
1109
SHA256() const1110 CString CString::SHA256() const {
1111 unsigned char digest[SHA256_DIGEST_SIZE];
1112 char digest_hex[SHA256_DIGEST_SIZE * 2 + 1];
1113 const unsigned char* message = (const unsigned char*)c_str();
1114
1115 sha256(message, length(), digest);
1116
1117 snprintf(digest_hex, sizeof(digest_hex),
1118 "%02x%02x%02x%02x%02x%02x%02x%02x"
1119 "%02x%02x%02x%02x%02x%02x%02x%02x"
1120 "%02x%02x%02x%02x%02x%02x%02x%02x"
1121 "%02x%02x%02x%02x%02x%02x%02x%02x",
1122 digest[0], digest[1], digest[2], digest[3], digest[4], digest[5],
1123 digest[6], digest[7], digest[8], digest[9], digest[10], digest[11],
1124 digest[12], digest[13], digest[14], digest[15], digest[16],
1125 digest[17], digest[18], digest[19], digest[20], digest[21],
1126 digest[22], digest[23], digest[24], digest[25], digest[26],
1127 digest[27], digest[28], digest[29], digest[30], digest[31]);
1128
1129 return digest_hex;
1130 }
1131
1132 #ifdef HAVE_LIBSSL
Encrypt_n(const CString & sPass,const CString & sIvec) const1133 CString CString::Encrypt_n(const CString& sPass, const CString& sIvec) const {
1134 CString sRet;
1135 sRet.Encrypt(sPass, sIvec);
1136 return sRet;
1137 }
1138
Decrypt_n(const CString & sPass,const CString & sIvec) const1139 CString CString::Decrypt_n(const CString& sPass, const CString& sIvec) const {
1140 CString sRet;
1141 sRet.Decrypt(sPass, sIvec);
1142 return sRet;
1143 }
1144
Encrypt(const CString & sPass,const CString & sIvec)1145 void CString::Encrypt(const CString& sPass, const CString& sIvec) {
1146 Crypt(sPass, true, sIvec);
1147 }
1148
Decrypt(const CString & sPass,const CString & sIvec)1149 void CString::Decrypt(const CString& sPass, const CString& sIvec) {
1150 Crypt(sPass, false, sIvec);
1151 }
1152
Crypt(const CString & sPass,bool bEncrypt,const CString & sIvec)1153 void CString::Crypt(const CString& sPass, bool bEncrypt, const CString& sIvec) {
1154 unsigned char szIvec[8] = {0, 0, 0, 0, 0, 0, 0, 0};
1155 BF_KEY bKey;
1156
1157 if (sIvec.length() >= 8) {
1158 memcpy(szIvec, sIvec.data(), 8);
1159 }
1160
1161 BF_set_key(&bKey, (unsigned int)sPass.length(),
1162 (unsigned char*)sPass.data());
1163 unsigned int uPad = (length() % 8);
1164
1165 if (uPad) {
1166 uPad = 8 - uPad;
1167 append(uPad, '\0');
1168 }
1169
1170 size_t uLen = length();
1171 unsigned char* szBuff = (unsigned char*)malloc(uLen);
1172 BF_cbc_encrypt((const unsigned char*)data(), szBuff, uLen, &bKey, szIvec,
1173 ((bEncrypt) ? BF_ENCRYPT : BF_DECRYPT));
1174
1175 clear();
1176 append((const char*)szBuff, uLen);
1177 free(szBuff);
1178 }
1179 #endif // HAVE_LIBSSL
1180
ToPercent(double d)1181 CString CString::ToPercent(double d) {
1182 char szRet[32];
1183 snprintf(szRet, 32, "%.02f%%", d);
1184 return szRet;
1185 }
1186
ToByteStr(unsigned long long d)1187 CString CString::ToByteStr(unsigned long long d) {
1188 const unsigned long long KiB = 1024;
1189 const unsigned long long MiB = KiB * 1024;
1190 const unsigned long long GiB = MiB * 1024;
1191 const unsigned long long TiB = GiB * 1024;
1192
1193 if (d > TiB) {
1194 return CString(d / TiB) + " TiB";
1195 } else if (d > GiB) {
1196 return CString(d / GiB) + " GiB";
1197 } else if (d > MiB) {
1198 return CString(d / MiB) + " MiB";
1199 } else if (d > KiB) {
1200 return CString(d / KiB) + " KiB";
1201 }
1202
1203 return CString(d) + " B";
1204 }
1205
ToTimeStr(unsigned long s)1206 CString CString::ToTimeStr(unsigned long s) {
1207 const unsigned long m = 60;
1208 const unsigned long h = m * 60;
1209 const unsigned long d = h * 24;
1210 const unsigned long w = d * 7;
1211 const unsigned long y = d * 365;
1212 CString sRet;
1213
1214 #define TIMESPAN(time, str) \
1215 if (s >= time) { \
1216 sRet += CString(s / time) + str " "; \
1217 s = s % time; \
1218 }
1219 TIMESPAN(y, "y");
1220 TIMESPAN(w, "w");
1221 TIMESPAN(d, "d");
1222 TIMESPAN(h, "h");
1223 TIMESPAN(m, "m");
1224 TIMESPAN(1, "s");
1225
1226 if (sRet.empty()) return "0s";
1227
1228 return sRet.RightChomp_n();
1229 }
1230
ToBool() const1231 bool CString::ToBool() const {
1232 CString sTrimmed = Trim_n();
1233 return (!sTrimmed.Trim_n("0").empty() && !sTrimmed.Equals("false") &&
1234 !sTrimmed.Equals("off") && !sTrimmed.Equals("no") &&
1235 !sTrimmed.Equals("n"));
1236 }
1237
ToShort() const1238 short CString::ToShort() const {
1239 return (short int)strtol(this->c_str(), (char**)nullptr, 10);
1240 }
ToUShort() const1241 unsigned short CString::ToUShort() const {
1242 return (unsigned short int)strtoul(this->c_str(), (char**)nullptr, 10);
1243 }
ToUInt() const1244 unsigned int CString::ToUInt() const {
1245 return (unsigned int)strtoul(this->c_str(), (char**)nullptr, 10);
1246 }
ToInt() const1247 int CString::ToInt() const {
1248 return (int)strtol(this->c_str(), (char**)nullptr, 10);
1249 }
ToLong() const1250 long CString::ToLong() const {
1251 return strtol(this->c_str(), (char**)nullptr, 10);
1252 }
ToULong() const1253 unsigned long CString::ToULong() const { return strtoul(c_str(), nullptr, 10); }
ToULongLong() const1254 unsigned long long CString::ToULongLong() const {
1255 return strtoull(c_str(), nullptr, 10);
1256 }
ToLongLong() const1257 long long CString::ToLongLong() const { return strtoll(c_str(), nullptr, 10); }
ToDouble() const1258 double CString::ToDouble() const { return strtod(c_str(), nullptr); }
1259
Trim(const CString & s)1260 bool CString::Trim(const CString& s) {
1261 bool bLeft = TrimLeft(s);
1262 return (TrimRight(s) || bLeft);
1263 }
1264
TrimLeft(const CString & s)1265 bool CString::TrimLeft(const CString& s) {
1266 size_type i = find_first_not_of(s);
1267
1268 if (i == 0) return false;
1269
1270 if (i != npos)
1271 this->erase(0, i);
1272 else
1273 this->clear();
1274
1275 return true;
1276 }
1277
TrimRight(const CString & s)1278 bool CString::TrimRight(const CString& s) {
1279 size_type i = find_last_not_of(s);
1280
1281 if (i + 1 == length()) return false;
1282
1283 if (i != npos)
1284 this->erase(i + 1, npos);
1285 else
1286 this->clear();
1287
1288 return true;
1289 }
1290
Trim_n(const CString & s) const1291 CString CString::Trim_n(const CString& s) const {
1292 CString sRet = *this;
1293 sRet.Trim(s);
1294 return sRet;
1295 }
1296
TrimLeft_n(const CString & s) const1297 CString CString::TrimLeft_n(const CString& s) const {
1298 CString sRet = *this;
1299 sRet.TrimLeft(s);
1300 return sRet;
1301 }
1302
TrimRight_n(const CString & s) const1303 CString CString::TrimRight_n(const CString& s) const {
1304 CString sRet = *this;
1305 sRet.TrimRight(s);
1306 return sRet;
1307 }
1308
TrimPrefix(const CString & sPrefix)1309 bool CString::TrimPrefix(const CString& sPrefix) {
1310 if (StartsWith(sPrefix)) {
1311 LeftChomp(sPrefix.length());
1312 return true;
1313 } else {
1314 return false;
1315 }
1316 }
1317
TrimSuffix(const CString & sSuffix)1318 bool CString::TrimSuffix(const CString& sSuffix) {
1319 if (Right(sSuffix.length()).Equals(sSuffix)) {
1320 RightChomp(sSuffix.length());
1321 return true;
1322 } else {
1323 return false;
1324 }
1325 }
1326
Find(const CString & s,CaseSensitivity cs) const1327 size_t CString::Find(const CString& s, CaseSensitivity cs) const {
1328 if (cs == CaseSensitive) {
1329 return find(s);
1330 } else {
1331 return AsLower().find(s.AsLower());
1332 }
1333 }
1334
StartsWith(const CString & sPrefix,CaseSensitivity cs) const1335 bool CString::StartsWith(const CString& sPrefix, CaseSensitivity cs) const {
1336 return Left(sPrefix.length()).Equals(sPrefix, cs);
1337 }
1338
EndsWith(const CString & sSuffix,CaseSensitivity cs) const1339 bool CString::EndsWith(const CString& sSuffix, CaseSensitivity cs) const {
1340 return Right(sSuffix.length()).Equals(sSuffix, cs);
1341 }
1342
Contains(const CString & s,CaseSensitivity cs) const1343 bool CString::Contains(const CString& s, CaseSensitivity cs) const {
1344 return Find(s, cs) != npos;
1345 }
1346
TrimPrefix_n(const CString & sPrefix) const1347 CString CString::TrimPrefix_n(const CString& sPrefix) const {
1348 CString sRet = *this;
1349 sRet.TrimPrefix(sPrefix);
1350 return sRet;
1351 }
1352
TrimSuffix_n(const CString & sSuffix) const1353 CString CString::TrimSuffix_n(const CString& sSuffix) const {
1354 CString sRet = *this;
1355 sRet.TrimSuffix(sSuffix);
1356 return sRet;
1357 }
1358
LeftChomp_n(size_type uLen) const1359 CString CString::LeftChomp_n(size_type uLen) const {
1360 CString sRet = *this;
1361 sRet.LeftChomp(uLen);
1362 return sRet;
1363 }
1364
RightChomp_n(size_type uLen) const1365 CString CString::RightChomp_n(size_type uLen) const {
1366 CString sRet = *this;
1367 sRet.RightChomp(uLen);
1368 return sRet;
1369 }
1370
LeftChomp(size_type uLen)1371 bool CString::LeftChomp(size_type uLen) {
1372 bool bRet = false;
1373
1374 while ((uLen--) && (length())) {
1375 erase(0, 1);
1376 bRet = true;
1377 }
1378
1379 return bRet;
1380 }
1381
RightChomp(size_type uLen)1382 bool CString::RightChomp(size_type uLen) {
1383 bool bRet = false;
1384
1385 while ((uLen--) && (length())) {
1386 erase(length() - 1);
1387 bRet = true;
1388 }
1389
1390 return bRet;
1391 }
1392
StripControls_n() const1393 CString CString::StripControls_n() const {
1394 CString sRet;
1395 const unsigned char* pStart = (const unsigned char*)data();
1396 unsigned char ch = *pStart;
1397 size_type iLength = length();
1398 sRet.reserve(iLength);
1399 bool colorCode = false;
1400 unsigned int digits = 0;
1401 bool comma = false;
1402
1403 for (unsigned int a = 0; a < iLength; a++, ch = pStart[a]) {
1404 // Color code. Format: \x03([0-9]{1,2}(,[0-9]{1,2})?)?
1405 if (ch == 0x03) {
1406 colorCode = true;
1407 digits = 0;
1408 comma = false;
1409 continue;
1410 }
1411 if (colorCode) {
1412 if (isdigit(ch) && digits < 2) {
1413 digits++;
1414 continue;
1415 }
1416 if (ch == ',' && !comma) {
1417 comma = true;
1418 digits = 0;
1419 continue;
1420 }
1421
1422 colorCode = false;
1423
1424 if (digits == 0 && comma) {
1425 // There was a ',' which wasn't followed by digits, we should
1426 // print it.
1427 sRet += ',';
1428 }
1429 }
1430 // CO controls codes
1431 if (ch < 0x20 || ch == 0x7F) continue;
1432 sRet += ch;
1433 }
1434 if (colorCode && digits == 0 && comma) {
1435 sRet += ',';
1436 }
1437
1438 sRet.reserve(0);
1439 return sRet;
1440 }
1441
StripControls()1442 CString& CString::StripControls() { return (*this = StripControls_n()); }
1443
1444 //////////////// MCString ////////////////
1445 const MCString MCString::EmptyMap;
1446
WriteToDisk(const CString & sPath,mode_t iMode) const1447 MCString::status_t MCString::WriteToDisk(const CString& sPath,
1448 mode_t iMode) const {
1449 CFile cFile(sPath);
1450
1451 if (this->empty()) {
1452 if (!cFile.Exists()) return MCS_SUCCESS;
1453 if (cFile.Delete()) return MCS_SUCCESS;
1454 }
1455
1456 if (!cFile.Open(O_WRONLY | O_CREAT | O_TRUNC, iMode)) {
1457 return MCS_EOPEN;
1458 }
1459
1460 for (const auto& it : *this) {
1461 CString sKey = it.first;
1462 CString sValue = it.second;
1463 if (!WriteFilter(sKey, sValue)) {
1464 return MCS_EWRITEFIL;
1465 }
1466
1467 if (sKey.empty()) {
1468 continue;
1469 }
1470
1471 if (cFile.Write(Encode(sKey) + " " + Encode(sValue) + "\n") <= 0) {
1472 return MCS_EWRITE;
1473 }
1474 }
1475
1476 cFile.Close();
1477
1478 return MCS_SUCCESS;
1479 }
1480
ReadFromDisk(const CString & sPath)1481 MCString::status_t MCString::ReadFromDisk(const CString& sPath) {
1482 clear();
1483 CFile cFile(sPath);
1484 if (!cFile.Open(O_RDONLY)) {
1485 return MCS_EOPEN;
1486 }
1487
1488 CString sBuffer;
1489
1490 while (cFile.ReadLine(sBuffer)) {
1491 sBuffer.Trim();
1492 CString sKey = sBuffer.Token(0);
1493 CString sValue = sBuffer.Token(1);
1494 Decode(sKey);
1495 Decode(sValue);
1496
1497 if (!ReadFilter(sKey, sValue)) return MCS_EREADFIL;
1498
1499 (*this)[sKey] = sValue;
1500 }
1501 cFile.Close();
1502
1503 return MCS_SUCCESS;
1504 }
1505
1506 static const char hexdigits[] = "0123456789abcdef";
1507
Encode(CString & sValue) const1508 CString& MCString::Encode(CString& sValue) const {
1509 CString sTmp;
1510 for (unsigned char c : sValue) {
1511 // isalnum() needs unsigned char as argument and this code
1512 // assumes unsigned, too.
1513 if (isalnum(c)) {
1514 sTmp += c;
1515 } else {
1516 sTmp += "%";
1517 sTmp += hexdigits[c >> 4];
1518 sTmp += hexdigits[c & 0xf];
1519 sTmp += ";";
1520 }
1521 }
1522 sValue = sTmp;
1523 return sValue;
1524 }
1525
Decode(CString & sValue) const1526 CString& MCString::Decode(CString& sValue) const {
1527 const char* pTmp = sValue.c_str();
1528 char* endptr;
1529 CString sTmp;
1530
1531 while (*pTmp) {
1532 if (*pTmp != '%') {
1533 sTmp += *pTmp++;
1534 } else {
1535 char ch = (char)strtol(pTmp + 1, &endptr, 16);
1536 if (*endptr == ';') {
1537 sTmp += ch;
1538 pTmp = ++endptr;
1539 } else {
1540 sTmp += *pTmp++;
1541 }
1542 }
1543 }
1544
1545 sValue = sTmp;
1546 return sValue;
1547 }
1548