view th_string.c @ 124:fe4d5f3b486c

Sync.
author Matti Hamalainen <ccr@tnsp.org>
date Fri, 29 Oct 2010 16:41:08 +0300
parents 69aed051f84d
children 9ae3d87a686f
line wrap: on
line source

/*
 * Miscellaneous string-handling related utility-functions
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "th_string.h"

#define LPREV (pNode->pPrev)
#define LNEXT (pNode->pNext)


/* strdup with a NULL check
 */
char *th_strdup(const char *s)
{
    char *res;
    if (s == NULL)
        return NULL;
    
    if ((res = th_malloc(strlen(s) + 1)) == NULL)
        return NULL;
    
    strcpy(res, s);
    return res;
}


char *th_strncpy(char * dst, const char * src, const size_t n)
{
    const char *s = src;
    char *d = dst;
    size_t i;
    assert(src != NULL);
    assert(dst != NULL);

    /* Copy to the destination */
    i = n;
    while (*s && i > 0) {
        *(d++) = *(s++);
        i--;
    }

    /* Fill rest of space with zeros */
    while (i > 0) {
        *(d++) = 0;
        i--;
    }

    /* Ensure that last is always zero */
    dst[n - 1] = 0;

    return dst;
}


#ifdef STRDUP_PRINTF
/*
 */
enum {
    TH_PAD_RIGHT = 1,
    TH_PAD_ZERO = 2
};


static size_t th_printbuf_str(char **buf, const char *str, size_t width, int flags)
{
    size_t len = strlen(str);
    char *out = (buf != NULL) ? *buf : NULL;
    char pad = ' ';

    if (width > 0) {
        width = (len >= width) ? 0 : width - len;
        if (flags & TH_PAD_ZERO)
            pad = '0';
    }

    if ((flags & TH_PAD_RIGHT) == 0 && out != NULL) {
        while (width-- > 0)
            *out++ = pad;
    }

    if (out != NULL) {
        while (*str)
            *out++ = *str++;
    }

    if (flags & TH_PAD_RIGHT && out != NULL) {
        while (width-- > 0)
            *out++ = pad;
    }

    if (buf != NULL)
        *buf = out;

    return len + width;
}

#define TH_INTBUF_LEN (32)

static size_t th_printbuf_int(char **buf, int i, int b, int sg, int width, int pad, int letbase)
{
    char tmpbuf[TH_INTBUF_LEN], *s;
    int t, neg = 0, pc = 0;
    unsigned int u = i;

    if (i == 0) {
        tmpbuf[0] = '0';
        tmpbuf[1] = 0;
        return th_printbuf_str(buf, tmpbuf, width, pad);
    }

    if (sg && b == 10 && i < 0) {
        neg = 1;
        u = -i;
    }
    
    s = tmpbuf + TH_INTBUF_LEN - 1;
    *s = 0;

    while (u) {
        t = u % b;
        if (t >= 10)
            t += letbase - '0' - 10;
        
        *--s = t + '0';
        u /= b;
    }
    
    if (neg) {
        if (width && (pad & PAD_ZERO)) {
            printchar (out, '-');
            ++pc;
            --width;
        } else {
            *--s = '-';
        }
    }

    return pc + th_printbuf_str(buf, s, width, pad);
}


char * th_strdup_vprintf(const char *fmt, va_list args)
{
    const char *s = fmt;
    char *res;
    size_t len = 0;

    /* 1. Determine required space for final string */
    while (*s) {
        if (*s == '%') {
            s++;
        } else {
            s++;
            len++;
        }
    }
    
    /* 2. Allocate space */
    
    /* 3. Create final string */
    
    return res;
}


char * th_strdup_printf(const char *fmt, ...)
{
    char *res;
    va_list ap;

    va_start(ap, fmt);
    res = th_strdup_vprintf(fmt, ap);
    va_end(ap);

    return res;
}
#endif

/* Compare two strings ignoring case [strcasecmp, strncasecmp]
 */
int th_strcasecmp(const char * str1, const char * str2)
{
    const char *s1 = str1, *s2 = str2;
    assert(str1 != NULL);
    assert(str2 != NULL);

    if (str1 == str2)
        return 0;

    while (*s1 && *s2 && th_tolower(*s1) == th_tolower(*s2)) {
        s1++;
        s2++;
    }

    return (th_tolower(*s1) - th_tolower(*s2));
}


int th_strncasecmp(const char * str1, const char * str2, size_t n)
{
    const char *s1 = str1, *s2 = str2;
    assert(str1 != NULL);
    assert(str2 != NULL);

    if (str1 == str2)
        return 0;

    while (n > 0 && *s1 && *s2 && th_tolower(*s1) == th_tolower(*s2)) {
        s1++;
        s2++;
        n--;
    }

    return n > 0 ? (th_tolower(*s1) - th_tolower(*s2)) : 0;
}


/* Remove all occurences of control characters, in-place.
 * Resulting string is always shorter or same length than original.
 */
void th_strip_ctrlchars(char * str)
{
    char *i, *j;
    assert(str != NULL);

    i = str;
    j = str;
    while (*i) {
        if (!th_iscntrl(*i))
            *(j++) = *i;
        i++;
    }

    *j = 0;
}


/* Copy a given string over in *result.
 */
int th_pstrcpy(char ** result, const char * str)
{
    assert(result != NULL);

    if (str == NULL)
        return -1;

    th_free(*result);
    if ((*result = th_malloc(strlen(str) + 1)) == NULL)
        return -2;

    strcpy(*result, str);
    return 0;
}


/* Concatenates a given string into string pointed by *result.
 */
int th_pstrcat(char ** result, const char * str)
{
    assert(result != NULL);

    if (str == NULL)
        return -1;

    if (*result != NULL) {
        *result = th_realloc(*result, strlen(*result) + strlen(str) + 1);
        if (*result == NULL)
            return -1;

        strcat(*result, str);
    } else {
        *result = th_malloc(strlen(str) + 1);
        if (*result == NULL)
            return -1;

        strcpy(*result, str);
    }

    return 0;
}


/* Find next non-whitespace character in string.
 * Updates iPos into the position of such character and
 * returns pointer to the string.
 */
const char *th_findnext(const char * str, size_t * pos)
{
    assert(str != NULL);

    /* Terminating NULL-character is not whitespace! */
    while (th_isspace(str[*pos]))
        (*pos)++;
    return &str[*pos];
}


/* Find next sep-character from string
 */
const char *th_findsep(const char * str, size_t * pos, char sep)
{
    assert(str != NULL);

    while (str[*pos] && str[*pos] != sep)
        (*pos)++;

    return &str[*pos];
}


/* Find next sep- or whitespace from string
 */
const char *th_findseporspace(const char * str, size_t * pos, char sep)
{
    assert(str != NULL);

    while (!th_isspace(str[*pos]) && str[*pos] != sep)
        (*pos)++;

    return &str[*pos];
}


/* Compare a string to a pattern. Case-SENSITIVE version.
 * The matching pattern can consist of any normal characters plus
 * wildcards ? and *. "?" matches any character and "*" matches
 * any number of characters.
 */
BOOL th_strmatch(const char * str, const char * pattern)
{
    BOOL didMatch = TRUE, isAnyMode = FALSE, isEnd = FALSE;
    const char *tmpPattern = NULL;

    /* Check given pattern and string */
    if (str == NULL || pattern == NULL)
        return FALSE;

    /* Start comparision */
    do {
        didMatch = FALSE;
        switch (*pattern) {
        case '?':
            /* Any single character matches */
            if (*str) {
                didMatch = TRUE;
                pattern++;
                str++;
            }
            break;

        case '*':
            didMatch = TRUE;
            pattern++;
            if (!*pattern)
                isEnd = TRUE;
            isAnyMode = TRUE;
            tmpPattern = pattern;
            break;

        case 0:
            if (isAnyMode) {
                if (*str)
                    str++;
                else
                    isEnd = TRUE;
            } else {
                if (*str) {
                    if (tmpPattern) {
                        isAnyMode = TRUE;
                        pattern = tmpPattern;
                    } else
                        didMatch = FALSE;
                } else
                    isEnd = TRUE;
            }
            break;
        default:
            if (isAnyMode) {
                if (*pattern == *str) {
                    isAnyMode = FALSE;
                    didMatch = TRUE;
                } else {
                    if (*str) {
                        didMatch = TRUE;
                        str++;
                    }
                }
            } else {
                if (*pattern == *str) {
                    didMatch = TRUE;
                    if (*pattern)
                        pattern++;
                    if (*str)
                        str++;
                } else {
                    if (tmpPattern) {
                        didMatch = TRUE;
                        isAnyMode = TRUE;
                        pattern = tmpPattern;
                    }
                }
            }

            if (!*str && !*pattern)
                isEnd = TRUE;
            break;

        }        /* switch */

    } while (didMatch && !isEnd);

    return didMatch;
}


/* Compare a string to a pattern. Case-INSENSITIVE version.
 */
BOOL th_strcasematch(const char * str, const char * pattern)
{
    BOOL didMatch = TRUE, isAnyMode = FALSE, isEnd = FALSE;
    const char *tmpPattern = NULL;

    /* Check given pattern and string */
    if (str == NULL || pattern == NULL)
        return FALSE;

    /* Start comparision */
    do {
        switch (*pattern) {
        case '?':
            /* Any single character matches */
            if (*str) {
                pattern++;
                str++;
            } else
                didMatch = FALSE;
            break;

        case '*':
            pattern++;
            if (!*pattern || *pattern == '?')
                isEnd = TRUE;
            isAnyMode = TRUE;
            tmpPattern = pattern;
            break;

        case 0:
            if (isAnyMode) {
                if (*str)
                    str++;
                else
                    isEnd = TRUE;
            } else {
                if (*str) {
                    if (tmpPattern) {
                        isAnyMode = TRUE;
                        pattern = tmpPattern;
                    } else
                        didMatch = FALSE;
                } else
                    isEnd = TRUE;
            }
            break;

        default:
            if (isAnyMode) {
                if (th_tolower(*pattern) == th_tolower(*str)) {
                    isAnyMode = FALSE;
                } else {
                    if (*str)
                        str++;
                    else
                        didMatch = FALSE;
                }
            } else {
                if (th_tolower(*pattern) == th_tolower(*str)) {
                    if (*pattern)
                        pattern++;
                    if (*str)
                        str++;
                } else {
                    if (tmpPattern) {
                        isAnyMode = TRUE;
                        pattern = tmpPattern;
                    } else
                        didMatch = FALSE;
                }
            }

            if (!*str && !*pattern)
                isEnd = TRUE;
            break;

        }        /* switch */

    } while (didMatch && !isEnd);

    return didMatch;
}