diff options
author | default <nobody@localhost> | 2023-11-20 18:33:24 +0100 |
---|---|---|
committer | default <nobody@localhost> | 2023-11-20 18:33:24 +0100 |
commit | bc5d0d4ed09833640856ee0193a53553dbb1eb20 (patch) | |
tree | afd97135fb606445cb8f5348ae2857cb3088dbd8 /xs_html.h | |
parent | b68ed66669aa65bda1c55b9dcd7afcc126c7c2cf (diff) |
Replaced encode_html_strict() with xs_html_encode().
Diffstat (limited to 'xs_html.h')
-rw-r--r-- | xs_html.h | 240 |
1 files changed, 240 insertions, 0 deletions
diff --git a/xs_html.h b/xs_html.h new file mode 100644 index 0000000..744df5b --- /dev/null +++ b/xs_html.h @@ -0,0 +1,240 @@ +/* copyright (c) 2022 - 2023 grunfink et al. / MIT license */ + +#ifndef _XS_HTML_H + +#define _XS_HTML_H + +typedef struct xs_html xs_html; + +xs_str *xs_html_encode(char *str); + +xs_html *xs_html_attr(char *key, char *value); +xs_html *xs_html_text(char *content); +xs_html *xs_html_raw(char *content); + +xs_html *xs_html_add(xs_html *tag, xs_html *data); + +xs_html *_xs_html_tag(char *tag, xs_html *var[]); +#define xs_html_tag(tag, ...) _xs_html_tag(tag, (xs_html *[]) { __VA_ARGS__, NULL }) +xs_html *_xs_html_sctag(char *tag, xs_html *var[]); +#define xs_html_sctag(tag, ...) _xs_html_sctag(tag, (xs_html *[]) { __VA_ARGS__, NULL }) +xs_str *_xs_html_render(xs_html *h, xs_str *s); +#define xs_html_render(h) _xs_html_render(h, xs_str_new(NULL)) + +#ifdef XS_IMPLEMENTATION + +typedef enum { + XS_HTML_TAG, + XS_HTML_SCTAG, + XS_HTML_ATTR, + XS_HTML_TEXT +} xs_html_type; + +struct xs_html { + xs_html_type type; + xs_str *content; + xs_html *f_attr; + xs_html *l_attr; + xs_html *f_tag; + xs_html *l_tag; + xs_html *next; +}; + +xs_str *xs_html_encode(char *str) +/* encodes str using HTML entities */ +{ + xs_str *s = xs_str_new(NULL); + int o = 0; + char *e = str + strlen(str); + + for (;;) { + char *ec = "<>\"'&"; /* characters to escape */ + char *q = e; + int z; + + /* find the nearest happening of a char */ + while (*ec) { + char *m = memchr(str, *ec++, q - str); + if (m) + q = m; + } + + /* copy string to here */ + z = q - str; + s = xs_insert_m(s, o, str, z); + o += z; + + /* if q points to the end, nothing more to do */ + if (q == e) + break; + + /* insert the escaped char */ + char tmp[8]; + snprintf(tmp, sizeof(tmp), "&#%d;", *q); + + z = strlen(tmp); + s = xs_insert_m(s, o, tmp, z); + o += z; + + str = q + 1; + } + + return s; +} + + +#define XS_HTML_NEW() memset(xs_realloc(NULL, sizeof(xs_html)), '\0', sizeof(xs_html)) + +xs_html *xs_html_attr(char *key, char *value) +/* creates an HTML block with an attribute */ +{ + xs_html *a = XS_HTML_NEW(); + + a->type = XS_HTML_ATTR; + + if (value) { + xs *ev = xs_html_encode(value); + a->content = xs_fmt("%s=\"%s\"", key, ev); + } + else + a->content = xs_dup(key); + + return a; +} + + +xs_html *xs_html_text(char *content) +/* creates an HTML block of text, escaping it previously */ +{ + xs_html *a = XS_HTML_NEW(); + + a->type = XS_HTML_TEXT; + a->content = xs_html_encode(content); + + return a; +} + + +xs_html *xs_html_raw(char *content) +/* creates an HTML block without escaping (for pre-formatted HTML, comments, etc) */ +{ + xs_html *a = XS_HTML_NEW(); + + a->type = XS_HTML_TEXT; + a->content = xs_dup(content); + + return a; +} + + +xs_html *xs_html_add(xs_html *tag, xs_html *data) +/* add data (attrs, tags or text) to a tag */ +{ + xs_html **first; + xs_html **last; + + if (data->type == XS_HTML_ATTR) { + first = &tag->f_attr; + last = &tag->l_attr; + } + else { + first = &tag->f_tag; + last = &tag->l_tag; + } + + if (*first == NULL) + *first = data; + + if (*last != NULL) + (*last)->next = data; + + *last = data; + + return tag; +} + + +static xs_html *_xs_html_tag_t(xs_html_type type, char *tag, xs_html *var[]) +/* creates a tag with a variable list of attributes and subtags */ +{ + xs_html *a = XS_HTML_NEW(); + + a->type = type; + a->content = xs_dup(tag); + + while (*var) + xs_html_add(a, *var++); + + return a; +} + + +xs_html *_xs_html_tag(char *tag, xs_html *var[]) +{ + return _xs_html_tag_t(XS_HTML_TAG, tag, var); +} + + +xs_html *_xs_html_sctag(char *tag, xs_html *var[]) +{ + return _xs_html_tag_t(XS_HTML_SCTAG, tag, var); +} + + +xs_str *_xs_html_render(xs_html *h, xs_str *s) +/* renders the tag and its subtags */ +{ + xs_html *st; + + switch (h->type) { + case XS_HTML_TAG: + case XS_HTML_SCTAG: + s = xs_str_cat(s, "<", h->content); + + /* render the attributes */ + st = h->f_attr; + while (st) { + xs_html *nst = st->next; + s = _xs_html_render(st, s); + st = nst; + } + + if (h->type == XS_HTML_SCTAG) { + /* self-closing tags should not have subtags */ + s = xs_str_cat(s, "/>"); + } + else { + s = xs_str_cat(s, ">"); + + /* render the subtags */ + st = h->f_tag; + while (st) { + xs_html *nst = st->next; + s = _xs_html_render(st, s); + st = nst; + } + + s = xs_str_cat(s, "</", h->content, ">"); + } + + break; + + case XS_HTML_ATTR: + s = xs_str_cat(s, " ", h->content); + break; + + case XS_HTML_TEXT: + s = xs_str_cat(s, h->content); + break; + } + + xs_free(h->content); + xs_free(h); + + return s; +} + + +#endif /* XS_IMPLEMENTATION */ + +#endif /* _XS_HTML_H */ |