/* Copyright (C) 1995 NetWin World, New Zealand, All rights reserved. */
/* General purpose array of string routines */

#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <assert.h>
#include "emsg.h"
#include "lib.h"
#define STR_NEW TRUE
#include "str.h"
void die(char *s);

#ifdef OS2
#define vsnprintf _vbprintf
#endif


#ifdef WIN32
#define vsnprintf _vsnprintf
#endif

#define SYSMALLOC TRUE

#ifdef WIN32 /* Win32 malloc seems more broken than most :-) */
#undef SYSMALLOC
#endif

#ifdef BOUNDSCHECKER
#define SYSMALLOC TRUE
#endif


#ifdef OS2 /* Win32 malloc seems more broken than most :-) */
#undef SYSMALLOC
#endif

#ifdef Mac /* Win32 malloc seems more broken than most :-) */
#undef SYSMALLOC
#endif

#ifdef NETWARExxx /* Trying the netware malloc, I think it's a good one. */
#undef SYSMALLOC
#endif

#ifdef BADMALLOC
#undef SYSMALLOC
#endif

/* It looks like this is always a good idea */
#undef SYSMALLOC

#include "mem.h"

typedef struct SBLOCK {int *data; int width, size, used, upto; 
         double wanted,got,lost; struct SBLOCK *next;} Sblock;
int mem_block_init(Sblock *b, int width, int size);
int *mem_fromblock(int bi, int wanted);
char *mem_malloc(int sz);
void mem_dofree(Sblock *b,int *x, char *file, int line);
void mem_blocks_show(void);
int mem_block_total(void);

#undef mymalloc
#undef myfree
#define mymalloc(x) mymalloc2(x,M_STR)
#define myfree(x) myfree2(x,M_STR)


static int strtotal;
String *str_newsz(int sz)
{
	String *s = str_new();
	str_expand(s,sz);
	return s;
}
String *str_new(void)
{
        String *s;

        s = mymalloc(sizeof(*s));
        if (s==NULL) die("string alloc");
        str_init(s);
        return s;       
}
int str_read(String *s, char *fname)
{
        FILE *f;
        char inbuff[2001];
        int i;
        char *e;

        str_copys(s,"",0);
	if (strlen(fname)==0) return FALSE;
#ifdef Mac
        f = fopen(fname,"rb");
#else
        f = fopen(fname,"r");
#endif

        if (f==NULL) return FALSE;

        for (;!feof(f);) {
          if (fgets(inbuff,2000,f)!=NULL) {
                i = strlen(inbuff);
                if (i>0) inbuff[i-1] = 0; /*Delete newline character */
                e = strchr(inbuff,'\015');
                if (e!=NULL) *e = 0;
                e = strchr(inbuff,'\012');
                if (e!=NULL) *e = 0;
                strcat(inbuff,"\015\012");
                str_adds(s,inbuff,strlen(inbuff));                      
          }
        }
        fclose(f);
        return TRUE;
}
int str_addfile(String *s, char *fname)
{
        FILE *f;
        char inbuff[2001];
        int i;
        char *e;

        f = fopen(fname,"r");

        if (f==NULL) return FALSE;

        for (;!feof(f);) {
          if (fgets(inbuff,2000,f)!=NULL) {
                i = strlen(inbuff);
                if (i>0) inbuff[i-1] = 0; /*Delete newline character */
                e = strchr(inbuff,'\015'); if (e!=NULL) *e = 0;
                e = strchr(inbuff,'\012'); if (e!=NULL) *e = 0;
                strcat(inbuff,"\015\012");
                str_adds(s,inbuff,strlen(inbuff));                      
          }
        }
        fclose(f);
        return TRUE;
}
int str_checknulls(String *xitem, char *msg)
{
	return s_checknulls(str_s(xitem),str_len(xitem),msg);
}
int s_checknulls(char *s, int len, char *msg)
{
	int i;
	for (i=0; i<len; i++,s++) if (*s==0) emsg("Null %s at %d/%d\n",msg,i,len);
	return TRUE;
}

int str_write(String *xitem, char *fname)
{
        FILE *f;
        int chunk;
        static int sz=0;
        int len = str_len(xitem);

        f = fopen(fname,"wb");
        if (f==NULL) {filemsg("Unable to write {%s}\n",fname); return FALSE;}

        sz = 0;
        for (;sz<len; ) {
                chunk = 100;
                if (sz+chunk>len) chunk = len-sz;
                if (fwrite(str_s(xitem)+sz,1,chunk,f)!= (size_t) chunk) {
                        fclose(f); return FALSE;
                }
                sz += chunk;
        }
        fclose(f);
        return TRUE;
}
int str_append(String *xitem, char *fname)
{
        FILE *f;
        int chunk;
        static int sz=0;
        int len = str_len(xitem);

        f = fopen(fname,"ab");
        if (f==NULL) {filemsg("Unable to write {%s}\n",fname); return FALSE;}

        sz = 0;
        for (;sz<len; ) {
                chunk = 100;
                if (sz+chunk>len) chunk = len-sz;
                if (fwrite(str_s(xitem)+sz,1,chunk,f)!= (size_t) chunk) {
                        fclose(f); return FALSE;
                }
                sz += chunk;
        }
        fclose(f);
        return TRUE;
}

void str_init(String *s)
{
        s->len = 0; s->total = 0; s->s = NULL;
}
#define ifexpand(str,len) if (str->total<=(len)) str_expand(str,len);
#define iffree(x) if ((x)!=NULL) {myfree(x); (x) = NULL;}
#define iffree2(x) if ((x)!=NULL) {p_free(x); (x) = NULL;}
void str_adds(String *dst, char *s, int len)
{
        /* if (dst->s!=NULL) dmsg("str_add {%s} \n",dst->s);*/
        ifexpand(dst,dst->len+len+1);
        memcpy((*dst->s) +dst->len,s,len);
        dst->len += len;
        (*dst->s)[dst->len] = 0; /* Add a terminating 0 */
        /* if (dst->s!=NULL) dmsg("str_now {%s} \n",dst->s); */
}
void str_add(String *dst, String *src)
{
        /* if (dst->s!=NULL) dmsg("str_add {%s} \n",dst->s);*/
        ifexpand(dst,dst->len+src->len+1);
        memcpy((*dst->s)+dst->len,*(src->s),src->len);
        dst->len += src->len;
        (*dst->s)[dst->len] = 0; /* Add a terminating 0 */
}
void str_add_rest(String *dst, String *src, int from)
{
        /* if (dst->s!=NULL) dmsg("str_add {%s} \n",dst->s);*/
	if (from<0) from=0;
        ifexpand(dst,dst->len+src->len+1-from);
        memcpy((*dst->s)+dst->len,*(src->s) + from,src->len - from);
        dst->len += src->len - from;
        (*dst->s)[dst->len] = 0; /* Add a terminating 0 */
}
void str_setlen(String *s, int len)
{
        if (s->total <= (len+1)) str_expand(s,len+1);
        s->len = len;
        if (s->s == NULL) return;
        (*s->s)[len] = 0;
}
void str_setsize(String *s, int len)
{
        if (s->total <= (len+1)) str_expand(s,len+1);
        s->len = len;
}
void str_expand(String *s, int len)
{
        char **tmp;
        if (len==0) return;
        if (len<50000) len+=len/2; 
	else len+=len/10;
	
	if (len<100) len = 100;
        if (len<0) {
                emsg("str: str_expand, NEGATIVE value %d \n",len);
                die("str_expand_neagtive");
        }
        if (len>1290000) imsgf("str: str_expand %d \n",len);
        /* dmsg("str: str_expand %d \n",len);*/
        if (len>10000000) emsg("Panic, string growing to %d\n",len);
        strtotal += len;
        strtotal -= s->total;
        tmp = (char **) p_malloc(len);
        if (tmp==NULL) {
                emsg("str: Str_expand, malloc failed %d \n",len);
                die("str_expand");
        }
        if (s->len>0) memcpy(*tmp,*(s->s),s->len+1);
        else (*tmp)[0] = 0;
        if (s->total>0) iffree2(s->s);
        s->s = tmp;     
        s->total = len;
}
void str_copys(String *dst, char *src, int len)
{
        ifexpand(dst,len+1);
        if (len>0) memcpy((*dst->s),src,len);
        dst->len = len;
        (*dst->s)[dst->len] = 0; /* Add a terminating 0 */
}
void str_copy(String *dst, String *src)
{
        ifexpand(dst,src->len+1);
        if (src->len>0) memcpy((*dst->s),*src->s,src->len);
        dst->len = src->len;
        (*dst->s)[dst->len] = 0; /* Add a terminating 0 */
}
void str_clearbig(String *dst) 
{
        dst->len = 0;
        if (dst->s != NULL) (*dst->s)[0] = 0;
        if (dst->total> 10000) {
                strtotal -= dst->total;
                iffree2(dst->s);
                dst->s = NULL;
                dst->total = 0;
        }
}
void str_clear(String *dst) 
{
        dst->len = 0;
        strtotal -= dst->total;
        iffree2(dst->s);
        dst->s = NULL;
        dst->total = 0;
}
void str_free(String *s) 
{
        strtotal -= s->total;
        iffree2(s->s);
        iffree(s);
}
void str_lock(String *s)
{
        p_lock(s->s);
}
void str_unlock(String *s)
{
        p_unlock(s->s);
}
int str_total(void)
{
        return strtotal;
}
char *str_s(String *s)
{
        return *s->s;
}
int str_len(String *s)
{
        return s->len;
}
void str_dump(String *t)
{
        char *s = str_s(t);
        for (;*s!=0;) {
                if (isprint(*s)) dmsg("%c",*s);
                else dmsg("(%d)",*s);
                s++;
        }
}
char * strdump(char *s)
{
        static char bf[2000];
        char *out=bf;
        for (;*s!=0;) {
                if (isprint(*s)) *out++ = *s;
                else {
                        lprintf(out,BFSZ-1,"(%d)",*s);
                        out += strlen(out);
                }                               
                s++;
        }
        *out = 0;
        return bf;
}
void str_addsn(String *a, char *s)
{
        str_adds(a,s,strlen(s));
}
void str_copysn(String *a, char *s)
{
        str_copys(a,s,strlen(s));
}
void str_right(String *a, int r)  /* Eat the left r bytes of string */
{
        int rlen = str_len(a)-r;
        if (rlen<=0) {
                str_setlen(a,0);                
        } else {
                memmove(str_s(a),str_s(a)+r,rlen);
                str_setlen(a,rlen);
        }
}
void str_printf(String *a, char * arg_list, ...)
{
        va_list arg_ptr;
        char *format;
        int len;
	char output[4000];

        va_start(arg_ptr, arg_list);
        format = arg_list;
        len = vsnprintf(output, 2000, format, arg_ptr);
        if (len>=1000) dnews_ex("str_printf length exceeded");
        str_addsn(a,output);
}

/* ------------------------------------------------------ */
/* Memory debugging */
void mmsg_do(char *s);
void mmsg(char * arg_list, ...);
void mmsg(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format, output[8000];
        va_start(arg_ptr, arg_list);
        format = arg_list;
        vsnprintf(output, BFSZ-1,format, arg_ptr);
        mmsg_do(output);
}
void mmsg_do(char *s)
{
     FILE *f;
     return;
     f = fopen("mm.log","a");
     if (f==NULL) {perror("Can't open mm.log"); return;}
     fprintf(f,"%s",s);
     fclose(f);
}


/* My malloc code */

/* ------------------------------------------------------ */
/* typedef struct BLOCK {int *data; int width, size, used, upto; struct BLOCK *next;} Block; */
#define MAX_BLOCKS 32
static Sblock blocks[MAX_BLOCKS];
static Sblock *block_last[MAX_BLOCKS];
static int max_blocks = MAX_BLOCKS;
static int nblocks = 12;
/*static int nblocks = 12;*/
static int block_total;

#ifdef NETWARE
#define malloc big_malloc
#define free big_myfree
void *big_malloc(int x);
void big_myfree(int *x);
#endif

#define dbg if (1==2) 
int mem_block_init(Sblock *b, int width, int size)
{
        int i;
        b->width = width;
        b->size = size;
        b->used = 0;
        b->wanted = 0; b->got = 0;
        b->data = calloc(sizeof(int)*2,width*size/2);
        dbg dmsg("mem: malloc %d %d = %d \n",width,size,sizeof(int)*width*size);
        block_total += size*width*sizeof(int);
 if (block_total>4000000) dmsg("Block Total Increased by %d to %d (%d:%d)\n",size*width*4,block_total,size,width);
 
        if (b->data == NULL) {imsg("Memory allocation failed in str\n"); dnews_ex("Memory"); }
        for (i=0; i<b->size; i++) b->data[i*b->width] = 0;
        b->upto = 0;
        b->next = NULL;
        return TRUE;
}
int mem_malloc_init(void)
{
        mem_block_init(&blocks[0],2,1000);
        mem_block_init(&blocks[1],3,1000); /* Warning these next two can cause non double aligned , may cause trouble later */
        mem_block_init(&blocks[2],5,1000);
        mem_block_init(&blocks[3],12,1000);
        mem_block_init(&blocks[4],14,1000);
        mem_block_init(&blocks[5],20,100);
        mem_block_init(&blocks[6],60,40);
        mem_block_init(&blocks[7],80,40);
        mem_block_init(&blocks[8],100,40);
        mem_block_init(&blocks[9],200,40);
        mem_block_init(&blocks[10],320,100);
        mem_block_init(&blocks[11],290,4);
	nblocks = 12;

/*      mem_block_init(&blocks[10],2000,4);
        mem_block_init(&blocks[8],6000,2);
        mem_block_init(&blocks[9],12000,2);
        mem_block_init(&blocks[10],22000,5);
        mem_block_init(&blocks[11],70000,1);
*/
        return TRUE;
}
int *mem_fromblock(int bi, int wanted)
{
        int newsz;
        Sblock *b;
        int i;

        b = block_last[bi];
        if (b==NULL) b = &blocks[bi];
        for (;(b->used >= b->size) && (b->next!=NULL);) b = b->next;
        if (b->used >= b->size) { b = &blocks[bi]; }
        for (;(b->used >= b->size) && (b->next!=NULL);) b = b->next;
        if (b->used >= b->size) {
                b->next = malloc(sizeof(Sblock));
                if (b->next==NULL) {emsg("Memhistory\n"); dnews_ex("Memory");}
                newsz = b->size;
                if (b->width<1000) newsz = b->size*2;
                mem_block_init(b->next,b->width,newsz);
                b = b->next;
                /* mem_blocks_show(); */
        }
        if (b==NULL) {emsg("Mem block \n"); dnews_ex("Mem block");}
        for (i=b->upto; i<b->size; i++) {
                if (b->data[i*b->width] == 0) goto found_free;
        }                       
        for (i=0; i<b->upto; i++) {
                if (b->data[i*b->width] == 0) goto found_free;
        }                       
        imsg("Shouldn't ever get here in mem_block\n"); 
	mem_blocks_show();
	dnews_ex("mem block");

found_free:
        block_last[bi] = b;
        b->wanted += wanted;
        b->got += b->width;
        b->used++;
        b->upto = i; 
        /* dmsg("hist: malloc used=%d upto=%d width=%d %p \n",b->used,b->upto,b->width
                ,&(b->data[i*b->width]));               */
        b->data[i*b->width] = b->width;         
        return &(b->data[i*b->width+1]);                
}
#ifdef SYSMALLOC
int check_cell(void *x, char *name)
{
	dididieieidieie
}
#endif

char *mem_malloc(int sz)
{
        int i;
	int nz,zwidth;
        static int done_init;
        int *r;
#ifdef SYSMALLOC
        return malloc(sz); 
#endif

        if (!done_init) { mem_malloc_init(); done_init = TRUE;}

        if (sz==0) return NULL;
        sz = (sz+7)/4;
        if (sz>10000) dmsg("mem: malloc sz=%d*int \n",sz);
/*dmsg("mem: malloc sz=%d*int \n",sz);*/
        for (i=0; i<nblocks; i++) if (blocks[i].width>=sz) return (char *) mem_fromblock(i,sz);
        for (i=nblocks; i<max_blocks; i++) {
                nblocks++;
				if (nblocks>=MAX_BLOCKS) abort();
		nz = 1;
		if (nblocks==12) zwidth = 550;
		else if (nblocks==13) zwidth = 1100;
		else if (nblocks==14) {zwidth = 2020; nz = 8;}
		else zwidth = blocks[i-1].width*2;
                mem_block_init(&blocks[i],zwidth,nz);
                if (blocks[i].width>=sz) return (char *) mem_fromblock(i,sz);
        }
        dmsg("mem: Real malloc used %d \n",sz);
        r = malloc(sz*sizeof(int));
        block_total += sz*sizeof(int);
emsg("Block Total Increased by %d to %d\n",sz*4,block_total);
        if (r==NULL) {emsg("Mem_malloc failed\n"); return NULL;}
        *r = sz;
        return (char *) (r+1);
}
void mem_dofree(Sblock *b, int *x, char *file, int line)
{
        for (;b!=NULL;b = b->next) {
                /* dmsg("hist: free %p %p-%p \n",x,b->data,b->data + b->width*b->size); */
                if ((x>= b->data) && (x< (b->data + b->width*b->size))) goto foundit;
        }
        emsg("mem: free shouldn't happen here %p\n",x);
	emsg("mem: free called at %s:%d\n",file,line);
	emsg("mem: Now attempting to crash to create drwtsn32.log file\n");
	{char *s=NULL; if (*s==0) s++;}
	dnews_ex("mem dofree");
        return;
foundit:
        *x = 0;
        b->used--; 
}
void mem_free(char *xx, char *file, int line)
{
        int i;
        int *x=(int *) xx;
        int sz;
#ifdef SYSMALLOC
        free(x);  return; 
#endif
        x--;
        sz = *x;
        dbg mmsg("mem: free %p width %d \n",x,sz);
        for (i=0; i<nblocks; i++) if (blocks[i].width>=sz) {mem_dofree(&blocks[i],x, file,line); return;}
        imsg("mem: free real malloc? %p \n",x);
        /* block_total -= sz; */      
        emsg("Free called from MEM_FREE, I didn't expect this\n"); 
        free(x);
}
void mem_blocks_show(void)
{
        int i;
        int total,total_used;
        int gtotal,ww;
        Sblock *b;
        gtotal = 0;
        for (i=0; i<nblocks; i++) {
                 b = &blocks[i]; 
                 total =0; total_used = 0;
                 ww = b->width;
                 for (;b!=NULL;b=b->next) {
                        imsg("mem:  Size %d, used %d %g\n",b->size,b->used,b->wanted/(b->got+.001));
                        total += b->size*b->width*4;
                        total_used += b->width*4*b->used;
                 }
                 imsg("mem:     Summary width %d,   %d/%d \n",ww*4,total_used,total);
                 gtotal += total;
        }
        imsg("mem: Allocated more, Grand total %d\n",gtotal);
}
void mem_blocks_show_str(String *str)
{
        int i;
	char bf[BFSZ];
        int total,total_used;
        int gtotal,ww;
        Sblock *b;
        gtotal = 0;
        for (i=0; i<nblocks; i++) {
                 b = &blocks[i]; 
                 total =0; total_used = 0;
                 ww = b->width;
                 for (;b!=NULL;b=b->next) {
			 lprintf(bf,BFSZ-1,"block:  Size %d, used %d %g\n",b->size,b->used,b->wanted/(b->got+.001));
			 str_addsn(str,bf);
                        total += b->size*b->width*4;
                        total_used += b->width*4*b->used;
                 }
                 lprintf(bf,BFSZ-1,"block:     Summary width %d,   %d/%d \n",ww*4,total_used,total);
		 str_addsn(str,bf);
                 gtotal += total;
        }
        lprintf(bf,BFSZ-1,"block: Allocated more, Grand total %d\n",gtotal);
	str_addsn(str,bf);
}
int mem_block_total(void)
{
        return block_total;
}

int lib_test_malloc(int mb)
{
 	int i,r;
	void *x[1000];
	for (i=0; i<1000; i++) x[i] = NULL;
	

	for (i=0; i<mb; i++) {
		emsg("Allocated %d MB\n",i);
		x[i] = malloc(1000000);
		if (x[i]==NULL) break;
	}
	r = i;
	for (; i>=0; i--) {
		emsg("Freeing, Allocated %d MB\n",i);
		if (x[i]!=NULL) free(x[i]);
		x[i] = NULL;
	}	
	emsg("Test completed \n");
	return r;
}
char *test_create(int k)
{
 	static char *bf;
	int i;
	char *s;
 	if (bf==NULL) bf = mymalloc(1000000);
	s = bf;
	k = k + 5;
	
	for (i=0; (i<k) && (i<1000); i++) {
	 	lprintf(s,BFSZ-1,"%d:",k);
		s += strlen(s);
	}
	return bf;
}

#define LOTS 4000
int lib_test_str(void)
{
	int i,j;
 	String *lots[LOTS];
	char *data;

	for (i=0; i<LOTS; i++) {
		lots[i] = str_new();
		data = test_create(i);
		str_copys(lots[i],data,strlen(data));
		emsg("test_len %d \n",str_len(lots[i]));
		if (((i-10) % 10)==0) {
		 	if ((i-10)>0) {
		 		str_free(lots[i-10]);
				lots[i-10] = NULL;
			}

		}		
	}
		for (j=0; j<LOTS; j++) if (lots[j]!=NULL) {
		 	if (strcmp(str_s(lots[j]),test_create(j))!=0) {
				emsg("Test failed %d\n",j);
				return FALSE;
			}
		}

	emsg("test_str finished OK\n");
	return TRUE;
}



int str_getline(String *item, int *upto, char *bf, int bfsz)
{
	char *s;
	char *s2;
	char *s3;
	int len;

	if (*upto>str_len(item)) return FALSE;
	s = str_s(item);
	if (s==NULL) return FALSE;
	s += *upto;
	for (s2=s;*s2!=0;s2++) if (*s2=='\n') break;
	if (*s2==0) return FALSE;
	len = s2-s;
	*upto = s2 - str_s(item) + 1;
	if (len<0) len = 0; if (len>=bfsz) len = bfsz-1;
	ncpy(bf,s,len);
	s3 = strchr(bf,'\r');
	if (s3!=NULL) *s3 = 0;
	return TRUE;
}