cnfparse.c

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.finwo.net/lib/cnfparse.c
Log | Files | Refs | LICENSE

commit 596d3e868e617aa97b62a7f77ee79c60c80c7a5c
parent 62214fdbedd35c931c12346672bd21d77d0c333a
Author: finwo <finwo@pm.me>
Date:   Wed, 26 Aug 2026 22:00:49 +0200

Adapt from original poc

Diffstat:
M.gitignore | 2++
Msrc/cnfparse.c | 167++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msrc/cnfparse.h | 10++++++++++
Atest/example.cnf | 10++++++++++
Mtest/main.c | 46++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 234 insertions(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,5 @@ *.o /lib/ /cnfparse_test +.cache/ +compile_commands.json diff --git a/src/cnfparse.c b/src/cnfparse.c @@ -1,7 +1,172 @@ -#include "finwo/buf.h" +#ifdef __cplusplus +extern "C" { +#endif +// includes/system {{{ +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +// }}} +// includes/deps {{{ +#include "finwo/buf.h" +// }}} +// includes/local {{{ #include "cnfparse.h" +// }}} + +#define STATE_BLANK 0 +#define STATE_COMMENT 1 +#define STATE_QUOTE 2 +#define STATE_WORD 3 + +// cnf_directive_append(subject,value,len) {{{ +// caller frees +void cnf_directive_append(struct cnf_directive *subject, char *value, size_t len) { + if (!value) return; + char *val = calloc(1, len+1); + memcpy(val, value, len); + if (!subject->name) { + subject->name = val; + return; + } + subject->argv = realloc(subject->argv, (subject->argc + 1) * sizeof(char*)); + subject->argv[subject->argc++] = val; +} +// }}} +// cnf_directive_free(subject) {{{ +void cnf_directive_free(struct cnf_directive *subject) { + if (!subject) return; + if (subject->name) { + free(subject->name); + } + for(int i = 0 ; i < subject->argc ; i++) { + free(subject->argv[i]); + } + if (subject->argv) { + free(subject->argv); + } + free(subject); +} +// }}} +// cnf_directive_read(fd) {{{ +struct cnf_directive * cnf_directive_read(FILE *fd) { + if (feof(fd)) return NULL; + + struct buf *acc = calloc(1, sizeof(struct buf)); + struct cnf_directive *output = calloc(1, sizeof(struct cnf_directive)); + + int state = STATE_BLANK; + char c; + char quote; + bool escape; + + while(!feof(fd)) { + if (!fread(&c, sizeof(char), 1, fd)) break; + + switch(state) { + case STATE_BLANK: + switch(c) { + case ' ': + case '\t': + case '\r': + // Skip leading whitespace + // Consume without further action + continue; + case '\n': + // Output directive if we have something + if (output->name) goto cnf_directive_read_finalize; + continue; + case '#': + state = STATE_COMMENT; + continue; + case '"': + case '\'': + state = STATE_QUOTE; + quote = c; + escape = false; + continue; + default: + buf_append_byte(acc, c); + state = STATE_WORD; + continue; + } + break; + case STATE_COMMENT: + switch(c) { + case '\n': // newline breaks comment + if (output->name) goto cnf_directive_read_finalize; // Operator = directive complete + state = STATE_BLANK; // No operator = read one more line + // acc->len = 0; + continue; + default: + // Consume without further action + continue; + } + break; + case STATE_QUOTE: + if (escape) { + buf_append_byte(acc, c); + escape = false; + continue; + } + if (c == '\\') { + escape = true; + continue; + } + if (c == quote) { + cnf_directive_append(output, acc->dat, acc->len); + state = STATE_BLANK; + memset(acc->dat, 0, acc->cap); + acc->len = 0; + continue; + } + buf_append_byte(acc, c); + continue;; + case STATE_WORD: + switch(c) { + case ' ': + case '\t': + case '\r': + case '\n': + // whitespace breaks word + cnf_directive_append(output, acc->dat, acc->len); + state = STATE_BLANK; + memset(acc->dat, 0, acc->cap); + acc->len = 0; + if ('\n' == c) { + goto cnf_directive_read_finalize; + } + continue; + default: + // append char to word + buf_append_byte(acc, c); + continue; + } + break; + default: + fprintf(stderr, "Invalid state: %d\n", state); + exit(1); + } + printf("char: %c\n", c); + } +cnf_directive_read_finalize: + if (acc->len) { + cnf_directive_append(output, acc->dat, acc->len); + } + buf_clear(acc); + free(acc); + if (!output->name) { + free(output); + return NULL; + } + return output; +} +// }}} +#ifdef __cplusplus +} // extern "C" +#endif +// vim:fdm=marker:fdl=0 diff --git a/src/cnfparse.h b/src/cnfparse.h @@ -1,6 +1,16 @@ #ifndef __FINWO_CNFPARSE_H__ #define __FINWO_CNFPARSE_H__ +#include <stddef.h> +#include <stdio.h> +struct cnf_directive { + char *name; + size_t argc; + char **argv; +}; + +void cnf_directive_free(struct cnf_directive *subject); +struct cnf_directive * cnf_directive_read(FILE *fd); #endif // __FINWO_CNFPARSE_H__ diff --git a/test/example.cnf b/test/example.cnf @@ -0,0 +1,10 @@ +# this is a comment line +add 10.0.0.0/8 via 192.168.1.1 + del 10.1.0.0/16 + +set note "a # b" and "a \" b" +op arg # trailing comment +route 172.16.0.0/12 #comment with no space + + +final one two three diff --git a/test/main.c b/test/main.c @@ -1,5 +1,51 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + #include "cnfparse.h" +void usage() { + printf("Usage:\n"); + printf(" cnfparse_test --config <path>\n"); + printf(" cnfparse_test --config=<path>\n"); + exit(0); +} + int main(int argc, char *argv[]) { + char *path = NULL; + + // Handle params + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], "--config") == 0) { + if (i + 1 >= argc) { usage(); } + path = argv[++i]; + } else if (strncmp(argv[i], "--config=", 9) == 0) { + path = argv[i] + 9; + } else { + usage(); + } + } + if (!path) { + usage(); + } + + FILE *fd = fopen(path, "r"); + if (!fd) { + fprintf(stderr, "%s: cannot open\n", path); + exit(1); + } + + while(1) { + struct cnf_directive *dir = cnf_directive_read(fd); + if (!dir) break; + printf("dir:"); + if (dir->name) printf(" name=%s", dir->name); + printf(" argc=%ld", dir->argc); + for(int i = 0; i < dir->argc ; i++ ) { + printf(" argv[%d]=[%s]", i, dir->argv[i]); + } + printf("\n"); + } + return 42; }