From 77f503bd6db90bd882e389ea977f02a2773e14f2 Mon Sep 17 00:00:00 2001
From: Aleksey Veresov <aleksey@veresov.pro>
Date: Fri, 4 Oct 2019 16:35:57 +0300
Subject: Semi-working html. =)

---
 src/file.hpp  |   1 +
 src/html.cpp  | 298 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/html.hpp  |  59 +++++++++++-
 src/plain.hpp |   1 +
 src/texo.cpp  |  10 ++
 src/texo.hpp  |   3 +
 6 files changed, 366 insertions(+), 6 deletions(-)

(limited to 'src')

diff --git a/src/file.hpp b/src/file.hpp
index 68e31cd..c867bcc 100644
--- a/src/file.hpp
+++ b/src/file.hpp
@@ -8,6 +8,7 @@ class TexoFileExporter: public TexoExporter {
 public:
     TexoFileExporter(FILE *file);
     void Put(const char c);
+
 private:
     FILE *file;
 };
diff --git a/src/html.cpp b/src/html.cpp
index c9e3cde..76e9fb9 100644
--- a/src/html.cpp
+++ b/src/html.cpp
@@ -1,5 +1,7 @@
 #include "html.hpp"
 
+#include <string.h>
+
 
 TexoHTMLProducer::TexoHTMLProducer(TexoExporter &exporter):
     TexoProducer(exporter)
@@ -23,7 +25,7 @@ void TexoHTMLProducer::Put(const Texo &piece)
     case Texo::link_begin:      BeginLink(piece);          break;
     case Texo::link_end:        exporter.PutStr("</a>");   break;
     case Texo::image:           Image(piece);              break;
-    default:                    break;
+    default:                                               break;
     }
 }
 
@@ -55,11 +57,297 @@ void TexoHTMLProducer::Image(const Texo &piece)
 }
 
 
-TexoHTMLImporter::TexoHTMLImporter(TexoProducer &producer):
-    TexoImporter(producer)
+TexoHTMLStack::TexoHTMLStack(): next(0)
+{}
+
+TexoHTMLStack::~TexoHTMLStack()
+{
+    if (next) {
+        while (next != this) {
+            Pop();
+        }
+    }
+}
+
+void TexoHTMLStack::Push(Texo::Type tag)
+{
+    if (next) {
+        TexoHTMLStack *buf = next;
+        next               = new TexoHTMLStack;
+        next->next         = buf;
+        next->tag          = this->tag;
+    } else {
+        next = this;
+    }
+    this->tag = tag;
+}
+
+Texo::Type TexoHTMLStack::Pop()
+{
+    if (next) {
+        Texo::Type res = tag;
+        if (next != this) {
+            TexoHTMLStack *buf = next;
+            tag  = next->tag;
+            next = next->next;
+            delete buf;
+        } else {
+            next = 0;
+        }
+        return res;
+    } else {
+        return Texo::character;
+    }
+}
+
+
+TexoHTMLImporter::TexoHTMLImporter(TexoProducer &producer, bool is_autoclose):
+    TexoImporter(producer), state(text), is_closing(0), tag(Texo::character),
+    readed(0), readed_len(0), readed_size(0), is_autoclose(is_autoclose)
 {}
 
+TexoHTMLImporter::~TexoHTMLImporter()
+{
+    ReadedClear();
+}
+
 void TexoHTMLImporter::Put(const char c)
-{  // TODO by automata
-    producer.Put(Texo(c));
+{
+    if (c) {
+        switch (state) {
+        case text:                   state = Text(c);                break;
+        case tag_open:               state = TagOpen(c);             break;
+        case tag_name:               state = TagName(c);             break;
+        case tag_param_skip:         state = TagParamSkip(c);        break;
+        case tag_param_pre:          state = TagParamPre(c);         break;
+        case tag_param:              state = TagParam(c);            break;
+        case tag_param_post:         state = TagParamPost(c);        break;
+        case tag_param_value_pre:    state = TagParamValuePre(c);    break;
+        case tag_param_value:        state = TagParamValue(c);       break;
+        case tag_param_value_quoted: state = TagParamValueQuoted(c); break;
+        default:                                                     break;
+        }
+    } else {
+        tag = Texo::character;
+        Close();
+    }
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::Text(const char c)
+{
+    State st = text;
+    switch (c) {
+    case '<': st = tag_open;           break;
+    case '>': producer.PutStr("&gt;"); break;
+    default:  producer.Put(Texo(c));   break;
+    }
+    return st;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagOpen(const char c)
+{
+    State st = tag_open;
+    switch (c) {
+    case '/':
+        is_closing = 1;
+        break;
+    case ' ':
+        st = text;
+        producer.PutStr("&lt;");
+        if (is_closing) {
+            producer.Put(Texo('/'));
+        }
+        producer.Put(Texo(' '));
+        break;
+    case '>':
+        st = text;
+        producer.PutStr("&lt;");
+        if (is_closing) {
+            producer.Put(Texo('/'));
+        }
+        producer.PutStr("&gt;");
+        break;
+    default:
+        st = tag_name;
+        ReadedAdd(c);
+        break;
+    }
+    return st;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagName(const char c)
+{
+    State st = tag_name;
+    switch (c) {
+    case ' ':
+        st = tag_param_skip;
+        SetTag();
+        break;
+    case '/':
+        st = tag_param_skip;
+        SetTag();
+        break;
+    case '>':
+        st = text;
+        SetTag();
+        if (tag) {
+            if (is_closing) {
+                Close();
+            } else {
+                producer.Put(Texo(tag));
+            }
+        }
+        is_closing = false;
+        break;
+    default:
+        ReadedAdd(c);
+        break;
+    }
+    return st;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagParamSkip(const char c)
+{
+    State st = tag_param_skip;
+    switch (c) {
+    case '>':
+        st = text;
+        if (tag) {
+            if (is_closing) {
+                Close();
+            } else {
+                producer.Put(Texo(tag));
+            }
+        }
+        is_closing = false;
+        break;
+    default:
+        break;
+    }
+    return st;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagParamPre(const char c)
+{
+    return text;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagParam(const char c)
+{
+    return text;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagParamPost(const char c)
+{
+    return text;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagParamValuePre(const char c)
+{
+    return text;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagParamValue(const char c)
+{
+    return text;
+}
+
+TexoHTMLImporter::State TexoHTMLImporter::TagParamValueQuoted(const char c)
+{
+    return text;
+}
+
+void TexoHTMLImporter::Close()
+{
+    Texo::Type current = opened.Pop();
+    while ((current != tag) && current) {
+        producer.Put(Texo(current));
+        current = opened.Pop();
+    }
+    if (current) {
+        producer.Put(Texo(current));
+    }
+}
+
+void TexoHTMLImporter::SetTag()
+{
+    if (!strcmp(readed, "a")) {
+        if (is_closing) {
+            tag = Texo::link_end;
+        } else {
+            tag = Texo::link_begin;
+            opened.Push(Texo::link_end);
+        }
+    } else if (!strcmp(readed, "img")) {
+        tag = Texo::image;
+    } else if (!strcmp(readed, "b")) {
+        if (is_closing) {
+            tag = Texo::bold_end;
+        } else {
+            tag = Texo::bold_begin;
+            opened.Push(Texo::bold_end);
+        }
+    } else if (!strcmp(readed, "i")) {
+        if (is_closing) {
+            tag = Texo::italic_end;
+        } else {
+            tag = Texo::italic_begin;
+            opened.Push(Texo::italic_end);
+        }
+    } else if (!strcmp(readed, "del")) {
+        if (is_closing) {
+            tag = Texo::strike_end;
+        } else {
+            tag = Texo::strike_begin;
+            opened.Push(Texo::strike_end);
+        }
+    } else if (!strcmp(readed, "ins")) {
+        if (is_closing) {
+            tag = Texo::underline_end;
+        } else {
+            tag = Texo::underline_begin;
+            opened.Push(Texo::underline_end);
+        }
+    } else if (!strcmp(readed, "p")) {
+        if (is_closing) {
+            tag = Texo::paragraph_end;
+        } else {
+            tag = Texo::paragraph_begin;
+            opened.Push(Texo::paragraph_end);
+        }
+    } else if (!strcmp(readed, "br")) {
+        tag = Texo::newline;
+    } else {
+        tag = Texo::character;
+    }
+    ReadedClear();
+}
+
+void TexoHTMLImporter::ReadedAdd(const char c)
+{
+    if (readed_size == 0) {
+        readed_size = 2;
+        readed      = new char[readed_size];
+    } else if (readed_size - 1 == readed_len) {
+        char *buf = readed;
+        readed_size = readed_size << 1;
+        readed      = new char[readed_size];
+        for (int i = 0; i < readed_len; ++i) {
+            readed[i] = buf[i];
+        }
+        delete buf;
+    }
+    ++readed_len;
+    readed[readed_len - 1] = c;
+    readed[readed_len]     = 0;
+}
+
+void TexoHTMLImporter::ReadedClear()
+{
+    readed_len  = 0;
+    readed_size = 0;
+    if (readed) {
+        delete readed;
+        readed = 0;
+    }
 }
diff --git a/src/html.hpp b/src/html.hpp
index 265ee39..7a5a511 100644
--- a/src/html.hpp
+++ b/src/html.hpp
@@ -8,16 +8,73 @@ class TexoHTMLProducer: public TexoProducer {
 public:
     TexoHTMLProducer(TexoExporter &exporter);
     void Put(const Texo &piece);
+
 private:
     void BeginLink(const Texo &piece);
     void Image(const Texo &piece);
 };
 
 
+class TexoHTMLStack {
+public:
+    TexoHTMLStack();
+    ~TexoHTMLStack();
+    void Push(Texo::Type tag);
+    Texo::Type Pop();
+
+private:
+    TexoHTMLStack *next;
+    Texo::Type    tag;
+};
+
 class TexoHTMLImporter: public TexoImporter {
 public:
-    TexoHTMLImporter(TexoProducer &producer);
+    TexoHTMLImporter(TexoProducer &producer, bool is_autoclose = true);
+    ~TexoHTMLImporter();
     void Put(const char c);
+
+private:
+    bool is_autoclose;
+
+    enum State {
+        text = 0,
+        tag_open,
+        tag_name,
+        tag_param_skip,
+        tag_param_pre,
+        tag_param,
+        tag_param_post,
+        tag_param_value_pre,
+        tag_param_value,
+        tag_param_value_quoted
+    } state;
+
+    State Text(const char c);
+    State TagOpen(const char c);
+    State TagName(const char c);
+    State TagParamSkip(const char c);
+    State TagParamPre(const char c);
+    State TagParam(const char c);
+    State TagParamPost(const char c);
+    State TagParamValuePre(const char c);
+    State TagParamValue(const char c);
+    State TagParamValueQuoted(const char c);
+
+    TexoHTMLStack opened;
+
+    void Close();
+
+    bool       is_closing;
+    Texo::Type tag;
+
+    void SetTag();
+
+    char *readed;
+    int  readed_len;
+    int  readed_size;
+
+    void ReadedAdd(const char c);
+    void ReadedClear();
 };
 
 
diff --git a/src/plain.hpp b/src/plain.hpp
index 8c74bce..c88f8ef 100644
--- a/src/plain.hpp
+++ b/src/plain.hpp
@@ -8,6 +8,7 @@ class TexoPlainImporter: public TexoImporter {
 public:
     TexoPlainImporter(TexoProducer &producer);
     void Put(const char c);
+
 private:
     int  space;
     int  newline;
diff --git a/src/texo.cpp b/src/texo.cpp
index 03b31e7..3610184 100644
--- a/src/texo.cpp
+++ b/src/texo.cpp
@@ -23,6 +23,16 @@ void TexoExporter::PutStr(const char *str)
 
 TexoProducer::TexoProducer(TexoExporter &exporter): exporter(exporter)  {}
 
+void TexoProducer::PutStr(const char *str)
+{
+    if (str) {
+        while (*str) {
+            Put(Texo(*str));
+            ++str;
+        }
+    }
+}
+
 
 TexoImporter::TexoImporter(TexoProducer &producer): producer(producer)  {}
 
diff --git a/src/texo.hpp b/src/texo.hpp
index 2129f61..bfea181 100644
--- a/src/texo.hpp
+++ b/src/texo.hpp
@@ -43,6 +43,8 @@ class TexoProducer {
 public:
     TexoProducer(TexoExporter &exporter);
     virtual void Put(const Texo &piece) = 0;
+    virtual void PutStr(const char *str);
+
 protected:
     TexoExporter &exporter;
 };
@@ -54,6 +56,7 @@ public:
     virtual void Put(const char c) = 0;
     virtual void PutStr(const char *str);
     virtual void PutFile(FILE *file);
+
 protected:
     TexoProducer &producer;
 };
-- 
cgit v1.2.3