# HG changeset patch # User Matti Hamalainen # Date 1577340654 -7200 # Node ID b506bff0a7ab6b755c8b1f4cfdd1fed229a42cb1 # Parent fe5e7bf704e50f05d8810cdddb037a4bbbca2280 Some cleanup work and refactoring on the configuration file parser and writer. diff -r fe5e7bf704e5 -r b506bff0a7ab tests.c --- a/tests.c Thu Dec 26 08:06:38 2019 +0200 +++ b/tests.c Thu Dec 26 08:10:54 2019 +0200 @@ -398,7 +398,7 @@ // Create the configuration structure tprint(2, "Creating configuration structure\n"); sect1 = NULL; - th_cfg_add_comment(§1, "A comment"); + th_cfg_add_comment(§1, "A comment that\nspans multiple\nlines automatically"); th_cfg_add_string(§1, "a_string_setting", &v_str1, "v_str1"); th_cfg_add_comment(§1, "Hex triplet value setting"); diff -r fe5e7bf704e5 -r b506bff0a7ab th_config.c --- a/th_config.c Thu Dec 26 08:06:38 2019 +0200 +++ b/th_config.c Thu Dec 26 08:10:54 2019 +0200 @@ -202,46 +202,181 @@ PM_NUMERIC, PM_BOOL, PM_SECTION, - PM_ARRAY + PM_LIST, + + PM_LAST +}; + + +static const char *th_cfg_parse_modes[PM_LAST] = +{ + "EOF", + "ERROR", + "IDLE", + "COMMENT", + "NEXT", + "KEYNAME", + "KEYSET", + "STRING", + "NUMERIC", + "BOOL", + "SECTION", + "LIST", }; + +typedef struct +{ + int ch, strDelim, + prevMode, nextMode, parseMode; +} th_cfgparserctx_t; + + +static void th_cfg_set_parsemode(th_cfgparserctx_t *ctx, const int mode) +{ + ctx->prevMode = ctx->parseMode; + ctx->parseMode = mode; +} + + +static void th_cfg_set_next_parsemode(th_cfgparserctx_t *ctx, const int mode) +{ + th_cfg_set_parsemode(ctx, PM_NEXT); + ctx->nextMode = mode; +} + + #define VADDCH(ch) if (strPos < SET_MAX_BUF) { tmpStr[strPos++] = ch; } -#define VISEND(ch) (ch == '\r' || ch == '\n' || ch == ';' || th_isspace(c) || ch == '#') + + +static BOOL th_cfg_is_end(const int ch) +{ + return + ch == '\r' || + ch == '\n' || + ch == ';' || + ch == '#' || + th_isspace(ch); +} + + +static int th_cfg_get_parsemode(const int type) +{ + switch (type) + { + case ITEM_HEX_TRIPLET: + case ITEM_STRING: + return PM_STRING; + + case ITEM_INT: + case ITEM_UINT: + case ITEM_FLOAT: + return PM_NUMERIC; + + case ITEM_BOOL: + return PM_BOOL; + + case ITEM_SECTION: + return PM_SECTION; + + case ITEM_STRING_LIST: + return PM_LIST; + + default: + return PM_ERROR; + } +} + + +static BOOL th_cfg_set_item(th_cfgparserctx_t *ctx, th_cfgitem_t *item, const char *str) +{ + BOOL ret = TRUE; + + printf("th_cfg_set_item: '%s' = '%s'\n", item->name, str); -static int th_cfg_read_sect(th_ioctx *fh, th_cfgitem_t *cfg, int nesting) + switch (item->type) + { + case ITEM_HEX_TRIPLET: + ret = th_get_hex_triplet(str, item->v.val_uint); + break; + + case ITEM_STRING: + th_pstr_cpy(item->v.val_str, str); + break; + + case ITEM_STRING_LIST: + { + char *tmps; + if ((tmps = th_strdup(str)) == NULL) + ret = FALSE; + else + { + th_llist_append(item->v.list, tmps); + th_cfg_set_next_parsemode(ctx, PM_LIST); + } + } + return ret; + + case ITEM_INT: + *(item->v.val_int) = atoi(str); + break; + + case ITEM_UINT: + *(item->v.val_uint) = atol(str); + break; + + case ITEM_FLOAT: + *(item->v.val_float) = atof(str); + break; + + case ITEM_BOOL: + ret = th_get_boolean(str, item->v.val_bool); + break; + + default: + ret = FALSE; + break; + } + + th_cfg_set_parsemode(ctx, ret ? PM_IDLE : PM_ERROR); + return ret; +} + + +static int th_cfg_read_sect(th_ioctx *fh, th_cfgitem_t *sect, int nesting) { + th_cfgparserctx_t ctx; th_cfgitem_t *item = NULL; char *tmpStr = NULL; size_t strPos; - int c, parseMode, prevMode, nextMode, tmpCh; - BOOL isFound, isStart, isError, validError, fpSet; + BOOL isEscaped, isFound, isStart, isError, validError, fpSet; // Initialize values - tmpCh = 0; + memset(&ctx, 0, sizeof(ctx)); + ctx.ch = -1; strPos = 0; - c = -1; - fpSet = isFound = isStart = isError = validError = FALSE; - nextMode = prevMode = parseMode = PM_IDLE; + ctx.nextMode = ctx.prevMode = ctx.parseMode = PM_IDLE; + isEscaped = fpSet = isFound = isStart = isError = validError = FALSE; if ((tmpStr = th_malloc(SET_MAX_BUF + 1)) == NULL) goto out; // Parse the configuration - while (parseMode != PM_EOF && parseMode != PM_ERROR) + while (ctx.parseMode != PM_EOF && ctx.parseMode != PM_ERROR) { - if (c == -1) + if (ctx.ch == -1) { // Get next character - switch (c = thfgetc(fh)) + switch (ctx.ch = thfgetc(fh)) { case EOF: - if (parseMode != PM_IDLE) + if (ctx.parseMode != PM_IDLE) { th_io_error(fh, THERR_OUT_OF_DATA, "Unexpected end of file.\n"); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } else - parseMode = PM_EOF; + ctx.parseMode = PM_EOF; break; case '\n': @@ -249,86 +384,90 @@ } } - switch (parseMode) +// tmpStr[strPos] = 0; printf("PM_%-8s: '%c' [%s]\n", th_cfg_parse_modes[ctx.parseMode], ctx.ch, tmpStr); + + switch (ctx.parseMode) { case PM_COMMENT: // Comment parsing mode - if (c == '\n') + if (ctx.ch == '\n') { // End of line, end of comment - parseMode = prevMode; - prevMode = PM_COMMENT; + th_cfg_set_parsemode(&ctx, ctx.prevMode); } - c = -1; + ctx.ch = -1; break; case PM_IDLE: // Normal parsing mode - if (c == '#') + if (ctx.ch == '#') { - prevMode = parseMode; - parseMode = PM_COMMENT; - c = -1; + th_cfg_set_parsemode(&ctx, PM_COMMENT); + ctx.ch = -1; } - else if (VISEND(c)) + else + if (th_cfg_is_end(ctx.ch)) { - c = -1; + ctx.ch = -1; } - else if (c == '}') + else + if (ctx.ch == '}') { if (nesting > 0) + { // Check for validation errors goto out; + } else { th_io_error(fh, THERR_INVALID_DATA, "Invalid nesting sequence encountered.\n"); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } } - else if (th_isalpha(c)) + else + if (th_isalpha(ctx.ch)) { // Start of key name found - prevMode = parseMode; - parseMode = PM_KEYNAME; + th_cfg_set_parsemode(&ctx, PM_KEYNAME); strPos = 0; } else { // Error! Invalid character found th_io_error(fh, THERR_INVALID_DATA, - "Unexpected character '%c'.\n", c); - parseMode = PM_ERROR; + "Unexpected character '%c'.\n", ctx.ch); + ctx.parseMode = PM_ERROR; } break; case PM_KEYNAME: // Configuration KEY name parsing mode - if (c == '#') + if (ctx.ch == '#') { // Start of comment - prevMode = parseMode; - parseMode = PM_COMMENT; - c = -1; + th_cfg_set_parsemode(&ctx, PM_COMMENT); + ctx.ch = -1; } - else if (th_iscrlf(c) || th_isspace(c) || c == '=') + else + if (th_iscrlf(ctx.ch) || th_isspace(ctx.ch) || ctx.ch == '=') { // End of key name - prevMode = parseMode; - parseMode = PM_NEXT; - nextMode = PM_KEYSET; + th_cfg_set_next_parsemode(&ctx, PM_KEYSET); } - else if (th_isalnum(c) || c == '_' || c == '-') + else + if (th_isalnum(ctx.ch) || ctx.ch == '_' || ctx.ch == '-') { // Add to key name string - VADDCH(c) + VADDCH(ctx.ch) else { // Error! Key name string too long! th_io_error(fh, THERR_INVALID_DATA, "Config key name too long!"); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } - c = -1; + ctx.ch = -1; + tmpStr[strPos] = 0; } else { @@ -336,18 +475,18 @@ tmpStr[strPos] = 0; th_io_error(fh, THERR_INVALID_DATA, "Unexpected character '%c' in key name '%s'.\n", - c, tmpStr); - parseMode = PM_ERROR; + ctx.ch, tmpStr); + ctx.parseMode = PM_ERROR; } break; case PM_KEYSET: - if (c == '=') + if (ctx.ch == '=') { // Find key from configuration tmpStr[strPos] = 0; isFound = FALSE; - item = cfg; + item = sect; while (item != NULL && !isFound) { if (item->name != NULL && strcmp(item->name, tmpStr) == 0) @@ -360,34 +499,7 @@ if (isFound) { // Okay, set next mode - switch (item->type) - { - case ITEM_HEX_TRIPLET: - case ITEM_STRING: - nextMode = PM_STRING; - break; - - case ITEM_STRING_LIST: - nextMode = PM_ARRAY; - break; - - case ITEM_INT: - case ITEM_UINT: - case ITEM_FLOAT: - nextMode = PM_NUMERIC; - break; - - case ITEM_BOOL: - nextMode = PM_BOOL; - break; - - case ITEM_SECTION: - nextMode = PM_SECTION; - break; - } - - prevMode = parseMode; - parseMode = PM_NEXT; + th_cfg_set_next_parsemode(&ctx, th_cfg_get_parsemode(item->type)); isStart = TRUE; fpSet = FALSE; strPos = 0; @@ -398,95 +510,81 @@ th_io_error(fh, THERR_INVALID_DATA, "No such configuration setting ('%s')\n", tmpStr); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } - c = -1; + ctx.ch = -1; } else { // Error! '=' expected! th_io_error(fh, THERR_INVALID_DATA, "Unexpected character '%c', assignation '=' was expected.\n", - c); - parseMode = PM_ERROR; + ctx.ch); + ctx.parseMode = PM_ERROR; } break; case PM_NEXT: // Search next item parsing mode - if (c == '#') + if (ctx.ch == '#') { // Start of comment - prevMode = parseMode; - parseMode = PM_COMMENT; + th_cfg_set_parsemode(&ctx, PM_COMMENT); } - else if (th_isspace(c) || th_iscrlf(c)) + else + if (th_isspace(ctx.ch) || th_iscrlf(ctx.ch)) { // Ignore whitespaces and linechanges - c = -1; + ctx.ch = -1; } else { // Next item found - prevMode = parseMode; - parseMode = nextMode; + th_cfg_set_parsemode(&ctx, ctx.nextMode); } break; - case PM_ARRAY: + case PM_LIST: if (isStart) { - switch (item->type) - { - case ITEM_STRING_LIST: - prevMode = parseMode; - parseMode = PM_STRING; - break; - } + th_cfg_set_parsemode(&ctx, PM_STRING); } - else if (c == ',') + else + if (ctx.ch == ',') { - switch (item->type) - { - case ITEM_STRING_LIST: - c = -1; - isStart = TRUE; - prevMode = parseMode; - parseMode = PM_NEXT; - nextMode = PM_STRING; - break; - } + th_cfg_set_next_parsemode(&ctx, PM_STRING); + ctx.ch = -1; + isStart = TRUE; } else { - prevMode = parseMode; - parseMode = PM_IDLE; + th_cfg_set_parsemode(&ctx, PM_IDLE); } break; case PM_SECTION: // Section parsing mode - if (c != '{') + if (ctx.ch != '{') { // Error! Section start '{' expected! th_io_error(fh, THERR_INVALID_DATA, "Unexpected character '%c', section start '{' was expected.\n", - c); - parseMode = PM_ERROR; + ctx.ch); + ctx.parseMode = PM_ERROR; } else { int res = th_cfg_read_sect(fh, item->v.section, nesting + 1); - c = -1; + ctx.ch = -1; if (res > 0) validError = TRUE; - else if (res < 0) - parseMode = PM_ERROR; + else + if (res < 0) + ctx.parseMode = PM_ERROR; else { - prevMode = parseMode; - parseMode = PM_IDLE; + th_cfg_set_parsemode(&ctx, PM_IDLE); } } break; @@ -496,122 +594,100 @@ if (isStart) { // Start of string, get delimiter - tmpCh = c; + ctx.strDelim = ctx.ch; isStart = FALSE; strPos = 0; + isEscaped = FALSE; } - else if (c == tmpCh) + else + if (!isEscaped && ctx.ch == ctx.strDelim) { // End of string, set the value tmpStr[strPos] = 0; - - switch (item->type) - { - case ITEM_HEX_TRIPLET: - th_get_hex_triplet(tmpStr, item->v.val_uint); - prevMode = parseMode; - parseMode = PM_IDLE; - break; - case ITEM_STRING: - th_pstr_cpy(item->v.val_str, tmpStr); - prevMode = parseMode; - parseMode = PM_IDLE; - break; - case ITEM_STRING_LIST: - th_llist_append(item->v.list, th_strdup(tmpStr)); - prevMode = parseMode; - parseMode = PM_NEXT; - nextMode = PM_ARRAY; - break; - } - + th_cfg_set_item(&ctx, item, tmpStr); + } + else + if (!isEscaped && ctx.ch == '\\') + { + // Escape sequence + isEscaped = TRUE; } else { // Add character to string - VADDCH(c) + VADDCH(ctx.ch) else { // Error! String too long! th_io_error(fh, THERR_INVALID_DATA, "String too long! Maximum is %d characters.", SET_MAX_BUF); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } } - c = -1; + ctx.ch = -1; break; case PM_NUMERIC: // Integer parsing mode - if (isStart && item->type == ITEM_UINT && c == '-') + if (isStart && item->type == ITEM_UINT && ctx.ch == '-') { // Error! Negative values not allowed for unsigned ints th_io_error(fh, THERR_INVALID_DATA, "Negative value specified for %s, unsigned value expected.", item->name); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } - else if (isStart && (c == '-' || c == '+')) + else + if (isStart && (ctx.ch == '-' || ctx.ch == '+')) { - VADDCH(c) + VADDCH(ctx.ch) else isError = TRUE; } - else if (isStart && item->type == ITEM_FLOAT && c == '.') + else + if (isStart && item->type == ITEM_FLOAT && ctx.ch == '.') { fpSet = TRUE; VADDCH('0') else isError = TRUE; - VADDCH(c) + VADDCH(ctx.ch) else isError = TRUE; } - else if (item->type == ITEM_FLOAT && c == '.' && !fpSet) + else + if (item->type == ITEM_FLOAT && ctx.ch == '.' && !fpSet) { fpSet = TRUE; - VADDCH(c) - else - isError = TRUE; - } - else if (th_isdigit(c)) - { - VADDCH(c) + VADDCH(ctx.ch) else isError = TRUE; } - else if (VISEND(c)) + else + if (th_isdigit(ctx.ch)) + { + VADDCH(ctx.ch) + else + isError = TRUE; + } + else + if (th_cfg_is_end(ctx.ch)) { // End of integer parsing mode tmpStr[strPos] = 0; - switch (item->type) - { - case ITEM_INT: - *(item->v.val_int) = atoi(tmpStr); - break; - - case ITEM_UINT: - *(item->v.val_uint) = atol(tmpStr); - break; - - case ITEM_FLOAT: - *(item->v.val_float) = atof(tmpStr); - break; - } - - prevMode = parseMode; - parseMode = PM_IDLE; + th_cfg_set_item(&ctx, item, tmpStr); + th_cfg_set_parsemode(&ctx, PM_IDLE); } else { // Error! Unexpected character. th_io_error(fh, THERR_INVALID_DATA, "Unexpected character '%c' for numeric setting '%s'.", - c, item->name); - parseMode = PM_ERROR; + ctx.ch, item->name); + ctx.parseMode = PM_ERROR; } if (isError) @@ -620,11 +696,11 @@ th_io_error(fh, THERR_INVALID_DATA, "String too long! Maximum is %d characters.", SET_MAX_BUF); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } isStart = FALSE; - c = -1; + ctx.ch = -1; break; case PM_BOOL: @@ -635,24 +711,17 @@ strPos = 0; } - if (th_isalnum(c)) + if (th_isalnum(ctx.ch)) { - VADDCH(c) + VADDCH(ctx.ch) else isError = TRUE; } else - if (VISEND(c)) + if (th_cfg_is_end(ctx.ch)) { - BOOL tmpBool; tmpStr[strPos] = 0; - isError = !th_get_boolean(tmpStr, &tmpBool); - if (!isError) - { - *(item->v.val_bool) = tmpBool; - prevMode = parseMode; - parseMode = PM_IDLE; - } + isError = !th_cfg_set_item(&ctx, item, tmpStr); } if (isError) @@ -660,22 +729,24 @@ th_io_error(fh, THERR_INVALID_DATA, "Invalid boolean value for '%s'.\n", item->name); - parseMode = PM_ERROR; + ctx.parseMode = PM_ERROR; } - c = -1; + ctx.ch = -1; break; } } + printf("EXIT: PM_%s\n", th_cfg_parse_modes[ctx.parseMode]); + out: th_free(tmpStr); // Check for validation errors if (validError) - return 1; + return -91; // Return result - if (parseMode == PM_ERROR) + if (ctx.parseMode == PM_ERROR) return -2; else return 0; @@ -693,120 +764,206 @@ /* Write a configuration into file */ -static void th_print_indent(th_ioctx *fh, int nesting) +static BOOL th_print_indent_a(th_ioctx *fh, const int nesting, const char *fmt, va_list ap) +{ + for (int i = 0; i < nesting * 4; i++) + { + if (thfputc(' ', fh) == EOF) + return FALSE; + } + + return thvfprintf(fh, fmt, ap) >= 0; +} + + +static BOOL th_print_indent(th_ioctx *fh, const int nesting, const char *fmt, ...) { - for (int i = 0; i < nesting * 2; i++) - thfputc(' ', fh); + BOOL ret; + va_list ap; + va_start(ap, fmt); + ret = th_print_indent_a(fh, nesting, fmt, ap); + va_end(ap); + return ret; +} + + +static BOOL th_cfg_is_item_valid(const th_cfgitem_t *item) +{ + switch (item->type) + { + case ITEM_STRING: + return (*(item->v.val_str) != NULL); + + case ITEM_STRING_LIST: + return (*(item->v.list) != NULL); + + case ITEM_SECTION: + return TRUE; + + case ITEM_INT: + case ITEM_UINT: + case ITEM_FLOAT: + case ITEM_BOOL: + case ITEM_HEX_TRIPLET: + return TRUE; + + default: + return FALSE; + } } -static int th_cfg_write_sect(th_ioctx *fh, const th_cfgitem_t *item, int nesting) +static BOOL th_cfg_write_string_escaped(th_ioctx *fh, const char *str, const char delim) +{ + for (const char *ptr = str; *ptr; ptr++) + { + if (*ptr == delim) + { + if (thfputc('\\', fh) == EOF || + thfputc(*ptr, fh) == EOF) + return FALSE; + } + else + { + if (thfputc(*ptr, fh) == EOF) + return FALSE; + } + } + + return TRUE; +} + + +static int th_cfg_write_item(th_ioctx *fh, const th_cfgitem_t *item) +{ + switch (item->type) + { + case ITEM_STRING: + if (thfputc('"', fh) != EOF && + th_cfg_write_string_escaped(fh, *(item->v.val_str), '"') && + thfputc('"', fh) != EOF) + return 1; + else + return -1; + + case ITEM_INT: + return thfprintf(fh, "%d", *(item->v.val_int)); + + case ITEM_UINT: + return thfprintf(fh, "%u", *(item->v.val_uint)); + + case ITEM_FLOAT: + return thfprintf(fh, "%1.5f", *(item->v.val_float)); + + case ITEM_BOOL: + return thfprintf(fh, "%s", *(item->v.val_bool) ? "yes" : "no"); + + case ITEM_HEX_TRIPLET: + return thfprintf(fh, "\"%06x\"", *(item->v.val_int)); + + default: + return -1; + } +} + + +static int th_cfg_write_sect(th_ioctx *fh, const th_cfgitem_t *item, const int nesting) { while (item != NULL) { + if (item->name == NULL) + return -1; + if (item->type == ITEM_COMMENT) { - th_print_indent(fh, nesting); - if (thfprintf(fh, "# %s\n", - (item->name != NULL) ? item->name : "") < 0) - return -1; + BOOL lineStart = TRUE, lineFeed = FALSE; + + for (const char *ptr = item->name; *ptr; ptr++) + switch (*ptr) + { + case '\r': + case '\n': + lineStart = lineFeed = TRUE; + break; + + default: + if (lineFeed) + { + thfprintf(fh, "\n"); + lineFeed = FALSE; + } + if (lineStart) + { + if (!th_print_indent(fh, nesting, "# ")) + return -3; + + lineStart = FALSE; + } + + if (thfputc(*ptr, fh) == EOF) + return -9; + } + + if (!lineFeed) + thfprintf(fh, "\n"); } - else if (item->name != NULL) + else + if (item->type == ITEM_SECTION) { - th_print_indent(fh, nesting); + int res; + + if (!th_print_indent(fh, nesting, "%s = {\n", item->name)) + return -3; + + if ((res = th_cfg_write_sect(fh, item->v.section, nesting + 1)) != 0) + return res; - switch (item->type) + if (!th_print_indent(fh, nesting, "}\n\n")) + return -3; + } + else + if (item->type == ITEM_STRING_LIST) + { + if (!th_cfg_is_item_valid(item)) { - case ITEM_STRING: - if (*(item->v.val_str) == NULL) + if (!th_print_indent(fh, nesting, "#%s = \"\", \"\"", item->name)) + return -3; + } + else + { + th_llist_t *node = *(item->v.list); + size_t n = th_llist_length(node); + + if (!th_print_indent(fh, nesting, "%s = \n", item->name)) + return -3; + + for (; node != NULL; node = node->next) + if (node->data != NULL) { - if (thfprintf(fh, "#%s = \"\"\n", - item->name) < 0) - return -3; - } - else - { - if (thfprintf(fh, "%s = \"%s\"\n", - item->name, *(item->v.val_str)) < 0) - return -3; - } - break; - - case ITEM_STRING_LIST: - if (*(item->v.list) == NULL) - { - if (thfprintf(fh, - "#%s = \"\", \"\"\n", item->name) < 0) + if (!th_print_indent(fh, nesting, "\"%s\"%s\n", + (char *) node->data, + --n > 0 ? "," : "")) return -3; } - else - { - th_llist_t *node = *(item->v.list); - size_t n = th_llist_length(node); - if (thfprintf(fh, "%s = ", item->name) < 0) - return -3; - for (; node != NULL; node = node->next) - { - if (node->data != NULL) - thfprintf(fh, "\"%s\"", (char *) node->data); - - if (--n > 0) - { - thfprintf(fh, ",\n"); - th_print_indent(fh, nesting); - } - } - - if (thfprintf(fh, "\n") < 0) - return -3; - } - break; - - case ITEM_INT: - if (thfprintf(fh, "%s = %i\n", - item->name, *(item->v.val_int)) < 0) - return -4; - break; - - case ITEM_UINT: - if (thfprintf(fh, "%s = %d\n", - item->name, *(item->v.val_uint)) < 0) - return -5; - break; - - case ITEM_FLOAT: - if (thfprintf(fh, "%s = %1.5f\n", - item->name, *(item->v.val_float)) < 0) - return -5; - break; - - case ITEM_BOOL: - if (thfprintf(fh, "%s = %s\n", item->name, - *(item->v.val_bool) ? "yes" : "no") < 0) - return -6; - break; - - case ITEM_SECTION: - { - int res; - if (thfprintf(fh, "%s = {\n", item->name) < 0) - return -7; - res = th_cfg_write_sect(fh, item->v.section, nesting + 1); - if (res != 0) - return res; - if (thfprintf(fh, "}\n\n") < 0) - return -8; - } - break; - - case ITEM_HEX_TRIPLET: - if (thfprintf(fh, "%s = \"%06x\"\n", - item->name, *(item->v.val_int)) < 0) - return -6; - break; + if (!th_print_indent(fh, nesting, "\n")) + return -3; } } + else + { + if (!th_print_indent(fh, nesting, "%s%s = ", + th_cfg_is_item_valid(item) ? "" : "#", + item->name)) + return -3; + + if (th_cfg_write_item(fh, item) < 0) + return -4; + + if (thfprintf(fh, "\n") < 0) + return -3; + } + item = (th_cfgitem_t *) item->node.next; }