/* ==========================================================================
 * libarena/src/util.c - Custom Memory Allocator Interface
 * --------------------------------------------------------------------------
 * Copyright (c) 2006  William Ahern
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to permit
 * persons to whom the Software is furnished to do so, subject to the
 * following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
 * NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
 * USE OR OTHER DEALINGS IN THE SOFTWARE.
 * ==========================================================================
 */
#include <stdio.h>	/* vsnprintf(3) */
#include <stdarg.h>	/* va_list va_copy va_start va_end */

#include <string.h>	/* memchr(3) memcpy(3) strsep(3) strerror(3) */

#include <errno.h>	/* EFAULT errno */

#include "proto.h"	/* struct arena_prototype */
#include "util.h"


#ifndef va_copy
#ifdef __va_copy
#define va_copy(a, b)	__va_copy((a), (b)
#else
#define va_copy(a, b)	((a) = (b))
#endif
#endif


/*
 * Intermediate stack buffers
 */
#ifndef ARENA_UTIL_BUFSZ
#define ARENA_UTIL_BUFSZ	256
#endif


char *arena_util_strldup(const struct arena_prototype *a, const char *src, size_t srclen) {
	char *dst;

	if (!(dst = a->malloc(a,srclen + 1,1)))
		return 0;

	dst[srclen]	= '\0';

	return memcpy(dst,src,srclen);
} /* arena_util_strldup() */


char *arena_util_strndup(const struct arena_prototype *a, const char *src, size_t maxlen) {
	char *nul;
	size_t srclen;

	if ((nul = memchr(src,'\0',maxlen)))
		srclen	= nul - src;
	else
		srclen	= maxlen;

	return arena_util_strldup(a,src,srclen);
} /* arena_util_strndup() */


char *arena_util_strdup(const struct arena_prototype *a, const char *src) {
	size_t srclen	= strlen(src);
	char *dst;

	if (!(dst = a->malloc(a,srclen + 1,1)))
		return 0;

	(void)memcpy(dst,src,srclen);

	dst[srclen]	= '\0';

	return dst;
} /* arena_util_strndup() */


void *arena_util_memdup(const struct arena_prototype *a, const void *src, size_t srclen) {
	void *dst;

	if ((dst = a->malloc(a,srclen,0)))
		return memcpy(dst,src,srclen);
	else
		return 0;
} /* arena_util_memdup() */


int arena_util_vasprintf(const struct arena_prototype *a, char **dstp, const char *fmt, va_list ap) {
	char buf[ARENA_UTIL_BUFSZ];
	int len;
	va_list tap;

	*dstp	= 0;

	va_copy(tap, ap);

	len	= vsnprintf(buf,sizeof buf,fmt,tap);

	if (len < 0)
		return -1;

	if (!(*dstp = a->malloc(a,len + 1,1)))
		return -1;

	if ((size_t)len < sizeof buf)
		return memcpy(*dstp,buf,len + 1), len;

	va_copy(tap,ap);

	if (len	!= vsnprintf(*dstp,len + 1,fmt,tap)) {
		int errsav	= errno;

		a->free(a,*dstp);

		*dstp	= 0;

		errno	= errsav;

		return -1;
	}

	return len;
} /* arena_util_vasprintf() */


int arena_util_asprintf(const struct arena_prototype *a, char **dstp, const char *fmt, ...) {
	va_list ap;
	int len;

	va_start(ap,fmt);

	len	= arena_util_vasprintf(a,dstp,fmt,ap);

	va_end(ap);

	return len;
} /* arena_util_asprintf() */


char *arena_util_vsprintf(const struct arena_prototype *a, const char *fmt, va_list ap) {
	char *dst;

	if (-1 != arena_util_vasprintf(a,&dst,fmt,ap))
		return dst;
	else
		return 0;
} /* arena_util_vsprintf() */


char *arena_util_sprintf(const struct arena_prototype *a, const char *fmt, ...) {
	va_list ap;
	char *dst;

	va_start(ap,fmt);

	if (-1 == arena_util_vasprintf(a,&dst,fmt,ap))
		dst	= 0;

	va_end(ap);

	return dst;
} /* arena_util_sprintf() */


#if HAVE_STRSEP
int arena_util_split(const struct arena_prototype *a, int *argcp, char ***argvp, char **strp, const char *sep) {
	#define argc	(*argcp)
	#define argv	(*argvp)

	char *str	= *strp;
	char *tmpv[ARENA_UTIL_BUFSZ / sizeof (char *)];
	int nargv;
	char **ap;
	char *nxt;
	char *sub;

	argc	= 0;
	argv	= tmpv;
	nargv	= sizeof tmpv / sizeof *tmpv;

	if (!sep)
		sep	= " \t\r\n";

split:
	if (!(nxt = *strp = arena_util_strdup(a,str)))
		goto fail;

	for (argc = 0, ap = argv;;) {
		if (0 == (sub = strsep(&nxt,sep)))
			break;

		if (argc < nargv)
			*ap	= sub;

		if (*sub != '\0')
			ap++, argc++;
	}

	if (argc >= nargv) {
		if (argv == tmpv) {
			errno	= EFAULT;

			goto fail;
		}

		a->free(a,*strp);
		*strp	= 0;

		if (!(argv = a->malloc(a,sizeof *argv * (argc + 1),0)))
			goto fail;

		nargv	= argc;

		goto split;
	} else if (argv == tmpv) {
		if (!(argv = arena_util_memdup(a,tmpv,sizeof *tmpv * (argc + 1))))
			goto fail;
	}

	argv[argc]	= 0;

	return argc;
fail:
	if (argv != tmpv)
		a->free(a,argv), argv = 0;

	a->free(a,*strp), *strp = 0;

	return -1;

	#undef argc
	#undef argv
} /* arena_util_split() */
#endif


#if 0 && HAVE_STRSEP
int main(int argc, char **argv) {
	const struct arena_prototype *a	= ARENA_STDLIB;
	char *argbuf	= argv[1];

	if (-1 == arena_util_split(a,&argc,&argv,&argbuf,0))
		return EXIT_FAILURE;

	for (int i = 0; i < argc; i++) {
		printf("[%.3d] %s\n",i,argv[i]);
	}

	return 0;
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1