Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
| Comment: | Enhance the function to find case-preserved filenames on Windows to deal with non-ASCII filenames. |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
9919dfbbaa2019e7eb7bbfdb2f821058 |
| User & Date: | florian 2024-10-15 05:18:00.000 |
References
|
2024-10-19
| ||
| 14:03 | Another update for[9919dfbbaa]: Make sure reallocated buffers always grow, guard all buffer writes by overflow checks (next time use blobs), make sure the `case-sensitive' setting and command-line option are followed, and use brute force to achieve binary (vs.linguistic) file name comparison (only on older versions of Windows). check-in: fe6ef89f5f user: florian tags: trunk | |
|
2024-10-16
| ||
| 05:16 | Amend [9919dfbbaa], again: Include an optional directory separator in buffer size calculation, guard against the unlikely (impossible?) case that a case-adjusted filename component round-tripping from UTF-8 to UTF-16 and back get longer, plus some unrelated white space fix. check-in: 49262642f4 user: florian tags: trunk | |
|
2024-10-15
| ||
| 05:36 | Amend [9919dfbbaa]: fix a comment typo and rename a variable. check-in: d7d106227f user: florian tags: trunk | |
Context
|
2024-10-15
| ||
| 05:21 | Mention the comment formatter updates in the change as a hint to users encountering problems with timeline output. check-in: 3c6e5a1e4c user: florian tags: trunk | |
| 05:18 | Enhance the function to find case-preserved filenames on Windows to deal with non-ASCII filenames. check-in: 9919dfbbaa user: florian tags: trunk | |
|
2024-10-14
| ||
| 19:00 | When building with tcl8.7 or higher, eliminate the call to Tcl_MakeSafe(), which does not exist in those versions (8.7 includes it in their headers but not their lib). Building with tcl8.7+ reveals an unrelated function-type conversion error caused (apparently) by changes in tcl8.7+, and that's still unresolved. check-in: 2d5a23e919 user: stephan tags: trunk | |
Changes
Changes to src/file.c.
| ︙ | ︙ | |||
1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 |
**
** For case-sensitive filesystems, such as on Linux, this routine is
** just fossil_strdup(). But for case-insenstiive but "case preserving"
** filesystems, such as on MacOS or Windows, we want the filename to be
** in the preserved casing. That's what this routine does.
*/
char *file_case_preferred_name(const char *zDir, const char *zPath){
DIR *d;
int i;
char *zResult = 0;
void *zNative = 0;
if( filenames_are_case_sensitive() ){
return fossil_strdup(zPath);
| > | 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 |
**
** For case-sensitive filesystems, such as on Linux, this routine is
** just fossil_strdup(). But for case-insenstiive but "case preserving"
** filesystems, such as on MacOS or Windows, we want the filename to be
** in the preserved casing. That's what this routine does.
*/
char *file_case_preferred_name(const char *zDir, const char *zPath){
#ifndef _WIN32 /* Call win32_file_case_preferred_name() on Windows. */
DIR *d;
int i;
char *zResult = 0;
void *zNative = 0;
if( filenames_are_case_sensitive() ){
return fossil_strdup(zPath);
|
| ︙ | ︙ | |||
1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 |
fossil_path_free(zUtf8);
}
closedir(d);
}
fossil_path_free(zNative);
if( zResult==0 ) zResult = fossil_strdup(zPath);
return zResult;
}
/*
** COMMAND: test-case-filename
**
** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH ....
**
| > > > | 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 |
fossil_path_free(zUtf8);
}
closedir(d);
}
fossil_path_free(zNative);
if( zResult==0 ) zResult = fossil_strdup(zPath);
return zResult;
#else /* _WIN32 */
return win32_file_case_preferred_name(zDir,zPath);
#endif /* _WIN32 */
}
/*
** COMMAND: test-case-filename
**
** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH ....
**
|
| ︙ | ︙ |
Changes to src/winfile.c.
| ︙ | ︙ | |||
288 289 290 291 292 293 294 295 | } zUtf8 = fossil_path_to_utf8(zWide); fossil_free(zWide); for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/'; strncpy(zBuf, zUtf8, nBuf); fossil_path_free(zUtf8); } #endif /* _WIN32 -- This code is for win32 only */ | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
}
zUtf8 = fossil_path_to_utf8(zWide);
fossil_free(zWide);
for(i=0; zUtf8[i]; i++) if( zUtf8[i]=='\\' ) zUtf8[i] = '/';
strncpy(zBuf, zUtf8, nBuf);
fossil_path_free(zUtf8);
}
/* Perform case-insensitive comparison of two UTF-16 file names. Try to load the
** CompareStringOrdinal() function on Windows Vista and newer, and resort to the
** lstrcmpiW() function on Windows XP.
*/
int win32_compare_filenames_nocase(
const wchar_t *fn1,
const wchar_t *fn2
){
static FARPROC fnCompareStringOrdinal;
static int try_fnCompareStringOrdinal;
if( !try_fnCompareStringOrdinal ){
fnCompareStringOrdinal =
GetProcAddress(GetModuleHandleA("kernel32"),"CompareStringOrdinal");
try_fnCompareStringOrdinal = 1;
}
if( fnCompareStringOrdinal ){
return -2 + fnCompareStringOrdinal(fn1,-1,fn2,-1,1);
}else{
return lstrcmpiW(fn1,fn2);
}
}
/* Helper macros to deal with directory separators. */
#define IS_DIRSEP(s,i) ( s[i]=='/' || s[i]=='\\' )
#define NEXT_DIRSEP(s,i) while( s[i] && s[i]!='/' && s[i]!='\\' ){i++;}
/* The Win32 version of file_case_preferred_name() from file.c, which is able to
** find case-preserved file names containing non-ASCII characters. The result is
** allocated by fossil_malloc() and *should* be free'd by tha caller. While this
** function usually gets canonicalized paths, it is able to handle any input and
** figure out more cases than the original:
**
** fossil test-case-filename C:/ .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE
** → Original: .//..\WINDOWS\/.//.\SYSTEM32\.\NOTEPAD.EXE
** → Modified: .//..\Windows\/.//.\System32\.\notepad.exe
**
** md ÄÖÜ
** fossil test-case-filename ./\ .\äöü\/[empty]\\/
** → Original: ./äöü\/[empty]\\/
** → Modified: .\ÄÖÜ\/[empty]\\/
**
** The function preserves slashes and backslashes: only single file or directory
** components without directory separators ("basenames") are converted to UTF-16
** using fossil_utf8_to_path(), so bypassing its slash ↔ backslash translations.
** Note that the original function doesn't preserve all slashes and backslashes,
** for example in the second example above.
**
** NOTE: As of Windows 10, version 1803, case sensitivity may be enabled on a
** per-directory basis, as returned by NtQueryInformationFile() with the file
** information class FILE_CASE_SENSITIVE_INFORMATION. So this function may be
** changed to act like fossil_strdup() for files located in such directories.
*/
char *win32_file_case_preferred_name(
const char *zBase,
const char *zPath
){
int cchBase = strlen(zBase);
int cchPath = strlen(zPath);
int cchBuf = cchBase + cchPath + 1;
int cchRes = cchPath + 1;
char *zBuf = fossil_malloc(cchBuf);
char *zRes = fossil_malloc(cchRes);
int i, j;
memcpy(zBuf,zBase,cchBase);
cchRes = 0;
if( !IS_DIRSEP(zBuf,cchBase-1) ){
zBuf[cchBase++]=L'/';
}
memcpy(zBuf+cchBase,zPath,cchPath+1);
i = j = cchBase;
while( 1 ){
WIN32_FIND_DATAW fd;
HANDLE hFind;
wchar_t *wzBuf;
char *zCompBuf = 0;
char *zComp = &zBuf[i];
int cchComp;
char chSep;
int fDone;
if( IS_DIRSEP(zBuf,i) ){
zRes[cchRes++] = zBuf[i];
i = j = i+1;
continue;
}
NEXT_DIRSEP(zBuf,j);
fDone = zBuf[j]==0;
chSep = zBuf[j];
zBuf[j] = 0; /* Truncate working buffer. */
wzBuf = fossil_utf8_to_path(zBuf,0);
hFind = FindFirstFileW(wzBuf,&fd);
if( hFind!= INVALID_HANDLE_VALUE ){
wchar_t *wzComp = fossil_utf8_to_path(zComp,0);
FindClose(hFind);
/* Test fd.cFileName, not fd.cAlternateFileName (classic 8.3 format). */
if( win32_compare_filenames_nocase(wzComp,fd.cFileName)==0 ){
zCompBuf = fossil_path_to_utf8(fd.cFileName);
zComp = zCompBuf;
}
fossil_path_free(wzComp);
}
fossil_path_free(wzBuf);
cchComp = strlen(zComp);
memcpy(zRes+cchRes,zComp,cchComp);
cchRes += cchComp;
if( zCompBuf ){
fossil_path_free(zCompBuf);
}
if( fDone ){
zRes[cchRes] = 0;
break;
}
zBuf[j] = chSep; /* Undo working buffer truncation. */
i = j;
}
fossil_free(zBuf);
return zRes;
}
#endif /* _WIN32 -- This code is for win32 only */
|