Commit 459e2dcd7deb379b9a013ab70aa70206fc17f16a

nulltoken 2011-12-27T11:18:57

path: add git__percent_decode()

diff --git a/src/path.c b/src/path.c
index e4b49f3..bd62a3e 100644
--- a/src/path.c
+++ b/src/path.c
@@ -237,3 +237,38 @@ void git_path_string_to_dir(char* path, size_t size)
 	}
 }
 
+int git__percent_decode(git_buf *decoded_out, const char *input)
+{
+	int len, hi, lo, i, error = GIT_SUCCESS;
+	assert(decoded_out && input);
+
+	len = strlen(input);
+	git_buf_clear(decoded_out);
+
+	for(i = 0; i < len; i++)
+	{
+		char c = input[i];
+
+		if (c != '%')
+			goto append;
+
+		if (i >= len - 2)
+			goto append;
+
+		hi = git__fromhex(input[i + 1]);
+		lo = git__fromhex(input[i + 2]);
+
+		if (hi < 0 || lo < 0)
+			goto append;
+
+		c = (char)(hi << 4 | lo);
+		i += 2;
+
+append:
+		error = git_buf_putc(decoded_out, c);
+		if (error < GIT_SUCCESS)
+			return git__rethrow(error, "Failed to percent decode '%s'.", input);
+	}
+
+	return error;
+}
diff --git a/src/path.h b/src/path.h
index 0c8cc34..6397fee 100644
--- a/src/path.h
+++ b/src/path.h
@@ -74,4 +74,6 @@ GIT_INLINE(void) git_path_mkposix(char *path)
 #	define git_path_mkposix(p) /* blank */
 #endif
 
+extern int git__percent_decode(git_buf *decoded_out, const char *input);
+
 #endif
diff --git a/tests-clay/clay.h b/tests-clay/clay.h
index c2b69c8..b2b31be 100644
--- a/tests-clay/clay.h
+++ b/tests-clay/clay.h
@@ -116,6 +116,7 @@ extern void test_core_path__5_joins(void);
 extern void test_core_path__6_long_joins(void);
 extern void test_core_path__7_path_to_dir(void);
 extern void test_core_path__8_self_join(void);
+extern void test_core_path__9_percent_decode(void);
 extern void test_core_rmdir__delete_recursive(void);
 extern void test_core_rmdir__fail_to_delete_non_empty_dir(void);
 extern void test_core_rmdir__initialize(void);
diff --git a/tests-clay/clay_main.c b/tests-clay/clay_main.c
index 7df342c..e6bb804 100644
--- a/tests-clay/clay_main.c
+++ b/tests-clay/clay_main.c
@@ -180,7 +180,8 @@ static const struct clay_func _clay_cb_core_path[] = {
 	{"5_joins", &test_core_path__5_joins},
 	{"6_long_joins", &test_core_path__6_long_joins},
 	{"7_path_to_dir", &test_core_path__7_path_to_dir},
-	{"8_self_join", &test_core_path__8_self_join}
+	{"8_self_join", &test_core_path__8_self_join},
+	{"9_percent_decode", &test_core_path__9_percent_decode}
 };
 static const struct clay_func _clay_cb_core_rmdir[] = {
     {"delete_recursive", &test_core_rmdir__delete_recursive},
@@ -381,7 +382,7 @@ static const struct clay_suite _clay_suites[] = {
         "core::path",
         {NULL, NULL},
         {NULL, NULL},
-        _clay_cb_core_path, 7
+        _clay_cb_core_path, 8
     },
 	{
         "core::rmdir",
@@ -548,7 +549,7 @@ static const struct clay_suite _clay_suites[] = {
 };
 
 static size_t _clay_suite_count = 39;
-static size_t _clay_callback_count = 123;
+static size_t _clay_callback_count = 124;
 
 /* Core test functions */
 static void
diff --git a/tests-clay/core/path.c b/tests-clay/core/path.c
index 49f85f0..8744247 100644
--- a/tests-clay/core/path.c
+++ b/tests-clay/core/path.c
@@ -273,3 +273,27 @@ void test_core_path__8_self_join(void)
 	
 	git_buf_free(&path);
 }
+
+static void check_percent_decoding(const char *expected_result, const char *input)
+{
+	git_buf buf = GIT_BUF_INIT;
+
+	cl_git_pass(git__percent_decode(&buf, input));
+	cl_assert_strequal(expected_result, git_buf_cstr(&buf));
+
+	git_buf_free(&buf);
+}
+
+void test_core_path__9_percent_decode(void)
+{
+	check_percent_decoding("abcd", "abcd");
+	check_percent_decoding("a2%", "a2%");
+	check_percent_decoding("a2%3", "a2%3");
+	check_percent_decoding("a2%%3", "a2%%3");
+	check_percent_decoding("a2%3z", "a2%3z");
+	check_percent_decoding("a,", "a%2c");
+	check_percent_decoding("a21", "a2%31");
+	check_percent_decoding("a2%1", "a2%%31");
+	check_percent_decoding("a bc ", "a%20bc%20");
+	check_percent_decoding("Vicent Mart" "\355", "Vicent%20Mart%ED");
+}