---
Note: this assumes that the library user always calls
scfg_block_finish() regardless of whether parsing a file has succeeded
or not.
scfg.c | 142 +++++++++++++++++++++++++++++++++++++--------------------
1 file changed, 93 insertions(+), 49 deletions(-)
diff --git a/scfg.c b/scfg.c
index a5a9419..41d39a0 100644
--- a/scfg.c
+++ b/scfg.c
@@ -37,17 +37,21 @@ static int buffer_append_char(struct scfg_buffer *buf, char ch) {
return buffer_append(buf, &ch, 1);
}
-static void buffer_finish(struct scfg_buffer *buf) {
- free(buf->data);
- memset(buf, 0, sizeof(*buf));
-}
-
static void *buffer_steal(struct scfg_buffer *buf) {
void *data = buf->data;
memset(buf, 0, sizeof(*buf));
return data;
}
+static void directive_finish(struct scfg_directive *dir) {
+ free(dir->name);
+ for (size_t i = 0; i < dir->params_len; i++) {
+ free(dir->params[i]);
+ }
+ free(dir->params);
+ scfg_block_finish(&dir->children);
+}
+
struct scfg_parser {
FILE *f;
int prev_ch;
@@ -106,11 +110,14 @@ static void parser_consume_line(struct scfg_parser *parser) {
}
static int parser_read_atom(struct scfg_parser *parser, char **str_ptr) {
+ int res = 0;
+
struct scfg_buffer buf = {0};
while (1) {
int ch = parser_read_char(parser);
if (ch < 0) {
- return ch;
+ res = ch;
+ goto end;
}
switch (ch) {
@@ -119,80 +126,106 @@ static int parser_read_atom(struct scfg_parser *parser, char **str_ptr) {
case '\t':
case '\n':
parser_unread_char(parser);
- buffer_append_char(&buf, '\0');
- *str_ptr = buffer_steal(&buf);
- return 0;
+ res = buffer_append_char(&buf, '\0');
+ goto end;
case '"':
case '\'':
case '{':
case '}':
- buffer_finish(&buf);
fprintf(stderr, "scfg: unexpected '%c' in atom\n", ch);
- return -EINVAL;
+ res = -EINVAL;
+ goto end;
default:
- buffer_append_char(&buf, ch);
+ res = buffer_append_char(&buf, ch);
+ if (res != 0) {
+ goto end;
+ }
break;
}
}
+
+end:
+ *str_ptr = buffer_steal(&buf);
+ return res;
}
static int parser_read_dquote_word(struct scfg_parser *parser, char **str_ptr) {
+ int res = 0;
+
struct scfg_buffer buf = {0};
while (1) {
int ch = parser_read_char(parser);
if (ch < 0) {
- return ch;
+ res = ch;
+ goto end;
}
switch (ch) {
case '\0':
case '\n':
- buffer_finish(&buf);
fprintf(stderr, "scfg: unterminated double-quoted string\n");
- return -EINVAL;
+ res = -EINVAL;
+ goto end;
case '"':
- buffer_append_char(&buf, '\0');
- *str_ptr = buffer_steal(&buf);
- return 0;
+ res = buffer_append_char(&buf, '\0');
+ goto end;
case '\\':
ch = parser_read_char(parser);
if (ch < 0) {
- return ch;
+ res = ch;
+ goto end;
} else if (ch == '\n') {
fprintf(stderr, "scfg: can't escape '\\n' in double-quoted string\n");
- return -EINVAL;
+ res = -EINVAL;
+ goto end;
}
/* fallthrough */
default:
- buffer_append_char(&buf, ch);
+ res = buffer_append_char(&buf, ch);
+ if (res != 0) {
+ goto end;
+ }
break;
}
}
+
+end:
+ *str_ptr = buffer_steal(&buf);
+ return res;
}
static int parser_read_squote_word(struct scfg_parser *parser, char **str_ptr) {
+ int res = 0;
+
struct scfg_buffer buf = {0};
while (1) {
int ch = parser_read_char(parser);
if (ch < 0) {
- return ch;
+ res = ch;
+ goto end;
}
switch (ch) {
case '\0':
case '\n':
- buffer_finish(&buf);
fprintf(stderr, "scfg: unterminated single-quoted string\n");
- return -EINVAL;
+ res = -EINVAL;
+ goto end;
case '\'':
- buffer_append_char(&buf, '\0');
- *str_ptr = buffer_steal(&buf);
- return 0;
+ res = buffer_append_char(&buf, '\0');
+ goto end;
default:
- buffer_append_char(&buf, ch);
+ res = buffer_append_char(&buf, ch);
+ if (res != 0) {
+ goto end;
+ }
break;
}
}
+
+end:
+ *str_ptr = buffer_steal(&buf);
+ return res;
}
static int parser_read_word(struct scfg_parser *parser, char **str_ptr) {
@@ -229,17 +262,19 @@ static int parser_read_directive(struct scfg_parser *parser, struct scfg_directi
while (1) {
int ch = parser_read_char(parser);
if (ch < 0) {
- return ch;
+ res = ch;
+ goto end;
} else if (ch == '\0' || ch == '\n') {
break;
} else if (ch == '{') {
bool closing_brace = false;
res = parser_read_block(parser, &dir->children, &closing_brace);
if (res != 0) {
- return res;
+ goto end;
} else if (!closing_brace) {
fprintf(stderr, "scfg: expected '}'\n");
- return -EINVAL;
+ res = -EINVAL;
+ goto end;
}
break;
}
@@ -247,36 +282,47 @@ static int parser_read_directive(struct scfg_parser *parser, struct scfg_directi
switch (ch) {
case '}':
fprintf(stderr, "scfg: unexpected '}'\n");
- return -EINVAL;
+ res = -EINVAL;
+ goto end;
default:
parser_unread_char(parser);
char *param = NULL;
res = parser_read_word(parser, ¶m);
if (res != 0) {
- return res;
+ free(param);
+ goto end;
+ }
+ res = buffer_append(¶ms_buf, ¶m, sizeof(param));
+ if (res != 0) {
+ free(param);
+ goto end;
}
- buffer_append(¶ms_buf, ¶m, sizeof(param));
parser_consume_whitespace(parser);
break;
}
}
+
+end:
dir->params_len = params_buf.len / sizeof(char *);
dir->params = buffer_steal(¶ms_buf);
- return 0;
+ return res;
}
static int parser_read_block(struct scfg_parser *parser, struct scfg_block *block, bool *closing_brace) {
memset(block, 0, sizeof(*block));
*closing_brace = false;
+ int res = 0;
+
struct scfg_buffer dirs_buf = {0};
while (1) {
parser_consume_whitespace(parser);
int ch = parser_read_char(parser);
if (ch < 0) {
- return ch;
+ res = ch;
+ goto end;
} else if (ch == '\n') {
continue;
} else if (ch == '#') {
@@ -289,16 +335,23 @@ static int parser_read_block(struct scfg_parser *parser, struct scfg_block *bloc
parser_unread_char(parser);
struct scfg_directive dir = {0};
- int res = parser_read_directive(parser, &dir);
+ res = parser_read_directive(parser, &dir);
if (res != 0) {
- return res;
+ directive_finish(&dir);
+ goto end;
+ }
+ res = buffer_append(&dirs_buf, &dir, sizeof(dir));
+ if (res != 0) {
+ directive_finish(&dir);
+ goto end;
}
- buffer_append(&dirs_buf, &dir, sizeof(dir));
}
+
+end:
block->directives_len = dirs_buf.len / sizeof(struct scfg_directive);
block->directives = buffer_steal(&dirs_buf);
- return 0;
+ return res;
}
int scfg_load_file(struct scfg_block *block, const char *path) {
@@ -325,15 +378,6 @@ int scfg_parse_file(struct scfg_block *block, FILE *f) {
return 0;
}
-static void directive_finish(struct scfg_directive *dir) {
- free(dir->name);
- for (size_t i = 0; i < dir->params_len; i++) {
- free(dir->params[i]);
- }
- free(dir->params);
- scfg_block_finish(&dir->children);
-}
-
void scfg_block_finish(struct scfg_block *block) {
for (size_t i = 0; i < block->directives_len; i++) {
directive_finish(&block->directives[i]);
--
2.41.0