1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "common/hash-str.h"
24 #include "common/list.h"
25 #include "common/memorypool.h"
26 #include "common/str.h"
27 #include "common/util.h"
28 #include "common/mutex.h"
29
30 namespace Common {
31
String(char c)32 String::String(char c)
33 : BaseString<char>() {
34
35 _storage[0] = c;
36 _storage[1] = 0;
37
38 _size = (c == 0) ? 0 : 1;
39 }
40
41 #ifndef SCUMMVM_UTIL
String(const U32String & str,Common::CodePage page)42 String::String(const U32String &str, Common::CodePage page)
43 : BaseString<char>() {
44 _storage[0] = 0;
45 *this = String(str.encode(page));
46 }
47 #endif
48
operator =(const char * str)49 String &String::operator=(const char *str) {
50 assign(str);
51 return *this;
52 }
53
operator =(const String & str)54 String &String::operator=(const String &str) {
55 assign(str);
56 return *this;
57 }
58
operator =(char c)59 String &String::operator=(char c) {
60 assign(c);
61 return *this;
62 }
63
operator +=(const char * str)64 String &String::operator+=(const char *str) {
65 assignAppend(str);
66 return *this;
67 }
68
operator +=(const String & str)69 String &String::operator+=(const String &str) {
70 assignAppend(str);
71 return *this;
72 }
73
operator +=(char c)74 String &String::operator+=(char c) {
75 assignAppend(c);
76 return *this;
77 }
78
hasPrefix(const String & x) const79 bool String::hasPrefix(const String &x) const {
80 return hasPrefix(x.c_str());
81 }
82
hasPrefix(const char * x) const83 bool String::hasPrefix(const char *x) const {
84 assert(x != nullptr);
85 // Compare x with the start of _str.
86 const char *y = c_str();
87 while (*x && *x == *y) {
88 ++x;
89 ++y;
90 }
91 // It's a prefix, if and only if all letters in x are 'used up' before
92 // _str ends.
93 return *x == 0;
94 }
95
hasPrefixIgnoreCase(const String & x) const96 bool String::hasPrefixIgnoreCase(const String &x) const {
97 return hasPrefixIgnoreCase(x.c_str());
98 }
99
hasPrefixIgnoreCase(const char * x) const100 bool String::hasPrefixIgnoreCase(const char *x) const {
101 assert(x != nullptr);
102 // Compare x with the start of _str.
103 const char *y = c_str();
104 while (*x && tolower(*x) == tolower(*y)) {
105 ++x;
106 ++y;
107 }
108 // It's a prefix, if and only if all letters in x are 'used up' before
109 // _str ends.
110 return *x == 0;
111 }
112
hasSuffix(const String & x) const113 bool String::hasSuffix(const String &x) const {
114 return hasSuffix(x.c_str());
115 }
116
hasSuffix(const char * x) const117 bool String::hasSuffix(const char *x) const {
118 assert(x != nullptr);
119 // Compare x with the end of _str.
120 const uint32 x_size = strlen(x);
121 if (x_size > _size)
122 return false;
123 const char *y = c_str() + _size - x_size;
124 while (*x && *x == *y) {
125 ++x;
126 ++y;
127 }
128 // It's a suffix, if and only if all letters in x are 'used up' before
129 // _str ends.
130 return *x == 0;
131 }
132
hasSuffixIgnoreCase(const String & x) const133 bool String::hasSuffixIgnoreCase(const String &x) const {
134 return hasSuffixIgnoreCase(x.c_str());
135 }
136
hasSuffixIgnoreCase(const char * x) const137 bool String::hasSuffixIgnoreCase(const char *x) const {
138 assert(x != nullptr);
139 // Compare x with the end of _str.
140 const uint32 x_size = strlen(x);
141 if (x_size > _size)
142 return false;
143 const char *y = c_str() + _size - x_size;
144 while (*x && tolower(*x) == tolower(*y)) {
145 ++x;
146 ++y;
147 }
148 // It's a suffix, if and only if all letters in x are 'used up' before
149 // _str ends.
150 return *x == 0;
151 }
152
contains(const String & x) const153 bool String::contains(const String &x) const {
154 return strstr(c_str(), x.c_str()) != nullptr;
155 }
156
contains(const char * x) const157 bool String::contains(const char *x) const {
158 assert(x != nullptr);
159 return strstr(c_str(), x) != nullptr;
160 }
161
contains(char x) const162 bool String::contains(char x) const {
163 return strchr(c_str(), x) != nullptr;
164 }
165
contains(uint32 x) const166 bool String::contains(uint32 x) const {
167 for (String::const_iterator itr = begin(); itr != end(); itr++) {
168 if (uint32(*itr) == x) {
169 return true;
170 }
171 }
172 return false;
173 }
174
175 #ifdef USE_CXX11
contains(char32_t x) const176 bool String::contains(char32_t x) const {
177 return contains((uint32)x);
178 }
179 #endif
180
181 #ifndef SCUMMVM_UTIL
182
matchString(const char * pat,bool ignoreCase,const char * wildcardExclusions) const183 bool String::matchString(const char *pat, bool ignoreCase, const char *wildcardExclusions) const {
184 return Common::matchString(c_str(), pat, ignoreCase, wildcardExclusions);
185 }
186
matchString(const String & pat,bool ignoreCase,const char * wildcardExclusions) const187 bool String::matchString(const String &pat, bool ignoreCase, const char *wildcardExclusions) const {
188 return Common::matchString(c_str(), pat.c_str(), ignoreCase, wildcardExclusions);
189 }
190
191 #endif
192
replace(uint32 pos,uint32 count,const String & str)193 void String::replace(uint32 pos, uint32 count, const String &str) {
194 replace(pos, count, str, 0, str._size);
195 }
196
replace(uint32 pos,uint32 count,const char * str)197 void String::replace(uint32 pos, uint32 count, const char *str) {
198 replace(pos, count, str, 0, strlen(str));
199 }
200
replace(iterator begin_,iterator end_,const String & str)201 void String::replace(iterator begin_, iterator end_, const String &str) {
202 replace(begin_ - _str, end_ - begin_, str._str, 0, str._size);
203 }
204
replace(iterator begin_,iterator end_,const char * str)205 void String::replace(iterator begin_, iterator end_, const char *str) {
206 replace(begin_ - _str, end_ - begin_, str, 0, strlen(str));
207 }
208
replace(uint32 posOri,uint32 countOri,const String & str,uint32 posDest,uint32 countDest)209 void String::replace(uint32 posOri, uint32 countOri, const String &str,
210 uint32 posDest, uint32 countDest) {
211 replace(posOri, countOri, str._str, posDest, countDest);
212 }
213
replace(uint32 posOri,uint32 countOri,const char * str,uint32 posDest,uint32 countDest)214 void String::replace(uint32 posOri, uint32 countOri, const char *str,
215 uint32 posDest, uint32 countDest) {
216
217 // Prepare string for the replaced text.
218 if (countOri < countDest) {
219 uint32 offset = countDest - countOri; ///< Offset to copy the characters
220 uint32 newSize = _size + offset;
221
222 ensureCapacity(newSize, true);
223
224 _size = newSize;
225
226 // Push the old characters to the end of the string
227 for (uint32 i = _size; i >= posOri + countDest; i--)
228 _str[i] = _str[i - offset];
229
230 } else if (countOri > countDest){
231 uint32 offset = countOri - countDest; ///< Number of positions that we have to pull back
232
233 makeUnique();
234
235 // Pull the remainder string back
236 for (uint32 i = posOri + countDest; i + offset <= _size; i++)
237 _str[i] = _str[i + offset];
238
239 _size -= offset;
240 } else {
241 makeUnique();
242 }
243
244 // Copy the replaced part of the string
245 for (uint32 i = 0; i < countDest; i++)
246 _str[posOri + i] = str[posDest + i];
247
248 }
249
250 // static
format(const char * fmt,...)251 String String::format(const char *fmt, ...) {
252 String output;
253
254 va_list va;
255 va_start(va, fmt);
256 output = String::vformat(fmt, va);
257 va_end(va);
258
259 return output;
260 }
261
262 // static
vformat(const char * fmt,va_list args)263 String String::vformat(const char *fmt, va_list args) {
264 String output;
265 assert(output.isStorageIntern());
266
267 va_list va;
268 scumm_va_copy(va, args);
269 int len = vsnprintf(output._str, _builtinCapacity, fmt, va);
270 va_end(va);
271
272 if (len == -1 || len == _builtinCapacity - 1) {
273 // MSVC and IRIX don't return the size the full string would take up.
274 // MSVC returns -1, IRIX returns the number of characters actually written,
275 // which is at the most the size of the buffer minus one, as the string is
276 // truncated to fit.
277
278 // We assume MSVC failed to output the correct, null-terminated string
279 // if the return value is either -1 or size.
280 // For IRIX, because we lack a better mechanism, we assume failure
281 // if the return value equals size - 1.
282 // The downside to this is that whenever we try to format a string where the
283 // size is 1 below the built-in capacity, the size is needlessly increased.
284
285 // Try increasing the size of the string until it fits.
286 int size = _builtinCapacity;
287 do {
288 size *= 2;
289 output.ensureCapacity(size - 1, false);
290 assert(!output.isStorageIntern());
291 size = output._extern._capacity;
292
293 scumm_va_copy(va, args);
294 len = vsnprintf(output._str, size, fmt, va);
295 va_end(va);
296 } while (len == -1 || len >= size - 1);
297 output._size = len;
298 } else if (len < (int)_builtinCapacity) {
299 // vsnprintf succeeded
300 output._size = len;
301 } else {
302 // vsnprintf didn't have enough space, so grow buffer
303 output.ensureCapacity(len, false);
304 scumm_va_copy(va, args);
305 int len2 = vsnprintf(output._str, len + 1, fmt, va);
306 va_end(va);
307 assert(len == len2);
308 output._size = len2;
309 }
310
311 return output;
312 }
313
rfind(const char * s) const314 size_t String::rfind(const char *s) const {
315 int sLen = strlen(s);
316
317 for (int idx = (int)_size - sLen; idx >= 0; --idx) {
318 if (!strncmp(_str + idx, s, sLen))
319 return idx;
320 }
321
322 return npos;
323 }
324
rfind(char c,size_t pos) const325 size_t String::rfind(char c, size_t pos) const {
326 for (int idx = MIN((int)_size - 1, (int)pos); idx >= 0; --idx) {
327 if ((*this)[idx] == c)
328 return idx;
329 }
330
331 return npos;
332 }
333
findFirstOf(char c,size_t pos) const334 size_t String::findFirstOf(char c, size_t pos) const {
335 const char *strP = (pos >= _size) ? 0 : strchr(_str + pos, c);
336 return strP ? strP - _str : npos;
337 }
338
findFirstOf(const char * chars,size_t pos) const339 size_t String::findFirstOf(const char *chars, size_t pos) const {
340 for (uint idx = pos; idx < _size; ++idx) {
341 if (strchr(chars, (*this)[idx]))
342 return idx;
343 }
344
345 return npos;
346 }
347
findLastOf(char c,size_t pos) const348 size_t String::findLastOf(char c, size_t pos) const {
349 int start = (pos == npos) ? (int)_size - 1 : MIN((int)_size - 1, (int)pos);
350 for (int idx = start; idx >= 0; --idx) {
351 if ((*this)[idx] == c)
352 return idx;
353 }
354
355 return npos;
356 }
357
findLastOf(const char * chars,size_t pos) const358 size_t String::findLastOf(const char *chars, size_t pos) const {
359 int start = (pos == npos) ? (int)_size - 1 : MIN((int)_size - 1, (int)pos);
360 for (int idx = start; idx >= 0; --idx) {
361 if (strchr(chars, (*this)[idx]))
362 return idx;
363 }
364
365 return npos;
366 }
367
findFirstNotOf(char c,size_t pos) const368 size_t String::findFirstNotOf(char c, size_t pos) const {
369 for (uint idx = pos; idx < _size; ++idx) {
370 if ((*this)[idx] != c)
371 return idx;
372 }
373
374 return npos;
375 }
376
findFirstNotOf(const char * chars,size_t pos) const377 size_t String::findFirstNotOf(const char *chars, size_t pos) const {
378 for (uint idx = pos; idx < _size; ++idx) {
379 if (!strchr(chars, (*this)[idx]))
380 return idx;
381 }
382
383 return npos;
384 }
385
findLastNotOf(char c) const386 size_t String::findLastNotOf(char c) const {
387 for (int idx = (int)_size - 1; idx >= 0; --idx) {
388 if ((*this)[idx] != c)
389 return idx;
390 }
391
392 return npos;
393 }
394
findLastNotOf(const char * chars) const395 size_t String::findLastNotOf(const char *chars) const {
396 for (int idx = (int)_size - 1; idx >= 0; --idx) {
397 if (!strchr(chars, (*this)[idx]))
398 return idx;
399 }
400
401 return npos;
402 }
403
substr(size_t pos,size_t len) const404 String String::substr(size_t pos, size_t len) const {
405 if (pos >= _size)
406 return String();
407 else if (len == npos)
408 return String(_str + pos);
409 else
410 return String(_str + pos, MIN((size_t)_size - pos, len));
411 }
412
forEachLine(String (* func)(const String,va_list args),...) const413 String String::forEachLine(String(*func)(const String, va_list args), ...) const {
414 String result = "";
415 size_t index = findFirstOf('\n', 0);
416 size_t prev_index = 0;
417 va_list args;
418 va_start(args, func);
419 while (index != npos) {
420 String textLine = substr(prev_index, index - prev_index);
421 textLine = (*func)(textLine, args);
422 result = result + textLine + '\n';
423 prev_index = index + 1;
424 index = findFirstOf('\n', index + 1);
425 }
426
427 String textLine = substr(prev_index);
428 textLine = (*func)(textLine, args);
429 result = result + textLine;
430 va_end(args);
431 return result;
432 }
433
434 #pragma mark -
435
operator ==(const char * y,const String & x)436 bool operator==(const char* y, const String &x) {
437 return (x == y);
438 }
439
operator !=(const char * y,const String & x)440 bool operator!=(const char* y, const String &x) {
441 return x != y;
442 }
443
444 #pragma mark -
445
equalsIgnoreCase(const String & x) const446 bool String::equalsIgnoreCase(const String &x) const {
447 return (0 == compareToIgnoreCase(x));
448 }
449
equalsIgnoreCase(const char * x) const450 bool String::equalsIgnoreCase(const char *x) const {
451 assert(x != nullptr);
452 return (0 == compareToIgnoreCase(x));
453 }
454
compareToIgnoreCase(const String & x) const455 int String::compareToIgnoreCase(const String &x) const {
456 return compareToIgnoreCase(x.c_str());
457 }
458
compareToIgnoreCase(const char * x) const459 int String::compareToIgnoreCase(const char *x) const {
460 assert(x != nullptr);
461 return scumm_stricmp(c_str(), x);
462 }
463
compareDictionary(const String & x) const464 int String::compareDictionary(const String &x) const {
465 return compareDictionary(x.c_str());
466 }
467
compareDictionary(const char * x) const468 int String::compareDictionary(const char *x) const {
469 assert(x != nullptr);
470 return scumm_compareDictionary(c_str(), x);
471 }
472
473 #pragma mark -
474
operator +(const String & x,const String & y)475 String operator+(const String &x, const String &y) {
476 String temp(x);
477 temp += y;
478 return temp;
479 }
480
operator +(const char * x,const String & y)481 String operator+(const char *x, const String &y) {
482 String temp(x);
483 temp += y;
484 return temp;
485 }
486
operator +(const String & x,const char * y)487 String operator+(const String &x, const char *y) {
488 String temp(x);
489 temp += y;
490 return temp;
491 }
492
operator +(char x,const String & y)493 String operator+(char x, const String &y) {
494 String temp(x);
495 temp += y;
496 return temp;
497 }
498
operator +(const String & x,char y)499 String operator+(const String &x, char y) {
500 String temp(x);
501 temp += y;
502 return temp;
503 }
504
505 #ifndef SCUMMVM_UTIL
506
ltrim(char * t)507 char *ltrim(char *t) {
508 while (isSpace(*t))
509 t++;
510 return t;
511 }
512
rtrim(char * t)513 char *rtrim(char *t) {
514 int l = strlen(t) - 1;
515 while (l >= 0 && isSpace(t[l]))
516 t[l--] = 0;
517 return t;
518 }
519
trim(char * t)520 char *trim(char *t) {
521 return rtrim(ltrim(t));
522 }
523
524 #endif
525
lastPathComponent(const String & path,const char sep)526 String lastPathComponent(const String &path, const char sep) {
527 const char *str = path.c_str();
528 const char *last = str + path.size();
529
530 // Skip over trailing slashes
531 while (last > str && *(last - 1) == sep)
532 --last;
533
534 // Path consisted of only slashes -> return empty string
535 if (last == str)
536 return String();
537
538 // Now scan the whole component
539 const char *first = last - 1;
540 while (first > str && *first != sep)
541 --first;
542
543 if (*first == sep)
544 first++;
545
546 return String(first, last);
547 }
548
normalizePath(const String & path,const char sep)549 String normalizePath(const String &path, const char sep) {
550 if (path.empty())
551 return path;
552
553 const char *cur = path.c_str();
554 String result;
555
556 // If there is a leading slash, preserve that:
557 if (*cur == sep) {
558 result += sep;
559 // Skip over multiple leading slashes, so "//" equals "/"
560 while (*cur == sep)
561 ++cur;
562 }
563
564 // Scan for path components till the end of the String
565 List<String> comps;
566 while (*cur != 0) {
567 const char *start = cur;
568
569 // Scan till the next path separator resp. the end of the string
570 while (*cur != sep && *cur != 0)
571 cur++;
572
573 const String component(start, cur);
574
575 if (component.empty() || component == ".") {
576 // Skip empty components and dot components
577 } else if (!comps.empty() && component == ".." && comps.back() != "..") {
578 // If stack is non-empty and top is not "..", remove top
579 comps.pop_back();
580 } else {
581 // Add the component to the stack
582 comps.push_back(component);
583 }
584
585 // Skip over separator chars
586 while (*cur == sep)
587 cur++;
588 }
589
590 // Finally, assemble all components back into a path
591 while (!comps.empty()) {
592 result += comps.front();
593 comps.pop_front();
594 if (!comps.empty())
595 result += sep;
596 }
597
598 return result;
599 }
600
601 #ifndef SCUMMVM_UTIL
602
matchString(const char * str,const char * pat,bool ignoreCase,const char * wildcardExclusions)603 bool matchString(const char *str, const char *pat, bool ignoreCase, const char *wildcardExclusions) {
604 assert(str);
605 assert(pat);
606
607 const char *p = nullptr;
608 const char *q = nullptr;
609 bool escaped = false;
610
611 for (;;) {
612 if (wildcardExclusions && strchr(wildcardExclusions, *str)) {
613 p = nullptr;
614 q = nullptr;
615 if (*pat == '?')
616 return false;
617 }
618
619 const char curPat = *pat;
620 switch (*pat) {
621 case '*':
622 if (*str) {
623 // Record pattern / string position for backtracking
624 p = ++pat;
625 q = str;
626 } else {
627 // If we've reached the end of str, we can't backtrack further
628 // NB: We can't simply check if pat also ended here, because
629 // the pattern might end with any number of *s.
630 ++pat;
631 p = nullptr;
632 q = nullptr;
633 }
634 // If pattern ended with * -> match
635 if (!*pat)
636 return true;
637 break;
638
639 case '\\':
640 if (!escaped) {
641 pat++;
642 break;
643 }
644 // fallthrough
645
646 case '#':
647 // treat # as a wildcard for digits unless escaped
648 if (!escaped) {
649 if (!isDigit(*str))
650 return false;
651 pat++;
652 str++;
653 break;
654 }
655 // fallthrough
656
657 default:
658 if ((!ignoreCase && *pat != *str) ||
659 (ignoreCase && tolower(*pat) != tolower(*str))) {
660 if (p) {
661 // No match, oops -> try to backtrack
662 pat = p;
663 str = ++q;
664 if (!*str)
665 return !*pat;
666 break;
667 }
668 else
669 return false;
670 }
671 // fallthrough
672 case '?':
673 if (!*str)
674 return !*pat;
675 pat++;
676 str++;
677 }
678
679 escaped = !escaped && (curPat == '\\');
680 }
681 }
682
replace(Common::String & source,const Common::String & what,const Common::String & with)683 void replace(Common::String &source, const Common::String &what, const Common::String &with) {
684 const char *cstr = source.c_str();
685 const char *position = strstr(cstr, what.c_str());
686 if (position) {
687 uint32 index = position - cstr;
688 source.replace(index, what.size(), with);
689 }
690 }
691
tag2string(uint32 tag,bool nonPrintable)692 String tag2string(uint32 tag, bool nonPrintable) {
693 Common::String res;
694
695 for (int i = 3; i >= 0; i--) {
696 byte b = (tag >> (8 * i)) & 0xff;
697
698 if (!Common::isPrint(b)) {
699 if (nonPrintable) {
700 res += Common::String::format("\\%03o", b);
701 } else {
702 res += '.';
703 }
704 } else {
705 res += b;
706 }
707 }
708
709 return res;
710 }
711
712 #endif
713
strlcpy(char * dst,const char * src,size_t size)714 size_t strlcpy(char *dst, const char *src, size_t size) {
715 // Our backup of the source's start, we need this
716 // to calculate the source's length.
717 const char * const srcStart = src;
718
719 // In case a non-empty size was specified we
720 // copy over (size - 1) bytes at max.
721 if (size != 0) {
722 // Copy over (size - 1) bytes at max.
723 while (--size != 0) {
724 if ((*dst++ = *src) == 0)
725 break;
726 ++src;
727 }
728
729 // In case the source string was longer than the
730 // destination, we need to add a terminating
731 // zero.
732 if (size == 0)
733 *dst = 0;
734 }
735
736 // Move to the terminating zero of the source
737 // string, we need this to determine the length
738 // of the source string.
739 while (*src)
740 ++src;
741
742 // Return the source string's length.
743 return src - srcStart;
744 }
745
strlcat(char * dst,const char * src,size_t size)746 size_t strlcat(char *dst, const char *src, size_t size) {
747 // In case the destination buffer does not contain
748 // space for at least 1 character, we will just
749 // return the source string's length.
750 if (size == 0)
751 return strlen(src);
752
753 // Our backup of the source's start, we need this
754 // to calculate the source's length.
755 const char * const srcStart = src;
756
757 // Our backup of the destination's start, we need
758 // this to calculate the destination's length.
759 const char * const dstStart = dst;
760
761 // Search the end of the destination, but do not
762 // move past the terminating zero.
763 while (size-- != 0 && *dst != 0)
764 ++dst;
765
766 // Calculate the destination's length;
767 const size_t dstLength = dst - dstStart;
768
769 // In case we reached the end of the destination
770 // buffer before we had a chance to append any
771 // characters we will just return the destination
772 // length plus the source string's length.
773 if (size == 0)
774 return dstLength + strlen(srcStart);
775
776 // Copy over all of the source that fits
777 // the destination buffer. We also need
778 // to take the terminating zero we will
779 // add into consideration.
780 while (size-- != 0 && *src != 0)
781 *dst++ = *src++;
782 *dst = 0;
783
784 // Move to the terminating zero of the source
785 // string, we need this to determine the length
786 // of the source string.
787 while (*src)
788 ++src;
789
790 // Return the total length of the result string
791 return dstLength + (src - srcStart);
792 }
793
strnlen(const char * src,size_t maxSize)794 size_t strnlen(const char *src, size_t maxSize) {
795 size_t counter = 0;
796 while (counter != maxSize && *src++)
797 ++counter;
798 return counter;
799 }
800
toPrintable(const String & in,bool keepNewLines)801 String toPrintable(const String &in, bool keepNewLines) {
802 Common::String res;
803
804 const char *tr = "\x01\x01\x02\x03\x04\x05\x06" "a"
805 //"\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f";
806 "b" "t" "n" "v" "f" "r\x0e\x0f"
807 "\x10\x11\x12\x13\x14\x15\x16\x17"
808 "\x18\x19\x1a" "e\x1c\x1d\x1e\x1f";
809
810 for (const byte *p = (const byte *)in.c_str(); *p; p++) {
811 if (*p == '\n') {
812 if (keepNewLines)
813 res += *p;
814 else
815 res += "\\n";
816
817 continue;
818 }
819
820 if (*p < 0x20 || *p == '\'' || *p == '\"' || *p == '\\') {
821 res += '\\';
822
823 if (*p < 0x20) {
824 if (tr[*p] < 0x20)
825 res += Common::String::format("x%02x", *p);
826 else
827 res += tr[*p];
828 } else {
829 res += *p; // We will escape it
830 }
831 } else if (*p > 0x7e) {
832 res += Common::String::format("\\x%02x", *p);
833 } else
834 res += *p;
835 }
836
837 return res;
838 }
839
840 } // End of namespace Common
841
842 // Portable implementation of stricmp / strcasecmp / strcmpi.
843 // TODO: Rename this to Common::strcasecmp
scumm_stricmp(const char * s1,const char * s2)844 int scumm_stricmp(const char *s1, const char *s2) {
845 byte l1, l2;
846 do {
847 // Don't use ++ inside tolower, in case the macro uses its
848 // arguments more than once.
849 l1 = (byte)*s1++;
850 l1 = tolower(l1);
851 l2 = (byte)*s2++;
852 l2 = tolower(l2);
853 } while (l1 == l2 && l1 != 0);
854 return l1 - l2;
855 }
856
857 // Portable implementation of strnicmp / strncasecmp / strncmpi.
858 // TODO: Rename this to Common::strncasecmp
scumm_strnicmp(const char * s1,const char * s2,uint n)859 int scumm_strnicmp(const char *s1, const char *s2, uint n) {
860 byte l1, l2;
861 do {
862 if (n-- == 0)
863 return 0; // no difference found so far -> signal equality
864
865 // Don't use ++ inside tolower, in case the macro uses its
866 // arguments more than once.
867 l1 = (byte)*s1++;
868 l1 = tolower(l1);
869 l2 = (byte)*s2++;
870 l2 = tolower(l2);
871 } while (l1 == l2 && l1 != 0);
872 return l1 - l2;
873 }
874
scumm_skipArticle(const char * s1)875 const char *scumm_skipArticle(const char *s1) {
876 int o1 = 0;
877 if (!scumm_strnicmp(s1, "the ", 4))
878 o1 = 4;
879 else if (!scumm_strnicmp(s1, "a ", 2))
880 o1 = 2;
881 else if (!scumm_strnicmp(s1, "an ", 3))
882 o1 = 3;
883
884 return &s1[o1];
885 }
886
scumm_compareDictionary(const char * s1,const char * s2)887 int scumm_compareDictionary(const char *s1, const char *s2) {
888 return scumm_stricmp(scumm_skipArticle(s1), scumm_skipArticle(s2));
889 }
890
891 // Portable implementation of strdup.
scumm_strdup(const char * in)892 char *scumm_strdup(const char *in) {
893 const size_t len = strlen(in) + 1;
894 char *out = (char *)malloc(len);
895 if (out) {
896 strcpy(out, in);
897 }
898 return out;
899 }
900
901 // Portable implementation of strcasestr.
scumm_strcasestr(const char * s,const char * find)902 const char *scumm_strcasestr(const char *s, const char *find) {
903 char c, sc;
904 size_t len;
905
906 if ((c = *find++) != 0) {
907 c = (char)tolower((unsigned char)c);
908 len = strlen(find);
909 do {
910 do {
911 if ((sc = *s++) == 0)
912 return (NULL);
913 } while ((char)tolower((unsigned char)sc) != c);
914 } while (scumm_strnicmp(s, find, len) != 0);
915 s--;
916 }
917 return s;
918 }
919