// utf-8

/*	Copyright  (c)	Günter Woigk   2001-2003
  					mailto:kio@little-bat.de

	This file is free software

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  

 Permission to use, copy, modify, distribute, and sell this software and
 its documentation for any purpose is hereby granted without fee, provided
 that the above copyright notice appear in all copies and that both that
 copyright notice and this permission notice appear in supporting
 documentation, and that the name of the copyright holder not be used
 in advertising or publicity pertaining to distribution of the software
 without specific, written prior permission. The copyright holder makes no
 representations about the suitability of this software for any purpose.
 It is provided "as is" without express or implied warranty.

 THE COPYRIGHT HOLDER DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 PERFORMANCE OF THIS SOFTWARE.


 ---------------------------------------------------------------------------
	
			class String

			this is a variant of the char-based (UTF-8) class String.
			+ supports UTF-8, UCS-1 (ISO Latin-1), UCS-2 and UCS-4 characters
			+ lots of conversions
			+ indexable characters (which is not possible with UTF-8 Strings)
			- slower than char-based (UTF-8) Strings

---------------------------------------------------------------------------
*/


#ifndef _string_h_
#define _string_h_

#include "kio/kio.h"
#include "utf-8/utf-8.h"




/* ********************************************************************************
					class String
******************************************************************************** */


enum CharSize  { csz1=1, csz2=2, csz4=4 };
enum MatchType { fnm_basic=0, fnm_escape=1, fnm_pathname=2, fnm_period=4, fnm_casefold=8 };


class String
{
	ptr			text;			// ptr -> start of text
	long		count;			// number of characters
	size_t		data_and_csz;	// data ptr for delete[]; lower 2 bits: csz-1
	String*		next;			// next string sharing same data
	String*		prev;			// prev string sharing same data

	void 		_init_from_utf8	( cptr p, cptr e, bool );
	void		_init 			( );
	void 		_init 			( cString& q );
	void 		_init 			( CharSize csz, long n );
	void		_move			( String& q );
	void 		_kill			( );
	long		_compare	  	( cString& q ) const;
	void		_resize			( long newlen );
	UCS1Char*	Ucs1			( )									{ return (UCS1Char*)text; }
	UCS2Char*	Ucs2			( )									{ return (UCS2Char*)text; }
	UCS4Char*	Ucs4			( )									{ return (UCS4Char*)text; }
	cUCS1Char*	Ucs1			( )	const							{ return (cUCS1Char*)text; }
	cUCS2Char*	Ucs2			( )	const							{ return (cUCS2Char*)text; }
	cUCS4Char*	Ucs4			( ) const							{ return (cUCS4Char*)text; }
	UCS1Char&	Ucs1			( long i )							{ return ((UCS1Char*)text)[i]; }
	UCS2Char&	Ucs2			( long i )							{ return ((UCS2Char*)text)[i]; }
	UCS4Char&	Ucs4			( long i )							{ return ((UCS4Char*)text)[i]; }
	cUCS1Char&	Ucs1			( long i ) const					{ return ((cUCS1Char*)text)[i]; }
	cUCS2Char&	Ucs2			( long i ) const					{ return ((cUCS2Char*)text)[i]; }
	cUCS4Char&	Ucs4			( long i ) const					{ return ((cUCS4Char*)text)[i]; }

	ptr			Data			( ) const							{ return ptr(data_and_csz&~3); }
	size_t		DataAndCsz		( )	const							{ return data_and_csz; }
	void		SetData			( cptr p )							{ XXXTRAP(size_t(p)&3); data_and_csz = (data_and_csz&3)+(size_t)p; }
	void		SetCsz			( CharSize sz )						{ data_and_csz = (data_and_csz&~3)+sz-1; }
	void		SetDataAndCsz	( cptr p, CharSize sz )				{ XXXTRAP(size_t(p)&3); data_and_csz = size_t(p)+sz-1; }
	void		SetDataAndCsz	( size_t q )						{ data_and_csz = q; }

public:
	void*		operator new	( size_t );
	void*		operator new	( size_t sz, void* p )				{ XXXTRAP(sz!=sizeof(String)); return p; }
	void		operator delete	( void*, size_t );

				~String			( );
				String			( );

				String			( UCS4Char c );

				String   		( cUCS1Char* p, long chars );
				String   		( cUCS2Char* p, long chars );
				String   		( cUCS4Char* p, long chars );
				String   		( cUTF8CharPtr p, long bytes );
				String   		( cptr p, long chars, CharSize sz );

				String   		( long n, CharSize sz );			// uncleared
				String   		( long n, UCS4Char c );				// cleared

				String   		( cUTF8Str s );						// string from c-string
				String			( cUTF8Str s, CharEncoding );		// string from c-string: interpret bogus utf-8 as latin-1
				String   		( cString& q );						// copy creator
				String   		( cString& q, long a, long e );		// substring
				String			( cString& q, cuptr txt, long chars, CharSize sz );	// special re-interpret substring

	CharSize	ReqCsz			( );
	void		Swap			( String& q );
	void		Copy			( cString& q );
	void		Clear    		( );								// quick clear string: len=0
	void		Truncate		( long newlen );
	void		Resize			( long newlen, UCS4Char padding=' ' );

	long		Len				( ) const							{ return count; }
	CharSize	Csz				( )	const							{ return CharSize((data_and_csz&3)+1); }
	void		ResizeCsz		( CharSize );
	UCS4Char	operator[]  	( long i ) const;

	UCS1Char*	UCS1Text		( )									{ XXXTRAP(Csz()!=csz1); return Ucs1(); }
	UCS2Char*	UCS2Text		( )									{ XXXTRAP(Csz()!=csz2); return Ucs2(); }
	UCS4Char*	UCS4Text		( )									{ XXXTRAP(Csz()!=csz4); return Ucs4(); }
	ptr			Text			( )									{ return text; }
	cUCS1Char*	UCS1Text		( ) const							{ XXXTRAP(Csz()!=csz1); return Ucs1(); }
	cUCS2Char*	UCS2Text		( ) const							{ XXXTRAP(Csz()!=csz2); return Ucs2(); }
	cUCS4Char*	UCS4Text		( ) const							{ XXXTRAP(Csz()!=csz4); return Ucs4(); }
	cptr		Text			( ) const							{ return text; }

	bool		IsWritable  	( ) const							{ return next==this; }
	bool		NotWritable  	( ) const							{ return next!=this; }
	void		MakeWritable	( );
	void		_check			( cstr filename, long line_no ) const;

	String		SubString   	( long a, long e ) const			{ return String(*this,a,e); }
	String		MidString   	( long a, long n ) const			{ return String(*this,a,a+n); }
	String		MidString		( long a ) const					{ return String(*this,a,Len()); }
	String		LeftString  	( long n ) const					{ return String(*this,(long)0,n); }
	String		RightString 	( long n ) const					{ return String(*this,Len()-n,Len()); }

	String&		operator=   	( cString& q );

	String		operator+   	( cString& q ) const;				// concatenate strings
	String&		operator+=  	( cString& q )						{ return *this = *this + q; }

	String		operator*   	( long n ) const;					// repeat string contents n times
	String&		operator*=  	( long n )							{ return *this = *this * n; }

	bool		operator==  	( cString& q ) const				{ return count==q.count && _compare(q)==0; }
	bool		operator!=  	( cString& q ) const				{ return count!=q.count || _compare(q)!=0; }
	bool		operator<   	( cString& q ) const				{ return _compare(q)<0; }
	bool		operator>   	( cString& q ) const				{ return _compare(q)>0; }
	bool		operator>=  	( cString& q ) const				{ return _compare(q)>=0; }
	bool		operator<=  	( cString& q ) const				{ return _compare(q)<=0; }

	str			CString			( ) const;
	double		NumVal			( long* index_inout=NULL ) const;
	String		StrVal			( long* index_inout=NULL ) const;

	long		Find			( UCS4Char c, long startidx= 0 ) const;
	long		RFind			( UCS4Char c, long startidx= 0x7fffffff ) const;
	long		Find			( cString& s, long startidx= 0 ) const;
	long		RFind			( cString& s, long startidx= 0x7fffffff ) const;
	long		Find			( cUTF8Str s, long startidx= 0 ) const;
	long		RFind			( cUTF8Str s, long startidx= 0x7fffffff ) const;

	void		Replace     	( UCS4Char o, UCS4Char n );
	void		Replace     	( cString& o, cString& n );
	void		Swap			( UCS4Char o, UCS4Char n );

	String		ToUpper  		( )	const;
	String		ToLower  		( )	const;
	String		ToHtml			( ) const;
	String		FromHtml		( ) const;
	String		ToEscaped		( UCS4Char addquotes=no ) const;
	String		FromEscaped		( UCS4Char rmvquotes=no ) const;
	String		ToQuoted		( UCS4Char leftquote='"' ) const	{ return ToEscaped(leftquote); }
	String		FromQuoted		( UCS4Char leftquote='"' ) const	{ return FromEscaped(leftquote); }
	String		ToUCS1			( ) const;
	String		FromUCS1		( ) const;
	String		ToUCS2			( ) const;
	String		FromUCS2		( ) const;
	String		ToUCS4			( ) const;
	String		FromUCS4		( ) const;
	String		ToUTF8			( ) const;
	String		FromUTF8		( ) const;
	String		ToUrl			( bool preserve_dirsep=yes ) const;
	String		FromUrl			( ) const;
	String		ToMime			( ) const;
	String		FromMime		( ) const;
	String		ToTab			( int tabstops=8 ) const;
	String		FromTab			( int tabstops=8 ) const;

	String		ConvertedTo		( CharEncoding e ) const;
	String		ConvertedFrom	( CharEncoding e ) const;

friend  class   Var;
friend	class	VarData;
friend	void	TestStringClass	( );

static	void	PurgeCaches		( );
};

extern void		TestStringClass	( );



/* ********************************************************************************
				NON-MEMBER FUNCTIONS
******************************************************************************** */

extern cString	emptyString;
extern String	CharString	( UCS4Char c );
extern String	SpaceString	( long n, UCS4Char c=' ' );

extern String  	NumString	( double n );
extern String  	NumString	( long n );
extern String  	NumString	( ulong n );
inline String  	NumString	( int n )						{ return NumString((long)n); }
inline String  	NumString	( uint n )						{ return NumString((ulong)n); }
extern String  	HexString	( ulong n, int digits=0 );

inline String	SubString	( cString& s, long a, long e )	{ return s.SubString(a,e); }
inline String	MidString	( cString& s, long a, long n )	{ return s.MidString(a,n); }
inline String	MidString	( cString& s, long a )			{ return s.MidString(a); }
inline String	LeftString	( cString& s, long n )			{ return s.LeftString(n); }
inline String	RightString	( cString& s, long n )			{ return s.RightString(n); }

inline String	ToUpper		( cString& s )					{ return s.ToUpper(); }
inline String	ToLower		( cString& s )					{ return s.ToLower(); }
inline String	ToHtml		( cString& s )					{ return s.ToHtml(); }
inline String	FromHtml	( cString& s )					{ return s.FromHtml(); }
inline String	ToQuoted	( cString& s, UCS4Char f='"' )	{ return s.ToQuoted(f); }
inline String	FromQuoted	( cString& s, UCS4Char f='"' )	{ return s.FromQuoted(f); }
inline String	ToEscaped	( cString& s, UCS4Char f=no )	{ return s.ToEscaped(f); }
inline String	FromEscaped	( cString& s, UCS4Char f=no )	{ return s.FromEscaped(f); }

inline str		CString		( cString& s )					{ return s.CString(); }
inline double	NumVal		( cString& s )					{ return s.NumVal(); }
extern bool		Match		( cString& filename, cString& pattern );
extern bool		FNMatch		( cString& filename, cString& pattern, MatchType=fnm_basic );

// serrors.cp
extern	void	ForceError	( OSErr e, cString& msg );
inline	void	SetError	( OSErr e, cString& msg )		{ if(NoError()) ForceError(e,msg); }
inline	void	ForceError	( cString& msg )				{ ForceError(-1,msg); }
inline	void	SetError	( cString& msg )				{ if(NoError()) ForceError(-1,msg); }
extern	void	AppendToError	( cString& msg );
extern	void	PrependToError	( cString& msg );

inline	String	ErrorString	( OSErr e )						{ return ErrorStr(e); }
extern	String	ErrorString	( );




/* ****************************************************************
					INLINE FUNCTIONS ((public for class Var))
**************************************************************** */

inline void String::_init ( )
{
	text   = NULL;
	count  = 0;
	SetDataAndCsz(NULL,csz1);
	next   = this;
	prev   = this;
}

inline void String::_init ( cString& q )
{
	if (XXXSAFE) q._check(__FILE__,__LINE__);
	text   = q.text;
	count  = q.count;
	SetDataAndCsz(q.DataAndCsz());
	next   = q.next;
	prev   = (String*)&q;
	next->prev = prev->next = this;
}

inline void String::_init ( CharSize sz, long n )
{
	XXXTRAP(sz!=csz1&&sz!=csz2&&sz!=csz4);
	count  = n;
	text   = new char[sz*n];
	SetDataAndCsz(text,sz);
	next   = this;
	prev   = this;
}

inline void String::_kill()
{
	if (XXXSAFE) _check(__FILE__,__LINE__);

	if (next==this)
	{
		delete[] Data();
		//#if XXXSAFE					DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//	next = prev = NULL;			DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//	SetDataAndCsz(0);			DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//	text = NULL;				DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//#endif						DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
	}
	else
	{
		next->prev = prev;
		prev->next = next;
		//#if XXXSAFE					DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//	next = prev = NULL;			DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//	SetDataAndCsz(0);			DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//	text = NULL;				DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
		//#endif						DO NEVER EVER CHANGE DATA ONLY IN CERTAIN SECURITY LEVELS!!
	}
}


#endif



























