Mercurial > hg > th-libs
diff th_config.c @ 0:bd61a80a6c54
Initial import into Mercurial repository. Discarding old cvs/svn history
here, because it's cluttered and commit messages are mostly crap.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Wed, 26 Mar 2008 04:41:58 +0200 |
parents | |
children | 5a327a2988fa |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/th_config.c Wed Mar 26 04:41:58 2008 +0200 @@ -0,0 +1,575 @@ +/* + * Very simple configuration handling functions + * Programmed and designed by Matti 'ccr' Hamalainen + * (C) Copyright 2004-2007 Tecnic Software productions (TNSP) + * + * Please read file 'COPYING' for information on license and distribution. + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif +#include <stdio.h> +#include <stdarg.h> +#include "th_config.h" +#include "th_util.h" +#include "th_string.h" + +#define LPREV (pNode->pPrev) +#define LNEXT (pNode->pNext) + + +void th_config_error(char_t * pcFilename, size_t lineNum, + const char_t * pcFormat, ...) +{ + va_list ap; + va_start(ap, pcFormat); + fprintf(stderr, "%s: '%s', line #%d: ", th_prog_name, pcFilename, lineNum); + vfprintf(stderr, pcFormat, ap); + va_end(ap); +} + + +/* Create a new configuration + */ +t_config *th_config_new(void) +{ + t_config *cfg; + + cfg = (t_config *) th_calloc(1, sizeof(t_config)); + if (!cfg) + return NULL; + + return cfg; +} + + +/* Free a given configuration (the values are not free'd) + */ +void th_config_free(t_config * cfg) +{ + t_config_item *pCurr, *pNext; + + if (!cfg) + return; + + pCurr = cfg->pItems; + while (pCurr) { + pNext = pCurr->pNext; + th_free(pCurr->itemName); + th_free(pCurr); + pCurr = pNext; + } +} + + +/* Allocate and add new item to configuration + */ +t_config_item *th_config_add(t_config * cfg, char_t * itemName, int itemType, + BOOL(*itemValidate) (t_config_item *), void *itemData) +{ + t_config_item *pNode; + + if (!cfg) + return NULL; + + /* Allocate new item */ + pNode = (t_config_item *) th_calloc(1, sizeof(t_config_item)); + if (!pNode) + return NULL; + + /* Set values */ + pNode->itemType = itemType; + pNode->itemData = itemData; + pNode->itemValidate = itemValidate; + th_pstrcpy(&pNode->itemName, itemName); + + /* Insert into linked list */ + if (cfg->pItems) { + /* The first node's pPrev points to last node */ + LPREV = cfg->pItems->pPrev; /* New node's prev = Previous last node */ + cfg->pItems->pPrev->pNext = pNode; /* Previous last node's next = New node */ + cfg->pItems->pPrev = pNode; /* New last node = New node */ + LNEXT = NULL; /* But next is NULL! */ + } else { + cfg->pItems = pNode; /* First node ... */ + LPREV = pNode; /* ... it's also last */ + LNEXT = NULL; /* But next is NULL! */ + } + + return pNode; +} + + +/* Add integer type setting into give configuration + */ +int th_config_add_int(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *), + int *itemData, int itemDef) +{ + t_config_item *pNode; + + pNode = th_config_add(cfg, itemName, ITEM_INT, itemValidate, (void *) itemData); + if (!pNode) + return -1; + + *itemData = itemDef; + + return 0; +} + + +/* Add unsigned integer type setting into give configuration + */ +int th_config_add_uint(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *), + unsigned int * itemData, unsigned int itemDef) +{ + t_config_item *pNode; + + pNode = th_config_add(cfg, itemName, ITEM_UINT, itemValidate, (void *) itemData); + if (!pNode) + return -1; + + *itemData = itemDef; + + return 0; +} + + +/* Add strint type setting into given configuration + */ +int th_config_add_str(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *), + char_t ** itemData, char_t * itemDef) +{ + t_config_item *pNode; + + pNode = th_config_add(cfg, itemName, ITEM_STRING, itemValidate, (void *) itemData); + if (!pNode) + return -1; + + if (itemDef != NULL) + *itemData = th_strdup(itemDef); + else + *itemData = NULL; + + return 0; +} + + +/* Add boolean type setting into given configuration + */ +int th_config_add_bool(t_config * cfg, char_t * itemName, BOOL(*itemValidate) (t_config_item *), + BOOL * itemData, BOOL itemDef) +{ + t_config_item *pNode; + + pNode = th_config_add(cfg, itemName, ITEM_BOOL, itemValidate, (void *) itemData); + if (!pNode) + return -1; + + *itemData = itemDef; + + return 0; +} + + +/* Read a given file into configuration structure and variables + */ +enum +{ + PM_EOF, + PM_ERROR, + PM_NORMAL, + PM_COMMENT, + PM_NEXT, + PM_KEYNAME, + PM_KEYSET, + PM_STRING, + PM_INT, + PM_BOOL +}; + +#define VADDCH(ch) if (strPos < SET_MAX_BUF) { tmpStr[strPos++] = ch; } +#define VISEND(ch) (ch == '\r' || ch == '\n' || ch == ';' || th_isspace(c)) + +int th_config_read(char_t * pcFilename, t_config * cfg) +{ + FILE *inFile; + t_config_item *pItem; + char_t tmpStr[SET_MAX_BUF + 1]; + size_t lineNum, strPos; + int c, parseMode, prevMode, nextMode, tmpCh; + BOOL isFound, isStart, tmpBool, isError, validError; + + assert(cfg); + + /* Open the file */ + if ((inFile = fopen(pcFilename, "rb")) == NULL) + return -1; + + /* Initialize values */ + pItem = NULL; + tmpCh = 0; + strPos = 0; + lineNum = 1; + c = -1; + isFound = isStart = tmpBool = isError = validError = FALSE; + nextMode = prevMode = parseMode = PM_NORMAL; + + /* Parse the configuration */ + while ((parseMode != PM_EOF) && (parseMode != PM_ERROR)) { + if (c == -1) { + /* Get next character */ + switch (c = fgetc(inFile)) { + case EOF: + if (parseMode != PM_NORMAL) { + th_config_error(pcFilename, lineNum, + "Unexpected end of file.\n"); + parseMode = PM_ERROR; + } else + parseMode = PM_EOF; + break; + + case '\n': + lineNum++; + } + } + + switch (parseMode) { + case PM_COMMENT: + /* Comment parsing mode */ + if (c == '\n') { + /* End of line, end of comment */ + parseMode = prevMode; + prevMode = PM_COMMENT; + } + c = -1; + break; + + case PM_NORMAL: + /* Normal parsing mode */ + if (c == '#') { + prevMode = parseMode; + parseMode = PM_COMMENT; + c = -1; + } else if (VISEND(c)) { + c = -1; + } else if (th_isalpha(c)) { + /* Start of key name found */ + prevMode = parseMode; + parseMode = PM_KEYNAME; + strPos = 0; + } else { + /* Error! Invalid character found */ + th_config_error(pcFilename, lineNum, + "Unexpected character '%c'\n", c); + parseMode = PM_ERROR; + } + break; + + case PM_KEYNAME: + /* Configuration KEY name parsing mode */ + if (c == '#') { + /* Start of comment */ + prevMode = parseMode; + parseMode = PM_COMMENT; + c = -1; + } else if (th_iscrlf(c) || th_isspace(c) || c == '=') { + /* End of key name */ + prevMode = parseMode; + parseMode = PM_NEXT; + nextMode = PM_KEYSET; + } else if (th_isalnum(c) || (c == '_')) { + /* Add to key name string */ + VADDCH(c) + else + { + /* Error! Key name string too long! */ + th_config_error(pcFilename, lineNum, + "Config key name too long!"); + parseMode = PM_ERROR; + } + c = -1; + } else { + /* Error! Invalid character found */ + th_config_error(pcFilename, lineNum, + "Unexpected character '%c'\n", c); + parseMode = PM_ERROR; + } + break; + + case PM_KEYSET: + if (c == '=') { + /* Find key from configuration */ + tmpStr[strPos] = 0; + isFound = FALSE; + pItem = cfg->pItems; + while (pItem && !isFound) { + if (strcmp(pItem->itemName, tmpStr) == 0) + isFound = TRUE; + else + pItem = pItem->pNext; + } + + /* Check if key was found */ + if (isFound) { + /* Okay, set next mode */ + switch (pItem->itemType) { + case ITEM_STRING: + nextMode = PM_STRING; + break; + + case ITEM_INT: + case ITEM_UINT: + nextMode = PM_INT; + break; + + case ITEM_BOOL: + nextMode = PM_BOOL; + break; + } + + prevMode = parseMode; + parseMode = PM_NEXT; + isStart = TRUE; + strPos = 0; + } else { + /* Error! No configuration key by this name found */ + th_config_error(pcFilename, lineNum, + "No such configuration setting ('%s')\n", + tmpStr); + parseMode = PM_ERROR; + } + + c = -1; + } else { + /* Error! '=' expected! */ + th_config_error(pcFilename, lineNum, + "Unexpected character '%c', '=' expected.\n", c); + parseMode = PM_ERROR; + } + break; + + case PM_NEXT: + /* Search next item parsing mode */ + if (c == '#') { + /* Start of comment */ + prevMode = parseMode; + parseMode = PM_COMMENT; + } else if (th_isspace(c) || th_iscrlf(c)) { + /* Ignore whitespaces and linechanges */ + c = -1; + } else { + /* Next item found */ + prevMode = parseMode; + parseMode = nextMode; + } + break; + + case PM_STRING: + /* String parsing mode */ + if (isStart) { + /* Start of string, get delimiter */ + tmpCh = c; + isStart = FALSE; + strPos = 0; + } else if (c == tmpCh) { + /* End of string, set the value */ + tmpStr[strPos] = 0; + th_pstrcpy((char_t **) pItem->itemData, tmpStr); + if (pItem->itemValidate) { + if (!pItem->itemValidate(pItem)) + validError = TRUE; + } + + prevMode = parseMode; + parseMode = PM_NORMAL; + } else { + /* Add character to string */ + VADDCH(c) + else + { + /* Error! String too long! */ + th_config_error(pcFilename, lineNum, + "String too long! Maximum is %d characters.", + SET_MAX_BUF); + parseMode = PM_ERROR; + } + } + + c = -1; + break; + + case PM_INT: + /* Integer parsing mode */ + if (isStart && (pItem->itemType == ITEM_UINT) && (c == '-')) { + /* Error! Negative values not allowed for unsigned ints */ + th_config_error(pcFilename, lineNum, + "Negative value specified, unsigned value expected."); + parseMode = PM_ERROR; + } else if (isStart && (c == '-' || c == '+')) { + VADDCH(c) + else + isError = TRUE; + } else if (th_isdigit(c)) { + VADDCH(c) + else + isError = TRUE; + } else if (VISEND(c)) { + /* End of integer parsing mode */ + tmpStr[strPos] = 0; + switch (pItem->itemType) { + case ITEM_INT: + *((int *) pItem->itemData) = atoi(tmpStr); + break; + + case ITEM_UINT: + *((unsigned int *) pItem->itemData) = atol(tmpStr); + break; + } + if (pItem->itemValidate) { + if (!pItem->itemValidate(pItem)) + validError = TRUE; + } + + prevMode = parseMode; + parseMode = PM_NORMAL; + } else { + /* Error! Unexpected character. */ + th_config_error(pcFilename, lineNum, + "Unexpected character, ", SET_MAX_BUF); + parseMode = PM_ERROR; + } + + if (isError) { + /* Error! String too long! */ + th_config_error(pcFilename, lineNum, + "String too long! Maximum is %d characters.", + SET_MAX_BUF); + parseMode = PM_ERROR; + } + + isStart = FALSE; + c = -1; + break; + + case PM_BOOL: + /* Boolean parsing mode */ + if (isStart) { + tmpCh = c; + isStart = FALSE; + } else if (VISEND(c)) { + /* End of boolean parsing */ + switch (tmpCh) { + case 'Y': + case 'y': + case 'T': + case 't': + case '1': + tmpBool = TRUE; + break; + + default: + tmpBool = FALSE; + break; + } + + /* Set the value */ + *((BOOL *) pItem->itemData) = tmpBool; + if (pItem->itemValidate) { + if (!pItem->itemValidate(pItem)) + validError = TRUE; + } + + prevMode = parseMode; + parseMode = PM_NORMAL; + } + + c = -1; + break; + } + } + + + /* Close files */ + fclose(inFile); + + /* Check for validation errors */ + if (validError) + return 1; + + /* Return result */ + if (parseMode == PM_ERROR) + return -2; + else + return 0; +} + + +/* Write a configuration into file + */ +int th_config_write(FILE * outFile, t_config * cfg) +{ + t_config_item *pItem; + + if (!cfg) + return -1; + + fprintf(outFile, "# Configuration written by %s %s\n\n", + th_prog_fullname, th_prog_version); + + pItem = cfg->pItems; + while (pItem) { + if (!pItem->itemData || ((pItem->itemType == ITEM_STRING) && + *(char_t **) pItem->itemData == NULL)) { + + fprintf(outFile, "#%s = ", pItem->itemName); + + switch (pItem->itemType) { + case ITEM_STRING: + fprintf(outFile, "\"string\""); + break; + + case ITEM_INT: + fprintf(outFile, "int"); + break; + + case ITEM_UINT: + fprintf(outFile, "uint"); + break; + + case ITEM_BOOL: + fprintf(outFile, "boolean"); + break; + } + + } else { + fprintf(outFile, "%s = ", pItem->itemName); + + switch (pItem->itemType) { + case ITEM_STRING: + fprintf(outFile, "\"%s\"", + *((char_t **) pItem->itemData)); + break; + + case ITEM_INT: + fprintf(outFile, "%i", + *((int *) pItem->itemData)); + break; + + case ITEM_UINT: + fprintf(outFile, "%d", + *((unsigned int *) pItem->itemData)); + break; + + case ITEM_BOOL: + fprintf(outFile, "%s", + *((BOOL *) pItem->itemData) ? "yes" : "no"); + break; + } + } + + fprintf(outFile, "\n\n"); + pItem = pItem->pNext; + } + + return 0; +}