Fossil SCM

Enhance the "fossil grep" command with new options. Work in progress. Needs more testing.

drh 2019-11-29 14:44 trunk
Commit 1bf2f84843b063c61afc7f3ba95313dc5035d69d57dcad1266c4811fe1167b01
1 file changed +99 -30
+99 -30
--- src/regexp.c
+++ src/regexp.c
@@ -719,10 +719,11 @@
719719
720720
/*
721721
** Flags for grep_buffer()
722722
*/
723723
#define GREP_EXISTS 0x001 /* If any match, print only the name and stop */
724
+#define GREP_QUIET 0x002 /* Return code only */
724725
725726
/*
726727
** Run a "grep" over a text file
727728
*/
728729
static int grep_buffer(
@@ -737,14 +738,16 @@
737738
n = j - i;
738739
ln++;
739740
if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){
740741
cnt++;
741742
if( flags & GREP_EXISTS ){
742
- fossil_print("%S\n", zName);
743
+ if( (flags & GREP_QUIET)==0 ) fossil_print("%S\n", zName);
743744
break;
744745
}
745
- fossil_print("%S:%d:%.*s\n", zName, ln, n, z+i);
746
+ if( (flags & GREP_QUIET)==0 ){
747
+ fossil_print("%S:%d:%.*s\n", zName, ln, n, z+i);
748
+ }
746749
}
747750
}
748751
return cnt;
749752
}
750753
@@ -787,61 +790,127 @@
787790
}
788791
789792
/*
790793
** COMMAND: grep
791794
**
792
-** Usage: %fossil grep [OPTIONS] PATTERN FILENAME
795
+** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ...
793796
**
794797
** Attempt to match the given POSIX extended regular expression PATTERN
795798
** over all historic versions of FILENAME. For details of the supported
796799
** RE dialect, see https://fossil-scm.org/fossil/doc/trunk/www/grep.md
797800
**
798801
** Options:
799802
**
800
-** -i|--ignore-case Ignore case
801
-** -l|--files-with-matches List only checkin ID for versions that match
802
-** -v|--verbose Show each file as it is analyzed
803
+** -c|--count Suppress normal output; instead print a count
804
+** of the number of matching files
805
+** -H|--checkin-hash Show the check-in hash rather than
806
+** file artifact hash for each match
807
+** -i|--ignore-case Ignore case
808
+** -l|--files-with-matches List only hash for each match
809
+** --once Stop searching after the first match
810
+** -s|--no-messages Suppress error messages about nonexistant
811
+** or unreadable files
812
+** -v|--invert-match Invert the sense of matching. Show only
813
+** files that have no matches. Implies -l
814
+** --verbose Show each file as it is analyzed
803815
*/
804816
void re_grep_cmd(void){
805817
u32 flags = 0;
806818
int bVerbose = 0;
807819
ReCompiled *pRe;
808820
const char *zErr;
809821
int ignoreCase = 0;
810822
Blob fullName;
823
+ int ckinHash = 0;
824
+ int ii;
825
+ int nMatch = 0;
826
+ int bNoMsg;
827
+ int cntFlag;
828
+ int bOnce;
829
+ int bInvert;
830
+ int nSearch = 0;
811831
812832
if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1;
813833
if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS;
814
- if( find_option("verbose","v",0)!=0 ) bVerbose = 1;
834
+ if( find_option("verbose",0,0)!=0 ) bVerbose = 1;
835
+ ckinHash = find_option("checkin-hash","H",0)!=0;
836
+ if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS;
837
+ bNoMsg = find_option("no-messages","s",0)!=0;
838
+ bOnce = find_option("once",0,0)!=0;
839
+ bInvert = find_option("invert-match","v",0)!=0;
840
+ if( bInvert ){
841
+ flags |= GREP_QUIET|GREP_EXISTS;
842
+ }
843
+ cntFlag = find_option("count","c",0)!=0;
844
+ if( cntFlag ){
845
+ flags |= GREP_QUIET|GREP_EXISTS;
846
+ }
815847
db_find_and_open_repository(0, 0);
816848
verify_all_options();
817849
if( g.argc<4 ){
818
- usage("REGEXP FILENAME");
850
+ usage("REGEXP FILENAME ...");
819851
}
820852
zErr = re_compile(&pRe, g.argv[2], ignoreCase);
821853
if( zErr ) fossil_fatal("%s", zErr);
822854
823
- if( file_tree_name(g.argv[3], &fullName, 0, 0) ){
824
- int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
825
- blob_str(&fullName));
826
- if( fnid ){
827
- Stmt q;
828
- add_content_sql_commands(g.db);
829
- db_prepare(&q,
830
- "SELECT content(ux), ux FROM ("
831
- " SELECT blob.uuid AS ux, min(event.mtime) AS mx"
832
- " FROM mlink, blob, event"
833
- " WHERE mlink.mid=event.objid"
834
- " AND mlink.fid=blob.rid"
835
- " AND mlink.fnid=%d"
836
- " GROUP BY blob.uuid"
837
- ") ORDER BY mx DESC;",
838
- fnid
839
- );
840
- while( db_step(&q)==SQLITE_ROW ){
841
- if( bVerbose ) fossil_print("%S:\n", db_column_text(&q,1));
842
- grep_buffer(pRe, db_column_text(&q,1), db_column_text(&q,0), flags);
843
- }
844
- db_finalize(&q);
855
+ add_content_sql_commands(g.db);
856
+ for(ii=3; ii<g.argc; ii++){
857
+ const char *zTarget = g.argv[ii];
858
+ if( nMatch ){
859
+ if( (flags & GREP_QUIET)!=0 ) break;
860
+ if( bOnce ) break;
861
+ }
862
+ if( file_tree_name(zTarget, &fullName, 0, 1) ){
863
+ int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
864
+ blob_str(&fullName));
865
+ if( !fnid ){
866
+ if( bNoMsg ) continue;
867
+ if( file_size(zTarget, ExtFILE)<0 ){
868
+ fossil_fatal("no such file: %s", zTarget);
869
+ }
870
+ fossil_fatal("not a managed file: %s", zTarget);
871
+ }else{
872
+ Stmt q;
873
+ db_prepare(&q,
874
+ "SELECT content(ux), %w FROM ("
875
+ " SELECT A.uuid AS ux, B.uuid AS ckin, min(event.mtime) AS mx"
876
+ " FROM mlink, blob A, blob B, event"
877
+ " WHERE mlink.mid=event.objid"
878
+ " AND mlink.fid=A.rid"
879
+ " AND mlink.mid=B.rid"
880
+ " AND mlink.fnid=%d"
881
+ " GROUP BY A.uuid"
882
+ ") ORDER BY mx DESC;",
883
+ ckinHash ? "ckin" : "ux",
884
+ fnid
885
+ );
886
+ while( db_step(&q)==SQLITE_ROW ){
887
+ const char *zHash = db_column_text(&q,1);
888
+ const char *zContent = db_column_text(&q,0);
889
+ if( bVerbose ) fossil_print("%S:\n", zHash);
890
+ nSearch++;
891
+ nMatch += grep_buffer(pRe, zHash, zContent, flags);
892
+ if( bInvert && cntFlag==0 ){
893
+ if( nMatch==0 ){
894
+ fossil_print("%S\n", zHash);
895
+ if( bOnce ) nMatch = 1;
896
+ }else{
897
+ nMatch = 0;
898
+ }
899
+ }
900
+ if( nMatch ){
901
+ if( (flags & GREP_QUIET)!=0 ) break;
902
+ if( bOnce ) break;
903
+ }
904
+ }
905
+ db_finalize(&q);
906
+ }
907
+ }
908
+ }
909
+ if( cntFlag ){
910
+ if( bInvert ){
911
+ fossil_print("%d\n", nSearch-nMatch);
912
+ }else{
913
+ fossil_print("%d\n", nMatch);
845914
}
846915
}
847916
}
848917
--- src/regexp.c
+++ src/regexp.c
@@ -719,10 +719,11 @@
719
720 /*
721 ** Flags for grep_buffer()
722 */
723 #define GREP_EXISTS 0x001 /* If any match, print only the name and stop */
 
724
725 /*
726 ** Run a "grep" over a text file
727 */
728 static int grep_buffer(
@@ -737,14 +738,16 @@
737 n = j - i;
738 ln++;
739 if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){
740 cnt++;
741 if( flags & GREP_EXISTS ){
742 fossil_print("%S\n", zName);
743 break;
744 }
745 fossil_print("%S:%d:%.*s\n", zName, ln, n, z+i);
 
 
746 }
747 }
748 return cnt;
749 }
750
@@ -787,61 +790,127 @@
787 }
788
789 /*
790 ** COMMAND: grep
791 **
792 ** Usage: %fossil grep [OPTIONS] PATTERN FILENAME
793 **
794 ** Attempt to match the given POSIX extended regular expression PATTERN
795 ** over all historic versions of FILENAME. For details of the supported
796 ** RE dialect, see https://fossil-scm.org/fossil/doc/trunk/www/grep.md
797 **
798 ** Options:
799 **
800 ** -i|--ignore-case Ignore case
801 ** -l|--files-with-matches List only checkin ID for versions that match
802 ** -v|--verbose Show each file as it is analyzed
 
 
 
 
 
 
 
 
 
803 */
804 void re_grep_cmd(void){
805 u32 flags = 0;
806 int bVerbose = 0;
807 ReCompiled *pRe;
808 const char *zErr;
809 int ignoreCase = 0;
810 Blob fullName;
 
 
 
 
 
 
 
 
811
812 if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1;
813 if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS;
814 if( find_option("verbose","v",0)!=0 ) bVerbose = 1;
 
 
 
 
 
 
 
 
 
 
 
 
815 db_find_and_open_repository(0, 0);
816 verify_all_options();
817 if( g.argc<4 ){
818 usage("REGEXP FILENAME");
819 }
820 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
821 if( zErr ) fossil_fatal("%s", zErr);
822
823 if( file_tree_name(g.argv[3], &fullName, 0, 0) ){
824 int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
825 blob_str(&fullName));
826 if( fnid ){
827 Stmt q;
828 add_content_sql_commands(g.db);
829 db_prepare(&q,
830 "SELECT content(ux), ux FROM ("
831 " SELECT blob.uuid AS ux, min(event.mtime) AS mx"
832 " FROM mlink, blob, event"
833 " WHERE mlink.mid=event.objid"
834 " AND mlink.fid=blob.rid"
835 " AND mlink.fnid=%d"
836 " GROUP BY blob.uuid"
837 ") ORDER BY mx DESC;",
838 fnid
839 );
840 while( db_step(&q)==SQLITE_ROW ){
841 if( bVerbose ) fossil_print("%S:\n", db_column_text(&q,1));
842 grep_buffer(pRe, db_column_text(&q,1), db_column_text(&q,0), flags);
843 }
844 db_finalize(&q);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
845 }
846 }
847 }
848
--- src/regexp.c
+++ src/regexp.c
@@ -719,10 +719,11 @@
719
720 /*
721 ** Flags for grep_buffer()
722 */
723 #define GREP_EXISTS 0x001 /* If any match, print only the name and stop */
724 #define GREP_QUIET 0x002 /* Return code only */
725
726 /*
727 ** Run a "grep" over a text file
728 */
729 static int grep_buffer(
@@ -737,14 +738,16 @@
738 n = j - i;
739 ln++;
740 if( re_match(pRe, (const unsigned char*)(z+i), j-i) ){
741 cnt++;
742 if( flags & GREP_EXISTS ){
743 if( (flags & GREP_QUIET)==0 ) fossil_print("%S\n", zName);
744 break;
745 }
746 if( (flags & GREP_QUIET)==0 ){
747 fossil_print("%S:%d:%.*s\n", zName, ln, n, z+i);
748 }
749 }
750 }
751 return cnt;
752 }
753
@@ -787,61 +790,127 @@
790 }
791
792 /*
793 ** COMMAND: grep
794 **
795 ** Usage: %fossil grep [OPTIONS] PATTERN FILENAME ...
796 **
797 ** Attempt to match the given POSIX extended regular expression PATTERN
798 ** over all historic versions of FILENAME. For details of the supported
799 ** RE dialect, see https://fossil-scm.org/fossil/doc/trunk/www/grep.md
800 **
801 ** Options:
802 **
803 ** -c|--count Suppress normal output; instead print a count
804 ** of the number of matching files
805 ** -H|--checkin-hash Show the check-in hash rather than
806 ** file artifact hash for each match
807 ** -i|--ignore-case Ignore case
808 ** -l|--files-with-matches List only hash for each match
809 ** --once Stop searching after the first match
810 ** -s|--no-messages Suppress error messages about nonexistant
811 ** or unreadable files
812 ** -v|--invert-match Invert the sense of matching. Show only
813 ** files that have no matches. Implies -l
814 ** --verbose Show each file as it is analyzed
815 */
816 void re_grep_cmd(void){
817 u32 flags = 0;
818 int bVerbose = 0;
819 ReCompiled *pRe;
820 const char *zErr;
821 int ignoreCase = 0;
822 Blob fullName;
823 int ckinHash = 0;
824 int ii;
825 int nMatch = 0;
826 int bNoMsg;
827 int cntFlag;
828 int bOnce;
829 int bInvert;
830 int nSearch = 0;
831
832 if( find_option("ignore-case","i",0)!=0 ) ignoreCase = 1;
833 if( find_option("files-with-matches","l",0)!=0 ) flags |= GREP_EXISTS;
834 if( find_option("verbose",0,0)!=0 ) bVerbose = 1;
835 ckinHash = find_option("checkin-hash","H",0)!=0;
836 if( find_option("quiet","q",0) ) flags |= GREP_QUIET|GREP_EXISTS;
837 bNoMsg = find_option("no-messages","s",0)!=0;
838 bOnce = find_option("once",0,0)!=0;
839 bInvert = find_option("invert-match","v",0)!=0;
840 if( bInvert ){
841 flags |= GREP_QUIET|GREP_EXISTS;
842 }
843 cntFlag = find_option("count","c",0)!=0;
844 if( cntFlag ){
845 flags |= GREP_QUIET|GREP_EXISTS;
846 }
847 db_find_and_open_repository(0, 0);
848 verify_all_options();
849 if( g.argc<4 ){
850 usage("REGEXP FILENAME ...");
851 }
852 zErr = re_compile(&pRe, g.argv[2], ignoreCase);
853 if( zErr ) fossil_fatal("%s", zErr);
854
855 add_content_sql_commands(g.db);
856 for(ii=3; ii<g.argc; ii++){
857 const char *zTarget = g.argv[ii];
858 if( nMatch ){
859 if( (flags & GREP_QUIET)!=0 ) break;
860 if( bOnce ) break;
861 }
862 if( file_tree_name(zTarget, &fullName, 0, 1) ){
863 int fnid = db_int(0, "SELECT fnid FROM filename WHERE name=%Q",
864 blob_str(&fullName));
865 if( !fnid ){
866 if( bNoMsg ) continue;
867 if( file_size(zTarget, ExtFILE)<0 ){
868 fossil_fatal("no such file: %s", zTarget);
869 }
870 fossil_fatal("not a managed file: %s", zTarget);
871 }else{
872 Stmt q;
873 db_prepare(&q,
874 "SELECT content(ux), %w FROM ("
875 " SELECT A.uuid AS ux, B.uuid AS ckin, min(event.mtime) AS mx"
876 " FROM mlink, blob A, blob B, event"
877 " WHERE mlink.mid=event.objid"
878 " AND mlink.fid=A.rid"
879 " AND mlink.mid=B.rid"
880 " AND mlink.fnid=%d"
881 " GROUP BY A.uuid"
882 ") ORDER BY mx DESC;",
883 ckinHash ? "ckin" : "ux",
884 fnid
885 );
886 while( db_step(&q)==SQLITE_ROW ){
887 const char *zHash = db_column_text(&q,1);
888 const char *zContent = db_column_text(&q,0);
889 if( bVerbose ) fossil_print("%S:\n", zHash);
890 nSearch++;
891 nMatch += grep_buffer(pRe, zHash, zContent, flags);
892 if( bInvert && cntFlag==0 ){
893 if( nMatch==0 ){
894 fossil_print("%S\n", zHash);
895 if( bOnce ) nMatch = 1;
896 }else{
897 nMatch = 0;
898 }
899 }
900 if( nMatch ){
901 if( (flags & GREP_QUIET)!=0 ) break;
902 if( bOnce ) break;
903 }
904 }
905 db_finalize(&q);
906 }
907 }
908 }
909 if( cntFlag ){
910 if( bInvert ){
911 fossil_print("%d\n", nSearch-nMatch);
912 }else{
913 fossil_print("%d\n", nMatch);
914 }
915 }
916 }
917

Keyboard Shortcuts

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