source: cpp/common/util-string.cpp @ 1176

Last change on this file since 1176 was 1130, checked in by Maciej Komosinski, 4 years ago

Used std::min(), std::max() explicitly to avoid compiler confusion. Used std::size() explicitly instead of the equivalent macro

  • Property svn:eol-style set to native
File size: 5.7 KB
Line 
1// This file is a part of Framsticks SDK.  http://www.framsticks.com/
2// Copyright (C) 1999-2021  Maciej Komosinski and Szymon Ulatowski.
3// See LICENSE.txt for details.
4
5#include "util-string.h"
6#include <stdarg.h>
7#include "nonstd_stdio.h"
8#include "nonstd.h"
9#include <assert.h>
10#include <cstdlib> //malloc()
11#include <algorithm>
12#ifdef USE_VIRTFILE
13#include <common/virtfile/virtfile.h>
14#endif
15#ifdef __ANDROID__
16#include <android/log.h> //only needed to print error messages related to a workaround for Android bug
17#endif
18
19string repr(const char *str)
20{
21        string out = "";
22        char hex[4];
23        while (*str)
24        {
25                unsigned char ch = *str;
26                if (ch >= 32 && ch < 128)
27                        out += ch;
28                else
29                {
30                        if (ch == 10) out += "\\n"; else
31                                if (ch == 13) out += "\\r"; else
32                                {
33                                        sprintf(hex, "%X", ch);
34                                        out += "\\x";
35                                        out += hex;
36                                }
37                }
38                str++;
39        }
40        return out;
41}
42
43
44string ssprintf_va(const char* format, va_list ap)
45{
46        string s; //clang crashed when this declaration was in s=buf
47        int size = 256;
48        char* buf;
49        va_list ap_copy; // "va_list ap" can only by used once by printf-type functions as they advance the current argument pointer (crashed on linux x86_64)
50        // (does not apply to SString::sprintf, it does not have the va_list variant)
51
52        //almost like SString::sprintf, but there is no common code to share because SString can use its directWrite to avoid double allocating/copying
53#ifdef USE_VSCPRINTF
54        va_copy(ap_copy, ap);
55        size = _vscprintf(format, ap_copy) + 1; //+1 for terminating null character
56        va_end(ap_copy);
57#endif
58
59        while (true)
60        {
61                buf = (char*)malloc(size);
62                assert(buf != NULL);
63                va_copy(ap_copy, ap);
64                int n = vsnprintf(buf, size, format, ap_copy);
65                va_end(ap_copy);
66
67#ifdef __ANDROID__
68                //Workaround for Android bug. /system/lib64/libc.so? maybe only arm 64-bit? "If an encoding error occurs, a negative number is returned". On some devices keeps returning -1 forever.
69                //https://github.com/android-ndk/ndk/issues/879 but unfortunately during google play tests (Firebase Test Lab) this problem turned out to be not limited to Chinese devices and occurred in Mate 9, Galaxy S9, Pixel, Pixel 2, Moto Z (even with the en_GB locale; the locale is not important but the problem seem to be utf8 non-ascii chars in the format string).
70                if (n < 0 && size >= (1 << 24)) //wants more than 16M
71                {
72                        buf[size - 1] = 0; //just to ensure there is at least some ending \0 in memory... who knows what buggy vsnprintf() did.
73                        __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "Giving up due to Android bug: vsnprintf() wants more than %d bytes, it used %zu bytes, for format='%s'", size, strlen(buf), format);
74                        //in my tests, it always used 0 bytes, so it produced a 0-length string: ""
75                        va_copy(ap_copy, ap);
76                        n = vsprintf(buf, format, ap_copy); //hoping 16M is enough
77                        va_end(ap_copy);
78                        __android_log_print(ANDROID_LOG_INFO, LOG_APP_NAME, "Fallback to vsprintf() produced string: '%s'", buf);
79                        if (n < 0) //vsprintf was also buggy. If we were strict, we should abort the app now.
80                        {
81                                strcpy(buf, "[STR_ERR] "); //a special prefix just to indicate the returned string is incorrect
82                                strcat(buf, format); //append and return the original formatting string
83                                __android_log_print(ANDROID_LOG_ERROR, LOG_APP_NAME, "vsprintf() also failed, using the incorrect resulting string: '%s'", buf);
84                        }
85                        n = strlen(buf); //pretend vsnprintf() or vsprintf() was OK to exit the endless loop
86                }
87#endif
88
89                if (n > -1 && n < size)
90                {
91                        s = buf;
92                        free(buf);
93                        return s;
94                }
95#ifdef VSNPRINTF_RETURNS_REQUIRED_SIZE
96                if (n > -1)    /* glibc 2.1 */
97                        size = n + 1; /* precisely what is needed */
98                else           /* glibc 2.0 */
99#endif
100                        size *= 2;  /* twice the old size */
101                free(buf);
102        }
103}
104
105bool str_starts_with(const char *str, const char *prefix)
106{
107        return strncmp(str, prefix, strlen(prefix)) == 0;
108}
109
110char* strmove(char *a, char *b) //strcpy that works well for overlapping strings ("Source and destination overlap")
111{
112        if (a == NULL || b == NULL)
113                return NULL;
114        memmove(a, b, strlen(b) + 1);
115        return a;
116}
117
118string ssprintf(const char* format, ...)
119{
120        va_list ap;
121        va_start(ap, format);
122        string ret = ssprintf_va(format, ap); //is it too wasteful? copying the string again... unless the compiler can handle it better
123        va_end(ap);
124        return ret;
125}
126
127string stripExt(const string& filename)
128{
129        size_t dot = filename.rfind('.');
130        if (dot == string::npos) return filename;
131        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
132        if ((sep == string::npos) || (sep < dot))
133                return filename.substr(0, dot);
134        return filename;
135}
136
137string stripFileDir(const string& filename)
138{
139        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
140        if (sep == string::npos) return filename;
141        return filename.substr(sep + 1);
142}
143
144string getFileExt(const string& filename)
145{
146        size_t dot = filename.rfind('.');
147        if (dot == string::npos) return string("");
148        size_t sep = filename.rfind(PATH_SEPARATOR_CHAR);
149        if ((sep == string::npos) || (sep < dot))
150                return filename.substr(dot);
151        return string("");
152}
153
154string getFileDir(const string& filename)
155{
156        size_t slash = filename.rfind(PATH_SEPARATOR_CHAR);
157        if (slash == string::npos) return string("");
158        return filename.substr(0, slash);
159}
160
161//trimming functions, https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring
162
163void ltrim_inplace(string &s)
164{
165        s.erase(s.begin(), find_if(s.begin(), s.end(), [](int ch) {
166                return !isspace(ch);
167                }));
168}
169
170void rtrim_inplace(string &s)
171{
172        s.erase(find_if(s.rbegin(), s.rend(), [](int ch) {
173                return !isspace(ch);
174                }).base(), s.end());
175}
176
177void trim_inplace(string &s)
178{
179        ltrim_inplace(s);
180        rtrim_inplace(s);
181}
182
183string ltrim(string s)
184{
185        ltrim_inplace(s);
186        return s;
187}
188
189string rtrim(string s)
190{
191        rtrim_inplace(s);
192        return s;
193}
194
195string trim(string s)
196{
197        trim_inplace(s);
198        return s;
199}
Note: See TracBrowser for help on using the repository browser.