1 /* 2 * Copyright 2001-2014 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stefano Ceccherini, burton666@libero.it 7 * Axel Dörfler, axeld@pinc-software.de 8 * Marc Flerackers, mflerackers@androme.be 9 * Julun, host.haiku@gmx.de 10 * Michael Lotz, mmlr@mlotz.ch 11 * Oliver Tappe, openbeos@hirschkaefer.de 12 */ 13 14 15 /*! String class supporting common string operations. */ 16 17 #include <String.h> 18 19 #include <ctype.h> 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <strings.h> 24 25 #include <Debug.h> 26 #include <StringList.h> 27 28 #include <StringPrivate.h> 29 #include <utf8_functions.h> 30 31 32 // define proper names for case-option of _DoReplace() 33 #define KEEP_CASE false 34 #define IGNORE_CASE true 35 36 // define proper names for count-option of _DoReplace() 37 #define REPLACE_ALL 0x7FFFFFFF 38 39 40 static const uint32 kPrivateDataOffset = BString::Private::kPrivateDataOffset; 41 42 const char* B_EMPTY_STRING = ""; 43 44 45 // helper function, returns minimum of two given values (but clamps to 0): 46 static inline int32 47 min_clamp0(int32 num1, int32 num2) 48 { 49 if (num1 < num2) 50 return num1 > 0 ? num1 : 0; 51 52 return num2 > 0 ? num2 : 0; 53 } 54 55 56 //! Returns length of given string (but clamps to given maximum). 57 static inline int32 58 strlen_clamp(const char* string, int32 max) 59 { 60 // this should yield 0 for max<0: 61 return max <= 0 ? 0 : strnlen(string, max); 62 } 63 64 65 //! Helper function for strlen() that can handle NULL strings. 66 static inline size_t 67 string_length(const char* string) 68 { 69 return string != NULL ? strlen(string) : 0; 70 } 71 72 73 //! helper function, massages given pointer into a legal c-string: 74 static inline const char* 75 safestr(const char* string) 76 { 77 return string != NULL ? string : ""; 78 } 79 80 81 // #pragma mark - PosVect 82 83 84 class BString::PosVect { 85 public: 86 PosVect() 87 : 88 fSize(0), 89 fBufferSize(20), 90 fBuffer(NULL) 91 { 92 } 93 94 ~PosVect() 95 { 96 free(fBuffer); 97 } 98 99 bool Add(int32 pos) 100 { 101 if (fBuffer == NULL || fSize == fBufferSize) { 102 if (fBuffer != NULL) 103 fBufferSize *= 2; 104 105 int32* newBuffer = NULL; 106 newBuffer = (int32*)realloc(fBuffer, fBufferSize * sizeof(int32)); 107 if (newBuffer == NULL) 108 return false; 109 110 fBuffer = newBuffer; 111 } 112 113 fBuffer[fSize++] = pos; 114 115 return true; 116 } 117 118 inline int32 ItemAt(int32 index) const 119 { 120 return fBuffer[index]; 121 } 122 123 inline int32 CountItems() const 124 { 125 return fSize; 126 } 127 128 private: 129 int32 fSize; 130 int32 fBufferSize; 131 int32* fBuffer; 132 }; 133 134 135 // #pragma mark - BString 136 137 138 inline int32& 139 BString::_ReferenceCount() 140 { 141 return Private::DataRefCount(fPrivateData); 142 } 143 144 145 inline const int32& 146 BString::_ReferenceCount() const 147 { 148 return Private::DataRefCount(fPrivateData); 149 } 150 151 152 inline bool 153 BString::_IsShareable() const 154 { 155 return fPrivateData != NULL && _ReferenceCount() >= 0; 156 } 157 158 159 BString::BString() 160 : 161 fPrivateData(NULL) 162 { 163 _Init("", 0); 164 } 165 166 167 BString::BString(const char* string) 168 : 169 fPrivateData(NULL) 170 { 171 _Init(string, strlen(safestr(string))); 172 } 173 174 175 BString::BString(const BString& string) 176 : 177 fPrivateData(NULL) 178 { 179 // check if source is sharable - if so, share else clone 180 if (string._IsShareable()) { 181 fPrivateData = string.fPrivateData; 182 atomic_add(&_ReferenceCount(), 1); 183 // string cannot go away right now 184 } else 185 _Init(string.String(), string.Length()); 186 } 187 188 189 BString::BString(const char* string, int32 maxLength) 190 : fPrivateData(NULL) 191 { 192 _Init(string, strlen_clamp(safestr(string), maxLength)); 193 } 194 195 196 BString::~BString() 197 { 198 if (!_IsShareable() || atomic_add(&_ReferenceCount(), -1) == 1) 199 _FreePrivateData(); 200 } 201 202 203 // #pragma mark - Access 204 205 206 int32 207 BString::CountChars() const 208 { 209 return UTF8CountChars(fPrivateData, Length()); 210 } 211 212 213 int32 214 BString::CountBytes(int32 fromCharOffset, int32 charCount) const 215 { 216 return UTF8CountBytes( 217 fPrivateData + UTF8CountBytes(fPrivateData, fromCharOffset), charCount); 218 } 219 220 221 /*static*/ uint32 222 BString::HashValue(const char* string) 223 { 224 // from the Dragon Book: a slightly modified hashpjw() 225 uint32 h = 0; 226 if (string != NULL) { 227 for (; *string; string++) { 228 uint32 g = h & 0xf0000000; 229 if (g) 230 h ^= g >> 24; 231 h = (h << 4) + *string; 232 } 233 } 234 return h; 235 } 236 237 238 // #pragma mark - Assignment 239 240 241 BString& 242 BString::operator=(const BString& string) 243 { 244 return SetTo(string); 245 } 246 247 248 BString& 249 BString::operator=(const char* string) 250 { 251 if (!string) 252 string = ""; 253 if (string != String()) 254 SetTo(string, strlen(string)); 255 return *this; 256 } 257 258 259 BString& 260 BString::operator=(char c) 261 { 262 return SetTo(c, 1); 263 } 264 265 266 BString& 267 BString::SetTo(const char* string, int32 maxLength) 268 { 269 if (maxLength < 0) 270 maxLength = INT32_MAX; 271 272 maxLength = strlen_clamp(safestr(string), maxLength); 273 274 if (_MakeWritable(maxLength, false) == B_OK) 275 memcpy(fPrivateData, string, maxLength); 276 277 return *this; 278 } 279 280 281 BString& 282 BString::SetTo(const BString& string) 283 { 284 // we share the information already 285 if (fPrivateData == string.fPrivateData) 286 return *this; 287 288 bool freeData = true; 289 290 if (_IsShareable() && atomic_add(&_ReferenceCount(), -1) > 1) { 291 // there is still someone who shares our data 292 freeData = false; 293 } 294 295 if (freeData) 296 _FreePrivateData(); 297 298 // if source is sharable share, otherwise clone 299 if (string._IsShareable()) { 300 fPrivateData = string.fPrivateData; 301 atomic_add(&_ReferenceCount(), 1); 302 // the string cannot go away right now 303 } else 304 _Init(string.String(), string.Length()); 305 306 return *this; 307 } 308 309 310 BString& 311 BString::Adopt(BString& from) 312 { 313 SetTo(from); 314 from.SetTo(""); 315 316 return *this; 317 } 318 319 320 BString& 321 BString::SetTo(const BString& string, int32 maxLength) 322 { 323 if (maxLength < 0) 324 maxLength = INT32_MAX; 325 if (fPrivateData != string.fPrivateData 326 // make sure we reassing in case length is different 327 || (fPrivateData == string.fPrivateData && Length() > maxLength)) { 328 maxLength = min_clamp0(maxLength, string.Length()); 329 if (_MakeWritable(maxLength, false) == B_OK) 330 memcpy(fPrivateData, string.String(), maxLength); 331 } 332 return *this; 333 } 334 335 336 BString& 337 BString::Adopt(BString& from, int32 maxLength) 338 { 339 SetTo(from, maxLength); 340 from.SetTo(""); 341 342 return *this; 343 } 344 345 346 BString& 347 BString::SetTo(char c, int32 count) 348 { 349 if (count < 0) 350 count = 0; 351 352 if (_MakeWritable(count, false) == B_OK) 353 memset(fPrivateData, c, count); 354 355 return *this; 356 } 357 358 359 BString& 360 BString::SetToChars(const char* string, int32 charCount) 361 { 362 return SetTo(string, UTF8CountBytes(string, charCount)); 363 } 364 365 366 BString& 367 BString::SetToChars(const BString& string, int32 charCount) 368 { 369 return SetTo(string, UTF8CountBytes(string.String(), charCount)); 370 } 371 372 373 BString& 374 BString::AdoptChars(BString& string, int32 charCount) 375 { 376 return Adopt(string, UTF8CountBytes(string.String(), charCount)); 377 } 378 379 380 BString& 381 BString::SetToFormat(const char* format, ...) 382 { 383 va_list args; 384 va_start(args, format); 385 SetToFormatVarArgs(format, args); 386 va_end(args); 387 388 return *this; 389 } 390 391 392 BString& 393 BString::SetToFormatVarArgs(const char* format, va_list args) 394 { 395 // Use a small on-stack buffer to save a second vsnprintf() call for most 396 // use cases. 397 int32 bufferSize = 1024; 398 char buffer[bufferSize]; 399 400 va_list clonedArgs; 401 #if __GNUC__ == 2 402 __va_copy(clonedArgs, args); 403 #else 404 va_copy(clonedArgs, args); 405 #endif 406 int32 bytes = vsnprintf(buffer, bufferSize, format, clonedArgs); 407 va_end(clonedArgs); 408 409 if (bytes < 0) 410 return Truncate(0); 411 412 if (bytes < bufferSize) { 413 SetTo(buffer); 414 return *this; 415 } 416 417 bytes = vsnprintf(LockBuffer(bytes), bytes + 1, format, args); 418 if (bytes < 0) 419 bytes = 0; 420 421 UnlockBuffer(bytes); 422 return *this; 423 } 424 425 426 int 427 BString::ScanWithFormat(const char* format, ...) 428 { 429 va_list args; 430 va_start(args, format); 431 int result = ScanWithFormatVarArgs(format, args); 432 va_end(args); 433 434 return result; 435 } 436 437 438 int 439 BString::ScanWithFormatVarArgs(const char* format, va_list args) 440 { 441 return vsscanf(fPrivateData, format, args); 442 } 443 444 445 // #pragma mark - Substring copying 446 447 448 BString& 449 BString::CopyInto(BString& into, int32 fromOffset, int32 length) const 450 { 451 if (this != &into) 452 into.SetTo(fPrivateData + fromOffset, length); 453 return into; 454 } 455 456 457 void 458 BString::CopyInto(char* into, int32 fromOffset, int32 length) const 459 { 460 if (into) { 461 length = min_clamp0(length, Length() - fromOffset); 462 memcpy(into, fPrivateData + fromOffset, length); 463 } 464 } 465 466 467 BString& 468 BString::CopyCharsInto(BString& into, int32 fromCharOffset, 469 int32 charCount) const 470 { 471 int32 fromOffset = UTF8CountBytes(fPrivateData, fromCharOffset); 472 int32 length = UTF8CountBytes(fPrivateData + fromOffset, charCount); 473 return CopyInto(into, fromOffset, length); 474 } 475 476 477 bool 478 BString::CopyCharsInto(char* into, int32* intoLength, int32 fromCharOffset, 479 int32 charCount) const 480 { 481 if (into == NULL) 482 return false; 483 484 int32 fromOffset = UTF8CountBytes(fPrivateData, fromCharOffset); 485 int32 length = UTF8CountBytes(fPrivateData + fromOffset, charCount); 486 length = min_clamp0(length, Length() - fromOffset); 487 488 if (intoLength != NULL) { 489 if (*intoLength < length) 490 return false; 491 *intoLength = length; 492 } 493 494 memcpy(into, fPrivateData + fromOffset, length); 495 return true; 496 } 497 498 499 bool 500 BString::Split(const char* separator, bool noEmptyStrings, 501 BStringList& _list) const 502 { 503 int32 separatorLength = strlen(separator); 504 int32 length = Length(); 505 if (separatorLength == 0 || length == 0 || separatorLength > length) { 506 if (length == 0 && noEmptyStrings) 507 return true; 508 return _list.Add(*this); 509 } 510 511 int32 index = 0; 512 for (;;) { 513 int32 endIndex = index < length ? FindFirst(separator, index) : length; 514 if (endIndex < 0) 515 endIndex = length; 516 517 if (endIndex > index || !noEmptyStrings) { 518 BString toAppend(String() + index, endIndex - index); 519 if (toAppend.Length() != endIndex - index 520 || !_list.Add(toAppend)) { 521 return false; 522 } 523 } 524 525 if (endIndex == length) 526 break; 527 528 index = endIndex + 1; 529 } 530 531 return true; 532 } 533 534 535 // #pragma mark - Appending 536 537 538 BString& 539 BString::operator+=(const char* string) 540 { 541 if (string) { 542 int32 length = strlen(string); 543 if (length > 0) 544 _DoAppend(string, length); 545 } 546 return *this; 547 } 548 549 550 BString& 551 BString::operator+=(char c) 552 { 553 _DoAppend(&c, 1); 554 return *this; 555 } 556 557 558 BString& 559 BString::Append(const BString& string, int32 length) 560 { 561 if (&string != this) { 562 length = min_clamp0(length, string.Length()); 563 if (length > 0) 564 _DoAppend(string.fPrivateData, length); 565 } 566 return *this; 567 } 568 569 570 BString& 571 BString::Append(const char* string, int32 length) 572 { 573 if (string) { 574 length = strlen_clamp(string, length); 575 if (length > 0) 576 _DoAppend(string, length); 577 } 578 return *this; 579 } 580 581 582 BString& 583 BString::Append(char c, int32 count) 584 { 585 int32 oldLength = Length(); 586 if (count > 0 && _DoAppend("", count)) 587 memset(fPrivateData + oldLength, c, count); 588 return *this; 589 } 590 591 592 BString& 593 BString::AppendChars(const BString& string, int32 charCount) 594 { 595 return Append(string, UTF8CountBytes(string.String(), charCount)); 596 } 597 598 599 BString& 600 BString::AppendChars(const char* string, int32 charCount) 601 { 602 return Append(string, UTF8CountBytes(string, charCount)); 603 } 604 605 606 // #pragma mark - Prepending 607 608 609 BString& 610 BString::Prepend(const char* string) 611 { 612 if (string) 613 _DoPrepend(string, strlen(string)); 614 return *this; 615 } 616 617 618 BString& 619 BString::Prepend(const BString& string) 620 { 621 if (&string != this) 622 _DoPrepend(string.String(), string.Length()); 623 return *this; 624 } 625 626 627 BString& 628 BString::Prepend(const char* string, int32 length) 629 { 630 if (string) 631 _DoPrepend(string, strlen_clamp(string, length)); 632 return *this; 633 } 634 635 636 BString& 637 BString::Prepend(const BString& string, int32 length) 638 { 639 if (&string != this) 640 _DoPrepend(string.fPrivateData, min_clamp0(length, string.Length())); 641 return *this; 642 } 643 644 645 BString& 646 BString::Prepend(char c, int32 count) 647 { 648 if (count > 0 && _DoPrepend("", count)) 649 memset(fPrivateData, c, count); 650 return *this; 651 } 652 653 654 BString& 655 BString::PrependChars(const char* string, int32 charCount) 656 { 657 return Prepend(string, UTF8CountBytes(string, charCount)); 658 } 659 660 661 BString& 662 BString::PrependChars(const BString& string, int32 charCount) 663 { 664 return Prepend(string, UTF8CountBytes(string.String(), charCount)); 665 } 666 667 668 // #pragma mark - Inserting 669 670 671 BString& 672 BString::Insert(const char* string, int32 position) 673 { 674 if (string != NULL && position <= Length()) { 675 int32 len = int32(strlen(string)); 676 if (position < 0) { 677 int32 skipLen = min_clamp0(-1 * position, len); 678 string += skipLen; 679 len -= skipLen; 680 position = 0; 681 } else { 682 position = min_clamp0(position, Length()); 683 } 684 _DoInsert(string, position, len); 685 } 686 return *this; 687 } 688 689 690 BString& 691 BString::Insert(const char* string, int32 length, int32 position) 692 { 693 if (string != NULL && position <= Length()) { 694 int32 len = strlen_clamp(string, length); 695 if (position < 0) { 696 int32 skipLen = min_clamp0(-1 * position, len); 697 string += skipLen; 698 len -= skipLen; 699 position = 0; 700 } else { 701 position = min_clamp0(position, Length()); 702 } 703 _DoInsert(string, position, len); 704 } 705 return *this; 706 } 707 708 709 BString& 710 BString::Insert(const char* string, int32 fromOffset, int32 length, 711 int32 position) 712 { 713 if (string) 714 Insert(string + fromOffset, length, position); 715 return *this; 716 } 717 718 719 BString& 720 BString::Insert(const BString& string, int32 position) 721 { 722 if (&string != this && string.Length() > 0) 723 Insert(string.fPrivateData, position); 724 return *this; 725 } 726 727 728 BString& 729 BString::Insert(const BString& string, int32 length, int32 position) 730 { 731 if (&string != this && string.Length() > 0) 732 Insert(string.String(), length, position); 733 return *this; 734 } 735 736 737 BString& 738 BString::Insert(const BString& string, int32 fromOffset, int32 length, 739 int32 position) 740 { 741 if (&string != this && string.Length() > 0) 742 Insert(string.String() + fromOffset, length, position); 743 return *this; 744 } 745 746 747 BString& 748 BString::Insert(char c, int32 count, int32 position) 749 { 750 if (position < 0) { 751 count = MAX(count + position, 0); 752 position = 0; 753 } else 754 position = min_clamp0(position, Length()); 755 756 if (count > 0 && _DoInsert("", position, count)) 757 memset(fPrivateData + position, c, count); 758 759 return *this; 760 } 761 762 763 BString& 764 BString::InsertChars(const char* string, int32 charPosition) 765 { 766 return Insert(string, UTF8CountBytes(fPrivateData, charPosition)); 767 } 768 769 770 BString& 771 BString::InsertChars(const char* string, int32 charCount, int32 charPosition) 772 { 773 return Insert(string, UTF8CountBytes(string, charCount), 774 UTF8CountBytes(fPrivateData, charPosition)); 775 } 776 777 778 BString& 779 BString::InsertChars(const char* string, int32 fromCharOffset, 780 int32 charCount, int32 charPosition) 781 { 782 int32 fromOffset = UTF8CountBytes(string, fromCharOffset); 783 return Insert(string, fromOffset, 784 UTF8CountBytes(string + fromOffset, charCount), 785 UTF8CountBytes(fPrivateData, charPosition)); 786 } 787 788 789 BString& 790 BString::InsertChars(const BString& string, int32 charPosition) 791 { 792 return Insert(string, UTF8CountBytes(fPrivateData, charPosition)); 793 } 794 795 796 BString& 797 BString::InsertChars(const BString& string, int32 charCount, int32 charPosition) 798 { 799 return Insert(string, UTF8CountBytes(string.String(), charCount), 800 UTF8CountBytes(fPrivateData, charPosition)); 801 } 802 803 804 BString& 805 BString::InsertChars(const BString& string, int32 fromCharOffset, 806 int32 charCount, int32 charPosition) 807 { 808 int32 fromOffset = UTF8CountBytes(string.String(), fromCharOffset); 809 return Insert(string, fromOffset, 810 UTF8CountBytes(string.String() + fromOffset, charCount), 811 UTF8CountBytes(fPrivateData, charPosition)); 812 } 813 814 815 // #pragma mark - Removing 816 817 818 BString& 819 BString::Truncate(int32 newLength, bool lazy) 820 { 821 if (newLength < 0) 822 newLength = 0; 823 824 if (newLength < Length()) { 825 // ignore lazy, since we might detach 826 _MakeWritable(newLength, true); 827 } 828 829 return *this; 830 } 831 832 833 BString& 834 BString::TruncateChars(int32 newCharCount, bool lazy) 835 { 836 return Truncate(UTF8CountBytes(fPrivateData, newCharCount)); 837 } 838 839 840 BString& 841 BString::Remove(int32 from, int32 length) 842 { 843 if (length > 0 && from < Length()) 844 _ShrinkAtBy(from, min_clamp0(length, (Length() - from))); 845 return *this; 846 } 847 848 849 BString& 850 BString::RemoveChars(int32 fromCharOffset, int32 charCount) 851 { 852 int32 fromOffset = UTF8CountBytes(fPrivateData, fromCharOffset); 853 return Remove(fromOffset, 854 UTF8CountBytes(fPrivateData + fromOffset, charCount)); 855 } 856 857 858 BString& 859 BString::RemoveFirst(const BString& string) 860 { 861 if (string.Length() > 0) { 862 int32 pos = _ShortFindAfter(string.String(), string.Length()); 863 if (pos >= 0) 864 _ShrinkAtBy(pos, string.Length()); 865 } 866 return *this; 867 } 868 869 870 BString& 871 BString::RemoveLast(const BString& string) 872 { 873 int32 pos = _FindBefore(string.String(), Length(), string.Length()); 874 if (pos >= 0) 875 _ShrinkAtBy(pos, string.Length()); 876 877 return *this; 878 } 879 880 881 BString& 882 BString::RemoveAll(const BString& string) 883 { 884 if (string.Length() == 0 || Length() == 0 || FindFirst(string) < 0) 885 return *this; 886 887 if (_MakeWritable() != B_OK) 888 return *this; 889 890 return _DoReplace(string.String(), "", REPLACE_ALL, 0, KEEP_CASE); 891 } 892 893 894 BString& 895 BString::RemoveFirst(const char* string) 896 { 897 int32 length = string ? strlen(string) : 0; 898 if (length > 0) { 899 int32 pos = _ShortFindAfter(string, length); 900 if (pos >= 0) 901 _ShrinkAtBy(pos, length); 902 } 903 return *this; 904 } 905 906 907 BString& 908 BString::RemoveLast(const char* string) 909 { 910 int32 length = string ? strlen(string) : 0; 911 if (length > 0) { 912 int32 pos = _FindBefore(string, Length(), length); 913 if (pos >= 0) 914 _ShrinkAtBy(pos, length); 915 } 916 return *this; 917 } 918 919 920 BString& 921 BString::RemoveAll(const char* string) 922 { 923 if (!string || Length() == 0 || FindFirst(string) < 0) 924 return *this; 925 926 if (_MakeWritable() != B_OK) 927 return *this; 928 929 return _DoReplace(string, "", REPLACE_ALL, 0, KEEP_CASE); 930 } 931 932 933 BString& 934 BString::RemoveSet(const char* setOfBytesToRemove) 935 { 936 return ReplaceSet(setOfBytesToRemove, ""); 937 } 938 939 940 BString& 941 BString::RemoveCharsSet(const char* setOfCharsToRemove) 942 { 943 return ReplaceCharsSet(setOfCharsToRemove, ""); 944 } 945 946 947 BString& 948 BString::MoveInto(BString& into, int32 from, int32 length) 949 { 950 if (length) { 951 CopyInto(into, from, length); 952 Remove(from, length); 953 } 954 return into; 955 } 956 957 958 void 959 BString::MoveInto(char* into, int32 from, int32 length) 960 { 961 if (into) { 962 CopyInto(into, from, length); 963 Remove(from, length); 964 } 965 } 966 967 968 BString& 969 BString::MoveCharsInto(BString& into, int32 fromCharOffset, int32 charCount) 970 { 971 if (charCount > 0) { 972 CopyCharsInto(into, fromCharOffset, charCount); 973 RemoveChars(fromCharOffset, charCount); 974 } 975 976 return into; 977 } 978 979 980 bool 981 BString::MoveCharsInto(char* into, int32* intoLength, int32 fromCharOffset, 982 int32 charCount) 983 { 984 if (!CopyCharsInto(into, intoLength, fromCharOffset, charCount)) 985 return false; 986 987 RemoveChars(fromCharOffset, charCount); 988 return true; 989 } 990 991 992 // #pragma mark - Compare functions 993 994 995 bool 996 BString::operator<(const char* string) const 997 { 998 return strcmp(String(), safestr(string)) < 0; 999 } 1000 1001 1002 bool 1003 BString::operator<=(const char* string) const 1004 { 1005 return strcmp(String(), safestr(string)) <= 0; 1006 } 1007 1008 1009 bool 1010 BString::operator==(const char* string) const 1011 { 1012 return strcmp(String(), safestr(string)) == 0; 1013 } 1014 1015 1016 bool 1017 BString::operator>=(const char* string) const 1018 { 1019 return strcmp(String(), safestr(string)) >= 0; 1020 } 1021 1022 1023 bool 1024 BString::operator>(const char* string) const 1025 { 1026 return strcmp(String(), safestr(string)) > 0; 1027 } 1028 1029 1030 // #pragma mark - strcmp()-style compare functions 1031 1032 1033 int 1034 BString::Compare(const BString& string) const 1035 { 1036 return strcmp(String(), string.String()); 1037 } 1038 1039 1040 int 1041 BString::Compare(const char* string) const 1042 { 1043 return strcmp(String(), safestr(string)); 1044 } 1045 1046 1047 int 1048 BString::Compare(const BString& string, int32 length) const 1049 { 1050 return strncmp(String(), string.String(), length); 1051 } 1052 1053 1054 int 1055 BString::Compare(const char* string, int32 length) const 1056 { 1057 return strncmp(String(), safestr(string), length); 1058 } 1059 1060 1061 int 1062 BString::CompareAt(size_t offset, const BString& string, int32 length) const 1063 { 1064 return strncmp(String() + offset, string.String(), length); 1065 } 1066 1067 1068 int 1069 BString::CompareChars(const BString& string, int32 charCount) const 1070 { 1071 return Compare(string, UTF8CountBytes(fPrivateData, charCount)); 1072 } 1073 1074 1075 int 1076 BString::CompareChars(const char* string, int32 charCount) const 1077 { 1078 return Compare(string, UTF8CountBytes(fPrivateData, charCount)); 1079 } 1080 1081 1082 int 1083 BString::ICompare(const BString& string) const 1084 { 1085 return strcasecmp(String(), string.String()); 1086 } 1087 1088 1089 int 1090 BString::ICompare(const char* string) const 1091 { 1092 return strcasecmp(String(), safestr(string)); 1093 } 1094 1095 1096 int 1097 BString::ICompare(const BString& string, int32 length) const 1098 { 1099 return strncasecmp(String(), string.String(), length); 1100 } 1101 1102 1103 int 1104 BString::ICompare(const char* string, int32 length) const 1105 { 1106 return strncasecmp(String(), safestr(string), length); 1107 } 1108 1109 1110 // #pragma mark - Searching 1111 1112 1113 int32 1114 BString::FindFirst(const BString& string) const 1115 { 1116 return _ShortFindAfter(string.String(), string.Length()); 1117 } 1118 1119 1120 int32 1121 BString::FindFirst(const char* string) const 1122 { 1123 if (string == NULL) 1124 return B_BAD_VALUE; 1125 1126 return _ShortFindAfter(string, strlen(string)); 1127 } 1128 1129 1130 int32 1131 BString::FindFirst(const BString& string, int32 fromOffset) const 1132 { 1133 if (fromOffset < 0) 1134 return B_ERROR; 1135 1136 return _FindAfter(string.String(), min_clamp0(fromOffset, Length()), 1137 string.Length()); 1138 } 1139 1140 1141 int32 1142 BString::FindFirst(const char* string, int32 fromOffset) const 1143 { 1144 if (string == NULL) 1145 return B_BAD_VALUE; 1146 1147 if (fromOffset < 0) 1148 return B_ERROR; 1149 1150 return _FindAfter(string, min_clamp0(fromOffset, Length()), 1151 strlen(safestr(string))); 1152 } 1153 1154 1155 int32 1156 BString::FindFirst(char c) const 1157 { 1158 const char* start = String(); 1159 const char* end = String() + Length(); 1160 1161 // Scans the string until we found the 1162 // character, or we reach the string's start 1163 while (start != end && *start != c) { 1164 start++; 1165 } 1166 1167 if (start == end) 1168 return B_ERROR; 1169 1170 return start - String(); 1171 } 1172 1173 1174 int32 1175 BString::FindFirst(char c, int32 fromOffset) const 1176 { 1177 if (fromOffset < 0) 1178 return B_ERROR; 1179 1180 const char* start = String() + min_clamp0(fromOffset, Length()); 1181 const char* end = String() + Length(); 1182 1183 // Scans the string until we found the 1184 // character, or we reach the string's start 1185 while (start < end && *start != c) { 1186 start++; 1187 } 1188 1189 if (start >= end) 1190 return B_ERROR; 1191 1192 return start - String(); 1193 } 1194 1195 1196 int32 1197 BString::FindFirstChars(const BString& string, int32 fromCharOffset) const 1198 { 1199 return FindFirst(string, UTF8CountBytes(fPrivateData, fromCharOffset)); 1200 } 1201 1202 1203 int32 1204 BString::FindFirstChars(const char* string, int32 fromCharOffset) const 1205 { 1206 return FindFirst(string, UTF8CountBytes(fPrivateData, fromCharOffset)); 1207 } 1208 1209 1210 int32 1211 BString::FindLast(const BString& string) const 1212 { 1213 return _FindBefore(string.String(), Length(), string.Length()); 1214 } 1215 1216 1217 int32 1218 BString::FindLast(const char* string) const 1219 { 1220 if (string == NULL) 1221 return B_BAD_VALUE; 1222 1223 return _FindBefore(string, Length(), strlen(safestr(string))); 1224 } 1225 1226 1227 int32 1228 BString::FindLast(const BString& string, int32 beforeOffset) const 1229 { 1230 if (beforeOffset < 0) 1231 return B_ERROR; 1232 1233 return _FindBefore(string.String(), min_clamp0(beforeOffset, Length()), 1234 string.Length()); 1235 } 1236 1237 1238 int32 1239 BString::FindLast(const char* string, int32 beforeOffset) const 1240 { 1241 if (string == NULL) 1242 return B_BAD_VALUE; 1243 1244 if (beforeOffset < 0) 1245 return B_ERROR; 1246 1247 return _FindBefore(string, min_clamp0(beforeOffset, Length()), 1248 strlen(safestr(string))); 1249 } 1250 1251 1252 int32 1253 BString::FindLast(char c) const 1254 { 1255 const char* const start = String(); 1256 const char* end = String() + Length() - 1; 1257 1258 // Scans the string backwards until we found 1259 // the character, or we reach the string's start 1260 while (end >= start && *end != c) { 1261 end--; 1262 } 1263 1264 if (end < start) 1265 return B_ERROR; 1266 1267 return end - start; 1268 } 1269 1270 1271 int32 1272 BString::FindLast(char c, int32 beforeOffset) const 1273 { 1274 if (beforeOffset < 0) 1275 return B_ERROR; 1276 1277 const char* const start = String(); 1278 const char* end = String() + min_clamp0(beforeOffset + 1, Length()) - 1; 1279 1280 // Scans the string backwards until we found 1281 // the character, or we reach the string's start 1282 while (end >= start && *end != c) { 1283 end--; 1284 } 1285 1286 if (end < start) 1287 return B_ERROR; 1288 1289 return end - start; 1290 } 1291 1292 1293 int32 1294 BString::FindLastChars(const BString& string, int32 beforeCharOffset) const 1295 { 1296 return FindLast(string, UTF8CountBytes(fPrivateData, beforeCharOffset)); 1297 } 1298 1299 1300 int32 1301 BString::FindLastChars(const char* string, int32 beforeCharOffset) const 1302 { 1303 return FindLast(string, UTF8CountBytes(fPrivateData, beforeCharOffset)); 1304 } 1305 1306 1307 int32 1308 BString::IFindFirst(const BString& string) const 1309 { 1310 return _IFindAfter(string.String(), 0, string.Length()); 1311 } 1312 1313 1314 int32 1315 BString::IFindFirst(const char* string) const 1316 { 1317 if (string == NULL) 1318 return B_BAD_VALUE; 1319 1320 return _IFindAfter(string, 0, strlen(safestr(string))); 1321 } 1322 1323 1324 int32 1325 BString::IFindFirst(const BString& string, int32 fromOffset) const 1326 { 1327 if (fromOffset < 0) 1328 return B_ERROR; 1329 1330 return _IFindAfter(string.String(), min_clamp0(fromOffset, Length()), 1331 string.Length()); 1332 } 1333 1334 1335 int32 1336 BString::IFindFirst(const char* string, int32 fromOffset) const 1337 { 1338 if (string == NULL) 1339 return B_BAD_VALUE; 1340 1341 if (fromOffset < 0) 1342 return B_ERROR; 1343 1344 return _IFindAfter(string, min_clamp0(fromOffset,Length()), 1345 strlen(safestr(string))); 1346 } 1347 1348 1349 int32 1350 BString::IFindLast(const BString& string) const 1351 { 1352 return _IFindBefore(string.String(), Length(), string.Length()); 1353 } 1354 1355 1356 int32 1357 BString::IFindLast(const char* string) const 1358 { 1359 if (string == NULL) 1360 return B_BAD_VALUE; 1361 1362 return _IFindBefore(string, Length(), strlen(safestr(string))); 1363 } 1364 1365 1366 int32 1367 BString::IFindLast(const BString& string, int32 beforeOffset) const 1368 { 1369 if (beforeOffset < 0) 1370 return B_ERROR; 1371 1372 return _IFindBefore(string.String(), min_clamp0(beforeOffset, Length()), 1373 string.Length()); 1374 } 1375 1376 1377 int32 1378 BString::IFindLast(const char* string, int32 beforeOffset) const 1379 { 1380 if (string == NULL) 1381 return B_BAD_VALUE; 1382 1383 if (beforeOffset < 0) 1384 return B_ERROR; 1385 1386 return _IFindBefore(string, min_clamp0(beforeOffset, Length()), 1387 strlen(safestr(string))); 1388 } 1389 1390 1391 bool 1392 BString::StartsWith(const BString& string) const 1393 { 1394 return StartsWith(string.String(), string.Length()); 1395 } 1396 1397 1398 bool 1399 BString::StartsWith(const char* string) const 1400 { 1401 return StartsWith(string, strlen(safestr(string))); 1402 } 1403 1404 1405 bool 1406 BString::StartsWith(const char* string, int32 length) const 1407 { 1408 if (length > Length()) 1409 return false; 1410 1411 return memcmp(String(), string, length) == 0; 1412 } 1413 1414 1415 bool 1416 BString::IStartsWith(const BString& string) const 1417 { 1418 return IStartsWith(string.String(), string.Length()); 1419 } 1420 1421 1422 bool 1423 BString::IStartsWith(const char* string) const 1424 { 1425 return IStartsWith(string, strlen(safestr(string))); 1426 } 1427 1428 1429 bool 1430 BString::IStartsWith(const char* string, int32 length) const 1431 { 1432 if (length > Length() || length > (int32)strlen(safestr(string))) 1433 return false; 1434 1435 return _IFindAfter(string, 0, length) == 0; 1436 } 1437 1438 1439 bool 1440 BString::EndsWith(const BString& string) const 1441 { 1442 return EndsWith(string.String(), string.Length()); 1443 } 1444 1445 1446 bool 1447 BString::EndsWith(const char* string) const 1448 { 1449 return EndsWith(string, strlen(safestr(string))); 1450 } 1451 1452 1453 bool 1454 BString::EndsWith(const char* string, int32 length) const 1455 { 1456 int32 offset = Length() - length; 1457 if (offset < 0) 1458 return false; 1459 1460 return memcmp(String() + offset, string, length) == 0; 1461 } 1462 1463 1464 bool 1465 BString::IEndsWith(const BString& string) const 1466 { 1467 return IEndsWith(string.String(), string.Length()); 1468 } 1469 1470 1471 bool 1472 BString::IEndsWith(const char* string) const 1473 { 1474 return IEndsWith(string, strlen(safestr(string))); 1475 } 1476 1477 1478 bool 1479 BString::IEndsWith(const char* string, int32 length) const 1480 { 1481 int32 offset = Length() - length; 1482 if (offset < 0) 1483 return false; 1484 1485 return _IFindBefore(string, Length(), length) == offset; 1486 } 1487 1488 1489 // #pragma mark - Replacing 1490 1491 1492 BString& 1493 BString::ReplaceFirst(char replaceThis, char withThis) 1494 { 1495 int32 pos = FindFirst(replaceThis); 1496 if (pos >= 0 && _MakeWritable() == B_OK) 1497 fPrivateData[pos] = withThis; 1498 return *this; 1499 } 1500 1501 1502 BString& 1503 BString::ReplaceLast(char replaceThis, char withThis) 1504 { 1505 int32 pos = FindLast(replaceThis); 1506 if (pos >= 0 && _MakeWritable() == B_OK) 1507 fPrivateData[pos] = withThis; 1508 return *this; 1509 } 1510 1511 1512 BString& 1513 BString::ReplaceAll(char replaceThis, char withThis, int32 fromOffset) 1514 { 1515 fromOffset = min_clamp0(fromOffset, Length()); 1516 int32 pos = FindFirst(replaceThis, fromOffset); 1517 1518 // detach and set first match 1519 if (pos >= 0 && _MakeWritable() == B_OK) { 1520 for( ; pos >= 0; pos = FindFirst(replaceThis, pos + 1)) 1521 fPrivateData[pos] = withThis; 1522 } 1523 return *this; 1524 } 1525 1526 1527 BString& 1528 BString::Replace(char replaceThis, char withThis, int32 maxReplaceCount, 1529 int32 fromOffset) 1530 { 1531 fromOffset = min_clamp0(fromOffset, Length()); 1532 int32 pos = FindFirst(replaceThis, fromOffset); 1533 1534 if (maxReplaceCount > 0 && pos >= 0 && _MakeWritable() == B_OK) { 1535 for( ; maxReplaceCount > 0 && pos >= 0; 1536 pos = FindFirst(replaceThis, pos + 1)) { 1537 fPrivateData[pos] = withThis; 1538 maxReplaceCount--; 1539 } 1540 } 1541 return *this; 1542 } 1543 1544 1545 BString& 1546 BString::ReplaceFirst(const char* replaceThis, const char* withThis) 1547 { 1548 if (!replaceThis || !withThis || FindFirst(replaceThis) < 0) 1549 return *this; 1550 1551 if (_MakeWritable() != B_OK) 1552 return *this; 1553 1554 return _DoReplace(replaceThis, withThis, 1, 0, KEEP_CASE); 1555 } 1556 1557 1558 BString& 1559 BString::ReplaceLast(const char* replaceThis, const char* withThis) 1560 { 1561 if (!replaceThis || !withThis) 1562 return *this; 1563 1564 int32 replaceThisLength = strlen(replaceThis); 1565 int32 pos = _FindBefore(replaceThis, Length(), replaceThisLength); 1566 1567 if (pos >= 0) { 1568 int32 withThisLength = strlen(withThis); 1569 int32 difference = withThisLength - replaceThisLength; 1570 1571 if (difference > 0) { 1572 if (!_OpenAtBy(pos, difference)) 1573 return *this; 1574 } else if (difference < 0) { 1575 if (!_ShrinkAtBy(pos, -difference)) 1576 return *this; 1577 } else { 1578 if (_MakeWritable() != B_OK) 1579 return *this; 1580 } 1581 memcpy(fPrivateData + pos, withThis, withThisLength); 1582 } 1583 1584 return *this; 1585 } 1586 1587 1588 BString& 1589 BString::ReplaceAll(const char* replaceThis, const char* withThis, 1590 int32 fromOffset) 1591 { 1592 if (!replaceThis || !withThis || FindFirst(replaceThis) < 0) 1593 return *this; 1594 1595 if (_MakeWritable() != B_OK) 1596 return *this; 1597 1598 return _DoReplace(replaceThis, withThis, REPLACE_ALL, 1599 min_clamp0(fromOffset, Length()), KEEP_CASE); 1600 } 1601 1602 1603 BString& 1604 BString::Replace(const char* replaceThis, const char* withThis, 1605 int32 maxReplaceCount, int32 fromOffset) 1606 { 1607 if (!replaceThis || !withThis || maxReplaceCount <= 0 1608 || FindFirst(replaceThis) < 0) 1609 return *this; 1610 1611 if (_MakeWritable() != B_OK) 1612 return *this; 1613 1614 return _DoReplace(replaceThis, withThis, maxReplaceCount, 1615 min_clamp0(fromOffset, Length()), KEEP_CASE); 1616 } 1617 1618 1619 BString& 1620 BString::ReplaceAllChars(const char* replaceThis, const char* withThis, 1621 int32 fromCharOffset) 1622 { 1623 return ReplaceAll(replaceThis, withThis, 1624 UTF8CountBytes(fPrivateData, fromCharOffset)); 1625 } 1626 1627 1628 BString& 1629 BString::ReplaceChars(const char* replaceThis, const char* withThis, 1630 int32 maxReplaceCount, int32 fromCharOffset) 1631 { 1632 return Replace(replaceThis, withThis, maxReplaceCount, 1633 UTF8CountBytes(fPrivateData, fromCharOffset)); 1634 } 1635 1636 1637 BString& 1638 BString::IReplaceFirst(char replaceThis, char withThis) 1639 { 1640 char tmp[2] = { replaceThis, '\0' }; 1641 1642 int32 pos = _IFindAfter(tmp, 0, 1); 1643 if (pos >= 0 && _MakeWritable() == B_OK) 1644 fPrivateData[pos] = withThis; 1645 return *this; 1646 } 1647 1648 1649 BString& 1650 BString::IReplaceLast(char replaceThis, char withThis) 1651 { 1652 char tmp[2] = { replaceThis, '\0' }; 1653 1654 int32 pos = _IFindBefore(tmp, Length(), 1); 1655 if (pos >= 0 && _MakeWritable() == B_OK) 1656 fPrivateData[pos] = withThis; 1657 return *this; 1658 } 1659 1660 1661 BString& 1662 BString::IReplaceAll(char replaceThis, char withThis, int32 fromOffset) 1663 { 1664 char tmp[2] = { replaceThis, '\0' }; 1665 fromOffset = min_clamp0(fromOffset, Length()); 1666 int32 pos = _IFindAfter(tmp, fromOffset, 1); 1667 1668 if (pos >= 0 && _MakeWritable() == B_OK) { 1669 for( ; pos >= 0; pos = _IFindAfter(tmp, pos + 1, 1)) 1670 fPrivateData[pos] = withThis; 1671 } 1672 return *this; 1673 } 1674 1675 1676 BString& 1677 BString::IReplace(char replaceThis, char withThis, int32 maxReplaceCount, 1678 int32 fromOffset) 1679 { 1680 char tmp[2] = { replaceThis, '\0' }; 1681 fromOffset = min_clamp0(fromOffset, Length()); 1682 int32 pos = _IFindAfter(tmp, fromOffset, 1); 1683 1684 if (maxReplaceCount > 0 && pos >= 0 && _MakeWritable() == B_OK) { 1685 for( ; maxReplaceCount > 0 && pos >= 0; 1686 pos = _IFindAfter(tmp, pos + 1, 1)) { 1687 fPrivateData[pos] = withThis; 1688 maxReplaceCount--; 1689 } 1690 } 1691 1692 return *this; 1693 } 1694 1695 1696 BString& 1697 BString::IReplaceFirst(const char* replaceThis, const char* withThis) 1698 { 1699 if (!replaceThis || !withThis || IFindFirst(replaceThis) < 0) 1700 return *this; 1701 1702 if (_MakeWritable() != B_OK) 1703 return *this; 1704 return _DoReplace(replaceThis, withThis, 1, 0, IGNORE_CASE); 1705 } 1706 1707 1708 BString& 1709 BString::IReplaceLast(const char* replaceThis, const char* withThis) 1710 { 1711 if (!replaceThis || !withThis) 1712 return *this; 1713 1714 int32 replaceThisLength = strlen(replaceThis); 1715 int32 pos = _IFindBefore(replaceThis, Length(), replaceThisLength); 1716 1717 if (pos >= 0) { 1718 int32 withThisLength = strlen(withThis); 1719 int32 difference = withThisLength - replaceThisLength; 1720 1721 if (difference > 0) { 1722 if (!_OpenAtBy(pos, difference)) 1723 return *this; 1724 } else if (difference < 0) { 1725 if (!_ShrinkAtBy(pos, -difference)) 1726 return *this; 1727 } else { 1728 if (_MakeWritable() != B_OK) 1729 return *this; 1730 } 1731 memcpy(fPrivateData + pos, withThis, withThisLength); 1732 } 1733 1734 return *this; 1735 } 1736 1737 1738 BString& 1739 BString::IReplaceAll(const char* replaceThis, const char* withThis, 1740 int32 fromOffset) 1741 { 1742 if (!replaceThis || !withThis || IFindFirst(replaceThis) < 0) 1743 return *this; 1744 1745 if (_MakeWritable() != B_OK) 1746 return *this; 1747 1748 return _DoReplace(replaceThis, withThis, REPLACE_ALL, 1749 min_clamp0(fromOffset, Length()), IGNORE_CASE); 1750 } 1751 1752 1753 BString& 1754 BString::IReplace(const char* replaceThis, const char* withThis, 1755 int32 maxReplaceCount, int32 fromOffset) 1756 { 1757 if (!replaceThis || !withThis || maxReplaceCount <= 0 1758 || FindFirst(replaceThis) < 0) 1759 return *this; 1760 1761 if (_MakeWritable() != B_OK) 1762 return *this; 1763 1764 return _DoReplace(replaceThis, withThis, maxReplaceCount, 1765 min_clamp0(fromOffset, Length()), IGNORE_CASE); 1766 } 1767 1768 1769 BString& 1770 BString::ReplaceSet(const char* setOfBytes, char with) 1771 { 1772 if (!setOfBytes || strcspn(fPrivateData, setOfBytes) >= uint32(Length())) 1773 return *this; 1774 1775 if (_MakeWritable() != B_OK) 1776 return *this; 1777 1778 int32 offset = 0; 1779 int32 length = Length(); 1780 for (int32 pos;;) { 1781 pos = strcspn(fPrivateData + offset, setOfBytes); 1782 1783 offset += pos; 1784 if (offset >= length) 1785 break; 1786 1787 fPrivateData[offset] = with; 1788 offset++; 1789 } 1790 1791 return *this; 1792 } 1793 1794 1795 BString& 1796 BString::ReplaceSet(const char* setOfBytes, const char* with) 1797 { 1798 if (!setOfBytes || !with 1799 || strcspn(fPrivateData, setOfBytes) >= uint32(Length())) 1800 return *this; 1801 1802 // delegate simple case 1803 int32 withLen = strlen(with); 1804 if (withLen == 1) 1805 return ReplaceSet(setOfBytes, *with); 1806 1807 if (_MakeWritable() != B_OK) 1808 return *this; 1809 1810 int32 pos = 0; 1811 int32 searchLen = 1; 1812 int32 len = Length(); 1813 1814 PosVect positions; 1815 for (int32 offset = 0; offset < len; offset += (pos + searchLen)) { 1816 pos = strcspn(fPrivateData + offset, setOfBytes); 1817 if (pos + offset >= len) 1818 break; 1819 if (!positions.Add(offset + pos)) 1820 return *this; 1821 } 1822 1823 _ReplaceAtPositions(&positions, searchLen, with, withLen); 1824 return *this; 1825 } 1826 1827 1828 BString& 1829 BString::ReplaceCharsSet(const char* setOfChars, const char* with) 1830 { 1831 if (!setOfChars || !with) 1832 return *this; 1833 1834 int32 setCharCount = UTF8CountChars(setOfChars, -1); 1835 if ((uint32)setCharCount == strlen(setOfChars)) { 1836 // no multi-byte chars at all 1837 return ReplaceSet(setOfChars, with); 1838 } 1839 1840 BString setString(setOfChars); 1841 BString result; 1842 1843 int32 withLength = strlen(with); 1844 int32 charCount = CountChars(); 1845 for (int32 i = 0; i < charCount; i++) { 1846 int32 charLength; 1847 const char* sourceChar = CharAt(i, &charLength); 1848 bool match = false; 1849 1850 for (int32 j = 0; j < setCharCount; j++) { 1851 int32 setCharLength; 1852 const char* setChar = setString.CharAt(j, &setCharLength); 1853 if (charLength == setCharLength 1854 && memcmp(sourceChar, setChar, charLength) == 0) { 1855 match = true; 1856 break; 1857 } 1858 } 1859 1860 if (match) 1861 result.Append(with, withLength); 1862 else 1863 result.Append(sourceChar, charLength); 1864 } 1865 1866 *this = result; 1867 return *this; 1868 } 1869 1870 1871 // #pragma mark - Unchecked char access 1872 1873 1874 #if __GNUC__ == 2 1875 char& 1876 BString::operator[](int32 index) 1877 { 1878 if (_MakeWritable() != B_OK) { 1879 static char invalid; 1880 return invalid; 1881 } 1882 1883 _ReferenceCount() = -1; 1884 // mark string as unshareable 1885 1886 return fPrivateData[index]; 1887 } 1888 #endif 1889 1890 1891 const char* 1892 BString::CharAt(int32 charIndex, int32* bytes) const 1893 { 1894 int32 offset = UTF8CountBytes(fPrivateData, charIndex); 1895 if (bytes != NULL) 1896 *bytes = UTF8NextCharLen(fPrivateData + offset); 1897 return fPrivateData + offset; 1898 } 1899 1900 1901 bool 1902 BString::CharAt(int32 charIndex, char* buffer, int32* bytes) const 1903 { 1904 int32 length; 1905 const char* charAt = CharAt(charIndex, &length); 1906 if (bytes != NULL) { 1907 if (*bytes < length) 1908 return false; 1909 *bytes = length; 1910 } 1911 1912 memcpy(buffer, charAt, length); 1913 return true; 1914 } 1915 1916 1917 // #pragma mark - Fast low-level manipulation 1918 1919 1920 char* 1921 BString::LockBuffer(int32 maxLength) 1922 { 1923 int32 length = Length(); 1924 if (maxLength > length) 1925 length = maxLength; 1926 1927 if (_MakeWritable(length, true) != B_OK) 1928 return NULL; 1929 1930 _ReferenceCount() = -1; 1931 // mark unshareable 1932 1933 return fPrivateData; 1934 } 1935 1936 1937 BString& 1938 BString::UnlockBuffer(int32 length) 1939 { 1940 if (length > 0) 1941 length = min_clamp0(length, Length()); 1942 else 1943 length = fPrivateData == NULL ? 0 : strlen(fPrivateData); 1944 1945 if (_Resize(length) != NULL) { 1946 fPrivateData[length] = '\0'; 1947 _ReferenceCount() = 1; 1948 // mark shareable again 1949 } 1950 1951 return *this; 1952 } 1953 1954 1955 BString& 1956 BString::SetByteAt(int32 pos, char to) 1957 { 1958 if (pos < Length() && _MakeWritable() == B_OK) 1959 fPrivateData[pos] = to; 1960 1961 return *this; 1962 } 1963 1964 1965 // #pragma mark - Uppercase <-> Lowercase 1966 1967 1968 BString& 1969 BString::ToLower() 1970 { 1971 int32 length = Length(); 1972 if (length > 0 && _MakeWritable() == B_OK) { 1973 for (int32 count = 0; count < length; count++) 1974 fPrivateData[count] = tolower(fPrivateData[count]); 1975 } 1976 return *this; 1977 } 1978 1979 1980 BString& 1981 BString::ToUpper() 1982 { 1983 int32 length = Length(); 1984 if (length > 0 && _MakeWritable() == B_OK) { 1985 for (int32 count = 0; count < length; count++) 1986 fPrivateData[count] = toupper(fPrivateData[count]); 1987 } 1988 return *this; 1989 } 1990 1991 1992 BString& 1993 BString::Capitalize() 1994 { 1995 int32 length = Length(); 1996 1997 if (length > 0 && _MakeWritable() == B_OK) { 1998 fPrivateData[0] = toupper(fPrivateData[0]); 1999 for (int32 count = 1; count < length; count++) 2000 fPrivateData[count] = tolower(fPrivateData[count]); 2001 } 2002 return *this; 2003 } 2004 2005 2006 BString& 2007 BString::CapitalizeEachWord() 2008 { 2009 int32 length = Length(); 2010 2011 if (length > 0 && _MakeWritable() == B_OK) { 2012 int32 count = 0; 2013 do { 2014 // Find the first alphabetical character... 2015 for (; count < length; count++) { 2016 if (isalpha(fPrivateData[count])) { 2017 // ...found! Convert it to uppercase. 2018 fPrivateData[count] = toupper(fPrivateData[count]); 2019 count++; 2020 break; 2021 } 2022 } 2023 2024 // Now find the first non-alphabetical character, 2025 // and meanwhile, turn to lowercase all the alphabetical ones 2026 for (; count < length; count++) { 2027 if (isalpha(fPrivateData[count])) 2028 fPrivateData[count] = tolower(fPrivateData[count]); 2029 else 2030 break; 2031 } 2032 } while (count < length); 2033 } 2034 return *this; 2035 } 2036 2037 2038 // #pragma mark - Escaping and De-escaping 2039 2040 2041 BString& 2042 BString::CharacterEscape(const char* original, 2043 const char* setOfCharsToEscape, char escapeWith) 2044 { 2045 if (setOfCharsToEscape) 2046 _DoCharacterEscape(original, setOfCharsToEscape, escapeWith); 2047 return *this; 2048 } 2049 2050 2051 BString& 2052 BString::CharacterEscape(const char* setOfCharsToEscape, char escapeWith) 2053 { 2054 if (setOfCharsToEscape && Length() > 0) 2055 _DoCharacterEscape(fPrivateData, setOfCharsToEscape, escapeWith); 2056 return *this; 2057 } 2058 2059 2060 BString& 2061 BString::CharacterDeescape(const char* original, char escapeChar) 2062 { 2063 return _DoCharacterDeescape(original, escapeChar); 2064 } 2065 2066 2067 BString& 2068 BString::CharacterDeescape(char escapeChar) 2069 { 2070 if (Length() > 0) 2071 _DoCharacterDeescape(fPrivateData, escapeChar); 2072 return *this; 2073 } 2074 2075 2076 // #pragma mark - Trimming 2077 2078 2079 BString& 2080 BString::Trim() 2081 { 2082 if (Length() <= 0) 2083 return *this; 2084 2085 const char* string = String(); 2086 2087 // string is \0 terminated thus we don't need to check if we reached the end 2088 int32 startCount = 0; 2089 while (isspace(string[startCount])) 2090 startCount++; 2091 2092 int32 endIndex = Length() - 1; 2093 while (endIndex >= startCount && isspace(string[endIndex])) 2094 endIndex--; 2095 2096 if (startCount == 0 && endIndex == Length() - 1) 2097 return *this; 2098 2099 // We actually need to trim 2100 2101 ssize_t length = endIndex + 1 - startCount; 2102 ASSERT(length >= 0); 2103 if (startCount == 0 || length == 0) { 2104 _MakeWritable(length, true); 2105 } else if (_MakeWritable() == B_OK) { 2106 memmove(fPrivateData, fPrivateData + startCount, length); 2107 fPrivateData[length] = '\0'; 2108 _SetLength(length); 2109 } 2110 2111 return *this; 2112 } 2113 2114 2115 // #pragma mark - Insert 2116 2117 2118 BString& 2119 BString::operator<<(const char* string) 2120 { 2121 if (string != NULL) { 2122 int32 length = strlen(string); 2123 if (length > 0) 2124 _DoAppend(string, length); 2125 } 2126 return *this; 2127 } 2128 2129 2130 BString& 2131 BString::operator<<(const BString& string) 2132 { 2133 if (string.Length() > 0) 2134 _DoAppend(string.String(), string.Length()); 2135 return *this; 2136 } 2137 2138 2139 BString& 2140 BString::operator<<(char c) 2141 { 2142 _DoAppend(&c, 1); 2143 return *this; 2144 } 2145 2146 2147 BString& 2148 BString::operator<<(bool value) 2149 { 2150 if (value) 2151 _DoAppend("true", 4); 2152 else 2153 _DoAppend("false", 5); 2154 2155 return *this; 2156 } 2157 2158 2159 BString& 2160 BString::operator<<(int i) 2161 { 2162 char num[32]; 2163 int32 length = snprintf(num, sizeof(num), "%d", i); 2164 2165 _DoAppend(num, length); 2166 return *this; 2167 } 2168 2169 2170 BString& 2171 BString::operator<<(unsigned int i) 2172 { 2173 char num[32]; 2174 int32 length = snprintf(num, sizeof(num), "%u", i); 2175 2176 _DoAppend(num, length); 2177 return *this; 2178 } 2179 2180 2181 BString& 2182 BString::operator<<(unsigned long i) 2183 { 2184 char num[32]; 2185 int32 length = snprintf(num, sizeof(num), "%lu", i); 2186 2187 _DoAppend(num, length); 2188 return *this; 2189 } 2190 2191 2192 BString& 2193 BString::operator<<(long i) 2194 { 2195 char num[32]; 2196 int32 length = snprintf(num, sizeof(num), "%ld", i); 2197 2198 _DoAppend(num, length); 2199 return *this; 2200 } 2201 2202 2203 BString& 2204 BString::operator<<(unsigned long long i) 2205 { 2206 char num[64]; 2207 int32 length = snprintf(num, sizeof(num), "%llu", i); 2208 2209 _DoAppend(num, length); 2210 return *this; 2211 } 2212 2213 2214 BString& 2215 BString::operator<<(long long i) 2216 { 2217 char num[64]; 2218 int32 length = snprintf(num, sizeof(num), "%lld", i); 2219 2220 _DoAppend(num, length); 2221 return *this; 2222 } 2223 2224 2225 BString& 2226 BString::operator<<(float f) 2227 { 2228 char num[64]; 2229 int32 length = snprintf(num, sizeof(num), "%.2f", f); 2230 2231 _DoAppend(num, length); 2232 return *this; 2233 } 2234 2235 2236 BString& 2237 BString::operator<<(double value) 2238 { 2239 char num[64]; 2240 int32 length = snprintf(num, sizeof(num), "%.2f", value); 2241 2242 _DoAppend(num, length); 2243 return *this; 2244 } 2245 2246 2247 // #pragma mark - Private or reserved 2248 2249 2250 BString::BString(char* privateData, PrivateDataTag tag) 2251 : 2252 fPrivateData(privateData) 2253 { 2254 if (fPrivateData != NULL) 2255 atomic_add(&_ReferenceCount(), 1); 2256 } 2257 2258 2259 /*! Detaches this string from an eventually shared fPrivateData, ie. this makes 2260 this string writable. 2261 */ 2262 status_t 2263 BString::_MakeWritable() 2264 { 2265 if (atomic_get(&_ReferenceCount()) > 1) { 2266 // It might be shared, and this requires special treatment 2267 char* newData = _Clone(fPrivateData, Length()); 2268 if (atomic_add(&_ReferenceCount(), -1) == 1) { 2269 // someone else left, we were the last owner 2270 _FreePrivateData(); 2271 } 2272 if (newData == NULL) 2273 return B_NO_MEMORY; 2274 2275 fPrivateData = newData; 2276 } 2277 2278 return B_OK; 2279 } 2280 2281 2282 /*! Makes this string writable, and resizes the buffer to \a length bytes (not 2283 including the terminating null). 2284 2285 @param length The length of the new buffer in bytes. 2286 @param copy If true, the current string will be copied into the new string. 2287 */ 2288 status_t 2289 BString::_MakeWritable(int32 length, bool copy) 2290 { 2291 char* newData = NULL; 2292 2293 if (atomic_get(&_ReferenceCount()) > 1) { 2294 // we might share our data with someone else 2295 if (copy) 2296 newData = _Clone(fPrivateData, length); 2297 else 2298 newData = _Allocate(length); 2299 2300 if (newData == NULL) 2301 return B_NO_MEMORY; 2302 2303 if (atomic_add(&_ReferenceCount(), -1) == 1) { 2304 // someone else left, we were the last owner 2305 _FreePrivateData(); 2306 } 2307 } else { 2308 // we don't share our data with someone else 2309 newData = _Resize(length); 2310 2311 if (newData == NULL) 2312 return B_NO_MEMORY; 2313 } 2314 2315 fPrivateData = newData; 2316 return B_OK; 2317 } 2318 2319 2320 /*! Allocates a new private data buffer with the space to store \a length bytes 2321 (not including the terminating null). 2322 */ 2323 /*static*/ char* 2324 BString::_Allocate(int32 length) 2325 { 2326 if (length < 0) 2327 return NULL; 2328 2329 char* newData = (char*)malloc(length + kPrivateDataOffset + 1); 2330 if (newData == NULL) 2331 return NULL; 2332 2333 newData += kPrivateDataOffset; 2334 newData[length] = '\0'; 2335 2336 // initialize reference count & length 2337 Private::DataRefCount(newData) = 1; 2338 Private::DataLength(newData) = length & 0x7fffffff; 2339 2340 return newData; 2341 } 2342 2343 2344 /*! Resizes the private data buffer. You must already have a writable buffer 2345 when you call this method. 2346 */ 2347 char* 2348 BString::_Resize(int32 length) 2349 { 2350 ASSERT(_ReferenceCount() == 1 || _ReferenceCount() == -1); 2351 2352 if (length == Length()) 2353 return fPrivateData; 2354 2355 char* data = fPrivateData ? fPrivateData - kPrivateDataOffset : NULL; 2356 if (length < 0) 2357 length = 0; 2358 2359 data = (char*)realloc(data, length + kPrivateDataOffset + 1); 2360 if (data == NULL) 2361 return NULL; 2362 2363 data += kPrivateDataOffset; 2364 2365 fPrivateData = data; 2366 fPrivateData[length] = '\0'; 2367 2368 _SetLength(length); 2369 _ReferenceCount() = 1; 2370 2371 return data; 2372 } 2373 2374 2375 void 2376 BString::_Init(const char* src, int32 length) 2377 { 2378 fPrivateData = _Clone(src, length); 2379 if (fPrivateData == NULL) 2380 fPrivateData = _Clone(NULL, 0); 2381 } 2382 2383 2384 char* 2385 BString::_Clone(const char* data, int32 length) 2386 { 2387 char* newData = _Allocate(length); 2388 if (newData == NULL) 2389 return NULL; 2390 2391 if (data != NULL && length > 0) { 2392 // "data" may not span over the whole length 2393 strncpy(newData, data, length); 2394 } 2395 2396 return newData; 2397 } 2398 2399 2400 char* 2401 BString::_OpenAtBy(int32 offset, int32 length) 2402 { 2403 int32 oldLength = Length(); 2404 2405 if (_MakeWritable() != B_OK) 2406 return NULL; 2407 2408 memmove(fPrivateData + offset + length, fPrivateData + offset, 2409 oldLength - offset); 2410 return _Resize(oldLength + length); 2411 } 2412 2413 2414 char* 2415 BString::_ShrinkAtBy(int32 offset, int32 length) 2416 { 2417 int32 oldLength = Length(); 2418 if (_MakeWritable() != B_OK) 2419 return NULL; 2420 2421 memmove(fPrivateData + offset, fPrivateData + offset + length, 2422 oldLength - offset - length); 2423 return _Resize(oldLength - length); 2424 } 2425 2426 2427 void 2428 BString::_SetLength(int32 length) 2429 { 2430 Private::DataLength(fPrivateData) = length & 0x7fffffff; 2431 } 2432 2433 2434 void 2435 BString::_FreePrivateData() 2436 { 2437 if (fPrivateData != NULL) { 2438 free(fPrivateData - kPrivateDataOffset); 2439 fPrivateData = NULL; 2440 } 2441 } 2442 2443 2444 bool 2445 BString::_DoAppend(const char* string, int32 length) 2446 { 2447 int32 oldLength = Length(); 2448 if (_MakeWritable(oldLength + length, true) == B_OK) { 2449 strncpy(fPrivateData + oldLength, string, length); 2450 return true; 2451 } 2452 return false; 2453 } 2454 2455 2456 bool 2457 BString::_DoPrepend(const char* string, int32 length) 2458 { 2459 // TODO: this could be optimized (allocate a new buffer, use memcpy()) 2460 int32 oldLength = Length(); 2461 if (_MakeWritable(oldLength + length, true) == B_OK) { 2462 memmove(fPrivateData + length, fPrivateData, oldLength); 2463 if (string && length) 2464 strncpy(fPrivateData, string, length); 2465 return true; 2466 } 2467 return false; 2468 } 2469 2470 2471 bool 2472 BString::_DoInsert(const char* string, int32 offset, int32 length) 2473 { 2474 int32 oldLength = Length(); 2475 if (_MakeWritable(oldLength + length, true) == B_OK) { 2476 memmove(fPrivateData + offset + length, fPrivateData + offset, 2477 oldLength - offset); 2478 if (string != NULL && length) 2479 strncpy(fPrivateData + offset, string, length); 2480 return true; 2481 } 2482 return false; 2483 } 2484 2485 2486 int32 2487 BString::_ShortFindAfter(const char* string, int32 len) const 2488 { 2489 const char* ptr = strstr(String(), string); 2490 2491 if (ptr != NULL) 2492 return ptr - String(); 2493 2494 return B_ERROR; 2495 } 2496 2497 2498 int32 2499 BString::_FindAfter(const char* string, int32 offset, int32 length) const 2500 { 2501 const char* ptr = strstr(String() + offset, string); 2502 2503 if (ptr != NULL) 2504 return ptr - String(); 2505 2506 return B_ERROR; 2507 } 2508 2509 2510 int32 2511 BString::_IFindAfter(const char* string, int32 offset, int32 length) const 2512 { 2513 const char* ptr = strcasestr(String() + offset, string); 2514 2515 if (ptr != NULL) 2516 return ptr - String(); 2517 2518 return B_ERROR; 2519 } 2520 2521 2522 int32 2523 BString::_FindBefore(const char* string, int32 offset, int32 length) const 2524 { 2525 if (fPrivateData != NULL) { 2526 const char* ptr = fPrivateData + offset - length; 2527 2528 while (ptr >= fPrivateData) { 2529 if (!memcmp(ptr, string, length)) 2530 return ptr - fPrivateData; 2531 ptr--; 2532 } 2533 } 2534 return B_ERROR; 2535 } 2536 2537 2538 int32 2539 BString::_IFindBefore(const char* string, int32 offset, int32 length) const 2540 { 2541 if (fPrivateData != NULL) { 2542 char* ptr1 = fPrivateData + offset - length; 2543 2544 while (ptr1 >= fPrivateData) { 2545 if (!strncasecmp(ptr1, string, length)) 2546 return ptr1 - fPrivateData; 2547 ptr1--; 2548 } 2549 } 2550 return B_ERROR; 2551 } 2552 2553 2554 BString& 2555 BString::_DoCharacterEscape(const char* string, const char* setOfCharsToEscape, 2556 char escapeChar) 2557 { 2558 if (_MakeWritable(string_length(string), false) != B_OK) 2559 return *this; 2560 2561 memcpy(fPrivateData, string, Length()); 2562 2563 PosVect positions; 2564 int32 length = Length(); 2565 int32 pos; 2566 for (int32 offset = 0; offset < length; offset += pos + 1) { 2567 pos = strcspn(fPrivateData + offset, setOfCharsToEscape); 2568 if (pos < length - offset && !positions.Add(offset + pos)) 2569 return *this; 2570 } 2571 2572 uint32 count = positions.CountItems(); 2573 int32 newLength = length + count; 2574 if (!newLength) { 2575 _Resize(0); 2576 return *this; 2577 } 2578 2579 char* newData = _Allocate(newLength); 2580 if (newData) { 2581 char* oldString = fPrivateData; 2582 char* newString = newData; 2583 int32 lastPos = 0; 2584 2585 for (uint32 i = 0; i < count; ++i) { 2586 pos = positions.ItemAt(i); 2587 length = pos - lastPos; 2588 if (length > 0) { 2589 memcpy(newString, oldString, length); 2590 oldString += length; 2591 newString += length; 2592 } 2593 *newString++ = escapeChar; 2594 *newString++ = *oldString++; 2595 lastPos = pos + 1; 2596 } 2597 2598 length = Length() + 1 - lastPos; 2599 if (length > 0) 2600 memcpy(newString, oldString, length); 2601 2602 _FreePrivateData(); 2603 fPrivateData = newData; 2604 } 2605 return *this; 2606 } 2607 2608 2609 BString& 2610 BString::_DoCharacterDeescape(const char* string, char escapeChar) 2611 { 2612 if (_MakeWritable(string_length(string), false) != B_OK) 2613 return *this; 2614 2615 memcpy(fPrivateData, string, Length()); 2616 const char escape[2] = { escapeChar, '\0' }; 2617 return _DoReplace(escape, "", REPLACE_ALL, 0, KEEP_CASE); 2618 } 2619 2620 2621 BString& 2622 BString::_DoReplace(const char* findThis, const char* replaceWith, 2623 int32 maxReplaceCount, int32 fromOffset, bool ignoreCase) 2624 { 2625 if (findThis == NULL || maxReplaceCount <= 0 2626 || fromOffset < 0 || fromOffset >= Length()) 2627 return *this; 2628 2629 int32 findLen = strlen(findThis); 2630 if (findLen == 0) 2631 return *this; 2632 2633 typedef int32 (BString::*TFindMethod)(const char*, int32, int32) const; 2634 TFindMethod findMethod = ignoreCase 2635 ? &BString::_IFindAfter : &BString::_FindAfter; 2636 2637 if (!replaceWith) 2638 replaceWith = ""; 2639 2640 int32 replaceLen = strlen(replaceWith); 2641 int32 lastSrcPos = fromOffset; 2642 PosVect positions; 2643 for (int32 srcPos = 0; maxReplaceCount > 0 2644 && (srcPos = (this->*findMethod)(findThis, lastSrcPos, findLen)) >= 0; 2645 maxReplaceCount--) { 2646 positions.Add(srcPos); 2647 lastSrcPos = srcPos + findLen; 2648 } 2649 _ReplaceAtPositions(&positions, findLen, replaceWith, replaceLen); 2650 return *this; 2651 } 2652 2653 2654 void 2655 BString::_ReplaceAtPositions(const PosVect* positions, int32 searchLength, 2656 const char* with, int32 withLength) 2657 { 2658 int32 length = Length(); 2659 uint32 count = positions->CountItems(); 2660 int32 newLength = length + count * (withLength - searchLength); 2661 if (!newLength) { 2662 _Resize(0); 2663 return; 2664 } 2665 2666 char* newData = _Allocate(newLength); 2667 if (newData == NULL) 2668 return; 2669 2670 char* oldString = fPrivateData; 2671 char* newString = newData; 2672 int32 lastPos = 0; 2673 2674 for (uint32 i = 0; i < count; ++i) { 2675 int32 pos = positions->ItemAt(i); 2676 length = pos - lastPos; 2677 if (length > 0) { 2678 memcpy(newString, oldString, length); 2679 oldString += length; 2680 newString += length; 2681 } 2682 memcpy(newString, with, withLength); 2683 oldString += searchLength; 2684 newString += withLength; 2685 lastPos = pos + searchLength; 2686 } 2687 2688 length = Length() + 1 - lastPos; 2689 if (length > 0) 2690 memcpy(newString, oldString, length); 2691 2692 _FreePrivateData(); 2693 fPrivateData = newData; 2694 } 2695 2696 2697 // #pragma mark - backwards compatibility 2698 2699 2700 /*! Translates to (missing const): 2701 BString& BString::operator<<(BString& string) 2702 */ 2703 extern "C" BString& 2704 __ls__7BStringR7BString(BString* self, BString& string) 2705 { 2706 return self->operator<<(string); 2707 } 2708 2709 2710 #if __GNUC__ > 3 2711 2712 // #pragma mark - BStringRef backwards compatibility 2713 2714 2715 class BStringRef { 2716 public: 2717 BStringRef(BString& string, int32 position); 2718 ~BStringRef() {} 2719 2720 operator char() const; 2721 2722 char* operator&(); 2723 const char* operator&() const; 2724 2725 BStringRef& operator=(char c); 2726 BStringRef& operator=(const BStringRef& rc); 2727 2728 private: 2729 BString& fString; 2730 int32 fPosition; 2731 }; 2732 2733 2734 BStringRef::BStringRef(BString& string, int32 position) 2735 : 2736 fString(string), fPosition(position) 2737 { 2738 } 2739 2740 2741 BStringRef::operator char() const 2742 { 2743 return fPosition < fString.Length() ? fString.fPrivateData[fPosition] : 0; 2744 } 2745 2746 2747 BStringRef& 2748 BStringRef::operator=(char c) 2749 { 2750 fString._MakeWritable(); 2751 fString.fPrivateData[fPosition] = c; 2752 return *this; 2753 } 2754 2755 2756 BStringRef& 2757 BStringRef::operator=(const BStringRef &rc) 2758 { 2759 return operator=(rc.fString.fPrivateData[rc.fPosition]); 2760 } 2761 2762 2763 const char* 2764 BStringRef::operator&() const 2765 { 2766 return &fString.fPrivateData[fPosition]; 2767 } 2768 2769 2770 char* 2771 BStringRef::operator&() 2772 { 2773 if (fString._MakeWritable() != B_OK) 2774 return NULL; 2775 2776 fString._ReferenceCount() = -1; 2777 // mark as unsharable 2778 return &fString.fPrivateData[fPosition]; 2779 } 2780 2781 2782 2783 extern "C" BStringRef 2784 _ZN7BStringixEi(BString* self, int32 index) 2785 2786 { 2787 return BStringRef(*self, index); 2788 } 2789 #endif 2790 2791 2792 // #pragma mark - Non-member compare for sorting, etc. 2793 2794 2795 int 2796 Compare(const BString& string1, const BString& string2) 2797 { 2798 return strcmp(string1.String(), string2.String()); 2799 } 2800 2801 2802 int 2803 ICompare(const BString& string1, const BString& string2) 2804 { 2805 return strcasecmp(string1.String(), string2.String()); 2806 } 2807 2808 2809 int 2810 Compare(const BString* string1, const BString* string2) 2811 { 2812 return strcmp(string1->String(), string2->String()); 2813 } 2814 2815 2816 int 2817 ICompare(const BString* string1, const BString* string2) 2818 { 2819 return strcasecmp(string1->String(), string2->String()); 2820 } 2821