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

Last change on this file since 1038 was 1036, checked in by Maciej Komosinski, 3 years ago

Fixed comparison for compilers with default char = signed

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