the repository which powers this website
Implement themed refs page
RunasSudo 10 months ago
parent 873e51f · commit e4b1081
-rw-r--r--cgit.mk3
-rw-r--r--themed/base.html114
-rw-r--r--themed/index.html31
-rw-r--r--themed/mincrypt_sha256.c184
-rw-r--r--themed/mincrypt_sha256.h115
-rw-r--r--themed/refs.html190
-rw-r--r--ui-refs.c17
-rw-r--r--ui-refs.h4
-rw-r--r--ui-shared.c37
-rw-r--r--ui-shared.h1
10 files changed, 669 insertions, 27 deletions
diff --git a/cgit.mk b/cgit.mk
index e582ca36..566a2adc 100644
--- a/cgit.mk
+++ b/cgit.mk
@@ -96,6 +96,7 @@ CGIT_OBJ_NAMES += ui-tag.o
CGIT_OBJ_NAMES += ui-tree.o
CGIT_OBJ_NAMES += themed/themed.o
+CGIT_OBJ_NAMES += themed/mincrypt_sha256.o
CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))
@@ -128,7 +129,7 @@ $(CGIT_PREFIX).depend:
$(CGIT_PREFIX)themed/.depend:
@mkdir -p $@
-$(CGIT_PREFIX)themed/themed.c: $(CGIT_PREFIX)themed/index.html
+$(CGIT_PREFIX)themed/themed.c: $(CGIT_PREFIX)themed/base.html $(CGIT_PREFIX)themed/index.html $(CGIT_PREFIX)themed/refs.html
cd $(CGIT_PREFIX)themed; python -m htmlcc $^ > $@
$(CGIT_PREFIX)CGIT-CFLAGS: FORCE
diff --git a/themed/base.html b/themed/base.html
new file mode 100644
index 00000000..5905df7b
--- /dev/null
+++ b/themed/base.html
@@ -0,0 +1,114 @@
+{% block page_start %}
+<!DOCTYPE html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>{{ ctx.page.title }}</title>{# ctx.page.title is usually set by prepare_repo_cmd #}
+ <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap">
+ <link rel="stylesheet" href="/style.css">
+ </head>
+ <body class="text-gray-900">
+{% endblock %}
+{% block repo_header %}
+ <header class="bg-gray-50 border-b border-gray-300">{# Repo header #}
+ <div class="max-w-[1280px] mx-auto py-4 flex gap-x-1">
+ {# Heroicons outline cube #}
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6"><path stroke-linecap="round" stroke-linejoin="round" d="m21 7.5-9-5.25L3 7.5m18 0-9 5.25m9-5.25v9l-9 5.25M3 7.5l9 5.25M3 7.5v9l9 5.25m0-9v9" /></svg>
+ <div class="text-lg"><a href="{{ cgit_rooturl()|attr }}" class="hover:text-blue-600 hover:underline">index</a> / <a href="{! cgit_shared_repolink_url(NULL, NULL, NULL); !}" class="font-semibold hover:text-blue-600 hover:underline">{{ ctx.repo->name }}</a></div>
+ </div>
+ </header>
+{% endblock %}
+{% block page_end %}
+ <footer class="border-t border-gray-300">
+ {# Footer panel #}
+ <div class="max-w-[1280px] mx-auto py-4">
+ <div class="text-sm text-gray-500">
+ {# cgit footer #}
+ generated by <a href="https://git.zx2c4.com/cgit/about/" class="hover:text-blue-600 hover:underline">cgit {{ cgit_version }}</a> (<a href="https://git-scm.com/" class="hover:text-blue-600 hover:underline">git {{ git_version_string }}</a>) at
+ {{ show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)) }}
+ </div>
+ </div>
+ </footer>
+ </body>
+</html>
+{% endblock %}
+
+{!
+ static int get_num_commits() {
+ int num_commits = 0;
+
+ // Based on ui-stats collect_stats
+ struct rev_info rev;
+ struct commit *commit;
+
+ const char *argv[] = {NULL, ctx.qry.head};
+ int argc = 2;
+
+ repo_init_revisions(the_repository, &rev, NULL);
+ rev.abbrev = DEFAULT_ABBREV;
+ rev.commit_format = CMIT_FMT_DEFAULT;
+ rev.max_parents = 1;
+ rev.verbose_header = 1;
+ rev.show_root_diff = 0;
+ setup_revisions(argc, argv, &rev, NULL);
+ prepare_revision_walk(&rev);
+ while ((commit = get_revision(&rev)) != NULL) {
+ // Process the commit
+ num_commits++;
+
+ release_commit_memory(the_repository->parsed_objects, commit);
+ commit->parents = NULL;
+ }
+ return num_commits;
+ }
+
+ static int get_num_branches() {
+ struct reflist list;
+ list.refs = NULL;
+ list.alloc = list.count = 0;
+ refs_for_each_branch_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
+ int num_branches = list.count;
+ cgit_free_reflist_inner(&list);
+ return num_branches;
+ }
+
+ static int get_num_tags() {
+ struct reflist list;
+ list.refs = NULL;
+ list.alloc = list.count = 0;
+ refs_for_each_tag_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
+ int num_tags = list.count;
+ cgit_free_reflist_inner(&list);
+ return num_tags;
+ }
+!}
+
+{!
+ #include "mincrypt_sha256.h"
+
+ static void gravatar_url(char *email) {
+ // Trim email of whitespace, < and >
+ while (isspace(*email) || *email == '<') email++;
+ char* strend = email + strlen(email) - 1;
+ while (isspace(*strend) || *strend == '>') strend--; // Now strend points to the last character
+
+ // Email to lowercase
+ size_t email_len = strend - email + 1;
+ char *email_lower = malloc(email_len + 1); // +1 for null terminator
+ for (int i = 0; i < email_len; i++) { email_lower[i] = tolower(email[i]); }
+ email_lower[email_len] = '\0'; // For good measure
+
+ // Compute hash and print Gravatar link
+ uint8_t digest[32];
+ SHA256_hash(email, strend - email + 1, digest);
+ htmlf(
+ "https://gravatar.com/avatar/%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
+ digest[0], digest[1], digest[2], digest[3], digest[4], digest[5], digest[6], digest[7],
+ digest[8], digest[9], digest[10], digest[11], digest[12], digest[13], digest[14], digest[15],
+ digest[16], digest[17], digest[18], digest[19], digest[20], digest[21], digest[22], digest[23],
+ digest[24], digest[25], digest[26], digest[27], digest[28], digest[29], digest[30], digest[31]
+ );
+
+ free(email_lower);
+ }
+!}
diff --git a/themed/index.html b/themed/index.html
index 6933898c..6e88d435 100644
--- a/themed/index.html
+++ b/themed/index.html
@@ -4,24 +4,16 @@
{% page cgit_print_repolist %}
{! ctx.page.title = ctx.cfg.root_title; !}
-<!DOCTYPE html>
-<html>
- <head>
- <meta charset="utf-8">
- <title>{{ ctx.cfg.root_title }}</title>
- <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wght@0,100..900;1,100..900&display=swap">
- <link rel="stylesheet" href="/style.css">
- </head>
- <body class="text-gray-900">
+{! page_start(); !}
<header class="bg-gray-50 border-b border-gray-300">
{# Repo header #}
<div class="max-w-[1280px] mx-auto py-4 flex gap-x-1">
- <a href="{! cgit_shared_site_url(NULL, NULL, NULL, 0, 1); !}" class="text-lg hover:underline">{{ ctx.cfg.root_title }}</a>
+ <a href="{{ cgit_rooturl()|attr }}" class="text-lg hover:underline">{{ ctx.cfg.root_title }}</a>
</div>
</header>
<main class="max-w-[1280px] mx-auto py-4">
{# Main content #}
- <form method="GET" action="{! cgit_shared_site_url(NULL, NULL, NULL, 0, 1); !}" class="flex text-sm mb-4 outline-1 outline-gray-300 rounded-lg has-[input:focus-within]:outline-2 has-[input:focus-within]:outline-blue-600">
+ <form method="GET" action="{{ cgit_rooturl()|attr }}" class="flex text-sm mb-4 outline-1 outline-gray-300 rounded-lg has-[input:focus-within]:outline-2 has-[input:focus-within]:outline-blue-600">
{# Search box #}
<input name="q" value="{{ ctx.qry.search|attr }}" type="text" placeholder="Search repos&hellip;" class="flex-1 py-1.5 pl-2 text-sm focus:outline-none">
<button class="py-1.5 px-2 cursor-pointer" action="submit" value="search">
@@ -31,7 +23,7 @@
</form>
<div class="grid grid-cols-[auto_1fr]">
{# Repo list #}
- {! for (int i = 0; i < cgit_repolist.count; i++) { !}
+ {% for int i = 0; i < cgit_repolist.count; i++ %}
{! ctx.repo = &cgit_repolist.repos[i]; !}
{! if (!cgit_repolist_is_visible(ctx.repo)) { continue; } !}
<div{% if i > 0 %} class="border-t border-gray-300 pt-4 mt-4"{% endif %}>
@@ -45,19 +37,8 @@
<div class="text-gray-500">{{ ctx.repo->desc }}</div>
<div class="text-gray-500">Updated {! cgit_repolist_print_modtime(ctx.repo); !} ago</div>
</div>
- {! } !}
+ {% endfor %}
</div>
</main>
- <footer class="border-t border-gray-300">
- {# Footer panel #}
- <div class="max-w-[1280px] mx-auto py-4">
- <div class="text-sm text-gray-500">
- {# cgit footer #}
- generated by <a href="https://git.zx2c4.com/cgit/about/" class="hover:text-blue-600 hover:underline">cgit {{ cgit_version }}</a> (<a href="https://git-scm.com/" class="hover:text-blue-600 hover:underline">git {{ git_version_string }}</a>) at
- {{ show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)) }}
- </div>
- </div>
- </footer>
- </body>
-</html>
+{! page_end(); !}
{% endpage %}
diff --git a/themed/mincrypt_sha256.c b/themed/mincrypt_sha256.c
new file mode 100644
index 00000000..540e1a14
--- /dev/null
+++ b/themed/mincrypt_sha256.c
@@ -0,0 +1,184 @@
+/* sha256.c
+**
+** Copyright 2013, The Android Open Source Project
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** * Neither the name of Google Inc. nor the names of its contributors may
+** be used to endorse or promote products derived from this software
+** without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+** MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+** EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+** PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+** OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+** OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+** ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+// Optimized for minimal code size.
+
+#include "mincrypt_sha256.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#define ror(value, bits) (((value) >> (bits)) | ((value) << (32 - (bits))))
+#define shr(value, bits) ((value) >> (bits))
+
+static const uint32_t K[64] = {
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+ 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+ 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+ 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+ 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+ 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+ 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+ 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+ 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 };
+
+static void SHA256_Transform(mincrypt_SHA256_CTX* ctx) {
+ uint32_t W[64];
+ uint32_t A, B, C, D, E, F, G, H;
+ uint8_t* p = ctx->buf;
+ int t;
+
+ for(t = 0; t < 16; ++t) {
+ uint32_t tmp = *p++ << 24;
+ tmp |= *p++ << 16;
+ tmp |= *p++ << 8;
+ tmp |= *p++;
+ W[t] = tmp;
+ }
+
+ for(; t < 64; t++) {
+ uint32_t s0 = ror(W[t-15], 7) ^ ror(W[t-15], 18) ^ shr(W[t-15], 3);
+ uint32_t s1 = ror(W[t-2], 17) ^ ror(W[t-2], 19) ^ shr(W[t-2], 10);
+ W[t] = W[t-16] + s0 + W[t-7] + s1;
+ }
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+ E = ctx->state[4];
+ F = ctx->state[5];
+ G = ctx->state[6];
+ H = ctx->state[7];
+
+ for(t = 0; t < 64; t++) {
+ uint32_t s0 = ror(A, 2) ^ ror(A, 13) ^ ror(A, 22);
+ uint32_t maj = (A & B) ^ (A & C) ^ (B & C);
+ uint32_t t2 = s0 + maj;
+ uint32_t s1 = ror(E, 6) ^ ror(E, 11) ^ ror(E, 25);
+ uint32_t ch = (E & F) ^ ((~E) & G);
+ uint32_t t1 = H + s1 + ch + K[t] + W[t];
+
+ H = G;
+ G = F;
+ F = E;
+ E = D + t1;
+ D = C;
+ C = B;
+ B = A;
+ A = t1 + t2;
+ }
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+ ctx->state[4] += E;
+ ctx->state[5] += F;
+ ctx->state[6] += G;
+ ctx->state[7] += H;
+}
+
+static const HASH_VTAB SHA256_VTAB = {
+ SHA256_init,
+ SHA256_update,
+ SHA256_final,
+ SHA256_hash,
+ SHA256_DIGEST_SIZE
+};
+
+void SHA256_init(mincrypt_SHA256_CTX* ctx) {
+ ctx->f = &SHA256_VTAB;
+ ctx->state[0] = 0x6a09e667;
+ ctx->state[1] = 0xbb67ae85;
+ ctx->state[2] = 0x3c6ef372;
+ ctx->state[3] = 0xa54ff53a;
+ ctx->state[4] = 0x510e527f;
+ ctx->state[5] = 0x9b05688c;
+ ctx->state[6] = 0x1f83d9ab;
+ ctx->state[7] = 0x5be0cd19;
+ ctx->count = 0;
+}
+
+
+void SHA256_update(mincrypt_SHA256_CTX* ctx, const void* data, int len) {
+ int i = (int) (ctx->count & 63);
+ const uint8_t* p = (const uint8_t*)data;
+
+ ctx->count += len;
+
+ while (len--) {
+ ctx->buf[i++] = *p++;
+ if (i == 64) {
+ SHA256_Transform(ctx);
+ i = 0;
+ }
+ }
+}
+
+
+const uint8_t* SHA256_final(mincrypt_SHA256_CTX* ctx) {
+ uint8_t *p = ctx->buf;
+ uint64_t cnt = ctx->count * 8;
+ int i;
+
+ SHA256_update(ctx, (uint8_t*)"\x80", 1);
+ while ((ctx->count & 63) != 56) {
+ SHA256_update(ctx, (uint8_t*)"\0", 1);
+ }
+ for (i = 0; i < 8; ++i) {
+ uint8_t tmp = (uint8_t) (cnt >> ((7 - i) * 8));
+ SHA256_update(ctx, &tmp, 1);
+ }
+
+ for (i = 0; i < 8; i++) {
+ uint32_t tmp = ctx->state[i];
+ *p++ = tmp >> 24;
+ *p++ = tmp >> 16;
+ *p++ = tmp >> 8;
+ *p++ = tmp >> 0;
+ }
+
+ return ctx->buf;
+}
+
+/* Convenience function */
+const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest) {
+ mincrypt_SHA256_CTX ctx;
+ SHA256_init(&ctx);
+ SHA256_update(&ctx, data, len);
+ memcpy(digest, SHA256_final(&ctx), SHA256_DIGEST_SIZE);
+ return digest;
+}
diff --git a/themed/mincrypt_sha256.h b/themed/mincrypt_sha256.h
new file mode 100644
index 00000000..f2dc24fa
--- /dev/null
+++ b/themed/mincrypt_sha256.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2007 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+struct HASH_CTX; // forward decl
+
+typedef struct HASH_VTAB {
+ void (* const init)(struct HASH_CTX*);
+ void (* const update)(struct HASH_CTX*, const void*, int);
+ const uint8_t* (* const final)(struct HASH_CTX*);
+ const uint8_t* (* const hash)(const void*, int, uint8_t*);
+ int size;
+} HASH_VTAB;
+
+typedef struct HASH_CTX {
+ const HASH_VTAB * f;
+ uint64_t count;
+ uint8_t buf[64];
+ uint32_t state[8]; // upto SHA2
+} HASH_CTX;
+
+#define HASH_init(ctx) (ctx)->f->init(ctx)
+#define HASH_update(ctx, data, len) (ctx)->f->update(ctx, data, len)
+#define HASH_final(ctx) (ctx)->f->final(ctx)
+#define HASH_hash(data, len, digest) (ctx)->f->hash(data, len, digest)
+#define HASH_size(ctx) (ctx)->f->size
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_HASH_INTERNAL_H_
+
+/*
+ * Copyright 2011 The Android Open Source Project
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of Google Inc. nor the names of its contributors may
+ * be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Google Inc. ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL Google Inc. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
+#define SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+typedef HASH_CTX mincrypt_SHA256_CTX;
+
+void SHA256_init(mincrypt_SHA256_CTX* ctx);
+void SHA256_update(mincrypt_SHA256_CTX* ctx, const void* data, int len);
+const uint8_t* SHA256_final(mincrypt_SHA256_CTX* ctx);
+
+// Convenience method. Returns digest address.
+const uint8_t* SHA256_hash(const void* data, int len, uint8_t* digest);
+
+#define SHA256_DIGEST_SIZE 32
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+#endif // SYSTEM_CORE_INCLUDE_MINCRYPT_SHA256_H_
diff --git a/themed/refs.html b/themed/refs.html
new file mode 100644
index 00000000..d0ef6fd1
--- /dev/null
+++ b/themed/refs.html
@@ -0,0 +1,190 @@
+{! int cgit_refs_cmp_branch_age(const void *a, const void *b); !}
+{! int cgit_refs_cmp_ref_name(const void *a, const void *b); !}
+{! int cgit_refs_cmp_tag_age(const void *a, const void *b); !}
+
+{% page cgit_print_refs %}
+{! page_start(); !}
+{! repo_header(); !}
+ <main class="max-w-[1280px] mx-auto py-4">{# Main content #}
+ <div class="mb-4">
+ {# Description panel #}
+ {{ ctx.repo->desc }}
+ </div>
+ <nav class="flex text-sm mb-4">
+ {# Repo navigation panel #}
+ <a href="{! cgit_shared_repolink_url("refs", NULL, NULL); !}" class="flex gap-x-1.5 py-1.5 px-3 bg-gray-50 border border-gray-300 rounded-md hover:bg-gray-100">
+ {# Heroicons micro list-bullet #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path d="M3 4.75a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM6.25 3a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 7.25a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 11.5a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM4 12.25a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM3 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" /></svg>
+ <span class="font-semibold self-baseline">{{ ctx.qry.head }}</span>
+ {# Heroicons micro chevron-down #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
+ </a>
+ <a href="{! cgit_shared_repolink_url("log", NULL, NULL); !}" class="flex gap-x-1 py-1.5 px-3 ml-3 rounded-md hover:bg-gray-100">
+ {# Heroicons micro clock #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path fill-rule="evenodd" d="M1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Zm7.75-4.25a.75.75 0 0 0-1.5 0V8c0 .414.336.75.75.75h3.25a.75.75 0 0 0 0-1.5h-2.5v-3.5Z" clip-rule="evenodd" /></svg>
+ {! int num_commits = get_num_commits(); !}
+ <span class="font-semibold">{{ num_commits|%d }}</span><span class="font-semibold text-gray-500">Commit{% if num_commits != 1 %}s{% endif %}</span>
+ </a>
+ <a href="{! cgit_shared_repolink_url("refs", NULL, "heads"); !}" class="flex gap-x-1 py-1.5 px-3 rounded-md hover:bg-gray-100">
+ {# Heroicons micro list-bullet #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path d="M3 4.75a1 1 0 1 0 0-2 1 1 0 0 0 0 2ZM6.25 3a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 7.25a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM6.25 11.5a.75.75 0 0 0 0 1.5h7a.75.75 0 0 0 0-1.5h-7ZM4 12.25a1 1 0 1 1-2 0 1 1 0 0 1 2 0ZM3 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" /></svg>
+ {! int num_branches = get_num_branches(); !}
+ <span class="font-semibold">{{ num_branches|%d }}</span><span class="font-semibold text-gray-500">Branch{% if num_branches != 1 %}es{% endif %}</span>
+ </a>
+ <a href="{! cgit_shared_repolink_url("refs", NULL, "tags"); !}" class="flex gap-x-1 py-1.5 px-3 rounded-md hover:bg-gray-100">
+ {# Heroicons micro tag #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center text-gray-500"><path fill-rule="evenodd" d="M4.5 2A2.5 2.5 0 0 0 2 4.5v2.879a2.5 2.5 0 0 0 .732 1.767l4.5 4.5a2.5 2.5 0 0 0 3.536 0l2.878-2.878a2.5 2.5 0 0 0 0-3.536l-4.5-4.5A2.5 2.5 0 0 0 7.38 2H4.5ZM5 6a1 1 0 1 0 0-2 1 1 0 0 0 0 2Z" clip-rule="evenodd" /></svg>
+ {! int num_tags = get_num_tags(); !}
+ <span class="font-semibold">{{ num_tags|%d }}</span><span class="font-semibold text-gray-500">Tag{% if num_tags != 1 %}s{% endif %}</span>
+ </a>
+ <div class="flex-1"></div>
+ <div class="flex outline-1 outline-gray-300 rounded-lg has-[input:focus-within]:outline-2 has-[input:focus-within]:outline-blue-600">
+ {# Search box #}
+ <select class="py-1.5 px-2 text-sm text-gray-500 focus:outline-none">
+ <option>Log msg</option>
+ <option>Author</option>
+ <option>Committer</option>
+ <option>Range</option>
+ </select>
+ <input type="text" placeholder="Search commits&hellip;" class="py-1.5 pl-2 text-sm focus:outline-none">
+ <button class="py-1.5 px-2 cursor-pointer">
+ {# Heroicons micro magnifying-glass #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="text-gray-700 size-4"><path fill-rule="evenodd" d="M9.965 11.026a5 5 0 1 1 1.06-1.06l2.755 2.754a.75.75 0 1 1-1.06 1.06l-2.755-2.754ZM10.5 7a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0Z" clip-rule="evenodd" /></svg>
+ </button>
+ </div>
+ <div class="flex relative">
+ {# Code box and panel #}
+ <button class="flex gap-x-1.5 py-1.5 px-3 ml-3 bg-blue-500 text-white rounded-md cursor-pointer hover:bg-blue-600 peer">
+ {# Code box #}
+ {# Heroicons micro code-bracket #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center"><path fill-rule="evenodd" d="M4.78 4.97a.75.75 0 0 1 0 1.06L2.81 8l1.97 1.97a.75.75 0 1 1-1.06 1.06l-2.5-2.5a.75.75 0 0 1 0-1.06l2.5-2.5a.75.75 0 0 1 1.06 0ZM11.22 4.97a.75.75 0 0 0 0 1.06L13.19 8l-1.97 1.97a.75.75 0 1 0 1.06 1.06l2.5-2.5a.75.75 0 0 0 0-1.06l-2.5-2.5a.75.75 0 0 0-1.06 0ZM8.856 2.008a.75.75 0 0 1 .636.848l-1.5 10.5a.75.75 0 0 1-1.484-.212l1.5-10.5a.75.75 0 0 1 .848-.636Z" clip-rule="evenodd" /></svg>
+ <span class="font-semibold">Code</span>
+ {# Heroicons micro chevron-down #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 self-center"><path fill-rule="evenodd" d="M4.22 6.22a.75.75 0 0 1 1.06 0L8 8.94l2.72-2.72a.75.75 0 1 1 1.06 1.06l-3.25 3.25a.75.75 0 0 1-1.06 0L4.22 7.28a.75.75 0 0 1 0-1.06Z" clip-rule="evenodd" /></svg>
+ </button>
+ <div class="absolute top-10 right-0 w-[300px] border border-gray-300 rounded-md bg-white p-2 hidden peer-focus:block hover:block">
+ {# Code panel #}
+ <div class="flex gap-x-1 items-center mb-2">
+ {# Heroicons micro command-line #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path fill-rule="evenodd" d="M2 4a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4Zm2.22 1.97a.75.75 0 0 0 0 1.06l.97.97-.97.97a.75.75 0 1 0 1.06 1.06l1.5-1.5a.75.75 0 0 0 0-1.06l-1.5-1.5a.75.75 0 0 0-1.06 0ZM8.75 8.5a.75.75 0 0 0 0 1.5h2.5a.75.75 0 0 0 0-1.5h-2.5Z" clip-rule="evenodd" /></svg>
+ <span class="font-semibold text-sm">Clone</span>
+ </div>
+ <div class="flex outline-1 outline-gray-300 rounded-lg mb-2 has-[input:focus-within]:outline-2 has-[input:focus-within]:outline-blue-600">
+ {# Clone URL box #}
+ <span class="py-1.5 px-2 text-sm text-gray-500 bg-gray-50 border-r border-gray-300">
+ HTTPS
+ </span>
+ <input type="text" value="{! cgit_add_clone_urls(html_attr); !}" class="repo-clone-url flex-1 py-1.5 pl-2 text-sm focus:outline-none" onfocus="this.select();" readonly>
+ <button class="py-1.5 px-2 cursor-pointer" onclick="document.querySelector('.repo-clone-url').select();document.execCommand('copy');">
+ {# Heroicons micro clipboard #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4 text-gray-500"><path fill-rule="evenodd" d="M10.986 3H12a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h1.014A2.25 2.25 0 0 1 7.25 1h1.5a2.25 2.25 0 0 1 2.236 2ZM9.5 4v-.75a.75.75 0 0 0-.75-.75h-1.5a.75.75 0 0 0-.75.75V4h3Z" clip-rule="evenodd" /></svg>
+ </button>
+ </div>
+ <a href="vscode://vscode.git/clone?url={! cgit_add_clone_urls(html_url_arg); !}" class="flex items-center gap-x-1 hover:text-blue-600 hover:underline mb-2">
+ {# Heroicons micro arrow-top-right-on-square #}
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z" /><path d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z" /></svg>
+ Open with VS Code
+ </a>
+ <a href="vscodium://vscode.git/clone?url={! cgit_add_clone_urls(html_url_arg); !}" class="flex items-center gap-x-1 hover:text-blue-600 hover:underline mb-2">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z" /><path d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z" /></svg>
+ Open with VSCodium
+ </a>
+ <a href="jetbrains://idea/checkout/git?idea.required.plugins.id=Git4Idea&checkout.repo={! cgit_add_clone_urls(html_url_arg); !}" class="flex items-center gap-x-1 hover:text-blue-600 hover:underline">
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4"><path d="M6.22 8.72a.75.75 0 0 0 1.06 1.06l5.22-5.22v1.69a.75.75 0 0 0 1.5 0v-3.5a.75.75 0 0 0-.75-.75h-3.5a.75.75 0 0 0 0 1.5h1.69L6.22 8.72Z" /><path d="M3.5 6.75c0-.69.56-1.25 1.25-1.25H7A.75.75 0 0 0 7 4H4.75A2.75 2.75 0 0 0 2 6.75v4.5A2.75 2.75 0 0 0 4.75 14h4.5A2.75 2.75 0 0 0 12 11.25V9a.75.75 0 0 0-1.5 0v2.25c0 .69-.56 1.25-1.25 1.25h-4.5c-.69 0-1.25-.56-1.25-1.25v-4.5Z" /></svg>
+ Open with IntelliJ IDEA
+ </a>
+ </div>
+ </div>
+ </nav>
+ {% if !ctx.qry.path || starts_with(ctx.qry.path, "heads") %}
+ {!
+ struct reflist list;
+ list.refs = NULL;
+ list.alloc = list.count = 0;
+ refs_for_each_branch_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
+ qsort(list.refs, list.count, sizeof(*list.refs), cgit_refs_cmp_branch_age);
+ if (ctx.repo->branch_sort == 0) {
+ qsort(list.refs, list.count, sizeof(*list.refs), cgit_refs_cmp_ref_name);
+ }
+ !}
+ <div class="grid grid-cols-1 border border-gray-300 rounded-md divide-y divide-gray-300">
+ {# Branches list #}
+ <div class="rounded-t-md bg-gray-50 px-3 py-2 font-semibold text-sm">
+ Branches
+ </div>
+ {% for int i = 0; i < list.count; i++ %}
+ {! struct refinfo *ref = list.refs[i]; !}
+ {! struct commitinfo *info = ref->commit; !}
+ {! char *name = (char *)ref->refname; !}
+ {! if (!info) { continue; } !}
+ <div class="px-3 py-2">
+ <div><a href="{! cgit_shared_repolink_url(NULL, name, NULL); !}" class="text-blue-500 hover:text-blue-600 hover:underline">{{ name }}</a></div>
+ {% if ref->object->type == OBJ_COMMIT %}
+ <div class="text-sm text-gray-500 flex gap-x-1">
+ {{ info->subject }} &middot; Updated {! cgit_print_age(info->committer_date, info->committer_tz, -1); !} ago
+ <img src="{! gravatar_url(info->author_email); !}?s=24" class="mt-[-0.2rem]">
+ {{ info->author }}
+ </div>
+ {% endif %}
+ </div>
+ {% endfor %}
+ </div>
+ {! cgit_free_reflist_inner(&list); !}
+ {% endif %}
+ {% if !ctx.qry.path || starts_with(ctx.qry.path, "tags") %}
+ {!
+ struct reflist list;
+ list.refs = NULL;
+ list.alloc = list.count = 0;
+ refs_for_each_tag_ref(get_main_ref_store(the_repository), cgit_refs_cb, &list);
+ !}
+ {% if list.count > 0 %}
+ {! qsort(list.refs, list.count, sizeof(*list.refs), cgit_refs_cmp_tag_age); !}
+ <div class="grid grid-cols-1 border border-gray-300 rounded-md divide-y divide-gray-300 mt-4">
+ {# Tags list #}
+ <div class="rounded-t-md bg-gray-50 px-3 py-2 font-semibold text-sm">
+ Tags
+ </div>
+ {% for int i = 0; i < list.count; i++ %}
+ {!
+ struct refinfo *ref = list.refs[i];
+ struct tag *tag = NULL;
+ struct taginfo *info = NULL;
+ char *name = (char *)ref->refname;
+ struct object *obj = ref->object;
+
+ if (obj->type == OBJ_TAG) {
+ tag = (struct tag *)obj;
+ obj = tag->tagged;
+ info = ref->tag;
+ if (!info) { continue; }
+ }
+ !}
+ <div class="px-3 py-2">
+ <div><a href="{! cgit_shared_repolink_url(NULL, name, NULL); !}" class="text-blue-500 hover:text-blue-600 hover:underline">{{ name }}</a></div>
+ {% if info && (info->tagger_date > 0 || info->tagger) %}
+ <div class="text-sm text-gray-500 flex gap-x-1">
+ {% if info->tagger_date > 0 %}
+ Updated {! cgit_print_age(info->tagger_date, info->tagger_tz, -1); !} ago
+ {% endif %}
+ {% if info->tagger %}
+ <img src="{! gravatar_url(info->tagger_email); !}?s=24" class="mt-[-0.2rem]">
+ {{ info->tagger }}
+ {% endif %}
+ </div>
+ {% elif ref->object->type == OBJ_COMMIT %}
+ <div class="text-sm text-gray-500 flex gap-x-1">
+ Updated {! cgit_print_age(ref->commit->commit->date, 0, -1); !} ago
+ <img src="{! gravatar_url(ref->commit->author_email); !}?s=24" class="mt-[-0.2rem]">
+ {{ ref->commit->author }}
+ </div>
+ {% endif %}
+ </div>
+ {% endfor %}
+ </div>
+ {! cgit_free_reflist_inner(&list); !}
+ {% endif %}{# if list.count > 0 #}
+ {% endif %}{# if !ctx.qry.path || starts_with(ctx.qry.path, "tags") #}
+ </main>
+{! page_end(); !}
+{% endpage %}
diff --git a/ui-refs.c b/ui-refs.c
index 11fb9fc0..54b82835 100644
--- a/ui-refs.c
+++ b/ui-refs.c
@@ -27,6 +27,11 @@ static int cmp_ref_name(const void *a, const void *b)
return strcmp(r1->refname, r2->refname);
}
+int cgit_refs_cmp_ref_name(const void *a, const void *b)
+{
+ return cmp_ref_name(a, b);
+}
+
static int cmp_branch_age(const void *a, const void *b)
{
struct refinfo *r1 = *(struct refinfo **)a;
@@ -35,6 +40,11 @@ static int cmp_branch_age(const void *a, const void *b)
return cmp_age(r1->commit->committer_date, r2->commit->committer_date);
}
+int cgit_refs_cmp_branch_age(const void *a, const void *b)
+{
+ return cmp_branch_age(a, b);
+}
+
static int get_ref_age(struct refinfo *ref)
{
if (!ref->object)
@@ -56,6 +66,11 @@ static int cmp_tag_age(const void *a, const void *b)
return cmp_age(get_ref_age(r1), get_ref_age(r2));
}
+int cgit_refs_cmp_tag_age(const void *a, const void *b)
+{
+ return cmp_tag_age(a, b);
+}
+
static int print_branch(struct refinfo *ref)
{
struct commitinfo *info = ref->commit;
@@ -205,7 +220,7 @@ void cgit_print_tags(int maxcount)
cgit_free_reflist_inner(&list);
}
-void cgit_print_refs(void)
+void _orig_cgit_print_refs(void)
{
cgit_print_layout_start();
html("<table class='list nowrap'>");
diff --git a/ui-refs.h b/ui-refs.h
index 1d4a54a2..c2ab68ac 100644
--- a/ui-refs.h
+++ b/ui-refs.h
@@ -1,6 +1,10 @@
#ifndef UI_REFS_H
#define UI_REFS_H
+extern int cgit_refs_cmp_branch_age(const void *a, const void *b);
+extern int cgit_refs_cmp_ref_name(const void *a, const void *b);
+extern int cgit_refs_cmp_tag_age(const void *a, const void *b);
+
extern void cgit_print_branches(int maxcount);
extern void cgit_print_tags(int maxcount);
extern void cgit_print_refs(void);
diff --git a/ui-shared.c b/ui-shared.c
index 5853a969..8922e7d3 100644
--- a/ui-shared.c
+++ b/ui-shared.c
@@ -318,6 +318,43 @@ static char *repolink(const char *title, const char *class, const char *page,
return fmt("%s", delim);
}
+void cgit_shared_repolink_url(const char *page, const char *head, const char *path)
+{
+ char *delim = "?";
+
+ if (ctx.cfg.virtual_root) {
+ html_url_path(ctx.cfg.virtual_root);
+ html_url_path(ctx.repo->url);
+ if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
+ html("/");
+ if (page) {
+ html_url_path(page);
+ html("/");
+ if (path)
+ html_url_path(path);
+ }
+ } else {
+ html_url_path(ctx.cfg.script_name);
+ html("?url=");
+ html_url_arg(ctx.repo->url);
+ if (ctx.repo->url[strlen(ctx.repo->url) - 1] != '/')
+ html("/");
+ if (page) {
+ html_url_arg(page);
+ html("/");
+ if (path)
+ html_url_arg(path);
+ }
+ delim = "&amp;";
+ }
+ if (head && ctx.repo->defbranch && strcmp(head, ctx.repo->defbranch)) {
+ html(delim);
+ html("h=");
+ html_url_arg(head);
+ delim = "&amp;";
+ }
+}
+
static void reporevlink(const char *page, const char *name, const char *title,
const char *class, const char *head, const char *rev,
const char *path)
diff --git a/ui-shared.h b/ui-shared.h
index ce05a9b5..8f96c286 100644
--- a/ui-shared.h
+++ b/ui-shared.h
@@ -1,6 +1,7 @@
#ifndef UI_SHARED_H
#define UI_SHARED_H
+extern void cgit_shared_repolink_url(const char *page, const char *head, const char *path);
extern void cgit_shared_site_url(const char *page, const char *search, const char *sort, int ofs, int always_root);
extern const char *cgit_httpscheme(void);