Go to the documentation of this file.
15 #include "string_base.h"
38 #include <unicode/ustring.h>
43 #if defined(WITH_COCOA)
62 int CDECL
vseprintf(
char *str,
const char *last,
const char *format, va_list ap)
64 ptrdiff_t diff = last - str;
65 if (diff < 0)
return 0;
66 return std::min(
static_cast<int>(diff), vsnprintf(str, diff + 1, format, ap));
85 char *
strecat(
char *dst,
const char *src,
const char *last)
88 while (*dst !=
'\0') {
89 if (dst == last)
return dst;
113 char *
strecpy(
char *dst,
const char *src,
const char *last)
116 while (dst != last && *src !=
'\0') {
121 if (dst == last && *src !=
'\0') {
122 #if defined(STRGEN) || defined(SETTINGSGEN)
123 error(
"String too long for destination buffer");
125 Debug(misc, 0,
"String too long for destination buffer");
138 char *
stredup(
const char *s,
const char *last)
140 size_t len = last ==
nullptr ? strlen(s) :
ttd_strnlen(s, last - s + 1);
141 char *tmp = CallocT<char>(len + 1);
159 char *p = MallocT<char>(len + 1);
160 memcpy(p, buf, len + 1);
172 str.reserve(data.size() * 2 + 1);
174 for (
auto b : data) {
175 fmt::format_to(std::back_inserter(str),
"{:02X}", b);
189 while (str <= last && *str !=
'\0') {
191 if ((len == 0 && str + 4 > last) || str + len > last)
break;
195 if (c ==
'\0')
break;
197 if (c == 0xE028 || c == 0xE02A) {
211 while (str <= last && *str !=
'\0') {
237 if (len == 0 || str + len > last || len !=
Utf8Decode(&c, str)) {
249 }
while (--len != 0);
301 auto buf = str.data();
302 auto last = buf + str.size();
304 std::ostringstream dst;
305 std::ostreambuf_iterator<char> dst_iter(dst);
322 while (str <= last && *str !=
'\0') {
328 if (len == 0 || str + len > last)
return false;
332 if (!IsPrintable(c) || (c >= SCC_SPRITE_START && c <= SCC_SPRITE_END)) {
350 size_t pos = str.find_first_not_of(
' ');
362 size_t pos = str.find_last_not_of(
' ');
363 if (pos != std::string::npos) str.erase(pos + 1);
385 bool StrStartsWith(
const std::string_view str,
const std::string_view prefix)
387 size_t prefix_len = prefix.size();
388 if (str.size() < prefix_len)
return false;
389 return str.compare(0, prefix_len, prefix, 0, prefix_len) == 0;
398 bool StrEndsWith(
const std::string_view str,
const std::string_view suffix)
400 size_t suffix_len = suffix.size();
401 if (str.size() < suffix_len)
return false;
402 return str.compare(str.size() - suffix_len, suffix_len, suffix, 0, suffix_len) == 0;
414 if (c < SCC_BLUE || c > SCC_BLACK) {
420 }
while (--len != 0);
439 while (Utf8Consume(&t) != 0) len++;
467 bool changed =
false;
468 for (; *str !=
'\0'; str++) {
469 char new_str = tolower(*str);
470 changed |= new_str != *str;
476 bool strtolower(std::string &str, std::string::size_type offs)
478 bool changed =
false;
479 for (
auto ch = str.begin() + offs; ch != str.end(); ++ch) {
480 auto new_ch =
static_cast<char>(tolower(
static_cast<unsigned char>(*ch)));
481 changed |= new_ch != *ch;
498 case CS_NUMERAL:
return (key >=
'0' && key <=
'9');
501 case CS_ALPHA:
return IsPrintable(key) && !(key >=
'0' && key <=
'9');
502 case CS_HEXADECIMAL:
return (key >=
'0' && key <=
'9') || (key >=
'a' && key <=
'f') || (key >=
'A' && key <=
'F');
503 default: NOT_REACHED();
508 #if defined(_MSC_VER) && _MSC_VER < 1900
516 int CDECL vsnprintf(
char *str,
size_t size,
const char *format, va_list ap)
518 if (size == 0)
return 0;
521 int ret = _vsnprintf(str, size, format, ap);
524 if (errno != ERANGE) {
529 }
else if ((
size_t)ret < size) {
538 str[size - 1] =
'\0';
554 int CDECL
seprintf(
char *str,
const char *last,
const char *format, ...)
558 va_start(ap, format);
559 int ret =
vseprintf(str, last, format, ap);
576 for (uint i = 0; i < 16; i++) {
577 p +=
seprintf(p, last,
"%02X", md5sum[i]);
595 assert(c !=
nullptr);
601 }
else if (
GB(s[0], 5, 3) == 6) {
602 if (IsUtf8Part(s[1])) {
604 *c =
GB(s[0], 0, 5) << 6 |
GB(s[1], 0, 6);
605 if (*c >= 0x80)
return 2;
607 }
else if (
GB(s[0], 4, 4) == 14) {
608 if (IsUtf8Part(s[1]) && IsUtf8Part(s[2])) {
610 *c =
GB(s[0], 0, 4) << 12 |
GB(s[1], 0, 6) << 6 |
GB(s[2], 0, 6);
611 if (*c >= 0x800)
return 3;
613 }
else if (
GB(s[0], 3, 5) == 30) {
614 if (IsUtf8Part(s[1]) && IsUtf8Part(s[2]) && IsUtf8Part(s[3])) {
616 *c =
GB(s[0], 0, 3) << 18 |
GB(s[1], 0, 6) << 12 |
GB(s[2], 0, 6) << 6 |
GB(s[3], 0, 6);
617 if (*c >= 0x10000 && *c <= 0x10FFFF)
return 4;
640 }
else if (c < 0x800) {
641 *buf++ = 0xC0 +
GB(c, 6, 5);
642 *buf = 0x80 +
GB(c, 0, 6);
644 }
else if (c < 0x10000) {
645 *buf++ = 0xE0 +
GB(c, 12, 4);
646 *buf++ = 0x80 +
GB(c, 6, 6);
647 *buf = 0x80 +
GB(c, 0, 6);
649 }
else if (c < 0x110000) {
650 *buf++ = 0xF0 +
GB(c, 18, 3);
651 *buf++ = 0x80 +
GB(c, 12, 6);
652 *buf++ = 0x80 +
GB(c, 6, 6);
653 *buf = 0x80 +
GB(c, 0, 6);
664 return Utf8Encode<char *>(buf, c);
669 return Utf8Encode<std::ostreambuf_iterator<char> &>(buf, c);
683 for (
const char *ptr = strchr(s,
'\0'); *s !=
'\0';) {
686 if (len == 0) len = 1;
690 if (length + len >= maxlen || (s + len > ptr))
break;
699 #ifdef DEFINE_STRCASESTR
700 char *strcasestr(
const char *haystack,
const char *needle)
702 size_t hay_len = strlen(haystack);
703 size_t needle_len = strlen(needle);
704 while (hay_len >= needle_len) {
705 if (strncasecmp(haystack, needle, needle_len) == 0)
return const_cast<char *
>(haystack);
737 int strnatcmp(
const char *s1,
const char *s2,
bool ignore_garbage_at_front)
739 if (ignore_garbage_at_front) {
746 UErrorCode status = U_ZERO_ERROR;
748 if (U_SUCCESS(status))
return result;
752 #if defined(_WIN32) && !defined(STRGEN) && !defined(SETTINGSGEN)
753 int res = OTTDStringCompare(s1, s2);
754 if (res != 0)
return res - 2;
757 #if defined(WITH_COCOA) && !defined(STRGEN) && !defined(SETTINGSGEN)
759 if (res != 0)
return res - 2;
763 return strcasecmp(s1, s2);
766 #ifdef WITH_UNISCRIBE
773 #elif defined(WITH_ICU_I18N)
775 #include <unicode/utext.h>
776 #include <unicode/brkiter.h>
790 UErrorCode status = U_ZERO_ERROR;
794 this->utf16_str.push_back(
'\0');
795 this->utf16_to_utf8.push_back(0);
806 const char *string_base = s;
812 this->utf16_str.clear();
813 this->utf16_to_utf8.clear();
816 size_t idx = s - string_base;
818 WChar c = Utf8Consume(&s);
820 this->utf16_str.push_back((UChar)c);
823 this->utf16_str.push_back((UChar)(0xD800 + ((c - 0x10000) >> 10)));
824 this->utf16_str.push_back((UChar)(0xDC00 + ((c - 0x10000) & 0x3FF)));
825 this->utf16_to_utf8.push_back(idx);
827 this->utf16_to_utf8.push_back(idx);
829 this->utf16_str.push_back(
'\0');
830 this->utf16_to_utf8.push_back(s - string_base);
832 UText text = UTEXT_INITIALIZER;
833 UErrorCode status = U_ZERO_ERROR;
834 utext_openUChars(&text, this->utf16_str.data(), this->utf16_str.size() - 1, &status);
835 this->char_itr->setText(&text, status);
836 this->word_itr->setText(&text, status);
837 this->char_itr->first();
838 this->word_itr->first();
845 for (uint i = 0; i < this->utf16_to_utf8.size(); i++) {
846 if (this->utf16_to_utf8[i] == pos) {
855 this->char_itr->isBoundary(utf16_pos);
856 return this->utf16_to_utf8[this->char_itr->current()];
864 pos = this->char_itr->next();
868 pos = this->word_itr->following(this->char_itr->current());
872 while (pos != icu::BreakIterator::DONE &&
874 int32_t new_pos = this->word_itr->next();
877 if (new_pos == icu::BreakIterator::DONE)
break;
881 this->char_itr->isBoundary(pos);
888 return pos == icu::BreakIterator::DONE ?
END : this->utf16_to_utf8[pos];
896 pos = this->char_itr->previous();
900 pos = this->word_itr->preceding(this->char_itr->current());
904 while (pos != icu::BreakIterator::DONE &&
906 int32_t new_pos = this->word_itr->previous();
909 if (new_pos == icu::BreakIterator::DONE)
break;
913 this->char_itr->isBoundary(pos);
920 return pos == icu::BreakIterator::DONE ?
END : this->utf16_to_utf8[pos];
939 DefaultStringIterator() : string(nullptr), len(0), cur_pos(0)
946 this->len = strlen(s);
952 assert(this->
string !=
nullptr && pos <= this->len);
954 while (pos > 0 && IsUtf8Part(this->
string[pos])) pos--;
955 return this->cur_pos = pos;
958 virtual size_t Next(IterType what)
960 assert(this->
string !=
nullptr);
963 if (this->cur_pos >= this->len)
return END;
968 this->cur_pos +=
Utf8Decode(&c, this->
string + this->cur_pos);
969 return this->cur_pos;
975 size_t offs =
Utf8Decode(&c, this->
string + this->cur_pos);
977 this->cur_pos += offs;
978 offs =
Utf8Decode(&c, this->
string + this->cur_pos);
982 this->cur_pos += offs;
983 offs =
Utf8Decode(&c, this->
string + this->cur_pos);
986 return this->cur_pos;
996 virtual size_t Prev(IterType what)
998 assert(this->
string !=
nullptr);
1001 if (this->cur_pos == 0)
return END;
1005 return this->cur_pos =
Utf8PrevChar(this->
string + this->cur_pos) - this->string;
1008 const char *s = this->
string + this->cur_pos;
1023 return this->cur_pos = s - this->string;
1034 #if defined(WITH_COCOA) && !defined(STRGEN) && !defined(SETTINGSGEN)
1038 if (i !=
nullptr)
return i;
1040 return new DefaultStringIterator();
1045 return new DefaultStringIterator();
static constexpr bool IsInsideMM(const T x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
virtual size_t Prev(IterType what=ITER_CHARACTER)=0
Move the cursor back by one iteration unit.
std::vector< size_t > utf16_to_utf8
Mapping from UTF-16 code point position to index in the UTF-8 source string.
char32_t WChar
Type for wide characters, i.e.
@ SVS_ALLOW_NEWLINE
Allow newlines.
static uint GB(const T x, const uint8 s, const uint8 n)
Fetch n bits from x, started at bit s.
IterType
Type of the iterator.
bool strtolower(char *str)
Convert a given ASCII string to lowercase.
static void StrRightTrimInPlace(std::string &str)
Trim the spaces from the end of given string in place, i.e.
static const size_t END
Sentinel to indicate end-of-iteration.
void str_fix_scc_encoded(char *str, const char *last)
Scan the string for old values of SCC_ENCODED and fix it to it's new, static value.
static bool HasBit(const T x, const uint8 y)
Checks if a bit in a value is set.
icu::BreakIterator * word_itr
ICU iterator for words.
std::unique_ptr< icu::Collator > _current_collator
Collator for the language currently in use.
size_t Utf8Encode(T buf, WChar c)
Encode a unicode character and place it in the buffer.
virtual size_t Next(IterType what=ITER_CHARACTER)=0
Advance the cursor by one iteration unit.
@ CS_ALPHA
Only alphabetic values.
String iterator using Uniscribe as a backend.
static WChar Utf16DecodeChar(const uint16 *c)
Decode an UTF-16 character.
virtual void SetString(const char *s)=0
Set a new iteration string.
icu::BreakIterator * char_itr
ICU iterator for characters.
@ ITER_CHARACTER
Iterate over characters (or more exactly grapheme clusters).
size_t Next(IterType what) override
Advance the cursor by one iteration unit.
std::string FormatArrayAsHex(span< const byte > data)
Format a byte array into a continuous hex string.
size_t Utf8StringLength(const char *s)
Get the length of an UTF-8 encoded string in number of characters and thus not the number of bytes th...
bool StrValid(const char *str, const char *last)
Checks whether the given string is valid, i.e.
static StringIterator * Create()
Create a new iterator instance.
@ SVS_ALLOW_CONTROL_CODE
Allow the special control codes.
Class for iterating over different kind of parts of a string.
size_t SetCurPosition(size_t pos) override
Change the current string cursor.
virtual size_t SetCurPosition(size_t pos)=0
Change the current string cursor.
void StrMakeValidInPlace(char *str, const char *last, StringValidationSettings settings)
Scans the string for invalid characters and replaces then with a question mark '?' (if not ignored).
void StrTrimInPlace(std::string &str)
Trim the spaces from given string in place, i.e.
void str_strip_colours(char *str)
Scans the string for colour codes and strips them.
bool StrStartsWith(const std::string_view str, const std::string_view prefix)
Check whether the given string starts with the given prefix.
const LanguageMetadata * _current_language
The currently loaded language.
std::vector< UChar > utf16_str
UTF-16 copy of the string.
bool IsValidChar(WChar key, CharSetFilter afilter)
Only allow certain keys.
static size_t ttd_strnlen(const char *str, size_t maxlen)
Get the length of a string, within a limited buffer.
fluid_settings_t * settings
FluidSynth settings handle.
int CDECL vseprintf(char *str, const char *last, const char *format, va_list ap)
Safer implementation of vsnprintf; same as vsnprintf except:
@ ITER_WORD
Iterate over words.
@ CS_ALPHANUMERAL
Both numeric and alphabetic and spaces and stuff.
StringValidationSettings
Settings for the string validation.
bool StrEndsWith(const std::string_view str, const std::string_view suffix)
Check whether the given string ends with the given suffix.
size_t Utf8Decode(WChar *c, const char *s)
Decode and consume the next UTF-8 encoded character.
std::string StrMakeValid(const std::string &str, StringValidationSettings settings)
Scans the string for invalid characters and replaces then with a question mark '?' (if not ignored).
char *CDECL str_fmt(const char *str,...)
Format, "printf", into a newly allocated string.
void SetString(const char *s) override
Set a new iteration string.
static void StrLeftTrimInPlace(std::string &str)
Trim the spaces from the begin of given string in place, i.e.
String iterator using ICU as a backend.
int CDECL seprintf(char *str, const char *last, const char *format,...)
Safer implementation of snprintf; same as snprintf except:
@ CS_NUMERAL_SIGNED
Only numbers and '-' for negative values.
char * stredup(const char *s, const char *last)
Create a duplicate of the given string.
void CDECL error(const char *s,...)
Error handling for fatal non-user errors.
#define Debug(name, level, format_string,...)
Ouptut a line of debugging information.
size_t Utf8TrimString(char *s, size_t maxlen)
Properly terminate an UTF8 string to some maximum length.
@ CS_NUMERAL_SPACE
Only numbers and spaces.
static const char * SkipGarbage(const char *str)
Skip some of the 'garbage' in the string that we don't want to use to sort on.
static bool IsWhitespace(WChar c)
Check whether UNICODE character is whitespace or not, i.e.
int strnatcmp(const char *s1, const char *s2, bool ignore_garbage_at_front)
Compares two strings using case insensitive natural sort.
char * md5sumToString(char *buf, const char *last, const uint8 md5sum[16])
Convert the md5sum to a hexadecimal string representation.
char * strecpy(char *dst, const char *src, const char *last)
Copies characters from one buffer to another.
@ CS_HEXADECIMAL
Only hexadecimal characters.
char * strecat(char *dst, const char *src, const char *last)
Appends characters from one string to another.
int MacOSStringCompare(const char *s1, const char *s2)
Compares two strings using case insensitive natural sort.
#define lastof(x)
Get the last element of an fixed size array.
@ SVS_REPLACE_WITH_QUESTION_MARK
Replace the unknown/bad bits with question marks.
@ CS_NUMERAL
Only numeric ones.
CharSetFilter
Valid filter types for IsValidChar.
static char * Utf8PrevChar(char *s)
Retrieve the previous UNICODE character in an UTF-8 encoded string.
static int8 Utf8EncodedCharLen(char c)
Return the length of an UTF-8 encoded value based on a single char.
size_t Prev(IterType what) override
Move the cursor back by one iteration unit.