/*
 * xcommon.c - various functions put together to avoid basic error checking.
 *
 * added fcntl locking by Kjetil T. (kjetilho@math.uio.no) - aeb, 950927
 *
 * 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
 * - added Native Language Support
 *
 * 2006-06-06 Amit Gud <agud@redhat.com>
 * - Moved code snippets here from mount/sundries.c of util-linux
 *   and merged code from support/nfs/xmalloc.c by Olaf Kirch <okir@monad.swb.de> here.
 */

#include <unistd.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "xcommon.h"
#include "nls.h"	/* _() */

void (*at_die)(void ) = NULL;

char *
xstrndup (const char *s, int n) {
     char *t;

     if (s == NULL)
	  die (EX_SOFTWARE, _("bug in xstrndup call"));

     t = xmalloc(n+1);
     strncpy(t,s,n);
     t[n] = 0;

     return t;
}

char *
xstrconcat2 (const char *s, const char *t) {
     char *res;

     if (!s) s = "";
     if (!t) t = "";
     res = xmalloc(strlen(s) + strlen(t) + 1);
     strcpy(res, s);
     strcat(res, t);
     return res;
}

/* frees its first arg - typical use: s = xstrconcat3(s,t,u); */
char *
xstrconcat3 (const char *s, const char *t, const char *u) {
     char *res;

     int dofree = 1;

     if (!s) s = "", dofree=0;
     if (!t) t = "";
     if (!u) u = "";
     res = xmalloc(strlen(s) + strlen(t) + strlen(u) + 1);
     strcpy(res, s);
     strcat(res, t);
     strcat(res, u);
     if (dofree)
         free((void *) s);
     return res;
}

/* frees its first arg - typical use: s = xstrconcat4(s,t,u,v); */
char *
xstrconcat4 (const char *s, const char *t, const char *u, const char *v) {
     char *res;

     int dofree = 1;

     if (!s) s = "", dofree=0;
     if (!t) t = "";
     if (!u) u = "";
     if (!v) v = "";
     res = xmalloc(strlen(s) + strlen(t) + strlen(u) + strlen(v) + 1);
     strcpy(res, s);
     strcat(res, t);
     strcat(res, u);
     strcat(res, v);
     if (dofree)
         free((void *) s);
     return res;
}

/* Non-fatal error.  Print message and return.  */
/* (print the message in a single printf, in an attempt
    to avoid mixing output of several threads) */
void
nfs_error (const char *fmt, ...) {
     va_list args;
     char *fmt2;

     fmt2 = xstrconcat2 (fmt, "\n");
     va_start (args, fmt);
     vfprintf (stderr, fmt2, args);
     va_end (args);
     free (fmt2);
}

/* Make a canonical pathname from PATH.  Returns a freshly malloced string.
   It is up the *caller* to ensure that the PATH is sensible.  i.e.
   canonicalize ("/dev/fd0/.") returns "/dev/fd0" even though ``/dev/fd0/.''
   is not a legal pathname for ``/dev/fd0''.  Anything we cannot parse
   we return unmodified.   */
char *canonicalize (const char *path) {
	char canonical[PATH_MAX+2];

	if (path == NULL)
		return NULL;

#if 1
	if (streq(path, "none") ||
	    streq(path, "proc") ||
	    streq(path, "devpts"))
		return xstrdup(path);
#endif
	if (realpath (path, canonical))
		return xstrdup(canonical);

	return xstrdup(path);
}

/* Fatal error.  Print message and exit.  */
void
die(int err, const char *fmt, ...) {
	va_list args;

	va_start(args, fmt);
	vfprintf(stderr, fmt, args);
	fprintf(stderr, "\n");
	va_end(args);

	if (at_die)
		(*at_die)();

	exit(err);
}

static void
die_if_null(void *t) {
	if (t == NULL)
		die(EX_SYSERR, _("not enough memory"));
}

void *
xmalloc (size_t size) {
	void *t;

	if (size == 0)
		return NULL;

	t = malloc(size);
	die_if_null(t);

	return t;
}

void *
xrealloc (void *p, size_t size) {
	void *t;

	t = realloc(p, size);
	die_if_null(t);

	return t;
}

void
xfree(void *ptr)
{
	free(ptr);
}

char *
xstrdup (const char *s) {
	char *t;

	if (s == NULL)
		return NULL;

	t = strdup(s);
	die_if_null(t);

	return t;
}
