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