Luanti 5.11.0-dev
 
Loading...
Searching...
No Matches
string.h
Go to the documentation of this file.
1// Luanti
2// SPDX-License-Identifier: LGPL-2.1-or-later
3// Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5#pragma once
6
8#include "config.h" // IS_CLIENT_BUILD
9#if IS_CLIENT_BUILD
10#include "irrString.h"
11#endif
12#include <cstdlib>
13#include <string>
14#include <string_view>
15#include <cstring>
16#include <vector>
17#include <limits>
18#include <sstream>
19#include <iomanip>
20#include <cctype>
21#include <cwctype>
22#include <unordered_map>
23
24class Translations;
25
26#define STRINGIFY(x) #x
27#define TOSTRING(x) STRINGIFY(x)
28
29// Checks whether a value is an ASCII printable character
30#define IS_ASCII_PRINTABLE_CHAR(x) \
31 (((unsigned int)(x) >= 0x20) && \
32 ( (unsigned int)(x) <= 0x7e))
33
34// Checks whether a value is in a Unicode private use area
35#define IS_PRIVATE_USE_CHAR16(x) \
36 ((wchar_t)(x) >= 0xE000 && \
37 (wchar_t)(x) <= 0xF8FF)
38#define IS_PRIVATE_USE_CHAR32(x) \
39 (((wchar_t)(x) >= 0xF0000 && \
40 (wchar_t)(x) <= 0xFFFFD) || \
41 ((wchar_t)(x) >= 0x100000 && \
42 (wchar_t)(x) <= 0x10FFFD))
43#if WCHAR_MAX > 0xFFFF
44#define IS_PRIVATE_USE_CHAR(x) (IS_PRIVATE_USE_CHAR16(x) || IS_PRIVATE_USE_CHAR32(x))
45#else
46#define IS_PRIVATE_USE_CHAR(x) IS_PRIVATE_USE_CHAR16(x)
47#endif
48
49// Checks whether a byte is an inner byte for an utf-8 multibyte sequence
50#define IS_UTF8_MULTB_INNER(x) \
51 (((unsigned char)(x) >= 0x80) && \
52 ( (unsigned char)(x) <= 0xbf))
53
54// Checks whether a byte is a start byte for an utf-8 multibyte sequence
55#define IS_UTF8_MULTB_START(x) \
56 (((unsigned char)(x) >= 0xc2) && \
57 ( (unsigned char)(x) <= 0xf4))
58
59// Given a start byte x for an utf-8 multibyte sequence
60// it gives the length of the whole sequence in bytes.
61#define UTF8_MULTB_START_LEN(x) \
62 (((unsigned char)(x) < 0xe0) ? 2 : \
63 (((unsigned char)(x) < 0xf0) ? 3 : 4))
64
65typedef std::unordered_map<std::string, std::string> StringMap;
66
67struct FlagDesc {
68 const char *name;
69 u32 flag;
70};
71
72// Try to avoid converting between wide and UTF-8 unless you need to
73// input/output stuff via Irrlicht
74std::wstring utf8_to_wide(std::string_view input);
75std::string wide_to_utf8(std::wstring_view input);
76
77void wide_add_codepoint(std::wstring &result, char32_t codepoint);
78
79std::string urlencode(std::string_view str);
80std::string urldecode(std::string_view str);
81
82u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
83std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask);
84
85size_t mystrlcpy(char *dst, const char *src, size_t size) noexcept;
86char *mystrtok_r(char *s, const char *sep, char **lasts) noexcept;
87
88u64 read_seed(const char *str);
89bool parseColorString(const std::string &value, video::SColor &color, bool quiet,
90 unsigned char default_alpha = 0xff);
91std::string encodeHexColorString(video::SColor color);
92
98inline char my_tolower(char c)
99{
100 // By design this function cannot handle any Unicode (codepoints don't fit into char),
101 // but make sure to pass it through unchanged.
102 // tolower() can mangle it if the POSIX locale is not UTF-8.
103 if (static_cast<unsigned char>(c) > 0x7f)
104 return c;
105 // toupper(3): "If the argument c is of type char, it must be cast to unsigned char"
106 return tolower(static_cast<unsigned char>(c));
107}
108
114inline std::string padStringRight(std::string str, size_t len)
115{
116 if (len > str.size())
117 str.insert(str.end(), len - str.size(), ' ');
118
119 return str;
120}
121
133inline std::string_view removeStringEnd(std::string_view str,
134 const char *ends[])
135{
136 const char **p = ends;
137
138 for (; *p && (*p)[0] != '\0'; p++) {
139 std::string_view end(*p);
140 if (str.size() < end.size())
141 continue;
142 if (str.compare(str.size() - end.size(), end.size(), end) == 0)
143 return str.substr(0, str.size() - end.size());
144 }
145
146 return std::string_view();
147}
148
149
150#define MAKE_VARIANT(_name, _t0, _t1) \
151 template <typename T, typename... Args> \
152 inline auto _name(_t0 arg1, _t1 arg2, Args&&... args) \
153 { \
154 return (_name)(std::basic_string_view<T>(arg1), std::basic_string_view<T>(arg2), \
155 std::forward<Args>(args)...); \
156 }
157
158
168template <typename T>
169inline bool str_equal(std::basic_string_view<T> s1,
170 std::basic_string_view<T> s2,
171 bool case_insensitive = false)
172{
173 if (!case_insensitive)
174 return s1 == s2;
175
176 if (s1.size() != s2.size())
177 return false;
178
179 for (size_t i = 0; i < s1.size(); ++i)
180 if (my_tolower(s1[i]) != my_tolower(s2[i]))
181 return false;
182
183 return true;
184}
185
186// For some reason an std::string will not implicitly get converted
187// to an std::basic_string_view<char> in the template case above, so we need
188// these three wrappers. It works if you take out the template parameters.
189// see also <https://stackoverflow.com/questions/68380141/>
190MAKE_VARIANT(str_equal, const std::basic_string<T> &, const std::basic_string<T> &)
191
192MAKE_VARIANT(str_equal, std::basic_string_view<T>, const std::basic_string<T> &)
193
194MAKE_VARIANT(str_equal, const std::basic_string<T> &, std::basic_string_view<T>)
195
196
197
207template <typename T>
208inline bool str_starts_with(std::basic_string_view<T> str,
209 std::basic_string_view<T> prefix,
210 bool case_insensitive = false)
211{
212 if (str.size() < prefix.size())
213 return false;
214
215 if (!case_insensitive)
216 return str.compare(0, prefix.size(), prefix) == 0;
217
218 for (size_t i = 0; i < prefix.size(); ++i)
219 if (my_tolower(str[i]) != my_tolower(prefix[i]))
220 return false;
221 return true;
222}
223
224// (same conversion issue here)
225MAKE_VARIANT(str_starts_with, const std::basic_string<T> &, const std::basic_string<T> &)
226
227MAKE_VARIANT(str_starts_with, std::basic_string_view<T>, const std::basic_string<T> &)
228
229MAKE_VARIANT(str_starts_with, const std::basic_string<T> &, std::basic_string_view<T>)
230
231// (the same but with char pointers, only for the prefix argument)
232MAKE_VARIANT(str_starts_with, const std::basic_string<T> &, const T*)
233
234MAKE_VARIANT(str_starts_with, std::basic_string_view<T>, const T*)
235
236
237
247template <typename T>
248inline bool str_ends_with(std::basic_string_view<T> str,
249 std::basic_string_view<T> suffix,
250 bool case_insensitive = false)
251{
252 if (str.size() < suffix.size())
253 return false;
254
255 size_t start = str.size() - suffix.size();
256 if (!case_insensitive)
257 return str.compare(start, suffix.size(), suffix) == 0;
258
259 for (size_t i = 0; i < suffix.size(); ++i)
260 if (my_tolower(str[start + i]) != my_tolower(suffix[i]))
261 return false;
262 return true;
263}
264
265// (same conversion issue here)
266MAKE_VARIANT(str_ends_with, const std::basic_string<T> &, const std::basic_string<T> &)
267
268MAKE_VARIANT(str_ends_with, std::basic_string_view<T>, const std::basic_string<T> &)
269
270MAKE_VARIANT(str_ends_with, const std::basic_string<T> &, std::basic_string_view<T>)
271
272// (the same but with char pointers, only for the suffix argument)
273MAKE_VARIANT(str_ends_with, const std::basic_string<T> &, const T*)
274
275MAKE_VARIANT(str_ends_with, std::basic_string_view<T>, const T*)
276
277
278#undef MAKE_VARIANT
279
280
287template <typename T>
288inline std::vector<std::basic_string<T> > str_split(
289 const std::basic_string<T> &str,
290 T delimiter)
291{
292 std::vector<std::basic_string<T> > parts;
293 std::basic_stringstream<T> sstr(str);
294 std::basic_string<T> part;
295
296 while (std::getline(sstr, part, delimiter))
297 parts.push_back(part);
298
299 return parts;
300}
301
302
307inline std::string lowercase(std::string_view str)
308{
309 std::string s2;
310 s2.resize(str.size());
311 for (size_t i = 0; i < str.size(); i++)
312 s2[i] = my_tolower(str[i]);
313 return s2;
314}
315
316
317inline bool my_isspace(const char c)
318{
319 return std::isspace(c);
320}
321
322inline bool my_isspace(const wchar_t c)
323{
324 return std::iswspace(c);
325}
326
331template<typename T>
332inline std::basic_string_view<T> trim(const std::basic_string_view<T> &str)
333{
334 size_t front = 0;
335 size_t back = str.size();
336
337 while (front < back && my_isspace(str[front]))
338 ++front;
339
340 while (back > front && my_isspace(str[back - 1]))
341 --back;
342
343 return str.substr(front, back - front);
344}
345
346// If input was a temporary string keep it one to make sure patterns like
347// trim(func_that_returns_str()) are predictable regarding memory allocation
348// and don't lead to UAF. ↓ ↓ ↓
349
354template<typename T>
355inline std::basic_string<T> trim(std::basic_string<T> &&str)
356{
357 std::basic_string<T> ret(trim(std::basic_string_view<T>(str)));
358 return ret;
359}
360
361template<typename T>
362inline std::basic_string_view<T> trim(const std::basic_string<T> &str)
363{
364 return trim(std::basic_string_view<T>(str));
365}
366
367// The above declaration causes ambiguity with char pointers so we have to fix that:
368template<typename T>
369inline std::basic_string_view<T> trim(const T *str)
370{
371 return trim(std::basic_string_view<T>(str));
372}
373
374
381inline bool is_yes(std::string_view str)
382{
383 std::string s2 = lowercase(trim(str));
384
385 return s2 == "y" || s2 == "yes" || s2 == "true" || atoi(s2.c_str()) != 0;
386}
387
388
401inline s32 mystoi(const std::string &str, s32 min, s32 max)
402{
403 s32 i = atoi(str.c_str());
404
405 if (i < min)
406 i = min;
407 if (i > max)
408 i = max;
409
410 return i;
411}
412
417inline s32 mystoi(const std::string &str)
418{
419 return atoi(str.c_str());
420}
421
426inline float mystof(const std::string &str)
427{
428 return atof(str.c_str());
429}
430
431#define stoi mystoi
432#define stof mystof
433
435template <typename T>
436inline T from_string(const std::string &str)
437{
438 std::istringstream tmp(str);
439 T t;
440 tmp >> t;
441 return t;
442}
443
445inline s64 stoi64(const std::string &str) { return from_string<s64>(str); }
446
448inline std::string itos(s32 i) { return std::to_string(i); }
450inline std::string i64tos(s64 i) { return std::to_string(i); }
451
453inline std::string ftos(float f)
454{
455 std::ostringstream oss;
456 oss << std::setprecision(std::numeric_limits<float>::max_digits10) << f;
457 return oss.str();
458}
459
460
468inline void str_replace(std::string &str, std::string_view pattern,
469 std::string_view replacement)
470{
471 std::string::size_type start = str.find(pattern, 0);
472 while (start != str.npos) {
473 str.replace(start, pattern.size(), replacement);
474 start = str.find(pattern, start + replacement.size());
475 }
476}
477
481inline void str_formspec_escape(std::string &str)
482{
483 str_replace(str, "\\", "\\\\");
484 str_replace(str, "]", "\\]");
485 str_replace(str, "[", "\\[");
486 str_replace(str, ";", "\\;");
487 str_replace(str, ",", "\\,");
488 str_replace(str, "$", "\\$");
489}
490
498void str_replace(std::string &str, char from, char to);
499
500
511inline bool string_allowed(std::string_view str, std::string_view allowed_chars)
512{
513 return str.find_first_not_of(allowed_chars) == str.npos;
514}
515
516
527inline bool string_allowed_blacklist(std::string_view str,
528 std::string_view blacklisted_chars)
529{
530 return str.find_first_of(blacklisted_chars) == str.npos;
531}
532
533
550std::string wrap_rows(std::string_view from, unsigned row_len, bool has_color_codes = false);
551
552
556template <typename T>
557inline std::basic_string<T> unescape_string(const std::basic_string<T> &s)
558{
559 std::basic_string<T> res;
560 res.reserve(s.size());
561
562 for (size_t i = 0; i < s.length(); i++) {
563 if (s[i] == '\\') {
564 i++;
565 if (i >= s.length())
566 break;
567 }
568 res += s[i];
569 }
570
571 return res;
572}
573
580template <typename T>
581std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
582{
583 std::basic_string<T> output;
584 output.reserve(s.size());
585 size_t i = 0;
586 while (i < s.length()) {
587 if (s[i] == '\x1b') {
588 ++i;
589 if (i == s.length()) continue;
590 if (s[i] == '(') {
591 ++i;
592 while (i < s.length() && s[i] != ')') {
593 if (s[i] == '\\') {
594 ++i;
595 }
596 ++i;
597 }
598 ++i;
599 } else {
600 ++i;
601 }
602 continue;
603 }
604 output += s[i];
605 ++i;
606 }
607 return output;
608}
609
610template <typename T>
611std::vector<std::basic_string<T> > split(const std::basic_string<T> &s, T delim)
612{
613 std::vector<std::basic_string<T> > tokens;
614
615 std::basic_string<T> current;
616 bool last_was_escape = false;
617 for (size_t i = 0; i < s.length(); i++) {
618 T si = s[i];
619 if (last_was_escape) {
620 current += '\\';
621 current += si;
622 last_was_escape = false;
623 } else {
624 if (si == delim) {
625 tokens.push_back(current);
626 current.clear();
627 last_was_escape = false;
628 } else if (si == '\\') {
629 last_was_escape = true;
630 } else {
631 current += si;
632 last_was_escape = false;
633 }
634 }
635 }
636 //push last element
637 tokens.push_back(current);
638
639 return tokens;
640}
641
642std::wstring translate_string(std::wstring_view s, Translations *translations);
643
644std::wstring translate_string(std::wstring_view s);
645
646inline std::wstring unescape_translate(std::wstring_view s)
647{
649}
650
658inline bool is_number(std::string_view to_check)
659{
660 for (char i : to_check)
661 if (!std::isdigit(i))
662 return false;
663
664 return !to_check.empty();
665}
666
667
673inline const char *bool_to_cstr(bool val)
674{
675 return val ? "true" : "false";
676}
677
685inline const std::string duration_to_string(int sec)
686{
687 std::ostringstream ss;
688 const char *neg = "";
689 if (sec < 0) {
690 sec = -sec;
691 neg = "-";
692 }
693 int total_sec = sec;
694 int min = sec / 60;
695 sec %= 60;
696 int hour = min / 60;
697 min %= 60;
698 int day = hour / 24;
699 hour %= 24;
700
701 if (day > 0) {
702 ss << neg << day << "d";
703 if (hour > 0 || min > 0 || sec > 0)
704 ss << " ";
705 }
706
707 if (hour > 0) {
708 ss << neg << hour << "h";
709 if (min > 0 || sec > 0)
710 ss << " ";
711 }
712
713 if (min > 0) {
714 ss << neg << min << "min";
715 if (sec > 0)
716 ss << " ";
717 }
718
719 if (sec > 0 || total_sec == 0) {
720 ss << neg << sec << "s";
721 }
722
723 return ss.str();
724}
725
731inline std::string str_join(const std::vector<std::string> &list,
732 std::string_view delimiter)
733{
734 std::ostringstream oss;
735 bool first = true;
736 for (const auto &part : list) {
737 if (!first)
738 oss << delimiter;
739 oss << part;
740 first = false;
741 }
742 return oss.str();
743}
744
745#if IS_CLIENT_BUILD
749inline std::string stringw_to_utf8(const irr::core::stringw &input)
750{
751 std::wstring_view sv(input.c_str(), input.size());
752 return wide_to_utf8(sv);
753}
754
758inline irr::core::stringw utf8_to_stringw(std::string_view input)
759{
760 std::wstring str = utf8_to_wide(input);
761 return irr::core::stringw(str.c_str(), str.size());
762}
763#endif
764
771std::string sanitizeDirName(std::string_view str, std::string_view optional_prefix);
772
780[[nodiscard]]
781std::string sanitize_untrusted(std::string_view str, bool keep_escapes = true);
782
789void safe_print_string(std::ostream &os, std::string_view str);
790
797v3f str_to_v3f(std::string_view str);
Definition translation.h:19
core::vector3df v3f
Definition irr_v3d.h:11
bool my_isspace(const char c)
Definition string.h:317
s64 stoi64(const std::string &str)
Returns a 64-bit signed value represented by the string str (decimal).
Definition string.h:445
std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask)
Definition string.cpp:276
bool string_allowed(std::string_view str, std::string_view allowed_chars)
Check that a string only contains whitelisted characters.
Definition string.h:511
std::string wide_to_utf8(std::wstring_view input)
Definition string.cpp:115
bool str_equal(std::basic_string_view< T > s1, std::basic_string_view< T > s2, bool case_insensitive=false)
Check two strings for equivalence.
Definition string.h:169
std::string sanitize_untrusted(std::string_view str, bool keep_escapes=true)
Sanitize an untrusted string (e.g.
Definition string.cpp:1021
char * mystrtok_r(char *s, const char *sep, char **lasts) noexcept
Definition string.cpp:310
u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask)
Definition string.cpp:240
std::wstring unescape_translate(std::wstring_view s)
Definition string.h:646
void wide_add_codepoint(std::wstring &result, char32_t codepoint)
Definition string.cpp:142
const std::string duration_to_string(int sec)
Converts a duration in seconds to a pretty-printed duration in days, hours, minutes and seconds.
Definition string.h:685
const char * bool_to_cstr(bool val)
Returns a C-string, either "true" or "false", corresponding to val.
Definition string.h:673
std::wstring translate_string(std::wstring_view s, Translations *translations)
Definition string.cpp:912
char my_tolower(char c)
Converts a letter to lowercase, with safe handling of the char type and non-ASCII.
Definition string.h:98
std::vector< std::basic_string< T > > split(const std::basic_string< T > &s, T delim)
Definition string.h:611
std::string wrap_rows(std::string_view from, unsigned row_len, bool has_color_codes=false)
Create a string based on from where a newline is forcefully inserted every row_len characters.
Definition string.cpp:635
bool parseColorString(const std::string &value, video::SColor &color, bool quiet, unsigned char default_alpha=0xff)
Definition string.cpp:600
void str_replace(std::string &str, std::string_view pattern, std::string_view replacement)
Replace all occurrences of pattern in str with replacement.
Definition string.h:468
T from_string(const std::string &str)
Returns a value represented by the string val.
Definition string.h:436
std::vector< std::basic_string< T > > str_split(const std::basic_string< T > &str, T delimiter)
Splits a string into its component parts separated by the character delimiter.
Definition string.h:288
std::string sanitizeDirName(std::string_view str, std::string_view optional_prefix)
Sanitize the name of a new directory.
Definition string.cpp:968
std::unordered_map< std::string, std::string > StringMap
Definition string.h:65
bool str_ends_with(std::basic_string_view< T > str, std::basic_string_view< T > suffix, bool case_insensitive=false)
Check whether str ends with the string suffix.
Definition string.h:248
std::string urlencode(std::string_view str)
Definition string.cpp:204
bool string_allowed_blacklist(std::string_view str, std::string_view blacklisted_chars)
Check that a string contains no blacklisted characters.
Definition string.h:527
size_t mystrlcpy(char *dst, const char *src, size_t size) noexcept
Definition string.cpp:297
std::string str_join(const std::vector< std::string > &list, std::string_view delimiter)
Joins a vector of strings by the string delimiter.
Definition string.h:731
std::string urldecode(std::string_view str)
Definition string.cpp:222
std::string padStringRight(std::string str, size_t len)
Returns a copy of str with spaces inserted at the right hand side to ensure that the string is len ch...
Definition string.h:114
bool str_starts_with(std::basic_string_view< T > str, std::basic_string_view< T > prefix, bool case_insensitive=false)
Check whether str begins with the string prefix.
Definition string.h:208
float mystof(const std::string &str)
Returns a float reprensented by the string str (decimal).
Definition string.h:426
std::string itos(s32 i)
Returns a string representing the decimal value of the 32-bit value i.
Definition string.h:448
void str_formspec_escape(std::string &str)
Escapes characters that cannot be used in formspecs.
Definition string.h:481
void safe_print_string(std::ostream &os, std::string_view str)
Prints a sanitized version of a string without control characters.
Definition string.cpp:1056
std::basic_string< T > unescape_enriched(const std::basic_string< T > &s)
Remove all escape sequences in s.
Definition string.h:581
std::string i64tos(s64 i)
Returns a string representing the decimal value of the 64-bit value i.
Definition string.h:450
std::basic_string_view< T > trim(const std::basic_string_view< T > &str)
Definition string.h:332
std::string lowercase(std::string_view str)
Definition string.h:307
std::string ftos(float f)
Returns a string representing the exact decimal value of the float value f.
Definition string.h:453
std::string_view removeStringEnd(std::string_view str, const char *ends[])
Returns a version of str with the first occurrence of a string contained within ends[] removed from t...
Definition string.h:133
std::string encodeHexColorString(video::SColor color)
Definition string.cpp:616
#define MAKE_VARIANT(_name, _t0, _t1)
Definition string.h:150
v3f str_to_v3f(std::string_view str)
Parses a string of form (1, 2, 3) to a v3f.
Definition string.cpp:1072
std::wstring utf8_to_wide(std::string_view input)
Definition string.cpp:87
bool is_yes(std::string_view str)
Returns whether str should be regarded as (bool) true.
Definition string.h:381
u64 read_seed(const char *str)
Definition string.cpp:336
bool is_number(std::string_view to_check)
Checks that all characters in to_check are a decimal digits.
Definition string.h:658
std::basic_string< T > unescape_string(const std::basic_string< T > &s)
Removes backslashes from an escaped string (FormSpec strings)
Definition string.h:557
s32 mystoi(const std::string &str, s32 min, s32 max)
Converts the string str to a signed 32-bit integer.
Definition string.h:401
Definition string.h:67
u32 flag
Definition string.h:69
const char * name
Definition string.h:68
static std::string p(std::string path)
Definition test_filesys.cpp:53