path: ensure dirname on Win32 prefix always has a trailing '/' When calling `git_path_dirname_r` on a Win32 prefix, e.g. a drive or network share prefix, we always want to return the trailing '/'. This does not work currently when passing in a path like 'C:', where the '/' would not be appended correctly. Fix this by appending a '/' if we try to normalize a Win32 prefix and there is no trailing '/'.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83
diff --git a/src/path.c b/src/path.c
index 3c78c8b..c3d3eb1 100644
--- a/src/path.c
+++ b/src/path.c
@@ -125,14 +125,14 @@ static int win32_prefix_length(const char *path, int len)
* 'C:/' here
*/
if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path))
- return 3;
+ return 2;
/*
* Similarly checks if we're dealing with a network computer name
* '//computername/.git' will return '//computername/'
*/
if (looks_like_network_computer_name(path, len))
- return len + 1;
+ return len;
#endif
return -1;
@@ -145,7 +145,7 @@ static int win32_prefix_length(const char *path, int len)
int git_path_dirname_r(git_buf *buffer, const char *path)
{
const char *endp;
- int len;
+ int is_prefix = 0, len;
/* Empty or NULL string gets treated as "." */
if (path == NULL || *path == '\0') {
@@ -159,8 +159,10 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
while (endp > path && *endp == '/')
endp--;
- if ((len = win32_prefix_length(path, endp - path + 1)) > 0)
+ if ((len = win32_prefix_length(path, endp - path + 1)) > 0) {
+ is_prefix = 1;
goto Exit;
+ }
/* Find the start of the dir */
while (endp > path && *endp != '/')
@@ -177,15 +179,21 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
endp--;
} while (endp > path && *endp == '/');
- if ((len = win32_prefix_length(path, endp - path + 1)) > 0)
+ if ((len = win32_prefix_length(path, endp - path + 1)) > 0) {
+ is_prefix = 1;
goto Exit;
+ }
/* Cast is safe because max path < max int */
len = (int)(endp - path + 1);
Exit:
- if (buffer != NULL && git_buf_set(buffer, path, len) < 0)
- return -1;
+ if (buffer) {
+ if (git_buf_set(buffer, path, len) < 0)
+ return -1;
+ if (is_prefix && git_buf_putc(buffer, '/') < 0)
+ return -1;
+ }
return len;
}
diff --git a/tests/core/path.c b/tests/core/path.c
index eaaaf72..fefe2ae 100644
--- a/tests/core/path.c
+++ b/tests/core/path.c
@@ -90,9 +90,11 @@ void test_core_path__00_dirname(void)
#ifdef GIT_WIN32
check_dirname("C:/", "C:/");
+ check_dirname("C:", "C:/");
check_dirname("C:/path/", "C:/");
check_dirname("C:/path", "C:/");
check_dirname("//computername/", "//computername/");
+ check_dirname("//computername", "//computername/");
check_dirname("//computername/path/", "//computername/");
check_dirname("//computername/path", "//computername/");
check_dirname("//computername/sub/path/", "//computername/sub");