1 /*
2 This file is part of Warzone 2100.
3 Copyright (C) 1999-2004 Eidos Interactive
4 Copyright (C) 2005-2020 Warzone 2100 Project
5
6 Warzone 2100 is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 Warzone 2100 is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with Warzone 2100; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20 #ifndef STRING_EXT_H
21 #define STRING_EXT_H
22
23 #define FRAME_LIB_INCLUDE
24 #include "debug.h"
25 #include <string.h>
26 #include <stddef.h>
27 #include <assert.h>
28
29 #ifdef HAVE_STRINGS_H
30 #include <strings.h>
31 #endif
32
33 #include <string>
34 #include <utility>
35 #include <stdarg.h>
36
37 /*!
38 * On MSVC, in order to squelch tons of 'memory leaks' we set the allocator
39 * to not count the memory received by strdup() calls.
40 *
41 */
42 #if defined(WZ_CC_MSVC)
43 # ifdef strdup
44 # undef strdup
45 # endif
46 # if defined(DEBUG)
47 # define strdup(s) \
48 strdup2(s,__FILE__,__LINE__)
strdup2(const char * s,char * fileName,int line)49 static inline char *strdup2(const char *s, char *fileName, int line)
50 {
51 char *result;
52
53 (void)debug_MEMCHKOFF();
54 result = (char *)malloc(strlen(s) + 1);
55 (void)debug_MEMCHKON();
56
57 if (result == (char *)0)
58 {
59 return (char *)0;
60 }
61 strcpy(result, s);
62 return result;
63 }
64 # else // for release builds
65 # define strdup _strdup
66 # endif //debug block
67 #endif
68
69 /*!
70 * Safe variant of strlen.
71 * Finds the length of the required buffer to store string.
72 * \param string holds the string to scan the required buffer size for
73 * \param maxlen the maximum amount of bytes to scan in string
74 * \return the required size for a buffer to hold \c string or \c maxlen if
75 * no terminating '\\0' is found.
76 *
77 * \note This is the same as strnlen(string, maxlen - 1) + 1 when using the
78 * GNU C library.
79 */
strnlen1(const char * string,size_t maxlen)80 WZ_DECL_PURE static inline size_t strnlen1(const char *string, size_t maxlen)
81 {
82 // Find the first NUL char
83 const char *end = (const char *)memchr(string, '\0', maxlen); // Cast required for C++
84
85 if (end != NULL)
86 {
87 return end - string + 1;
88 }
89 else
90 {
91 return maxlen;
92 }
93 }
94
95
96 #ifndef HAVE_VALID_STRLCPY
97 # ifdef HAVE_SYSTEM_STRLCPY
98 // If the system provides a non-conformant strlcpy we use our own
99 # ifdef strlcpy
100 # undef strlcpy
101 # endif
102 # define strlcpy wz_strlcpy
103 # endif // HAVE_SYSTEM_STRLCPY
104
105 /*!
106 * A safer variant of \c strncpy and its completely unsafe variant \c strcpy.
107 * Copy src to string dest of size "size". At most size-1 characters will be copied.
108 * Always nul-terminates, unless size = 0. Returned value is the entire length of string src.
109 * \param dest a pointer to the destination buffer
110 * \param src the source string to copy into the \c dest buffer
111 * \param size the buffer size (in bytes) of buffer \c dest
112 * \return Length to string src, if >= size truncation occurred
113 */
strlcpy(char * WZ_DECL_RESTRICT dest,const char * WZ_DECL_RESTRICT src,size_t size)114 static inline size_t strlcpy(char *WZ_DECL_RESTRICT dest, const char *WZ_DECL_RESTRICT src, size_t size)
115 {
116 #ifdef DEBUG
117 ASSERT_OR_RETURN(0, src != NULL, "strlcpy was passed an invalid src parameter.");
118 ASSERT_OR_RETURN(0, dest != NULL, "strlcpy was passed an invalid dest parameter.");
119 #endif
120
121 #if defined(WZ_CC_GNU) && !defined(WZ_CC_INTEL) && !defined(WZ_CC_CLANG) && (8 <= __GNUC__)
122 # pragma GCC diagnostic push
123 # pragma GCC diagnostic ignored "-Wstringop-truncation"
124 #endif
125
126 if (size > 0)
127 {
128 strncpy(dest, src, size - 1);
129 // Guarantee to nul-terminate
130 dest[size - 1] = '\0';
131 }
132
133 #if defined(WZ_CC_GNU) && !defined(WZ_CC_INTEL) && !defined(WZ_CC_CLANG) && (8 <= __GNUC__)
134 # pragma GCC diagnostic pop
135 #endif
136
137 return strlen(src);
138 }
139 #endif // HAVE_VALID_STRLCPY
140
141 #ifndef HAVE_VALID_STRLCAT
142 # ifdef HAVE_SYSTEM_STRLCAT
143 // If the system provides a non-conformant strlcat we use our own
144 # ifdef strlcat
145 # undef strlcat
146 # endif
147 # define strlcat wz_strlcat
148 # endif // HAVE_SYSTEM_STRLCAT
149 /**
150 * A safer variant of \c strncat and its completely unsafe variant \c strcat.
151 * Append src to string dest of size "size" (unlike strncat, size is the
152 * full size of dest, not space left). At most size-1 characters will be copied.
153 * Always nul-terminates, unless size < strlen(dest).
154 * Returned value is the entire length of string src + min(size, strlen(dest)).
155 * \param dest a pointer to the destination buffer
156 * \param src the source string to copy into the \c dest buffer
157 * \param size the buffer size (in bytes) of buffer \c dest
158 * \return Length to string src + dest, if >= size truncation occurred.
159 */
strlcat(char * WZ_DECL_RESTRICT dest,const char * WZ_DECL_RESTRICT src,size_t size)160 static inline size_t strlcat(char *WZ_DECL_RESTRICT dest, const char *WZ_DECL_RESTRICT src, size_t size)
161 {
162 size_t len;
163
164 #ifdef DEBUG
165 ASSERT_OR_RETURN(0, src != NULL, "strlcat was passed an invalid src parameter.");
166 ASSERT_OR_RETURN(0, dest != NULL, "strlcat was passed an invalid dest parameter.");
167 #endif
168
169 len = strlen(src);
170
171 if (size > 0)
172 {
173 size_t dlen;
174
175 dlen = strnlen1(dest, size);
176 len += dlen;
177
178 assert(dlen > 0);
179
180 strlcpy(&dest[dlen - 1], src, size - dlen);
181 }
182
183 return len;
184 }
185 #endif // HAVE_VALID_STRLCAT
186
187 /*
188 * Static array versions of common string functions. Safer because one less parameter to screw up.
189 */
190 template <unsigned N>
sstrcpy(char (& dest)[N],char const * src)191 static inline size_t sstrcpy(char (&dest)[N], char const *src) { return strlcpy(dest, src, N); }
192 template <unsigned N>
sstrcat(char (& dest)[N],char const * src)193 static inline size_t sstrcat(char (&dest)[N], char const *src) { return strlcat(dest, src, N); }
194 template <unsigned N1, unsigned N2>
sstrcmp(char const (& str1)[N1],char const (& str2)[N2])195 static inline int sstrcmp(char const (&str1)[N1], char const (&str2)[N2]) { return strncmp(str1, str2, std::min(N1, N2)); }
196 template <unsigned N, typename... P>
ssprintf(char (& dest)[N],char const * format,P &&...params)197 static inline int ssprintf(char (&dest)[N], char const *format, P &&... params) { return snprintf(dest, N, format, std::forward<P>(params)...); }
198 template <unsigned N>
vssprintf(char (& dest)[N],char const * format,va_list params)199 static inline int vssprintf(char (&dest)[N], char const *format, va_list params) { return vsnprintf(dest, N, format, params); }
200
201 template <typename... P>
astringf(char const * format,P &&...params)202 static inline std::string astringf(char const *format, P &&... params)
203 {
204 int len = snprintf(nullptr, 0, format, std::forward<P>(params)...);
205 if (len <= 0)
206 {
207 return {};
208 }
209 std::string str;
210 str.resize(len + 1);
211 snprintf(&str[0], len + 1, format, std::forward<P>(params)...);
212 str.resize(len);
213 return str;
214 }
215
216
217 template <typename... P>
sstringf(std::string & str,char const * format,P &&...params)218 static inline void sstringf(std::string &str, char const *format, P &&... params)
219 {
220 str.resize(str.capacity());
221 int len = snprintf(&str[0], str.size(), format, std::forward<P>(params)...);
222 if (len <= 0)
223 {
224 str.clear();
225 }
226 else if ((unsigned)len >= str.size())
227 {
228 str.resize(len + 1);
229 snprintf(&str[0], len + 1, format, std::forward<P>(params)...);
230 }
231 str.resize(len);
232 }
233
234 #endif // STRING_EXT_H
235