#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <stdarg.h>

#include "setup.h"

struct safe_allocation {
	size_t	size;
	char	data[1];
};

static char *error;

void set_error_va(const char *fmt, va_list va)
{
	int bufsize;

	bufsize = fmt ? (strlen(fmt) + 1) : 0;
	if (bufsize < 128)
		bufsize = 128;

	if (error)
		free(error);
	if (!fmt) {
		error = NULL;
		return;
	}

	error = malloc(bufsize);

	for(;;) {
		int n;

		n = vsnprintf(error, bufsize, fmt, va);

		if (n >= 0 && n < bufsize)
			break;

		if (n >= 0)
			bufsize = n + 1;
		else
			bufsize *= 2;

		error = realloc(error, bufsize);
	}
}

void set_error(const char *fmt, ...)
{
	va_list va;

	va_start(va, fmt);
	set_error_va(fmt, va);
	va_end(va);
}

const char *get_error(void)
{
	return error;
}

void *safe_alloc(size_t size)
{
	struct safe_allocation *alloc;

	if (!size)
		return NULL;

	alloc = malloc(size + offsetof(struct safe_allocation, data));
	if (!alloc)
		return NULL;

	alloc->size = size;

	return &alloc->data;
}

void safe_free(void *data)
{
	struct safe_allocation *alloc;

	if (!data)
		return;

	alloc = data - offsetof(struct safe_allocation, data);

	memset(data, 0, alloc->size);

	alloc->size = 0x55aa55aa;
	free(alloc);
}

void *safe_realloc(void *data, size_t size)
{
	void *new_data;

	new_data = safe_alloc(size);

	if (new_data && data) {
		struct safe_allocation *alloc;

		alloc = data - offsetof(struct safe_allocation, data);

		if (size > alloc->size)
			size = alloc->size;

		memcpy(new_data, data, size);
	}

	safe_free(data);
	return new_data;
}

char *safe_strdup(const char *s)
{
	char *s2 = safe_alloc(strlen(s) + 1);

	if (!s2)
		return NULL;

	return strcpy(s2, s);
}
