diff options
| author | Chloe M. <chloe@aptel.org> | 2026-08-23 04:49:51 +0000 |
|---|---|---|
| committer | Chloe M. <chloe@aptel.org> | 2026-08-23 04:49:51 +0000 |
| commit | 78b925a4d86b57e58e6322b7cad7bc47098618fd (patch) | |
| tree | e66da7a598bb4be1ca869f72600112dda34a4067 /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/cflags | 1 | ||||
| -rw-r--r-- | core/.bop/posthook.sh | 1 | ||||
| -rw-r--r-- | core/defconf.c | 70 | ||||
| -rw-r--r-- | core/lexer.c | 191 | ||||
| -rw-r--r-- | core/state.c | 35 |
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; +} |
