X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Futil%2Fstring.h;h=122262af85348d227c10ba94c3fd5fb902df9f89;hb=1c1c97cbd1d7913ac12bf550ec02c97f843a0fd3;hp=388184ca49a645cdd480ebd042892f97763ce7a7;hpb=9e2a9b55e185b92074b32d3df336920f33b29e5a;p=minetest.git diff --git a/src/util/string.h b/src/util/string.h index 388184ca4..122262af8 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -17,33 +17,69 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#ifndef UTIL_STRING_HEADER -#define UTIL_STRING_HEADER +#pragma once #include "irrlichttypes_bloated.h" -#include +#include #include #include #include +#include #include +#include #include +#include #define STRINGIFY(x) #x #define TOSTRING(x) STRINGIFY(x) +// Checks whether a value is an ASCII printable character +#define IS_ASCII_PRINTABLE_CHAR(x) \ + (((unsigned int)(x) >= 0x20) && \ + ( (unsigned int)(x) <= 0x7e)) + +// Checks whether a byte is an inner byte for an utf-8 multibyte sequence +#define IS_UTF8_MULTB_INNER(x) \ + (((unsigned char)(x) >= 0x80) && \ + ( (unsigned char)(x) <= 0xbf)) + +// Checks whether a byte is a start byte for an utf-8 multibyte sequence +#define IS_UTF8_MULTB_START(x) \ + (((unsigned char)(x) >= 0xc2) && \ + ( (unsigned char)(x) <= 0xf4)) + +// Given a start byte x for an utf-8 multibyte sequence +// it gives the length of the whole sequence in bytes. +#define UTF8_MULTB_START_LEN(x) \ + (((unsigned char)(x) < 0xe0) ? 2 : \ + (((unsigned char)(x) < 0xf0) ? 3 : 4)) + +typedef std::unordered_map StringMap; + struct FlagDesc { const char *name; u32 flag; }; +// try not to convert between wide/utf8 encodings; this can result in data loss +// try to only convert between them when you need to input/output stuff via Irrlicht +std::wstring utf8_to_wide(const std::string &input); +std::string wide_to_utf8(const std::wstring &input); + +wchar_t *utf8_to_wide_c(const char *str); + +// NEVER use those two functions unless you have a VERY GOOD reason to +// they just convert between wide and multibyte encoding +// multibyte encoding depends on current locale, this is no good, especially on Windows + // You must free the returned string! -const wchar_t *narrow_to_wide_c(const char *mbs); +// The returned string is allocated using new +wchar_t *narrow_to_wide_c(const char *str); +std::wstring narrow_to_wide(const std::string &mbs); +std::string wide_to_narrow(const std::wstring &wcs); -std::wstring narrow_to_wide(const std::string& mbs); -std::string wide_to_narrow(const std::wstring& wcs); -std::string translatePassword(std::string playername, std::wstring password); -std::string urlencode(std::string str); -std::string urldecode(std::string str); +std::string urlencode(const std::string &str); +std::string urldecode(const std::string &str); u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask); std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask); size_t mystrlcpy(char *dst, const char *src, size_t size); @@ -148,6 +184,24 @@ inline bool str_starts_with(const std::basic_string &str, return true; } +/** + * Check whether \p str begins with the string prefix. If \p case_insensitive + * is true then the check is case insensitve (default is false; i.e. case is + * significant). + * + * @param str + * @param prefix + * @param case_insensitive + * @return true if the str begins with prefix + */ +template +inline bool str_starts_with(const std::basic_string &str, + const T *prefix, + bool case_insensitive = false) +{ + return str_starts_with(str, std::basic_string(prefix), + case_insensitive); +} /** * Splits a string into its component parts separated by the character @@ -181,8 +235,8 @@ inline std::string lowercase(const std::string &str) s2.reserve(str.size()); - for (size_t i = 0; i < str.size(); i++) - s2 += tolower(str[i]); + for (char i : str) + s2 += tolower(i); return s2; } @@ -246,15 +300,6 @@ inline s32 mystoi(const std::string &str, s32 min, s32 max) } -/// Returns a 64-bit value represented by the string \p str (decimal). -inline s64 stoi64(const std::string &str) -{ - std::stringstream tmp(str); - s64 t; - tmp >> t; - return t; -} - // MSVC2010 includes it's own versions of these //#if !defined(_MSC_VER) || _MSC_VER < 1600 @@ -293,23 +338,70 @@ inline float mystof(const std::string &str) #define stoi mystoi #define stof mystof -// TODO: Replace with C++11 std::to_string. +/// Returns a value represented by the string \p val. +template +inline T from_string(const std::string &str) +{ + std::stringstream tmp(str); + T t; + tmp >> t; + return t; +} + +/// Returns a 64-bit signed value represented by the string \p str (decimal). +inline s64 stoi64(const std::string &str) { return from_string(str); } + +#if __cplusplus < 201103L +namespace std { -/// Returns A string representing the value \p val. +/// Returns a string representing the value \p val. template -inline std::string to_string(T val) +inline string to_string(T val) { - std::ostringstream oss; + ostringstream oss; oss << val; return oss.str(); } +#define DEFINE_STD_TOSTRING_FLOATINGPOINT(T) \ + template <> \ + inline string to_string(T val) \ + { \ + ostringstream oss; \ + oss << std::fixed \ + << std::setprecision(6) \ + << val; \ + return oss.str(); \ + } +DEFINE_STD_TOSTRING_FLOATINGPOINT(float) +DEFINE_STD_TOSTRING_FLOATINGPOINT(double) +DEFINE_STD_TOSTRING_FLOATINGPOINT(long double) + +#undef DEFINE_STD_TOSTRING_FLOATINGPOINT + +/// Returns a wide string representing the value \p val +template +inline wstring to_wstring(T val) +{ + return utf8_to_wide(to_string(val)); +} +} +#endif /// Returns a string representing the decimal value of the 32-bit value \p i. -inline std::string itos(s32 i) { return to_string(i); } +inline std::string itos(s32 i) { return std::to_string(i); } /// Returns a string representing the decimal value of the 64-bit value \p i. -inline std::string i64tos(s64 i) { return to_string(i); } +inline std::string i64tos(s64 i) { return std::to_string(i); } + +// std::to_string uses the '%.6f' conversion, which is inconsistent with +// std::ostream::operator<<() and impractical too. ftos() uses the +// more generic and std::ostream::operator<<()-compatible '%G' format. /// Returns a string representing the decimal value of the float value \p f. -inline std::string ftos(float f) { return to_string(f); } +inline std::string ftos(float f) +{ + std::ostringstream oss; + oss << f; + return oss.str(); +} /** @@ -329,6 +421,17 @@ inline void str_replace(std::string &str, const std::string &pattern, } } +/** + * Escapes characters [ ] \ , ; that can not be used in formspecs + */ +inline void str_formspec_escape(std::string &str) +{ + str_replace(str, "\\", "\\\\"); + str_replace(str, "]", "\\]"); + str_replace(str, "[", "\\["); + str_replace(str, ";", "\\;"); + str_replace(str, ",", "\\,"); +} /** * Replace all occurrences of the character \p from in \p str with \p to. @@ -381,7 +484,10 @@ inline bool string_allowed_blacklist(const std::string &str, * every \p row_len characters whether it breaks a word or not. It is * intended to be used for, for example, showing paths in the GUI. * - * @param from The string to be wrapped into rows. + * @note This function doesn't wrap inside utf-8 multibyte sequences and also + * counts multibyte sequences correcly as single characters. + * + * @param from The (utf-8) string to be wrapped into rows. * @param row_len The row length (in characters). * @return A new string with the wrapping applied. */ @@ -390,9 +496,14 @@ inline std::string wrap_rows(const std::string &from, { std::string to; + size_t character_idx = 0; for (size_t i = 0; i < from.size(); i++) { - if (i != 0 && i % row_len == 0) - to += '\n'; + if (!IS_UTF8_MULTB_INNER(from[i])) { + // Wrap string after last inner byte of char + if (character_idx > 0 && character_idx % row_len == 0) + to += '\n'; + character_idx++; + } to += from[i]; } @@ -404,7 +515,7 @@ inline std::string wrap_rows(const std::string &from, * Removes backslashes from an escaped string (FormSpec strings) */ template -inline std::basic_string unescape_string(std::basic_string &s) +inline std::basic_string unescape_string(const std::basic_string &s) { std::basic_string res; @@ -420,6 +531,72 @@ inline std::basic_string unescape_string(std::basic_string &s) return res; } +/** + * Remove all escape sequences in \p s. + * + * @param s The string in which to remove escape sequences. + * @return \p s, with escape sequences removed. + */ +template +std::basic_string unescape_enriched(const std::basic_string &s) +{ + std::basic_string output; + size_t i = 0; + while (i < s.length()) { + if (s[i] == '\x1b') { + ++i; + if (i == s.length()) continue; + if (s[i] == '(') { + ++i; + while (i < s.length() && s[i] != ')') { + if (s[i] == '\\') { + ++i; + } + ++i; + } + ++i; + } else { + ++i; + } + continue; + } + output += s[i]; + ++i; + } + return output; +} + +template +std::vector > split(const std::basic_string &s, T delim) +{ + std::vector > tokens; + + std::basic_string current; + bool last_was_escape = false; + for (size_t i = 0; i < s.length(); i++) { + T si = s[i]; + if (last_was_escape) { + current += '\\'; + current += si; + last_was_escape = false; + } else { + if (si == delim) { + tokens.push_back(current); + current = std::basic_string(); + last_was_escape = false; + } else if (si == '\\') { + last_was_escape = true; + } else { + current += si; + last_was_escape = false; + } + } + } + //push last element + tokens.push_back(current); + + return tokens; +} /** * Checks that all characters in \p to_check are a decimal digits. @@ -430,8 +607,8 @@ inline std::basic_string unescape_string(std::basic_string &s) */ inline bool is_number(const std::string &to_check) { - for (size_t i = 0; i < to_check.size(); i++) - if (!std::isdigit(to_check[i])) + for (char i : to_check) + if (!std::isdigit(i)) return false; return !to_check.empty(); @@ -448,4 +625,25 @@ inline const char *bool_to_cstr(bool val) return val ? "true" : "false"; } -#endif +inline const std::string duration_to_string(int sec) +{ + int min = sec / 60; + sec %= 60; + int hour = min / 60; + min %= 60; + + std::stringstream ss; + if (hour > 0) { + ss << hour << "h "; + } + + if (min > 0) { + ss << min << "m "; + } + + if (sec > 0) { + ss << sec << "s "; + } + + return ss.str(); +}