changeset 74:8ff993207415

Branch merge.
author Matti Hamalainen <ccr@tnsp.org>
date Sun, 18 Nov 2012 23:12:47 +0200
parents e37d7c1f8a03 (current diff) 43df05a632cb (diff)
children cfb475e11b9f
files
diffstat 6 files changed, 285 insertions(+), 150 deletions(-) [+]
line wrap: on
line diff
--- a/th_config.c	Sun Nov 18 23:12:33 2012 +0200
+++ b/th_config.c	Sun Nov 18 23:12:47 2012 +0200
@@ -16,36 +16,36 @@
 
 /* Free a given configuration (the values are not free'd)
  */
-void th_cfg_free(cfgitem_t *cfg)
+void th_cfg_free(th_cfgitem_t *cfg)
 {
-    cfgitem_t *curr = cfg;
+    th_cfgitem_t *node = cfg;
 
-    while (curr != NULL)
+    while (node != NULL)
     {
-        cfgitem_t *next = curr->next;
+        th_cfgitem_t *next = node->next;
 
-        if (curr->type == ITEM_SECTION)
-            th_cfg_free((cfgitem_t *) curr->v.data);
+        if (node->type == ITEM_SECTION)
+            th_cfg_free((th_cfgitem_t *) node->v.data);
 
-        th_free(curr->name);
-        th_free(curr);
-        curr = next;
+        th_free(node->name);
+        th_free(node);
+        node = next;
     }
 }
 
 
 /* Allocate and add new item to configuration
  */
-static cfgitem_t *th_cfg_add(cfgitem_t **cfg, const char *name,
+static th_cfgitem_t *th_cfg_add(th_cfgitem_t **cfg, const char *name,
                              const int type, void *data)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     if (cfg == NULL)
         return NULL;
 
     /* Allocate new item */
-    node = (cfgitem_t *) th_calloc(1, sizeof(cfgitem_t));
+    node = (th_cfgitem_t *) th_calloc(1, sizeof(th_cfgitem_t));
     if (node == NULL)
         return NULL;
 
@@ -74,9 +74,9 @@
 
 /* Add integer type setting into give configuration
  */
-int th_cfg_add_int(cfgitem_t **cfg, char *name, int *itemData, int itemDef)
+int th_cfg_add_int(th_cfgitem_t **cfg, const char *name, int *itemData, int itemDef)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     node = th_cfg_add(cfg, name, ITEM_INT, (void *) itemData);
     if (node == NULL)
@@ -88,10 +88,10 @@
 }
 
 
-int th_cfg_add_hexvalue(cfgitem_t **cfg, char *name,
+int th_cfg_add_hexvalue(th_cfgitem_t **cfg, const char *name,
                         int *itemData, int itemDef)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     node = th_cfg_add(cfg, name, ITEM_HEX_TRIPLET, (void *) itemData);
     if (node == NULL)
@@ -105,10 +105,10 @@
 
 /* Add unsigned integer type setting into give configuration
  */
-int th_cfg_add_uint(cfgitem_t **cfg, char *name,
+int th_cfg_add_uint(th_cfgitem_t **cfg, const char *name,
                     unsigned int *itemData, unsigned int itemDef)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     node = th_cfg_add(cfg, name, ITEM_UINT, (void *) itemData);
     if (node == NULL)
@@ -122,10 +122,10 @@
 
 /* Add strint type setting into given configuration
  */
-int th_cfg_add_string(cfgitem_t **cfg, char *name,
+int th_cfg_add_string(th_cfgitem_t **cfg, const char *name,
                       char **itemData, char *itemDef)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     node = th_cfg_add(cfg, name, ITEM_STRING, (void *) itemData);
     if (node == NULL)
@@ -139,10 +139,10 @@
 
 /* Add boolean type setting into given configuration
  */
-int th_cfg_add_bool(cfgitem_t **cfg, char *name,
+int th_cfg_add_bool(th_cfgitem_t **cfg, const char *name,
                     BOOL *itemData, BOOL itemDef)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     node = th_cfg_add(cfg, name, ITEM_BOOL, (void *) itemData);
     if (node == NULL)
@@ -156,9 +156,9 @@
 
 /* Add implicit comment
  */
-int th_cfg_add_comment(cfgitem_t **cfg, char *comment)
+int th_cfg_add_comment(th_cfgitem_t **cfg, const char *comment)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     node = th_cfg_add(cfg, comment, ITEM_COMMENT, NULL);
     if (node == NULL)
@@ -170,9 +170,9 @@
 
 /* Add new section
  */
-int th_cfg_add_section(cfgitem_t **cfg, char *name, cfgitem_t *data)
+int th_cfg_add_section(th_cfgitem_t **cfg, const char *name, th_cfgitem_t *data)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     node = th_cfg_add(cfg, name, ITEM_SECTION, (void *) data);
     if (node == NULL)
@@ -182,9 +182,9 @@
 }
 
 
-int th_cfg_add_string_list(cfgitem_t **cfg, char *name, qlist_t **data)
+int th_cfg_add_string_list(th_cfgitem_t **cfg, const char *name, qlist_t **data)
 {
-    cfgitem_t *node;
+    th_cfgitem_t *node;
 
     if (data == NULL)
         return -5;
@@ -218,28 +218,9 @@
 #define VADDCH(ch) if (strPos < SET_MAX_BUF) { tmpStr[strPos++] = ch; }
 #define VISEND(ch) (ch == '\r' || ch == '\n' || ch == ';' || th_isspace(c) || ch == '#')
 
-typedef struct
-{
-    FILE *file;
-    char *filename;
-    size_t line;
-} conffile_t;
-
-
-static void th_cfg_error(conffile_t *f, const char *fmt, ...)
+static int th_cfg_read_sect(th_ioctx_t *ctx, th_cfgitem_t *cfg, int nesting)
 {
-    va_list ap;
-    va_start(ap, fmt);
-    fprintf(stderr, "%s: '%s', line #%d: ", th_prog_name, f->filename,
-            (unsigned int) f->line);
-    vfprintf(stderr, fmt, ap);
-    va_end(ap);
-}
-
-
-static int th_cfg_read_sect(conffile_t *f, cfgitem_t *cfg, int nesting)
-{
-    cfgitem_t *item = NULL;
+    th_cfgitem_t *item = NULL;
     char tmpStr[SET_MAX_BUF + 1];
     size_t strPos;
     int c, parseMode, prevMode, nextMode, tmpCh;
@@ -258,12 +239,12 @@
         if (c == -1)
         {
             /* Get next character */
-            switch (c = fgetc(f->file))
+            switch (c = fgetc(ctx->fp))
             {
             case EOF:
                 if (parseMode != PM_NORMAL)
                 {
-                    th_cfg_error(f, "Unexpected end of file.\n");
+                    th_ioctx_error(ctx, -1, "Unexpected end of file.\n");
                     parseMode = PM_ERROR;
                 }
                 else
@@ -271,7 +252,7 @@
                 break;
 
             case '\n':
-                f->line++;
+                ctx->line++;
             }
         }
 
@@ -309,8 +290,8 @@
                 }
                 else
                 {
-                    th_cfg_error(f,
-                                 "Invalid nesting sequence encountered.\n");
+                    th_ioctx_error(ctx, -1,
+                        "Invalid nesting sequence encountered.\n");
                     parseMode = PM_ERROR;
                 }
             }
@@ -324,7 +305,7 @@
             else
             {
                 /* Error! Invalid character found */
-                th_cfg_error(f, "Unexpected character '%c'.\n", c);
+                th_ioctx_error(ctx, -1, "Unexpected character '%c'.\n", c);
                 parseMode = PM_ERROR;
             }
             break;
@@ -352,7 +333,7 @@
                 else
                 {
                     /* Error! Key name string too long! */
-                    th_cfg_error(f, "Config key name too long!");
+                    th_ioctx_error(ctx, -1, "Config key name too long!");
                     parseMode = PM_ERROR;
                 }
                 c = -1;
@@ -361,9 +342,9 @@
             {
                 /* Error! Invalid character found */
                 tmpStr[strPos] = 0;
-                th_cfg_error(f,
-                             "Unexpected character '%c' in key name '%s'.\n",
-                             c, tmpStr);
+                th_ioctx_error(ctx, -1,
+                    "Unexpected character '%c' in key name '%s'.\n",
+                    c, tmpStr);
                 parseMode = PM_ERROR;
             }
             break;
@@ -420,9 +401,9 @@
                 else
                 {
                     /* Error! No configuration key by this name found */
-                    th_cfg_error(f,
-                                 "No such configuration setting ('%s')\n",
-                                 tmpStr);
+                    th_ioctx_error(ctx, -1,
+                        "No such configuration setting ('%s')\n",
+                        tmpStr);
                     parseMode = PM_ERROR;
                 }
 
@@ -431,9 +412,9 @@
             else
             {
                 /* Error! '=' expected! */
-                th_cfg_error(f,
-                             "Unexpected character '%c', assignation '=' was expected.\n",
-                             c);
+                th_ioctx_error(ctx, -1,
+                    "Unexpected character '%c', assignation '=' was expected.\n",
+                    c);
                 parseMode = PM_ERROR;
             }
             break;
@@ -495,14 +476,14 @@
             if (c != '{')
             {
                 /* Error! Section start '{' expected! */
-                th_cfg_error(f,
-                             "Unexpected character '%c', section start '{' was expected.\n",
-                             c);
+                th_ioctx_error(ctx, -1,
+                    "Unexpected character '%c', section start '{' was expected.\n",
+                    c);
                 parseMode = PM_ERROR;
             }
             else
             {
-                int res = th_cfg_read_sect(f, item->v.section, nesting + 1);
+                int res = th_cfg_read_sect(ctx, item->v.section, nesting + 1);
                 c = -1;
                 if (res > 0)
                     validError = TRUE;
@@ -558,9 +539,9 @@
                 else
                 {
                     /* Error! String too long! */
-                    th_cfg_error(f,
-                                 "String too long! Maximum is %d characters.",
-                                 SET_MAX_BUF);
+                    th_ioctx_error(ctx, -1,
+                        "String too long! Maximum is %d characters.",
+                        SET_MAX_BUF);
                     parseMode = PM_ERROR;
                 }
             }
@@ -573,9 +554,9 @@
             if (isStart && item->type == ITEM_UINT && c == '-')
             {
                 /* Error! Negative values not allowed for unsigned ints */
-                th_cfg_error(f,
-                             "Negative value specified for %s, unsigned value expected.",
-                             item->name);
+                th_ioctx_error(ctx, -1,
+                    "Negative value specified for %s, unsigned value expected.",
+                    item->name);
                 parseMode = PM_ERROR;
             }
             else if (isStart && (c == '-' || c == '+'))
@@ -611,17 +592,18 @@
             else
             {
                 /* Error! Unexpected character. */
-                th_cfg_error(f,
-                             "Unexpected character '%c' for integer setting '%s'.",
-                             c, item->name);
+                th_ioctx_error(ctx, -1,
+                    "Unexpected character '%c' for integer setting '%s'.",
+                    c, item->name);
                 parseMode = PM_ERROR;
             }
 
             if (isError)
             {
                 /* Error! String too long! */
-                th_cfg_error(f, "String too long! Maximum is %d characters.",
-                             SET_MAX_BUF);
+                th_ioctx_error(ctx, -1,
+                    "String too long! Maximum is %d characters.",
+                    SET_MAX_BUF);
                 parseMode = PM_ERROR;
             }
 
@@ -665,8 +647,9 @@
 
                 if (isError)
                 {
-                    th_cfg_error(f, "Invalid boolean value for '%s'.\n",
-                                 item->name);
+                    th_ioctx_error(ctx, -1,
+                        "Invalid boolean value for '%s'.\n",
+                        item->name);
                     parseMode = PM_ERROR;
                 }
                 else
@@ -694,56 +677,53 @@
 }
 
 
-int th_cfg_read(FILE *inFile, char *filename, cfgitem_t *cfg)
+int th_cfg_read(th_ioctx_t *ctx, th_cfgitem_t *cfg)
 {
-    conffile_t f;
+    if (ctx == NULL || cfg == NULL)
+        return -1;
 
-    f.file = inFile;
-    f.filename = filename;
-    f.line = 1;
-
-    return th_cfg_read_sect(&f, cfg, 0);
+    return th_cfg_read_sect(ctx, cfg, 0);
 }
 
 
 /* Write a configuration into file
  */
-static void th_print_indent(conffile_t *f, int nesting)
+static void th_print_indent(th_ioctx_t *ctx, int nesting)
 {
     int i;
     for (i = 0; i < nesting * 2; i++)
-        fputc(' ', f->file);
+        fputc(' ', ctx->fp);
 }
 
 
-static int th_cfg_write_sect(conffile_t *f, cfgitem_t *item, int nesting)
+static int th_cfg_write_sect(th_ioctx_t *ctx, const th_cfgitem_t *item, int nesting)
 {
     while (item != NULL)
     {
         if (item->type == ITEM_COMMENT)
         {
-            th_print_indent(f, nesting);
-            if (fprintf
-                (f->file, "# %s\n",
-                 (item->name != NULL) ? item->name : "") < 0)
+            th_print_indent(ctx, nesting);
+            if (fprintf(ctx->fp, "# %s\n",
+                (item->name != NULL) ? item->name : "") < 0)
                 return -1;
         }
         else if (item->name != NULL)
         {
-            th_print_indent(f, nesting);
+            th_print_indent(ctx, nesting);
 
             switch (item->type)
             {
             case ITEM_STRING:
                 if (*(item->v.val_str) == NULL)
                 {
-                    if (fprintf(f->file, "#%s = \"\"\n", item->name) < 0)
+                    if (fprintf(ctx->fp, "#%s = \"\"\n",
+                        item->name) < 0)
                         return -3;
                 }
                 else
                 {
-                    if (fprintf(f->file, "%s = \"%s\"\n",
-                                item->name, *(item->v.val_str)) < 0)
+                    if (fprintf(ctx->fp, "%s = \"%s\"\n",
+                        item->name, *(item->v.val_str)) < 0)
                         return -3;
                 }
                 break;
@@ -751,70 +731,69 @@
             case ITEM_STRING_LIST:
                 if (*(item->v.list) == NULL)
                 {
-                    if (fprintf(f->file, "#%s = \"\", \"\"\n", item->name) <
-                        0)
+                    if (fprintf(ctx->fp,
+                        "#%s = \"\", \"\"\n", item->name) < 0)
                         return -3;
                 }
                 else
                 {
                     qlist_t *node = *(item->v.list);
                     size_t n = th_llist_length(node);
-                    if (fprintf(f->file, "%s = ", item->name) < 0)
+                    if (fprintf(ctx->fp, "%s = ", item->name) < 0)
                         return -3;
 
                     while (node != NULL)
                     {
                         if (node->data != NULL)
-                            fprintf(f->file, "\"%s\"", (char *) node->data);
+                            fprintf(ctx->fp, "\"%s\"", (char *) node->data);
 
                         if (--n > 0)
                         {
-                            fprintf(f->file, ",\n");
-                            th_print_indent(f, nesting);
+                            fprintf(ctx->fp, ",\n");
+                            th_print_indent(ctx, nesting);
                         }
                         node = node->next;
                     }
 
-                    if (fprintf(f->file, "\n") < 0)
+                    if (fprintf(ctx->fp, "\n") < 0)
                         return -3;
                 }
                 break;
 
             case ITEM_INT:
-                if (fprintf(f->file, "%s = %i\n",
-                            item->name, *(item->v.val_int)) < 0)
+                if (fprintf(ctx->fp, "%s = %i\n",
+                    item->name, *(item->v.val_int)) < 0)
                     return -4;
                 break;
 
             case ITEM_UINT:
-                if (fprintf(f->file, "%s = %d\n",
-                            item->name, *(item->v.val_uint)) < 0)
+                if (fprintf(ctx->fp, "%s = %d\n",
+                    item->name, *(item->v.val_uint)) < 0)
                     return -5;
                 break;
 
             case ITEM_BOOL:
-                if (fprintf(f->file, "%s = %s\n",
-                            item->name,
-                            *(item->v.val_bool) ? "yes" : "no") < 0)
+                if (fprintf(ctx->fp, "%s = %s\n", item->name,
+                    *(item->v.val_bool) ? "yes" : "no") < 0)
                     return -6;
                 break;
 
             case ITEM_SECTION:
                 {
                     int res;
-                    if (fprintf(f->file, "%s = {\n", item->name) < 0)
+                    if (fprintf(ctx->fp, "%s = {\n", item->name) < 0)
                         return -7;
-                    res = th_cfg_write_sect(f, item->v.section, nesting + 1);
+                    res = th_cfg_write_sect(ctx, item->v.section, nesting + 1);
                     if (res != 0)
                         return res;
-                    if (fprintf(f->file, "}\n\n") < 0)
+                    if (fprintf(ctx->fp, "}\n\n") < 0)
                         return -8;
                 }
                 break;
 
             case ITEM_HEX_TRIPLET:
-                if (fprintf(f->file, "%s = \"%06x\"\n",
-                            item->name, *(item->v.val_int)) < 0)
+                if (fprintf(ctx->fp, "%s = \"%06x\"\n",
+                    item->name, *(item->v.val_int)) < 0)
                     return -6;
                 break;
             }
@@ -826,19 +805,13 @@
 }
 
 
-int th_cfg_write(FILE *outFile, char *filename, cfgitem_t *cfg)
+int th_cfg_write(th_ioctx_t *ctx, const th_cfgitem_t *cfg)
 {
-    conffile_t f;
-
-    if (cfg == NULL)
+    if (ctx == NULL || cfg == NULL)
         return -1;
 
-    f.file = outFile;
-    f.filename = filename;
-    f.line = 1;
-
-    fprintf(outFile, "# Configuration written by %s %s\n\n",
+    fprintf(ctx->fp, "# Configuration written by %s %s\n\n",
             th_prog_desc, th_prog_version);
 
-    return th_cfg_write_sect(&f, cfg, 0);
+    return th_cfg_write_sect(ctx, cfg, 0);
 }
--- a/th_config.h	Sun Nov 18 23:12:33 2012 +0200
+++ b/th_config.h	Sun Nov 18 23:12:47 2012 +0200
@@ -12,13 +12,13 @@
 extern "C" {
 #endif
 
-#include "th_util.h"
-#include <stdio.h>
+#include "th_ioctx.h"
 
 
 /* Definitions
  */
-enum ITEM_TYPE {
+enum ITEM_TYPE
+{
     ITEM_SECTION = 1,
     ITEM_COMMENT,
     ITEM_STRING,
@@ -33,7 +33,8 @@
 };
 
 
-typedef struct _cfgitem_t {
+typedef struct _th_cfgitem_t
+{
     int  type;
     char *name;
     union {
@@ -44,29 +45,30 @@
 
         void *data;
         qlist_t **list;
-        struct _cfgitem_t *section;
+        struct _th_cfgitem_t *section;
     } v;
     
-    struct _cfgitem_t *next, *prev;
-} cfgitem_t;
+    struct _th_cfgitem_t *next, *prev;
+} th_cfgitem_t;
 
 
 /* Functions
  */
-int     th_cfg_read(FILE *, char *, cfgitem_t *);
-void    th_cfg_free(cfgitem_t *);
-int     th_cfg_write(FILE *, char *, cfgitem_t *);
+int     th_cfg_read(th_ioctx_t *, th_cfgitem_t *);
+void    th_cfg_free(th_cfgitem_t *);
+int     th_cfg_write(th_ioctx_t *, const th_cfgitem_t *);
+
+int     th_cfg_add_section(th_cfgitem_t **cfg, const char *name, th_cfgitem_t *data);
+int     th_cfg_add_comment(th_cfgitem_t **cfg, const char *comment);
 
-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_int(th_cfgitem_t **cfg, const char *name, int *data, int itemDef);
+int     th_cfg_add_uint(th_cfgitem_t **cfg, const char *name, unsigned int *data, unsigned int itemDef);
+int     th_cfg_add_string(th_cfgitem_t **cfg, const char *name, char **data, char *itemDef);
+int     th_cfg_add_bool(th_cfgitem_t **cfg, const char *name, BOOL *data, BOOL itemDef);
+int     th_cfg_add_float(th_cfgitem_t **cfg, const char *name, float *data, float itemDef);
+int     th_cfg_add_hexvalue(th_cfgitem_t **cfg, const char *name, int *data, int itemDef);
+int     th_cfg_add_string_list(th_cfgitem_t **cfg, const char *name, qlist_t **list);
 
-int     th_cfg_add_string_list(cfgitem_t **cfg, char *name, qlist_t **list);
 
 #ifdef __cplusplus
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_ioctx.c	Sun Nov 18 23:12:47 2012 +0200
@@ -0,0 +1,98 @@
+/*
+ * Standard I/O context helpers
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2012 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include "th_ioctx.h"
+#include "th_string.h"
+
+/* Simple STD I/O contexts
+ */
+BOOL th_ioctx_init(th_ioctx_t *ctx, const char *filename,
+    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
+    void (*msg)(struct _th_ioctx_t *, const char *msg))
+
+{
+    if (ctx == NULL || filename == NULL)
+        return FALSE;
+
+    th_memset(ctx, 0, sizeof(ctx));
+    ctx->error = error;
+    ctx->msg   = msg;
+
+    if ((ctx->filename = th_strdup(filename)) == NULL)
+        return FALSE;
+    
+    return TRUE;
+}
+
+
+BOOL th_ioctx_open(th_ioctx_t *ctx, const char *filename, const char *mode,
+    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
+    void (*msg)(struct _th_ioctx_t *, const char *msg))
+{
+    if (!th_ioctx_init(ctx, filename, error, msg) || mode == NULL)
+        return FALSE;
+
+    if ((ctx->fp = fopen(filename, mode)) == NULL)
+        return FALSE;
+
+    return TRUE;
+}
+
+
+void th_ioctx_close(th_ioctx_t *ctx)
+{
+    if (ctx != NULL)
+    {
+        th_free(ctx->filename);
+        ctx->filename = NULL;
+
+        if (ctx->fp != NULL)
+            fclose(ctx->fp);
+        ctx->fp = NULL;
+    }
+}
+
+
+void th_ioctx_error(th_ioctx_t *ctx, const int err, const char *fmt, ...)
+{
+    char *msg;
+    va_list ap;
+
+    va_start(ap, fmt);
+    msg = th_strdup_vprintf(fmt, ap);
+    va_end(ap);
+
+    if (ctx->error != NULL)
+        ctx->error(ctx, err, msg);
+    else
+        THERR("'%s' #%d: %s\n", ctx->filename, (unsigned int) ctx->line, msg);
+
+    th_free(msg);
+}
+
+
+th_ioctx_t *th_ioctx_new(const char *filename,
+    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
+    void (*msg)(struct _th_ioctx_t *, const char *msg))
+{
+    th_ioctx_t *ctx = th_malloc0(sizeof(th_ioctx_t));
+    if (ctx == NULL)
+        return NULL;
+    
+    th_ioctx_init(ctx, filename, error, msg);
+    ctx->allocated = TRUE;
+    return ctx;
+}
+
+
+void th_ioctx_free(th_ioctx_t *ctx)
+{
+    th_ioctx_close(ctx);
+
+    if (ctx->allocated)
+        th_free(ctx);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_ioctx.h	Sun Nov 18 23:12:47 2012 +0200
@@ -0,0 +1,53 @@
+/*
+ * Standard I/O context helpers
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2012 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifndef TH_IOCTX_H
+#define TH_IOCTX_H
+
+#include "th_util.h"
+#include <stdio.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* Simple STD I/O contexts
+ */
+typedef struct _th_ioctx_t
+{
+    BOOL allocated;
+    char *filename;
+    FILE *fp;
+    size_t line;
+
+    void (*error)(struct _th_ioctx_t *, const int, const char *msg);
+    void (*msg)(struct _th_ioctx_t *, const char *msg);
+} th_ioctx_t;
+
+
+BOOL th_ioctx_init(th_ioctx_t *ctx, const char *filename,
+    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
+    void (*msg)(struct _th_ioctx_t *, const char *msg));
+
+BOOL th_ioctx_open(th_ioctx_t *ctx, const char *filename, const char *mode,
+    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
+    void (*msg)(struct _th_ioctx_t *, const char *msg));
+
+void th_ioctx_close(th_ioctx_t *ctx);
+void th_ioctx_error(th_ioctx_t *ctx, const int err, const char *fmt, ...);
+
+th_ioctx_t * th_ioctx_new(const char *filename,
+    void (*error)(struct _th_ioctx_t *, const int, const char *msg),
+    void (*msg)(struct _th_ioctx_t *, const char *msg));
+void th_ioctx_free(th_ioctx_t *ctx);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* TH_IOCTX_H */
--- a/th_util.c	Sun Nov 18 23:12:33 2012 +0200
+++ b/th_util.c	Sun Nov 18 23:12:47 2012 +0200
@@ -130,6 +130,12 @@
 }
 
 
+void *th_malloc0(size_t l)
+{
+    return calloc(1, l);
+}
+
+
 void *th_calloc(size_t n, size_t l)
 {
     return calloc(n, l);
--- a/th_util.h	Sun Nov 18 23:12:33 2012 +0200
+++ b/th_util.h	Sun Nov 18 23:12:47 2012 +0200
@@ -80,6 +80,7 @@
 void    THPRINT_V(int, const char *, va_list);
 
 void    *th_malloc(size_t);
+void    *th_malloc0(size_t);
 void    *th_calloc(size_t, size_t);
 void    *th_realloc(void *, size_t);
 void    th_free(void *);
@@ -93,7 +94,8 @@
 
 /* Doubly linked list handling
  */
-typedef struct _qlist_t {
+typedef struct _qlist_t
+{
     void *data;
     size_t num;
     struct _qlist_t *prev, *next;
@@ -124,7 +126,8 @@
 
 /* Ringbuffer implementation
  */
-typedef struct {
+typedef struct
+{
     char **data;
     int n, size;
     void (*deallocator)(void *);