/*
 *	support.c - support functions
 *	Copyright (C) 2000-2003 Fred Barnes <frmb2@ukc.ac.uk>
 *
 *	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 <stdio.h>
#include <string.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_MALLOC_H
#include <malloc.h>
#endif
#include <sys/time.h>
#include <netinet/in.h>
#include <termios.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#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;
}



syntax highlighted by Code2HTML, v. 0.9.1