
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <stddef.h>
#include <stdio.h>
#include <stdarg.h>

#include "logger.h"
#include "pldstr.h"


/*-----------------------------------------------------------------\
 Function Name	: *PLD_strstr
 Returns Type	: char
 	----Parameter List
	1. char *haystack, 
	2.  char *needle, 
	3.  int insensitive, 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
char *PLD_strstr(char *haystack, char *needle, int insensitive)
{
	char *hs, *ne;
	char *result;

//	LOGGER_log("%s:%d:\nHS=%s\nNE=%s\nIS=%d\n",FL, haystack, needle, insensitive );

	if (insensitive > 0)
	{
		hs = strdup(haystack);
		PLD_strlower(hs);
		ne = strdup(needle);
		PLD_strlower(ne);
	} else {
		hs = haystack;
		ne = needle;
	}

	result = strstr(hs, ne);
//	if (result) LOGGER_log("%s:%d:HIT: %s",FL, result);
//	else LOGGER_log("%s:%d:MISS (looking for %s|%s)",FL, needle,ne);

	if ((result != NULL)&&(insensitive > 0))
	{
		result = result -hs +haystack;
//		free(hs);
//		free(ne);

//		LOGGER_log("%s:%d:HIT - %s",FL, result );
	}
	if (insensitive) {
		free(hs);
		free(ne);
	}

	return result;
}

/*------------------------------------------------------------------------
Procedure:     PLD_strncpy ID:1
Purpose:       Copy characters from 'src' to 'dst', writing not more than 'len'
characters to the destination, including the terminating \0.
Thus, for any effective copying, len must be > 1.
Input:         char *dst: Destination string
char *src: Source string
size_t len: length of string
Output:        Returns a pointer to the destination string.
Errors:
------------------------------------------------------------------------*/
char *PLD_strncpy (char *dst, const char *src, size_t len)
{

	// Thanks go to 'defrost' of #c for providing the replacement
	//		code which you now see here.  It covers the errors better
	//		than my own previous code.

	// If we have no buffer space, then it's futile attempting
	//		to copy anything, just return NULL
	if (len==0) return NULL;

	// Providing our destination pointer isn't NULL, we can
	//		commence copying data across

	if (dst)
	{
		char *dp = dst;

		// If our source string exists, start moving it to the
		//		destination string character at a time.
		if (src)
		{
			char *sp = (char *)src;
			while ((--len)&&(*sp)) { *dp=*sp; dp++; sp++; }
		}

		*dp='\0';
	}

	return dst;
}



/*------------------------------------------------------------------------
Procedure:     PLD_strncat ID:1
Purpose:       Buffer size limited string concat function for two strings.
Input:         char *dst: Destination string
char *src: Source string
size_t len: Destination string buffer size - total string size cannot exceed this
Output:
Errors:        If the length of both strings in total is greater than the available buffer space
in *dst, we copy the maximum possible amount of chars from *src such that
buffer does not overflow.  A suffixed '\0' will always be appended.
------------------------------------------------------------------------*/
char *PLD_strncat( char *dst, const char *src, size_t len )
{
	char *dp = dst;
	const char *sp = src;
	size_t cc;

	if (len == 0) return dst;

	len--;

	// Locate the end of the current string.
	cc = 0;
	while ((*dp)&&(cc < len)) { dp++; cc++; }

	// If we have no more buffer space, then return the destination

	if (cc >= len) return dst;

	// While we have more source, and there's more char space left in the buffer

	while ((*sp)&&(cc < len))
	{
		cc++;
		*dp = *sp;
		dp++;
		sp++;
	}

	// Terminate dst, as a gaurantee of string ending.

	*dp = '\0';

	return dst;
}


/*------------------------------------------------------------------------
Procedure:     PLD_strncate ID:1
Purpose:       Catencates a source string to the destination string starting from a given
endpoint.  This allows for faster catencation of strings by avoiding the
computation required to locate the endpoint of the destination string.
Input:         char *dst: Destination string
char *src: Source string
size_t len: Destination buffer size
char *endpoint: Endpoint of destination string, location from where new
string will be appended
Output:
Errors:
------------------------------------------------------------------------*/
char *PLD_strncate( char *dst, const char *src, size_t len, char *endpoint )
{
	char *dp = dst;
	const char *sp = src;
	size_t cc = 0;

	if (len == 0) return dst;

	len--;

	// If endpoint does not relate correctly, then force manual detection
	// of the endpoint.

	if ((!endpoint)||(endpoint == dst)||((endpoint -dst +1)>(int)len))
	{
		// Locate the end of the current string.
		cc = 0;
		while ((*dp != '\0')&&(cc < len)) { dp++; cc++; }
	}
	else {
		cc = endpoint -dst +1;
		dp = endpoint;
	}

	// If we have no more buffer space, then return the destination

	if (cc >= len) return dst;

	// While we have more source, and there's more char space left in the buffer

	while ((*sp)&&(cc < len))
	{
		cc++;
		*dp = *sp;
		dp++;
		sp++;
	}

	// Terminate dst, as a gaurantee of string ending.

	*dp = '\0';

	return dst;
}





/*------------------------------------------------------------------------
Procedure:     XAM_strncasecmp ID:1
Purpose:       Portable version of strncasecmp(), this may be removed in later
versions as the strncase* type functions are more widely
implemented
Input:
Output:
Errors:
------------------------------------------------------------------------*/
int PLD_strncasecmp( char *s1, char *s2, int n )
{
	char *ds1 = s1, *ds2 = s2;
	char c1, c2;
	int result = 0;

	while(n > 0)
	{
		c1 = tolower(*ds1);
		c2 = tolower(*ds2);

		if (c1 == c2)
		{
			n--;
			ds1++;
			ds2++;
		}
		else
		{
			result = c2 - c1;
			n = 0;
		}

	}

	return result;

}





/*------------------------------------------------------------------------
Procedure:     XAM_strtok ID:1
Purpose:       A thread safe version of strtok()
Input:
Output:
Errors:
------------------------------------------------------------------------*/
char *PLD_strtok( struct PLD_strtok *st, char *line, char *delimeters )
{
	char *stop;
	char *dc;
	char *result = NULL;

	if ( line )
	{
		st->start = line;
	}

	//Strip off any leading delimeters

	dc = delimeters;
	while ((st->start)&&(*dc != '\0'))
	{
		if (*dc == *(st->start))
		{
			st->start++;
			dc = delimeters;
		}
		else dc++;
	}

	// Where we are left, is the start of our token.

	result = st->start;

	if ((st->start)&&(st->start != '\0'))
	{
		stop = strpbrk( st->start, delimeters ); /* locate our next delimeter */

		// If we found a delimeter, then that is good.  We must now break the string here
		// and don't forget to store the character which we stopped on.  Very useful bit
		// of information for programs which process expressions.

		if (stop)
		{

			// Store our delimeter.

			st->delimeter = *stop;

			// Terminate our token.

			*stop = '\0';


			// Because we're emulating strtok() behaviour here, we have to
			// absorb all the concurrent delimeters, that is, unless we
			// reach the end of the string, we cannot return a string with
			// no chars.

			stop++;
			dc = delimeters;
			while (*dc != '\0')
			{
				if (*dc == *stop)
				{
					stop++;
					dc = delimeters;
				}
				else dc++;
			} // While

			if (*stop == '\0') st->start = NULL;
			else st->start = stop;

		}
		else {
			st->start = NULL;
			st->delimeter = '\0';
		}
	}
	else  {
		st->start = NULL;
		result = NULL;
	}


	return result;
}



/*------------------------------------------------------------------------
Procedure:     PLD_strlower ID:1
Purpose:       Converts a string to lowercase
Input:         char *convertme : string to convert
Output:
Errors:
Comments:		Really need to validate against high-ASCII chars.
					Tested against strings like;
						Logo de la République française
						Македонски

------------------------------------------------------------------------*/
int PLD_strlower( char *convertme )
{

	char *c = convertme;

	while ( *c != '\0') {*c = (unsigned char)tolower((int)*c); c++;}

	return 0;
}


/*-----------------------------------------------------------------\
  Function Name	: *PLD_strreplace
  Returns Type	: char
  ----Parameter List
  1. char *source,		Original buffer, \0 terminated
  2.  char *searchfor, String sequence to search for
  3.  char *replacewith, String sequence to replace 'searchfor' with
  4.  int replacenumber , How many times to replace 'searchfor', 0 == unlimited
  ------------------
  Exit Codes	: Returns a pointer to the new buffer space.  The original 
  buffer will still remain intact - ensure that the calling
  program free()'s the original buffer if it's no longer
  needed
  Side Effects	: 
  --------------------------------------------------------------------
Comments:
Start out with static text matching - upgrade to regex later.

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
char *PLD_strreplace_general( struct PLD_strreplace *replace_details )
{
	char *new_buffer=NULL;
	char *source_end;
	char *segment_start, *segment_end, *segment_p;
	char *new_p;
	char *preexist_location=NULL;
	char *postexist_location=NULL;
	int replace_count=0;
	int size_required;
	int size_difference;
	int source_length;
	int searchfor_length;
	int replacewith_length;
	int segment_ok;

	if (replace_details->source == NULL) return NULL;

	source_length = strlen( replace_details->source );
	source_end = replace_details->source +source_length;
	searchfor_length = strlen(replace_details->searchfor);
	replacewith_length = strlen(replace_details->replacewith);
	size_difference = replacewith_length -searchfor_length;
	size_required = source_length;
	replace_count = replace_details->replacenumber;

	if ((replace_details->preexist != NULL)&&(strlen(replace_details->preexist) < 1)) replace_details->preexist = NULL;
	if ((replace_details->postexist != NULL)&&(strlen(replace_details->postexist) < 1)) replace_details->postexist = NULL;

	// If we have a 'pre-exist' request, then we need to check this out first
	//		because if the pre-exist string cannot be found, then there's very
	//		little point us continuing on in our search ( because without the
	//		preexist string existing, we are thus not qualified to replace anything )
	if (replace_details->preexist != NULL)
	{
		preexist_location = PLD_strstr(replace_details->source, replace_details->preexist, replace_details->insensitive);
		if (preexist_location == NULL)
		{
			return replace_details->source;
		}
	} 

	// Determine if initial POSTexist tests will pass, if we don't pick up
	//		anything here, then there's no point in continuing either
	if (replace_details->postexist != NULL)
	{
		char *p = replace_details->source;
		postexist_location = NULL;
		do {
			p = PLD_strstr(p, replace_details->postexist, replace_details->insensitive);
			if (p != NULL)
			{
				postexist_location = p;
				p = p +strlen(replace_details->postexist);
			} 
		} while (p != NULL);

		if (postexist_location == NULL)
		{
			return replace_details->source;
		}
	} 


	// Step 1 - determine the MAXIMUM number of times we might have to replace this string ( or the limit
	//		set by replacenumber
	//
	//	Note - we only need this number if the string we're going to be inserting into the 
	//	source is larger than the one we're replacing - this is so that we can ensure that
	//	we have sufficient memory available in the buffer.
	if (size_difference > 0)
	{
		if (replace_count == 0)
		{
			char *p, *q;

			p = replace_details->source;
			q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive);
			while (q != NULL)
			{
				replace_count++;
				//size_required += size_difference;
				p = q +searchfor_length;
				q = PLD_strstr(p, replace_details->searchfor, replace_details->insensitive);
			}

		}
		size_required = source_length +(size_difference *replace_count) +1;
	} else size_required = source_length +1;

	
	// Allocate the memory required to hold the new string [at least], check to see that
	//		all went well, if not, then return an error
	new_buffer = malloc( sizeof(char) *size_required);
	if (new_buffer == NULL)
	{
		LOGGER_log("%s:%d:PLD_strreplace:ERROR: Cannot allocate %d bytes of memory to perform replacement operation", FL, size_required);
		return replace_details->source;
	} 

	// Our segment must always start at the beginning of the source, 
	//		on the other hand, the segment_end can be anything from the
	//		next byte to NULL ( which is specially treated to mean to 
	//		the end of the source )
	segment_start = replace_details->source;


	// Locate the first segment 
	segment_ok = 0;
	segment_end = PLD_strstr(replace_details->source, replace_details->searchfor, replace_details->insensitive);

	// Determine if the first segment is valid in the presence of the 
	//	pre-exist and post-exist requirements
	while ((segment_end != NULL)&&(segment_ok == 0)\
			&&((replace_details->preexist != NULL)||(replace_details->postexist != NULL)))
	{
		int pre_ok = 0;
		int post_ok = 0;

		// The PREexist test assumes a couple of factors - please ensure these are
		//		relevant if you change any code prior to this point.
		//	
		//	1. preexist_location has already been computed and is not NULL
		//
		//	2. By relative position, the first preexist_location will be a valid location
		//			on which to validate for ALL replacements beyond that point, thus, we
		//			never actually have to recompute preexist_location again.
		//
		// 3. Conversely, the last computed postexist_location is valid for all 
		//			matches before it
		//
		if (preexist_location == NULL) pre_ok = 1;
		else if (preexist_location < segment_end){ pre_ok = 1;}

		if (postexist_location == NULL) post_ok = 1;
		else if (postexist_location > segment_end){ post_ok = 1;}

		if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); }
		else segment_ok = 1;
	} 

	segment_p = segment_start;
	new_p = new_buffer;
	while (segment_start != NULL)
	{	
		int replacewith_count;
		char *replacewith_p;

		if (segment_end == NULL) segment_end = source_end;

		replace_count--;

		// Perform the segment copy
		segment_p = segment_start;
		while ((segment_p < segment_end)&&(size_required > 0))
		{
			*new_p = *segment_p;
			new_p++;
			segment_p++;
			size_required--;
		}

		// Perform the string replacement
		if (segment_end < source_end)
		{
			replacewith_count = replacewith_length;
			replacewith_p = replace_details->replacewith;
			while ((replacewith_count--)&&(size_required > 0))
			{
				*new_p = *replacewith_p;
				new_p++;
				replacewith_p++;
				size_required--;
			}
		}

		if (size_required < 1 )
		{
			LOGGER_log("%s:%d:PLD_strreplace_general: Allocated memory ran out while replacing '%s' with '%s'",FL, replace_details->searchfor, replace_details->replacewith);
			*new_p='\0';
			break;
		}

		// Find the next segment
		segment_start = segment_end +searchfor_length;

		// If we've reached the end of the number of replacements we're supposed
		//		to do, then we prepare the termination of the while loop by setting
		//		our segment end to the end of the source.
		//
		// NOTE: Remember that the replace_count is pre-decremented at the start
		//		of the while loop, so, if the caller requested '0' replacements
		//		this will now be -1, thus, it won't get terminated from this == 0
		//		match.  Just thought you'd like to be reminded of that incase you
		//		were wondering "Huh? this would terminate an unlimited replacement"
		if (replace_count == 0)
		{
			segment_end = NULL;
		} else {
			// If our new segment to copy starts after the
			//		end of the source, then we actually have 
			//		nothing else to copy, thus, we prepare the
			//		segment_start varible to cause the while loop 
			//		to terminate.
			//
			// Otherwise, we try and locate the next segment
			//		ending point, and set the starting point to
			//		be on the 'other side' of the 'searchfor' string
			//		which we found in the last search.
			//
			if (segment_start > source_end) 
			{
				segment_start = NULL;
			} else {

				// Try find the next segment
				segment_ok = 0;
				segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive);

				// If we have a pre/post-exist requirement, then enter into this
				//		series of tests.  NOTE - at least one of the pre or post tests
				//		must fire to give an meaningful result - else we'll end up with 
				//		a loop which simply goes to the end of the searchspace buffer
				while ((segment_end != NULL)&&(segment_ok == 0)\
						&&((replace_details->preexist != NULL)||(replace_details->postexist != NULL)))
				{
					int pre_ok = 0;
					int post_ok = 0;

					// The PREexist test assumes a couple of factors - please ensure these are
					//		relevant if you change any code prior to this point.
					//	
					//	1. preexist_location has already been computed and is not NULL
					//
					//	2. By relative position, the first preexist_location will be a valid location
					//			on which to validate for ALL replacements beyond that point, thus, we
					//			never actually have to recompute preexist_location again.
					//
					// 3. Conversely, the last computed postexist_location is valid for all 
					//			matches before it
					//
					if (preexist_location == NULL) pre_ok = 1;
					else if (preexist_location < segment_end){ pre_ok = 1;}

					if (postexist_location == NULL) post_ok = 1;
					else if (postexist_location > segment_end){ post_ok = 1;}

					if ((pre_ok == 0)||(post_ok == 0)) { segment_end = PLD_strstr(segment_end +searchfor_length, replace_details->searchfor, replace_details->insensitive); }
					else segment_ok = 1;
				} 

			} // If-else segment_start > source_end

		}

	}

	*new_p = '\0';

//	if (replace_details->source != NULL) free (replace_details->source);
//	replace_details->source = new_buffer;
	return new_buffer;
}

/*-----------------------------------------------------------------\
  Function Name	: *PLD_strreplace
  Returns Type	: char
  ----Parameter List
  1. char **source, 
  2.  char *searchfor, 
  3.  char *replacewith, 
  4.  int replacenumber , 
  ------------------
  Exit Codes	: 
  Side Effects	: 
  --------------------------------------------------------------------
Comments:

--------------------------------------------------------------------
Changes:

\------------------------------------------------------------------*/
char *PLD_strreplace( char **source, char *searchfor, char *replacewith, int replacenumber )
{
	struct PLD_strreplace replace_details;
	char *tmp_source;

	replace_details.source = *source;
	replace_details.searchfor = searchfor;
	replace_details.replacewith = replacewith;
	replace_details.replacenumber = replacenumber;
	replace_details.preexist = NULL;
	replace_details.postexist = NULL;
	replace_details.insensitive = 0;

	tmp_source = PLD_strreplace_general( &replace_details );

	if (tmp_source != *source) *source = tmp_source;

	return *source;
}


/*-----------------------------------------------------------------\
 Function Name	: *PLD_dprintf
 Returns Type	: char
 	----Parameter List
	1. const char *format, 
	2.  ..., 
 	------------------
 Exit Codes	: 
 Side Effects	: 
--------------------------------------------------------------------
 Comments:
	This is a dynamic string allocation function, not as fast as some
	other methods, but it works across the board with both glibc 2.0 
	and 2.1 series.  
 
--------------------------------------------------------------------
 Changes:
 
\------------------------------------------------------------------*/
char *PLD_dprintf(const char *format, ...) 
{
	int n, size = 1024; // Assume we don't need more than 1K to start with
	char *p;
	va_list ap;

	// Attempt to allocate and then check 
	p = malloc(size *sizeof(char));
	if (p == NULL) return NULL;

	while (1) 
	{
		// Attempt to print out string out into the allocated space
		va_start(ap, format);
		n = vsnprintf (p, size, format, ap);
		va_end(ap);

		// If things went well, then return the new string
		if ((n > -1) && (n < size)) return p;

		// If things didn't go well, then we have to allocate more space
		//	based on which glibc we're using ( fortunately, the return codes
		//	tell us which glibc is being used! *phew*
		//
		// If n > -1, then we're being told precisely how much space we need
		// else (older glibc) we have to just guess again ...

		if (n > -1) size = n+1; // Allocate precisely what is needed
		else size *= 2;  // Double the amount allocated, note, we could just increase by 1K, but if we have a long string, we'd end up using a lot of realloc's

		// We could just realloc 'blind', but that'd be wrong and potentially cause a DoS, so
		//	instead, we'll be good and first attempt to realloc to a temp variable then, if all
		//	is well, we go ahead and update
		if (1)
		{
			char *tmp_p;

			tmp_p = realloc(p, size);
			if (tmp_p == NULL){ if (p != NULL) free(p); return NULL; }
			else p = tmp_p;
		}
	}

}


//-----------------END.
