changeset 0:728243125263

Import.
author Matti Hamalainen <ccr@tnsp.org>
date Thu, 20 Mar 2008 00:15:03 +0000
parents
children 351e96e01f4c
files Makefile Makefile.gen Makefile.w32 TRICKS nnchat.c th_args.c th_args.h th_string.c th_string.h th_types.h th_util.c th_util.h
diffstat 12 files changed, 2673 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,27 @@
+#
+# Configuration settings for typical UNIX
+#
+
+# Installation prefix
+PREFIX=/usr/local
+BINPATH=./
+
+# C-compiler, flags and linker flags
+CC=gcc
+CFLAGS=-DHAVE_STRING_H -DHAVE_INT_TYPES
+LDFLAGS=
+
+#CFLAGS += -DHAVE_STDINT_H
+#CFLAGS += -DHAVE_SYS_TYPES_H
+
+# For Solaris 9 with GCC, you need:
+#CFLAGS += -DHAVE_UINT_T
+#LDFLAGS += -lresolv
+
+
+# Miscellaneous
+RM=rm
+EXEEXT=
+
+
+include Makefile.gen
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile.gen	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,31 @@
+CFLAGS+=-g -W -Wall
+COMP=$(CC) $(CFLAGS)
+
+#
+# Objects
+#
+NNCHAT_BIN=$(BINPATH)nnchat$(EXEEXT)
+
+TARGETS=$(NNCHAT_BIN)
+
+
+#
+# Target rules
+#
+all: $(TARGETS)
+
+%.o: %.c %.h
+	$(COMP) -c -o $@ $<
+
+$(NNCHAT_BIN): nnchat.c th_util.o th_string.o th_args.o
+	$(COMP) -o $@ $+ $(LDFLAGS)
+
+#
+# Special targets
+#
+clean:
+	$(RM) -f $(TARGETS) *.o
+
+srcclean: clean
+	$(RM) -f *~
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile.w32	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,17 @@
+#
+# For win32 version cross-compilation with MiNGW suite @ Linux
+#
+PATH=/usr/local/cross-tools/i386-mingw32/bin:/usr/bin:/bin
+PREFIX=./win32/
+BINPATH=./win32/
+
+# C-compiler, flags and linker flags
+CC=gcc
+CFLAGS=-DHAVE_STRING_H
+LDFLAGS=-L/usr/local/cross-tools/lib -liberty -lws2_32 -s
+
+# Miscellaneous
+RM=rm
+EXEEXT=.exe
+
+include Makefile.gen
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/TRICKS	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,22 @@
+
+<B><FONT COLOR='#00FF00'>--,--'</FONT><FONT COLOR='#FF0000'>&lt;@</FONT></B>
+
+<U></U>
+
+<B></B>
+
+----- untested -----
+
+<IMG SRC='http://www.newbienudes.com/l/p/4218/032005505.jpg'>
+	- width=
+	- height=
+
+
+
+<left>
+
+<right>
+
+<center>
+
+<justify>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nnchat.c	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,764 @@
+#ifdef __WIN32
+#include <winsock2.h>
+#else
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+#include <sys/time.h>
+#include <netdb.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "th_args.h"
+#include "th_string.h"
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+
+
+#define SET_ALLOC_SIZE	(128)
+#define SET_SELECT_USEC (100000)
+
+
+/* Options
+ */
+int	optPort = 8005;
+int	optUserColor = 0x408060;
+char 	*optServer = "www11.servemedata.com",
+	*optUserName = NULL,
+	*optUserName2 = NULL,
+	*optPassword = NULL,
+	*optLogFilename = NULL;
+
+FILE	*optLogFile = NULL;
+
+
+/* Arguments
+ */
+optarg_t optList[] = {
+	{ 0, '?', "help",	"Show this help", OPT_NONE },
+	{ 1, 'v', "verbose",	"Be more verbose", OPT_NONE },
+	{ 2, 'p', "port",	"Connect to port", OPT_ARGREQ },
+	{ 3, 's', "server",	"Server to connect to", OPT_ARGREQ },
+	{ 4, 'C', "color",	"Initial color in RGB hex 000000", OPT_ARGREQ },
+	{ 5, 'l', "logfile",	"Log filename", OPT_ARGREQ },
+/*
+	{ 6, 'p', "plaintext",	"Use plaintext logging", OPT_NONE },
+*/
+};
+
+const int optListN = (sizeof(optList) / sizeof(optarg_t));
+
+
+void argShowHelp()
+{
+	th_args_help(stdout, optList, optListN, th_prog_name,
+		"[options] <username> <password>");
+}
+
+
+#ifdef __WIN32
+const char *hstrerror(int err)
+{
+	return "???";
+}
+#endif
+
+BOOL argHandleOpt(const int optN, char *optArg, char *currArg)
+{
+	switch (optN) {
+	case 0:
+		argShowHelp();
+		exit(0);
+		break;
+
+	case 1:
+		th_verbosityLevel++;
+		break;
+	
+	case 2:
+		optPort = atoi(optArg);
+		break;
+
+	case 3:
+		optServer = optArg;
+		break;
+	
+	case 4:
+		if (sscanf(optArg, "%06x", &optUserColor) != 1) {
+			THERR("Invalid color argument '%s', should be a RGB hex triplet '000000'.\n",
+				optArg);
+			return FALSE;
+		}
+		THMSG(1, "Using color #%06x\n", optUserColor);
+		break;
+
+	case 5:
+		optLogFilename = optArg;
+		break;
+
+	default:
+		THERR("Unknown option '%s'.\n", currArg);
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+
+BOOL argHandleFile(char *currArg)
+{
+	if (!optUserName)
+		optUserName = currArg;
+	else if (!optPassword)
+		optPassword = currArg;
+	else {
+		THERR("Username '%s' already specified on commandline!\n", optUserName);
+		return FALSE;
+	}
+	
+	return TRUE;
+}
+
+
+BOOL sendToSocket(int sock, char *buf, const size_t bufLen)
+{
+	size_t bufLeft = bufLen;
+	char *bufPtr = buf;
+	
+	while (bufLeft > 0) {
+		ssize_t bufSent;
+		bufSent = send(sock, bufPtr, bufLeft, 0);
+		if (bufSent < 0) return FALSE;
+		bufLeft -= bufSent;
+		bufPtr += bufSent;
+	}
+	return TRUE;
+}
+
+
+void printMsg(char *fmt, ...)
+{
+	char tmpStr[64] = "";
+	va_list ap;
+	time_t timeStamp;
+	struct tm *tmpTime;;
+	
+	timeStamp = time(NULL);
+	if ((tmpTime = localtime(&timeStamp)) != NULL) {
+		strftime(tmpStr, sizeof(tmpStr), "[%H:%M:%S] ", tmpTime);
+	}
+	
+	if (optLogFile) {
+		fputs(tmpStr, optLogFile);
+		va_start(ap, fmt);
+		vfprintf(optLogFile, fmt, ap);
+		va_end(ap);
+		fflush(optLogFile);
+	}
+	
+	fputs(tmpStr, stdout);
+	va_start(ap, fmt);
+	vfprintf(stdout, fmt, ap);
+	va_end(ap);
+	fflush(stdout);
+}
+
+
+BOOL bufRealloc(char **buf, size_t *size, size_t add)
+{
+	return ((*buf = th_realloc(*buf, *size + add)) != NULL);
+}
+
+#define pushChar(x) bufPushChar(&result, &resSize, &resPos, x)
+BOOL bufPushChar(char **buf, size_t *size, size_t *pos, char ch)
+{
+	if (*pos >= *size && !bufRealloc(buf, size, SET_ALLOC_SIZE))
+		return FALSE;
+	
+	(*buf)[*pos] = ch;
+	(*pos)++;
+	return TRUE;
+}
+
+#define pushStr(x) bufPushStr(&result, &resSize, &resPos, x)
+BOOL bufPushStr(char **buf, size_t *size, size_t *pos, char *str)
+{
+	size_t tmpLen;
+	
+	if (!str) return FALSE;
+	tmpLen = strlen(str);
+	
+	if ((*pos + tmpLen) >= *size && !bufRealloc(buf, size, tmpLen + SET_ALLOC_SIZE))
+		return FALSE;
+	
+	strcpy(*buf + *pos, str);
+	(*pos) += tmpLen;
+	return TRUE;
+}
+
+
+char *encodeStr1(char *str)
+{
+	char *result, *s = str;
+	size_t resSize, resPos = 0;
+	
+	if (!str) return NULL;
+	
+	resSize = strlen(str) + SET_ALLOC_SIZE;
+	if ((result = th_malloc(resSize)) == NULL)
+		return NULL;
+	
+	while (*s) {
+		switch (*s) {
+		case 32:
+			pushChar('+');
+			break;
+		
+		default:
+			if (th_isalnum(*s))
+				pushChar(*s);
+			else {
+				char tmpStr[4];
+				sprintf(tmpStr, "%2X", (unsigned char) *s);
+				pushChar('%');
+				pushStr(tmpStr);
+			}
+			break;
+		}
+		s++;
+	}
+	pushChar(0);
+	
+	return result;
+}
+
+int getxdigit(int c, int shift)
+{
+	int i;
+	
+	if (c >= 'A' && c <= 'F')
+		i = c - 'A' + 10;
+	else if (c >= 'a' && c <= 'f')
+		i = c - 'a' + 10;
+	else if (c >= '0' && c <= '9')
+		i = c - '0';
+	else
+		return -1;
+	
+	return i << shift;
+}
+
+char *decodeStr1(char *str)
+{
+	char *result, *s = str;
+	size_t resSize, resPos = 0;
+	int c;
+	
+	if (!str) return NULL;
+	
+	resSize = strlen(str) + SET_ALLOC_SIZE;
+	if ((result = th_malloc(resSize)) == NULL)
+		return NULL;
+	
+	while (*s) {
+		switch (*s) {
+		case '+':
+			pushChar(' ');
+			s++;
+			break;
+		
+		case '%':
+			s++;
+			if (*s == '%')
+				pushChar('%');
+			else if ((c = getxdigit(*s, 4)) >= 0) {
+				int i = getxdigit(*(++s), 0);
+				if (i >= 0) {
+					pushChar(c | i);
+				} else {
+					pushChar('§');
+					pushChar(*s);
+				}
+			} else {
+				pushChar('§');
+				pushChar(*s);
+			}
+			s++;
+			break;
+		
+		default:
+			pushChar(*s);
+			s++;
+		}
+	}
+	pushChar(0);
+	
+	return result;
+}
+
+
+char *stripTags(char *str)
+{
+	char *result, *s = str;
+	size_t resSize, resPos = 0;
+	
+	if (!str) return NULL;
+	
+	resSize = strlen(str) + SET_ALLOC_SIZE;
+	if ((result = th_malloc(resSize)) == NULL)
+		return NULL;
+	
+	while (*s) {
+		if (*s == '<') {
+			while (*s && *s != '>') s++;
+			if (*s == '>') s++;
+		} else
+			pushChar(*s++);
+	}
+	pushChar(0);
+	
+	return result;
+}
+
+
+typedef struct {
+	char c;
+	char *ent;
+} html_entity_t;
+
+
+html_entity_t HTMLEntities[] = {
+	{ '<', "&lt;" },
+	{ '>', "&gt;" },
+	/*
+	{ '&', "&amp;" },
+	{ 'ä', "&auml;" },
+	{ 'ö', "&ouml;" },
+	{ 'Ä', "&Auml;" },
+	{ 'Ö', "&Ouml;" },
+	*/
+};
+
+const int numHTMLEntities = (sizeof(HTMLEntities) / sizeof(html_entity_t));
+
+
+char *encodeStr2(char *str)
+{
+	char *result, *s = str;
+	size_t resSize, resPos = 0;
+	
+	if (!str) return NULL;
+	
+	resSize = strlen(str) + SET_ALLOC_SIZE;
+	if ((result = th_malloc(resSize)) == NULL)
+		return NULL;
+	
+	while (*s) {
+		int i;
+		BOOL found = FALSE;
+		for (i = 0; i < numHTMLEntities; i++)
+		if (HTMLEntities[i].c == *s) {
+			pushStr(HTMLEntities[i].ent);
+			found = TRUE;
+			break;
+		}
+		if (!found) pushChar(*s);
+		
+		s++;
+	}
+	pushChar(0);
+	
+	return result;
+}
+
+
+char *decodeStr2(char *str)
+{
+	char *result, *s = str;
+	size_t resSize, resPos = 0;
+	
+	if (!str) return NULL;
+	
+	resSize = strlen(str);
+	if ((result = th_malloc(resSize)) == NULL)
+		return NULL;
+	
+	while (*s) {
+		if (*s == '&') {
+			int i;
+			BOOL found = FALSE;
+			for (i = 0; i < numHTMLEntities; i++) {
+				html_entity_t *ent = &HTMLEntities[i];
+				int len = strlen(ent->ent);
+				if (!strncmp(s, ent->ent, len)) {
+					pushChar(ent->c);
+					s += len;
+					found = TRUE;
+					break;
+				}
+			}
+			if (!found) pushChar(*s++);
+		} else
+			pushChar(*s++);
+	}
+	pushChar(0);
+	
+	return result;
+}
+
+
+BOOL sendUserMsg(int sock, char *user, char *fmt, ...)
+{
+	char tmpBuf[4096], tmpBuf2[4096+256];
+	int n;
+	va_list ap;
+	
+	va_start(ap, fmt);
+	n = vsnprintf(tmpBuf, sizeof(tmpBuf), fmt, ap);
+	va_end(ap);
+	
+	if (n < 0) return FALSE;
+	
+	snprintf(tmpBuf2, sizeof(tmpBuf2),
+		"<USER>%s</USER><MESSAGE>%s</MESSAGE>",
+		user, tmpBuf);
+	
+	return sendToSocket(sock, tmpBuf2, strlen(tmpBuf2) + 1);
+}
+
+
+int handleUser(int sock, char *str)
+{
+	const char *msg = "</USER><MESSAGE>";
+	char *p = str, *q, *s;
+	
+	(void) sock;
+	
+	s = strstr(str, msg);
+	if (!s) return 1;
+	*s = 0;
+	s += strlen(msg);
+	
+	q = strstr(s, "</MESSAGE>");
+	if (!q) return 3;
+	*q = 0;
+		
+	s = decodeStr1(s);
+	if (!s) return -1;
+		
+	p = decodeStr1(p);
+	if (!p) {
+		th_free(s);
+		return -2;
+	}
+	
+	/* FIXME: decodeStr2() */
+	
+	if (*s == '/') {
+		char *t = stripTags(s+1);
+		printMsg("* %s\n", t);
+		th_free(t);
+	} else {
+		char *t = stripTags(s);
+		printMsg("<%s> %s\n", p, t);
+		th_free(t);
+	}
+		
+	th_free(s);
+	th_free(p);
+	return 0;
+}
+
+
+int handleLogin(int sock, char *str)
+{
+	if (!strncmp(str, "FAILURE", 7)) {
+		printMsg("Login failure.\n");
+		return -2;
+	} else if (!strncmp(str, "SUCCESS", 7)) {
+		printMsg("Login success.\n");
+		sendUserMsg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor);
+		return 0;
+	} else
+		return 1;
+}
+
+
+int handleAddUser(int sock, char *str)
+{
+	char *s = strstr(str, "</ADD_USER>");
+
+	(void) sock;
+
+	if (!s) return 1;
+	*s = 0;
+	printMsg("! %s ADDED.\n", str);
+	return 0;
+}
+
+
+int handleDeleteUser(int sock, char *str)
+{
+	char *s = strstr(str, "</DELETE_USER>");
+
+	(void) sock;
+
+	if (!s) return 1;
+	*s = 0;
+	printMsg("! %s DELETED.\n", str);
+	return 0;
+}
+
+
+int handleFoo(int sock, char *str)
+{
+	(void) sock; (void) str;
+	
+	return 0;
+}
+
+
+typedef struct {
+	char *cmd;
+	int (*handler)(int, char *);
+} protocmd_t;
+
+
+protocmd_t protoCmds[] = {
+	{ "<USER>", handleUser },
+	{ "<LOGIN_", handleLogin },
+	{ "<DELETE_USER>", handleDeleteUser },
+	{ "<ADD_USER>", handleAddUser },
+	{ "<NUMCLIENTS>", handleFoo },
+};
+
+const int nprotoCmds = (sizeof(protoCmds) / sizeof(protocmd_t));
+
+
+int handleProtocol(int sock, char *buf, size_t bufLen)
+{
+	int i;
+	
+	for (i = 0; i < nprotoCmds; i++) {
+		size_t cmdLen = strlen(protoCmds[i].cmd);
+		if (cmdLen < bufLen && !strncmp(buf, protoCmds[i].cmd, cmdLen)) {
+			return protoCmds[i].handler(sock, buf + cmdLen);
+		}
+	}
+	
+	return 1;
+}
+
+
+int handleInput(int sock, char *buf, size_t bufLen)
+{
+	char *tmpStr, *tmpStr2;
+	BOOL result;
+	
+	/* Trim right */
+	buf[--bufLen] = 0;
+	while (bufLen > 0 && (buf[bufLen] == '\n' || buf[bufLen] == '\r' || th_isspace(buf[bufLen])))
+		buf[bufLen--] = 0;
+	
+	//fprintf(stderr, "'%s'\n", buf); fflush(stderr);
+	
+	/* Check command */
+	if (*buf == 0) {
+		return 1;
+	} else if (*buf == '@') {
+		/* Send 1-pass encoded 'RAW' */
+		buf++;
+		printf("RAW>%s\n", buf);
+		fflush(stdout);
+		
+		tmpStr = encodeStr1(buf);
+		if (!tmpStr) return -2;
+		
+		result = sendUserMsg(sock, optUserName2, "%s", tmpStr);
+		th_free(tmpStr);
+		if (result)
+			return 0;
+		else
+			return -1;
+	} else {
+		/* Send double-encoded */
+		printf("ENC>%s\n", buf);
+		fflush(stdout);
+	
+		tmpStr = encodeStr2(buf);
+		if (!tmpStr) return -2;
+		tmpStr2 = encodeStr1(tmpStr);
+		if (!tmpStr2) {
+			th_free(tmpStr);
+			return -3;
+		}
+		
+		result = sendUserMsg(sock, optUserName2, "%s", tmpStr2);
+		th_free(tmpStr);
+		th_free(tmpStr2);
+		if (result)
+			return 0;
+		else
+			return -1;
+	}
+}
+
+
+int main(int argc, char *argv[])
+{
+	int tmpSocket;
+	struct hostent *tmpHost;
+	struct sockaddr_in tmpAddr;
+	BOOL exitProg = FALSE;
+
+	/* Initialize */
+	th_init("NNChat", "Newbie Nudes chat client", "0.2", NULL, NULL);
+	th_verbosityLevel = 0;
+	
+	/* Parse arguments */
+	th_args_process(argc, argv, optList, optListN,
+		argHandleOpt, argHandleFile, FALSE);
+	
+	/* Check the mode and arguments */
+	if (optUserName == NULL || optPassword == NULL) {
+		THERR("User/pass not specified, get some --help\n");
+		return -1;
+	}
+	
+	/* Open logfile */
+	if (optLogFilename) {
+		THMSG(1, "Opening logfile '%s'\n", optLogFilename);
+		
+		if ((optLogFile = fopen(optLogFilename, "a")) == NULL) {
+			THERR("Could not open logfile for appending!\n");
+			return -9;
+		}
+	}
+	
+	/* Okay ... */
+	THMSG(1, "Trying to resolve host '%s' ...\n", optServer);
+	tmpHost = gethostbyname(optServer);
+	if (tmpHost == NULL) {
+		THERR("Could not resolve hostname: %s.\n",
+			hstrerror(h_errno));
+		return -3;
+	}
+	THMSG(2, "True hostname: %s\n", tmpHost->h_name);
+	
+	tmpAddr.sin_family = AF_INET;
+	tmpAddr.sin_port = htons(optPort);
+	tmpAddr.sin_addr = *((struct in_addr *) tmpHost->h_addr);
+
+	THMSG(1, "Connecting to %s:%d ...\n",
+		inet_ntoa(tmpAddr.sin_addr), optPort);
+
+	if ((tmpSocket = socket(PF_INET, SOCK_STREAM, 0)) == -1) {
+		THERR("Could not open socket: %s\n", strerror(errno));
+		return -2;
+	}
+	
+	THMSG(2, "Using socket %d.\n", tmpSocket);
+	
+	if (connect(tmpSocket, (struct sockaddr *) &tmpAddr, sizeof(tmpAddr)) == -1) {
+		THERR("Could not connect: %s\n", strerror(errno));
+		return -5;
+	}
+	
+	THMSG(1, "Connected, logging in as '%s'.\n", optUserName);
+	optUserName2 = encodeStr1(optUserName);
+	
+	sendUserMsg(tmpSocket, optUserName2, "%%2Flogin%%20%%2Dsite%%20NN%%20%%2Dpassword%%20%s", optPassword);
+	
+	struct timeval tv;
+	fd_set sockfds;
+	fd_set inputfds;
+	
+	
+	FD_ZERO(&inputfds);
+	FD_SET(0, &inputfds);
+	
+	FD_ZERO(&sockfds);
+	FD_SET(tmpSocket, &sockfds);
+
+	while (!exitProg) {
+		ssize_t gotBuf;
+		int result;
+		char tmpBuf[4096];
+		fd_set tmpfds;
+		
+		/* Check for incoming data from the server */
+		tv.tv_sec = 0;
+		tv.tv_usec = SET_SELECT_USEC;
+		tmpfds = sockfds;
+		if ((result = select(tmpSocket+1, &tmpfds, NULL, NULL, &tv)) == -1) {
+			THERR("Error occured in select(sockfds): %s\n", strerror(errno));
+			exitProg = TRUE;
+		} else if (FD_ISSET(tmpSocket, &tmpfds)) {
+			gotBuf = recv(tmpSocket, tmpBuf, sizeof(tmpBuf), 0);
+			
+			if (gotBuf < 0) {
+				THERR("Error in recv: %s\n", strerror(errno));
+				exitProg = TRUE;
+			} else if (gotBuf == 0) {
+				THERR("Server closed connection.\n");
+				exitProg = TRUE;
+			} else {
+				/* Handle protocol data */
+				tmpBuf[gotBuf] = 0;
+				result = handleProtocol(tmpSocket, tmpBuf, gotBuf);
+				
+				if (result > 0) {
+					/* Couldn't handle the message for some reason */
+					THERR("Could not handle: %s\n", tmpBuf);
+				} else if (result < 0) {
+					/* Fatal error, quit */
+					THERR("Fatal error with message: %s\n", tmpBuf);
+					exitProg = TRUE;
+				}
+			}
+		}
+		
+		/* Check for user input */
+		tv.tv_sec = 0;
+		tv.tv_usec = SET_SELECT_USEC;
+		tmpfds = inputfds;
+		if ((result = select(1, &tmpfds, NULL, NULL, &tv)) == -1) {
+			THERR("Error occured in select(inputfds): %s\n", strerror(errno));
+			exitProg = TRUE;
+		} else if (FD_ISSET(0, &tmpfds)) {
+			gotBuf = read(0, tmpBuf, sizeof(tmpBuf));
+			
+			if (gotBuf < 0) {
+				THERR("Error in reading stdio.\n");
+				exitProg = TRUE;
+			} else {
+				/* Call the user input handler */
+				result = handleInput(tmpSocket, tmpBuf, gotBuf);
+				if (result < 0) {
+					THERR("Fatal error handling user input: %s\n",
+						tmpBuf);
+					exitProg = TRUE;
+				}
+			}
+		}
+		
+		fflush(stdout);
+		fflush(stderr);
+	}
+	
+	/* .. */
+	th_free(optUserName2);
+	close(tmpSocket);
+	
+	if (optLogFile) {
+		THMSG(1, "Closing logfile.\n");
+		fclose(optLogFile);
+	}
+	
+	THMSG(1, "Connection terminated.\n");
+	
+	return 0;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_args.c	Thu Mar 20 00:15:03 2008 +0000
@@ -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 = th_strlen(optList[i].optLong);
+		if (th_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	Thu Mar 20 00:15:03 2008 +0000
@@ -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_string.c	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,926 @@
+/*
+ * 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_t *th_stralloc(const size_t l)
+{
+	assert(l > 0);
+	return th_malloc(sizeof(char_t) * l);
+}
+
+
+char_t *th_strrealloc(char_t * s, const size_t l)
+{
+	assert(l > 0);
+	return th_realloc(s, sizeof(char_t) * l);
+}
+
+
+/* Calculate the length of a string [strlen]
+ */
+size_t th_strlen(char_t * pStr)
+{
+	size_t l = 0;
+	char_t *s = pStr;
+	assert(pStr);
+
+	while (*s) {
+		s++;
+		l++;
+	}
+
+	return l;
+}
+
+
+/* Duplicate a string [strdup]
+ */
+char_t *th_strdup(char_t * pStr)
+{
+	char_t *pResult, *s, *d;
+
+	if (!pStr) return NULL;
+	
+	/* Allocate memory for destination */
+	pResult = th_stralloc(th_strlen(pStr) + 1);
+	if (!pResult)
+		return NULL;
+
+	/* Copy to the destination */
+	s = pStr;
+	d = pResult;
+	while (*s) {
+		*(d++) = *(s++);
+	}
+	*d = 0;
+
+	return pResult;
+}
+
+
+/* Concatenate a string [strcat]
+ */
+char_t *th_strcat(char_t * pDest, char_t * pSource)
+{
+	char_t *s, *d;
+	assert(pSource);
+	assert(pDest);
+
+	/* Copy to the destination */
+	s = pSource;
+	d = pDest;
+
+	while (*d) d++;
+
+	while (*s) {
+		*(d++) = *(s++);
+	}
+	*d = 0;
+
+	return pDest;
+}
+
+
+/* Copy a string [strcpy, strncpy]
+ */
+char_t *th_strcpy(char_t * pDest, char_t * pSource)
+{
+	char_t *s, *d;
+	assert(pSource);
+	assert(pDest);
+
+	/* Copy to the destination */
+	s = pSource;
+	d = pDest;
+
+	while (*s) {
+		*(d++) = *(s++);
+	}
+	*d = 0;
+
+	return pDest;
+}
+
+
+char_t *th_strncpy(char_t * pDest, char_t * pSource, size_t n)
+{
+	char_t *s, *d;
+	size_t i;
+	assert(pSource);
+	assert(pDest);
+
+	/* Copy to the destination */
+	i = n;
+	s = pSource;
+	d = pDest;
+	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 */
+	pDest[n - 1] = 0;
+
+	return pDest;
+}
+
+
+/* Compare given strings [strcmp, strncmp]
+ */
+int th_strcmp(char_t * pStr1, char_t * pStr2)
+{
+	char_t *s1, *s2;
+	assert(pStr1);
+	assert(pStr2);
+
+	/* Check the string pointers */
+	if (pStr1 == pStr2)
+		return 0;
+
+	/* Go through the string */
+	s1 = pStr1;
+	s2 = pStr2;
+	while (*s1 && *s2 && (*s1 == *s2)) {
+		s1++;
+		s2++;
+	}
+
+	return ((*s1) - (*s2));
+}
+
+
+int th_strncmp(char_t * pStr1, char_t * pStr2, size_t n)
+{
+	char_t *s1, *s2;
+	assert(pStr1);
+	assert(pStr2);
+
+	/* Check the string pointers */
+	if (pStr1 == pStr2)
+		return 0;
+
+	/* Go through the string */
+	s1 = pStr1;
+	s2 = pStr2;
+	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_t * pStr1, char_t * pStr2)
+{
+	char_t *s1, *s2;
+	assert(pStr1);
+	assert(pStr2);
+
+	/* Check the string pointers */
+	if (pStr1 == pStr2)
+		return 0;
+
+	/* Go through the string */
+	s1 = pStr1;
+	s2 = pStr2;
+	while (*s1 && *s2 && (th_tolower(*s1) == th_tolower(*s2))) {
+		s1++;
+		s2++;
+	}
+
+	return (th_tolower(*s1) - th_tolower(*s2));
+}
+
+
+int th_strncasecmp(char_t * pStr1, char_t * pStr2, size_t n)
+{
+	char_t *s1, *s2;
+	assert(pStr1);
+	assert(pStr2);
+
+	/* Check the string pointers */
+	if (pStr1 == pStr2)
+		return 0;
+
+	/* Go through the string */
+	s1 = pStr1;
+	s2 = pStr2;
+	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_t * pStr)
+{
+	char_t *i, *j;
+	assert(pStr);
+
+	i = pStr;
+	j = pStr;
+	while (*i) {
+		if (!th_iscntrl(*i))
+			*(j++) = *i;
+		i++;
+	}
+
+	*j = 0;
+}
+
+
+/* Locate a substring [strstr]
+ */
+char_t *th_strstr(char_t * haystack, char_t * needle)
+{
+	char_t *h, *n, *s;
+	assert(haystack);
+	assert(needle);
+
+	/* If 'needle' is empty, we return 'haystack' */
+	if (!*needle)
+		return haystack;
+
+	/* Search for 'needle' in 'haystack' */
+	h = haystack;
+	n = needle;
+	while (*h) {
+		/* Find possible start of 'needle' */
+		while (*h && (*h != *n))
+			h++;
+
+		if (*h == *n) {
+			/* Found, check rest */
+			s = h;
+			n = needle;
+			while (*h) {
+				if (!*n)
+					return s;
+				else
+				if (*h != *n)
+					break;
+				n++;
+				h++;
+			}
+			h = s;
+			h++;
+		}
+	}
+
+	return NULL;
+}
+
+
+/* Copy a given string over in *ppResult.
+ */
+int th_pstrcpy(char_t ** ppResult, char_t * pStr)
+{
+	assert(ppResult);
+
+	/* Check the string pointers */
+	if (!pStr)
+		return -1;
+
+	/* Allocate memory for destination */
+	th_free(*ppResult);
+	*ppResult = th_stralloc(th_strlen(pStr) + 1);
+	if (!*ppResult)
+		return -2;
+
+	/* Copy to the destination */
+	th_strcpy(*ppResult, pStr);
+
+	return 0;
+}
+
+
+/* Concatenates a given string into string pointed by *ppResult.
+ */
+int th_pstrcat(char_t ** ppResult, char_t * pStr)
+{
+	assert(ppResult);
+
+	/* Check the string pointers */
+	if (!pStr)
+		return -1;
+
+	if (*ppResult != NULL) {
+		*ppResult = th_strrealloc(*ppResult, th_strlen(*ppResult) + th_strlen(pStr) + 1);
+		if (*ppResult == NULL)
+			return -1;
+
+		th_strcat(*ppResult, pStr);
+	} else {
+		*ppResult = th_stralloc(th_strlen(pStr) + 1);
+		if (*ppResult == NULL)
+			return -1;
+
+		th_strcpy(*ppResult, pStr);
+	}
+
+	return 0;
+}
+
+
+/* Find next non-whitespace character in string.
+ * Updates iPos into the position of such character and
+ * returns pointer to the string.
+ */
+char_t *th_findnext(char_t * pStr, size_t * iPos)
+{
+	assert(pStr);
+
+	/* Terminating NULL-character is not whitespace! */
+	while (th_isspace(pStr[*iPos]))
+		(*iPos)++;
+	return &pStr[*iPos];
+}
+
+
+/* Find next chSep-character from string
+ */
+char_t *th_findsep(char_t * pStr, size_t * iPos, char_t chSep)
+{
+	assert(pStr);
+
+	/* Terminating NULL-character is not digit! */
+	while (pStr[*iPos] && (pStr[*iPos] != chSep))
+		(*iPos)++;
+	return &pStr[*iPos];
+}
+
+
+/* Find next chSep- or whitespace from string
+ */
+char_t *th_findseporspace(char_t * pStr, size_t * iPos, char_t chSep)
+{
+	assert(pStr);
+
+	/* Terminating NULL-character is not digit! */
+	while (!th_isspace(pStr[*iPos]) && (pStr[*iPos] != chSep))
+		(*iPos)++;
+	return &pStr[*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_t * pStr, char_t * pPattern)
+{
+	BOOL didMatch, isAnyMode, isEnd;
+	char_t *tmpPattern;
+
+	/* Check given pattern and string */
+	if (!pStr)
+		return FALSE;
+	if (!pPattern)
+		return FALSE;
+
+	/* Initialize */
+	tmpPattern = NULL;
+	didMatch = TRUE;
+	isEnd = FALSE;
+	isAnyMode = FALSE;
+
+	/* Start comparision */
+	do {
+		didMatch = FALSE;
+		switch (*pPattern) {
+		case '?':
+			/* Any single character matches */
+			if (*pStr) {
+				didMatch = TRUE;
+				pPattern++;
+				pStr++;
+			}
+			break;
+
+		case '*':
+			didMatch = TRUE;
+			pPattern++;
+			if (!*pPattern)
+				isEnd = TRUE;
+			isAnyMode = TRUE;
+			tmpPattern = pPattern;
+			break;
+
+		case 0:
+			if (isAnyMode) {
+				if (*pStr)
+					pStr++;
+				else
+					isEnd = TRUE;
+			} else {
+				if (*pStr) {
+					if (tmpPattern) {
+						isAnyMode = TRUE;
+						pPattern = tmpPattern;
+					} else
+						didMatch = FALSE;
+				} else
+					isEnd = TRUE;
+			}
+			break;
+		default:
+			if (isAnyMode) {
+				if ((*pPattern) == (*pStr)) {
+					isAnyMode = FALSE;
+					didMatch = TRUE;
+				} else {
+					if (*pStr) {
+						didMatch = TRUE;
+						pStr++;
+					}
+				}
+			} else {
+				if ((*pPattern) == (*pStr)) {
+					didMatch = TRUE;
+					if (*pPattern)
+						pPattern++;
+					if (*pStr)
+						pStr++;
+				} else {
+					if (tmpPattern) {
+						didMatch = TRUE;
+						isAnyMode = TRUE;
+						pPattern = tmpPattern;
+					}
+				}
+			}
+
+			if (!*pStr && !*pPattern)
+				isEnd = TRUE;
+			break;
+
+		}		/* switch */
+
+	} while ((didMatch) && (!isEnd));
+
+	return didMatch;
+}
+
+
+/* Compare a string to a pattern. Case-INSENSITIVE version.
+ */
+BOOL th_strcasematch(char_t * pStr, char_t * pPattern)
+{
+	BOOL didMatch, isAnyMode, isEnd;
+	char_t *tmpPattern;
+
+	/* Check given pattern and string */
+	if (!pStr)
+		return FALSE;
+	if (!pPattern)
+		return FALSE;
+
+	/* Initialize */
+	tmpPattern = NULL;
+	didMatch = TRUE;
+	isEnd = FALSE;
+	isAnyMode = FALSE;
+
+	/* Start comparision */
+	do {
+		switch (*pPattern) {
+		case '?':
+			/* Any single character matches */
+			if (*pStr) {
+				pPattern++;
+				pStr++;
+			} else
+				didMatch = FALSE;
+			break;
+
+		case '*':
+			pPattern++;
+			if (!*pPattern || (*pPattern == '?'))
+				isEnd = TRUE;
+			isAnyMode = TRUE;
+			tmpPattern = pPattern;
+			break;
+
+		case 0:
+			if (isAnyMode) {
+				if (*pStr)
+					pStr++;
+				else
+					isEnd = TRUE;
+			} else {
+				if (*pStr) {
+					if (tmpPattern) {
+						isAnyMode = TRUE;
+						pPattern = tmpPattern;
+					} else
+						didMatch = FALSE;
+				} else
+					isEnd = TRUE;
+			}
+			break;
+
+		default:
+			if (isAnyMode) {
+				if (th_tolower(*pPattern) == th_tolower(*pStr)) {
+					isAnyMode = FALSE;
+				} else {
+					if (*pStr)
+						pStr++;
+					else
+						didMatch = FALSE;
+				}
+			} else {
+				if (th_tolower(*pPattern) == th_tolower(*pStr)) {
+					if (*pPattern)
+						pPattern++;
+					if (*pStr)
+						pStr++;
+				} else {
+					if (tmpPattern) {
+						isAnyMode = TRUE;
+						pPattern = tmpPattern;
+					} else
+						didMatch = FALSE;
+				}
+			}
+
+			if (!*pStr && !*pPattern)
+				isEnd = TRUE;
+			break;
+
+		}		/* switch */
+
+	} while ((didMatch) && (!isEnd));
+
+	return didMatch;
+}
+
+
+/*
+ * Handling of string-lists and hashes
+ */
+t_str_node *th_strnode_new(char_t * pcStr, ulint_t nUsed, void *pData)
+{
+	t_str_node *pResult;
+
+	/* Allocate memory for new node */
+	pResult = (t_str_node *) th_calloc(1, sizeof(t_str_node));
+	if (!pResult)
+		return NULL;
+
+	/* Set fields */
+	th_pstrcpy(&pResult->pcStr, pcStr);
+	pResult->nUsed = nUsed;
+	pResult->pData = pData;
+
+	return pResult;
+}
+
+
+void th_strnode_free(t_str_node * pNode)
+{
+	assert(pNode);
+
+	th_free(pNode->pcStr);
+	th_free(pNode);
+}
+
+
+/* Insert a new node into strlist
+ */
+void th_strlist_insert(t_str_node ** strList, t_str_node * pNode)
+{
+	assert(strList);
+	assert(pNode);
+
+	/* Insert into linked list */
+	if (*strList) {
+		/* The first node's pPrev points to last node */
+		LPREV = (*strList)->pPrev;	/* New node's prev = Previous last node */
+		(*strList)->pPrev->pNext = pNode;	/* Previous last node's next = New node */
+		(*strList)->pPrev = pNode;	/* New last node = New node */
+		LNEXT = NULL;	/* But next is NULL! */
+	} else {
+		(*strList) = pNode;	/* First node ... */
+		LPREV = pNode;	/* ... it's also last */
+		LNEXT = NULL;	/* But next is NULL! */
+	}
+
+}
+
+
+/* Free a given strlist
+ */
+void th_strlist_free(t_str_node * strList)
+{
+	t_str_node *pNode, *nNode;
+
+	pNode = strList;
+	while (pNode) {
+		nNode = pNode->pNext;
+		th_strnode_free(pNode);
+		pNode = nNode;
+	}
+}
+
+
+/* Create a strIndex from strlist
+ */
+t_str_index *th_strlist_makeindex(t_str_node * strList)
+{
+	t_str_index *pResult;
+	t_str_node *pCurr;
+	ulint_t n;
+	assert(strList);
+
+	/* Computer number of nodes */
+	for (n = 0, pCurr = strList; pCurr; pCurr = pCurr->pNext)
+		n++;
+
+	/* Check number of nodes */
+	if (n == 0)
+		return NULL;
+
+	/* Allocate memory for index */
+	pResult = (t_str_index *) th_calloc(1, sizeof(t_str_index));
+	if (!pResult)
+		return NULL;
+
+	pResult->n = n;
+	pResult->ppIndex = (t_str_node **) th_calloc(n, sizeof(t_str_node *));
+	if (!pResult->ppIndex) {
+		th_free(pResult);
+		return NULL;
+	}
+
+	/* Create the index */
+	for (n = 0, pCurr = strList; pCurr && (n < pResult->n); pCurr = pCurr->pNext)
+		pResult->ppIndex[n++] = pCurr;
+
+	return pResult;
+}
+
+
+/* Insert a node into given strhash
+ */
+int th_strhash_insert(t_str_hash strHash, t_str_node * pNode, BOOL ignoreCase)
+{
+	int i;
+	assert(strHash);
+	assert(pNode);
+	assert(pNode->pcStr);
+
+	if (ignoreCase)
+		i = th_tolower(pNode->pcStr[0]);
+	else
+		i = pNode->pcStr[0];
+
+	/* Check the hashcode */
+	if ((i < 0) && (i >= SET_HASH_MAXINDEX))
+		return -1;
+
+	if (strHash[i]) {
+		/* The first node's pPrev points to last node */
+		pNode->pPrev = strHash[i]->pPrev;	/* New node's prev = Previous last node */
+		strHash[i]->pPrev->pNext = pNode;	/* Previous last node's next = New node */
+		strHash[i]->pPrev = pNode;	/* New last node = New node */
+		pNode->pNext = NULL;	/* But next is NULL! */
+	} else {
+		strHash[i] = pNode;	/* First node */
+		pNode->pPrev = pNode;	/* But also last */
+		pNode->pNext = NULL;	/* But next is NULL! */
+	}
+
+	return 0;
+}
+
+
+/* Free a given strhash
+ */
+void th_strhash_free(t_str_hash strHash)
+{
+	int i;
+	assert(strHash);
+
+	for (i = 0; i < SET_HASH_MAXINDEX; i++)
+		th_strlist_free(strHash[i]);
+}
+
+
+/* Change pData for matching entries to new value
+ */
+void th_strhash_change_pdata(t_str_hash strHash, void *pFind, void *pNew)
+{
+	t_str_node *pCurr;
+	int i;
+	assert(strHash);
+
+	for (i = 0; i < SET_HASH_MAXINDEX; i++) {
+		/* Find from linked list */
+		pCurr = strHash[i];
+		while (pCurr) {
+			if (pCurr->pData == pFind)
+				pCurr->pData = pNew;
+
+			pCurr = pCurr->pNext;
+		}
+	}
+}
+
+
+/* Search a string from a given stringhash, either case-sensitive or insensitive
+ */
+t_str_node *th_strhash_search(t_str_hash strHash, char_t * findStr, BOOL ignoreCase)
+{
+	t_str_node *pCurr;
+	int i;
+	BOOL isFound;
+	assert(strHash);
+	assert(findStr);
+
+	isFound = FALSE;
+	pCurr = NULL;
+
+	/* Check hashcode */
+	if (ignoreCase)
+		i = ((unsigned char) th_tolower(findStr[0]));
+	else
+		i = ((unsigned char) findStr[0]);
+
+	if ((i < 0) && (i >= SET_HASH_MAXINDEX))
+		return NULL;
+
+	/* Find from linked list */
+	pCurr = strHash[i];
+
+	if (ignoreCase) {
+		/* Case in-sensitive search */
+		while (pCurr && !isFound) {
+			if (th_strcasecmp(findStr, pCurr->pcStr) == 0)
+				isFound = TRUE;
+			else
+				pCurr = pCurr->pNext;
+		}
+	} else {
+		/* Case sensitive search */
+		while (pCurr && !isFound) {
+			if (th_strcmp(findStr, pCurr->pcStr) == 0)
+				isFound = TRUE;
+			else
+				pCurr = pCurr->pNext;
+		}
+	}
+
+	/* Return result */
+	if (isFound)
+		return pCurr;
+	else
+		return NULL;
+}
+
+
+/* Create a strIndex from strHash
+ */
+t_str_index *th_strhash_makeindex(t_str_hash strHash)
+{
+	t_str_index *pResult;
+	t_str_node *pCurr;
+	unsigned int n, i;
+	assert(strHash);
+
+	/* Computer number of nodes */
+	for (n = i = 0; i < SET_HASH_MAXINDEX; i++) {
+		pCurr = strHash[i];
+		while (pCurr) {
+			n++;
+			pCurr = pCurr->pNext;
+		}
+	}
+
+	/* Check number of nodes */
+	if (n <= 0)
+		return NULL;
+
+	/* Allocate memory for index */
+	pResult = (t_str_index *) th_calloc(1, sizeof(t_str_index));
+	if (!pResult)
+		return NULL;
+
+	pResult->n = n;
+	pResult->ppIndex = (t_str_node **) th_calloc(n, sizeof(t_str_node *));
+	if (!pResult->ppIndex) {
+		th_free(pResult);
+		return NULL;
+	}
+
+	/* Create the index */
+	for (n = i = 0; (i < SET_HASH_MAXINDEX) && (n < pResult->n); i++) {
+		pCurr = strHash[i];
+		while (pCurr && (n < pResult->n)) {
+			pResult->ppIndex[n++] = pCurr;
+			pCurr = pCurr->pNext;
+		}
+	}
+
+	return pResult;
+}
+
+
+/* Free a given strIndex
+ */
+void th_strindex_free(t_str_index * strIndex)
+{
+	if (strIndex) {
+		th_free(strIndex->ppIndex);
+		th_free(strIndex);
+	}
+}
+
+
+/* Compare two t_str_nodes by nUsed
+ */
+int th_strindex_cmp_used(const void *pNode1, const void *pNode2)
+{
+	t_str_node *pStr1, *pStr2;
+
+	pStr1 = *(t_str_node **) pNode1;
+	pStr2 = *(t_str_node **) pNode2;
+
+	if (pStr1->nUsed > pStr2->nUsed)
+		return -1;
+	else if (pStr1->nUsed < pStr2->nUsed)
+		return 1;
+	else
+		return 0;
+}
+
+
+/* Sort an strIndex by nUsed, using th_strindex_cmp_used()
+ */
+void th_strindex_sort_nused(t_str_index * strIndex)
+{
+	assert(strIndex);
+	assert(strIndex->ppIndex);
+
+	qsort(strIndex->ppIndex, strIndex->n, sizeof(t_str_node *), th_strindex_cmp_used);
+}
+
+
+/* Compare two t_str_nodes via strcmp()
+ */
+int th_strindex_cmp_alpha(const void *pNode1, const void *pNode2)
+{
+	t_str_node *pStr1, *pStr2;
+
+	pStr1 = *(t_str_node **) pNode1;
+	pStr2 = *(t_str_node **) pNode2;
+
+	return th_strcmp(pStr1->pcStr, pStr2->pcStr);
+}
+
+
+/* Sort an strIndex by nUsed, using th_strindex_cmp_used()
+ */
+void th_strindex_sort_alpha(t_str_index * strIndex)
+{
+	assert(strIndex);
+	assert(strIndex->ppIndex);
+
+	qsort(strIndex->ppIndex, strIndex->n, sizeof(t_str_node *), th_strindex_cmp_alpha);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_string.h	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,116 @@
+/*
+ * 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
+ */
+/* TODO - Remove usage of libc functions for increased portability.
+ * Possible issues are locales, impossible to implement them...
+ */
+#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)
+
+/*
+ * Typedefs and structures
+ */
+typedef struct tstrnode {
+	char_t	*pcStr;		/* String */
+	ulint_t	nUsed;		/* Times this string has been referenced */
+	void	*pData;		/* Extra data */
+	
+	struct tstrnode *pPrev, *pNext;
+} t_str_node;
+
+
+#define SET_HASH_MAXINDEX	(256)
+
+typedef t_str_node *t_str_hash[SET_HASH_MAXINDEX];
+
+
+typedef struct tstrindex {
+	t_str_node	**ppIndex;
+	ulint_t		n;
+} t_str_index;
+
+
+/*
+ * Normal NUL-terminated string functions
+ */
+char_t *th_stralloc(size_t);
+char_t *th_strrealloc(char_t *, size_t);
+size_t	th_strlen(char_t *);
+char_t *th_strdup(char_t *);
+char_t *th_strcat(char_t *, char_t *);
+char_t *th_strcpy(char_t *, char_t *);
+char_t *th_strncpy(char_t *, char_t *, size_t);
+int	th_strcmp(char_t *, char_t *);
+int	th_strncmp(char_t *, char_t *, size_t);
+int	th_strcasecmp(char_t *, char_t *);
+int	th_strncasecmp(char_t *, char_t *, size_t);
+void	th_strip_ctrlchars(char_t *);
+char_t *th_strstr(char_t *, char_t *);
+
+int	th_pstrcpy(char_t **, char_t *);
+int	th_pstrcat(char_t **, char_t *);
+
+char_t	*th_findnext(char_t *, size_t *);
+char_t	*th_findsep(char_t *, size_t *, char_t);
+char_t	*th_findseporspace(char_t *, size_t *, char_t);
+
+BOOL	th_strmatch(char_t *, char_t *);
+BOOL	th_strcasematch(char_t *, char_t *);
+
+
+/*
+ * Handling of string-lists and hashes
+ */
+t_str_node *	th_strnode_new(char_t *, ulint_t, void *);
+void		th_strnode_free(t_str_node *);
+void		th_strlist_free(t_str_node *);
+void		th_strlist_insert(t_str_node **, t_str_node *);
+int		th_strhash_insert(t_str_node **, t_str_node *, BOOL);
+void		th_strhash_free(t_str_node **);
+void		th_strhash_change_pdata(t_str_hash, void *, void *);
+t_str_node *	th_strhash_search(t_str_node **, char_t *, BOOL);
+t_str_index *	th_strhash_makeindex(t_str_node **);
+t_str_index *	th_strlist_makeindex(t_str_node *);
+void		th_strindex_free(t_str_index *);
+void		th_strindex_sort_nused(t_str_index *);
+void		th_strindex_sort_alpha(t_str_index *);
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _TH_STRING_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/th_types.h	Thu Mar 20 00:15:03 2008 +0000
@@ -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	Thu Mar 20 00:15:03 2008 +0000
@@ -0,0 +1,153 @@
+/*
+ * 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
+ */
+#define	TH_PROG_NAME		"program"
+#define	TH_PROG_FULLNAME	"A Program"
+#define TH_PROG_VERSION		"0.0.0"
+#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"
+
+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)
+{
+	/* Free previous values */
+	if (progName)
+		th_prog_name = progName;
+	else
+		th_prog_name = TH_PROG_NAME;
+
+	if (progFullName)
+		th_prog_fullname = progFullName;
+	else
+		th_prog_fullname = TH_PROG_FULLNAME;
+
+	if (progVersion)
+		th_prog_version = progVersion;
+	else
+		th_prog_version = TH_PROG_VERSION;
+
+	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
+ * TODO: Write alternate implementations for calloc and realloc,
+ * to improve portability to platforms which don't implement them.
+ */
+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	Thu Mar 20 00:15:03 2008 +0000
@@ -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 */