/* * support.c - support functions * Copyright (C) 2000-2003 Fred Barnes * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #ifdef HAVE_STDLIB_H #include #endif #ifdef HAVE_MALLOC_H #include #endif #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include "support.h" #include "openupsd.h" #ifdef TRACE_MEMORY #define SS_FILE_SIZE 64 typedef struct TAG_ss_memblock { struct TAG_ss_memblock *next, *prev; void *ptr; unsigned int vptr; size_t size; int line; char file[SS_FILE_SIZE]; } ss_memblock; static ss_memblock *ss_head = NULL; static ss_memblock *ss_tail = NULL; static int ss_numalloc = 0; static int ss_numfree = 0; static int ss_numrealloc = 0; static int ss_numhardrealloc = 0; /* * void ss_cleanup (void) * called on program exit to note what didn't get cleaned up */ void ss_cleanup (void) { ss_memblock *tmpblk; fprintf (stderr, "%d blocks allocated\n", ss_numalloc); fprintf (stderr, "%d blocks freed\n", ss_numfree); fprintf (stderr, "%d blocks re-allocated\n", ss_numrealloc); fprintf (stderr, "%d blocks coppied for re-allocation\n", ss_numhardrealloc); fprintf (stderr, "left-over memory blocks:\n"); for (tmpblk = ss_head; tmpblk; tmpblk = tmpblk->next) { fprintf (stderr, "0x%-8x %-8d %s:%d\n", tmpblk->vptr, tmpblk->size, tmpblk->file, tmpblk->line); } return; } /* * void ss_insert_blk (ss_memblock *blk) * inserts a memory block in the list */ void ss_insert_blk (ss_memblock *blk) { ss_memblock *tb; if (!ss_head && !ss_tail) { ss_head = ss_tail = blk; } else if (!ss_head || !ss_tail) { fprintf (stderr, "%s: fatal: ss_head = %p, ss_tail = %p\n", progname, ss_head, ss_tail); _exit (1); } else { for (tb=ss_head; tb && (tb->vptr < blk->vptr); tb = tb->next); if (!tb) { /* insert at end */ blk->prev = ss_tail; ss_tail->next = blk; ss_tail = blk; } else if (!tb->prev) { /* insert at start */ blk->next = ss_head; ss_head->prev = blk; ss_head = blk; } else { /* insert before `tb' */ blk->prev = tb->prev; blk->next = tb; tb->prev->next = blk; tb->prev = blk; } } return; } /* * void ss_remove_blk (ss_memblock *blk) * removes (disconnects) a memory block from the list */ void ss_remove_blk (ss_memblock *blk) { if (blk->prev && blk->next) { blk->prev->next = blk->next; blk->next->prev = blk->prev; } else if (!blk->prev) { ss_head = blk->next; ss_head->prev = NULL; } else if (!blk->next) { ss_tail = blk->prev; ss_tail->next = NULL; } else if (!blk->prev && !blk->next) { ss_head = ss_tail = NULL; } return; } #endif /* TRACE_MEMORY */ /* * void *smalloc (size_t) * allocates some memory */ #ifdef TRACE_MEMORY void *ss_malloc (char *file, int line, size_t length) #else void *smalloc (size_t length) #endif { void *tmp; tmp = malloc (length); if (!tmp) { fprintf (stderr, "%s: out of memory!\n", progname); _exit (1); } memset (tmp, 0, length); #ifdef TRACE_MEMORY { ss_memblock *tmpblk; ss_numalloc++; tmpblk = (ss_memblock *)malloc (sizeof (ss_memblock)); if (!tmpblk) { fprintf (stderr, "%s: out of memory!\n", progname); _exit (1); } tmpblk->prev = tmpblk->next = NULL; tmpblk->ptr = tmp; tmpblk->vptr = (unsigned int)tmp; tmpblk->size = length; strncpy (tmpblk->file, file, SS_FILE_SIZE); tmpblk->line = line; ss_insert_blk (tmpblk); } #endif /* TRACE_MEMORY */ return tmp; } /* * void *srealloc (void *, size_t, size_t) * re-allocates a memory block, moving it entirely if necessary */ #ifdef TRACE_MEMORY void *ss_realloc (char *file, int line, void *ptr, size_t old_size, size_t new_size) #else void *srealloc (void *ptr, size_t old_size, size_t new_size) #endif { void *tmp; if (!ptr || !old_size) { tmp = smalloc (new_size); } else { tmp = realloc (ptr, new_size); if (!tmp) { tmp = smalloc (new_size); if (new_size > old_size) { memcpy (tmp, ptr, old_size); } else { memcpy (tmp, ptr, new_size); } sfree (ptr); #ifdef TRACE_MEMORY ss_numhardrealloc++; #endif /* TRACE_MEMORY */ } else { #ifdef TRACE_MEMORY ss_memblock *tmpblk; /* relocate block */ ss_numrealloc++; for (tmpblk=ss_head; tmpblk; tmpblk = tmpblk->next) { if (tmpblk->ptr == ptr) { break; } } if (!tmpblk) { fprintf (stderr, "%s: serious: attempt to srealloc() non-allocated memory (%p) in %s:%d\n", progname, ptr, file, line); } else { ss_remove_blk (tmpblk); if (tmpblk->size != old_size) { fprintf (stderr, "%s: serious: inconsistency in old_size (specified %d, allocated %d) (%p) in %s:%d\n", progname, old_size, tmpblk->size, ptr, file, line); fprintf (stderr, "%s: serious: memory was allocated in %s:%d\n", progname, tmpblk->file, tmpblk->line); } tmpblk->prev = tmpblk->next = NULL; tmpblk->ptr = tmp; tmpblk->vptr = (unsigned int)tmp; tmpblk->size = new_size; ss_insert_blk (tmpblk); } #endif /* TRACE_MEMORY */ } if (new_size > old_size) { memset (tmp + old_size, 0, new_size - old_size); } } return tmp; } /* * void sfree (void *ptr) * frees previously allocated memory */ #ifdef TRACE_MEMORY void ss_free (char *file, int line, void *ptr) #else void sfree (void *ptr) #endif { if (ptr) { #ifdef TRACE_MEMORY ss_memblock *tmpblk; ss_numfree++; for (tmpblk = ss_head; tmpblk; tmpblk = tmpblk->next) { if (tmpblk->ptr == ptr) { break; } } if (!tmpblk) { fprintf (stderr, "%s: serious: attempt to sfree() non-allocated memory (%p) in %s:%d\n", progname, ptr, file, line); } else { ss_remove_blk (tmpblk); free (tmpblk); } #endif free (ptr); } return; } /* * char *string_ndup (char *, int) * duplicates a chunk of string */ char *string_ndup (char *str, int length) { char *tmp; tmp = (char *)smalloc (length + 1); memcpy (tmp, str, length); tmp[length] = '\0'; return tmp; } /* * char *string_dup (char *) * duplicates a string */ char *string_dup (char *str) { return string_ndup (str, strlen (str)); } /* * void *mem_ndup (void *, int) * duplicates a bit of memory */ void *mem_ndup (void *ptr, int length) { void *tmp; tmp = smalloc (length); memcpy (tmp, ptr, length); return tmp; } /* * void da_init (int *cur, int *max, void ***array) * initialises a dynamic array */ void da_init (int *cur, int *max, void ***array) { *cur = 0; *max = 0; *array = NULL; return; } /* * void da_additem (int *cur, int *max, void ***array, void *item) * adds an item to a dynamic array (at the end of it) */ void da_additem (int *cur, int *max, void ***array, void *item) { if (*max == 0) { *array = (void **)smalloc (8 * sizeof (void *)); *max = 8; } else if (*cur == *max) { *array = (void **)srealloc ((void *)(*array), *max * sizeof(void *), (*max + 8) * sizeof(void *)); *max = *max + 8; } (*array)[*cur] = item; *cur = *cur + 1; return; } /* * int da_maybeadditem (int *cur, int *max, void ***array, void *item) * adds an item to a dynamic array, but only if it's not there already */ int da_maybeadditem (int *cur, int *max, void ***array, void *item) { int idx; for (idx = 0; idx < *cur; idx++) { if ((*array)[idx] == item) { return 0; } } da_additem (cur, max, array, item); return 1; } /* * void da_delitem (int *cur, int *max, void ***array, int idx) * removes an item from a dynamic array, at a specified index */ void da_delitem (int *cur, int *max, void ***array, int idx) { if (idx >= *cur) { fprintf (stderr, "%s: fatal: item at index %d in array at %p (%d,%d) does not exist!\n", progname, idx, *array, *cur, *max); exit (EXIT_FAILURE); } if (idx == (*cur - 1)) { *cur = *cur - 1; } else { void **walk; int i = (*cur - idx) - 1; for (walk = (*array) + idx; i > 0; walk++, i--) { walk[0] = walk[1]; } *cur = *cur - 1; } return; } /* * void da_rmitem (int *cur, int *max, void ***array, void *item) * removes an item from a dynamic array, based on its value * (scans array and calls da_delitem) */ void da_rmitem (int *cur, int *max, void ***array, void *item) { int idx; for (idx = 0; idx < *cur; idx++) { if ((*array)[idx] == item) { da_delitem (cur, max, array, idx); idx--; } } return; } /* * void da_trash (int *cur, int *max, void ***array) * trashes a dynamic array and resets it to zero. doesn't do anything with any contents */ void da_trash (int *cur, int *max, void ***array) { if (*max && *array) { sfree (*array); } *array = NULL; *cur = 0; *max = 0; return; }