/*

    This file contains routines that do string and file manipulations

    (c) 2000 written by Jesse @ Netwin Ltd. (tm)

*/

#include <stdarg.h>
#include <io.h>
#include <stdio.h>
#include <alloc.h>
#include <stdlib.h>
#include <string.h>
#include "astr.h"

#ifndef TRUE
	#define FALSE 0
	#define TRUE (!FALSE)
#endif

#ifndef NULL
#define NULL 0x00000000
#endif

#ifdef WIN32
	#include <windows.h>
	#define vsnprintf _vsnprintf
#endif

char *safe_strdup(char *str){
    return strdup(str);
}
void safe_free(void **ptr){
    if (*ptr != NULL)
        free(*ptr);
    (*ptr) = NULL;
}
void *safe_malloc(int n){
    return malloc(n);
}
FILE *safe_fopen(char *filename, char *mode){
    return fopen(filename, mode);
}
int safe_fclose(FILE *f){
    return fclose(f);
}

long calculate_filelength(FILE *handle){
	long put_back_to, filelength;

	put_back_to = ftell(handle);

	fseek(handle, 0, SEEK_END);
	filelength = ftell(handle);

	fseek(handle, put_back_to, SEEK_SET);
	return filelength;
}


char *c_stristr(char *src, char *find){
	astr *s, *f;
	char *tmp, *ret;

	if (src == NULL || find == NULL) return NULL;
	if (*src == 0 || *find == 0) return NULL;

	s = astr_strdup("");
	f = astr_strdup("");
	astr_makestr(s, "%s", src);
	astr_makestr(f, "%s", find);
	strlwr(s->str);
	strlwr(f->str);
	tmp = strstr(s->str, f->str);
	ret = src - s->str + tmp;
	free_astr(&f);
	free_astr(&s);

	if (tmp == NULL) return NULL;
	return ret;
}

static astr bulk_astrings[4096];
static int bulk_astrings_total_count = 4096;
static int current_bulk_astrings_count = 1; //is one



static int spare_astr_buffer_sizes[200];
static char *spare_astr_buffer_ptrs[200];
static int spare_astr_count = 0;

void free_astr_buffers(void){
	int i;
	for (i=0; i<spare_astr_count; i++)
		safe_free(&((void *)(spare_astr_buffer_ptrs[i])));
	spare_astr_count = 0;
}

void get_new_astr_buffer(astr *fstr, int min_size){
	int i;
	for (i=0; i<spare_astr_count; i++)
		if (spare_astr_buffer_sizes[i] >= min_size){
			fstr->str = spare_astr_buffer_ptrs[i];
			fstr->max_size = spare_astr_buffer_sizes[i];
			fstr->strlen = 0;
			spare_astr_count--;
		 	spare_astr_buffer_ptrs[i]  = spare_astr_buffer_ptrs[spare_astr_count];
			spare_astr_buffer_sizes[i] = spare_astr_buffer_sizes[spare_astr_count];
			return;
		}
	fstr->str = (char *)safe_malloc(min_size);
	fstr->max_size = min_size;
	fstr->strlen = 0;
}

void alt_finished_with_astr_buffer(char *str, int n){
	if (str == NULL) return;
	if (n < 100 || spare_astr_count >= 198) {safe_free(&((void *)(str))); return;}
	spare_astr_buffer_ptrs[spare_astr_count] = str;
	spare_astr_buffer_sizes[spare_astr_count] = n;
	spare_astr_count++;
}

void finished_with_astr_buffer(astr *fstr){
	if (fstr->str == NULL) return;
	if (fstr->max_size < 100 || spare_astr_count >= 198) {safe_free(&((void*)(fstr->str))); return;}
	spare_astr_buffer_ptrs[spare_astr_count] = fstr->str; *(fstr->str) = 0;       fstr->str = NULL;
	spare_astr_buffer_sizes[spare_astr_count] = fstr->max_size; fstr->max_size = 0;
	spare_astr_count++;
}


astr *new_astr(void){
	if (current_bulk_astrings_count >= bulk_astrings_total_count){
		return (astr *)safe_malloc(sizeof(struct astr));
	}

	bulk_astrings[current_bulk_astrings_count].strlen = 0;
	bulk_astrings[current_bulk_astrings_count].str = NULL;
	bulk_astrings[current_bulk_astrings_count].pos = 0;
	bulk_astrings[current_bulk_astrings_count].max_size = -1;
	return &(bulk_astrings[current_bulk_astrings_count++]);
}

void done_with_astr_space(astr *fstr){
	if (&(bulk_astrings[current_bulk_astrings_count-1]) == fstr){
		current_bulk_astrings_count--;
		bulk_astrings[current_bulk_astrings_count].strlen = 0;
		bulk_astrings[current_bulk_astrings_count].str = NULL;
		bulk_astrings[current_bulk_astrings_count].pos = 0;
		bulk_astrings[current_bulk_astrings_count].max_size = -1;
	}else{
        int i;
        for (i=0;i<current_bulk_astrings_count;i++){
            if (&(bulk_astrings[i]) == fstr){
                current_bulk_astrings_count--;
        		bulk_astrings[i].strlen =   bulk_astrings[current_bulk_astrings_count].strlen;
         		bulk_astrings[i].str =      bulk_astrings[current_bulk_astrings_count].str;
    	    	bulk_astrings[i].pos =      bulk_astrings[current_bulk_astrings_count].pos;
    		    bulk_astrings[i].max_size = bulk_astrings[current_bulk_astrings_count].max_size;
            }
        }
    }
}


void remove_astr(astr *x){
    if (x != NULL)
        if (x->str != NULL)
	        safe_free(&((void *)(x->str)));
}

void remove_all_bulk_faststrings(void){
	int i;
	for (i=0; i<current_bulk_astrings_count; i++){
		safe_free(&((void *)(bulk_astrings[i].str)));
	}
	current_bulk_astrings_count = 1;
}



void removeCR(char *pass){
		char *tmp;
		if (pass == NULL) return;
		tmp = strchr(pass,'\r'); if (tmp != NULL) *(tmp) = 0;
		tmp = strchr(pass,'\n'); if (tmp != NULL) *(tmp) = 0;
}


void astr_ncopy(astr *fstr, char *str, int n){
	int m;

	m = (int)strlen(str);
	if (n+2 > fstr->max_size){
		finished_with_astr_buffer(fstr);
		get_new_astr_buffer(fstr, n+500);
		//fstr->max_size = n+500;
	}
	if (n > m) n = m;
	strncpy(fstr->str, str, n);
	fstr->str[n] = 0;
	fstr->strlen = n;
	fstr->pos = 0;
}
void astr_ncopy_append(astr *fstr, char *str, int n){
	char *s;
	int slen;

	if (n+fstr->strlen+2 > fstr->max_size ){
		s = fstr->str;
		slen = fstr->max_size;
		get_new_astr_buffer(fstr, n+fstr->strlen+500);
		//fstr->max_size = (n+fstr->strlen+500);
		strncpy(fstr->str, s, fstr->strlen + 1);
		alt_finished_with_astr_buffer(s, slen);
	}
	strncpy(fstr->str+fstr->strlen, str, n);
	fstr->str[fstr->strlen+n] = 0;
	fstr->strlen += n;
	fstr->pos = 0;
}





static char make_str_buff[64000];
static char *dynamic_make_str_buff;
static int dynamic_make_str_buff_size = 64000;


char *makestr(char *arg_list, ...){
	char *return_value;
    va_list arg_ptr;

    va_start(arg_ptr, arg_list);

    if (-1==vsnprintf(make_str_buff, 63000, arg_list, arg_ptr)){
		if (dynamic_make_str_buff == NULL){
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
		}
		if (dynamic_make_str_buff == NULL)
			abort();
			//exit(9);
		while (-1 == vsnprintf(dynamic_make_str_buff, (unsigned int)(dynamic_make_str_buff_size - 2), arg_list, arg_ptr)){
			safe_free(&((void *)(dynamic_make_str_buff)));
			dynamic_make_str_buff_size *= 2;
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
			if (dynamic_make_str_buff == NULL) abort();
//				exit(9);
		}
		return_value = safe_strdup(dynamic_make_str_buff);
	}else
		return_value = safe_strdup(make_str_buff);
	return return_value;
}



char *makestr_concat(char *firststr, char * arg_list, ...){
	char *cat;
    va_list arg_ptr;
	int n,m;
	char *output_ptr;

    va_start(arg_ptr, arg_list);
    if (-1==vsnprintf(make_str_buff, 63000, arg_list, arg_ptr)){
		if (dynamic_make_str_buff == NULL){
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
		}
		if (dynamic_make_str_buff == NULL)
			exit(9);
		while (-1 == vsnprintf(dynamic_make_str_buff, (unsigned int)(dynamic_make_str_buff_size - 2), arg_list, arg_ptr)){
			safe_free(&((void *)(dynamic_make_str_buff)));
			dynamic_make_str_buff_size *= 2;
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
			if (dynamic_make_str_buff == NULL)
				exit(9);
		}
		output_ptr = dynamic_make_str_buff;
	}else
		output_ptr = make_str_buff;
	n = (int) strlen(firststr); m = (int) strlen(output_ptr);
	cat = (char *) safe_malloc(n+m+2);
	if (cat==NULL) exit(9);
	strcpy(cat,firststr);//safe strcpy
	strcpy(cat+n,output_ptr); //safe strcpy
	*(cat+n+m) = 0;
	safe_free(&((void *)(firststr)));
	return cat;
}

void free_astr(astr **fstr_ptr){
	astr *fstr;

	if (fstr_ptr != NULL){
		fstr = *fstr_ptr;
		if (fstr != NULL){
			if (fstr->str != NULL)
				finished_with_astr_buffer(fstr);
			done_with_astr_space(fstr);
		}
		fstr_ptr = NULL;
	}
}

void astr_char_append(astr *firststr, char c){
	if (firststr->strlen + 3 > firststr->max_size){
		astr_makestr_append(firststr, "%c", c);
	}else{
		firststr->str[firststr->strlen++] = c;
		firststr->str[firststr->strlen] = 0;
	}
}

/*returns an int for combination purposes*/
void astr_makestr_append(astr *firststr, char * arg_list, ...){
	char *cat;
    va_list arg_ptr;
	char *output_ptr;
	int n,m, catlen;

    va_start(arg_ptr, arg_list);
    if (-1==vsnprintf(make_str_buff, 63000, arg_list, arg_ptr)){
		if (dynamic_make_str_buff == NULL){
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
		}
		if (dynamic_make_str_buff == NULL)
			exit(9);
		while (-1 == vsnprintf(dynamic_make_str_buff, (unsigned int)(dynamic_make_str_buff_size - 2), arg_list, arg_ptr)){
			safe_free(&((void *)(dynamic_make_str_buff)));
			dynamic_make_str_buff_size *= 5;
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
			if (dynamic_make_str_buff == NULL)
				exit(9);
		}
		output_ptr = dynamic_make_str_buff;
	}else
		output_ptr = make_str_buff;
	n = (int) firststr->strlen; m = (int) strlen(output_ptr);
	if(n+m+3<firststr->max_size){
		strcpy(firststr->str+firststr->strlen,output_ptr); //safe strcpy
		firststr->strlen += m;
	}
	cat = firststr->str;
	catlen = firststr->max_size;
	get_new_astr_buffer(firststr, 3*(n+m)+20000);
	strcpy(firststr->str, cat); //safe strcpy
	alt_finished_with_astr_buffer(cat, catlen);
	strcpy(firststr->str+n,output_ptr); //safe strcpy
	*(firststr->str+n+m) = 0;
	firststr->strlen = n+m;
	firststr->pos = 0;
	firststr->max_size = 3*(n+m)+20000;
}

astr *astr_strdup(char *copy_from){
	int i;
	struct astr *retstr;

	retstr = new_astr();
	i = (int) strlen(copy_from);
	retstr->strlen = i;
	if (i < 5000) i = 0;
	get_new_astr_buffer(retstr, i+64000);
	retstr->pos = 0;
	strcpy(retstr->str, copy_from); //safe strcpy
	return retstr;
}

void astr_makestr(astr *firststr, char * arg_list, ...){
    va_list arg_ptr;
	int m;
	char *output_ptr;

    va_start(arg_ptr, arg_list);
    if (-1==vsnprintf(make_str_buff, 63000, arg_list, arg_ptr)){
		if (dynamic_make_str_buff == NULL){
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
		}
		if (dynamic_make_str_buff == NULL)
			exit(9);
		while (-1 == vsnprintf(dynamic_make_str_buff, (unsigned int)(dynamic_make_str_buff_size - 2), arg_list, arg_ptr)){
			safe_free(&((void*)(dynamic_make_str_buff)));
			dynamic_make_str_buff_size *= 2;
			dynamic_make_str_buff = (char *) safe_malloc(dynamic_make_str_buff_size);
			if (dynamic_make_str_buff == NULL)
				exit(9);
		}
		output_ptr = dynamic_make_str_buff;
	}else
		output_ptr = make_str_buff;

	m = (int)strlen(output_ptr);
	if(m+1<firststr->max_size){
		strcpy(firststr->str,output_ptr); //safe strcpy
		firststr->strlen = m;
		return;
	}
	finished_with_astr_buffer(firststr);
	get_new_astr_buffer(firststr, m+20000);
	strcpy(firststr->str,output_ptr);//safe strcpy
	*(firststr->str+m) = 0;
	firststr->strlen = m;
	firststr->pos = 0;
}


astr *astr_load_file(char *buff){
	long l,num_bytes;
	FILE *f;
	astr *retstr;

	retstr = new_astr();
	f = safe_fopen(buff,"r");
	if (f==NULL) {
		//LogMessage("fast_load_file file {%s} not opened, error = \"%s\"\n",buff,strerror(errno));
		get_new_astr_buffer(retstr, 64000);
		*(retstr->str) = 0;
		retstr->strlen = 0;
		retstr->pos = 0;
		return retstr;
	}else{
		l = calculate_filelength(f);
		get_new_astr_buffer(retstr,l+2);
		//retstr->max_size = l+2;
			//(char *)mysafe_malloc(l+2, makestr_from_file, makestr_from_line);
		num_bytes = (long)fread(retstr->str, 1, (unsigned int)l, f);
		*(retstr->str+num_bytes) = 0;
		safe_fclose(f);
		retstr->strlen = num_bytes;
		retstr->pos = 0;
		return retstr;
	}
}

char *load_file_e(char *file){
	long l, numread;
	char *bytes;
	FILE *f;
	f = safe_fopen(file,"r");
	if (f==NULL) {
		//bytes = makestr("load_file, file {%s} not opened, error = \"%s\"\n<br>",file,strerror(errno));
		//ErrorLogMessage(bytes);
		return NULL;
	}
	l = calculate_filelength(f);
	bytes = (char *)safe_malloc(l+2);
	if (bytes==NULL){ /*ErrorLogMessage("not enough memory to load a file!");*/	safe_fclose(f); exit(9);}
	numread=(long)fread(bytes, 1, (unsigned int)l, f);
	*(bytes+numread) = 0;
	safe_fclose(f);
	return bytes;
}
char *load_file(char *file){
	long l, numread;
	char *bytes;
	FILE *f;
	f = safe_fopen(file,"r");
	if (f==NULL) {
		return safe_strdup("");
	}
	l = calculate_filelength(f);
	bytes = (char *)safe_malloc(l+2);
	if (bytes==NULL){ /*ErrorLogMessage("not enough memory to load a file!");*/	safe_fclose(f); exit(9);}
	numread=(long)fread(bytes, 1, (unsigned int)l, f);
	*(bytes+numread) = 0;
	safe_fclose(f);
	return bytes;
}
int save_file(char *file, char *content){
	FILE *f;
	f = safe_fopen(file,"w");
	if (f==NULL) return FALSE;
	fwrite(content, 1, strlen(content), f);
	safe_fclose(f);
	return TRUE;
}


