Filename.st
changeset 24235 210f242ffbd9
parent 24101 0bbf8d6ed48a
child 24379 852be153b0ec
--- a/Filename.st	Wed May 29 11:33:14 2019 +0200
+++ b/Filename.st	Wed May 29 11:33:34 2019 +0200
@@ -2883,6 +2883,138 @@
     "Modified: / 06-02-2019 / 11:45:22 / Stefan Vogel"
 !
 
+recursiveFilesDo:fileBlock directoriesDo:dirBlock filterForVisitingDirectories:filterOrNil
+    "evaluate aBlock for all files and directories found under the receiver.
+     The blocks are invoked with a relative pathname as string-argument.
+     The walk is depth-first (but files first, then directories).
+     This excludes any entries for '.' or '..'.
+     A proceedable exception is raised for non-accessible directories.
+     If filterOrNil is nonNil, it is passed every directory about to be walked into;
+     if it returns false, that directory is not entered.
+     Warning: this may take a long time to execute 
+     (especially with deep and/or remote fileSystems).
+     Names are enumerated in the order they appear in the folder,
+     (which is not required to be sorted).
+    "
+
+    self 
+        recursiveFilesDo:fileBlock directoriesDo:dirBlock 
+        filterForVisitingDirectories:filterOrNil sortedBy:nil
+    
+    "
+     '.' asFilename 
+        recursiveFilesDo:[:f | Transcript show:'file: '; showCR:f]
+        directoriesDo:[:f | Transcript show:'dir: '; showCR:f]
+        filterForVisitingDirectories:nil
+    "
+
+    "Created: / 29-05-2019 / 09:58:12 / Claus Gittinger"
+    "Modified (comment): / 29-05-2019 / 11:24:57 / Claus Gittinger"
+!
+
+recursiveFilesDo:fileBlock directoriesDo:dirBlock filterForVisitingDirectories:filterOrNil sortedBy:sortedByBlock
+    "evaluate aBlock for all files and directories found under the receiver.
+     The blocks are invoked with a relative pathname as string-argument.
+     The walk is breadth-first (files first, then directories).
+     This excludes any entries for '.' or '..'.
+     A proceedable exception is raised for non-accessible directories.
+     If filterOrNil is nonNil, it is passed every directory about to be walked into;
+     if it returns false, that directory is not entered.
+     Warning: this may take a long time to execute 
+     (especially with deep and/or remote fileSystems).
+     By default, names are enumerated in the order they appear in the folder,
+     (which is not required to be sorted).
+     If sortedByBlock is non nil, it is applied with the file names to sort them
+     (usually a block is provided to sort by name, aka [:a :b | a name < b name]
+     or by caseless name, like [:a :b | a name caselessBefore: b name]     
+    "
+
+    |getFilesAndDirs toDo fileNames dirNames job thisDir|
+
+    toDo := OrderedCollection new.
+
+    getFilesAndDirs := 
+        [:aRoot |
+            |contents fileNames dirNames wrap|
+            
+            "/ first collect files and dirs
+            fileNames := OrderedCollection new.
+            dirNames := OrderedCollection new.
+
+            contents := aRoot directoryContents.
+            sortedByBlock notNil ifTrue:[
+                contents sort:[:a :b | sortedByBlock value:(aRoot construct:a) value:(aRoot construct:b)].
+            ].    
+            contents do:[:f | |t|
+                t := aRoot construct:f.
+                t isDirectory ifTrue:[
+                    (filterOrNil isNil or:[filterOrNil value:t]) ifTrue:[
+                        dirBlock notNil ifTrue:[
+                            t isSymbolicLink ifFalse:[
+                                dirNames add:f
+                            ]
+                        ]
+                    ]
+                ] ifFalse:[
+                    fileBlock notNil ifTrue:[
+                        fileNames add:f
+                    ]
+                ]
+            ].
+            toDo add:{ aRoot . fileNames . dirNames }
+        ].    
+
+    getFilesAndDirs value:self.
+
+    [toDo notEmpty] whileTrue:[
+        job := toDo removeFirst.
+        "/ process files..
+        thisDir := job at:1.
+        fileNames := job at:2.
+        dirNames := job at:3.
+        
+        fileNames do:[:fN | fileBlock value:(thisDir constructString:fN)].
+        dirNames do:[:dN |
+            |subDir|
+
+            subDir := (thisDir construct:dN).
+            (filterOrNil isNil or:[filterOrNil value:subDir]) ifTrue:[
+                dirBlock value:(subDir name).
+                getFilesAndDirs value:subDir.
+            ].
+        ].
+    ].
+    
+    "
+     '.' asFilename 
+        recursiveFilesDo:[:f | Transcript show:'file: '; showCR:f]
+        directoriesDo:[:f | Transcript show:'dir: '; showCR:f]
+        filterForVisitingDirectories:nil
+        sortedBy:[:a :b | a name caselessBefore: b name]
+
+     '.' asFilename 
+        recursiveFilesDo:[:f | Transcript showCR:f]
+        directoriesDo:[:f | Transcript showCR:f]
+        filterForVisitingDirectories:nil
+        sortedBy:[:a :b | a name caselessBefore: b name]
+
+     '../../..' asFilename 
+        recursiveFilesDo:[:f | Transcript show:'file: '; showCR:f]
+        directoriesDo:[:f | Transcript show:'dir: '; showCR:f]
+        filterForVisitingDirectories:nil
+        sortedBy:[:a :b | a name caselessBefore: b name]
+
+     '../../..' asFilename 
+        recursiveFilesDo:[:f | Transcript show:'file: '; showCR:f]
+        directoriesDo:[:f | Transcript show:'dir: '; showCR:f]
+        filterForVisitingDirectories:nil
+        sortedBy:nil
+    "
+
+    "Created: / 29-05-2019 / 10:15:40 / Claus Gittinger"
+    "Modified (comment): / 29-05-2019 / 11:28:57 / Claus Gittinger"
+!
+
 withAllDirectoriesDo:aBlock
     "evaluate aBlock for myself and all (recursive) directories contained in the directory represented by the receiver.
      The block is invoked with a filename-arguments.