/*
 * Copyright (C) 2001 Andrew Ferguson <andrew@owsla.cjb.net>
 * Copyright (C) 2001 Sasha Vasko <sasha at aftercode.net>
 * Copyright (C) 1999 Ethan Fischer <allanon@crystaltokyo.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "config.h"

#include <string.h>							   /* for memset */
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <signal.h>

#include "astypes.h"
#include "output.h"
#include "ashash.h"
#include "audit.h"
#include "selfdiag.h"

#ifdef DEBUG_ALLOCS

#undef malloc
#undef safemalloc
#undef safecalloc
#undef calloc
#undef realloc
#undef add_hash_item
#undef free
#undef mystrdup
#undef mystrndup

#ifndef X_DISPLAY_MISSING

#undef XCreatePixmap
#undef XCreateBitmapFromData
#undef XCreatePixmapFromBitmapData
#undef XFreePixmap

#undef XCreateGC
#undef XFreeGC

#undef XCreateImage
#undef XGetImage
#undef XSubImage
#undef XDestroyImage

#undef XGetVisualInfo
#undef XGetWindowProperty
#undef XListProperties
#undef XGetTextProperty
#undef XAllocClassHint
#undef XAllocSizeHints
#undef XQueryTree
#undef XGetWMHints
#undef XGetWMProtocols
#undef XGetWMName
#undef XGetClassHint
#undef XGetAtomName
#undef XStringListToTextProperty
#undef XFree

#endif /* #ifndef X_DISPLAY_MISSING */
#endif /* DEBUG_ALLOCS */

#include "mystring.h"
#include "safemalloc.h"

int
as_assert (void *p, const char *fname, int line, const char *call)
{
	if (p == NULL)
		fprintf (stderr, "ASSERT FAILED: NULL pointer in %s, line# %d (%s())\n", fname, line, call);
	return (p == NULL);
}

#define MAX_AUDIT_ALLOCS 30000

typedef struct mem
{
	void         *ptr;
	const char	 *fname;
	size_t        length;
	short int     type;
	short int     line;
	char          freed;
}
mem;

enum
{
	C_MEM = 0,
	C_MALLOC = 0x100,
	C_CALLOC = 0x200,
	C_REALLOC = 0x300,
	C_ADD_HASH_ITEM = 0x400,
	C_ADD_HASH_OPTIONAL_ITEM = 0x500,
	C_MYSTRDUP = 0x600,
	C_MYSTRNDUP = 0x700,

	C_PIXMAP = 1,
	C_CREATEPIXMAP = 0x100,
	C_BITMAPFROMDATA = 0x200,
	C_FROMBITMAP = 0x300,

	C_GC = 2,

	C_IMAGE = 3,
	C_GETIMAGE = 0x100,
/*    C_XPMFILE = 0x200, *//* must be same as pixmap version above */
	C_SUBIMAGE = 0x300,

	C_XMEM = 4,
	C_XGETVISUALINFO = 	   	(0x100<<0),
	C_XGETWINDOWPROPERTY = 	(0x100<<1),
    C_XLISTPROPERTIES = 	(0x100<<2),
    C_XGETTEXTPROPERTY = 	(0x100<<3),
    C_XALLOCCLASSHINT = 	(0x100<<4),
    C_XALLOCSIZEHINTS = 	(0x100<<5),
    C_XQUERYTREE = 			(0x100<<6),
    C_XGETWMHINTS = 		(0x100<<7),
    C_XGETWMPROTOCOLS = 	(0x100<<8),
    C_XGETWMNAME = 			(0x100<<9),
    C_XGETCLASSHINT = 		(0x100<<10),
    C_XGETATOMNAME = 		(0x100<<11),
    C_XSTRINGLISTTOTEXTPROPERTY = (0x100<<12),

	C_LAST_TYPE
};

static ASHashTable *allocs_hash = NULL ;

#define DEALLOC_CACHE_SIZE      128
static mem* deallocated_mem[DEALLOC_CACHE_SIZE+10] ;
static unsigned int deallocated_used = 0 ;

static long allocations = 0;
static long reallocations = 0;
static long deallocations = 0;
static long max_allocations = 0;
static unsigned long max_alloc = 0;
static unsigned long max_x_alloc = 0;
static unsigned long total_alloc = 0;
static unsigned long total_x_alloc = 0;
static unsigned long max_service = 0;
static unsigned long total_service = 0;

static int    service_mode = 0 ;
static int    cleanup_mode = 0 ;

int set_audit_cleanup_mode(int mode)
{ int old = cleanup_mode; cleanup_mode = mode ; return old;}

void          count_alloc (const char *fname, int line, void *ptr, size_t length, int type);
mem          *count_find (const char *fname, int line, void *ptr, int type);
mem          *count_find_and_extract (const char *fname, int line, void *ptr, int type);

void mem_destroy (ASHashableValue value,void *data)
{
	if( data != NULL )
	{
		if( deallocated_used < DEALLOC_CACHE_SIZE )
			deallocated_mem[deallocated_used++] = (mem*)data ;
		else
        {
            if( total_service < sizeof(mem) )
			{
                show_error( "it seems that we have too little auditing memory (%lu) while deallocating pointer %p.\n   Called from %s:%d", total_service, ((mem*)data)->ptr, ((mem*)data)->fname, ((mem*)data)->line );
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
            }else
                total_service -= sizeof(mem);
            free( data );
        }
	}
}


void
count_alloc (const char *fname, int line, void *ptr, size_t length, int type)
{
    mem          *m = NULL;
	ASHashResult  res ;
	ASHashData hdata = {0};

    if( service_mode > 0 )
		return ;
	if( allocs_hash == NULL )
	{
		service_mode++ ;
		allocs_hash = create_ashash( 256, pointer_hash_value, NULL, mem_destroy );
		service_mode-- ;
	}

	if( get_hash_item( allocs_hash, (ASHashableValue)ptr, &hdata.vptr ) == ASH_Success )
	{
		m = (mem*)hdata.vptr ;
		if( type != (C_MEM|C_ADD_HASH_OPTIONAL_ITEM) )
		{	
			show_error( "Same pointer value 0x%lX is being counted twice!\n  Called from %s:%d - previously allocated in %s:%d", (unsigned long)ptr, fname, line, m->fname, m->line );
			print_simple_backtrace();
#ifdef DEBUG_ALLOC_STRICT
			{	char *segv = NULL ;	*segv = 0 ;  }
#endif
		}else
			return ;
	}else if( deallocated_used > 0 )
    {
        m = deallocated_mem[--deallocated_used];
/*        show_warning( "<mem> reusing deallocation cache  - element %d, pointer %p auditing service memory used (%lu )\n   Called from %s:%d",
                        deallocated_used, m, total_service, fname, line );
 */ }else
    {
		m = safecalloc (1, sizeof (mem));
        if( total_service+sizeof(mem) > AUDIT_SERVICE_MEM_LIMIT )
        {
            show_error( "<mem> too much auditing service memory used (%lu - was %lu)- aborting, please investigate.\n   Called from %s:%d",
                        total_service+sizeof(mem), total_service, fname, line );
            print_simple_backtrace();
			output_unfreed_mem (stderr);
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
            exit(0);
        }
        total_service += sizeof(mem);
        if( total_service > max_service )
            max_service = total_service ;
    }
    m->fname = fname;
	m->line = line;
	m->length = length;
	m->type = type;
	m->ptr = ptr;
	m->freed = 0;

	allocations++;
	if ((type & 0xff) == C_MEM)
	{
		total_alloc += length;
		if (total_alloc > max_alloc)
			max_alloc = total_alloc;
	} else
	{
		total_x_alloc += length;
		if (total_x_alloc > max_x_alloc)
			max_x_alloc = total_x_alloc;
	}
	if (allocations - deallocations > max_allocations)
		max_allocations = allocations - deallocations;

	if( (res = add_hash_item( allocs_hash, (ASHashableValue)ptr, m )) != ASH_Success )
		show_error( "failed to log allocation for pointer 0x%lX - result = %d", ptr, res);
    else
    {
        if( total_service+sizeof(ASHashItem) > AUDIT_SERVICE_MEM_LIMIT )
        {
            show_error( "<add_hash_item> too much auditing service memory used (%lu - was %lu)- aborting, please investigate.\n   Called from %s:%d",
                        total_service+sizeof(ASHashItem), total_service, fname, line );
            print_simple_backtrace();
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
            exit(0);
        }
        total_service += sizeof(ASHashItem);
        if( total_service > max_service )
            max_service = total_service ;
    }
}

mem          *
count_find (const char *fname, int line, void *ptr, int type)
{
	ASHashData hdata = {0};

	if( allocs_hash != NULL )
		if( get_hash_item( allocs_hash, (ASHashableValue)ptr, &hdata.vptr) == ASH_Success )
		{
			mem *m = hdata.vptr ;
			if( (m->type & 0xff) == (type & 0xff) )
				return m ;
		}
	return NULL ;
}

mem          *
count_find_and_extract (const char *fname, int line, void *ptr, int type)
{
	mem          *m = NULL;
	if( allocs_hash && ptr )
	{
		ASHashData hd ;
		service_mode++ ;
		if( remove_hash_item (allocs_hash, AS_HASHABLE(ptr), &hd.vptr, False) == ASH_Success )
		{
			m = hd.vptr ;
			if( allocs_hash->items_num <= 0 )
				destroy_ashash(&allocs_hash);
			if( (m->type & 0xff) != (type & 0xff) && (m->type & 0xff) != C_IMAGE )
			{
            	show_error( "while deallocating pointer %p discovered that it was allocated with different type\n   Called from %s:%d", ptr, fname, line );
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
			}
			if( total_service < sizeof(ASHashItem) )
			{
            	show_error( "it seems that we have too little auditing memory (%lu) while deallocating pointer %p.\n   Called from %s:%d", total_service, ptr, fname, line );
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
      		}else
		        total_service -= sizeof(ASHashItem);
        }
        service_mode-- ;
	}
	/* fprintf( stderr, "looking for ptr %p, produced result %p at %s:%d", ptr, m, fname, line ); */
	if( m )
	{
		if ((m->type & 0xff) == C_MEM)
			total_alloc -= m->length;
		else
			total_x_alloc -= m->length;
		deallocations++;
	}
	return m;
}

void         *
countmalloc (const char *fname, int line, size_t length)
{
	void         *ptr;
    if( (int)length < 0 )
	{
		fprintf( stderr, "too large malloc of %lu from %s:%d\n", (unsigned long)length, fname, line );
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
	}
	ptr = safemalloc (length);
	count_alloc (fname, line, ptr, length, C_MEM | C_MALLOC);
	return ptr;
}

void         *
countcalloc (const char *fname, int line, size_t nrecords, size_t length)
{
	void         *ptr = safecalloc (nrecords, length);

    if( (int)(length*nrecords) < 0 )
	{
		fprintf( stderr, "too large calloc of %lu from %s:%d\n", (unsigned long)length*nrecords, fname, line );
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
	}
	count_alloc (fname, line, ptr, nrecords * length, C_MEM | C_CALLOC);
	return ptr;
}

void         *
countrealloc (const char *fname, int line, void *ptr, size_t length)
{
	if (ptr != NULL && length == 0)
		countfree (fname, line, ptr);
	if (length == 0)
		return NULL;
	if (ptr != NULL)
	{
		mem          *m = NULL;
		ASHashResult  res ;

		if( allocs_hash != NULL )
		{
			ASHashData hd ;
			service_mode++ ;
			if( remove_hash_item (allocs_hash, AS_HASHABLE(ptr), &hd.vptr, False) == ASH_Success )
			{
				m = hd.vptr ;	  
				if( (m->type & 0xff) != C_MEM )
				{
					show_error( "while deallocating pointer 0x%lX discovered that it was allocated with different type", ptr );
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
					m = NULL ;
				}
			}
			service_mode-- ;
		}
		if (m == NULL)
		{
			show_error ("countrealloc:attempt in %s:%d to realloc memory(%p) that was never allocated!\n",
					     fname, line, ptr);
			print_simple_backtrace();
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
			return NULL;
		}
		if ((m->type & 0xff) == C_MEM)
		{
			total_alloc -= m->length;
			total_alloc += length;
			if (total_alloc > max_alloc)
				max_alloc = total_alloc;
		} else
		{
			total_x_alloc -= m->length;
			total_x_alloc += length;
			if (total_x_alloc > max_x_alloc)
				max_x_alloc = total_x_alloc;
		}
		m->fname = fname;
		m->line = line;
		m->length = length;
		m->type = C_MEM | C_REALLOC;
		m->ptr = saferealloc (ptr, length);
		m->freed = 0;
		ptr = m->ptr;
		if( (res = add_hash_item( allocs_hash, (ASHashableValue)ptr, m )) != ASH_Success )
		{
			show_error( "failed to log allocation for pointer 0x%lX - result = %d", ptr, res);
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
		}
  		reallocations++;
	} else
		ptr = countmalloc (fname, line, length);

	return ptr;
}

void
countfree (const char *fname, int line, void *ptr)
{
	mem          *m ;

	/* fprintf( stderr, "Service mode = %d, alloca_hash = %p ptr = %p at %s:%d\n", service_mode, allocs_hash, ptr, fname, line );*/
    if( service_mode > 0 || allocs_hash == NULL )
		return ;

	if (ptr == NULL)
	{
		show_error("countfree:attempt to free NULL memory in %s:%d", fname, line);
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
		return;
	}

	m = count_find_and_extract (fname, line, ptr, C_MEM);
	if (m == NULL)
	{
		if( cleanup_mode == 0 ) 
		{
			show_error( "countfree:attempt in %s:%d to free memory(%p) that was never allocated!", fname, line, ptr);
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
		}
		return;
	}
#if 0
// this is invalid code!!
	if (m1->freed > 0)
	{
		fprintf (stderr, "%s:mem already freed %d time(s)!\n", __FUNCTION__, m1->freed);
		fprintf (stderr, "%s:freed from %s:%d\n", __FUNCTION__, (*m1).fname, (*m1).line);
		fprintf (stderr, "%s:called from %s:%d\n", __FUNCTION__, fname, line);
#ifdef DEBUG_ALLOC_STRICT
{	char *segv = NULL ;	*segv = 0 ;  }
#endif
		/* exit (1); */
	} else
		safefree (m1->ptr);
	m1->freed++;
	m1->fname = fname;
	m1->line = line;
#else
	/*fprintf( stderr, "%s: freeing %p at %s:%d\n", __FUNCTION__, m->ptr, fname, line );*/
	safefree (m->ptr);
	mem_destroy( (ASHashableValue)NULL, m );
#endif
}

Bool check_hash_item_reused (ASHashItem *item);

ASHashResult
countadd_hash_item (const char *fname, int line, struct ASHashTable *hash, ASHashableValue value, void *data )
{
	ASHashResult   res = add_hash_item(hash, value, data );

	if( res == ASH_Success )
	{	
		if( check_hash_item_reused (hash->most_recent) )
		{	
			show_debug( __FILE__, fname, line, "reused hash item %p", hash->most_recent );
			count_alloc (fname, line, hash->most_recent, sizeof(ASHashItem), C_MEM | C_ADD_HASH_OPTIONAL_ITEM );
    	}else 
			count_alloc (fname, line, hash->most_recent, sizeof(ASHashItem), C_MEM | C_ADD_HASH_ITEM);
	}
	return res;
}

char* countadd_mystrdup(const char *fname, int line, const char *a)
{
	char *ptr = mystrdup(a);

    if( a != NULL )
		count_alloc (fname, line, ptr, strlen(ptr)+1, C_MEM | C_MYSTRDUP);
	return ptr;
}

char* countadd_mystrndup(const char *fname, int line, const char *a, int len)
{
	char *ptr = mystrndup(a, len );

    if( a != NULL )
		count_alloc (fname, line, ptr, strlen(ptr)+1, C_MEM | C_MYSTRNDUP);
	return ptr;
}

void
output_unfreed_mem (FILE *stream)
{
	ASHashIterator i;
	long asimage_apply_buffer_sum = 0 ;
	long load_freetype_locale_glyph_sum = 0 ;
	long compress_glyph_pixmap_sum = 0 ;
	

    if( stream == NULL )
        stream = stderr ;
    fprintf (stream, "===============================================================================\n");
    fprintf (stream, "Memory audit: %s\n", ApplicationName);
    fprintf (stream, "\n");
    fprintf (stream, "   Total   allocs: %lu\n", allocations);
    fprintf (stream, "   Total reallocs: %lu\n", reallocations);
    fprintf (stream, "   Total deallocs: %lu\n", deallocations);
    fprintf (stream, "Max allocs at any one time: %lu\n", max_allocations);
    fprintf (stream, "Lost audit memory: %lu\n", total_service - deallocated_used*sizeof(mem));
    fprintf (stream, "      Lost memory: %lu\n", total_alloc);
    fprintf (stream, "    Lost X memory: %lu\n", total_x_alloc);
    fprintf (stream, " Max audit memory: %lu\n", max_service);
    fprintf (stream, "  Max memory used: %lu\n", max_alloc);
    fprintf (stream, "Max X memory used: %lu\n", max_x_alloc);
    fprintf (stream, "\n");
    fprintf (stream, "List of unfreed memory\n");
    fprintf (stream, "----------------------\n");
    fprintf (stream, "allocating function    |line |length |pointer    |type (subtype)\n");
    fprintf (stream, "-----------------------+-----+-------+-----------+--------------\n");
	if( start_hash_iteration( allocs_hash, &i ) )
	do
	{
		register mem *m;
		m = curr_hash_data( &i );
		if( m == NULL )
		{
            fprintf (stream, "hmm, wierd, encoutered NULL pointer while trying to check allocation record for %p!", (void*)((*(i.curr_item))->value));
			continue;
		}else if (m->freed == 0)
		{
			if( strcmp( m->fname, "asimage_apply_buffer" ) == 0 ) 
				asimage_apply_buffer_sum += (long)m->length ;
			else if( strcmp( m->fname, "load_freetype_locale_glyph" ) == 0 ) 
				load_freetype_locale_glyph_sum += (long)m->length ;
			else if( strcmp( m->fname, "compress_glyph_pixmap" ) == 0 ) 
				compress_glyph_pixmap_sum += (long)m->length ;

            fprintf (stream, "%23s|%-5d|%-7ld|%p ", m->fname, m->line, (long)m->length, m->ptr);
			switch (m->type & 0xff)
			{
			 case C_MEM:
                 fprintf (stream, "| malloc");
				 switch (m->type & ~0xff)
				 {
				  case C_MALLOC:
                      fprintf (stream, " (malloc)");
					  break;
				  case C_CALLOC:
                      fprintf (stream, " (calloc)");
					  break;
				  case C_REALLOC:
                      fprintf (stream, " (realloc)");
					  break;
				  case C_ADD_HASH_ITEM:
                      fprintf (stream, " (add_hash_item)");
					  break;
				  case C_ADD_HASH_OPTIONAL_ITEM :
                      fprintf (stream, " (add_hash_item)");
					  break;
				  case C_MYSTRDUP:
                      fprintf (stream, " (mystrdup)");
					  break;
				  case C_MYSTRNDUP:
                      fprintf (stream, " (mystrndup)");
					  break;
				 }
				 /* if it seems to be a string, print it */
				 {
					 int           i;
					 const unsigned char *ptr = m->ptr;

					 for (i = 0; i < m->length; i++)
					 {
						 if (ptr[i] == '\0')
							 break;
						 /* don't print strings containing non-space control characters or high ASCII */
						 if ((ptr[i] <= 0x20 && !isspace (ptr[i])) || ptr[i] >= 0x80)
							 i = m->length;
					 }
					 if (i < m->length)
                         fprintf (stream, " '%s'", ptr);
				 }
				 break;
			 case C_PIXMAP:
                 fprintf (stream, "| pixmap");
				 switch (m->type & ~0xff)
				 {
				  case C_CREATEPIXMAP:
                      fprintf (stream, " (XCreatePixmap)");
					  break;
				  case C_BITMAPFROMDATA:
                      fprintf (stream, " (XCreateBitmapFromData)");
					  break;
				  case C_FROMBITMAP:
                      fprintf (stream, " (XCreatePixmapFromBitmapData)");
					  break;
				 }
				 break;
			 case C_GC:
                 fprintf (stream, "| gc (XCreateGC)");
				 break;
			 case C_IMAGE:
                 fprintf (stream, "| image");
				 switch (m->type & ~0xff)
				 {
				  case 0:
                      fprintf (stream, " (XCreateImage)");
					  break;
				  case C_GETIMAGE:
                      fprintf (stream, " (XGetImage)");
					  break;
				  case C_SUBIMAGE:
                      fprintf (stream, " (XSubImage)");
					  break;
				 }
				 break;
			 case C_XMEM:
                 fprintf (stream, "| X mem");
				 switch (m->type & ~0xff)
				 {
				  case C_XGETVISUALINFO:
                      fprintf (stream, " (XGetVisualInfo)");
					  break;
				  case C_XGETWINDOWPROPERTY:
                      fprintf (stream, " (XGetWindowProperty)");
					  break;
                  case C_XLISTPROPERTIES:
                      fprintf (stream, " (XListProperties)");
					  break;
                  case C_XGETTEXTPROPERTY:
                      fprintf (stream, " (XGetTextProperty)");
                      break;
                  case C_XALLOCCLASSHINT :
                      fprintf (stream, " (XAllocClassHint)");
                      break;
                  case C_XALLOCSIZEHINTS :
                      fprintf (stream, " (XAllocSizeHints)");
                      break;
                  case C_XQUERYTREE:
                      fprintf (stream, " (XQueryTree)");
					  break;
				  case C_XGETWMHINTS:
                      fprintf (stream, " (XGetWMHints)");
					  break;
				  case C_XGETWMPROTOCOLS:
                      fprintf (stream, " (XGetWMProtocols)");
					  break;
				  case C_XGETWMNAME:
                      fprintf (stream, " (XGetWMName)");
					  break;
				  case C_XGETCLASSHINT:
                      fprintf (stream, " (XGetClassHint)");
					  break;
				  case C_XGETATOMNAME:
                      fprintf (stream, " (XGetAtomName)");
					  break;
				  case C_XSTRINGLISTTOTEXTPROPERTY:
                      fprintf (stream, " (XStringListToTextProperty)");
					  break;
				 }
				 break;
			}
            fprintf (stream, "\n");
		}
	}while( next_hash_item(&i) );
    fprintf (stream, "===============================================================================\n");
	fprintf( stream, "Total memory allocated in :\n");
	fprintf( stream, "\tasimage_apply_buffer() = %ld\n", asimage_apply_buffer_sum );
	fprintf( stream, "\tload_freetype_locale_glyph() = %ld\n", load_freetype_locale_glyph_sum );
	fprintf( stream, "\tcompress_glyph_pixmap() = %ld\n", compress_glyph_pixmap_sum );
    fprintf (stream, "===============================================================================\n");
}

void
spool_unfreed_mem (char *filename, const char *comments)
{
    FILE *spoolfile = fopen(filename, "w+");
    if( spoolfile )
    {
        fprintf( spoolfile, "%s: Memory Usage Snapshot <%s>", ApplicationName, comments?comments:"no comments\n" );
        output_unfreed_mem( spoolfile );
        fclose( spoolfile );
    }
}

void print_unfreed_mem()
{
    output_unfreed_mem(NULL);
}


void
print_unfreed_mem_stats (const char *file, const char *func, int line, const char *msg)
{
    if( msg )
        fprintf( stderr, "%s:%s:%s:%d: Memory audit %s\n", ApplicationName, file, func, line, msg );
    fprintf( stderr, "%s:%s:%s:%d: Memory audit counts: allocs %lu, reallocs: %lu, deallocs: %lu, max simultaneous %lu\n",
                     ApplicationName, file, func, line, allocations, reallocations, deallocations, max_allocations);
    fprintf( stderr, "%s:%s:%s:%d: Memory audit used memory: private %lu, X %lu, audit %lu, max private %lu, max X %lu, max audit %lu\n",
                     ApplicationName, file, func, line, total_alloc, total_x_alloc, total_service - deallocated_used*sizeof(mem), max_alloc, max_x_alloc, max_service);
}


#ifndef X_DISPLAY_MISSING
Pixmap
count_xcreatepixmap (const char *fname, int line, Display * display,
					 Drawable drawable, unsigned int width, unsigned int height, unsigned int depth)
{
	Pixmap        pmap = XCreatePixmap (display, drawable, width, height, depth);

	if (pmap == None)
		return None;
	count_alloc (fname, line, (void *)pmap, width * height * depth / 8, C_PIXMAP | C_CREATEPIXMAP);
	return pmap;
}


Pixmap
count_xcreatebitmapfromdata (const char *fname, int line, Display * display,
							 Drawable drawable, char *data, unsigned int width, unsigned int height)
{
	Pixmap        pmap = XCreateBitmapFromData (display, drawable, data, width, height);

	if (pmap == None)
		return None;
	count_alloc (fname, line, (void *)pmap, width * height / 8, C_PIXMAP | C_BITMAPFROMDATA);
	return pmap;
}

Pixmap
count_xcreatepixmapfrombitmapdata (const char *fname, int line,
								   Display * display, Drawable drawable,
								   char *data, unsigned int width,
								   unsigned int height, unsigned long fg, unsigned long bg, unsigned int depth)
{
	Pixmap        pmap = XCreatePixmapFromBitmapData (display, drawable, data, width, height,
													  fg,
													  bg, depth);

	if (pmap == None)
		return None;
	count_alloc (fname, line, (void *)pmap, width * height * depth / 8, C_PIXMAP | C_FROMBITMAP);
	return pmap;
}

int
count_xfreepixmap (const char *fname, int line, Display * display, Pixmap pmap)
{
	mem          *m = count_find_and_extract (fname, line, (void *)pmap, C_PIXMAP);

	if (pmap == None)
	{
		show_error("count_xfreepixmap:attempt to free None pixmap in %s:%d", fname, line);
		return !Success;
	}

	if (m == NULL)
	{
		show_error ("count_xfreepixmap:attempt in %s:%d to free Pixmap(0x%X) that was never created, or already freed!",
				 	 fname, line, (unsigned int)pmap);
		raise( SIGUSR2 );
		XFreePixmap (display, pmap );
		return !Success;
	}
/*	fprintf (stderr,"%s:%s:%d freeing Pixmap(0x%X)\n", __FUNCTION__, fname, line, (unsigned int)pmap);
*/

	XFreePixmap (display, pmap);
	mem_destroy( (ASHashableValue)NULL, m );
	return Success;
}

GC
count_xcreategc (const char *fname, int line, Display * display,
				 Drawable drawable, unsigned int mask, XGCValues * values)
{
	GC            gc = XCreateGC (display, drawable, mask, values);

	if (gc == None)
		return None;
	count_alloc (fname, line, (void *)gc, sizeof (XGCValues), C_GC);
	return gc;
}

int
count_xfreegc (const char *fname, int line, Display * display, GC gc)
{
	mem          *m = count_find_and_extract (fname, line, (void *)gc, C_GC);

	if (gc == None)
	{
		show_error( "count_xfreegc:attempt to free None GC in %s:%d", fname, line);
		return !Success;
	}

	if (m == NULL)
	{
		show_error( "count_xfreegc:attempt in %s:%d to free a GC (%p)that was never created or already destroyed!",
				    fname, line, gc);
		return !Success;
	}

	XFreeGC (display, gc);
	mem_destroy( (ASHashableValue)NULL, m );
	return Success;
}

XImage       *
count_xcreateimage (const char *fname, int line, Display * display,
					Visual * visual, unsigned int depth, int format,
					int offset, char *data, unsigned int width, unsigned int height, int bitmap_pad, int byte_per_line)
{
	XImage       *image = XCreateImage (display, visual, depth, format, offset, data, width,
										height,
										bitmap_pad, byte_per_line);

	if (image == NULL)
		return NULL;
	count_alloc (fname, line, (void *)image, sizeof (*image), C_IMAGE);
	return image;
}

XImage       *
count_xgetimage (const char *fname, int line, Display * display,
				 Drawable drawable, int x, int y, unsigned int width,
				 unsigned int height, unsigned long plane_mask, int format)
{
	XImage       *image = XGetImage (display, drawable, x, y, width, height, plane_mask,

									 format);

	if (image == NULL)
		return NULL;
	count_alloc (fname, line, (void *)image,
				 sizeof (*image) + image->height * image->bytes_per_line, C_IMAGE | C_GETIMAGE);
	return image;
}

XImage       *
count_xsubimage (const char *fname, int line, XImage *img,
				 int x, int y, unsigned int width, unsigned int height )

{
	XImage       *image = (*(img->f.sub_image))(img, x, y, width, height);

	if (image == NULL)
		return NULL;
	count_alloc (fname, line, (void *)image,
				 sizeof (*image) + image->height * image->bytes_per_line, C_IMAGE | C_SUBIMAGE);
	return image;
}

int
count_xdestroyimage (const char *fname, int line, XImage * image)
{
	mem          *m;
	void         *image_data;
	void         *image_obdata;

	if (image == NULL)
	{
		show_error("count_xdestroyimage:attempt to free NULL XImage in %s:%d", fname, line);
		return BadValue;
	}
	image_data = (void *)(image->data);
	image_obdata = (void *)(image->obdata);

	if ((m = count_find (fname, line, (void *)image, C_IMAGE)) == NULL)
		/* can also be of C_MEM type if we allocated it ourselvs */
		if ((m = count_find (fname, line, (void *)image, C_MEM)) == NULL)
		{
			show_error("count_xdestroyimage:attempt in %s:%d to destroy an XImage that was never created or already destroyed.\n",
					    fname, line);
			return !Success;
		}

	(*image->f.destroy_image) (image);

	if ((m = count_find_and_extract (fname, line, (void *)image, C_IMAGE)) == NULL)
		/* can also be of C_MEM type if we allocated it ourselvs */
		m = count_find_and_extract (fname, line, (void *)image, C_MEM);
	if (m)
		mem_destroy( (ASHashableValue)NULL, m );

	/* find and free the image->data pointer if it is in our list */
	if( image_data )
		if ((m = count_find_and_extract (fname, line, image_data, C_MEM)) != NULL)
			mem_destroy( (ASHashableValue)NULL, m );

	/* find and free the image->obdata pointer if it is in our list */
	if( image_obdata )
		if ((m = count_find_and_extract (fname, line, image_obdata, C_MEM)) != NULL)
			mem_destroy( (ASHashableValue)NULL, m );

	return Success;
}

XVisualInfo *
count_xgetvisualinfo(const char *fname, int line, 
					 Display *dpy, long vinfo_mask, 
					 XVisualInfo *vinfo_template, int *nitems_return)
{
	int my_nitems_return = 0;
	XVisualInfo *val = XGetVisualInfo( dpy, vinfo_mask, vinfo_template, &my_nitems_return );	
	
	if (val != NULL && my_nitems_return)
		count_alloc (fname, line, (void *)val,
					 my_nitems_return * sizeof(XVisualInfo), C_XMEM | C_XGETVISUALINFO);
	*nitems_return = my_nitems_return;		   /* need to do this in case bytes_after_return and nitems_return point to the same var */
	return val;
}

int
count_xgetwindowproperty (const char *fname, int line, Display * display,
						  Window w, Atom property, long long_offset,
						  long long_length, Bool prop_delete, Atom req_type,
						  Atom * actual_type_return,
						  int *actual_format_return,
						  unsigned long *nitems_return, unsigned long *bytes_after_return, unsigned char **prop_return)
{
	int           val;
	unsigned long my_nitems_return;

	val =
		XGetWindowProperty (display, w, property, long_offset, long_length,
							prop_delete, req_type, actual_type_return,
							actual_format_return, &my_nitems_return, bytes_after_return, prop_return);
	if (val == Success && my_nitems_return)
		count_alloc (fname, line, (void *)*prop_return,
					 my_nitems_return * *actual_format_return / 8, C_XMEM | C_XGETWINDOWPROPERTY);
	*nitems_return = my_nitems_return;		   /* need to do this in case bytes_after_return and nitems_return point to the same var */
	return val;
}

Atom *
count_xlistproperties( const char *fname, int line, Display * display,
                       Window w, int *props_num )
{
    Atom *props ;

    props = XListProperties (display, w, props_num);
    if( props != NULL && *props_num > 0 )
        count_alloc (fname, line, (void *)props,
                     (*props_num)*sizeof(Atom), C_XMEM | C_XLISTPROPERTIES);
    return props;
}


Status
count_xgettextproperty(const char *fname, int line, Display * display, Window w,
                       XTextProperty *trg, Atom property)
{
    Status        val;

    val = XGetTextProperty(display,w,trg,property);
    if (val && trg->value )
        count_alloc (fname, line, (void *)(trg->value), strlen((char*)trg->value)+1, C_XMEM | C_XGETTEXTPROPERTY );
    return val;
}

XClassHint *
count_xallocclasshint(const char *fname, int line)
{
    XClassHint *wmclass ;
    wmclass = XAllocClassHint();
    if( wmclass != NULL )
        count_alloc (fname, line, (void *)wmclass, sizeof(XClassHint), C_XMEM | C_XALLOCCLASSHINT );
    return wmclass;
}

XSizeHints *
count_xallocsizehints(const char *fname, int line)
{
    XSizeHints *size_hints ;
    size_hints = XAllocSizeHints();
    if( size_hints != NULL )
        count_alloc (fname, line, (void *)size_hints, sizeof(XSizeHints), C_XMEM | C_XALLOCSIZEHINTS );
    return size_hints;
}


Status
count_xquerytree (const char *fname, int line, Display * display, Window w,
				  Window * root_return, Window * parent_return,
				  Window ** children_return, unsigned int *nchildren_return)
{
	Status        val;

	val = XQueryTree (display, w, root_return, parent_return, children_return, nchildren_return);
	if (val && *nchildren_return)
		count_alloc (fname, line, (void *)*children_return, *nchildren_return * sizeof (Window), C_XMEM | C_XQUERYTREE);
	return val;
}

/* really returns XWMHints*, but to avoid requiring extra includes, we'll return void* */
void         *
count_xgetwmhints (const char *fname, int line, Display * display, Window w)
{
	XWMHints     *val;

	val = XGetWMHints (display, w);
	if (val != NULL)
		count_alloc (fname, line, (void *)val, sizeof (XWMHints), C_XMEM | C_XGETWMHINTS);
	return (void *)val;
}

/* protocols_return is really Atom**, but to avoid extra includes, we'll use void* */
Status
count_xgetwmprotocols (const char *fname, int line, Display * display,
					   Window w, void *protocols_return, int *count_return)
{
	Status        val;

	val = XGetWMProtocols (display, w, (Atom **) protocols_return, count_return);
	if (val && *count_return)
		count_alloc (fname, line, *(void **)protocols_return,
					 *count_return * sizeof (Atom), C_XMEM | C_XGETWMPROTOCOLS);
	return val;
}

/* text_prop_return is really XTextProperty*, but to avoid extra includes, we'll use void* */
Status
count_xgetwmname (const char *fname, int line, Display * display, Window w, void *text_prop_return)
{
	Status        val;
	XTextProperty *prop = text_prop_return;

	val = XGetWMName (display, w, prop);
	if (val && prop->nitems)
		count_alloc (fname, line, (void *)prop->value, prop->nitems * prop->format / 8, C_XMEM | C_XGETWMNAME);
	return val;
}

/* class_hints_return is really XTextProperty*, but to avoid extra includes, we'll use void* */
Status
count_xgetclasshint (const char *fname, int line, Display * display, Window w, void *class_hint_return)
{
	Status        val;
	XClassHint   *prop = class_hint_return;

	val = XGetClassHint (display, w, prop);
	if (val && prop->res_name)
		count_alloc (fname, line, (void *)prop->res_name, strlen (prop->res_name), C_XMEM | C_XGETCLASSHINT);
	if (val && prop->res_class)
		count_alloc (fname, line, (void *)prop->res_class, strlen (prop->res_class), C_XMEM | C_XGETCLASSHINT);
	return val;
}

char         *
count_xgetatomname (const char *fname, int line, Display * display, Atom atom)
{
	char         *val = XGetAtomName (display, atom);

	if (val != NULL)
		count_alloc (fname, line, (void *)val, strlen (val), C_XMEM | C_XGETATOMNAME);
	return val;
}

/* text_prop_return is really XTextProperty*, but to avoid extra includes, we'll use void* */
Status
count_xstringlisttotextproperty (const char *fname, int line, char **list, int count, void *text_prop_return)
{
	Status        val;
	XTextProperty *prop = text_prop_return;

	val = XStringListToTextProperty (list, count, prop);
	if (val && prop->nitems)
		count_alloc (fname, line, (void *)prop->value,
					 prop->nitems * prop->format / 8, C_XMEM | C_XSTRINGLISTTOTEXTPROPERTY);
	return val;
}

int
count_xfree (const char *fname, int line, void *data)
{
	mem          *m = count_find_and_extract (fname, line, (void *)data, C_XMEM);

	if (data == NULL)
	{
		show_error("count_xfree:attempt to free NULL X memory in %s:%d", fname, line);
		return !Success;
	}

	if (m == NULL)
	{
		show_error("count_xfree:attempt in %s:%d to free X memory (%p) that was never allocated or already freed!",
				    fname, line, data);
		return !Success;
	}

	XFree (data);
	mem_destroy( (ASHashableValue)NULL, m );
	return Success;
}

#endif /* #ifndef X_DISPLAY_MISSING */

