/**
 * Copyright (C) 2007 iptego GmbH
 *
 * This file is part of ser, a free SIP server.
 *
 * ser 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
 *
 * ser 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
 */

#ifndef __ASI_STRUTIL_H__
#define __ASI_STRUTIL_H__

#include <string.h>
#include <stdlib.h>
#include <errno.h>

#include "mem/mem.h"
#include "mem/shm_mem.h"
#include "str.h"

#define STR_EQ(_a_, _b_) \
	(((_a_)->len == (_b_)->len) && \
	(strncmp((_a_)->s, (_b_)->s, (_b_)->len) == 0))

//strclone_shm
enum STR_CLONE_FL {
	STR_CLONE_PKG	= 1 << 0, /* alloc in private mem */
	STR_CLONE_SHM	= 1 << 1, /* alloc in private mem */
	STR_CLONE_TERM	= 1 << 2, /* add 0-terminator to clone */
	STR_CLONE_TRIM	= 1 << 3, /* strip 0-terminator from clone */
};

#define _chr_clone(FUNC, _src_, _dst_, _alloc, _copy) \
	do { \
		_dst_ = (char *)FUNC(_alloc * sizeof(char)); \
		if (! (_dst_)) { \
			errno = ENOMEM; \
			_dst_ = NULL; \
		} else { \
			memcpy(_dst_, _src_, _copy); \
		} \
	} while (0)

static inline char *_chr_clone_pkg(char *orig, size_t alloc, size_t copy)
{
	char *dup;
	_chr_clone(pkg_malloc, orig, dup, alloc, copy);
	return dup;
}

static inline char *_chr_clone_shm(char *orig, size_t alloc, size_t copy)
{
	char *dup;
	_chr_clone(shm_malloc, orig, dup, alloc, copy);
	return dup;
}

static inline int strclo(char *orig, size_t olen, str *dup, unsigned flags)
{
	size_t alloc, copy;

#define init_alloc_copy(_val)	copy = alloc = _val
#define init_alloc(_val)		alloc = _val
#define clone_str(FUNC) \
	do { \
		init_alloc(olen); \
		if (! (dup->s = FUNC(orig, alloc, alloc))) {  \
			errno = ENOMEM; \
			return -1; \
		} \
	} while (0)
#define clone_term(FUNC) \
	do { \
		init_alloc_copy(olen); \
		alloc ++; \
		if (! (dup->s = FUNC(orig, alloc, copy))) { \
			errno = ENOMEM; \
			return -1; \
		} \
		dup->s[copy] = 0; \
	} while (0)
#define clone_trim(FUNC) \
	do { \
		init_alloc(olen - 1); \
		if (! (dup->s = FUNC(orig, alloc, alloc))) { \
			errno = ENOMEM; \
			return -1; \
		} \
	} while (0)

	switch (flags) {
		case STR_CLONE_PKG: clone_str(_chr_clone_pkg); break;
		case STR_CLONE_PKG | STR_CLONE_TERM: clone_term(_chr_clone_pkg); break;
		case STR_CLONE_PKG | STR_CLONE_TRIM: clone_trim(_chr_clone_pkg); break;

#if defined(SHM_MEM) && defined(USE_SHM_MEM)
		case STR_CLONE_SHM: clone_str(_chr_clone_shm); break;
		case STR_CLONE_SHM | STR_CLONE_TERM: clone_term(_chr_clone_shm); break;
		case STR_CLONE_SHM | STR_CLONE_TRIM: clone_trim(_chr_clone_shm); break;
#endif

		default:
			errno = EINVAL;
			return -1;
	}
	dup->len = alloc;
	return 0;
#undef init_alloc_copy
#undef init_alloc
#undef clone_str
#undef clone_term
#undef clone_trim
}

static inline int strdclo(char *orig, size_t olen, str **dup, unsigned flags)
{
	switch (flags) {
		case STR_CLONE_PKG:
		case STR_CLONE_PKG | STR_CLONE_TERM:
		case STR_CLONE_PKG | STR_CLONE_TRIM:
			if (! (dup = (str **)pkg_malloc(sizeof(str *)))) {
				errno = ENOMEM;
				return -1;
			}
			if (strclo(orig, olen, *dup, flags) < 0) {
				pkg_free(dup);
				return -1;
			}
			break;

#if defined(SHM_MEM) && defined(USE_SHM_MEM)
		case STR_CLONE_SHM:
		case STR_CLONE_SHM | STR_CLONE_TERM:
		case STR_CLONE_SHM | STR_CLONE_TRIM:
			if (! (dup = (str **)shm_malloc(sizeof(str *)))) {
				errno = ENOMEM;
				return -1;
			}
			if (strclo(orig, olen, *dup, flags) < 0) {
				shm_free(dup);
				return -1;
			}
			break;
#endif
		default:
			errno = EINVAL;
			return -1;
	}
	return 0;
}

#endif /*__ASI_STRUTIL_H__ */
