view th_util.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 a6304a3719ee
children
line wrap: on
line source

/*
 * Generic utility-functions, macros and defaults
 * Programmed and designed by Matti 'ccr' Hamalainen
 * (C) Copyright 2002-2010 Tecnic Software productions (TNSP)
 *
 * Please read file 'COPYING' for information on license and distribution.
 */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "th_util.h"
#include <stdio.h>

/* Default settings
 */
static BOOL    th_initialized = FALSE;
int    th_verbosityLevel = 2;
char    *th_prog_name = NULL,
    *th_prog_fullname = NULL,
    *th_prog_version = NULL,
    *th_prog_author = NULL,
    *th_prog_license = NULL;


/* Initialize th_util-library and global variables
 */
void th_init(char *progName, char *progFullName, char *progVersion,
    char *progAuthor, char *progLicense)
{
    th_prog_name = progName;
    th_prog_fullname = progFullName;
    th_prog_version = progVersion;

    if (progAuthor)
        th_prog_author = progAuthor;
    else
        th_prog_author = TH_PROG_AUTHOR;

    if (progLicense)
        th_prog_license = progLicense;
    else
        th_prog_license = TH_PROG_LICENSE;

    th_initialized = TRUE;
}


/* Print formatted error, warning and information messages
 * TODO: Implement th_vfprintf() and friends?
 */
void THERR_V(const char *fmt, va_list ap)
{
    assert(th_initialized == TRUE);

    fprintf(stderr, "%s: ", th_prog_name);
    vfprintf(stderr, fmt, ap);
}


void THMSG_V(int level, const char *fmt, va_list ap)
{
    assert(th_initialized == TRUE);

    if (th_verbosityLevel >= level) {
        fprintf(stderr, "%s: ", th_prog_name);
        vfprintf(stderr, fmt, ap);
    }
}


void THPRINT_V(int level, const char *fmt, va_list ap)
{
    assert(th_initialized == TRUE);

    if (th_verbosityLevel >= level) {
        vfprintf(stderr, fmt, ap);
    }
}


void THERR(const char *fmt, ...)
{
    va_list ap;
    assert(th_initialized == TRUE);

    va_start(ap, fmt);
    THERR_V(fmt, ap);
    va_end(ap);
}


void THMSG(int level, const char *fmt, ...)
{
    va_list ap;
    assert(th_initialized == TRUE);

    va_start(ap, fmt);
    THMSG_V(level, fmt, ap);
    va_end(ap);
}


void THPRINT(int level, const char *fmt, ...)
{
    va_list ap;
    assert(th_initialized == TRUE);

    va_start(ap, fmt);
    THPRINT_V(level, fmt, ap);
    va_end(ap);
}


/* Memory handling routines
 */
void *th_malloc(size_t l)
{
    return malloc(l);
}


void *th_calloc(size_t n, size_t l)
{
    return calloc(n, l);
}


void *th_realloc(void *p, size_t l)
{
    return realloc(p, l);
}


void th_free(void *p)
{
    /* Check for NULL pointers for portability due to some libc
     * implementations not handling free(NULL) too well.
     */
    if (p) free(p);
}


#ifndef HAVE_MEMSET
void *th_memset(void *p, int c, size_t n)
{
    unsigned char *dp = (unsigned char *) p;
    
    while (n--)
        *(dp++) = c;

    return p;
}
#endif


/* Doubly linked list handling
 *
 * In this implementation first node's prev points to last node of the list,
 * and last node's next is NULL. This way we can semi-efficiently traverse to
 * beginning and end of the list, assuming user does not do weird things.
 */
qlist_t * th_llist_new(void *data)
{
    qlist_t *res = th_calloc(sizeof(qlist_t), 1);
    res->data = data;
    return res;
}

void th_llist_free_func(qlist_t *list, void (*freefunc)(void *data))
{
    qlist_t *curr = list;

    while (curr != NULL) {
        qlist_t *next = curr->next;
        if (freefunc != NULL && curr->data != NULL)
            freefunc(curr->data);
        th_free(curr);
        curr = next;
    }
}


void th_llist_free(qlist_t *list)
{
    th_llist_free_func(list, NULL);
}

void th_llist_append_node(qlist_t **list, qlist_t *node)
{
    if (*list != NULL) {
        node->prev = (*list)->prev;
        (*list)->prev->next = node;
        (*list)->prev = node;
        (*list)->num++;
    } else {
        *list = node;
        node->prev = *list;
        (*list)->num = 1;
    }

    node->next = NULL;
}


qlist_t *th_llist_append(qlist_t **list, void *data)
{
    qlist_t *node = th_llist_new(data);

    th_llist_append_node(list, node);

    return node;
}


void th_llist_prepend_node(qlist_t **list, qlist_t *node)
{
    if (*list != NULL) {
        node->prev = (*list)->prev;
        node->next = *list;
        (*list)->prev = node;
        node->num = (*list)->num + 1;
        *list = node;
    } else {
        *list = node->prev = node;
        node->next = NULL;
        (*list)->num = 1;
    }

}


qlist_t *th_llist_prepend(qlist_t **list, void *data)
{
    qlist_t *node = th_llist_new(data);

    th_llist_prepend_node(list, node);

    return node;
}

/*
1) Remove a middle node

    node0->prev->next = node->next (node1)
    node0->next->prev = node->prev (list)

    node2 <- list <=> node0 <=> node1 <=> node2 -> NULL
    node2 <- list <=> node1 <=> node2 -> NULL

2) Remove first node when many items


    node2 <- list <=> node0 <=> node1 <=> node2 -> NULL
    node2 <- node0 <=> node1 <=> node2 -> NULL

    *list = node0

3) Remove last node in list

    if (node->next == NULL) {
        list->prev = node->prev;
        node->prev->next = NULL;
    }

    node2 <- list <=> node0 <=> node1 <=> node2 -> NULL
    node1 <- list <=> node0 <=> node1 -> NULL

4) Remove last

    list <- list -> NULL
    
    
*/
static void th_llist_delete_node_fast(qlist_t **list, qlist_t *node)
{
    if (node == *list) {
        /* First node in list */
        qlist_t *tmp = (*list)->next;
        if (tmp != NULL) {
            tmp->num = (*list)->num - 1;
            tmp->prev = (*list)->prev;
        }
        *list = tmp;
    } else {
        /* Somewhere in middle or end */
        if (node->prev != NULL)
            node->prev->next = node->next;

        if (node->next != NULL)
            node->next->prev = node->prev;
        else
            (*list)->prev = node; /* Last node */

        (*list)->num--;
    }
    
    node->next = node->prev = NULL;
}


void th_llist_delete_node(qlist_t **list, qlist_t *node)
{
    qlist_t *curr = *list;

    while (curr != NULL) {
        qlist_t *next = curr->next;
        if (curr == node) {
            th_llist_delete_node_fast(list, curr);
            th_free(node);
            break;
        }
        curr = next;
    }
}


void th_llist_delete(qlist_t **list, const void *data)
{
    qlist_t *curr = *list;

    while (curr != NULL) {
        qlist_t *next = curr->next;
        if (curr->data == data) {
            th_llist_delete_node_fast(list, curr);
            th_free(curr);
            break;
        }
        curr = next;
    }
}


qlist_t * th_llist_get_nth(qlist_t *list, const size_t n)
{
    qlist_t *curr = list;
    size_t i;

    for (i = 0; curr != NULL && i < n; curr = curr->next, i++);

    return curr;
}


size_t th_llist_length(const qlist_t *list)
{
    if (list == NULL)
        return 0;
    else
        return list->num;
}


ssize_t th_llist_position(const qlist_t *list, const qlist_t *node)
{
    const qlist_t *curr = list;
    ssize_t i = 0;

    while (curr != NULL) {
        if (curr == node)
            return i;
        else
            i++;

        curr = curr->next;
    }
    
    return -1;
}


qlist_t * th_llist_find(qlist_t *list, const void *data)
{
    qlist_t *curr = list;

    while (curr != NULL) {
        if (curr->data == data)
            return curr;
        curr = curr->next;
    }

    return NULL;
}


qlist_t * th_llist_find_func(qlist_t *list, const void *userdata, int (compare)(const void *, const void *))
{
    qlist_t *curr = list;

    while (curr != NULL) {
        if (compare(curr->data, userdata) == 0)
            return curr;
        curr = curr->next;
    }

    return NULL;
}


/*
 * Ringbuffers
 */
qringbuf_t * th_ringbuf_new(const size_t size, void (*mdeallocator)(void *data))
{
    qringbuf_t *res = th_calloc(1, sizeof(qringbuf_t));
    
    res->data = (char **) th_calloc(size, sizeof(char *));
    res->size = size;
    res->n = 0;
    res->deallocator = mdeallocator;
    
    return res;
}


BOOL th_ringbuf_grow(qringbuf_t *buf, const size_t n)
{
    buf->data = (char **) th_realloc(buf->data, (buf->size + n) * sizeof(char *));
    if (buf->data != NULL) {
        memset(buf->data + buf->size, 0, sizeof(char *) * n);
        buf->size += n;
        return TRUE;
    } else
        return FALSE;
}


void th_ringbuf_free(qringbuf_t *buf)
{
    int i;
    
    for (i = 0; i < buf->size; i++)
        if (buf->data[i] != NULL)
            buf->deallocator(buf->data[i]);
    
    th_free(buf->data);
    th_free(buf);
}


void th_ringbuf_add(qringbuf_t *buf, void *ptr)
{
    if (buf->n < buf->size)
        buf->n++;

    th_free(buf->data[0]);
    memmove(&(buf->data[0]), &(buf->data[1]), (buf->size - 1) * sizeof(void *));
    buf->data[buf->size - 1] = ptr;
}