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:
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");
}