/* util.c: General utility functions not specific to install-log.
 *
 * $Id: util.c,v 1.6 2003/02/10 06:47:23 andy Exp $
 *
 * Copyright (C) 2002 Andy Goth <unununium@openverse.com>
 * For more information visit http://ioioio.net/devel/install-log/
 * 
 * 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., 59 Temple
 * Place - Suite 330, Boston, MA  02111-1307, USA. */

#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "install-log.h"

/* sprintf wrapper that can allocate memory for itself.  Prints 'fmt' and
 * later arguments to *'to', a buffer with maximum capacity *'to_cap'. */
char* safe_sprintf(char** to, int* to_cap, char* fmt, ...)
{
	char* string_buf = NULL;
	int string_cap = 0;
	va_list ap;
	int len;
	
	/* Allocate a string if necessary */
	if (to == NULL) to = &string_buf;
	if (to_cap == NULL) to_cap = &string_cap;
	if (*to == NULL) {
		*to_cap = strlen(fmt) * 2;
		*to = xmalloc(strlen(fmt) * 2);
	}

	while (1) {
		/* Generate the formatted string */
		va_start(ap, fmt);
		len = vsnprintf(*to, *to_cap, fmt, ap);
		va_end(ap);

		/* Resize and repeat if necessary */
		if (len > -1 && len < *to_cap) return *to;
		if (len > -1) *to_cap = len + 1;
		else *to_cap *= 2;
		free(*to);
		*to = xmalloc(*to_cap);
	}
}

/* Replaces 'from' with 'to' in *'context'; *'context_cap' is the capacity. */
void replace(char** context, int* context_cap, char* from, char* to)
{
	int from_len = strlen(from);
	int to_len = strlen(to);
	char* context_p;

	/* Easiest case (whew!) */
	if (from_len == to_len) {
		while ((context_p = strstr(context_p, from)) != NULL) {
			memcpy(context_p, to, to_len);
			context_p += to_len;
		}
		return;
	}

	/* Hardest case (aaugh): allocate enough memory */
	if (to_len > from_len) {
		int diff = to_len - from_len;
		int count = 0;
		int len = 1;

		/* Calculate length of final string */
		context_p = *context;
		while (strstr(context_p, from) != NULL) {
			len += diff;
			context_p += from_len;
		}

		/* Ensure there is enough room */
		if (len > *context_cap) {
			*context_cap = len;
			*context = xrealloc(*context, *context_cap);
		}
	}
	
	/* Replace from with to */
	context_p = *context;
	while ((context_p = strstr(context_p, from)) != NULL) {
		memmove(context_p + to_len, context_p + from_len,
				strlen(context_p + from_len) + 1);
		memcpy(context_p, to, to_len);
		context_p += to_len;
	}
}

/* Removes repetitions of 'c' in 'string'. */
void collapse(char* string, char c)
{
	char rep[3];
	char* string_p = string;

	/* Build search string */
	rep[0] = rep[1] = c;
	rep[2] = 0;

	/* Replace search string with c */
	while ((string_p = strstr(string_p, rep)) != NULL)
		memmove(string_p, string_p + 1,	strlen(string_p + 1) + 1);
}

/* Outputs a printf-style message. */
void alert(const char* format, ...)
{
	va_list ap;
	va_start(ap, format);
	fprintf(stderr, "%s: ", program_name);
	vfprintf(stderr, format, ap);
	va_end(ap);
}

/* Outputs a printf-style message and exit. */
void fail(const char* format, ...)
{
	va_list ap;
	va_start(ap, format);
	fprintf(stderr, "%s: ", program_name);
	vfprintf(stderr, format, ap);
	va_end(ap);
	exit(EXIT_FAILURE);
}

/* Depending on verbosity level, outputs a printf-style message. */
void report(int level, const char* format, ...)
{
	va_list ap;
	if (level > verbosity) return;
	va_start(ap, format);
	vprintf(format, ap);
	va_end(ap);
}

/* Calls malloc and dies on failure. */
void* xmalloc(size_t size)
{
	void* ret = malloc(size);
	if (ret == NULL) fail("Out of memory\n");
	return ret;
}

/* Calls realloc and dies on failure. */
void* xrealloc(void* ptr, size_t size)
{
	void* ret = realloc(ptr, size);
	if (ret == NULL) fail("Out of memory\n");
	return ret;
}

/* EOF */

