Skip to content

Commit f2b114b

Browse files
committed
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.
1 parent fb7da15 commit f2b114b

File tree

3 files changed

+81
-2
lines changed

3 files changed

+81
-2
lines changed

src/win32/path_w32.c

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
#define path__is_unc(p) \
2626
(((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
2727

28+
#define path__startswith_slash(p) \
29+
((p)[0] == '\\' || (p)[0] == '/')
30+
2831
GIT_INLINE(int) path__cwd(wchar_t *path, int size)
2932
{
3033
int len;
@@ -221,7 +224,7 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
221224
goto on_error;
222225
}
223226
/* Absolute paths omitting the drive letter */
224-
else if (src[0] == '\\' || src[0] == '/') {
227+
else if (path__startswith_slash(src)) {
225228
if (path__cwd(dest, MAX_PATH) < 0)
226229
goto on_error;
227230

@@ -257,6 +260,30 @@ int git_win32_path_from_utf8(git_win32_path out, const char *src)
257260
return -1;
258261
}
259262

263+
int git_win32_path_relative_from_utf8(git_win32_path out, const char *src)
264+
{
265+
wchar_t *dest = out, *p;
266+
int len;
267+
268+
/* Handle absolute paths */
269+
if (git_path_is_absolute(src) ||
270+
path__is_nt_namespace(src) ||
271+
path__is_unc(src) ||
272+
path__startswith_slash(src)) {
273+
return git_win32_path_from_utf8(out, src);
274+
}
275+
276+
if ((len = git__utf8_to_16(dest, MAX_PATH, src)) < 0)
277+
return -1;
278+
279+
for (p = dest; p < (dest + len); p++) {
280+
if (*p == L'/')
281+
*p = L'\\';
282+
}
283+
284+
return len;
285+
}
286+
260287
int git_win32_path_to_utf8(git_win32_utf8_path dest, const wchar_t *src)
261288
{
262289
char *out = dest;

src/win32/path_w32.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,26 @@
1111
#include "vector.h"
1212

1313
/**
14-
* Create a Win32 path (in UCS-2 format) from a UTF-8 string.
14+
* Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given
15+
* path is relative, then it will be turned into an absolute path by having
16+
* the current working directory prepended.
1517
*
1618
* @param dest The buffer to receive the wide string.
1719
* @param src The UTF-8 string to convert.
1820
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
1921
*/
2022
extern int git_win32_path_from_utf8(git_win32_path dest, const char *src);
2123

24+
/**
25+
* Create a Win32 path (in UCS-2 format) from a UTF-8 string. If the given
26+
* path is relative, then it will not be turned into an absolute path.
27+
*
28+
* @param dest The buffer to receive the wide string.
29+
* @param src The UTF-8 string to convert.
30+
* @return The length of the wide string, in characters (not counting the NULL terminator), or < 0 for failure
31+
*/
32+
extern int git_win32_path_relative_from_utf8(git_win32_path dest, const char *src);
33+
2234
/**
2335
* Canonicalize a Win32 UCS-2 path so that it is suitable for delivery to the
2436
* Win32 APIs: remove multiple directory separators, squashing to a single one,

tests/path/win32.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,21 @@ void test_utf8_to_utf16(const char *utf8_in, const wchar_t *utf16_expected)
2121
#endif
2222
}
2323

24+
void test_utf8_to_utf16_relative(const char* utf8_in, const wchar_t* utf16_expected)
25+
{
26+
#ifdef GIT_WIN32
27+
git_win32_path path_utf16;
28+
int path_utf16len;
29+
30+
cl_assert((path_utf16len = git_win32_path_relative_from_utf8(path_utf16, utf8_in)) >= 0);
31+
cl_assert_equal_wcs(utf16_expected, path_utf16);
32+
cl_assert_equal_i(wcslen(utf16_expected), path_utf16len);
33+
#else
34+
GIT_UNUSED(utf8_in);
35+
GIT_UNUSED(utf16_expected);
36+
#endif
37+
}
38+
2439
void test_path_win32__utf8_to_utf16(void)
2540
{
2641
#ifdef GIT_WIN32
@@ -129,6 +144,31 @@ void test_path_win32__absolute_from_relative(void)
129144
#endif
130145
}
131146

147+
void test_path_win32__keeps_relative(void)
148+
{
149+
#ifdef GIT_WIN32
150+
/* Relative paths stay relative */
151+
test_utf8_to_utf16_relative("Foo", L"Foo");
152+
test_utf8_to_utf16_relative("..\\..\\Foo", L"..\\..\\Foo");
153+
test_utf8_to_utf16_relative("Foo\\..", L"Foo\\..");
154+
test_utf8_to_utf16_relative("Foo\\..\\..", L"Foo\\..\\..");
155+
test_utf8_to_utf16_relative("Foo\\Bar", L"Foo\\Bar");
156+
test_utf8_to_utf16_relative("Foo\\..\\Bar", L"Foo\\..\\Bar");
157+
test_utf8_to_utf16_relative("../../Foo", L"..\\..\\Foo");
158+
test_utf8_to_utf16_relative("Foo/..", L"Foo\\..");
159+
test_utf8_to_utf16_relative("Foo/../..", L"Foo\\..\\..");
160+
test_utf8_to_utf16_relative("Foo/Bar", L"Foo\\Bar");
161+
test_utf8_to_utf16_relative("Foo/../Bar", L"Foo\\..\\Bar");
162+
test_utf8_to_utf16_relative("Foo/../Bar/", L"Foo\\..\\Bar\\");
163+
test_utf8_to_utf16_relative("", L"");
164+
165+
/* Absolute paths are canonicalized */
166+
test_utf8_to_utf16_relative("\\Foo", L"\\\\?\\C:\\Foo");
167+
test_utf8_to_utf16_relative("/Foo/Bar/", L"\\\\?\\C:\\Foo\\Bar");
168+
test_utf8_to_utf16_relative("\\\\server\\c$\\unc\\path", L"\\\\?\\UNC\\server\\c$\\unc\\path");
169+
#endif
170+
}
171+
132172
#ifdef GIT_WIN32
133173
static void test_canonicalize(const wchar_t *in, const wchar_t *expected)
134174
{

0 commit comments

Comments
 (0)