Sun, 02 Feb 2014 17:06:39 +0200
- reformatting
88 | 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 | } |