]> git.lizzy.rs Git - minetest.git/blob - src/util/string.h
Increase `ftos` precision (#13141)
[minetest.git] / src / util / string.h
1 /*
2 Minetest
3 Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #pragma once
21
22 #include "irrlichttypes_bloated.h"
23 #include "irrString.h"
24 #include <cstdlib>
25 #include <string>
26 #include <cstring>
27 #include <vector>
28 #include <limits>
29 #include <map>
30 #include <sstream>
31 #include <iomanip>
32 #include <cctype>
33 #include <unordered_map>
34
35 class Translations;
36
37 #define STRINGIFY(x) #x
38 #define TOSTRING(x) STRINGIFY(x)
39
40 // Checks whether a value is an ASCII printable character
41 #define IS_ASCII_PRINTABLE_CHAR(x)   \
42         (((unsigned int)(x) >= 0x20) &&  \
43         ( (unsigned int)(x) <= 0x7e))
44
45 // Checks whether a value is in a Unicode private use area
46 #define IS_PRIVATE_USE_CHAR(x)    \
47         (((wchar_t)(x) >= 0xE000 &&   \
48           (wchar_t)(x) <= 0xF8FF) ||  \
49          ((wchar_t)(x) >= 0xF0000 &&  \
50           (wchar_t)(x) <= 0xFFFFD) || \
51          ((wchar_t)(x) >= 0x100000 && \
52           (wchar_t)(x) <= 0x10FFFD))  \
53
54 // Checks whether a byte is an inner byte for an utf-8 multibyte sequence
55 #define IS_UTF8_MULTB_INNER(x)       \
56         (((unsigned char)(x) >= 0x80) && \
57         ( (unsigned char)(x) <= 0xbf))
58
59 // Checks whether a byte is a start byte for an utf-8 multibyte sequence
60 #define IS_UTF8_MULTB_START(x)       \
61         (((unsigned char)(x) >= 0xc2) && \
62         ( (unsigned char)(x) <= 0xf4))
63
64 // Given a start byte x for an utf-8 multibyte sequence
65 // it gives the length of the whole sequence in bytes.
66 #define UTF8_MULTB_START_LEN(x)            \
67         (((unsigned char)(x) < 0xe0) ? 2 :     \
68         (((unsigned char)(x) < 0xf0) ? 3 : 4))
69
70 typedef std::unordered_map<std::string, std::string> StringMap;
71
72 struct FlagDesc {
73         const char *name;
74         u32 flag;
75 };
76
77 // Try to avoid converting between wide and UTF-8 unless you need to
78 // input/output stuff via Irrlicht
79 std::wstring utf8_to_wide(const std::string &input);
80 std::string wide_to_utf8(const std::wstring &input);
81
82 // You must free the returned string!
83 // The returned string is allocated using new[]
84 wchar_t *utf8_to_wide_c(const char *str);
85
86 std::string urlencode(const std::string &str);
87 std::string urldecode(const std::string &str);
88 u32 readFlagString(std::string str, const FlagDesc *flagdesc, u32 *flagmask);
89 std::string writeFlagString(u32 flags, const FlagDesc *flagdesc, u32 flagmask);
90 size_t mystrlcpy(char *dst, const char *src, size_t size);
91 char *mystrtok_r(char *s, const char *sep, char **lasts);
92 u64 read_seed(const char *str);
93 bool parseColorString(const std::string &value, video::SColor &color, bool quiet,
94                 unsigned char default_alpha = 0xff);
95
96
97 /**
98  * Returns a copy of \p str with spaces inserted at the right hand side to ensure
99  * that the string is \p len characters in length. If \p str is <= \p len then the
100  * returned string will be identical to str.
101  */
102 inline std::string padStringRight(std::string str, size_t len)
103 {
104         if (len > str.size())
105                 str.insert(str.end(), len - str.size(), ' ');
106
107         return str;
108 }
109
110 /**
111  * Returns a version of \p str with the first occurrence of a string
112  * contained within ends[] removed from the end of the string.
113  *
114  * @param str
115  * @param ends A NULL- or ""- terminated array of strings to remove from s in
116  *      the copy produced.  Note that once one of these strings is removed
117  *      that no further postfixes contained within this array are removed.
118  *
119  * @return If no end could be removed then "" is returned.
120  */
121 inline std::string removeStringEnd(const std::string &str,
122                 const char *ends[])
123 {
124         const char **p = ends;
125
126         for (; *p && (*p)[0] != '\0'; p++) {
127                 std::string end = *p;
128                 if (str.size() < end.size())
129                         continue;
130                 if (str.compare(str.size() - end.size(), end.size(), end) == 0)
131                         return str.substr(0, str.size() - end.size());
132         }
133
134         return "";
135 }
136
137
138 /**
139  * Check two strings for equivalence.  If \p case_insensitive is true
140  * then the case of the strings is ignored (default is false).
141  *
142  * @param s1
143  * @param s2
144  * @param case_insensitive
145  * @return true if the strings match
146  */
147 template <typename T>
148 inline bool str_equal(const std::basic_string<T> &s1,
149                 const std::basic_string<T> &s2,
150                 bool case_insensitive = false)
151 {
152         if (!case_insensitive)
153                 return s1 == s2;
154
155         if (s1.size() != s2.size())
156                 return false;
157
158         for (size_t i = 0; i < s1.size(); ++i)
159                 if(tolower(s1[i]) != tolower(s2[i]))
160                         return false;
161
162         return true;
163 }
164
165
166 /**
167  * Check whether \p str begins with the string prefix. If \p case_insensitive
168  * is true then the check is case insensitve (default is false; i.e. case is
169  * significant).
170  *
171  * @param str
172  * @param prefix
173  * @param case_insensitive
174  * @return true if the str begins with prefix
175  */
176 template <typename T>
177 inline bool str_starts_with(const std::basic_string<T> &str,
178                 const std::basic_string<T> &prefix,
179                 bool case_insensitive = false)
180 {
181         if (str.size() < prefix.size())
182                 return false;
183
184         if (!case_insensitive)
185                 return str.compare(0, prefix.size(), prefix) == 0;
186
187         for (size_t i = 0; i < prefix.size(); ++i)
188                 if (tolower(str[i]) != tolower(prefix[i]))
189                         return false;
190         return true;
191 }
192
193 /**
194  * Check whether \p str begins with the string prefix. If \p case_insensitive
195  * is true then the check is case insensitve (default is false; i.e. case is
196  * significant).
197  *
198  * @param str
199  * @param prefix
200  * @param case_insensitive
201  * @return true if the str begins with prefix
202  */
203 template <typename T>
204 inline bool str_starts_with(const std::basic_string<T> &str,
205                 const T *prefix,
206                 bool case_insensitive = false)
207 {
208         return str_starts_with(str, std::basic_string<T>(prefix),
209                         case_insensitive);
210 }
211
212
213 /**
214  * Check whether \p str ends with the string suffix. If \p case_insensitive
215  * is true then the check is case insensitve (default is false; i.e. case is
216  * significant).
217  *
218  * @param str
219  * @param suffix
220  * @param case_insensitive
221  * @return true if the str begins with suffix
222  */
223 template <typename T>
224 inline bool str_ends_with(const std::basic_string<T> &str,
225                 const std::basic_string<T> &suffix,
226                 bool case_insensitive = false)
227 {
228         if (str.size() < suffix.size())
229                 return false;
230
231         size_t start = str.size() - suffix.size();
232         if (!case_insensitive)
233                 return str.compare(start, suffix.size(), suffix) == 0;
234
235         for (size_t i = 0; i < suffix.size(); ++i)
236                 if (tolower(str[start + i]) != tolower(suffix[i]))
237                         return false;
238         return true;
239 }
240
241
242 /**
243  * Check whether \p str ends with the string suffix. If \p case_insensitive
244  * is true then the check is case insensitve (default is false; i.e. case is
245  * significant).
246  *
247  * @param str
248  * @param suffix
249  * @param case_insensitive
250  * @return true if the str begins with suffix
251  */
252 template <typename T>
253 inline bool str_ends_with(const std::basic_string<T> &str,
254                 const T *suffix,
255                 bool case_insensitive = false)
256 {
257         return str_ends_with(str, std::basic_string<T>(suffix),
258                         case_insensitive);
259 }
260
261
262 /**
263  * Splits a string into its component parts separated by the character
264  * \p delimiter.
265  *
266  * @return An std::vector<std::basic_string<T> > of the component parts
267  */
268 template <typename T>
269 inline std::vector<std::basic_string<T> > str_split(
270                 const std::basic_string<T> &str,
271                 T delimiter)
272 {
273         std::vector<std::basic_string<T> > parts;
274         std::basic_stringstream<T> sstr(str);
275         std::basic_string<T> part;
276
277         while (std::getline(sstr, part, delimiter))
278                 parts.push_back(part);
279
280         return parts;
281 }
282
283
284 /**
285  * @param str
286  * @return A copy of \p str converted to all lowercase characters.
287  */
288 inline std::string lowercase(const std::string &str)
289 {
290         std::string s2;
291
292         s2.reserve(str.size());
293
294         for (char i : str)
295                 s2 += tolower(i);
296
297         return s2;
298 }
299
300
301 /**
302  * @param str
303  * @return A copy of \p str with leading and trailing whitespace removed.
304  */
305 inline std::string trim(const std::string &str)
306 {
307         size_t front = 0;
308         size_t back = str.size();
309
310         while (front < back && std::isspace(str[front]))
311                 ++front;
312
313         while (back > front && std::isspace(str[back - 1]))
314                 --back;
315
316         return str.substr(front, back - front);
317 }
318
319
320 /**
321  * Returns whether \p str should be regarded as (bool) true.  Case and leading
322  * and trailing whitespace are ignored.  Values that will return
323  * true are "y", "yes", "true" and any number that is not 0.
324  * @param str
325  */
326 inline bool is_yes(const std::string &str)
327 {
328         std::string s2 = lowercase(trim(str));
329
330         return s2 == "y" || s2 == "yes" || s2 == "true" || atoi(s2.c_str()) != 0;
331 }
332
333
334 /**
335  * Converts the string \p str to a signed 32-bit integer. The converted value
336  * is constrained so that min <= value <= max.
337  *
338  * @see atoi(3) for limitations
339  *
340  * @param str
341  * @param min Range minimum
342  * @param max Range maximum
343  * @return The value converted to a signed 32-bit integer and constrained
344  *      within the range defined by min and max (inclusive)
345  */
346 inline s32 mystoi(const std::string &str, s32 min, s32 max)
347 {
348         s32 i = atoi(str.c_str());
349
350         if (i < min)
351                 i = min;
352         if (i > max)
353                 i = max;
354
355         return i;
356 }
357
358 /**
359  * Returns a 32-bit value reprensented by the string \p str (decimal).
360  * @see atoi(3) for further limitations
361  */
362 inline s32 mystoi(const std::string &str)
363 {
364         return atoi(str.c_str());
365 }
366
367 /**
368  * Returns a float reprensented by the string \p str (decimal).
369  * @see atof(3)
370  */
371 inline float mystof(const std::string &str)
372 {
373         return atof(str.c_str());
374 }
375
376 #define stoi mystoi
377 #define stof mystof
378
379 /// Returns a value represented by the string \p val.
380 template <typename T>
381 inline T from_string(const std::string &str)
382 {
383         std::stringstream tmp(str);
384         T t;
385         tmp >> t;
386         return t;
387 }
388
389 /// Returns a 64-bit signed value represented by the string \p str (decimal).
390 inline s64 stoi64(const std::string &str) { return from_string<s64>(str); }
391
392 #if __cplusplus < 201103L
393 namespace std {
394
395 /// Returns a string representing the value \p val.
396 template <typename T>
397 inline string to_string(T val)
398 {
399         ostringstream oss;
400         oss << val;
401         return oss.str();
402 }
403 #define DEFINE_STD_TOSTRING_FLOATINGPOINT(T)            \
404         template <>                                     \
405         inline string to_string<T>(T val)               \
406         {                                               \
407                 ostringstream oss;                      \
408                 oss << std::fixed                       \
409                         << std::setprecision(6)         \
410                         << val;                         \
411                 return oss.str();                       \
412         }
413 DEFINE_STD_TOSTRING_FLOATINGPOINT(float)
414 DEFINE_STD_TOSTRING_FLOATINGPOINT(double)
415 DEFINE_STD_TOSTRING_FLOATINGPOINT(long double)
416
417 #undef DEFINE_STD_TOSTRING_FLOATINGPOINT
418
419 /// Returns a wide string representing the value \p val
420 template <typename T>
421 inline wstring to_wstring(T val)
422 {
423         return utf8_to_wide(to_string(val));
424 }
425 }
426 #endif
427
428 /// Returns a string representing the decimal value of the 32-bit value \p i.
429 inline std::string itos(s32 i) { return std::to_string(i); }
430 /// Returns a string representing the decimal value of the 64-bit value \p i.
431 inline std::string i64tos(s64 i) { return std::to_string(i); }
432
433 /// Returns a string representing the exact decimal value of the float value \p f.
434 inline std::string ftos(float f)
435 {
436         std::ostringstream oss;
437         oss << std::setprecision(std::numeric_limits<float>::max_digits10) << f;
438         return oss.str();
439 }
440
441
442 /**
443  * Replace all occurrences of \p pattern in \p str with \p replacement.
444  *
445  * @param str String to replace pattern with replacement within.
446  * @param pattern The pattern to replace.
447  * @param replacement What to replace the pattern with.
448  */
449 inline void str_replace(std::string &str, const std::string &pattern,
450                 const std::string &replacement)
451 {
452         std::string::size_type start = str.find(pattern, 0);
453         while (start != str.npos) {
454                 str.replace(start, pattern.size(), replacement);
455                 start = str.find(pattern, start + replacement.size());
456         }
457 }
458
459 /**
460  * Escapes characters [ ] \ , ; that cannot be used in formspecs
461  */
462 inline void str_formspec_escape(std::string &str)
463 {
464         str_replace(str, "\\", "\\\\");
465         str_replace(str, "]", "\\]");
466         str_replace(str, "[", "\\[");
467         str_replace(str, ";", "\\;");
468         str_replace(str, ",", "\\,");
469         str_replace(str, "$", "\\$");
470 }
471
472 /**
473  * Replace all occurrences of the character \p from in \p str with \p to.
474  *
475  * @param str The string to (potentially) modify.
476  * @param from The character in str to replace.
477  * @param to The replacement character.
478  */
479 void str_replace(std::string &str, char from, char to);
480
481
482 /**
483  * Check that a string only contains whitelisted characters. This is the
484  * opposite of string_allowed_blacklist().
485  *
486  * @param str The string to be checked.
487  * @param allowed_chars A string containing permitted characters.
488  * @return true if the string is allowed, otherwise false.
489  *
490  * @see string_allowed_blacklist()
491  */
492 inline bool string_allowed(const std::string &str, const std::string &allowed_chars)
493 {
494         return str.find_first_not_of(allowed_chars) == str.npos;
495 }
496
497
498 /**
499  * Check that a string contains no blacklisted characters. This is the
500  * opposite of string_allowed().
501  *
502  * @param str The string to be checked.
503  * @param blacklisted_chars A string containing prohibited characters.
504  * @return true if the string is allowed, otherwise false.
505
506  * @see string_allowed()
507  */
508 inline bool string_allowed_blacklist(const std::string &str,
509                 const std::string &blacklisted_chars)
510 {
511         return str.find_first_of(blacklisted_chars) == str.npos;
512 }
513
514
515 /**
516  * Create a string based on \p from where a newline is forcefully inserted
517  * every \p row_len characters.
518  *
519  * @note This function does not honour word wraps and blindy inserts a newline
520  *      every \p row_len characters whether it breaks a word or not.  It is
521  *      intended to be used for, for example, showing paths in the GUI.
522  *
523  * @note This function doesn't wrap inside utf-8 multibyte sequences and also
524  *      counts multibyte sequences correcly as single characters.
525  *
526  * @param from The (utf-8) string to be wrapped into rows.
527  * @param row_len The row length (in characters).
528  * @return A new string with the wrapping applied.
529  */
530 inline std::string wrap_rows(const std::string &from,
531                 unsigned row_len)
532 {
533         std::string to;
534
535         size_t character_idx = 0;
536         for (size_t i = 0; i < from.size(); i++) {
537                 if (!IS_UTF8_MULTB_INNER(from[i])) {
538                         // Wrap string after last inner byte of char
539                         if (character_idx > 0 && character_idx % row_len == 0)
540                                 to += '\n';
541                         character_idx++;
542                 }
543                 to += from[i];
544         }
545
546         return to;
547 }
548
549
550 /**
551  * Removes backslashes from an escaped string (FormSpec strings)
552  */
553 template <typename T>
554 inline std::basic_string<T> unescape_string(const std::basic_string<T> &s)
555 {
556         std::basic_string<T> res;
557
558         for (size_t i = 0; i < s.length(); i++) {
559                 if (s[i] == '\\') {
560                         i++;
561                         if (i >= s.length())
562                                 break;
563                 }
564                 res += s[i];
565         }
566
567         return res;
568 }
569
570 /**
571  * Remove all escape sequences in \p s.
572  *
573  * @param s The string in which to remove escape sequences.
574  * @return \p s, with escape sequences removed.
575  */
576 template <typename T>
577 std::basic_string<T> unescape_enriched(const std::basic_string<T> &s)
578 {
579         std::basic_string<T> output;
580         size_t i = 0;
581         while (i < s.length()) {
582                 if (s[i] == '\x1b') {
583                         ++i;
584                         if (i == s.length()) continue;
585                         if (s[i] == '(') {
586                                 ++i;
587                                 while (i < s.length() && s[i] != ')') {
588                                         if (s[i] == '\\') {
589                                                 ++i;
590                                         }
591                                         ++i;
592                                 }
593                                 ++i;
594                         } else {
595                                 ++i;
596                         }
597                         continue;
598                 }
599                 output += s[i];
600                 ++i;
601         }
602         return output;
603 }
604
605 template <typename T>
606 std::vector<std::basic_string<T> > split(const std::basic_string<T> &s, T delim)
607 {
608         std::vector<std::basic_string<T> > tokens;
609
610         std::basic_string<T> current;
611         bool last_was_escape = false;
612         for (size_t i = 0; i < s.length(); i++) {
613                 T si = s[i];
614                 if (last_was_escape) {
615                         current += '\\';
616                         current += si;
617                         last_was_escape = false;
618                 } else {
619                         if (si == delim) {
620                                 tokens.push_back(current);
621                                 current.clear();
622                                 last_was_escape = false;
623                         } else if (si == '\\') {
624                                 last_was_escape = true;
625                         } else {
626                                 current += si;
627                                 last_was_escape = false;
628                         }
629                 }
630         }
631         //push last element
632         tokens.push_back(current);
633
634         return tokens;
635 }
636
637 std::wstring translate_string(const std::wstring &s, Translations *translations);
638
639 std::wstring translate_string(const std::wstring &s);
640
641 inline std::wstring unescape_translate(const std::wstring &s) {
642         return unescape_enriched(translate_string(s));
643 }
644
645 /**
646  * Checks that all characters in \p to_check are a decimal digits.
647  *
648  * @param to_check
649  * @return true if to_check is not empty and all characters in to_check are
650  *      decimal digits, otherwise false
651  */
652 inline bool is_number(const std::string &to_check)
653 {
654         for (char i : to_check)
655                 if (!std::isdigit(i))
656                         return false;
657
658         return !to_check.empty();
659 }
660
661
662 /**
663  * Returns a C-string, either "true" or "false", corresponding to \p val.
664  *
665  * @return If \p val is true, then "true" is returned, otherwise "false".
666  */
667 inline const char *bool_to_cstr(bool val)
668 {
669         return val ? "true" : "false";
670 }
671
672 /**
673  * Converts a duration in seconds to a pretty-printed duration in
674  * days, hours, minutes and seconds.
675  *
676  * @param sec duration in seconds
677  * @return pretty-printed duration
678  */
679 inline const std::string duration_to_string(int sec)
680 {
681         std::ostringstream ss;
682         const char *neg = "";
683         if (sec < 0) {
684                 sec = -sec;
685                 neg = "-";
686         }
687         int total_sec = sec;
688         int min = sec / 60;
689         sec %= 60;
690         int hour = min / 60;
691         min %= 60;
692         int day = hour / 24;
693         hour %= 24;
694
695         if (day > 0) {
696                 ss << neg << day << "d";
697                 if (hour > 0 || min > 0 || sec > 0)
698                         ss << " ";
699         }
700
701         if (hour > 0) {
702                 ss << neg << hour << "h";
703                 if (min > 0 || sec > 0)
704                         ss << " ";
705         }
706
707         if (min > 0) {
708                 ss << neg << min << "min";
709                 if (sec > 0)
710                         ss << " ";
711         }
712
713         if (sec > 0 || total_sec == 0) {
714                 ss << neg << sec << "s";
715         }
716
717         return ss.str();
718 }
719
720 /**
721  * Joins a vector of strings by the string \p delimiter.
722  *
723  * @return A std::string
724  */
725 inline std::string str_join(const std::vector<std::string> &list,
726                 const std::string &delimiter)
727 {
728         std::ostringstream oss;
729         bool first = true;
730         for (const auto &part : list) {
731                 if (!first)
732                         oss << delimiter;
733                 oss << part;
734                 first = false;
735         }
736         return oss.str();
737 }
738
739 /**
740  * Create a UTF8 std::string from an irr::core::stringw.
741  */
742 inline std::string stringw_to_utf8(const irr::core::stringw &input)
743 {
744         std::wstring str(input.c_str());
745         return wide_to_utf8(str);
746 }
747
748  /**
749   * Create an irr::core:stringw from a UTF8 std::string.
750   */
751 inline irr::core::stringw utf8_to_stringw(const std::string &input)
752 {
753         std::wstring str = utf8_to_wide(input);
754         return irr::core::stringw(str.c_str());
755 }
756
757 /**
758  * Sanitize the name of a new directory. This consists of two stages:
759  * 1. Check for 'reserved filenames' that can't be used on some filesystems
760  *    and add a prefix to them
761  * 2. Remove 'unsafe' characters from the name by replacing them with '_'
762  */
763 std::string sanitizeDirName(const std::string &str, const std::string &optional_prefix);
764
765 /**
766  * Prints a sanitized version of a string without control characters.
767  * '\t' and '\n' are allowed, as are UTF-8 control characters (e.g. RTL).
768  * ASCII control characters are replaced with their hex encoding in angle
769  * brackets (e.g. "a\x1eb" -> "a<1e>b").
770  */
771 void safe_print_string(std::ostream &os, const std::string &str);