changeset 0:bd61a80a6c54

Initial import into Mercurial repository. Discarding old cvs/svn history here, because it's cluttered and commit messages are mostly crap.
author Matti Hamalainen <ccr@tnsp.org>
date Wed, 26 Mar 2008 04:41:58 +0200
parents
children 7e9b0c476a8a
files th_args.c th_args.h th_config.c th_config.h th_endian.c th_endian.h th_endian2.h th_string.c th_string.h th_types.h th_util.c th_util.h
diffstat 12 files changed, 2122 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_args.c	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,387 @@
+/*
+ * Simple commandline argument processing
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+/*
+Description
+===========
+Structures and functions for simple commandline argument processing,
+option argument handling (short and long forms supported).
+
+Output function for printing out short on-line help for said options
+and program commandline usage.
+
+
+Format for typical commandline:
+
+$ program -a -b -c --long-d -e argument-for-e --long-f="argument for f"
+
+where -a, -b and -c are short options without required arguments;
+--long-d is a long option without argument; -e is short option with
+argument and finally --long-f is long option with argument.
+
+
+Introduction
+============
+Handling of commandline options in th_args_process() has various
+options, which effect how an option is handled. Also the error
+situations (unknown option/no required argument) can be handled
+in different ways.
+
+Typically th_args_process() is called as follows:
+
+BOOL optionHandlerCallback(int optionNumber, char *optionArgument, char *optionName)
+{
+  if (option is OK)
+    return TRUE;
+  else
+    return FALSE;
+}
+
+BOOL nonoptionHandlerCallback(char *argumentString)
+{
+  // return value same as in optionHandlerCallback
+}
+
+
+int main(int argc, char *argv[])
+{
+  if (th_args_process(argc, argv, optList, optListN,
+      optionHandlerCallback, nonoptionHandlerCallback)) {
+    ... arguments OK ...
+  } else {
+    ... arguments invalid or required arguments missing ...
+  }
+}
+
+
+NOTICE!
+-------
+The return value from handler callbacks affects the return value of
+th_args_process(). Additionally, a failure in callback (returns FALSE)
+effects the argument processing if bailOut argument of th_args_process()
+is TRUE!
+
+If bailOut is TRUE, any error/failure in argument processing (including
+callbacks) immediately stops the argument processing and FALSE is
+returned from th_args_process().
+
+If bailOut is FALSE, most errors are "ignored", but FALSE is still returned
+if any errors occured.
+
+
+NOTICE #2!
+----------
+A small temporary buffer of N*sizeof(BOOL) (where N is number of
+options in optList[]) is allocated for processing required options.
+If this allocation fails, the program is immediately exited with
+code 128.
+
+
+Examples
+========
+Example of different options, in a fictional optionlist struct:
+
+optarg_t optList[] = {
+  // Option without arguments
+  { 0, '?', "help",      "Show this help",                    OPT_NONE },
+
+  // Option with a required argument
+  { 1, 'o', "output",    "Output file name",                  OPT_ARGREQ },
+  
+  // Option with optional argument
+  { 2, 'f', "foobar",    "Use foobar, with optional string",  OPT_ARGOPT },
+
+  // This option is required to be given, though without other flags
+  // it may not make much sense.
+  { 4, 'S', "stupid",     "You must give this option",        OPT_REQUIRED },
+
+  // The flags can be combined with OR operator: this option is both
+  // required to be specified, and also requires argument (the filename)
+  { 5, 'i', "input",     "Input file name",                   OPT_REQUIRED | OPT_ARGREQ },
+
+
+  // Option with only long form
+  { 0, 0,   "only-long", "Long option",                       OPT_NONE },
+
+  // Option with only short form
+  { 0, 's', NULL,        "Short option",                      OPT_NONE },
+
+};
+
+const DINT optListN = (sizeof(optList) / sizeof(t_opt));
+
+
+*/
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "th_util.h"
+#include "th_args.h"
+#include "th_string.h"
+
+
+/* Check if option requires an argument
+ */
+static BOOL th_args_check_arg(optarg_t *o, char *optArg)
+{
+	if ((o->optFlags & OPT_ARGMASK) == OPT_ARGREQ && optArg == NULL) {
+		if (o->optShort != 0 && o->optLong != NULL) {
+			THERR("Option '-%c ARG' (--%s=ARG) requires an argument!\n",
+				o->optShort, o->optLong);
+		} else if (o->optShort != 0) {
+			THERR("Option '-%c ARG' requires an argument!\n", o->optShort);
+		} else if (o->optLong != NULL) {
+			THERR("Option --%s=ARG requires an argument!\n", o->optLong);
+		}
+		
+		return FALSE;
+	} else
+		return TRUE;
+
+}
+
+
+/* Handle short options
+ */
+static BOOL th_args_process_short(char *currArg, int *newArgIndex,
+	BOOL *wasGiven, int argc, char *argv[],
+	optarg_t optList[], int optListN,
+	BOOL (*handleOpt)(int, char *, char *))
+{
+	char *tmpArg = currArg, *optArg;
+	int optN;
+	BOOL isFound;
+	
+	/* Short options can be combined: -a -b -c == -abc */
+	while (*tmpArg) {
+		
+		for (optN = 0, isFound = FALSE; (optN < optListN) && !isFound; optN++)
+		if (*tmpArg == optList[optN].optShort) {
+			/* Get possible option argument, if needed */
+			if ((optList[optN].optFlags & OPT_ARGMASK) != 0 && (++(*newArgIndex) < argc))
+				optArg = argv[*newArgIndex];
+			else
+				optArg = NULL;
+			
+			/* Check if option argument is required */
+			if (!th_args_check_arg(&optList[optN], optArg))
+				return FALSE;			
+			else {
+				char tmpStr[2] = { 0, 0 };
+
+				/* Option was given succesfully, try to handle it */
+				wasGiven[optN] = TRUE;
+				
+				tmpStr[0] = *tmpArg;
+				
+				if (!handleOpt(optList[optN].optID, optArg, tmpStr))
+					return FALSE;
+			}
+							
+			isFound = TRUE;
+		}
+		
+		if (!isFound) {
+			THERR("Unknown short option '%c' in argument '-%s'\n",
+				*tmpArg, currArg);
+			return FALSE;
+		}
+
+		tmpArg++;
+	}
+
+	return TRUE;
+}
+
+
+/* Handle long options
+ */
+static BOOL th_args_process_long(char *currArg, int *newArgIndex,
+	BOOL *wasGiven, int argc, char *argv[],
+	optarg_t optList[], int optListN,
+	BOOL (*handleOpt)(int, char *, char *))
+{
+	int optN, optLen, i;
+	char *optArg;
+	
+	(void) argc; (void) argv; (void) newArgIndex;
+	
+	/* Long option */
+	for (optN = -1, optLen = i = 0; (i < optListN) && (optN < 0); i++)
+	if (optList[i].optLong) {
+		optLen = strlen(optList[i].optLong);
+		if (strncmp(currArg, optList[i].optLong, optLen) == 0)
+			optN = i;
+	}
+	
+	/* Get possible option argument, if needed */
+	if (optN >= 0) {
+		if ((optList[optN].optFlags & OPT_ARGMASK) != 0) {
+			if (currArg[optLen] == '=')
+				optArg = &currArg[optLen + 1];
+			else
+				optArg = NULL;
+		} else
+			optArg = NULL;
+
+		/* Check if option argument is required */
+		if (!th_args_check_arg(&optList[optN], optArg))
+			return FALSE;			
+		else {
+			/* Option was given succesfully, try to handle it */
+			wasGiven[optN] = TRUE;
+			if (!handleOpt(optList[optN].optID, optArg, currArg))
+				return FALSE;
+		}
+	} else {
+		THERR("Unknown long option '--%s'\n", currArg);
+		return FALSE;
+	}
+
+	return TRUE;
+}
+
+
+/* Process arguments, handling short and long options by
+ * calling the given callback functions.
+ */
+BOOL th_args_process(int argc, char *argv[],
+		     optarg_t optList[], int optListN,
+		     BOOL (*handleOpt) (int, char *, char *),
+		     BOOL (*handleNonOption) (char *), BOOL bailOut)
+{
+	BOOL endOptions, optionsOK;
+	int argIndex, newArgIndex, i;
+	char *currArg;
+	BOOL *wasGiven;
+
+	/* Allocate wasGiven */
+	wasGiven = (BOOL *) th_calloc(optListN, sizeof(BOOL));
+	if (!wasGiven) {
+		THERR("FATAL ERROR! Could not allocate wasGiven in th_args_process()!\n");
+		exit(128);
+	}
+
+	/* Parse arguments */
+	argIndex = 1;
+	optionsOK = TRUE;
+	endOptions = FALSE;
+	while (argIndex < argc) {
+		currArg = argv[argIndex];
+		if ((currArg[0] == '-') && !endOptions) {
+			newArgIndex = argIndex;
+			currArg++;
+			if (*currArg == '-') {
+				/* Check for "--", which ends the options-list */
+				currArg++;
+				if (*currArg == 0) {
+					endOptions = TRUE;
+					continue;
+				}
+				
+				/* Long options */
+				if (!th_args_process_long(currArg, &newArgIndex,
+					wasGiven, argc, argv, optList, optListN,
+					handleOpt))
+					optionsOK = FALSE;
+			} else {
+				/* Short options */
+				if (!th_args_process_short(currArg, &newArgIndex,
+					wasGiven, argc, argv, optList, optListN,
+					handleOpt))
+					optionsOK = FALSE;
+			}
+
+			argIndex = newArgIndex;
+		} else {
+			/* Was not option argument */
+			if (!handleNonOption || (handleNonOption && !handleNonOption(currArg))) {
+				THERR("Invalid argument '%s'\n", currArg);
+				optionsOK = FALSE;
+			}
+		}
+		
+		/* Check if we bail out on invalid argument */
+		if (!optionsOK && bailOut) {
+			th_free(wasGiven);
+			return FALSE;
+		}
+		
+		argIndex++;
+	}
+
+	/* Check wasGiven by isRequired */
+	for (i = 0; i < optListN; i++)
+	if ((optList[i].optFlags & OPT_REQUIRED) != 0 && !wasGiven[i]) {
+		THERR("Option -%s (--%s) is required.\n",
+			optList[i].optShort, optList[i].optLong);
+
+		optionsOK = FALSE;
+		if (bailOut) break;
+	}
+	
+	th_free(wasGiven);
+	return optionsOK;
+}
+
+
+/* Print help for commandline arguments/options
+ */
+void th_args_help(FILE * outFile,
+	optarg_t optList[], int optListN,
+	char * progName, char * progUsage)
+{
+	int i, nrequired;
+
+	fprintf(outFile,
+		"\n%s v%s (%s)\n"
+		"%s\n"
+		"%s\n"
+		"Usage: %s %s\n",
+		th_prog_name, th_prog_version, th_prog_fullname,
+		th_prog_author, th_prog_license, progName, progUsage);
+
+
+	for (i = nrequired = 0; i < optListN; i++) {
+		optarg_t *o = &optList[i];
+		
+		/* Print short option */
+		if (o->optShort != 0)
+			fprintf(outFile, "  -%c,  ", o->optShort);
+		else
+			fprintf(outFile, "       ");
+		
+		/* Print long option */
+		if (o->optLong) {
+			char tmpStr[64], *p;
+			
+			if ((o->optFlags & OPT_ARGMASK) == OPT_ARGOPT) {
+				snprintf(tmpStr, sizeof(tmpStr), "%s[=ARG]", optList[i].optLong);
+				p = tmpStr;
+			} else if ((o->optFlags & OPT_ARGMASK) == OPT_ARGREQ) {
+				snprintf(tmpStr, sizeof(tmpStr), "%s=ARG", optList[i].optLong);
+				p = tmpStr;
+			} else
+				p = o->optLong;
+			
+			fprintf(outFile, "--%-15s", p);
+		} else
+			fprintf(outFile, "                 ");
+
+		fprintf(outFile, "  %s.", optList[i].optDesc);
+		
+		if (o->optFlags & OPT_REQUIRED) {
+			fprintf(outFile, " [*]\n");
+			nrequired++;
+		} else
+			fprintf(outFile, "\n");
+	}
+	
+	if (nrequired > 0)
+		fprintf(outFile, "(Options marked with [*] are required)\n");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_args.h	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,50 @@
+/*
+ * Simple commandline argument processing function
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifndef _TH_ARGS
+#define _TH_ARGS
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdio.h>
+#include "th_util.h"
+
+
+/* Option flags
+ */
+#define OPT_NONE	(0)	/* Simple option with no arguments */
+
+#define OPT_ARGREQ	(1)	/* Option's argument is required */
+#define	OPT_ARGOPT	(3)	/* Option's argument is optional */
+#define OPT_ARGMASK	(3)	/* Mask for option argument flags */
+
+#define OPT_REQUIRED	(4)	/* This option is required to be given */
+
+
+typedef struct {
+	int optID;
+	char optShort;
+	char *optLong;
+	char *optDesc;
+	int optFlags;
+} optarg_t;
+
+
+BOOL th_args_process(int argc, char *argv[],
+	optarg_t argList[], int argListN,
+	BOOL (*handleOpt)(int, char *, char *),
+	BOOL (*handleFile)(char *), BOOL);
+
+void th_args_help(FILE *, optarg_t optList[], int optListN,
+	char *, char *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TH_ARGS */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_config.c	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,575 @@
+/*
+ * Very simple configuration handling functions
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2004-2007 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include <stdarg.h>
+#include "th_config.h"
+#include "th_util.h"
+#include "th_string.h"
+
+#define LPREV (pNode->pPrev)
+#define LNEXT (pNode->pNext)
+
+
+void th_config_error(char_t * pcFilename, size_t lineNum,
+	const char_t * pcFormat, ...)
+{
+	va_list ap;
+	va_start(ap, pcFormat);
+	fprintf(stderr, "%s: '%s', line #%d: ", th_prog_name, pcFilename, lineNum);
+	vfprintf(stderr, pcFormat, ap);
+	va_end(ap);
+}
+
+
+/* Create a new configuration
+ */
+t_config *th_config_new(void)
+{
+	t_config *cfg;
+
+	cfg = (t_config *) th_calloc(1, sizeof(t_config));
+	if (!cfg)
+		return NULL;
+
+	return cfg;
+}
+
+
+/* Free a given configuration (the values are not free'd)
+ */
+void th_config_free(t_config * cfg)
+{
+	t_config_item *pCurr, *pNext;
+
+	if (!cfg)
+		return;
+
+	pCurr = cfg->pItems;
+	while (pCurr) {
+		pNext = pCurr->pNext;
+		th_free(pCurr->itemName);
+		th_free(pCurr);
+		pCurr = pNext;
+	}
+}
+
+
+/* Allocate and add new item to configuration
+ */
+t_config_item *th_config_add(t_config * cfg, char_t * itemName, int itemType,
+			     BOOL(*itemValidate) (t_config_item *), void *itemData)
+{
+	t_config_item *pNode;
+
+	if (!cfg)
+		return NULL;
+
+	/* Allocate new item */
+	pNode = (t_config_item *) th_calloc(1, sizeof(t_config_item));
+	if (!pNode)
+		return NULL;
+
+	/* Set values */
+	pNode->itemType = itemType;
+	pNode->itemData = itemData;
+	pNode->itemValidate = itemValidate;
+	th_pstrcpy(&pNode->itemName, itemName);
+
+	/* Insert into linked list */
+	if (cfg->pItems) {
+		/* The first node's pPrev points to last node */
+		LPREV = cfg->pItems->pPrev;	/* New node's prev = Previous last node */
+		cfg->pItems->pPrev->pNext = pNode;	/* Previous last node's next = New node */
+		cfg->pItems->pPrev = pNode;	/* New last node = New node */
+		LNEXT = NULL;	/* But next is NULL! */
+	} else {
+		cfg->pItems = pNode;	/* First node ... */
+		LPREV = pNode;	/* ... it's also last */
+		LNEXT = NULL;	/* But next is NULL! */
+	}
+
+	return pNode;
+}
+
+
+/* Add integer type setting into give configuration
+ */
+int th_config_add_int(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *),
+		      int *itemData, int itemDef)
+{
+	t_config_item *pNode;
+
+	pNode = th_config_add(cfg, itemName, ITEM_INT, itemValidate, (void *) itemData);
+	if (!pNode)
+		return -1;
+
+	*itemData = itemDef;
+
+	return 0;
+}
+
+
+/* Add unsigned integer type setting into give configuration
+ */
+int th_config_add_uint(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *),
+		       unsigned int * itemData, unsigned int itemDef)
+{
+	t_config_item *pNode;
+
+	pNode = th_config_add(cfg, itemName, ITEM_UINT, itemValidate, (void *) itemData);
+	if (!pNode)
+		return -1;
+
+	*itemData = itemDef;
+
+	return 0;
+}
+
+
+/* Add strint type setting into given configuration
+ */
+int th_config_add_str(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *),
+		      char_t ** itemData, char_t * itemDef)
+{
+	t_config_item *pNode;
+
+	pNode = th_config_add(cfg, itemName, ITEM_STRING, itemValidate, (void *) itemData);
+	if (!pNode)
+		return -1;
+
+	if (itemDef != NULL)
+		*itemData = th_strdup(itemDef);
+	else
+		*itemData = NULL;
+
+	return 0;
+}
+
+
+/* Add boolean type setting into given configuration
+ */
+int th_config_add_bool(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *),
+		       BOOL * itemData, BOOL itemDef)
+{
+	t_config_item *pNode;
+
+	pNode = th_config_add(cfg, itemName, ITEM_BOOL, itemValidate, (void *) itemData);
+	if (!pNode)
+		return -1;
+
+	*itemData = itemDef;
+
+	return 0;
+}
+
+
+/* Read a given file into configuration structure and variables
+ */
+enum
+{
+	PM_EOF,
+	PM_ERROR,
+	PM_NORMAL,
+	PM_COMMENT,
+	PM_NEXT,
+	PM_KEYNAME,
+	PM_KEYSET,
+	PM_STRING,
+	PM_INT,
+	PM_BOOL
+};
+
+#define VADDCH(ch) if (strPos < SET_MAX_BUF) { tmpStr[strPos++] = ch; }
+#define VISEND(ch) (ch == '\r' || ch == '\n' || ch == ';' || th_isspace(c))
+
+int th_config_read(char_t * pcFilename, t_config * cfg)
+{
+	FILE *inFile;
+	t_config_item *pItem;
+	char_t tmpStr[SET_MAX_BUF + 1];
+	size_t lineNum, strPos;
+	int c, parseMode, prevMode, nextMode, tmpCh;
+	BOOL isFound, isStart, tmpBool, isError, validError;
+
+	assert(cfg);
+
+	/* Open the file */
+	if ((inFile = fopen(pcFilename, "rb")) == NULL)
+		return -1;
+
+	/* Initialize values */
+	pItem = NULL;
+	tmpCh = 0;
+	strPos = 0;
+	lineNum = 1;
+	c = -1;
+	isFound = isStart = tmpBool = isError = validError = FALSE;
+	nextMode = prevMode = parseMode = PM_NORMAL;
+
+	/* Parse the configuration */
+	while ((parseMode != PM_EOF) && (parseMode != PM_ERROR)) {
+		if (c == -1) {
+			/* Get next character */
+			switch (c = fgetc(inFile)) {
+			case EOF:
+				if (parseMode != PM_NORMAL) {
+					th_config_error(pcFilename, lineNum,
+					"Unexpected end of file.\n");
+					parseMode = PM_ERROR;
+				} else
+					parseMode = PM_EOF;
+				break;
+
+			case '\n':
+				lineNum++;
+			}
+		}
+
+		switch (parseMode) {
+		case PM_COMMENT:
+			/* Comment parsing mode */
+			if (c == '\n') {
+				/* End of line, end of comment */
+				parseMode = prevMode;
+				prevMode = PM_COMMENT;
+			}
+			c = -1;
+			break;
+
+		case PM_NORMAL:
+			/* Normal parsing mode */
+			if (c == '#') {
+				prevMode = parseMode;
+				parseMode = PM_COMMENT;
+				c = -1;
+			} else if (VISEND(c)) {
+				c = -1;
+			} else if (th_isalpha(c)) {
+				/* Start of key name found */
+				prevMode = parseMode;
+				parseMode = PM_KEYNAME;
+				strPos = 0;
+			} else {
+				/* Error! Invalid character found */
+				th_config_error(pcFilename, lineNum,
+					"Unexpected character '%c'\n", c);
+				parseMode = PM_ERROR;
+			}
+			break;
+
+		case PM_KEYNAME:
+			/* Configuration KEY name parsing mode */
+			if (c == '#') {
+				/* Start of comment */
+				prevMode = parseMode;
+				parseMode = PM_COMMENT;
+				c = -1;
+			} else if (th_iscrlf(c) || th_isspace(c) || c == '=') {
+				/* End of key name */
+				prevMode = parseMode;
+				parseMode = PM_NEXT;
+				nextMode = PM_KEYSET;
+			} else if (th_isalnum(c) || (c == '_')) {
+				/* Add to key name string */
+				VADDCH(c)
+				else
+				{
+					/* Error! Key name string too long! */
+					th_config_error(pcFilename, lineNum,
+						"Config key name too long!");
+					parseMode = PM_ERROR;
+				}
+				c = -1;
+			} else {
+				/* Error! Invalid character found */
+				th_config_error(pcFilename, lineNum,
+					"Unexpected character '%c'\n", c);
+				parseMode = PM_ERROR;
+			}
+			break;
+
+		case PM_KEYSET:
+			if (c == '=') {
+				/* Find key from configuration */
+				tmpStr[strPos] = 0;
+				isFound = FALSE;
+				pItem = cfg->pItems;
+				while (pItem && !isFound) {
+					if (strcmp(pItem->itemName, tmpStr) == 0)
+						isFound = TRUE;
+					else
+						pItem = pItem->pNext;
+				}
+
+				/* Check if key was found */
+				if (isFound) {
+					/* Okay, set next mode */
+					switch (pItem->itemType) {
+					case ITEM_STRING:
+						nextMode = PM_STRING;
+						break;
+
+					case ITEM_INT:
+					case ITEM_UINT:
+						nextMode = PM_INT;
+						break;
+
+					case ITEM_BOOL:
+						nextMode = PM_BOOL;
+						break;
+					}
+
+					prevMode = parseMode;
+					parseMode = PM_NEXT;
+					isStart = TRUE;
+					strPos = 0;
+				} else {
+					/* Error! No configuration key by this name found */
+					th_config_error(pcFilename, lineNum,
+						"No such configuration setting ('%s')\n",
+						tmpStr);
+					parseMode = PM_ERROR;
+				}
+
+				c = -1;
+			} else {
+				/* Error! '=' expected! */
+				th_config_error(pcFilename, lineNum,
+					"Unexpected character '%c', '=' expected.\n", c);
+				parseMode = PM_ERROR;
+			}
+			break;
+
+		case PM_NEXT:
+			/* Search next item parsing mode */
+			if (c == '#') {
+				/* Start of comment */
+				prevMode = parseMode;
+				parseMode = PM_COMMENT;
+			} else if (th_isspace(c) || th_iscrlf(c)) {
+				/* Ignore whitespaces and linechanges */
+				c = -1;
+			} else {
+				/* Next item found */
+				prevMode = parseMode;
+				parseMode = nextMode;
+			}
+			break;
+
+		case PM_STRING:
+			/* String parsing mode */
+			if (isStart) {
+				/* Start of string, get delimiter */
+				tmpCh = c;
+				isStart = FALSE;
+				strPos = 0;
+			} else if (c == tmpCh) {
+				/* End of string, set the value */
+				tmpStr[strPos] = 0;
+				th_pstrcpy((char_t **) pItem->itemData, tmpStr);
+				if (pItem->itemValidate) {
+					if (!pItem->itemValidate(pItem))
+						validError = TRUE;
+				}
+
+				prevMode = parseMode;
+				parseMode = PM_NORMAL;
+			} else {
+				/* Add character to string */
+				VADDCH(c)
+				else
+				{
+					/* Error! String too long! */
+					th_config_error(pcFilename, lineNum,
+						"String too long! Maximum is %d characters.",
+						SET_MAX_BUF);
+					parseMode = PM_ERROR;
+				}
+			}
+
+			c = -1;
+			break;
+
+		case PM_INT:
+			/* Integer parsing mode */
+			if (isStart && (pItem->itemType == ITEM_UINT) && (c == '-')) {
+				/* Error! Negative values not allowed for unsigned ints */
+				th_config_error(pcFilename, lineNum,
+						"Negative value specified, unsigned value expected.");
+				parseMode = PM_ERROR;
+			} else if (isStart && (c == '-' || c == '+')) {
+				VADDCH(c)
+				else
+				isError = TRUE;
+			} else if (th_isdigit(c)) {
+				VADDCH(c)
+				else
+				isError = TRUE;
+			} else if (VISEND(c)) {
+				/* End of integer parsing mode */
+				tmpStr[strPos] = 0;
+				switch (pItem->itemType) {
+				case ITEM_INT:
+					*((int *) pItem->itemData) = atoi(tmpStr);
+					break;
+
+				case ITEM_UINT:
+					*((unsigned int *) pItem->itemData) = atol(tmpStr);
+					break;
+				}
+				if (pItem->itemValidate) {
+					if (!pItem->itemValidate(pItem))
+						validError = TRUE;
+				}
+
+				prevMode = parseMode;
+				parseMode = PM_NORMAL;
+			} else {
+				/* Error! Unexpected character. */
+				th_config_error(pcFilename, lineNum,
+						"Unexpected character, ", SET_MAX_BUF);
+				parseMode = PM_ERROR;
+			}
+
+			if (isError) {
+				/* Error! String too long! */
+				th_config_error(pcFilename, lineNum,
+						"String too long! Maximum is %d characters.",
+						SET_MAX_BUF);
+				parseMode = PM_ERROR;
+			}
+
+			isStart = FALSE;
+			c = -1;
+			break;
+
+		case PM_BOOL:
+			/* Boolean parsing mode */
+			if (isStart) {
+				tmpCh = c;
+				isStart = FALSE;
+			} else if (VISEND(c)) {
+				/* End of boolean parsing */
+				switch (tmpCh) {
+				case 'Y':
+				case 'y':
+				case 'T':
+				case 't':
+				case '1':
+					tmpBool = TRUE;
+					break;
+
+				default:
+					tmpBool = FALSE;
+					break;
+				}
+
+				/* Set the value */
+				*((BOOL *) pItem->itemData) = tmpBool;
+				if (pItem->itemValidate) {
+					if (!pItem->itemValidate(pItem))
+						validError = TRUE;
+				}
+
+				prevMode = parseMode;
+				parseMode = PM_NORMAL;
+			}
+
+			c = -1;
+			break;
+		}
+	}
+
+
+	/* Close files */
+	fclose(inFile);
+
+	/* Check for validation errors */
+	if (validError)
+		return 1;
+
+	/* Return result */
+	if (parseMode == PM_ERROR)
+		return -2;
+	else
+		return 0;
+}
+
+
+/* Write a configuration into file
+ */
+int th_config_write(FILE * outFile, t_config * cfg)
+{
+	t_config_item *pItem;
+
+	if (!cfg)
+		return -1;
+
+	fprintf(outFile, "# Configuration written by %s %s\n\n", 
+		th_prog_fullname, th_prog_version);
+
+	pItem = cfg->pItems;
+	while (pItem) {
+		if (!pItem->itemData || ((pItem->itemType == ITEM_STRING) &&
+			*(char_t **) pItem->itemData == NULL)) {
+			
+			fprintf(outFile, "#%s = ", pItem->itemName);
+			
+			switch (pItem->itemType) {
+			case ITEM_STRING:
+				fprintf(outFile, "\"string\"");
+				break;
+
+			case ITEM_INT:
+				fprintf(outFile, "int");
+				break;
+
+			case ITEM_UINT:
+				fprintf(outFile, "uint");
+				break;
+
+			case ITEM_BOOL:
+				fprintf(outFile, "boolean");
+				break;
+			}
+			
+		} else {
+			fprintf(outFile, "%s = ", pItem->itemName);
+			
+			switch (pItem->itemType) {
+			case ITEM_STRING:
+				fprintf(outFile, "\"%s\"",
+					*((char_t **) pItem->itemData));
+				break;
+
+			case ITEM_INT:
+				fprintf(outFile, "%i",
+					*((int *) pItem->itemData));
+				break;
+
+			case ITEM_UINT:
+				fprintf(outFile, "%d",
+					*((unsigned int *) pItem->itemData));
+				break;
+
+			case ITEM_BOOL:
+				fprintf(outFile, "%s",
+					*((BOOL *) pItem->itemData) ? "yes" : "no");
+				break;
+			}
+		}
+
+		fprintf(outFile, "\n\n");
+		pItem = pItem->pNext;
+	}
+
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_config.h	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,62 @@
+/*
+ * Very simple configuration file parsing functions
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2004-2007 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifndef _TH_CONFIG_H
+#define _TH_CONFIG_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "th_util.h"
+#include <stdio.h>
+
+/*
+ * Type definations
+ */
+enum ITEM_TYPE {
+	ITEM_BLOCK = 1,
+	ITEM_STRING,
+	ITEM_INT,
+	ITEM_UINT,
+	ITEM_BOOL
+};
+
+
+typedef struct tconfignode {
+	char	*itemName;	/* Config item name */
+	int	itemType;	/* Type of the item */
+	void	*itemData;	/* Data / value */
+
+	BOOL	(*itemValidate)(struct tconfignode *);
+
+	struct tconfignode *pNext, *pPrev;
+} t_config_item;
+
+
+typedef struct {
+	t_config_item	*pItems;
+} t_config;
+
+
+/*
+ * Functions
+ */
+t_config * th_config_new(void);
+int	th_config_read(char *, t_config *);
+void	th_config_free(t_config *);
+int	th_config_write(FILE *, t_config *);
+
+int th_config_add_int(t_config *cfg, char *itemName, BOOL (*itemValidate)(t_config_item *), int *itemData, int itemDef);
+int th_config_add_uint(t_config *cfg, char *itemName, BOOL (*itemValidate)(t_config_item *), unsigned int *itemData, unsigned int itemDef);
+int th_config_add_str(t_config *cfg, char *itemName, BOOL (*itemValidate)(t_config_item *), char **itemData, char *itemDef);
+int th_config_add_bool(t_config *cfg, char *itemName, BOOL (*itemValidate)(t_config_item *), BOOL *itemData, BOOL itemDef);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TH_CONFIG_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_endian.c	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,96 @@
+/*
+ * Endianess handling
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2007 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#include "th_endian.h"
+#include <stdio.h>
+
+
+/*
+ * Endianess conversion routines
+ */
+#ifdef TH_BIGENDIAN
+/* Convert to from Small Endian
+ */
+void TH_NE16(uint16_t * iVal)
+{
+	uint16_t r = *iVal;
+	*iVal = ((r & 0xff) << 8) | ((r >> 8) & 0xff);
+}
+
+
+void TH_NE32(uint32_t * iVal)
+{
+	uint32_t r = *iVal;
+	*iVal = ((r & 0xff) << 8) | ((r >> 8) & 0xff) |
+	    ((r & 0x00ff0000L) << 8) | ((r >> 8) & 0x00ff0000L);
+}
+
+
+/* Convert to Small/Little Endian
+ */
+uint16_t TH_LE16(uint16_t iVal)
+{
+	return ((iVal & 0xff) << 8) | ((iVal >> 8) & 0xff);
+}
+
+
+uint32_t TH_LE32(uint32_t iVal)
+{
+	return ((iVal & 0xff) << 8) | ((iVal >> 8) & 0xff) |
+	    ((iVal & 0x00ff0000L) << 8) | ((iVal >> 8) & 0x00ff0000L);
+}
+#endif		/* TH_BIGENDIAN */
+
+
+/*
+ * File routines for endian-dependant data
+ */
+#define TH_FILE FILE
+#define TH_FREAD fread
+#define TH_FGETC fgetc
+#define TH_RNAME(X) TH_READ_##X
+#define TH_CODE_ENDIAN2
+#include "th_endian2.h"
+#undef TH_CODE_ENDIAN2
+
+
+BOOL TH_WRITE_STR(FILE * f, uint8_t * s, size_t l)
+{
+	return (fwrite(s, sizeof(uint8_t), l, f) == l);
+}
+
+
+void TH_WRITE_BE16(FILE * f, uint16_t v)
+{
+	fputc((v >> 8) & 0xFF, f);
+	fputc(v & 0xFF, f);
+}
+
+
+void TH_WRITE_BE32(FILE * f, uint32_t v)
+{
+	fputc((v >> 24) & 0xFF, f);
+	fputc((v >> 16) & 0xFF, f);
+	fputc((v >> 8) & 0xFF, f);
+	fputc(v & 0xFF, f);
+}
+
+
+void TH_WRITE_LE16(FILE * f, uint16_t v)
+{
+	fputc(v & 0xFF, f);
+	fputc((v >> 8) & 0xFF, f);
+}
+
+
+void TH_WRITE_LE32(FILE * f, uint32_t v)
+{
+	fputc(v & 0xFF, f);
+	fputc((v >> 8) & 0xFF, f);
+	fputc((v >> 16) & 0xFF, f);
+	fputc((v >> 24) & 0xFF, f);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_endian.h	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,55 @@
+/*
+ * Endianess handling
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2007 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifndef _TH_ENDIAN_H
+#define _TH_ENDIAN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <stdio.h>
+#include "th_types.h"
+
+/*
+ * Data endianess conversion routines
+ */
+#ifdef TH_BIGENDIAN
+void		TH_NE16(uint16_t *);
+void		TH_NE32(uint32_t *);
+uint16_t	TH_LE16(uint16_t);
+uint32_t	TH_LE32(uint32_t);
+#else
+#define		TH_NE16(X)
+#define		TH_NE32(X)
+#define		TH_LE16(X) X
+#define		TH_LE32(X) X
+#endif
+
+
+/*
+ * Endian-handling file read/write routines
+ */
+BOOL		TH_READ_STR(FILE *f, uint8_t *s, size_t l);
+uint16_t	TH_READ_BE16(FILE *f);
+uint32_t	TH_READ_BE32(FILE *f);
+uint16_t	TH_READ_LE16(FILE *f);
+uint32_t	TH_READ_LE32(FILE *f);
+
+BOOL		TH_WRITE_STR(FILE *f, uint8_t *s, size_t l);
+void		TH_WRITE_BE16(FILE *f, uint16_t v);
+void		TH_WRITE_BE32(FILE *f, uint32_t v);
+void		TH_WRITE_LE16(FILE *f, uint16_t v);
+void		TH_WRITE_LE32(FILE *f, uint32_t v);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TH_ENDIAN_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_endian2.h	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,49 @@
+/*
+ * Endianess handling
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2007 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#if (!defined(TH_RNAME) || !defined(TH_FILE) || !defined(TH_FREAD) || !defined(TH_FGETC))
+#error Some required preprocessor macros NOT defined, but th_endian2.h included!
+#endif
+
+#ifdef TH_CODE_ENDIAN2
+BOOL TH_RNAME(STR) (TH_FILE *f, uint8_t *s, size_t l)
+{
+	return (TH_FREAD(s, sizeof(uint8_t), l, f) == l);
+}
+
+
+uint16_t TH_RNAME(BE16) (TH_FILE *f)
+{
+	return (((uint16_t) TH_FGETC(f)) << 8) |
+		((uint16_t) TH_FGETC(f));
+}
+
+
+uint32_t TH_RNAME(BE32) (TH_FILE *f)
+{
+	return (((uint32_t) TH_FGETC(f)) << 24) |
+		(((uint32_t) TH_FGETC(f)) << 16) |
+		(((uint32_t) TH_FGETC(f)) << 8) |
+		((uint32_t) TH_FGETC(f));
+}
+
+
+uint16_t TH_RNAME(LE16) (TH_FILE *f)
+{
+	return ((uint16_t) TH_FGETC(f)) |
+		(((uint16_t) TH_FGETC(f)) << 8);
+}
+
+
+uint32_t TH_RNAME(LE32) (TH_FILE *f)
+{
+	return ((uint32_t) TH_FGETC(f)) |
+		(((uint32_t) TH_FGETC(f)) << 8) |
+		(((uint32_t) TH_FGETC(f)) << 16) |
+		(((uint32_t) TH_FGETC(f)) << 24);
+}
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_string.c	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,462 @@
+/*
+ * Miscellaneous string-handling related utility-functions
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "th_string.h"
+
+#define LPREV (pNode->pPrev)
+#define LNEXT (pNode->pNext)
+
+/* Allocate memory for a string with given length
+ */
+char *th_stralloc(const size_t l)
+{
+	assert(l > 0);
+	return th_malloc(sizeof(char) * l);
+}
+
+
+char *th_strrealloc(char * s, const size_t l)
+{
+	assert(l > 0);
+	return th_realloc(s, sizeof(char) * l);
+}
+
+
+/* Duplicate a string [strdup]
+ */
+char *th_strdup(char * str)
+{
+	char *result, *s, *d;
+
+	if (!str) return NULL;
+	
+	/* Allocate memory for destination */
+	result = th_stralloc(strlen(str) + 1);
+	if (!result)
+		return NULL;
+
+	/* Copy to the destination */
+	s = str;
+	d = result;
+	while (*s) {
+		*(d++) = *(s++);
+	}
+	*d = 0;
+
+	return result;
+}
+
+
+char *th_strncpy(char * dst, char * src, size_t n)
+{
+	char *s, *d;
+	size_t i;
+	assert(src);
+	assert(dst);
+
+	/* Copy to the destination */
+	i = n;
+	s = src;
+	d = dst;
+	while (*s && (i > 0)) {
+		*(d++) = *(s++);
+		i--;
+	}
+
+	/* Fill rest of space with zeros */
+	while (i > 0) {
+		*(d++) = 0;
+		i--;
+	}
+
+	/* Ensure that last is always zero */
+	dst[n - 1] = 0;
+
+	return dst;
+}
+
+
+int th_strncmp(char * str1, char * str2, size_t n)
+{
+	char *s1, *s2;
+	assert(str1);
+	assert(str2);
+
+	/* Check the string pointers */
+	if (str1 == str2)
+		return 0;
+
+	/* Go through the string */
+	s1 = str1;
+	s2 = str2;
+	while ((n > 0) && *s1 && *s2 && (*s1 == *s2)) {
+		s1++;
+		s2++;
+		n--;
+	}
+
+	if (n > 0)
+		return ((*s1) - (*s2));
+	else
+		return 0;
+}
+
+
+/* Compare two strings ignoring case [strcasecmp, strncasecmp]
+ */
+int th_strcasecmp(char * str1, char * str2)
+{
+	char *s1 = str1, *s2 = str2;
+	assert(str1);
+	assert(str2);
+
+	/* Check the string pointers */
+	if (str1 == str2)
+		return 0;
+
+	/* Go through the string */
+	while (*s1 && *s2 && (th_tolower(*s1) == th_tolower(*s2))) {
+		s1++;
+		s2++;
+	}
+
+	return (th_tolower(*s1) - th_tolower(*s2));
+}
+
+
+int th_strncasecmp(char * str1, char * str2, size_t n)
+{
+	char *s1 = str1, *s2 = str2;
+	assert(str1);
+	assert(str2);
+
+	/* Check the string pointers */
+	if (str1 == str2)
+		return 0;
+
+	/* Go through the string */
+	while ((n > 0) && *s1 && *s2 && (th_tolower(*s1) == th_tolower(*s2))) {
+		s1++;
+		s2++;
+		n--;
+	}
+
+	if (n > 0)
+		return (th_tolower(*s1) - th_tolower(*s2));
+	else
+		return 0;
+}
+
+
+/* Remove all occurences of control characters, in-place.
+ * Resulting string is always shorter or same length than original.
+ */
+void th_strip_ctrlchars(char * str)
+{
+	char *i, *j;
+	assert(str);
+
+	i = str;
+	j = str;
+	while (*i) {
+		if (!th_iscntrl(*i))
+			*(j++) = *i;
+		i++;
+	}
+
+	*j = 0;
+}
+
+
+/* Copy a given string over in *result.
+ */
+int th_pstrcpy(char ** result, char * str)
+{
+	assert(result);
+
+	/* Check the string pointers */
+	if (!str)
+		return -1;
+
+	/* Allocate memory for destination */
+	th_free(*result);
+	*result = th_stralloc(strlen(str) + 1);
+	if (!*result)
+		return -2;
+
+	/* Copy to the destination */
+	strcpy(*result, str);
+
+	return 0;
+}
+
+
+/* Concatenates a given string into string pointed by *result.
+ */
+int th_pstrcat(char ** result, char * str)
+{
+	assert(result);
+
+	/* Check the string pointers */
+	if (!str)
+		return -1;
+
+	if (*result != NULL) {
+		*result = th_strrealloc(*result, strlen(*result) + strlen(str) + 1);
+		if (*result == NULL)
+			return -1;
+
+		strcat(*result, str);
+	} else {
+		*result = th_stralloc(strlen(str) + 1);
+		if (*result == NULL)
+			return -1;
+
+		strcpy(*result, str);
+	}
+
+	return 0;
+}
+
+
+/* Find next non-whitespace character in string.
+ * Updates iPos into the position of such character and
+ * returns pointer to the string.
+ */
+char *th_findnext(char * str, size_t * iPos)
+{
+	assert(str);
+
+	/* Terminating NULL-character is not whitespace! */
+	while (th_isspace(str[*iPos]))
+		(*iPos)++;
+	return &str[*iPos];
+}
+
+
+/* Find next chSep-character from string
+ */
+char *th_findsep(char * str, size_t * iPos, char chSep)
+{
+	assert(str);
+
+	/* Terminating NULL-character is not digit! */
+	while (str[*iPos] && (str[*iPos] != chSep))
+		(*iPos)++;
+	return &str[*iPos];
+}
+
+
+/* Find next chSep- or whitespace from string
+ */
+char *th_findseporspace(char * str, size_t * iPos, char chSep)
+{
+	assert(str);
+
+	/* Terminating NULL-character is not digit! */
+	while (!th_isspace(str[*iPos]) && (str[*iPos] != chSep))
+		(*iPos)++;
+	return &str[*iPos];
+}
+
+
+/* Compare a string to a pattern. Case-SENSITIVE version.
+ * The matching pattern can consist of any normal characters plus
+ * wildcards ? and *. "?" matches any character and "*" matches
+ * any number of characters.
+ */
+BOOL th_strmatch(char * str, char * pattern)
+{
+	BOOL didMatch, isAnyMode, isEnd;
+	char *tmpPattern;
+
+	/* Check given pattern and string */
+	if (!str)
+		return FALSE;
+	if (!pattern)
+		return FALSE;
+
+	/* Initialize */
+	tmpPattern = NULL;
+	didMatch = TRUE;
+	isEnd = FALSE;
+	isAnyMode = FALSE;
+
+	/* Start comparision */
+	do {
+		didMatch = FALSE;
+		switch (*pattern) {
+		case '?':
+			/* Any single character matches */
+			if (*str) {
+				didMatch = TRUE;
+				pattern++;
+				str++;
+			}
+			break;
+
+		case '*':
+			didMatch = TRUE;
+			pattern++;
+			if (!*pattern)
+				isEnd = TRUE;
+			isAnyMode = TRUE;
+			tmpPattern = pattern;
+			break;
+
+		case 0:
+			if (isAnyMode) {
+				if (*str)
+					str++;
+				else
+					isEnd = TRUE;
+			} else {
+				if (*str) {
+					if (tmpPattern) {
+						isAnyMode = TRUE;
+						pattern = tmpPattern;
+					} else
+						didMatch = FALSE;
+				} else
+					isEnd = TRUE;
+			}
+			break;
+		default:
+			if (isAnyMode) {
+				if ((*pattern) == (*str)) {
+					isAnyMode = FALSE;
+					didMatch = TRUE;
+				} else {
+					if (*str) {
+						didMatch = TRUE;
+						str++;
+					}
+				}
+			} else {
+				if ((*pattern) == (*str)) {
+					didMatch = TRUE;
+					if (*pattern)
+						pattern++;
+					if (*str)
+						str++;
+				} else {
+					if (tmpPattern) {
+						didMatch = TRUE;
+						isAnyMode = TRUE;
+						pattern = tmpPattern;
+					}
+				}
+			}
+
+			if (!*str && !*pattern)
+				isEnd = TRUE;
+			break;
+
+		}		/* switch */
+
+	} while ((didMatch) && (!isEnd));
+
+	return didMatch;
+}
+
+
+/* Compare a string to a pattern. Case-INSENSITIVE version.
+ */
+BOOL th_strcasematch(char * str, char * pattern)
+{
+	BOOL didMatch, isAnyMode, isEnd;
+	char *tmpPattern;
+
+	/* Check given pattern and string */
+	if (!str)
+		return FALSE;
+	if (!pattern)
+		return FALSE;
+
+	/* Initialize */
+	tmpPattern = NULL;
+	didMatch = TRUE;
+	isEnd = FALSE;
+	isAnyMode = FALSE;
+
+	/* Start comparision */
+	do {
+		switch (*pattern) {
+		case '?':
+			/* Any single character matches */
+			if (*str) {
+				pattern++;
+				str++;
+			} else
+				didMatch = FALSE;
+			break;
+
+		case '*':
+			pattern++;
+			if (!*pattern || (*pattern == '?'))
+				isEnd = TRUE;
+			isAnyMode = TRUE;
+			tmpPattern = pattern;
+			break;
+
+		case 0:
+			if (isAnyMode) {
+				if (*str)
+					str++;
+				else
+					isEnd = TRUE;
+			} else {
+				if (*str) {
+					if (tmpPattern) {
+						isAnyMode = TRUE;
+						pattern = tmpPattern;
+					} else
+						didMatch = FALSE;
+				} else
+					isEnd = TRUE;
+			}
+			break;
+
+		default:
+			if (isAnyMode) {
+				if (th_tolower(*pattern) == th_tolower(*str)) {
+					isAnyMode = FALSE;
+				} else {
+					if (*str)
+						str++;
+					else
+						didMatch = FALSE;
+				}
+			} else {
+				if (th_tolower(*pattern) == th_tolower(*str)) {
+					if (*pattern)
+						pattern++;
+					if (*str)
+						str++;
+				} else {
+					if (tmpPattern) {
+						isAnyMode = TRUE;
+						pattern = tmpPattern;
+					} else
+						didMatch = FALSE;
+				}
+			}
+
+			if (!*str && !*pattern)
+				isEnd = TRUE;
+			break;
+
+		}		/* switch */
+
+	} while ((didMatch) && (!isEnd));
+
+	return didMatch;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_string.h	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,66 @@
+/*
+ * Miscellaneous string-handling related utility-functions
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifndef _TH_STRING_H
+#define _TH_STRING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "th_util.h"
+#include <stdlib.h>
+#include <ctype.h>
+
+/* Macros
+ */
+#define th_isalnum(c)	isalnum((int)(unsigned char) c)
+#define th_isalpha(c)	isalpha((int)(unsigned char) c)
+#define th_isascii(c)	isascii((int)(unsigned char) c)
+#define th_isblank(c)	isblank((int)(unsigned char) c)
+#define th_iscntrl(c)	iscntrl((int)(unsigned char) c)
+#define th_isdigit(c)	isdigit((int)(unsigned char) c)
+#define th_isgraph(c)	isgraph((int)(unsigned char) c)
+#define th_islower(c)	islower((int)(unsigned char) c)
+#define th_isprint(c)	isprint((int)(unsigned char) c)
+#define th_ispunct(c)	ispunct((int)(unsigned char) c)
+#define th_isspace(c)	isspace((int)(unsigned char) c)
+#define th_isupper(c)	isupper((int)(unsigned char) c)
+#define th_isxdigit(c)	isxdigit((int)(unsigned char) c)
+#define th_iscrlf(c)	((c=='\r')||(c=='\n'))
+
+#define th_isspecial(q)	(((q >= 0x5b) && (q <= 0x60)) || ((q >= 0x7b) && (q <= 0x7d)))
+
+#define th_tolower(c)	tolower((int)(unsigned char) c)
+#define th_toupper(c)	toupper((int)(unsigned char) c)
+
+
+/* Normal NUL-terminated string functions
+ */
+char	*th_stralloc(size_t);
+char	*th_strrealloc(char *, size_t);
+char	*th_strdup(char *);
+char	*th_strncpy(char *, char *, size_t);
+int	th_strcasecmp(char *, char *);
+int	th_strncasecmp(char *, char *, size_t);
+void	th_strip_ctrlchars(char *);
+
+int	th_pstrcpy(char **, char *);
+int	th_pstrcat(char **, char *);
+
+char	*th_findnext(char *, size_t *);
+char	*th_findsep(char *, size_t *, char);
+char	*th_findseporspace(char *, size_t *, char);
+
+BOOL	th_strmatch(char *, char *);
+BOOL	th_strcasematch(char *, char *);
+
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TH_STRING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_types.h	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,104 @@
+/*
+ * Type definations
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+/* If your code uses "config.h", you need to #include
+ * it before including this header.
+ */
+#ifndef _TH_TYPES_H
+#define _TH_TYPES_H
+
+#ifdef HAVE_STDINT_H
+#include <stdint.h>
+#ifndef HAVE_INT_TYPES
+#define HAVE_INT_TYPES 1
+#endif
+#endif
+
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#ifndef HAVE_INT_TYPES
+#define HAVE_INT_TYPES 1
+#endif
+#endif
+
+/* Shorthand types */
+typedef unsigned long int ulint_t;
+typedef signed long int lint_t;
+#ifndef HAVE_UINT_T
+typedef unsigned int uint_t;
+#endif
+
+/* Default assumptions for these types should be ok for most 32bit platforms...
+ * feel free to define TH_TYPE_* if necessary to remedy
+ */
+#ifdef TH_TYPE_I8
+typedef unsigned TH_TYPE_I8 uint8_t;	/* 8 bits, unsigned */
+typedef signed TH_TYPE_I8 int8_t;	/* 8 bits, signed */
+#else
+#ifndef HAVE_INT_TYPES
+typedef unsigned char uint8_t;
+typedef signed char int8_t;
+#endif
+#endif
+
+
+#ifdef TH_TYPE_I16
+typedef unsigned TH_TYPE_I16 uint16_t;	/* 16 bits, unsigned == 2 BYTEs */
+typedef signed TH_TYPE_I16 int16_t;	/* 16 bits, signed */
+#else
+#ifndef HAVE_INT_TYPES
+typedef unsigned short int uint16_t;
+typedef signed short int int16_t;
+#endif
+#endif
+
+#ifdef TH_TYPE_I32
+typedef unsigned TH_TYPE_I32 uint32_t;	/* 32 bits, unsigned == 4 BYTES == 2 WORDs */
+typedef signed TH_TYPE_I32 int32_t;	/* 32 bits, signed */
+#else
+#ifndef HAVE_INT_TYPES
+typedef unsigned int uint32_t;
+typedef signed int int32_t;
+#endif
+#endif
+
+#ifdef TH_TYPE_I64
+typedef unsigned TH_TYPE_I64 uint64_t;	/* 64 bits, unsigned == 8 BYTES == 2 DWORDs */
+typedef signed TH_TYPE_I64 int64_t;	/* 64 bits, signed */
+#else
+#ifndef HAVE_INT_TYPES
+typedef unsigned long long uint64_t;
+typedef signed long long int64_t;
+#endif
+#endif
+
+
+/* This is the character type used in all string-related routines of
+ * th_libs. Currently it is set to be equivalent of basetype "char",
+ * but under some platforms it may be necessary to use
+ * "unsigned char" instead.
+ *
+ * Also in future this type may be changed to hold 32-bit UNICODE
+ */
+typedef char char_t;
+
+
+/* Define a boolean type
+ */
+#if ((!defined(FALSE)) && (!defined(TRUE)) && (!defined(BOOL)))
+typedef enum { FALSE=0, TRUE=1 } BOOL;
+#endif
+
+#ifndef BOOL
+#ifdef bool
+#define BOOL bool
+#else
+#define BOOL int
+#endif
+#endif
+
+#endif /* _TH_TYPES_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_util.c	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,140 @@
+/*
+ * Generic utility-functions, macros and defaults
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2008 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
+ */
+#ifdef TH_NO_DEFAULTS
+#define TH_PROG_AUTHOR		""
+#define TH_PROG_LICENSE		""
+#else
+#define TH_PROG_AUTHOR		"By Matti 'ccr' Hämäläinen (C) Copyright 2008 TNSP"
+#define TH_PROG_LICENSE		"This software is licensed under GNU GPL version 2"
+#endif
+
+BOOL	th_isInitialized = 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_isInitialized = TRUE;
+}
+
+
+/* Print formatted error, warning and information messages
+ * TODO: Implement th_vfprintf() and friends?
+ */
+void THERR(const char *pcFormat, ...)
+{
+	va_list ap;
+	assert(th_isInitialized);
+
+	va_start(ap, pcFormat);
+	fprintf(stderr, "%s: ", th_prog_name);
+	vfprintf(stderr, pcFormat, ap);
+	va_end(ap);
+}
+
+
+void THMSG(int verbLevel, const char *pcFormat, ...)
+{
+	va_list ap;
+	assert(th_isInitialized);
+
+	/* Check if the current verbosity level is enough */
+	if (th_verbosityLevel >= verbLevel) {
+		va_start(ap, pcFormat);
+		fprintf(stderr, "%s: ", th_prog_name);
+		vfprintf(stderr, pcFormat, ap);
+		va_end(ap);
+	}
+}
+
+
+void THPRINT(int verbLevel, const char *pcFormat, ...)
+{
+	va_list ap;
+	assert(th_isInitialized);
+
+	/* Check if the current verbosity level is enough */
+	if (th_verbosityLevel >= verbLevel) {
+		va_start(ap, pcFormat);
+		vfprintf(stderr, pcFormat, 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_util.h	Wed Mar 26 04:41:58 2008 +0200
@@ -0,0 +1,76 @@
+/*
+ * Generic utility-functions, macros and defaults
+ * Programmed and designed by Matti 'ccr' Hamalainen
+ * (C) Copyright 2002-2008 Tecnic Software productions (TNSP)
+ *
+ * Please read file 'COPYING' for information on license and distribution.
+ */
+#ifndef _TH_UTIL_H
+#define _TH_UTIL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "th_types.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#ifndef HAVE_NO_ASSERT
+#include <assert.h>
+#endif
+
+#ifdef HAVE_STRING_H
+#include <string.h>
+#define HAVE_MEMSET 1
+#else
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#endif
+
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+
+/* Replacement for assert() */
+#ifdef HAVE_NO_ASSERT
+#  ifdef NDEBUG
+#    define assert(NEXPR) /* stub */
+#  else
+#    define assert(NEXPR) do { if (!(NEXPR)) { fprintf(stderr, "[%s:%d] assert(" # NEXPR ") failed!\n", __FILE__, __LINE__); abort(); } } while (0)
+#  endif
+#endif
+
+/* Global variables
+ */
+extern	int	th_verbosityLevel;
+extern	char	*th_prog_name,
+		*th_prog_fullname,
+		*th_prog_version,
+		*th_prog_author,
+		*th_prog_license;
+
+/* Functions
+ */
+void		th_init(char *progName, char *progFullName, char *progVersion,
+		char *progAuthor, char *progLicense);
+void		THERR(const char *, ...);
+void		THMSG(int, const char *, ...);
+void		THPRINT(int, const char *, ...);
+
+void	*	th_malloc(size_t);
+void	*	th_calloc(size_t, size_t);
+void	*	th_realloc(void *, size_t);
+void		th_free(void *);
+
+#ifdef HAVE_MEMSET
+#define	th_memset memset
+#else
+void	*th_memset(void *, int, size_t);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TH_UTIL_H */