/*
 * Copyright (c) 2004-2009, Luiz Otavio O Souza <loos.br@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

static const char rcsid[] = "$Id: string.c 96 2009-01-20 15:34:19Z loos-br $";

#include <ctype.h>
#include <strings.h>

#include "string.h"

int
str_cmp(string *str1, string *str2) {
 string         s1;
 string         s2;

    s1.s   = str1->s;
    s1.len = str1->len;
    s2.s   = str2->s;
    s2.len = str2->len;

    while (s1.len > 0 && s2.len > 0) {

	if (*s1.s != *s2.s)
	    return(*s1.s - *s2.s);

	s2.s++;
	s2.len--;

	if (*s1.s++ == '\0')
	    return(0);
	s1.len--;
    }
    return(0);
}

int
str_casecmp(string *str1, string *str2) {
 string         s1;
 string         s2;

    s1.s   = str1->s;
    s1.len = str1->len;
    s2.s   = str2->s;
    s2.len = str2->len;

    while (s1.len > 0 && s2.len > 0) {

	if (tolower(*s1.s) != tolower(*s2.s))
	    return(tolower(*s1.s) - tolower(*s2.s));

	s2.s++;
	s2.len--;

	if (*s1.s++ == '\0')
	    return(0);
	s1.len--;
    }
    return(0);
}

int
str_ncmp(string *str1, string *str2, __int32_t n) {
 string         s1;
 string         s2;

    if (n != 0) {

	s1.s   = str1->s;
	s1.len = str1->len;
	s2.s   = str2->s;
	s2.len = str2->len;

	while (n-- != 0 && s1.len > 0 && s2.len > 0) {

	    if (*s1.s != *s2.s)
		return(*s1.s - *s2.s);

	    s2.s++;
	    s2.len--;

	    if (*s1.s++ == '\0')
		break;
	    s1.len--;
	}
    }
    return(0);
}

int
str_ncasecmp(string *str1, string *str2, __int32_t n) {
 string         s1;
 string         s2;

    if (n != 0) {

	s1.s   = str1->s;
	s1.len = str1->len;
	s2.s   = str2->s;
	s2.len = str2->len;

	while (n-- != 0 && s1.len > 0 && s2.len > 0) {

	    if (tolower(*s1.s) != tolower(*s2.s))
		return(tolower(*s1.s) - tolower(*s2.s));

	    s2.s++;
	    s2.len--;

	    if (*s1.s++ == '\0')
		break;
	    s1.len--;
	}
    }
    return(0);
}

unsigned char *
_strchr(register unsigned char *s, int c) {
    if (!s)
	return((unsigned char *)0);

    while (*s && *s != c) ++s;

    if (*s)
	return(s);
    else
	return((unsigned char *)0);
}

__int32_t
str_get_size(__uint32_t len) {
 __int32_t		size = 0;

    if (len > STRINGOVER)
	len = STRINGOVER;	/* clamp len - caution ! */

    if (len < STRING_MIN_LEN)
	return(STRING_MIN_LEN);

    while (len > 0) {

	size += STRING_MIN_LEN;

	if (len >= STRING_MIN_LEN)
	    len  -= STRING_MIN_LEN;
	else
	    len = 0;
    }
    return(size);
}

__int32_t str_copy(string *s, register const unsigned char *p, __int32_t len) {
 register unsigned char *ss;

    if (s == (string *)0)
	/* oops */
	return(0);

    /* reset string length */
    str_reset(s);

    if (p == (unsigned char *)0 || len == 0)
	return(0);

    /* alloc string */
    if (s->a == 0 || s->a <= len) {

	if (s->a + len + 1 > STRINGOVER)
	    return(0);	/* overflow */

	/* alloc requested size */
	if (str_ready(s, str_get_size(len + 1)) == 0)
	    return(0);	/* nomem */
    }

    /* copy */
    ss  = s->s;
    while (s->len < len) {
	*ss++ = *p++;
	s->len++;
    }
    *ss = 0;

    return(s->len);
}

__int32_t str_copys(string *s, register const unsigned char *p) {
 register unsigned char *ss;

    /* check for string */
    if (s == (string *)0)
	/* oops */
	return(0);

    /* reset string length */
    str_reset(s);

    if (!p)
	return(0);

    /* alloc string or take another one */
    if (s->a == 0) {

	/* alloc len */
	if (str_ready(s, STRING_MIN_LEN) == 0)
	    return(0);	/* nomem */
    }

    /* copy */
    ss = s->s;
    while (*p) {

	/* check for size */
	if ((s->len + 1) == s->a) {

	    if (s->a + STRING_MIN_LEN > STRINGOVER)
		return(0);	/* overflow */

	    /* realloc len */
	    if (str_ready(s, s->a + STRING_MIN_LEN) == 0)
		return(0);

	    /* reset pointers */
	    ss  = s->s;
	    ss += s->len;
	}

	*ss++ = *p++;
	s->len++;
    }

    *ss = 0;
    return(s->len);
}

__int32_t str_len(string *s) {
 register unsigned char *ss;
 register __int32_t	len = 0;

    if (!s || !s->s)
	return len;

    ss = s->s;
    while (len < s->a && *ss != 0) {
	++len; ++ss;
    }

    s->len = len;
    return(len);
}

string *str_alloc(void) {
 string *rtrn;

    rtrn = (string *)malloc(sizeof(string));
    if (rtrn == (string *)0)
	return((string *)0);

    str_zero(rtrn);
    return(rtrn);
}

void str_reset(string *s) {
    s->len = 0;
}

void str_zero(string *s) {
    s->s   = (unsigned char *)0;
    s->p   = (unsigned char *)0;
    s->a   = 0;
    s->len = 0;
}

void str_free(string *s) {
    if (s == (string *)0)
	return;

    if (s->a > 0 && s->s)
	free(s->s);

    str_zero(s);
}

/*
 * this function strip the cr/lf from a string
 */
void
str_strip(string *s) {
 unsigned char		*p;

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

    /* search cr/lf */
    p = _strchr(s->s, '\r');
    if (!p)
	p = _strchr(s->s, '\n');

    /* strip cr/lf and reduce len */
    if (p && p >= s->s && p <= (s->s + s->len)) {
	*p = 0;
	s->len = p - s->s;
    }
    return;
}

int
str_ready(string *s, __int32_t size) {
    if (s == (string *)0)
	return(0);

    if (size <= 0)
	return(0);

    s->s = (unsigned char *)realloc(s->s, size);
    if (s->s == (unsigned char *)0)
	return(0);

    s->a = size;
    s->p = s->s;
    s->s[s->a - 1] = 0;
    return(1);
}

__int32_t
str_cat(string *s, const register unsigned char *p, __int32_t len) {
 register unsigned char	*ss;
 __int32_t tlen;

    if (s == (string *)0)
	/* oops */
	return(0);

    if (p == (unsigned char *)0 || len == 0)
	return(0);

    /* calcule total len */
    tlen = s->len + len;
    if (tlen > STRINGOVER)
	return(0);	/* overflow */

    if (s->a == 0 || s->a <= tlen) {

	if (s->a + tlen + 1 > STRINGOVER)
	    return(0);	/* overflow */

	if (str_ready(s, str_get_size(tlen + 1)) == 0)
	    return(0);	/* nomem */
    }

    ss   = s->s;
    ss  += s->len;
    while (s->len < tlen) {
	*ss++ = *p++;
	s->len++;
    }

    *ss  = 0;
    return(s->len);
}

__int32_t
str_cats(string *s, register const unsigned char *p) {
 register unsigned char	*ss;

    if (s == (string *)0)
	/* oops */
	return(0);

    if (p == (unsigned char *)0)
	return(0);

    if (s->a == 0) {

	if (str_ready(s, STRING_MIN_LEN) == 0) {
	    str_zero(s);
	    return(0);
	}
    }

    ss  = s->s;
    ss += s->len;
    while (*p) {

	if ((s->len + 1) == s->a) {

	    if (s->a + STRING_MIN_LEN > STRINGOVER)
		return(0);	/* overflow */

	    if (str_ready(s, s->len + STRING_MIN_LEN) == 0) {
		str_zero(s);
		return(0);
	    }

	    /* reset pointers */
	    ss  = s->s;
	    ss += s->len;
	}

	*ss++ = *p++;
	s->len++;
    }

    *ss  = 0;
    return(s->len);
}
