Fossil SCM

Break the 260 character path barrier on Win32. Add test-case "win32-longpath" for proving that it works.

jan.nijtmans 2013-12-17 10:20 win32-longpath merge
Commit 4b49bb9cc3234b713c71a680ab7385788dfb6ef0
+5 -1
--- src/db.c
+++ src/db.c
@@ -711,12 +711,16 @@
711711
*/
712712
LOCAL sqlite3 *db_open(const char *zDbName){
713713
int rc;
714714
sqlite3 *db;
715715
716
-#if defined(__CYGWIN__)
716
+#if defined(__CYGWIN__) || defined(_WIN32)
717717
zDbName = fossil_utf8_to_filename(zDbName);
718
+#ifdef _WIN32
719
+ /* Convert back to utf-8. TODO: SQLite should handle this */
720
+ zDbName = fossil_filename_to_utf8(zDbName);
721
+#endif
718722
#endif
719723
if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
720724
rc = sqlite3_open_v2(
721725
zDbName, &db,
722726
SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
723727
--- src/db.c
+++ src/db.c
@@ -711,12 +711,16 @@
711 */
712 LOCAL sqlite3 *db_open(const char *zDbName){
713 int rc;
714 sqlite3 *db;
715
716 #if defined(__CYGWIN__)
717 zDbName = fossil_utf8_to_filename(zDbName);
 
 
 
 
718 #endif
719 if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
720 rc = sqlite3_open_v2(
721 zDbName, &db,
722 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
723
--- src/db.c
+++ src/db.c
@@ -711,12 +711,16 @@
711 */
712 LOCAL sqlite3 *db_open(const char *zDbName){
713 int rc;
714 sqlite3 *db;
715
716 #if defined(__CYGWIN__) || defined(_WIN32)
717 zDbName = fossil_utf8_to_filename(zDbName);
718 #ifdef _WIN32
719 /* Convert back to utf-8. TODO: SQLite should handle this */
720 zDbName = fossil_filename_to_utf8(zDbName);
721 #endif
722 #endif
723 if( g.fSqlTrace ) fossil_trace("-- sqlite3_open: [%s]\n", zDbName);
724 rc = sqlite3_open_v2(
725 zDbName, &db,
726 SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
727
+1 -1
--- src/main.c
+++ src/main.c
@@ -580,11 +580,11 @@
580580
#endif
581581
g.mainTimerId = fossil_timer_start();
582582
g.zVfsName = find_option("vfs",0,1);
583583
if( g.zVfsName==0 ){
584584
g.zVfsName = fossil_getenv("FOSSIL_VFS");
585
-#if defined(__CYGWIN__)
585
+#if defined(_WIN32) || defined(__CYGWIN__)
586586
if( g.zVfsName==0 && sqlite3_libversion_number()>=3008001 ){
587587
g.zVfsName = "win32-longpath";
588588
}
589589
#endif
590590
}
591591
--- src/main.c
+++ src/main.c
@@ -580,11 +580,11 @@
580 #endif
581 g.mainTimerId = fossil_timer_start();
582 g.zVfsName = find_option("vfs",0,1);
583 if( g.zVfsName==0 ){
584 g.zVfsName = fossil_getenv("FOSSIL_VFS");
585 #if defined(__CYGWIN__)
586 if( g.zVfsName==0 && sqlite3_libversion_number()>=3008001 ){
587 g.zVfsName = "win32-longpath";
588 }
589 #endif
590 }
591
--- src/main.c
+++ src/main.c
@@ -580,11 +580,11 @@
580 #endif
581 g.mainTimerId = fossil_timer_start();
582 g.zVfsName = find_option("vfs",0,1);
583 if( g.zVfsName==0 ){
584 g.zVfsName = fossil_getenv("FOSSIL_VFS");
585 #if defined(_WIN32) || defined(__CYGWIN__)
586 if( g.zVfsName==0 && sqlite3_libversion_number()>=3008001 ){
587 g.zVfsName = "win32-longpath";
588 }
589 #endif
590 }
591
+23 -7
--- src/utf8.c
+++ src/utf8.c
@@ -191,32 +191,48 @@
191191
**
192192
*/
193193
void *fossil_utf8_to_filename(const char *zUtf8){
194194
#ifdef _WIN32
195195
int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
196
- wchar_t *zUnicode = sqlite3_malloc( nChar * sizeof(wchar_t) );
196
+ /* Overallocate 6 chars, making some room for extended paths */
197
+ wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * sizeof(wchar_t) );
197198
wchar_t *wUnicode = zUnicode;
198199
if( zUnicode==0 ){
199200
return 0;
200201
}
201202
if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
202203
&& (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
203204
/* If path starts with "<drive>:[/\]", don't process the ':' */
204
- zUnicode[0] = zUtf8[0];
205
- memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
206
- wUnicode += 3;
207
- MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
205
+ if( nChar>MAX_PATH ) {
206
+ memcpy(zUnicode, L"\\\\?\\", 8);
207
+ wUnicode += 4;
208
+ MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, wUnicode, nChar);
209
+ wUnicode[2] = '\\';
210
+ wUnicode += 3;
211
+ goto finish;
212
+ }else{
213
+ zUnicode[0] = zUtf8[0];
214
+ memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
215
+ wUnicode += 3;
216
+ MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
217
+ }
208218
goto finish;
209219
}else if( (zUtf8[0]=='\\' || zUtf8[0]=='/') &&
210220
(zUtf8[1]=='\\' || zUtf8[1]=='/') ) {
211
- if( zUtf8[2]=='?' && nChar>5 ){
221
+ if (zUtf8[2]=='?' && nChar>5 ){
212222
/* Don't postprocess [?:] in extended path, but do '/' -> '\' */
213
- memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
223
+ memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
214224
MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, zUnicode+2, nChar-2);
215225
if( zUtf8[3]=='/' ) zUnicode[3]='\\';
216226
wUnicode += 6;
217227
goto finish;
228
+ }else if( nChar>MAX_PATH ){
229
+ /* Convert to extended UNC path. */
230
+ memcpy(zUnicode, L"\\\\?\\UNC\\", 16);
231
+ wUnicode += 8;
232
+ MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, wUnicode, nChar-8);
233
+ goto finish;
218234
}
219235
}
220236
MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
221237
finish:
222238
while( *wUnicode != '\0' ){
223239
224240
ADDED test/win32-longpath.test
--- src/utf8.c
+++ src/utf8.c
@@ -191,32 +191,48 @@
191 **
192 */
193 void *fossil_utf8_to_filename(const char *zUtf8){
194 #ifdef _WIN32
195 int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
196 wchar_t *zUnicode = sqlite3_malloc( nChar * sizeof(wchar_t) );
 
197 wchar_t *wUnicode = zUnicode;
198 if( zUnicode==0 ){
199 return 0;
200 }
201 if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
202 && (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
203 /* If path starts with "<drive>:[/\]", don't process the ':' */
204 zUnicode[0] = zUtf8[0];
205 memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
206 wUnicode += 3;
207 MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
 
 
 
 
 
 
 
 
 
208 goto finish;
209 }else if( (zUtf8[0]=='\\' || zUtf8[0]=='/') &&
210 (zUtf8[1]=='\\' || zUtf8[1]=='/') ) {
211 if( zUtf8[2]=='?' && nChar>5 ){
212 /* Don't postprocess [?:] in extended path, but do '/' -> '\' */
213 memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
214 MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, zUnicode+2, nChar-2);
215 if( zUtf8[3]=='/' ) zUnicode[3]='\\';
216 wUnicode += 6;
217 goto finish;
 
 
 
 
 
 
218 }
219 }
220 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
221 finish:
222 while( *wUnicode != '\0' ){
223
224 DDED test/win32-longpath.test
--- src/utf8.c
+++ src/utf8.c
@@ -191,32 +191,48 @@
191 **
192 */
193 void *fossil_utf8_to_filename(const char *zUtf8){
194 #ifdef _WIN32
195 int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
196 /* Overallocate 6 chars, making some room for extended paths */
197 wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * sizeof(wchar_t) );
198 wchar_t *wUnicode = zUnicode;
199 if( zUnicode==0 ){
200 return 0;
201 }
202 if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
203 && (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
204 /* If path starts with "<drive>:[/\]", don't process the ':' */
205 if( nChar>MAX_PATH ) {
206 memcpy(zUnicode, L"\\\\?\\", 8);
207 wUnicode += 4;
208 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, wUnicode, nChar);
209 wUnicode[2] = '\\';
210 wUnicode += 3;
211 goto finish;
212 }else{
213 zUnicode[0] = zUtf8[0];
214 memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
215 wUnicode += 3;
216 MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
217 }
218 goto finish;
219 }else if( (zUtf8[0]=='\\' || zUtf8[0]=='/') &&
220 (zUtf8[1]=='\\' || zUtf8[1]=='/') ) {
221 if (zUtf8[2]=='?' && nChar>5 ){
222 /* Don't postprocess [?:] in extended path, but do '/' -> '\' */
223 memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
224 MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, zUnicode+2, nChar-2);
225 if( zUtf8[3]=='/' ) zUnicode[3]='\\';
226 wUnicode += 6;
227 goto finish;
228 }else if( nChar>MAX_PATH ){
229 /* Convert to extended UNC path. */
230 memcpy(zUnicode, L"\\\\?\\UNC\\", 16);
231 wUnicode += 8;
232 MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, wUnicode, nChar-8);
233 goto finish;
234 }
235 }
236 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
237 finish:
238 while( *wUnicode != '\0' ){
239
240 DDED test/win32-longpath.test
+23 -7
--- src/utf8.c
+++ src/utf8.c
@@ -191,32 +191,48 @@
191191
**
192192
*/
193193
void *fossil_utf8_to_filename(const char *zUtf8){
194194
#ifdef _WIN32
195195
int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
196
- wchar_t *zUnicode = sqlite3_malloc( nChar * sizeof(wchar_t) );
196
+ /* Overallocate 6 chars, making some room for extended paths */
197
+ wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * sizeof(wchar_t) );
197198
wchar_t *wUnicode = zUnicode;
198199
if( zUnicode==0 ){
199200
return 0;
200201
}
201202
if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
202203
&& (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
203204
/* If path starts with "<drive>:[/\]", don't process the ':' */
204
- zUnicode[0] = zUtf8[0];
205
- memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
206
- wUnicode += 3;
207
- MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
205
+ if( nChar>MAX_PATH ) {
206
+ memcpy(zUnicode, L"\\\\?\\", 8);
207
+ wUnicode += 4;
208
+ MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, wUnicode, nChar);
209
+ wUnicode[2] = '\\';
210
+ wUnicode += 3;
211
+ goto finish;
212
+ }else{
213
+ zUnicode[0] = zUtf8[0];
214
+ memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
215
+ wUnicode += 3;
216
+ MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
217
+ }
208218
goto finish;
209219
}else if( (zUtf8[0]=='\\' || zUtf8[0]=='/') &&
210220
(zUtf8[1]=='\\' || zUtf8[1]=='/') ) {
211
- if( zUtf8[2]=='?' && nChar>5 ){
221
+ if (zUtf8[2]=='?' && nChar>5 ){
212222
/* Don't postprocess [?:] in extended path, but do '/' -> '\' */
213
- memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
223
+ memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
214224
MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, zUnicode+2, nChar-2);
215225
if( zUtf8[3]=='/' ) zUnicode[3]='\\';
216226
wUnicode += 6;
217227
goto finish;
228
+ }else if( nChar>MAX_PATH ){
229
+ /* Convert to extended UNC path. */
230
+ memcpy(zUnicode, L"\\\\?\\UNC\\", 16);
231
+ wUnicode += 8;
232
+ MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, wUnicode, nChar-8);
233
+ goto finish;
218234
}
219235
}
220236
MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
221237
finish:
222238
while( *wUnicode != '\0' ){
223239
224240
ADDED test/win32-longpath.test
--- src/utf8.c
+++ src/utf8.c
@@ -191,32 +191,48 @@
191 **
192 */
193 void *fossil_utf8_to_filename(const char *zUtf8){
194 #ifdef _WIN32
195 int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
196 wchar_t *zUnicode = sqlite3_malloc( nChar * sizeof(wchar_t) );
 
197 wchar_t *wUnicode = zUnicode;
198 if( zUnicode==0 ){
199 return 0;
200 }
201 if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
202 && (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
203 /* If path starts with "<drive>:[/\]", don't process the ':' */
204 zUnicode[0] = zUtf8[0];
205 memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
206 wUnicode += 3;
207 MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
 
 
 
 
 
 
 
 
 
208 goto finish;
209 }else if( (zUtf8[0]=='\\' || zUtf8[0]=='/') &&
210 (zUtf8[1]=='\\' || zUtf8[1]=='/') ) {
211 if( zUtf8[2]=='?' && nChar>5 ){
212 /* Don't postprocess [?:] in extended path, but do '/' -> '\' */
213 memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
214 MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, zUnicode+2, nChar-2);
215 if( zUtf8[3]=='/' ) zUnicode[3]='\\';
216 wUnicode += 6;
217 goto finish;
 
 
 
 
 
 
218 }
219 }
220 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
221 finish:
222 while( *wUnicode != '\0' ){
223
224 DDED test/win32-longpath.test
--- src/utf8.c
+++ src/utf8.c
@@ -191,32 +191,48 @@
191 **
192 */
193 void *fossil_utf8_to_filename(const char *zUtf8){
194 #ifdef _WIN32
195 int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
196 /* Overallocate 6 chars, making some room for extended paths */
197 wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * sizeof(wchar_t) );
198 wchar_t *wUnicode = zUnicode;
199 if( zUnicode==0 ){
200 return 0;
201 }
202 if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
203 && (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
204 /* If path starts with "<drive>:[/\]", don't process the ':' */
205 if( nChar>MAX_PATH ) {
206 memcpy(zUnicode, L"\\\\?\\", 8);
207 wUnicode += 4;
208 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, wUnicode, nChar);
209 wUnicode[2] = '\\';
210 wUnicode += 3;
211 goto finish;
212 }else{
213 zUnicode[0] = zUtf8[0];
214 memcpy(&zUnicode[1], L":\\", 2 * sizeof(wchar_t));
215 wUnicode += 3;
216 MultiByteToWideChar(CP_UTF8, 0, zUtf8+3, -1, wUnicode, nChar-3);
217 }
218 goto finish;
219 }else if( (zUtf8[0]=='\\' || zUtf8[0]=='/') &&
220 (zUtf8[1]=='\\' || zUtf8[1]=='/') ) {
221 if (zUtf8[2]=='?' && nChar>5 ){
222 /* Don't postprocess [?:] in extended path, but do '/' -> '\' */
223 memcpy(zUnicode, L"\\\\", 2 * sizeof(wchar_t));
224 MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, zUnicode+2, nChar-2);
225 if( zUtf8[3]=='/' ) zUnicode[3]='\\';
226 wUnicode += 6;
227 goto finish;
228 }else if( nChar>MAX_PATH ){
229 /* Convert to extended UNC path. */
230 memcpy(zUnicode, L"\\\\?\\UNC\\", 16);
231 wUnicode += 8;
232 MultiByteToWideChar(CP_UTF8, 0, zUtf8+2, -1, wUnicode, nChar-8);
233 goto finish;
234 }
235 }
236 MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
237 finish:
238 while( *wUnicode != '\0' ){
239
240 DDED test/win32-longpath.test
--- a/test/win32-longpath.test
+++ b/test/win32-longpath.test
@@ -0,0 +1,32 @@
1
+#
2
+# Tests for 'win32-longpath' VFS, using a repo path >260 chars.
3
+#
4
+# Actually, this test should pass on any platform.
5
+#
6
+
7
+# Fossil will write data on $HOME, running 'fossil new' here.
8
+# We need not to clutter the $HOME of the test caller.
9
+#
10
+set env(HOME) [pwd]
11
+
12
+# Create the repo
13
+#
14
+set x [string repeat x 132]
15
+set longpath [pwd]/$x
16
+file mkdir $longpath
17
+catch {
18
+ # Use "cygpath" for converting it to win32 path. If not
19
+ # in Msys or Cygwin shell, nothing needs to be done.
20
+ set longpath [exec cygpath -w $longpath]
21
+}
22
+
23
+test win32-longpath-test.1 {
24
+ ![regexp CANTOPEN [fossil new $longpath/$x.fossil]]
25
+}
26
+
27
+# Try to remove the file/dir various ways, different
28
+# Shells/Tcl versions expect it differently.
29
+catch {file delete \\\\?\\$longpath\\$x.fossil}
30
+catch {file delete $longpath/$x.fossil}
31
+catch {file delete [pwd]/$x/$x.fossil}
32
+catch {file delete [pwd]/$x}
--- a/test/win32-longpath.test
+++ b/test/win32-longpath.test
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
--- a/test/win32-longpath.test
+++ b/test/win32-longpath.test
@@ -0,0 +1,32 @@
1 #
2 # Tests for 'win32-longpath' VFS, using a repo path >260 chars.
3 #
4 # Actually, this test should pass on any platform.
5 #
6
7 # Fossil will write data on $HOME, running 'fossil new' here.
8 # We need not to clutter the $HOME of the test caller.
9 #
10 set env(HOME) [pwd]
11
12 # Create the repo
13 #
14 set x [string repeat x 132]
15 set longpath [pwd]/$x
16 file mkdir $longpath
17 catch {
18 # Use "cygpath" for converting it to win32 path. If not
19 # in Msys or Cygwin shell, nothing needs to be done.
20 set longpath [exec cygpath -w $longpath]
21 }
22
23 test win32-longpath-test.1 {
24 ![regexp CANTOPEN [fossil new $longpath/$x.fossil]]
25 }
26
27 # Try to remove the file/dir various ways, different
28 # Shells/Tcl versions expect it differently.
29 catch {file delete \\\\?\\$longpath\\$x.fossil}
30 catch {file delete $longpath/$x.fossil}
31 catch {file delete [pwd]/$x/$x.fossil}
32 catch {file delete [pwd]/$x}

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button