view util.c @ 677:f40d5613c753

Use th_free_r().
author Matti Hamalainen <ccr@tnsp.org>
date Sat, 30 Dec 2017 04:26:29 +0200
parents f212cbfbd93c
children ceb73b712121
line wrap: on
line source

/*
 * NNChat - Custom chat client for NewbieNudes.com chatrooms
 * Written by Matti 'ccr' Hämäläinen
 * (C) Copyright 2008-2016 Tecnic Software productions (TNSP)
 */
#include "util.h"
#include <time.h>


BOOL str_get_timestamp(char *str, size_t len, const char *fmt)
{
    time_t stamp = time(NULL);
    struct tm *stamp_tm;
    if ((stamp_tm = localtime(&stamp)) != NULL)
    {
        strftime(str, len, fmt, stamp_tm);
        return TRUE;
    }
    else
    {
        str[0] = 0;
        return FALSE;
    }
}


char * str_trim_left(char *buf)
{
    while (*buf != 0 && th_isspace(*buf)) buf++;
    return buf;
}


char * str_trim_right(char *buf)
{
    size_t pos;

    for (pos = strlen(buf) - 1; pos > 0 && th_isspace(buf[pos]); pos--)
        buf[pos] = 0;
    
    return buf;
}


int str_compare(const void *s1, const void *s2)
{
    return th_strcasecmp((const char *) s1, (const char *) s2);
}


#define PUSHCHAR(x) th_strbuf_putch(&result, &resSize, &resPos, x)
#define PUSHSTR(x) th_strbuf_puts(&result, &resSize, &resPos, x)

char *nn_encode_str1(const char *str)
{
    const char *s = str;
    char *result;
    size_t resSize, resPos = 0;

    if (str == NULL) return NULL;

    resSize = strlen(str) + NN_ALLOC_SIZE;
    if ((result = th_malloc(resSize)) == NULL)
        return NULL;

    while (*s)
    {
        switch (*s)
        {
        case 32:
            PUSHCHAR('+');
            break;

        default:
            if (th_isalnum(*s))
                PUSHCHAR(*s);
            else
            {
                char tmpStr[4];
                snprintf(tmpStr, sizeof(tmpStr), "%2X", (unsigned char) *s);
                PUSHCHAR('%');
                PUSHSTR(tmpStr);
            }
            break;
        }
        s++;
    }
    PUSHCHAR(0);

    return result;
}


static int nn_get_hexdigit(const int c, const int shift)
{
    int i;

    if (c >= 'A' && c <= 'F')
        i = c - 'A' + 10;
    else if (c >= 'a' && c <= 'f')
        i = c - 'a' + 10;
    else if (c >= '0' && c <= '9')
        i = c - '0';
    else
        return -1;

    return i << shift;
}


char *nn_decode_str1(const char *str)
{
    const char *s = str;
    char *result;
    size_t resSize, resPos = 0;
    int c;

    if (str == NULL) return NULL;

    resSize = strlen(str) + NN_ALLOC_SIZE;
    if ((result = th_malloc(resSize)) == NULL)
        return NULL;

    while (*s)
    {
        switch (*s)
        {
        case '+':
            PUSHCHAR(' ');
            s++;
            break;

        case '½':
            // Escape these ..
            PUSHCHAR('½');
            PUSHCHAR('½');
            s++;
            break;

        case '\r':
            PUSHCHAR(' ');
            s++;
            break;

        case '%':
            s++;
            if (*s == '%')
                PUSHCHAR('%');
            else if ((c = nn_get_hexdigit(*s, 4)) >= 0)
            {
                int i = nn_get_hexdigit(*(++s), 0);
                if (i >= 0)
                {
                    PUSHCHAR((char) (c | i));
                }
                else
                {
                    PUSHCHAR('§');
                    PUSHCHAR(*s);
                }
            }
            else
            {
                PUSHCHAR('§');
                PUSHCHAR(*s);
            }
            s++;
            break;

        default:
            PUSHCHAR(*s);
            s++;
        }
    }
    PUSHCHAR(0);

    return result;
}


char *nn_strip_tags(const char *str)
{
    const char *s = str;
    char *result;
    size_t resSize, resPos = 0;

    if (str == NULL) return NULL;

    resSize = strlen(str) + NN_ALLOC_SIZE;
    if ((result = th_malloc(resSize)) == NULL)
        return NULL;

    while (*s)
    {
        if (*s == '<')
        {
            while (*s && *s != '>') s++;
            if (*s == '>') s++;
        }
        else
            PUSHCHAR(*s++);
    }
    PUSHCHAR(0);

    return result;
}


typedef struct
{
    char c;
    char *ent;
} html_entity_t;


static const html_entity_t HTMLEntities[] =
{
    { '<', "&lt;" },
    { '>', "&gt;" },
};

static const int numHTMLEntities = sizeof(HTMLEntities) / sizeof(HTMLEntities[0]);


char *nn_encode_str2(const char *str)
{
    const char *s = str;
    char *result;
    size_t resSize, resPos = 0;

    if (str == NULL) return NULL;

    resSize = strlen(str) + NN_ALLOC_SIZE;
    if ((result = th_malloc(resSize)) == NULL)
        return NULL;

    while (*s)
    {
        int i;
        BOOL found = FALSE;
        for (i = 0; i < numHTMLEntities; i++)
            if (HTMLEntities[i].c == *s)
            {
                PUSHSTR(HTMLEntities[i].ent);
                found = TRUE;
                break;
            }
        if (!found) PUSHCHAR(*s);

        s++;
    }
    PUSHCHAR(0);

    return result;
}


char *nn_decode_str2(const char *str)
{
    const char *s = str;
    char *result;
    size_t resSize, resPos = 0;

    if (str == NULL) return NULL;

    resSize = strlen(str);
    if ((result = th_malloc(resSize)) == NULL)
        return NULL;

    while (*s)
    {
        if (*s == '&')
        {
            int i;
            BOOL found = FALSE;
            for (i = 0; i < numHTMLEntities; i++)
            {
                const html_entity_t *ent = &HTMLEntities[i];
                size_t len = strlen(ent->ent);
                if (!strncmp(s, ent->ent, len))
                {
                    PUSHCHAR(ent->c);
                    s += len;
                    found = TRUE;
                    break;
                }
            }
            if (!found) PUSHCHAR(*s++);
        }
        else
            PUSHCHAR(*s++);
    }
    PUSHCHAR(0);

    return result;
}


char *nn_dbldecode_str(const char *str)
{
    char *res, *tmp;

    if ((tmp = nn_decode_str1(str)) == NULL)
        return NULL;

    res = nn_decode_str2(tmp);
    th_free(tmp);

    return res;
}


char *nn_dblencode_str(const char *str)
{
    char *res, *tmp;

    if ((tmp = nn_encode_str2(str)) == NULL)
        return NULL;

    res = nn_encode_str1(tmp);
    th_free(tmp);

    return res;
}


int nn_editbuf_write(nn_editbuf_t *buf, size_t pos, char ch)
{
    if (buf->len+1 >= buf->size) return -3;

    if (pos >= buf->len)
        buf->data[buf->len++] = ch;
    else
        buf->data[pos] = ch;

    buf->dirty = TRUE;
    return 0;
}


int nn_editbuf_insert(nn_editbuf_t *buf, size_t pos, char ch)
{
    if (buf->len+1 >= buf->size) return -3;

    if (pos >= buf->len)
    {
        buf->data[buf->len] = ch;
    }
    else
    {
        memmove(&(buf->data[pos+1]), &(buf->data[pos]), buf->len - pos + 1);
        buf->data[pos] = ch;
    }
    buf->len++;

    buf->dirty = TRUE;
    return 0;
}


int nn_editbuf_delete(nn_editbuf_t *buf, size_t pos)
{
    if (pos < buf->len)
    {
        memmove(&(buf->data[pos]), &(buf->data[pos+1]), buf->len - pos);
        buf->len--;
        buf->dirty = TRUE;
        return 0;
    }
    else
        return -2;
}


void nn_editbuf_clear(nn_editbuf_t *buf)
{
    buf->len = 0;
    buf->pos = 0;
    buf->dirty = TRUE;
}


nn_editbuf_t * nn_editbuf_new(size_t n)
{
    nn_editbuf_t *res = th_malloc0(sizeof(nn_editbuf_t));

    res->data = (char *) th_malloc(n);
    res->size = n;
    res->dirty = TRUE;

    return res;
}


void nn_editbuf_free(nn_editbuf_t *buf)
{
    if (buf != NULL)
    {
        th_free(buf->data);
        th_free(buf);
    }
}


nn_editbuf_t * nn_editbuf_copy(nn_editbuf_t *src)
{
    nn_editbuf_t *res;

    assert(src != NULL);

    if (src == NULL) return NULL;

    if ((res = nn_editbuf_new(src->size)) == NULL)
        return NULL;

    memcpy(res->data, src->data, src->size);
    res->pos = res->len = src->len;
    res->dirty = TRUE;

    return res;
}


char * nn_editbuf_get_string(nn_editbuf_t *buf, size_t start, size_t end)
{
    char *str;
    size_t siz;

    if (buf == NULL || end > buf->len || start >= buf->len)
        return NULL;

    if (start <= end)
        siz = end - start + 1;
    else
        return NULL;

    if ((str = th_malloc(siz + 1)) == NULL)
        return NULL;

    memcpy(str, buf->data + start, siz);
    str[siz] = 0;

    return str;
}


void nn_editbuf_setpos(nn_editbuf_t *buf, size_t pos)
{
    assert(buf != NULL);

    if (pos >= buf->len)
        buf->pos = buf->len;
    else
        buf->pos = pos;

    buf->dirty = TRUE;
}


static uint8_t nn_hash_user(const char *name)
{
/*
    int n = 0;
    const uint8_t *c = (uint8_t *)name;
    uint8_t hash = 0xff;
    
    while (*c && n < 4)
    {
        hash = (hash << 1) ^ tolower(*c);
        c++; n++;
    }
    
    return (hash & 0xff);
*/
    return (uint8_t) tolower(name[0]);
}


static void nn_user_insert(nn_user_t **list, nn_user_t *node)
{
    node->next = *list;
    *list = node;
}


static void nn_user_free(nn_user_t *user)
{
    th_free(user->name);
    th_free(user);
}


nn_user_t *nn_userhash_foreach(const nn_userhash_t *list, int (*func)(const nn_user_t *, void *userdata), void *data)
{
    int i;

    if (list == NULL) return NULL;

    for (i = 0; i < NN_NUM_BUCKETS; i++)
        if (list->buckets[i] != NULL)
        {
            nn_user_t *curr = list->buckets[i];
            while (curr != NULL)
            {
                if (func(curr, data) != 0)
                    return curr;
                curr = curr->next;
            }
        }

    return NULL;
}


nn_user_t *nn_userhash_find(const nn_userhash_t *list, const char *name)
{
    uint8_t hash;

    if (list == NULL) return NULL;

    hash = nn_hash_user(name);
    if (list->buckets[hash] != NULL)
    {
        nn_user_t *curr = list->buckets[hash];
        while (curr != NULL)
        {
            if (th_strcasecmp(curr->name, name) == 0)
                return curr;
            curr = curr->next;
        }
    }

    return NULL;
}


static nn_user_t *nn_userhash_match_do(nn_user_t *list, const char *pattern, size_t len)
{
    nn_user_t *node;
    for (node = list; node != NULL; node = node->next)
    {
        if (len <= strlen(node->name) && th_strncasecmp(node->name, pattern, len) == 0)
            return node;
    }
    return NULL;
}


nn_user_t *nn_userhash_match(const nn_userhash_t *list, const char *pattern, const char *current, BOOL again)
{
    uint8_t hash;

    if (list == NULL || pattern == NULL) return NULL;

    hash = nn_hash_user(pattern);
    if (list->buckets[hash] != NULL)
    {
        nn_user_t *node;
        size_t len = strlen(pattern);

        if (current != NULL)
        {
            for (node = list->buckets[hash]; node != NULL; node = node->next)
            {
                if (th_strcasecmp(node->name, current) == 0)
                {
                    nn_user_t *found;

                    if (again)
                        return node;

                    if ((found = nn_userhash_match_do(node->next, pattern, len)) != NULL)
                        return found;
                }
            }
        }

        if ((node = nn_userhash_match_do(list->buckets[hash], pattern, len)) != NULL)
            return node;
    }

    return NULL;
}


nn_userhash_t *nn_userhash_new(void)
{
    return th_malloc0(sizeof(nn_userhash_t));
}


int nn_userhash_insert(nn_userhash_t *list, const char *name)
{
    uint8_t hash;
    nn_user_t *user;

    // Check arguments
    if (list == NULL || name == NULL)
        return -1;

    // Check if username is already there
    if (nn_userhash_find(list, name) != NULL)
        return 1;

    // No, we'll add it
    if ((user = th_malloc0(sizeof(nn_user_t))) == NULL)
        return -3;

    user->name = th_strdup(name);
    if (user->name == NULL)
        return -4;

    hash = nn_hash_user(name);
    nn_user_insert(&(list->buckets[hash]), user);

    return 0;
}


int nn_userhash_delete(nn_userhash_t *list, const char *name)
{
    uint8_t hash;

    // Check arguments
    if (list == NULL || name == NULL)
        return -1;

    // Check if username is already there
    hash = nn_hash_user(name);
    if (list->buckets[hash] != NULL)
    {
        nn_user_t *curr, *prev;
        curr = list->buckets[hash];
        prev = NULL;
        while (curr != NULL)
        {
            if (th_strcasecmp(curr->name, name) == 0)
            {
                if (prev)
                    prev->next = curr->next;
                else
                    list->buckets[hash] = curr->next;

                nn_user_free(curr);

                return 0;
            }
            else
            {
                prev = curr;
                curr = curr->next;
            }
        }
    }

    return 1;
}


void nn_userhash_free(nn_userhash_t *hash)
{
    int i;
    if (hash == NULL)
        return;

    for (i = 0; i < NN_NUM_BUCKETS; i++)
    {
        nn_user_t *curr = hash->buckets[i];

        while (curr != NULL)
        {
            nn_user_t *next = curr->next;
            nn_user_free(curr);
            curr = next;
        }

        hash->buckets[i] = NULL;
    }

    th_free(hash);
}


char *nn_username_encode(char *str)
{
    unsigned char *c = (unsigned char *) str;
    if (str == NULL) return NULL;
    for (; *c ; c++)
        if (*c == ' ') *c = 255;
    return str;
}


char *nn_username_decode(char *str)
{
    unsigned char *c = (unsigned char *) str;
    if (str == NULL) return NULL;
    for (; *c ; c++)
        if (*c == 255) *c = ' ';
    return str;
}


nn_strtuple_t *nn_strtuple_new(size_t len, char *str)
{
    nn_strtuple_t *tuple = th_malloc0(sizeof(nn_strtuple_t));
    tuple->len = len;
    tuple->str = str;
    return tuple;
}


void nn_strtuple_free(nn_strtuple_t *tuple)
{
    th_free(tuple->str);
    th_free(tuple);
}