//-----------------------------------------------------------------------------
// Copyright © 2005 - Philip Howard - All rights reserved
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
//
// 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.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
//-----------------------------------------------------------------------------
// package	libh/string
// homepage	http://libh.slashusr.org/
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	libh at ipal dot org
// homepage	http://phil.ipal.org/
//-----------------------------------------------------------------------------
// This file is best viewed using a fixed spaced font such as Courier
// and in a display at least 120 columns wide.
//-----------------------------------------------------------------------------

__DEFINE_BEGIN__
//-----------------------------------------------------------------------------
// struct       strpea_s
// typedef      strpea_t, strpea_p
//
// purpose      Define a value containing a string pointer, end pointer, and
//		allocation end pointer.
//-----------------------------------------------------------------------------
struct strpea_s {
    char *	ptr				;
    char *	end				;
    char *	aend				;
};
typedef struct strpea_s		strpea_t	;
typedef struct strpea_s *	strpea_p	;

__DEFINE_END__

__FMACRO_BEGIN__
//-----------------------------------------------------------------------------
// macro        strpea
//
// purpose      Create a strpea_s value consisting of a string pointer, end
//		pointer, and allocation end pointer.
//
// arguments    1 (char *) string pointer
//              2 (char *) string end pointer
//		3 (char *) allocation end pointer
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea(p,e,q) ({							\
	struct strpea_s libh__strpea;						\
	libh__strpea.ptr = (p);							\
	libh__strpea.end = (e);							\
	libh__strpea.aend = (q);						\
	libh__strpea;								\
})

//-----------------------------------------------------------------------------
// macro	strpea_null
//
// purpose	Make a null value strpea.
//-----------------------------------------------------------------------------
#define strpea_null strpea((NULL),(NULL),(NULL))

//-----------------------------------------------------------------------------
// macro        strpea_ptr
//
// purpose      Extract the string pointer from a strpea_s value.
//-----------------------------------------------------------------------------
#define strpea_ptr(s) ((s).ptr)

//-----------------------------------------------------------------------------
// macro        strpea_end
//
// purpose      Extract the end pointer from strpea_s value.
//-----------------------------------------------------------------------------
#define strpea_end(s) ((s).end)

//-----------------------------------------------------------------------------
// macro        strpea_aend
//
// purpose      Extract the allocation end pointer from strpea_s value.
//-----------------------------------------------------------------------------
#define strpea_aend(s) ((s).aend)

//-----------------------------------------------------------------------------
// macro        strpea_len
//
// purpose      Extract the string length from a strpea_s value.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_len(s) ({							\
	struct strpea_s libh__strpea;						\
	libh__strpea = (s);							\
	libh__strpea.end - libh__strpea.ptr;					\
})

//-----------------------------------------------------------------------------
// macro        strpea_alen
//
// purpose      Extract the allocation length from a strpea_s value.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_alen(s) ({							\
	struct strpea_s libh__strpea;						\
	libh__strpea = (s);							\
	libh__strpea.aend - libh__strpea.ptr;					\
})

//-----------------------------------------------------------------------------
// macro	strpea_strpe
//
// purpose	Convert a strpea value to a strpe value.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_strpe(s) ({							\
	struct strpea_s libh__strpea;						\
	struct strpe_s libh__strpe;						\
	libh__strpe.ptr = libh__strpea.ptr;					\
	libh__strpe.end = libh__strpea.end;					\
	libh__strpe;								\
})

//-----------------------------------------------------------------------------
// macro	strpea_strpl
//
// purpose	Convert a strpea value to a strpl value.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_strpl(s) ({							\
	struct strpea_s libh__strpea;						\
	struct strpl_s libh__strpl;						\
	libh__strpl.ptr = libh__strpea.ptr;					\
	libh__strpl.len = libh__strpea.end - libh__strpea.ptr;			\
	libh__strpl;								\
})

//-----------------------------------------------------------------------------
// macro	strpea_strpla
//
// purpose	Convert a strpea value to a strpla value.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_strpla(s) ({							\
	struct strpea_s libh__strpea;						\
	struct strpla_s libh__strpla;						\
	libh__strpla.ptr = libh__strpea.ptr;					\
	libh__strpla.len = libh__strpea.end - libh__strpea.ptr;			\
	libh__strpla.alen = libh__strpea.aend - libh__strpea.ptr;		\
	libh__strpla;								\
})

//-----------------------------------------------------------------------------
// macro	strpea_trim
//
// purpose	Trim the memory allocation of a pointed to strpea struct to
//		the size of the current string length, plus one for space to
//		append a null termination character.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_trim(p) ({							\
	struct strpea_s *libh__strpea_p;					\
	char *libh__ptr;							\
	libh__strpea_p = (p);							\
	libh__ptr = realloc( libh__strpea_p->ptr, libh__strpea_p->len + 1 );	\
	if ( libh__ptr ) {							\
		libh__strpea_p->ptr = libh__ptr;				\
		libh__strpea_p->aend = libh__ptr + libh__strpea_p->len + 1;	\
	}									\
})

//-----------------------------------------------------------------------------
// macro	strpea_new
//
// purpose	Allocate new string space of the specified size and return a
//		strpea value using this allocation.
//
// note		If the returned value is assigned to a struct strpea, that
//		struct strpea must not have any existing allocation of string
//		space or this will create a memory leak.  This is intended for
//		first time initialization.
//
// note		If memory allocation fails, or the requested size is 0, the
//		returned value will have a NULL pointer and zero length.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_new(s) ({							\
	struct strpea_s libh__strpea;						\
	size_t libh__alen;							\
	libh__alen = (s);							\
	libh__strpea.ptr = libh__alen ? malloc( libh__alen ) : (NULL);		\
	libh__strpea.end = libh__strpea.ptr;					\
	libh__strpea.aend = libh__strpea.ptr;					\
	if ( libh__strpea.aend ) libh__strpea.aend += libh__alen;		\
	libh__strpea;								\
})

//-----------------------------------------------------------------------------
// macro	strpea_realloc
//
// purpose	Reallocate memory for the pointed to strpea struct to the
//		specified size, if the current string will fit.  If the string
//		will not fit, make no changes.
//
// yields	0 - string fits in size, reallocation performed
//		1 - string does not fit, reallocation not performed
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_realloc(p,s) ({							\
	struct strpea_s *libh__strpea_p;					\
	char *libh__ptr;							\
	size_t libh__size;							\
	libh__strpea_p = (p);							\
	libh__size = (s);							\
	libh__strpea_p->end - libh__strpea_p->ptr >= libh__size ? ({		\
		libh__ptr = realloc( libh__strpea_p->ptr, libh__size );		\
		if ( libh__ptr || ! libh__size ) {				\
			libh__strpea_p->ptr = libh__ptr;			\
			libh__strpea_p->aend = libh__ptr + libh__size;		\
		}								\
	0; }) : 1;								\
})

//-----------------------------------------------------------------------------
// macro	strpea_free
//
// purpose	Free memory allocated for string space in the specified struct
//		strpea.
//
// note		This macro is "multiple evaluation safe".  All expressions
//		passed as arguments will only be evaluated once.
//-----------------------------------------------------------------------------
#define strpea_free(p) ({							\
	struct strpea_s *libh__strpea_p;					\
	libh__strpea_p = (p);							\
	if ( libh__strpea_p->ptr && libh__strpea_p->aend ) {			\
		free( libh__strpea_p->ptr );					\
		libh__strpea_p->ptr = (NULL);					\
		libh__strpea_p->end = (NULL);					\
		libh__strpea_p->aend = (NULL);					\
	}									\
	libh__strpea_p;								\
})

__FMACRO_END__

