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