view th_string.c @ 322:b9c15c57dc8f

Clean up message functions, add new printMsgQ() helper function for messages that should not go into the log file. Add skeleton help function, accessible via F1 key. And other cleanups.
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 11 Jun 2011 09:48:26 +0300
parents f1049f487987
children fae4651d37bc
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;
}


static BOOL th_growbuf(char **buf, size_t *bufsize, size_t *len, size_t grow)
{
    assert(buf != NULL);
    assert(bufsize != NULL);
    assert(len != NULL);
    
    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;
}