/*
** utils.c -- various utility routines
**
** This code is Copyright (c) 2006, by the authors of nmh.  See the
** COPYRIGHT file in the root directory of the nmh distribution for
** complete copyright information.
*/

#include <h/mh.h>
#include <h/utils.h>
#include <stdlib.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sysexits.h>

/*
** We allocate space for messages (msgs array)
** this number of elements at a time.
*/
#define MAXMSGS 256

/*
** Safely call realloc
*/
void *
mh_xrealloc(void *ptr, size_t size)
{
	void *memory;

	/* Some non-POSIX realloc()s don't cope with realloc(NULL,sz) */
	if (!ptr) {
		return mh_xcalloc(1, size);
	}
	if (size == 0) {
		adios(EX_SOFTWARE, NULL, "Tried to realloc 0 bytes");
	}

	memory = realloc(ptr, size);
	if (!memory) {
		adios(EX_OSERR, NULL, "Realloc failed");
	}

	return memory;
}

/*
** Safely call calloc
*/
void *
mh_xcalloc(size_t nmemb, size_t size)
{
	void *memory;

	if (nmemb == 0  ||  size == 0) {
		adios(EX_SOFTWARE, NULL, "Tried to calloc 0 bytes");
	}

	if ((memory = calloc(nmemb, size))) {
		return memory;
	} else {
		adios(EX_OSERR, NULL, "calloc failed");
	}
}

/*
** Free a pointer and set it to NULL.
*/
void
mh_free0(void * ptr)
{
	void ** p;
	p = ptr;
	free(*p);
	*p = NULL;
}

/*
** Return the present working directory, if the current directory does not
** exist, or is too long, make / the pwd.
*/
char *
pwd(void)
{
	char *cp;
	static char curwd[PATH_MAX];

	if (!getcwd(curwd, PATH_MAX)) {
		admonish(NULL, "unable to determine working directory");
		if (!mypath || !*mypath ||
				(strcpy(curwd, mypath), chdir(curwd)) == -1) {
			strcpy(curwd, "/");
			chdir(curwd);
		}
		return curwd;
	}

	if ((cp = curwd + strlen(curwd) - 1) > curwd && *cp == '/') {
		/* strip trailing slash */
		*cp = '\0';
	}

	return curwd;
}

/*
** add   -- If "s1" is NULL, this routine just creates a
**       -- copy of "s2" into newly malloc'ed memory.
**       -- (use mh_xstrdup() instead in this case)
**       --
**       -- If "s1" is not NULL, then copy the concatenation
**       -- of "s1" and "s2" (note the order) into newly
**       -- malloc'ed memory.  Then free "s1".
*/
char *
add(const char *s2, char *s1)
{
	char *cp;
	size_t len1 = 0, len2 = 0;

	if (s1) {
		len1 = strlen(s1);
	}
	if (s2) {
		len2 = strlen(s2);
	}

	cp = mh_xcalloc(len1 + len2 + 1, sizeof(char));

	/* Copy s1 and free it */
	if (s1) {
		memcpy(cp, s1, len1);
		mh_free0(&s1);
	}

	/* Copy s2 */
	if (s2) {
		memcpy(cp + len1, s2, len2);
	}

	/* Now NULL terminate the string */
	cp[len1 + len2] = '\0';

	return cp;
}


/*
** create_folder
**     Check to see if a folder exists, if not, prompt the user to create it.
*/
void
create_folder(char *folder, int autocreate, void (*done_callback)(int))
{
	struct stat st;
	extern int errno;
	char *cp;

	if (stat(folder, &st) == -1) {
		if (errno != ENOENT) {
			adios(EX_IOERR, folder, "error on folder");
		}
		if (autocreate == 0) {
			/* ask before creating folder */
			cp = concat("Create folder \"", folder, "\"? ", NULL);
			if (!getanswer(cp)) {
				done_callback(EX_CANTCREAT);
			}
			mh_free0(&cp);
		} else if (autocreate == -1) {
			/* do not create, so exit */
			done_callback(EX_CANTCREAT);
		}
		if (!makedir(folder)) {
			adios(EX_CANTCREAT, NULL, "unable to create folder %s", folder);
		}
	}
}

/*
** num_digits
**     Return the number of digits in a nonnegative integer.
*/
int
num_digits(int n)
{
	int ndigits = 0;

	/* Sanity check */
	if (n < 0) {
		adios(EX_SOFTWARE, NULL, "oops, num_digits called with negative value");
	}

	if (n == 0) {
		return 1;
	}

	while (n) {
		n /= 10;
		ndigits++;
	}

	return ndigits;
}

/*
** Append a message arg to an array of them, resizing it if necessary.
** The function is written to suit the arg parsing code it was extracted
** from, and will probably be changed when the other code is cleaned up.
*/
void
app_msgarg(struct msgs_array *msgs, char *cp)
{
	if(msgs->size >= msgs->max) {
		msgs->msgs = mh_xrealloc(msgs->msgs,
				(msgs->max+=MAXMSGS)*sizeof(*msgs->msgs));
	}
	msgs->msgs[msgs->size++] = cp;
}

/*
** mh_xstrdup() is a wrapper of strdup() to replace getcpy().  It returns
** a copy of its argument if this is nonnull; otherwise, it returns a
** string of length 0.
*/
char *
mh_xstrdup(const char * s)
{
	char * tmp;
	tmp = strdup(s ? s : "");
	if (!tmp) {
		adios(EX_OSERR, "strdup", "can't copy string");
	}
	return tmp;
}
