aboutsummaryrefslogtreecommitdiff
path: root/ui-shared.c
diff options
from:
to:
context:
space:
mode:
authorGravatar Saya Andy <saya.andy@posteo.com> 2026-08-01 13:15:38 +0700
committerGravatar Saya Andy <saya.andy@posteo.com> 2026-08-01 13:15:38 +0700
commit8a90bca75c8d8755016b46747ca943bb7b37140c (patch)
treee45289bb96aea9216cb33a550e13c3fe477fa2cd /ui-shared.c
parent793860dbd6df42d5bbf05542f2292a1c6d30fc76 (diff)
downloadcgitext-8a90bca75c8d8755016b46747ca943bb7b37140c.tar.gz
cgitext-8a90bca75c8d8755016b46747ca943bb7b37140c.zip
logo: include svg variant for both favicon and logov1.3.1+11
The reasoning behind using svg as primary defaults is simple – at close-up it is much neater, plus svg favicon is supported since 2015 in Firefox, 2020 in Chromium, 2025 in Safari. For old browsers ico is still kept as a fallback. The svg was auto-generated using a Python script and then simplified to a path, but kept close to an original.
Diffstat (limited to 'ui-shared.c')
-rw-r--r--ui-shared.c79
1 files changed, 74 insertions, 5 deletions
diff --git a/ui-shared.c b/ui-shared.c
index 97d1839..686fde0 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -7,6 +7,8 @@
* Implement ci link tab
* Check repo existence
* Replace memrchr() for macOS compatibility
+ * Allow multiple favicons of different mime types to be used
+ * Include favicon.svg as default
*
* Licensed under GNU General Public License v2
* (see COPYING for full license text)
@@ -774,6 +776,51 @@ void cgit_redirect(const char *url, bool permanent)
html("\n\n");
}
+static const struct {
+ const char *ext;
+ const char *mimetype;
+} favicon_mimetypes[] = {
+ { "ico", "image/x-icon" },
+ { "png", "image/png" },
+ { "svg", "image/svg+xml" },
+ { "gif", "image/gif" },
+ { "jpg", "image/jpeg" },
+ { "jpeg", "image/jpeg" },
+ { "webp", "image/webp" },
+ { "avif", "image/avif" },
+};
+
+/* Deduce the mimetype of a favicon from the extension of its url, ignoring
+ * any query string or fragment. Returns NULL for unknown extensions.
+ */
+static const char *favicon_mimetype(const char *url)
+{
+ const char *p, *end, *ext = NULL;
+ size_t len;
+ int i;
+
+ end = strpbrk(url, "?#");
+ if (!end)
+ end = url + strlen(url);
+
+ for (p = url; p < end; p++) {
+ if (*p == '/')
+ ext = NULL;
+ else if (*p == '.')
+ ext = p + 1;
+ }
+ if (!ext)
+ return NULL;
+ len = end - ext;
+
+ for (i = 0; i < ARRAY_SIZE(favicon_mimetypes); i++) {
+ if (strlen(favicon_mimetypes[i].ext) == len &&
+ !strncasecmp(favicon_mimetypes[i].ext, ext, len))
+ return favicon_mimetypes[i].mimetype;
+ }
+ return NULL;
+}
+
static void print_rel_vcs_link(const char *url)
{
html("<link rel='vcs-git' href='");
@@ -815,6 +862,27 @@ static int emit_js_link(struct string_list_item *s, void *arg)
return 0;
}
+static int emit_favicon_link(struct string_list_item *s, void *arg)
+{
+ const char *url = s ? s->string : (const char *)arg;
+ const char *mimetype;
+
+ /* Do not emit anything if favicon= is specified. */
+ if (!*url)
+ return 0;
+
+ html("<link rel='shortcut icon' href='");
+ html_attr(url);
+ mimetype = favicon_mimetype(url);
+ if (mimetype) {
+ html("' type='");
+ html_attr(mimetype);
+ }
+ html("'/>\n");
+
+ return 0;
+}
+
void cgit_print_docstart(void)
{
char *host = cgit_hosturl();
@@ -845,11 +913,12 @@ void cgit_print_docstart(void)
else
emit_js_link(NULL, "/cgit.js");
- if (ctx.cfg.favicon && *ctx.cfg.favicon) {
- html("<link rel='shortcut icon' href='");
- html_attr(ctx.cfg.favicon);
- html("'/>\n");
- }
+ if (ctx.cfg.favicon.items)
+ for_each_string_list(&ctx.cfg.favicon, emit_favicon_link, NULL);
+ else
+ emit_favicon_link(NULL, "/favicon.svg");
+ emit_favicon_link(NULL, "/favicon.ico");
+
if (host && ctx.repo && ctx.qry.head) {
char *fileurl;
struct strbuf sb = STRBUF_INIT;