/* nlib.c  new general purpose function library */
/*
 Module Name: nlib
Programed By: Lynden Sherriff
 Tested Date: 20/1/99
	 Version: 2.0f
*/

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

#include "nlib.h"
//#include "mymalloc.h"
/* */
char *utoken_decode(char *str)
{
	static char bf[LBUFSZ];
	int tmp;
	char *ptr = bf;

	while (*str != '\0' && ptr-bf < LBUFSZ-1) {
		if (*str == '^') {
			if ( ( ( str[1] >= '0' && str[1] <= '9') ||
				   ( str[1] >= 'A' && str[1] <= 'F') ) &&

				 ( ( str[2] >= '0' && str[2] <= '9') ||
					( str[2] >= 'A' && str[2] <= 'F') ) ) {

				tmp = toupper(*++str) - '0';
				if (*str > '9')
					tmp -= 'A' - ('9' + 1);
				tmp *= 16;

				tmp += toupper(*++str) - '0';
				if (*str > '9')
					tmp -= 'A' - ('9' + 1);

				*ptr++ = tmp;
				str++;
			}
			else
				*ptr++ = *str++;
		}
		else
			*ptr++ = *str++;
	}
	*ptr = '\0';

	return bf;
}

char *utoken_encode(char *str)
{
	static char bf[LBUFSZ];
	char temp[5];
	char *ptr = bf;

	while (*str != '\0' && ptr-bf < LBUFSZ-1) {

		if (*str == ' ' || *str == '+' ||
			*str == '<' || *str == '>' ||
			*str == '(' || *str == ')' ||
			*str == ':' || *str == ';' ||
			*str == '\"'|| *str == '&' ||
			*str == ':' || *str == '%' ||
			*str == '^' ||
			*str == '\n'|| *str == '\r') {

			sprintf(temp,"%2X", (unsigned)*str);
			*ptr++ = '^';
			*ptr++ = temp[0];
			*ptr++ = temp[1];
			str++;
		}
		else
			*ptr++ = *str++;
	}
	*ptr = '\0';

	return bf;
}


/* Used to encrypt username/password for autologin */
char *pass_user_encrypt(char *pass, char *user)
{
	if (user && pass) {
		static char enc[BFSZ];
		char *u, *p;

		lcpy(enc, pass, BFSZ);
		for (u=user, p=enc; *p; p++) {
			*p += *u++;
			if (!*u) u = user;
		}
		return enc;
	}
	return NULL;
}

char *pass_user_decrypt(char *encoded, char *user)
{
	if (user && encoded) {
		static char enc[BFSZ];
		char *u, *p;

		lcpy(enc, encoded, BFSZ);
		for (u=user, p=enc; *p; p++) {
			*p -= *u++;
			if (!*u) u = user;
		}
		return enc;
	}
	return NULL;
}



char *remove_quote(char *str, char *result)
{
	char *buf = result;

	if (str) {
		while (*str != '\"' && *str != '\0') str++;

		if (str) {
			str++;
			while (*str != '\"' && *str != '\0')
				*buf++ = *str++;
		}
	}

	*buf = '\0';
	return result;
}

char *ncpy(char *dst, char *src, int len)
{
	if (dst) {
		if (src) {
			char *xdst = dst;
			int xlen = len;

			for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}

			*dst = 0;
			xdst[xlen] = 0;
		}
		else
			*dst = '\0';
	}
	return dst;
}

char *ncat(char *dst, char *src, int len)
{
    char *xdst = dst;
    int xlen = len;

	if (!dst || !src)
		return dst;

	for (; *dst != 0; dst++) len--;
    for (;(*src !=0) && (len>0); ) {*dst++  = *src++; len--;}

    *dst++ = 0;
    xdst[xlen] = 0;

    return xdst;
}

char *lcpy(char *dst, char *src, int len)
{
	if (dst && src && len > 0) {
		char *ptr = dst;

		len--;
		while (len > 0 && *src) {
			*ptr++ = *src++;
			len--;
		}
		*ptr = '\0';
	}
	return dst;
}

char *lcat(char *dst, char *src, int len)
{
	if (dst && src) {
		char *ptr = dst + strlen(dst);

		len -= ptr - dst;
		len--;
		while (len > 0 && *src) {
			*ptr++ = *src++;
			len--;
		}
		*ptr = '\0';
	}
	return dst;
}

int lprintf( char *buffer, int bfsize, char *format, ... )
{
	if (buffer && format && bfsize) {
		va_list arg_list;
		va_start(arg_list, format);

		return lvprintf( buffer, bfsize, format, arg_list);
	}
	return FALSE;
}

int lvprintf( char *buffer, int bfsize, char *format, va_list arg_list)
{
	if (buffer && format && bfsize) {
		char *bf = buffer;
		char temp[BFSZ],*data = NULL;
		char *start, *end;
		int size_left = bfsize, size, extras, i, min, max;
		long value[3];
		double dvalue;
		char *strvalue;

		bf[0] = '\0';

		start = format;
		end = strchr(format,'%');

		while (end && size_left > 0) {

		/* Update Buffer With ".....%" the dots first */
			size = end-start;
			if (size+1 > size_left) size = size_left-1;

			lcpy(bf,start,size+1);	/* Account for NULL */
			size_left -= size;
			bf += size;

		/* Find out where the ending '%xx' value is */
			start = end + 1;
			extras = 1;
			max = min = -1;
			while (	*start != '\0' &&
					*start != 'c' && *start != 'C' &&
					*start != 'd' && *start != 'i' &&
					*start != 'o' && *start != 'u' &&
					*start != 'x' && *start != 'X' &&
					*start != 'e' && *start != 'E' &&
					*start != 'f' && *start != 'g' &&
					*start != 'G' && *start != 'n' &&
					*start != 'p' && *start != '%' &&
					*start != 's' && *start != 'S') {
				if (*start == '*') {
					extras++;
					if (max == -1 && min == -1) {
						max = va_arg(arg_list, long);
						if (max < 0)
							max = 0;
					}
				}
				else if (*start == '.') {
					if (start[1] == '*')
						min = va_arg(arg_list, long);
					else
						min = atoi(start+1);
					if (min < 0)
						min = 0;
				}
				else if (max == -1 && (*start >= '0' && *start <= '9'))
					max = atoi(start);
				start++;
			}

			if (*start != ' ' && *start != '\0' && extras <= 3) {
				lcpy(temp,end,(start+1-end) + 1);

			/* Setup Values min-max variables */
				if (min > -1) {
					value[0] = min;
					value[1] = max;
				}
				else {
					value[0] = max;
					value[1] = min;
				}

				if (*start == 'f' || *start == 'e' || *start == 'g' ||
					*start == 'E' || *start == 'G') {
					start++;

					dvalue = va_arg(arg_list, double);

					data = mymalloc( 2000 );
					if (data) {
						data[0] = '\0';

					/* User sprintf to display */
						if (extras == 1)
							sprintf(data,temp,dvalue);
						else if (extras == 2)
							sprintf(data,temp,value[0], dvalue);
						else if (extras == 3)
							sprintf(data,temp,value[0],value[1],dvalue);
					}
				}
				else if (*start == 's' || *start == 'S') {
					start++;

				/* Setup Values */
					strvalue = va_arg(arg_list, char *);

				/* Determine the amount of space needed. (min + max + 5) */
					if (strvalue) {
						if (min != -1) {
							if (max > min)
								size = max + 2;
							else
								size = min + 2;
						}
						else {
							size = max;

						/* Have to realloc data */
							i = strlen(strvalue);
							if (i+10 > size)
								size = i +10;
						}

						data = mymalloc( size );
						if (data) {
							data[0] = '\0';

							if (extras == 1)
								sprintf(data,temp,strvalue);
							else if (extras == 2)
								sprintf(data,temp,value[0], strvalue);
							else if (extras == 3)
								sprintf(data,temp,value[0],value[1],strvalue);
						}
					}
					else {
						data = mymalloc( 10 );
						if (data)
							strcpy(data,"(NULL)");
					}
				}
				else if (*start == '%') {
					data = mymalloc( 3);
					strcpy(data,"%");
					start++;
				}
				else {
					data = mymalloc( 23 );
					if (data) {
						data[0] = '\0';
						start++;

					/* Setup Values */
						value[extras-1] = va_arg(arg_list, long);

					/* User sprintf to display */
						sprintf(data,temp,value[0],value[1],value[2]);
					}
				}

				if (data) {
					size = strlen(data);
					if (size+1 > size_left) size = size_left-1;
					lcpy(bf,data,size+1);
					size_left -= size;
					bf += size;

					myfree(data);
				}
			}
			else {
			/* Not sure if so leave untouched */
				size = ++start-end;
				if (size+1 > size_left) size = size_left-1;

				lcpy(bf,end,size+1);
				size_left -= size;
				bf += size;
				start++;
			}
			end = strchr(start,'%');
		}

		lcpy(bf,start,size_left);
		size_left -= strlen(bf);
		return bfsize - size_left;
	}
	return FALSE;
}

/*****************************************************************/
/*****************************************************************/
/*****************************************************************/

#ifdef NEED_STRERROR
	extern int sys_nerr;
	extern char *sys_errlist[];

	char *strerror(int x)
	{
		if ( (x < sys_nerr) && (x >= 0) )
			return sys_errlist[x];
		return "No error";
	}
#endif

/*****************************************************************/
/*****************************************************************/
/*****************************************************************/

/*
 * This array is designed for mapping upper and lower case letter
 * together for a case independent comparison.  The mappings are
 * based upon ascii character sequences.
 */
const unsigned char charmap[] = {
        0000, 0001, 0002, 0003, 0004, 0005, 0006, 0007,
        0010, 0011, 0012, 0013, 0014, 0015, 0016, 0017,
        0020, 0021, 0022, 0023, 0024, 0025, 0026, 0027,
        0030, 0031, 0032, 0033, 0034, 0035, 0036, 0037,
        0040, 0041, 0042, 0043, 0044, 0045, 0046, 0047,
        0050, 0051, 0052, 0053, 0054, 0055, 0056, 0057,
        0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
        0070, 0071, 0072, 0073, 0074, 0075, 0076, 0077,
        0100, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0133, 0134, 0135, 0136, 0137,
        0140, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
        0150, 0151, 0152, 0153, 0154, 0155, 0156, 0157,
        0160, 0161, 0162, 0163, 0164, 0165, 0166, 0167,
        0170, 0171, 0172, 0173, 0174, 0175, 0176, 0177,
        0200, 0201, 0202, 0203, 0204, 0205, 0206, 0207,
        0210, 0211, 0212, 0213, 0214, 0215, 0216, 0217,
        0220, 0221, 0222, 0223, 0224, 0225, 0226, 0227,
        0230, 0231, 0232, 0233, 0234, 0235, 0236, 0237,
        0240, 0241, 0242, 0243, 0244, 0245, 0246, 0247,
        0250, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
        0260, 0261, 0262, 0263, 0264, 0265, 0266, 0267,
        0270, 0271, 0272, 0273, 0274, 0275, 0276, 0277,
        0300, 0301, 0302, 0303, 0304, 0305, 0306, 0307,
        0310, 0311, 0312, 0313, 0314, 0315, 0316, 0317,
        0320, 0321, 0322, 0323, 0324, 0325, 0326, 0327,
        0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
        0340, 0341, 0342, 0343, 0344, 0345, 0346, 0347,
        0350, 0351, 0352, 0353, 0354, 0355, 0356, 0357,
        0360, 0361, 0362, 0363, 0364, 0365, 0366, 0367,
        0370, 0371, 0372, 0373, 0374, 0375, 0376, 0377
};

char *s_strcpy( char *string1, char *string2)
{
	if (string1 && string2) {
		return strcpy(string1,string2);
	}
	else if (string1)
		string1[0] = '\0';
	return string1;
}

int s_strcmp( char *string1, char *string2 )
{
	if (string1 && string2) {
		if (*string1 == *string2)
			return strcmp(string1,string2);
		else
			return (*string1 - *string2);
	}
	return TRUE;
}

int s_stricmp( char *string1, char *string2)
{
	if (string1 && string2) {
		int ret = FALSE;
		char *ptr,*buf;

		ptr = string1;
		buf = string2;

		while (!ret &&
				*ptr != '\0' &&
				*buf != '\0') {
			ret = charmap[*ptr] - charmap[*buf];
			ptr++; buf++;
		}

		if (ret)
			return ret;
		else if (*ptr == '\0' && *buf == '\0')
			return FALSE;
		else if (*buf == '\0')
			return -TRUE;
	}
	return TRUE;
}

int s_strncmp( char *string1, char *string2, int count )
{
	if (string1 && string2) {
		int ret = FALSE;
		char *ptr,*buf;

		ptr = string1;
		buf = string2;

		while (count && !ret &&
				*ptr != '\0' &&
				*buf != '\0') {
			ret = *ptr - *buf;
			count--;
			ptr++; buf++;
		}

		if (!count)
			return ret;
		else if (*ptr == '\0' && *buf == '\0')
			return FALSE;
		else if (*buf == '\0')
			return -TRUE;
	}
	return TRUE;
}

int s_strnicmp( char *string1, char *string2, int count )
{
	if (string1 && string2) {
		int ret = FALSE;
		char *ptr,*buf;

		ptr = string1;
		buf = string2;

		while (count && !ret &&
				*ptr != '\0' &&
				*buf != '\0') {
			ret = charmap[*ptr] - charmap[*buf];
			count--;
			ptr++; buf++;
		}

		if (!count)
			return ret;
		else if (*ptr == '\0' && *buf == '\0')
			return FALSE;
		else if (*buf == '\0')
			return -TRUE;
	}
	return TRUE;
}

char *s_strlwr( char *string )
{
	if (string) {
#		ifdef unix
			char *ptr;
			ptr = string;

			while (*ptr != '\0') {
				*ptr = tolower(*ptr);
				ptr++;
			}
#		else
			return strlwr(string);
#		endif
	}
	return string;
}

char *s_strupr( char *string )
{
	if (string) {
#		ifdef unix
			char *ptr;
			ptr = string;

			while (*ptr != '\0') {
				*ptr = toupper(*ptr);
				ptr++;
			}
#		else
			return strupr(string);
#		endif
	}
	return string;
}


int s_strtrm(char *str)
{
	if (str) {
		char *ptr = str;

	/* remove trailing spaces */
		while (*ptr) ptr++;
		while (*ptr == ' ' && ptr > str)
			*ptr-- = '\0';

		return TRUE;
	}
	return FALSE;
}

char *s_strcat(char *des, char *src,...)
{
	if (des && src) {
		va_list arg_list;
		char bf[LBUFSZ];

		va_start(arg_list, src);
		lvprintf(bf,LBUFSZ, src, arg_list);

		return strcat(des,bf);
	}
	return des;
}

char *s_stradd(char *base, char *add)
{
	char *value;
	if (add) {
		unsigned int new_size;
		if (base)
			new_size = strlen(base) + strlen(add);
		else
			new_size = strlen(add);

		value =(char *)myrealloc(base, new_size + 5);
		if (value) {
			if (base)
				strcat(value,add);
			else
				strcpy(value,add);

			return value;
		}
	}
	return base;
}

char *s_strstr( char *string1, char *string2 )
{
	if (string1 && string2)
		return strstr(string1,string2);

	return NULL;
}

char *s_strchr( char *string1, char string2 )
{
	if (string1 )
		return strchr(string1,string2);

	return NULL;
}

// MW: The s_stristr function is fundamentally broken: "-->" will not be found in the string "--->"
//   due to the way the "s2 = string2" is done. The caller code that was broken, is fixed by
//   using s_strstr instead (or strstrnc) and I am leaving this alone to avoid undesirable side effects of fixing this!!
char *s_stristr( char *string1, char *string2 )
{
	if (string1 && string2) {
		char *result = NULL;
		char *s1 = string1, *s2 = string2;

		while (*s1) {

			if (charmap[*s1] == charmap[*s2]) {
				if (s2 == string2)
					result = s1;

				s2++;
				if (!*s2)
					break;
			}
			else if (result) {
				s2 = string2;
				result = NULL;
			}
			s1++;
		}

	/* if we have found entire word */
		if (*s2)
			result = NULL;

		return result;
	}
	return NULL;
}


unsigned int s_strlen( char *string1 )
{
	if (string1)
		return strlen(string1);
	return 0;
}

char *s_strtoken( char *string, char *sep )
{
	static char bf[LBUFSZ], *ptr = NULL;
	char *end, *result = NULL;
	unsigned int len, i;

	if (string) {
		sprintf(bf,"%.*s",LBUFSZ-1,string);
		ptr = bf;
	}

	if (sep && ptr) {
		len = strlen(sep);
		end = ptr;

		while (1) {
			if (*end == '\0')
				break;
			for (i=0;i<len;i++) {
				if (*end == sep[i])
					break;
			}
			if (*end == sep[i])
				break;
			end++;
		}

		if (*end == '\0') {
			result = ptr;
			ptr = NULL;
		}
		else {
			*end = '\0';
			result = ptr;
			ptr = end+1;
		}
	}
	return result;
}

char *mystrtok(char *start, char *toks, char **upto)
{
	char *s=start;
	if (s==NULL) s = *upto;
	if (s==NULL) return NULL;

	for (;*s!=0;s++) {
		if (strchr(toks,*s)==NULL) break;
	}
	if (*s==0) return NULL;
	start = s;
	for (;*s!=0;s++) {
		if (strchr(toks,*s)!=NULL) break;
	}
	if (*s==0) {
		*upto = NULL;
		return start;
	}
	*s = 0;
	*upto = s+1;
	return start;
}


int s_atoi( char *string )
{
	if (string)
		return atoi(string);
	return 0;
}

long s_atol( char *string )
{
	if (string)
		return atol(string);
	return 0;
}

double s_atof( char *string )
{
	if (string)
		return atof(string);
	return 0;
}

/***************************************************/
/***************************************************/

char *valid_file(char *file)
{
	static char bf[BFSZ];
	char *str, *ptr;

	bf[0] = '\0';
	if (file) {
		ptr = bf;
		str = file;

		while (*str != '\0' && str - file < BFSZ-1) {

			if (isalpha(*str) || isdigit(*str)
				 || *str == '.' || *str == '_' || *str == '*' || *str == '-' || *str == '#'
				 || *str == ':' || *str == SEP || *str == '@' || *str == '%')
				*ptr++ = *str;
			else if (*str == ' ')
				*ptr++ = '_';

			str++;
		}
		*ptr = '\0';
	/*	while (*ptr == '_' && ptr > bf) *ptr-- = '\0'; */
	}

	return bf;
}

char *update_path(char *path)
{
	static char bf[BFSZ];
	char *begin, *str;

	bf[0] = '0';
	if (path) {
		lcpy(bf,path,BFSZ);
		begin = strstr(bf,"..\\");

		while (begin) {

			str = begin - 1;
			begin += 2;

			if (*str == SEP) {
				str--;

				while (str > bf && *str != SEP)
					str--;

				if (*str == SEP) {
					strcpy(str,begin);
					begin = str;
				}
			}
			begin = strstr(begin,"..\\");
		}


		begin = strstr(bf,".\\");
		while (begin) {
			str = begin - 1;
			begin += 1;
			if (*str == SEP) {
				strcpy(str,begin);
				begin = str;
			}

			begin = strstr(path,".\\");
		}
	}
	return bf;
}

char *attach_path(char *work_path, char *file)
{
	static char bf[BFSZ];
	char *v_file, *ptr;

	if (work_path && !strlen(work_path))
		work_path = NULL;

	if (file && !strlen(file))
		file = NULL;

	v_file = valid_file(file);
	if (work_path)
		lcpy(bf,work_path,BFSZ);
	else
		bf[0] = '\0';

	if (work_path && file) {
		ptr = bf + strlen(bf) - 1;

		if (*ptr == SEP && *v_file == SEP)
			*ptr = '\0';
		else if (*ptr != SEP && *v_file != SEP) {
			*++ptr = SEP;
			*++ptr = '\0';
			ptr--;
		}

	/* Account for 'd:\' */
		if (v_file[1] == ':')
			*ptr = '\0';

	/* Double check filename */
		lcat(bf,v_file,BFSZ);
	}
	else if (file)
		lcpy(bf,v_file,BFSZ);
	else if (work_path) {
		ptr = bf + strlen(bf) - 1;
		if (*ptr != SEP) {
			*++ptr = SEP;
			*++ptr = '\0';
		}
	}

	return bf;
}

char *attach_ext(char *path_file, char *ext)
{
	static char bf[BFSZ];
	char temp[BFSZ];

	if (path_file && ext) {
		lcpy(temp,ext,BFSZ);
		lcpy(bf,path_file,BFSZ);
		if (temp[0] != '.')
			strcat(bf,".");
		lcat(bf,temp,BFSZ);
	}
	else if (path_file)
		lcpy(bf,path_file,BFSZ);
	else
		bf[0] = '\0';
	return bf;
}

char *strip_ext(char *in)
{
	static char out[BFSZ];
	char *s, *p;

	out[0] = '\0';
	if (in) {
		lcpy(out,in,BFSZ);

		s = strstr(out,".");
		if (s) {
			while (p = strstr(s+1,"."))
				s = p;
		}
		if (s) {
			if (s[1] != '\0')
				*s = '\0';
			else if (--s > out && *s != '.')
				s[1] = '\0';
		}
	}
	return out;
}

char *get_ext(char *in)
{
	static char out[BFSZ];
	char *s, *p;

	out[0] = '\0';
	if (in) {
		s = strstr(in,".");
		if (s) {
			while (p = strstr(s+1,"."))
				s = p;
		}
		if (s) {
			s++;
			lcpy(out,s,BFSZ);
		}
	}
	return out;
}

char *strip_path(char *in, int rm_extend)
{
	static char out[BFSZ];
	char *s;

	out[0] = '\0';

	if (in) {
		s = in + strlen(in);
		while (*s != SEP &&  s > in) s--;
		if (*s == SEP) s++;

		lcpy(out,s,BFSZ);

		if (rm_extend)
			return strip_ext(out);
	}
	return out;
}

char *strip_filename(char *in)
{
	static char out[BFSZ];
	char *s;

	if (in) {
		lcpy(out,in,BFSZ);
		s = out + strlen(out);

		while (*s != SEP &&  s > out) s--;
		if (*s == SEP)
			s++;

		if (s) *s = '\0';
	}
	else
		out[0] = '\0';

	if (*out == '\0')
		return NULL;
	return out;
}


char *remove_path(char *path, int want_remove)
{
	static char out[BFSZ];
	char *s;

	if (path) {
		lcpy(out,strip_filename(path),BFSZ);
		s = out + strlen(out) - 1;

		while (*s != SEP &&  s > out) s--;
		if (s) *s = '\0';

		if (want_remove)
			s = strip_path(path,FALSE);
		else
			s = strip_filename(path);

		if (s)
			lcpy(out,s,BFSZ);
		else
			out[0] = '\0';
	}
	else
		out[0] = '\0';

	if (*out == '\0')
		return NULL;
	return out;
}

/***************************************************/
/***************************************************/

void char_replace(char *ptr, char search, char with)
{
	while (*ptr != '\0') {
		if (*ptr == search)
			*ptr = with;
		ptr++;
	}
}

/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/

void exchange(void *a, void *b, size_t size) {
    size_t i;

    /******************
     *  exchange a,b  *
     ******************/
	{
		int t, *x, *y;
		for (i = sizeof(int); i <= size; i += sizeof(int)) {
			x = (int *)a;
			y = (int *)b;
			t = *x;
			*(x++) = *y;
			*(y++) = t;
		}
	}
	{
		char t, *x, *y;
		for (i = i - sizeof(int) + 1; i <= size; i++) {
			x = (char *)a;
			y = (char *)b;
			t = *x;
			*(x++) = *y;
			*(y++) = t;
		}
	}
}

void myqsort(void *base, size_t nmemb, size_t size,
        int (*compar)(const void *, const void *)) {
    void *lbStack[MAXSTACK], *ubStack[MAXSTACK];
    int sp;
    unsigned int offset;

	if (nmemb < 1)
		return;

    /********************
     *  ANSI-C qsort()  *
     ********************/

    lbStack[0] = (char *)base;
    ubStack[0] = (char *)base + (nmemb-1)*size;
    for (sp = 0; sp >= 0; sp--) {
        char *lb, *ub, *m;
        char *P, *i, *j;

        lb = lbStack[sp];
        ub = ubStack[sp];

        while (lb < ub) {

            /* select pivot and exchange with 1st element */
            offset = (unsigned)(ub - lb) >> 1;
            P = lb + offset - offset % size;
            exchange (lb, P, size);

            /* partition into two segments */
            i = lb + size;
            j = ub;
            while (1) {
                while (i < j && compar(lb, i) > 0) i += size;
                while (j >= i && compar(j, lb) > 0) j -= size;
                if (i >= j) break;
                exchange (i, j, size);
                j -= size;
                i += size;
            }

            /* pivot belongs in A[j] */
            exchange (lb, j, size);
            m = j;

            /* keep processing smallest segment, and stack largest */
            if (m - lb <= ub - m) {
                if (m + size < ub) {
                    lbStack[sp] = m + size;
                    ubStack[sp++] = ub;
                }
                ub = m - size;
            } else {
                if (m - size > lb) {
                    lbStack[sp] = lb;
                    ubStack[sp++] = m - size;
                }
                lb = m + size;
            }
        }
    }
}

/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/

/* MEMORY FUNCTIONS */
/* **************** */
#ifdef _MEMORY_DEBUG
#	include <stdlib.h>
#	include <malloc.h>

	int	emsg		(char * arg_list, ...);

	MEM_SAFE memory_list = {0,0,0,0,0,0L,NULL};


	int ms_sort(const void *elem1, const void *elem2 )
	{
		MEM_ITEM *m1 = (MEM_ITEM *)elem1;
		MEM_ITEM *m2 = (MEM_ITEM *)elem2;

		return (char *)m1->memory - (char *)m2->memory;
	}

	int ms_exists(void *mem)
	{
		if (memory_list.number) {
			int i;
			for (i=0;i<memory_list.number;i++) {
				if (mem == memory_list.memory[i].memory)
					return TRUE;
			}
		}
		return FALSE;
	}

	int ms_add(void *mem, char *file, int line)
	{
		if (memory_list.number < MAX_MEM_SAFE_SIZE && mem) {
			memory_list.memory[memory_list.number].memory = mem;
			lcpy(memory_list.memory[memory_list.number].file,file,BFSZ);
			memory_list.memory[memory_list.number].line = line;
			memory_list.number++;

			memory_list.count++;
			if (memory_list.max < memory_list.number)
				memory_list.max = memory_list.number;

			memory_list.sort++;
			memory_list.cleared = 0;
			return TRUE;
		}
		return FALSE;
	}

	int ms_remove(void *mem)
	{
		if (memory_list.number) {
			int i;
			for (i=0;i<memory_list.number;i++) {
				if (mem == memory_list.memory[i].memory) {
					memory_list.number--;
					memcpy(	&memory_list.memory[i],
							&memory_list.memory[i+1],
							sizeof(MEM_ITEM) * (memory_list.number-i) );
					return TRUE;
				}
			}
		}
		return FALSE;
	}

	int ms_change(void *mem,void *result,char *file,int line)
	{
		if (!mem)
			return ms_add(result, file, line);
		if (!result)
			return ms_remove(mem);

		if (memory_list.number) {
			int i;

			for (i=0;i<memory_list.number;i++) {
				if (mem == memory_list.memory[i].memory) {
					memory_list.memory[i].memory = result;
					lcpy(memory_list.memory[i].file,file,BFSZ);
					memory_list.memory[i].line = line;
					return TRUE;
				}
			}
		}
		return FALSE;
	}

	void *ms_realloc(void *mem, int size, char *file, int line)
	{
		void *result = NULL;
		if (mem) {
			if (!ms_exists(mem))
				emsg("MEMORY: Tried to 'realloc' Invalid Address (%s:%d)",file,line);
		}

		result = realloc(mem,size);
		if (!result && size)
			emsg("MEMORY: Failed to correctly 'realloc' memory (size=%d) (%s:%d)",size,file,line);
		else {
			if (size < 0) {
				emsg("MEMORY: Tried to 'realloc' a negative size (%s:%d)",file,line);
				return NULL;
			}

			if (!ms_change(mem,result,file,line))
				emsg("MEMORY: Tried to 'realloc' invalid memory (%s:%d)",file,line);

		}

		return result;
	}

	void *ms_malloc(int amount, char *file, int line)
	{
		void *result = NULL;
		if (amount == 0)
			emsg("MEMORY: Tried to 'malloc' zero size (%s:%d)",file,line);
		if (amount < 0) {
			emsg("MEMORY: Tried to 'malloc' a negative size (%s:%d)",file,line);
			return NULL;
		}

		result = malloc(amount);
		if (!result)
			emsg("MEMORY: Failed to correctly 'malloc' memory (amount=%d) (%s:%d)",amount,file,line);
		else
			ms_add(result,file,line);

		return result;
	}

	void ms_free(void *mem, char *file, int line)
	{
		if (mem) {
			if (!ms_remove(mem))
				emsg("MEMORY: Tried to 'free' Invalid Address (%s:%d)",file,line);

			free(mem);
		}
	}

	void ms_info(void)
	{
		if (memory_list.number) {
			int i;
			emsg("MEMORY: INFO: Un-dealloced Memory = %d",memory_list.number);
			for (i=0;i<memory_list.number;i++)
				emsg("MEMORY: Failed to clear: (%s:%d)",memory_list.memory[i].file,memory_list.memory[i].line);
		}

		emsg("MEMORY: INFO: MAX = %d,  count = %d",memory_list.max,memory_list.count);
	}
#endif

/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/
/*******************************************************************************/

/* FILE FUNCTIONS */
/* ************** */
#ifdef	_FILE_DEBUG

	int	emsg		(char * arg_list, ...);

	FILE_SAFE file_list = {0,0,0,0,0,0L,NULL};


	int fs_add(FILE *mem, char *path, char *file, int line)
	{
		if (file_list.number < MAX_FILE_SAFE_SIZE && mem) {
			file_list.file[file_list.number].stream = mem;

			lcpy(file_list.file[file_list.number].file,file,BFSZ);
			lcpy(file_list.file[file_list.number].path,path,BFSZ);
			file_list.file[file_list.number].line = line;
			file_list.number++;

			file_list.count++;
			if (file_list.max < file_list.number)
				file_list.max = file_list.number;

			file_list.sort++;
			file_list.cleared = 0;
			return TRUE;
		}
		return FALSE;
	}

	int fs_remove(void *mem)
	{
		if (file_list.number) {
			int i;
			for (i=0;i<file_list.number;i++) {
				if (mem == file_list.file[i].stream) {
					file_list.number--;
					memcpy(	&file_list.file[i],
							&file_list.file[i+1],
							sizeof(FILE_ITEM) * (file_list.number-i) );
					return TRUE;
				}
			}
		}
		return FALSE;
	}


	FILE *fs_open(char *path,char *mode, char *file, int line)
	{
		FILE *result = NULL;
		if (file == NULL)
			emsg("FILE: Tried to 'open' NULL (%s:%d)",file,line);
		if (mode == NULL)
			emsg("FILE: Tried to 'open' {%s} in mode NULL (%s:%d)",path,file,line);

		result = fopen(path,mode);
		if (!result)
			emsg("FILE: Failed to correctly 'open' file (%s) (%s:%d)",path,file,line);
		else
			fs_add(result,path,file,line);

		return result;
	}

	void fs_close(FILE *mem, char *file, int line)
	{
		if (mem) {
			fclose(mem);
			if (!fs_remove(mem))
				emsg("FILE: Tried to 'close' Invalid File (%s:%d)",file,line);
		}
	}

	void fs_info(void)
	{
		if (file_list.number) {
			int i;
			emsg("FILE: INFO: Un-dealloced Files = %d",file_list.number);
			for (i=0;i<file_list.number;i++)
//#ifdef _DEBUG
				emsg("FILE: Failed to close: {%s} (%s:%d)",file_list.file[i].path,file_list.file[i].file,file_list.file[i].line);
//#else
//				emsg("FILE: Failed to close: {%s}",file_list.file[i].path);
//#endif
		}

		emsg("FILE: INFO: MAX = %d,  count = %d",file_list.max,file_list.count);
	}

#endif
