summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorChloe M. <chloe@aptel.org>2026-08-23 04:49:51 +0000
committerChloe M. <chloe@aptel.org>2026-08-23 04:49:51 +0000
commit78b925a4d86b57e58e6322b7cad7bc47098618fd (patch)
treee66da7a598bb4be1ca869f72600112dda34a4067 /core
initial commit
There is some work to be done on top of this such as creating a way to manage memory to be freed later without needing to manually manage every pointer. Signed-off-by: Chloe M. <chloe@aptel.org>
Diffstat (limited to 'core')
-rw-r--r--core/.bop/cflags1
-rw-r--r--core/.bop/posthook.sh1
-rw-r--r--core/defconf.c70
-rw-r--r--core/lexer.c191
-rw-r--r--core/state.c35
5 files changed, 298 insertions, 0 deletions
diff --git a/core/.bop/cflags b/core/.bop/cflags
new file mode 100644
index 0000000..1865e42
--- /dev/null
+++ b/core/.bop/cflags
@@ -0,0 +1 @@
+-Ihead
diff --git a/core/.bop/posthook.sh b/core/.bop/posthook.sh
new file mode 100644
index 0000000..e03550a
--- /dev/null
+++ b/core/.bop/posthook.sh
@@ -0,0 +1 @@
+clang core/*.o -o defconf
diff --git a/core/defconf.c b/core/defconf.c
new file mode 100644
index 0000000..4444179
--- /dev/null
+++ b/core/defconf.c
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2026, Apollo Telephone Laboratories.
+ * Provided under the BSD-3 clause.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include "defconf/state.h"
+
+/* Globals */
+static char *config_path = NULL;
+
+static void
+help(void)
+{
+ printf("usage: ./defconf [flags]\n");
+ printf("[-h] Display this help menu\n");
+ printf("[-c] Target config file\n");
+}
+
+static int
+defconf_run(void)
+{
+ struct defconf_state state;
+
+ if (defconf_state_init(config_path, &state) < 0) {
+ return -1;
+ }
+
+ defconf_state_destroy(&state);
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int opt, retval;
+
+ if (argc < 2) {
+ printf("fatal: too few arguments\n");
+ help();
+ return -1;
+ }
+
+ while ((opt = getopt(argc, argv, "hc:")) != -1) {
+ switch (opt) {
+ case 'h':
+ help();
+ return -1;
+ case 'c':
+ if ((config_path = strdup(optarg)) == NULL) {
+ printf("fatal: out of memory\n");
+ return -1;
+ }
+
+ break;
+ }
+ }
+
+ if (config_path == NULL) {
+ printf("fatal: expected config path\n");
+ return -1;
+ }
+
+ retval = defconf_run();
+ free(config_path);
+ return retval;
+}
diff --git a/core/lexer.c b/core/lexer.c
new file mode 100644
index 0000000..2eb50a1
--- /dev/null
+++ b/core/lexer.c
@@ -0,0 +1,191 @@
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include "defconf/lexer.h"
+
+/* Constants */
+#define MAX_KW_LEN 32
+
+/*
+ * Returns true if the given character is a whitespace character
+ *
+ * @c: Character to check
+ */
+static inline bool
+lexer_is_ws(char c)
+{
+ switch (c) {
+ case '\t':
+ case ' ':
+ case '\f':
+ case '\r':
+ case '\n':
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Place a character back in the putback buffer
+ *
+ * @state: Defconf state
+ * @c: Character to put back
+ */
+static inline void
+lexer_putback(struct defconf_state *state, int c)
+{
+ if (state == NULL) {
+ return;
+ }
+
+ state->lex_cache = c;
+}
+
+/*
+ * Nom a single character from the input source file
+ *
+ * @state: Defconf state machine
+ * @skip_ws: If true, skip whitespace
+ *
+ * Returns the character on success, otherwise EOF on failure
+ */
+static int
+lexer_nom(struct defconf_state *state, bool skip_ws)
+{
+ int c;
+
+ if (state == NULL) {
+ return EOF;
+ }
+
+ if ((c = state->lex_cache) != '\0') {
+ state->lex_cache = '\0';
+ if (lexer_is_ws(c) && !skip_ws)
+ return c;
+ if (!lexer_is_ws(c))
+ return c;
+ }
+
+ while ((c = fgetc(state->in_fp)) != EOF) {
+ if (lexer_is_ws(c) && skip_ws)
+ continue;
+
+ break;
+ }
+
+ return c;
+}
+
+/*
+ * Scan a keyword
+ *
+ * @state: Defconf state
+ * @lc: Last character provided
+ */
+static char *
+lexer_scan_ident(struct defconf_state *state, int lc)
+{
+ char c, buf[MAX_KW_LEN];
+ size_t bufind = 0;
+
+ if (state == NULL) {
+ return NULL;
+ }
+
+ if (!isalpha(lc)) {
+ return NULL;
+ }
+
+ buf[bufind++] = lc;
+ for (;;) {
+ if (bufind >= sizeof(buf) - 1) {
+ printf("fatal: token exceeds maximum length\n");
+ return NULL;
+ }
+
+ c = lexer_nom(state, false);
+ if (!isalnum(c) && c != '_') {
+ lexer_putback(state, c);
+ buf[bufind] = '\0';
+ return strdup(buf);
+ }
+
+ buf[bufind++] = c;
+ }
+
+ return NULL;
+}
+
+/*
+ * Check if a keyword matches against known keywords
+ *
+ * @kw: Keyword to check
+ * @result: Result is written here
+ *
+ * Returns zero on success
+ */
+static int
+lexer_check_kw(const char *kw, struct token *result)
+{
+ if (kw == NULL || result == NULL) {
+ return -1;
+ }
+
+ switch (*kw) {
+ case 'd':
+ if (strcmp(kw, "define") == 0) {
+ result->type = TT_DEFINE;
+ return 0;
+ }
+
+ break;
+ case 't':
+ if (strcmp(kw, "true") == 0) {
+ result->type = TT_TRUE;
+ return 0;
+ }
+
+ break;
+ case 'f':
+ if (strcmp(kw, "false") == 0) {
+ result->type = TT_FALSE;
+ return 0;
+ }
+
+ break;
+ }
+
+ return -1;
+}
+
+int
+lexer_scan(struct defconf_state *state, struct token *result)
+{
+ int c;
+ char *ident;
+
+ if (state == NULL || result == NULL) {
+ return -1;
+ }
+
+ if ((c = lexer_nom(state, true)) == EOF) {
+ return -1;
+ }
+
+ /* Scan for an identifier */
+ if ((ident = lexer_scan_ident(state, c)) != NULL) {
+ if (lexer_check_kw(ident, result) == 0) {
+ free(ident);
+ return 0;
+ }
+
+ result->type = TT_IDENT;
+ result->s = ident;
+ return 0;
+ }
+
+ printf("fatal: unexpected token '%c'\n", c);
+ return -1;
+}
diff --git a/core/state.c b/core/state.c
new file mode 100644
index 0000000..ce1c286
--- /dev/null
+++ b/core/state.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2026, Apollo Telephone Laboratories.
+ * Provided under the BSD-3 clause.
+ */
+
+#include <stdio.h>
+#include "defconf/state.h"
+
+int
+defconf_state_init(const char *in_path, struct defconf_state *state)
+{
+ if (in_path == NULL || state == NULL) {
+ return -1;
+ }
+
+ state->in_fp = fopen(in_path, "r");
+ if (state->in_fp == NULL) {
+ perror("fopen");
+ return -1;
+ }
+
+ state->lex_cache = '\0';
+ return 0;
+}
+
+void
+defconf_state_destroy(struct defconf_state *state)
+{
+ if (state == NULL) {
+ return;
+ }
+
+ fclose(state->in_fp);
+ state->in_fp = NULL;
+}