snac2

Fork of https://codeberg.org/grunfink/snac2
git clone https://git.inz.fi/snac2
Log | Files | Refs | README | LICENSE

commit 3d9256337ad035a7ba1395530dbee3adb0fe9bdc
parent 88b9a3187c6f41b8a752a13c04478bf6b4ba0593
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date:   Fri, 21 Feb 2025 16:59:04 +0200

Add string builder

Diffstat:
Mformat.c | 91++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mhtml.c | 14+++++++-------
Mxs.h | 36+++++++++++++++++++++++++++++++++++-
Mxs_regex.h | 12++++++------
4 files changed, 94 insertions(+), 59 deletions(-)

diff --git a/format.c b/format.c @@ -85,7 +85,7 @@ xs_dict *emojis(void) static xs_str *format_line(const char *line, xs_list **attach) /* formats a line */ { - xs_str *s = xs_str_new(NULL); + xs_str_bld b = { 0 }; char *p; const char *v; @@ -110,35 +110,35 @@ static xs_str *format_line(const char *line, xs_list **attach) if (xs_startswith(v, "`")) { xs *s1 = xs_strip_chars_i(xs_dup(v), "`"); xs *e1 = encode_html(s1); - s = xs_str_cat_fmt(s, "<code>%s</code>", e1); + xs_str_bld_cat_fmt(&b, "<code>%s</code>", e1); } else if (xs_startswith(v, "***")) { xs *s1 = xs_strip_chars_i(xs_dup(v), "*"); - s = xs_str_cat_fmt(s, "<b><i>%s</i></b>", s1); + xs_str_bld_cat_fmt(&b, "<b><i>%s</i></b>", s1); } else if (xs_startswith(v, "**")) { xs *s1 = xs_strip_chars_i(xs_dup(v), "*"); - s = xs_str_cat_fmt(s, "<b>%s</b>", s1); + xs_str_bld_cat_fmt(&b, "<b>%s</b>", s1); } else if (xs_startswith(v, "*")) { xs *s1 = xs_strip_chars_i(xs_dup(v), "*"); - s = xs_str_cat_fmt(s, "<i>%s</i>", s1); + xs_str_bld_cat_fmt(&b, "<i>%s</i>", s1); } //anzu - begin else if (xs_startswith(v, "__")) { xs *s1 = xs_strip_chars_i(xs_dup(v), "_"); - s = xs_str_cat_fmt(s, "<u>%s</u>", s1); + xs_str_bld_cat_fmt(&b, "<u>%s</u>", s1); } //anzu - end else if (xs_startswith(v, "~~")) { xs *s1 = xs_strip_chars_i(xs_dup(v), "~"); xs *e1 = encode_html(s1); - s = xs_str_cat_fmt(s, "<s>%s</s>", e1); + xs_str_bld_cat_fmt(&b, "<s>%s</s>", e1); } else if (*v == '[') { @@ -153,11 +153,11 @@ static xs_str *format_line(const char *line, xs_list **attach) name = xs_crop_i(name, 1, 0); url = xs_crop_i(url, 0, -1); - s = xs_str_cat_fmt(s, "<a href=\"%s\">%s</a>", + xs_str_bld_cat_fmt(&b, "<a href=\"%s\">%s</a>", url, name); } else - s = xs_str_cat(s, v); + xs_str_bld_cat(&b, v); } else if (*v == '!') { @@ -197,11 +197,11 @@ static xs_str *format_line(const char *line, xs_list **attach) } } else { - s = xs_str_cat_fmt(s, "<a href=\"%s\">%s</a>", img_url, alt_text); + xs_str_bld_cat_fmt(&b, "<a href=\"%s\">%s</a>", img_url, alt_text); } } else - s = xs_str_cat(s, v); + xs_str_bld_cat(&b, v); } else if (xs_str_in(v, ":/" "/") != -1) { @@ -236,7 +236,7 @@ static xs_str *format_line(const char *line, xs_list **attach) } } else { - s = xs_str_cat_fmt(s, "<a href=\"%s\" target=\"_blank\">%s</a>", v2, u); + xs_str_bld_cat_fmt(&b, "<a href=\"%s\" target=\"_blank\">%s</a>", v2, u); } } else @@ -245,26 +245,26 @@ static xs_str *format_line(const char *line, xs_list **attach) xs *v2 = xs_strip_chars_i(xs_dup(u), ".,)"); - s = xs_str_cat_fmt("<a href=\"%s\" target=\"_blank\">%s</a>", v2, u); + xs_str_bld_cat_fmt(&b, "<a href=\"%s\" target=\"_blank\">%s</a>", v2, u); } else - s = xs_str_cat(s, v); + xs_str_bld_cat(&b, v); } else /* surrounded text, copy directly */ - s = xs_str_cat(s, v); + xs_str_bld_cat(&b, v); n++; } - return s; + return b.data; } xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag) /* formats a content using some Markdown rules */ { - xs_str *s = xs_str_new(NULL); + xs_str_bld b = { 0 }; int in_pre = 0; int in_blq = 0; xs *list; @@ -280,9 +280,9 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag if (strcmp(v, "```") == 0) { if (!in_pre) - s = xs_str_cat(s, "<pre>"); + xs_str_bld_cat(&b, "<pre>"); else - s = xs_str_cat(s, "</pre>"); + xs_str_bld_cat(&b, "</pre>"); in_pre = !in_pre; continue; @@ -292,8 +292,8 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag // Encode all HTML characters when we're in pre element until we are out. ss = encode_html(v); - s = xs_str_cat(s, ss); - s = xs_str_cat(s, "<br>"); + xs_str_bld_cat(&b, ss); + xs_str_bld_cat(&b, "<br>"); continue; } @@ -303,9 +303,9 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag if (xs_startswith(ss, "---")) { /* delete the --- */ ss = xs_strip_i(xs_crop_i(ss, 3, 0)); - s = xs_str_cat(s, "<hr>"); + xs_str_bld_cat(&b, "<hr>"); - s = xs_str_cat(s, ss); + xs_str_bld_cat(&b, ss); continue; } @@ -314,23 +314,23 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag // h1 reserved for snac? if (xs_startswith(ss, "# ")) { ss = xs_strip_i(xs_crop_i(ss, 2, 0)); - s = xs_str_cat(s, "<h2>"); - s = xs_str_cat(s, ss); - s = xs_str_cat(s, "</h2>"); + xs_str_bld_cat(&b, "<h2>"); + xs_str_bld_cat(&b, ss); + xs_str_bld_cat(&b, "</h2>"); continue; } if (xs_startswith(ss, "## ")) { ss = xs_strip_i(xs_crop_i(ss, 3, 0)); - s = xs_str_cat(s, "<h2>"); - s = xs_str_cat(s, ss); - s = xs_str_cat(s, "</h2>"); + xs_str_bld_cat(&b, "<h2>"); + xs_str_bld_cat(&b, ss); + xs_str_bld_cat(&b, "</h2>"); continue; } if (xs_startswith(ss, "### ")) { ss = xs_strip_i(xs_crop_i(ss, 4, 0)); - s = xs_str_cat(s, "<h3>"); - s = xs_str_cat(s, ss); - s = xs_str_cat(s, "</h3>"); + xs_str_bld_cat(&b, "<h3>"); + xs_str_bld_cat(&b, ss); + xs_str_bld_cat(&b, "</h3>"); continue; } //anzu - end @@ -340,30 +340,31 @@ xs_str *not_really_markdown(const char *content, xs_list **attach, xs_list **tag ss = xs_strip_i(xs_crop_i(ss, 1, 0)); if (!in_blq) { - s = xs_str_cat(s, "<blockquote>"); + xs_str_bld_cat(&b, "<blockquote>"); in_blq = 1; } - s = xs_str_cat(s, ss); - s = xs_str_cat(s, "<br>"); + xs_str_bld_cat(&b, ss); + xs_str_bld_cat(&b, "<br>"); continue; } if (in_blq) { - s = xs_str_cat(s, "</blockquote>"); + xs_str_bld_cat(&b, "</blockquote>"); in_blq = 0; } - s = xs_str_cat(s, ss); - s = xs_str_cat(s, "<br>"); + xs_str_bld_cat(&b, ss); + xs_str_bld_cat(&b, "<br>"); } if (in_blq) - s = xs_str_cat(s, "</blockquote>"); + xs_str_bld_cat(&b, "</blockquote>"); if (in_pre) - s = xs_str_cat(s, "</pre>"); + xs_str_bld_cat(&b, "</pre>"); + xs_str *s = b.data; /* some beauty fixes */ s = xs_replace_i(s, "<br><br><blockquote>", "<br><blockquote>"); s = xs_replace_i(s, "</blockquote><br>", "</blockquote>"); @@ -421,7 +422,7 @@ const char *valid_tags[] = { xs_str *sanitize(const char *content) /* cleans dangerous HTML output */ { - xs_str *s = xs_str_new(NULL); + xs_str_bld b = { 0 }; xs *sl; int n = 0; char *p; @@ -451,23 +452,23 @@ xs_str *sanitize(const char *content) xs *el = xs_regex_select(v, "(src|href|rel|class|target)=(\"[^\"]*\"|'[^']*')"); xs *s3 = xs_join(el, " "); - s = xs_str_cat_fmt(s, "<%s%s%s%s>", + xs_str_bld_cat_fmt(&b, "<%s%s%s%s>", v[1] == '/' ? "/" : "", tag, xs_list_len(el) ? " " : "", s3); } else { /* treat end of divs as paragraph breaks */ if (strcmp(v, "</div>")) - s = xs_str_cat(s, "<p>"); + xs_str_bld_cat(&b, "<p>"); } } else { /* non-tag */ - s = xs_str_cat(s, v); + xs_str_bld_cat(&b, v); } n++; } - return s; + return b.data; } diff --git a/html.c b/html.c @@ -1307,23 +1307,23 @@ xs_html *html_top_controls(snac *user) const char *webhook = xs_dict_get_def(user->config, "notify_webhook", ""); xs *metadata = NULL; - const xs_dict *md = xs_dict_get(user->config, "metadata"); + const xs_val *md = xs_dict_get(user->config, "metadata"); if (xs_type(md) == XSTYPE_DICT) { + xs_str_bld b = { 0 }; const xs_str *k; const xs_str *v; const char *s = ""; - metadata = xs_str_new(NULL); - xs_dict_foreach(md, k, v) { - metadata = xs_str_cat_fmt(metadata, "%s%s=%s", s, k, v); + xs_str_bld_cat_fmt(&b, "%s%s=%s", s, k, v); s = "\n"; } + md = metadata = b.data; } else - if (xs_type(md) == XSTYPE_STRING) - metadata = xs_dup(md); + if (xs_type(md) != XSTYPE_STRING) + md = NULL; /* ui language */ xs_html *lang_select = xs_html_tag("select", @@ -1564,7 +1564,7 @@ xs_html *html_top_controls(snac *user) xs_html_attr("rows", "4"), xs_html_attr("placeholder", "Blog=https:/" "/example.com/my-blog\nGPG Key=1FA54\n..."), - xs_html_text(metadata ? metadata : ""))), + xs_html_text(md ? md : ""))), xs_html_tag("p", xs_html_text(L("Web interface language:")), diff --git a/xs.h b/xs.h @@ -36,6 +36,10 @@ typedef char xs_keyval; typedef char xs_dict; typedef char xs_number; typedef char xs_data; +typedef struct { + xs_str *data; + int len; +} xs_str_bld; /* size in bytes of the type size */ #define _XS_TYPE_SIZE 4 @@ -76,6 +80,8 @@ xs_str *xs_str_wrap_i(const char *prefix, xs_str *str, const char *suffix); xs_str *_xs_str_cat(xs_str *str, const char *strs[]); #define xs_str_cat(str, ...) _xs_str_cat(str, (const char *[]){ __VA_ARGS__, NULL }) xs_str *xs_str_cat_fmt(xs_str *str, const char *fmt, ...); +void xs_str_bld_cat(xs_str_bld *b, const char *str); +void xs_str_bld_cat_fmt(xs_str_bld *b, const char *fmt, ...); xs_str *xs_replace_in(xs_str *str, const char *sfrom, const char *sto, int times); #define xs_replace_i(str, sfrom, sto) xs_replace_in(str, sfrom, sto, XS_ALL) #define xs_replace(str, sfrom, sto) xs_replace_in(xs_dup(str), sfrom, sto, XS_ALL) @@ -523,7 +529,6 @@ xs_str *xs_str_cat_fmt(xs_str *str, const char *fmt, ...) { int o = strlen(str); int n; - xs_str *s = NULL; va_list ap; va_start(ap, fmt); @@ -540,6 +545,33 @@ xs_str *xs_str_cat_fmt(xs_str *str, const char *fmt, ...) return str; } +void xs_str_bld_cat_fmt(xs_str_bld *b, const char *fmt, ...) +{ + int n; + va_list ap; + + va_start(ap, fmt); + n = vsnprintf(NULL, 0, fmt, ap); + va_end(ap); + + if (n > 0) { + b->data = xs_realloc(b->data, _xs_blk_size(b->len + n + 1)); + va_start(ap, fmt); + vsnprintf(b->data + b->len, n + 1, fmt, ap); + va_end(ap); + + b->len += n; + } +} + +void xs_str_bld_cat(xs_str_bld *b, const xs_str *s) +{ + int n = strlen(s); + b->data = xs_realloc(b->data, _xs_blk_size(b->len + n + 1)); + memcpy(b->data + b->len, s, n + 1); + b->len += n; +} + xs_str *_xs_str_cat(xs_str *str, const char *strs[]) /* concatenates all strings after str */ { @@ -1234,6 +1266,8 @@ xs_dict *xs_dict_set_fmt(xs_dict *dict, const xs_str *key, const char *fmt, ...) vsnprintf(dict + o, vsz, fmt, ap); va_end(ap); } + + return dict; } xs_dict *xs_dict_set(xs_dict *dict, const xs_str *key, const xs_val *value) diff --git a/xs_regex.h b/xs_regex.h @@ -93,7 +93,7 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c /* replaces all matches with the rep string. If it contains unescaped &, they are replaced with the match */ { - xs_str *s = xs_str_new(NULL); + xs_str_bld b = { 0 }; xs *split = xs_regex_split_n(str, rx, count); const xs_val *v; int n = 0; @@ -107,7 +107,7 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c while (*p) { if (*p == '&') - s = xs_str_cat(s, v); + xs_str_bld_cat(&b, v); else { if (*p == '\\') p++; @@ -115,24 +115,24 @@ xs_list *xs_regex_replace_in(xs_str *str, const char *rx, const char *rep, int c if (!*p) break; - s = xs_append_m(s, p, 1); + xs_str_bld_cat(&b, (char[2]){ *p }); } p++; } } else - s = xs_str_cat(s, rep); + xs_str_bld_cat(&b, rep); } else - s = xs_str_cat(s, v); + xs_str_bld_cat(&b, v); n++; } xs_free(str); - return s; + return b.data; }