/*
**  Str - String Library
**  Copyright (c) 1999-2001 Ralf S. Engelschall <rse@engelschall.com>
**
**  This file is part of Str, a string handling and manipulation 
**  library which can be found at http://www.engelschall.com/sw/str/.
**
**  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.
**
**  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
**  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
**  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
**  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
**  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
**  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
**  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
**  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
**  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
**  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
**  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
**  SUCH DAMAGE.
**
**  str_handle.c: handling and manipulation functions
*/

#include "str_p.h"

/* 
 * str_len -- determine length of string.
 * This is exactly equal to POSIX strlen(3).
 */
str_size_t str_len(const char *s)
{
    register const char *t;

    if (s == NULL)
        return 0;
    t = s;
    while (*t++ != NUL)
        /*nop*/;
    return (t-s-1);
}

/*
 * str_copy -- copy a string.
 * This is inspired by POSIX strncpy(3), but the source and target
 * and overlap and the the target is always NUL-terminated.
 */
char *str_copy(char *as, const char *at, str_size_t n)
{
    register char *s;
    register const char *t;
    char *rv;

    if (as == NULL || at == NULL)
        return NULL;
    if (n == 0) 
        n = str_len(at);
    t = at;
    s = as;
    rv = as;
    if (s > t) { 
        /* must go high to low */
        t += n - 1;
        s += n;
        rv = s;
        *s-- = NUL;
        while (n-- > 0)
            *s-- = *t--;
    }
    else if (s < t) {
        /* must go low to high */
        while (n-- > 0)
            *s++ = *t++;
        *s = NUL;
        rv = s;
    }
    return rv;
}

/* 
 * str_dup -- duplicate a string.
 * This is inspired by POSIX strdup(3), but provides 
 * the ability to specify a maximum length.
 */
char *str_dup(const char *s, str_size_t n)
{
    char *t;
    char *rv;

    if (s == NULL)
        return NULL;
    if (n == 0)
        n = str_len(s);
    if ((rv = str_mem_alloc(n+1)) == NULL)
        return NULL;
    t = rv;
    while (n-- > 0)
        *t++ = *s++;
    *t = NUL;
    return rv;
}

/* 
 * str_concat -- concatenate one or more strings.
 * This function allows one to concatenate an arbitrary number of
 * strings and is inspired by Apache's ap_pstrcat() function.
 */
char *str_concat(char *s, ...)
{
    va_list ap;
    char *rv;

    va_start(ap, s);
    rv = str_concat_va(s, ap);
    va_end(ap);
    return rv;
}

char *str_concat_va(char *s, va_list ap)
{
    va_list ap_safe;
    int n;
    char *rv;
    char *cp;
    char *ds;

    if (s == NULL)
        return NULL;

    /* determine required target string length */
    ap_safe = ap;
    n = str_len(s);
    while ((cp = va_arg(ap, char *)) != NULL)
        n += str_len(cp);
    ap = ap_safe;

    /* allocate target string */
    if ((rv = str_mem_alloc(n+1)) == NULL)
        return NULL;

    /* concatenate the strings */
    ds = rv;
    while ((*ds++ = *s++) != NUL)
        /*nop*/;
    while ((cp = va_arg(ap, char *)) != NULL)
        while ((*ds++ = *cp++) != NUL)
            /*nop*/;
    *ds = NUL;

    /* return target string */
    return rv;
}

/* 
 * str_splice -- splice one string into another.
 * This is inspired by Perl's splice() function and can be used 
 * both for inplace movements, insert and cut-out operations.
 */
char *str_splice(char *s, str_size_t off, str_size_t n, char *t, str_size_t m)
{
    int sl;

    /* check for invalid arguments */
    if (s == NULL || t == NULL)
        return NULL;
    /* check for overlapping areas */
    if (!((t+m) <= s || (s+off+n) <= t))
        return NULL;

    sl = str_len(s);
    if ((t+m) < s || (s+sl) < t) {
        /* splice _external_ area into internal area */
        if (m != n)
            str_mem_move(s+off+m, s+off+n, sl-off-n+1);
        str_mem_move(s+off, t, m);
    }
    else {
        /* splice _internal_ area into internal area */
        if (t > s) {
            /* move t from larger to lower address of s */
            str_mem_rev(s+off, (t+m)-(s+off));
            str_mem_rev(s+off, m);
            str_mem_rev(s+off+m, t-(s+n));
            str_mem_move(t+m-n, t+m, (s+sl)-(t+m)+1);
        }
        else {
            /* move t from lower to higher address of s */
            str_mem_rev(t, (s+off)-t);
            str_mem_rev(t, (s+off)-t-m);
            str_mem_rev(s+off-m, m);
            str_mem_move(s+off, s+off+n, sl-off-n+1);
        }
    }
    return s;
}

/* 
 * str_compare -- compare a two strings.
 * This is inspired by POSIX str[n][case]cmp(3), but
 * merges all functionality in a single function.
 */
int str_compare(const char *s1, const char *s2, str_size_t n, int mode)
{
    int rv;
    int bWithLen;

    rv = 0;
    if (n == 0)
        bWithLen = FALSE;
    else
        bWithLen = TRUE;
    if (mode & STR_NOCASE) {
        /* compare case insensitive */
        do {
            if (str_tolower(*s1) != str_tolower(*s2++)) {
                rv = ((str_tolower(*s1) - 
                       str_tolower(*(s2 - 1))) < 0 ? -1 : +1);
                break;
            }
            if (bWithLen)
                if (--n <= 0)
                    break;
        } while (*s1++ != NUL);
    }
    else {
        /* compare case sensitive */
        do {
            if (*s1 != *s2++) {
                rv = (((int)(*(const unsigned char *)s1) - 
                       (int)(*(const unsigned char *)(s2 - 1))) < 0 ? -1 : +1);
                break;
            }
            if (bWithLen)
                if (--n <= 0)
                    break;
        } while (*s1++ != NUL);
    }
    return rv;
}

