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 = 106 (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 + separatorLength; 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 == NULL || 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 == NULL) 1562 return *this; 1563 if (withThis == NULL) 1564 withThis = ""; 1565 1566 int32 replaceThisLength = strlen(replaceThis); 1567 int32 pos = _FindBefore(replaceThis, Length(), replaceThisLength); 1568 1569 if (pos >= 0) { 1570 int32 withThisLength = strlen(withThis); 1571 int32 difference = withThisLength - replaceThisLength; 1572 1573 if (difference > 0) { 1574 if (!_OpenAtBy(pos, difference)) 1575 return *this; 1576 } else if (difference < 0) { 1577 if (!_ShrinkAtBy(pos, -difference)) 1578 return *this; 1579 } else { 1580 if (_MakeWritable() != B_OK) 1581 return *this; 1582 } 1583 memcpy(fPrivateData + pos, withThis, withThisLength); 1584 } 1585 1586 return *this; 1587 } 1588 1589 1590 BString& 1591 BString::ReplaceAll(const char* replaceThis, const char* withThis, 1592 int32 fromOffset) 1593 { 1594 if (replaceThis == NULL || FindFirst(replaceThis) < 0) 1595 return *this; 1596 1597 if (_MakeWritable() != B_OK) 1598 return *this; 1599 1600 return _DoReplace(replaceThis, withThis, REPLACE_ALL, 1601 min_clamp0(fromOffset, Length()), KEEP_CASE); 1602 } 1603 1604 1605 BString& 1606 BString::Replace(const char* replaceThis, const char* withThis, 1607 int32 maxReplaceCount, int32 fromOffset) 1608 { 1609 if (replaceThis == NULL || maxReplaceCount <= 0 1610 || FindFirst(replaceThis) < 0) 1611 return *this; 1612 1613 if (_MakeWritable() != B_OK) 1614 return *this; 1615 1616 return _DoReplace(replaceThis, withThis, maxReplaceCount, 1617 min_clamp0(fromOffset, Length()), KEEP_CASE); 1618 } 1619 1620 1621 BString& 1622 BString::ReplaceAllChars(const char* replaceThis, const char* withThis, 1623 int32 fromCharOffset) 1624 { 1625 return ReplaceAll(replaceThis, withThis, 1626 UTF8CountBytes(fPrivateData, fromCharOffset)); 1627 } 1628 1629 1630 BString& 1631 BString::ReplaceChars(const char* replaceThis, const char* withThis, 1632 int32 maxReplaceCount, int32 fromCharOffset) 1633 { 1634 return Replace(replaceThis, withThis, maxReplaceCount, 1635 UTF8CountBytes(fPrivateData, fromCharOffset)); 1636 } 1637 1638 1639 BString& 1640 BString::IReplaceFirst(char replaceThis, char withThis) 1641 { 1642 char tmp[2] = { replaceThis, '\0' }; 1643 1644 int32 pos = _IFindAfter(tmp, 0, 1); 1645 if (pos >= 0 && _MakeWritable() == B_OK) 1646 fPrivateData[pos] = withThis; 1647 return *this; 1648 } 1649 1650 1651 BString& 1652 BString::IReplaceLast(char replaceThis, char withThis) 1653 { 1654 char tmp[2] = { replaceThis, '\0' }; 1655 1656 int32 pos = _IFindBefore(tmp, Length(), 1); 1657 if (pos >= 0 && _MakeWritable() == B_OK) 1658 fPrivateData[pos] = withThis; 1659 return *this; 1660 } 1661 1662 1663 BString& 1664 BString::IReplaceAll(char replaceThis, char withThis, int32 fromOffset) 1665 { 1666 char tmp[2] = { replaceThis, '\0' }; 1667 fromOffset = min_clamp0(fromOffset, Length()); 1668 int32 pos = _IFindAfter(tmp, fromOffset, 1); 1669 1670 if (pos >= 0 && _MakeWritable() == B_OK) { 1671 for( ; pos >= 0; pos = _IFindAfter(tmp, pos + 1, 1)) 1672 fPrivateData[pos] = withThis; 1673 } 1674 return *this; 1675 } 1676 1677 1678 BString& 1679 BString::IReplace(char replaceThis, char withThis, int32 maxReplaceCount, 1680 int32 fromOffset) 1681 { 1682 char tmp[2] = { replaceThis, '\0' }; 1683 fromOffset = min_clamp0(fromOffset, Length()); 1684 int32 pos = _IFindAfter(tmp, fromOffset, 1); 1685 1686 if (maxReplaceCount > 0 && pos >= 0 && _MakeWritable() == B_OK) { 1687 for( ; maxReplaceCount > 0 && pos >= 0; 1688 pos = _IFindAfter(tmp, pos + 1, 1)) { 1689 fPrivateData[pos] = withThis; 1690 maxReplaceCount--; 1691 } 1692 } 1693 1694 return *this; 1695 } 1696 1697 1698 BString& 1699 BString::IReplaceFirst(const char* replaceThis, const char* withThis) 1700 { 1701 if (replaceThis == NULL || IFindFirst(replaceThis) < 0) 1702 return *this; 1703 1704 if (_MakeWritable() != B_OK) 1705 return *this; 1706 return _DoReplace(replaceThis, withThis, 1, 0, IGNORE_CASE); 1707 } 1708 1709 1710 BString& 1711 BString::IReplaceLast(const char* replaceThis, const char* withThis) 1712 { 1713 if (replaceThis == NULL) 1714 return *this; 1715 if (withThis == NULL) 1716 withThis = ""; 1717 1718 int32 replaceThisLength = strlen(replaceThis); 1719 int32 pos = _IFindBefore(replaceThis, Length(), replaceThisLength); 1720 1721 if (pos >= 0) { 1722 int32 withThisLength = strlen(withThis); 1723 int32 difference = withThisLength - replaceThisLength; 1724 1725 if (difference > 0) { 1726 if (!_OpenAtBy(pos, difference)) 1727 return *this; 1728 } else if (difference < 0) { 1729 if (!_ShrinkAtBy(pos, -difference)) 1730 return *this; 1731 } else { 1732 if (_MakeWritable() != B_OK) 1733 return *this; 1734 } 1735 memcpy(fPrivateData + pos, withThis, withThisLength); 1736 } 1737 1738 return *this; 1739 } 1740 1741 1742 BString& 1743 BString::IReplaceAll(const char* replaceThis, const char* withThis, 1744 int32 fromOffset) 1745 { 1746 if (replaceThis == NULL || IFindFirst(replaceThis) < 0) 1747 return *this; 1748 1749 if (_MakeWritable() != B_OK) 1750 return *this; 1751 1752 return _DoReplace(replaceThis, withThis, REPLACE_ALL, 1753 min_clamp0(fromOffset, Length()), IGNORE_CASE); 1754 } 1755 1756 1757 BString& 1758 BString::IReplace(const char* replaceThis, const char* withThis, 1759 int32 maxReplaceCount, int32 fromOffset) 1760 { 1761 if (replaceThis == NULL || maxReplaceCount <= 0 1762 || FindFirst(replaceThis) < 0) 1763 return *this; 1764 1765 if (_MakeWritable() != B_OK) 1766 return *this; 1767 1768 return _DoReplace(replaceThis, withThis, maxReplaceCount, 1769 min_clamp0(fromOffset, Length()), IGNORE_CASE); 1770 } 1771 1772 1773 BString& 1774 BString::ReplaceSet(const char* setOfBytes, char with) 1775 { 1776 if (!setOfBytes || strcspn(fPrivateData, setOfBytes) >= uint32(Length())) 1777 return *this; 1778 1779 if (_MakeWritable() != B_OK) 1780 return *this; 1781 1782 int32 offset = 0; 1783 int32 length = Length(); 1784 for (int32 pos;;) { 1785 pos = strcspn(fPrivateData + offset, setOfBytes); 1786 1787 offset += pos; 1788 if (offset >= length) 1789 break; 1790 1791 fPrivateData[offset] = with; 1792 offset++; 1793 } 1794 1795 return *this; 1796 } 1797 1798 1799 BString& 1800 BString::ReplaceSet(const char* setOfBytes, const char* with) 1801 { 1802 if (!setOfBytes || !with 1803 || strcspn(fPrivateData, setOfBytes) >= uint32(Length())) 1804 return *this; 1805 1806 // delegate simple case 1807 int32 withLen = strlen(with); 1808 if (withLen == 1) 1809 return ReplaceSet(setOfBytes, *with); 1810 1811 if (_MakeWritable() != B_OK) 1812 return *this; 1813 1814 int32 pos = 0; 1815 int32 searchLen = 1; 1816 int32 len = Length(); 1817 1818 PosVect positions; 1819 for (int32 offset = 0; offset < len; offset += (pos + searchLen)) { 1820 pos = strcspn(fPrivateData + offset, setOfBytes); 1821 if (pos + offset >= len) 1822 break; 1823 if (!positions.Add(offset + pos)) 1824 return *this; 1825 } 1826 1827 _ReplaceAtPositions(&positions, searchLen, with, withLen); 1828 return *this; 1829 } 1830 1831 1832 BString& 1833 BString::ReplaceCharsSet(const char* setOfChars, const char* with) 1834 { 1835 if (!setOfChars || !with) 1836 return *this; 1837 1838 int32 setCharCount = UTF8CountChars(setOfChars, -1); 1839 if ((uint32)setCharCount == strlen(setOfChars)) { 1840 // no multi-byte chars at all 1841 return ReplaceSet(setOfChars, with); 1842 } 1843 1844 BString setString(setOfChars); 1845 BString result; 1846 1847 int32 withLength = strlen(with); 1848 int32 charCount = CountChars(); 1849 for (int32 i = 0; i < charCount; i++) { 1850 int32 charLength; 1851 const char* sourceChar = CharAt(i, &charLength); 1852 bool match = false; 1853 1854 for (int32 j = 0; j < setCharCount; j++) { 1855 int32 setCharLength; 1856 const char* setChar = setString.CharAt(j, &setCharLength); 1857 if (charLength == setCharLength 1858 && memcmp(sourceChar, setChar, charLength) == 0) { 1859 match = true; 1860 break; 1861 } 1862 } 1863 1864 if (match) 1865 result.Append(with, withLength); 1866 else 1867 result.Append(sourceChar, charLength); 1868 } 1869 1870 *this = result; 1871 return *this; 1872 } 1873 1874 1875 // #pragma mark - Unchecked char access 1876 1877 1878 #if __GNUC__ == 2 1879 char& 1880 BString::operator[](int32 index) 1881 { 1882 if (_MakeWritable() != B_OK) { 1883 static char invalid; 1884 return invalid; 1885 } 1886 1887 _ReferenceCount() = -1; 1888 // mark string as unshareable 1889 1890 return fPrivateData[index]; 1891 } 1892 #endif 1893 1894 1895 const char* 1896 BString::CharAt(int32 charIndex, int32* bytes) const 1897 { 1898 int32 offset = UTF8CountBytes(fPrivateData, charIndex); 1899 if (bytes != NULL) 1900 *bytes = UTF8NextCharLen(fPrivateData + offset); 1901 return fPrivateData + offset; 1902 } 1903 1904 1905 bool 1906 BString::CharAt(int32 charIndex, char* buffer, int32* bytes) const 1907 { 1908 int32 length; 1909 const char* charAt = CharAt(charIndex, &length); 1910 if (bytes != NULL) { 1911 if (*bytes < length) 1912 return false; 1913 *bytes = length; 1914 } 1915 1916 memcpy(buffer, charAt, length); 1917 return true; 1918 } 1919 1920 1921 // #pragma mark - Fast low-level manipulation 1922 1923 1924 char* 1925 BString::LockBuffer(int32 maxLength) 1926 { 1927 int32 length = Length(); 1928 if (maxLength > length) 1929 length = maxLength; 1930 1931 if (_MakeWritable(length, true) != B_OK) 1932 return NULL; 1933 1934 _ReferenceCount() = -1; 1935 // mark unshareable 1936 1937 return fPrivateData; 1938 } 1939 1940 1941 BString& 1942 BString::UnlockBuffer(int32 length) 1943 { 1944 if (length > 0) 1945 length = min_clamp0(length, Length()); 1946 else 1947 length = fPrivateData == NULL ? 0 : strlen(fPrivateData); 1948 1949 if (_Resize(length) != NULL) { 1950 fPrivateData[length] = '\0'; 1951 _ReferenceCount() = 1; 1952 // mark shareable again 1953 } 1954 1955 return *this; 1956 } 1957 1958 1959 BString& 1960 BString::SetByteAt(int32 pos, char to) 1961 { 1962 if (pos < Length() && _MakeWritable() == B_OK) 1963 fPrivateData[pos] = to; 1964 1965 return *this; 1966 } 1967 1968 1969 // #pragma mark - Uppercase <-> Lowercase 1970 1971 1972 BString& 1973 BString::ToLower() 1974 { 1975 int32 length = Length(); 1976 if (length > 0 && _MakeWritable() == B_OK) { 1977 for (int32 count = 0; count < length; count++) 1978 fPrivateData[count] = tolower(fPrivateData[count]); 1979 } 1980 return *this; 1981 } 1982 1983 1984 BString& 1985 BString::ToUpper() 1986 { 1987 int32 length = Length(); 1988 if (length > 0 && _MakeWritable() == B_OK) { 1989 for (int32 count = 0; count < length; count++) 1990 fPrivateData[count] = toupper(fPrivateData[count]); 1991 } 1992 return *this; 1993 } 1994 1995 1996 BString& 1997 BString::Capitalize() 1998 { 1999 int32 length = Length(); 2000 2001 if (length > 0 && _MakeWritable() == B_OK) { 2002 fPrivateData[0] = toupper(fPrivateData[0]); 2003 for (int32 count = 1; count < length; count++) 2004 fPrivateData[count] = tolower(fPrivateData[count]); 2005 } 2006 return *this; 2007 } 2008 2009 2010 BString& 2011 BString::CapitalizeEachWord() 2012 { 2013 int32 length = Length(); 2014 2015 if (length > 0 && _MakeWritable() == B_OK) { 2016 int32 count = 0; 2017 do { 2018 // Find the first alphabetical character... 2019 for (; count < length; count++) { 2020 if (isalpha(fPrivateData[count])) { 2021 // ...found! Convert it to uppercase. 2022 fPrivateData[count] = toupper(fPrivateData[count]); 2023 count++; 2024 break; 2025 } 2026 } 2027 2028 // Now find the first non-alphabetical character, 2029 // and meanwhile, turn to lowercase all the alphabetical ones 2030 for (; count < length; count++) { 2031 if (isalpha(fPrivateData[count])) 2032 fPrivateData[count] = tolower(fPrivateData[count]); 2033 else 2034 break; 2035 } 2036 } while (count < length); 2037 } 2038 return *this; 2039 } 2040 2041 2042 // #pragma mark - Escaping and De-escaping 2043 2044 2045 BString& 2046 BString::CharacterEscape(const char* original, 2047 const char* setOfCharsToEscape, char escapeWith) 2048 { 2049 if (setOfCharsToEscape) 2050 _DoCharacterEscape(original, setOfCharsToEscape, escapeWith); 2051 return *this; 2052 } 2053 2054 2055 BString& 2056 BString::CharacterEscape(const char* setOfCharsToEscape, char escapeWith) 2057 { 2058 if (setOfCharsToEscape && Length() > 0) 2059 _DoCharacterEscape(fPrivateData, setOfCharsToEscape, escapeWith); 2060 return *this; 2061 } 2062 2063 2064 BString& 2065 BString::CharacterDeescape(const char* original, char escapeChar) 2066 { 2067 return _DoCharacterDeescape(original, escapeChar); 2068 } 2069 2070 2071 BString& 2072 BString::CharacterDeescape(char escapeChar) 2073 { 2074 if (Length() > 0) 2075 _DoCharacterDeescape(fPrivateData, escapeChar); 2076 return *this; 2077 } 2078 2079 2080 // #pragma mark - Trimming 2081 2082 2083 BString& 2084 BString::Trim() 2085 { 2086 if (Length() <= 0) 2087 return *this; 2088 2089 const char* string = String(); 2090 2091 // string is \0 terminated thus we don't need to check if we reached the end 2092 int32 startCount = 0; 2093 while (isspace(string[startCount])) 2094 startCount++; 2095 2096 int32 endIndex = Length() - 1; 2097 while (endIndex >= startCount && isspace(string[endIndex])) 2098 endIndex--; 2099 2100 if (startCount == 0 && endIndex == Length() - 1) 2101 return *this; 2102 2103 // We actually need to trim 2104 2105 ssize_t length = endIndex + 1 - startCount; 2106 ASSERT(length >= 0); 2107 if (startCount == 0 || length == 0) { 2108 _MakeWritable(length, true); 2109 } else if (_MakeWritable() == B_OK) { 2110 memmove(fPrivateData, fPrivateData + startCount, length); 2111 fPrivateData[length] = '\0'; 2112 _SetLength(length); 2113 } 2114 2115 return *this; 2116 } 2117 2118 2119 // #pragma mark - Insert 2120 2121 2122 BString& 2123 BString::operator<<(const char* string) 2124 { 2125 if (string != NULL) { 2126 int32 length = strlen(string); 2127 if (length > 0) 2128 _DoAppend(string, length); 2129 } 2130 return *this; 2131 } 2132 2133 2134 BString& 2135 BString::operator<<(const BString& string) 2136 { 2137 if (string.Length() > 0) 2138 _DoAppend(string.String(), string.Length()); 2139 return *this; 2140 } 2141 2142 2143 BString& 2144 BString::operator<<(char c) 2145 { 2146 _DoAppend(&c, 1); 2147 return *this; 2148 } 2149 2150 2151 BString& 2152 BString::operator<<(bool value) 2153 { 2154 if (value) 2155 _DoAppend("true", 4); 2156 else 2157 _DoAppend("false", 5); 2158 2159 return *this; 2160 } 2161 2162 2163 BString& 2164 BString::operator<<(int i) 2165 { 2166 char num[32]; 2167 int32 length = snprintf(num, sizeof(num), "%d", i); 2168 2169 _DoAppend(num, length); 2170 return *this; 2171 } 2172 2173 2174 BString& 2175 BString::operator<<(unsigned int i) 2176 { 2177 char num[32]; 2178 int32 length = snprintf(num, sizeof(num), "%u", i); 2179 2180 _DoAppend(num, length); 2181 return *this; 2182 } 2183 2184 2185 BString& 2186 BString::operator<<(unsigned long i) 2187 { 2188 char num[32]; 2189 int32 length = snprintf(num, sizeof(num), "%lu", i); 2190 2191 _DoAppend(num, length); 2192 return *this; 2193 } 2194 2195 2196 BString& 2197 BString::operator<<(long i) 2198 { 2199 char num[32]; 2200 int32 length = snprintf(num, sizeof(num), "%ld", i); 2201 2202 _DoAppend(num, length); 2203 return *this; 2204 } 2205 2206 2207 BString& 2208 BString::operator<<(unsigned long long i) 2209 { 2210 char num[64]; 2211 int32 length = snprintf(num, sizeof(num), "%llu", i); 2212 2213 _DoAppend(num, length); 2214 return *this; 2215 } 2216 2217 2218 BString& 2219 BString::operator<<(long long i) 2220 { 2221 char num[64]; 2222 int32 length = snprintf(num, sizeof(num), "%lld", i); 2223 2224 _DoAppend(num, length); 2225 return *this; 2226 } 2227 2228 2229 BString& 2230 BString::operator<<(float f) 2231 { 2232 char num[64]; 2233 int32 length = snprintf(num, sizeof(num), "%.2f", f); 2234 2235 _DoAppend(num, length); 2236 return *this; 2237 } 2238 2239 2240 BString& 2241 BString::operator<<(double value) 2242 { 2243 char num[64]; 2244 int32 length = snprintf(num, sizeof(num), "%.2f", value); 2245 2246 _DoAppend(num, length); 2247 return *this; 2248 } 2249 2250 2251 // #pragma mark - Private or reserved 2252 2253 2254 BString::BString(char* privateData, PrivateDataTag tag) 2255 : 2256 fPrivateData(privateData) 2257 { 2258 if (fPrivateData != NULL) 2259 atomic_add(&_ReferenceCount(), 1); 2260 } 2261 2262 2263 /*! Detaches this string from an eventually shared fPrivateData, ie. this makes 2264 this string writable. 2265 */ 2266 status_t 2267 BString::_MakeWritable() 2268 { 2269 if (atomic_get(&_ReferenceCount()) > 1) { 2270 // It might be shared, and this requires special treatment 2271 char* newData = _Clone(fPrivateData, Length()); 2272 if (atomic_add(&_ReferenceCount(), -1) == 1) { 2273 // someone else left, we were the last owner 2274 _FreePrivateData(); 2275 } 2276 if (newData == NULL) 2277 return B_NO_MEMORY; 2278 2279 fPrivateData = newData; 2280 } 2281 2282 return B_OK; 2283 } 2284 2285 2286 /*! Makes this string writable, and resizes the buffer to \a length bytes (not 2287 including the terminating null). 2288 2289 @param length The length of the new buffer in bytes. 2290 @param copy If true, the current string will be copied into the new string. 2291 */ 2292 status_t 2293 BString::_MakeWritable(int32 length, bool copy) 2294 { 2295 char* newData = NULL; 2296 2297 if (atomic_get(&_ReferenceCount()) > 1) { 2298 // we might share our data with someone else 2299 if (copy) 2300 newData = _Clone(fPrivateData, length); 2301 else 2302 newData = _Allocate(length); 2303 2304 if (newData == NULL) 2305 return B_NO_MEMORY; 2306 2307 if (atomic_add(&_ReferenceCount(), -1) == 1) { 2308 // someone else left, we were the last owner 2309 _FreePrivateData(); 2310 } 2311 } else { 2312 // we don't share our data with someone else 2313 newData = _Resize(length); 2314 2315 if (newData == NULL) 2316 return B_NO_MEMORY; 2317 } 2318 2319 fPrivateData = newData; 2320 return B_OK; 2321 } 2322 2323 2324 /*! Allocates a new private data buffer with the space to store \a length bytes 2325 (not including the terminating null). 2326 */ 2327 /*static*/ char* 2328 BString::_Allocate(int32 length) 2329 { 2330 if (length < 0) 2331 return NULL; 2332 2333 char* newData = (char*)malloc(length + kPrivateDataOffset + 1); 2334 if (newData == NULL) 2335 return NULL; 2336 2337 newData += kPrivateDataOffset; 2338 newData[length] = '\0'; 2339 2340 // initialize reference count & length 2341 Private::DataRefCount(newData) = 1; 2342 Private::DataLength(newData) = length & 0x7fffffff; 2343 2344 return newData; 2345 } 2346 2347 2348 /*! Resizes the private data buffer. You must already have a writable buffer 2349 when you call this method. 2350 */ 2351 char* 2352 BString::_Resize(int32 length) 2353 { 2354 ASSERT(_ReferenceCount() == 1 || _ReferenceCount() == -1); 2355 2356 if (length == Length()) 2357 return fPrivateData; 2358 2359 char* data = fPrivateData ? fPrivateData - kPrivateDataOffset : NULL; 2360 if (length < 0) 2361 length = 0; 2362 2363 data = (char*)realloc(data, length + kPrivateDataOffset + 1); 2364 if (data == NULL) 2365 return NULL; 2366 2367 data += kPrivateDataOffset; 2368 2369 fPrivateData = data; 2370 fPrivateData[length] = '\0'; 2371 2372 _SetLength(length); 2373 _ReferenceCount() = 1; 2374 2375 return data; 2376 } 2377 2378 2379 void 2380 BString::_Init(const char* src, int32 length) 2381 { 2382 fPrivateData = _Clone(src, length); 2383 if (fPrivateData == NULL) 2384 fPrivateData = _Clone(NULL, 0); 2385 } 2386 2387 2388 char* 2389 BString::_Clone(const char* data, int32 length) 2390 { 2391 char* newData = _Allocate(length); 2392 if (newData == NULL) 2393 return NULL; 2394 2395 if (data != NULL && length > 0) { 2396 // "data" may not span over the whole length 2397 strncpy(newData, data, length); 2398 } 2399 2400 return newData; 2401 } 2402 2403 2404 char* 2405 BString::_OpenAtBy(int32 offset, int32 length) 2406 { 2407 int32 oldLength = Length(); 2408 2409 if (_MakeWritable() != B_OK) 2410 return NULL; 2411 2412 memmove(fPrivateData + offset + length, fPrivateData + offset, 2413 oldLength - offset); 2414 return _Resize(oldLength + length); 2415 } 2416 2417 2418 char* 2419 BString::_ShrinkAtBy(int32 offset, int32 length) 2420 { 2421 int32 oldLength = Length(); 2422 if (_MakeWritable() != B_OK) 2423 return NULL; 2424 2425 memmove(fPrivateData + offset, fPrivateData + offset + length, 2426 oldLength - offset - length); 2427 return _Resize(oldLength - length); 2428 } 2429 2430 2431 void 2432 BString::_SetLength(int32 length) 2433 { 2434 Private::DataLength(fPrivateData) = length & 0x7fffffff; 2435 } 2436 2437 2438 void 2439 BString::_FreePrivateData() 2440 { 2441 if (fPrivateData != NULL) { 2442 free(fPrivateData - kPrivateDataOffset); 2443 fPrivateData = NULL; 2444 } 2445 } 2446 2447 2448 bool 2449 BString::_DoAppend(const char* string, int32 length) 2450 { 2451 int32 oldLength = Length(); 2452 if (_MakeWritable(oldLength + length, true) == B_OK) { 2453 strncpy(fPrivateData + oldLength, string, length); 2454 return true; 2455 } 2456 return false; 2457 } 2458 2459 2460 bool 2461 BString::_DoPrepend(const char* string, int32 length) 2462 { 2463 // TODO: this could be optimized (allocate a new buffer, use memcpy()) 2464 int32 oldLength = Length(); 2465 if (_MakeWritable(oldLength + length, true) == B_OK) { 2466 memmove(fPrivateData + length, fPrivateData, oldLength); 2467 if (string && length) 2468 strncpy(fPrivateData, string, length); 2469 return true; 2470 } 2471 return false; 2472 } 2473 2474 2475 bool 2476 BString::_DoInsert(const char* string, int32 offset, int32 length) 2477 { 2478 int32 oldLength = Length(); 2479 if (_MakeWritable(oldLength + length, true) == B_OK) { 2480 memmove(fPrivateData + offset + length, fPrivateData + offset, 2481 oldLength - offset); 2482 if (string != NULL && length) 2483 strncpy(fPrivateData + offset, string, length); 2484 return true; 2485 } 2486 return false; 2487 } 2488 2489 2490 int32 2491 BString::_ShortFindAfter(const char* string, int32 len) const 2492 { 2493 const char* ptr = strstr(String(), string); 2494 2495 if (ptr != NULL) 2496 return ptr - String(); 2497 2498 return B_ERROR; 2499 } 2500 2501 2502 int32 2503 BString::_FindAfter(const char* string, int32 offset, int32 length) const 2504 { 2505 const char* ptr = strstr(String() + offset, string); 2506 2507 if (ptr != NULL) 2508 return ptr - String(); 2509 2510 return B_ERROR; 2511 } 2512 2513 2514 int32 2515 BString::_IFindAfter(const char* string, int32 offset, int32 length) const 2516 { 2517 const char* ptr = strcasestr(String() + offset, string); 2518 2519 if (ptr != NULL) 2520 return ptr - String(); 2521 2522 return B_ERROR; 2523 } 2524 2525 2526 int32 2527 BString::_FindBefore(const char* string, int32 offset, int32 length) const 2528 { 2529 if (fPrivateData != NULL) { 2530 const char* ptr = fPrivateData + offset - length; 2531 2532 while (ptr >= fPrivateData) { 2533 if (!memcmp(ptr, string, length)) 2534 return ptr - fPrivateData; 2535 ptr--; 2536 } 2537 } 2538 return B_ERROR; 2539 } 2540 2541 2542 int32 2543 BString::_IFindBefore(const char* string, int32 offset, int32 length) const 2544 { 2545 if (fPrivateData != NULL) { 2546 char* ptr1 = fPrivateData + offset - length; 2547 2548 while (ptr1 >= fPrivateData) { 2549 if (!strncasecmp(ptr1, string, length)) 2550 return ptr1 - fPrivateData; 2551 ptr1--; 2552 } 2553 } 2554 return B_ERROR; 2555 } 2556 2557 2558 BString& 2559 BString::_DoCharacterEscape(const char* string, const char* setOfCharsToEscape, 2560 char escapeChar) 2561 { 2562 if (_MakeWritable(string_length(string), false) != B_OK) 2563 return *this; 2564 2565 memcpy(fPrivateData, string, Length()); 2566 2567 PosVect positions; 2568 int32 length = Length(); 2569 int32 pos; 2570 for (int32 offset = 0; offset < length; offset += pos + 1) { 2571 pos = strcspn(fPrivateData + offset, setOfCharsToEscape); 2572 if (pos < length - offset && !positions.Add(offset + pos)) 2573 return *this; 2574 } 2575 2576 uint32 count = positions.CountItems(); 2577 int32 newLength = length + count; 2578 if (!newLength) { 2579 _Resize(0); 2580 return *this; 2581 } 2582 2583 char* newData = _Allocate(newLength); 2584 if (newData) { 2585 char* oldString = fPrivateData; 2586 char* newString = newData; 2587 int32 lastPos = 0; 2588 2589 for (uint32 i = 0; i < count; ++i) { 2590 pos = positions.ItemAt(i); 2591 length = pos - lastPos; 2592 if (length > 0) { 2593 memcpy(newString, oldString, length); 2594 oldString += length; 2595 newString += length; 2596 } 2597 *newString++ = escapeChar; 2598 *newString++ = *oldString++; 2599 lastPos = pos + 1; 2600 } 2601 2602 length = Length() + 1 - lastPos; 2603 if (length > 0) 2604 memcpy(newString, oldString, length); 2605 2606 _FreePrivateData(); 2607 fPrivateData = newData; 2608 } 2609 return *this; 2610 } 2611 2612 2613 BString& 2614 BString::_DoCharacterDeescape(const char* string, char escapeChar) 2615 { 2616 if (_MakeWritable(string_length(string), false) != B_OK) 2617 return *this; 2618 2619 memcpy(fPrivateData, string, Length()); 2620 const char escape[2] = { escapeChar, '\0' }; 2621 return _DoReplace(escape, "", REPLACE_ALL, 0, KEEP_CASE); 2622 } 2623 2624 2625 BString& 2626 BString::_DoReplace(const char* findThis, const char* replaceWith, 2627 int32 maxReplaceCount, int32 fromOffset, bool ignoreCase) 2628 { 2629 if (findThis == NULL || maxReplaceCount <= 0 2630 || fromOffset < 0 || fromOffset >= Length()) 2631 return *this; 2632 2633 int32 findLen = strlen(findThis); 2634 if (findLen == 0) 2635 return *this; 2636 2637 typedef int32 (BString::*TFindMethod)(const char*, int32, int32) const; 2638 TFindMethod findMethod = ignoreCase 2639 ? &BString::_IFindAfter : &BString::_FindAfter; 2640 2641 if (replaceWith == NULL) 2642 replaceWith = ""; 2643 2644 int32 replaceLen = strlen(replaceWith); 2645 int32 lastSrcPos = fromOffset; 2646 PosVect positions; 2647 for (int32 srcPos = 0; maxReplaceCount > 0 2648 && (srcPos = (this->*findMethod)(findThis, lastSrcPos, findLen)) >= 0; 2649 maxReplaceCount--) { 2650 positions.Add(srcPos); 2651 lastSrcPos = srcPos + findLen; 2652 } 2653 _ReplaceAtPositions(&positions, findLen, replaceWith, replaceLen); 2654 return *this; 2655 } 2656 2657 2658 void 2659 BString::_ReplaceAtPositions(const PosVect* positions, int32 searchLength, 2660 const char* with, int32 withLength) 2661 { 2662 int32 length = Length(); 2663 uint32 count = positions->CountItems(); 2664 int32 newLength = length + count * (withLength - searchLength); 2665 if (!newLength) { 2666 _Resize(0); 2667 return; 2668 } 2669 2670 char* newData = _Allocate(newLength); 2671 if (newData == NULL) 2672 return; 2673 2674 char* oldString = fPrivateData; 2675 char* newString = newData; 2676 int32 lastPos = 0; 2677 2678 for (uint32 i = 0; i < count; ++i) { 2679 int32 pos = positions->ItemAt(i); 2680 length = pos - lastPos; 2681 if (length > 0) { 2682 memcpy(newString, oldString, length); 2683 oldString += length; 2684 newString += length; 2685 } 2686 memcpy(newString, with, withLength); 2687 oldString += searchLength; 2688 newString += withLength; 2689 lastPos = pos + searchLength; 2690 } 2691 2692 length = Length() + 1 - lastPos; 2693 if (length > 0) 2694 memcpy(newString, oldString, length); 2695 2696 _FreePrivateData(); 2697 fPrivateData = newData; 2698 } 2699 2700 2701 // #pragma mark - backwards compatibility 2702 2703 2704 /*! Translates to (missing const): 2705 BString& BString::operator<<(BString& string) 2706 */ 2707 extern "C" BString& 2708 __ls__7BStringR7BString(BString* self, BString& string) 2709 { 2710 return self->operator<<(string); 2711 } 2712 2713 2714 #if __GNUC__ > 3 2715 2716 // #pragma mark - BStringRef backwards compatibility 2717 2718 2719 class BStringRef { 2720 public: 2721 BStringRef(BString& string, int32 position); 2722 ~BStringRef() {} 2723 2724 operator char() const; 2725 2726 char* operator&(); 2727 const char* operator&() const; 2728 2729 BStringRef& operator=(char c); 2730 BStringRef& operator=(const BStringRef& rc); 2731 2732 private: 2733 BString& fString; 2734 int32 fPosition; 2735 }; 2736 2737 2738 BStringRef::BStringRef(BString& string, int32 position) 2739 : 2740 fString(string), fPosition(position) 2741 { 2742 } 2743 2744 2745 BStringRef::operator char() const 2746 { 2747 return fPosition < fString.Length() ? fString.fPrivateData[fPosition] : 0; 2748 } 2749 2750 2751 BStringRef& 2752 BStringRef::operator=(char c) 2753 { 2754 fString._MakeWritable(); 2755 fString.fPrivateData[fPosition] = c; 2756 return *this; 2757 } 2758 2759 2760 BStringRef& 2761 BStringRef::operator=(const BStringRef &rc) 2762 { 2763 return operator=(rc.fString.fPrivateData[rc.fPosition]); 2764 } 2765 2766 2767 const char* 2768 BStringRef::operator&() const 2769 { 2770 return &fString.fPrivateData[fPosition]; 2771 } 2772 2773 2774 char* 2775 BStringRef::operator&() 2776 { 2777 if (fString._MakeWritable() != B_OK) 2778 return NULL; 2779 2780 fString._ReferenceCount() = -1; 2781 // mark as unsharable 2782 return &fString.fPrivateData[fPosition]; 2783 } 2784 2785 2786 2787 extern "C" BStringRef 2788 _ZN7BStringixEi(BString* self, int32 index) 2789 2790 { 2791 return BStringRef(*self, index); 2792 } 2793 #endif 2794 2795 2796 // #pragma mark - Non-member compare for sorting, etc. 2797 2798 2799 int 2800 Compare(const BString& string1, const BString& string2) 2801 { 2802 return strcmp(string1.String(), string2.String()); 2803 } 2804 2805 2806 int 2807 ICompare(const BString& string1, const BString& string2) 2808 { 2809 return strcasecmp(string1.String(), string2.String()); 2810 } 2811 2812 2813 int 2814 Compare(const BString* string1, const BString* string2) 2815 { 2816 return strcmp(string1->String(), string2->String()); 2817 } 2818 2819 2820 int 2821 ICompare(const BString* string1, const BString* string2) 2822 { 2823 return strcasecmp(string1->String(), string2->String()); 2824 } 2825