# HG changeset patch # User Matti Hamalainen # Date 1288349311 -10800 # Node ID adcbcac66125242cc9a7fc5126c47cc886b7a2c3 # Parent 83f7c71e4772f331e6764bea14112a72b949b26b Import improved config code from chat client fork of th-libs. diff -r 83f7c71e4772 -r adcbcac66125 th_config.c --- a/th_config.c Fri Oct 29 13:47:57 2010 +0300 +++ b/th_config.c Fri Oct 29 13:48:31 2010 +0300 @@ -14,563 +14,630 @@ #include "th_util.h" #include "th_string.h" -#define SET_MAX_BUF (8192) -#define LPREV (pNode->prev) -#define LNEXT (pNode->next) - - -void th_config_error(char * filename, size_t lineNum, - const char * pcFormat, ...) -{ - va_list ap; - va_start(ap, pcFormat); - fprintf(stderr, "%s: '%s', line #%d: ", th_prog_name, filename, lineNum); - vfprintf(stderr, pcFormat, ap); - va_end(ap); -} - - -/* Create a new configuration - */ -th_config_t *th_config_new(void) -{ - th_config_t *cfg; - - cfg = (th_config_t *) th_calloc(1, sizeof(th_config_t)); - if (!cfg) - return NULL; - - return cfg; -} +#define SET_MAX_BUF (8192) /* Free a given configuration (the values are not free'd) */ -void th_config_free(th_config_t * cfg) +void th_config_free(cfgitem_t *cfg) { - th_cfgitem_t *curr, *next; - - if (!cfg) - return; + cfgitem_t *curr = cfg; - curr = cfg->items; - while (curr) { - next = curr->next; - th_free(curr->itemName); - th_free(curr); - curr = next; - } + while (curr != NULL) { + cfgitem_t *next = curr->next; + + if (curr->type == ITEM_BLOCK) + th_config_free((cfgitem_t *) curr->data); + + th_free(curr->name); + th_free(curr); + curr = next; + } } /* Allocate and add new item to configuration */ -th_cfgitem_t *th_config_add(th_config_t * cfg, char * itemName, int itemType, - BOOL(*itemValidate) (th_cfgitem_t *), void *itemData) +static cfgitem_t *th_config_add(cfgitem_t **cfg, char *name, int type, + BOOL (*validate)(cfgitem_t *), void *data) { - th_cfgitem_t *pNode; + cfgitem_t *node; - if (!cfg) - return NULL; + if (cfg == NULL) + return NULL; - /* Allocate new item */ - pNode = (th_cfgitem_t *) th_calloc(1, sizeof(th_cfgitem_t)); - if (!pNode) - return NULL; + /* Allocate new item */ + node = (cfgitem_t *) th_calloc(1, sizeof(cfgitem_t)); + if (node == NULL) + return NULL; - /* Set values */ - pNode->itemType = itemType; - pNode->itemData = itemData; - pNode->itemValidate = itemValidate; - th_pstrcpy(&pNode->itemName, itemName); + /* Set values */ + node->type = type; + node->data = data; + node->validate = validate; + 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; - /* Insert into linked list */ - if (cfg->items) { - /* The first node's prev points to last node */ - LPREV = cfg->items->prev; /* New node's prev = Previous last node */ - cfg->items->prev->next = pNode; /* Previous last node's next = New node */ - cfg->items->prev = pNode; /* New last node = New node */ - LNEXT = NULL; /* But next is NULL! */ - } else { - cfg->items = pNode; /* First node ... */ - LPREV = pNode; /* ... it's also last */ - LNEXT = NULL; /* But next is NULL! */ - } - - return pNode; + return node; } /* Add integer type setting into give configuration */ -int th_config_add_int(th_config_t * cfg, char * itemName, BOOL(*itemValidate) (th_cfgitem_t *), - int *itemData, int itemDef) +int th_config_add_int(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), + int *itemData, int itemDef) { - th_cfgitem_t *pNode; + cfgitem_t *node; - pNode = th_config_add(cfg, itemName, ITEM_INT, itemValidate, (void *) itemData); - if (!pNode) - return -1; + node = th_config_add(cfg, name, ITEM_INT, itemValidate, (void *) itemData); + if (node == NULL) + return -1; - *itemData = itemDef; + *itemData = itemDef; - return 0; + return 0; } /* Add unsigned integer type setting into give configuration */ -int th_config_add_uint(th_config_t * cfg, char * itemName, BOOL(*itemValidate) (th_cfgitem_t *), - unsigned int * itemData, unsigned int itemDef) +int th_config_add_uint(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), + unsigned int * itemData, unsigned int itemDef) { - th_cfgitem_t *pNode; + cfgitem_t *node; - pNode = th_config_add(cfg, itemName, ITEM_UINT, itemValidate, (void *) itemData); - if (!pNode) - return -1; + node = th_config_add(cfg, name, ITEM_UINT, itemValidate, (void *) itemData); + if (node == NULL) + return -1; - *itemData = itemDef; + *itemData = itemDef; - return 0; + return 0; } /* Add strint type setting into given configuration */ -int th_config_add_str(th_config_t * cfg, char * itemName, BOOL(*itemValidate) (th_cfgitem_t *), - char ** itemData, char * itemDef) +int th_config_add_str(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), + char ** itemData, char * itemDef) { - th_cfgitem_t *pNode; + cfgitem_t *node; - pNode = th_config_add(cfg, itemName, ITEM_STRING, itemValidate, (void *) itemData); - if (!pNode) - return -1; + node = th_config_add(cfg, name, ITEM_STRING, itemValidate, (void *) itemData); + if (node == NULL) + return -1; - if (itemDef != NULL) - *itemData = strdup(itemDef); - else - *itemData = NULL; + *itemData = th_strdup(itemDef); - return 0; + return 0; } /* Add boolean type setting into given configuration */ -int th_config_add_bool(th_config_t * cfg, char * itemName, BOOL(*itemValidate) (th_cfgitem_t *), - BOOL * itemData, BOOL itemDef) +int th_config_add_bool(cfgitem_t ** cfg, char * name, BOOL(*itemValidate) (cfgitem_t *), + BOOL * itemData, BOOL itemDef) { - th_cfgitem_t *pNode; + cfgitem_t *node; + + node = th_config_add(cfg, name, ITEM_BOOL, itemValidate, (void *) itemData); + if (node == NULL) + return -1; + + *itemData = itemDef; + + return 0; +} + - pNode = th_config_add(cfg, itemName, ITEM_BOOL, itemValidate, (void *) itemData); - if (!pNode) - return -1; +/* Add implicit comment + */ +int th_config_add_comment(cfgitem_t ** cfg, char * comment) +{ + cfgitem_t *node; + + node = th_config_add(cfg, comment, ITEM_COMMENT, NULL, NULL); + if (node == NULL) + return -1; + + return 0; +} - *itemData = itemDef; - return 0; +/* Add new block + */ +int th_config_add_section(cfgitem_t ** cfg, char * name, cfgitem_t *data) +{ + cfgitem_t *node; + + node = th_config_add(cfg, name, ITEM_BLOCK, NULL, (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 +enum { + PM_EOF, + PM_ERROR, + PM_NORMAL, + PM_COMMENT, + PM_NEXT, + PM_KEYNAME, + PM_KEYSET, + PM_STRING, + PM_INT, + PM_BOOL, + PM_BLOCK }; #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 * filename, th_config_t * cfg) -{ - FILE *inFile; - th_cfgitem_t *item; - char 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(filename, "rb")) == NULL) - return -1; - - /* Initialize values */ - item = 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(filename, 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; +#define VISEND(ch) (ch == '\r' || ch == '\n' || ch == ';' || th_isspace(c) || ch == '#') - 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(filename, lineNum, - "Unexpected character '%c'\n", c); - parseMode = PM_ERROR; - } - break; +typedef struct { + FILE *file; + char *filename; + size_t line; +} conffile_t; - 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(filename, lineNum, - "Config key name too long!"); - parseMode = PM_ERROR; - } - c = -1; - } else { - /* Error! Invalid character found */ - th_config_error(filename, lineNum, - "Unexpected character '%c'\n", c); - parseMode = PM_ERROR; - } - break; - - case PM_KEYSET: - if (c == '=') { - /* Find key from configuration */ - tmpStr[strPos] = 0; - isFound = FALSE; - item = cfg->items; - while (item && !isFound) { - if (strcmp(item->itemName, tmpStr) == 0) - isFound = TRUE; - else - item = item->next; - } - - /* Check if key was found */ - if (isFound) { - /* Okay, set next mode */ - switch (item->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(filename, lineNum, - "No such configuration setting ('%s')\n", - tmpStr); - parseMode = PM_ERROR; - } - - c = -1; - } else { - /* Error! '=' expected! */ - th_config_error(filename, 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 **) item->itemData, tmpStr); - if (item->itemValidate) { - if (!item->itemValidate(item)) - validError = TRUE; - } - - prevMode = parseMode; - parseMode = PM_NORMAL; - } else { - /* Add character to string */ - VADDCH(c) - else - { - /* Error! String too long! */ - th_config_error(filename, 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 && (item->itemType == ITEM_UINT) && (c == '-')) { - /* Error! Negative values not allowed for unsigned ints */ - th_config_error(filename, 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 (item->itemType) { - case ITEM_INT: - *((int *) item->itemData) = atoi(tmpStr); - break; - - case ITEM_UINT: - *((unsigned int *) item->itemData) = atol(tmpStr); - break; - } - if (item->itemValidate) { - if (!item->itemValidate(item)) - validError = TRUE; - } - - prevMode = parseMode; - parseMode = PM_NORMAL; - } else { - /* Error! Unexpected character. */ - th_config_error(filename, lineNum, - "Unexpected character, ", SET_MAX_BUF); - parseMode = PM_ERROR; - } - - if (isError) { - /* Error! String too long! */ - th_config_error(filename, 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 *) item->itemData) = tmpBool; - if (item->itemValidate) { - if (!item->itemValidate(item)) - validError = TRUE; - } - - prevMode = parseMode; - parseMode = PM_NORMAL; - } - - c = -1; - break; - } - } +static void th_config_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); +} - /* Close files */ - fclose(inFile); +static int th_config_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_config_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_config_error(f, + "HMMM!\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_config_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_config_error(f, + "Config key name too long!"); + parseMode = PM_ERROR; + } + c = -1; + } else { + /* Error! Invalid character found */ + tmpStr[strPos] = 0; + th_config_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_STRING: + nextMode = PM_STRING; + break; + + case ITEM_INT: + case ITEM_UINT: + nextMode = PM_INT; + break; + + case ITEM_BOOL: + nextMode = PM_BOOL; + break; + + case ITEM_BLOCK: + nextMode = PM_BLOCK; + break; + } + + prevMode = parseMode; + parseMode = PM_NEXT; + isStart = TRUE; + strPos = 0; + } else { + /* Error! No configuration key by this name found */ + th_config_error(f, + "No such configuration setting ('%s')\n", tmpStr); + parseMode = PM_ERROR; + } + + c = -1; + } else { + /* Error! '=' expected! */ + th_config_error(f, + "Unexpected character '%c', assignation '=' was expected.\n", c); + parseMode = PM_ERROR; + } + break; - /* Check for validation errors */ - if (validError) - return 1; + 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_BLOCK: + /* Block parsing mode */ + if (c != '{') { + /* Error! Block start '{' expected! */ + th_config_error(f, + "Unexpected character '%c', block start '{' was expected.\n", c); + parseMode = PM_ERROR; + } else { + int res = th_config_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; + th_pstrcpy((char **) item->data, tmpStr); + if (item->validate != NULL) { + if (!item->validate(item)) + validError = TRUE; + } + + prevMode = parseMode; + parseMode = PM_NORMAL; + } else { + /* Add character to string */ + VADDCH(c) + else + { + /* Error! String too long! */ + th_config_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_config_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; - /* Return result */ - if (parseMode == PM_ERROR) - return -2; - else - return 0; + case ITEM_UINT: + *((unsigned int *) item->data) = atol(tmpStr); + break; + } + if (item->validate != NULL) { + if (!item->validate(item)) + validError = TRUE; + } + + prevMode = parseMode; + parseMode = PM_NORMAL; + } else { + /* Error! Unexpected character. */ + th_config_error(f, + "Unexpected character '%c' for integer setting '%s'.", + c, item->name); + parseMode = PM_ERROR; + } + + if (isError) { + /* Error! String too long! */ + th_config_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_config_error(f, "Invalid boolean value for '%s'.\n", item->name); + parseMode = PM_ERROR; + } else { + *((BOOL *) item->data) = tmpBool; + if (item->validate != NULL) { + if (!item->validate(item)) + validError = TRUE; + } + + 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_config_read(FILE *inFile, char *filename, cfgitem_t * cfg) +{ + conffile_t f; + + f.file = inFile; + f.filename = filename; + f.line = 1; + + return th_config_read_sect(&f, cfg, 0); } /* Write a configuration into file */ -int th_config_write(FILE * outFile, th_config_t * cfg) +static void th_print_indent(conffile_t *f, int nesting) { - th_cfgitem_t *item; + int i; + for (i = 0; i < nesting * 2; i++) + fputc(' ', f->file); +} - if (!cfg) - return -1; - - fprintf(outFile, "# Configuration written by %s %s\n\n", - th_prog_fullname, th_prog_version); - item = cfg->items; - while (item) { - if (!item->itemData || ((item->itemType == ITEM_STRING) && - *(char **) item->itemData == NULL)) { - - fprintf(outFile, "#%s = ", item->itemName); - - switch (item->itemType) { - case ITEM_STRING: - fprintf(outFile, "\"string\""); - break; +static int th_config_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 (fprintf(f->file, "%s = \"%s\"\n", + item->name, *((char **) item->data)) < 0) + return -3; + break; - case ITEM_INT: - fprintf(outFile, "int"); - break; + case ITEM_INT: + if (fprintf(f->file, "%s = %i\n", + item->name, *((int *) item->data)) < 0) + return -4; + break; - case ITEM_UINT: - fprintf(outFile, "uint"); - break; + case ITEM_UINT: + if (fprintf(f->file, "%s = %d\n", + item->name, *((unsigned int *) item->data)) < 0) + return -5; + break; - case ITEM_BOOL: - fprintf(outFile, "boolean"); - break; - } - - } else { - fprintf(outFile, "%s = ", item->itemName); - - switch (item->itemType) { - case ITEM_STRING: - fprintf(outFile, "\"%s\"", - *((char **) item->itemData)); - break; - - case ITEM_INT: - fprintf(outFile, "%i", - *((int *) item->itemData)); - break; + case ITEM_BOOL: + if (fprintf(f->file, "%s = %s\n", + item->name, *((BOOL *) item->data) ? "yes" : "no") < 0) + return -6; + break; + + case ITEM_BLOCK: + { + int res; + if (fprintf(f->file, "\n%s = {\n", item->name) < 0) + return -7; + res = th_config_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; + } + } + } + item = item->next; + } + + return 0; +} - case ITEM_UINT: - fprintf(outFile, "%d", - *((unsigned int *) item->itemData)); - break; + +int th_config_write(FILE *outFile, char *filename, cfgitem_t *cfg) +{ + conffile_t f; + + if (cfg == NULL) + return -1; - case ITEM_BOOL: - fprintf(outFile, "%s", - *((BOOL *) item->itemData) ? "yes" : "no"); - break; - } - } - - fprintf(outFile, "\n\n"); - item = item->next; - } - - return 0; + 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_config_write_sect(&f, cfg, 0); }