commit 3d9256337ad035a7ba1395530dbee3adb0fe9bdc
parent 88b9a3187c6f41b8a752a13c04478bf6b4ba0593
Author: Santtu Lakkala <santtu.lakkala@unikie.com>
Date: Fri, 21 Feb 2025 16:59:04 +0200
Add string builder
Diffstat:
M | format.c | | | 91 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
M | html.c | | | 14 | +++++++------- |
M | xs.h | | | 36 | +++++++++++++++++++++++++++++++++++- |
M | xs_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;
}