Commit 9e8d75c7d447531d81133bf880c1a5f45a45c340

Patrick Steinhardt 2017-02-08T11:41:10

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 '/'.

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