gout

A static git page generator
git clone https://git.bracken.jp/gout.git
Log | Files | Refs | README | LICENSE

commit d3fc7db161a3d643590ab389d6cdf007bb30bdac
parent 1edd802a51791f31eb7971ff570bbeeb817d8311
Author: Chris Bracken <chris@bracken.jp>
Date:   Fri, 20 Feb 2026 17:05:53 +0900

repo: better handling for non-unixy line endings

In theory, if a user rsync'ed a repo from a Windows machine (\r\n line
endings) or an ancient macOS classic machine (\r line endings) these
files could 

Diffstat:
Msrc/git/repo.c | 6++----
Msrc/git/repo_tests.c | 23+++++++++++++++++++++++
Msrc/writer/cache/cache.c | 16++++++----------
3 files changed, 31 insertions(+), 14 deletions(-)

diff --git a/src/git/repo.c b/src/git/repo.c @@ -76,10 +76,8 @@ static char* first_line_of_file(const FileSystem* fs, const char* path) { free(buf); return estrdup(""); } - // Remove trailing newline. - if (len > 0 && buf[len - 1] == '\n') { - buf[len - 1] = '\0'; - } + // Truncate at the first newline or carriage return. + buf[strcspn(buf, "\r\n")] = '\0'; return buf; } diff --git a/src/git/repo_tests.c b/src/git/repo_tests.c @@ -67,3 +67,26 @@ UTEST_F(git_repo, load_filesystem_metadata_bare) { gitrepo_free(repo); } + +UTEST_F(git_repo, load_filesystem_metadata_crlf) { + const char* repo_path = "/path/to/crlf-repo.git"; + + /* Mock description with CRLF */ + FILE* f_desc = + g_fs_inmemory->fopen("/path/to/crlf-repo.git/description", "w"); + fprintf(f_desc, "Description with CRLF\r\n"); + g_fs_inmemory->fclose(f_desc); + + /* Mock owner with just CR (unlikely but good for testing robustness) */ + FILE* f_owner = g_fs_inmemory->fopen("/path/to/crlf-repo.git/owner", "w"); + fprintf(f_owner, "Owner with CR\r"); + g_fs_inmemory->fclose(f_owner); + + GitRepo* repo = ecalloc(1, sizeof(GitRepo)); + gitrepo_load_filesystem_metadata(repo, g_fs_inmemory, repo_path); + + EXPECT_STREQ("Description with CRLF", repo->description); + EXPECT_STREQ("Owner with CR", repo->owner); + + gitrepo_free(repo); +} diff --git a/src/writer/cache/cache.c b/src/writer/cache/cache.c @@ -34,17 +34,13 @@ Cache* cache_create(FILE* cache_in, // Read lastoid from previous cache, if it exists. cache->lastoid_in = ecalloc(kOidLen, sizeof(char)); if (cache->cache_in) { - // OID + '\n' + '\0'. - char buf[kOidLen + 1]; - char* oid_str = fgets(buf, sizeof(buf), cache->cache_in); + // Read into a local buffer that has extra space for \r\n. + char line_buf[kOidLen + 2]; + char* oid_str = fgets(line_buf, sizeof(line_buf), cache->cache_in); if (oid_str) { - // Copy oid_str and trim trailing newline if it exists. - estrlcpy(cache->lastoid_in, oid_str, kOidLen); - size_t len = strlen(cache->lastoid_in); - while (len > 0 && (cache->lastoid_in[len - 1] == '\n' || - cache->lastoid_in[len - 1] == '\r')) { - cache->lastoid_in[--len] = '\0'; - } + // Strip trailing newline/carriage return before copying. + line_buf[strcspn(line_buf, "\r\n")] = '\0'; + estrlcpy(cache->lastoid_in, line_buf, kOidLen); } else { warnx("corrupt cachefile"); }