
#include <limits.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#ifdef WIN32
#include <windows.h>
#include <malloc.h>
#endif
#include "dnews.h"
#include "conf.h"
#include "emsg.h"
#include "str.h"

#ifndef TRUE
#define FALSE 0
#define TRUE !FALSE
#endif
#define BFSZ 1000
#define STRDUP_MAIN 1

#define MEMDEBUG TRUE
#include "memdebug.h"

#define MAGIC1 0x12345678
void check_init(void);
#define MAGIC2 0x79
#define MAGIC3 0x78
#define MAGIC4 0x77
#define MAGIC5 0x76
extern int memdebug_total;
static int memdebug_max;
static int memdebug_wasted;

void show_block(int *x);
void show_block2(int *x);
/* #define NOCHECK TRUE*/

#ifdef QUAD_WORDS
#define NOCHECK TRUE
#endif

#define MAX_LOCAL 50000 
/* #define MAX_LOCAL 4000000 */

void *big_malloc(int x);
void big_myfree(int *x);
void mem_add(void *x);
void mem_exit(void);
void mem_sub(void *x);
static int mlist = FALSE;

int mem_biggest(void) {return memdebug_max;}
void mem_kill(void)
{
        char *x = malloc(5);
/*      show_block((int *) (x-3*sizeof(int)));*/
        strcpy(x,"eeeee"); 
        mem_check();
        x = 0;
        *x = 1;

}

void print_mem(char * arg_list, ...);
static FILE *ftrace;
void trace_flush(void)
{
        if (ftrace!=NULL) fclose(ftrace);
        ftrace = NULL;
}
static int amount_freed;
static int total2;
void trace_mem(int size, char *file, int line, int *x)
{
        static int tupto;
        char *fname="mem1.log";
        if (tupto==0) ftrace = fopen(fname,"w");
        tupto++;
        if (tupto>200000) tupto = 100000;
        if (tupto==100000) {
                fname = "mem2.log";
                if (ftrace!=NULL) fclose(ftrace);
                ftrace = NULL;
                ftrace = fopen(fname,"w");
        }
        if (ftrace==NULL) {
                ftrace = fopen(fname,"a");
                if (ftrace==NULL) return;
        }
        fprintf(ftrace,"%d %d %p %d %s:%d\n",memdebug_total,total2,x,size,file,line);
}

/* size,table,magic,data,magic */

void *check_malloc2(int  size, int t,char *file, int line)
{
	void *x = check_malloc(size,file,line);

	/*trace_mem(size,file,line,x);*/
	mem_log(t,amount_freed);
	total2 += amount_freed;
	return x;
}
void check_free2(void *s, int t, char *file,int line)
{

	check_free(s,file,line);
	mem_log(t,-amount_freed);
	total2 -= amount_freed;
}
int check_total2(void)
{
	return total2;
}

char * mystrdup_get(int x, int *used)
{
	if (strdup_stats[x]==0) return NULL;
	*used = strdup_stats[x];
	return strdup_names[x];
}

char *mystrdup(char *s, int type)
{
  int xlen = strlen(s)+1;
  char *n;

  n = mymalloc2(xlen,M_STRDUP);
  strcpy(n,s);

  strdup_stats[type] += xlen;
  return n;
}
int mystrfree(char *s, int type)
{
	if (s==NULL) return 0;
	myfree2(s,M_STRDUP);
	strdup_stats[type] -= amount_freed;
	return amount_freed;
}

void *check_malloc(int  size, char *file, int line)
{
        int *x;
        char *cx;
        static int done_init;
	static int busy;
/*      dmsg("mallocd: %d %s:%d\n",size,file,line); */
	/*trace_mem(size,file,line,0);*/

#ifdef NOCHECK
        return malloc(size);
#endif
        if (!done_init) check_init();
        done_init = TRUE;
/*        if (size>150000) dmsg("mem: big_malloc %d %s:%d total now %d\n",size,file,line,memdebug_total);*/

        if (size==0) return NULL;

/*      size +=3;
        size = size & 0x5ffffffc; */

        if (size<0) {
                emsg("Memory exceeded %s:%d, NEGATIVE %d bytes \n",file,line,size); 
                abort();
        }
        memdebug_total += size;
	memdebug_wasted += 16;
        if (memdebug_total > memdebug_max) memdebug_max = memdebug_total;
        if (size>900000000) {
                emsg("Memory exceeded %s:%d, WAY TOO BIG %d bytes \n",file,line,size); 
                abort();
        }
/*        if (size>10000) {
                dmsg("mem: Mem big malloc %s:%d sz=%d\n",file,line,size); 
        }
*/
        if (size>MAX_LOCAL) {
                x = (int *) big_malloc(size+sizeof(int)*4);
                dmsg("mem: large malloc %s:%d %d bytes\n",file,line,size);
        } else {
                x = (int *) mem_malloc(size+sizeof(int)*4);
        }
        if (mlist) trace_mem(size,file,line,x);
        if (x!=NULL) memset(x,size+sizeof(int)*4,0); /* Wipe memory to be safe */

        cx = (char *) x;
        if (x==NULL) {
                emsg("Memory exceeded %s:%d, %d bytes, total %d \n",file,line,size,memdebug_total); 
                abort();
        }
        
        x[0] = size;
	amount_freed = size; /* + 16;*/
        x[1] = mem_next();
        x[2] = MAGIC1;
        cx[3*sizeof(int)+size] = MAGIC2;
        cx[3*sizeof(int)+size+1] = MAGIC3;
        cx[3*sizeof(int)+size+2] = MAGIC4;
        cx[3*sizeof(int)+size+3] = MAGIC5;
/*print_mem("mem: Setused [%d] %p  %s:%d %d\n",x[1],x,file,line,size);*/
        if (size<=MAX_LOCAL) mem_setused(x[1],x,file,line);
        return x+3;
}
void print_mem(char * arg_list, ...)
{
        va_list arg_ptr;
        char *format, output[8000];
        static FILE *fmem;

        va_start(arg_ptr, arg_list);
        format = arg_list;
        vsprintf(output,format, arg_ptr);
        
        if (fmem==NULL) {
                remove("mem.debug_5");
                rename("mem.debug_4","mem.debug_5");
                rename("mem.debug_3","mem.debug_4");
                rename("mem.debug_2","mem.debug_3");
                rename("mem.debug","mem.debug_2");
                fmem = fopen("mem.debug","w");
                if (fmem==NULL) filemsg("Can't open mem.debug\n");
        }
        fprintf(fmem,"%s",output);
        fflush(fmem);
}

void *r_malloc(int  size)
{
        int *x;

#ifdef NOCHECK
        return malloc(size);
#endif
        if (size==0) return NULL;
        if (size<0) {
                emsg("Memory negative alloc %d\n",size);
                abort();
        }
        memdebug_total += size;
        if (memdebug_total > memdebug_max) memdebug_max = memdebug_total;
        x = (int *) mem_malloc(size+sizeof(int));
        if (x==NULL) {
                emsg("Memory exceeded %d bytes, total %d \n",size,memdebug_total); 
                abort();
        }
        
        x[0] = size;
	mem_log(M_RAW,size);
        return x+1;
}
#define MAXBLOCKS memmax

typedef struct {char *file; int line; int *pnt;} MemBlock;
/*
static int *memused[MAXBLOCKS];
static char *memname[MAXBLOCKS];
*/
static MemBlock *blocks;
static int memmax = 200000;
static int memnext;
int dnews_memdebug(void);
void check_init(void)
{
        static int done_init;
        int i;
        if (done_init) return;
        done_init = TRUE;
        mlist = FALSE;
        if (dnews_memdebug()) {
         mlist = TRUE;
         if (mlist) {
                blocks = (MemBlock *) mem_malloc(memmax*sizeof(MemBlock));
                for (i=0; i<memmax; i++) blocks[i].pnt = NULL;
         }
        }
}
static int biggest;
int mem_next(void)
{
        if (!mlist) return 0;
        for (;(blocks[memnext].pnt!=NULL) && (memnext<memmax);memnext++) ;
        if (memnext>=memmax) {
                memnext = 0;
                for (;(blocks[memnext].pnt!=NULL) && (memnext<memmax);memnext++) ;
        }
        if (memnext>=memmax) {
                emsg("Too many memory blocks for debug code\n");
                return -2;
        }
        if (biggest<memnext) 
                biggest = memnext;
        return memnext;
}
int mem_check(void)
{
        int i;
        int n=0;

        if (!mlist) return TRUE;
        imsg("mem: Mem check called blocks %d \n",biggest);
        for (i=0; i<=biggest; i++) {
                if (blocks[i].pnt!=NULL) {
                        if (!check_block(blocks[i].pnt,blocks[i].file)) {
                                emsg("mem: Corruption at [%d] %s:%d\n",i,blocks[i].file,blocks[i].line);
                                mem_report();
                                abort();
                        };
                        n++;
                }
        }
        imsg("mem: Mem check tested %d/%d memory blocks still active \n",n,biggest);
        return TRUE;
}

int mem_cmp(const void *aa, const void *bb);
int mem_cmp(const void *aa, const void *bb)
{
        int *a = (int *) aa;
        int *b = (int *) bb;
        if (blocks[*a].pnt>blocks[*b].pnt) return -1;
        if (blocks[*a].pnt<blocks[*b].pnt) return 1;
        return 0;
}
int mem_report(void)
{
        int i;
        int n=0;
        int ii,jj;
        int k;
        int *p;
        
        if (!mlist) return TRUE;
        p = (int *) mem_malloc(sizeof(int) * memmax);
        for (i=0; i<memmax; i++) p[i] = i;
        qsort(p,memmax,sizeof(int),mem_cmp);

        for (ii=0; ii<memmax; ii++) {
                i = p[ii];
                if (blocks[i].pnt!=NULL) {
                        if (!check_block(blocks[i].pnt,blocks[i].file)) {
                                emsg("mem: Corruption at [%d:%d] %s:%d\n",ii,i,blocks[i].file,blocks[i].line);
                                for (jj=ii-4;jj<ii+4;jj++) {
                                        if (jj<0) continue; if (jj>=memmax) break;
                                        k = p[jj];
                                        if (blocks[k].pnt==NULL) continue;
                                        emsg("mem: Block [%d:%d] %s:%d -> %p\n",jj,k,blocks[k].file,blocks[k].line,blocks[k].pnt);
                                        check_block(blocks[k].pnt,blocks[k].file);
                                }
                                for (jj=ii-2;jj<ii+2;jj++) {
                                        if (jj<0) continue; if (jj>=memmax) break;
                                        k = p[jj];
                                        if (blocks[k].pnt==NULL) continue;
                                        emsg("mem: Block [%d:%d] %s:%d -> %p\n",jj,k,blocks[k].file,blocks[k].line,blocks[k].pnt);
                                        check_block(blocks[k].pnt,blocks[k].file);
                                        show_block(blocks[k].pnt);                                              
                                }
                        }
                        n++;
                }
        }
        mem_free((char *) p,"report",0);
        emsg("mem: Mem check tested %d memory blocks still active \n",n);
        return TRUE;
}
void mem_setused(int m, int  *x, char *file, int line)
{
        if (!mlist) return;
        if (m<0) return;
        blocks[m].pnt = x;
        blocks[m].file = file;
        blocks[m].line = line;
}
void mem_setfree(int m, int  *x)
{
        if (!mlist) return;
        if (m<0) return;
        if (blocks[m].pnt!=x) {
                emsg("Free pointers don't match %s:%d\n",blocks[m].file,blocks[m].line);
                mem_report();
                abort();
        }
        blocks[m].pnt = NULL;
        blocks[m].file = NULL;
}
int check_cell(void *x, char *name)
{
#ifndef NOCHECK
	int *a = x;
	a -= 3;
	return check_block(a,name);
#endif
}
int check_block(int *x, char *name)
{
        char *cx = (char *) x;

        if (x[2]!=MAGIC1) {
                emsg("mem: cMagic1 lost %p %x, %s \n",x,x[2],name);
                return FALSE;
        }
        if (x[0]<0 || (x[0]>32000000)) {
                emsg("mem: cMagic 1 lost size %p %x %d, %s \n",x,x[0],x[0],name);
                return FALSE;
        }
        if (x[0]>2000) return TRUE;
        if (cx[3*sizeof(int)+x[0]] != MAGIC2) {
                emsg("mem: cMagic2 lost %p %x, %s \n",x,cx[3*sizeof(int)+x[0]] ,name);
                return FALSE;
        }
        if (cx[3*sizeof(int)+x[0]+1] != MAGIC3) {
                emsg("mem: cMagic3 lost %p %x, %s \n",x,cx[3*sizeof(int)+x[0]] ,name);
                return FALSE;
        }
        if (cx[3*sizeof(int)+x[0]+2] != MAGIC4) {
                emsg("mem: cMagic4 lost %p %x, %s \n",x,cx[3*sizeof(int)+x[0]] ,name);
                return FALSE;
        }
        if (cx[3*sizeof(int)+x[0]+3] != MAGIC5) {
                emsg("mem: cMagic5 lost %p %x, %s \n",x,cx[3*sizeof(int)+x[0]] ,name);
                return FALSE;
        }
        return TRUE;
}

void check_free(void *s,char *file,int line)
{
        char *cx;
        int *x = (int *) s;


        /* dmsg("free: %d %p %s:%d\n",x[0],x,file,line);*/

#ifdef NOCHECK
        free(s);
        return;
#endif


        if (s==NULL) {
                emsg("Free NULL pointer %s %d \n",file,line);
        }
        x -= 3;
        cx = (char *) x;
        if (x[2]!=MAGIC1) {
                emsg("Magic1 lost %d %x, %s %d \n",x,x[2],file,line);
                trace_flush();
                mem_report();
                show_block(x);
                abort();
        }
        if (cx[3*sizeof(int)+x[0]] !=MAGIC2) {
                emsg("Magic2 lost %d, %s %d \n",x,file,line);
                trace_flush();
                emsg("Magic2 lost %x, %s %d \n",cx[3*sizeof(int)+x[0]] ,file,line);
                emsg("Block size=%d pointer %d, magic %x \n",x[0],x[1],x[2]);
                mem_report();
                show_block(x);
                abort();
        }
   /*     if (cx[3*sizeof(int)+x[0]] != MAGIC2) {
                emsg("ccMagic2 lost %x,  \n",cx[3*sizeof(int)+x[0]] );
                mem_report();
                show_block(x);
                abort();
        }
        */
/*        if (cx[3*sizeof(int)+x[0]+1] != MAGIC3) {
                emsg("ccMagic3 lost %x,  \n",cx[3*sizeof(int)+x[0]] );
                gets(bf);
        }
        if (cx[3*sizeof(int)+x[0]+2] != MAGIC4) {
                emsg("ccMagic4 lost %x,  \n",cx[3*sizeof(int)+x[0]] );
                gets(bf);
        }
        if (cx[3*sizeof(int)+x[0]+3] != MAGIC5) {
                emsg("ccMagic5 lost %x,  \n",cx[3*sizeof(int)+x[0]] );
                gets(bf);
        }

  */


        if (x[0]<0) {
                emsg("Size stuffed up \n");
                trace_flush();
                show_block(x);
                mem_report();
                abort();
        }
	amount_freed = x[0];
        memdebug_total -= x[0];
	memdebug_wasted -= 16;
        if (x[0]>MAX_LOCAL) {
                /* dmsg("big_myfree %d %p %s:%d\n",x[0],x,file,line);*/
                big_myfree(x);
                return;
        }
        mem_setfree(x[1],x);
        memset(x,x[0],0); /* Wipe out memory to make certain we aren't re-using it */
        mem_free((char *)x,file,line);
}
void r_free(void *s)
{
        int *x = (int *) s;

#ifdef NOCHECK
        free(s);
        return;
#endif

        if (s==NULL) {
                emsg("Free NULL pointer \n");
                return;
        }
        x -= 1;
        if (x[0]<=0) {
                emsg("Size stuffed up %d\n",x[0]);
                abort();
        }

        memdebug_total -= x[0];
	mem_log(M_RAW,-x[0]);
        mem_free((char *)x,"r_Free",0);
}

void *check_calloc(int  number, int  size,char *file,int line)
{
        void *x = check_malloc(number*size,file,line);
        if (x!=NULL) memset(x,0,number*size);
        return x;
}
void *check_calloc2(int  number, int  size, int t, char *file,int line)
{
        void *x = check_malloc2(number*size,t,file,line);
        if (x!=NULL) memset(x,0,number*size);
        return x;
}

void show_block(int *x)
{
        int i;
        int b = x[1];
        char *cx = (char *) x; 

        emsg("mem: pnt %d size=%d revb=%d, magic=%x \n",x,x[0],x[1],x[2]);
        if ((b>=0) && (b<memmax)) {
         emsg("mem: Allocated [%d] %s:%d [%d]\n",b,blocks[b].file,blocks[b].line,x);
        }
        emsg("mem: Magic2 [%d]=%x \n",3*sizeof(int)+x[0] ,cx[3*sizeof(int)+x[0]] );
        for (i=3; i< ((x[0]/4)+5) ; i++) {
                if ((i<10) || (i> (x[0]/4-10))) emsg("Data [%d] = %x \n",i,x[i]);
        }
   for (i=-5; i<5 ; i++) {
            emsg("Data [%d] = %x \n",i,x[i]);
   }
}


void *big_malloc(int x)
{
#ifdef Mac
        char *p;
        p = NewPtr(x);
        if (p==NULL) p = (char *) NewPtrSys(x);
        return p;
#else
#ifdef WIN32
        LPVOID p;
        p = VirtualAlloc(NULL,x,MEM_COMMIT,PAGE_READWRITE);
        return p;
#else

#ifdef NETWARE
	void *r = malloc(x);
	if (r!=NULL) mem_add(r);
	return r;
#else
	return malloc(x);
#endif

#endif
#endif
}
void big_myfree(int *x)
{
#ifdef Mac
        DisposePtr((Ptr) x);
#else
#ifdef WIN32
        if (!VirtualFree((LPVOID) x,0,MEM_RELEASE)) emsg("Release failed %p\n",x);
#else
#ifdef NETWARE
	mem_sub(x);
	free(x);
#else
        free(x);
#endif
#endif
#endif
}

void mem_flush(void)
{
#ifdef WIN32
        int r = _heapchk();
        if (r==_HEAPBADBEGIN) emsg("Initial header information is bad or cannot be found\n");
        if (r==_HEAPBADNODE) emsg("Bad node has been found or heap is damaged\n");
        if (r==_HEAPBADPTR) emsg("Pointer into heap is not valid\n");
        if (r==_HEAPEMPTY) emsg("Heap has not been initialized\n");
        _heapmin();
#endif
}
void dnews_exit(char *reason, char *file, int line); /* __FILE__,__LINE__) */

typedef struct MLIST {void *x; struct MLIST *next; } Mlist;
static Mlist *head;
void mem_add(void *x)
{
#ifdef NETWARE
	Mlist *m;
	m = (Mlist *) malloc(sizeof(Mlist));
	if (m==NULL) {
		emsg("mem_add failed, memory");
		abort();
	}
	m->x = x;
	m->next = head;
	head = m;
#endif	
}
void mem_exit(void)
{
        /*trace_flush();*/
#ifdef NETWARE
	Mlist *m,*mnext;
	for (m=head;m!=NULL;m=mnext) {
		mnext = m->next;
		free(m->x);
		free(m);
	}
	head = NULL;
#endif
}
void mem_sub(void *x)
{
#ifdef NETWARE
	Mlist *m,**last;
	last = &head;
	for (m=head;m!=NULL;m=m->next) {
		if (m->x == x) {
			*last = m->next;
			free(m);
			break;
		}
		*last = m;
	}
	head = NULL;
#endif
}

int mem_wasted(void)
{
	return 	memdebug_wasted;	
}

