--- a/Filename.st Fri May 13 13:56:18 2016 +0200
+++ b/Filename.st Fri May 13 13:59:39 2016 +0200
@@ -1291,10 +1291,13 @@
isCaseSensitive
"return true, if filenames are case sensitive.
- We ask the OS about this, to be independent here."
+ We ask the OS about this, to be independent here.
+ This is not really correct, as the sensitivity may depend on
+ the paricular mounted file system (NFS, for example).
+ So we need a query on the instance side"
self isAbstract ifTrue:[
- ^ ConcreteClass isCaseSensitive
+ ^ ConcreteClass isCaseSensitive
].
^ OperatingSystem caseSensitiveFilenames
@@ -1536,7 +1539,7 @@
|sep f vol rest components|
self isAbstract ifTrue:[
- ^ ConcreteClass components:aString
+ ^ ConcreteClass components:aString
].
"/ the following works on Unix & MSDOS (but not on openVMS)
@@ -1547,59 +1550,78 @@
f := aString asFilename.
vol := f volume.
vol size ~~ 0 ifTrue:[
- rest := f localPathName.
+ rest := f localPathName.
] ifFalse:[
- rest := aString
+ rest := aString
].
components := rest asCollectionOfSubstringsSeparatedBy:sep.
(rest startsWith:sep) ifTrue:[
- "first was a separator - root directory - restore"
- (rest size > 1 and:[rest second = sep and:[vol isEmptyOrNil]]) ifTrue:[
- "keep \\ for windows network paths"
- components at:1 put:(String with:sep with:sep).
- ] ifFalse:[
- components at:1 put:sep asString.
- ].
+ "first was a separator - root directory - restore"
+ (rest size > 1 and:[rest second = sep and:[vol isEmptyOrNil]]) ifTrue:[
+ "keep \\ for windows network paths"
+ components at:1 put:(String with:sep with:sep).
+ ] ifFalse:[
+ components at:1 put:sep asString.
+ ].
].
components := components select:[:each| each notEmpty].
"/ prepend volume to first component (the root directory)
vol size ~~ 0 ifTrue:[
- components at:1 put:(vol , (components at:1)).
+ components at:1 put:(vol , (components at:1)).
].
^ components
"
- Filename components:'/foo/bar/baz'
- Filename components:'/'
- Filename components:'//'
- Filename components:'foo/bar/baz'
- Filename components:'foo/bar'
- Filename components:'foo'
- Filename components:'/foo'
- Filename components:'//foo'
- Filename components:''
-
- Filename components:'\'
- Filename components:'\foo'
- Filename components:'\foo\'
- Filename components:'\foo\bar'
- Filename components:'\foo\bar\'
- Filename components:'c:'
- Filename components:'c:\'
- Filename components:'c:\foo'
- Filename components:'c:\foo\'
- Filename components:'c:\foo\bar'
- Filename components:'c:\foo\bar\'
- Filename components:'\\idefix'
- Filename components:'\\idefix\home'
- Filename components:'\\idefix\home\bar'
+ Unix:
+ UnixFilename components:'/foo/bar/baz'
+ UnixFilename components:'/'
+ UnixFilename components:'//'
+ UnixFilename components:'foo/bar/baz'
+ UnixFilename components:'foo/bar'
+ UnixFilename components:'foo'
+ UnixFilename components:'/foo'
+ UnixFilename components:'//foo'
+ UnixFilename components:''
+
+ Windows:
+ PCFilename components:'\'
+ PCFilename components:'\foo'
+ PCFilename components:'\foo\'
+ PCFilename components:'\foo\bar'
+ PCFilename components:'\foo\bar\'
+ PCFilename components:'c:'
+ PCFilename components:'c:\'
+ PCFilename components:'c:\foo'
+ PCFilename components:'c:\foo\'
+ PCFilename components:'c:\foo\bar'
+ PCFilename components:'c:\foo\bar\'
+ PCFilename components:'\\idefix'
+ PCFilename components:'\\idefix\home'
+ PCFilename components:'\\idefix\home\bar'
"
"Modified: / 24.9.1998 / 19:10:52 / cg"
!
+filesMatchingGLOB:pattern
+ "does a GLOB filename expansion.
+ Generates and returns a possibly empty list of files which match
+ the given glob pattern"
+
+ ^ OrderedCollection withCollectedContents:[:coll |
+ pattern asFilename filesMatchingGLOBDo:[:each | coll add:each]
+ ]
+
+ "
+ Filename filesMatchingGLOB:'./A*'
+ Filename filesMatchingGLOB:'/etc/A*'
+ Filename filesMatchingGLOB:'/*/A*'
+ '.' asFilename filesMatchingGLOB:'A*'
+ "
+!
+
nameFromComponents:aCollectionOfDirectoryNames
"return a filenameString from components given in aCollectionOfDirectoryNames.
If the first component is the name of the root directory (i.e. '/'),
@@ -2176,16 +2198,8 @@
directories
"return a collection of directories contained in the directory represented by the receiver."
- |collection|
-
- collection := OrderedCollection new.
- self directoryContentsAsFilenamesDo:[:eachFileOrDirectory |
- eachFileOrDirectory isDirectory ifTrue:[
- collection add:eachFileOrDirectory.
- ].
- ].
-
- ^ collection
+ ^ OrderedCollection withCollectedContents:[:coll |
+ self directoriesDo:[:eachDirectory | coll add:eachDirectory]]
"
'.' asFilename directories.
@@ -2253,25 +2267,25 @@
s := DirectoryStream directoryNamed:self osNameForDirectoryContents.
"check for nil, in order to allow to proceed from an OpenError"
s notNil ifTrue:[
- [
- [s atEnd] whileFalse:[
- |fn|
-
- fn := s nextLine.
- (fn ~= '.' and:[fn ~= '..']) ifTrue:[
- aBlock value:fn
- ].
- ].
- ] ensure:[
- s close.
- ].
+ [
+ [s atEnd] whileFalse:[
+ |fn|
+
+ fn := s nextLine.
+ (fn notNil and:[fn ~= '.' and:[fn ~= '..']]) ifTrue:[
+ aBlock value:fn
+ ].
+ ].
+ ] ensure:[
+ s close.
+ ].
].
"
'.' asFilename directoryContentsDo:[:fn | Transcript showCR:fn].
'doeSnotExIST' asFilename directoryContentsDo:[:fn | Transcript showCR:fn].
[
- 'doeSnotExIST' asFilename directoryContentsDo:[:fn | Transcript showCR:fn].
+ 'doeSnotExIST' asFilename directoryContentsDo:[:fn | Transcript showCR:fn].
] on:OpenError do:[:ex| ex proceed]
"
@@ -2283,11 +2297,8 @@
"return a collection of regular files
contained in the directory represented by the receiver."
- |collection|
-
- collection := OrderedCollection new.
- self filesDo:[:eachFileName | collection add:eachFileName].
- ^ collection.
+ ^ OrderedCollection withCollectedContents:[:coll |
+ self filesDo:[:eachFileName | coll add:eachFileName]].
"
'.' asFilename files.
@@ -2314,19 +2325,140 @@
"Modified: / 29-05-2007 / 12:02:46 / cg"
!
+filesMatchingGLOB:pattern do:aBlock
+ "Interpreting pattern as a GLOB pattern,
+ evaluate aBlock for each file in me, which matches.
+ Returns the number of matches."
+
+ self assert:(pattern asFilename isAbsolute not).
+ ^ self filesMatchingGLOBComponents:(Filename components:pattern) do:aBlock
+
+ "
+ '..' asFilename filesMatchingGLOB:'A*' do:[:fn | Transcript showCR:fn].
+ '../..' asFilename filesMatchingGLOB:'lib*/*.st' do:[:fn | Transcript showCR:fn].
+ "
+!
+
+filesMatchingGLOBComponents:patternComponents do:aBlock
+ "patternComponents is a component-collection with possible GLOB patterns.
+ Evaluate aBlock for each file in me, which matches.
+ Returns the number of matches"
+
+ |dirPath subComponents count|
+
+ dirPath := patternComponents first.
+ subComponents := patternComponents copyFrom:2.
+
+ dirPath includesMatchCharacters ifFalse:[
+ | sub |
+
+ sub := (self / dirPath).
+ subComponents isEmpty ifTrue:[
+ "/ I am a leaf
+ sub exists ifTrue:[
+ aBlock value:sub.
+ ^ 1.
+ ].
+ ^ 0
+ ] ifFalse:[
+ ^ sub filesMatchingGLOBComponents:subComponents do:aBlock
+ ].
+ ] ifTrue:[
+ count := 0.
+ subComponents isEmpty ifTrue:[
+ "/ I am a leaf
+ self isDirectory ifTrue:[
+ self filesMatching:dirPath do:[:eachMatchingFile |
+ aBlock value:(self / eachMatchingFile).
+ count := count + 1.
+ ]
+ ]
+ ] ifFalse:[
+ self filesMatching:dirPath do:[:eachMatchingSubDir |
+ |sub|
+ sub := (self / eachMatchingSubDir).
+ sub isDirectory ifTrue:[
+ count := count + (sub filesMatchingGLOBComponents:subComponents do:aBlock)
+ ].
+ ].
+ ].
+ ^ count
+ ].
+
+ "
+ '/etc/A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../../lib*/*.st' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ "
+!
+
+filesMatchingGLOBDo:aBlock
+ "Interpreting myself as a GLOB pattern,
+ evaluate aBlock for each file which matches."
+
+ |parts dirPath subComponents count top|
+
+ parts := self components.
+ dirPath := parts first.
+ subComponents := parts copyFrom:2.
+
+ OpenError handle:[:ex |
+ ('%1 [info]: failed to open %2: %3'
+ bindWith:self class name
+ with:ex pathName
+ with:ex description) infoPrintCR.
+ self breakPoint:#cg.
+ ex proceed.
+ ] do:[
+ top := dirPath asFilename.
+
+ dirPath includesMatchCharacters ifFalse:[
+ top isAbsolute ifFalse:[
+ top := Filename currentDirectory construct:dirPath.
+ ].
+ ^ top filesMatchingGLOBComponents:subComponents do:aBlock
+ ].
+
+ top isAbsolute ifFalse:[
+ top := Filename currentDirectory.
+ ].
+ count := 0.
+ top filesMatching:dirPath do:[:sub |
+ count := count + ((self / sub) filesMatchingGLOBComponents:subComponents do:aBlock)
+ ].
+ ^ count
+ ].
+
+ "
+ '/etc/A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '/etc/a*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../../lib*/*.st' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../../lib*/[A-D]*.st' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../../*/[A-D]*.st' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../../*/*/[A-D]*.st' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../../*java*/*/[A-D]*.st' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../../*java*/*/Ary.st' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '/*/A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '*/A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '../*/A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ './*/A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ './*/*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ '././A*' asFilename filesMatchingGLOBDo:[:fn | Transcript showCR:fn].
+ "
+!
+
filesWithSuffix:suffix
"return a collection of regular files (i.e. not subdirectories)
with a given suffix which are contained in the directory
represented by the receiver."
- |collection|
-
- collection := OrderedCollection new.
- self filesWithSuffix:suffix do:[:eachFileName | collection add:eachFileName].
- ^ collection.
+ ^ OrderedCollection withCollectedContents:[:coll |
+ self filesWithSuffix:suffix do:[:eachFileName | coll add:eachFileName]].
"
'.' asFilename filesWithSuffix:'so'.
+ 'packages' asFilename filesWithSuffix:'so'.
"
!
@@ -4358,14 +4490,8 @@
The pattern may be a simple matchPattern, or a set of
multiple patterns separated by semicolons."
- |matchers caseSensitive|
-
- matchers := aPattern asCollectionOfSubstringsSeparatedBy:$;.
- caseSensitive := self species isCaseSensitive.
- ^ self directoryContents
- select:[:name |
- (matchers detect:[:p | p match:name caseSensitive:caseSensitive] ifNone:0) ~~ 0
- ]
+ ^ OrderedCollection withCollectedContents:[:coll |
+ self filesMatching:aPattern do:[:fn | coll add:fn]]
"
'/etc' asFilename filesMatching:'a*;c*'
@@ -4375,6 +4501,45 @@
"Modified: / 3.8.1998 / 21:22:15 / cg"
!
+filesMatching:aPattern caseSensitive:caseSensitive do:aBlock
+ "given the receiver, representing a directory;
+ evaluate aBlock for files which match a pattern.
+ The pattern may be a simple matchPattern, or a set of
+ multiple patterns separated by semicolons."
+
+ |matchers|
+
+ matchers := aPattern asCollectionOfSubstringsSeparatedBy:$;.
+ self directoryContentsDo:[:name |
+ (matchers contains:[:p | p match:name caseSensitive:caseSensitive]) ifTrue:[
+ aBlock value:name
+ ]
+ ].
+
+ "
+ '/etc' asFilename filesMatching:'a*;c*' do:[:f | Transcript showCR:f]
+ "
+
+ "Created: / 15.4.1997 / 15:40:02 / cg"
+ "Modified: / 3.8.1998 / 21:22:15 / cg"
+!
+
+filesMatching:aPattern do:aBlock
+ "given the receiver, representing a directory;
+ evaluate aBlock for files which match a pattern.
+ The pattern may be a simple matchPattern, or a set of
+ multiple patterns separated by semicolons."
+
+ self filesMatching:aPattern caseSensitive:self species isCaseSensitive do:aBlock
+
+ "
+ '/etc' asFilename filesMatching:'a*;c*' do:[:f | Transcript showCR:f]
+ "
+
+ "Created: / 15.4.1997 / 15:40:02 / cg"
+ "Modified: / 3.8.1998 / 21:22:15 / cg"
+!
+
filesMatchingWithoutDotDirs:aPattern
"given the receiver, representing a directory;
return a collection of files matching a pattern.
@@ -4382,17 +4547,57 @@
The pattern may be a simple matchPattern, or a set of
multiple patterns separated by semicolons."
- |matchers caseSensitive|
+ ^ OrderedCollection withCollectedContents:[:coll |
+ self filesMatchingWithoutDotDirs:aPattern do:[:fn | coll add:fn]]
+
+ "
+ Filename currentDirectory filesMatching:'.*'
+ Filename currentDirectory filesMatchingWithoutDotDirs:'*.*'
+ '/etc' asFilename filesMatchingWithoutDotDirs:'*'
+ "
+
+ "Created: / 15.4.1997 / 12:52:10 / cg"
+ "Modified: / 3.8.1998 / 21:22:30 / cg"
+!
+
+filesMatchingWithoutDotDirs:aPattern caseSensitive:caseSensitive do:aBlock
+ "given the receiver, representing a directory;
+ evaluate aBlock for files matching a pattern.
+ Exclude '.' and '..'.
+ The pattern may be a simple matchPattern, or a set of
+ multiple patterns separated by semicolons."
+
+ |matchers|
matchers := aPattern asCollectionOfSubstringsSeparatedBy:$;.
- caseSensitive := self species isCaseSensitive.
-
- ^ self directoryContents
- select:[:name |
- name ~= '.'
- and:[name ~= '..'
- and:[(matchers detect:[:p | p match:name caseSensitive:caseSensitive] ifNone:0) ~~ 0]]
- ]
+
+ self directoryContentsDo:[:name |
+ (name ~= '.'
+ and:[ name ~= '..'
+ and:[ (matchers contains:[:p | p match:name caseSensitive:caseSensitive]) ]])
+ ifTrue:[
+ aBlock value:name
+ ]
+ ].
+
+ "
+ Filename currentDirectory filesMatching:'M*'
+ '/etc' asFilename filesMatching:'[a-z]*'
+ '../../libbasic' asFilename filesMatching:'[A-D]*.st'
+ "
+
+ "Created: / 15.4.1997 / 12:52:10 / cg"
+ "Modified: / 3.8.1998 / 21:22:30 / cg"
+!
+
+filesMatchingWithoutDotDirs:aPattern do:aBlock
+ "given the receiver, representing a directory;
+ evaluate aBlock for files matching a pattern.
+ Exclude '.' and '..'.
+ The pattern may be a simple matchPattern, or a set of
+ multiple patterns separated by semicolons."
+
+ self filesMatchingWithoutDotDirs:aPattern caseSensitive:self species isCaseSensitive do:aBlock
"
Filename currentDirectory filesMatching:'M*'
@@ -4405,6 +4610,10 @@
!
fullAlternativePathName
+ "some filesystems (aka: windows) have alternative (short) filenames.
+ Those systems redefine this method to return it.
+ Otherwise, the same as the regular name is returned here"
+
^ nameString
!
@@ -5521,29 +5730,11 @@
may be changed in the near future, to raise an exception instead.
So users of this method better test for existing directory before.
Notice:
- this returns the file-names as strings;
- see also #directoryContentsAsFilenames, which returns fileName instances."
-
- |directoryStream contents|
-
- contents := OrderedCollection new.
- directoryStream := DirectoryStream directoryNamed:(self osNameForDirectoryContents).
- directoryStream isNil ifTrue:[^ nil].
-
- [
- [directoryStream atEnd] whileFalse:[
- |entry|
-
- entry := directoryStream nextLine.
- (entry notNil and:[entry ~= '.' and:[entry ~= '..']]) ifTrue:[
- contents add:entry
- ].
- ].
- ] ensure:[
- directoryStream close
- ].
-
- ^ contents.
+ this returns the file-names as strings;
+ see also #directoryContentsAsFilenames, which returns fileName instances."
+
+ ^ OrderedCollection withCollectedContents:[:coll |
+ self directoryContentsDo:[:each | coll add:each]]
"
'.' asFilename directoryContents
@@ -5561,14 +5752,11 @@
may be changed in the near future, to raise an exception instead.
So users of this method better test for existing directory before.
Notice:
- this returns the file-names as fileName instances;
- see also #directoryContents, which returns strings."
-
- |names|
-
- names := self directoryContents.
- names isNil ifTrue:[^ nil].
- ^ names collect:[:entry | self construct:entry].
+ this returns the file-names as fileName instances;
+ see also #directoryContents, which returns strings."
+
+ ^ OrderedCollection withCollectedContents:[:coll |
+ self directoryContentsAsFilenamesDo:[:each | coll add:each]]
"
'.' asFilename directoryContentsAsFilenames