Mercurial > hg > th-libs
view 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 source
/* * 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; }