path: add git__percent_decode()
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 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
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");
+}