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