Fossil SCM

Adjust the "fossil add" command so that on systems with case-insensitive filenames, the named added to the repository is the operating-systems preferred case for the name.

drh 2024-04-11 12:38 trunk merge
Commit 78e63995be3b2dd6a45c320c5987c68797ac6f74b5f4c84a45a14570d74675f7
2 files changed +2 -1 +69
+2 -1
--- src/add.c
+++ src/add.c
@@ -447,11 +447,11 @@
447447
if( isDir==1 ){
448448
vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
449449
}else if( isDir==0 ){
450450
fossil_warning("not found: %s", zName);
451451
}else{
452
- char *zTreeName = &zName[nRoot];
452
+ char *zTreeName = file_case_preferred_name(g.zLocalRoot,&zName[nRoot]);
453453
if( !forceFlag && glob_match(pIgnore, zTreeName) ){
454454
Blob ans;
455455
char cReply;
456456
char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". "
457457
"Add it (a=all/y/N)? ", zTreeName);
@@ -468,10 +468,11 @@
468468
}
469469
db_multi_exec(
470470
"INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)",
471471
zTreeName
472472
);
473
+ fossil_free(zTreeName);
473474
}
474475
blob_reset(&fullName);
475476
}
476477
glob_free(pIgnore);
477478
glob_free(pClean);
478479
--- src/add.c
+++ src/add.c
@@ -447,11 +447,11 @@
447 if( isDir==1 ){
448 vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
449 }else if( isDir==0 ){
450 fossil_warning("not found: %s", zName);
451 }else{
452 char *zTreeName = &zName[nRoot];
453 if( !forceFlag && glob_match(pIgnore, zTreeName) ){
454 Blob ans;
455 char cReply;
456 char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". "
457 "Add it (a=all/y/N)? ", zTreeName);
@@ -468,10 +468,11 @@
468 }
469 db_multi_exec(
470 "INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)",
471 zTreeName
472 );
 
473 }
474 blob_reset(&fullName);
475 }
476 glob_free(pIgnore);
477 glob_free(pClean);
478
--- src/add.c
+++ src/add.c
@@ -447,11 +447,11 @@
447 if( isDir==1 ){
448 vfile_scan(&fullName, nRoot-1, scanFlags, pClean, pIgnore, RepoFILE);
449 }else if( isDir==0 ){
450 fossil_warning("not found: %s", zName);
451 }else{
452 char *zTreeName = file_case_preferred_name(g.zLocalRoot,&zName[nRoot]);
453 if( !forceFlag && glob_match(pIgnore, zTreeName) ){
454 Blob ans;
455 char cReply;
456 char *prompt = mprintf("file \"%s\" matches \"ignore-glob\". "
457 "Add it (a=all/y/N)? ", zTreeName);
@@ -468,10 +468,11 @@
468 }
469 db_multi_exec(
470 "INSERT OR IGNORE INTO sfile(pathname) VALUES(%Q)",
471 zTreeName
472 );
473 fossil_free(zTreeName);
474 }
475 blob_reset(&fullName);
476 }
477 glob_free(pIgnore);
478 glob_free(pClean);
479
+69
--- src/file.c
+++ src/file.c
@@ -1290,10 +1290,79 @@
12901290
if( zOrigName==0 ) return 0;
12911291
blob_init(&x, 0, 0);
12921292
file_canonical_name(zOrigName, &x, 0);
12931293
return blob_str(&x);
12941294
}
1295
+
1296
+/*
1297
+** Convert zPath, which is a relative pathname rooted at zDir, into the
1298
+** case preferred by the underlying filesystem. Return the a copy
1299
+** of the converted path in memory obtained from fossil_malloc().
1300
+**
1301
+** For case-sensitive filesystems, such as on Linux, this routine is
1302
+** just fossil_strdup(). But for case-insenstiive but "case preserving"
1303
+** filesystems, such as on MacOS or Windows, we want the filename to be
1304
+** in the preserved casing. That's what this routine does.
1305
+*/
1306
+char *file_case_preferred_name(const char *zDir, const char *zPath){
1307
+ DIR *d;
1308
+ int i;
1309
+ char *zResult = 0;
1310
+ void *zNative = 0;
1311
+
1312
+ if( filenames_are_case_sensitive() ){
1313
+ return fossil_strdup(zPath);
1314
+ }
1315
+ for(i=0; zPath[i] && zPath[i]!='/' && zPath[i]!='\\'; i++){}
1316
+ zNative = fossil_utf8_to_path(zDir, 1);
1317
+ d = opendir(zNative);
1318
+ if( d ){
1319
+ struct dirent *pEntry;
1320
+ while( (pEntry = readdir(d))!=0 ){
1321
+ char *zUtf8 = fossil_path_to_utf8(pEntry->d_name);
1322
+ if( fossil_strnicmp(zUtf8, zPath, i)==0 && zUtf8[i]==0 ){
1323
+ if( zPath[i]==0 ){
1324
+ zResult = fossil_strdup(zUtf8);
1325
+ }else{
1326
+ char *zSubDir = mprintf("%s/%s", zDir, zUtf8);
1327
+ char *zSubPath = file_case_preferred_name(zSubDir, &zPath[i+1]);
1328
+ zResult = mprintf("%s/%s", zUtf8, zSubPath);
1329
+ fossil_free(zSubPath);
1330
+ fossil_free(zSubDir);
1331
+ }
1332
+ fossil_path_free(zUtf8);
1333
+ break;
1334
+ }
1335
+ fossil_path_free(zUtf8);
1336
+ }
1337
+ closedir(d);
1338
+ }
1339
+ fossil_path_free(zNative);
1340
+ if( zResult==0 ) zResult = fossil_strdup(zPath);
1341
+ return zResult;
1342
+}
1343
+
1344
+/*
1345
+** COMMAND: test-case-filename
1346
+**
1347
+** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH ....
1348
+**
1349
+** All the PATH arguments (there must be one at least one) are pathnames
1350
+** relative to DIRECTORY. This test command prints the OS-preferred name
1351
+** for each PATH in filesystems where case is not significant.
1352
+*/
1353
+void test_preferred_fn(void){
1354
+ int i;
1355
+ if( g.argc<4 ){
1356
+ usage("DIRECTORY PATH ...");
1357
+ }
1358
+ for(i=3; i<g.argc; i++){
1359
+ char *z = file_case_preferred_name(g.argv[2], g.argv[i]);
1360
+ fossil_print("%s -> %s\n", g.argv[i], z);
1361
+ fossil_free(z);
1362
+ }
1363
+}
12951364
12961365
/*
12971366
** The input is the name of an executable, such as one might
12981367
** type on a command-line. This routine resolves that name into
12991368
** a full pathname. The result is obtained from fossil_malloc()
13001369
--- src/file.c
+++ src/file.c
@@ -1290,10 +1290,79 @@
1290 if( zOrigName==0 ) return 0;
1291 blob_init(&x, 0, 0);
1292 file_canonical_name(zOrigName, &x, 0);
1293 return blob_str(&x);
1294 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1295
1296 /*
1297 ** The input is the name of an executable, such as one might
1298 ** type on a command-line. This routine resolves that name into
1299 ** a full pathname. The result is obtained from fossil_malloc()
1300
--- src/file.c
+++ src/file.c
@@ -1290,10 +1290,79 @@
1290 if( zOrigName==0 ) return 0;
1291 blob_init(&x, 0, 0);
1292 file_canonical_name(zOrigName, &x, 0);
1293 return blob_str(&x);
1294 }
1295
1296 /*
1297 ** Convert zPath, which is a relative pathname rooted at zDir, into the
1298 ** case preferred by the underlying filesystem. Return the a copy
1299 ** of the converted path in memory obtained from fossil_malloc().
1300 **
1301 ** For case-sensitive filesystems, such as on Linux, this routine is
1302 ** just fossil_strdup(). But for case-insenstiive but "case preserving"
1303 ** filesystems, such as on MacOS or Windows, we want the filename to be
1304 ** in the preserved casing. That's what this routine does.
1305 */
1306 char *file_case_preferred_name(const char *zDir, const char *zPath){
1307 DIR *d;
1308 int i;
1309 char *zResult = 0;
1310 void *zNative = 0;
1311
1312 if( filenames_are_case_sensitive() ){
1313 return fossil_strdup(zPath);
1314 }
1315 for(i=0; zPath[i] && zPath[i]!='/' && zPath[i]!='\\'; i++){}
1316 zNative = fossil_utf8_to_path(zDir, 1);
1317 d = opendir(zNative);
1318 if( d ){
1319 struct dirent *pEntry;
1320 while( (pEntry = readdir(d))!=0 ){
1321 char *zUtf8 = fossil_path_to_utf8(pEntry->d_name);
1322 if( fossil_strnicmp(zUtf8, zPath, i)==0 && zUtf8[i]==0 ){
1323 if( zPath[i]==0 ){
1324 zResult = fossil_strdup(zUtf8);
1325 }else{
1326 char *zSubDir = mprintf("%s/%s", zDir, zUtf8);
1327 char *zSubPath = file_case_preferred_name(zSubDir, &zPath[i+1]);
1328 zResult = mprintf("%s/%s", zUtf8, zSubPath);
1329 fossil_free(zSubPath);
1330 fossil_free(zSubDir);
1331 }
1332 fossil_path_free(zUtf8);
1333 break;
1334 }
1335 fossil_path_free(zUtf8);
1336 }
1337 closedir(d);
1338 }
1339 fossil_path_free(zNative);
1340 if( zResult==0 ) zResult = fossil_strdup(zPath);
1341 return zResult;
1342 }
1343
1344 /*
1345 ** COMMAND: test-case-filename
1346 **
1347 ** Usage: fossil test-case-filename DIRECTORY PATH PATH PATH ....
1348 **
1349 ** All the PATH arguments (there must be one at least one) are pathnames
1350 ** relative to DIRECTORY. This test command prints the OS-preferred name
1351 ** for each PATH in filesystems where case is not significant.
1352 */
1353 void test_preferred_fn(void){
1354 int i;
1355 if( g.argc<4 ){
1356 usage("DIRECTORY PATH ...");
1357 }
1358 for(i=3; i<g.argc; i++){
1359 char *z = file_case_preferred_name(g.argv[2], g.argv[i]);
1360 fossil_print("%s -> %s\n", g.argv[i], z);
1361 fossil_free(z);
1362 }
1363 }
1364
1365 /*
1366 ** The input is the name of an executable, such as one might
1367 ** type on a command-line. This routine resolves that name into
1368 ** a full pathname. The result is obtained from fossil_malloc()
1369

Keyboard Shortcuts

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