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 char* newData = (char*)realloc(data, length + kPrivateDataOffset + 1); 2364 if (newData == NULL) { 2365 free(data); 2366 data = NULL; 2367 return NULL; 2368 } else { 2369 data = newData; 2370 } 2371 2372 data += kPrivateDataOffset; 2373 2374 fPrivateData = data; 2375 fPrivateData[length] = '\0'; 2376 2377 _SetLength(length); 2378 _ReferenceCount() = 1; 2379 2380 return data; 2381 } 2382 2383 2384 void 2385 BString::_Init(const char* src, int32 length) 2386 { 2387 fPrivateData = _Clone(src, length); 2388 if (fPrivateData == NULL) 2389 fPrivateData = _Clone(NULL, 0); 2390 } 2391 2392 2393 char* 2394 BString::_Clone(const char* data, int32 length) 2395 { 2396 char* newData = _Allocate(length); 2397 if (newData == NULL) 2398 return NULL; 2399 2400 if (data != NULL && length > 0) { 2401 // "data" may not span over the whole length 2402 strncpy(newData, data, length); 2403 } 2404 2405 return newData; 2406 } 2407 2408 2409 char* 2410 BString::_OpenAtBy(int32 offset, int32 length) 2411 { 2412 int32 oldLength = Length(); 2413 2414 if (_MakeWritable() != B_OK) 2415 return NULL; 2416 2417 memmove(fPrivateData + offset + length, fPrivateData + offset, 2418 oldLength - offset); 2419 return _Resize(oldLength + length); 2420 } 2421 2422 2423 char* 2424 BString::_ShrinkAtBy(int32 offset, int32 length) 2425 { 2426 int32 oldLength = Length(); 2427 if (_MakeWritable() != B_OK) 2428 return NULL; 2429 2430 memmove(fPrivateData + offset, fPrivateData + offset + length, 2431 oldLength - offset - length); 2432 return _Resize(oldLength - length); 2433 } 2434 2435 2436 void 2437 BString::_SetLength(int32 length) 2438 { 2439 Private::DataLength(fPrivateData) = length & 0x7fffffff; 2440 } 2441 2442 2443 void 2444 BString::_FreePrivateData() 2445 { 2446 if (fPrivateData != NULL) { 2447 free(fPrivateData - kPrivateDataOffset); 2448 fPrivateData = NULL; 2449 } 2450 } 2451 2452 2453 bool 2454 BString::_DoAppend(const char* string, int32 length) 2455 { 2456 int32 oldLength = Length(); 2457 if (_MakeWritable(oldLength + length, true) == B_OK) { 2458 strncpy(fPrivateData + oldLength, string, length); 2459 return true; 2460 } 2461 return false; 2462 } 2463 2464 2465 bool 2466 BString::_DoPrepend(const char* string, int32 length) 2467 { 2468 // TODO: this could be optimized (allocate a new buffer, use memcpy()) 2469 int32 oldLength = Length(); 2470 if (_MakeWritable(oldLength + length, true) == B_OK) { 2471 memmove(fPrivateData + length, fPrivateData, oldLength); 2472 if (string && length) 2473 strncpy(fPrivateData, string, length); 2474 return true; 2475 } 2476 return false; 2477 } 2478 2479 2480 bool 2481 BString::_DoInsert(const char* string, int32 offset, int32 length) 2482 { 2483 int32 oldLength = Length(); 2484 if (_MakeWritable(oldLength + length, true) == B_OK) { 2485 memmove(fPrivateData + offset + length, fPrivateData + offset, 2486 oldLength - offset); 2487 if (string != NULL && length) 2488 strncpy(fPrivateData + offset, string, length); 2489 return true; 2490 } 2491 return false; 2492 } 2493 2494 2495 int32 2496 BString::_ShortFindAfter(const char* string, int32 len) const 2497 { 2498 const char* ptr = strstr(String(), string); 2499 2500 if (ptr != NULL) 2501 return ptr - String(); 2502 2503 return B_ERROR; 2504 } 2505 2506 2507 int32 2508 BString::_FindAfter(const char* string, int32 offset, int32 length) const 2509 { 2510 const char* ptr = strstr(String() + offset, string); 2511 2512 if (ptr != NULL) 2513 return ptr - String(); 2514 2515 return B_ERROR; 2516 } 2517 2518 2519 int32 2520 BString::_IFindAfter(const char* string, int32 offset, int32 length) const 2521 { 2522 const char* ptr = strcasestr(String() + offset, string); 2523 2524 if (ptr != NULL) 2525 return ptr - String(); 2526 2527 return B_ERROR; 2528 } 2529 2530 2531 int32 2532 BString::_FindBefore(const char* string, int32 offset, int32 length) const 2533 { 2534 if (fPrivateData != NULL) { 2535 const char* ptr = fPrivateData + offset - length; 2536 2537 while (ptr >= fPrivateData) { 2538 if (!memcmp(ptr, string, length)) 2539 return ptr - fPrivateData; 2540 ptr--; 2541 } 2542 } 2543 return B_ERROR; 2544 } 2545 2546 2547 int32 2548 BString::_IFindBefore(const char* string, int32 offset, int32 length) const 2549 { 2550 if (fPrivateData != NULL) { 2551 char* ptr1 = fPrivateData + offset - length; 2552 2553 while (ptr1 >= fPrivateData) { 2554 if (!strncasecmp(ptr1, string, length)) 2555 return ptr1 - fPrivateData; 2556 ptr1--; 2557 } 2558 } 2559 return B_ERROR; 2560 } 2561 2562 2563 BString& 2564 BString::_DoCharacterEscape(const char* string, const char* setOfCharsToEscape, 2565 char escapeChar) 2566 { 2567 if (_MakeWritable(string_length(string), false) != B_OK) 2568 return *this; 2569 2570 memcpy(fPrivateData, string, Length()); 2571 2572 PosVect positions; 2573 int32 length = Length(); 2574 int32 pos; 2575 for (int32 offset = 0; offset < length; offset += pos + 1) { 2576 pos = strcspn(fPrivateData + offset, setOfCharsToEscape); 2577 if (pos < length - offset && !positions.Add(offset + pos)) 2578 return *this; 2579 } 2580 2581 uint32 count = positions.CountItems(); 2582 int32 newLength = length + count; 2583 if (!newLength) { 2584 _Resize(0); 2585 return *this; 2586 } 2587 2588 char* newData = _Allocate(newLength); 2589 if (newData) { 2590 char* oldString = fPrivateData; 2591 char* newString = newData; 2592 int32 lastPos = 0; 2593 2594 for (uint32 i = 0; i < count; ++i) { 2595 pos = positions.ItemAt(i); 2596 length = pos - lastPos; 2597 if (length > 0) { 2598 memcpy(newString, oldString, length); 2599 oldString += length; 2600 newString += length; 2601 } 2602 *newString++ = escapeChar; 2603 *newString++ = *oldString++; 2604 lastPos = pos + 1; 2605 } 2606 2607 length = Length() + 1 - lastPos; 2608 if (length > 0) 2609 memcpy(newString, oldString, length); 2610 2611 _FreePrivateData(); 2612 fPrivateData = newData; 2613 } 2614 return *this; 2615 } 2616 2617 2618 BString& 2619 BString::_DoCharacterDeescape(const char* string, char escapeChar) 2620 { 2621 if (_MakeWritable(string_length(string), false) != B_OK) 2622 return *this; 2623 2624 memcpy(fPrivateData, string, Length()); 2625 const char escape[2] = { escapeChar, '\0' }; 2626 return _DoReplace(escape, "", REPLACE_ALL, 0, KEEP_CASE); 2627 } 2628 2629 2630 BString& 2631 BString::_DoReplace(const char* findThis, const char* replaceWith, 2632 int32 maxReplaceCount, int32 fromOffset, bool ignoreCase) 2633 { 2634 if (findThis == NULL || maxReplaceCount <= 0 2635 || fromOffset < 0 || fromOffset >= Length()) 2636 return *this; 2637 2638 int32 findLen = strlen(findThis); 2639 if (findLen == 0) 2640 return *this; 2641 2642 typedef int32 (BString::*TFindMethod)(const char*, int32, int32) const; 2643 TFindMethod findMethod = ignoreCase 2644 ? &BString::_IFindAfter : &BString::_FindAfter; 2645 2646 if (replaceWith == NULL) 2647 replaceWith = ""; 2648 2649 int32 replaceLen = strlen(replaceWith); 2650 int32 lastSrcPos = fromOffset; 2651 PosVect positions; 2652 for (int32 srcPos = 0; maxReplaceCount > 0 2653 && (srcPos = (this->*findMethod)(findThis, lastSrcPos, findLen)) >= 0; 2654 maxReplaceCount--) { 2655 positions.Add(srcPos); 2656 lastSrcPos = srcPos + findLen; 2657 } 2658 _ReplaceAtPositions(&positions, findLen, replaceWith, replaceLen); 2659 return *this; 2660 } 2661 2662 2663 void 2664 BString::_ReplaceAtPositions(const PosVect* positions, int32 searchLength, 2665 const char* with, int32 withLength) 2666 { 2667 int32 length = Length(); 2668 uint32 count = positions->CountItems(); 2669 int32 newLength = length + count * (withLength - searchLength); 2670 if (!newLength) { 2671 _Resize(0); 2672 return; 2673 } 2674 2675 char* newData = _Allocate(newLength); 2676 if (newData == NULL) 2677 return; 2678 2679 char* oldString = fPrivateData; 2680 char* newString = newData; 2681 int32 lastPos = 0; 2682 2683 for (uint32 i = 0; i < count; ++i) { 2684 int32 pos = positions->ItemAt(i); 2685 length = pos - lastPos; 2686 if (length > 0) { 2687 memcpy(newString, oldString, length); 2688 oldString += length; 2689 newString += length; 2690 } 2691 memcpy(newString, with, withLength); 2692 oldString += searchLength; 2693 newString += withLength; 2694 lastPos = pos + searchLength; 2695 } 2696 2697 length = Length() + 1 - lastPos; 2698 if (length > 0) 2699 memcpy(newString, oldString, length); 2700 2701 _FreePrivateData(); 2702 fPrivateData = newData; 2703 } 2704 2705 2706 // #pragma mark - backwards compatibility 2707 2708 2709 /*! Translates to (missing const): 2710 BString& BString::operator<<(BString& string) 2711 */ 2712 extern "C" BString& 2713 __ls__7BStringR7BString(BString* self, BString& string) 2714 { 2715 return self->operator<<(string); 2716 } 2717 2718 2719 #if __GNUC__ > 3 2720 2721 // #pragma mark - BStringRef backwards compatibility 2722 2723 2724 class BStringRef { 2725 public: 2726 BStringRef(BString& string, int32 position); 2727 ~BStringRef() {} 2728 2729 operator char() const; 2730 2731 char* operator&(); 2732 const char* operator&() const; 2733 2734 BStringRef& operator=(char c); 2735 BStringRef& operator=(const BStringRef& rc); 2736 2737 private: 2738 BString& fString; 2739 int32 fPosition; 2740 }; 2741 2742 2743 BStringRef::BStringRef(BString& string, int32 position) 2744 : 2745 fString(string), fPosition(position) 2746 { 2747 } 2748 2749 2750 BStringRef::operator char() const 2751 { 2752 return fPosition < fString.Length() ? fString.fPrivateData[fPosition] : 0; 2753 } 2754 2755 2756 BStringRef& 2757 BStringRef::operator=(char c) 2758 { 2759 fString._MakeWritable(); 2760 fString.fPrivateData[fPosition] = c; 2761 return *this; 2762 } 2763 2764 2765 BStringRef& 2766 BStringRef::operator=(const BStringRef &rc) 2767 { 2768 return operator=(rc.fString.fPrivateData[rc.fPosition]); 2769 } 2770 2771 2772 const char* 2773 BStringRef::operator&() const 2774 { 2775 return &fString.fPrivateData[fPosition]; 2776 } 2777 2778 2779 char* 2780 BStringRef::operator&() 2781 { 2782 if (fString._MakeWritable() != B_OK) 2783 return NULL; 2784 2785 fString._ReferenceCount() = -1; 2786 // mark as unsharable 2787 return &fString.fPrivateData[fPosition]; 2788 } 2789 2790 2791 2792 extern "C" BStringRef 2793 _ZN7BStringixEi(BString* self, int32 index) 2794 2795 { 2796 return BStringRef(*self, index); 2797 } 2798 #endif 2799 2800 2801 // #pragma mark - Non-member compare for sorting, etc. 2802 2803 2804 int 2805 Compare(const BString& string1, const BString& string2) 2806 { 2807 return strcmp(string1.String(), string2.String()); 2808 } 2809 2810 2811 int 2812 ICompare(const BString& string1, const BString& string2) 2813 { 2814 return strcasecmp(string1.String(), string2.String()); 2815 } 2816 2817 2818 int 2819 Compare(const BString* string1, const BString* string2) 2820 { 2821 return strcmp(string1->String(), string2->String()); 2822 } 2823 2824 2825 int 2826 ICompare(const BString* string1, const BString* string2) 2827 { 2828 return strcasecmp(string1->String(), string2->String()); 2829 } 2830