aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarkus Uhlin <markus@nifty-networks.net>2025-10-18 13:15:15 +0200
committerMarkus Uhlin <markus@nifty-networks.net>2025-10-18 13:15:15 +0200
commit207978267241afb74ce7132ed9132c2467850941 (patch)
treead14db590a01cc52c9582e23e45e911868029fd5
parentfd39305a6d1b05f18096c31c2abf083e6839eba1 (diff)
Config file interpreter
-rw-r--r--FICS/build.mk2
-rw-r--r--FICS/interpreter.cpp196
-rw-r--r--FICS/interpreter.h57
3 files changed, 255 insertions, 0 deletions
diff --git a/FICS/build.mk b/FICS/build.mk
index 5c2e4f7..7355421 100644
--- a/FICS/build.mk
+++ b/FICS/build.mk
@@ -17,6 +17,7 @@ OBJS = $(SRC_DIR)adminproc.o\
$(SRC_DIR)formula.o\
$(SRC_DIR)gamedb.o\
$(SRC_DIR)gameproc.o\
+ $(SRC_DIR)interpreter.o\
$(SRC_DIR)iset.o\
$(SRC_DIR)legal.o\
$(SRC_DIR)legal2.o\
@@ -52,6 +53,7 @@ SRCS = $(SRC_DIR)adminproc.c\
$(SRC_DIR)formula.c\
$(SRC_DIR)gamedb.c\
$(SRC_DIR)gameproc.c\
+ $(SRC_DIR)interpreter.cpp\
$(SRC_DIR)iset.cpp\
$(SRC_DIR)legal.c\
$(SRC_DIR)legal2.c\
diff --git a/FICS/interpreter.cpp b/FICS/interpreter.cpp
new file mode 100644
index 0000000..4894c2f
--- /dev/null
+++ b/FICS/interpreter.cpp
@@ -0,0 +1,196 @@
+// SPDX-FileCopyrightText: 2012-2025 Markus Uhlin <maxxe@rpblc.net>
+// SPDX-License-Identifier: ISC
+
+#include <err.h>
+#include <errno.h>
+#include <iostream>
+#include <stdexcept>
+
+#include "interpreter.h"
+#include "utils.h"
+
+const char g_fgets_nullret_err1[70] = "error: fgets() returned null and the "
+ "error indicator is set";
+const char g_fgets_nullret_err2[70] = "error: fgets() returned null for an "
+ "unknown reason";
+
+static const char ArgBegin = '"';
+static const char ArgEnd = '"';
+static const char CommentChar = '#';
+
+static const size_t identifier_maxSize = 64;
+static const size_t argument_maxSize = 512;
+
+/**
+ * Copy identifier
+ */
+static char *
+copy_identifier(const char *&id)
+{
+ size_t count = identifier_maxSize;
+ char *dest_buf = new char[count + 1];
+ char *dest = &dest_buf[0];
+
+ while ((isalnum(*id) || *id == '_') && count > 1)
+ *dest++ = *id++, count--;
+
+ *dest = '\0';
+
+ if (count == 1)
+ errx(1, "%s: fatal: string was truncated", __func__);
+ return dest_buf;
+}
+
+/**
+ * Copy argument
+ */
+//lint -sem(copy_argument, r_null)
+static char *
+copy_argument(const char *&arg)
+{
+ bool inside_arg = true;
+ size_t count = argument_maxSize;
+ char *dest_buf = new char[count + 1];
+ char *dest = &dest_buf[0];
+
+ while (*arg && count > 1) {
+ if (*arg == ArgEnd) {
+ inside_arg = false;
+ arg++;
+ break;
+ }
+
+ *dest++ = *arg++, count--;
+ }
+
+ *dest = '\0';
+
+ if (inside_arg && count == 1)
+ errx(1, "%s: fatal: string was truncated", __func__);
+ if (inside_arg) {
+ delete[] dest_buf;
+ return nullptr;
+ }
+ return dest_buf;
+}
+
+static void
+clean_up(char *id, char *arg)
+{
+ delete[] id;
+ delete[] arg;
+}
+
+/**
+ * Interpreter
+ *
+ * @param in Context structure
+ * @return Void
+ *
+ * An interpreter for configuration files. The context structure
+ * contains the data to be passed to the interpreter.
+ */
+void
+Interpreter(const struct Interpreter_in *in)
+{
+ char *id = nullptr;
+ char *arg = nullptr;
+
+ if (in == nullptr)
+ errx(1, "%s: invalid argument", __func__);
+
+ try {
+ const char *cp = &in->line[0];
+
+ if (!isalnum(*cp) && *cp != '_')
+ throw std::runtime_error("unexpected leading "
+ "character");
+ id = copy_identifier(cp);
+ adv_while_isspace(&cp);
+ if (*cp++ != '=') {
+ throw std::runtime_error("expected assignment "
+ "operator");
+ }
+
+ adv_while_isspace(&cp);
+ if (*cp++ != ArgBegin)
+ throw std::runtime_error("expected arg begin");
+ else if ((arg = copy_argument(cp)) == nullptr)
+ throw std::runtime_error("unterminated argument");
+
+ adv_while_isspace(&cp);
+ if (*cp++ != ';')
+ throw std::runtime_error("no line terminator!");
+
+ adv_while_isspace(&cp);
+ if (*cp && *cp != CommentChar) {
+ throw std::runtime_error("implicit data after "
+ "line terminator!");
+ } else if (!(in->validator_func(id))) {
+#if IGNORE_UNRECOGNIZED_IDENTIFIERS
+ /* ignore */;
+#else
+ throw std::runtime_error("no such identifier");
+#endif
+ } else if ((errno = in->install_func(id, arg)) != 0) {
+ throw std::runtime_error("install error");
+ }
+ } catch (const std::bad_alloc &e) {
+ std::cerr << "out of memory: " << e.what() << '\n';
+ clean_up(id, arg);
+ abort();
+ } catch (const std::runtime_error &e) {
+ std::cerr << '\t' << in->line << '\n';
+
+ if (strings_match(e.what(), "install error")) {
+ warn("%s:%ld: error: "
+ "install_func returned %d", in->path, in->line_num,
+ errno);
+ } else {
+ warnx("%s:%ld: error: %s", in->path, in->line_num,
+ e.what());
+ }
+
+ clean_up(id, arg);
+ abort();
+ } catch (...) {
+ std::cerr << "unknown exception -- should not be reached\n";
+ clean_up(id, arg);
+ abort();
+ }
+
+ clean_up(id, arg);
+}
+
+void
+Interpreter_processAllLines(FILE *fp, const char *path, Interpreter_vFunc func1,
+ Interpreter_instFunc func2)
+{
+ char buf[MAXLINE] = { '\0' };
+ long int line_num = 0;
+
+ while (fgets(buf, sizeof buf, fp) != nullptr) {
+ char *line;
+ const char *cp;
+ struct Interpreter_in in;
+
+ cp = &buf[0];
+ adv_while_isspace(&cp);
+ if (strings_match(cp, "") || *cp == CommentChar) {
+ line_num++;
+ continue;
+ }
+
+ if ((line = eattailwhite(xstrdup(cp))) == nullptr)
+ errx(1, "%s: unexpected null pointer", __func__);
+
+ in.path = const_cast<char *>(path);
+ in.line = line;
+ in.line_num = ++line_num;
+ in.validator_func = func1;
+ in.install_func = func2;
+ Interpreter(&in);
+
+ free(line);
+ }
+}
diff --git a/FICS/interpreter.h b/FICS/interpreter.h
new file mode 100644
index 0000000..b807ad6
--- /dev/null
+++ b/FICS/interpreter.h
@@ -0,0 +1,57 @@
+#ifndef INTERPRETER_H
+#define INTERPRETER_H
+
+#include <ctype.h> /* isspace */
+#include <stdbool.h>
+#include <stdio.h> /* FILE */
+#include <string.h>
+
+#include "common.h"
+
+/*
+ * Set to 0 to turn off this feature.
+ */
+#define IGNORE_UNRECOGNIZED_IDENTIFIERS 1
+
+#define MAXLINE 3200
+
+enum setting_type {
+ TYPE_BOOLEAN,
+ TYPE_INTEGER,
+ TYPE_STRING
+};
+
+typedef bool (*Interpreter_vFunc)(const char *);
+typedef int (*Interpreter_instFunc)(const char *, const char *);
+
+struct Interpreter_in {
+ char *path;
+ char *line;
+ long int line_num;
+ Interpreter_vFunc validator_func;
+ Interpreter_instFunc install_func;
+};
+
+__FICS_BEGIN_DECLS
+extern const char g_fgets_nullret_err1[70];
+extern const char g_fgets_nullret_err2[70];
+
+void Interpreter(const struct Interpreter_in *);
+void Interpreter_processAllLines(FILE *, const char *, Interpreter_vFunc,
+ Interpreter_instFunc);
+__FICS_END_DECLS
+
+static inline void
+adv_while_isspace(const char **ptr)
+{
+ while (isspace(**ptr))
+ (*ptr)++;
+}
+
+static inline bool
+strings_match(const char *str1, const char *str2)
+{
+ return (strcmp(str1, str2) == 0);
+}
+
+#endif