| |
1 /* |
| |
2 Copyright 2012-2014 Santeri 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 "String.h" |
| |
32 |
| |
33 // ============================================================================= |
| |
34 // |
| |
35 int String::Compare (const String& other) const |
| |
36 { |
| |
37 return mString.compare (other.STDString()); |
| |
38 } |
| |
39 |
| |
40 // ============================================================================= |
| |
41 // |
| |
42 void String::Trim (int n) |
| |
43 { |
| |
44 if (n > 0) |
| |
45 mString = Mid (0, Length() - n).STDString(); |
| |
46 else |
| |
47 mString = Mid (n, -1).STDString(); |
| |
48 } |
| |
49 |
| |
50 // ============================================================================= |
| |
51 // |
| |
52 String String::Strip (const List< char >& unwanted) |
| |
53 { |
| |
54 String copy (mString); |
| |
55 |
| |
56 for (char c : unwanted) |
| |
57 for (int i = 0; i < copy.Length(); ++i) |
| |
58 if (copy[i] == c) |
| |
59 copy.RemoveAt (i); |
| |
60 |
| |
61 /* |
| |
62 while(( pos = copy.first( c )) != -1 ) |
| |
63 copy.erase( pos ); |
| |
64 */ |
| |
65 |
| |
66 return copy; |
| |
67 } |
| |
68 |
| |
69 // ============================================================================= |
| |
70 // |
| |
71 String String::ToUppercase() const |
| |
72 { |
| |
73 String newstr = mString; |
| |
74 |
| |
75 for (char & c : newstr) |
| |
76 if (c >= 'a' && c <= 'z') |
| |
77 c -= 'a' - 'A'; |
| |
78 |
| |
79 return newstr; |
| |
80 } |
| |
81 |
| |
82 // ============================================================================= |
| |
83 // |
| |
84 String String::ToLowercase() const |
| |
85 { |
| |
86 String newstr = mString; |
| |
87 |
| |
88 for (char & c : newstr) |
| |
89 if (c >= 'A' && c <= 'Z') |
| |
90 c += 'a' - 'A'; |
| |
91 |
| |
92 return newstr; |
| |
93 } |
| |
94 |
| |
95 // ============================================================================= |
| |
96 // |
| |
97 StringList String::Split (char del) const |
| |
98 { |
| |
99 String delimstr; |
| |
100 delimstr += del; |
| |
101 return Split (delimstr); |
| |
102 } |
| |
103 |
| |
104 // ============================================================================= |
| |
105 // |
| |
106 StringList String::Split (String del) const |
| |
107 { |
| |
108 StringList res; |
| |
109 long a = 0; |
| |
110 |
| |
111 // Find all separators and store the text left to them. |
| |
112 for (;;) |
| |
113 { |
| |
114 long b = FirstIndexOf (del, a); |
| |
115 |
| |
116 if (b == -1) |
| |
117 break; |
| |
118 |
| |
119 String sub = Mid (a, b); |
| |
120 |
| |
121 if (sub.Length() > 0) |
| |
122 res.Append (Mid (a, b)); |
| |
123 |
| |
124 a = b + del.Length(); |
| |
125 } |
| |
126 |
| |
127 // Add the string at the right of the last separator |
| |
128 if (a < (int) Length()) |
| |
129 res.Append (Mid (a, Length())); |
| |
130 |
| |
131 return res; |
| |
132 } |
| |
133 |
| |
134 // ============================================================================= |
| |
135 // |
| |
136 void String::Replace (const char* a, const char* b) |
| |
137 { |
| |
138 long pos; |
| |
139 |
| |
140 while ( (pos = FirstIndexOf (a)) != -1) |
| |
141 mString = mString.replace (pos, strlen (a), b); |
| |
142 } |
| |
143 |
| |
144 // ============================================================================= |
| |
145 // |
| |
146 int String::Count (char needle) const |
| |
147 { |
| |
148 int needles = 0; |
| |
149 |
| |
150 for (const char & c : mString) |
| |
151 if (c == needle) |
| |
152 needles++; |
| |
153 |
| |
154 return needles; |
| |
155 } |
| |
156 |
| |
157 // ============================================================================= |
| |
158 // |
| |
159 String String::Mid (long a, long b) const |
| |
160 { |
| |
161 if (b == -1) |
| |
162 b = Length(); |
| |
163 |
| |
164 if (b == a) |
| |
165 return ""; |
| |
166 |
| |
167 if (b < a) |
| |
168 { |
| |
169 // Swap the variables |
| |
170 int c = a; |
| |
171 a = b; |
| |
172 b = c; |
| |
173 } |
| |
174 |
| |
175 char* newstr = new char[b - a + 1]; |
| |
176 strncpy (newstr, mString.c_str() + a, b - a); |
| |
177 newstr[b - a] = '\0'; |
| |
178 |
| |
179 String other (newstr); |
| |
180 delete[] newstr; |
| |
181 return other; |
| |
182 } |
| |
183 |
| |
184 // ============================================================================= |
| |
185 // |
| |
186 int String::WordPosition (int n) const |
| |
187 { |
| |
188 int count = 0; |
| |
189 |
| |
190 for (int i = 0; i < Length(); ++i) |
| |
191 { |
| |
192 if (mString[i] != ' ') |
| |
193 continue; |
| |
194 |
| |
195 if (++count < n) |
| |
196 continue; |
| |
197 |
| |
198 return i; |
| |
199 } |
| |
200 |
| |
201 return -1; |
| |
202 } |
| |
203 |
| |
204 // ============================================================================= |
| |
205 // |
| |
206 int String::FirstIndexOf (const char* c, int a) const |
| |
207 { |
| |
208 for (; a < Length(); a++) |
| |
209 if (mString[a] == c[0] && strncmp (mString.c_str() + a, c, strlen (c)) == 0) |
| |
210 return a; |
| |
211 |
| |
212 return -1; |
| |
213 } |
| |
214 |
| |
215 // ============================================================================= |
| |
216 // |
| |
217 int String::LastIndexOf (const char* c, int a) const |
| |
218 { |
| |
219 if (a == -1 || a >= Length()) |
| |
220 a = Length() - 1; |
| |
221 |
| |
222 for (; a > 0; a--) |
| |
223 if (mString[a] == c[0] && strncmp (mString.c_str() + a, c, strlen (c)) == 0) |
| |
224 return a; |
| |
225 |
| |
226 return -1; |
| |
227 } |
| |
228 |
| |
229 // ============================================================================= |
| |
230 // |
| |
231 void String::Dump() const |
| |
232 { |
| |
233 Print ("`%1`:\n", CString()); |
| |
234 int i = 0; |
| |
235 |
| |
236 for (char u : mString) |
| |
237 Print ("\t%1. [%d2] `%3`\n", i++, u, String (u)); |
| |
238 } |
| |
239 |
| |
240 // ============================================================================= |
| |
241 // |
| |
242 long String::ToLong (bool* ok, int base) const |
| |
243 { |
| |
244 errno = 0; |
| |
245 char* endptr; |
| |
246 long i = strtol (mString.c_str(), &endptr, base); |
| |
247 |
| |
248 if (ok) |
| |
249 *ok = (errno == 0 && *endptr == '\0'); |
| |
250 |
| |
251 return i; |
| |
252 } |
| |
253 |
| |
254 // ============================================================================= |
| |
255 // |
| |
256 float String::ToFloat (bool* ok) const |
| |
257 { |
| |
258 errno = 0; |
| |
259 char* endptr; |
| |
260 float i = strtof (mString.c_str(), &endptr); |
| |
261 |
| |
262 if (ok) |
| |
263 *ok = (errno == 0 && *endptr == '\0'); |
| |
264 |
| |
265 return i; |
| |
266 } |
| |
267 |
| |
268 // ============================================================================= |
| |
269 // |
| |
270 double String::ToDouble (bool* ok) const |
| |
271 { |
| |
272 errno = 0; |
| |
273 char* endptr; |
| |
274 double i = strtod (mString.c_str(), &endptr); |
| |
275 |
| |
276 if (ok) |
| |
277 *ok = (errno == 0 && *endptr == '\0'); |
| |
278 |
| |
279 return i; |
| |
280 } |
| |
281 |
| |
282 // ============================================================================= |
| |
283 // |
| |
284 String String::operator+ (const String& data) const |
| |
285 { |
| |
286 String newString = *this; |
| |
287 newString.Append (data); |
| |
288 return newString; |
| |
289 } |
| |
290 |
| |
291 // ============================================================================= |
| |
292 // |
| |
293 String String::operator+ (const char* data) const |
| |
294 { |
| |
295 String newstr = *this; |
| |
296 newstr.Append (data); |
| |
297 return newstr; |
| |
298 } |
| |
299 |
| |
300 // ============================================================================= |
| |
301 // |
| |
302 bool String::IsNumeric() const |
| |
303 { |
| |
304 bool gotDot = false; |
| |
305 |
| |
306 for (const char & c : mString) |
| |
307 { |
| |
308 // Allow leading hyphen for negatives |
| |
309 if (&c == &mString[0] && c == '-') |
| |
310 continue; |
| |
311 |
| |
312 // Check for decimal point |
| |
313 if (!gotDot && c == '.') |
| |
314 { |
| |
315 gotDot = true; |
| |
316 continue; |
| |
317 } |
| |
318 |
| |
319 if (c >= '0' && c <= '9') |
| |
320 continue; // Digit |
| |
321 |
| |
322 // If the above cases didn't catch this character, it was |
| |
323 // illegal and this is therefore not a number. |
| |
324 return false; |
| |
325 } |
| |
326 |
| |
327 return true; |
| |
328 } |
| |
329 |
| |
330 // ============================================================================= |
| |
331 // |
| |
332 bool String::EndsWith (const String& other) |
| |
333 { |
| |
334 if (Length() < other.Length()) |
| |
335 return false; |
| |
336 |
| |
337 const int ofs = Length() - other.Length(); |
| |
338 return strncmp (CString() + ofs, other.CString(), other.Length()) == 0; |
| |
339 } |
| |
340 |
| |
341 // ============================================================================= |
| |
342 // |
| |
343 bool String::StartsWith (const String& other) |
| |
344 { |
| |
345 if (Length() < other.Length()) |
| |
346 return false; |
| |
347 |
| |
348 return strncmp (CString(), other.CString(), other.Length()) == 0; |
| |
349 } |
| |
350 |
| |
351 // ============================================================================= |
| |
352 // |
| |
353 void String::SPrintf (const char* fmtstr, ...) |
| |
354 { |
| |
355 char* buf; |
| |
356 int bufsize = 256; |
| |
357 va_list va; |
| |
358 va_start (va, fmtstr); |
| |
359 |
| |
360 do |
| |
361 buf = new char[bufsize]; |
| |
362 while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize); |
| |
363 |
| |
364 va_end (va); |
| |
365 mString = buf; |
| |
366 delete[] buf; |
| |
367 } |
| |
368 |
| |
369 // ============================================================================= |
| |
370 // |
| |
371 String StringList::Join (const String& delim) |
| |
372 { |
| |
373 String result; |
| |
374 |
| |
375 for (const String& it : GetDeque()) |
| |
376 { |
| |
377 if (result.IsEmpty() == false) |
| |
378 result += delim; |
| |
379 |
| |
380 result += it; |
| |
381 } |
| |
382 |
| |
383 return result; |
| |
384 } |
| |
385 |
| |
386 // ============================================================================= |
| |
387 // |
| |
388 bool String::MaskAgainst (const String& pattern) const |
| |
389 { |
| |
390 // Elevate to uppercase for case-insensitive matching |
| |
391 String pattern_upper = pattern.ToUppercase(); |
| |
392 String this_upper = ToUppercase(); |
| |
393 const char* maskstring = pattern_upper.CString(); |
| |
394 const char* mptr = &maskstring[0]; |
| |
395 |
| |
396 for (const char* sptr = this_upper.CString(); *sptr != '\0'; sptr++) |
| |
397 { |
| |
398 if (*mptr == '?') |
| |
399 { |
| |
400 if (*(sptr + 1) == '\0') |
| |
401 { |
| |
402 // ? demands that there's a character here and there wasn't. |
| |
403 // Therefore, mask matching fails |
| |
404 return false; |
| |
405 } |
| |
406 } |
| |
407 elif (*mptr == '*') |
| |
408 { |
| |
409 char end = *(++mptr); |
| |
410 |
| |
411 // If '*' is the final character of the message, all of the remaining |
| |
412 // string matches against the '*'. We don't need to bother checking |
| |
413 // the string any further. |
| |
414 if (end == '\0') |
| |
415 return true; |
| |
416 |
| |
417 // Skip to the end character |
| |
418 while (*sptr != end && *sptr != '\0') |
| |
419 sptr++; |
| |
420 |
| |
421 // String ended while the mask still had stuff |
| |
422 if (*sptr == '\0') |
| |
423 return false; |
| |
424 } |
| |
425 elif (*sptr != *mptr) |
| |
426 return false; |
| |
427 |
| |
428 mptr++; |
| |
429 } |
| |
430 |
| |
431 return true; |
| |
432 } |
| |
433 |
| |
434 // ============================================================================= |
| |
435 // |
| |
436 String String::FromNumber (int a) |
| |
437 { |
| |
438 char buf[32]; |
| |
439 sprintf (buf, "%d", a); |
| |
440 return String (buf); |
| |
441 } |
| |
442 |
| |
443 // ============================================================================= |
| |
444 // |
| |
445 String String::FromNumber (long a) |
| |
446 { |
| |
447 char buf[32]; |
| |
448 sprintf (buf, "%ld", a); |
| |
449 return String (buf); |
| |
450 } |