diff --git a/src/fileops.c b/src/fileops.c index 7f8418d7a..aea8075d5 100644 --- a/src/fileops.c +++ b/src/fileops.c @@ -58,9 +58,9 @@ int git_futils_creat_locked(const char *path, const mode_t mode) int fd; #ifdef GIT_WIN32 - wchar_t buf[GIT_WIN_PATH]; + git_win32_path buf; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path_from_c(buf, path); fd = _wopen(buf, O_WRONLY | O_CREAT | O_TRUNC | O_EXCL | O_BINARY | O_CLOEXEC, mode); #else diff --git a/src/path.c b/src/path.c index b81675b49..a753a734d 100644 --- a/src/path.c +++ b/src/path.c @@ -8,7 +8,6 @@ #include "path.h" #include "posix.h" #ifdef GIT_WIN32 -#include "win32/dir.h" #include "win32/posix.h" #else #include @@ -486,24 +485,26 @@ bool git_path_is_empty_dir(const char *path) { git_buf pathbuf = GIT_BUF_INIT; HANDLE hFind = INVALID_HANDLE_VALUE; - wchar_t wbuf[GIT_WIN_PATH]; + git_win32_path wbuf; WIN32_FIND_DATAW ffd; bool retval = true; if (!git_path_isdir(path)) return false; git_buf_printf(&pathbuf, "%s\\*", path); - git__utf8_to_16(wbuf, GIT_WIN_PATH, git_buf_cstr(&pathbuf)); + git_win32_path_from_c(wbuf, git_buf_cstr(&pathbuf)); hFind = FindFirstFileW(wbuf, &ffd); if (INVALID_HANDLE_VALUE == hFind) { giterr_set(GITERR_OS, "Couldn't open '%s'", path); + git_buf_free(&pathbuf); return false; } do { if (!git_path_is_dot_or_dotdotW(ffd.cFileName)) { retval = false; + break; } } while (FindNextFileW(hFind, &ffd) != 0); diff --git a/src/transports/winhttp.c b/src/transports/winhttp.c index 95e422dc0..8decd8d51 100644 --- a/src/transports/winhttp.c +++ b/src/transports/winhttp.c @@ -893,7 +893,7 @@ static int winhttp_connect( const char *url) { wchar_t *ua = L"git/1.0 (libgit2 " WIDEN(LIBGIT2_VERSION) L")"; - wchar_t host[GIT_WIN_PATH]; + git_win32_path host; int32_t port; const char *default_port = "80"; int ret; @@ -920,7 +920,7 @@ static int winhttp_connect( return -1; /* Prepare host */ - git__utf8_to_16(host, GIT_WIN_PATH, t->host); + git_win32_path_from_c(host, t->host); /* Establish session */ t->session = WinHttpOpen( @@ -934,7 +934,7 @@ static int winhttp_connect( giterr_set(GITERR_OS, "Failed to init WinHTTP"); return -1; } - + /* Establish connection */ t->connection = WinHttpConnect( t->session, diff --git a/src/win32/dir.c b/src/win32/dir.c index 8c51d8378..f7859b73f 100644 --- a/src/win32/dir.c +++ b/src/win32/dir.c @@ -5,8 +5,7 @@ * a Linking Exception. For full terms see the included COPYING file. */ #define GIT__WIN32_NO_WRAP_DIR -#include "dir.h" -#include "utf-conv.h" +#include "posix.h" static int init_filter(char *filter, size_t n, const char *dir) { @@ -25,36 +24,32 @@ static int init_filter(char *filter, size_t n, const char *dir) git__DIR *git__opendir(const char *dir) { - char filter[GIT_WIN_PATH]; - wchar_t filter_w[GIT_WIN_PATH]; + git_win32_path_as_utf8 filter; + git_win32_path filter_w; git__DIR *new = NULL; + size_t dirlen; if (!dir || !init_filter(filter, sizeof(filter), dir)) return NULL; - new = git__calloc(1, sizeof(*new)); + dirlen = strlen(dir); + + new = git__calloc(sizeof(*new) + dirlen + 1, 1); if (!new) return NULL; + memcpy(new->dir, dir, dirlen); - new->dir = git__strdup(dir); - if (!new->dir) - goto fail; - - git__utf8_to_16(filter_w, GIT_WIN_PATH, filter); + git_win32_path_from_c(filter_w, filter); new->h = FindFirstFileW(filter_w, &new->f); if (new->h == INVALID_HANDLE_VALUE) { giterr_set(GITERR_OS, "Could not open directory '%s'", dir); - goto fail; + git__free(new); + return NULL; } new->first = 1; return new; - -fail: - git__free(new->dir); - git__free(new); - return NULL; } int git__readdir_ext( @@ -80,7 +75,7 @@ int git__readdir_ext( if (wcslen(d->f.cFileName) >= sizeof(entry->d_name)) return -1; - git__utf16_to_8(entry->d_name, d->f.cFileName); + git_win32_path_to_c(entry->d_name, d->f.cFileName); entry->d_ino = 0; *result = entry; @@ -101,8 +96,8 @@ struct git__dirent *git__readdir(git__DIR *d) void git__rewinddir(git__DIR *d) { - char filter[GIT_WIN_PATH]; - wchar_t filter_w[GIT_WIN_PATH]; + git_win32_path_as_utf8 filter; + git_win32_path filter_w; if (!d) return; @@ -116,7 +111,7 @@ void git__rewinddir(git__DIR *d) if (!init_filter(filter, sizeof(filter), d->dir)) return; - git__utf8_to_16(filter_w, GIT_WIN_PATH, filter); + git_win32_path_from_c(filter_w, filter); d->h = FindFirstFileW(filter_w, &d->f); if (d->h == INVALID_HANDLE_VALUE) @@ -134,8 +129,7 @@ int git__closedir(git__DIR *d) FindClose(d->h); d->h = INVALID_HANDLE_VALUE; } - git__free(d->dir); - d->dir = NULL; + git__free(d); return 0; } diff --git a/src/win32/dir.h b/src/win32/dir.h index 7696d468e..24d48f6ba 100644 --- a/src/win32/dir.h +++ b/src/win32/dir.h @@ -11,15 +11,15 @@ struct git__dirent { int d_ino; - char d_name[261]; + git_win32_path_as_utf8 d_name; }; typedef struct { HANDLE h; WIN32_FIND_DATAW f; struct git__dirent entry; - char *dir; int first; + char dir[GIT_FLEX_ARRAY]; } git__DIR; extern git__DIR *git__opendir(const char *); diff --git a/src/win32/findfile.c b/src/win32/findfile.c index 9d9051bff..a1c11fcfb 100644 --- a/src/win32/findfile.c +++ b/src/win32/findfile.c @@ -23,11 +23,11 @@ int git_win32__expand_path(struct git_win32__path *s_root, const wchar_t *templ) return s_root->len ? 0 : -1; } -static int win32_path_utf16_to_8(git_buf *path_utf8, const wchar_t *path_utf16) +static int win32_path_to_8(git_buf *path_utf8, const wchar_t *path) { char temp_utf8[GIT_PATH_MAX]; - git__utf16_to_8(temp_utf8, path_utf16); + git__utf16_to_8(temp_utf8, GIT_PATH_MAX, path); git_path_mkposix(temp_utf8); return git_buf_sets(path_utf8, temp_utf8); @@ -53,7 +53,7 @@ int git_win32__find_file( if (*filename == '/' || *filename == '\\') filename++; - git__utf8_to_16(file_utf16 + root->len - 1, alloc_len, filename); + git__utf8_to_16(file_utf16 + root->len - 1, alloc_len - root->len, filename); /* check access */ if (_waccess(file_utf16, F_OK) < 0) { @@ -61,7 +61,7 @@ int git_win32__find_file( return GIT_ENOTFOUND; } - win32_path_utf16_to_8(path, file_utf16); + win32_path_to_8(path, file_utf16); git__free(file_utf16); return 0; @@ -113,7 +113,7 @@ static int win32_find_git_in_path(git_buf *buf, const wchar_t *gitexe) /* replace "bin\\" or "cmd\\" with "etc\\" */ wcscpy(&root.path[root.len - 4], L"etc\\"); - win32_path_utf16_to_8(buf, root.path); + win32_path_to_8(buf, root.path); return 0; } } @@ -146,7 +146,7 @@ static int win32_find_git_in_registry( wcscat(path16.path, L"etc\\"); path16.len += 4; - win32_path_utf16_to_8(buf, path16.path); + win32_path_to_8(buf, path16.path); } RegCloseKey(hKey); @@ -168,7 +168,7 @@ static int win32_find_existing_dirs( path16.path[0] != L'%' && !_waccess(path16.path, F_OK)) { - win32_path_utf16_to_8(&buf, path16.path); + win32_path_to_8(&buf, path16.path); if (buf.size) git_buf_join(out, GIT_PATH_LIST_SEPARATOR, out->ptr, buf.ptr); diff --git a/src/win32/posix.h b/src/win32/posix.h index c49c2175c..5f924a026 100644 --- a/src/win32/posix.h +++ b/src/win32/posix.h @@ -8,7 +8,9 @@ #define INCLUDE_posix__w32_h__ #include "common.h" +#include "../posix.h" #include "utf-conv.h" +#include "dir.h" GIT_INLINE(int) p_link(const char *old, const char *new) { @@ -20,9 +22,9 @@ GIT_INLINE(int) p_link(const char *old, const char *new) GIT_INLINE(int) p_mkdir(const char *path, mode_t mode) { - wchar_t buf[GIT_WIN_PATH]; + git_win32_path buf; GIT_UNUSED(mode); - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path_from_c(buf, path); return _wmkdir(buf); } diff --git a/src/win32/posix_w32.c b/src/win32/posix_w32.c index 036632e2a..9d5b8d877 100644 --- a/src/win32/posix_w32.c +++ b/src/win32/posix_w32.c @@ -16,8 +16,8 @@ int p_unlink(const char *path) { - wchar_t buf[GIT_WIN_PATH]; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path buf; + git_win32_path_from_c(buf, path); _wchmod(buf, 0666); return _wunlink(buf); } @@ -59,10 +59,11 @@ static int do_lstat( const char *file_name, struct stat *buf, int posix_enotdir) { WIN32_FILE_ATTRIBUTE_DATA fdata; - wchar_t fbuf[GIT_WIN_PATH], lastch; + git_win32_path fbuf; + wchar_t lastch; int flen; - flen = git__utf8_to_16(fbuf, GIT_WIN_PATH, file_name); + flen = git_win32_path_from_c(fbuf, file_name); /* truncate trailing slashes */ for (; flen > 0; --flen) { @@ -108,10 +109,10 @@ static int do_lstat( * the length of the path pointed to, which we expect everywhere else */ if (S_ISLNK(fMode)) { - char target[GIT_WIN_PATH]; + git_win32_path_as_utf8 target; int readlink_result; - readlink_result = p_readlink(file_name, target, GIT_WIN_PATH); + readlink_result = p_readlink(file_name, target, sizeof(target)); if (readlink_result == -1) return -1; @@ -165,7 +166,7 @@ int p_readlink(const char *link, char *target, size_t target_len) static fpath_func pGetFinalPath = NULL; HANDLE hFile; DWORD dwRet; - wchar_t link_w[GIT_WIN_PATH]; + git_win32_path link_w; wchar_t* target_w; int error = 0; @@ -188,7 +189,7 @@ int p_readlink(const char *link, char *target, size_t target_len) } } - git__utf8_to_16(link_w, GIT_WIN_PATH, link); + git_win32_path_from_c(link_w, link); hFile = CreateFileW(link_w, // file to open GENERIC_READ, // open for reading @@ -254,10 +255,10 @@ int p_symlink(const char *old, const char *new) int p_open(const char *path, int flags, ...) { - wchar_t buf[GIT_WIN_PATH]; + git_win32_path buf; mode_t mode = 0; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path_from_c(buf, path); if (flags & O_CREAT) { va_list arg_list; @@ -272,8 +273,8 @@ int p_open(const char *path, int flags, ...) int p_creat(const char *path, mode_t mode) { - wchar_t buf[GIT_WIN_PATH]; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path buf; + git_win32_path_from_c(buf, path); return _wopen(buf, _O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY, mode); } @@ -299,7 +300,7 @@ int p_getcwd(char *buffer_out, size_t size) int p_stat(const char* path, struct stat* buf) { - char target[GIT_WIN_PATH]; + git_win32_path_as_utf8 target; int error = 0; error = do_lstat(path, buf, 0); @@ -307,7 +308,7 @@ int p_stat(const char* path, struct stat* buf) /* We need not do this in a loop to unwind chains of symlinks since * p_readlink calls GetFinalPathNameByHandle which does it for us. */ if (error >= 0 && S_ISLNK(buf->st_mode) && - (error = p_readlink(path, target, GIT_WIN_PATH)) >= 0) + (error = p_readlink(path, target, sizeof(target))) >= 0) error = do_lstat(target, buf, 0); return error; @@ -315,23 +316,23 @@ int p_stat(const char* path, struct stat* buf) int p_chdir(const char* path) { - wchar_t buf[GIT_WIN_PATH]; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path buf; + git_win32_path_from_c(buf, path); return _wchdir(buf); } int p_chmod(const char* path, mode_t mode) { - wchar_t buf[GIT_WIN_PATH]; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path buf; + git_win32_path_from_c(buf, path); return _wchmod(buf, mode); } int p_rmdir(const char* path) { int error; - wchar_t buf[GIT_WIN_PATH]; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path buf; + git_win32_path_from_c(buf, path); error = _wrmdir(buf); @@ -347,24 +348,24 @@ int p_rmdir(const char* path) int p_hide_directory__w32(const char *path) { - wchar_t buf[GIT_WIN_PATH]; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path buf; + git_win32_path_from_c(buf, path); return (SetFileAttributesW(buf, FILE_ATTRIBUTE_HIDDEN) != 0) ? 0 : -1; } char *p_realpath(const char *orig_path, char *buffer) { int ret; - wchar_t orig_path_w[GIT_WIN_PATH]; - wchar_t buffer_w[GIT_WIN_PATH]; + git_win32_path orig_path_w; + git_win32_path buffer_w; - git__utf8_to_16(orig_path_w, GIT_WIN_PATH, orig_path); + git_win32_path_from_c(orig_path_w, orig_path); /* Implicitly use GetCurrentDirectory which can be a threading issue */ - ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH, buffer_w, NULL); + ret = GetFullPathNameW(orig_path_w, GIT_WIN_PATH_UTF16, buffer_w, NULL); /* According to MSDN, a return value equals to zero means a failure. */ - if (ret == 0 || ret > GIT_WIN_PATH) + if (ret == 0 || ret > GIT_WIN_PATH_UTF16) buffer = NULL; else if (GetFileAttributesW(buffer_w) == INVALID_FILE_ATTRIBUTES) { @@ -448,18 +449,18 @@ int p_setenv(const char* name, const char* value, int overwrite) int p_access(const char* path, mode_t mode) { - wchar_t buf[GIT_WIN_PATH]; - git__utf8_to_16(buf, GIT_WIN_PATH, path); + git_win32_path buf; + git_win32_path_from_c(buf, path); return _waccess(buf, mode); } int p_rename(const char *from, const char *to) { - wchar_t wfrom[GIT_WIN_PATH]; - wchar_t wto[GIT_WIN_PATH]; + git_win32_path wfrom; + git_win32_path wto; - git__utf8_to_16(wfrom, GIT_WIN_PATH, from); - git__utf8_to_16(wto, GIT_WIN_PATH, to); + git_win32_path_from_c(wfrom, from); + git_win32_path_from_c(wto, to); return MoveFileExW(wfrom, wto, MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED) ? 0 : -1; } @@ -513,10 +514,10 @@ p_gmtime_r (const time_t *timer, struct tm *result) #else #define DELTA_EPOCH_IN_MICROSECS 11644473600000000ULL #endif - + #ifndef _TIMEZONE_DEFINED #define _TIMEZONE_DEFINED -struct timezone +struct timezone { int tz_minuteswest; /* minutes W of Greenwich */ int tz_dsttime; /* type of dst correction */ diff --git a/src/win32/utf-conv.c b/src/win32/utf-conv.c index c06f3a8c2..d4dbfbab9 100644 --- a/src/win32/utf-conv.c +++ b/src/win32/utf-conv.c @@ -70,12 +70,12 @@ void git__utf8_to_16(wchar_t *dest, size_t length, const char *src) } #endif -int git__utf8_to_16(wchar_t *dest, size_t length, const char *src) +int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src) { - return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)length); + return MultiByteToWideChar(CP_UTF8, 0, src, -1, dest, (int)dest_size); } -int git__utf16_to_8(char *out, const wchar_t *input) +int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src) { - return WideCharToMultiByte(CP_UTF8, 0, input, -1, out, GIT_WIN_PATH, NULL, NULL); + return WideCharToMultiByte(CP_UTF8, 0, src, -1, dest, (int)dest_size, NULL, NULL); } diff --git a/src/win32/utf-conv.h b/src/win32/utf-conv.h index 6cc9205f7..3af77580e 100644 --- a/src/win32/utf-conv.h +++ b/src/win32/utf-conv.h @@ -4,16 +4,35 @@ * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ - -#include - #ifndef INCLUDE_git_utfconv_h__ #define INCLUDE_git_utfconv_h__ -#define GIT_WIN_PATH (260 + 1) +#include +#include "common.h" -int git__utf8_to_16(wchar_t *dest, size_t length, const char *src); -int git__utf16_to_8(char *dest, const wchar_t *src); +/* Maximum characters in a Windows path plus one for NUL byte */ +#define GIT_WIN_PATH_UTF16 (260 + 1) + +/* Maximum bytes necessary to convert a full-length UTF16 path to UTF8 */ +#define GIT_WIN_PATH_UTF8 (260 * 4 + 1) + +typedef wchar_t git_win32_path[GIT_WIN_PATH_UTF16]; + +typedef char git_win32_path_as_utf8[GIT_WIN_PATH_UTF8]; + +/* dest_size is the size of dest in wchar_t's */ +int git__utf8_to_16(wchar_t * dest, size_t dest_size, const char *src); +/* dest_size is the size of dest in char's */ +int git__utf16_to_8(char *dest, size_t dest_size, const wchar_t *src); + +GIT_INLINE(int) git_win32_path_from_c(git_win32_path dest, const char *src) +{ + return git__utf8_to_16(dest, GIT_WIN_PATH_UTF16, src); +} + +GIT_INLINE(int) git_win32_path_to_c(git_win32_path_as_utf8 dest, const wchar_t *src) +{ + return git__utf16_to_8(dest, GIT_WIN_PATH_UTF8, src); +} #endif - diff --git a/tests-clar/checkout/tree.c b/tests-clar/checkout/tree.c index e4bfbce06..fff530cdd 100644 --- a/tests-clar/checkout/tree.c +++ b/tests-clar/checkout/tree.c @@ -677,3 +677,22 @@ void test_checkout_tree__target_directory_from_bare(void) cl_git_pass(git_futils_rmdir_r( "alternative", NULL, GIT_RMDIR_REMOVE_FILES)); } + +void test_checkout_tree__extremely_long_file_name(void) +{ + // A utf-8 string with 83 characters, but 249 bytes. + const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97"; + char path[1024]; + + g_opts.checkout_strategy = GIT_CHECKOUT_FORCE; + cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name")); + cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); + + sprintf(path, "testrepo/%s.txt", longname); + cl_assert(git_path_exists(path)); + + git_object_free(g_object); + cl_git_pass(git_revparse_single(&g_object, g_repo, "master")); + cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts)); + cl_assert(!git_path_exists(path)); +} diff --git a/tests-clar/clar_libgit2.c b/tests-clar/clar_libgit2.c index de0e41bf7..bf35a68eb 100644 --- a/tests-clar/clar_libgit2.c +++ b/tests-clar/clar_libgit2.c @@ -56,23 +56,24 @@ void cl_git_rewritefile(const char *filename, const char *new_content) char *cl_getenv(const char *name) { - wchar_t name_utf16[GIT_WIN_PATH]; + git_win32_path name_utf16; DWORD alloc_len; wchar_t *value_utf16; char *value_utf8; - git__utf8_to_16(name_utf16, GIT_WIN_PATH, name); + git_win32_path_from_c(name_utf16, name); alloc_len = GetEnvironmentVariableW(name_utf16, NULL, 0); if (alloc_len <= 0) return NULL; - alloc_len = GIT_WIN_PATH; cl_assert(value_utf16 = git__calloc(alloc_len, sizeof(wchar_t))); GetEnvironmentVariableW(name_utf16, value_utf16, alloc_len); - cl_assert(value_utf8 = git__malloc(alloc_len)); - git__utf16_to_8(value_utf8, value_utf16); + alloc_len = alloc_len * 4 + 1; /* worst case UTF16->UTF8 growth */ + cl_assert(value_utf8 = git__calloc(alloc_len, 1)); + + git__utf16_to_8(value_utf8, alloc_len, value_utf16); git__free(value_utf16); @@ -81,13 +82,13 @@ char *cl_getenv(const char *name) int cl_setenv(const char *name, const char *value) { - wchar_t name_utf16[GIT_WIN_PATH]; - wchar_t value_utf16[GIT_WIN_PATH]; + git_win32_path name_utf16; + git_win32_path value_utf16; - git__utf8_to_16(name_utf16, GIT_WIN_PATH, name); + git_win32_path_from_c(name_utf16, name); if (value) { - git__utf8_to_16(value_utf16, GIT_WIN_PATH, value); + git_win32_path_from_c(value_utf16, value); cl_assert(SetEnvironmentVariableW(name_utf16, value_utf16)); } else { /* Windows XP returns 0 (failed) when passing NULL for lpValue when @@ -107,12 +108,12 @@ int cl_setenv(const char *name, const char *value) * the source is a directory, a child of the source). */ int cl_rename(const char *source, const char *dest) { - wchar_t source_utf16[GIT_WIN_PATH]; - wchar_t dest_utf16[GIT_WIN_PATH]; + git_win32_path source_utf16; + git_win32_path dest_utf16; unsigned retries = 1; - git__utf8_to_16(source_utf16, GIT_WIN_PATH, source); - git__utf8_to_16(dest_utf16, GIT_WIN_PATH, dest); + git_win32_path_from_c(source_utf16, source); + git_win32_path_from_c(dest_utf16, dest); while (!MoveFileW(source_utf16, dest_utf16)) { /* Only retry if the error is ERROR_ACCESS_DENIED; diff --git a/tests-clar/refs/list.c b/tests-clar/refs/list.c index c9c2af4a7..de5c0fd3d 100644 --- a/tests-clar/refs/list.c +++ b/tests-clar/refs/list.c @@ -36,7 +36,7 @@ void test_refs_list__all(void) /* We have exactly 12 refs in total if we include the packed ones: * there is a reference that exists both in the packfile and as * loose, but we only list it once */ - cl_assert_equal_i((int)ref_list.count, 13); + cl_assert_equal_i((int)ref_list.count, 14); git_strarray_free(&ref_list); } @@ -51,7 +51,7 @@ void test_refs_list__do_not_retrieve_references_which_name_end_with_a_lock_exten "144344043ba4d4a405da03de3844aa829ae8be0e\n"); cl_git_pass(git_reference_list(&ref_list, g_repo)); - cl_assert_equal_i((int)ref_list.count, 13); + cl_assert_equal_i((int)ref_list.count, 14); git_strarray_free(&ref_list); } diff --git a/tests-clar/repo/iterator.c b/tests-clar/repo/iterator.c index 11a7d2a23..1c513e9e7 100644 --- a/tests-clar/repo/iterator.c +++ b/tests-clar/repo/iterator.c @@ -906,6 +906,7 @@ void test_repo_iterator__fs2(void) static const char *expect_base[] = { "heads/br2", "heads/dir", + "heads/long-file-name", "heads/master", "heads/packed-test", "heads/subtrees", @@ -922,6 +923,6 @@ void test_repo_iterator__fs2(void) cl_git_pass(git_iterator_for_filesystem( &i, "testrepo/.git/refs", 0, NULL, NULL)); - expect_iterator_items(i, 11, expect_base, 11, expect_base); + expect_iterator_items(i, 12, expect_base, 12, expect_base); git_iterator_free(i); } diff --git a/tests-clar/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 b/tests-clar/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 new file mode 100644 index 000000000..ee7c78174 Binary files /dev/null and b/tests-clar/resources/testrepo/.gitted/objects/6b/377958d8c6a4906e8573b53672a1a23a4e8ce6 differ diff --git a/tests-clar/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e b/tests-clar/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e new file mode 100644 index 000000000..197685b86 Binary files /dev/null and b/tests-clar/resources/testrepo/.gitted/objects/6b/9b767af9992b4abad5e24ffb1ba2d688ca602e differ diff --git a/tests-clar/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a b/tests-clar/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a new file mode 100644 index 000000000..db778aaae Binary files /dev/null and b/tests-clar/resources/testrepo/.gitted/objects/7b/2417a23b63e1fdde88c80e14b33247c6e5785a differ diff --git a/tests-clar/resources/testrepo/.gitted/refs/heads/long-file-name b/tests-clar/resources/testrepo/.gitted/refs/heads/long-file-name new file mode 100644 index 000000000..1f942a746 --- /dev/null +++ b/tests-clar/resources/testrepo/.gitted/refs/heads/long-file-name @@ -0,0 +1 @@ +6b377958d8c6a4906e8573b53672a1a23a4e8ce6 diff --git a/tests-clar/revwalk/basic.c b/tests-clar/revwalk/basic.c index cb8fcb9c7..6d55aed54 100644 --- a/tests-clar/revwalk/basic.c +++ b/tests-clar/revwalk/basic.c @@ -177,7 +177,7 @@ void test_revwalk_basic__glob_heads_with_invalid(void) /* walking */; /* git log --branches --oneline | wc -l => 16 */ - cl_assert_equal_i(16, i); + cl_assert_equal_i(17, i); } void test_revwalk_basic__push_head(void) diff --git a/tests-clar/status/status_data.h b/tests-clar/status/status_data.h index 3efa934ea..8ad4235fd 100644 --- a/tests-clar/status/status_data.h +++ b/tests-clar/status/status_data.h @@ -1,5 +1,9 @@ #include "status_helpers.h" +// A utf-8 string with 83 characters, but 249 bytes. +static const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97"; + + /* entries for a plain copy of tests/resources/status */ static const char *entry_paths0[] = { diff --git a/tests-clar/status/worktree.c b/tests-clar/status/worktree.c index 0e315cd60..be7398cb6 100644 --- a/tests-clar/status/worktree.c +++ b/tests-clar/status/worktree.c @@ -865,3 +865,35 @@ void test_status_worktree__sorting_by_case(void) cl_assert_equal_i(0, counts.wrong_status_flags_count); cl_assert_equal_i(0, counts.wrong_sorted_path); } + +void test_status_worktree__long_filenames(void) +{ + char path[260*4+1]; + const char *expected_paths[] = {path}; + const unsigned int expected_statuses[] = {GIT_STATUS_WT_NEW}; + + git_repository *repo = cl_git_sandbox_init("empty_standard_repo"); + git_status_options opts = GIT_STATUS_OPTIONS_INIT; + status_entry_counts counts = {0}; + + // Create directory with amazingly long filename + sprintf(path, "empty_standard_repo/%s", longname); + cl_git_pass(git_futils_mkdir_r(path, NULL, 0777)); + sprintf(path, "empty_standard_repo/%s/foo", longname); + cl_git_mkfile(path, "dummy"); + + sprintf(path, "%s/foo", longname); + counts.expected_entry_count = 1; + counts.expected_paths = expected_paths; + counts.expected_statuses = expected_statuses; + + opts.show = GIT_STATUS_SHOW_WORKDIR_ONLY; + opts.flags = GIT_STATUS_OPT_DEFAULTS; + + cl_git_pass( + git_status_foreach_ext(repo, &opts, cb_status__normal, &counts) ); + cl_assert_equal_i(counts.expected_entry_count, counts.entry_count); + cl_assert_equal_i(0, counts.wrong_status_flags_count); + cl_assert_equal_i(0, counts.wrong_sorted_path); +} +