view src/dmstring.c @ 2027:750a7e125546

Add in several string helper functions from th-libs.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 27 Nov 2018 11:31:10 +0200
parents 47fe47f01fea
children eeddaf411083
line wrap: on
line source

#include "dmlib.h"
#include <stdarg.h>


/* Returns the filename and path without the last filename extension,
 * E.g. everything before the last '.', if any.
 */
char *dm_basefilename(const char *filename)
{
    char *tmp, *fext;

    if (filename == NULL ||
        (tmp = dm_strdup(filename)) == NULL)
        return NULL;

    if ((fext = strrchr(tmp, '.')) != NULL)
    {
        char *fpath = strrchr(tmp, DM_DIR_SEPARATOR);
        if (fpath == NULL || (fpath != NULL && fext > fpath))
            *fext = 0;
    }

    return tmp;
}


/* Replace filename extension based on format pattern.
 * Usage: res = dm_strdup_fext(orig_filename, "foo_%s.cmp");
 */
char *dm_strdup_fext(const char *filename, const char *fmt)
{
    char *result, *tmp;

    if ((tmp = dm_basefilename(filename)) == NULL)
        return NULL;

    result = dm_strdup_printf(fmt, tmp);

    dmFree(tmp);

    return result;
}


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

    if (haystack == needle)
        return 0;

    while (*s1 && *s2)
    {
        int k = tolower(*s1) - tolower(*s2);
        if (k != 0)
            return k;
        s1++;
        s2++;
    }

    return 0;
}


int dm_strncasecmp(const char *haystack, const char *needle, size_t n)
{
    const char *s1 = haystack, *s2 = needle;
    assert(haystack != NULL);
    assert(needle != NULL);

    if (haystack == needle)
        return 0;

    while (n > 0 && *s1 && *s2)
    {
        int k = tolower(*s1) - tolower(*s2);
        if (k != 0)
            return k;
        s1++;
        s2++;
        n--;
    }

    return 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 *dm_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 (dm_strcasecmp(str - nlen - 1, needle) == 0)
        return str - nlen - 1;
    else
        return NULL;
}


/* Implementation of strdup() with a NULL check
 */
char *dm_strdup(const char *s)
{
    char *res;
    if (s == NULL)
        return NULL;

    if ((res = dmMalloc(strlen(s) + 1)) == NULL)
        return NULL;

    strcpy(res, s);
    return res;
}


/* Implementation of strndup() with NULL check
 */
char *dm_strndup(const char *str, const size_t n)
{
    char *res;
    if (str == NULL)
        return NULL;

    size_t len = strlen(str);
    if (len > n)
        len = n;

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

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

    return res;
}


/* Like strdup, but trims whitespace from the string according to specified flags.
 * See DM_TRIM_* in dmlib.h. If the resulting string would be empty (length 0),
 * NULL is returned.
 */
static char * dm_strdup_trim_do(const char *src, size_t len, const int flags)
{
    char *res;
    size_t start, end;

    if (len == 0)
        return NULL;

    // Trim start: find first non-whitespace character
    if (flags & DM_TRIM_START)
        for (start = 0; start < len && isspace(src[start]); start++);
    else
        start = 0;

    // Trim end: find last non-whitespace character
    if (flags & DM_TRIM_END)
        for (end = len - 1; end > start && isspace(src[end]); end--);
    else
        end = len;

    // Allocate memory for result
    if (src[end] == 0 || isspace(src[end]))
        return NULL;

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

    memcpy(res, src + start, len);
    res[len] = 0;
    return res;
}


char *dm_strdup_trim(const char *src, const int flags)
{
    if (src == NULL)
        return NULL;

    return dm_strdup_trim_do(src, strlen(src), flags);
}


char *dm_strndup_trim(const char *src, const size_t n, const int flags)
{
    size_t len;
    if (src == NULL || n == 0)
        return NULL;

    for (len = 0; len < n && src[len]; len++);

    return dm_strdup_trim_do(src, len, flags);
}


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

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

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

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

        if ((tmp = dmRealloc(buf, size)) == NULL)
        {
            dmFree(buf);
            return NULL;
        }
        else
            buf = tmp;
    }
}


char *dm_strdup_vprintf(const char *fmt, va_list args)
{
    int len;
    return dm_strdup_vprintf_len(fmt, args, &len);
}


char *dm_strdup_printf(const char *fmt, ...)
{
    int len;
    char *res;
    va_list ap;

    va_start(ap, fmt);
    res = dm_strdup_vprintf_len(fmt, ap, &len);
    va_end(ap);

    return res;
}