]>
git.proxmox.com Git - libgit2.git/blob - src/win32/path_w32.c
2 * Copyright (C) the libgit2 contributors. All rights reserved.
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
13 #define PATH__NT_NAMESPACE L"\\\\?\\"
14 #define PATH__NT_NAMESPACE_LEN 4
16 #define PATH__ABSOLUTE_LEN 3
18 #define path__is_dirsep(p) ((p) == '/' || (p) == '\\')
20 #define path__is_absolute(p) \
21 (git__isalpha((p)[0]) && (p)[1] == ':' && ((p)[2] == '\\' || (p)[2] == '/'))
23 #define path__is_nt_namespace(p) \
24 (((p)[0] == '\\' && (p)[1] == '\\' && (p)[2] == '?' && (p)[3] == '\\') || \
25 ((p)[0] == '/' && (p)[1] == '/' && (p)[2] == '?' && (p)[3] == '/'))
27 #define path__is_unc(p) \
28 (((p)[0] == '\\' && (p)[1] == '\\') || ((p)[0] == '/' && (p)[1] == '/'))
30 GIT_INLINE(int) path__cwd(wchar_t *path
, int size
)
34 if ((len
= GetCurrentDirectoryW(size
, path
)) == 0) {
35 errno
= GetLastError() == ERROR_ACCESS_DENIED
? EACCES
: ENOENT
;
37 } else if (len
> size
) {
42 /* The Win32 APIs may return "\\?\" once you've used it first.
43 * But it may not. What a gloriously predictible API!
45 if (wcsncmp(path
, PATH__NT_NAMESPACE
, PATH__NT_NAMESPACE_LEN
))
48 len
-= PATH__NT_NAMESPACE_LEN
;
50 memmove(path
, path
+ PATH__NT_NAMESPACE_LEN
, sizeof(wchar_t) * len
);
54 static wchar_t *path__skip_server(wchar_t *path
)
58 for (c
= path
; *c
; c
++) {
59 if (path__is_dirsep(*c
))
66 static wchar_t *path__skip_prefix(wchar_t *path
)
68 if (path__is_nt_namespace(path
)) {
69 path
+= PATH__NT_NAMESPACE_LEN
;
71 if (wcsncmp(path
, L
"UNC\\", 4) == 0)
72 path
= path__skip_server(path
+ 4);
73 else if (path__is_absolute(path
))
74 path
+= PATH__ABSOLUTE_LEN
;
75 } else if (path__is_absolute(path
)) {
76 path
+= PATH__ABSOLUTE_LEN
;
77 } else if (path__is_unc(path
)) {
78 path
= path__skip_server(path
+ 2);
84 int git_win32_path_canonicalize(git_win32_path path
)
86 wchar_t *base
, *from
, *to
, *next
;
89 base
= to
= path__skip_prefix(path
);
91 /* Unposixify if the prefix */
92 for (from
= path
; from
< to
; from
++) {
98 for (next
= from
; *next
; ++next
) {
110 if (len
== 1 && from
[0] == L
'.')
111 /* do nothing with singleton dot */;
113 else if (len
== 2 && from
[0] == L
'.' && from
[1] == L
'.') {
115 /* no more path segments to strip, eat the "../" */
121 /* back up a path segment */
122 while (to
> base
&& to
[-1] == L
'\\') to
--;
123 while (to
> base
&& to
[-1] != L
'\\') to
--;
126 if (*next
== L
'\\' && *from
!= L
'\\')
130 memmove(to
, from
, sizeof(wchar_t) * len
);
137 while (*from
== L
'\\') from
++;
140 /* Strip trailing backslashes */
141 while (to
> base
&& to
[-1] == L
'\\') to
--;
148 int git_win32_path__cwd(wchar_t *out
, size_t len
)
152 if ((cwd_len
= path__cwd(out
, len
)) < 0)
156 if (wcsncmp(L
"\\\\", out
, 2) == 0) {
157 /* Our buffer must be at least 5 characters larger than the
158 * current working directory: we swallow one of the leading
159 * '\'s, but we we add a 'UNC' specifier to the path, plus
160 * a trailing directory separator, plus a NUL.
162 if (cwd_len
> MAX_PATH
- 4) {
163 errno
= ENAMETOOLONG
;
167 memmove(out
+2, out
, sizeof(wchar_t) * cwd_len
);
175 /* Our buffer must be at least 2 characters larger than the current
176 * working directory. (One character for the directory separator,
179 else if (cwd_len
> MAX_PATH
- 2) {
180 errno
= ENAMETOOLONG
;
187 int git_win32_path_from_utf8(git_win32_path out
, const char *src
)
191 /* All win32 paths are in NT-prefixed format, beginning with "\\?\". */
192 memcpy(dest
, PATH__NT_NAMESPACE
, sizeof(wchar_t) * PATH__NT_NAMESPACE_LEN
);
193 dest
+= PATH__NT_NAMESPACE_LEN
;
195 /* See if this is an absolute path (beginning with a drive letter) */
196 if (path__is_absolute(src
)) {
197 if (git__utf8_to_16(dest
, MAX_PATH
, src
) < 0)
200 /* File-prefixed NT-style paths beginning with \\?\ */
201 else if (path__is_nt_namespace(src
)) {
202 /* Skip the NT prefix, the destination already contains it */
203 if (git__utf8_to_16(dest
, MAX_PATH
, src
+ PATH__NT_NAMESPACE_LEN
) < 0)
207 else if (path__is_unc(src
)) {
208 memcpy(dest
, L
"UNC\\", sizeof(wchar_t) * 4);
211 /* Skip the leading "\\" */
212 if (git__utf8_to_16(dest
, MAX_PATH
- 2, src
+ 2) < 0)
215 /* Absolute paths omitting the drive letter */
216 else if (src
[0] == '\\' || src
[0] == '/') {
217 if (path__cwd(dest
, MAX_PATH
) < 0)
220 if (!path__is_absolute(dest
)) {
225 /* Skip the drive letter specification ("C:") */
226 if (git__utf8_to_16(dest
+ 2, MAX_PATH
- 2, src
) < 0)
233 if ((cwd_len
= git_win32_path__cwd(dest
, MAX_PATH
)) < 0)
236 dest
[cwd_len
++] = L
'\\';
238 if (git__utf8_to_16(dest
+ cwd_len
, MAX_PATH
- cwd_len
, src
) < 0)
242 return git_win32_path_canonicalize(out
);
245 int git_win32_path_to_utf8(git_win32_utf8_path dest
, const wchar_t *src
)
250 /* Strip NT namespacing "\\?\" */
251 if (path__is_nt_namespace(src
)) {
254 /* "\\?\UNC\server\share" -> "\\server\share" */
255 if (wcsncmp(src
, L
"UNC\\", 4) == 0) {
258 memcpy(dest
, "\\\\", 2);
263 if ((len
= git__utf16_to_8(out
, GIT_WIN_PATH_UTF8
, src
)) < 0)
266 git_path_mkposix(dest
);
271 char *git_win32_path_8dot3_name(const char *path
)
273 git_win32_path longpath
, shortpath
;
276 int len
, namelen
= 1;
278 if (git_win32_path_from_utf8(longpath
, path
) < 0)
281 len
= GetShortPathNameW(longpath
, shortpath
, GIT_WIN_PATH_UTF16
);
283 while (len
&& shortpath
[len
-1] == L
'\\')
284 shortpath
[--len
] = L
'\0';
286 if (len
== 0 || len
>= GIT_WIN_PATH_UTF16
)
289 for (start
= shortpath
+ (len
- 1);
290 start
> shortpath
&& *(start
-1) != '/' && *(start
-1) != '\\';
294 /* We may not have actually been given a short name. But if we have,
295 * it will be in the ASCII byte range, so we don't need to worry about
296 * multi-byte sequences and can allocate naively.
298 if (namelen
> 12 || (shortname
= git__malloc(namelen
+ 1)) == NULL
)
301 if ((len
= git__utf16_to_8(shortname
, namelen
+ 1, start
)) < 0)