| 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 "string.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 } |
|