# HG changeset patch # User Matti Hamalainen # Date 1288460229 -10800 # Node ID ffe8bbd429fa0aa53b116c2f4667516e83c8f5ee # Parent 10daf4660caeb3b037737f3621e25ebd39aad0aa Config file. diff -r 10daf4660cae -r ffe8bbd429fa nnchat.c --- a/nnchat.c Sat Oct 30 17:03:29 2010 +0300 +++ b/nnchat.c Sat Oct 30 20:37:09 2010 +0300 @@ -6,6 +6,7 @@ #include "libnnchat.h" #include #include "th_args.h" +#include "th_config.h" #include #include #ifdef __WIN32 @@ -55,6 +56,9 @@ BOOL setPrvMode = FALSE; BOOL ignoreMode = FALSE; +char *setConfigFile = "config"; +cfgitem_t *cfg = NULL; + /* Arguments */ @@ -563,6 +567,19 @@ printMsg("Setting color to #%06x\n", optUserColor); nn_send_msg(sock, optUserName2, "%%2FSetFontColor%%20%%2Dcolor%%20%06X", optUserColor); return 0; + } else if (!strncasecmp(buf, "/save", 5)) { + /* Save configuration */ + FILE *f = fopen(setConfigFile, "w"); + if (f == NULL) { + printMsg("Could not save configuration to file '%s'!\n", setConfigFile); + return 0; + } + printMsg("Configuration saved in file '%s', res=%d\n", + setConfigFile, + th_cfg_write(f, setConfigFile, cfg)); + + fclose(f); + return 0; } else if (!strncasecmp(buf, "/w ", 3)) { /* Open given username's profile via firefox in a new tab */ char *name = trimLeft(buf + 3), @@ -766,6 +783,31 @@ "Written and designed by Anonymous Finnish Guy (C) 2008-2010", "This software is freeware, use and distribute as you wish."); th_verbosityLevel = 0; + + /* Read config */ + { + cfgitem_t *tmpcfg = NULL; + FILE *cfgfile; + + th_cfg_add_comment(&tmpcfg, "General settings"); + th_cfg_add_string(&tmpcfg, "username", &optUserName, NULL); + th_cfg_add_string(&tmpcfg, "password", &optPassword, NULL); + th_cfg_add_hexvalue(&tmpcfg, "color", &optUserColor, optUserColor); + th_cfg_add_section(&cfg, "general", tmpcfg); + + tmpcfg = NULL; + th_cfg_add_comment(&tmpcfg, "Chat server hostname or IP address"); + th_cfg_add_string(&tmpcfg, "host", &optServer, optServer); + th_cfg_add_comment(&tmpcfg, "Default port to connect to (8002 = public room, 8003 = passion pit, 8005 = members only)"); + th_cfg_add_int(&tmpcfg, "port", &optPort, optPort); + th_cfg_add_section(&cfg, "server", tmpcfg); + + th_cfg_add_comment(&cfg, "People to be ignored in ignore mode"); + th_cfg_add_string(&cfg, "ignores", ignoreList, NULL); + + if ((cfgfile = fopen(setConfigFile, "r")) != NULL) + th_cfg_read(cfgfile, setConfigFile, cfg); + } /* Parse arguments */ argsOK = th_args_process(argc, argv, optList, optListN, @@ -1202,7 +1244,6 @@ THMSG(1, "Closing logfile.\n"); fclose(optLogFile); } - - + return 0; } diff -r 10daf4660cae -r ffe8bbd429fa th_config.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/th_config.c Sat Oct 30 20:37:09 2010 +0300 @@ -0,0 +1,662 @@ +/* + * Very simple configuration handling functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2004-2008 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include +#include +#include "th_config.h" +#include "th_util.h" +#include "th_string.h" + +#define SET_MAX_BUF (8192) + + +/* Free a given configuration (the values are not free'd) + */ +void th_cfg_free(cfgitem_t *cfg) +{ + cfgitem_t *curr = cfg; + + while (curr != NULL) { + cfgitem_t *next = curr->next; + + if (curr->type == ITEM_SECTION) + th_cfg_free((cfgitem_t *) curr->data); + + th_free(curr->name); + th_free(curr); + curr = next; + } +} + + +/* Allocate and add new item to configuration + */ +static cfgitem_t *th_cfg_add(cfgitem_t **cfg, char *name, int type, void *data) +{ + cfgitem_t *node; + + if (cfg == NULL) + return NULL; + + /* Allocate new item */ + node = (cfgitem_t *) th_calloc(1, sizeof(cfgitem_t)); + if (node == NULL) + return NULL; + + /* Set values */ + node->type = type; + node->data = data; + node->name = th_strdup(name); + + /* Insert into linked list */ + if (*cfg != NULL) { + node->prev = (*cfg)->prev; + (*cfg)->prev->next = node; + (*cfg)->prev = node; + } else { + *cfg = node; + node->prev = node; + } + node->next = NULL; + + return node; +} + + +/* Add integer type setting into give configuration + */ +int th_cfg_add_int(cfgitem_t ** cfg, char * name, + int *itemData, int itemDef) +{ + cfgitem_t *node; + + node = th_cfg_add(cfg, name, ITEM_INT, (void *) itemData); + if (node == NULL) + return -1; + + *itemData = itemDef; + + return 0; +} + + +int th_cfg_add_hexvalue(cfgitem_t ** cfg, char * name, + int *itemData, int itemDef) +{ + cfgitem_t *node; + + node = th_cfg_add(cfg, name, ITEM_HEX_TRIPLET, (void *) itemData); + if (node == NULL) + return -1; + + *itemData = itemDef; + + return 0; +} + + +/* Add unsigned integer type setting into give configuration + */ +int th_cfg_add_uint(cfgitem_t ** cfg, char * name, + unsigned int * itemData, unsigned int itemDef) +{ + cfgitem_t *node; + + node = th_cfg_add(cfg, name, ITEM_UINT, (void *) itemData); + if (node == NULL) + return -1; + + *itemData = itemDef; + + return 0; +} + + +/* Add strint type setting into given configuration + */ +int th_cfg_add_string(cfgitem_t ** cfg, char * name, + char ** itemData, char * itemDef) +{ + cfgitem_t *node; + + node = th_cfg_add(cfg, name, ITEM_STRING, (void *) itemData); + if (node == NULL) + return -1; + + *itemData = th_strdup(itemDef); + + return 0; +} + + +/* Add boolean type setting into given configuration + */ +int th_cfg_add_bool(cfgitem_t ** cfg, char * name, + BOOL * itemData, BOOL itemDef) +{ + cfgitem_t *node; + + node = th_cfg_add(cfg, name, ITEM_BOOL, (void *) itemData); + if (node == NULL) + return -1; + + *itemData = itemDef; + + return 0; +} + + +/* Add implicit comment + */ +int th_cfg_add_comment(cfgitem_t ** cfg, char * comment) +{ + cfgitem_t *node; + + node = th_cfg_add(cfg, comment, ITEM_COMMENT, NULL); + if (node == NULL) + return -1; + + return 0; +} + + +/* Add new section + */ +int th_cfg_add_section(cfgitem_t ** cfg, char * name, cfgitem_t *data) +{ + cfgitem_t *node; + + node = th_cfg_add(cfg, name, ITEM_SECTION, (void *) data); + if (node == NULL) + return -1; + + return 0; +} + + +/* Read a given file into configuration structure and variables + */ +enum { + PM_EOF, + PM_ERROR, + PM_NORMAL, + PM_COMMENT, + PM_NEXT, + PM_KEYNAME, + PM_KEYSET, + PM_STRING, + PM_INT, + PM_BOOL, + PM_SECTION +}; + +#define VADDCH(ch) if (strPos < SET_MAX_BUF) { tmpStr[strPos++] = ch; } +#define VISEND(ch) (ch == '\r' || ch == '\n' || ch == ';' || th_isspace(c) || ch == '#') + +typedef struct { + FILE *file; + char *filename; + size_t line; +} conffile_t; + + +static void th_cfg_error(conffile_t *f, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + fprintf(stderr, "%s: '%s', line #%d: ", th_prog_name, f->filename, f->line); + vfprintf(stderr, fmt, ap); + va_end(ap); +} + + +static int th_cfg_read_sect(conffile_t *f, cfgitem_t * cfg, int nesting) +{ + cfgitem_t *item = NULL; + char tmpStr[SET_MAX_BUF + 1]; + size_t strPos; + int c, parseMode, prevMode, nextMode, tmpCh; + BOOL isFound, isStart, isError, validError; + + /* Initialize values */ + tmpCh = 0; + strPos = 0; + c = -1; + isFound = isStart = isError = validError = FALSE; + nextMode = prevMode = parseMode = PM_NORMAL; + + /* Parse the configuration */ + while (parseMode != PM_EOF && parseMode != PM_ERROR) { + if (c == -1) { + /* Get next character */ + switch (c = fgetc(f->file)) { + case EOF: + if (parseMode != PM_NORMAL) { + th_cfg_error(f, + "Unexpected end of file.\n"); + parseMode = PM_ERROR; + } else + parseMode = PM_EOF; + break; + + case '\n': + f->line++; + } + } + + switch (parseMode) { + case PM_COMMENT: + /* Comment parsing mode */ + if (c == '\n') { + /* End of line, end of comment */ + parseMode = prevMode; + prevMode = PM_COMMENT; + } + c = -1; + break; + + case PM_NORMAL: + /* Normal parsing mode */ + if (c == '#') { + prevMode = parseMode; + parseMode = PM_COMMENT; + c = -1; + } else if (VISEND(c)) { + c = -1; + } else if (c == '}') { + if (nesting > 0) { + /* Check for validation errors */ + return (validError) ? 1 : 0; + } else { + th_cfg_error(f, "Invalid nesting sequence encountered.\n"); + parseMode = PM_ERROR; + } + } else if (th_isalpha(c)) { + /* Start of key name found */ + prevMode = parseMode; + parseMode = PM_KEYNAME; + strPos = 0; + } else { + /* Error! Invalid character found */ + th_cfg_error(f, + "Unexpected character '%c'.\n", c); + parseMode = PM_ERROR; + } + break; + + case PM_KEYNAME: + /* Configuration KEY name parsing mode */ + if (c == '#') { + /* Start of comment */ + prevMode = parseMode; + parseMode = PM_COMMENT; + c = -1; + } else if (th_iscrlf(c) || th_isspace(c) || c == '=') { + /* End of key name */ + prevMode = parseMode; + parseMode = PM_NEXT; + nextMode = PM_KEYSET; + } else if (th_isalnum(c) || c == '_') { + /* Add to key name string */ + VADDCH(c) + else + { + /* Error! Key name string too long! */ + th_cfg_error(f, + "Config key name too long!"); + parseMode = PM_ERROR; + } + c = -1; + } else { + /* Error! Invalid character found */ + tmpStr[strPos] = 0; + th_cfg_error(f, + "Unexpected character '%c' in key name '%s'.\n", c, tmpStr); + parseMode = PM_ERROR; + } + break; + + case PM_KEYSET: + if (c == '=') { + /* Find key from configuration */ + tmpStr[strPos] = 0; + isFound = FALSE; + item = cfg; + while (item != NULL && !isFound) { + if (item->name != NULL && strcmp(item->name, tmpStr) == 0) + isFound = TRUE; + else + item = item->next; + } + + /* Check if key was found */ + if (isFound) { + /* Okay, set next mode */ + switch (item->type) { + case ITEM_HEX_TRIPLET: + case ITEM_STRING: + nextMode = PM_STRING; + break; + + case ITEM_INT: + case ITEM_UINT: + nextMode = PM_INT; + break; + + case ITEM_BOOL: + nextMode = PM_BOOL; + break; + + case ITEM_SECTION: + nextMode = PM_SECTION; + break; + } + + prevMode = parseMode; + parseMode = PM_NEXT; + isStart = TRUE; + strPos = 0; + } else { + /* Error! No configuration key by this name found */ + th_cfg_error(f, + "No such configuration setting ('%s')\n", tmpStr); + parseMode = PM_ERROR; + } + + c = -1; + } else { + /* Error! '=' expected! */ + th_cfg_error(f, + "Unexpected character '%c', assignation '=' was expected.\n", c); + parseMode = PM_ERROR; + } + break; + + case PM_NEXT: + /* Search next item parsing mode */ + if (c == '#') { + /* Start of comment */ + prevMode = parseMode; + parseMode = PM_COMMENT; + } else if (th_isspace(c) || th_iscrlf(c)) { + /* Ignore whitespaces and linechanges */ + c = -1; + } else { + /* Next item found */ + prevMode = parseMode; + parseMode = nextMode; + } + break; + + case PM_SECTION: + /* Section parsing mode */ + if (c != '{') { + /* Error! Section start '{' expected! */ + th_cfg_error(f, + "Unexpected character '%c', section start '{' was expected.\n", c); + parseMode = PM_ERROR; + } else { + int res = th_cfg_read_sect(f, (cfgitem_t *) item->data, nesting + 1); + c = -1; + if (res > 0) + validError = TRUE; + else if (res < 0) + parseMode = PM_ERROR; + else { + prevMode = parseMode; + parseMode = PM_NORMAL; + } + } + break; + + case PM_STRING: + /* String parsing mode */ + if (isStart) { + /* Start of string, get delimiter */ + tmpCh = c; + isStart = FALSE; + strPos = 0; + } else if (c == tmpCh) { + /* End of string, set the value */ + tmpStr[strPos] = 0; + + if (item->type == ITEM_HEX_TRIPLET) { + } else if (item->type == ITEM_STRING) { + th_pstrcpy((char **) item->data, tmpStr); + } + + prevMode = parseMode; + parseMode = PM_NORMAL; + } else { + /* Add character to string */ + VADDCH(c) + else + { + /* Error! String too long! */ + th_cfg_error(f, + "String too long! Maximum is %d characters.", + SET_MAX_BUF); + parseMode = PM_ERROR; + } + } + + c = -1; + break; + + case PM_INT: + /* Integer parsing mode */ + if (isStart && item->type == ITEM_UINT && c == '-') { + /* Error! Negative values not allowed for unsigned ints */ + th_cfg_error(f, + "Negative value specified for %s, unsigned value expected.", + item->name); + parseMode = PM_ERROR; + } else if (isStart && (c == '-' || c == '+')) { + VADDCH(c) + else + isError = TRUE; + } else if (th_isdigit(c)) { + VADDCH(c) + else + isError = TRUE; + } else if (VISEND(c)) { + /* End of integer parsing mode */ + tmpStr[strPos] = 0; + switch (item->type) { + case ITEM_INT: + *((int *) item->data) = atoi(tmpStr); + break; + + case ITEM_UINT: + *((unsigned int *) item->data) = atol(tmpStr); + break; + } + + prevMode = parseMode; + parseMode = PM_NORMAL; + } else { + /* Error! Unexpected character. */ + th_cfg_error(f, + "Unexpected character '%c' for integer setting '%s'.", + c, item->name); + parseMode = PM_ERROR; + } + + if (isError) { + /* Error! String too long! */ + th_cfg_error(f, "String too long! Maximum is %d characters.", + SET_MAX_BUF); + parseMode = PM_ERROR; + } + + isStart = FALSE; + c = -1; + break; + + case PM_BOOL: + /* Boolean parsing mode */ + if (isStart) { + tmpCh = c; + isStart = FALSE; + } else if (VISEND(c)) { + BOOL tmpBool; + + /* End of boolean parsing */ + switch (tmpCh) { + case 'Y': case 'y': + case 'T': case 't': + case '1': + tmpBool = TRUE; + break; + + case 'N': case 'n': + case 'F': case 'f': + case '0': + tmpBool = FALSE; + break; + + default: + isError = TRUE; + } + + if (isError) { + th_cfg_error(f, "Invalid boolean value for '%s'.\n", item->name); + parseMode = PM_ERROR; + } else { + *((BOOL *) item->data) = tmpBool; + + prevMode = parseMode; + parseMode = PM_NORMAL; + } + } + c = -1; + break; + } + } + + /* Check for validation errors */ + if (validError) + return 1; + + /* Return result */ + if (parseMode == PM_ERROR) + return -2; + else + return 0; +} + + +int th_cfg_read(FILE *inFile, char *filename, cfgitem_t * cfg) +{ + conffile_t f; + + f.file = inFile; + f.filename = filename; + f.line = 1; + + return th_cfg_read_sect(&f, cfg, 0); +} + + +/* Write a configuration into file + */ +static void th_print_indent(conffile_t *f, int nesting) +{ + int i; + for (i = 0; i < nesting * 2; i++) + fputc(' ', f->file); +} + + +static int th_cfg_write_sect(conffile_t *f, cfgitem_t *item, int nesting) +{ + while (item != NULL) { + if (item->type == ITEM_COMMENT) { + th_print_indent(f, nesting); + if (fprintf(f->file, "# %s\n", (item->name != NULL) ? item->name : "" ) < 0) + return -1; + } else + if (item->name != NULL) { + th_print_indent(f, nesting); + + switch (item->type) { + case ITEM_STRING: + if (*((char **) item->data) == NULL) { + if (fprintf(f->file, "#%s = \"\"\n", item->name) < 0) + return -3; + } else { + if (fprintf(f->file, "%s = \"%s\"\n", + item->name, *((char **) item->data)) < 0) + return -3; + } + break; + + case ITEM_INT: + if (fprintf(f->file, "%s = %i\n", + item->name, *((int *) item->data)) < 0) + return -4; + break; + + case ITEM_UINT: + if (fprintf(f->file, "%s = %d\n", + item->name, *((unsigned int *) item->data)) < 0) + return -5; + break; + + case ITEM_BOOL: + if (fprintf(f->file, "%s = %s\n", + item->name, *((BOOL *) item->data) ? "yes" : "no") < 0) + return -6; + break; + + case ITEM_SECTION: + { + int res; + if (fprintf(f->file, "\n%s = {\n", item->name) < 0) + return -7; + res = th_cfg_write_sect(f, (cfgitem_t *) item->data, nesting + 1); + if (res != 0) return res; + if (fprintf(f->file, "} # End of '%s'\n\n", item->name) < 0) + return -8; + } + break; + + case ITEM_HEX_TRIPLET: + if (fprintf(f->file, "%s = \"%06x\"\n", + item->name, *((int *) item->data)) < 0) + return -6; + break; + } + } + item = item->next; + } + + return 0; +} + + +int th_cfg_write(FILE *outFile, char *filename, cfgitem_t *cfg) +{ + conffile_t f; + + if (cfg == NULL) + return -1; + + f.file = outFile; + f.filename = filename; + f.line = 1; + + fprintf(outFile, "# Configuration written by %s %s\n\n", + th_prog_fullname, th_prog_version); + + return th_cfg_write_sect(&f, cfg, 0); +} + + diff -r 10daf4660cae -r ffe8bbd429fa th_config.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/th_config.h Sat Oct 30 20:37:09 2010 +0300 @@ -0,0 +1,68 @@ +/* + * Very simple configuration file handling + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2004-2008 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#ifndef _TH_CONFIG_H +#define _TH_CONFIG_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include "th_util.h" +#include + + +/* Definitions + */ +enum ITEM_TYPE { + ITEM_SECTION = 1, + ITEM_COMMENT, + ITEM_STRING, + ITEM_INT, + ITEM_UINT, + ITEM_BOOL, + ITEM_FLOAT, + ITEM_HEX_TRIPLET +}; + + +typedef struct _cfgitem_t { + int type; + char *name; + union { + void *data; + int *val_int; + unsigned int *val_uint; + char *val_str; + BOOL *val_bool; + struct _cfgitem_t *section; + }; + + struct _cfgitem_t *next, *prev; +} cfgitem_t; + + +/* Functions + */ +int th_cfg_read(FILE *, char *, cfgitem_t *); +void th_cfg_free(cfgitem_t *); +int th_cfg_write(FILE *, char *, cfgitem_t *); + +int th_cfg_add_section(cfgitem_t **cfg, char *name, cfgitem_t *data); +int th_cfg_add_comment(cfgitem_t **cfg, char *comment); +int th_cfg_add_int(cfgitem_t **cfg, char *name, int *data, int itemDef); +int th_cfg_add_uint(cfgitem_t **cfg, char *name, unsigned int *data, unsigned int itemDef); +int th_cfg_add_string(cfgitem_t **cfg, char *name, char **data, char *itemDef); +int th_cfg_add_bool(cfgitem_t **cfg, char *name, BOOL *data, BOOL itemDef); +int th_cfg_add_float(cfgitem_t **cfg, char *name, float *data, float itemDef); +int th_cfg_add_hexvalue(cfgitem_t **cfg, char *name, int *data, int itemDef); + + +#ifdef __cplusplus +} +#endif +#endif /* _TH_CONFIG_H */