commit f4a2c59b7cf12d727e11f12d98ccb0688c3070f0
parent d1c012787ee97eddea5d0f5ef79f62a3705f6614
Author: Chris Bracken <chris@bracken.jp>
Date: Fri, 20 Feb 2026 18:56:25 +0900
cache: centralise cachefile creation/closing
Diffstat:
4 files changed, 110 insertions(+), 111 deletions(-)
diff --git a/src/writer/cache/cache.c b/src/writer/cache/cache.c
@@ -11,7 +11,16 @@
#include "git/git.h"
#include "utils.h"
+static const char* kTempCachePath = "cache.XXXXXXXXXXXX";
+static const mode_t kReadWriteAll =
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
+
struct Cache {
+ const FileSystem* fs;
+ char* cache_path;
+ char* temp_cache_path;
+ bool owns_streams;
+
bool can_add_commits;
WriteCommitRow write_commit_row;
FILE* cache_in;
@@ -50,10 +59,55 @@ Cache* cache_create(FILE* cache_in,
return cache;
}
+Cache* cache_open(const FileSystem* fs,
+ const char* path,
+ WriteCommitRow write_func) {
+ assert(fs != NULL);
+ assert(path != NULL);
+ assert(write_func != NULL);
+
+ char* temp_cache_path = estrdup(kTempCachePath);
+ FILE* cache_in = fs->fopen(path, "r");
+ int out_fd = fs->mkstemp(temp_cache_path);
+ if (out_fd == -1) {
+ free(temp_cache_path);
+ if (cache_in) {
+ fs->fclose(cache_in);
+ }
+ err(1, "mkstemp: %s", temp_cache_path);
+ }
+ FILE* cache_out = fs->fdopen(out_fd, "w");
+ if (!cache_out) {
+ fs->close(out_fd);
+ free(temp_cache_path);
+ if (cache_in) {
+ fs->fclose(cache_in);
+ }
+ err(1, "fdopen: %s", temp_cache_path);
+ }
+
+ Cache* cache = cache_create(cache_in, cache_out, write_func);
+ cache->fs = fs;
+ cache->cache_path = estrdup(path);
+ cache->temp_cache_path = temp_cache_path;
+ cache->owns_streams = true;
+ return cache;
+}
+
void cache_free(Cache* cache) {
if (!cache) {
return;
}
+ if (cache->owns_streams) {
+ if (cache->cache_in) {
+ cache->fs->fclose(cache->cache_in);
+ }
+ if (cache->cache_out) {
+ cache->fs->fclose(cache->cache_out);
+ }
+ }
+ free(cache->cache_path);
+ free(cache->temp_cache_path);
free(cache->lastoid_in);
cache->lastoid_in = NULL;
free(cache);
@@ -104,6 +158,38 @@ void cache_finish(Cache* cache) {
}
}
+void cache_close_and_replace(Cache* cache, FILE* out) {
+ assert(cache != NULL);
+ assert(cache->owns_streams);
+ assert(out != NULL);
+
+ cache_finish(cache);
+
+ if (cache->cache_in) {
+ cache->fs->fclose(cache->cache_in);
+ cache->cache_in = NULL;
+ }
+ cache->fs->fclose(cache->cache_out);
+ cache->cache_out = NULL;
+
+ if (cache->fs->rename(cache->temp_cache_path, cache->cache_path)) {
+ err(1, "rename: %s -> %s", cache->temp_cache_path, cache->cache_path);
+ }
+
+ mode_t mask;
+ umask(mask = umask(0));
+ if (cache->fs->chmod(cache->cache_path, kReadWriteAll & ~mask)) {
+ err(1, "chmod: %s", cache->cache_path);
+ }
+
+ FILE* fcache = cache->fs->fopen(cache->cache_path, "r");
+ if (!fcache) {
+ err(1, "fopen: %s", cache->cache_path);
+ }
+ cache_copy_log(fcache, out);
+ cache->fs->fclose(fcache);
+}
+
void cache_copy_log(FILE* fcache, FILE* out) {
assert(fcache != NULL);
assert(out != NULL);
diff --git a/src/writer/cache/cache.h b/src/writer/cache/cache.h
@@ -5,6 +5,7 @@
#include <stdio.h>
#include "git/commit.h"
+#include "utils.h"
/* Commit log cache file.
*
@@ -52,7 +53,20 @@ typedef void (*WriteCommitRow)(FILE* out, const GitCommit* commit);
*/
Cache* cache_create(FILE* cache_in, FILE* cache_out, WriteCommitRow write_func);
-/* Frees the specified cache. */
+/* Higher-level API that manages its own files.
+ *
+ * Opens the cache at the specified path for reading, and creates a temporary
+ * file for writing the updated cache.
+ *
+ * Returns a Cache object that owns the opened file streams and paths.
+ */
+Cache* cache_open(const FileSystem* fs,
+ const char* path,
+ WriteCommitRow write_func);
+
+/* Frees the specified cache. If the cache was created via cache_open, the
+ * associated file streams and paths are also freed.
+ */
void cache_free(Cache* cache);
/* Returns true if commits can be added to the cache.
@@ -75,6 +89,11 @@ void cache_add_commit_row(Cache* cache, const GitCommit* commit);
*/
void cache_finish(Cache* cache);
+/* Finishes, closes files, renames the temporary cache file to the final path,
+ * and copies the log contents to the specified output stream.
+ */
+void cache_close_and_replace(Cache* cache, FILE* out);
+
/* Copies the contents of the cache stream to the specified output stream,
* skipping the OID header.
*/
diff --git a/src/writer/gopher/log.c b/src/writer/gopher/log.c
@@ -18,10 +18,6 @@ struct GopherLog {
const FileSystem* fs;
FILE* out;
Cache* cache;
- char* cache_path;
- char* temp_cache_path;
- FILE* cache_in;
- FILE* cache_out;
GopherPage* page;
size_t remaining_commits;
size_t unlogged_commits;
@@ -29,10 +25,6 @@ struct GopherLog {
static void write_commit_row(FILE* out, const GitCommit* commit);
-static const char* kTempCachePath = "cache.XXXXXXXXXXXX";
-static const mode_t kReadWriteAll =
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
-
GopherLog* gopher_log_create(const GitRepo* repo, const FileSystem* fs) {
assert(repo != NULL);
assert(fs != NULL);
@@ -57,14 +49,6 @@ void gopher_log_free(GopherLog* log) {
log->out = NULL;
cache_free(log->cache);
log->cache = NULL;
- free(log->cache_path);
- free(log->temp_cache_path);
- if (log->cache_in) {
- log->fs->fclose(log->cache_in);
- }
- if (log->cache_out) {
- log->fs->fclose(log->cache_out);
- }
gopher_page_free(log->page);
log->page = NULL;
free(log);
@@ -73,21 +57,7 @@ void gopher_log_free(GopherLog* log) {
void gopher_log_set_cachefile(GopherLog* log, const char* cachefile) {
assert(log != NULL);
assert(cachefile != NULL);
- log->cache_path = estrdup(cachefile);
- log->temp_cache_path = estrdup(kTempCachePath);
-
- log->cache_in = log->fs->fopen(cachefile, "r");
- int out_fd = log->fs->mkstemp(log->temp_cache_path);
- if (out_fd == -1) {
- err(1, "mkstemp: %s", log->temp_cache_path);
- }
- log->cache_out = log->fs->fdopen(out_fd, "w");
- if (!log->cache_out) {
- log->fs->close(out_fd);
- err(1, "fdopen: %s", log->temp_cache_path);
- }
-
- log->cache = cache_create(log->cache_in, log->cache_out, write_commit_row);
+ log->cache = cache_open(log->fs, cachefile, write_commit_row);
}
void gopher_log_set_commit_limit(GopherLog* log, size_t count) {
@@ -126,30 +96,7 @@ void gopher_log_end(GopherLog* log) {
assert(log != NULL);
FILE* out = log->out;
if (log->cache) {
- cache_finish(log->cache);
- if (log->cache_in) {
- log->fs->fclose(log->cache_in);
- log->cache_in = NULL;
- }
- log->fs->fclose(log->cache_out);
- log->cache_out = NULL;
-
- if (log->fs->rename(log->temp_cache_path, log->cache_path)) {
- err(1, "rename: %s -> %s", log->temp_cache_path, log->cache_path);
- }
-
- mode_t mask;
- umask(mask = umask(0));
- if (log->fs->chmod(log->cache_path, kReadWriteAll & ~mask)) {
- err(1, "chmod: %s", log->cache_path);
- }
-
- FILE* fcache = log->fs->fopen(log->cache_path, "r");
- if (!fcache) {
- err(1, "fopen: %s", log->cache_path);
- }
- cache_copy_log(fcache, log->out);
- log->fs->fclose(fcache);
+ cache_close_and_replace(log->cache, log->out);
} else if (log->unlogged_commits > 0) {
size_t count = log->unlogged_commits;
fprintf(out, "%16.16s %zu more commits remaining, fetch the repository\n",
diff --git a/src/writer/html/log.c b/src/writer/html/log.c
@@ -18,10 +18,6 @@ struct HtmlLog {
const FileSystem* fs;
FILE* out;
Cache* cache;
- char* cache_path;
- char* temp_cache_path;
- FILE* cache_in;
- FILE* cache_out;
HtmlPage* page;
size_t remaining_commits;
size_t unlogged_commits;
@@ -29,10 +25,6 @@ struct HtmlLog {
static void write_commit_row(FILE* out, const GitCommit* commit);
-static const char* kTempCachePath = "cache.XXXXXXXXXXXX";
-static const mode_t kReadWriteAll =
- S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
-
HtmlLog* html_log_create(const GitRepo* repo, const FileSystem* fs) {
assert(repo != NULL);
assert(fs != NULL);
@@ -57,14 +49,6 @@ void html_log_free(HtmlLog* log) {
log->out = NULL;
cache_free(log->cache);
log->cache = NULL;
- free(log->cache_path);
- free(log->temp_cache_path);
- if (log->cache_in) {
- log->fs->fclose(log->cache_in);
- }
- if (log->cache_out) {
- log->fs->fclose(log->cache_out);
- }
html_page_free(log->page);
log->page = NULL;
free(log);
@@ -73,21 +57,7 @@ void html_log_free(HtmlLog* log) {
void html_log_set_cachefile(HtmlLog* log, const char* cachefile) {
assert(log != NULL);
assert(cachefile != NULL);
- log->cache_path = estrdup(cachefile);
- log->temp_cache_path = estrdup(kTempCachePath);
-
- log->cache_in = log->fs->fopen(cachefile, "r");
- int out_fd = log->fs->mkstemp(log->temp_cache_path);
- if (out_fd == -1) {
- err(1, "mkstemp: %s", log->temp_cache_path);
- }
- log->cache_out = log->fs->fdopen(out_fd, "w");
- if (!log->cache_out) {
- log->fs->close(out_fd);
- err(1, "fdopen: %s", log->temp_cache_path);
- }
-
- log->cache = cache_create(log->cache_in, log->cache_out, write_commit_row);
+ log->cache = cache_open(log->fs, cachefile, write_commit_row);
}
void html_log_set_commit_limit(HtmlLog* log, size_t count) {
@@ -133,30 +103,7 @@ void html_log_end(HtmlLog* log) {
assert(log != NULL);
FILE* out = log->out;
if (log->cache) {
- cache_finish(log->cache);
- if (log->cache_in) {
- log->fs->fclose(log->cache_in);
- log->cache_in = NULL;
- }
- log->fs->fclose(log->cache_out);
- log->cache_out = NULL;
-
- if (log->fs->rename(log->temp_cache_path, log->cache_path)) {
- err(1, "rename: %s -> %s", log->temp_cache_path, log->cache_path);
- }
-
- mode_t mask;
- umask(mask = umask(0));
- if (log->fs->chmod(log->cache_path, kReadWriteAll & ~mask)) {
- err(1, "chmod: %s", log->cache_path);
- }
-
- FILE* fcache = log->fs->fopen(log->cache_path, "r");
- if (!fcache) {
- err(1, "fopen: %s", log->cache_path);
- }
- cache_copy_log(fcache, log->out);
- log->fs->fclose(fcache);
+ cache_close_and_replace(log->cache, log->out);
} else if (log->unlogged_commits > 0) {
size_t count = log->unlogged_commits;
fprintf(out, "<tr><td></td><td colspan=\"5\">");