/*
 * $Id$
 *
 * Code (c)2006 Bruno Cornec <bruno@mondorescue.org>
 *
 *     Main file of mr_mem : a very small and simple
 *     library for memory management
 *
 * Provided under the GPLv2
 */

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>

#include "mr_err.h"
#include "mr_msg.h"

/*
 * Function that frees memory if necessary
 * A pointer to the memory pointed is passed to it.
 * *allocated variable points then to the original content 
 * pointed to by the caller
 * In case of NULL pointer it just logs it
 */
void mr_free_int(void **allocated, int line, const char *file) {

	/* free man pages says that if allocated is NULL 
	 * nothing happens 
	 */
	if (*allocated != NULL) {
		free(*allocated);
		*allocated = NULL;
	} else {
		mr_msg_int(8,line,file,"Attempt to free NULL pointer");
	}
}

/* encapsulation function for malloc */
void *mr_malloc_int(size_t size, int line, const char *file) {
	
	void *ret;

	ret = malloc(size);
	if (ret == NULL) {
		mr_msg_int(1,line,file,"Unable to alloc memory in mr_malloc\nExiting...");
		mr_exit(-1,"Unable to alloc memory in mr_malloc");
	}
	return(ret);
}

/* encapsulation function for asprintf */
void mr_asprintf_int(char **strp, int line, const char *file, const char *fmt, ...) {

	int res = 0;
	va_list args;

	va_start(args,fmt);
	res = vasprintf(strp, fmt, args);
	if (res == -1) {
		mr_msg_int(1,line,file,"Unable to alloc memory in mr_asprintf\nExiting...");
		mr_exit(-1,"Unable to alloc memory in mr_asprintf");
	}
	va_end(args);
}

/* encapsulation function for getline */
void mr_getline_int(char **lineptr, FILE *fd, int line, const char *file) {
	
	ssize_t ret;
	size_t n = 0;

	ret = getline(lineptr,&n,fd);
	if ((ret == -1) && (! feof(fd))) {
		mr_msg_int(1,line,file,"Unable to alloc memory in mr_getline\nExiting...");
		mr_exit(-1,"Unable to alloc memory in mr_getline");
	}
	/*  We reached end of file, allocating empty string */
	if (ret == -1) {
		mr_asprintf_int(lineptr, line, file, "");
	}
}

/*
 * Function that properly allocates a string from another one
 * freeing it before in any case
 */
void mr_allocstr_int(char *alloc, const char *orig, int line, const char *file) {

	if (alloc != NULL) {
		mr_free_int((void **)&alloc, line, file); 
	}
	mr_asprintf_int(&alloc, line, file, orig);
}

/* 
 * Function that properly put a variable in the environment
 */
void mr_setenv_int(const char *name, const char *value, int line, char *file) {
	
	if (name == NULL) {
		mr_msg_int(1,line,file,"Unable to setenv a NULL variable\nExiting...");
		mr_exit(-1, "Unable to setenv a NULL variable");
	}
	if (value == NULL) {
		mr_msg_int(1,line,file,"Unable to affect NULL to %s\nExiting...", name);
		mr_exit(-1, "Unable to affect a NULL variable");
	}
	if (setenv(name, value, 1) != 0) {
		mr_msg_int(1,line,file,"Unable to put %s in environment", name);
		mr_exit(-1,"Unable to put in environment");
	}
}

/*
 * Equivalent function of strcat but safe 
 * from memory allocation point of view
 * and richer from an interface point of view
 */
void mr_strcat_int(char **in, int line, const char *file, const char *fmt, ...) {
	char *fmt2 = NULL;
	va_list args;
	int res = 0;
	
	if (fmt == NULL) {
		return;
	}
	if (in == NULL) {
		mr_msg_int(1,line,file,"Unable to add to NULL pointer\nExiting...");
		mr_exit(-1, "Unable to add to a NULL pointer");
	}
	va_start(args,fmt);
	if (*in == NULL) {
		res = vasprintf(in, fmt, args);
		if (res == -1) {
			mr_msg_int(1,line,file,"Unable to alloc memory in mr_strcat\nExiting...");
			mr_exit(-1,"Unable to alloc memory in mr_strcat");
		}
	} else {
		mr_asprintf_int(&fmt2, line, file, "%s%s", *in, fmt);
		mr_free_int((void **)in,line,file);
		res = vasprintf(in, fmt2, args);
		if (res == -1) {
			mr_msg_int(1,line,file,"Unable to alloc memory in mr_strcat\nExiting...");
			mr_exit(-1,"Unable to alloc memory in mr_strcat");
		}
		mr_free_int((void **)&fmt2,line,file);
	}
	va_end(args);
}


