commit 283b2dd167101436795984f257f59b0161e102d4
Author: anon <anon@anon.anon>
Date:   Fri Mar 1 22:05:18 2024 +0100

    init

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0bc4eca
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.gdb_history
+fu-shell
+*.yy.*
+*.o
+*.out
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..d2673e8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,17 @@
+CXXFLAGS := --std=c++17 -ggdb
+LDLIBS   := $$(pkg-config --cflags --libs readline) -lboost_system
+
+%.yy.c: %.l
+	flex -o $@ --header-file=$*.yy.h $<
+
+%.yy.o: %.yy.c
+	${COMPILE.c} $< -o $@
+
+%.o: %.cpp
+	${COMPILE.cpp} $< -o $@
+
+main: lisp_balance.yy.o main.o
+	${LINK.cpp} -o fu-shell $+ ${LDLIBS}
+
+clean:
+	rm *.o
diff --git a/lisp_balance.h b/lisp_balance.h
new file mode 100644
index 0000000..47707df
--- /dev/null
+++ b/lisp_balance.h
@@ -0,0 +1,9 @@
+#ifndef LISP_BANALNCE_H
+#define LISP_BANALNCE_H
+// Return values:
+enum {
+    FULL = 0, // the provided statement seems complete
+    PARTIAL,  // the provided statement is not closed
+    BROKEN,   // the provided is all fucked'n'shieet
+};
+#endif
diff --git a/lisp_balance.l b/lisp_balance.l
new file mode 100644
index 0000000..d241fc4
--- /dev/null
+++ b/lisp_balance.l
@@ -0,0 +1,53 @@
+%{
+    #include "lisp_balance.h"
+    static int paren_stack = 0;
+
+    static char * lb_input;
+	static int         len;
+	static int      offset;
+
+    void lb_set_input(char * const s) {
+        lb_input = s;
+        len      = strlen(lb_input);
+        offset   = len;
+    }
+
+	#define YY_INPUT(buf, result, max_size) {                        \
+		int cpi = (offset && offset > max_size) ? max_size : offset; \
+		memcpy(buf, lb_input+(len-offset), cpi);                     \
+		result = cpi;                                                \
+		offset = (cpi > offset) ? 0 : offset - cpi;                  \
+	}
+%}
+
+%option noyywrap
+%option nodefault
+
+%x LITERAL
+%%
+<INITIAL>{
+\(      { ++paren_stack; }
+\)      {
+            if (paren_stack >= 0) {
+                --paren_stack;
+            } else {
+                return BROKEN;
+            }
+        }
+\"      { BEGIN LITERAL; }
+.|\n    { ; }
+<<EOF>> {
+            if (paren_stack == 0) {
+                return FULL;
+            } else {
+                return PARTIAL;
+            }
+        }
+}
+
+<LITERAL>{
+\"      { BEGIN INITIAL; }
+.|\n    { ; }
+<<EOF>> { return PARTIAL; }
+}
+%%
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..4536ea0
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,123 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <readline/readline.h>
+#include <boost/asio.hpp>
+
+#include "lisp_balance.h"
+#include "lisp_balance.yy.h"
+
+using namespace boost::asio;
+
+#define PS1 "> "
+
+io_service hio_service;
+ip::tcp::socket hsocket(hio_service);
+
+bool fu_connect() {
+    ip::tcp::endpoint endpoint(ip::address::from_string("127.0.0.1"), 10008);
+    hsocket.connect(endpoint);
+
+    return true;
+}
+
+char * transmit(const char * const s) {
+    static char reply[1024];
+    try {
+        std::string message = s;
+        message = std::string() + (char)0x47 + (char)(message.size() / 256) + (char)(message.size() % 256) + message;
+        boost::system::error_code error;
+        write(hsocket, buffer(message), error);
+        if (error) {
+            throw boost::system::system_error(error);
+        }
+
+        size_t reply_length = hsocket.read_some(buffer(reply), error);
+        if (error) {
+            throw boost::system::system_error(error);
+        }
+        return reply;
+    } catch (std::exception& e) {
+        puts(e.what());
+        return NULL;
+    }
+}
+
+int special(const char * const l) {
+    if (!strcmp(l, ".exit")) {
+        return 1;
+    } else if (!strcmp(l, ".connect ")) {
+    } else if (!strcmp(l, ".ping")) {
+    }
+
+    return 0;
+}
+
+void put_response(const char * const s) {
+    /* The script-fu server protocol uses leading bytes
+     *  to communicate meta information for clients
+     *  YYY: https://docs.gimp.org/2.10/en/gimp-filters-script-fu.html#plug-in-script-fu-console
+     */
+    enum {
+        MAGIC_BYTE,
+        ERROR_CODE,
+        LEN_HIGH,
+        LEN_LOW,
+        LEADING_META_SIZE,
+    };
+    const char * const msg = s + LEADING_META_SIZE;
+
+    printf("high: %d low: %d", s[LEN_HIGH], s[LEN_LOW]);
+
+    if (!s[ERROR_CODE]) {
+        fputs("\033[32m", stdout);
+    } else {
+        fputs("\033[31m", stdout);
+    }
+
+    for (int i = 0, h = s[LEN_HIGH]; i != h; i++) {
+        puts("f");
+        fwrite((msg + LEADING_META_SIZE) + (i*256), 256, sizeof(char), stdout);
+    }
+    fwrite(msg + (s[LEN_HIGH]*256), s[LEN_LOW], sizeof(char), stdout);
+
+    fputs("\033[0m", stdout);
+}
+
+bool fu_judge(const char * const input) {
+    bool r;
+    if (special(input)) {
+        return true;
+    }
+
+    char * const cpy = strdup(input);
+    lb_set_input(input);
+
+    const int p = lb_parse();
+    switch (p) {
+        default: {
+            r = false;
+        };
+    }
+
+    put_response(transmit(input));
+    free(cpy);
+    return r;
+}
+
+bool fu_interpret() {
+    bool r;
+    char * input = readline(PS1);
+    r = fu_judge();
+
+    free(input);
+    return r;
+}
+
+signed main() {
+    if (not fu_connect()) {
+        return 1;
+    }
+
+    while(fu_interpret()) { ; }
+    return 0;
+}