1 /**
2  * @file
3  * @brief C-style null-terminated-character-array string library.
4  */
5 
6 /*
7  Copyright (C) 2001-2006, William Joseph.
8  All Rights Reserved.
9 
10  This file is part of GtkRadiant.
11 
12  GtkRadiant is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  GtkRadiant is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with GtkRadiant; if not, write to the Free Software
24  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
25  */
26 
27 #if !defined(INCLUDED_STRING_STRING_H)
28 #define INCLUDED_STRING_STRING_H
29 
30 #include <string>
31 #include <cstring>
32 #include <cctype>
33 #include <algorithm>
34 #include <cstdarg>
35 #include <stdio.h>
36 #include <glib.h>
37 #include "WildcardMatcher.h"
38 
39 #include "memory/allocator.h"
40 #include "generic/arrayrange.h"
41 
42 /// \brief Returns true if \p string length is zero.
43 /// O(1)
string_empty(const std::string & string)44 inline bool string_empty (const std::string& string)
45 {
46 	return string.empty();
47 }
48 
49 /// \brief Returns <0 if \p string is lexicographically less than \p other.
50 /// Returns >0 if \p string is lexicographically greater than \p other.
51 /// Returns 0 if \p string is lexicographically equal to \p other.
52 /// O(n)
string_compare(const std::string & string,const std::string & other)53 inline int string_compare (const std::string& string, const std::string& other)
54 {
55 	return std::strcmp(string.c_str(), other.c_str());
56 }
57 
58 /// \brief Returns true if \p string is lexicographically equal to \p other.
59 /// O(n)
string_equal(const std::string & string,const std::string & other)60 inline bool string_equal (const std::string& string, const std::string& other)
61 {
62 	return string == other;
63 }
64 
65 /// \brief Returns true if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n).
66 /// O(n)
string_equal_n(const std::string & string,const std::string & other,std::size_t n)67 inline bool string_equal_n (const std::string& string, const std::string& other, std::size_t n)
68 {
69 	return std::strncmp(string.c_str(), other.c_str(), n) == 0;
70 }
71 
72 /// \brief Returns <0 if \p string is lexicographically less than \p other after converting both to lower-case.
73 /// Returns >0 if \p string is lexicographically greater than \p other after converting both to lower-case.
74 /// Returns 0 if \p string is lexicographically equal to \p other after converting both to lower-case.
75 /// O(n)
string_compare_nocase(const std::string & string,const std::string & other)76 inline int string_compare_nocase (const std::string& string, const std::string& other)
77 {
78 	return g_ascii_strcasecmp(string.c_str(), other.c_str());
79 }
80 
81 /// \brief Returns <0 if [\p string, \p string + \p n) is lexicographically less than [\p other, \p other + \p n).
82 /// Returns >0 if [\p string, \p string + \p n) is lexicographically greater than [\p other, \p other + \p n).
83 /// Returns 0 if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n).
84 /// Treats all ascii characters as lower-case during comparisons.
85 /// O(n)
string_compare_nocase_n(const std::string & string,const std::string & other,std::size_t n)86 inline int string_compare_nocase_n (const std::string& string, const std::string& other, std::size_t n)
87 {
88 	return g_ascii_strncasecmp(string.c_str(), other.c_str(), n);
89 }
90 
91 /// \brief Returns true if \p string is lexicographically equal to \p other.
92 /// Treats all ascii characters as lower-case during comparisons.
93 /// O(n)
string_equal_nocase(const std::string & string,const std::string & other)94 inline bool string_equal_nocase (const std::string& string, const std::string& other)
95 {
96 	return string_compare_nocase(string, other) == 0;
97 }
98 
99 /// \brief Returns true if [\p string, \p string + \p n) is lexicographically equal to [\p other, \p other + \p n).
100 /// Treats all ascii characters as lower-case during comparisons.
101 /// O(n)
string_equal_nocase_n(const std::string & string,const std::string & other,std::size_t n)102 inline bool string_equal_nocase_n (const std::string& string, const std::string& other, std::size_t n)
103 {
104 	return string_compare_nocase_n(string, other, n) == 0;
105 }
106 
107 /// \brief Returns true if \p string is lexicographically less than \p other.
108 /// Treats all ascii characters as lower-case during comparisons.
109 /// O(n)
string_less_nocase(const std::string & string,const std::string & other)110 inline bool string_less_nocase (const std::string& string, const std::string& other)
111 {
112 	return string_compare_nocase(string, other) < 0;
113 }
114 
115 /// \brief Returns the number of non-null characters in \p string.
116 /// O(n)
string_length(const std::string & string)117 inline std::size_t string_length (const std::string& string)
118 {
119 	return string.length();
120 }
121 
122 struct RawStringEqual
123 {
operatorRawStringEqual124 		bool operator() (const std::string& x, const std::string& y) const
125 		{
126 			return x.compare(y) == 0;
127 		}
128 };
129 
130 struct RawStringLess
131 {
operatorRawStringLess132 		bool operator() (const std::string& x, const std::string& y) const
133 		{
134 			return x.compare(y) < 0;
135 		}
136 };
137 
138 struct RawStringLessNoCase
139 {
operatorRawStringLessNoCase140 		bool operator() (const std::string& x, const std::string& y) const
141 		{
142 			return string_less_nocase(x, y);
143 		}
144 };
145 
146 #include <sstream>
147 #include <string>
148 #include <algorithm>
149 #include <cctype>
150 #include <vector>
151 
152 namespace string {
153 
154 template<class T>
toString(const T & t)155 inline std::string toString (const T& t)
156 {
157 	std::stringstream ss;
158 	ss << t;
159 	return ss.str();
160 }
161 
startsWith(const std::string & source,const std::string & contains)162 inline bool startsWith (const std::string& source, const std::string& contains)
163 {
164 	return !source.compare(0, contains.size(), contains);
165 }
166 
contains(const std::string & source,const std::string & contains)167 inline bool contains (const std::string& source, const std::string& contains)
168 {
169 	return source.rfind(contains) != std::string::npos;
170 }
171 
172 inline int toInt (const std::string& str, int defaultValue = 0)
173 {
174 	if (str.empty())
175 		return defaultValue;
176 	return atoi(str.c_str());
177 }
178 
179 inline float toFloat (const std::string& str, float defaultValue = 0.0f)
180 {
181 	if (str.empty())
182 		return defaultValue;
183 	return atof(str.c_str());
184 }
185 
toLower(const std::string & str)186 inline std::string toLower (const std::string& str)
187 {
188 	std::string convert = str;
189 	std::transform(convert.begin(), convert.end(), convert.begin(), (int(*) (int)) std::tolower);
190 	return convert;
191 }
192 
toUpper(const std::string & str)193 inline std::string toUpper (const std::string& str)
194 {
195 	std::string convert = str;
196 	std::transform(convert.begin(), convert.end(), convert.begin(), (int(*) (int)) std::toupper);
197 	return convert;
198 }
199 
200 inline void splitBy (const std::string& str, std::vector<std::string>& tokens, const std::string& delimiters = " ")
201 {
202 	// Skip delimiters at beginning.
203 	std::string::size_type lastPos = str.find_first_not_of(delimiters, 0);
204 	// Find first "non-delimiter".
205 	std::string::size_type pos = str.find_first_of(delimiters, lastPos);
206 
207 	while (std::string::npos != pos || std::string::npos != lastPos) {
208 		// Found a token, add it to the vector.
209 		tokens.push_back(str.substr(lastPos, pos - lastPos));
210 		// Skip delimiters.  Note the "not_of"
211 		lastPos = str.find_first_not_of(delimiters, pos);
212 		// Find next "non-delimiter"
213 		pos = str.find_first_of(delimiters, lastPos);
214 	}
215 }
216 
217 /**
218  * Replace all occurrences of @c searchStr with @c replaceStr
219  * @param str source where all occurrences should be replaced
220  * @param searchStr search for this string
221  * @param replaceStr replace with that string
222  * @return string with all occurrences replaced
223  */
replaceAll(const std::string & str,const std::string & searchStr,const std::string & replaceStr)224 inline std::string replaceAll (const std::string& str, const std::string& searchStr, const std::string& replaceStr)
225 {
226 	if (str.empty())
227 		return str;
228 	std::string sNew = str;
229 	std::string::size_type loc;
230 	const std::string::size_type replaceLength = replaceStr.length();
231 	const std::string::size_type searchLength = searchStr.length();
232 	std::string::size_type lastPosition = 0;
233 	while (std::string::npos != (loc = sNew.find(searchStr, lastPosition))) {
234 		sNew.replace(loc, searchLength, replaceStr);
235 		lastPosition = loc + replaceLength;
236 	}
237 	return sNew;
238 }
239 
matchesWildcard(const std::string & str,const std::string & wildcard)240 inline bool matchesWildcard (const std::string& str, const std::string& wildcard)
241 {
242 	return WildcardMatcher::matches(str, wildcard);
243 }
244 
endsWith(const std::string & str,const std::string & end)245 inline bool endsWith (const std::string& str, const std::string& end)
246 {
247 	const std::size_t strLength = str.length();
248 	const std::size_t endLength = end.length();
249 	if (strLength >= endLength) {
250 		const std::size_t index = strLength - endLength;
251 		return str.compare(index, endLength, end) == 0;
252 	} else {
253 		return false;
254 	}
255 }
256 
257 /**
258  * Returns a new string that has all the spaces from the given string removed.
259  */
eraseAllSpaces(const std::string & str)260 inline std::string eraseAllSpaces (const std::string& str)
261 {
262 	std::string tmp(str);
263 	tmp.erase(std::remove(tmp.begin(), tmp.end(), ' '), tmp.end());
264 	return tmp;
265 }
266 
cutAfterFirstMatch(const std::string & str,const std::string & pattern)267 inline std::string cutAfterFirstMatch (const std::string& str, const std::string& pattern)
268 {
269 	std::string::size_type pos = str.find_first_of(pattern, 0);
270 	return str.substr(0, pos);
271 }
272 
format(const std::string & msg,...)273 inline std::string format (const std::string &msg, ...)
274 {
275 	va_list ap;
276 	const std::size_t size = 1024;
277 	char text[size];
278 
279 	va_start(ap, msg);
280 	vsnprintf(text, size, msg.c_str(), ap);
281 	va_end(ap);
282 
283 	return std::string(text);
284 }
285 
286 // inline newline (\r and \n) removal
287 // true if newlines where replaced, false if no newlines where in the source string
removeNewlines(std::string & string)288 inline bool removeNewlines (std::string& string)
289 {
290 	int count = 0;
291 
292 	// Baal: check string for newline characters \n \r and count them
293 	for (std::string::const_iterator i = string.begin(); i != string.end(); i++)
294 		if (*i == '\n' || *i == '\r')
295 			count++;
296 
297 	// Baal: copy the string and remove newlines (only if one of these was found before)
298 	if (count > 0) {
299 		std::string temp(string.length() - count, 0);
300 		std::string::const_iterator stringIt;
301 		std::string::iterator tempIt;
302 
303 		for ((stringIt = string.begin(), tempIt = temp.begin()); stringIt != string.end(); stringIt++) {
304 			if (*stringIt == '\n' || *stringIt == '\r')
305 				continue;
306 
307 			*tempIt++ = *stringIt;
308 		}
309 		string = temp;
310 		return true;
311 	}
312 	return false;
313 }
314 
315 }
316 
317 #endif
318