commit 596d3e868e617aa97b62a7f77ee79c60c80c7a5c
parent 62214fdbedd35c931c12346672bd21d77d0c333a
Author: finwo <finwo@pm.me>
Date: Wed, 26 Aug 2026 22:00:49 +0200
Adapt from original poc
Diffstat:
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;
}