commit c6e814bc389c90eef887b075d5790fc45c99f8e9
parent d429e285091db54f4b2d2bea157bd28a28355a93
Author: Chris Bracken <chris@bracken.jp>
Date: Fri, 20 Feb 2026 20:18:45 +0900
git: extract special files list (README, etc.)
Diffstat:
8 files changed, 64 insertions(+), 55 deletions(-)
diff --git a/src/git/repo.c b/src/git/repo.c
@@ -24,6 +24,7 @@
#include <unistd.h>
#include "git/internal.h"
+#include "third_party/openbsd/reallocarray.h"
#include "utils.h"
/* Local const data */
@@ -167,15 +168,35 @@ void gitrepo_load_filesystem_metadata(GitRepo* repo,
free(url_path);
}
+static void gitrepo_add_special_file(GitRepo* repo,
+ const char* label,
+ const char* path) {
+ if (!path || path[0] == '\0') {
+ return;
+ }
+ repo->special_files = reallocarray(
+ repo->special_files, repo->special_files_len + 1, sizeof(RepoSpecialFile));
+ if (!repo->special_files) {
+ err(1, "reallocarray");
+ }
+ repo->special_files[repo->special_files_len].label = estrdup(label);
+ repo->special_files[repo->special_files_len].path = estrdup(path);
+ repo->special_files_len++;
+}
+
static void gitrepo_load_git_metadata(GitRepo* repo, git_repository* git_repo) {
assert(repo != NULL);
assert(git_repo != NULL);
- repo->submodules = estrdup(
- gitrepo_has_blob(git_repo, "HEAD:.gitmodules") ? ".gitmodules" : "");
- repo->readme =
- estrdup(gitrepo_first_matching_file(git_repo, kReadmes, kReadmesLen));
- repo->license =
- estrdup(gitrepo_first_matching_file(git_repo, kLicenses, kLicensesLen));
+
+ if (gitrepo_has_blob(git_repo, "HEAD:.gitmodules")) {
+ gitrepo_add_special_file(repo, "Submodules", ".gitmodules");
+ }
+ gitrepo_add_special_file(
+ repo, "README",
+ gitrepo_first_matching_file(git_repo, kReadmes, kReadmesLen));
+ gitrepo_add_special_file(
+ repo, "LICENSE",
+ gitrepo_first_matching_file(git_repo, kLicenses, kLicensesLen));
repo->last_commit_time = 0;
git_object* obj = NULL;
@@ -225,11 +246,14 @@ void gitrepo_free(GitRepo* repo) {
repo->description = NULL;
free(repo->clone_url);
repo->clone_url = NULL;
- free(repo->submodules);
- repo->submodules = NULL;
- free(repo->readme);
- repo->readme = NULL;
- free(repo->license);
- repo->license = NULL;
+
+ for (size_t i = 0; i < repo->special_files_len; i++) {
+ free(repo->special_files[i].label);
+ free(repo->special_files[i].path);
+ }
+ free(repo->special_files);
+ repo->special_files = NULL;
+ repo->special_files_len = 0;
+
free(repo);
}
diff --git a/src/git/repo.h b/src/git/repo.h
@@ -1,17 +1,24 @@
#ifndef GOUT_GIT_REPO_H_
#define GOUT_GIT_REPO_H_
+#include <stddef.h>
#include <time.h>
+typedef struct RepoSpecialFile {
+ char* label;
+ char* path;
+} RepoSpecialFile;
+
typedef struct GitRepo {
char* name;
char* short_name;
char* owner;
char* description;
char* clone_url;
- char* submodules;
- char* readme;
- char* license;
+
+ RepoSpecialFile* special_files;
+ size_t special_files_len;
+
time_t last_commit_time;
} GitRepo;
diff --git a/src/writer/gopher/page.c b/src/writer/gopher/page.c
@@ -72,22 +72,12 @@ void gopher_page_begin(GopherPage* page) {
fprintf(out, "[1|Files|%sfiles.gph|server|port]\n", page->relpath);
fprintf(out, "[1|Refs|%srefs.gph|server|port]\n", page->relpath);
- const char* submodules = page->repo->submodules;
- if (submodules && submodules[0] != '\0') {
- fprintf(out, "[1|Submodules|%sfile/", page->relpath);
- print_gopher_link(out, submodules);
- fprintf(out, ".gph|server|port]\n");
- }
- const char* readme = page->repo->readme;
- if (readme && readme[0] != '\0') {
- fprintf(out, "[1|README|%sfile/", page->relpath);
- print_gopher_link(out, readme);
- fprintf(out, ".gph|server|port]\n");
- }
- const char* license = page->repo->license;
- if (license && license[0] != '\0') {
- fprintf(out, "[1|LICENSE|%sfile/", page->relpath);
- print_gopher_link(out, license);
+ for (size_t i = 0; i < page->repo->special_files_len; i++) {
+ RepoSpecialFile* sf = &page->repo->special_files[i];
+ fprintf(out, "[1|");
+ print_gopher_text(out, sf->label, false);
+ fprintf(out, "|%sfile/", page->relpath);
+ print_gopher_link(out, sf->path);
fprintf(out, ".gph|server|port]\n");
}
fprintf(out, "---\n");
diff --git a/src/writer/gopher/page_tests.c b/src/writer/gopher/page_tests.c
@@ -20,11 +20,13 @@ UTEST_F_SETUP(gopher_page) {
UTEST_F_TEARDOWN(gopher_page) {}
UTEST_F(gopher_page, basic) {
+ RepoSpecialFile sf = {.label = "LICENSE", .path = "LICENSE"};
GitRepo repo = {
.short_name = "test-repo",
.description = "Repo description",
.clone_url = "git://example.com/repo.git",
- .license = "LICENSE",
+ .special_files = &sf,
+ .special_files_len = 1,
};
FILE* out = g_fs_inmemory->fopen("test.gph", "w");
diff --git a/src/writer/gopher/repo_writer_tests.c b/src/writer/gopher/repo_writer_tests.c
@@ -26,9 +26,6 @@ UTEST_F(gopher_repo_writer, orchestration) {
.description = "A test repository",
.owner = "Test Owner",
.clone_url = "git://example.com/test-repo.git",
- .submodules = "",
- .readme = "",
- .license = "",
};
GopherRepoWriter* writer = gopher_repowriter_create(&repo, g_fs_inmemory);
ASSERT_NE(NULL, writer);
diff --git a/src/writer/html/page.c b/src/writer/html/page.c
@@ -123,23 +123,13 @@ void html_page_begin(HtmlPage* page) {
fprintf(out, "<a href=\"%sfiles.html\">Files</a> | ", relpath);
fprintf(out, "<a href=\"%srefs.html\">Refs</a>", relpath);
- const char* submodules = page->repo->submodules;
- if (submodules && submodules[0] != '\0') {
+ for (size_t i = 0; i < page->repo->special_files_len; i++) {
+ RepoSpecialFile* sf = &page->repo->special_files[i];
fprintf(out, " | <a href=\"%sfile/", relpath);
- print_xml_encoded(out, submodules);
- fprintf(out, ".html\">Submodules</a>");
- }
- const char* readme = page->repo->readme;
- if (readme && readme[0] != '\0') {
- fprintf(out, " | <a href=\"%sfile/", relpath);
- print_xml_encoded(out, readme);
- fprintf(out, ".html\">README</a>");
- }
- const char* license = page->repo->license;
- if (license && license[0] != '\0') {
- fprintf(out, " | <a href=\"%sfile/", relpath);
- print_xml_encoded(out, license);
- fprintf(out, ".html\">LICENSE</a>");
+ print_percent_encoded(out, sf->path);
+ fprintf(out, ".html\">");
+ print_xml_encoded(out, sf->label);
+ fprintf(out, "</a>");
}
fprintf(out, "</td></tr></table>\n<hr/>\n<div id=\"content\">\n");
}
diff --git a/src/writer/html/page_tests.c b/src/writer/html/page_tests.c
@@ -20,11 +20,13 @@ UTEST_F_SETUP(html_page) {
UTEST_F_TEARDOWN(html_page) {}
UTEST_F(html_page, basic) {
+ RepoSpecialFile sf = {.label = "LICENSE", .path = "LICENSE"};
GitRepo repo = {
.short_name = "test-repo",
.description = "Repo description",
.clone_url = "git://example.com/repo.git",
- .license = "LICENSE",
+ .special_files = &sf,
+ .special_files_len = 1,
};
FILE* out = g_fs_inmemory->fopen("test.html", "w");
diff --git a/src/writer/html/repo_writer_tests.c b/src/writer/html/repo_writer_tests.c
@@ -26,9 +26,6 @@ UTEST_F(html_repo_writer, orchestration) {
.description = "A test repository",
.owner = "Test Owner",
.clone_url = "git://example.com/test-repo.git",
- .submodules = "",
- .readme = "",
- .license = "",
};
HtmlRepoWriter* writer = html_repowriter_create(&repo, g_fs_inmemory);
ASSERT_NE(NULL, writer);