/*
** iksemel (XML Parser for Jabber)
** Copyright (C) 2000-2001 Gurer Ozen <palpa@jabber.org>
**
** This code is free software; you can redistribute it and/or
** modify it under the terms of GNU Lesser General Public License.
**
** memory and string utility functions
*/

#include "common.h"
#include "iksemel.h"

/*****  Memory Pools  *****/

ikspool *iks_pool_new(int pagesize)
{
	ikspool *p;

	p = malloc(sizeof(ikspool));
	if(!p) return(NULL);

	p->data = malloc(pagesize);
	if(!p->data)
	{
		free(p);
		return(NULL);
	}

	p->next = NULL;
	p->max = pagesize;
	p->len = 0;

	return p;
}


void *iks_pool_alloc(ikspool *p, int size)
{
	void *mem;
	int i;

	i = size & 8;
	if(i) size += (8 - i);
	i = p->max;

	if(size > i)
	{
		while(p->next) p=p->next;
		p->next = iks_pool_new(size);
		p = p->next;
		if(!p) return(NULL);
		p->len = size;
		return p->data;
	}

	while(p)
	{
		if((p->max - p->len) >= size)
		{
			mem = p->data + p->len;
			p->len += size;
			return mem;
		}

		if(p->next)
			p = p->next;
		else
			p->next = iks_pool_new(i);
	}

	return NULL;
}


char *iks_pool_strdup(ikspool *p, const char *src)
{
	char *dest;
	int len;

	if(!src) return(NULL);

	len = strlen(src);
	if(len == 0) return(NULL);
	dest = iks_pool_alloc(p, len + 1);
	if(!dest) return(NULL);
	memcpy(dest, src, len);
	dest[len] = '\0';

	return dest;
}


void iks_pool_delete(ikspool *p)
{
	ikspool *tmp;

	tmp = p;
	while(tmp)
	{
		tmp = p->next;
		free(p->data);
		free(p);
		p = tmp;
	}
}


/*****  NULL-safe Functions  *****/

char *iks_strdup(const char *src)
{
	if(src) return(strdup(src));
	return NULL;
}


char *iks_strcat(char *dest, const char *src)
{
	if(!src) return(dest);

	while(*src) *dest++ = *src++;
	*dest = '\0';

	return dest;
}


int iks_strcmp(const char *a, const char *b)
{
	if(!a || !b) return(-1);
	return strcmp(a, b);
}


int iks_strcasecmp(const char *a, const char *b)
{
	if(!a || !b) return(-1);
	return strcasecmp(a, b);
}


int iks_strncmp(const char *a, const char *b, int n)
{
	if(!a || !b) return(-1);
	return strncmp(a, b, n);
}


int iks_strncasecmp(const char *a, const char *b, int n)
{
	if(!a || !b) return(-1);
	return strncasecmp(a, b, n);
}


int iks_strlen(const char *src)
{
	if(!src) return(0);
	return (int)strlen(src);
}


/*****  String Stuff  *****/

ikstr *iks_str_new(ikspool *p)
{
	ikstr *s;
	int flag = 0;

	if(!p)
	{
		p = iks_pool_new(512);
		if(!p) return(NULL);
		flag = 1;
	}

	s = iks_pool_alloc(p, sizeof(ikstr));
	if(!s)
	{
		if(flag) iks_pool_delete(p);
		return NULL;
	}

	s->p = p;
	s->flag = flag;
	s->len = 0;
	s->first = NULL;
	s->last = NULL;

	return s;
}


void iks_str_add(ikstr *s, char *src)
{
	struct ikstrnode *sn;
	int len;

	if(!s || !src) return;

	len = strlen(src);
	if(len == 0) return;

	sn = iks_pool_alloc(s->p, sizeof(struct ikstrnode));
	if(!sn) return;
	sn->data = iks_pool_alloc(s->p, len);
	if(!sn->data) return;
	memcpy(sn->data, src, len);
	sn->len = len;

	s->len += len;
	sn->next = NULL;
	if(!s->first) s->first = sn;
	if(s->last) s->last->next = sn;
	s->last = sn;
}


void iks_spool(ikstr *s, ...)
{
	va_list ap;
	char *arg;

	va_start(ap, s);

	while(1)
	{
		arg = va_arg(ap, char *);
		if(((void *)arg) == ((void *)s)) break;
		iks_str_add(s, arg);
	}

	va_end(ap);
}


char *iks_str_print(ikstr *s)
{
	char *ret, *tmp;
	struct ikstrnode *sn;

	if(!s || !s->first) return(NULL);

	ret = iks_pool_alloc(s->p, s->len + 1);

	sn = s->first;
	tmp = ret;
	while(sn)
	{
		memcpy(tmp, sn->data, sn->len);
		tmp += sn->len;
		sn = sn->next;
	}
	*tmp = '\0';

	return ret;
}


void iks_str_delete(ikstr *s)
{
	if(s->flag) iks_pool_delete(s->p);
}


/*****  XML Escaping  *****/

char *iks_escape(ikspool *p, char *src, int len)
{
	char *ret;
	int i, j, nlen;

	if(!src || !p) return(NULL);

	if(len == -1) len = strlen(src);

	nlen = len;
	for(i=0; i<len; i++)
	{
		switch(src[i])
		{
		case '&': nlen += 4; break;
		case '<': nlen += 3; break;
		case '>': nlen += 3; break;
		case '\'': nlen += 5; break;
		case '"': nlen += 5; break;
		}
	}

	if(len == nlen) return(src);

	ret = iks_pool_alloc(p, nlen + 1);
	if(!ret) return(NULL);

	for(i=j=0; i<len; i++)
	{
		switch(src[i])
		{
		case '&': memcpy(&ret[j], "&amp;", 5); j += 5; break;
		case '\'': memcpy(&ret[j], "&apos;", 6); j += 6; break;
		case '"': memcpy(&ret[j], "&quot;", 6); j += 6; break;
		case '<': memcpy(&ret[j], "&lt;", 4); j += 4; break;
		case '>': memcpy(&ret[j], "&gt;", 4); j += 4; break;
		default:
			ret[j++] = src[i];
		}
	}
	ret[j] = '\0';

	return ret;
}


char *iks_unescape(ikspool *p, char *src, int len)
{
	int i,j;
	char *ret;

	if(!p || !src) return(NULL);
	if(!strchr(src, '&')) return(src);
	if(len == -1) len = strlen(src);

	ret = iks_pool_alloc(p, len + 1);
	if(!ret) return(NULL);

	for(i=j=0; i<len; i++)
	{
		if(src[i] == '&')
		{
			i++;
			if(strncmp(&src[i], "amp;", 4) == 0)
			{
				ret[j] = '&';
				i += 3;
			}
			else if(strncmp(&src[i], "quot;", 5) == 0)
			{
				ret[j] = '"';
				i += 4;
			}
			else if(strncmp(&src[i], "apos;", 5) == 0)
			{
				ret[j] = '\'';
				i += 4;
			}
			else if(strncmp(&src[i], "lt;", 3) == 0)
			{
				ret[j] = '<';
				i += 2;
			}
			else if(strncmp(&src[i], "gt;", 3) == 0)
			{
				ret[j] = '>';
				i += 2;
			}
			else
			{
				ret[j] = src[--i];
			}
		}
		else
		{
			ret[j] = src[i];
		}
		j++;
	}
	ret[j] = '\0';

	return ret;
}
