Mercurial > hg > th-libs
changeset 16:0cea9c0cfce7
Sync.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 02 Nov 2010 23:22:44 +0200 |
parents | 4adf7093060c |
children | 77e34ec14f05 |
files | th_config.c th_config.h th_util.c th_util.h |
diffstat | 4 files changed, 437 insertions(+), 91 deletions(-) [+] |
line wrap: on
line diff
--- a/th_config.c Sat Oct 30 17:48:40 2010 +0300 +++ b/th_config.c Tue Nov 02 23:22:44 2010 +0200 @@ -1,7 +1,7 @@ /* * Very simple configuration handling functions * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2004-2008 Tecnic Software productions (TNSP) + * (C) Copyright 2004-2010 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ @@ -19,15 +19,15 @@ /* Free a given configuration (the values are not free'd) */ -void th_config_free(cfgitem_t *cfg) +void th_cfg_free(cfgitem_t *cfg) { cfgitem_t *curr = cfg; while (curr != NULL) { cfgitem_t *next = curr->next; - if (curr->type == ITEM_BLOCK) - th_config_free((cfgitem_t *) curr->data); + if (curr->type == ITEM_SECTION) + th_cfg_free((cfgitem_t *) curr->data); th_free(curr->name); th_free(curr); @@ -38,8 +38,7 @@ /* Allocate and add new item to configuration */ -static cfgitem_t *th_config_add(cfgitem_t **cfg, char *name, int type, - BOOL (*validate)(cfgitem_t *), void *data) +static cfgitem_t *th_cfg_add(cfgitem_t **cfg, const char *name, const int type, void *data) { cfgitem_t *node; @@ -54,7 +53,6 @@ /* Set values */ node->type = type; node->data = data; - node->validate = validate; node->name = th_strdup(name); /* Insert into linked list */ @@ -74,12 +72,12 @@ /* Add integer type setting into give configuration */ -int th_config_add_int(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), +int th_cfg_add_int(cfgitem_t ** cfg, char * name, int *itemData, int itemDef) { cfgitem_t *node; - node = th_config_add(cfg, name, ITEM_INT, itemValidate, (void *) itemData); + node = th_cfg_add(cfg, name, ITEM_INT, (void *) itemData); if (node == NULL) return -1; @@ -89,12 +87,12 @@ } -int th_config_add_hexvalue(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), +int th_cfg_add_hexvalue(cfgitem_t ** cfg, char * name, int *itemData, int itemDef) { cfgitem_t *node; - node = th_config_add(cfg, name, ITEM_HEX_TRIPLET, itemValidate, (void *) itemData); + node = th_cfg_add(cfg, name, ITEM_HEX_TRIPLET, (void *) itemData); if (node == NULL) return -1; @@ -106,12 +104,12 @@ /* Add unsigned integer type setting into give configuration */ -int th_config_add_uint(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), +int th_cfg_add_uint(cfgitem_t ** cfg, char * name, unsigned int * itemData, unsigned int itemDef) { cfgitem_t *node; - node = th_config_add(cfg, name, ITEM_UINT, itemValidate, (void *) itemData); + node = th_cfg_add(cfg, name, ITEM_UINT, (void *) itemData); if (node == NULL) return -1; @@ -123,12 +121,12 @@ /* Add strint type setting into given configuration */ -int th_config_add_string(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), +int th_cfg_add_string(cfgitem_t ** cfg, char * name, char ** itemData, char * itemDef) { cfgitem_t *node; - node = th_config_add(cfg, name, ITEM_STRING, itemValidate, (void *) itemData); + node = th_cfg_add(cfg, name, ITEM_STRING, (void *) itemData); if (node == NULL) return -1; @@ -140,12 +138,12 @@ /* Add boolean type setting into given configuration */ -int th_config_add_bool(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), +int th_cfg_add_bool(cfgitem_t ** cfg, char * name, BOOL * itemData, BOOL itemDef) { cfgitem_t *node; - node = th_config_add(cfg, name, ITEM_BOOL, itemValidate, (void *) itemData); + node = th_cfg_add(cfg, name, ITEM_BOOL, (void *) itemData); if (node == NULL) return -1; @@ -157,11 +155,11 @@ /* Add implicit comment */ -int th_config_add_comment(cfgitem_t ** cfg, char * comment) +int th_cfg_add_comment(cfgitem_t ** cfg, char * comment) { cfgitem_t *node; - node = th_config_add(cfg, comment, ITEM_COMMENT, NULL, NULL); + node = th_cfg_add(cfg, comment, ITEM_COMMENT, NULL); if (node == NULL) return -1; @@ -169,13 +167,28 @@ } -/* Add new block +/* Add new section */ -int th_config_add_section(cfgitem_t ** cfg, char * name, cfgitem_t *data) +int th_cfg_add_section(cfgitem_t ** cfg, char * name, cfgitem_t *data) { cfgitem_t *node; - node = th_config_add(cfg, name, ITEM_BLOCK, NULL, (void *) data); + node = th_cfg_add(cfg, name, ITEM_SECTION, (void *) data); + if (node == NULL) + return -1; + + return 0; +} + + +int th_cfg_add_string_list(cfgitem_t ** cfg, char * name, qlist_t **data) +{ + cfgitem_t *node; + + if (data == NULL) + return -5; + + node = th_cfg_add(cfg, name, ITEM_STRING_LIST, (void *) data); if (node == NULL) return -1; @@ -196,7 +209,8 @@ PM_STRING, PM_INT, PM_BOOL, - PM_BLOCK + PM_SECTION, + PM_ARRAY }; #define VADDCH(ch) if (strPos < SET_MAX_BUF) { tmpStr[strPos++] = ch; } @@ -209,7 +223,7 @@ } conffile_t; -static void th_config_error(conffile_t *f, const char *fmt, ...) +static void th_cfg_error(conffile_t *f, const char *fmt, ...) { va_list ap; va_start(ap, fmt); @@ -219,7 +233,7 @@ } -static int th_config_read_sect(conffile_t *f, cfgitem_t * cfg, int nesting) +static int th_cfg_read_sect(conffile_t *f, cfgitem_t * cfg, int nesting) { cfgitem_t *item = NULL; char tmpStr[SET_MAX_BUF + 1]; @@ -241,7 +255,7 @@ switch (c = fgetc(f->file)) { case EOF: if (parseMode != PM_NORMAL) { - th_config_error(f, + th_cfg_error(f, "Unexpected end of file.\n"); parseMode = PM_ERROR; } else @@ -277,8 +291,7 @@ /* Check for validation errors */ return (validError) ? 1 : 0; } else { - th_config_error(f, - "HMMM!\n"); + th_cfg_error(f, "Invalid nesting sequence encountered.\n"); parseMode = PM_ERROR; } } else if (th_isalpha(c)) { @@ -288,7 +301,7 @@ strPos = 0; } else { /* Error! Invalid character found */ - th_config_error(f, + th_cfg_error(f, "Unexpected character '%c'.\n", c); parseMode = PM_ERROR; } @@ -312,7 +325,7 @@ else { /* Error! Key name string too long! */ - th_config_error(f, + th_cfg_error(f, "Config key name too long!"); parseMode = PM_ERROR; } @@ -320,7 +333,7 @@ } else { /* Error! Invalid character found */ tmpStr[strPos] = 0; - th_config_error(f, + th_cfg_error(f, "Unexpected character '%c' in key name '%s'.\n", c, tmpStr); parseMode = PM_ERROR; } @@ -348,6 +361,10 @@ nextMode = PM_STRING; break; + case ITEM_STRING_LIST: + nextMode = PM_ARRAY; + break; + case ITEM_INT: case ITEM_UINT: nextMode = PM_INT; @@ -357,8 +374,8 @@ nextMode = PM_BOOL; break; - case ITEM_BLOCK: - nextMode = PM_BLOCK; + case ITEM_SECTION: + nextMode = PM_SECTION; break; } @@ -368,7 +385,7 @@ strPos = 0; } else { /* Error! No configuration key by this name found */ - th_config_error(f, + th_cfg_error(f, "No such configuration setting ('%s')\n", tmpStr); parseMode = PM_ERROR; } @@ -376,7 +393,7 @@ c = -1; } else { /* Error! '=' expected! */ - th_config_error(f, + th_cfg_error(f, "Unexpected character '%c', assignation '=' was expected.\n", c); parseMode = PM_ERROR; } @@ -398,15 +415,39 @@ } break; - case PM_BLOCK: - /* Block parsing mode */ + case PM_ARRAY: + if (isStart) { + switch (item->type) { + case ITEM_STRING_LIST: + prevMode = parseMode; + parseMode = PM_STRING; + break; + } + } else if (c == ',') { + switch (item->type) { + case ITEM_STRING_LIST: + c = -1; + isStart = TRUE; + prevMode = parseMode; + parseMode = PM_NEXT; + nextMode = PM_STRING; + break; + } + } else { + prevMode = parseMode; + parseMode = PM_NORMAL; + } + break; + + case PM_SECTION: + /* Section parsing mode */ if (c != '{') { - /* Error! Block start '{' expected! */ - th_config_error(f, - "Unexpected character '%c', block start '{' was expected.\n", c); + /* Error! Section start '{' expected! */ + th_cfg_error(f, + "Unexpected character '%c', section start '{' was expected.\n", c); parseMode = PM_ERROR; } else { - int res = th_config_read_sect(f, (cfgitem_t *) item->data, nesting + 1); + int res = th_cfg_read_sect(f, (cfgitem_t *) item->data, nesting + 1); c = -1; if (res > 0) validError = TRUE; @@ -430,23 +471,32 @@ /* End of string, set the value */ tmpStr[strPos] = 0; - if (item->type == ITEM_HEX_TRIPLET) { - } else if (item->type == ITEM_STRING) { - th_pstrcpy((char **) item->data, tmpStr); + switch (item->type) { + case ITEM_HEX_TRIPLET: + *(int *) item->data = th_get_hex_triplet(tmpStr); + prevMode = parseMode; + parseMode = PM_NORMAL; + break; + case ITEM_STRING: + th_pstrcpy((char **) item->data, tmpStr); + prevMode = parseMode; + parseMode = PM_NORMAL; + break; + case ITEM_STRING_LIST: + th_llist_append(item->list, th_strdup(tmpStr)); + prevMode = parseMode; + parseMode = PM_NEXT; + nextMode = PM_ARRAY; + break; } - if (item->validate != NULL && !item->validate(item)) - validError = TRUE; - - prevMode = parseMode; - parseMode = PM_NORMAL; } else { /* Add character to string */ VADDCH(c) else { /* Error! String too long! */ - th_config_error(f, + th_cfg_error(f, "String too long! Maximum is %d characters.", SET_MAX_BUF); parseMode = PM_ERROR; @@ -460,7 +510,7 @@ /* Integer parsing mode */ if (isStart && item->type == ITEM_UINT && c == '-') { /* Error! Negative values not allowed for unsigned ints */ - th_config_error(f, + th_cfg_error(f, "Negative value specified for %s, unsigned value expected.", item->name); parseMode = PM_ERROR; @@ -484,14 +534,12 @@ *((unsigned int *) item->data) = atol(tmpStr); break; } - if (item->validate != NULL && !item->validate(item)) - validError = TRUE; prevMode = parseMode; parseMode = PM_NORMAL; } else { /* Error! Unexpected character. */ - th_config_error(f, + th_cfg_error(f, "Unexpected character '%c' for integer setting '%s'.", c, item->name); parseMode = PM_ERROR; @@ -499,7 +547,7 @@ if (isError) { /* Error! String too long! */ - th_config_error(f, "String too long! Maximum is %d characters.", + th_cfg_error(f, "String too long! Maximum is %d characters.", SET_MAX_BUF); parseMode = PM_ERROR; } @@ -535,14 +583,11 @@ } if (isError) { - th_config_error(f, "Invalid boolean value for '%s'.\n", item->name); + th_cfg_error(f, "Invalid boolean value for '%s'.\n", item->name); parseMode = PM_ERROR; } else { *((BOOL *) item->data) = tmpBool; - if (item->validate != NULL && !item->validate(item)) - validError = TRUE; - prevMode = parseMode; parseMode = PM_NORMAL; } @@ -564,7 +609,7 @@ } -int th_config_read(FILE *inFile, char *filename, cfgitem_t * cfg) +int th_cfg_read(FILE *inFile, char *filename, cfgitem_t * cfg) { conffile_t f; @@ -572,7 +617,7 @@ f.filename = filename; f.line = 1; - return th_config_read_sect(&f, cfg, 0); + return th_cfg_read_sect(&f, cfg, 0); } @@ -586,7 +631,7 @@ } -static int th_config_write_sect(conffile_t *f, cfgitem_t *item, int nesting) +static int th_cfg_write_sect(conffile_t *f, cfgitem_t *item, int nesting) { while (item != NULL) { if (item->type == ITEM_COMMENT) { @@ -599,42 +644,67 @@ switch (item->type) { case ITEM_STRING: - if (*((char **) item->data) == NULL) { + if (*(item->val_str) == NULL) { if (fprintf(f->file, "#%s = \"\"\n", item->name) < 0) return -3; } else { if (fprintf(f->file, "%s = \"%s\"\n", - item->name, *((char **) item->data)) < 0) + item->name, *(item->val_str)) < 0) + return -3; + } + break; + + case ITEM_STRING_LIST: + if (*(item->list) == NULL) { + if (fprintf(f->file, "#%s = \"\", \"\"\n", item->name) < 0) + return -3; + } else { + qlist_t *node = *(item->list); + size_t n = th_llist_length(node); + if (fprintf(f->file, "%s = ", item->name) < 0) + return -3; + + while (node != NULL) { + if (node->data != NULL) + fprintf(f->file, "\"%s\"", (char *) node->data); + + if (--n > 0) + fprintf(f->file, ", "); + + node = node->next; + } + + if (fprintf(f->file, "\n") < 0) return -3; } break; case ITEM_INT: if (fprintf(f->file, "%s = %i\n", - item->name, *((int *) item->data)) < 0) + item->name, *(item->val_int)) < 0) return -4; break; case ITEM_UINT: if (fprintf(f->file, "%s = %d\n", - item->name, *((unsigned int *) item->data)) < 0) + item->name, *(item->val_uint)) < 0) return -5; break; case ITEM_BOOL: if (fprintf(f->file, "%s = %s\n", - item->name, *((BOOL *) item->data) ? "yes" : "no") < 0) + item->name, *(item->val_bool) ? "yes" : "no") < 0) return -6; break; - case ITEM_BLOCK: + case ITEM_SECTION: { int res; - if (fprintf(f->file, "\n%s = {\n", item->name) < 0) + if (fprintf(f->file, "%s = {\n", item->name) < 0) return -7; - res = th_config_write_sect(f, (cfgitem_t *) item->data, nesting + 1); + res = th_cfg_write_sect(f, (cfgitem_t *) item->data, nesting + 1); if (res != 0) return res; - if (fprintf(f->file, "} # End of '%s'\n\n", item->name) < 0) + if (fprintf(f->file, "}\n\n") < 0) return -8; } break; @@ -653,7 +723,7 @@ } -int th_config_write(FILE *outFile, char *filename, cfgitem_t *cfg) +int th_cfg_write(FILE *outFile, char *filename, cfgitem_t *cfg) { conffile_t f; @@ -667,7 +737,7 @@ fprintf(outFile, "# Configuration written by %s %s\n\n", th_prog_fullname, th_prog_version); - return th_config_write_sect(&f, cfg, 0); + return th_cfg_write_sect(&f, cfg, 0); }
--- a/th_config.h Sat Oct 30 17:48:40 2010 +0300 +++ b/th_config.h Tue Nov 02 23:22:44 2010 +0200 @@ -19,14 +19,17 @@ /* Definitions */ enum ITEM_TYPE { - ITEM_BLOCK = 1, + ITEM_SECTION = 1, ITEM_COMMENT, ITEM_STRING, ITEM_INT, ITEM_UINT, ITEM_BOOL, ITEM_FLOAT, - ITEM_HEX_TRIPLET + ITEM_HEX_TRIPLET, + + ITEM_STRING_LIST, + ITEM_HEX_TRIPLET_LIST }; @@ -34,14 +37,15 @@ int type; char *name; union { - void *data; int *val_int; unsigned int *val_uint; - char *val_str; + char **val_str; BOOL *val_bool; + + void *data; + qlist_t **list; struct _cfgitem_t *section; }; - BOOL (*validate)(struct _cfgitem_t *); struct _cfgitem_t *next, *prev; } cfgitem_t; @@ -49,19 +53,20 @@ /* Functions */ -int th_config_read(FILE *, char *, cfgitem_t *); -void th_config_free(cfgitem_t *); -int th_config_write(FILE *, char *, cfgitem_t *); +int th_cfg_read(FILE *, char *, cfgitem_t *); +void th_cfg_free(cfgitem_t *); +int th_cfg_write(FILE *, char *, cfgitem_t *); -int th_config_add_section(cfgitem_t **cfg, char *name, cfgitem_t *data); -int th_config_add_comment(cfgitem_t **cfg, char *comment); -int th_config_add_int(cfgitem_t **cfg, char *name, BOOL (*validate)(cfgitem_t *), int *data, int itemDef); -int th_config_add_uint(cfgitem_t **cfg, char *name, BOOL (*validate)(cfgitem_t *), unsigned int *data, unsigned int itemDef); -int th_config_add_string(cfgitem_t **cfg, char *name, BOOL (*validate)(cfgitem_t *), char **data, char *itemDef); -int th_config_add_bool(cfgitem_t **cfg, char *name, BOOL (*validate)(cfgitem_t *), BOOL *data, BOOL itemDef); -int th_config_add_float(cfgitem_t **cfg, char *name, BOOL (*validate)(cfgitem_t *), float *data, float itemDef); -int th_config_add_hexvalue(cfgitem_t **cfg, char *name, BOOL (*validate)(cfgitem_t *), int *data, int itemDef); +int th_cfg_add_section(cfgitem_t **cfg, char *name, cfgitem_t *data); +int th_cfg_add_comment(cfgitem_t **cfg, char *comment); +int th_cfg_add_int(cfgitem_t **cfg, char *name, int *data, int itemDef); +int th_cfg_add_uint(cfgitem_t **cfg, char *name, unsigned int *data, unsigned int itemDef); +int th_cfg_add_string(cfgitem_t **cfg, char *name, char **data, char *itemDef); +int th_cfg_add_bool(cfgitem_t **cfg, char *name, BOOL *data, BOOL itemDef); +int th_cfg_add_float(cfgitem_t **cfg, char *name, float *data, float itemDef); +int th_cfg_add_hexvalue(cfgitem_t **cfg, char *name, int *data, int itemDef); +int th_cfg_add_string_list(cfgitem_t **cfg, char *name, qlist_t **list); #ifdef __cplusplus }
--- a/th_util.c Sat Oct 30 17:48:40 2010 +0300 +++ b/th_util.c Tue Nov 02 23:22:44 2010 +0200 @@ -1,7 +1,7 @@ /* * Generic utility-functions, macros and defaults * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2002-2008 Tecnic Software productions (TNSP) + * (C) Copyright 2002-2010 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ @@ -151,3 +151,246 @@ 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; +}
--- a/th_util.h Sat Oct 30 17:48:40 2010 +0300 +++ b/th_util.h Tue Nov 02 23:22:44 2010 +0200 @@ -1,7 +1,7 @@ /* * Generic utility-functions, macros and defaults * Programmed and designed by Matti 'ccr' Hamalainen - * (C) Copyright 2002-2009 Tecnic Software productions (TNSP) + * (C) Copyright 2002-2010 Tecnic Software productions (TNSP) * * Please read file 'COPYING' for information on license and distribution. */ @@ -15,6 +15,7 @@ #include "th_types.h" #include <stdarg.h> #include <stdlib.h> +#include <sys/types.h> #ifndef HAVE_NO_ASSERT #include <assert.h> #endif @@ -83,6 +84,33 @@ void *th_memset(void *, int, size_t); #endif + +/* Doubly linked list handling + */ +typedef struct _qlist_t { + void *data; + size_t num; + struct _qlist_t *prev, *next; +} qlist_t; + + +qlist_t * th_llist_new(void *data); +void th_llist_free(qlist_t *list); +void th_llist_free_func(qlist_t *list, void (*freefunc)(void *data)); + +qlist_t * th_llist_append(qlist_t **list, void *data); +qlist_t * th_llist_prepend(qlist_t **list, void *data); +void th_llist_delete(qlist_t **list, const void *data); +void th_llist_delete_node(qlist_t **list, qlist_t *node); + +qlist_t * th_llist_get_nth(qlist_t *list, const size_t n); +size_t th_llist_length(const qlist_t *list); +ssize_t th_llist_position(const qlist_t *list, const qlist_t *node); + +qlist_t * th_llist_find(qlist_t *list, const void *data); +qlist_t * th_llist_find_func(qlist_t *list, const void *userdata, int (compare)(const void *, const void *)); + + #ifdef __cplusplus } #endif