view th_string.c @ 327:fae4651d37bc

Make th_growbuf() public.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 13 Jun 2011 15:08:51 +0300
parents f1049f487987
children b3556ff686fc
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"

/* 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;
}


/* Simulate a sprintf() that allocates memory
 */
char * th_strdup_vprintf(const char *fmt, va_list args)
{
    int size = 100;
    char *buf, *nbuf = NULL;

    if ((buf = th_malloc(size)) == NULL)
        return NULL;
    
    while (1) {
        int n = vsnprintf(buf, size, fmt, args);
        if (n > -1 && n < size)
            return buf;
        if (n > -1)
            size = n + 1;
        else
            size *= 2;
        
        if ((nbuf = th_realloc(nbuf, size)) == NULL) {
            th_free(buf);
            return NULL;
        }
        
        buf = nbuf;
    }
}


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;
}


void th_pstr_vprintf(char **buf, const char *fmt, va_list ap)
{
    char *tmp = th_strdup_vprintf(fmt, ap);
    th_free(*buf);
    *buf = tmp;
}


void th_pstr_printf(char **buf, const char *fmt, ...)
{
    char *tmp;
    va_list ap;

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

    th_free(*buf);
    *buf = tmp;
}

/* 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;
}


int th_get_hex_triplet(const char *str)
{
    const char *p = str;
    int len, val = 0;
    
    for (len = 0; *p && len < 6; p++, len++) {
        if (*p >= '0' && *p <= '9') {
            val *= 16; val += (*p - '0');
        } else if (*p >= 'A' && *p <= 'F') {
            val *= 16; val += (*p - 'A') + 10;
        } else if (*p >= 'a' && *p <= 'f') {
            val *= 16; val += (*p - 'a') + 10;
        } else
            return -1;
    }
    
    return (len == 6) ? val : -1;
}


BOOL th_growbuf(char **buf, size_t *bufsize, size_t *len, size_t grow)
{
    if (*buf == NULL)
        *bufsize = *len = 0;
    
    if (*buf == NULL || *len + grow >= *bufsize) {
        *bufsize += grow + TH_BUFGROW;
        *buf = (char *) th_realloc(*buf, *bufsize);
        if (*buf == NULL)
            return FALSE;
    }
    return TRUE;
}


BOOL th_vputch(char **buf, size_t *bufsize, size_t *len, const char ch)
{
    if (!th_growbuf(buf, bufsize, len, 1))
        return FALSE;
    
    (*buf)[*len] = ch;
    (*len)++;

    return TRUE;
}


BOOL th_vputs(char **buf, size_t *bufsize, size_t *len, const char *str)
{
    size_t slen;
    if (str == NULL)
        return FALSE;
    
    slen = strlen(str);
    if (!th_growbuf(buf, bufsize, len, slen))
        return FALSE;

    strcpy(*buf + *len, str);
    (*len) += slen;
    
    return TRUE;
}