1 /* 2 * Copyright 2001-2008, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Stefano Ceccherini (burton666@libero.it) 8 * Oliver Tappe (openbeos@hirschkaefer.de) 9 * Axel Dörfler, axeld@pinc-software.de 10 * Julun <host.haiku@gmx.de> 11 */ 12 13 /*! String class supporting common string operations. */ 14 15 16 #include <Debug.h> 17 #include <String.h> 18 19 #include <ctype.h> 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 24 25 // Set this to 1 to make some private methods inline 26 #define ENABLE_INLINES 0 27 28 // define proper names for case-option of _DoReplace() 29 #define KEEP_CASE false 30 #define IGNORE_CASE true 31 32 // define proper names for count-option of _DoReplace() 33 #define REPLACE_ALL 0x7FFFFFFF 34 35 36 const uint32 kPrivateDataOffset = 2 * sizeof(int32); 37 38 const char *B_EMPTY_STRING = ""; 39 40 41 // helper function, returns minimum of two given values (but clamps to 0): 42 static inline int32 43 min_clamp0(int32 num1, int32 num2) 44 { 45 if (num1 < num2) 46 return num1 > 0 ? num1 : 0; 47 48 return num2 > 0 ? num2 : 0; 49 } 50 51 52 //! helper function, returns length of given string (but clamps to given maximum): 53 static inline int32 54 strlen_clamp(const char* str, int32 max) 55 { 56 // this should yield 0 for max<0: 57 int32 length = 0; 58 while (length < max && *str++) { 59 length++; 60 } 61 return length; 62 } 63 64 65 //! helper function, massages given pointer into a legal c-string: 66 static inline const char * 67 safestr(const char* str) 68 { 69 return str ? str : ""; 70 } 71 72 73 // #pragma mark - PosVect 74 75 76 class BString::PosVect { 77 public: 78 PosVect() 79 : fSize(0), 80 fBufferSize(20), 81 fBuffer(NULL) { } 82 83 ~PosVect() 84 { 85 free(fBuffer); 86 } 87 88 bool Add(int32 pos) 89 { 90 if (fBuffer == NULL || fSize == fBufferSize) { 91 if (fBuffer != NULL) 92 fBufferSize *= 2; 93 94 int32* newBuffer = NULL; 95 newBuffer = (int32 *)realloc(fBuffer, fBufferSize * sizeof(int32)); 96 if (newBuffer == NULL) 97 return false; 98 99 fBuffer = newBuffer; 100 } 101 102 fBuffer[fSize++] = pos; 103 return true; 104 } 105 106 inline int32 ItemAt(int32 index) const 107 { return fBuffer[index]; } 108 inline int32 CountItems() const 109 { return fSize; } 110 111 private: 112 int32 fSize; 113 int32 fBufferSize; 114 int32* fBuffer; 115 }; 116 117 118 // #pragma mark - BStringRef 119 120 121 BStringRef::BStringRef(BString& string, int32 position) 122 : fString(string), fPosition(position) 123 { 124 } 125 126 127 BStringRef::operator char() const 128 { 129 return fPosition < fString.Length() ? fString.fPrivateData[fPosition] : 0; 130 } 131 132 133 BStringRef& 134 BStringRef::operator=(char c) 135 { 136 fString._Detach(); 137 fString.fPrivateData[fPosition] = c; 138 return *this; 139 } 140 141 142 BStringRef& 143 BStringRef::operator=(const BStringRef &rc) 144 { 145 return operator=(rc.fString.fPrivateData[rc.fPosition]); 146 } 147 148 149 const char* 150 BStringRef::operator&() const 151 { 152 return &fString.fPrivateData[fPosition]; 153 } 154 155 156 char* 157 BStringRef::operator&() 158 { 159 if (fString._Detach() != B_OK) 160 return NULL; 161 162 fString._ReferenceCount() = -1; 163 // mark as unsharable 164 return &fString.fPrivateData[fPosition]; 165 } 166 167 168 // #pragma mark - BString 169 170 171 BString::BString() 172 : fPrivateData(NULL) 173 { 174 _Init("", 0); 175 } 176 177 178 BString::BString(const char* string) 179 : fPrivateData(NULL) 180 { 181 _Init(string, strlen(safestr(string))); 182 } 183 184 185 BString::BString(const BString& string) 186 : fPrivateData(NULL) 187 { 188 // check if source is sharable - if so, share else clone 189 if (string._IsShareable()) { 190 fPrivateData = string.fPrivateData; 191 atomic_add(&_ReferenceCount(), 1); 192 // string cannot go away right now 193 } else 194 _Init(string.String(), string.Length()); 195 } 196 197 198 BString::BString(const char* string, int32 maxLength) 199 : fPrivateData(NULL) 200 { 201 _Init(string, strlen_clamp(safestr(string), maxLength)); 202 } 203 204 205 BString::~BString() 206 { 207 if (!_IsShareable() || atomic_add(&_ReferenceCount(), -1) == 1) 208 _FreePrivateData(); 209 } 210 211 212 // #pragma mark - Access 213 214 215 int32 216 BString::CountChars() const 217 { 218 int32 count = 0; 219 220 const char *start = fPrivateData; 221 const char *end = fPrivateData + Length(); 222 223 while (start++ != end) { 224 count++; 225 226 // Jump to next UTF8 character 227 for (; (*start & 0xc0) == 0x80; start++); 228 } 229 230 return count; 231 } 232 233 234 // #pragma mark - Assignment 235 236 237 BString& 238 BString::operator=(const BString& string) 239 { 240 return SetTo(string); 241 } 242 243 244 BString& 245 BString::operator=(const char* string) 246 { 247 if (string && string != String()) 248 SetTo(string, strlen(string)); 249 return *this; 250 } 251 252 253 BString& 254 BString::operator=(char c) 255 { 256 return SetTo(c, 1); 257 } 258 259 260 BString& 261 BString::SetTo(const char* string, int32 maxLength) 262 { 263 if (maxLength < 0) 264 maxLength = INT32_MAX; 265 maxLength = strlen_clamp(safestr(string), maxLength); 266 if (_DetachWith("", maxLength) == B_OK) 267 memcpy(fPrivateData, string, maxLength); 268 269 return *this; 270 } 271 272 273 BString& 274 BString::SetTo(const BString& string) 275 { 276 // we share the information already 277 if (fPrivateData == string.fPrivateData) 278 return *this; 279 280 bool freeData = true; 281 282 if (_IsShareable() && atomic_add(&_ReferenceCount(), -1) > 1) { 283 // there is still someone who shares our data 284 freeData = false; 285 } 286 287 if (freeData) 288 _FreePrivateData(); 289 290 // if source is sharable share, otherwise clone 291 if (string._IsShareable()) { 292 fPrivateData = string.fPrivateData; 293 atomic_add(&_ReferenceCount(), 1); 294 // the string cannot go away right now 295 } else 296 _Init(string.String(), string.Length()); 297 298 return *this; 299 } 300 301 302 BString& 303 BString::Adopt(BString& from) 304 { 305 SetTo(from); 306 from.SetTo(""); 307 308 return *this; 309 } 310 311 312 BString& 313 BString::SetTo(const BString& string, int32 maxLength) 314 { 315 if (maxLength < 0) 316 maxLength = INT32_MAX; 317 if (fPrivateData != string.fPrivateData 318 // make sure we reassing in case length is different 319 || (fPrivateData == string.fPrivateData && Length() > maxLength)) { 320 maxLength = min_clamp0(maxLength, string.Length()); 321 if (_DetachWith("", maxLength) == B_OK) 322 memcpy(fPrivateData, string.String(), maxLength); 323 } 324 return *this; 325 } 326 327 328 BString& 329 BString::Adopt(BString& from, int32 maxLength) 330 { 331 SetTo(from, maxLength); 332 from.SetTo(""); 333 334 return *this; 335 } 336 337 338 BString& 339 BString::SetTo(char c, int32 count) 340 { 341 if (count < 0) 342 count = 0; 343 344 if (_DetachWith("", count) == B_OK) 345 memset(fPrivateData, c, count); 346 return *this; 347 } 348 349 350 // #pragma mark - Substring copying 351 352 353 BString& 354 BString::CopyInto(BString& into, int32 fromOffset, int32 length) const 355 { 356 if (this != &into) 357 into.SetTo(fPrivateData + fromOffset, length); 358 return into; 359 } 360 361 362 void 363 BString::CopyInto(char* into, int32 fromOffset, int32 length) const 364 { 365 if (into) { 366 length = min_clamp0(length, Length() - fromOffset); 367 memcpy(into, fPrivateData + fromOffset, length); 368 } 369 } 370 371 372 // #pragma mark - Appending 373 374 375 BString& 376 BString::operator+=(const char* string) 377 { 378 if (string) { 379 int32 length = strlen(string); 380 if (length > 0) 381 _DoAppend(string, length); 382 } 383 return *this; 384 } 385 386 387 BString& 388 BString::operator+=(char c) 389 { 390 _DoAppend(&c, 1); 391 return *this; 392 } 393 394 395 BString& 396 BString::Append(const BString& string, int32 length) 397 { 398 if (&string != this) { 399 length = min_clamp0(length, string.Length()); 400 if (length > 0) 401 _DoAppend(string.fPrivateData, length); 402 } 403 return *this; 404 } 405 406 407 BString& 408 BString::Append(const char* string, int32 length) 409 { 410 if (string) { 411 length = strlen_clamp(string, length); 412 if (length > 0) 413 _DoAppend(string, length); 414 } 415 return *this; 416 } 417 418 419 BString& 420 BString::Append(char c, int32 count) 421 { 422 int32 oldLength = Length(); 423 if (count > 0 && _DoAppend("", count)) 424 memset(fPrivateData + oldLength, c, count); 425 return *this; 426 } 427 428 429 // #pragma mark - Prepending 430 431 432 BString& 433 BString::Prepend(const char* string) 434 { 435 if (string) 436 _DoPrepend(string, strlen(string)); 437 return *this; 438 } 439 440 441 BString& 442 BString::Prepend(const BString& string) 443 { 444 if (&string != this) 445 _DoPrepend(string.String(), string.Length()); 446 return *this; 447 } 448 449 450 BString& 451 BString::Prepend(const char* string, int32 length) 452 { 453 if (string) 454 _DoPrepend(string, strlen_clamp(string, length)); 455 return *this; 456 } 457 458 459 BString& 460 BString::Prepend(const BString& string, int32 length) 461 { 462 if (&string != this) 463 _DoPrepend(string.fPrivateData, min_clamp0(length, string.Length())); 464 return *this; 465 } 466 467 468 BString& 469 BString::Prepend(char c, int32 count) 470 { 471 if (count > 0 && _DoPrepend("", count)) 472 memset(fPrivateData, c, count); 473 return *this; 474 } 475 476 477 // #pragma mark - Inserting 478 479 480 BString& 481 BString::Insert(const char* string, int32 position) 482 { 483 if (string && position <= Length()) { 484 int32 len = int32(strlen(string)); 485 if (position < 0) { 486 int32 skipLen = min_clamp0(-1 * position, len); 487 string += skipLen; 488 len -= skipLen; 489 position = 0; 490 } else { 491 position = min_clamp0(position, Length()); 492 } 493 _DoInsert(string, position, len); 494 } 495 return *this; 496 } 497 498 499 BString& 500 BString::Insert(const char* string, int32 length, int32 position) 501 { 502 if (string && position <= Length()) { 503 int32 len = strlen_clamp(string, length); 504 if (position < 0) { 505 int32 skipLen = min_clamp0(-1 * position, len); 506 string += skipLen; 507 len -= skipLen; 508 position = 0; 509 } else { 510 position = min_clamp0(position, Length()); 511 } 512 _DoInsert(string, position, len); 513 } 514 return *this; 515 } 516 517 518 BString& 519 BString::Insert(const char* string, int32 fromOffset, int32 length, 520 int32 position) 521 { 522 if (string) 523 Insert(string + fromOffset, length, position); 524 return *this; 525 } 526 527 528 BString& 529 BString::Insert(const BString& string, int32 position) 530 { 531 if (&string != this && string.Length() > 0) 532 Insert(string.fPrivateData, position); 533 return *this; 534 } 535 536 537 BString& 538 BString::Insert(const BString& string, int32 length, int32 position) 539 { 540 if (&string != this && string.Length() > 0) 541 Insert(string.String(), length, position); 542 return *this; 543 } 544 545 546 BString& 547 BString::Insert(const BString& string, int32 fromOffset, int32 length, 548 int32 position) 549 { 550 if (&string != this && string.Length() > 0) 551 Insert(string.String() + fromOffset, length, position); 552 return *this; 553 } 554 555 556 BString& 557 BString::Insert(char c, int32 count, int32 position) 558 { 559 if (position < 0) { 560 count = MAX(count + position, 0); 561 position = 0; 562 } else 563 position = min_clamp0(position, Length()); 564 565 if (count > 0 && _DoInsert("", position, count)) 566 memset(fPrivateData + position, c, count); 567 568 return *this; 569 } 570 571 572 // #pragma mark - Removing 573 574 575 BString& 576 BString::Truncate(int32 newLength, bool lazy) 577 { 578 if (newLength < 0) 579 newLength = 0; 580 581 if (newLength < Length()) 582 // ignore lazy, since we might detach 583 _DetachWith(fPrivateData, newLength); 584 585 return *this; 586 } 587 588 589 BString& 590 BString::Remove(int32 from, int32 length) 591 { 592 if (length > 0 && from < Length()) 593 _ShrinkAtBy(from, min_clamp0(length, (Length() - from))); 594 return *this; 595 } 596 597 598 BString& 599 BString::RemoveFirst(const BString& string) 600 { 601 if (string.Length() > 0) { 602 int32 pos = _ShortFindAfter(string.String(), string.Length()); 603 if (pos >= 0) 604 _ShrinkAtBy(pos, string.Length()); 605 } 606 return *this; 607 } 608 609 610 BString& 611 BString::RemoveLast(const BString& string) 612 { 613 int32 pos = _FindBefore(string.String(), Length(), string.Length()); 614 if (pos >= 0) 615 _ShrinkAtBy(pos, string.Length()); 616 617 return *this; 618 } 619 620 621 BString& 622 BString::RemoveAll(const BString& string) 623 { 624 if (string.Length() == 0 || Length() == 0 || FindFirst(string) < 0) 625 return *this; 626 627 if (_Detach() != B_OK) 628 return *this; 629 630 return _DoReplace(string.String(), "", REPLACE_ALL, 0, KEEP_CASE); 631 } 632 633 634 BString& 635 BString::RemoveFirst(const char* string) 636 { 637 int32 length = string ? strlen(string) : 0; 638 if (length > 0) { 639 int32 pos = _ShortFindAfter(string, length); 640 if (pos >= 0) 641 _ShrinkAtBy(pos, length); 642 } 643 return *this; 644 } 645 646 647 BString& 648 BString::RemoveLast(const char* string) 649 { 650 int32 length = string ? strlen(string) : 0; 651 if (length > 0) { 652 int32 pos = _FindBefore(string, Length(), length); 653 if (pos >= 0) 654 _ShrinkAtBy(pos, length); 655 } 656 return *this; 657 } 658 659 660 BString& 661 BString::RemoveAll(const char* string) 662 { 663 if (!string || Length() == 0 || FindFirst(string) < 0) 664 return *this; 665 666 if (_Detach() != B_OK) 667 return *this; 668 669 return _DoReplace(string, "", REPLACE_ALL, 0, KEEP_CASE); 670 } 671 672 673 BString& 674 BString::RemoveSet(const char* setOfCharsToRemove) 675 { 676 return ReplaceSet(setOfCharsToRemove, ""); 677 } 678 679 680 BString& 681 BString::MoveInto(BString& into, int32 from, int32 length) 682 { 683 if (length) { 684 CopyInto(into, from, length); 685 Remove(from, length); 686 } 687 return into; 688 } 689 690 691 void 692 BString::MoveInto(char* into, int32 from, int32 length) 693 { 694 if (into) { 695 CopyInto(into, from, length); 696 Remove(from, length); 697 } 698 } 699 700 701 // #pragma mark - Compare functions 702 703 704 bool 705 BString::operator<(const char* string) const 706 { 707 return strcmp(String(), safestr(string)) < 0; 708 } 709 710 711 bool 712 BString::operator<=(const char* string) const 713 { 714 return strcmp(String(), safestr(string)) <= 0; 715 } 716 717 718 bool 719 BString::operator==(const char* string) const 720 { 721 return strcmp(String(), safestr(string)) == 0; 722 } 723 724 725 bool 726 BString::operator>=(const char* string) const 727 { 728 return strcmp(String(), safestr(string)) >= 0; 729 } 730 731 732 bool 733 BString::operator>(const char* string) const 734 { 735 return strcmp(String(), safestr(string)) > 0; 736 } 737 738 739 // #pragma mark - strcmp()-style compare functions 740 741 742 int 743 BString::Compare(const BString& string) const 744 { 745 return strcmp(String(), string.String()); 746 } 747 748 749 int 750 BString::Compare(const char* string) const 751 { 752 return strcmp(String(), safestr(string)); 753 } 754 755 756 int 757 BString::Compare(const BString& string, int32 length) const 758 { 759 return strncmp(String(), string.String(), length); 760 } 761 762 763 int 764 BString::Compare(const char* string, int32 length) const 765 { 766 return strncmp(String(), safestr(string), length); 767 } 768 769 770 int 771 BString::ICompare(const BString& string) const 772 { 773 return strcasecmp(String(), string.String()); 774 } 775 776 777 int 778 BString::ICompare(const char* string) const 779 { 780 return strcasecmp(String(), safestr(string)); 781 } 782 783 784 int 785 BString::ICompare(const BString& string, int32 length) const 786 { 787 return strncasecmp(String(), string.String(), length); 788 } 789 790 791 int 792 BString::ICompare(const char* string, int32 length) const 793 { 794 return strncasecmp(String(), safestr(string), length); 795 } 796 797 798 // #pragma mark - Searching 799 800 801 int32 802 BString::FindFirst(const BString& string) const 803 { 804 return _ShortFindAfter(string.String(), string.Length()); 805 } 806 807 808 int32 809 BString::FindFirst(const char* string) const 810 { 811 if (string == NULL) 812 return B_BAD_VALUE; 813 814 return _ShortFindAfter(string, strlen(string)); 815 } 816 817 818 int32 819 BString::FindFirst(const BString& string, int32 fromOffset) const 820 { 821 if (fromOffset < 0) 822 return B_ERROR; 823 824 return _FindAfter(string.String(), min_clamp0(fromOffset, Length()), 825 string.Length()); 826 } 827 828 829 int32 830 BString::FindFirst(const char* string, int32 fromOffset) const 831 { 832 if (string == NULL) 833 return B_BAD_VALUE; 834 835 if (fromOffset < 0) 836 return B_ERROR; 837 838 return _FindAfter(string, min_clamp0(fromOffset, Length()), 839 strlen(safestr(string))); 840 } 841 842 843 int32 844 BString::FindFirst(char c) const 845 { 846 const char *start = String(); 847 const char *end = String() + Length(); 848 849 // Scans the string until we found the 850 // character, or we reach the string's start 851 while (start != end && *start != c) { 852 start++; 853 } 854 855 if (start == end) 856 return B_ERROR; 857 858 return start - String(); 859 } 860 861 862 int32 863 BString::FindFirst(char c, int32 fromOffset) const 864 { 865 if (fromOffset < 0) 866 return B_ERROR; 867 868 const char *start = String() + min_clamp0(fromOffset, Length()); 869 const char *end = String() + Length(); 870 871 // Scans the string until we found the 872 // character, or we reach the string's start 873 while (start < end && *start != c) { 874 start++; 875 } 876 877 if (start >= end) 878 return B_ERROR; 879 880 return start - String(); 881 } 882 883 884 int32 885 BString::FindLast(const BString& string) const 886 { 887 return _FindBefore(string.String(), Length(), string.Length()); 888 } 889 890 891 int32 892 BString::FindLast(const char* string) const 893 { 894 if (string == NULL) 895 return B_BAD_VALUE; 896 897 return _FindBefore(string, Length(), strlen(safestr(string))); 898 } 899 900 901 int32 902 BString::FindLast(const BString& string, int32 beforeOffset) const 903 { 904 if (beforeOffset < 0) 905 return B_ERROR; 906 907 return _FindBefore(string.String(), min_clamp0(beforeOffset, Length()), 908 string.Length()); 909 } 910 911 912 int32 913 BString::FindLast(const char* string, int32 beforeOffset) const 914 { 915 if (string == NULL) 916 return B_BAD_VALUE; 917 918 if (beforeOffset < 0) 919 return B_ERROR; 920 921 return _FindBefore(string, min_clamp0(beforeOffset, Length()), 922 strlen(safestr(string))); 923 } 924 925 926 int32 927 BString::FindLast(char c) const 928 { 929 const char *start = String(); 930 const char *end = String() + Length(); 931 932 // Scans the string backwards until we found 933 // the character, or we reach the string's start 934 while (end != start && *end != c) { 935 end--; 936 } 937 938 if (end == start) 939 return B_ERROR; 940 941 return end - String(); 942 } 943 944 945 int32 946 BString::FindLast(char c, int32 beforeOffset) const 947 { 948 if (beforeOffset < 0) 949 return B_ERROR; 950 951 const char *start = String(); 952 const char *end = String() + min_clamp0(beforeOffset, Length()); 953 954 // Scans the string backwards until we found 955 // the character, or we reach the string's start 956 while (end > start && *end != c) { 957 end--; 958 } 959 960 if (end <= start) 961 return B_ERROR; 962 963 return end - String(); 964 } 965 966 967 int32 968 BString::IFindFirst(const BString& string) const 969 { 970 return _IFindAfter(string.String(), 0, string.Length()); 971 } 972 973 974 int32 975 BString::IFindFirst(const char* string) const 976 { 977 if (string == NULL) 978 return B_BAD_VALUE; 979 980 return _IFindAfter(string, 0, strlen(safestr(string))); 981 } 982 983 984 int32 985 BString::IFindFirst(const BString& string, int32 fromOffset) const 986 { 987 if (fromOffset < 0) 988 return B_ERROR; 989 990 return _IFindAfter(string.String(), min_clamp0(fromOffset, Length()), 991 string.Length()); 992 } 993 994 995 int32 996 BString::IFindFirst(const char* string, int32 fromOffset) const 997 { 998 if (string == NULL) 999 return B_BAD_VALUE; 1000 1001 if (fromOffset < 0) 1002 return B_ERROR; 1003 1004 return _IFindAfter(string, min_clamp0(fromOffset,Length()), 1005 strlen(safestr(string))); 1006 } 1007 1008 1009 int32 1010 BString::IFindLast(const BString& string) const 1011 { 1012 return _IFindBefore(string.String(), Length(), string.Length()); 1013 } 1014 1015 1016 int32 1017 BString::IFindLast(const char* string) const 1018 { 1019 if (string == NULL) 1020 return B_BAD_VALUE; 1021 1022 return _IFindBefore(string, Length(), strlen(safestr(string))); 1023 } 1024 1025 1026 int32 1027 BString::IFindLast(const BString& string, int32 beforeOffset) const 1028 { 1029 if (beforeOffset < 0) 1030 return B_ERROR; 1031 1032 return _IFindBefore(string.String(), min_clamp0(beforeOffset, Length()), 1033 string.Length()); 1034 } 1035 1036 1037 int32 1038 BString::IFindLast(const char* string, int32 beforeOffset) const 1039 { 1040 if (string == NULL) 1041 return B_BAD_VALUE; 1042 1043 if (beforeOffset < 0) 1044 return B_ERROR; 1045 1046 return _IFindBefore(string, min_clamp0(beforeOffset, Length()), 1047 strlen(safestr(string))); 1048 } 1049 1050 1051 // #pragma mark - Replacing 1052 1053 1054 BString& 1055 BString::ReplaceFirst(char replaceThis, char withThis) 1056 { 1057 int32 pos = FindFirst(replaceThis); 1058 if (pos >= 0 && _Detach() == B_OK) 1059 fPrivateData[pos] = withThis; 1060 return *this; 1061 } 1062 1063 1064 BString& 1065 BString::ReplaceLast(char replaceThis, char withThis) 1066 { 1067 int32 pos = FindLast(replaceThis); 1068 if (pos >= 0 && _Detach() == B_OK) 1069 fPrivateData[pos] = withThis; 1070 return *this; 1071 } 1072 1073 1074 BString& 1075 BString::ReplaceAll(char replaceThis, char withThis, int32 fromOffset) 1076 { 1077 fromOffset = min_clamp0(fromOffset, Length()); 1078 int32 pos = FindFirst(replaceThis, fromOffset); 1079 1080 // detach and set first match 1081 if (pos >= 0 && _Detach() == B_OK) { 1082 fPrivateData[pos] = withThis; 1083 for (pos = pos;;) { 1084 pos = FindFirst(replaceThis, pos); 1085 if (pos < 0) 1086 break; 1087 fPrivateData[pos] = withThis; 1088 } 1089 } 1090 return *this; 1091 } 1092 1093 1094 BString& 1095 BString::Replace(char replaceThis, char withThis, int32 maxReplaceCount, 1096 int32 fromOffset) 1097 { 1098 fromOffset = min_clamp0(fromOffset, Length()); 1099 int32 pos = FindFirst(replaceThis, fromOffset); 1100 1101 if (maxReplaceCount > 0 && pos >= 0 && _Detach() == B_OK) { 1102 maxReplaceCount--; 1103 fPrivateData[pos] = withThis; 1104 for (pos = pos; maxReplaceCount > 0; maxReplaceCount--) { 1105 pos = FindFirst(replaceThis, pos); 1106 if (pos < 0) 1107 break; 1108 fPrivateData[pos] = withThis; 1109 } 1110 } 1111 return *this; 1112 } 1113 1114 1115 BString& 1116 BString::ReplaceFirst(const char* replaceThis, const char* withThis) 1117 { 1118 if (!replaceThis || !withThis || FindFirst(replaceThis) < 0) 1119 return *this; 1120 1121 if (_Detach() != B_OK) 1122 return *this; 1123 1124 return _DoReplace(replaceThis, withThis, 1, 0, KEEP_CASE); 1125 } 1126 1127 1128 BString& 1129 BString::ReplaceLast(const char* replaceThis, const char* withThis) 1130 { 1131 if (!replaceThis || !withThis) 1132 return *this; 1133 1134 int32 replaceThisLength = strlen(replaceThis); 1135 int32 pos = _FindBefore(replaceThis, Length(), replaceThisLength); 1136 1137 if (pos >= 0) { 1138 int32 withThisLength = strlen(withThis); 1139 int32 difference = withThisLength - replaceThisLength; 1140 1141 if (difference > 0) { 1142 if (!_OpenAtBy(pos, difference)) 1143 return *this; 1144 } else if (difference < 0) { 1145 if (!_ShrinkAtBy(pos, -difference)) 1146 return *this; 1147 } else { 1148 if (_Detach() != B_OK) 1149 return *this; 1150 } 1151 memcpy(fPrivateData + pos, withThis, withThisLength); 1152 } 1153 1154 return *this; 1155 } 1156 1157 1158 BString& 1159 BString::ReplaceAll(const char* replaceThis, const char* withThis, 1160 int32 fromOffset) 1161 { 1162 if (!replaceThis || !withThis || FindFirst(replaceThis) < 0) 1163 return *this; 1164 1165 if (_Detach() != B_OK) 1166 return *this; 1167 1168 return _DoReplace(replaceThis, withThis, REPLACE_ALL, 1169 min_clamp0(fromOffset, Length()), KEEP_CASE); 1170 } 1171 1172 1173 BString& 1174 BString::Replace(const char* replaceThis, const char* withThis, 1175 int32 maxReplaceCount, int32 fromOffset) 1176 { 1177 if (!replaceThis || !withThis || maxReplaceCount <= 0 1178 || FindFirst(replaceThis) < 0) 1179 return *this; 1180 1181 if (_Detach() != B_OK) 1182 return *this; 1183 1184 return _DoReplace(replaceThis, withThis, maxReplaceCount, 1185 min_clamp0(fromOffset, Length()), KEEP_CASE); 1186 } 1187 1188 1189 BString& 1190 BString::IReplaceFirst(char replaceThis, char withThis) 1191 { 1192 char tmp[2] = { replaceThis, '\0' }; 1193 1194 int32 pos = _IFindAfter(tmp, 0, 1); 1195 if (pos >= 0 && _Detach() == B_OK) 1196 fPrivateData[pos] = withThis; 1197 return *this; 1198 } 1199 1200 1201 BString& 1202 BString::IReplaceLast(char replaceThis, char withThis) 1203 { 1204 char tmp[2] = { replaceThis, '\0' }; 1205 1206 int32 pos = _IFindBefore(tmp, Length(), 1); 1207 if (pos >= 0 && _Detach() == B_OK) 1208 fPrivateData[pos] = withThis; 1209 return *this; 1210 } 1211 1212 1213 BString& 1214 BString::IReplaceAll(char replaceThis, char withThis, int32 fromOffset) 1215 { 1216 char tmp[2] = { replaceThis, '\0' }; 1217 fromOffset = min_clamp0(fromOffset, Length()); 1218 int32 pos = _IFindAfter(tmp, fromOffset, 1); 1219 1220 if (pos >= 0 && _Detach() == B_OK) { 1221 fPrivateData[pos] = withThis; 1222 for (pos = pos;;) { 1223 pos = _IFindAfter(tmp, pos, 1); 1224 if (pos < 0) 1225 break; 1226 fPrivateData[pos] = withThis; 1227 } 1228 } 1229 return *this; 1230 } 1231 1232 1233 BString& 1234 BString::IReplace(char replaceThis, char withThis, int32 maxReplaceCount, 1235 int32 fromOffset) 1236 { 1237 char tmp[2] = { replaceThis, '\0' }; 1238 fromOffset = min_clamp0(fromOffset, Length()); 1239 int32 pos = _IFindAfter(tmp, fromOffset, 1); 1240 1241 if (maxReplaceCount > 0 && pos >= 0 && _Detach() == B_OK) { 1242 fPrivateData[pos] = withThis; 1243 maxReplaceCount--; 1244 for (pos = pos; maxReplaceCount > 0; maxReplaceCount--) { 1245 pos = _IFindAfter(tmp, pos, 1); 1246 if (pos < 0) 1247 break; 1248 fPrivateData[pos] = withThis; 1249 } 1250 } 1251 1252 return *this; 1253 } 1254 1255 1256 BString& 1257 BString::IReplaceFirst(const char* replaceThis, const char* withThis) 1258 { 1259 if (!replaceThis || !withThis || IFindFirst(replaceThis) < 0) 1260 return *this; 1261 1262 if (_Detach() != B_OK) 1263 return *this; 1264 return _DoReplace(replaceThis, withThis, 1, 0, IGNORE_CASE); 1265 } 1266 1267 1268 BString& 1269 BString::IReplaceLast(const char* replaceThis, const char* withThis) 1270 { 1271 if (!replaceThis || !withThis) 1272 return *this; 1273 1274 int32 replaceThisLength = strlen(replaceThis); 1275 int32 pos = _IFindBefore(replaceThis, Length(), replaceThisLength); 1276 1277 if (pos >= 0) { 1278 int32 withThisLength = strlen(withThis); 1279 int32 difference = withThisLength - replaceThisLength; 1280 1281 if (difference > 0) { 1282 if (!_OpenAtBy(pos, difference)) 1283 return *this; 1284 } else if (difference < 0) { 1285 if (!_ShrinkAtBy(pos, -difference)) 1286 return *this; 1287 } else { 1288 if (_Detach() != B_OK) 1289 return *this; 1290 } 1291 memcpy(fPrivateData + pos, withThis, withThisLength); 1292 } 1293 1294 return *this; 1295 } 1296 1297 1298 BString& 1299 BString::IReplaceAll(const char* replaceThis, const char* withThis, 1300 int32 fromOffset) 1301 { 1302 if (!replaceThis || !withThis || IFindFirst(replaceThis) < 0) 1303 return *this; 1304 1305 if (_Detach() != B_OK) 1306 return *this; 1307 1308 return _DoReplace(replaceThis, withThis, REPLACE_ALL, 1309 min_clamp0(fromOffset, Length()), IGNORE_CASE); 1310 } 1311 1312 1313 BString& 1314 BString::IReplace(const char* replaceThis, const char* withThis, 1315 int32 maxReplaceCount, int32 fromOffset) 1316 { 1317 if (!replaceThis || !withThis || maxReplaceCount <= 0 1318 || FindFirst(replaceThis) < 0) 1319 return *this; 1320 1321 if (_Detach() != B_OK) 1322 return *this; 1323 1324 return _DoReplace(replaceThis, withThis, maxReplaceCount, 1325 min_clamp0(fromOffset, Length()), IGNORE_CASE); 1326 } 1327 1328 1329 BString& 1330 BString::ReplaceSet(const char* setOfChars, char with) 1331 { 1332 if (!setOfChars || strcspn(fPrivateData, setOfChars) >= uint32(Length())) 1333 return *this; 1334 1335 if (_Detach() != B_OK) 1336 return *this; 1337 1338 int32 offset = 0; 1339 int32 length = Length(); 1340 for (int32 pos;;) { 1341 pos = strcspn(fPrivateData + offset, setOfChars); 1342 1343 offset += pos; 1344 if (offset >= length) 1345 break; 1346 1347 fPrivateData[offset] = with; 1348 offset++; 1349 } 1350 1351 return *this; 1352 } 1353 1354 1355 BString& 1356 BString::ReplaceSet(const char* setOfChars, const char* with) 1357 { 1358 if (!setOfChars || !with 1359 || strcspn(fPrivateData, setOfChars) >= uint32(Length())) 1360 return *this; 1361 1362 // delegate simple case 1363 int32 withLen = strlen(with); 1364 if (withLen == 1) 1365 return ReplaceSet(setOfChars, *with); 1366 1367 if (_Detach() != B_OK) 1368 return *this; 1369 1370 int32 pos = 0; 1371 int32 searchLen = 1; 1372 int32 len = Length(); 1373 1374 PosVect positions; 1375 for (int32 offset = 0; offset < len; offset += (pos + searchLen)) { 1376 pos = strcspn(fPrivateData + offset, setOfChars); 1377 if (pos + offset >= len) 1378 break; 1379 if (!positions.Add(offset + pos)) 1380 return *this; 1381 } 1382 1383 _ReplaceAtPositions(&positions, searchLen, with, withLen); 1384 return *this; 1385 } 1386 1387 1388 // #pragma mark - Unchecked char access 1389 1390 1391 #if __GNUC__ > 3 1392 BStringRef 1393 BString::operator[](int32 index) 1394 { 1395 return BStringRef(*this, index); 1396 } 1397 #else 1398 char& 1399 BString::operator[](int32 index) 1400 { 1401 if (_Detach() != B_OK) { 1402 static char invalid; 1403 return invalid; 1404 } 1405 1406 _ReferenceCount() = -1; 1407 // mark string as unshareable 1408 1409 return fPrivateData[index]; 1410 } 1411 #endif 1412 1413 1414 // #pragma mark - Fast low-level manipulation 1415 1416 1417 char* 1418 BString::LockBuffer(int32 maxLength) 1419 { 1420 int32 length = Length(); 1421 if (maxLength > length) 1422 length += maxLength - length; 1423 1424 if (_DetachWith(fPrivateData, length) == B_OK) { 1425 _ReferenceCount() = -1; 1426 // mark unshareable 1427 } 1428 return fPrivateData; 1429 } 1430 1431 1432 BString& 1433 BString::UnlockBuffer(int32 length) 1434 { 1435 if (length > 0) { 1436 if (length) 1437 length = min_clamp0(length, Length()); 1438 } else { 1439 length = (fPrivateData == NULL) ? 0 : strlen(fPrivateData); 1440 } 1441 1442 if (_Realloc(length) != NULL) { 1443 fPrivateData[length] = '\0'; 1444 _ReferenceCount() = 1; 1445 // mark shareable again 1446 } 1447 1448 return *this; 1449 } 1450 1451 1452 // #pragma mark - Uppercase <-> Lowercase 1453 1454 1455 BString& 1456 BString::ToLower() 1457 { 1458 int32 length = Length(); 1459 if (length > 0 && _Detach() == B_OK) { 1460 for (int32 count = 0; count < length; count++) 1461 fPrivateData[count] = tolower(fPrivateData[count]); 1462 } 1463 return *this; 1464 } 1465 1466 1467 BString& 1468 BString::ToUpper() 1469 { 1470 int32 length = Length(); 1471 if (length > 0 && _Detach() == B_OK) { 1472 for (int32 count = 0; count < length; count++) 1473 fPrivateData[count] = toupper(fPrivateData[count]); 1474 } 1475 return *this; 1476 } 1477 1478 1479 BString& 1480 BString::Capitalize() 1481 { 1482 int32 length = Length(); 1483 1484 if (length > 0 && _Detach() == B_OK) { 1485 fPrivateData[0] = toupper(fPrivateData[0]); 1486 for (int32 count = 1; count < length; count++) 1487 fPrivateData[count] = tolower(fPrivateData[count]); 1488 } 1489 return *this; 1490 } 1491 1492 1493 BString& 1494 BString::CapitalizeEachWord() 1495 { 1496 int32 length = Length(); 1497 1498 if (length > 0 && _Detach() == B_OK) { 1499 int32 count = 0; 1500 do { 1501 // Find the first alphabetical character... 1502 for (; count < length; count++) { 1503 if (isalpha(fPrivateData[count])) { 1504 // ...found! Convert it to uppercase. 1505 fPrivateData[count] = toupper(fPrivateData[count]); 1506 count++; 1507 break; 1508 } 1509 } 1510 1511 // Now find the first non-alphabetical character, 1512 // and meanwhile, turn to lowercase all the alphabetical ones 1513 for (; count < length; count++) { 1514 if (isalpha(fPrivateData[count])) 1515 fPrivateData[count] = tolower(fPrivateData[count]); 1516 else 1517 break; 1518 } 1519 } while (count < length); 1520 } 1521 return *this; 1522 } 1523 1524 1525 // #pragma mark - Escaping and De-escaping 1526 1527 1528 BString& 1529 BString::CharacterEscape(const char* original, 1530 const char* setOfCharsToEscape, char escapeWith) 1531 { 1532 if (setOfCharsToEscape) 1533 _DoCharacterEscape(original, setOfCharsToEscape, escapeWith); 1534 return *this; 1535 } 1536 1537 1538 BString& 1539 BString::CharacterEscape(const char* setOfCharsToEscape, char escapeWith) 1540 { 1541 if (setOfCharsToEscape && Length() > 0) 1542 _DoCharacterEscape(fPrivateData, setOfCharsToEscape, escapeWith); 1543 return *this; 1544 } 1545 1546 1547 BString& 1548 BString::CharacterDeescape(const char* original, char escapeChar) 1549 { 1550 return _DoCharacterDeescape(original, escapeChar); 1551 } 1552 1553 1554 BString& 1555 BString::CharacterDeescape(char escapeChar) 1556 { 1557 if (Length() > 0) 1558 _DoCharacterDeescape(fPrivateData, escapeChar); 1559 return *this; 1560 } 1561 1562 1563 // #pragma mark - Insert 1564 1565 1566 BString& 1567 BString::operator<<(const char* string) 1568 { 1569 if (string != NULL) { 1570 int32 length = strlen(string); 1571 if (length > 0) 1572 _DoAppend(string, length); 1573 } 1574 return *this; 1575 } 1576 1577 1578 BString& 1579 BString::operator<<(const BString& string) 1580 { 1581 if (string.Length() > 0) 1582 _DoAppend(string.String(), string.Length()); 1583 return *this; 1584 } 1585 1586 1587 BString& 1588 BString::operator<<(char c) 1589 { 1590 _DoAppend(&c, 1); 1591 return *this; 1592 } 1593 1594 1595 BString& 1596 BString::operator<<(int i) 1597 { 1598 char num[32]; 1599 int32 length = snprintf(num, sizeof(num), "%d", i); 1600 1601 _DoAppend(num, length); 1602 return *this; 1603 } 1604 1605 1606 BString& 1607 BString::operator<<(unsigned int i) 1608 { 1609 char num[32]; 1610 int32 length = snprintf(num, sizeof(num), "%u", i); 1611 1612 _DoAppend(num, length); 1613 return *this; 1614 } 1615 1616 1617 BString& 1618 BString::operator<<(uint32 i) 1619 { 1620 char num[32]; 1621 int32 length = snprintf(num, sizeof(num), "%lu", i); 1622 1623 _DoAppend(num, length); 1624 return *this; 1625 } 1626 1627 1628 BString& 1629 BString::operator<<(int32 i) 1630 { 1631 char num[32]; 1632 int32 length = snprintf(num, sizeof(num), "%ld", i); 1633 1634 _DoAppend(num, length); 1635 return *this; 1636 } 1637 1638 1639 BString& 1640 BString::operator<<(uint64 i) 1641 { 1642 char num[64]; 1643 int32 length = snprintf(num, sizeof(num), "%llu", i); 1644 1645 _DoAppend(num, length); 1646 return *this; 1647 } 1648 1649 1650 BString& 1651 BString::operator<<(int64 i) 1652 { 1653 char num[64]; 1654 int32 length = snprintf(num, sizeof(num), "%lld", i); 1655 1656 _DoAppend(num, length); 1657 return *this; 1658 } 1659 1660 1661 BString& 1662 BString::operator<<(float f) 1663 { 1664 char num[64]; 1665 int32 length = snprintf(num, sizeof(num), "%.2f", f); 1666 1667 _DoAppend(num, length); 1668 return *this; 1669 } 1670 1671 1672 // #pragma mark - Private or reserved 1673 1674 1675 /*! Detaches this string from an eventually shared fPrivateData. 1676 */ 1677 status_t 1678 BString::_Detach() 1679 { 1680 char* newData = fPrivateData; 1681 1682 if (atomic_get(&_ReferenceCount()) > 1) { 1683 // It might be shared, and this requires special treatment 1684 newData = _Clone(fPrivateData, Length()); 1685 if (atomic_add(&_ReferenceCount(), -1) == 1) { 1686 // someone else left, we were the last owner 1687 _FreePrivateData(); 1688 } 1689 } 1690 1691 if (newData) 1692 fPrivateData = newData; 1693 1694 return newData != NULL ? B_OK : B_NO_MEMORY; 1695 } 1696 1697 1698 char* 1699 BString::_Alloc(int32 length, bool adoptReferenceCount) 1700 { 1701 if (length < 0) 1702 return NULL; 1703 1704 char* newData = (char *)malloc(length + kPrivateDataOffset + 1); 1705 if (newData == NULL) 1706 return NULL; 1707 1708 newData += kPrivateDataOffset; 1709 newData[length] = '\0'; 1710 1711 // initialize reference count & length 1712 int32 referenceCount = 1; 1713 if (adoptReferenceCount && fPrivateData != NULL) 1714 referenceCount = _ReferenceCount(); 1715 1716 *(((vint32*)newData) - 2) = referenceCount; 1717 *(((int32*)newData) - 1) = length & 0x7fffffff; 1718 1719 return newData; 1720 } 1721 1722 1723 char* 1724 BString::_Realloc(int32 length) 1725 { 1726 if (length == Length()) 1727 return fPrivateData; 1728 1729 char *dataPtr = fPrivateData ? fPrivateData - kPrivateDataOffset : NULL; 1730 if (length < 0) 1731 length = 0; 1732 1733 dataPtr = (char*)realloc(dataPtr, length + kPrivateDataOffset + 1); 1734 if (dataPtr) { 1735 int32 oldReferenceCount = _ReferenceCount(); 1736 1737 dataPtr += kPrivateDataOffset; 1738 1739 fPrivateData = dataPtr; 1740 fPrivateData[length] = '\0'; 1741 1742 _SetLength(length); 1743 _ReferenceCount() = oldReferenceCount; 1744 } 1745 return dataPtr; 1746 } 1747 1748 1749 void 1750 BString::_Init(const char* src, int32 length) 1751 { 1752 fPrivateData = _Clone(src, length); 1753 if (fPrivateData == NULL) 1754 fPrivateData = _Clone(NULL, 0); 1755 } 1756 1757 1758 char* 1759 BString::_Clone(const char* data, int32 length) 1760 { 1761 char* newData = _Alloc(length, false); 1762 if (newData == NULL) 1763 return NULL; 1764 1765 if (data != NULL && length > 0) { 1766 // "data" may not span over the whole length 1767 strncpy(newData, data, length); 1768 } 1769 1770 return newData; 1771 } 1772 1773 1774 char* 1775 BString::_OpenAtBy(int32 offset, int32 length) 1776 { 1777 int32 oldLength = Length(); 1778 1779 if (_Detach() != B_OK) 1780 return NULL; 1781 1782 memmove(fPrivateData + offset + length, fPrivateData + offset, 1783 oldLength - offset); 1784 return _Realloc(oldLength + length); 1785 } 1786 1787 1788 char* 1789 BString::_ShrinkAtBy(int32 offset, int32 length) 1790 { 1791 int32 oldLength = Length(); 1792 if (_Detach() != B_OK) 1793 return NULL; 1794 1795 memmove(fPrivateData + offset, fPrivateData + offset + length, 1796 oldLength - offset - length); 1797 return _Realloc(oldLength - length); 1798 } 1799 1800 1801 status_t 1802 BString::_DetachWith(const char* string, int32 length) 1803 { 1804 char* newData = NULL; 1805 1806 if (atomic_get(&_ReferenceCount()) > 1) { 1807 // we might share our data with someone else 1808 newData = _Clone(string, length); 1809 if (atomic_add(&_ReferenceCount(), -1) == 1) { 1810 // someone else left, we were the last owner 1811 _FreePrivateData(); 1812 } 1813 } else { 1814 // we don't share our data with someone else 1815 newData = _Realloc(length); 1816 } 1817 1818 if (newData) 1819 fPrivateData = newData; 1820 1821 return newData != NULL ? B_OK : B_NO_MEMORY; 1822 } 1823 1824 1825 void 1826 BString::_SetLength(int32 length) 1827 { 1828 *(((int32*)fPrivateData) - 1) = length & 0x7fffffff; 1829 } 1830 1831 1832 inline int32& 1833 BString::_ReferenceCount() 1834 { 1835 return *(((int32 *)fPrivateData) - 2); 1836 } 1837 1838 1839 inline const int32& 1840 BString::_ReferenceCount() const 1841 { 1842 return *(((int32 *)fPrivateData) - 2); 1843 } 1844 1845 1846 inline bool 1847 BString::_IsShareable() const 1848 { 1849 return fPrivateData != NULL && _ReferenceCount() >= 0; 1850 } 1851 1852 1853 void 1854 BString::_FreePrivateData() 1855 { 1856 if (fPrivateData != NULL) { 1857 free(fPrivateData - kPrivateDataOffset); 1858 fPrivateData = NULL; 1859 } 1860 } 1861 1862 1863 bool 1864 BString::_DoAppend(const char* string, int32 length) 1865 { 1866 int32 oldLength = Length(); 1867 if (_DetachWith(fPrivateData, oldLength + length) == B_OK) { 1868 strncpy(fPrivateData + oldLength, string, length); 1869 return true; 1870 } 1871 return false; 1872 } 1873 1874 1875 bool 1876 BString::_DoPrepend(const char* string, int32 length) 1877 { 1878 int32 oldLength = Length(); 1879 if (_DetachWith(fPrivateData, oldLength + length) == B_OK) { 1880 memmove(fPrivateData + length, fPrivateData, oldLength); 1881 if (string && length) 1882 strncpy(fPrivateData, string, length); 1883 return true; 1884 } 1885 return false; 1886 } 1887 1888 1889 bool 1890 BString::_DoInsert(const char* string, int32 offset, int32 length) 1891 { 1892 int32 oldLength = Length(); 1893 if (_DetachWith(fPrivateData, oldLength + length) == B_OK) { 1894 memmove(fPrivateData + offset + length, fPrivateData + offset, 1895 oldLength - offset); 1896 if (string && length) 1897 strncpy(fPrivateData + offset, string, length); 1898 return true; 1899 } 1900 return false; 1901 } 1902 1903 1904 int32 1905 BString::_ShortFindAfter(const char* string, int32 len) const 1906 { 1907 const char *ptr = strstr(String(), string); 1908 1909 if (ptr != NULL) 1910 return ptr - String(); 1911 1912 return B_ERROR; 1913 } 1914 1915 1916 int32 1917 BString::_FindAfter(const char* string, int32 offset, int32 strlen) const 1918 { 1919 const char *ptr = strstr(String() + offset, string); 1920 1921 if (ptr != NULL) 1922 return ptr - String(); 1923 1924 return B_ERROR; 1925 } 1926 1927 1928 int32 1929 BString::_IFindAfter(const char* string, int32 offset, int32 strlen) const 1930 { 1931 const char *ptr = strcasestr(String() + offset, string); 1932 1933 if (ptr != NULL) 1934 return ptr - String(); 1935 1936 return B_ERROR; 1937 } 1938 1939 1940 int32 1941 BString::_FindBefore(const char* string, int32 offset, int32 strlen) const 1942 { 1943 if (fPrivateData) { 1944 const char *ptr = fPrivateData + offset - strlen; 1945 1946 while (ptr >= fPrivateData) { 1947 if (!memcmp(ptr, string, strlen)) 1948 return ptr - fPrivateData; 1949 ptr--; 1950 } 1951 } 1952 return B_ERROR; 1953 } 1954 1955 1956 int32 1957 BString::_IFindBefore(const char* string, int32 offset, int32 strlen) const 1958 { 1959 if (fPrivateData) { 1960 char *ptr1 = fPrivateData + offset - strlen; 1961 1962 while (ptr1 >= fPrivateData) { 1963 if (!strncasecmp(ptr1, string, strlen)) 1964 return ptr1 - fPrivateData; 1965 ptr1--; 1966 } 1967 } 1968 return B_ERROR; 1969 } 1970 1971 1972 BString& 1973 BString::_DoCharacterEscape(const char* string, const char* setOfCharsToEscape, 1974 char escapeChar) 1975 { 1976 if (_DetachWith(string, strlen(safestr(string))) != B_OK) 1977 return *this; 1978 1979 memcpy(fPrivateData, string, Length()); 1980 1981 PosVect positions; 1982 int32 length = Length(); 1983 int32 pos = 0; 1984 for (int32 offset = 0; offset < length; offset += pos + 1) { 1985 if ((pos = strcspn(fPrivateData + offset, setOfCharsToEscape)) 1986 < length - offset) { 1987 if (!positions.Add(offset + pos)) 1988 return *this; 1989 } 1990 } 1991 1992 uint32 count = positions.CountItems(); 1993 int32 newLength = length + count; 1994 if (!newLength) { 1995 _Realloc(0); 1996 return *this; 1997 } 1998 1999 char* newData = _Alloc(newLength); 2000 if (newData) { 2001 char* oldString = fPrivateData; 2002 char* newString = newData; 2003 int32 lastPos = 0; 2004 2005 for (uint32 i = 0; i < count; ++i) { 2006 pos = positions.ItemAt(i); 2007 length = pos - lastPos; 2008 if (length > 0) { 2009 memcpy(newString, oldString, length); 2010 oldString += length; 2011 newString += length; 2012 } 2013 *newString++ = escapeChar; 2014 *newString++ = *oldString++; 2015 lastPos = pos + 1; 2016 } 2017 2018 length = Length() + 1 - lastPos; 2019 if (length > 0) 2020 memcpy(newString, oldString, length); 2021 2022 _FreePrivateData(); 2023 fPrivateData = newData; 2024 } 2025 return *this; 2026 } 2027 2028 2029 BString& 2030 BString::_DoCharacterDeescape(const char* string, char escapeChar) 2031 { 2032 if (_DetachWith(string, strlen(safestr(string))) != B_OK) 2033 return *this; 2034 2035 memcpy(fPrivateData, string, Length()); 2036 const char escape[2] = { escapeChar, '\0' }; 2037 return _DoReplace(escape, "", REPLACE_ALL, 0, KEEP_CASE); 2038 } 2039 2040 2041 BString& 2042 BString::_DoReplace(const char* findThis, const char* replaceWith, 2043 int32 maxReplaceCount, int32 fromOffset, bool ignoreCase) 2044 { 2045 if (findThis == NULL || maxReplaceCount <= 0 2046 || fromOffset < 0 || fromOffset >= Length()) 2047 return *this; 2048 2049 typedef int32 (BString::*TFindMethod)(const char*, int32, int32) const; 2050 TFindMethod findMethod = ignoreCase ? &BString::_IFindAfter 2051 : &BString::_FindAfter; 2052 int32 findLen = strlen(findThis); 2053 2054 if (!replaceWith) 2055 replaceWith = ""; 2056 2057 int32 replaceLen = strlen(replaceWith); 2058 int32 lastSrcPos = fromOffset; 2059 PosVect positions; 2060 for (int32 srcPos = 0; maxReplaceCount > 0 2061 && (srcPos = (this->*findMethod)(findThis, lastSrcPos, findLen)) >= 0; 2062 maxReplaceCount--) { 2063 positions.Add(srcPos); 2064 lastSrcPos = srcPos + findLen; 2065 } 2066 _ReplaceAtPositions(&positions, findLen, replaceWith, replaceLen); 2067 return *this; 2068 } 2069 2070 2071 void 2072 BString::_ReplaceAtPositions(const PosVect* positions, int32 searchLength, 2073 const char* with, int32 withLength) 2074 { 2075 int32 length = Length(); 2076 uint32 count = positions->CountItems(); 2077 int32 newLength = length + count * (withLength - searchLength); 2078 if (!newLength) { 2079 _Realloc(0); 2080 return; 2081 } 2082 2083 char *newData = _Alloc(newLength); 2084 if (newData == NULL) 2085 return; 2086 2087 char *oldString = fPrivateData; 2088 char *newString = newData; 2089 int32 lastPos = 0; 2090 2091 for (uint32 i = 0; i < count; ++i) { 2092 int32 pos = positions->ItemAt(i); 2093 length = pos - lastPos; 2094 if (length > 0) { 2095 memcpy(newString, oldString, length); 2096 oldString += length; 2097 newString += length; 2098 } 2099 memcpy(newString, with, withLength); 2100 oldString += searchLength; 2101 newString += withLength; 2102 lastPos = pos + searchLength; 2103 } 2104 2105 length = Length() + 1 - lastPos; 2106 if (length > 0) 2107 memcpy(newString, oldString, length); 2108 2109 _FreePrivateData(); 2110 fPrivateData = newData; 2111 } 2112 2113 2114 // #pragma mark - backwards compatibility 2115 2116 2117 /* 2118 Translates to (missing const): 2119 BString& BString::operator<<(BString& string) 2120 */ 2121 extern "C" BString& 2122 __ls__7BStringR7BString(BString* self, BString& string) 2123 { 2124 return self->operator<<(string); 2125 } 2126 2127 2128 // #pragma mark - Non-member compare for sorting, etc. 2129 2130 2131 int 2132 Compare(const BString &string1, const BString &string2) 2133 { 2134 return strcmp(string1.String(), string2.String()); 2135 } 2136 2137 2138 int 2139 ICompare(const BString &string1, const BString &string2) 2140 { 2141 return strcasecmp(string1.String(), string2.String()); 2142 } 2143 2144 2145 int 2146 Compare(const BString *string1, const BString *string2) 2147 { 2148 return strcmp(string1->String(), string2->String()); 2149 } 2150 2151 2152 int 2153 ICompare(const BString *string1, const BString *string2) 2154 { 2155 return strcasecmp(string1->String(), string2->String()); 2156 } 2157