/*++

Copyright (c) 1998  Microsoft Corporation

Module Name:

    bsstring.h

Abstract:

    This module defines the CBsString class.  This class manages character
    arrays in a similar manner as the CString class in VC++.  In fact, this
    class is a copy of the CString class with the MFC specific stuff ripped
    out since LTS doesn't use MTF.
    Added methods in addition to CString:
        c_str() - returns a C string pointer ala the STL string class
        size()  - returns length of string ala the STL string class

Author:

    Stefan R. Steiner   [SSteiner]      1-Mar-1998

Revision History:

    Stefan R. Steiner   [SSteiner]      10-Apr-2000
        Added fixed allocator code and resynced with MFC 6 SR-1 code

--*/

#ifndef __H_BSSTRING_
#define __H_BSSTRING_

#ifndef __cplusplus
	#error requires C++ compilation
#endif

/////////////////////////////////////////////////////////////////////////////
// Other includes from standard "C" runtimes

#ifndef _INC_STRING
	#include <string.h>
#endif
#ifndef _INC_STDIO
	#include <stdio.h>
#endif
#ifndef _INC_STDLIB
	#include <stdlib.h>
#endif
#ifndef _INC_TIME
	#include <time.h>
#endif
#ifndef _INC_LIMITS
	#include <limits.h>
#endif
#ifndef _INC_STDDEF
	#include <stddef.h>
#endif
#ifndef _INC_STDARG
	#include <stdarg.h>
#endif
#ifndef _INC_ASSERT
	#include <assert.h>
#endif

#ifndef ASSERT
    #define ASSERT assert
#endif

#include "bsfixalloc.h"

class CBsString;

//
//  The purpose of this class is to generate a compiler error when different string classes
//  are used in bad contexts (example: CBsWString used if _UNICODE is not defined)
//
//  [aoltean] I introduce this small class to hide a class of LINT warnings.
//
class CBsStringErrorGenerator 
{};                     // Private constructor

// CBsString only operates on string that are TCHAR arrays.  Programs should use 
// the following types to make sure they are getting what they are expecting.
#ifdef _UNICODE
    #define CBsWString CBsString
    #define CBsAString CBsStringErrorGenerator   // trigger a compile time bug
#else
    #define CBsAString CBsString
    #define CBsWString CBsStringErrorGenerator   // trigger a compile time bug
#endif

#include <tchar.h>

#ifndef BSAFXAPI
    #define BSAFXAPI __cdecl
    #define BSAFX_CDECL __cdecl
#endif

// FASTCALL is used for static member functions with little or no params
#ifndef FASTCALL
	#define FASTCALL __fastcall
#endif

/////////////////////////////////////////////////////////////////////////////
// Turn off warnings for /W4
// To resume any of these warning: #pragma warning(default: 4xxx)
// which should be placed after the BSAFX include files
#ifndef ALL_WARNINGS
// warnings generated with common MFC/Windows code
#pragma warning(disable: 4127)  // constant expression for TRACE/ASSERT
#pragma warning(disable: 4134)  // message map member fxn casts
#pragma warning(disable: 4201)  // nameless unions are part of C++
#pragma warning(disable: 4511)  // private copy constructors are good to have
#pragma warning(disable: 4512)  // private operator= are good to have
#pragma warning(disable: 4514)  // unreferenced inlines are common
#pragma warning(disable: 4710)  // private constructors are disallowed
#pragma warning(disable: 4705)  // statement has no effect in optimized code
#pragma warning(disable: 4191)  // pointer-to-function casting
// warnings caused by normal optimizations
#ifndef _DEBUG
#pragma warning(disable: 4701)  // local variable *may* be used without init
#pragma warning(disable: 4702)  // unreachable code caused by optimizations
#pragma warning(disable: 4791)  // loss of debugging info in release version
#pragma warning(disable: 4189)  // initialized but unused variable
#pragma warning(disable: 4390)  // empty controlled statement
#endif
// warnings specific to _BSAFXDLL version
#ifdef _BSAFXDLL
#pragma warning(disable: 4204)  // non-constant aggregate initializer
#endif
#ifdef _BSAFXDLL
#pragma warning(disable: 4275)  // deriving exported class from non-exported
#pragma warning(disable: 4251)  // using non-exported as public in exported
#endif
#endif //!ALL_WARNINGS

#ifdef _DEBUG
#define UNUSED(x)
#else
#define UNUSED(x) x
#endif
#define UNUSED_ALWAYS(x) x

/////////////////////////////////////////////////////////////////////////////
// Strings

#ifndef _OLEAUTO_H_
#ifdef OLE2ANSI
	typedef LPSTR BSTR;
#else
	typedef LPWSTR BSTR;// must (semantically) match typedef in oleauto.h
#endif
#endif

struct CBsStringData
{
	long nRefs;             // reference count
	int nDataLength;        // length of data (including terminator)
	int nAllocLength;       // length of allocation
	// TCHAR data[nAllocLength]

	TCHAR* data()           // TCHAR* to managed data
		{ return (TCHAR*)(this+1); }
};

class CBsString
{
public:
// Constructors

	// constructs empty CBsString
	CBsString();
	// copy constructor
	CBsString(const CBsString& stringSrc);
	// from a single character
	CBsString(TCHAR ch, int nRepeat = 1);
	// from an ANSI string (converts to TCHAR)
	CBsString(LPCSTR lpsz);
	// from a UNICODE string (converts to TCHAR)
	CBsString(LPCWSTR lpsz);
	// subset of characters from an ANSI string (converts to TCHAR)
	CBsString(LPCSTR lpch, int nLength);
	// subset of characters from a UNICODE string (converts to TCHAR)
	CBsString(LPCWSTR lpch, int nLength);
	// from unsigned characters
	CBsString(const unsigned char* psz);
	CBsString(GUID guid);

// Attributes & Operations

	// get data length
	int GetLength() const;
	// TRUE if zero length
	BOOL IsEmpty() const;
	// clear contents to empty
	void Empty();
	int size() const;                   // ala STL string class size()

	// return single character at zero-based index
	TCHAR GetAt(int nIndex) const;
	// return single character at zero-based index
	TCHAR operator[](int nIndex) const;
	// set a single character at zero-based index
	void SetAt(int nIndex, TCHAR ch);
	// return pointer to const string
	operator LPCTSTR() const;
    const LPCTSTR c_str() const;        // as a C string in STL string style

	// overloaded assignment

	// ref-counted copy from another CBsString
	const CBsString& operator=(const CBsString& stringSrc);
	// set string content to single character
	const CBsString& operator=(TCHAR ch);
#ifdef _UNICODE
	const CBsString& operator=(char ch);
#endif
	// copy string content from ANSI string (converts to TCHAR)
	const CBsString& operator=(LPCSTR lpsz);
	// copy string content from UNICODE string (converts to TCHAR)
	const CBsString& operator=(LPCWSTR lpsz);
	// copy string content from unsigned chars
	const CBsString& operator=(const unsigned char* psz);

	// string concatenation

	// concatenate from another CBsString
	const CBsString& operator+=(const CBsString& string);

	// concatenate a single character
	const CBsString& operator+=(TCHAR ch);
#ifdef _UNICODE
	// concatenate an ANSI character after converting it to TCHAR
	const CBsString& operator+=(char ch);
#endif
	// concatenate a UNICODE character after converting it to TCHAR
	const CBsString& operator+=(LPCTSTR lpsz);

	friend CBsString BSAFXAPI operator+(const CBsString& string1,
			const CBsString& string2);
	friend CBsString BSAFXAPI operator+(const CBsString& string, TCHAR ch);
	friend CBsString BSAFXAPI operator+(TCHAR ch, const CBsString& string);
#ifdef _UNICODE
	friend CBsString BSAFXAPI operator+(const CBsString& string, char ch);
	friend CBsString BSAFXAPI operator+(char ch, const CBsString& string);
#endif
	friend CBsString BSAFXAPI operator+(const CBsString& string, LPCTSTR lpsz);
	friend CBsString BSAFXAPI operator+(LPCTSTR lpsz, const CBsString& string);

	// string comparison

	// straight character comparison
	int Compare(LPCTSTR lpsz) const;
	// compare ignoring case
	int CompareNoCase(LPCTSTR lpsz) const;
	// NLS aware comparison, case sensitive
	int Collate(LPCTSTR lpsz) const;
	// NLS aware comparison, case insensitive
	int CollateNoCase(LPCTSTR lpsz) const;

	// simple sub-string extraction

	// return nCount characters starting at zero-based nFirst
	CBsString Mid(int nFirst, int nCount) const;
	// return all characters starting at zero-based nFirst
	CBsString Mid(int nFirst) const;
	// return first nCount characters in string
	CBsString Left(int nCount) const;
	// return nCount characters from end of string
	CBsString Right(int nCount) const;

	//  characters from beginning that are also in passed string
	CBsString SpanIncluding(LPCTSTR lpszCharSet) const;
	// characters from beginning that are not also in passed string
	CBsString SpanExcluding(LPCTSTR lpszCharSet) const;

	// upper/lower/reverse conversion

	// NLS aware conversion to uppercase
	void MakeUpper();
	// NLS aware conversion to lowercase
	void MakeLower();
	// reverse string right-to-left
	void MakeReverse();

	// trimming whitespace (either side)

	// remove whitespace starting from right edge
	void TrimRight();
	// remove whitespace starting from left side
	void TrimLeft();

	// trimming anything (either side)

	// remove continuous occurrences of chTarget starting from right
	void TrimRight(TCHAR chTarget);
	// remove continuous occcurrences of characters in passed string,
	// starting from right
	void TrimRight(LPCTSTR lpszTargets);
	// remove continuous occurrences of chTarget starting from left
	void TrimLeft(TCHAR chTarget);
	// remove continuous occcurrences of characters in
	// passed string, starting from left
	void TrimLeft(LPCTSTR lpszTargets);

	// advanced manipulation

	// replace occurrences of chOld with chNew
	int Replace(TCHAR chOld, TCHAR chNew);
	// replace occurrences of substring lpszOld with lpszNew;
	// empty lpszNew removes instances of lpszOld
	int Replace(LPCTSTR lpszOld, LPCTSTR lpszNew);
	// remove occurrences of chRemove
	int Remove(TCHAR chRemove);
	// insert character at zero-based index; concatenates
	// if index is past end of string
	int Insert(int nIndex, TCHAR ch);
	// insert substring at zero-based index; concatenates
	// if index is past end of string
	int Insert(int nIndex, LPCTSTR pstr);
	// delete nCount characters starting at zero-based index
	int Delete(int nIndex, int nCount = 1);

	// searching

	// find character starting at left, -1 if not found
	int Find(TCHAR ch) const;
	// find character starting at right
	int ReverseFind(TCHAR ch) const;
	// find character starting at zero-based index and going right
	int Find(TCHAR ch, int nStart) const;
	// find first instance of any character in passed string
	int FindOneOf(LPCTSTR lpszCharSet) const;
	// find first instance of substring
	int Find(LPCTSTR lpszSub) const;
	// find first instance of substring starting at zero-based index
	int Find(LPCTSTR lpszSub, int nStart) const;

	// simple formatting

	// printf-like formatting using passed string
	void BSAFX_CDECL Format(LPCTSTR lpszFormat, ...);
	// printf-like formatting using variable arguments parameter
	void FormatV(LPCTSTR lpszFormat, va_list argList);

#ifndef _UNICODE
	// ANSI <-> OEM support (convert string in place)

	// convert string from ANSI to OEM in-place
	void AnsiToOem();
	// convert string from OEM to ANSI in-place
	void OemToAnsi();
#endif

#ifndef _BSAFX_NO_BSTR_SUPPORT
	// OLE BSTR support (use for OLE automation)

	// return a BSTR initialized with this CBsString's data
	BSTR AllocSysString() const;
	// reallocates the passed BSTR, copies content of this CBsString to it
	BSTR SetSysString(BSTR* pbstr) const;
#endif

	// Access to string implementation buffer as "C" character array

	// get pointer to modifiable buffer at least as long as nMinBufLength
	LPTSTR GetBuffer(int nMinBufLength);
	// release buffer, setting length to nNewLength (or to first nul if -1)
	void ReleaseBuffer(int nNewLength = -1);
	// get pointer to modifiable buffer exactly as long as nNewLength
	LPTSTR GetBufferSetLength(int nNewLength);
	// release memory allocated to but unused by string
	void FreeExtra();

	// Use LockBuffer/UnlockBuffer to turn refcounting off

	// turn refcounting back on
	LPTSTR LockBuffer();
	// turn refcounting off
	void UnlockBuffer();

// Implementation
public:
	~CBsString();
	int GetAllocLength() const;

protected:
	LPTSTR m_pchData;   // Pointer to ref counted string data.  This is actually
                        // a pointer to memory after the CBsStringData structure.

	// implementation helpers
	CBsStringData* GetData() const;
	void Init();
	void AllocCopy(CBsString& dest, int nCopyLen, int nCopyIndex, int nExtraLen) const;
	void AllocBuffer(int nLen);
	void AssignCopy(int nSrcLen, LPCTSTR lpszSrcData);
	void ConcatCopy(int nSrc1Len, LPCTSTR lpszSrc1Data, int nSrc2Len, LPCTSTR lpszSrc2Data);
	void ConcatInPlace(int nSrcLen, LPCTSTR lpszSrcData);
	void CopyBeforeWrite();
	void AllocBeforeWrite(int nLen);
	void Release();
	static void PASCAL Release(CBsStringData* pData);
	static int PASCAL SafeStrlen(LPCTSTR lpsz);
	static void FASTCALL FreeData(CBsStringData* pData);
};

// Compare helpers
bool BSAFXAPI operator==(const CBsString& s1, const CBsString& s2);
bool BSAFXAPI operator==(const CBsString& s1, LPCTSTR s2);
bool BSAFXAPI operator==(LPCTSTR s1, const CBsString& s2);
bool BSAFXAPI operator!=(const CBsString& s1, const CBsString& s2);
bool BSAFXAPI operator!=(const CBsString& s1, LPCTSTR s2);
bool BSAFXAPI operator!=(LPCTSTR s1, const CBsString& s2);
bool BSAFXAPI operator<(const CBsString& s1, const CBsString& s2);
bool BSAFXAPI operator<(const CBsString& s1, LPCTSTR s2);
bool BSAFXAPI operator<(LPCTSTR s1, const CBsString& s2);
bool BSAFXAPI operator>(const CBsString& s1, const CBsString& s2);
bool BSAFXAPI operator>(const CBsString& s1, LPCTSTR s2);
bool BSAFXAPI operator>(LPCTSTR s1, const CBsString& s2);
bool BSAFXAPI operator<=(const CBsString& s1, const CBsString& s2);
bool BSAFXAPI operator<=(const CBsString& s1, LPCTSTR s2);
bool BSAFXAPI operator<=(LPCTSTR s1, const CBsString& s2);
bool BSAFXAPI operator>=(const CBsString& s1, const CBsString& s2);
bool BSAFXAPI operator>=(const CBsString& s1, LPCTSTR s2);
bool BSAFXAPI operator>=(LPCTSTR s1, const CBsString& s2);

// conversion helpers
int BSAFX_CDECL _wcstombsz(char* mbstr, const wchar_t* wcstr, size_t count);
int BSAFX_CDECL _mbstowcsz(wchar_t* wcstr, const char* mbstr, size_t count);

// valid address test helpers
BOOL BSAFXAPI BsAfxIsValidString(LPCWSTR lpsz, int nLength = -1);
BOOL BSAFXAPI BsAfxIsValidString(LPCSTR lpsz, int nLength = -1);
BOOL BSAFXAPI BsAfxIsValidAddress(const void* lp, UINT nBytes, BOOL bReadWrite = TRUE);

// Globals
extern TCHAR bsafxChNil;
const CBsString& BSAFXAPI BsAfxGetEmptyString();
#define bsafxEmptyString BsAfxGetEmptyString()

inline CBsStringData* CBsString::GetData() const
	{ ASSERT(m_pchData != NULL); return ((CBsStringData*)m_pchData)-1; }
inline void CBsString::Init()
	{ m_pchData = bsafxEmptyString.m_pchData; }
inline CBsString::CBsString(const unsigned char* lpsz)
	{ Init(); *this = (LPCSTR)lpsz; }
inline const CBsString& CBsString::operator=(const unsigned char* lpsz)
	{ *this = (LPCSTR)lpsz; return *this; }
#ifdef _UNICODE
inline const CBsString& CBsString::operator+=(char ch)
	{ *this += (TCHAR)ch; return *this; }
inline const CBsString& CBsString::operator=(char ch)
	{ *this = (TCHAR)ch; return *this; }
inline CBsString BSAFXAPI operator+(const CBsString& string, char ch)
	{ return string + (TCHAR)ch; }
inline CBsString BSAFXAPI operator+(char ch, const CBsString& string)
	{ return (TCHAR)ch + string; }
#endif

inline int CBsString::GetLength() const
	{ return GetData()->nDataLength; }
inline int CBsString::size() const
	{ return GetData()->nDataLength; }
inline int CBsString::GetAllocLength() const
	{ return GetData()->nAllocLength; }
inline BOOL CBsString::IsEmpty() const
	{ return GetData()->nDataLength == 0; }
inline CBsString::operator LPCTSTR() const
	{ return m_pchData; }
inline const LPCTSTR CBsString::c_str() const
	{ return m_pchData; }
inline int PASCAL CBsString::SafeStrlen(LPCTSTR lpsz)
	{ return (lpsz == NULL) ? 0 : lstrlen(lpsz); }

// CBsString support (windows specific)
inline int CBsString::Compare(LPCTSTR lpsz) const
	{ ASSERT(BsAfxIsValidString(lpsz)); return _tcscmp(m_pchData, lpsz); }    // MBCS/Unicode aware
inline int CBsString::CompareNoCase(LPCTSTR lpsz) const
	{ ASSERT(BsAfxIsValidString(lpsz)); return _tcsicmp(m_pchData, lpsz); }   // MBCS/Unicode aware
// CBsString::Collate is often slower than Compare but is MBSC/Unicode
//  aware as well as locale-sensitive with respect to sort order.
inline int CBsString::Collate(LPCTSTR lpsz) const
	{ ASSERT(BsAfxIsValidString(lpsz)); return _tcscoll(m_pchData, lpsz); }   // locale sensitive
inline int CBsString::CollateNoCase(LPCTSTR lpsz) const
	{ ASSERT(BsAfxIsValidString(lpsz)); return _tcsicoll(m_pchData, lpsz); }   // locale sensitive

inline TCHAR CBsString::GetAt(int nIndex) const
{
	ASSERT(nIndex >= 0);
	ASSERT(nIndex < GetData()->nDataLength);
	return m_pchData[nIndex];
}
inline TCHAR CBsString::operator[](int nIndex) const
{
	// same as GetAt
	ASSERT(nIndex >= 0);
	ASSERT(nIndex < GetData()->nDataLength);
	return m_pchData[nIndex];
}
inline bool BSAFXAPI operator==(const CBsString& s1, const CBsString& s2)
	{ return s1.Compare(s2) == 0; }
inline bool BSAFXAPI operator==(const CBsString& s1, LPCTSTR s2)
	{ return s1.Compare(s2) == 0; }
inline bool BSAFXAPI operator==(LPCTSTR s1, const CBsString& s2)
	{ return s2.Compare(s1) == 0; }
inline bool BSAFXAPI operator!=(const CBsString& s1, const CBsString& s2)
	{ return s1.Compare(s2) != 0; }
inline bool BSAFXAPI operator!=(const CBsString& s1, LPCTSTR s2)
	{ return s1.Compare(s2) != 0; }
inline bool BSAFXAPI operator!=(LPCTSTR s1, const CBsString& s2)
	{ return s2.Compare(s1) != 0; }
inline bool BSAFXAPI operator<(const CBsString& s1, const CBsString& s2)
	{ return s1.Compare(s2) < 0; }
inline bool BSAFXAPI operator<(const CBsString& s1, LPCTSTR s2)
	{ return s1.Compare(s2) < 0; }
inline bool BSAFXAPI operator<(LPCTSTR s1, const CBsString& s2)
	{ return s2.Compare(s1) > 0; }
inline bool BSAFXAPI operator>(const CBsString& s1, const CBsString& s2)
	{ return s1.Compare(s2) > 0; }
inline bool BSAFXAPI operator>(const CBsString& s1, LPCTSTR s2)
	{ return s1.Compare(s2) > 0; }
inline bool BSAFXAPI operator>(LPCTSTR s1, const CBsString& s2)
	{ return s2.Compare(s1) < 0; }
inline bool BSAFXAPI operator<=(const CBsString& s1, const CBsString& s2)
	{ return s1.Compare(s2) <= 0; }
inline bool BSAFXAPI operator<=(const CBsString& s1, LPCTSTR s2)
	{ return s1.Compare(s2) <= 0; }
inline bool BSAFXAPI operator<=(LPCTSTR s1, const CBsString& s2)
	{ return s2.Compare(s1) >= 0; }
inline bool BSAFXAPI operator>=(const CBsString& s1, const CBsString& s2)
	{ return s1.Compare(s2) >= 0; }
inline bool BSAFXAPI operator>=(const CBsString& s1, LPCTSTR s2)
	{ return s1.Compare(s2) >= 0; }
inline bool BSAFXAPI operator>=(LPCTSTR s1, const CBsString& s2)
	{ return s2.Compare(s1) <= 0; }

#endif // __H_BSSTRING_