/****************************************************************************** * * 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); }