1 /*
2 	OpenLieroX
3 
4 	string utilities
5 
6 	code under LGPL
7 	created 01-05-2007
8 	by Albert Zeyer and Dark Charlie
9 */
10 
11 #ifndef __STRINGUTILS_H__
12 #define __STRINGUTILS_H__
13 
14 #include <SDL.h> // for Uint32
15 #include <cstdio> // for FILE
16 #include <string>
17 #include <sstream>
18 #include <vector>
19 #include <cassert>
20 #include <list>
21 #include <limits.h>
22 #include <set>
23 #include "types.h"
24 #include "Color.h" // for StrToCol
25 #include "Iterator.h"
26 
27 //
28 // C-string handling routines
29 //
30 // HINT: these are obsolete, use std::string where possible!!!
31 
32 // Secure c-string handling macros
33 // WARNING: don't use expressions like buf[i++] with the macros, because the "i" variable will be incremented twice in some macros!
34 #define		fix_markend(chrarray) \
35 				chrarray[sizeof(chrarray)-1] = '\0';
36 #define		fix_strnlen(chrarray) \
37 				strnlen(chrarray,sizeof(chrarray))
38 #define		fix_strncpy(chrarray, src) \
39 			{	strncpy(chrarray, src, sizeof(chrarray)); \
40 			 	chrarray[sizeof(chrarray)-1] = '\0'; }
41 #define		fix_strncat(chrarray, src) \
42 			{	size_t destlen = strnlen(chrarray, sizeof(chrarray)); \
43 				strncpy(&chrarray[destlen], src, sizeof(chrarray)-destlen); \
44 				chrarray[sizeof(chrarray)-1] = '\0'; }
45 #define		dyn_markend(dest, len) \
46 				dest[len-1] = '\0';
47 #define		dyn_strncpy(dest, src, len) \
48 			{	strncpy(dest, src, len); \
49 				dest[len-1] = '\0'; }
50 #define		dyn_strncat(dest, src, len) \
51 			{	size_t destlen = strnlen(dest, len); \
52 				strncpy(&dest[destlen], src, len-destlen); \
53 				dest[len-1] = '\0'; }
54 
55 
56 // Strnlen definition for compilers that don't have it
57 #if !defined(__USE_GNU) && _MSC_VER <= 1200
strnlen(const char * str,size_t maxlen)58 	inline size_t strnlen(const char *str, size_t maxlen) {
59 		size_t i;
60 		for(i = 0; (i < maxlen) && str[i]; ++i) {}
61 		return i;
62 	}
63 #endif
64 
65 // Misc cross-compiler compatibility problem solutions
66 #ifdef WIN32
67 #if (defined(_MSC_VER) && (_MSC_VER <= 1200))
strncasecmp(const char * str1,const char * str2,size_t l)68 	inline int strncasecmp(const char *str1, const char *str2, size_t l) {
69 		return _strnicmp(str1, str2, l);
70 	}
71 #endif
72 #	define vsnprintf _vsnprintf
73 #	define snprintf	 _snprintf
74 #	define stricmp _stricmp
75 #	define fcloseall _fcloseall
76 #	ifndef strcasecmp
77 #		define strcasecmp	stricmp
78 #	endif
79 #else
strlwr(char * string)80 inline void strlwr(char* string) {
81 	if(string)
82 		while( *string ) {
83 			*string = (char)tolower( *string );
84 			string++;
85 		}
86 }
87 #endif
88 
89 
90 /////////////
91 // Case-insensitive comparison of two chars, behaves like stringcasecmp
92 int chrcasecmp(const char c1, const char c2);
93 
94 /////////////
95 // C-string itoa for non-windows compilers (on Windows it's defined in windows.h)
96 #ifndef WIN32
97 // TODOL remove this
itoa(int val,char * buf,int base)98 inline char* itoa(int val, char* buf, int base) {
99 	int i = 29; // TODO: bad style
100 	buf[i+1] = '\0';
101 
102     do {
103         buf = "0123456789abcdefghijklmnopqrstuvwxyz"[val % base] + buf;
104         --i, val /= base;
105     } while(val && i);
106 
107     return &buf[i+1];
108 }
109 
110 // Cross-compiler compatibility
111 #	define		stricmp		strcasecmp
112 #endif
113 
114 
115 //
116 // C++ string (std::string) routines
117 //
118 // HINT: use these where possible
119 
120 void			TrimSpaces(std::string& szLine);
121 bool			replace(const std::string& text, const std::string& what, const std::string& with, std::string& result);
122 bool			replace(std::string& text, const std::string& what, const std::string& with);
123 std::string		Replace(const std::string & text, const std::string& what, const std::string& with);
124 std::string		replacemax(const std::string& text, const std::string& what, const std::string& with, std::string& result, int max);
125 std::string		replacemax(const std::string& text, const std::string& what, const std::string& with, int max);
126 std::string		strip(const std::string& text, int width);
127 bool			stripdot(std::string& text, int width);
128 void			ucfirst(std::string& text);
129 std::string		ReadUntil(const std::string& text, char until_character = '\n'); // will return whole text if not found
130 std::string		ReadUntil(const std::string& text, std::string::const_iterator& start, char until_character, const std::string& alternative = "");
131 std::string		ReadUntil(FILE* fp, char until_character = '\n');
132 Color			StrToCol(const std::string& str);
133 Color			StrToCol(const std::string& str, bool& fail);
134 std::vector<std::string> explode(const std::string& str, const std::string& delim);
135 void			freadstr(std::string& result, size_t maxlen, FILE *fp);
136 size_t			fwrite(const std::string& txt, size_t len, FILE* fp);
137 size_t			findLastPathSep(const std::string& path);
138 void			stringlwr(std::string& txt);
139 std::string		stringtolower(const std::string& txt);
140 bool			strincludes(const std::string& str, const std::string& what);
141 short			stringcasecmp(const std::string& s1, const std::string& s2);
142 bool			stringcaseequal(const std::string& s1, const std::string& s2);
143 bool			subStrEqual(const std::string& s1, const std::string s2, size_t p);
144 bool			subStrCaseEqual(const std::string& s1, const std::string s2, size_t p);
strStartsWith(const std::string & str,const std::string & start)145 inline bool		strStartsWith(const std::string& str, const std::string& start) { if(start.size() > str.size()) return false; return str.substr(0,start.size()) == start; }
strCaseStartsWith(const std::string & str,const std::string & start)146 inline bool		strCaseStartsWith(const std::string& str, const std::string& start) { if(start.size() > str.size()) return false; return subStrCaseEqual(str,start,start.size()); }
147 size_t			maxStartingEqualStr(const std::list<std::string>& strs);
148 size_t			maxStartingCaseEqualStr(const std::list<std::string>& strs);
149 std::vector<std::string> splitstring(const std::string& str, size_t maxlen, size_t maxwidth, class CFont& font);
150 std::string		splitStringWithNewLine(const std::string& str, size_t maxlen, size_t maxwidth, class CFont& font);
151 std::string		GetFileExtension(const std::string& filename);
152 std::string		GetBaseFilename(const std::string& filename);
153 std::string		GetBaseFilenameWithoutExt(const std::string& filename);
154 std::list<std::string> SplitFilename(const std::string& filename, size_t numPartsFromRight = (size_t)-1); // splits fn by PathSep
155 std::string		GetDirName(const std::string& filename);
156 size_t			stringcasefind(const std::string& text, const std::string& search_for);
157 size_t			stringcaserfind(const std::string& text, const std::string& search_for);
158 std::string		StripHtmlTags( const std::string & src );	// Also removes all "\r" and spaces at line beginning
159 std::string		GetNextWord(std::string::const_iterator it, const std::string& str);
160 bool 			Compress( const std::string & in, std::string * out, bool noCompression = false );	// Compress given string using zlib, noCompression will just add zlib header and checksum
161 bool 			Decompress( const std::string & in, std::string * out );	// Decompress, returns false if checksum fails
162 size_t			StringChecksum( const std::string & data );
163 bool			FileChecksum( const std::string & path, size_t * _checksum, size_t * _filesize );
164 std::string		Base64Encode(const std::string &data);
165 std::string		Base64Decode(const std::string &data);
166 std::string		UrlEncode(const std::string &data); // Substitute space with + and all non-alphanum symbols with %XX
167 std::string		AutoDetectLinks(const std::string& text);
168 std::string		HtmlEntityUnpairedBrackets(const std::string &txt);
169 size_t			GetPosByTextWidth(const std::string& text, int width, CFont *fnt);
170 std::string		ColToHex(Color col);
171 std::string		EscapeHtmlTags( const std::string & src );	// Escape all "<" and ">" and "&"
172 
173 bool			strSeemsLikeChatCommand(const std::string& str);
174 
subStrCount(const std::string & str,const std::string & substr)175 inline size_t subStrCount(const std::string& str, const std::string& substr) {
176 	size_t c = 0, p = 0;
177 	while((p = str.find(substr, p)) != std::string::npos) { c++; p++; }
178 	return c;
179 }
180 
181 
182 struct PrintOutFct {
~PrintOutFctPrintOutFct183 	virtual ~PrintOutFct() {}
184 	virtual void print(const std::string&) const = 0;
185 };
186 
printNullOut187 struct NullOut : PrintOutFct { void print(const std::string&) const {} };
188 
189 // returns true if last char was a newline
190 bool PrettyPrint(const std::string& prefix, const std::string& buf, const PrintOutFct& printOutFct, bool firstLineWithPrefix = true);
191 
192 Iterator<char>::Ref HexDump(Iterator<char>::Ref start, const PrintOutFct& printOutFct, const std::set<size_t>& marks = std::set<size_t>(), size_t count = (size_t)-1);
193 
194 
195 
196 
FixedWidthStr_RightFill(const std::string & str,size_t w,char c)197 inline std::string FixedWidthStr_RightFill(const std::string& str, size_t w, char c) {
198 	assert(str.size() <= w);
199 	return str + std::string(str.size() - w, c);
200 }
201 
FixedWidthStr_LeftFill(const std::string & str,size_t w,char c)202 inline std::string FixedWidthStr_LeftFill(const std::string& str, size_t w, char c) {
203 	assert(str.size() <= w);
204 	return std::string(w - str.size(), c) + str;
205 }
206 
StripQuotes(std::string & value)207 inline void StripQuotes(std::string& value) {
208 	if( value.size() >= 2 )
209 		if( value[0] == '"' && value[value.size()-1] == '"' )
210 			value = value.substr( 1, value.size()-2 );
211 }
212 
213 ////////////////////
214 // Read a fixed-length C-string from a file
freadfixedcstr(FILE * fp,size_t maxlen)215 inline std::string freadfixedcstr(FILE *fp, size_t maxlen) {
216 	std::string fileData;
217 	freadstr(fileData, maxlen, fp);
218 	return ReadUntil(fileData, '\0');
219 }
220 
221 ///////////////////
222 // Convert a numerical position to iterator
PositionToIterator(std::string & str,size_t pos)223 inline std::string::iterator PositionToIterator(std::string& str, size_t pos)  {
224 	std::string::iterator res = str.begin();
225 	for (size_t i=0; i < pos && res != str.end(); ++i, res++)  {}
226 	return res;
227 }
228 
229 
230 // Conversion functions from string to numbers
231 
232 template<typename T>
from_string(const std::string & s,std::ios_base & (* f)(std::ios_base &),bool & failed)233 T from_string(const std::string& s, std::ios_base& (*f)(std::ios_base&), bool& failed) {
234 	std::istringstream iss(s); T t = T();
235 	failed = (iss >> f >> t).fail();
236 	return t;
237 }
238 
239 template<typename T>
from_string(const std::string & s,std::ios_base & (* f)(std::ios_base &))240 T from_string(const std::string& s, std::ios_base& (*f)(std::ios_base&)) {
241 	std::istringstream iss(s); T t = T();
242 	iss >> f >> t;
243 	return t;
244 }
245 
246 template<typename T>
from_string(const std::string & s,bool & failed)247 T from_string(const std::string& s, bool& failed) {
248 	std::istringstream iss(s); T t = T();
249 	failed = (iss >> t).fail();
250 	return t;
251 }
252 
253 
254 // Conversion functions from numbers to string
255 
256 template<typename T>
to_string(T val)257 std::string to_string(T val) {
258 	std::ostringstream oss;
259 	oss << val;
260 	return oss.str();
261 }
262 
263 template<>
264 inline std::string to_string<bool>(bool val) {
265 	if(val) return "true"; else return "false";
266 }
267 
268 template<>
269 inline std::string to_string<const char*>(const char* val) {
270 	if(val) return val; else return "";
271 }
272 
273 template<>
274 inline bool from_string<bool>(const std::string& s, bool& fail) {
275 	std::string s1(stringtolower(s));
276 	TrimSpaces(s1);
277 	if( s1 == "true" || s1 == "yes" || s1 == "on" ) return true;
278 	else if( s1 == "false" || s1 == "no" || s1 == "off" ) return false;
279 	return from_string<int>(s, fail) != 0;
280 }
281 
282 template<> VectorD2<int> from_string< VectorD2<int> >(const std::string& s, bool& fail);
283 template<> inline std::string to_string< VectorD2<int> >(VectorD2<int> v) { return "(" + to_string(v.x) + "," + to_string(v.y) + ")"; }
284 
285 template<typename T>
from_string(const std::string & s)286 T from_string(const std::string& s) {
287 	bool fail; return from_string<T>(s, fail);
288 }
289 
atoi(const std::string & str)290 inline int atoi(const std::string& str)  { return from_string<int>(str);  }
atof(const std::string & str)291 inline float atof(const std::string& str) { return from_string<float>(str);  }
292 
293 
294 inline std::string ftoa(float val, int precision = -1)
295 {
296 	std::string res = to_string<float>(val);
297 	if (precision != -1)  {
298 		size_t dotpos = res.find_last_of('.');
299 		if (dotpos == std::string::npos)  {
300 			res += '.';
301 			for (int i = 0; i < precision; i++)
302 				res += '0';
303 		} else {
304 			res = res.substr(0, dotpos + precision);
305 		}
306 	}
307 
308 	return res;
309 }
310 
311 inline std::string itoa(unsigned long num, short base=10)  {
312 	std::string buf;
313 
314 	do {
315 		buf = "0123456789abcdefghijklmnopqrstuvwxyz"[num % base] + buf;
316 		num /= base;
317 	} while(num);
318 
319 	return buf;
320 }
321 
322 // std::string itoa
323 inline std::string itoa(long num, short base=10)  {
324 	if(num >= 0)
325 		return itoa((unsigned long)num, base);
326 	else
327 		return "-" + itoa((unsigned long)-num, base);
328 }
329 
330 inline std::string itoa(int num, short base=10)  { return itoa((long)num,base); }
331 inline std::string itoa(unsigned int num, short base=10)  { return itoa((unsigned long)num,base); }
332 
333 // If 64-bit long available?
334 #ifdef ULLONG_MAX
335 inline std::string itoa(unsigned long long num, short base=10)  {
336 	std::string buf;
337 
338 	do {
339 		buf = "0123456789abcdefghijklmnopqrstuvwxyz"[num % base] + buf;
340 		num /= base;
341 	} while(num);
342 
343 	return buf;
344 }
345 #endif
346 
hex(_T num)347 template<typename _T> std::string hex(_T num) { return itoa(num,16); }
348 
349 
350 struct simple_reversestring_hasher {
operatorsimple_reversestring_hasher351 	size_t operator() (const std::string& str) const {
352 		std::string::const_reverse_iterator pos = str.rbegin();
353 		unsigned short nibble = 0;
354 		size_t result = 0;
355 		for(; pos != str.rend() && nibble < sizeof(size_t)*2; pos++, nibble++)
356 			result += ((size_t)*pos % 16) << nibble*4;
357 		return result;
358 	}
359 };
360 
361 struct stringcaseless {
operatorstringcaseless362 	bool operator()(const std::string& s1, const std::string& s2) const {
363 		return stringcasecmp(s1,s2) < 0;
364 	}
365 };
366 
367 
368 struct const_string_iterator {
369 	const std::string& str;
370 	size_t pos;
371 
strconst_string_iterator372 	const_string_iterator(const std::string& s, size_t p = 0) : str(s), pos(p) {}
373 	const_string_iterator& operator++() { pos++; return *this; }
374 	const_string_iterator& operator--() { assert(pos > 0); pos--; return *this; }
375 
376 	bool operator==(const const_string_iterator& i) const {
377 		return &str == &i.str && (pos == i.pos || (pos > str.size() && i.pos > str.size()));
378 	}
379 	bool operator!=(const const_string_iterator& i) const { return !(*this == i); }
380 
381 	char operator*() const { return str[pos]; }
382 };
383 
384 #endif
385