Fossil SCM

Add --emptydirs option to the 'clean' command to remove empty directories. Corrections to style and comments. Remove superfluous local variable zDir in vfile_scan.

mistachkin 2013-09-30 11:45 UTC trunk
Commit 8f1e1ee8fbbe7625cfc2836de034200df11dda37
3 files changed +76 -24 +30 -3 +126 -7
+76 -24
--- src/checkin.c
+++ src/checkin.c
@@ -363,27 +363,28 @@
363363
}
364364
db_finalize(&q);
365365
}
366366
367367
/*
368
-** Create a TEMP table named SFILE and add all unmanaged files named on the command-line
369
-** to that table. If directories are named, then add all unmanaged files contained
370
-** underneath those directories. If there are no files or directories named on the
371
-** command-line, then add all unmanaged files anywhere in the checkout.
368
+** Create a TEMP table named SFILE and add all unmanaged files named on
369
+** the command-line to that table. If directories are named, then add
370
+** all unmanaged files contained underneath those directories. If there
371
+** are no files or directories named on the command-line, then add all
372
+** unmanaged files anywhere in the checkout.
372373
*/
373374
static void locate_unmanaged_files(
374
- int argc, /* Number of command-line arguments to examine */
375
- char **argv, /* values of command-line arguments */
376
- unsigned scanFlags, /* Zero or more SCAN_xxx flags */
377
- Glob *pIgnore1, /* Do not add files that match this GLOB */
378
- Glob *pIgnore2 /* Omit files matching this GLOB too */
375
+ int argc, /* Number of command-line arguments to examine */
376
+ char **argv, /* values of command-line arguments */
377
+ unsigned scanFlags, /* Zero or more SCAN_xxx flags */
378
+ Glob *pIgnore1, /* Do not add files that match this GLOB */
379
+ Glob *pIgnore2 /* Omit files matching this GLOB too */
379380
){
380
- Blob name; /* Name of a candidate file or directory */
381
- char *zName; /* Name of a candidate file or directory */
382
- int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */
383
- int i; /* Loop counter */
384
- int nRoot; /* length of g.zLocalRoot */
381
+ Blob name; /* Name of a candidate file or directory */
382
+ char *zName; /* Name of a candidate file or directory */
383
+ int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */
384
+ int i; /* Loop counter */
385
+ int nRoot; /* length of g.zLocalRoot */
385386
386387
db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)",
387388
filename_collation());
388389
nRoot = (int)strlen(g.zLocalRoot);
389390
if( argc==0 ){
@@ -508,10 +509,17 @@
508509
** is used.
509510
**
510511
** Options:
511512
** --case-sensitive <BOOL> override case-sensitive setting
512513
** --dotfiles Include files beginning with a dot (".").
514
+** --emptydirs Remove any empty directories that are not
515
+** explicitly exempted via the empty-dirs setting
516
+** or another applicable setting or command line
517
+** argument. Matching files, if any, are removed
518
+** prior to checking for any empty directories;
519
+** therefore, directories that contain only files
520
+** that were removed will be removed as well.
513521
** -f|--force Remove files without prompting.
514522
** --clean <CSG> Never prompt for files matching this
515523
** comma separated list of glob patterns.
516524
** --ignore <CSG> Ignore files matching patterns from the
517525
** comma separated list of glob patterns.
@@ -522,11 +530,11 @@
522530
** -v|--verbose Show all files as they are removed.
523531
**
524532
** See also: addremove, extra, status
525533
*/
526534
void clean_cmd(void){
527
- int allFlag, dryRunFlag, verboseFlag;
535
+ int allFileFlag, allDirFlag, dryRunFlag, emptyDirsFlag, verboseFlag;
528536
unsigned scanFlags = 0;
529537
const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag;
530538
Blob repo;
531539
Stmt q;
532540
Glob *pIgnore, *pKeep, *pClean;
@@ -534,11 +542,12 @@
534542
535543
dryRunFlag = find_option("dry-run","n",0)!=0;
536544
if( !dryRunFlag ){
537545
dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
538546
}
539
- allFlag = find_option("force","f",0)!=0;
547
+ allFileFlag = allDirFlag = find_option("force","f",0)!=0;
548
+ emptyDirsFlag = find_option("emptydirs","d",0)!=0;
540549
if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
541550
if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
542551
zIgnoreFlag = find_option("ignore",0,1);
543552
verboseFlag = find_option("verbose","v",0)!=0;
544553
zKeepFlag = find_option("keep",0,1);
@@ -557,12 +566,10 @@
557566
verify_all_options();
558567
pIgnore = glob_create(zIgnoreFlag);
559568
pKeep = glob_create(zKeepFlag);
560569
pClean = glob_create(zCleanFlag);
561570
locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, pKeep);
562
- glob_free(pKeep);
563
- glob_free(pIgnore);
564571
db_prepare(&q,
565572
"SELECT %Q || x FROM sfile"
566573
" WHERE x NOT IN (%s)"
567574
" ORDER BY 1",
568575
g.zLocalRoot, fossil_all_reserved_names(0)
@@ -572,34 +579,79 @@
572579
}
573580
db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)");
574581
nRoot = (int)strlen(g.zLocalRoot);
575582
while( db_step(&q)==SQLITE_ROW ){
576583
const char *zName = db_column_text(&q, 0);
577
- if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
584
+ if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
578585
Blob ans;
579586
char cReply;
580587
char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ",
581588
zName+nRoot);
582589
blob_zero(&ans);
583590
prompt_user(prompt, &ans);
584591
cReply = blob_str(&ans)[0];
585592
if( cReply=='a' || cReply=='A' ){
586
- allFlag = 1;
593
+ allFileFlag = 1;
587594
}else if( cReply!='y' && cReply!='Y' ){
588595
blob_reset(&ans);
589596
continue;
590597
}
591598
blob_reset(&ans);
592599
}
593
- if( verboseFlag || dryRunFlag ){
594
- fossil_print("Removed unmanaged file: %s\n", zName+nRoot);
600
+ if ( dryRunFlag || file_delete(zName)==0 ){
601
+ if( verboseFlag || dryRunFlag ){
602
+ fossil_print("Removed unmanaged file: %s\n", zName+nRoot);
603
+ }
604
+ }else if( verboseFlag ){
605
+ fossil_print("Could not remove file: %s\n", zName+nRoot);
595606
}
596
- if( !dryRunFlag ){
597
- file_delete(zName);
607
+ }
608
+ if( emptyDirsFlag ){
609
+ Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0));
610
+ Blob root;
611
+ blob_init(&root, g.zLocalRoot, nRoot - 1);
612
+ vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, pKeep,
613
+ pEmptyDirs);
614
+ blob_reset(&root);
615
+ db_finalize(&q);
616
+ db_prepare(&q,
617
+ "SELECT %Q || x FROM dscan_temp"
618
+ " WHERE x NOT IN (%s) AND y = 0"
619
+ " ORDER BY 1 DESC",
620
+ g.zLocalRoot, fossil_all_reserved_names(0)
621
+ );
622
+ while( db_step(&q)==SQLITE_ROW ){
623
+ const char *zName = db_column_text(&q, 0);
624
+ if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
625
+ Blob ans;
626
+ char cReply;
627
+ char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ",
628
+ zName+nRoot);
629
+ blob_zero(&ans);
630
+ prompt_user(prompt, &ans);
631
+ cReply = blob_str(&ans)[0];
632
+ if( cReply=='a' || cReply=='A' ){
633
+ allDirFlag = 1;
634
+ }else if( cReply!='y' && cReply!='Y' ){
635
+ blob_reset(&ans);
636
+ continue;
637
+ }
638
+ blob_reset(&ans);
639
+ }
640
+ if ( dryRunFlag || file_rmdir(zName)==0 ){
641
+ if( verboseFlag || dryRunFlag ){
642
+ fossil_print("Removed unmanaged directory: %s\n", zName+nRoot);
643
+ }
644
+ }else if( verboseFlag ){
645
+ fossil_print("Could not remove directory: %s\n", zName+nRoot);
646
+ }
598647
}
648
+ glob_free(pEmptyDirs);
599649
}
600650
glob_free(pClean);
651
+ glob_free(pKeep);
652
+ glob_free(pIgnore);
601653
db_finalize(&q);
602654
}
603655
604656
/*
605657
** Prompt the user for a check-in or stash comment (given in pPrompt),
606658
--- src/checkin.c
+++ src/checkin.c
@@ -363,27 +363,28 @@
363 }
364 db_finalize(&q);
365 }
366
367 /*
368 ** Create a TEMP table named SFILE and add all unmanaged files named on the command-line
369 ** to that table. If directories are named, then add all unmanaged files contained
370 ** underneath those directories. If there are no files or directories named on the
371 ** command-line, then add all unmanaged files anywhere in the checkout.
 
372 */
373 static void locate_unmanaged_files(
374 int argc, /* Number of command-line arguments to examine */
375 char **argv, /* values of command-line arguments */
376 unsigned scanFlags, /* Zero or more SCAN_xxx flags */
377 Glob *pIgnore1, /* Do not add files that match this GLOB */
378 Glob *pIgnore2 /* Omit files matching this GLOB too */
379 ){
380 Blob name; /* Name of a candidate file or directory */
381 char *zName; /* Name of a candidate file or directory */
382 int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */
383 int i; /* Loop counter */
384 int nRoot; /* length of g.zLocalRoot */
385
386 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)",
387 filename_collation());
388 nRoot = (int)strlen(g.zLocalRoot);
389 if( argc==0 ){
@@ -508,10 +509,17 @@
508 ** is used.
509 **
510 ** Options:
511 ** --case-sensitive <BOOL> override case-sensitive setting
512 ** --dotfiles Include files beginning with a dot (".").
 
 
 
 
 
 
 
513 ** -f|--force Remove files without prompting.
514 ** --clean <CSG> Never prompt for files matching this
515 ** comma separated list of glob patterns.
516 ** --ignore <CSG> Ignore files matching patterns from the
517 ** comma separated list of glob patterns.
@@ -522,11 +530,11 @@
522 ** -v|--verbose Show all files as they are removed.
523 **
524 ** See also: addremove, extra, status
525 */
526 void clean_cmd(void){
527 int allFlag, dryRunFlag, verboseFlag;
528 unsigned scanFlags = 0;
529 const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag;
530 Blob repo;
531 Stmt q;
532 Glob *pIgnore, *pKeep, *pClean;
@@ -534,11 +542,12 @@
534
535 dryRunFlag = find_option("dry-run","n",0)!=0;
536 if( !dryRunFlag ){
537 dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
538 }
539 allFlag = find_option("force","f",0)!=0;
 
540 if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
541 if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
542 zIgnoreFlag = find_option("ignore",0,1);
543 verboseFlag = find_option("verbose","v",0)!=0;
544 zKeepFlag = find_option("keep",0,1);
@@ -557,12 +566,10 @@
557 verify_all_options();
558 pIgnore = glob_create(zIgnoreFlag);
559 pKeep = glob_create(zKeepFlag);
560 pClean = glob_create(zCleanFlag);
561 locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, pKeep);
562 glob_free(pKeep);
563 glob_free(pIgnore);
564 db_prepare(&q,
565 "SELECT %Q || x FROM sfile"
566 " WHERE x NOT IN (%s)"
567 " ORDER BY 1",
568 g.zLocalRoot, fossil_all_reserved_names(0)
@@ -572,34 +579,79 @@
572 }
573 db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)");
574 nRoot = (int)strlen(g.zLocalRoot);
575 while( db_step(&q)==SQLITE_ROW ){
576 const char *zName = db_column_text(&q, 0);
577 if( !allFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
578 Blob ans;
579 char cReply;
580 char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ",
581 zName+nRoot);
582 blob_zero(&ans);
583 prompt_user(prompt, &ans);
584 cReply = blob_str(&ans)[0];
585 if( cReply=='a' || cReply=='A' ){
586 allFlag = 1;
587 }else if( cReply!='y' && cReply!='Y' ){
588 blob_reset(&ans);
589 continue;
590 }
591 blob_reset(&ans);
592 }
593 if( verboseFlag || dryRunFlag ){
594 fossil_print("Removed unmanaged file: %s\n", zName+nRoot);
 
 
 
 
595 }
596 if( !dryRunFlag ){
597 file_delete(zName);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598 }
 
599 }
600 glob_free(pClean);
 
 
601 db_finalize(&q);
602 }
603
604 /*
605 ** Prompt the user for a check-in or stash comment (given in pPrompt),
606
--- src/checkin.c
+++ src/checkin.c
@@ -363,27 +363,28 @@
363 }
364 db_finalize(&q);
365 }
366
367 /*
368 ** Create a TEMP table named SFILE and add all unmanaged files named on
369 ** the command-line to that table. If directories are named, then add
370 ** all unmanaged files contained underneath those directories. If there
371 ** are no files or directories named on the command-line, then add all
372 ** unmanaged files anywhere in the checkout.
373 */
374 static void locate_unmanaged_files(
375 int argc, /* Number of command-line arguments to examine */
376 char **argv, /* values of command-line arguments */
377 unsigned scanFlags, /* Zero or more SCAN_xxx flags */
378 Glob *pIgnore1, /* Do not add files that match this GLOB */
379 Glob *pIgnore2 /* Omit files matching this GLOB too */
380 ){
381 Blob name; /* Name of a candidate file or directory */
382 char *zName; /* Name of a candidate file or directory */
383 int isDir; /* 1 for a directory, 0 if doesn't exist, 2 for anything else */
384 int i; /* Loop counter */
385 int nRoot; /* length of g.zLocalRoot */
386
387 db_multi_exec("CREATE TEMP TABLE sfile(x TEXT PRIMARY KEY %s)",
388 filename_collation());
389 nRoot = (int)strlen(g.zLocalRoot);
390 if( argc==0 ){
@@ -508,10 +509,17 @@
509 ** is used.
510 **
511 ** Options:
512 ** --case-sensitive <BOOL> override case-sensitive setting
513 ** --dotfiles Include files beginning with a dot (".").
514 ** --emptydirs Remove any empty directories that are not
515 ** explicitly exempted via the empty-dirs setting
516 ** or another applicable setting or command line
517 ** argument. Matching files, if any, are removed
518 ** prior to checking for any empty directories;
519 ** therefore, directories that contain only files
520 ** that were removed will be removed as well.
521 ** -f|--force Remove files without prompting.
522 ** --clean <CSG> Never prompt for files matching this
523 ** comma separated list of glob patterns.
524 ** --ignore <CSG> Ignore files matching patterns from the
525 ** comma separated list of glob patterns.
@@ -522,11 +530,11 @@
530 ** -v|--verbose Show all files as they are removed.
531 **
532 ** See also: addremove, extra, status
533 */
534 void clean_cmd(void){
535 int allFileFlag, allDirFlag, dryRunFlag, emptyDirsFlag, verboseFlag;
536 unsigned scanFlags = 0;
537 const char *zIgnoreFlag, *zKeepFlag, *zCleanFlag;
538 Blob repo;
539 Stmt q;
540 Glob *pIgnore, *pKeep, *pClean;
@@ -534,11 +542,12 @@
542
543 dryRunFlag = find_option("dry-run","n",0)!=0;
544 if( !dryRunFlag ){
545 dryRunFlag = find_option("test",0,0)!=0; /* deprecated */
546 }
547 allFileFlag = allDirFlag = find_option("force","f",0)!=0;
548 emptyDirsFlag = find_option("emptydirs","d",0)!=0;
549 if( find_option("dotfiles",0,0)!=0 ) scanFlags |= SCAN_ALL;
550 if( find_option("temp",0,0)!=0 ) scanFlags |= SCAN_TEMP;
551 zIgnoreFlag = find_option("ignore",0,1);
552 verboseFlag = find_option("verbose","v",0)!=0;
553 zKeepFlag = find_option("keep",0,1);
@@ -557,12 +566,10 @@
566 verify_all_options();
567 pIgnore = glob_create(zIgnoreFlag);
568 pKeep = glob_create(zKeepFlag);
569 pClean = glob_create(zCleanFlag);
570 locate_unmanaged_files(g.argc-2, g.argv+2, scanFlags, pIgnore, pKeep);
 
 
571 db_prepare(&q,
572 "SELECT %Q || x FROM sfile"
573 " WHERE x NOT IN (%s)"
574 " ORDER BY 1",
575 g.zLocalRoot, fossil_all_reserved_names(0)
@@ -572,34 +579,79 @@
579 }
580 db_multi_exec("DELETE FROM sfile WHERE x IN (SELECT pathname FROM vfile)");
581 nRoot = (int)strlen(g.zLocalRoot);
582 while( db_step(&q)==SQLITE_ROW ){
583 const char *zName = db_column_text(&q, 0);
584 if( !allFileFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
585 Blob ans;
586 char cReply;
587 char *prompt = mprintf("Remove unmanaged file \"%s\" (a=all/y/N)? ",
588 zName+nRoot);
589 blob_zero(&ans);
590 prompt_user(prompt, &ans);
591 cReply = blob_str(&ans)[0];
592 if( cReply=='a' || cReply=='A' ){
593 allFileFlag = 1;
594 }else if( cReply!='y' && cReply!='Y' ){
595 blob_reset(&ans);
596 continue;
597 }
598 blob_reset(&ans);
599 }
600 if ( dryRunFlag || file_delete(zName)==0 ){
601 if( verboseFlag || dryRunFlag ){
602 fossil_print("Removed unmanaged file: %s\n", zName+nRoot);
603 }
604 }else if( verboseFlag ){
605 fossil_print("Could not remove file: %s\n", zName+nRoot);
606 }
607 }
608 if( emptyDirsFlag ){
609 Glob *pEmptyDirs = glob_create(db_get("empty-dirs", 0));
610 Blob root;
611 blob_init(&root, g.zLocalRoot, nRoot - 1);
612 vfile_dir_scan(&root, blob_size(&root), scanFlags, pIgnore, pKeep,
613 pEmptyDirs);
614 blob_reset(&root);
615 db_finalize(&q);
616 db_prepare(&q,
617 "SELECT %Q || x FROM dscan_temp"
618 " WHERE x NOT IN (%s) AND y = 0"
619 " ORDER BY 1 DESC",
620 g.zLocalRoot, fossil_all_reserved_names(0)
621 );
622 while( db_step(&q)==SQLITE_ROW ){
623 const char *zName = db_column_text(&q, 0);
624 if( !allDirFlag && !dryRunFlag && !glob_match(pClean, zName+nRoot) ){
625 Blob ans;
626 char cReply;
627 char *prompt = mprintf("Remove empty directory \"%s\" (a=all/y/N)? ",
628 zName+nRoot);
629 blob_zero(&ans);
630 prompt_user(prompt, &ans);
631 cReply = blob_str(&ans)[0];
632 if( cReply=='a' || cReply=='A' ){
633 allDirFlag = 1;
634 }else if( cReply!='y' && cReply!='Y' ){
635 blob_reset(&ans);
636 continue;
637 }
638 blob_reset(&ans);
639 }
640 if ( dryRunFlag || file_rmdir(zName)==0 ){
641 if( verboseFlag || dryRunFlag ){
642 fossil_print("Removed unmanaged directory: %s\n", zName+nRoot);
643 }
644 }else if( verboseFlag ){
645 fossil_print("Could not remove directory: %s\n", zName+nRoot);
646 }
647 }
648 glob_free(pEmptyDirs);
649 }
650 glob_free(pClean);
651 glob_free(pKeep);
652 glob_free(pIgnore);
653 db_finalize(&q);
654 }
655
656 /*
657 ** Prompt the user for a check-in or stash comment (given in pPrompt),
658
+30 -3
--- src/file.c
+++ src/file.c
@@ -461,20 +461,24 @@
461461
fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
462462
}
463463
464464
/*
465465
** Delete a file.
466
+**
467
+** Returns zero upon success.
466468
*/
467
-void file_delete(const char *zFilename){
469
+int file_delete(const char *zFilename){
470
+ int rc;
468471
#ifdef _WIN32
469472
wchar_t *z = fossil_utf8_to_filename(zFilename);
470
- _wunlink(z);
473
+ rc = _wunlink(z);
471474
#else
472475
char *z = fossil_utf8_to_filename(zFilename);
473
- unlink(zFilename);
476
+ rc = unlink(zFilename);
474477
#endif
475478
fossil_filename_free(z);
479
+ return rc;
476480
}
477481
478482
/*
479483
** Create the directory named in the argument, if it does not already
480484
** exist. If forceFlag is 1, delete any prior non-directory object
@@ -493,10 +497,33 @@
493497
wchar_t *zMbcs = fossil_utf8_to_filename(zName);
494498
rc = _wmkdir(zMbcs);
495499
#else
496500
char *zMbcs = fossil_utf8_to_filename(zName);
497501
rc = mkdir(zName, 0755);
502
+#endif
503
+ fossil_filename_free(zMbcs);
504
+ return rc;
505
+ }
506
+ return 0;
507
+}
508
+
509
+/*
510
+** Removes the directory named in the argument, if it exists. The directory
511
+** must be empty and cannot be the current directory or the root directory.
512
+**
513
+** Returns zero upon success.
514
+*/
515
+int file_rmdir(const char *zName){
516
+ int rc = file_wd_isdir(zName);
517
+ if( rc==2 ) return 1; /* cannot remove normal file */
518
+ if( rc==1 ){
519
+#if defined(_WIN32)
520
+ wchar_t *zMbcs = fossil_utf8_to_filename(zName);
521
+ rc = _wrmdir(zMbcs);
522
+#else
523
+ char *zMbcs = fossil_utf8_to_filename(zName);
524
+ rc = rmdir(zName);
498525
#endif
499526
fossil_filename_free(zMbcs);
500527
return rc;
501528
}
502529
return 0;
503530
--- src/file.c
+++ src/file.c
@@ -461,20 +461,24 @@
461 fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
462 }
463
464 /*
465 ** Delete a file.
 
 
466 */
467 void file_delete(const char *zFilename){
 
468 #ifdef _WIN32
469 wchar_t *z = fossil_utf8_to_filename(zFilename);
470 _wunlink(z);
471 #else
472 char *z = fossil_utf8_to_filename(zFilename);
473 unlink(zFilename);
474 #endif
475 fossil_filename_free(z);
 
476 }
477
478 /*
479 ** Create the directory named in the argument, if it does not already
480 ** exist. If forceFlag is 1, delete any prior non-directory object
@@ -493,10 +497,33 @@
493 wchar_t *zMbcs = fossil_utf8_to_filename(zName);
494 rc = _wmkdir(zMbcs);
495 #else
496 char *zMbcs = fossil_utf8_to_filename(zName);
497 rc = mkdir(zName, 0755);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
498 #endif
499 fossil_filename_free(zMbcs);
500 return rc;
501 }
502 return 0;
503
--- src/file.c
+++ src/file.c
@@ -461,20 +461,24 @@
461 fossil_print("Set mtime of \"%s\" to %s (%lld)\n", zFile, zDate, iMTime);
462 }
463
464 /*
465 ** Delete a file.
466 **
467 ** Returns zero upon success.
468 */
469 int file_delete(const char *zFilename){
470 int rc;
471 #ifdef _WIN32
472 wchar_t *z = fossil_utf8_to_filename(zFilename);
473 rc = _wunlink(z);
474 #else
475 char *z = fossil_utf8_to_filename(zFilename);
476 rc = unlink(zFilename);
477 #endif
478 fossil_filename_free(z);
479 return rc;
480 }
481
482 /*
483 ** Create the directory named in the argument, if it does not already
484 ** exist. If forceFlag is 1, delete any prior non-directory object
@@ -493,10 +497,33 @@
497 wchar_t *zMbcs = fossil_utf8_to_filename(zName);
498 rc = _wmkdir(zMbcs);
499 #else
500 char *zMbcs = fossil_utf8_to_filename(zName);
501 rc = mkdir(zName, 0755);
502 #endif
503 fossil_filename_free(zMbcs);
504 return rc;
505 }
506 return 0;
507 }
508
509 /*
510 ** Removes the directory named in the argument, if it exists. The directory
511 ** must be empty and cannot be the current directory or the root directory.
512 **
513 ** Returns zero upon success.
514 */
515 int file_rmdir(const char *zName){
516 int rc = file_wd_isdir(zName);
517 if( rc==2 ) return 1; /* cannot remove normal file */
518 if( rc==1 ){
519 #if defined(_WIN32)
520 wchar_t *zMbcs = fossil_utf8_to_filename(zName);
521 rc = _wrmdir(zMbcs);
522 #else
523 char *zMbcs = fossil_utf8_to_filename(zName);
524 rc = rmdir(zName);
525 #endif
526 fossil_filename_free(zMbcs);
527 return rc;
528 }
529 return 0;
530
+126 -7
--- src/vfile.c
+++ src/vfile.c
@@ -428,15 +428,16 @@
428428
** of pPath when inserting into the SFILE table.
429429
**
430430
** Subdirectories are scanned recursively.
431431
** Omit files named in VFILE.
432432
**
433
-** Files whose names begin with "." are omitted unless allFlag is true.
433
+** Files whose names begin with "." are omitted unless the SCAN_ALL
434
+** flag is set.
434435
**
435
-** Any files or directories that match the glob pattern pIgnore are
436
-** excluded from the scan. Name matching occurs after the first
437
-** nPrefix characters are elided from the filename.
436
+** Any files or directories that match the glob patterns pIgnore*
437
+** are excluded from the scan. Name matching occurs after the
438
+** first nPrefix characters are elided from the filename.
438439
*/
439440
void vfile_scan(
440441
Blob *pPath, /* Directory to be scanned */
441442
int nPrefix, /* Number of bytes in directory name */
442443
unsigned scanFlags, /* Zero or more SCAN_xxx flags */
@@ -443,11 +444,10 @@
443444
Glob *pIgnore1, /* Do not add files that match this GLOB */
444445
Glob *pIgnore2 /* Omit files matching this GLOB too */
445446
){
446447
DIR *d;
447448
int origSize;
448
- const char *zDir;
449449
struct dirent *pEntry;
450450
int skipAll = 0;
451451
static Stmt ins;
452452
static int depth = 0;
453453
void *zNative;
@@ -468,12 +468,11 @@
468468
" pathname=:file %s)", filename_collation()
469469
);
470470
}
471471
depth++;
472472
473
- zDir = blob_str(pPath);
474
- zNative = fossil_utf8_to_filename(zDir);
473
+ zNative = fossil_utf8_to_filename(blob_str(pPath));
475474
d = opendir(zNative);
476475
if( d ){
477476
while( (pEntry=readdir(d))!=0 ){
478477
char *zPath;
479478
char *zUtf8;
@@ -509,10 +508,130 @@
509508
depth--;
510509
if( depth==0 ){
511510
db_finalize(&ins);
512511
}
513512
}
513
+
514
+/*
515
+** Scans the specified base directory for any directories within it, while
516
+** keeping a count of how many files they each contains, either directly or
517
+** indirectly.
518
+**
519
+** Subdirectories are scanned recursively.
520
+** Omit files named in VFILE.
521
+**
522
+** Directories whose names begin with "." are omitted unless the SCAN_ALL
523
+** flag is set.
524
+**
525
+** Any directories that match the glob patterns pIgnore* are excluded from
526
+** the scan. Name matching occurs after the first nPrefix characters are
527
+** elided from the filename.
528
+**
529
+** Returns the total number of files found.
530
+*/
531
+int vfile_dir_scan(
532
+ Blob *pPath, /* Base directory to be scanned */
533
+ int nPrefix, /* Number of bytes in base directory name */
534
+ unsigned scanFlags, /* Zero or more SCAN_xxx flags */
535
+ Glob *pIgnore1, /* Do not add directories that match this GLOB */
536
+ Glob *pIgnore2, /* Omit directories matching this GLOB too */
537
+ Glob *pIgnore3 /* Omit directories matching this GLOB too */
538
+){
539
+ int result = 0;
540
+ DIR *d;
541
+ int origSize;
542
+ struct dirent *pEntry;
543
+ int skipAll = 0;
544
+ static Stmt ins;
545
+ static Stmt upd;
546
+ static int depth = 0;
547
+ void *zNative;
548
+
549
+ origSize = blob_size(pPath);
550
+ if( pIgnore1 || pIgnore2 || pIgnore3 ){
551
+ blob_appendf(pPath, "/");
552
+ if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
553
+ if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
554
+ if( glob_match(pIgnore3, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
555
+ blob_resize(pPath, origSize);
556
+ }
557
+ if( skipAll ) return result;
558
+
559
+ if( depth==0 ){
560
+ db_multi_exec("DROP TABLE IF EXISTS dscan_temp;"
561
+ "CREATE TEMP TABLE dscan_temp("
562
+ " x TEXT PRIMARY KEY %s, y INTEGER)",
563
+ filename_collation());
564
+ db_prepare(&ins,
565
+ "INSERT OR IGNORE INTO dscan_temp(x, y) SELECT :file, :count"
566
+ " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE"
567
+ " pathname GLOB :pattern %s)", filename_collation()
568
+ );
569
+ db_prepare(&upd,
570
+ "UPDATE OR IGNORE dscan_temp SET y = coalesce(y, 0) + 1"
571
+ " WHERE x=:file %s",
572
+ filename_collation()
573
+ );
574
+ }
575
+ depth++;
576
+
577
+ zNative = fossil_utf8_to_filename(blob_str(pPath));
578
+ d = opendir(zNative);
579
+ if( d ){
580
+ while( (pEntry=readdir(d))!=0 ){
581
+ char *zOrigPath;
582
+ char *zPath;
583
+ char *zUtf8;
584
+ if( pEntry->d_name[0]=='.' ){
585
+ if( (scanFlags & SCAN_ALL)==0 ) continue;
586
+ if( pEntry->d_name[1]==0 ) continue;
587
+ if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
588
+ }
589
+ zOrigPath = mprintf("%s", blob_str(pPath));
590
+ zUtf8 = fossil_filename_to_utf8(pEntry->d_name);
591
+ blob_appendf(pPath, "/%s", zUtf8);
592
+ zPath = blob_str(pPath);
593
+ if( glob_match(pIgnore1, &zPath[nPrefix+1]) ||
594
+ glob_match(pIgnore2, &zPath[nPrefix+1]) ||
595
+ glob_match(pIgnore3, &zPath[nPrefix+1]) ){
596
+ /* do nothing */
597
+ }else if( file_wd_isdir(zPath)==1 ){
598
+ if( !vfile_top_of_checkout(zPath) ){
599
+ Blob dirPattern;
600
+ int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1,
601
+ pIgnore2, pIgnore3);
602
+ blob_init(&dirPattern, &zPath[nPrefix+1], -1);
603
+ blob_appendf(&dirPattern, "*");
604
+ db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
605
+ db_bind_int(&ins, ":count", count);
606
+ db_bind_text(&ins, ":pattern", blob_str(&dirPattern));
607
+ db_step(&ins);
608
+ db_reset(&ins);
609
+ blob_reset(&dirPattern);
610
+ result += count; /* found X normal files? */
611
+ }
612
+ }else if( file_wd_isfile_or_link(zPath) ){
613
+ db_bind_text(&upd, ":file", zOrigPath);
614
+ db_step(&upd);
615
+ db_reset(&upd);
616
+ result++; /* found 1 normal file */
617
+ }
618
+ fossil_filename_free(zUtf8);
619
+ blob_resize(pPath, origSize);
620
+ fossil_free(zOrigPath);
621
+ }
622
+ closedir(d);
623
+ }
624
+ fossil_filename_free(zNative);
625
+
626
+ depth--;
627
+ if( depth==0 ){
628
+ db_finalize(&upd);
629
+ db_finalize(&ins);
630
+ }
631
+ return result;
632
+}
514633
515634
/*
516635
** Compute an aggregate MD5 checksum over the disk image of every
517636
** file in vid. The file names are part of the checksum. The resulting
518637
** checksum is the same as is expected on the R-card of a manifest.
519638
--- src/vfile.c
+++ src/vfile.c
@@ -428,15 +428,16 @@
428 ** of pPath when inserting into the SFILE table.
429 **
430 ** Subdirectories are scanned recursively.
431 ** Omit files named in VFILE.
432 **
433 ** Files whose names begin with "." are omitted unless allFlag is true.
 
434 **
435 ** Any files or directories that match the glob pattern pIgnore are
436 ** excluded from the scan. Name matching occurs after the first
437 ** nPrefix characters are elided from the filename.
438 */
439 void vfile_scan(
440 Blob *pPath, /* Directory to be scanned */
441 int nPrefix, /* Number of bytes in directory name */
442 unsigned scanFlags, /* Zero or more SCAN_xxx flags */
@@ -443,11 +444,10 @@
443 Glob *pIgnore1, /* Do not add files that match this GLOB */
444 Glob *pIgnore2 /* Omit files matching this GLOB too */
445 ){
446 DIR *d;
447 int origSize;
448 const char *zDir;
449 struct dirent *pEntry;
450 int skipAll = 0;
451 static Stmt ins;
452 static int depth = 0;
453 void *zNative;
@@ -468,12 +468,11 @@
468 " pathname=:file %s)", filename_collation()
469 );
470 }
471 depth++;
472
473 zDir = blob_str(pPath);
474 zNative = fossil_utf8_to_filename(zDir);
475 d = opendir(zNative);
476 if( d ){
477 while( (pEntry=readdir(d))!=0 ){
478 char *zPath;
479 char *zUtf8;
@@ -509,10 +508,130 @@
509 depth--;
510 if( depth==0 ){
511 db_finalize(&ins);
512 }
513 }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
514
515 /*
516 ** Compute an aggregate MD5 checksum over the disk image of every
517 ** file in vid. The file names are part of the checksum. The resulting
518 ** checksum is the same as is expected on the R-card of a manifest.
519
--- src/vfile.c
+++ src/vfile.c
@@ -428,15 +428,16 @@
428 ** of pPath when inserting into the SFILE table.
429 **
430 ** Subdirectories are scanned recursively.
431 ** Omit files named in VFILE.
432 **
433 ** Files whose names begin with "." are omitted unless the SCAN_ALL
434 ** flag is set.
435 **
436 ** Any files or directories that match the glob patterns pIgnore*
437 ** are excluded from the scan. Name matching occurs after the
438 ** first nPrefix characters are elided from the filename.
439 */
440 void vfile_scan(
441 Blob *pPath, /* Directory to be scanned */
442 int nPrefix, /* Number of bytes in directory name */
443 unsigned scanFlags, /* Zero or more SCAN_xxx flags */
@@ -443,11 +444,10 @@
444 Glob *pIgnore1, /* Do not add files that match this GLOB */
445 Glob *pIgnore2 /* Omit files matching this GLOB too */
446 ){
447 DIR *d;
448 int origSize;
 
449 struct dirent *pEntry;
450 int skipAll = 0;
451 static Stmt ins;
452 static int depth = 0;
453 void *zNative;
@@ -468,12 +468,11 @@
468 " pathname=:file %s)", filename_collation()
469 );
470 }
471 depth++;
472
473 zNative = fossil_utf8_to_filename(blob_str(pPath));
 
474 d = opendir(zNative);
475 if( d ){
476 while( (pEntry=readdir(d))!=0 ){
477 char *zPath;
478 char *zUtf8;
@@ -509,10 +508,130 @@
508 depth--;
509 if( depth==0 ){
510 db_finalize(&ins);
511 }
512 }
513
514 /*
515 ** Scans the specified base directory for any directories within it, while
516 ** keeping a count of how many files they each contains, either directly or
517 ** indirectly.
518 **
519 ** Subdirectories are scanned recursively.
520 ** Omit files named in VFILE.
521 **
522 ** Directories whose names begin with "." are omitted unless the SCAN_ALL
523 ** flag is set.
524 **
525 ** Any directories that match the glob patterns pIgnore* are excluded from
526 ** the scan. Name matching occurs after the first nPrefix characters are
527 ** elided from the filename.
528 **
529 ** Returns the total number of files found.
530 */
531 int vfile_dir_scan(
532 Blob *pPath, /* Base directory to be scanned */
533 int nPrefix, /* Number of bytes in base directory name */
534 unsigned scanFlags, /* Zero or more SCAN_xxx flags */
535 Glob *pIgnore1, /* Do not add directories that match this GLOB */
536 Glob *pIgnore2, /* Omit directories matching this GLOB too */
537 Glob *pIgnore3 /* Omit directories matching this GLOB too */
538 ){
539 int result = 0;
540 DIR *d;
541 int origSize;
542 struct dirent *pEntry;
543 int skipAll = 0;
544 static Stmt ins;
545 static Stmt upd;
546 static int depth = 0;
547 void *zNative;
548
549 origSize = blob_size(pPath);
550 if( pIgnore1 || pIgnore2 || pIgnore3 ){
551 blob_appendf(pPath, "/");
552 if( glob_match(pIgnore1, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
553 if( glob_match(pIgnore2, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
554 if( glob_match(pIgnore3, &blob_str(pPath)[nPrefix+1]) ) skipAll = 1;
555 blob_resize(pPath, origSize);
556 }
557 if( skipAll ) return result;
558
559 if( depth==0 ){
560 db_multi_exec("DROP TABLE IF EXISTS dscan_temp;"
561 "CREATE TEMP TABLE dscan_temp("
562 " x TEXT PRIMARY KEY %s, y INTEGER)",
563 filename_collation());
564 db_prepare(&ins,
565 "INSERT OR IGNORE INTO dscan_temp(x, y) SELECT :file, :count"
566 " WHERE NOT EXISTS(SELECT 1 FROM vfile WHERE"
567 " pathname GLOB :pattern %s)", filename_collation()
568 );
569 db_prepare(&upd,
570 "UPDATE OR IGNORE dscan_temp SET y = coalesce(y, 0) + 1"
571 " WHERE x=:file %s",
572 filename_collation()
573 );
574 }
575 depth++;
576
577 zNative = fossil_utf8_to_filename(blob_str(pPath));
578 d = opendir(zNative);
579 if( d ){
580 while( (pEntry=readdir(d))!=0 ){
581 char *zOrigPath;
582 char *zPath;
583 char *zUtf8;
584 if( pEntry->d_name[0]=='.' ){
585 if( (scanFlags & SCAN_ALL)==0 ) continue;
586 if( pEntry->d_name[1]==0 ) continue;
587 if( pEntry->d_name[1]=='.' && pEntry->d_name[2]==0 ) continue;
588 }
589 zOrigPath = mprintf("%s", blob_str(pPath));
590 zUtf8 = fossil_filename_to_utf8(pEntry->d_name);
591 blob_appendf(pPath, "/%s", zUtf8);
592 zPath = blob_str(pPath);
593 if( glob_match(pIgnore1, &zPath[nPrefix+1]) ||
594 glob_match(pIgnore2, &zPath[nPrefix+1]) ||
595 glob_match(pIgnore3, &zPath[nPrefix+1]) ){
596 /* do nothing */
597 }else if( file_wd_isdir(zPath)==1 ){
598 if( !vfile_top_of_checkout(zPath) ){
599 Blob dirPattern;
600 int count = vfile_dir_scan(pPath, nPrefix, scanFlags, pIgnore1,
601 pIgnore2, pIgnore3);
602 blob_init(&dirPattern, &zPath[nPrefix+1], -1);
603 blob_appendf(&dirPattern, "*");
604 db_bind_text(&ins, ":file", &zPath[nPrefix+1]);
605 db_bind_int(&ins, ":count", count);
606 db_bind_text(&ins, ":pattern", blob_str(&dirPattern));
607 db_step(&ins);
608 db_reset(&ins);
609 blob_reset(&dirPattern);
610 result += count; /* found X normal files? */
611 }
612 }else if( file_wd_isfile_or_link(zPath) ){
613 db_bind_text(&upd, ":file", zOrigPath);
614 db_step(&upd);
615 db_reset(&upd);
616 result++; /* found 1 normal file */
617 }
618 fossil_filename_free(zUtf8);
619 blob_resize(pPath, origSize);
620 fossil_free(zOrigPath);
621 }
622 closedir(d);
623 }
624 fossil_filename_free(zNative);
625
626 depth--;
627 if( depth==0 ){
628 db_finalize(&upd);
629 db_finalize(&ins);
630 }
631 return result;
632 }
633
634 /*
635 ** Compute an aggregate MD5 checksum over the disk image of every
636 ** file in vid. The file names are part of the checksum. The resulting
637 ** checksum is the same as is expected on the R-card of a manifest.
638

Keyboard Shortcuts

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