#include <stdio.h>

/*----------------------------------------------------------------------
These are some quick and dirty routines for logging all memory
allocations and frees to make sure there are no leaks and get some
idea where memory is being used.  This is used by including memlog.h
in the source files and compiling this file which provides alternate
malloc, free and other routines that do the logging.  When the program
being checked is exiting, dump_memlog should be called to write the
result out to a file, memlog.xxx
 ----*/

extern int debug;

#define DB 8

static FILE *memlog = NULL;

struct m {
    char file[20];
    int  line;
    int  count;
    int  frees;
    long bytes;
    long bytes_free;
    struct m *next;
};


struct mm {
    char      *address;
    struct m  *where;
    long       size;
};

static struct mm mms[20000];

static struct m *memuse = NULL;

static   char filename[100];


/*----------------------------------------------------------------------
  ---*/
malloc2(n, file, line)
     int n, line;
     char *file;
{
   struct m *m, *m_last;
   struct mm *mm;
   char *foo;

   if(memlog == NULL) {
       sprintf(filename, "memlog.%d", getpid());
       memlog =  fopen(filename, "w");
       init_mm();
   }

   for(m_last = m = memuse; m != NULL; m_last = m, m = m->next)
     if(strncmp(file, m->file, 13) == 0 && m->line == line)
       break;

   if(m == NULL) {
       m = (struct m *)malloc(sizeof(struct m));
       strncpy(m->file, file, 13);
       m->file[13] = '\0';
       m->line = line;
       m->count = 1;
       m->frees = 0;
       m->bytes = n;
       m->bytes_free = 0;
       m->next = NULL;
       if(memuse == NULL) {
           memuse = m;
       } else {
           m_last->next = m;
       }

   } else {
       m->count++;
       m->bytes += n;
   }
       
   
   foo = malloc(n);

   /* Find empty slot in mm list */
   for(mm = mms; mm->address != NULL; mm++);
   mm->address = foo;
   mm->where   = m;
   mm->size    = n;
   if(debug >= DB)  
     fprintf(memlog, "Malloc %5d bytes, File: %-10.10s Line:%4d  Addr:%x\n",
           n, file, line, foo); 


   return(foo);
}



/*----------------------------------------------------------------------
  ---*/
fs_get2(n, file, line)
     int n, line;
     char *file;
{
   struct m *m, *m_last;
   struct mm *mm;
   char *foo;

   if(memlog == NULL) {
       sprintf(filename, "memlog.%d", getpid());
       memlog =  fopen(filename, "w");
       init_mm();
   }

   for(m_last = m = memuse; m != NULL; m_last = m, m = m->next)
     if(strncmp(file, m->file, 13) == 0 && m->line == line)
       break;

   if(m == NULL) {
       m = (struct m *)malloc(sizeof(struct m));
       strncpy(m->file, file, 13);
       m->file[13] = '\0';
       m->line = line;
       m->count = 1;
       m->frees = 0;
       m->bytes = n;
       m->bytes_free = 0;
       m->next = NULL;
       if(memuse == NULL) {
           memuse = m;
       } else {
           m_last->next = m;
       }

   } else {
       m->count++;
       m->bytes += n;
   }
       
   
   foo = malloc(n);

   /* Find empty slot in mm list */
   for(mm = mms; mm->address != NULL; mm++);
   mm->address = foo;
   mm->where   = m;
   mm->size    = n;
   if(debug >= DB)  
     fprintf(memlog, "Fs_get %5d bytes, File: %-10.10s Line:%4d  Addr:%x\n",
           n, file, line, foo); 


   return(foo);
}



/*---------------------------------------------------------------------- 
 *Copy string to free storage
 * Accepts: source string
 * Returns: free storage copy of string
  ----*/
char *cpystr2 (string, file, line)
	char *string;
{
   int n ;
   struct m *m, *m_last;

   if(string == NULL)
     return(NULL);

   n = strlen(string) + 1;

   if(memlog == NULL) {
       sprintf(filename, "memlog.%d", getpid());
       memlog =  fopen(filename, "w");
       init_mm();
   }


   for(m_last = m = memuse; m != NULL; m_last = m, m = m->next)
     if(strncmp(file, m->file, 13) == 0 && m->line == line)
       break;

   if(m == NULL) {
       m = (struct m *)malloc(sizeof(struct m));
       strncpy(m->file, file, 13);
       m->file[13] = '\0';
       m->line = line;
       m->count = 1;
       m->frees = 0;
       m->bytes = n;
       m->bytes_free = 0;
       m->next = NULL;
       if(memuse == NULL) {
           memuse = m;
       } else {
           m_last->next = m;
       }
   } else {
       m->count++;
       m->bytes += n;
   }


   {
    char *dst = (char *) malloc(1+strlen(string));
    struct mm *mm;

    strcpy (dst,string);

     /* Find empty slot in mm list */
     for(mm = mms; mm->address != NULL; mm++);
     mm->address = dst;
     mm->where   = m;
     mm->size    = 1 + strlen(string);
     if(debug >= DB)  
       fprintf(memlog, "Cpystr %5d bytes, File: %-10.10s Line:%4d  Addr:%x\n",
             n, file, line, dst); 

     return (dst);
  }
  return NULL;
}


/*----------------------------------------------------------------------
  ---*/
fs_give2(ptr, file, line)
     char **ptr, *file;
     int    line;
{
    struct mm *mm;

    for(mm = mms; mm->address != *ptr && mm < &mms[20000]; mm++);
    if(mm->address == *ptr) {
        if(debug >= DB)  {
          fprintf(memlog, "Give %5d bytes, File: %-10.10s Line:%4d  Addr:%x\n",
            mm->size,  file, line, *ptr); 
          fprintf(memlog, "  (orig: File %s Line %d)\n",
                  mm->where->file, mm->where->line);
        }
        mm->address = NULL;
        mm->where->frees++;
        mm->where->bytes_free += mm->size;
    }
    
    free(*ptr);
}



/*----------------------------------------------------------------------
  ---*/
free2(ptr, file, line)
     char *ptr, *file;
     int    line;
{
    struct mm *mm;

    for(mm = mms; mm->address != ptr && mm < &mms[20000]; mm++);
    if(mm->address == ptr) {
        if(debug >= DB)   {
          fprintf(memlog, "Free %5d bytes, File: %-10.10s Line:%4d  Addr:%x\n",
            mm->size,  file, line, ptr);
          fprintf(memlog, "  (orig: File %s Line %d)\n",
                  mm->where->file, mm->where->line);
        }
        mm->address = NULL;
        mm->where->frees++;
        mm->where->bytes_free += mm->size;
    }
    
    free(ptr);
}



/*----------------------------------------------------------------------
  ----*/
fs_resize2(foo, n, file, line)
     char **foo, *file;
     int   n, line;
{
   struct m *m, *m_last;
   struct mm *mm;

    for(mm = mms; mm->address != *foo && mm < &mms[20000]; mm++);
    if(mm->address == *foo) {
        mm->address = NULL;
        mm->where->frees++;
        mm->where->bytes_free += mm->size;
    }

   for(m_last = m = memuse; m != NULL; m_last = m, m = m->next)
     if(strncmp(file, m->file, 13) == 0 && m->line == line)
       break;

   if(m == NULL) {
       m = (struct m *)malloc(sizeof(struct m));
       strncpy(m->file, file, 13);
       m->file[13] = '\0';
       m->line = line;
       m->count = 1;
       m->frees = 0;
       m->bytes = n;
       m->bytes_free = 0;
       m->next = NULL;
       if(memuse == NULL) {
           memuse = m;
       } else {
           m_last->next = m;
       }
   } else {
       m->count++;
       m->bytes += n;
   }
    
    if (!(*foo = realloc (*foo, n))) panic("Can't resize free storage");

     for(mm = mms; mm->address != NULL; mm++);
     mm->address = *foo;
     mm->where   = m;
     mm->size    = n;
     if(debug >= DB)   
       fprintf(memlog, "Resize %5d bytes, File: %-10.10s Line:%4d  Addr:%x\n",
             n, file, line, *foo);
}


/*----------------------------------------------------------------------
  ----*/
char *
realloc2(foo, n, file, line)
     char *foo, *file;
     int   n, line;
{
   struct m *m, *m_last;
   struct mm *mm;

    for(mm = mms; mm->address != foo && mm < &mms[20000]; mm++);
    if(mm->address == foo) {
        mm->address = NULL;
        mm->where->frees++;
        mm->where->bytes_free += mm->size;
    }

   for(m_last = m = memuse; m != NULL; m_last = m, m = m->next)
     if(strncmp(file, m->file, 13) == 0 && m->line == line)
       break;

   if(m == NULL) {
       m = (struct m *)malloc(sizeof(struct m));
       strncpy(m->file, file, 13);
       m->file[13] = '\0';
       m->line = line;
       m->count = 1;
       m->frees = 0;
       m->bytes = n;
       m->bytes_free = 0;
       m->next = NULL;
       if(memuse == NULL) {
           memuse = m;
       } else {
           m_last->next = m;
       }
   } else {
       m->count++;
       m->bytes += n;
   }
    
    if (!(foo = realloc (foo, n))) fatal ("Can't resize free storage");

     for(mm = mms; mm->address != NULL; mm++);
     mm->address = foo;
     mm->where   = m;
     mm->size    = n;
     if(debug >= DB)  
       fprintf(memlog, "Realloc %5d bytes, File: %-10.10s Line:%4d  Addr:%x\n",
             n, file, line, foo);
     return(foo);
}


    
int comp(b, a)
     struct m **a, **b;
{
    int x, y;

    x = (*a)->bytes - (*a)->bytes_free;
    y = (*b)->bytes - (*b)->bytes_free;

    if(x == 0 && y == 0)
      return((*a)->bytes - (*b)->bytes);
    else if(x == 0)
      return(1);
    else if(y == 0)
      return(-1);
    else
      return(x - y);
}




/*----------------------------------------------------------------------
  ----*/
dump_memlog(x)
     int x;
{
    struct m *m, **ts;
    struct mm *mm;
    long total, waste, count;
    struct m  *tosort[500];
    char nfilename[100], *p;
    static char y = 'A';
    FILE *stream;

    if(x == 0) {
        strcpy(nfilename, filename);
        p = &nfilename[strlen(nfilename)];
        *p++ = '-';
        *p++ = y;
        *p++ = '\0';
        if(y == 'Z')
          y = 'a';
        else
          y++;
        stream = fopen(nfilename, "w");
    } else {
        stream = memlog;
    }


    ts = tosort;
    count = 0;
    for (m = memuse; m != NULL; m = m->next) {
        *ts++ = m;
        count++;
    }
    *ts = NULL;

    qsort(tosort, count, sizeof (struct m *), comp); 


    for(ts = tosort ; *ts != NULL; ts++) {
        fprintf(stream,
                "File: %-11.11s  Line: %4d  Count:%5d  Frees:%5d  Bytes:%9d (%d)\n",
                (*ts)->file, (*ts)->line, (*ts)->count, (*ts)->frees, (*ts)->bytes,
                (*ts)->bytes - (*ts)->bytes_free);
        total += (*ts)->bytes;
    }
    fprintf(stream, "Total bytes: %d\n\n", total);


    waste = 0;
    for(mm = mms; mm < &mms[20000]; mm++) 
      if(mm->address != NULL) {
          fprintf(stream,
                "Unfreed malloc: %x  File: %-11.11s  Line: %4d  Size:%9d\n",
                mm->address, mm->where->file, mm->where->line, mm->size);
          waste += mm->size;
      }
    fprintf(stream, "UNFREED MEMORY: %d\n", waste);

    if(x == 0)
      fclose(stream);
}




init_mm() 
{
    struct mm *mm;
    for(mm = mms; mm < &mms[20000]; mm++) 
        mm->address = NULL;
}
