diff options
| author | 2026-08-01 13:15:38 +0700 | |
|---|---|---|
| committer | 2026-08-01 13:15:38 +0700 | |
| commit | 8a90bca75c8d8755016b46747ca943bb7b37140c (patch) | |
| tree | e45289bb96aea9216cb33a550e13c3fe477fa2cd /ui-shared.c | |
| parent | 793860dbd6df42d5bbf05542f2292a1c6d30fc76 (diff) | |
| download | cgitext-1.3.1+11.tar.gz cgitext-1.3.1+11.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.c | 79 |
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; |