| | @@ -293,28 +293,53 @@ |
| 293 | 293 | fossil_path_free(zUtf8); |
| 294 | 294 | } |
| 295 | 295 | |
| 296 | 296 | /* Perform case-insensitive comparison of two UTF-16 file names. Try to load the |
| 297 | 297 | ** CompareStringOrdinal() function on Windows Vista and newer, and resort to the |
| 298 | | -** lstrcmpiW() function on Windows XP. |
| 298 | +** RtlEqualUnicodeString() function on Windows XP. |
| 299 | +** The dance to invoke RtlEqualUnicodeString() is necessary because lstrcmpiW() |
| 300 | +** performs linguistic comparison, while the former performs binary comparison. |
| 301 | +** As an example, matching "ß" (U+00DF Latin Small Letter Sharp S) with "ss" is |
| 302 | +** undesirable in file name comparison, so lstrcmpiW() is only invoked in cases |
| 303 | +** that are technically impossible and contradicting all known laws of physics. |
| 299 | 304 | */ |
| 300 | | -int win32_compare_filenames_nocase( |
| 305 | +int win32_filenames_equal_nocase( |
| 301 | 306 | const wchar_t *fn1, |
| 302 | 307 | const wchar_t *fn2 |
| 303 | 308 | ){ |
| 304 | 309 | static FARPROC fnCompareStringOrdinal; |
| 305 | | - static int tried_CompareStringOrdinal; |
| 306 | | - if( !tried_CompareStringOrdinal ){ |
| 310 | + static FARPROC fnRtlInitUnicodeString; |
| 311 | + static FARPROC fnRtlEqualUnicodeString; |
| 312 | + static int loaded_CompareStringOrdinal; |
| 313 | + static int loaded_RtlUnicodeStringAPIs; |
| 314 | + if( !loaded_CompareStringOrdinal ){ |
| 307 | 315 | fnCompareStringOrdinal = |
| 308 | 316 | GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal"); |
| 309 | | - tried_CompareStringOrdinal = 1; |
| 317 | + loaded_CompareStringOrdinal = 1; |
| 310 | 318 | } |
| 311 | 319 | if( fnCompareStringOrdinal ){ |
| 312 | | - return -2 + fnCompareStringOrdinal(fn1,-1,fn2,-1,1); |
| 313 | | - }else{ |
| 314 | | - return lstrcmpiW(fn1,fn2); |
| 320 | + return fnCompareStringOrdinal(fn1,-1,fn2,-1,1)-2==0; |
| 321 | + } |
| 322 | + if( !loaded_RtlUnicodeStringAPIs ){ |
| 323 | + fnRtlInitUnicodeString = |
| 324 | + GetProcAddress(GetModuleHandleA("ntdll"),"RtlInitUnicodeString"); |
| 325 | + fnRtlEqualUnicodeString = |
| 326 | + GetProcAddress(GetModuleHandleA("ntdll"),"RtlEqualUnicodeString"); |
| 327 | + loaded_RtlUnicodeStringAPIs = 1; |
| 328 | + } |
| 329 | + if( fnRtlInitUnicodeString && fnRtlEqualUnicodeString ){ |
| 330 | + struct { /* UNICODE_STRING from <ntdef.h> */ |
| 331 | + unsigned short Length; |
| 332 | + unsigned short MaximumLength; |
| 333 | + wchar_t *Buffer; |
| 334 | + } u1, u2; |
| 335 | + fnRtlInitUnicodeString(&u1,fn1); |
| 336 | + fnRtlInitUnicodeString(&u2,fn2); |
| 337 | + return (unsigned char)fnRtlEqualUnicodeString(&u1,&u2,1); |
| 315 | 338 | } |
| 339 | + /* In what kind of strange parallel universe are we? */ |
| 340 | + return lstrcmpiW(fn1,fn2)==0; |
| 316 | 341 | } |
| 317 | 342 | |
| 318 | 343 | /* Helper macros to deal with directory separators. */ |
| 319 | 344 | #define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' ) |
| 320 | 345 | #define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;} |
| | @@ -347,18 +372,28 @@ |
| 347 | 372 | */ |
| 348 | 373 | char *win32_file_case_preferred_name( |
| 349 | 374 | const char *zBase, |
| 350 | 375 | const char *zPath |
| 351 | 376 | ){ |
| 352 | | - int cchBase = strlen(zBase); |
| 353 | | - int cchPath = strlen(zPath); |
| 354 | | - int cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ |
| 355 | | - int cchRes = cchPath + 1; /* + NULL */ |
| 356 | | - char *zBuf = fossil_malloc(cchBuf); |
| 357 | | - char *zRes = fossil_malloc(cchRes); |
| 358 | | - int ncUsed = 0; |
| 377 | + int cchBase; |
| 378 | + int cchPath; |
| 379 | + int cchBuf; |
| 380 | + int cchRes; |
| 381 | + char *zBuf; |
| 382 | + char *zRes; |
| 383 | + int ncUsed; |
| 359 | 384 | int i, j; |
| 385 | + if( filenames_are_case_sensitive() ){ |
| 386 | + return fossil_strdup(zPath); |
| 387 | + } |
| 388 | + cchBase = strlen(zBase); |
| 389 | + cchPath = strlen(zPath); |
| 390 | + cchBuf = cchBase + cchPath + 2; /* + NULL + optional directory slash */ |
| 391 | + cchRes = cchPath + 1; /* + NULL */ |
| 392 | + zBuf = fossil_malloc(cchBuf); |
| 393 | + zRes = fossil_malloc(cchRes); |
| 394 | + ncUsed = 0; |
| 360 | 395 | memcpy(zBuf,zBase,cchBase); |
| 361 | 396 | if( !IS_DIRSEP(zBuf,cchBase-1) ){ |
| 362 | 397 | zBuf[cchBase++]=L'/'; |
| 363 | 398 | } |
| 364 | 399 | memcpy(zBuf+cchBase,zPath,cchPath+1); |
| | @@ -372,10 +407,14 @@ |
| 372 | 407 | int cchComp; |
| 373 | 408 | char chSep; |
| 374 | 409 | int fDone; |
| 375 | 410 | if( IS_DIRSEP(zBuf,i) ){ |
| 376 | 411 | zRes[ncUsed++] = zBuf[i]; |
| 412 | + if( ncUsed+2>cchRes ){ /* Directory slash + NULL*/ |
| 413 | + cchRes += 32; /* Overprovisioning. */ |
| 414 | + zRes = fossil_realloc(zRes,cchRes); |
| 415 | + } |
| 377 | 416 | i = j = i+1; |
| 378 | 417 | continue; |
| 379 | 418 | } |
| 380 | 419 | NEXT_DIRSEP(zBuf,j); |
| 381 | 420 | fDone = zBuf[j]==0; |
| | @@ -385,20 +424,20 @@ |
| 385 | 424 | hFind = FindFirstFileW(wzBuf,&fd); |
| 386 | 425 | if( hFind!=INVALID_HANDLE_VALUE ){ |
| 387 | 426 | wchar_t *wzComp = fossil_utf8_to_path(zComp,0); |
| 388 | 427 | FindClose(hFind); |
| 389 | 428 | /* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */ |
| 390 | | - if( win32_compare_filenames_nocase(wzComp,fd.cFileName)==0 ){ |
| 429 | + if( win32_filenames_equal_nocase(wzComp,fd.cFileName) ){ |
| 391 | 430 | zCompBuf = fossil_path_to_utf8(fd.cFileName); |
| 392 | 431 | zComp = zCompBuf; |
| 393 | 432 | } |
| 394 | 433 | fossil_path_free(wzComp); |
| 395 | 434 | } |
| 396 | 435 | fossil_path_free(wzBuf); |
| 397 | 436 | cchComp = strlen(zComp); |
| 398 | | - if( ncUsed+cchComp+1>cchRes ){ |
| 399 | | - cchRes = ncUsed + cchComp + 32; /* While at it, add some extra space. */ |
| 437 | + if( ncUsed+cchComp+1>cchRes ){ /* Current component + NULL */ |
| 438 | + cchRes += cchComp + 32; /* Overprovisioning. */ |
| 400 | 439 | zRes = fossil_realloc(zRes,cchRes); |
| 401 | 440 | } |
| 402 | 441 | memcpy(zRes+ncUsed,zComp,cchComp); |
| 403 | 442 | ncUsed += cchComp; |
| 404 | 443 | if( zCompBuf ){ |
| 405 | 444 | |