|
1 /* |
|
2 Copyright 2012-2014 Teemu Piippo |
|
3 All rights reserved. |
|
4 |
|
5 Redistribution and use in source and binary forms, with or without |
|
6 modification, are permitted provided that the following conditions |
|
7 are met: |
|
8 |
|
9 1. Redistributions of source code must retain the above copyright |
|
10 notice, this list of conditions and the following disclaimer. |
|
11 2. Redistributions in binary form must reproduce the above copyright |
|
12 notice, this list of conditions and the following disclaimer in the |
|
13 documentation and/or other materials provided with the distribution. |
|
14 3. The name of the author may not be used to endorse or promote products |
|
15 derived from this software without specific prior written permission. |
|
16 |
|
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
|
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
27 */ |
|
28 |
|
29 #include <cstring> |
|
30 #include "main.h" |
|
31 #include "stringClass.h" |
|
32 |
|
33 // ============================================================================= |
|
34 // |
|
35 int String::compare (const String& other) const |
|
36 { |
|
37 return _string.compare (other.stdString()); |
|
38 } |
|
39 |
|
40 // ============================================================================= |
|
41 // |
|
42 void String::trim (int n) |
|
43 { |
|
44 if (n > 0) |
|
45 _string = mid (0, length() - n).stdString(); |
|
46 else |
|
47 _string = mid (n, -1).stdString(); |
|
48 } |
|
49 |
|
50 // ============================================================================= |
|
51 // |
|
52 String String::strip (const List<char>& unwanted) |
|
53 { |
|
54 String copy (_string); |
|
55 |
|
56 for (char c : unwanted) |
|
57 { |
|
58 int pos = 0; |
|
59 while ((pos = copy.firstIndexOf (String (c))) != -1) |
|
60 copy.removeAt (pos--); |
|
61 } |
|
62 |
|
63 return copy; |
|
64 } |
|
65 |
|
66 // ============================================================================= |
|
67 // |
|
68 String String::toUppercase() const |
|
69 { |
|
70 String newstr (_string); |
|
71 |
|
72 for (char& c : newstr) |
|
73 { |
|
74 if (c >= 'a' and c <= 'z') |
|
75 c -= 'a' - 'A'; |
|
76 } |
|
77 |
|
78 return newstr; |
|
79 } |
|
80 |
|
81 // ============================================================================= |
|
82 // |
|
83 String String::toLowercase() const |
|
84 { |
|
85 String newstr (_string); |
|
86 |
|
87 for (char& c : newstr) |
|
88 { |
|
89 if (c >= 'A' and c <= 'Z') |
|
90 c += 'a' - 'A'; |
|
91 } |
|
92 |
|
93 return newstr; |
|
94 } |
|
95 |
|
96 // ============================================================================= |
|
97 // |
|
98 StringList String::split (char del) const |
|
99 { |
|
100 String delimstr; |
|
101 delimstr += del; |
|
102 return split (delimstr); |
|
103 } |
|
104 |
|
105 // ============================================================================= |
|
106 // |
|
107 StringList String::split (const String& del) const |
|
108 { |
|
109 StringList res; |
|
110 int a = 0; |
|
111 int b; |
|
112 |
|
113 // Find all separators and store the text left to them. |
|
114 while ((b = firstIndexOf (del, a)) != -1) |
|
115 { |
|
116 String sub = mid (a, b); |
|
117 |
|
118 if (sub.length() > 0) |
|
119 res << sub; |
|
120 |
|
121 a = b + del.length(); |
|
122 } |
|
123 |
|
124 // Add the string at the right of the last separator |
|
125 if (a < (int) length()) |
|
126 res.append (mid (a, length())); |
|
127 |
|
128 return res; |
|
129 } |
|
130 |
|
131 // ============================================================================= |
|
132 // |
|
133 void String::replace (const char* a, const char* b) |
|
134 { |
|
135 long pos; |
|
136 |
|
137 while ((pos = firstIndexOf (a)) != -1) |
|
138 _string = _string.replace (pos, strlen (a), b); |
|
139 } |
|
140 |
|
141 // ============================================================================= |
|
142 // |
|
143 int String::count (char needle) const |
|
144 { |
|
145 int needles = 0; |
|
146 |
|
147 for (const char & c : _string) |
|
148 if (c == needle) |
|
149 needles++; |
|
150 |
|
151 return needles; |
|
152 } |
|
153 |
|
154 // ============================================================================= |
|
155 // |
|
156 String String::mid (long a, long b) const |
|
157 { |
|
158 if (b == -1) |
|
159 b = length(); |
|
160 |
|
161 if (b == a) |
|
162 return ""; |
|
163 |
|
164 if (b < a) |
|
165 { |
|
166 // Swap the variables |
|
167 int c = a; |
|
168 a = b; |
|
169 b = c; |
|
170 } |
|
171 |
|
172 char* newstr = new char[b - a + 1]; |
|
173 strncpy (newstr, _string.c_str() + a, b - a); |
|
174 newstr[b - a] = '\0'; |
|
175 |
|
176 String other (newstr); |
|
177 delete[] newstr; |
|
178 return other; |
|
179 } |
|
180 |
|
181 // ============================================================================= |
|
182 // |
|
183 int String::wordPosition (int n) const |
|
184 { |
|
185 int count = 0; |
|
186 |
|
187 for (int i = 0; i < length(); ++i) |
|
188 { |
|
189 if (_string[i] != ' ') |
|
190 continue; |
|
191 |
|
192 if (++count < n) |
|
193 continue; |
|
194 |
|
195 return i; |
|
196 } |
|
197 |
|
198 return -1; |
|
199 } |
|
200 |
|
201 // ============================================================================= |
|
202 // |
|
203 int String::firstIndexOf (const char* c, int a) const |
|
204 { |
|
205 int pos = _string.find (c, a); |
|
206 |
|
207 if (pos == (int) std::string::npos) |
|
208 return -1; |
|
209 |
|
210 return pos; |
|
211 } |
|
212 |
|
213 // ============================================================================= |
|
214 // |
|
215 int String::lastIndexOf (const char* c, int a) const |
|
216 { |
|
217 if (a == -1 or a >= length()) |
|
218 a = length() - 1; |
|
219 |
|
220 for (; a > 0; a--) |
|
221 if (_string[a] == c[0] and strncmp (_string.c_str() + a, c, strlen (c)) == 0) |
|
222 return a; |
|
223 |
|
224 return -1; |
|
225 } |
|
226 |
|
227 // ============================================================================= |
|
228 // |
|
229 void String::dump() const |
|
230 { |
|
231 print ("`%1`:\n", c_str()); |
|
232 int i = 0; |
|
233 |
|
234 for (char u : _string) |
|
235 print ("\t%1. [%d2] `%3`\n", i++, u, String (u)); |
|
236 } |
|
237 |
|
238 // ============================================================================= |
|
239 // |
|
240 long String::toLong (bool* ok, int base) const |
|
241 { |
|
242 errno = 0; |
|
243 char* endptr; |
|
244 long i = strtol (_string.c_str(), &endptr, base); |
|
245 |
|
246 if (ok) |
|
247 *ok = (errno == 0 and *endptr == '\0'); |
|
248 |
|
249 return i; |
|
250 } |
|
251 |
|
252 // ============================================================================= |
|
253 // |
|
254 float String::toFloat (bool* ok) const |
|
255 { |
|
256 errno = 0; |
|
257 char* endptr; |
|
258 float i = strtof (_string.c_str(), &endptr); |
|
259 |
|
260 if (ok) |
|
261 *ok = (errno == 0 and *endptr == '\0'); |
|
262 |
|
263 return i; |
|
264 } |
|
265 |
|
266 // ============================================================================= |
|
267 // |
|
268 double String::toDouble (bool* ok) const |
|
269 { |
|
270 errno = 0; |
|
271 char* endptr; |
|
272 double i = strtod (_string.c_str(), &endptr); |
|
273 |
|
274 if (ok) |
|
275 *ok = (errno == 0 and *endptr == '\0'); |
|
276 |
|
277 return i; |
|
278 } |
|
279 |
|
280 // ============================================================================= |
|
281 // |
|
282 String String::operator+ (const String& data) const |
|
283 { |
|
284 String newString = *this; |
|
285 newString.append (data); |
|
286 return newString; |
|
287 } |
|
288 |
|
289 // ============================================================================= |
|
290 // |
|
291 String String::operator+ (const char* data) const |
|
292 { |
|
293 String newstr = *this; |
|
294 newstr.append (data); |
|
295 return newstr; |
|
296 } |
|
297 |
|
298 // ============================================================================= |
|
299 // |
|
300 bool String::isNumeric() const |
|
301 { |
|
302 bool gotDot = false; |
|
303 |
|
304 for (const char & c : _string) |
|
305 { |
|
306 // Allow leading hyphen for negatives |
|
307 if (&c == &_string[0] and c == '-') |
|
308 continue; |
|
309 |
|
310 // Check for decimal point |
|
311 if (!gotDot and c == '.') |
|
312 { |
|
313 gotDot = true; |
|
314 continue; |
|
315 } |
|
316 |
|
317 if (c >= '0' and c <= '9') |
|
318 continue; // Digit |
|
319 |
|
320 // If the above cases didn't catch this character, it was |
|
321 // illegal and this is therefore not a number. |
|
322 return false; |
|
323 } |
|
324 |
|
325 return true; |
|
326 } |
|
327 |
|
328 // ============================================================================= |
|
329 // |
|
330 bool String::endsWith (const String& other) |
|
331 { |
|
332 if (length() < other.length()) |
|
333 return false; |
|
334 |
|
335 const int ofs = length() - other.length(); |
|
336 return strncmp (c_str() + ofs, other.c_str(), other.length()) == 0; |
|
337 } |
|
338 |
|
339 // ============================================================================= |
|
340 // |
|
341 bool String::startsWith (const String& other) |
|
342 { |
|
343 if (length() < other.length()) |
|
344 return false; |
|
345 |
|
346 return strncmp (c_str(), other.c_str(), other.length()) == 0; |
|
347 } |
|
348 |
|
349 // ============================================================================= |
|
350 // |
|
351 void String::sprintf (const char* fmtstr, ...) |
|
352 { |
|
353 char* buf; |
|
354 int bufsize = 256; |
|
355 va_list va; |
|
356 va_start (va, fmtstr); |
|
357 |
|
358 do |
|
359 buf = new char[bufsize]; |
|
360 while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize); |
|
361 |
|
362 va_end (va); |
|
363 _string = buf; |
|
364 delete[] buf; |
|
365 } |
|
366 |
|
367 // ============================================================================= |
|
368 // |
|
369 String StringList::join (const String& delim) |
|
370 { |
|
371 String result; |
|
372 |
|
373 for (const String& it : deque()) |
|
374 { |
|
375 if (result.isEmpty() == false) |
|
376 result += delim; |
|
377 |
|
378 result += it; |
|
379 } |
|
380 |
|
381 return result; |
|
382 } |
|
383 |
|
384 // ============================================================================= |
|
385 // |
|
386 bool String::maskAgainst (const String& pattern) const |
|
387 { |
|
388 // Elevate to uppercase for case-insensitive matching |
|
389 String pattern_upper = pattern.toUppercase(); |
|
390 String this_upper = toUppercase(); |
|
391 const char* maskstring = pattern_upper.c_str(); |
|
392 const char* mptr = &maskstring[0]; |
|
393 |
|
394 for (const char* sptr = this_upper.c_str(); *sptr != '\0'; sptr++) |
|
395 { |
|
396 if (*mptr == '?') |
|
397 { |
|
398 if (*(sptr + 1) == '\0') |
|
399 { |
|
400 // ? demands that there's a character here and there wasn't. |
|
401 // Therefore, mask matching fails |
|
402 return false; |
|
403 } |
|
404 } |
|
405 elif (*mptr == '*') |
|
406 { |
|
407 char end = *(++mptr); |
|
408 |
|
409 // If '*' is the final character of the message, all of the remaining |
|
410 // string matches against the '*'. We don't need to bother checking |
|
411 // the string any further. |
|
412 if (end == '\0') |
|
413 return true; |
|
414 |
|
415 // Skip to the end character |
|
416 while (*sptr != end and *sptr != '\0') |
|
417 sptr++; |
|
418 |
|
419 // String ended while the mask still had stuff |
|
420 if (*sptr == '\0') |
|
421 return false; |
|
422 } |
|
423 elif (*sptr != *mptr) |
|
424 return false; |
|
425 |
|
426 mptr++; |
|
427 } |
|
428 |
|
429 return true; |
|
430 } |
|
431 |
|
432 // ============================================================================= |
|
433 // |
|
434 String String::fromNumber (int a) |
|
435 { |
|
436 char buf[32]; |
|
437 ::sprintf (buf, "%d", a); |
|
438 return String (buf); |
|
439 } |
|
440 |
|
441 // ============================================================================= |
|
442 // |
|
443 String String::fromNumber (long a) |
|
444 { |
|
445 char buf[32]; |
|
446 ::sprintf (buf, "%ld", a); |
|
447 return String (buf); |
|
448 } |