Fossil SCM
Follow-up to [8ab08d32c7]: Fossil still doesn't handle the extended path prefix on win32 ('\\?\') right, mainly in checking paths. e.g.: "fossil add //\?/C:/Localdata/workspace/fossil/foo.c". Fossil cannot know that this path is correct. Solution: Strip the extended path prefix in file_simplify_name(), and only add it back when needed. Latest "winhttp.c" changes could be reverted with this change when compiling with MSVC or MinGW-w64 (as the repository path after simplicifation doesn't contain '?' any more), but when using MinGW the command-line handling cannot be thrusted.
Commit
ce4afc891c4982769f1e2e73c22162e07934ba8a
Parent
007c32bdfd67711…
2 files changed
+10
-5
+14
-1
+10
-5
| --- src/file.c | ||
| +++ src/file.c | ||
| @@ -654,17 +654,22 @@ | ||
| 654 | 654 | ** Changes are made in-place. Return the new name length. |
| 655 | 655 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 656 | 656 | ** is retained. |
| 657 | 657 | */ |
| 658 | 658 | int file_simplify_name(char *z, int n, int slash){ |
| 659 | - int i, j; | |
| 659 | + int i = 1, j; | |
| 660 | 660 | if( n<0 ) n = strlen(z); |
| 661 | 661 | |
| 662 | - /* On windows and cygwin convert all \ characters to / */ | |
| 662 | + /* On windows and cygwin convert all \ characters to / | |
| 663 | + * and remove extended path prefix if present */ | |
| 663 | 664 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 664 | - for(i=0; i<n; i++){ | |
| 665 | - if( z[i]=='\\' ) z[i] = '/'; | |
| 665 | + for(j=0; j<n; j++){ | |
| 666 | + if( z[j]=='\\' ) z[j] = '/'; | |
| 667 | + } | |
| 668 | + if( n>3 && !memcmp(z, "//?/", 4) ){ | |
| 669 | + i += 4; | |
| 670 | + z[0] = z[4]; | |
| 666 | 671 | } |
| 667 | 672 | #endif |
| 668 | 673 | |
| 669 | 674 | /* Removing trailing "/" characters */ |
| 670 | 675 | if( !slash ){ |
| @@ -671,11 +676,11 @@ | ||
| 671 | 676 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 672 | 677 | } |
| 673 | 678 | |
| 674 | 679 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 675 | 680 | ** of a pathname is allowed since this is important on windows. */ |
| 676 | - for(i=j=1; i<n; i++){ | |
| 681 | + for(j=1; i<n; i++){ | |
| 677 | 682 | z[j++] = z[i]; |
| 678 | 683 | while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; |
| 679 | 684 | } |
| 680 | 685 | n = j; |
| 681 | 686 | |
| 682 | 687 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -654,17 +654,22 @@ | |
| 654 | ** Changes are made in-place. Return the new name length. |
| 655 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 656 | ** is retained. |
| 657 | */ |
| 658 | int file_simplify_name(char *z, int n, int slash){ |
| 659 | int i, j; |
| 660 | if( n<0 ) n = strlen(z); |
| 661 | |
| 662 | /* On windows and cygwin convert all \ characters to / */ |
| 663 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 664 | for(i=0; i<n; i++){ |
| 665 | if( z[i]=='\\' ) z[i] = '/'; |
| 666 | } |
| 667 | #endif |
| 668 | |
| 669 | /* Removing trailing "/" characters */ |
| 670 | if( !slash ){ |
| @@ -671,11 +676,11 @@ | |
| 671 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 672 | } |
| 673 | |
| 674 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 675 | ** of a pathname is allowed since this is important on windows. */ |
| 676 | for(i=j=1; i<n; i++){ |
| 677 | z[j++] = z[i]; |
| 678 | while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; |
| 679 | } |
| 680 | n = j; |
| 681 | |
| 682 |
| --- src/file.c | |
| +++ src/file.c | |
| @@ -654,17 +654,22 @@ | |
| 654 | ** Changes are made in-place. Return the new name length. |
| 655 | ** If the slash parameter is non-zero, the trailing slash, if any, |
| 656 | ** is retained. |
| 657 | */ |
| 658 | int file_simplify_name(char *z, int n, int slash){ |
| 659 | int i = 1, j; |
| 660 | if( n<0 ) n = strlen(z); |
| 661 | |
| 662 | /* On windows and cygwin convert all \ characters to / |
| 663 | * and remove extended path prefix if present */ |
| 664 | #if defined(_WIN32) || defined(__CYGWIN__) |
| 665 | for(j=0; j<n; j++){ |
| 666 | if( z[j]=='\\' ) z[j] = '/'; |
| 667 | } |
| 668 | if( n>3 && !memcmp(z, "//?/", 4) ){ |
| 669 | i += 4; |
| 670 | z[0] = z[4]; |
| 671 | } |
| 672 | #endif |
| 673 | |
| 674 | /* Removing trailing "/" characters */ |
| 675 | if( !slash ){ |
| @@ -671,11 +676,11 @@ | |
| 676 | while( n>1 && z[n-1]=='/' ){ n--; } |
| 677 | } |
| 678 | |
| 679 | /* Remove duplicate '/' characters. Except, two // at the beginning |
| 680 | ** of a pathname is allowed since this is important on windows. */ |
| 681 | for(j=1; i<n; i++){ |
| 682 | z[j++] = z[i]; |
| 683 | while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++; |
| 684 | } |
| 685 | n = j; |
| 686 | |
| 687 |
+14
-1
| --- src/utf8.c | ||
| +++ src/utf8.c | ||
| @@ -190,11 +190,12 @@ | ||
| 190 | 190 | ** |
| 191 | 191 | */ |
| 192 | 192 | void *fossil_utf8_to_filename(const char *zUtf8){ |
| 193 | 193 | #ifdef _WIN32 |
| 194 | 194 | int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); |
| 195 | - wchar_t *zUnicode = sqlite3_malloc( nChar * 2 ); | |
| 195 | + /* Overallocate 4 chars, making some room for extended paths */ | |
| 196 | + wchar_t *zUnicode = sqlite3_malloc( (nChar+4) * sizeof(wchar_t) ); | |
| 196 | 197 | wchar_t *wUnicode = zUnicode; |
| 197 | 198 | if( zUnicode==0 ){ |
| 198 | 199 | return 0; |
| 199 | 200 | } |
| 200 | 201 | MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar); |
| @@ -212,10 +213,22 @@ | ||
| 212 | 213 | ** If (remainder of) path starts with "<drive>:/" or "<drive>:\", |
| 213 | 214 | ** leave the ':' intact |
| 214 | 215 | */ |
| 215 | 216 | if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':' |
| 216 | 217 | && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { |
| 218 | + if( wUnicode==zUnicode && nChar>MAX_PATH){ | |
| 219 | + /* | |
| 220 | + ** If there is no "\\?\" prefix but there is a drive | |
| 221 | + ** prefix and the path is larger than MAX_PATH chars, | |
| 222 | + ** no Win32 API function can handle that unless it is | |
| 223 | + ** prefixed with the extended path prefix. See: | |
| 224 | + ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath> | |
| 225 | + **/ | |
| 226 | + memmove(wUnicode+4, wUnicode, nChar*sizeof(wchar_t)); | |
| 227 | + memcpy(wUnicode, L"\\\\?\\", 4*sizeof(wchar_t)); | |
| 228 | + wUnicode += 4; | |
| 229 | + } | |
| 217 | 230 | wUnicode[2] = '\\'; |
| 218 | 231 | wUnicode += 3; |
| 219 | 232 | } |
| 220 | 233 | /* |
| 221 | 234 | ** In the remainder of the path, translate invalid characters to |
| 222 | 235 |
| --- src/utf8.c | |
| +++ src/utf8.c | |
| @@ -190,11 +190,12 @@ | |
| 190 | ** |
| 191 | */ |
| 192 | void *fossil_utf8_to_filename(const char *zUtf8){ |
| 193 | #ifdef _WIN32 |
| 194 | int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); |
| 195 | wchar_t *zUnicode = sqlite3_malloc( nChar * 2 ); |
| 196 | wchar_t *wUnicode = zUnicode; |
| 197 | if( zUnicode==0 ){ |
| 198 | return 0; |
| 199 | } |
| 200 | MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar); |
| @@ -212,10 +213,22 @@ | |
| 212 | ** If (remainder of) path starts with "<drive>:/" or "<drive>:\", |
| 213 | ** leave the ':' intact |
| 214 | */ |
| 215 | if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':' |
| 216 | && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { |
| 217 | wUnicode[2] = '\\'; |
| 218 | wUnicode += 3; |
| 219 | } |
| 220 | /* |
| 221 | ** In the remainder of the path, translate invalid characters to |
| 222 |
| --- src/utf8.c | |
| +++ src/utf8.c | |
| @@ -190,11 +190,12 @@ | |
| 190 | ** |
| 191 | */ |
| 192 | void *fossil_utf8_to_filename(const char *zUtf8){ |
| 193 | #ifdef _WIN32 |
| 194 | int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0); |
| 195 | /* Overallocate 4 chars, making some room for extended paths */ |
| 196 | wchar_t *zUnicode = sqlite3_malloc( (nChar+4) * sizeof(wchar_t) ); |
| 197 | wchar_t *wUnicode = zUnicode; |
| 198 | if( zUnicode==0 ){ |
| 199 | return 0; |
| 200 | } |
| 201 | MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar); |
| @@ -212,10 +213,22 @@ | |
| 213 | ** If (remainder of) path starts with "<drive>:/" or "<drive>:\", |
| 214 | ** leave the ':' intact |
| 215 | */ |
| 216 | if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':' |
| 217 | && (zUtf8[2]=='\\' || zUtf8[2]=='/')) { |
| 218 | if( wUnicode==zUnicode && nChar>MAX_PATH){ |
| 219 | /* |
| 220 | ** If there is no "\\?\" prefix but there is a drive |
| 221 | ** prefix and the path is larger than MAX_PATH chars, |
| 222 | ** no Win32 API function can handle that unless it is |
| 223 | ** prefixed with the extended path prefix. See: |
| 224 | ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath> |
| 225 | **/ |
| 226 | memmove(wUnicode+4, wUnicode, nChar*sizeof(wchar_t)); |
| 227 | memcpy(wUnicode, L"\\\\?\\", 4*sizeof(wchar_t)); |
| 228 | wUnicode += 4; |
| 229 | } |
| 230 | wUnicode[2] = '\\'; |
| 231 | wUnicode += 3; |
| 232 | } |
| 233 | /* |
| 234 | ** In the remainder of the path, translate invalid characters to |
| 235 |