From 0be032c6998e712dc2c9f2ed97c3491d89eb05af Mon Sep 17 00:00:00 2001 From: Aleksey Veresov Date: Fri, 31 Jan 2020 17:16:27 +0300 Subject: [xift] Almost done. --- Makefile | 35 +++++ deps.mk | 8 ++ examples/Makefile | 29 ++++ examples/error.cpp | 20 +++ examples/error.html | 1 + examples/filter.cpp | 28 ++++ examples/message.html | 2 + license | 9 ++ src/attribute.cpp | 119 +++++++++++++++++ src/attribute.hpp | 47 +++++++ src/attribute.o | Bin 0 -> 4456 bytes src/exporter.cpp | 10 ++ src/exporter.hpp | 12 ++ src/exporter.o | Bin 0 -> 2488 bytes src/file.cpp | 17 +++ src/file.hpp | 20 +++ src/file.o | Bin 0 -> 2992 bytes src/str.cpp | 32 +++++ src/str.hpp | 21 +++ src/str.o | Bin 0 -> 3328 bytes src/tag.cpp | 120 +++++++++++++++++ src/tag.hpp | 44 +++++++ src/tag.o | Bin 0 -> 6832 bytes src/utils.cpp | 36 +++++ src/utils.hpp | 12 ++ src/utils.o | Bin 0 -> 1856 bytes src/xift.cpp | 358 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/xift.hpp | 87 ++++++++++++ 28 files changed, 1067 insertions(+) create mode 100644 Makefile create mode 100644 deps.mk create mode 100644 examples/Makefile create mode 100644 examples/error.cpp create mode 100644 examples/error.html create mode 100644 examples/filter.cpp create mode 100644 examples/message.html create mode 100644 license create mode 100644 src/attribute.cpp create mode 100644 src/attribute.hpp create mode 100644 src/attribute.o create mode 100644 src/exporter.cpp create mode 100644 src/exporter.hpp create mode 100644 src/exporter.o create mode 100644 src/file.cpp create mode 100644 src/file.hpp create mode 100644 src/file.o create mode 100644 src/str.cpp create mode 100644 src/str.hpp create mode 100644 src/str.o create mode 100644 src/tag.cpp create mode 100644 src/tag.hpp create mode 100644 src/tag.o create mode 100644 src/utils.cpp create mode 100644 src/utils.hpp create mode 100644 src/utils.o create mode 100644 src/xift.cpp create mode 100644 src/xift.hpp diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3cbfb95 --- /dev/null +++ b/Makefile @@ -0,0 +1,35 @@ +# Debug mode: +# DEBUG = yes + +CC = g++ +LIB = libxift.a + +CPPFLAGS = -Wall -ansi -pedantic +ifeq '$(DEBUG)' 'yes' +CPPFLAGS += -g -O0 +else +CPPFLAGS += -O3 +endif + +SRC_DIR = src +SRC = $(wildcard $(SRC_DIR)/*.cpp) +OBJ = $(SRC:.cpp=.o) + + +default: $(LIB) + +ifneq "clean" "$(MAKECMDGOALS)" +-include deps.mk +endif + +deps.mk: $(SRC) + $(CC) -MM $^ > $@ + +%.o: %.cpp %.hpp + $(CC) $(CPPFLAGS) -c $< -o $@ + +$(LIB): $(OBJ) + ar rcs $@ $^ + +clean: + rm -f $(OBJ) $(LIB) deps.mk diff --git a/deps.mk b/deps.mk new file mode 100644 index 0000000..327e510 --- /dev/null +++ b/deps.mk @@ -0,0 +1,8 @@ +attribute.o: src/attribute.cpp src/attribute.hpp src/utils.hpp +exporter.o: src/exporter.cpp src/exporter.hpp +file.o: src/file.cpp src/file.hpp src/exporter.hpp +str.o: src/str.cpp src/str.hpp src/exporter.hpp src/utils.hpp +tag.o: src/tag.cpp src/tag.hpp src/attribute.hpp src/utils.hpp +utils.o: src/utils.cpp src/utils.hpp +xift.o: src/xift.cpp src/xift.hpp src/exporter.hpp src/tag.hpp \ + src/attribute.hpp src/utils.hpp diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..3f40167 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,29 @@ +# Uncomment following to enable debug mode: +# DEBUG = yes + +CC = g++ +EXAMPLES = filter error + +CPPFLAGS = -Wall -ansi -pedantic +ifeq '$(DEBUG)' 'yes' +CPPFLAGS += -g -O0 +else +CPPFLAGS += -O3 +endif + +INCLUDE = -I../src +LFLAGS = -L.. -lxenophage + +XENOPHAGE = ../libxenophage.a + + +default: $(EXAMPLES) + +$(XENOPHAGE): + cd ..; $(MAKE) + +%: %.cpp $(XENOPHAGE) + $(CC) $(CPPFLAGS) $(INCLUDE) $< $(LFLAGS) -o $@ + +clean: + rm -f $(EXAMPLES) diff --git a/examples/error.cpp b/examples/error.cpp new file mode 100644 index 0000000..54bb17d --- /dev/null +++ b/examples/error.cpp @@ -0,0 +1,20 @@ +#include +#include + + +int main() +{ + XiftFile exporter(stdout); + Xift xift(exporter); + + xift.permitted.Tag("p").Attribute("class"); + + xift.PutFile("error.html"); + + if (!xift.End()) { + printf("Error on %d line, %d column:\n%s", + xift.ErrorLine(), xift.ErrorColumn(), xift.Error()); + } + + return 0; +} diff --git a/examples/error.html b/examples/error.html new file mode 100644 index 0000000..b687c25 --- /dev/null +++ b/examples/error.html @@ -0,0 +1 @@ +

text text text

diff --git a/examples/filter.cpp b/examples/filter.cpp new file mode 100644 index 0000000..6e786c9 --- /dev/null +++ b/examples/filter.cpp @@ -0,0 +1,28 @@ +#include +#include + + +int main() +{ + XiftFile exporter(stdout); + Xift xift(exporter); + + xift.Tag("a").Attribute("href"); + XiftTags::Tag &p = xift.Tag("p"); + p.Attribute("class"); + p.Attribute("hidden"); + p.Attribute("id"); + xift.Tag("script"); + + xift.Remove("script"); + p.Remove("id"); + + xift.PutFile("message.html"); + + if (!xift.End()) { + printf("Error on %d line, %d column:\n%s", + xift.ErrorLine(), xift.ErrorColumn(), xift.Error()); + } + + return 0; +} diff --git a/examples/message.html b/examples/message.html new file mode 100644 index 0000000..7646366 --- /dev/null +++ b/examples/message.html @@ -0,0 +1,2 @@ + +some link diff --git a/license b/license new file mode 100644 index 0000000..8f83b9c --- /dev/null +++ b/license @@ -0,0 +1,9 @@ +Copyright 2019 Aleksey Veresov + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from +the use of this software. + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. diff --git a/src/attribute.cpp b/src/attribute.cpp new file mode 100644 index 0000000..4458713 --- /dev/null +++ b/src/attribute.cpp @@ -0,0 +1,119 @@ +#include "attribute.hpp" + +#include "utils.hpp" +#include +#include + + +XiftAttributes::XiftAttributes(): stack(0) +{} + +XiftAttributes::~XiftAttributes() +{ + while (stack) { + Stack *old = stack; + stack = stack->next; + delete old; + } +} + + +XiftAttributes::Attr &XiftAttributes::Attribute(const char *name) +{ + Stack *current = stack; + while (current) { + if (!strcmp(current->item.name, name)) { + return current->item; + } + current = current->next; + } + Attr &res = New(); + res.nlen = strlen(name); + res.name = xift_str_create_copy(name, name + res.nlen); + res.nsize = res.nlen + 1; + return res; +} + +void XiftAttributes::Remove(const char *name) +{ + Stack **current = &stack; + while (*current) { + if (!strcmp((*current)->item.name, name)) { + Stack *old = *current; + *current = (*current)->next; + delete old; + return; + } + current = &(*current)->next; + } +} + + +XiftAttributes::Attr::Attr(): + name(0), nlen(0), nsize(0), value(0), vlen(0), vsize(0) +{} + +XiftAttributes::Attr::~Attr() +{ + if (name) free(name); + if (value) free(value); +} + + +bool XiftAttributes::Attr::MatchesForm(const XiftAttributes::Attr &form) const +{ + return !strcmp(name, form.name); +} + + +XiftAttributes::Attr *XiftAttributes::Top() +{ + if (stack) { + return &stack->item; + } else { + return 0; + } +} + +void XiftAttributes::Pop() +{ + if (stack) { + Stack *old = stack; + stack = stack->next; + delete old; + } +} + +XiftAttributes::Attr &XiftAttributes::New() +{ + Stack *old = stack; + stack = new Stack; + stack->next = old; + return stack->item; +} + +bool XiftAttributes::ContainsMatchedForm( + const XiftAttributes::Attr &attribute +) const +{ + Stack *current = stack; + while (current) { + if (attribute.MatchesForm(current->item)) { + return true; + } + current = current->next; + } + return false; +} + +bool XiftAttributes::MatchesForm(const XiftAttributes &form) const +{ + Stack *current = stack; + while (current) { + if (!form.ContainsMatchedForm(current->item)) { + return false; + } + current = current->next; + } + return true; +} diff --git a/src/attribute.hpp b/src/attribute.hpp new file mode 100644 index 0000000..d54d5a3 --- /dev/null +++ b/src/attribute.hpp @@ -0,0 +1,47 @@ +#ifndef XIFT_INCLUDED_ATTRIBUTE +#define XIFT_INCLUDED_ATTRIBUTE + + +class XiftAttributes { +public: + class Attr { + friend class XiftAttributes; + + Attr(); + ~Attr(); + + bool MatchesForm(const Attr &form) const; + + char *name; + int nlen; + int nsize; + char *value; + int vlen; + int vsize; + + char value_quota; + }; + + XiftAttributes(); + ~XiftAttributes(); + + Attr &Attribute(const char *name); + void Remove(const char *name); + +protected: + Attr *Top(); + void Pop(); + Attr &New(); + + bool ContainsMatchedForm(const Attr &attribute) const; + bool MatchesForm(const XiftAttributes &form) const; + +private: + struct Stack { + Stack *next; + Attr item; + } *stack; +}; + + +#endif diff --git a/src/attribute.o b/src/attribute.o new file mode 100644 index 0000000..88d99c2 Binary files /dev/null and b/src/attribute.o differ diff --git a/src/exporter.cpp b/src/exporter.cpp new file mode 100644 index 0000000..bd414a7 --- /dev/null +++ b/src/exporter.cpp @@ -0,0 +1,10 @@ +#include "exporter.hpp" + + +void XiftExporter::Put(const char *str) +{ + while (*str) { + Put(*str); + ++str; + } +} diff --git a/src/exporter.hpp b/src/exporter.hpp new file mode 100644 index 0000000..b28ad6c --- /dev/null +++ b/src/exporter.hpp @@ -0,0 +1,12 @@ +#ifndef XIFT_INCLUDED_EXPORTER +#define XIFT_INCLUDED_EXPORTER + + +class XiftExporter { +public: + virtual void Put(char c) = 0; + virtual void Put(const char *str); +}; + + +#endif diff --git a/src/exporter.o b/src/exporter.o new file mode 100644 index 0000000..18565b9 Binary files /dev/null and b/src/exporter.o differ diff --git a/src/file.cpp b/src/file.cpp new file mode 100644 index 0000000..ba6b78e --- /dev/null +++ b/src/file.cpp @@ -0,0 +1,17 @@ +#include "file.hpp" + +#include + + +XiftFile::XiftFile(FILE *file): file(file) +{} + +void XiftFile::Put(char c) +{ + fputc(c, file); +} + +void XiftFile::Put(const char *str) +{ + fwrite(str, 1, strlen(str), file); +} diff --git a/src/file.hpp b/src/file.hpp new file mode 100644 index 0000000..0e189d6 --- /dev/null +++ b/src/file.hpp @@ -0,0 +1,20 @@ +#ifndef XIFT_INCLUDED_FILE +#define XIFT_INCLUDED_FILE + +#include "exporter.hpp" +#include + + +class XiftFile: public XiftExporter { +public: + XiftFile(FILE *file); + + void Put(char c); + void Put(const char *str); + +private: + FILE *file; +}; + + +#endif diff --git a/src/file.o b/src/file.o new file mode 100644 index 0000000..857e72e Binary files /dev/null and b/src/file.o differ diff --git a/src/str.cpp b/src/str.cpp new file mode 100644 index 0000000..a0792c8 --- /dev/null +++ b/src/str.cpp @@ -0,0 +1,32 @@ +#include "str.hpp" + +#include "utils.hpp" +#include +#include + + +XiftString::XiftString(char *&str): str(str) +{ + str = (char *)malloc(1); + *str = 0; + len = 0; + size = 1; +} + +void XiftString::Put(char c) +{ + xift_str_add(str, len, size, c); +} + +void XiftString::Put(const char *addon) +{ + int alen = strlen(addon); + if (len + alen >= size) { + size = len + alen + 1; + str = (char *)realloc(str, size); + } + if (str) { + memcpy(str + len, addon, alen + 1); + len += alen; + } +} diff --git a/src/str.hpp b/src/str.hpp new file mode 100644 index 0000000..fbedb28 --- /dev/null +++ b/src/str.hpp @@ -0,0 +1,21 @@ +#ifndef XIFT_INCLUDED_STRING +#define XIFT_INCLUDED_STRING + +#include "exporter.hpp" + + +class XiftString: public XiftExporter { +public: + XiftString(char *&str); + + void Put(char c); + void Put(const char *addon); + +private: + char *&str; + int len; + int size; +}; + + +#endif diff --git a/src/str.o b/src/str.o new file mode 100644 index 0000000..0ebee34 Binary files /dev/null and b/src/str.o differ diff --git a/src/tag.cpp b/src/tag.cpp new file mode 100644 index 0000000..2f4bbcb --- /dev/null +++ b/src/tag.cpp @@ -0,0 +1,120 @@ +#include "tag.hpp" + +#include "utils.hpp" +#include +#include + + +XiftTags::Item::Item(): name(0), len(0), size(0) +{} + +XiftTags::Item::~Item() +{ + if (name) free(name); +} + + +bool XiftTags::Item::MatchesForm(const XiftTags::Item &form) const +{ + return !strcmp(name, form.name) && XiftAttributes::MatchesForm(form); +} + + +XiftTags::XiftTags(): stack(0) +{} + +XiftTags::~XiftTags() +{ + while (stack) { + Stack *old = stack; + stack = stack->next; + delete old->item; + delete old; + } +} + + +XiftTags::Item &XiftTags::Tag(const char *name) +{ + Stack *current = stack; + while (current) { + if (!strcmp(current->item->name, name)) { + return *current->item; + } + current = current->next; + } + Item &res = New(); + res.len = strlen(name); + res.name = xift_str_create_copy(name, name + res.len); + res.size = res.len + 1; + return res; +} + +void XiftTags::Remove(const char *name) +{ + Stack **current = &stack; + while (*current) { + if (!strcmp((*current)->item->name, name)) { + Stack *old = *current; + *current = (*current)->next; + delete old->item; + delete old; + return; + } + current = &(*current)->next; + } +} + + +XiftTags::Item *XiftTags::Top() +{ + if (stack) { + return stack->item; + } else { + return 0; + } +} + +void XiftTags::Pop() +{ + if (stack) { + Stack *old = stack; + stack = stack->next; + delete old->item; + delete old; + } +} + +XiftTags::Item *XiftTags::PopToBeDeleted() +{ + XiftTags::Item *res = 0; + if (stack) { + Stack *old = stack; + res = stack->item; + stack = stack->next; + delete old; + } + return res; +} + +XiftTags::Item &XiftTags::New() +{ + Stack *old = stack; + stack = new Stack; + stack->item = new XiftTags::Item; + stack->next = old; + return *stack->item; +} + + +bool XiftTags::ContainsMatchedForm(const XiftTags::Item & tag) const +{ + Stack *current = stack; + while (current) { + if (tag.MatchesForm(*current->item)) { + return true; + } + current = current->next; + } + return false; +} diff --git a/src/tag.hpp b/src/tag.hpp new file mode 100644 index 0000000..469c4fd --- /dev/null +++ b/src/tag.hpp @@ -0,0 +1,44 @@ +#ifndef XIFT_INCLUDED_TAG +#define XIFT_INCLUDED_TAG + +#include "attribute.hpp" + + +class XiftTags { +public: + class Item: public XiftAttributes { + friend class XiftTags; + + Item(); + ~Item(); + + bool MatchesForm(const Item & form) const; + + char *name; + int len; + int size; + }; + + XiftTags(); + ~XiftTags(); + + Item &Tag(const char *name); + void Remove(const char *name); + +protected: + Item *Top(); + void Pop(); + Item *PopToBeDeleted(); + Item &New(); + + bool ContainsMatchedForm(const Item &tag) const; + +private: + struct Stack { + Stack *next; + Item *item; + } *stack; +}; + + +#endif diff --git a/src/tag.o b/src/tag.o new file mode 100644 index 0000000..00a8cac Binary files /dev/null and b/src/tag.o differ diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..2137aa8 --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,36 @@ +#include "utils.hpp" + +#include +#include + + +char *xift_str_create_copy(const char *begin, const char *end) +{ + char *res; + res = (char *)malloc(end - begin + 1); + if (res) { + memcpy(res, begin, end - begin); + res[end - begin] = 0; + } + return res; +} + + +bool xift_str_add(char *&dest, int &len, int &size, char c) +{ + if (!dest) { + dest = (char *)malloc(2); + size = 2; + len = 0; + } + if (len + 1 == size) { + size *= 2; + dest = (char *)realloc(dest, size); + } + if (dest) { + dest[len] = c; + ++len; + dest[len] = 0; + } + return dest != 0; +} diff --git a/src/utils.hpp b/src/utils.hpp new file mode 100644 index 0000000..2db1436 --- /dev/null +++ b/src/utils.hpp @@ -0,0 +1,12 @@ +#ifndef XIFT_INCLUDED_UTILS +#define XIFT_INCLUDED_UTILS + + +/* Results of both create functions are malloced, so need to be freed. */ +char *xift_str_create_copy(const char *begin, const char *end); + +/* False only in case of error. */ +bool xift_str_add(char *&dest, int &len, int &size, char c); + + +#endif diff --git a/src/utils.o b/src/utils.o new file mode 100644 index 0000000..7531793 Binary files /dev/null and b/src/utils.o differ diff --git a/src/xift.cpp b/src/xift.cpp new file mode 100644 index 0000000..8b703cb --- /dev/null +++ b/src/xift.cpp @@ -0,0 +1,358 @@ +#include "xift.hpp" + +#include "utils.hpp" +#include + + +const char *XiftErrorMessage(XiftError error) +{ + switch (error) { + case xift_ok: return ""; + case xift_wrong_char: return "Wrong character in tag."; + case xift_attr_sep: return "Attribute separator missed."; + case xift_something_after_close: return "Met something after tag close."; + case xift_close_selfclose: return "Tag is closing and self-closing."; + case xift_unwanted_close: return "Closing not opened tag."; + } +} + + +Xift::Xift(XiftExporter &exporter): + error_pos(0), error_line(1), error_column(0), exporter(exporter), + state(reading_text), is_closing(false), is_self_closing(false) +{} + + +void Xift::Put(char c) +{ + if (state != state_error) { + ++error_pos; + if (c == '\n') { + ++error_line; + error_column = 0; + } + ++error_column; + } + switch (state) { + case state_error: break; + case reading_text: ReadText(c); break; + case opening_tag: OpenTag(c); break; + case reading_name: ReadName(c); break; + case waiting_attr: WaitAttr(c); break; + case reading_attr_name: ReadAttrName(c); break; + case waiting_attr_sep: WaitAttrSep(c); break; + case waiting_attr_value: WaitAttrValue(c); break; + case reading_attr_value: ReadAttrValue(c); break; + case closing_tag: CloseTag(c); break; + } +} + +void Xift::Put(const char *str) +{ + while (*str) { + Put(*str); + ++str; + } +} + +void Xift::Put(FILE *file) +{ + if (file) { + for (int c = fgetc(file); c != EOF; c = fgetc(file)) { + Put(c); + } + } +} + +void Xift::PutFile(const char *path) +{ + FILE *file = fopen(path, "r"); + Put(file); + fclose(file); +} + + +bool Xift::End() +{ + if (state != state_error) { + while (opened.Top()) { + CloseTop(); + } + } + return !IsError(); +} + + +bool Xift::IsError() { return state == state_error;} +int Xift::ErrorPosition() { return error_pos; } +int Xift::ErrorLine() { return error_line; } +int Xift::ErrorColumn() { return error_column; } +XiftError Xift::GetError() { return error; } + + +void Xift::ReadText(char c) +{ + switch (c) { + case '<': state = opening_tag; break; + case '>': exporter.Put(">"); break; + default: exporter.Put(c); break; + } +} + +void Xift::OpenTag(char c) +{ + Item &t = opened.New(); + if (c == '/') { + is_closing = true; + state = reading_name; + } else if (AllowedInTokenStart(c)) { + xift_str_add(t.name, t.len, t.size, c); + state = reading_name; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::ReadName(char c) +{ + if (AllowedInToken(c)) { + Item &t = *opened.Top(); + xift_str_add(t.name, t.len, t.size, c); + } else if (IsWhitespace(c)) { + state = waiting_attr; + } else if (c == '>') { + CompleteCurrent(); + state = reading_text; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::WaitAttr(char c) +{ + if (AllowedInTokenStart(c)) { + Item::Attr &a = opened.Top()->New(); + xift_str_add(a.name, a.nlen, a.nsize, c); + state = reading_attr_name; + } else if (c == '/') { + is_self_closing = true; + state = closing_tag; + } else if (c == '>') { + CompleteCurrent(); + state = reading_text; + } else if (!IsWhitespace(c)) { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::ReadAttrName(char c) +{ + if (AllowedInToken(c)) { + Item::Attr &a = *opened.Top()->Top(); + xift_str_add(a.name, a.nlen, a.nsize, c); + } else if (c == '=') { + state = waiting_attr_value; + } else if (c == '>') { + Item::Attr &a = *opened.Top()->Top(); + a.value = xift_str_create_copy(a.name, a.name + a.len); + a.vlen = a.nlen; + a.vsize = a.vlen + 1; + a.value_quota = '\''; + CompleteCurrent(); + state = reading_text; + } else if (IsWhitespace(c)) { + state = waiting_attr_sep; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::WaitAttrSep(char c) +{ + if (c == '=') { + state = waiting_attr_value; + } else if (c == '>') { + Item::Attr &a = *opened.Top()->Top(); + a.value = xift_str_create_copy(a.name, a.name + a.len); + a.vlen = a.nlen; + a.vsize = a.vlen + 1; + a.value_quota = '\''; + CompleteCurrent(); + state = reading_text; + } else if (AllowedInTokenStart(c)) { + Item::Attr &a = *opened.Top()->Top(); + a.value = xift_str_create_copy(a.name, a.name + a.len); + a.vlen = a.nlen; + a.vsize = a.vlen + 1; + a.value_quota = '\''; + Item::Attr &n = opened.Top()->New(); + xift_str_add(n.name, n.nlen, n.nsize, c); + state = reading_attr_name; + } else if (!IsWhitespace(c)) { + state = state_error; + error = xift_attr_sep; + } +} + +void Xift::WaitAttrValue(char c) +{ + if (c == '"' || c == '\'') { + opened.Top()->Top()->value_quota = c; + value_quota = c; + state = reading_attr_value; + } else if (AllowedInTokenStart(c)) { + Item::Attr &a = *opened.Top()->Top(); + a.value_quota = '\''; + xift_str_add(a.value, a.vlen, a.vsize, c); + value_quota = 0; + state = reading_attr_value; + } else { + state = state_error; + error = xift_wrong_char; + } +} + +void Xift::ReadAttrValue(char c) +{ + if (value_quota) { + if (c == value_quota) { + state = waiting_attr; + } else { + Item::Attr &a = *opened.Top()->Top(); + xift_str_add(a.value, a.vlen, a.vsize, c); + } + } else { + if (c == '>') { + CompleteCurrent(); + state = reading_text; + } + if (AllowedInToken(c)) { + Item::Attr &a = *opened.Top()->Top(); + xift_str_add(a.value, a.vlen, a.vsize, c); + } else if (IsWhitespace(c)) { + state = waiting_attr; + } else { + state = state_error; + error = xift_wrong_char; + } + } +} + +void Xift::CloseTag(char c) +{ + if (c == '>') { + CompleteCurrent(); + state = reading_text; + } else if (!IsWhitespace(c)) { + state = state_error; + error = something_after_close; + } +} + + +void Xift::CompleteCurrent() +{ + if (ContainsMatchedForm(*opened.Top())) { + if (is_closing && is_self_closing) { + state = state_error; + error = xift_close_selfclose; + } else if (is_closing) { + CloseCurrent(); + } else if (is_self_closing) { + SelfcloseCurrent(); + } else { + PutCurrent(); + } + } else { + opened.Pop(); + } + is_self_closing = false; + is_closing = false; +} + +void Xift::CloseCurrent() +{ + XiftTag *closing = opened.PopToBeDeleted(); + if (closing) { + while (opened.Top() && !opened.Top()->MatchesForm(*closing)) { + CloseTop(); + } + delete closing; + if (opened.Top()) { + CloseTop(); + } else { + state = state_error; + error = xift_unwanted_close; + } + } +} + +void Xift::CloseTop() +{ + CloseName(opened.Top()->name); + opened.Pop(); +} + +void Xift::CloseName(const ScriptVariable &name) +{ + exporter.Put("'); +} + +void Xift::SelfcloseCurrent() +{ + Item &t = *opened.Top(); + exporter.Put('<'); + exporter.Put(t.name); + Item::Attr *a; + while (a = t.Top()) { + exporter.Put(' '); + exporter.Put(a->name); + exporter.Put('='); + exporter.Put(a->value_quota); + exporter.Put(a->value); + exporter.Put(a->value_quota); + t.Pop(); + } + exporter.Put("/>"); + opened.Pop(); +} + +void Xift::PutCurrent() +{ + Item &t = *opened.Top(); + exporter.Put('<'); + exporter.Put(t.name); + Item::Attr *a; + while (a = t.Top()) { + exporter.Put(' '); + exporter.Put(a->name); + exporter.Put('='); + exporter.Put(a->value_quota); + exporter.Put(a->value); + exporter.Put(a->value_quota); + t.Pop(); + } + exporter.Put('>'); +} + + +bool Xift::AllowedInTokenStart(char c) +{ + return c == ':' || c == '_' || isalpha(c); +} + +bool Xift::AllowedInToken(char c) +{ + return c == '-' || c == '.' || isdigit(c) || AllowedInTokenStart(c); +} + +bool Xift::IsWhitespace(char c) +{ + return c == ' ' || c == '\t' || c == '\n'; +} diff --git a/src/xift.hpp b/src/xift.hpp new file mode 100644 index 0000000..a37f1c2 --- /dev/null +++ b/src/xift.hpp @@ -0,0 +1,87 @@ +#ifndef XIFT_INCLUDED_XIFT +#define XIFT_INCLUDED_XIFT + +#include "exporter.hpp" +#include "tag.hpp" +#include + + +enum XiftError { + xift_ok = 0, + xift_wrong_char, + xift_attr_sep, + xift_something_after_close, + xift_close_selfclose, + xift_unwanted_close +}; + +const char *XiftErrorMessage(XiftError error); + + +class Xift: public XiftTags { +public: + Xift(XiftExporter &exporter); + + void Put(char c); + void Put(const char *str); + void Put(FILE *file); + void PutFile(const char *path); + + bool End(); + + bool IsError(); + int ErrorPosition(); + int ErrorLine(); + int ErrorColumn(); + XiftError GetError(); + +private: + int error_pos; + int error_line; + int error_column; + XiftError error; + + XiftExporter &exporter; + XiftTags opened; + + enum State { + state_error = 0, + reading_text, + opening_tag, + reading_name, + waiting_attr, + reading_attr_name, + waiting_attr_sep, + waiting_attr_value, + reading_attr_value, + closing_tag + } state; + char value_quota; + + bool is_closing; + bool is_self_closing; + + void ReadText(char c); + void OpenTag(char c); + void ReadName(char c); + void WaitAttr(char c); + void ReadAttrName(char c); + void WaitAttrSep(char c); + void WaitAttrValue(char c); + void ReadAttrValue(char c); + void CloseTag(char c); + + void CompleteCurrent(); + void CloseCurrent(); + void CloseTop(); + void CloseName(const char *name); + void SelfcloseCurrent(); + void PutCurrent(); + + static bool AllowedInTokenStart(char c); + static bool AllowedInToken(char c); + static bool IsWhitespace(char c); +}; + + +#endif -- cgit v1.2.3