Fossil SCM

Handle win32 extended path prefix everywhere: Just strip it in the function file_simplify_name(), and only add it back when needed (just before feeding it to a win32 function when >260 chars). Includes additional test-cases.

jan.nijtmans 2014-02-28 13:46 trunk merge
Commit 95f004b1c535c545941ebe7512d7684f1250309b
+16 -5
--- src/file.c
+++ src/file.c
@@ -696,10 +696,11 @@
696696
}
697697
698698
/*
699699
** Simplify a filename by
700700
**
701
+** * Remove extended path prefix on windows and cygwin
701702
** * Convert all \ into / on windows and cygwin
702703
** * removing any trailing and duplicate /
703704
** * removing /./
704705
** * removing /A/../
705706
**
@@ -706,17 +707,27 @@
706707
** Changes are made in-place. Return the new name length.
707708
** If the slash parameter is non-zero, the trailing slash, if any,
708709
** is retained.
709710
*/
710711
int file_simplify_name(char *z, int n, int slash){
711
- int i, j;
712
+ int i = 1, j;
712713
if( n<0 ) n = strlen(z);
713714
714
- /* On windows and cygwin convert all \ characters to / */
715
+ /* On windows and cygwin convert all \ characters to /
716
+ * and remove extended path prefix if present */
715717
#if defined(_WIN32) || defined(__CYGWIN__)
716
- for(i=0; i<n; i++){
717
- if( z[i]=='\\' ) z[i] = '/';
718
+ for(j=0; j<n; j++){
719
+ if( z[j]=='\\' ) z[j] = '/';
720
+ }
721
+ if( n>3 && !memcmp(z, "//?/", 4) ){
722
+ if( fossil_strnicmp(z+4,"UNC", 3) ){
723
+ i += 4;
724
+ z[0] = z[4];
725
+ }else{
726
+ i += 6;
727
+ z[0] = '/';
728
+ }
718729
}
719730
#endif
720731
721732
/* Removing trailing "/" characters */
722733
if( !slash ){
@@ -723,11 +734,11 @@
723734
while( n>1 && z[n-1]=='/' ){ n--; }
724735
}
725736
726737
/* Remove duplicate '/' characters. Except, two // at the beginning
727738
** of a pathname is allowed since this is important on windows. */
728
- for(i=j=1; i<n; i++){
739
+ for(j=1; i<n; i++){
729740
z[j++] = z[i];
730741
while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++;
731742
}
732743
n = j;
733744
734745
--- src/file.c
+++ src/file.c
@@ -696,10 +696,11 @@
696 }
697
698 /*
699 ** Simplify a filename by
700 **
 
701 ** * Convert all \ into / on windows and cygwin
702 ** * removing any trailing and duplicate /
703 ** * removing /./
704 ** * removing /A/../
705 **
@@ -706,17 +707,27 @@
706 ** Changes are made in-place. Return the new name length.
707 ** If the slash parameter is non-zero, the trailing slash, if any,
708 ** is retained.
709 */
710 int file_simplify_name(char *z, int n, int slash){
711 int i, j;
712 if( n<0 ) n = strlen(z);
713
714 /* On windows and cygwin convert all \ characters to / */
 
715 #if defined(_WIN32) || defined(__CYGWIN__)
716 for(i=0; i<n; i++){
717 if( z[i]=='\\' ) z[i] = '/';
 
 
 
 
 
 
 
 
 
718 }
719 #endif
720
721 /* Removing trailing "/" characters */
722 if( !slash ){
@@ -723,11 +734,11 @@
723 while( n>1 && z[n-1]=='/' ){ n--; }
724 }
725
726 /* Remove duplicate '/' characters. Except, two // at the beginning
727 ** of a pathname is allowed since this is important on windows. */
728 for(i=j=1; i<n; i++){
729 z[j++] = z[i];
730 while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++;
731 }
732 n = j;
733
734
--- src/file.c
+++ src/file.c
@@ -696,10 +696,11 @@
696 }
697
698 /*
699 ** Simplify a filename by
700 **
701 ** * Remove extended path prefix on windows and cygwin
702 ** * Convert all \ into / on windows and cygwin
703 ** * removing any trailing and duplicate /
704 ** * removing /./
705 ** * removing /A/../
706 **
@@ -706,17 +707,27 @@
707 ** Changes are made in-place. Return the new name length.
708 ** If the slash parameter is non-zero, the trailing slash, if any,
709 ** is retained.
710 */
711 int file_simplify_name(char *z, int n, int slash){
712 int i = 1, j;
713 if( n<0 ) n = strlen(z);
714
715 /* On windows and cygwin convert all \ characters to /
716 * and remove extended path prefix if present */
717 #if defined(_WIN32) || defined(__CYGWIN__)
718 for(j=0; j<n; j++){
719 if( z[j]=='\\' ) z[j] = '/';
720 }
721 if( n>3 && !memcmp(z, "//?/", 4) ){
722 if( fossil_strnicmp(z+4,"UNC", 3) ){
723 i += 4;
724 z[0] = z[4];
725 }else{
726 i += 6;
727 z[0] = '/';
728 }
729 }
730 #endif
731
732 /* Removing trailing "/" characters */
733 if( !slash ){
@@ -723,11 +734,11 @@
734 while( n>1 && z[n-1]=='/' ){ n--; }
735 }
736
737 /* Remove duplicate '/' characters. Except, two // at the beginning
738 ** of a pathname is allowed since this is important on windows. */
739 for(j=1; i<n; i++){
740 z[j++] = z[i];
741 while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++;
742 }
743 n = j;
744
745
+16 -5
--- src/file.c
+++ src/file.c
@@ -696,10 +696,11 @@
696696
}
697697
698698
/*
699699
** Simplify a filename by
700700
**
701
+** * Remove extended path prefix on windows and cygwin
701702
** * Convert all \ into / on windows and cygwin
702703
** * removing any trailing and duplicate /
703704
** * removing /./
704705
** * removing /A/../
705706
**
@@ -706,17 +707,27 @@
706707
** Changes are made in-place. Return the new name length.
707708
** If the slash parameter is non-zero, the trailing slash, if any,
708709
** is retained.
709710
*/
710711
int file_simplify_name(char *z, int n, int slash){
711
- int i, j;
712
+ int i = 1, j;
712713
if( n<0 ) n = strlen(z);
713714
714
- /* On windows and cygwin convert all \ characters to / */
715
+ /* On windows and cygwin convert all \ characters to /
716
+ * and remove extended path prefix if present */
715717
#if defined(_WIN32) || defined(__CYGWIN__)
716
- for(i=0; i<n; i++){
717
- if( z[i]=='\\' ) z[i] = '/';
718
+ for(j=0; j<n; j++){
719
+ if( z[j]=='\\' ) z[j] = '/';
720
+ }
721
+ if( n>3 && !memcmp(z, "//?/", 4) ){
722
+ if( fossil_strnicmp(z+4,"UNC", 3) ){
723
+ i += 4;
724
+ z[0] = z[4];
725
+ }else{
726
+ i += 6;
727
+ z[0] = '/';
728
+ }
718729
}
719730
#endif
720731
721732
/* Removing trailing "/" characters */
722733
if( !slash ){
@@ -723,11 +734,11 @@
723734
while( n>1 && z[n-1]=='/' ){ n--; }
724735
}
725736
726737
/* Remove duplicate '/' characters. Except, two // at the beginning
727738
** of a pathname is allowed since this is important on windows. */
728
- for(i=j=1; i<n; i++){
739
+ for(j=1; i<n; i++){
729740
z[j++] = z[i];
730741
while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++;
731742
}
732743
n = j;
733744
734745
--- src/file.c
+++ src/file.c
@@ -696,10 +696,11 @@
696 }
697
698 /*
699 ** Simplify a filename by
700 **
 
701 ** * Convert all \ into / on windows and cygwin
702 ** * removing any trailing and duplicate /
703 ** * removing /./
704 ** * removing /A/../
705 **
@@ -706,17 +707,27 @@
706 ** Changes are made in-place. Return the new name length.
707 ** If the slash parameter is non-zero, the trailing slash, if any,
708 ** is retained.
709 */
710 int file_simplify_name(char *z, int n, int slash){
711 int i, j;
712 if( n<0 ) n = strlen(z);
713
714 /* On windows and cygwin convert all \ characters to / */
 
715 #if defined(_WIN32) || defined(__CYGWIN__)
716 for(i=0; i<n; i++){
717 if( z[i]=='\\' ) z[i] = '/';
 
 
 
 
 
 
 
 
 
718 }
719 #endif
720
721 /* Removing trailing "/" characters */
722 if( !slash ){
@@ -723,11 +734,11 @@
723 while( n>1 && z[n-1]=='/' ){ n--; }
724 }
725
726 /* Remove duplicate '/' characters. Except, two // at the beginning
727 ** of a pathname is allowed since this is important on windows. */
728 for(i=j=1; i<n; i++){
729 z[j++] = z[i];
730 while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++;
731 }
732 n = j;
733
734
--- src/file.c
+++ src/file.c
@@ -696,10 +696,11 @@
696 }
697
698 /*
699 ** Simplify a filename by
700 **
701 ** * Remove extended path prefix on windows and cygwin
702 ** * Convert all \ into / on windows and cygwin
703 ** * removing any trailing and duplicate /
704 ** * removing /./
705 ** * removing /A/../
706 **
@@ -706,17 +707,27 @@
707 ** Changes are made in-place. Return the new name length.
708 ** If the slash parameter is non-zero, the trailing slash, if any,
709 ** is retained.
710 */
711 int file_simplify_name(char *z, int n, int slash){
712 int i = 1, j;
713 if( n<0 ) n = strlen(z);
714
715 /* On windows and cygwin convert all \ characters to /
716 * and remove extended path prefix if present */
717 #if defined(_WIN32) || defined(__CYGWIN__)
718 for(j=0; j<n; j++){
719 if( z[j]=='\\' ) z[j] = '/';
720 }
721 if( n>3 && !memcmp(z, "//?/", 4) ){
722 if( fossil_strnicmp(z+4,"UNC", 3) ){
723 i += 4;
724 z[0] = z[4];
725 }else{
726 i += 6;
727 z[0] = '/';
728 }
729 }
730 #endif
731
732 /* Removing trailing "/" characters */
733 if( !slash ){
@@ -723,11 +734,11 @@
734 while( n>1 && z[n-1]=='/' ){ n--; }
735 }
736
737 /* Remove duplicate '/' characters. Except, two // at the beginning
738 ** of a pathname is allowed since this is important on windows. */
739 for(j=1; i<n; i++){
740 z[j++] = z[i];
741 while( z[i]=='/' && i<n-1 && z[i+1]=='/' ) i++;
742 }
743 n = j;
744
745
+24 -5
--- src/utf8.c
+++ src/utf8.c
@@ -190,11 +190,12 @@
190190
**
191191
*/
192192
void *fossil_utf8_to_filename(const char *zUtf8){
193193
#ifdef _WIN32
194194
int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
195
- wchar_t *zUnicode = sqlite3_malloc( nChar * 2 );
195
+ /* Overallocate 6 chars, making some room for extended paths */
196
+ wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * sizeof(wchar_t) );
196197
wchar_t *wUnicode = zUnicode;
197198
if( zUnicode==0 ){
198199
return 0;
199200
}
200201
MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
@@ -207,17 +208,35 @@
207208
wUnicode[0] = wUnicode[1] = wUnicode[3] = '\\';
208209
zUtf8 += 4;
209210
wUnicode += 4;
210211
}
211212
/*
212
- ** If (remainder of) path starts with "<drive>:/" or "<drive>:\",
213
- ** leave the ':' intact
214
- */
213
+ ** If there is no "\\?\" prefix but there is a drive or UNC
214
+ ** path prefix and the path is larger than MAX_PATH chars,
215
+ ** no Win32 API function can handle that unless it is
216
+ ** prefixed with the extended path prefix. See:
217
+ ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath>
218
+ **/
215219
if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
216
- && (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
220
+ && (zUtf8[2]=='\\' || zUtf8[2]=='/') ){
221
+ if( wUnicode==zUnicode && nChar>MAX_PATH){
222
+ memmove(wUnicode+4, wUnicode, nChar*sizeof(wchar_t));
223
+ memcpy(wUnicode, L"\\\\?\\", 4*sizeof(wchar_t));
224
+ wUnicode += 4;
225
+ }
226
+ /*
227
+ ** If (remainder of) path starts with "<drive>:/" or "<drive>:\",
228
+ ** leave the ':' intact but translate the backslash to a slash.
229
+ */
217230
wUnicode[2] = '\\';
218231
wUnicode += 3;
232
+ }else if( wUnicode==zUnicode && nChar>MAX_PATH
233
+ && (zUtf8[0]=='\\' || zUtf8[0]=='/')
234
+ && (zUtf8[1]=='\\' || zUtf8[1]=='/') && zUtf8[2]!='?'){
235
+ memmove(wUnicode+6, wUnicode, nChar*sizeof(wchar_t));
236
+ memcpy(wUnicode, L"\\\\?\\UNC", 7*sizeof(wchar_t));
237
+ wUnicode += 7;
219238
}
220239
/*
221240
** In the remainder of the path, translate invalid characters to
222241
** characters in the Unicode private use area. This is what makes
223242
** Win32 fossil.exe work well in a Cygwin environment even when a
224243
--- 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);
@@ -207,17 +208,35 @@
207 wUnicode[0] = wUnicode[1] = wUnicode[3] = '\\';
208 zUtf8 += 4;
209 wUnicode += 4;
210 }
211 /*
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 ** characters in the Unicode private use area. This is what makes
223 ** Win32 fossil.exe work well in a Cygwin environment even when a
224
--- 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 6 chars, making some room for extended paths */
196 wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * 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);
@@ -207,17 +208,35 @@
208 wUnicode[0] = wUnicode[1] = wUnicode[3] = '\\';
209 zUtf8 += 4;
210 wUnicode += 4;
211 }
212 /*
213 ** If there is no "\\?\" prefix but there is a drive or UNC
214 ** path prefix and the path is larger than MAX_PATH chars,
215 ** no Win32 API function can handle that unless it is
216 ** prefixed with the extended path prefix. See:
217 ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath>
218 **/
219 if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
220 && (zUtf8[2]=='\\' || zUtf8[2]=='/') ){
221 if( wUnicode==zUnicode && nChar>MAX_PATH){
222 memmove(wUnicode+4, wUnicode, nChar*sizeof(wchar_t));
223 memcpy(wUnicode, L"\\\\?\\", 4*sizeof(wchar_t));
224 wUnicode += 4;
225 }
226 /*
227 ** If (remainder of) path starts with "<drive>:/" or "<drive>:\",
228 ** leave the ':' intact but translate the backslash to a slash.
229 */
230 wUnicode[2] = '\\';
231 wUnicode += 3;
232 }else if( wUnicode==zUnicode && nChar>MAX_PATH
233 && (zUtf8[0]=='\\' || zUtf8[0]=='/')
234 && (zUtf8[1]=='\\' || zUtf8[1]=='/') && zUtf8[2]!='?'){
235 memmove(wUnicode+6, wUnicode, nChar*sizeof(wchar_t));
236 memcpy(wUnicode, L"\\\\?\\UNC", 7*sizeof(wchar_t));
237 wUnicode += 7;
238 }
239 /*
240 ** In the remainder of the path, translate invalid characters to
241 ** characters in the Unicode private use area. This is what makes
242 ** Win32 fossil.exe work well in a Cygwin environment even when a
243
--- test/file1.test
+++ test/file1.test
@@ -33,5 +33,10 @@
3333
simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b
3434
simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c
3535
simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y
3636
simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y
3737
simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y
38
+
39
+if {$::tcl_platform(os)=="Windows NT"} {
40
+ simplify-name 108 //?/a:/a/b a:/a/b //?/UNC/a/b //a/b //?/ {}
41
+ simplify-name 109 \\\\?\\a:\\a\\b a:/a/b \\\\?\\UNC\\a\\b //a/b \\\\?\\ {}
42
+}
3843
--- test/file1.test
+++ test/file1.test
@@ -33,5 +33,10 @@
33 simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b
34 simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c
35 simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y
36 simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y
37 simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y
 
 
 
 
 
38
--- test/file1.test
+++ test/file1.test
@@ -33,5 +33,10 @@
33 simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b
34 simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c
35 simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y
36 simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y
37 simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y
38
39 if {$::tcl_platform(os)=="Windows NT"} {
40 simplify-name 108 //?/a:/a/b a:/a/b //?/UNC/a/b //a/b //?/ {}
41 simplify-name 109 \\\\?\\a:\\a\\b a:/a/b \\\\?\\UNC\\a\\b //a/b \\\\?\\ {}
42 }
43

Keyboard Shortcuts

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