# HG changeset patch # User Matti Hamalainen # Date 1205972103 0 # Node ID 72824312526315d041bcf4500a9afb9da6813a3d Import. diff -r 000000000000 -r 728243125263 Makefile --- /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 diff -r 000000000000 -r 728243125263 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 *~ + diff -r 000000000000 -r 728243125263 Makefile.w32 --- /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 diff -r 000000000000 -r 728243125263 TRICKS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TRICKS Thu Mar 20 00:15:03 2008 +0000 @@ -0,0 +1,22 @@ + +--,--'<@ + + + + + +----- untested ----- + + + - width= + - height= + + + + + + + +
+ + diff -r 000000000000 -r 728243125263 nnchat.c --- /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 +#else +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include "th_args.h" +#include "th_string.h" +#include +#include +#include + + +#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] "); +} + + +#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[] = { + { '<', "<" }, + { '>', ">" }, + /* + { '&', "&" }, + { 'ä', "ä" }, + { 'ö', "ö" }, + { 'Ä', "Ä" }, + { 'Ö', "Ö" }, + */ +}; + +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), + "%s%s", + user, tmpBuf); + + return sendToSocket(sock, tmpBuf2, strlen(tmpBuf2) + 1); +} + + +int handleUser(int sock, char *str) +{ + const char *msg = ""; + char *p = str, *q, *s; + + (void) sock; + + s = strstr(str, msg); + if (!s) return 1; + *s = 0; + s += strlen(msg); + + q = strstr(s, ""); + 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, ""); + + (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, ""); + + (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[] = { + { "", handleUser }, + { "", handleDeleteUser }, + { "", handleAddUser }, + { "", 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; +} diff -r 000000000000 -r 728243125263 th_args.c --- /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 +#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"); +} + diff -r 000000000000 -r 728243125263 th_args.h --- /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 +#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 */ diff -r 000000000000 -r 728243125263 th_string.c --- /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 +#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); +} diff -r 000000000000 -r 728243125263 th_string.h --- /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 +#include + +/* + * 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 */ diff -r 000000000000 -r 728243125263 th_types.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 +#ifndef HAVE_INT_TYPES +#define HAVE_INT_TYPES 1 +#endif +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#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 */ diff -r 000000000000 -r 728243125263 th_util.c --- /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 +#endif +#include "th_util.h" +#include + +/* + * 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 diff -r 000000000000 -r 728243125263 th_util.h --- /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 +#include +#ifndef HAVE_NO_ASSERT +#include +#endif + +#ifdef HAVE_STRING_H +#include +#define HAVE_MEMSET 1 +#else +#ifdef HAVE_STRINGS_H +#include +#endif +#endif + +#ifdef HAVE_MEMORY_H +#include +#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 */