diff --git a/components/html.c b/components/html.c
new file mode 100644
index 0000000..6aa6c5d
--- /dev/null
+++ b/components/html.c
@@ -0,0 +1,465 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2017-2019 by Löwenware Ltd
+ * Please, refer LICENSE file for legal information
+ *
+ ******************************************************************************/
+
+/**
+ * @file html.c
+ * @author Ilja Kartašov
+ * @brief
+ *
+ * @see https://lowenware.com/
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "html.h"
+
+
+static char *
+ax_html_escape(char *src, uint32_t len)
+{
+ uint32_t extra = 0;
+ char *ptr = src;
+ char ch, *result;
+
+ while ((ch = *ptr) != 0) {
+ switch (ch) {
+ case '"':
+ case '\\':
+ case '\n':
+ extra++;
+ break;
+ default:
+ break;
+ }
+ ptr++;
+ }
+
+ if ((result = calloc(len + extra + 1, sizeof (char))) != NULL) {
+ if (extra) {
+ ptr = result;
+ while (len--) {
+ switch (*src) {
+ case '"':
+ case '\\':
+ *(ptr++) = '\\';
+ break;
+ case '\n':
+ *(ptr++) = '\\';
+ *(ptr++) = 'n';
+ case '\r': /* go through */
+ src++;
+ continue;
+ default:
+ break;
+ }
+ if (!(*(ptr++) = *(src++)))
+ break;
+ }
+ } else {
+ strncpy(result, src, len);
+ result[len] = 0;
+ }
+ }
+ return result;
+}
+
+static struct ax_html *
+ax_html_append_new(CStuffList list, const struct ax_html *src)
+{
+ struct ax_html *result;
+
+ if ((result = malloc(sizeof (*result))) != NULL) {
+ result->type = src->type;
+ result->offset = src->offset;
+ result->size = src->size;
+ if ((result->data = ax_html_escape(src->data, src->size)) != NULL) {
+ if (cstuff_list_append(list, result) != -1) {
+ return result;
+ }
+ free(result->data);
+ }
+ free(result);
+ }
+ return NULL;
+}
+
+
+static int
+ax_html_check_chars(char *begin, char *end)
+{
+ int result = 0;
+ while (begin < end) {
+ char c = *begin;
+ if (c == '-') {
+ c = (*begin = '_');
+ }
+ if (isalnum(c) || c == '_') {
+ result++;
+ } else {
+ return 0;
+ }
+ begin++;
+ }
+ return result;
+}
+
+
+static AislStatus
+ax_html_read_line(CStuffList list, char *line)
+{
+ const char input_open[] = "${",
+ input_close[] = "}",
+ block_open[] = "";
+
+ struct ax_html node;
+ size_t offset;
+ char *cur;
+
+ offset = strspn(line, "\t ");
+ cur = &line[offset];
+
+ while (*cur) {
+ char *p1, *p2, *p;
+ uint16_t type = AX_HTML_PLAIN;
+ uint32_t close_len = 0;
+
+ p1 = strstr(cur, input_open);
+ p2 = strstr(cur, block_open);
+
+ if (p1 && (!p2 || p2 > p1)) {
+ p = &p1[sizeof (input_open) - 1];
+ if ((p2 = strstr(p, input_close)) && ax_html_check_chars(p, p2)) {
+ type = AX_HTML_INPUT;
+ close_len = sizeof (input_close) - 1;
+ }
+ } else if (p2 && (!p1 || p1 > p2)) {
+ p1 = p2;
+ p = &p1[sizeof (block_open) - 1];
+ if ((p2 = strstr(p, block_close)) && ax_html_check_chars(p, p2)) {
+ type = AX_HTML_BLOCK;
+ close_len = sizeof (block_close) - 1;
+ }
+ }
+
+ if (type != AX_HTML_PLAIN) {
+ if (p1 != cur) {
+ node.type = AX_HTML_PLAIN;
+ node.offset = offset;
+ node.size = (uint32_t)(p1 - cur);
+ node.data = cur;
+ offset = 0;
+ if (ax_html_append_new(list, &node) == NULL) {
+ return AISL_MALLOC_ERROR;
+ }
+ }
+ node.type = type;
+ node.offset = offset;
+ node.size = (uint32_t)(p2 - p);
+ node.data = p;
+ offset = 0;
+ if (ax_html_append_new(list, &node) == NULL) {
+ return AISL_MALLOC_ERROR;
+ }
+ cur = p2 + close_len;
+ if (type == AX_HTML_BLOCK) {
+ size_t lfcr = strspn(cur, "\r\n");
+ if (lfcr) {
+ cur += lfcr;
+ }
+ }
+ } else {
+ node.type = type;
+ node.offset = offset;
+ node.size = strlen(cur);
+ node.data = cur;
+ if (ax_html_append_new(list, &node) == NULL) {
+ return AISL_MALLOC_ERROR;
+ }
+ cur = &cur[node.size];
+ }
+ }
+ return AISL_SUCCESS;
+}
+
+
+AislStatus
+ax_html_import(CStuffList list, const char *html_file)
+{
+ FILE *f = NULL;
+ size_t sz = 256;
+ char *line;
+ ssize_t rs;
+ AislStatus result = AISL_SUCCESS;
+
+ if (!(line = malloc(sz)))
+ goto e_malloc;
+
+ if (!(f = fopen(html_file, "r")))
+ goto e_syscall;
+
+ while ((rs = getdelim(&line, &sz, '\n', f)) != -1) {
+ if ((result = ax_html_read_line(list, line)) != AISL_SUCCESS) {
+ goto finally;
+ }
+ }
+
+ switch(errno) {
+ case 0:
+ break;
+
+ case ENOMEM:
+ goto e_malloc;
+
+ default:
+ goto e_syscall;
+ }
+ goto finally;
+
+e_malloc:
+ result = AISL_MALLOC_ERROR;
+ goto finally;
+
+e_syscall:
+ result = AISL_SYSCALL_ERROR;
+ goto finally;
+
+finally:
+ if (line)
+ free(line);
+ if (f)
+ fclose(f);
+ return result;
+}
+
+
+AislStatus
+ax_html_export(CStuffList list, const struct ax_html_options *opts)
+{
+ AislStatus result = AISL_SUCCESS;
+ FILE *out = NULL;
+ int i, l;
+ char *c_prefix = NULL, *u_prefix = NULL, *f = NULL;
+ const char *ofst = opts->offset;
+
+ /* Cammel case prefix */
+ if (cstuff_strcpy(&c_prefix, opts->name) == -1)
+ goto e_malloc;
+
+ *c_prefix = toupper(*c_prefix);
+
+ /* Upper case prefix */
+ if ((i = cstuff_strcpy(&u_prefix, opts->name)) == -1)
+ goto e_malloc;
+
+ while (i--) {
+ u_prefix[i] = toupper(u_prefix[i]);
+ }
+
+ /* header file */
+ if ((l = cstuff_sprintf(&f, "%s/%s.h", opts->path, opts->name)) == -1)
+ goto e_malloc;
+
+ if (!(out = fopen(f, "w")))
+ goto e_syscall;
+
+ fprintf(out, "#ifndef %s_H_AISL_COMPOSED\n", u_prefix);
+ fprintf(out, "#define %s_H_AISL_COMPOSED\n\n", u_prefix);
+ fprintf(out, "#include \n");
+
+ fprintf(out, "#ifdef DEVEL\n\n");
+ fprintf(out, "#include \n");
+ fprintf(out, "#else\n\n");
+ fprintf(out, "#define %s_put(STREAM, BLOCK, CB, CTX) ", opts->name);
+ fprintf(out, "%s_put_##BLOCK##(STREAM, CB, CTX)\n\n", opts->name);
+ fprintf(out, "#endif\n\n");
+ fprintf(out, "typedef int\n(*%sCallback)(AislStream s, const char *input, "
+ "void *ctx);\n\n", c_prefix);
+
+ fprintf(out, "#ifdef DEVEL\n\n");
+ fprintf(out, "int\n%s_put(AislStream s, %sCallback callback, "
+ "void *ctx, const char *block);\n\n", opts->name, c_prefix);
+ fprintf(out, "int\n%s_set(CStuffList list);\n\n", opts->name);
+ fprintf(out, "#else\n\n");
+ for (i = 0; i < list->length; i++) {
+ struct ax_html *html = list->items[i];
+
+ if (html->type == AX_HTML_BLOCK) {
+ fprintf(out, "int\n%s_put_%s(AislStream s, %sCallback callback, void *ctx);\n\n",
+ opts->name, html->data, c_prefix);
+ }
+ }
+ fprintf(out, "#endif\n\n");
+
+ fprintf(out, "#endif\n");
+ fprintf(out, "\n\n");
+
+ fclose(out);
+
+ /* source file */
+ f[l-1] = 'c';
+
+ if (!(out = fopen(f, "w")))
+ goto e_syscall;
+
+ fprintf(out, "#include \n");
+ fprintf(out, "#include \"%s.h\"\n\n", opts->name);
+ fprintf(out, "#define %s_PLAIN %d\n", u_prefix, AX_HTML_PLAIN);
+ fprintf(out, "#define %s_BLOCK %d\n", u_prefix, AX_HTML_BLOCK);
+ fprintf(out, "#define %s_INPUT %d\n\n", u_prefix, AX_HTML_INPUT);
+ fprintf(out, "struct %s {\n", opts->name);
+ fprintf(out, "%suint16_t type;\n", ofst);
+ fprintf(out, "%suint16_t offset;\n", ofst);
+ fprintf(out, "%suint32_t size;\n", ofst);
+ fprintf(out, "%sconst char *code;\n", ofst);
+ fprintf(out, "};\n\n");
+
+ fprintf(out, "#ifdef DEVEL\n\n");
+
+ fprintf(out, "static struct %s *m_%s = NULL;\n\n", opts->name, opts->name);
+ fprintf(out, "#else\n\n");
+
+ fprintf(out, "static const struct %s m_%s[] = {\n", opts->name, opts->name);
+
+ for (i = 0; i < list->length; i++) {
+ struct ax_html *html = list->items[i];
+ uint32_t offset, size = html->size;
+
+ if (opts->minimize) {
+ offset = 0;
+ if (html->type == AX_HTML_PLAIN) {
+ int l = strlen(html->data);
+
+ if (!(l < 2) && !strcmp(&html->data[l - 2], "\\n")) {
+ size--;
+ }
+ }
+ } else {
+ offset = html->offset & 0xFFFF;
+ }
+
+ fprintf(out, "%s{%u, %u, %u, \"%s\"},\n", ofst, html->type, offset,
+ size, html->data);
+ }
+ fprintf(out, "%s{0, 0, 0, NULL}\n};\n\n", ofst);
+
+ fprintf(out, "#endif\n\n");
+
+ fprintf(out, "static int\nput_from(AislStream s, %sCallback callback, "
+ "void *ctx, uint32_t from)\n{\n", c_prefix);
+ fprintf(out, "%sint result = 0, r;\n", ofst);
+ fprintf(out, "%swhile (m_%s[from]->data) {\n", ofst, opts->name);
+ fprintf(out, "%s%sswitch (m_%s[from]->type) {\n", ofst, ofst,
+ opts->name);
+ fprintf(out, "%s%scase %s_PLAIN:\n", ofst, ofst, u_prefix);
+ fprintf(out, "%s%s%sr = aisl_write(s, m_%s[from]->data, m_%s[from]->size);\n",
+ ofst, ofst, ofst, opts->name, opts->name);
+ fprintf(out, "%s%s%sbreak;\n", ofst, ofst, ofst);
+
+ fprintf(out, "%s%scase %s_INPUT:\n", ofst, ofst, u_prefix);
+ fprintf(out, "%s%s%sr = callback(s, m_%s[from]->data, ctx);\n",
+ ofst, ofst, ofst, opts->name);
+ fprintf(out, "%s%s%sbreak;\n", ofst, ofst, ofst);
+ fprintf(out, "%s%scase %s_BLOCK:\n", ofst, ofst, u_prefix);
+ fprintf(out, "%s%s%sreturn result;\n", ofst, ofst, ofst);
+ fprintf(out, "%s%sdefault:\n", ofst, ofst);
+ fprintf(out, "%s%s%sreturn -1;\n", ofst, ofst, ofst);
+ fprintf(out, "%s%s}\n", ofst, ofst);
+ fprintf(out, "%s%sif (r == -1) return r;\n", ofst, ofst);
+ fprintf(out, "%s%sresult += r;\n", ofst, ofst);
+ fprintf(out, "%s%sfrom++;\n", ofst, ofst);
+ fprintf(out, "%s}\n", ofst);
+ fprintf(out, "%sreturn result;\n}\n\n", ofst);
+
+ fprintf(out, "#ifdef DEVEL\n\n");
+ fprintf(out, "int\n%s_put(AislStream s, %sCallback callback, "
+ "void *ctx, const char *block)\n{\n", opts->name, c_prefix);
+ fprintf(out, "%sint i = 0;\n", ofst);
+ fprintf(out, "%swhile (m_%s[i]->data) {\n", ofst, opts->name);
+ fprintf(out, "%s%sif (m_%s[i]->type == %s_BLOCK && "
+ "!strcmp(m_%s[i]->data, block)) {\n", ofst, ofst, opts->name, u_prefix,
+ opts->name);
+ fprintf(out, "%s%s%sreturn put_from(s, callback, ctx, i + 1);\n", ofst, ofst,
+ ofst);
+ fprintf(out, "%s%s}\n", ofst, ofst);
+ fprintf(out, "%s}\n", ofst);
+ fprintf(out, "%sreturn 0;\n}\n\n", ofst);
+
+ fprintf(out, "int\n%s_set(CStuffList list)\n{\n", opts->name);
+ fprintf(out, "%sint i;\n", ofst);
+ fprintf(out, "%sif (m_%s) {\n", ofst, opts->name);
+ fprintf(out, "%s%si = 0;\n", ofst, ofst);
+ fprintf(out, "%s%swhile (m_%s[i].data) {\n", ofst, ofst, opts->name);
+ fprintf(out, "%s%s%sfree(m_%s[i].data);\n", ofst, ofst, ofst, opts->name);
+ fprintf(out, "%s%s}\n", ofst, ofst);
+ fprintf(out, "%s%sfree(m_%s);\n", ofst, ofst, opts->name);
+ fprintf(out, "%s}\n", ofst);
+ fprintf(out, "%sif (!(m_%s = malloc(list->length * sizeof (*m_%s))))\n", ofst,
+ opts->name, opts->name);
+ fprintf(out, "%s%sreturn -1;\n", ofst, ofst);
+ fprintf(out, "%sfor (i = 0; i < list->length; i++) {\n", ofst);
+ fprintf(out, "%s%smemcpy(&m_%s[i], list->items[i], sizeof (*m_%s));\n", ofst,
+ ofst, opts->name, opts->name);
+ fprintf(out, "%s}\n", ofst);
+ fprintf(out, "%smemset(&m_%s[i], 0, sizeof (m_%s[i]));\n", ofst, opts->name,
+ opts->name);
+ fprintf(out, "%sreturn i;\n", ofst);
+ fprintf(out, "}\n\n");
+
+
+ fprintf(out, "#else\n\n");
+
+ for (i = 0; i < list->length; i++) {
+ struct ax_html *html = list->items[i];
+ if (html->type == AX_HTML_BLOCK) {
+ fprintf(out, "int\n%s_put_%s(AislStream s, %sCallback callback, "
+ "void *ctx)\n", opts->name, html->data, c_prefix);
+ fprintf(out, "{\n");
+ fprintf(out, "%sreturn put_from(s, callback, ctx, %u);\n", ofst, i + 1);
+ fprintf(out, "}\n\n");
+ }
+ }
+ fprintf(out, "#endif\n\n");
+
+ fclose(out);
+
+ goto finally;
+
+e_syscall:
+ result = AISL_SYSCALL_ERROR;
+ goto finally;
+
+e_malloc:
+ result = AISL_MALLOC_ERROR;
+
+finally:
+ if (c_prefix)
+ free(c_prefix);
+
+ if (u_prefix)
+ free(u_prefix);
+
+ if (f)
+ free(f);
+
+ return result;
+}
+
+
+void
+ax_html_free(struct ax_html *html)
+{
+ if (html->data) {
+ free(html->data);
+ }
+ free(html);
+}
diff --git a/components/html.h b/components/html.h
new file mode 100644
index 0000000..c45b860
--- /dev/null
+++ b/components/html.h
@@ -0,0 +1,56 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2017-2019 by Löwenware Ltd
+ * Please, refer LICENSE file for legal information
+ *
+ ******************************************************************************/
+
+/**
+ * @file template.h
+ * @author Ilja Kartašov
+ * @brief
+ *
+ * @see https://lowenware.com/
+ */
+
+#ifndef AX_HTML_H_92C08532_E423_4CE4_A44C_27CF864A2C60
+#define AX_HTML_H_92C08532_E423_4CE4_A44C_27CF864A2C60
+
+#include
+#include
+#include
+#include
+
+#define AX_HTML_NULL 0
+#define AX_HTML_PLAIN 1
+#define AX_HTML_BLOCK 2
+#define AX_HTML_INPUT 3
+
+struct ax_html {
+ uint16_t type;
+ uint16_t offset;
+ uint32_t size;
+ char *data;
+};
+
+struct ax_html_options {
+ const char *offset;
+ const char *path;
+ const char *name;
+ bool minimize;
+};
+
+
+AislStatus
+ax_html_import(CStuffList list, const char *html_file);
+
+
+AislStatus
+ax_html_export(CStuffList list, const struct ax_html_options *opts);
+
+
+void
+ax_html_free(struct ax_html *html);
+
+
+#endif /* !HTML_H */