view th_string.c @ 162:578d9298cc1e

Actually, move th_print_wrap() to th_string module.
author Matti Hamalainen <ccr@tnsp.org>
date Mon, 09 Feb 2015 18:52:56 +0200
parents 51eec969b07a
children 7638fa9d191f
line wrap: on
line source

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


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


/* Implementation of strndup() with NULL check
 */
char *th_strndup(const char *s, const size_t n)
{
    char *res;
    size_t len;

    if (s == NULL)
        return NULL;

    len = strlen(s);
    if (len > n)
        len = n;

    if ((res = th_malloc(len + 1)) == NULL)
        return NULL;

    memcpy(res, s, len);
    res[len] = 0;

    return res;
}


/* Like strdup, but trims whitespace from stringImplementation of strdup() with a NULL check
 */
char *th_strdup_trim(const char *s, const int flags)
{
    char *res;
    size_t start, end, len;
    if (s == NULL)
        return NULL;

    len = strlen(s);
    if (flags & TH_TRIM_START)
        for (start = 0; start < len && th_isspace(s[start]); start++);
    else
        start = 0;
    
    if (flags & TH_TRIM_END)
        for (end = len; end > start && th_isspace(s[end]); end--);
    else
        end = len;

    len = end - start + 1;
    if ((res = th_malloc(len + 1)) == NULL)
        return NULL;

    memcpy(res, s, len);
    res[len] = 0;
    return res;
}


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

    if ((buf = th_malloc(size)) == NULL)
        return NULL;

    while (1)
    {
        int n;
        va_list ap;
        va_copy(ap, args);
        n = vsnprintf(buf, size, fmt, ap);
        va_end(ap);

        if (n > -1 && n < size)
            return buf;
        if (n > -1)
            size = n + 1;
        else
            size *= 2;

        if ((tmp = th_realloc(buf, size)) == NULL)
        {
            th_free(buf);
            return NULL;
        }
        else
            buf = tmp;
    }
}


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


/* Check if end of the given string str matches needle
 * case-insensitively, return pointer to start of the match,
 * if found, NULL otherwise.
 */
char *th_strrcasecmp(char *str, const char *needle)
{
    if (str == NULL || needle == NULL)
        return NULL;

    const size_t
        slen = strlen(str),
        nlen = strlen(needle);
    
    if (slen < nlen)
        return NULL;

    if (th_strcasecmp(str - nlen - 1, needle) == 0)
        return str - nlen - 1;
    else
        return NULL;
}


/* 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 void th_pad(FILE *outFile, int count)
{
    while (count--)
        fputc(' ', outFile);
}


void th_print_wrap(FILE *fh, const char *str, int spad, int rpad, int width)
{
    size_t pos = 0;
    BOOL first = TRUE;

    while (str[pos])
    {
        // Pre-pad line
        int linelen = first ? spad : rpad;
        th_pad(fh, first ? 0 : rpad);
        first = FALSE;

        // Skip whitespace at line start
        while (th_isspace(str[pos]) || str[pos] == '\n') pos++;

        // Handle each word
        while (str[pos] && str[pos] != '\n')
        {
            size_t next;
            int wlen;

            // Find word length and next break
            for (wlen = 0, next = pos; str[next] && !th_isspace(str[next]) && str[next] != '\n'; next++, wlen++);

            // Check if we have too much of text?
            if (linelen + wlen >= width)
                break;

            // Print what we have
            for (;pos < next; pos++, linelen++)
                fputc(str[pos], fh);

            // Check if we are at end of input or hard linefeed
            if (str[next] == '\n' || str[next] == 0)
                break;
            else
            {
                fputc(str[pos], fh);
                pos++;
                linelen++;
            }
        }
        fprintf(fh, "\n");
    }
}