Commit ae7ffea96132ee8df08f7c18aac6b36acbd689cf

nulltoken 2011-01-22T14:04:32

Fixed a parsing issue in git_prettify_dir_path().

diff --git a/src/fileops.c b/src/fileops.c
index 5953f26..191aa91 100644
--- a/src/fileops.c
+++ b/src/fileops.c
@@ -394,8 +394,9 @@ static int retrieve_previous_path_component_start(const char *path)
 int git_prettify_dir_path(char *buffer_out, const char *path)
 {
 	int len = 0;
-	char *current;
+	char *current, *end;
 	const char *buffer_out_start, *buffer_end;
+	int only_dots;
 
 	buffer_out_start = buffer_out;
 	current = (char *)path;
@@ -408,40 +409,52 @@ int git_prettify_dir_path(char *buffer_out, const char *path)
 			continue;
 		}
 		
-		/* Skip current directory */
-		if (*current == '.') {
-			current++;
-
-			/* Handle the double-dot upward directory navigation */
-			if (current < buffer_end && *current == '.') {
-				current++;
-
-				/* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */
-				if (*current == '.')
-					return GIT_ERROR;
+		end = current;
+		only_dots = 1;
+
+		/* Seek end of path segment */
+		while (end < buffer_end && *end !='/')
+		{
+			only_dots &= (*end == '.');
+			end++;
+		}
 
-				*buffer_out ='\0';
-				len = retrieve_previous_path_component_start(buffer_out_start);
-				if (len < GIT_SUCCESS)
-					return GIT_ERROR;
+		/* Skip current directory */
+		if (only_dots && end == current + 1)
+		{
+			current += 2;
+			continue;
+		}
 
-				buffer_out = (char *)buffer_out_start + len;
-			}
+		/* Handle the double-dot upward directory navigation */
+		if (only_dots && end == current + 2)
+		{
+			*buffer_out ='\0';
+			len = retrieve_previous_path_component_start(buffer_out_start);
+			if (len < GIT_SUCCESS)
+				return GIT_ERROR;
+
+			buffer_out = (char *)buffer_out_start + len;
+			
+			current += 3;
+			continue;
+		}
 
-			if (current < buffer_end && *current == '/')
-				current++;
+		/* Guard against potential multiple dot path traversal (cf http://cwe.mitre.org/data/definitions/33.html) */
+		if (only_dots && end > current)
+			return GIT_ERROR;
 
-			continue;
+		/* Copy to output the path segment */
+		while (current < end)
+		{
+			*buffer_out++ = *current++;
+			len++;
 		}
 
-		*buffer_out++ = *current++;
+		*buffer_out++ = '/';
 		len++;
 	}
 
-	/* Add a trailing slash if required */
-	if (len > 0 && buffer_out_start[len-1] != '/')
-		*buffer_out++ = '/';
-
 	*buffer_out = '\0';
 
 	return GIT_SUCCESS;
diff --git a/tests/t0005-path.c b/tests/t0005-path.c
index 222e07c..dc2ccc9 100644
--- a/tests/t0005-path.c
+++ b/tests/t0005-path.c
@@ -20,6 +20,10 @@ static int ensure_normalized(const char *input_path, const char *expected_path)
 }
 
 BEGIN_TEST(path_prettifying)
+	must_pass(ensure_normalized("./testrepo.git", "testrepo.git/"));
+	must_pass(ensure_normalized("./.git", ".git/"));
+	must_pass(ensure_normalized("./git.", "git./"));
+	must_pass(ensure_normalized("git./", "git./"));
 	must_pass(ensure_normalized("", ""));
 	must_pass(ensure_normalized(".", ""));
 	must_pass(ensure_normalized("./", ""));
@@ -53,7 +57,11 @@ BEGIN_TEST(path_prettifying)
 	must_fail(ensure_normalized("d1/...", NULL));
 	must_fail(ensure_normalized("d1/.../", NULL));
 	must_fail(ensure_normalized("d1/.../d2", NULL));
-	
+
+	must_pass(ensure_normalized("/./testrepo.git", "/testrepo.git/"));
+	must_pass(ensure_normalized("/./.git", "/.git/"));
+	must_pass(ensure_normalized("/./git.", "/git./"));
+	must_pass(ensure_normalized("/git./", "/git./"));
 	must_pass(ensure_normalized("/", "/"));
 	must_pass(ensure_normalized("//", "/"));
 	must_pass(ensure_normalized("///", "/"));