diff options
| author | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-01-09 01:22:54 +0100 |
|---|---|---|
| committer | Xavier Del Campo Romero <xavi.dcr@tutanota.com> | 2023-07-20 23:52:47 +0200 |
| commit | 2968c5f67daa1c571f5f9cf9445de907f9490636 (patch) | |
| tree | d6919b2446976f4818c62ad419065e3d92061e25 /html.c | |
Initial commit
Diffstat (limited to 'html.c')
| -rw-r--r-- | html.c | 288 |
1 files changed, 288 insertions, 0 deletions
@@ -0,0 +1,288 @@ +#include "html.h" +#include <dynstr.h> +#include <errno.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +struct html_node +{ + struct html_attribute + { + char *attr, *value; + } *attrs; + + char *element, *value; + size_t n; + struct html_node *child, *sibling; +}; + +static char *html_encode(const char *s) +{ + struct dynstr d; + + dynstr_init(&d); + + while (*s) + { + static const struct esc + { + char c; + const char *str; + } esc[] = + { + {.c = '<', .str = ">"}, + {.c = '>', .str = "<"}, + {.c = '&', .str = "&"}, + {.c = '\"', .str = """}, + {.c = '\'', .str = "'"} + }; + + char buf[sizeof "a"] = {0}; + const char *str = NULL; + + for (size_t i = 0; i < sizeof esc / sizeof *esc; i++) + { + const struct esc *const e = &esc[i]; + + if (*s == e->c) + { + str = e->str; + break; + } + } + + if (!str) + { + *buf = *s; + str = buf; + } + + if (dynstr_append(&d, "%s", str)) + { + fprintf(stderr, "%s: dynstr_append failed\n", __func__); + goto failure; + } + + s++; + } + + return d.str; + +failure: + dynstr_free(&d); + return NULL; +} + +int html_node_set_value(struct html_node *const n, const char *const val) +{ + if (!(n->value = html_encode(val))) + { + fprintf(stderr, "%s: html_encode failed\n", __func__); + return -1; + } + + return 0; +} + +int html_node_set_value_unescaped(struct html_node *const n, + const char *const val) +{ + if (!(n->value = strdup(val))) + { + fprintf(stderr, "%s: strdup(3): %s\n", __func__, strerror(errno)); + return -1; + } + + return 0; +} + +int html_node_add_attr(struct html_node *const n, const char *const attr, + const char *const val) +{ + if (!(n->attrs = realloc(n->attrs, (n->n + 1) * sizeof *n->attrs))) + { + fprintf(stderr, "%s: realloc(3): %s\n", __func__, strerror(errno)); + return -1; + } + + struct html_attribute *const a = &n->attrs[n->n++]; + + *a = (const struct html_attribute){0}; + + if (!(a->attr = strdup(attr)) + || (val && !(a->value = strdup(val)))) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + free(a->attr); + free(a->value); + return -1; + } + + return 0; +} + +void html_node_add_sibling(struct html_node *const n, + struct html_node *const sibling) +{ + for (struct html_node *c = n; c; c = c->sibling) + if (!c->sibling) + { + c->sibling = sibling; + break; + } +} + +struct html_node *html_node_add_child(struct html_node *const n, + const char *const element) +{ + struct html_node *const child = html_node_alloc(element); + + if (!child) + return NULL; + else if (n->child) + html_node_add_sibling(n->child, child); + else + n->child = child; + + return child; +} + +int serialize_node(struct dynstr *const d, const struct html_node *const n, + const unsigned level) +{ + for (unsigned i = 0; i < level; i++) + dynstr_append(d, "\t"); + + dynstr_append_or_ret_nonzero(d, "<%s", n->element); + + if (n->n) + dynstr_append_or_ret_nonzero(d, " "); + + for (size_t i = 0; i < n->n; i++) + { + const struct html_attribute *const a = &n->attrs[i]; + + if (a->value) + dynstr_append_or_ret_nonzero(d, "%s=\"%s\"", a->attr, a->value); + else + dynstr_append_or_ret_nonzero(d, "%s", a->attr); + + if (i + 1 < n->n) + dynstr_append_or_ret_nonzero(d, " "); + } + + if (!n->value && !n->child) + dynstr_append_or_ret_nonzero(d, "/>"); + else + { + dynstr_append_or_ret_nonzero(d, ">"); + + if (n->value) + dynstr_append_or_ret_nonzero(d, "%s", n->value); + + if (n->child) + { + dynstr_append_or_ret_nonzero(d, "\n"); + + if (serialize_node(d, n->child, level + 1)) + { + fprintf(stderr, "%s: serialize_node failed\n", __func__); + return -1; + } + + for (unsigned i = 0; i < level; i++) + dynstr_append(d, "\t"); + } + + dynstr_append_or_ret_nonzero(d, "</%s>", n->element); + } + + /* TODO: print siblings */ + + dynstr_append_or_ret_nonzero(d, "\n"); + + if (n->sibling) + return serialize_node(d, n->sibling, level); + + return 0; +} + +int html_serialize(const struct html_node *const n, struct dynstr *const d) +{ + return serialize_node(d, n, 0); +} + +static void html_attribute_free(struct html_attribute *const a) +{ + if (a) + { + free(a->attr); + free(a->value); + } +} + +void html_node_free(struct html_node *const n) +{ + if (n) + { + struct html_node *s = n->sibling; + + html_node_free(n->child); + + while (s) + { + struct html_node *const next = s->sibling; + + html_node_free(s->child); + free(s->element); + free(s->value); + + for (size_t i = 0 ; i < s->n; i++) + html_attribute_free(&s->attrs[i]); + + free(s->attrs); + free(s); + s = next; + } + + free(n->element); + free(n->value); + + for (size_t i = 0 ; i < n->n; i++) + html_attribute_free(&n->attrs[i]); + + free(n->attrs); + } + + free(n); +} + +struct html_node *html_node_alloc(const char *const element) +{ + struct html_node *const n = malloc(sizeof *n); + + if (!n) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + + *n = (const struct html_node) + { + .element = strdup(element) + }; + + if (!n->element) + { + fprintf(stderr, "%s: malloc(3): %s\n", __func__, strerror(errno)); + goto failure; + } + + return n; + +failure: + html_node_free(n); + return NULL; +} |
