summaryrefslogtreecommitdiff
path: root/xs_html.h
diff options
context:
space:
mode:
authordefault <nobody@localhost>2023-11-20 18:33:24 +0100
committerdefault <nobody@localhost>2023-11-20 18:33:24 +0100
commitbc5d0d4ed09833640856ee0193a53553dbb1eb20 (patch)
treeafd97135fb606445cb8f5348ae2857cb3088dbd8 /xs_html.h
parentb68ed66669aa65bda1c55b9dcd7afcc126c7c2cf (diff)
Replaced encode_html_strict() with xs_html_encode().
Diffstat (limited to 'xs_html.h')
-rw-r--r--xs_html.h240
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 */