FindFileApplication.st
changeset 17751 79bd35bda2d9
parent 17709 c5dcf0baef9a
child 17855 9122cdf37cfd
--- a/FindFileApplication.st	Sun Nov 12 14:21:14 2017 +0100
+++ b/FindFileApplication.st	Sun Nov 12 17:55:17 2017 +0100
@@ -25,11 +25,11 @@
 		fileSizeUnitHolder modificationTimeOperatorHolder
 		modificationTimeHolder enableModificationTimeFilter
 		showUnreadableFilesAndDirectoriesHolder
-		modificationTimeOperatorIndexHolder'
+		modificationTimeOperatorIndexHolder searchForBinaryContentsHolder'
 	classVariableNames:'ContentsInfoCache ContentsInfoCacheAccessLock LastRememberInCache
 		LastSearchIgnoredCaseInContents LastSearchIgnoredCaseInFilename
 		SearchStringHistory LastSearchIgnoredCaseInExcludedFilename
-		LastShowUnreadableFilesAndDirectories'
+		LastShowUnreadableFilesAndDirectories LastSearchForBinaryContents'
 	poolDictionaries:''
 	category:'Interface-Tools-File'
 !
@@ -113,7 +113,7 @@
     ^ super flyByHelpSpec addPairsFrom:#(
 
 #contentsPattern
-'Search for files containing this. Can be matchPatterns, separated by ";"'
+'Search for files containing this. Can be matchPatterns, separated by ";".\If search for binary contents is checked, this should be a sequence of hex bytes (i.e. xx xx xx...)'
 
 #namePattern
 'Filename(s) to search for. Can be matchPatterns, separated by ";".\(eg. "*.c ; *.h" searches for C and header files. So does "*.[ch]")'
@@ -122,11 +122,14 @@
 'Filename(s) to skip. Can be matchPatterns, separated by ";"'
 
 #notContentsPattern
-'Search for files NOT containing this. Can be matchPatterns, separated by ";"'
+'Search for files NOT containing this. Can be matchPatterns, separated by ";".\If search for binary contents is checked, this should be a sequence of hex bytes (i.e. xx xx xx...)'
 
 #searchDirectory
 'Folder, where the search starts'
 
+#searchForBinaryContents
+'Included/Not included patterns are given as a sequence of hex bytes instead of character sequences (eg. xx xx xx...)'
+
 #searchRecursive
 'Recursively search in sub-folders'
 
@@ -143,7 +146,7 @@
 'Search for files which are newer or older'
 )
 
-    "Modified: / 11-10-2017 / 15:48:53 / cg"
+    "Modified: / 12-11-2017 / 17:53:35 / cg"
 ! !
 
 !FindFileApplication class methodsFor:'history'!
@@ -184,10 +187,12 @@
     ^ 
     #(FullSpec
        name: windowSpec
+       uuid: '79f42116-c7c7-11e7-82f7-c42c033b4871'
        window: 
       (WindowSpec
          label: 'File Search'
          name: 'File Search'
+         uuid: '79f4341c-c7c7-11e7-82f7-c42c033b4871'
          min: (Point 377 131)
          bounds: (Rectangle 0 0 759 420)
        )
@@ -197,6 +202,7 @@
           (MenuPanelSpec
              name: 'ToolBar1'
              layout: (LayoutFrame 0 0.0 0 0 0 1.0 32 0)
+             uuid: '79f4376e-c7c7-11e7-82f7-c42c033b4871'
              level: 0
              menu: searchMenu
              textDefault: true
@@ -204,6 +210,7 @@
           (ProgressIndicatorSpec
              name: 'ProgressIndicator1'
              layout: (LayoutFrame 125 0 11 0 231 0 21 0)
+             uuid: '79f43a48-c7c7-11e7-82f7-c42c033b4871'
              visibilityChannel: enableStop
              backgroundColor: (Color 0.0 67.0 67.0)
              showPercentage: false
@@ -212,6 +219,7 @@
           (ViewSpec
              name: 'Box1'
              layout: (LayoutFrame 0 0.0 32 0 0 1.0 231 0)
+             uuid: '79f43caa-c7c7-11e7-82f7-c42c033b4871'
              component: 
             (SpecCollection
                collection: (
@@ -220,6 +228,7 @@
                    name: 'DirectoryLabel'
                    layout: (LayoutFrame 2 0 7 0 180 0 24 0)
                    activeHelpKey: searchDirectory
+                   uuid: '79f43e3a-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -227,6 +236,7 @@
                    name: 'DirectoryEntryField'
                    layout: (LayoutFrame 180 0 4 0 -350 1 24 0)
                    activeHelpKey: searchDirectory
+                   uuid: '79f44056-c7c7-11e7-82f7-c42c033b4871'
                    model: searchDirectoryHolder
                    immediateAccept: true
                    acceptOnPointerLeave: false
@@ -236,6 +246,7 @@
                    name: 'FileNameLabel'
                    layout: (LayoutFrame 2 0 31 0 180 0 48 0)
                    activeHelpKey: namePattern
+                   uuid: '79f4433a-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -243,6 +254,7 @@
                    name: 'FileNameEntryField'
                    layout: (LayoutFrame 180 0 28 0 -350 1 48 0)
                    activeHelpKey: namePattern
+                   uuid: '79f444b6-c7c7-11e7-82f7-c42c033b4871'
                    tabable: true
                    model: namePatternHolder
                    immediateAccept: true
@@ -254,6 +266,7 @@
                    name: 'Label1'
                    layout: (LayoutFrame 2 0 55 0 180 0 72 0)
                    activeHelpKey: excludedNamePattern
+                   uuid: '79f44704-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -261,6 +274,7 @@
                    name: 'EntryField1'
                    layout: (LayoutFrame 180 0 52 0 -350 1 72 0)
                    activeHelpKey: excludedNamePattern
+                   uuid: '79f44880-c7c7-11e7-82f7-c42c033b4871'
                    tabable: true
                    model: excludedNamePatternHolder
                    immediateAccept: true
@@ -272,6 +286,7 @@
                    name: 'ContentsLabel'
                    layout: (LayoutFrame 2 0 79 0 180 0 96 0)
                    activeHelpKey: contentsPattern
+                   uuid: '79f44a88-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -279,6 +294,7 @@
                    name: 'ComboBox1'
                    layout: (LayoutFrame 180 0 76 0 -350 1 96 0)
                    activeHelpKey: contentsPattern
+                   uuid: '79f44bfa-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: notSearchForSameContents
                    tabable: true
                    model: contentsPatternHolder
@@ -292,6 +308,7 @@
                    name: 'NotContentsLabel'
                    layout: (LayoutFrame 2 0 103 0 180 0 120 0)
                    activeHelpKey: notContentsPattern
+                   uuid: '79f44ea2-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -299,6 +316,7 @@
                    name: 'NotContentsEntryField'
                    layout: (LayoutFrame 180 0 100 0 -350 1 120 0)
                    activeHelpKey: notContentsPattern
+                   uuid: '79f4500a-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: notSearchForSameContents
                    tabable: true
                    model: notContentsPatternHolder
@@ -310,6 +328,7 @@
                    name: 'SameContentsAsLabel'
                    layout: (LayoutFrame 2 0 127 0 180 0 144 0)
                    activeHelpKey: sameContents
+                   uuid: '79f45208-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -317,6 +336,7 @@
                    name: 'SameContentsAsEntryField'
                    layout: (LayoutFrame 180 0 124 0 -367 1 144 0)
                    activeHelpKey: sameContents
+                   uuid: '79f45370-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: searchForSameContents
                    tabable: true
                    model: sameContentsAsHolder
@@ -327,6 +347,7 @@
                    name: 'EnableSameContentsCheckToggle'
                    layout: (LayoutOrigin -366 1 128 0)
                    activeHelpKey: sameContents
+                   uuid: '79f45582-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    model: searchForSameContents
                    isTriggerOnDown: true
@@ -338,6 +359,7 @@
                    name: 'FileSizeLabel'
                    layout: (LayoutFrame 2 0 151 0 180 0 168 0)
                    activeHelpKey: fileSize
+                   uuid: '79f458c0-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -346,6 +368,7 @@
                    name: 'FileSizeOperatorPopUpList'
                    layout: (LayoutFrame 180 0 148 0 260 0 168 0)
                    activeHelpKey: fileSize
+                   uuid: '79f45a64-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    tabable: true
                    model: fileSizeOperatorHolder
@@ -361,6 +384,7 @@
                    name: 'FileSizeEntryField'
                    layout: (LayoutFrame 260 0 148 0 -367 1 168 0)
                    activeHelpKey: fileSize
+                   uuid: '79f45cda-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: enableFileSizeFilterAndNotSearchForSameContents
                    tabable: true
                    model: fileSizeHolder
@@ -374,6 +398,7 @@
                    name: 'EnableSizeCheckToggle'
                    layout: (LayoutOrigin -366 1 151 0)
                    activeHelpKey: fileSize
+                   uuid: '79f45f0a-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    model: enableFileSizeFilter
                    enableChannel: notSearchForSameContents
@@ -386,6 +411,7 @@
                    name: 'Label2'
                    layout: (LayoutFrame 2 0 175 0 180 0 192 0)
                    activeHelpKey: modificationTime
+                   uuid: '79f46130-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    adjust: right
                  )
@@ -394,6 +420,7 @@
                    name: 'PopUpList1'
                    layout: (LayoutFrame 180 0 172 0 260 0 192 0)
                    activeHelpKey: modificationTime
+                   uuid: '79f462b6-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    tabable: true
                    model: modificationTimeOperatorIndexHolder
@@ -405,6 +432,7 @@
                    name: 'ModifiedEntryField'
                    layout: (LayoutFrame 260 0 172 0 -367 1 192 0)
                    activeHelpKey: modificationTime
+                   uuid: '79f46478-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: enableModificationTimeFilter
                    tabable: true
                    model: modificationTimeHolder
@@ -418,6 +446,7 @@
                    name: 'CheckToggle1'
                    layout: (LayoutOrigin -366 1 175 0)
                    activeHelpKey: modificationTime
+                   uuid: '79f4668a-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    model: enableModificationTimeFilter
                    isTriggerOnDown: true
@@ -429,6 +458,7 @@
                    name: 'UseLocateCheckBox'
                    layout: (LayoutFrame -350 1 5 0 -167 1 28 0)
                    activeHelpKey: useLocate
+                   uuid: '79f4689c-c7c7-11e7-82f7-c42c033b4871'
                    visibilityChannel: canUseLocate
                    tabable: true
                    model: useLocate
@@ -439,6 +469,7 @@
                    name: 'RecursiveSearchCheckBox'
                    layout: (LayoutFrame -200 1 5 0 -4 1 28 0)
                    activeHelpKey: recursiveSearch
+                   uuid: '79f46aa4-c7c7-11e7-82f7-c42c033b4871'
                    tabable: true
                    model: searchRecursively
                    translateLabel: true
@@ -447,6 +478,7 @@
                    label: 'Directories'
                    name: 'SearchDirectoriesCheckBox'
                    layout: (LayoutFrame -350 1 29 0 -167 1 52 0)
+                   uuid: '79f46c16-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: notSearchForSameContents
                    tabable: true
                    model: searchDirectories
@@ -457,6 +489,7 @@
                    name: 'IgnoreCaseInNameCheckBox'
                    layout: (LayoutFrame -200 1 29 0 -4 1 52 0)
                    activeHelpKey: ignoreCase
+                   uuid: '79f46d7e-c7c7-11e7-82f7-c42c033b4871'
                    tabable: true
                    model: ignoreCaseInName
                    translateLabel: true
@@ -466,6 +499,7 @@
                    name: 'CheckBox1'
                    layout: (LayoutFrame -200 1 53 0 -4 1 76 0)
                    activeHelpKey: ignoreCase
+                   uuid: '79f46edc-c7c7-11e7-82f7-c42c033b4871'
                    tabable: true
                    model: ignoreCaseInExcludedName
                    translateLabel: true
@@ -474,6 +508,7 @@
                    label: 'Use ''grep'' Cmd'
                    name: 'UseGrepCheckBox'
                    layout: (LayoutFrame -350 1 77 0 -167 1 100 0)
+                   uuid: '79f4703a-c7c7-11e7-82f7-c42c033b4871'
                    visibilityChannel: canUseGrep
                    enableChannel: notSearchForSameContents
                    tabable: true
@@ -485,6 +520,7 @@
                    name: 'IgnoreCaseInContentsCheckBox'
                    layout: (LayoutFrame -200 1 77 0 -4 1 100 0)
                    activeHelpKey: ignoreCase
+                   uuid: '79f47198-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: notSearchForSameContents
                    tabable: true
                    model: ignoreCaseInContents
@@ -495,6 +531,7 @@
                    name: 'IgnoreCaseInNotContentsCheckBox'
                    layout: (LayoutFrame -200 1 101 0 -4 1 124 0)
                    activeHelpKey: ignoreCase
+                   uuid: '79f472f6-c7c7-11e7-82f7-c42c033b4871'
                    enableChannel: notSearchForSameContents
                    tabable: true
                    model: ignoreCaseInNotContents
@@ -504,26 +541,42 @@
                    label: 'Cache Info'
                    name: 'RememberInCacheCheckBox'
                    layout: (LayoutFrame -350 1 125 0 -167 1 148 0)
+                   uuid: '79f47454-c7c7-11e7-82f7-c42c033b4871'
                    visibilityChannel: canUseGrep
                    enableChannel: searchForSameContents
                    tabable: true
                    model: rememberInCache
                    translateLabel: true
+                   activeHelpKey: rememberInCache
                  )
                 (ActionButtonSpec
                    label: 'Clear Cache'
                    name: 'ClearCacheButton'
                    layout: (LayoutFrame -169 1 125 0 -21 1 147 0)
+                   uuid: '79f475b2-c7c7-11e7-82f7-c42c033b4871'
                    translateLabel: true
                    model: clearCache
+                   activeHelpKey: clearCache
                  )
                 (CheckBoxSpec
                    label: 'Show Unreadable Files and Folders'
                    name: 'CheckBox2'
                    layout: (LayoutFrame -350 1 149 0 0 1 172 0)
+                   uuid: '79f477b0-c7c7-11e7-82f7-c42c033b4871'
                    tabable: true
                    model: showUnreadableFilesAndDirectoriesHolder
                    translateLabel: true
+                   activeHelpKey: showUnreadableFilesAndDirectories
+                 )
+                (CheckBoxSpec
+                   label: 'Search for Binary Contents'
+                   name: 'CheckBox3'
+                   layout: (LayoutFrame -350 1 173 0 0 1 196 0)
+                   uuid: '79f47918-c7c7-11e7-82f7-c42c033b4871'
+                   tabable: true
+                   model: searchForBinaryContentsHolder
+                   translateLabel: true
+                   activeHelpKey: searchForBinaryContents
                  )
                 )
               
@@ -532,6 +585,7 @@
           (SequenceViewSpec
              name: 'List1'
              layout: (LayoutFrame 0 0.0 238 0 0 1.0 0 1)
+             uuid: '79f47abc-c7c7-11e7-82f7-c42c033b4871'
              model: selectionHolder
              menu: menu
              hasHorizontalScrollBar: true
@@ -554,6 +608,7 @@
        )
      )
 
+    "Modified: / 12-11-2017 / 17:37:30 / cg"
 ! !
 
 !FindFileApplication class methodsFor:'menu specs'!
@@ -1279,6 +1334,15 @@
     ^ searchDirectoryHolder.
 !
 
+searchForBinaryContentsHolder
+    searchForBinaryContentsHolder isNil ifTrue:[
+        searchForBinaryContentsHolder := (LastSearchForBinaryContents ? false) asValue.
+    ].
+    ^ searchForBinaryContentsHolder.
+
+    "Created: / 12-11-2017 / 17:35:56 / cg"
+!
+
 searchForSameContents
     searchForSameContents isNil ifTrue:[
         searchForSameContents := false asValue.
@@ -1468,8 +1532,11 @@
      doesFileMatch contentsString notContentsString check checkNot 
      grepCommand nameMatch nameExcludedMatch realNameMatch 
      fileSizesToSearchFor filesToSearchFor fileMD5sToSearchFor 
-     setOfFilesToSearchFor remember cache fn dirSearchedRelative easyCheck|
+     setOfFilesToSearchFor remember cache fn dirSearchedRelative easyCheck
+     searchForBinaryContents|
 
+    searchForBinaryContents := self searchForBinaryContentsHolder value.
+    
     (aDirectoryOrCollectionOfDirectories isFilename
     or:[aDirectoryOrCollectionOfDirectories isString]) ifTrue:[
         theSingleDirectory := aDirectoryOrCollectionOfDirectories asFilename
@@ -1481,12 +1548,24 @@
     
     "/ dir := aDirectory asFilename.
     contentsString := contentsStringArg.
-    (contentsString notEmptyOrNil and:[ ignCaseInContents ]) ifTrue:[ 
-        contentsString := contentsString asLowercase
-    ].
     notContentsString := notContentsStringArg.
-    (notContentsString notEmptyOrNil and:[ ignCaseInNotContents ]) ifTrue:[ 
-        notContentsString := notContentsString asLowercase    
+
+    searchForBinaryContents ifTrue:[
+        contentsString notEmptyOrNil ifTrue:[
+            contentsString := (contentsStringArg asCollectionOfWords 
+                                    collect:[:s | Integer readFrom:s radix:16] as:ByteArray) asString.
+        ].
+        notContentsString notEmptyOrNil ifTrue:[
+            notContentsString := (notContentsStringArg asCollectionOfWords 
+                                    collect:[:s | Integer readFrom:s radix:16] as:ByteArray) asString.
+        ].
+    ] ifFalse:[    
+        (contentsString notEmptyOrNil and:[ ignCaseInContents ]) ifTrue:[ 
+            contentsString := contentsString asLowercase
+        ].
+        (notContentsString notEmptyOrNil and:[ ignCaseInNotContents ]) ifTrue:[ 
+            notContentsString := notContentsString asLowercase    
+        ].
     ].
     filenameToCompareContentsOrNil notEmptyOrNil ifTrue:[
         fileSizesToSearchFor := OrderedCollection new.
@@ -1617,35 +1696,37 @@
         (contentsString isEmptyOrNil and:[notContentsString isEmptyOrNil]) ifTrue:[
             doesFileMatch := [:f | true].
         ] ifFalse:[
-            (self canUseGrep and:[self useGrep value]) ifTrue:[
-                (ignCaseInContents not and:[ignCaseInNotContents not]) ifTrue:[
-                    contentsString notEmptyOrNil ifTrue:[
-                        notContentsString notEmptyOrNil ifTrue:[
-                            grepCommand := '(grep "',contentsString,'" %1) && (grep -v "',notContentsString,'" %1)'.
+            searchForBinaryContents ifFalse:[
+                (self canUseGrep and:[self useGrep value]) ifTrue:[
+                    (ignCaseInContents not and:[ignCaseInNotContents not]) ifTrue:[
+                        contentsString notEmptyOrNil ifTrue:[
+                            notContentsString notEmptyOrNil ifTrue:[
+                                grepCommand := '(grep "',contentsString,'" %1) && (grep -v "',notContentsString,'" %1)'.
+                            ] ifFalse:[
+                                grepCommand := 'grep "' , contentsString , '" %1'.
+                            ].
                         ] ifFalse:[
-                            grepCommand := 'grep "' , contentsString , '" %1'.
+                            grepCommand := 'grep -v "' , notContentsString , '" %1'.
                         ].
-                    ] ifFalse:[
-                        grepCommand := 'grep -v "' , notContentsString , '" %1'.
-                    ].
-                    doesFileMatch := [:f | |cmd ret|
-                                            cmd := grepCommand bindWith:f pathName.
-                                            ret := OperatingSystem executeCommand:cmd.
-                                            ret
-                                     ].
-                ]
+                        doesFileMatch := [:f | |cmd ret|
+                                                cmd := grepCommand bindWith:f pathName.
+                                                ret := OperatingSystem executeCommand:cmd.
+                                                ret
+                                         ].
+                    ]
+                ].
             ].
-
+            
             doesFileMatch isNil ifTrue:[
                 contentsString notEmptyOrNil ifTrue:[
-                    ignCaseInContents ifTrue:[
+                    (searchForBinaryContents not and:[ignCaseInContents]) ifTrue:[
                         check := easyCheck := [:l | l includesString:contentsString caseSensitive:false]
                     ] ifFalse:[
                         check := easyCheck := [:l | l includesString:contentsString]
                     ].
                 ].
                 notContentsString notEmptyOrNil ifTrue:[
-                    ignCaseInNotContents ifTrue:[
+                    (searchForBinaryContents not and:[ignCaseInNotContents]) ifTrue:[
                         checkNot := [:l | (l includesString:notContentsString caseSensitive:false)]
                     ] ifFalse:[
                         checkNot := [:l | (l includesString:notContentsString)]
@@ -1890,8 +1971,9 @@
             contentsMatch:doesFileMatch 
             in:eachDir.
     ]
-    
+
     "Created: / 03-08-2011 / 18:16:02 / cg"
+    "Modified: / 12-11-2017 / 17:50:01 / cg"
 !
 
 doFindFileNamed:namePatterns directories:searchDirectories nameMatch:nameMatch contentsMatch:doesFileMatch in:aDirectory