1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // string_utils:
7 // String helper functions.
8 //
9
10 #include "common/string_utils.h"
11
12 #include <stdlib.h>
13 #include <string.h>
14 #include <algorithm>
15 #include <cctype>
16 #include <fstream>
17 #include <sstream>
18
19 #include "common/platform.h"
20 #include "common/system_utils.h"
21
22 namespace
23 {
24
EndsWithSuffix(const char * str,const size_t strLen,const char * suffix,const size_t suffixLen)25 bool EndsWithSuffix(const char *str,
26 const size_t strLen,
27 const char *suffix,
28 const size_t suffixLen)
29 {
30 return suffixLen <= strLen && strncmp(str + strLen - suffixLen, suffix, suffixLen) == 0;
31 }
32
33 } // anonymous namespace
34
35 namespace angle
36 {
37
38 const char kWhitespaceASCII[] = " \f\n\r\t\v";
39
SplitString(const std::string & input,const std::string & delimiters,WhitespaceHandling whitespace,SplitResult resultType)40 std::vector<std::string> SplitString(const std::string &input,
41 const std::string &delimiters,
42 WhitespaceHandling whitespace,
43 SplitResult resultType)
44 {
45 std::vector<std::string> result;
46 if (input.empty())
47 {
48 return result;
49 }
50
51 std::string::size_type start = 0;
52 while (start != std::string::npos)
53 {
54 auto end = input.find_first_of(delimiters, start);
55
56 std::string piece;
57 if (end == std::string::npos)
58 {
59 piece = input.substr(start);
60 start = std::string::npos;
61 }
62 else
63 {
64 piece = input.substr(start, end - start);
65 start = end + 1;
66 }
67
68 if (whitespace == TRIM_WHITESPACE)
69 {
70 piece = TrimString(piece, kWhitespaceASCII);
71 }
72
73 if (resultType == SPLIT_WANT_ALL || !piece.empty())
74 {
75 result.push_back(std::move(piece));
76 }
77 }
78
79 return result;
80 }
81
SplitStringAlongWhitespace(const std::string & input,std::vector<std::string> * tokensOut)82 void SplitStringAlongWhitespace(const std::string &input, std::vector<std::string> *tokensOut)
83 {
84
85 std::istringstream stream(input);
86 std::string line;
87
88 while (std::getline(stream, line))
89 {
90 size_t prev = 0, pos;
91 while ((pos = line.find_first_of(kWhitespaceASCII, prev)) != std::string::npos)
92 {
93 if (pos > prev)
94 tokensOut->push_back(line.substr(prev, pos - prev));
95 prev = pos + 1;
96 }
97 if (prev < line.length())
98 tokensOut->push_back(line.substr(prev, std::string::npos));
99 }
100 }
101
TrimString(const std::string & input,const std::string & trimChars)102 std::string TrimString(const std::string &input, const std::string &trimChars)
103 {
104 auto begin = input.find_first_not_of(trimChars);
105 if (begin == std::string::npos)
106 {
107 return "";
108 }
109
110 std::string::size_type end = input.find_last_not_of(trimChars);
111 if (end == std::string::npos)
112 {
113 return input.substr(begin);
114 }
115
116 return input.substr(begin, end - begin + 1);
117 }
118
GetPrefix(const std::string & input,size_t offset,const char * delimiter)119 std::string GetPrefix(const std::string &input, size_t offset, const char *delimiter)
120 {
121 size_t match = input.find(delimiter, offset);
122 if (match == std::string::npos)
123 {
124 return input.substr(offset);
125 }
126 return input.substr(offset, match - offset);
127 }
128
GetPrefix(const std::string & input,size_t offset,char delimiter)129 std::string GetPrefix(const std::string &input, size_t offset, char delimiter)
130 {
131 size_t match = input.find(delimiter, offset);
132 if (match == std::string::npos)
133 {
134 return input.substr(offset);
135 }
136 return input.substr(offset, match - offset);
137 }
138
HexStringToUInt(const std::string & input,unsigned int * uintOut)139 bool HexStringToUInt(const std::string &input, unsigned int *uintOut)
140 {
141 unsigned int offset = 0;
142
143 if (input.size() >= 2 && input[0] == '0' && input[1] == 'x')
144 {
145 offset = 2u;
146 }
147
148 // Simple validity check
149 if (input.find_first_not_of("0123456789ABCDEFabcdef", offset) != std::string::npos)
150 {
151 return false;
152 }
153
154 std::stringstream inStream(input);
155 inStream >> std::hex >> *uintOut;
156 return !inStream.fail();
157 }
158
ReadFileToString(const std::string & path,std::string * stringOut)159 bool ReadFileToString(const std::string &path, std::string *stringOut)
160 {
161 std::ifstream inFile(path.c_str(), std::ios::binary);
162 if (inFile.fail())
163 {
164 return false;
165 }
166
167 inFile.seekg(0, std::ios::end);
168 auto size = static_cast<std::string::size_type>(inFile.tellg());
169 stringOut->resize(size);
170 inFile.seekg(0, std::ios::beg);
171
172 inFile.read(stringOut->data(), size);
173 return !inFile.fail();
174 }
175
BeginsWith(const std::string & str,const std::string & prefix)176 bool BeginsWith(const std::string &str, const std::string &prefix)
177 {
178 return strncmp(str.c_str(), prefix.c_str(), prefix.length()) == 0;
179 }
180
BeginsWith(const std::string & str,const char * prefix)181 bool BeginsWith(const std::string &str, const char *prefix)
182 {
183 return strncmp(str.c_str(), prefix, strlen(prefix)) == 0;
184 }
185
BeginsWith(const char * str,const char * prefix)186 bool BeginsWith(const char *str, const char *prefix)
187 {
188 return strncmp(str, prefix, strlen(prefix)) == 0;
189 }
190
BeginsWith(const std::string & str,const std::string & prefix,const size_t prefixLength)191 bool BeginsWith(const std::string &str, const std::string &prefix, const size_t prefixLength)
192 {
193 return strncmp(str.c_str(), prefix.c_str(), prefixLength) == 0;
194 }
195
EndsWith(const std::string & str,const std::string & suffix)196 bool EndsWith(const std::string &str, const std::string &suffix)
197 {
198 return EndsWithSuffix(str.c_str(), str.length(), suffix.c_str(), suffix.length());
199 }
200
EndsWith(const std::string & str,const char * suffix)201 bool EndsWith(const std::string &str, const char *suffix)
202 {
203 return EndsWithSuffix(str.c_str(), str.length(), suffix, strlen(suffix));
204 }
205
EndsWith(const char * str,const char * suffix)206 bool EndsWith(const char *str, const char *suffix)
207 {
208 return EndsWithSuffix(str, strlen(str), suffix, strlen(suffix));
209 }
210
ContainsToken(const std::string & tokenStr,char delimiter,const std::string & token)211 bool ContainsToken(const std::string &tokenStr, char delimiter, const std::string &token)
212 {
213 if (token.empty())
214 {
215 return false;
216 }
217 // Compare token with all sub-strings terminated by delimiter or end of string
218 std::string::size_type start = 0u;
219 do
220 {
221 std::string::size_type end = tokenStr.find(delimiter, start);
222 if (end == std::string::npos)
223 {
224 end = tokenStr.length();
225 }
226 const std::string::size_type length = end - start;
227 if (length == token.length() && tokenStr.compare(start, length, token) == 0)
228 {
229 return true;
230 }
231 start = end + 1u;
232 } while (start < tokenStr.size());
233 return false;
234 }
235
ToLower(std::string * str)236 void ToLower(std::string *str)
237 {
238 for (char &ch : *str)
239 {
240 ch = static_cast<char>(::tolower(ch));
241 }
242 }
243
ToUpper(std::string * str)244 void ToUpper(std::string *str)
245 {
246 for (char &ch : *str)
247 {
248 ch = static_cast<char>(::toupper(ch));
249 }
250 }
251
ReplaceSubstring(std::string * str,const std::string & substring,const std::string & replacement)252 bool ReplaceSubstring(std::string *str,
253 const std::string &substring,
254 const std::string &replacement)
255 {
256 size_t replacePos = str->find(substring);
257 if (replacePos == std::string::npos)
258 {
259 return false;
260 }
261 str->replace(replacePos, substring.size(), replacement);
262 return true;
263 }
264
ReplaceAllSubstrings(std::string * str,const std::string & substring,const std::string & replacement)265 int ReplaceAllSubstrings(std::string *str,
266 const std::string &substring,
267 const std::string &replacement)
268 {
269 int count = 0;
270 while (ReplaceSubstring(str, substring, replacement))
271 {
272 count++;
273 }
274 return count;
275 }
276
ToCamelCase(const std::string & str)277 std::string ToCamelCase(const std::string &str)
278 {
279 std::string result;
280
281 bool lastWasUnderscore = false;
282 for (char c : str)
283 {
284 if (c == '_')
285 {
286 lastWasUnderscore = true;
287 continue;
288 }
289
290 if (lastWasUnderscore)
291 {
292 c = static_cast<char>(std::toupper(c));
293 lastWasUnderscore = false;
294 }
295 result += c;
296 }
297
298 return result;
299 }
300
GetStringsFromEnvironmentVarOrAndroidProperty(const char * varName,const char * propertyName,const char * separator)301 std::vector<std::string> GetStringsFromEnvironmentVarOrAndroidProperty(const char *varName,
302 const char *propertyName,
303 const char *separator)
304 {
305 std::string environment = GetEnvironmentVarOrAndroidProperty(varName, propertyName);
306 return SplitString(environment, separator, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
307 }
308
GetCachedStringsFromEnvironmentVarOrAndroidProperty(const char * varName,const char * propertyName,const char * separator)309 std::vector<std::string> GetCachedStringsFromEnvironmentVarOrAndroidProperty(
310 const char *varName,
311 const char *propertyName,
312 const char *separator)
313 {
314 std::string environment = GetEnvironmentVarOrAndroidProperty(varName, propertyName);
315 return SplitString(environment, separator, TRIM_WHITESPACE, SPLIT_WANT_NONEMPTY);
316 }
317
318 // glob can have * as wildcard
NamesMatchWithWildcard(const char * glob,const char * name)319 bool NamesMatchWithWildcard(const char *glob, const char *name)
320 {
321 // Find the first * in glob.
322 const char *firstWildcard = strchr(glob, '*');
323
324 // If there are no wildcards, match the strings precisely.
325 if (firstWildcard == nullptr)
326 {
327 return strcmp(glob, name) == 0;
328 }
329
330 // Otherwise, match up to the wildcard first.
331 size_t preWildcardLen = firstWildcard - glob;
332 if (strncmp(glob, name, preWildcardLen) != 0)
333 {
334 return false;
335 }
336
337 const char *postWildcardRef = glob + preWildcardLen + 1;
338
339 // As a small optimization, if the wildcard is the last character in glob, accept the match
340 // already.
341 if (postWildcardRef[0] == '\0')
342 {
343 return true;
344 }
345
346 // Try to match the wildcard with a number of characters.
347 for (size_t matchSize = 0; name[matchSize] != '\0'; ++matchSize)
348 {
349 if (NamesMatchWithWildcard(postWildcardRef, name + matchSize))
350 {
351 return true;
352 }
353 }
354
355 return false;
356 }
357
358 } // namespace angle
359