diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2020-03-08 18:11:45 +0000 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2020-03-10 20:10:20 +0000 |
commit | f2b114ba828b561c63164dcfcc2707f11e31c8d8 (patch) | |
tree | 4d2c1b5cccd38e6358c1ed57039cf24c6058b0da | |
parent | fb7da15452f7069e6bfa7d39b7703e32eaa84d34 (diff) | |
download | libgit2-f2b114ba828b561c63164dcfcc2707f11e31c8d8.tar.gz |
win32: introduce relative path handling function
Add a function that takes a (possibly) relative UTF-8 path and emits a
UTF-16 path with forward slashes translated to backslashes. If the
given path is, in fact, absolute, it will be translated to absolute path
handling rules.
-rw-r--r-- | src/win32/path_w32.c | 29 | ||||
-rw-r--r-- | src/win32/path_w32.h | 14 | ||||
-rw-r--r-- | tests/path/win32.c | 40 |
3 files changed, 81 insertions, 2 deletions
diff --git a/src/win32/path_w32.c b/src/win32/path_w32.c index eda85abf4..18b43e728 100644 --- a/src/win32/path_w32.c +++ b/src/win32/path_w32.c @@ -25,6 +25,9 @@ #define path__is_unc(p) \ (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/')) +#define path__startswith_slash(p) \ + ((p)[0] == '\\' || (p)[0] == '/') + GIT_INLINE(int) path__cwd(wchar_t *path, int size) { int len; @@ -221,7 +224,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src) goto on_error; } /* Absolute paths omitting the drive letter */ - else if (src[0] == '\\' || src[0] == '/') { + else if (path__startswith_slash(src)) { if (path__cwd(dest, MAX_PATH) < 0) goto on_error; @@ -257,6 +260,30 @@ on_error: return -1; } +int git_win32_path_relative_from_utf8(git_win32_path out, const char *src) +{ + wchar_t *dest = out, *p; + int len; + + /* Handle absolute paths */ + if (git_path_is_absolute(src) || + path__is_nt_namespace(src) || + path__is_unc(src) || + path__startswith_slash(src)) { + return git_win32_path_from_utf8(out, src); + } + + if ((len = git__utf8_to_16(dest, MAX_PATH, src)) < 0) + return -1; + + for (p = dest; p < (dest + len); p++) { + if (*p == L'/') + *p = L'\\'; + } + + return len; +} + int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src) { char *out = dest; diff --git a/src/win32/path_w32.h b/src/win32/path_w32.h index 6d546f2f8..dab8b96fa 100644 --- a/src/win32/path_w32.h +++ b/src/win32/path_w32.h @@ -11,7 +11,9 @@ #include "vector.h" /** - * Create a Win32 path (in UCS-2 format) from a UTF-8 string. + * Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given + * path is relative, then it will be turned into an absolute path by having + * the current working directory prepended. * * @param dest The buffer to receive the wide string. * @param src The UTF-8 string to convert. @@ -20,6 +22,16 @@ extern int git_win32_path_from_utf8(git_win32_path dest, const char *src); /** + * Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given + * path is relative, then it will not be turned into an absolute path. + * + * @param dest The buffer to receive the wide string. + * @param src The UTF-8 string to convert. + * @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure + */ +extern int git_win32_path_relative_from_utf8(git_win32_path dest, const char *src); + +/** * Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the * Win32 APIs: remove multiple directory separators, squashing to a single one, * strip trailing directory separators, ensure directory separators are all diff --git a/tests/path/win32.c b/tests/path/win32.c index 347de8f43..b416e9e60 100644 --- a/tests/path/win32.c +++ b/tests/path/win32.c @@ -21,6 +21,21 @@ void test_utf8_to_utf16(const char *utf8_in, const wchar_t *utf16_expected) #endif } +void test_utf8_to_utf16_relative(const char* utf8_in, const wchar_t* utf16_expected) +{ +#ifdef GIT_WIN32 + git_win32_path path_utf16; + int path_utf16len; + + cl_assert((path_utf16len = git_win32_path_relative_from_utf8(path_utf16, utf8_in)) >= 0); + cl_assert_equal_wcs(utf16_expected, path_utf16); + cl_assert_equal_i(wcslen(utf16_expected), path_utf16len); +#else + GIT_UNUSED(utf8_in); + GIT_UNUSED(utf16_expected); +#endif +} + void test_path_win32__utf8_to_utf16(void) { #ifdef GIT_WIN32 @@ -129,6 +144,31 @@ void test_path_win32__absolute_from_relative(void) #endif } +void test_path_win32__keeps_relative(void) +{ +#ifdef GIT_WIN32 + /* Relative paths stay relative */ + test_utf8_to_utf16_relative("Foo", L"Foo"); + test_utf8_to_utf16_relative("..\\..\\Foo", L"..\\..\\Foo"); + test_utf8_to_utf16_relative("Foo\\..", L"Foo\\.."); + test_utf8_to_utf16_relative("Foo\\..\\..", L"Foo\\..\\.."); + test_utf8_to_utf16_relative("Foo\\Bar", L"Foo\\Bar"); + test_utf8_to_utf16_relative("Foo\\..\\Bar", L"Foo\\..\\Bar"); + test_utf8_to_utf16_relative("../../Foo", L"..\\..\\Foo"); + test_utf8_to_utf16_relative("Foo/..", L"Foo\\.."); + test_utf8_to_utf16_relative("Foo/../..", L"Foo\\..\\.."); + test_utf8_to_utf16_relative("Foo/Bar", L"Foo\\Bar"); + test_utf8_to_utf16_relative("Foo/../Bar", L"Foo\\..\\Bar"); + test_utf8_to_utf16_relative("Foo/../Bar/", L"Foo\\..\\Bar\\"); + test_utf8_to_utf16_relative("", L""); + + /* Absolute paths are canonicalized */ + test_utf8_to_utf16_relative("\\Foo", L"\\\\?\\C:\\Foo"); + test_utf8_to_utf16_relative("/Foo/Bar/", L"\\\\?\\C:\\Foo\\Bar"); + test_utf8_to_utf16_relative("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path"); +#endif +} + #ifdef GIT_WIN32 static void test_canonicalize(const wchar_t *in, const wchar_t *expected) { |