summaryrefslogtreecommitdiffstats
path: root/src/LYLeaks.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/LYLeaks.c')
-rw-r--r--src/LYLeaks.c1185
1 files changed, 1185 insertions, 0 deletions
diff --git a/src/LYLeaks.c b/src/LYLeaks.c
new file mode 100644
index 0000000..2f2de28
--- /dev/null
+++ b/src/LYLeaks.c
@@ -0,0 +1,1185 @@
+/*
+ * $LynxId: LYLeaks.c,v 1.41 2018/03/30 00:27:58 tom Exp $
+ *
+ * Copyright (c) 1994, University of Kansas, All Rights Reserved
+ * (this file was rewritten twice - 1998/1999 and 2003/2004)
+ *
+ * This code will be used only if LY_FIND_LEAKS is defined.
+ *
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ * 10-30-97 modified to handle StrAllocCopy() and
+ * StrAllocCat(). - KW & FM
+ * 07-23-07 free leaks of THIS module too -TD
+ * 02-09-12 add bstring functions -TD
+ */
+
+/*
+ * Disable the overriding of the memory routines for this file.
+ */
+#define NO_MEMORY_TRACKING
+
+#include <HTUtils.h>
+#include <LYexit.h>
+#include <LYLeaks.h>
+#include <LYUtils.h>
+#include <LYGlobalDefs.h>
+
+#ifdef LY_FIND_LEAKS
+
+static AllocationList *ALp_RunTimeAllocations = NULL;
+
+#define LEAK_SUMMARY
+
+#ifdef LEAK_SUMMARY
+
+static size_t now_allocated = 0;
+static size_t peak_alloced = 0;
+
+static size_t total_alloced = 0;
+static size_t total_freed = 0;
+
+static long count_mallocs = 0;
+static long count_frees = 0;
+
+static void CountMallocs(size_t size)
+{
+ ++count_mallocs;
+ total_alloced += size;
+ now_allocated += size;
+ if (peak_alloced < now_allocated)
+ peak_alloced = now_allocated;
+}
+
+static void CountFrees(size_t size)
+{
+ ++count_frees;
+ total_freed += size;
+ now_allocated -= size;
+}
+
+#else
+#define CountMallocs(size) ++count_mallocs
+#define CountFrees(size) /* nothing */
+#endif
+
+/*
+ * Purpose: Add a new allocation item to the list.
+ * Arguments: ALp_new The new item to add.
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * Static function made to make code reusable in projects beyond
+ * Lynx (some might ask why not use HTList).
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void AddToList(AllocationList * ALp_new)
+{
+ /*
+ * Just make this the first item in the list.
+ */
+ ALp_new->ALp_Next = ALp_RunTimeAllocations;
+ ALp_RunTimeAllocations = ALp_new;
+}
+
+/*
+ * Purpose: Find the place in the list where vp_find is currently
+ * tracked.
+ * Arguments: vp_find A pointer to look for in the list.
+ * Return Value: AllocationList * Either vp_find's place in the
+ * list or NULL if not found.
+ * Remarks/Portability/Dependencies/Restrictions:
+ * Static function made to make code reusable in projects outside
+ * of Lynx (some might ask why not use HTList).
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static AllocationList *FindInList(void *vp_find)
+{
+ AllocationList *ALp_find = ALp_RunTimeAllocations;
+
+ /*
+ * Go through the list of allocated pointers until end of list or vp_find
+ * is found.
+ */
+ while (ALp_find != NULL) {
+ if (ALp_find->vp_Alloced == vp_find) {
+ break;
+ }
+ ALp_find = ALp_find->ALp_Next;
+ }
+
+ return (ALp_find);
+}
+
+/*
+ * Purpose: Remove the specified item from the list.
+ * Arguments: ALp_del The item to remove from the list.
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * Static function made to make code reusable in projects outside
+ * of Lynx (some might ask why not use HTList).
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+static void RemoveFromList(AllocationList * ALp_del)
+{
+ AllocationList *ALp_findbefore = ALp_RunTimeAllocations;
+
+ /*
+ * There is one special case, where the item to remove is the first in the
+ * list.
+ */
+ if (ALp_del == ALp_findbefore) {
+ ALp_RunTimeAllocations = ALp_del->ALp_Next;
+ } else {
+
+ /*
+ * Loop through checking all of the next values, if a match don't
+ * continue. Always assume the item will be found.
+ */
+ while (ALp_findbefore->ALp_Next != ALp_del) {
+ ALp_findbefore = ALp_findbefore->ALp_Next;
+ }
+
+ /*
+ * We are one item before the one to get rid of. Get rid of it.
+ */
+ ALp_findbefore->ALp_Next = ALp_del->ALp_Next;
+ }
+}
+
+/*
+ * Make the malloc-sequence available for debugging/tracing.
+ */
+#ifndef LYLeakSequence
+long LYLeakSequence(void)
+{
+ return count_mallocs;
+}
+#endif
+
+/*
+ * Purpose: Print a report of all memory left unallocated by
+ * Lynx code or attempted unallocations on
+ * pointers that are not valid and then free
+ * all unfreed memory.
+ * Arguments: void
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * This function should be registered for execution with the
+ * atexit (stdlib.h) function as the first statement
+ * in main.
+ * All output of this function is sent to the file defined in
+ * the header LYLeaks.h (LEAKAGE_SINK).
+ */
+void LYLeaks(void)
+{
+ AllocationList *ALp_head;
+ size_t st_total = (size_t) 0;
+ FILE *Fp_leakagesink;
+
+ CTRACE((tfp, "entering LYLeaks, flag=%d\n", LYfind_leaks));
+
+ if (LYfind_leaks == FALSE) {
+ /*
+ * Free MY leaks too, in case someone else is watching.
+ */
+ while (ALp_RunTimeAllocations != NULL) {
+ ALp_head = ALp_RunTimeAllocations;
+ ALp_RunTimeAllocations = ALp_head->ALp_Next;
+ free(ALp_head);
+ }
+ return;
+ }
+
+ /*
+ * Open the leakage sink to take all the output. Recreate the file each
+ * time. Do nothing if unable to open the file.
+ */
+ Fp_leakagesink = LYNewTxtFile(LYLeaksPath);
+ if (Fp_leakagesink == NULL) {
+ return;
+ }
+
+ while (ALp_RunTimeAllocations != NULL) {
+ /*
+ * Take the head off of the run time allocation list.
+ */
+ ALp_head = ALp_RunTimeAllocations;
+ ALp_RunTimeAllocations = ALp_head->ALp_Next;
+
+ /*
+ * Print the type of leak/error. Release memory when we no longer
+ * need it.
+ */
+ if (ALp_head->vp_Alloced == NULL) {
+ /*
+ * If there is realloc information on the bad request, then it was
+ * a bad pointer value in a realloc statement.
+ */
+ fprintf(Fp_leakagesink, "%s.\n",
+ gettext("Invalid pointer detected."));
+ fprintf(Fp_leakagesink, "%s\t%ld\n",
+ gettext("Sequence:"),
+ ALp_head->st_Sequence);
+ fprintf(Fp_leakagesink, "%s\t%p\n",
+ gettext("Pointer:"), ALp_head->vp_BadRequest);
+
+ /*
+ * Don't free the bad request, it is an invalid pointer. If the
+ * free source information is empty, we should check the realloc
+ * information too since it can get passed bad pointer values also.
+ */
+ if (ALp_head->SL_memory.cp_FileName == NULL) {
+ fprintf(Fp_leakagesink, "%s\t%s\n",
+ gettext("FileName:"),
+ ALp_head->SL_realloc.cp_FileName);
+ fprintf(Fp_leakagesink, "%s\t%d\n",
+ gettext("LineCount:"),
+ ALp_head->SL_realloc.ssi_LineNumber);
+ } else {
+ fprintf(Fp_leakagesink, "%s\t%s\n",
+ gettext("FileName:"),
+ ALp_head->SL_memory.cp_FileName);
+ fprintf(Fp_leakagesink, "%s\t%d\n",
+ gettext("LineCount:"),
+ ALp_head->SL_memory.ssi_LineNumber);
+ }
+ } else {
+ size_t i_counter;
+ char *value = (char *) (ALp_head->vp_Alloced);
+
+ /*
+ * Increment the count of total memory lost and then print the
+ * information.
+ */
+ st_total += ALp_head->st_Bytes;
+
+ fprintf(Fp_leakagesink, "%s\n",
+ gettext("Memory leak detected."));
+ fprintf(Fp_leakagesink, "%s\t%ld\n",
+ gettext("Sequence:"),
+ ALp_head->st_Sequence);
+ fprintf(Fp_leakagesink, "%s\t%p\n",
+ gettext("Pointer:"),
+ ALp_head->vp_Alloced);
+ fprintf(Fp_leakagesink, "%s\t",
+ gettext("Contains:"));
+ for (i_counter = 0;
+ i_counter < ALp_head->st_Bytes &&
+ i_counter < MAX_CONTENT_LENGTH;
+ i_counter++) {
+ if (isprint(UCH(value[i_counter]))) {
+ fprintf(Fp_leakagesink, "%c", value[i_counter]);
+ } else {
+ fprintf(Fp_leakagesink, "|");
+ }
+ }
+ fprintf(Fp_leakagesink, "\n");
+ fprintf(Fp_leakagesink, "%s\t%d\n",
+ gettext("ByteSize:"),
+ (int) (ALp_head->st_Bytes));
+ fprintf(Fp_leakagesink, "%s\t%s\n",
+ gettext("FileName:"),
+ ALp_head->SL_memory.cp_FileName);
+ fprintf(Fp_leakagesink, "%s\t%d\n",
+ gettext("LineCount:"),
+ ALp_head->SL_memory.ssi_LineNumber);
+ /*
+ * Give the last time the pointer was realloced if it happened
+ * also.
+ */
+ if (ALp_head->SL_realloc.cp_FileName != NULL) {
+ fprintf(Fp_leakagesink, "%s\t%s\n",
+ gettext("realloced:"),
+ ALp_head->SL_realloc.cp_FileName);
+ fprintf(Fp_leakagesink, "%s\t%d\n",
+ gettext("LineCount:"),
+ ALp_head->SL_realloc.ssi_LineNumber);
+ }
+ fflush(Fp_leakagesink);
+ FREE(ALp_head->vp_Alloced);
+ }
+
+ /*
+ * Create a blank line and release the memory held by the item.
+ */
+ fprintf(Fp_leakagesink, "\n");
+ FREE(ALp_head);
+ }
+
+ /*
+ * Give a grand total of the leakage. Close the output file.
+ */
+ fprintf(Fp_leakagesink, "%s\t%u\n",
+ gettext("Total memory leakage this run:"),
+ (unsigned) st_total);
+#ifdef LEAK_SUMMARY
+ fprintf(Fp_leakagesink,
+ "%s\t%lu\n", gettext("Peak allocation"), (unsigned long) peak_alloced);
+ fprintf(Fp_leakagesink,
+ "%s\t%lu\n", gettext("Bytes allocated"), (unsigned long) total_alloced);
+ fprintf(Fp_leakagesink,
+ "%s\t%ld\n", gettext("Total mallocs"), count_mallocs);
+ fprintf(Fp_leakagesink,
+ "%s\t%ld\n", gettext("Total frees"), count_frees);
+#endif
+ fclose(Fp_leakagesink);
+
+ HTSYS_purge(LEAKAGE_SINK);
+}
+
+/*
+ * Purpose: Capture allocations using malloc (stdlib.h) and track
+ * the information in a list.
+ * Arguments: st_bytes The size of the allocation requested
+ * in bytes.
+ * cp_File The file from which the request for
+ * allocation came from.
+ * ssi_Line The line number in cp_File where the
+ * allocation request came from.
+ * Return Value: void * A pointer to the allocated memory or NULL on
+ * failure as per malloc (stdlib.h)
+ * Remarks/Portability/Dependencies/Restrictions:
+ * If no memory is allocated, then no entry is added to the
+ * allocation list.
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void *LYLeakMalloc(size_t st_bytes, const char *cp_File,
+ const short ssi_Line)
+{
+ void *vp_malloc;
+
+ if (LYfind_leaks == FALSE) {
+ vp_malloc = (void *) malloc(st_bytes);
+ } else {
+
+ /*
+ * Do the actual allocation.
+ */
+ vp_malloc = (void *) malloc(st_bytes);
+ CountMallocs(st_bytes);
+
+ /*
+ * Only on successful allocation do we track any information.
+ */
+ if (vp_malloc != NULL) {
+ /*
+ * Further allocate memory to store the information. Just return
+ * on failure to allocate more.
+ */
+ AllocationList *ALp_new = typecalloc(AllocationList);
+
+ if (ALp_new != NULL) {
+ /*
+ * Copy over the relevant information. There is no need to
+ * allocate more memory for the file name as it is a static
+ * string anyway.
+ */
+ ALp_new->st_Sequence = count_mallocs;
+ ALp_new->vp_Alloced = vp_malloc;
+ ALp_new->st_Bytes = st_bytes;
+ ALp_new->SL_memory.cp_FileName = cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+ /*
+ * Add the new item to the allocation list.
+ */
+ AddToList(ALp_new);
+ }
+ }
+ }
+ return (vp_malloc);
+}
+
+/*
+ * Purpose: Add information about new allocation to the list,
+ * after a call to malloc or calloc or an equivalent
+ * function which may or may not have already created
+ * a list entry.
+ * Arguments: vp_malloc The pointer to newly allocated memory.
+ * Arguments: st_bytes The size of the allocation requested
+ * in bytes.
+ * cp_File The file from which the request for
+ * allocation came from.
+ * ssi_Line The line number in cp_File where the
+ * allocation request came from.
+ * Return Value: void * A pointer to the allocated memory or NULL on
+ * failure.
+ * Remarks/Portability/Dependencies/Restrictions:
+ * If no memory is allocated, then no entry is added to the
+ * allocation list.
+ * Revision History:
+ * 1999-02-08 created, modelled after LYLeakMalloc - kw
+ */
+AllocationList *LYLeak_mark_malloced(void *vp_malloced,
+ size_t st_bytes,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ AllocationList *ALp_new = NULL;
+
+ if (LYfind_leaks != FALSE) {
+ /*
+ * The actual allocation has already been done!
+ *
+ * Only on successful allocation do we track any information.
+ */
+ if (vp_malloced != NULL) {
+ /*
+ * See if there is already an entry. If so, just update the source
+ * location info.
+ */
+ ALp_new = FindInList(vp_malloced);
+ if (ALp_new) {
+ ALp_new->SL_memory.cp_FileName = cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+ } else {
+ /*
+ * Further allocate memory to store the information. Just
+ * return on failure to allocate more.
+ */
+ ALp_new = typecalloc(AllocationList);
+ if (ALp_new != NULL) {
+ /*
+ * Copy over the relevant information.
+ */
+ ALp_new->vp_Alloced = vp_malloced;
+ ALp_new->st_Bytes = st_bytes;
+ ALp_new->SL_memory.cp_FileName = cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+ /*
+ * Add the new item to the allocation list.
+ */
+ AddToList(ALp_new);
+ CountMallocs(st_bytes);
+ }
+ }
+ }
+ }
+ return (ALp_new);
+}
+
+/*
+ * Purpose: Capture allocations by calloc (stdlib.h) and
+ * save relevant information in a list.
+ * Arguments: st_number The number of items to allocate.
+ * st_bytes The size of each item.
+ * cp_File The file which wants to allocation.
+ * ssi_Line The line number in cp_File requesting
+ * the allocation.
+ * Return Value: void * The allocated memory, or NULL on failure as
+ * per calloc (stdlib.h)
+ * Remarks/Portability/Dependencies/Restrictions:
+ * If no memory can be allocated, then no entry will be added
+ * to the list.
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void *LYLeakCalloc(size_t st_number, size_t st_bytes, const char *cp_File,
+ const short ssi_Line)
+{
+ void *vp_calloc;
+
+ if (LYfind_leaks == FALSE) {
+ vp_calloc = (void *) calloc(st_number, st_bytes);
+ } else {
+
+ /*
+ * Allocate the requested memory.
+ */
+ vp_calloc = (void *) calloc(st_number, st_bytes);
+ CountMallocs(st_bytes * st_number);
+
+ /*
+ * Only if the allocation was a success do we track information.
+ */
+ if (vp_calloc != NULL) {
+ /*
+ * Allocate memory for the item to be in the list. If unable, just
+ * return.
+ */
+ AllocationList *ALp_new = typecalloc(AllocationList);
+
+ if (ALp_new != NULL) {
+
+ /*
+ * Copy over the relevant information. There is no need to
+ * allocate memory for the file name as it is a static string
+ * anyway.
+ */
+ ALp_new->st_Sequence = count_mallocs;
+ ALp_new->vp_Alloced = vp_calloc;
+ ALp_new->st_Bytes = (st_number * st_bytes);
+ ALp_new->SL_memory.cp_FileName = cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+ /*
+ * Add the item to the allocation list.
+ */
+ AddToList(ALp_new);
+ }
+ }
+ }
+ return (vp_calloc);
+}
+
+/*
+ * Purpose: Capture any realloc (stdlib.h) calls in order to
+ * properly keep track of our run time allocation
+ * table.
+ * Arguments: vp_Alloced The previously allocated block of
+ * memory to resize. If NULL,
+ * realloc works just like
+ * malloc.
+ * st_newBytes The new size of the chunk of memory.
+ * cp_File The file containing the realloc.
+ * ssi_Line The line containing the realloc in cp_File.
+ * Return Value: void * The new pointer value (could be the same) or
+ * NULL if unable to resize (old block
+ * still exists).
+ * Remarks/Portability/Dependencies/Restrictions:
+ * If unable to resize vp_Alloced, then no change in the
+ * allocation list will be made.
+ * If vp_Alloced is an invalid pointer value, the program will
+ * exit after one last entry is added to the allocation list.
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void *LYLeakRealloc(void *vp_Alloced,
+ size_t st_newBytes,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ void *vp_realloc;
+ AllocationList *ALp_renew;
+
+ if (LYfind_leaks == FALSE) {
+ vp_realloc = (void *) realloc(vp_Alloced, st_newBytes);
+
+ } else if (vp_Alloced == NULL) {
+ /*
+ * If we are asked to resize a NULL pointer, this is just a malloc
+ * call.
+ */
+ vp_realloc = LYLeakMalloc(st_newBytes, cp_File, ssi_Line);
+
+ } else {
+
+ /*
+ * Find the current vp_Alloced block in the list. If NULL, this is an
+ * invalid pointer value.
+ */
+ ALp_renew = FindInList(vp_Alloced);
+ if (ALp_renew == NULL) {
+ /*
+ * Track the invalid pointer value and then exit. If unable to
+ * allocate, just exit.
+ */
+ AllocationList *ALp_new = typecalloc(AllocationList);
+
+ if (ALp_new == NULL) {
+ exit_immediately(EXIT_FAILURE);
+ }
+
+ /*
+ * Set the information up; no need to allocate file name since it is a
+ * static string.
+ */
+ ALp_new->vp_Alloced = NULL;
+ ALp_new->vp_BadRequest = vp_Alloced;
+ ALp_new->SL_realloc.cp_FileName = cp_File;
+ ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+
+ /*
+ * Add the item to the list. Exit.
+ */
+ AddToList(ALp_new);
+ exit_immediately(EXIT_FAILURE);
+ }
+
+ /*
+ * Perform the resize. If not NULL, record the information.
+ */
+ vp_realloc = (void *) realloc(vp_Alloced, st_newBytes);
+ CountFrees(ALp_renew->st_Bytes);
+ CountMallocs(st_newBytes);
+
+ if (vp_realloc != NULL) {
+ ALp_renew->st_Sequence = count_mallocs;
+ ALp_renew->vp_Alloced = vp_realloc;
+ ALp_renew->st_Bytes = st_newBytes;
+
+ /*
+ * Update the realloc information, too. No need to allocate file name,
+ * static string.
+ */
+ ALp_renew->SL_realloc.cp_FileName = cp_File;
+ ALp_renew->SL_realloc.ssi_LineNumber = ssi_Line;
+ }
+ }
+ return (vp_realloc);
+}
+
+/*
+ * Purpose: Add information about reallocated memory to the list,
+ * after a call to realloc or an equivalent
+ * function which has not already created or updated
+ * a list entry.
+ * Arguments: ALp_old List entry for previously allocated
+ * block of memory to resize. If NULL,
+ * mark_realloced works just like
+ * mark_malloced.
+ * vp_realloced The new pointer, after resizing.
+ * st_newBytes The new size of the chunk of memory.
+ * cp_File The file to record.
+ * ssi_Line The line to record.
+ * Return Value: Pointer to new or updated list entry
+ * for this memory block.
+ * NULL on allocation error.
+ * Revision History:
+ * 1999-02-11 created kw
+ */
+#if defined(LY_FIND_LEAKS) && defined(LY_FIND_LEAKS_EXTENDED)
+static AllocationList *mark_realloced(AllocationList * ALp_old, void *vp_realloced,
+ size_t st_newBytes,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ /*
+ * If there is no list entry for the old allocation, treat this as if a new
+ * allocation had happened.
+ */
+ if (ALp_old == NULL) {
+ return (LYLeak_mark_malloced(vp_realloced, st_newBytes, cp_File, ssi_Line));
+ }
+
+ /*
+ * ALp_old represents the memory block before reallocation. Assume that if
+ * we get here, there isn't yet a list entry for the new, possibly
+ * different, address after realloc, that is our list hasn't been updated -
+ * so we're going to do that now.
+ */
+
+ if (vp_realloced != NULL) {
+ ALp_old->vp_Alloced = vp_realloced;
+ ALp_old->st_Bytes = st_newBytes;
+ ALp_old->SL_realloc.cp_FileName = cp_File;
+ ALp_old->SL_realloc.ssi_LineNumber = ssi_Line;
+ }
+
+ return (ALp_old);
+}
+#endif /* not LY_FIND_LEAKS and LY_FIND_LEAKS_EXTENDED */
+
+/*
+ * Purpose: Capture all requests to free information and also
+ * remove items from the allocation list.
+ * Arguments: vp_Alloced The memory to free.
+ * cp_File The file calling free.
+ * ssi_Line The line of cp_File calling free.
+ * Return Value: void
+ * Remarks/Portability/Dependencies/Restrictions:
+ * If the pointer value is invalid, then an item will be added
+ * to the list and nothing else is done.
+ * I really like the name of this function and one day hope
+ * that Lynx is Leak Free.
+ * Revision History:
+ * 05-26-94 created Lynx 2-3-1 Garrett Arch Blythe
+ */
+void LYLeakFree(void *vp_Alloced,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ AllocationList *ALp_free;
+
+ if (LYfind_leaks == FALSE) {
+ free(vp_Alloced);
+ } else {
+
+ /*
+ * Find the pointer in the allocated list. If not found, bad pointer.
+ * If found, free list item and vp_Alloced.
+ */
+ ALp_free = FindInList(vp_Alloced);
+ if (ALp_free == NULL) {
+ /*
+ * Create the final entry before exiting marking this error. If
+ * unable to allocate more memory just exit.
+ */
+ AllocationList *ALp_new = typecalloc(AllocationList);
+
+ if (ALp_new == NULL) {
+ exit_immediately(EXIT_FAILURE);
+ }
+
+ /*
+ * Set up the information, no memory need be allocated for the file
+ * name since it is a static string.
+ */
+ ALp_new->vp_Alloced = NULL;
+ ALp_new->vp_BadRequest = vp_Alloced;
+ ALp_new->SL_memory.cp_FileName = cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = ssi_Line;
+
+ /*
+ * Add the entry to the list and then return.
+ */
+ AddToList(ALp_new);
+ } else {
+ /*
+ * Free off the memory. Take entry out of allocation list.
+ */
+ CountFrees(ALp_free->st_Bytes);
+ RemoveFromList(ALp_free);
+ FREE(ALp_free);
+ free(vp_Alloced);
+ }
+ }
+}
+
+/*
+ * Check for leaked strdup() results -TD
+ */
+char *LYLeakStrdup(const char *source,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ size_t length = strlen(source) + 1;
+ char *target = (char *) LYLeakMalloc(length, cp_File, ssi_Line);
+
+ if (target != 0) {
+ memcpy(target, source, length);
+ }
+ return target;
+}
+
+/*
+ * Allocates a new copy of a string, and returns it.
+ * Tracks allocations by using other LYLeakFoo functions.
+ * Equivalent to HTSACopy in HTString.c - KW
+ */
+char *LYLeakSACopy(char **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ if (src != NULL && src == *dest) {
+ CTRACE((tfp,
+ "LYLeakSACopy: *dest equals src, contains \"%s\"\n",
+ src));
+ } else {
+ if (*dest) {
+ LYLeakFree(*dest, cp_File, ssi_Line);
+ *dest = NULL;
+ }
+ if (src) {
+ *dest = (char *) LYLeakMalloc(strlen(src) + 1, cp_File, ssi_Line);
+ if (*dest == NULL)
+ outofmem(__FILE__, "LYLeakSACopy");
+ strcpy(*dest, src);
+ }
+ }
+ return *dest;
+}
+
+/*
+ * String Allocate and Concatenate.
+ * Tracks allocations by using other LYLeakFoo functions.
+ * Equivalent to HTSACat in HTUtils.c - KW
+ */
+char *LYLeakSACat(char **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ if (src && *src) {
+ if (src == *dest) {
+ CTRACE((tfp,
+ "LYLeakSACat: *dest equals src, contains \"%s\"\n",
+ src));
+ } else if (*dest) {
+ size_t length = strlen(*dest);
+
+ *dest = (char *) LYLeakRealloc(*dest,
+ (length + strlen(src) + 1),
+ cp_File,
+ ssi_Line);
+ if (*dest == NULL)
+ outofmem(__FILE__, "LYLeakSACat");
+ strcpy(*dest + length, src);
+ } else {
+ *dest = (char *) LYLeakMalloc((strlen(src) + 1),
+ cp_File,
+ ssi_Line);
+ if (*dest == NULL)
+ outofmem(__FILE__, "LYLeakSACat");
+ strcpy(*dest, src);
+ }
+ }
+ return *dest;
+}
+
+/******************************************************************************/
+
+/*
+ * Equivalents for bstring functions in HTString.c -TD
+ */
+/* same as HTSABAlloc */
+void LYLeakSABAlloc(bstring **dest,
+ int len,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ if (*dest == 0) {
+ *dest = LYLeakCalloc(1, sizeof(bstring), cp_File, ssi_Line);
+ }
+
+ if ((*dest)->len != len) {
+ (*dest)->str = (char *) LYLeakRealloc((*dest)->str,
+ (size_t) len,
+ cp_File,
+ ssi_Line);
+ if ((*dest)->str == NULL)
+ outofmem(__FILE__, "LYLeakSABalloc");
+
+ (*dest)->len = len;
+ }
+}
+
+/* same as HTSABCopy */
+void LYLeakSABCopy(bstring **dest,
+ const char *src,
+ int len,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ bstring *t;
+ unsigned need = (unsigned) (len + 1);
+
+ CTRACE2(TRACE_BSTRING,
+ (tfp, "HTSABCopy(%p, %p, %d)\n",
+ (void *) dest, (const void *) src, len));
+ LYLeakSABFree(dest, cp_File, ssi_Line);
+ if (src) {
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=== %4d:", len));
+ trace_bstring2(src, len);
+ CTRACE((tfp, "\n"));
+ }
+ if ((t = (bstring *) LYLeakMalloc(sizeof(bstring), cp_File, ssi_Line))
+ == NULL)
+ outofmem(__FILE__, "HTSABCopy");
+
+ if ((t->str = (char *) LYLeakMalloc(need, cp_File, ssi_Line)) == NULL)
+ outofmem(__FILE__, "HTSABCopy");
+
+ MemCpy(t->str, src, len);
+ t->len = len;
+ t->str[t->len] = '\0';
+ *dest = t;
+ }
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=> %4d:", BStrLen(*dest)));
+ trace_bstring(*dest);
+ CTRACE((tfp, "\n"));
+ }
+}
+
+/* same as HTSABCopy0 */
+void LYLeakSABCopy0(bstring **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ LYLeakSABCopy(dest, src, (int) strlen(src), cp_File, ssi_Line);
+}
+
+/* same as HTSABCat */
+void LYLeakSABCat(bstring **dest,
+ const char *src,
+ int len,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ bstring *t = *dest;
+
+ CTRACE2(TRACE_BSTRING,
+ (tfp, "HTSABCat(%p, %p, %d)\n",
+ (void *) dest, (const void *) src, len));
+ if (src) {
+ unsigned need = (unsigned) (len + 1);
+
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=== %4d:", len));
+ trace_bstring2(src, len);
+ CTRACE((tfp, "\n"));
+ }
+ if (t) {
+ unsigned length = (unsigned) t->len + need;
+
+ t->str = (char *) LYLeakRealloc(t->str, length, cp_File, ssi_Line);
+ } else {
+ if ((t = (bstring *) LYLeakCalloc(1, sizeof(bstring), cp_File,
+ ssi_Line)) == NULL)
+ outofmem(__FILE__, "HTSACat");
+
+ t->str = (char *) LYLeakMalloc(need, cp_File, ssi_Line);
+ }
+ if (t->str == NULL)
+ outofmem(__FILE__, "HTSACat");
+
+ MemCpy(t->str + t->len, src, len);
+ t->len += len;
+ t->str[t->len] = '\0';
+ *dest = t;
+ }
+ if (TRACE_BSTRING) {
+ CTRACE((tfp, "=> %4d:", BStrLen(*dest)));
+ trace_bstring(*dest);
+ CTRACE((tfp, "\n"));
+ }
+}
+
+/* same as HTSABCat0 */
+void LYLeakSABCat0(bstring **dest,
+ const char *src,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ LYLeakSABCat(dest, src, (int) strlen(src), cp_File, ssi_Line);
+}
+
+/* same as HTSABFree */
+void LYLeakSABFree(bstring **ptr,
+ const char *cp_File,
+ const short ssi_Line)
+{
+ if (*ptr != NULL) {
+ if ((*ptr)->str)
+ LYLeakFree((*ptr)->str, cp_File, ssi_Line);
+ LYLeakFree(*ptr, cp_File, ssi_Line);
+ *ptr = NULL;
+ }
+}
+
+/******************************************************************************/
+
+#if defined(LY_FIND_LEAKS) && defined(LY_FIND_LEAKS_EXTENDED)
+
+const char *leak_cp_File_hack = __FILE__;
+short leak_ssi_Line_hack = __LINE__;
+
+/*
+ * Purpose: A wrapper around StrAllocVsprintf (the workhorse of
+ * HTSprintf/HTSprintf0, implemented in HTString.c) that
+ * tries to make sure that our allocation list is always
+ * properly updated, whether StrAllocVsprintf itself was
+ * compiled with memory tracking or not (or even a mixture,
+ * like tracking the freeing but not the new allocation).
+ * Some source files can be compiled with LY_FIND_LEAKS_EXTENDED
+ * in effect while others only have LY_FIND_LEAKS in effect,
+ * and as long as HTString.c is complied with memory tracking
+ * (of either kind) string objects allocated by HTSprintf/
+ * HTSprintf0 (or otherwise) can be passed around among them and
+ * manipulated both ways.
+ * Arguments: dest As for StrAllocVsprintf.
+ * cp_File The source file of the caller (i.e. the
+ * caller of HTSprintf/HTSprintf0, hopefully).
+ * ssi_Line The line of cp_File calling.
+ * inuse,fmt,ap As for StrAllocVsprintf.
+ * Return Value: The char pointer to resulting string, as set
+ * by StrAllocVsprintf, or
+ * NULL if dest==0 (wrong use!).
+ * Remarks/Portability/Dependencies/Restrictions:
+ * The price for generality is severe inefficiency: several
+ * list lookups are done to be on the safe side.
+ * We don't get the real allocation size, only a minimum based
+ * on the string length of the result. So the amount of memory
+ * leakage may get underestimated.
+ * If *dest is an invalid pointer value on entry (i.e. was not
+ * tracked), the program will exit after one last entry is added
+ * to the allocation list.
+ * If StrAllocVsprintf fails to return a valid string via the
+ * indirect string pointer (its first parameter), invalid memory
+ * access will result and the program will probably terminate
+ * with a signal. This can happen if, on entry, *dest is NULL
+ * and fmt is empty or NULL, so just Don't Do That.
+ * Revision History:
+ * 1999-02-11 created kw
+ * 1999-10-15 added comments kw
+ */
+static char *LYLeakSAVsprintf(char **dest,
+ const char *cp_File,
+ const short ssi_Line,
+ size_t inuse,
+ const char *fmt,
+ va_list * ap)
+{
+ AllocationList *ALp_old;
+ void *vp_oldAlloced;
+
+ const char *old_cp_File = __FILE__;
+ short old_ssi_Line = __LINE__;
+
+ if (!dest)
+ return NULL;
+
+ if (LYfind_leaks == FALSE) {
+ StrAllocVsprintf(dest, inuse, fmt, ap);
+ return (*dest);
+ }
+
+ vp_oldAlloced = *dest;
+ if (!vp_oldAlloced) {
+ StrAllocVsprintf(dest, inuse, fmt, ap);
+ LYLeak_mark_malloced(*dest, strlen(*dest) + 1, cp_File, ssi_Line);
+ return (*dest);
+ } else {
+ void *vp_realloced;
+
+ ALp_old = FindInList(vp_oldAlloced);
+ if (ALp_old == NULL) {
+ /*
+ * Track the invalid pointer value and then exit. If unable to
+ * allocate, just exit.
+ */
+ AllocationList *ALp_new = typecalloc(AllocationList);
+
+ if (ALp_new == NULL) {
+ exit_immediately(EXIT_FAILURE);
+ }
+
+ /*
+ * Set the information up; no need to allocate file name since it
+ * is a static string.
+ */
+ ALp_new->vp_Alloced = NULL;
+ ALp_new->vp_BadRequest = vp_oldAlloced;
+ ALp_new->SL_realloc.cp_FileName = cp_File;
+ ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+
+ /*
+ * Add the item to the list. Exit.
+ */
+ AddToList(ALp_new);
+ exit_immediately(EXIT_FAILURE);
+ }
+
+ old_cp_File = ALp_old->SL_memory.cp_FileName;
+ old_ssi_Line = ALp_old->SL_memory.ssi_LineNumber;
+ /*
+ * DO THE REAL WORK, by calling StrAllocVsprintf. If result is not
+ * NULL, record the information.
+ */
+ StrAllocVsprintf(dest, inuse, fmt, ap);
+ vp_realloced = (void *) *dest;
+ if (vp_realloced != NULL) {
+ AllocationList *ALp_new = FindInList(vp_realloced);
+
+ if (!ALp_new) {
+ /* Look up again, list may have changed! - kw */
+ ALp_old = FindInList(vp_oldAlloced);
+ if (ALp_old == NULL) {
+ LYLeak_mark_malloced(*dest, strlen(*dest) + 1, cp_File, ssi_Line);
+ return (*dest);
+ }
+ mark_realloced(ALp_old, *dest, strlen(*dest) + 1, cp_File, ssi_Line);
+ return (*dest);
+ }
+ if (vp_realloced == vp_oldAlloced) {
+ ALp_new->SL_memory.cp_FileName = old_cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line;
+ ALp_new->SL_realloc.cp_FileName = cp_File;
+ ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+ return (*dest);
+ }
+ /* Look up again, list may have changed! - kw */
+ ALp_old = FindInList(vp_oldAlloced);
+ if (ALp_old == NULL) {
+ ALp_new->SL_memory.cp_FileName = old_cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line;
+ ALp_new->SL_realloc.cp_FileName = cp_File;
+ ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+ } else {
+ ALp_new->SL_memory.cp_FileName = old_cp_File;
+ ALp_new->SL_memory.ssi_LineNumber = old_ssi_Line;
+ ALp_new->SL_realloc.cp_FileName = cp_File;
+ ALp_new->SL_realloc.ssi_LineNumber = ssi_Line;
+ }
+ }
+ return (*dest);
+ }
+}
+
+/* Note: the following may need updating if HTSprintf in HTString.c
+ * is changed. - kw */
+static char *LYLeakHTSprintf(char **pstr, const char *fmt,...)
+{
+ char *str;
+ size_t inuse = 0;
+ va_list ap;
+
+ LYva_start(ap, fmt);
+
+ if (pstr != 0 && *pstr != 0)
+ inuse = strlen(*pstr);
+ str = LYLeakSAVsprintf(pstr, leak_cp_File_hack, leak_ssi_Line_hack,
+ inuse, fmt, &ap);
+
+ va_end(ap);
+ return str;
+}
+
+/* Note: the following may need updating if HTSprintf0 in HTString.c
+ * is changed. - kw */
+static char *LYLeakHTSprintf0(char **pstr, const char *fmt,...)
+{
+ char *str;
+ va_list ap;
+
+ LYva_start(ap, fmt);
+
+ str = LYLeakSAVsprintf(pstr, leak_cp_File_hack, leak_ssi_Line_hack,
+ 0, fmt, &ap);
+
+ va_end(ap);
+ return str;
+}
+
+/*
+ * HTSprintf and HTSprintf0 will be defined such that they effectively call one
+ * of the following two functions that store away a copy to the File & Line
+ * info in temporary hack variables, and then call the real function (which is
+ * returned here as a function pointer) to the regular HTSprintf/HTSprintf0
+ * arguments. It's probably a bit inefficient, but that shouldn't be
+ * noticeable compared to all the time that memory tracking takes up for list
+ * traversal. - kw
+ */
+HTSprintflike *Get_htsprintf_fn(const char *cp_File,
+ const short ssi_Line)
+{
+ leak_cp_File_hack = cp_File;
+ leak_ssi_Line_hack = ssi_Line;
+ return &LYLeakHTSprintf;
+}
+
+HTSprintflike *Get_htsprintf0_fn(const char *cp_File,
+ const short ssi_Line)
+{
+ leak_cp_File_hack = cp_File;
+ leak_ssi_Line_hack = ssi_Line;
+ return &LYLeakHTSprintf0;
+}
+
+#endif /* LY_FIND_LEAKS and LY_FIND_LEAKS_EXTENDED */
+#else
+/* Standard C forbids an empty file */
+void no_leak_checking(void);
+void no_leak_checking(void)
+{
+}
+#endif /* LY_FIND_LEAKS */