//-----------------------------------------------------------------------------
// Copyright © 2006 - 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	split_s
//
// purpose	Keep the current state of a string splitting sequence.
//-----------------------------------------------------------------------------
struct split_s {
    int			flags					;
    int ( *		call	)( struct split_s * )		;
    char *		data_ptr				;
    char *		data_end				;
    char *		part_ptr				;
    char *		part_end				;
    char *		next_ptr				;
    union {
	int		    ch					;
	int ( *		    ch_call 	)( int )		;
	ssize_t ( *	    call 	)( char *, size_t )	;
	struct {
	    char *		ptr				;
	    char *		end				;
	}		    data				;
    }			sep					;
};

#define SPLIT_OBJ_ALLOCATED	0x0001
#define SPLIT_STR_ALLOCATED	0x0002

#define SPLIT_SEP_REFERENCE	0x0004
#define SPLIT_SEP_ALLOCATED	0x0008
#define SPLIT_SEP_REF_ALLOC	((SPLIT_SEP_REFERENCE)|(SPLIT_SEP_ALLOCATED))

#define SPLIT_SEP_INVERT	0x0100
#define SPLIT_SEP_NOCASE	0x0200
#define SPLIT_SEP_TRIMSPACES	0x0400

typedef struct split_s		split_s			;
typedef struct split_s		split_t		[1]	;
typedef struct split_s *	split_p			;

__DEFINE_END__

__MACRO_BEGIN__

//-----------------------------------------------------------------------------
// macro	split_diagnose
//
// purpose	Print diagnostic info about a split state struct.
//
// note		This is mostly for debugging these split macros/functions
//		but it may be handy for programmers to detect other problems.
//
// arguments	1 (split_p) struct to initialize
//		2 (FILE *) open file to print to
//
// returns	(void)
//-----------------------------------------------------------------------------
#define split_diagnose(o,f) ({								\
	split_p		libh__spx;							\
	FILE *		libh__file;							\
	char *		libh__ptr;							\
	libh__spx = (o);								\
	libh__file = (f);								\
	fprintf( libh__file, "%8p: (struct split_s)\n", libh__spx );			\
	fprintf( libh__file, "->flags         = %08x\n", libh__spx->flags );		\
	fprintf( libh__file, "->call          = %08p\n", libh__spx->call );		\
	fprintf( libh__file, "->data_ptr      = %08p (\"", libh__spx->data_ptr );	\
	libh__ptr = libh__spx->data_ptr;						\
	while ( libh__ptr < libh__spx->data_end )					\
		fputc( * libh__ptr ++, libh__file );					\
	fputs( "\")\n", libh__file );							\
	fprintf( libh__file, "->data_end      = %08p\n", libh__spx->data_end );		\
	fprintf( libh__file, "->part_ptr      = %08p (\"", libh__spx->part_ptr );	\
	libh__ptr = libh__spx->part_ptr;						\
	while ( libh__ptr < libh__spx->data_end )					\
		fputc( * libh__ptr ++, libh__file );					\
	fputs( "\")\n", libh__file );							\
	fprintf( libh__file, "->part_end      = %08p\n", libh__spx->part_end );		\
	fprintf( libh__file, "->next_ptr      = %08p\n", libh__spx->next_ptr );		\
	fprintf( libh__file, "->sep.data.ch   = %02x\n", libh__spx->sep.data.ch );	\
	fprintf( libh__file, "->sep.data.ptr  = %08p\n", libh__spx->sep.data.ptr );	\
	fprintf( libh__file, "->sep.data.end  = %08p\n", libh__spx->sep.data.end );	\
})

//-----------------------------------------------------------------------------
// macro	split_init
//
// purpose	Initialize a string split state struct.
//
// warning	This function should only be called for objects not yet
//		initialized, otherwise memory allocation leakage may occur.
//
// arguments	1 (split_p) struct to initialize
//
// returns	(void)
//-----------------------------------------------------------------------------
#define split_init(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	libh__sp->flags = 0;							\
	libh__sp->call = NULL;							\
	libh__sp->data_ptr = NULL;						\
	libh__sp->data_end = NULL;						\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
	libh__sp->next_ptr = NULL;						\
	libh__sp->sep.data.ptr = NULL;						\
	libh__sp->sep.data.end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_new
//
// purpose	Create a new string split state object in allocated memory.
//		It is initialized so that when split_end() is called, it will
//		be deallocated.
//
// arguments	-none-
//
// returns	(split_p) pointer to string split state object
//-----------------------------------------------------------------------------
#define split_new() ({								\
	split_p		libh__sp;						\
	libh__sp = malloc( sizeof (split_t) );					\
	if ( libh__sp ) {							\
		libh__sp->flags = SPLIT_OBJ_ALLOCATED;				\
		libh__sp->call = NULL;						\
		libh__sp->data_ptr = NULL;					\
		libh__sp->data_end = NULL;					\
		libh__sp->part_ptr = NULL;					\
		libh__sp->part_end = NULL;					\
		libh__sp->next_ptr = NULL;					\
		libh__sp->sep.data.ptr = NULL;					\
		libh__sp->sep.data.end = NULL;					\
	}									\
	libh__sp;								\
})

//-----------------------------------------------------------------------------
// macro	split_new_alloca
//
// purpose	Create a new string split state object in memory allocated in
//		the current automatic memory frame that goes away upon return
//		from the function doing split_new_alloca().  All caveats about
//		using the alloca() function apply here.
//
// warning	Do not use split_new_alloca() unless you understand all the
//		implications of using alloca().
//
// arguments	-none-
//
// returns	(split_p) pointer to string split state object
//-----------------------------------------------------------------------------
#define split_new_alloca() ({							\
	split_p		libh__sp;						\
	libh__sp = alloca( sizeof (split_t) );					\
	if ( libh__sp ) {							\
		libh__sp->flags = 0;						\
		libh__sp->call = NULL;						\
		libh__sp->data_ptr = NULL;					\
		libh__sp->data_end = NULL;					\
		libh__sp->part_ptr = NULL;					\
		libh__sp->part_end = NULL;					\
		libh__sp->next_ptr = NULL;					\
		libh__sp->sep.data.ptr = NULL;					\
		libh__sp->sep.data.end = NULL;					\
	}									\
	libh__sp;								\
})

//-----------------------------------------------------------------------------
// macro	split_end
//
// purpose	End an existing split state object, releasing memory if
//		previously allocated.
//
// arguments	1 (split_p) split object to destroy
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_end(o) ({								\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_STR_ALLOCATED ) {				\
		free( libh__sp->data_ptr );					\
	}									\
	if ( libh__sp->flags & SPLIT_OBJ_ALLOCATED ) {				\
		free( libh__sp );						\
	}									\
})

//-----------------------------------------------------------------------------
// macro	split_data_str
//
// purpose	Specify a string to be split.  Any previously specified string
//		is replaced, and the split state is reset.
//
// arguments	1 (split_p) split object to bind string to
//		2 (char *) string to bind to split object
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_data_str(o,s) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
	if ( libh__sp->flags & SPLIT_STR_ALLOCATED ) {				\
		free( libh__sp->data_ptr );					\
		libh__sp->flags &= ~ ( SPLIT_STR_ALLOCATED );			\
	}									\
	libh__sp->data_ptr = (char *)(s);					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->data_end = libh__sp->data_ptr + strlen( libh__sp->data_ptr );	\
})

//-----------------------------------------------------------------------------
// macro	split_data_strpl
//
// purpose	Specify a string to be split, with length, as one argument.
//		Any previously specified string is replaced, and the split
//		state is reset.
//
// arguments	1 (split_p) split object to bind string to
//		2 (strpl_t) string to bind to split object
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_data_strpl(o,s) ({						\
	split_p		libh__sp;						\
	strpl_t		libh__st;						\
	libh__sp = (o);								\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
	if ( libh__sp->flags & SPLIT_STR_ALLOCATED ) {				\
		free( libh__sp->data_ptr );					\
		libh__sp->flags &= ~ ( SPLIT_STR_ALLOCATED );			\
	}									\
	libh__st = (s);								\
	libh__sp->data_ptr = strpl_ptr(libh__st);				\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->data_end = libh__sp->data_ptr + strpl_len(libh__st);		\
})

//-----------------------------------------------------------------------------
// macro	split_data_str_pl
//
// purpose	Specify a string to be split, with length, as separate
//		arguments.  Any previously specified string is replaced, and
//		the split state is reset.
//
// arguments	1 (split_p) split object to bind string to
//		2 (char *) string to bind to split object
//		3 (size_t) length of string to bind to split object
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_data_str_pl(o,s,l) ({						\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
	if ( libh__sp->flags & SPLIT_STR_ALLOCATED ) {				\
		free( libh__sp->data_ptr );					\
		libh__sp->flags &= ~ ( SPLIT_STR_ALLOCATED );			\
	}									\
	libh__sp->data_ptr = (char *)(s);					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->data_end = libh__sp->data_ptr + (l);				\
})

//-----------------------------------------------------------------------------
// macro	split_data_strpe
//
// purpose	Specify a string to be split, with end pointer, as one
//		argument.  Any previously specified string is replaced, and
//		the split state is reset.
//
// arguments	1 (split_p) split object to bind string to
//		2 (strpe_t) string to bind to split object
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_data_strpe(o,s) ({						\
	split_p		libh__sp;						\
	strpe_t		libh__st;						\
	libh__sp = (o);								\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
	if ( libh__sp->flags & SPLIT_STR_ALLOCATED ) {				\
		free( libh__sp->data_ptr );					\
		libh__sp->flags &= ~ ( SPLIT_STR_ALLOCATED );			\
	}									\
	libh__st = (s);								\
	libh__sp->data_ptr = strpe_ptr(libh__st);				\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->data_end = strpe_end(libh__st);				\
})

//-----------------------------------------------------------------------------
// macro	split_data_str_pe
//
// purpose	Specify a string to be split, with end pointer, as separate
//		arguments.  Any previously specified string is replaced, and
//		the split state is reset.
//
// arguments	1 (split_p) split object to bind string to
//		2 (char *) string to bind to split object
//		3 (char *) end pointer to string
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_data_str_pe(o,s,e) ({						\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
	if ( libh__sp->flags & SPLIT_STR_ALLOCATED ) {				\
		free( libh__sp->data_ptr );					\
		libh__sp->flags &= ~ ( SPLIT_STR_ALLOCATED );			\
	}									\
	libh__sp->data_ptr = (char *)(s);					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->data_end = (char *)(e);					\
})

//-----------------------------------------------------------------------------
// macro	split_data_dup
//
// purpose	Make an internal duplicate of the currently specified string
//		to be split, so it is no longer referenced from its original
//		memory location.  The split state is reset to the beginning.
//
// note		This string will be deallocated when a new string is specified
//		or when this split object is ended.
//
// warning	Any previously obtained split part pointers will be invalid.
//
// arguments	1 (split_p) split object to duplicate string in
//
// yields	(int)  0 : OK, now duplicated
//		(int)  1 : OK, already duplicated
//		(int) -1 : ERROR, malloc failed
//-----------------------------------------------------------------------------
#define split_data_dup(o) ({							\
	split_p		libh__sp;						\
	char *		libh__str;						\
	ptrdiff_t	libh__diff;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	( libh__sp->flags & SPLIT_STR_ALLOCATED )				\
	? 1									\
	: ({	libh__len = libh__sp->data_end - libh__sp->data_ptr;		\
		libh__str = malloc( libh__len + 1 );				\
	})									\
	? ({	memcpy( libh__str, libh__sp->data_ptr, libh__len );		\
		libh__str[libh__len] = 0;					\
		libh__sp->flags |= SPLIT_STR_ALLOCATED;				\
		libh__sp->next_ptr = libh__str;					\
		libh__sp->data_ptr = libh__str;					\
		libh__sp->data_end = libh__str + libh__len;			\
		libh__sp->part_ptr = NULL;					\
		libh__sp->part_end = NULL;					\
		0;								\
	})									\
	: -1;									\
})

//-----------------------------------------------------------------------------
// macro	split_data_dup_alloca
//
// purpose	Make an internal duplicate of the currently specified string
//		to be split, so it is no longer referenced from its original
//		memory location, using automatic frame memory as returned by
//		the alloca() function.  The split state is reset to the
//		beginning.
//
// note		This string will be deallocated when the function that does
//		split_data_dup_alloca() returns.
//
// warning	Do not use split_data_dup_alloca() unless you understand
//		all the implications of using alloca().
//
// warning	Any previously obtained split part pointers will be invalid.
//
// arguments	1 (split_p) split object to duplicate string in
//
// yields	(int)  0 : OK, now duplicated
//		(int)  1 : OK, already duplicated
//		(int) -1 : ERROR, alloca failed
//-----------------------------------------------------------------------------
#define split_data_dup_alloca(o) ({						\
	split_p		libh__sp;						\
	char *		libh__str;						\
	ptrdiff_t	libh__diff;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	( libh__sp->flags & SPLIT_STR_ALLOCATED )				\
	? 1									\
	: ({	libh__len = libh__sp->data_end - libh__sp->data_ptr;		\
		libh__str = alloca( libh__len + 1 );				\
	})									\
	? ({	memcpy( libh__str, libh__sp->data_ptr, libh__len );		\
		libh__str[libh__len] = 0;					\
		libh__sp->next_ptr = libh__str;					\
		libh__sp->data_ptr = libh__str;					\
		libh__sp->data_end = libh__str + libh__len;			\
		libh__sp->part_ptr = NULL;					\
		libh__sp->part_end = NULL;					\
		0;								\
	})									\
	: -1;									\
})

//-----------------------------------------------------------------------------
// macro	split_reset
//
// purpose	Reset a split object to start back at the beginning.
//
// arguments	1 (split_p) split object to reset
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_reset(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_sptab
//
// purpose	Specify splitting on a group of one or more spaces and tabs.
//
// arguments	1 (split_p) split object to bind separator to
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_sptab(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_sptab;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_space
//
// purpose	Specify splitting on a group of one or more whitespace
//		characters.
//
// arguments	1 (split_p) split object to bind separator to
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_space(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_space;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_spctl
//
// purpose	Specify splitting on a group of one or more whitespace
//		characters and control characters.
//
// arguments	1 (split_p) split object to bind separator to
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_spctl(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_spctl;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_punct
//
// purpose	Specify splitting on any punctuation character.
//
// arguments	1 (split_p) split object to bind separator to
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_punct(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_punct;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_crlf
//
// purpose	Specify splitting on a CRLF, or lone CR, or lone LF.
//
// arguments	1 (split_p) split object to bind separator to
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_crlf(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_crlf;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_line
//
// purpose	Specify splitting on a CR(any newline), lone CR, or lone
//		(any newline).
//
// arguments	1 (split_p) split object to bind separator to
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_line(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_line;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_char
//
// purpose	Specify splitting on a specified single character.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (int) separator character
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_char(o,c) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_char;					\
	libh__sp->sep.data.ch = (c);						\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_ch_call
//
// purpose	Specify splitting on a single character tested true by a
//		specified callback function.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (int (*)(int)) function to test character for separator
//
// yields	(void)
//
// note		This macro can use the isxxxxx() functions in the Standard
//		C library as callbacks; specify them without arguments and
//		enclosed in parenthesis to avoid expansion from macro
//		implementations.  For example:
//
// example	split_sep_ch_call( this_obj, (islower) )
//-----------------------------------------------------------------------------
#define split_sep_ch_call(o,f) ({						\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_ch_call;				\
	libh__sp->sep.data.ch_call = (f);					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_call
//
// purpose	Specify splitting on whatever separator a callback function
//		decides is a separator at a given point in the data string.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (size_t (*)(char *,size_t)) function to test for separator
//
// yields	(void)
//
// note		The callback function is called with a pointer into the data
//		string and the length of the remainder of the data string.  It
//		is expected to return either -1 to indicate a separator is not
//		present at that point, or the length of the separator if there
//		is one at that point.
//-----------------------------------------------------------------------------
#define split_sep_call(o,f) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_REF_ALLOC );			\
	}									\
	libh__sp->call = split_sep_find_call;					\
	libh__sp->sep.data.call = (f);						\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_clist_str
//
// purpose	Specify splitting on any character in a list given as a
//		zero terminated string.  The zero character cannot be a
//		separator character in this case.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (char *) pointer to character list string.
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_clist_str(o,l) ({						\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_ALLOCATED );			\
	}									\
	libh__sp->flags |= SPLIT_SEP_REFERENCE;					\
	libh__sp->sep.data.ptr = (char *)(l);					\
	libh__sp->sep.data.end = libh__sp->sep.data.ptr + strlen( libh__sp->sep.data.ptr );	\
	libh__sp->call = split_sep_find_clist;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_ctable
//
// purpose	Specify splitting on any 8-bit character in a table.  Such a
//		table is an array indexed by a value from 0 to 255 yielding a
//		zero value for an index equal to the code of a character that
//		is not a separator, or a non-zero value for an index equal to
//		the code of a character that that is a separator.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (char *) pointer to character indexed table
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_ctable(o,t) ({						\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_ALLOCATED );			\
	}									\
	libh__sp->flags |= SPLIT_SEP_REFERENCE;					\
	libh__sp->sep.data.ptr = (char *)(t);					\
	libh__sp->sep.data.end = libh__sp->sep.data.ptr + ( (UCHAR_MAX) + 1 );	\
	libh__sp->call = split_sep_find_ctable;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_str
//
// purpose	Specify splitting on specified string given as a pointer to a
//		zero terminated string.  The zero character cannot be part of
//		the separator string.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (char *) separator string
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_str(o,s) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_ALLOCATED );			\
	}									\
	libh__sp->flags |= SPLIT_SEP_REFERENCE;					\
	libh__sp->sep.data.ptr = (char *)(s);					\
	libh__sp->sep.data.end = libh__sp->sep.data.ptr + strlen( libh__sp->sep.data.ptr );	\
	libh__sp->call = split_sep_find_string;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_strpl
//
// purpose	Specify splitting on specified string given as a pointer and
//		length in one argument.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (strpl_t) separator string pointer/length
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_strpl(o,s) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_ALLOCATED );			\
	}									\
	{									\
		strpl_t	libh__st;						\
		libh__st = (s);							\
		libh__sp->sep.data.ptr = strpl_ptr(libh__st);			\
		libh__sp->sep.data.end = strpl_end(libh__st);			\
	}									\
	libh__sp->flags |= SPLIT_SEP_REFERENCE;					\
	libh__sp->call = split_sep_find_string;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_str_pl
//
// purpose	Specify splitting on specified string given as a pointer and
//		length in two separate arguments.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (char *) separator string pointer
//		3 (size_t) separator string length
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_str_pl(o,s,l) ({						\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_ALLOCATED );			\
	}									\
	libh__sp->flags |= SPLIT_SEP_REFERENCE;					\
	libh__sp->sep.data.ptr = (char *)(s);					\
	libh__sp->sep.data.end = libh__sp->sep.data.ptr + (l);			\
	libh__sp->call = split_sep_find_string;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_strpe
//
// purpose	Specify splitting on specified string given as a pointer and
//		end pointer in one argument.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (strpe_t) separator string pointer/end
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_strpe(o,s) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_ALLOCATED );			\
	}									\
	{									\
		strpe_t libh__st;						\
		libh__st = (s);							\
		libh__sp->sep.data.ptr = strpe_ptr(libh__st);			\
		libh__sp->sep.data.end = strpe_end(libh__st);			\
	}									\
	libh__sp->flags |= SPLIT_SEP_REFERENCE;					\
	libh__sp->call = split_sep_find_string;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_str_pe
//
// purpose	Specify splitting on specified string given as a pointer and
//		end pointer in two separate arguments.
//
// arguments	1 (split_p) split object to bind separator to
//		2 (char *) separator string pointer
//		3 (char *) separator string end pointer
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_str_pe(o,s,e) ({						\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	if ( libh__sp->flags & SPLIT_SEP_ALLOCATED ) {				\
		free( libh__sp->sep.data.ptr );					\
		libh__sp->flags &= ~ ( SPLIT_SEP_ALLOCATED );			\
	}									\
	libh__sp->flags |= SPLIT_SEP_REFERENCE;					\
	libh__sp->sep.data.ptr = (char *)(s);					\
	libh__sp->sep.data.end = (char *)(e);					\
	libh__sp->call = split_sep_find_string;					\
	libh__sp->next_ptr = libh__sp->data_ptr;				\
	libh__sp->part_ptr = NULL;						\
	libh__sp->part_end = NULL;						\
})

//-----------------------------------------------------------------------------
// macro	split_sep_trim_spaces
//
// purpose	Specify that split parts will have spaces trimmed.
//
// arguments	1 (split_p) split object to modify
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_trim_spaces(o) ({						\
	(o)->sep_flags |= SPLIT_SEP_TRIMSPACES;					\
})

//-----------------------------------------------------------------------------
// macro	split_sep_no_trim_spaces
//
// purpose	Specify that split parts will not have spaces trimmed.
//
// arguments	1 (split_p) split object to modify
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_no_trim_spaces(o) ({						\
	(o)->sep_flags &= ~ ( SPLIT_SEP_TRIMSPACES );				\
})

//-----------------------------------------------------------------------------
// macro	split_sep_invert
//
// purpose	Specify that separator character detection in a list or table
//		is to be logically inverted.
//
// note		If a list or table is not currently specified as the separator,
//		then this setting will have no effect on string splitting.
//		However, the setting will be stored and retained so that it
//		will be in effect should a list or table be specified as the
//		separator at a later time.  This allows the calling program
//		to call split_sep_invert() first then set the list or table
//		later, possibly multiple times.
//
// arguments	1 (split_p) split object to modify
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_invert(o) ({							\
	(o)->sep_flags |= SPLIT_SEP_INVERT;					\
})

//-----------------------------------------------------------------------------
// macro	split_sep_no_invert
//
// purpose	Specify that separator character detection in a list or table
//		is not to be logically inverted.
//
// note		If a list or table is not currently specified as the separator,
//		then this setting will have no effect on string splitting.
//		However, the setting will be stored and retained so that it
//		will be in effect should a list or table be specified as the
//		separator at a later time.  This allows the calling program
//		to call split_sep_invert() first then set the list or table
//		later, possibly multiple times.
//
// arguments	1 (split_p) split object to modify
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_no_invert(o) ({						\
	(o)->sep_flags &= ~ ( SPLIT_SEP_INVERT );				\
})

//-----------------------------------------------------------------------------
// macro	split_sep_no_case
//
// purpose	Specify that separator comparison will not be case sensitive.
//
// arguments	1 (split_p) split object to modify
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_no_case(o) ({							\
	(o)->sep_flags |= SPLIT_SEP_NOCASE;					\
})

//-----------------------------------------------------------------------------
// macro	split_sep_case
//
// purpose	Specify that separator comparison will be case sensitive.
//
// arguments	1 (split_p) split object to modify
//
// yields	(void)
//-----------------------------------------------------------------------------
#define split_sep_case(o) ({							\
	(o)->sep_flags &= ~ ( SPLIT_SEP_NOCASE );				\
})

//-----------------------------------------------------------------------------
// macro	split_sep_dup
//
// purpose	Make an internal duplicate of the currently specified separator
//		memory data, so it is no longer referenced from its original
//		memory location.
//
// note		This memory will be deallocated when a new separator is
//		specified or when this object is destroyed.
//
// arguments	1 (split_p) split object to duplicate separator in
//
// yields	(int)  0 : OK, duplicated
//		(int)  1 : OK, already duplicated, or not needed
//		(int) -1 : ERROR, malloc failed
//-----------------------------------------------------------------------------
#define split_sep_dup(o) ({							\
	split_p		libh__sp;						\
	char *		libh__sep;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	( libh__sp->flags & SPLIT_SEP_REF_ALLOC != SPLIT_SEP_REFERENCE )	\
	? 1									\
	: ({									\
		libh__len = libh__sp->sep.data.end - libh__sp->sep.data.ptr;	\
		libh__sep = malloc( libh__len + 1 );				\
		libh__sep;							\
	})									\
	? ({									\
		memcpy( libh__sep, libh__sp->sep.data.ptr, libh__len );		\
		libh__sep[libh__len] = 0;					\
		libh__sp->flags |= SPLIT_SEP_ALLOCATED;				\
		libh__sp->next_ptr = libh__sep;					\
		libh__sp->sep.data.ptr = libh__sep;				\
		libh__sp->sep.data.end = libh__sep + libh__len;			\
		libh__sp->part_ptr = NULL;					\
		libh__sp->part_end = NULL;					\
		0;								\
	})									\
	: -1;									\
})

//-----------------------------------------------------------------------------
// macro	split_sep_dup_alloca
//
// purpose	Make an internal duplicate of the currently specified separator
//		memory data, so it is no longer referenced from its original
//		memory location, using automatic frame memory as returned by
//		the alloca() function.  The split state is reset to the
//		beginning.
//
// note		This data will be deallocated when the function that does
//		split_sep_dup_alloca() returns.
//
// warning	Do not use split_sep_dup_alloca() unless you understand
//		all the implications of using alloca().
//
// arguments	1 (split_p) split object to duplicate string in
//
// yields	(int)  0 : OK, now duplicated
//		(int)  1 : OK, already duplicated, or not needed
//		(int) -1 : ERROR, alloca failed
//-----------------------------------------------------------------------------
#define split_sep_dup_alloca(o) ({						\
	split_p		libh__sp;						\
	char *		libh__sep;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	( libh__sp->flags & SPLIT_SEP_REF_ALLOC != SPLIT_SEP_REFERENCE )	\
	? 1									\
	: ({									\
		libh__len = libh__sp->sep.data.end - libh__sp->sep.data.ptr;	\
		libh__sep = alloca( libh__len + 1 );				\
		libh__sep;							\
	})									\
	? ({									\
		memcpy( libh__sep, libh__sp->sep.data.ptr, libh__len );		\
		libh__sep[libh__len] = 0;					\
		libh__sp->next_ptr = libh__sep;					\
		libh__sp->sep.data.ptr = libh__sep;				\
		libh__sp->sep.data.end = libh__sep + libh__len;			\
		libh__sp->part_ptr = NULL;					\
		libh__sp->part_end = NULL;					\
		0;								\
	})									\
	: -1;									\
})

//-----------------------------------------------------------------------------
// macro	split_part_ptr
//
// purpose	Access the pointer of the most recently split part.
//
// arguments	1 (split_p) split object to access
//
// yields	(char *) pointer of split part
//-----------------------------------------------------------------------------
#define split_part_ptr(o) ((o)->part_ptr)

//-----------------------------------------------------------------------------
// macro	split_part_end
//
// purpose	Access the end pointer of the most recently split part.
//
// arguments	1 (split_p) split object to access
//
// yields	(char *) end pointer of split part
//-----------------------------------------------------------------------------
#define split_part_end(o) ((o)->part_end)

//-----------------------------------------------------------------------------
// macro	split_part_len
//
// purpose	Get the length of the most recently split part.
//
// arguments	1 (split_p) split object to access
//
// yields	(size_t) length of split part
//-----------------------------------------------------------------------------
#define split_part_len(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	libh__sp->part_end - libh__sp->part_ptr;				\
})

//-----------------------------------------------------------------------------
// macro	split_part_str (see WARNING)
//
// purpose	Get the pointer of the most recently split part after zero
//		terminating it for use as an ordinary C string.
//
// arguments	1 (split_p) split object to access
//
// yields	(char *) pointer of split part
//
// WARNING	This macro modifies the string the parts are selected from.
//		If this is the original string, modifying it is likely to
//		impact re-use of the string, including repeat splits.  The
//		use of split_data_dup() provides safer operation.
//-----------------------------------------------------------------------------
#define split_part_str(o) ({							\
	split_p		libh__sp;						\
	libh__sp = (o);								\
	* libh__sp->part_end = 0;						\
	libh__sp->part_ptr;							\
})

//-----------------------------------------------------------------------------
// macro	split_part_strpl
//
// purpose	Access the pointer and length of the most recently split part.
//
// arguments	1 (split_p) split object to access
//
// yields	(strpl_t) pointer/length of split part
//-----------------------------------------------------------------------------
#define split_part_ptrpl(o) (strpl(((o)->part_ptr),(((o)->part_end)-((o)->part_ptr))))

//-----------------------------------------------------------------------------
// macro	split_part_strpe
//
// purpose	Access the pointer and end pointer of the most recently split
//		part.
//
// arguments	1 (split_p) split object to access
//
// yields	(strpe_t) pointer/end of split part
//-----------------------------------------------------------------------------
#define split_part_ptrpe(o) (strpe(((o)->part_ptr),((o)->part_end)))

//-----------------------------------------------------------------------------
// macro	split_part_mem_copy
//
// purpose	Copy the split part to the specified destination location.
//
// arguments	1 (split_p) split object to access
//		2 (char *) destination location
//		3 (size_t) size of destination
//
// yields	(size_t) length actually copied
//-----------------------------------------------------------------------------
#define split_part_mem_copy(o,p,s) ({						\
	split_p		libh__sp;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	libh__len = (s);							\
	if ( libh__len > libh__sp->part_end - libh__sp->part_ptr )		\
	     libh__len = libh__sp->part_end - libh__sp->part_ptr;		\
	memcpy( (p), libh__sp->part_ptr, libh__len );				\
	libh__len;								\
})

//-----------------------------------------------------------------------------
// macro	split_part_str_copy
//
// purpose	Copy the split part to the destination location and make sure
//		it is zero terminated.
//
// arguments	1 (split_p) split object to access
//		2 (char *) destination location
//		3 (size_t) size of destination (including zero termination)
//
// yields	(size_t) length actually copied
//-----------------------------------------------------------------------------
#define split_part_str_copy(o,p,s) ({						\
	split_p		libh__sp;						\
	char *		libh__ptr;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	libh__len = (s) - 1;							\
	if ( libh__len > libh__sp->part_end - libh__sp->part_ptr )		\
	     libh__len = libh__sp->part_end - libh__sp->part_ptr;		\
	libh__ptr = (p);							\
	memcpy( libh__ptr, libh__sp->part_ptr, libh__len );			\
	libh__ptr[ libh__len ] = 0;						\
	libh__len;								\
})

//-----------------------------------------------------------------------------
// macro	split_part_str_append
//
// purpose	Append the split part to the destination string (already zero
//		terminated) and make sure it remains zero terminated.
//
// arguments	1 (split_p) split object to access
//		2 (char *) destination location (with zero terminated string)
//		3 (size_t) size of destination (including zero termination)
//
// yields	(size_t) length actually copied
//-----------------------------------------------------------------------------
#define split_part_str_append(o,p,s) ({						\
	split_p		libh__sp;						\
	char *		libh__ptr;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	libh__ptr = (p);							\
	libh__len = (s) - 1;							\
	while ( * libh__ptr && libh__len ) { ++ libh__ptr; -- libh__len; }	\
	if ( libh__len > libh__sp->part_end - libh__sp->part_ptr )		\
	     libh__len = libh__sp->part_end - libh__sp->part_ptr;		\
	memcpy( libh__ptr, libh__sp->part_ptr, libh__len );			\
	libh__ptr[ libh__len ] = 0;						\
	libh__len;								\
})

//-----------------------------------------------------------------------------
// macro	split_part_str_dup
//
// purpose	Duplicate the split part to allocated memory and make sure it
//		is zero terminated.
//
// arguments	1 (split_p) split object to access
//
// yields	(char *) pointer to allocated memory with duplicated part
//		(char *) NULL if memory cannot be allocated
//
// note		The caller must free() this memory when no longer needed for
//		this purpose and memory usage is to be recycled.
//-----------------------------------------------------------------------------
#define split_part_str_dup(o) ({						\
	split_p		libh__sp;						\
	char *		libh__ptr;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	libh__len = libh__sp->part_end - libh__sp->part_ptr;			\
	libh__ptr = malloc( libh__len + 1 );					\
	if ( libh__ptr ) {							\
		memcpy( libh__ptr, libh__sp->part_ptr, libh__len );		\
		libh__ptr[ libh__len ] = 0;					\
	}									\
	libh__ptr;								\
})

//-----------------------------------------------------------------------------
// macro	split_part_str_dup_alloca
//
// purpose	Duplicate the split part to memory allocated using the alloca()
//		function and make sure it is zero terminated.
//
// arguments	1 (split_p) split object to access
//
// yields	(char *) pointer to allocated memory with duplicated part
//		(char *) NULL if memory cannot be allocated
//
// note		The memory used for this duplicate is not directly freeable
//		but will be freed upon exit from the function this macro is
//		used in.
//
// note		Use of this macro should only be made where use of the alloca()
//		function is appropriate.  If you do not understand how alloca()
//		works, then do not use the split_part_str_dup_alloca() macro.
//-----------------------------------------------------------------------------
#define split_part_str_dup_alloca(o) ({						\
	split_p		libh__sp;						\
	char *		libh__ptr;						\
	size_t		libh__len;						\
	libh__sp = (o);								\
	libh__len = libh__sp->part_end - libh__sp->part_ptr;			\
	libh__ptr = alloca( libh__len + 1 );					\
	if ( libh__ptr ) {							\
		memcpy( libh__ptr, libh__sp->part_ptr, libh__len );		\
		libh__ptr[ libh__len ] = 0;					\
	}									\
	libh__ptr;								\
})

__MACRO_END__
