Skip to content

Commit 683ff4f

Browse files
author
Edward Thomson
authored
Merge pull request libgit2#4111 from pks-t/pks/dos-prefix
dirname with DOS prefixes
2 parents 2b8163b + 3428a52 commit 683ff4f

File tree

4 files changed

+58
-24
lines changed

4 files changed

+58
-24
lines changed

src/path.c

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,42 @@ int git_path_basename_r(git_buf *buffer, const char *path)
110110
return result;
111111
}
112112

113+
/*
114+
* Determine if the path is a Windows prefix and, if so, returns
115+
* its actual lentgh. If it is not a prefix, returns -1.
116+
*/
117+
static int win32_prefix_length(const char *path, int len)
118+
{
119+
#ifndef GIT_WIN32
120+
GIT_UNUSED(path);
121+
GIT_UNUSED(len);
122+
#else
123+
/*
124+
* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
125+
* 'C:/' here
126+
*/
127+
if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path))
128+
return 2;
129+
130+
/*
131+
* Similarly checks if we're dealing with a network computer name
132+
* '//computername/.git' will return '//computername/'
133+
*/
134+
if (looks_like_network_computer_name(path, len))
135+
return len;
136+
#endif
137+
138+
return -1;
139+
}
140+
113141
/*
114142
* Based on the Android implementation, BSD licensed.
115143
* Check http://android.git.kernel.org/
116144
*/
117145
int git_path_dirname_r(git_buf *buffer, const char *path)
118146
{
119147
const char *endp;
120-
int result, len;
148+
int is_prefix = 0, len;
121149

122150
/* Empty or NULL string gets treated as "." */
123151
if (path == NULL || *path == '\0') {
@@ -131,6 +159,11 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
131159
while (endp > path && *endp == '/')
132160
endp--;
133161

162+
if ((len = win32_prefix_length(path, endp - path + 1)) > 0) {
163+
is_prefix = 1;
164+
goto Exit;
165+
}
166+
134167
/* Find the start of the dir */
135168
while (endp > path && *endp != '/')
136169
endp--;
@@ -146,35 +179,23 @@ int git_path_dirname_r(git_buf *buffer, const char *path)
146179
endp--;
147180
} while (endp > path && *endp == '/');
148181

149-
/* Cast is safe because max path < max int */
150-
len = (int)(endp - path + 1);
151-
152-
#ifdef GIT_WIN32
153-
/* Mimic unix behavior where '/.git' returns '/': 'C:/.git' will return
154-
'C:/' here */
155-
156-
if (len == 2 && LOOKS_LIKE_DRIVE_PREFIX(path)) {
157-
len = 3;
182+
if ((len = win32_prefix_length(path, endp - path + 1)) > 0) {
183+
is_prefix = 1;
158184
goto Exit;
159185
}
160186

161-
/* Similarly checks if we're dealing with a network computer name
162-
'//computername/.git' will return '//computername/' */
163-
164-
if (looks_like_network_computer_name(path, len)) {
165-
len++;
166-
goto Exit;
167-
}
168-
169-
#endif
187+
/* Cast is safe because max path < max int */
188+
len = (int)(endp - path + 1);
170189

171190
Exit:
172-
result = len;
173-
174-
if (buffer != NULL && git_buf_set(buffer, path, len) < 0)
175-
return -1;
191+
if (buffer) {
192+
if (git_buf_set(buffer, path, len) < 0)
193+
return -1;
194+
if (is_prefix && git_buf_putc(buffer, '/') < 0)
195+
return -1;
196+
}
176197

177-
return result;
198+
return len;
178199
}
179200

180201

tests/core/path.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,12 @@ void test_core_path__00_dirname(void)
8989
check_dirname(REP16("/abc"), REP15("/abc"));
9090

9191
#ifdef GIT_WIN32
92+
check_dirname("C:/", "C:/");
93+
check_dirname("C:", "C:/");
9294
check_dirname("C:/path/", "C:/");
9395
check_dirname("C:/path", "C:/");
96+
check_dirname("//computername/", "//computername/");
97+
check_dirname("//computername", "//computername/");
9498
check_dirname("//computername/path/", "//computername/");
9599
check_dirname("//computername/path", "//computername/");
96100
check_dirname("//computername/sub/path/", "//computername/sub");

tests/repo/discover.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,12 @@ void test_repo_discover__discovery_starting_at_file_succeeds(void)
199199

200200
ensure_repository_discover(SUB_REPOSITORY_FOLDER "/file", ceiling_dirs.ptr, SUB_REPOSITORY_GITDIR);
201201
}
202+
203+
void test_repo_discover__discovery_starting_at_system_root_causes_no_hang(void)
204+
{
205+
#ifdef GIT_WIN32
206+
git_buf out = GIT_BUF_INIT;
207+
cl_git_fail(git_repository_discover(&out, "C:/", 0, NULL));
208+
cl_git_fail(git_repository_discover(&out, "//localhost/", 0, NULL));
209+
#endif
210+
}

tests/resources/testrepo.git/index

100644100755
File mode changed.

0 commit comments

Comments
 (0)