#include "pconf.h"

void FREE(void *p);
int STRLEN(const char *s);
int STRISYES(const char *s);
char *STRDUP(const char *s);
const char *STRCHR(const char *s, int c);
void STRSTRIP(char *s);
int STRCASECMP(const char *s1, const char *s2);
int STRPFX(const char *str, const char *pfx);
int STRCASEPFX(const char *str, const char *pfx);
extern char *STRLISTAPPEND(char ***list, int *total, const char *s);
extern void STRFREEARRAY(char **list, int total);

char *GETENV(const char *parameter);

FILE *FOPEN(const char *path, const char *mode);
void FCLOSE(FILE *fp);
int FGETC(FILE *fp);
int FPUTC(int c, FILE *fp);
void FSEEKNEXTLINE(FILE *fp);
void FSEEKPASTSPACES(FILE *fp);
char *FGETLINE(FILE *fp);
char *FSEEKNEXTPARAMETER(
	FILE *fp, char *buf, char comment, char delim 
);



/*
 *	Safer form of free().
 */
void FREE(void *p)
{
	if(p == NULL)
	    return;

	free(p);

	return;
}

/*
 *	Returns length of string, does not distingush error.
 */
int STRLEN(const char *s)
{
	int len = 0;

	if(s == NULL)
	    return(0);

	while((*s) != '\0')
	{
	    len++;
	    s++;
	}

	return(len);
}

/*
 *	Returns true if string s implies a `yes'.
 */
int STRISYES(const char *s)
{
	if(s == NULL)
	    return(0);

	while(ISBLANK(*s))
	    s++;

	/* Yes? */
	if(toupper(*s) == 'Y')
	    return(1);
	/* On? */
	if(STRCASEPFX(s, "ON"))
	    return(1);
	/* True? */
	if(toupper(*s) == 'T')
	    return(1);
	/* All else non-zero number? */
	if((*s) != '0')
	    return(1);

	return(0);
}

/*
 *	Safer form of strdup().
 */
char *STRDUP(const char *s)
{
	int len;
	char *sr = NULL, *sr_ptr;

	if(s == NULL)
	    return(sr);

	len = STRLEN(s);
	sr = sr_ptr = (char *)malloc((len + 1) * sizeof(char));
	if(sr == NULL)
	    return(sr);

	while((*s) != '\0')
	{
	    *sr_ptr++ = *s++;
	}
	(*sr_ptr) = '\0';

	return(sr);
}

/*
 *	Returns the position of c in s or NULL on no match.
 */
const char *STRCHR(const char *s, int c)
{
	if((s == NULL) || ((char)c == '\0'))
	    return(NULL);

	while((*s) != '\0')
	{
	    if((*s) == (char)c)
		return(s);
	    s++;
	}

	return(NULL);
}

/*
 *      Strips blank characters leading and tailing string s.
 */
void STRSTRIP(char *s)
{
	int tar, src, lead, tail;

	if(s == NULL)
	    return;
	if((*s) == '\0')
	    return;

	/* Strip leading blank characters. */
	lead = 0;
	while(ISBLANK(s[lead]))
	    lead++;

	if(lead > 0)
	{
	    for(tar = 0, src = lead; s[src] != '\0'; tar++, src++)
		s[tar] = s[src];

	    s[tar] = '\0';

	    /* Calculate tail position. */
	    tail = (tar > 0) ? tar - 1 : 0;
	}
	else
	{
	    /* Calculate tail position. */
	    tar = STRLEN(s);
	    tail = (tar > 0) ? tar - 1 : 0;
	}

	/* Strip tailing blank characters. */
	for(tar = tail; tar >= 0; tar--)
	{
	    if(ISBLANK(s[tar]))  
		s[tar] = '\0';
	    else
		break;
	}

	return;
}


/*
 *	Returns 1 for no match and 0 for match.
 */
int STRCASECMP(const char *s1, const char *s2)
{
	if((s1 == NULL) ||
	   (s2 == NULL)
	)
	    return(1);  /* False. */

	while((*s1) && (*s2))
	{
	    if(toupper(*s1) != toupper(*s2))
		return(1);      /* False. */

	    s1++;
	    s2++;
	}

	if((*s1) == (*s2))
	    return(0);	/* True. */
	else
	    return(1);	/* False. */
}

/*
 *      Returns true if pfx is a prefix of str (case insensitive),
 *	otherwise returns false.
 */
int STRPFX(const char *str, const char *pfx)
{
	/* If either strings is NULL, return false. */
	if((str == NULL) ||
	   (pfx == NULL)
	)       
	    return(0);

	/* If pfx contains no characters, return false. */
	if((*pfx) == '\0') 
	    return(0);  

	/* Begin prefix matching. */
	while((*pfx) != '\0')
	{
	    if((*str) != (*pfx))
		return(0);

	    str++; pfx++;
	}

	return(1);
}

/*
 *	Returns true if pfx is a prefix of str (case insensitive),
 *	otherwise returns false.
 */
int STRCASEPFX(const char *str, const char *pfx)
{
	/* If either strings is NULL, return false. */
	if((str == NULL) ||
	   (pfx == NULL)
	)
	    return(0);

	/* If pfx contains no characters, return false. */
	if((*pfx) == '\0')
	    return(0);

	/* Begin prefix matching. */
	while((*pfx) != '\0')
	{
	    if(toupper(*str) != toupper(*pfx))  
		return(0);

	    str++; pfx++;
	}

	return(1);
}

/*
 *	Dupliates string s (may not be NULL) and adds it to the given
 *	list.
 *
 *	Reallocates the given list and increases the total.
 *
 *	Returns pointer to duplicated string or NULL on error.
 */
char *STRLISTAPPEND(char ***list, int *total, const char *s)
{
	int i;
	char *ns;

	if((list == NULL) || (total == NULL) || (s == NULL))
	    return(NULL);

	if((*total) < 0)
	    (*total) = 0;

	ns = STRDUP(s);
	if(ns == NULL)
	    return(NULL);

	i = (*total);
	(*total) = i + 1;

	(*list) = (char **)realloc(
	    *list, (*total) * sizeof(char *)
	);
	if((*list) == NULL)
	{
	    (*total) = 0;
	    FREE(ns);
	    return(NULL);
	}

	(*list)[i] = ns;	

	return(ns);
}

/*
 *	Deallocates the given list of strings.
 */
void STRFREEARRAY(char **list, int total)
{
	int i;

	if(list == NULL)
	    return;

	for(i = 0; i < total; i++)
	    FREE(list[i]);

	FREE(list);

	return;
}


/*
 *	Safe form of getenv().
 */
char *GETENV(const char *parameter)
{
	if(parameter == NULL)
	    return(NULL);

	return(getenv(parameter));
}


/*
 *	Opens a file, returns its handle or NULL on failure. Works
 *	just like ansi fopen().
 */
FILE *FOPEN(const char *path, const char *mode)
{
	if((path == NULL) || (mode == NULL))
	    return(NULL);
	else
	    return(fopen(path, mode));
}

/*
 *	Closes a file opened by FOPEN.
 */
void FCLOSE(FILE *fp)
{
	if(fp == NULL)
	    return;

	fclose(fp);

	return;
}

/*
 *	Safe form of fgetc.
 */
int FGETC(FILE *fp)
{
	if(fp == NULL)
	    return(EOF);
	else
	    return(fgetc(fp));
}

/*
 *	Safe form of fputc.
 */
int FPUTC(int c, FILE *fp)
{
	if(fp == NULL)
	    return(EOF);
	else
	    return(fputc(c, fp));
}


/*
 *	Seeks to next line, escape sequences will be parsed.
 */
void FSEEKNEXTLINE(FILE *fp)
{
	int c;

	if(fp == NULL)
	    return;

	do
	{
	    c = FGETC(fp);

	    /* Escape sequence? */
	    if(c == '\\')
		c = FGETC(fp);
	    /* New line? */
	    else if(ISCR(c))
		break;

	} while(c != EOF);

	return;
}

/*
 *      Seeks fp past any spaces.
 */
void FSEEKPASTSPACES(FILE *fp)
{
	int c;

	if(fp == NULL)
	    return;

	while(1)
	{
	    c = FGETC(fp);
	    if(c == EOF)
		break;
  
	    if(ISBLANK(c))
		continue;
	 
	    fseek(fp, -1, SEEK_CUR);
	    break;
	}

	return;
}

/*
 *	String is loaded literally and only escape sequence handled is
 *	the occurance of two characters '\\' '\n' and where the '\n'
 *	character will be saved into the return string.
 *
 *	Spaces will not be striped, the fp will be positioned after the
 *	non-escaped newline or EOF (whichever is encountered first).
 *
 *	Return must be deallocated by calling function.
 */
char *FGETLINE(FILE *fp)
{
	int c, i, len = 0;
	char *strptr = NULL;
	char *strptr2;

	if(fp == NULL)
	    return(strptr);

	/* Begin reading string from file. */

	/* Get first character. */
	c = FGETC(fp);
	if(c == EOF)
	    return(strptr);

	/* Read string. */
	while(1)
	{           
	    i = len;    /* Current string index i. */
	    len++;      /* Current string length. */

	    /* Reallocate string buffer. */
	    strptr = (char *)realloc(strptr, len * sizeof(char));
	    if(strptr == NULL)
		break;

	    strptr2 = &(strptr[i]);     /* Pointer to current string index. */
	    (*strptr2) = c;             /* Set new character value. */

	    /* End of the line? */
	    if((c == EOF) ||
	       ISCR(c)
	    )
	    {
		(*strptr2) = '\0';
		break;
	    }
	    /* Escape sequence? */
	    else if(c == '\\')
	    {
		/* Read next character after backslash. */
		c = FGETC(fp);
		if(c == EOF)
		{
		    continue;
		}   
		else if(ISCR(c))
		{
		    /* New line, store it as is. */
		    (*strptr2) = c;
		}
		else if(c == '\\')
		{
		    /* Another backslash, store as a single backslash. */
		    (*strptr2) = c;
		}
		else
		{
		    /* All other escaped characters leave as is
		     * it will be set on the next loop.
		     */
		    continue; 
		}

		/* Read next character. */
		c = FGETC(fp);
	    }
	    /* Regular character. */
	    else
	    {
		/* Read next character. */
		c = FGETC(fp);
	    }
	}

	return(strptr);
}

/*
 *	Fetches the next parameter found at the file position fp.
 *
 *	If buf is NULL then a newly allocated string will be returned
 *	containing the fetched parm. If buf is not NULL, then buf will
 *	be realloc()'ed and returned as a new pointer containing
 *	the fetched parm.
 *
 *	If EOF is reached by the given fp position, then NULL will
 *	be returned and the given buf will have been free()ed by this
 *	function.
 */
char *FSEEKNEXTPARAMETER(
	FILE *fp, char *buf, char comment, char delim 
)
{
#ifndef FREAD_ALLOC_CHUNK_SIZE
# define FREAD_ALLOC_CHUNK_SIZE	8
#endif
	int c, buf_pos = 0, buf_len, buf_inc = FREAD_ALLOC_CHUNK_SIZE;

	if(fp == NULL)
	{
	    FREE(buf);
	    return(NULL);
	}

	/* Get length of buf (less than actual allocated is okay). */
	buf_len = ((buf == NULL) ? 0 : STRLEN(buf));

	/* Seek past spaces and comments to next parameter. */
	while(1)
	{
	    FSEEKPASTSPACES(fp);
	    c = FGETC(fp);
	    if(c == EOF)
	    {
		FREE(buf);
		return(NULL);
	    }   
	    else if(c == comment)
	    {
		FSEEKNEXTLINE(fp);
		continue;
	    }
	    else if(ISCR(c))
	    {
		continue;
	    }
	    else
	    {
		fseek(fp, -1, SEEK_CUR);
		break;
	    }
	}

	/* Begin fetching this parm. */
	while(1)
	{
	    /* Get next char. */
	    c = FGETC(fp);
	    if(c == EOF)
	    {
		break;
	    }   

	    /* Blank character reached? */
	    if(ISBLANK(c))
	    {
		/* Blank char reached, seek past delimiantor and position
		 * fp at beginning of value.
		 */
		if(delim == '\0')
		{
		    FSEEKPASTSPACES(fp);
		}
		else
		{
		    FSEEKPASTSPACES(fp);
	     
		    /* Seek to deim or newline. */
		    do
		    {
			c = FGETC(fp);
			if((c == EOF) || (c == delim))
			    break;
	     
			if(ISCR(c))
			{
			    fseek(fp, -1, SEEK_CUR);
			    break;
			}
	
		    } while(1);
	    
		    FSEEKPASTSPACES(fp);
		}
		break;
	    }

	    /* CR reached? */
	    if(ISCR(c))
	    {
		fseek(fp, -1, SEEK_CUR);
		break;
	    }

	    /* Deliminator reached? */  
	    if(c == delim)
	    {
		FSEEKPASTSPACES(fp);
		break;
	    }

	    /* Need to allocate buffer? */
	    if(buf_pos <= buf_len)
	    {
		buf_len += buf_inc;
			    
		buf = (char *)realloc(buf, buf_len * sizeof(char));
		if(buf == NULL)
		{
		    FSEEKNEXTLINE(fp);
		    return(NULL); 
		}
	    }

	    buf[buf_pos] = (char)c;
	    buf_pos++;
	}

	/* Put null terminating byte on buffer. */
	if(c == EOF)
	{
	    FREE(buf); 
	    buf = NULL;
	}
	else
	{
	    if(buf_pos <= buf_len)
	    {
		buf_len = buf_pos + 1;
	     
		buf = (char *)realloc(buf, buf_len * sizeof(char));
		if(buf == NULL)
		    return(NULL);
	    }       
	    buf[buf_pos] = '\0';
	}

	return(buf);
}
