// Stuff that can't be included in vmallocator.h #include "globalincs/vmallocator.h" // std::locale("") pulls in the process's environment locale categories (LC_CTYPE, // LC_MONETARY, LC_PAPER, etc.), any one of which can name a locale that isn't actually // installed on the host (e.g. mismatched regional-format overrides). That throws // std::runtime_error, and since this runs during static initialization -- before // main(), before any of our own exception handling exists -- an uncaught throw here // aborts the whole process before the game even starts. SCP_default_locale is only // ever used for plain ASCII case-folding (SCP_toupper/tolower/isdigit), so falling // back to the classic "C" locale is a safe, silent degradation. static std::locale make_scp_default_locale() { try { return std::locale(""); } catch (const std::runtime_error&) { return std::locale::classic(); } } std::locale SCP_default_locale = make_scp_default_locale(); void SCP_tolower(SCP_string &str) { std::for_each(str.begin(), str.end(), [](char &ch) { ch = SCP_tolower(ch); }); } void SCP_toupper(SCP_string &str) { std::for_each(str.begin(), str.end(), [](char &ch) { ch = SCP_toupper(ch); }); } void SCP_tolower(char *str) { for (; *str != '\0'; ++str) *str = SCP_tolower(*str); } void SCP_toupper(char *str) { for (; *str != '\0'; ++str) *str = SCP_toupper(*str); } // in-place modification of string to title case; this is a bit naive but it is good enough for the time being void SCP_totitle(char *str) { bool prev_alpha = false; for (; *str != '\0'; ++str) { bool this_alpha = (*str >= 'a' && *str <= 'z') || (*str >= 'A' && *str <= 'Z'); if (this_alpha) { if (prev_alpha) *str = SCP_tolower(*str); else *str = SCP_toupper(*str); } prev_alpha = this_alpha; } } // in-place modification of string to title case; same naive algorithm as above void SCP_totitle(SCP_string &str) { SCP_string title_str; bool prev_alpha = false; std::for_each(str.begin(), str.end(), [&prev_alpha](char &ch) { bool this_alpha = (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); if (this_alpha) { if (prev_alpha) ch = SCP_tolower(ch); else ch = SCP_toupper(ch); } prev_alpha = this_alpha; }); } bool SCP_truncate(SCP_string &str, size_t len) { if (str.length() > len) { str.resize(len); return true; } else return false; } bool SCP_trim(SCP_string& str) { auto start = str.find_first_not_of(" \t\r\n"); auto end = str.find_last_not_of(" \t\r\n"); if (start == SCP_string::npos) { str.clear(); return true; } if (start > 0 || end < str.length() - 1) { str = str.substr(start, end - start + 1); return true; } return false; } bool lcase_equal(const SCP_string& _Left, const SCP_string& _Right) { if (_Left.size() != _Right.size()) return false; auto l_it = _Left.cbegin(); auto r_it = _Right.cbegin(); while (l_it != _Left.cend()) { if (SCP_tolower(*l_it) != SCP_tolower(*r_it)) return false; ++l_it; ++r_it; } return true; } bool lcase_lessthan(const SCP_string& _Left, const SCP_string& _Right) { auto l_it = _Left.cbegin(); auto r_it = _Right.cbegin(); while (true) { if (l_it == _Left.cend()) return (r_it != _Right.cend()); else if (r_it == _Right.cend()) return false; auto lch = SCP_tolower(*l_it); auto rch = SCP_tolower(*r_it); if (lch < rch) return true; else if (lch > rch) return false; ++l_it; ++r_it; } return true; }