Fossil

Check-in [ebb42b530e]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add support for extended UNC paths as well, and add Windows/Cygwin-specific test-cases for it.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | extended-path-prefix
Files: files | file ages | folders
SHA1: ebb42b530e4c3dc9c8e5c5e8077617ed0590b813
User & Date: jan.nijtmans 2014-02-26 10:42:44.436
Context
2014-02-28
13:46
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. check-in: 95f004b1c5 user: jan.nijtmans tags: trunk
2014-02-26
10:42
Add support for extended UNC paths as well, and add Windows/Cygwin-specific test-cases for it. Closed-Leaf check-in: ebb42b530e user: jan.nijtmans tags: extended-path-prefix
2014-02-25
13:31
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. check-in: ce4afc891c user: jan.nijtmans tags: extended-path-prefix
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/file.c.
642
643
644
645
646
647
648

649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668

669
670




671
672
673
674
675
676
677
  *pJ = i-1;
  return 1;
}

/*
** Simplify a filename by
**

**  * Convert all \ into / on windows and cygwin
**  * removing any trailing and duplicate /
**  * removing /./
**  * removing /A/../
**
** Changes are made in-place.  Return the new name length.
** If the slash parameter is non-zero, the trailing slash, if any,
** is retained.
*/
int file_simplify_name(char *z, int n, int slash){
  int i = 1, j;
  if( n<0 ) n = strlen(z);

  /* On windows and cygwin convert all \ characters to /
   * and remove extended path prefix if present */
#if defined(_WIN32) || defined(__CYGWIN__)
  for(j=0; j<n; j++){
    if( z[j]=='\\' ) z[j] = '/';
  }
  if( n>3 && !memcmp(z, "//?/", 4) ){

    i += 4;
    z[0] = z[4];




  }
#endif

  /* Removing trailing "/" characters */
  if( !slash ){
    while( n>1 && z[n-1]=='/' ){ n--; }
  }







>




















>
|
|
>
>
>
>







642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
  *pJ = i-1;
  return 1;
}

/*
** Simplify a filename by
**
**  * Remove extended path prefix on windows and cygwin
**  * Convert all \ into / on windows and cygwin
**  * removing any trailing and duplicate /
**  * removing /./
**  * removing /A/../
**
** Changes are made in-place.  Return the new name length.
** If the slash parameter is non-zero, the trailing slash, if any,
** is retained.
*/
int file_simplify_name(char *z, int n, int slash){
  int i = 1, j;
  if( n<0 ) n = strlen(z);

  /* On windows and cygwin convert all \ characters to /
   * and remove extended path prefix if present */
#if defined(_WIN32) || defined(__CYGWIN__)
  for(j=0; j<n; j++){
    if( z[j]=='\\' ) z[j] = '/';
  }
  if( n>3 && !memcmp(z, "//?/", 4) ){
    if( fossil_strnicmp(z+4,"UNC", 3) ){
      i += 4;
      z[0] = z[4];
    }else{
      i += 6;
      z[0] = '/';
    }
  }
#endif

  /* Removing trailing "/" characters */
  if( !slash ){
    while( n>1 && z[n-1]=='/' ){ n--; }
  }
Changes to src/utf8.c.
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225



226
227
228
229




230
231






232
233
234
235
236
237
238
**
** See: <http://cygwin.com/cygwin-ug-net/using-specialnames.html>
**
*/
void *fossil_utf8_to_filename(const char *zUtf8){
#ifdef _WIN32
  int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
  /* Overallocate 4 chars, making some room for extended paths */
  wchar_t *zUnicode = sqlite3_malloc( (nChar+4) * sizeof(wchar_t) );
  wchar_t *wUnicode = zUnicode;
  if( zUnicode==0 ){
    return 0;
  }
  MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
  /*
  ** If path starts with "//?/" or "\\?\" (extended path), translate
  ** any slashes to backslashes but leave the '?' intact
  */
  if( (zUtf8[0]=='\\' || zUtf8[0]=='/') && (zUtf8[1]=='\\' || zUtf8[1]=='/')
           && zUtf8[2]=='?' && (zUtf8[3]=='\\' || zUtf8[3]=='/')) {
    wUnicode[0] = wUnicode[1] = wUnicode[3] = '\\';
    zUtf8 += 4;
    wUnicode += 4;
  }
  /*
  ** If (remainder of) path starts with "<drive>:/" or "<drive>:\",
  ** leave the ':' intact
  */
  if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
           && (zUtf8[2]=='\\' || zUtf8[2]=='/')) {
    if( wUnicode==zUnicode && nChar>MAX_PATH){
      /*
      ** If there is no "\\?\" prefix but there is a drive
      ** prefix and the path is larger than MAX_PATH chars,
      ** no Win32 API function can handle that unless it is
      ** prefixed with the extended path prefix. See:
      ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath>
      **/



      memmove(wUnicode+4, wUnicode, nChar*sizeof(wchar_t));
      memcpy(wUnicode, L"\\\\?\\", 4*sizeof(wchar_t));
      wUnicode += 4;
    }




    wUnicode[2] = '\\';
    wUnicode += 3;






  }
  /*
  ** In the remainder of the path, translate invalid characters to
  ** characters in the Unicode private use area. This is what makes
  ** Win32 fossil.exe work well in a Cygwin environment even when a
  ** filename contains characters which are invalid for Win32.
  */







|
|
















<
<
<
<
<
<
<
|
|
|
|
|
|
>
>
>




>
>
>
>


>
>
>
>
>
>







188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212







213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
**
** See: <http://cygwin.com/cygwin-ug-net/using-specialnames.html>
**
*/
void *fossil_utf8_to_filename(const char *zUtf8){
#ifdef _WIN32
  int nChar = MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, 0, 0);
  /* Overallocate 6 chars, making some room for extended paths */
  wchar_t *zUnicode = sqlite3_malloc( (nChar+6) * sizeof(wchar_t) );
  wchar_t *wUnicode = zUnicode;
  if( zUnicode==0 ){
    return 0;
  }
  MultiByteToWideChar(CP_UTF8, 0, zUtf8, -1, zUnicode, nChar);
  /*
  ** If path starts with "//?/" or "\\?\" (extended path), translate
  ** any slashes to backslashes but leave the '?' intact
  */
  if( (zUtf8[0]=='\\' || zUtf8[0]=='/') && (zUtf8[1]=='\\' || zUtf8[1]=='/')
           && zUtf8[2]=='?' && (zUtf8[3]=='\\' || zUtf8[3]=='/')) {
    wUnicode[0] = wUnicode[1] = wUnicode[3] = '\\';
    zUtf8 += 4;
    wUnicode += 4;
  }
  /*







  ** If there is no "\\?\" prefix but there is a drive or UNC
  ** path prefix and the path is larger than MAX_PATH chars,
  ** no Win32 API function can handle that unless it is
  ** prefixed with the extended path prefix. See:
  ** <http://msdn.microsoft.com/en-us/library/aa365247(VS.85).aspx#maxpath>
  **/
  if( fossil_isalpha(zUtf8[0]) && zUtf8[1]==':'
           && (zUtf8[2]=='\\' || zUtf8[2]=='/') ){
    if( wUnicode==zUnicode && nChar>MAX_PATH){
      memmove(wUnicode+4, wUnicode, nChar*sizeof(wchar_t));
      memcpy(wUnicode, L"\\\\?\\", 4*sizeof(wchar_t));
      wUnicode += 4;
    }
    /*
    ** If (remainder of) path starts with "<drive>:/" or "<drive>:\",
    ** leave the ':' intact but translate the backslash to a slash.
    */
    wUnicode[2] = '\\';
    wUnicode += 3;
  }else if( wUnicode==zUnicode && nChar>MAX_PATH
            && (zUtf8[0]=='\\' || zUtf8[0]=='/')
            && (zUtf8[1]=='\\' || zUtf8[1]=='/') && zUtf8[2]!='?'){
    memmove(wUnicode+6, wUnicode, nChar*sizeof(wchar_t));
    memcpy(wUnicode, L"\\\\?\\UNC", 7*sizeof(wchar_t));
    wUnicode += 7;
  }
  /*
  ** In the remainder of the path, translate invalid characters to
  ** characters in the Unicode private use area. This is what makes
  ** Win32 fossil.exe work well in a Cygwin environment even when a
  ** filename contains characters which are invalid for Win32.
  */
Changes to test/file1.test.
31
32
33
34
35
36
37





simplify-name 101 {} {} / / ///////// / ././././ .
simplify-name 102 x x /x /x ///x //x
simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b
simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c
simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y
simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y
simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y












>
>
>
>
>
31
32
33
34
35
36
37
38
39
40
41
42
simplify-name 101 {} {} / / ///////// / ././././ .
simplify-name 102 x x /x /x ///x //x
simplify-name 103 a/b a/b /a/b /a/b a///b a/b ///a///b///// //a/b
simplify-name 104 a/b/../c/ a/c /a/b/../c /a/c /a/b//../c /a/c /a/b/..///c /a/c
simplify-name 105 a/b/../../x/y x/y /a/b/../../x/y /x/y
simplify-name 106 a/b/../../../x/y ../x/y /a/b/../../../x/y /../x/y
simplify-name 107 a/./b/.././../x/y x/y a//.//b//..//.//..//x//y/// x/y

if {$::tcl_platform(os)=="Windows NT"} {
  simplify-name 108 //?/a:/a/b a:/a/b //?/UNC/a/b //a/b //?/ {}
  simplify-name 109 \\\\?\\a:\\a\\b a:/a/b \\\\?\\UNC\\a\\b //a/b \\\\?\\ {}
}