FileDirectory.st
author claus
Wed, 13 Oct 1993 03:14:32 +0100
changeset 5 67342904af11
parent 3 24d81bf47225
child 13 62303f84ff5f
permissions -rw-r--r--
*** empty log message ***

"
 COPYRIGHT (c) 1989 by Claus Gittinger
              All Rights Reserved

 This software is furnished under a license and may be used
 only in accordance with the terms of that license and with the
 inclusion of the above copyright notice.   This software may not
 be provided or otherwise made available to, or used by, any
 other person.  No title to or ownership of the software is
 hereby transferred.
"

Collection subclass:#FileDirectory
       instanceVariableNames:'pathName lazy'
       classVariableNames:'pathOfCurrentDirectory'
       poolDictionaries:''
       category:'Collections-Files'
!

FileDirectory comment:'

COPYRIGHT (c) 1989 by Claus Gittinger
             All Rights Reserved

FileDirectories represent directories in the underlying host system.
They provide various methods to create/delete and query for files and/or
directories.

$Header: /cvs/stx/stx/libbasic/FileDirectory.st,v 1.4 1993-10-13 02:12:01 claus Exp $

written winter 89 by claus
'!

!FileDirectory class methodsFor:'initialization'!

initialize
    super initialize.
    ObjectMemory addDependent:self
!

update:something
    something == #restarted ifTrue:[
        pathOfCurrentDirectory := nil
    ]
! !

!FileDirectory class methodsFor:'instance creation'!

rootDirectory
    "create and return a new FileDirectory for the root directory"

    ^ (self basicNew) pathName:'/'
!

currentDirectory
    "create and return a new FileDirectory for the current directory"

    ^ (self basicNew) pathName:'.'
!

directoryNamed:name
    "create and return a new FileDirectory for the directory
     with given pathname"

    ^ (self basicNew) pathName:name
!

directoryNamed:name in:aFileDirectory
    "create and return a new FileDirectory for the directory with given name
     in another FileDirectory"

    |baseName|

    ((name at:1) == $/) ifTrue:[
        ^ self directoryNamed:name
    ].
    (aFileDirectory isKindOf:FileDirectory) ifTrue:[
        baseName := aFileDirectory pathName
    ] ifFalse:[
        baseName := aFileDirectory
    ].
"
    (name = '..') ifTrue:[
        ^ (self basicNew) pathName:(OperatingSystem directoryNameOf:baseName)
    ].
"
    (name = '.') ifTrue:[^ aFileDirectory].

    (baseName = '/') ifFalse:[
        (baseName endsWith:'/') ifFalse:[
            baseName := baseName , '/'
        ]
    ].
    ^ (self basicNew) pathName:(baseName , name)
! !

!FileDirectory methodsFor:'accessing'!

baseName
    "return my baseName
     - thats the directory name without leading parent-dirs"

    lazy ifTrue:[self getFullPathName].
    ^ OperatingSystem baseNameOf:pathName
!

directoryName
    "return my directoryName
     - thats the directory name where I'm in"

    lazy ifTrue:[self getFullPathName].
    ^ OperatingSystem directoryNameOf:pathName
!

pathName
    "return my full pathname"

    lazy ifTrue:[self getFullPathName].
    ^ pathName
!

pathName:dirName
    "set my pathname; return nil if not a valid path; self otherwise"

    pathName := dirName.
    (dirName startsWith:'/') ifFalse:[
        lazy := true
    ] ifTrue:[
        (dirName includes:$.) ifTrue:[
            lazy := true
        ]
    ].
    ^ self
"
    (OperatingSystem isDirectory:pathName) ifFalse:[^ nil]
"
!

contents 
    "return a collection with all files and subdirectories in the receiver"

    |coll|

    coll := OrderedCollection new.
    self do:[:name |
        coll add:name
    ].
    (coll size ~~ 0) ifTrue:[
        coll sort
    ].
    ^ coll
!

directories
    "return a collection with all subdirectories in the receiver directory"

    |coll|

    coll := OrderedCollection new.
    self directoriesDo:[:name |
        coll add:name
    ].
    (coll size ~~ 0) ifTrue:[
        coll sort
    ].
    ^ coll
!

files
    "return a collection with all plain files in the receiver directory"

    |coll|

    coll := OrderedCollection new.
    self filesDo:[:name |
        coll add:name
    ].
    ^ coll sort
! !

!FileDirectory methodsFor:'private'!

getFullPathName
    "make my pathname be a full pathname - i.e. starting at root"

    |aStream command shortPathName fullPathName|

    (pathName = '/') ifTrue:[
        lazy := false.
        ^ self
    ].

    "since currentDirectory is used very often, cache its path here"

    (pathName = '.') ifTrue:[
        pathOfCurrentDirectory notNil ifTrue:[
            pathName := pathOfCurrentDirectory.
            lazy := false.
            ^ self
        ]
    ].

    shortPathName := pathName.

    "sys5.4 and sunos have a convenient function for this ..."
%{
#if defined(SYSV4) || defined(sunos)
#   include <stdlib.h>
#   include <sys/param.h>

    char nameBuffer[MAXPATHLEN + 1];

    if (realpath(_stringVal(_INST(pathName)), nameBuffer)) {
        fullPathName = _MKSTRING(nameBuffer COMMA_CON);
    }
#endif
%}
.
    fullPathName notNil ifTrue:[
        pathName := fullPathName.
        lazy := false
    ] ifFalse:[
        "since there might be symbolic links and other stuff involved,
         better trust pwd than removing '..' by ourself
         - although this is very slow"

        command := 'cd ' , pathName , '; pwd'.
        aStream := PipeStream readingFrom:command.
        aStream isNil ifFalse:[
            (aStream atEnd) ifFalse:[
                fullPathName := aStream nextLine
            ].
            aStream close.
            fullPathName notNil ifTrue:[
                pathName := fullPathName.
                lazy := false
            ]
        ] ifTrue:[
            self error:('PipeStream for <' , command , '> failed').
            "by clearing lazy, we avoid triggering the error again"
            lazy := false
        ]
    ].

    "if it was the current dir, keep name for next query"
    (shortPathName = '.') ifTrue:[
        pathOfCurrentDirectory := fullPathName
    ]
! !

!FileDirectory methodsFor:'basic'!

createDirectory:newName
    "create a new filedirectory as a subdirectory of myself;
     return true if successful"

    |realName|

    (newName = '.') ifFalse:[
        (newName = '..') ifFalse:[
            ((newName at:1) == $/) ifTrue:[
                realName := newName copyFrom:2
            ] ifFalse:[
                realName := newName
            ].
            (realName startsWith:'/') ifTrue:[
                ^ OperatingSystem createDirectory:realName
            ] ifFalse:[
                ^ OperatingSystem createDirectory:(pathName , '/' , realName)
            ]
        ]
    ].
    ^ false
!

removeFile:fileName
    "remove the file 'fileName' from myself; return true if successful"

    (fileName startsWith:'/') ifTrue:[
        ^ OperatingSystem removeFile:fileName
    ].
    ^ OperatingSystem removeFile:(pathName , '/' , fileName)
!

removeDirectory:dirName
    "remove the directory 'dirName' from myself; return true if successful"

    (dirName startsWith:'/') ifTrue:[
        ^ OperatingSystem removeDirectory:dirName
    ].
    ^ OperatingSystem removeDirectory:(pathName , '/' , dirName)
!
    
remove:aFileOrDirectoryName
    "remove the file or directory from myself; return true if successful"

    |path|

    (aFileOrDirectoryName startsWith:'/') ifTrue:[
        path := aFileOrDirectoryName
    ] ifFalse:[
        path := (pathName , '/' , aFileOrDirectoryName)
    ].
    (OperatingSystem isDirectory:path) ifTrue:[
        ^ OperatingSystem removeDirectory:path
    ].
    ^ OperatingSystem removeFile:path
!

link:oldFileName to:newFileName
    "link oldFileName to newFileName in myself, return true if successful"

    |path1 path2|

    (oldFileName startsWith:'/') ifTrue:[
        path1 := oldFileName
    ] ifFalse:[
        path1 := (pathName , '/' , oldFileName)
    ].
    (newFileName startsWith:'/') ifTrue:[
        path2 := newFileName
    ] ifFalse:[
        path2 := (pathName , '/' , newFileName)
    ].
    ^ OperatingSystem link:path1 to:path2
!

renameFile:oldFileName newName:newFileName
    "rename the file; return true if successful"

    |path1 path2|

    (oldFileName startsWith:'/') ifTrue:[
        path1 := oldFileName
    ] ifFalse:[
        path1 := (pathName , '/' , oldFileName)
    ].
    (newFileName startsWith:'/') ifTrue:[
        path2 := newFileName
    ] ifFalse:[
        path2 := (pathName , '/' , newFileName)
    ].
    ^ OperatingSystem rename:path1 to:path2
! !

!FileDirectory methodsFor:'queries'!

id
    "return the directories file-id (inode number)"

    ^ OperatingSystem idOf:pathName
!

exists
    "return true if this directory exists"

    ^ OperatingSystem isDirectory:pathName
    "(FileDirectory directoryNamed:'fooBar') exists"
!

isEmpty
    "return true, if the directory is empty;
     redefined since '.' and '..' do not count as entries here."

    self do:[:fName |
        ((fName ~= '.') and:[fName ~= '..']) ifTrue:[^ false].
    ].
    ^ true
!

infoOf:name
    "return an array filled with file info for the file 'aFileName';
     return nil if such a file does not exist"

    (name startsWith:'/') ifTrue:[
        ^ OperatingSystem infoOf:name
    ].
    ^ OperatingSystem infoOf:(pathName , '/' , name)
!

timeOfLastChange:name
    "return the timeStamp of a file in myself"

    (name startsWith:'/') ifTrue:[
        ^ OperatingSystem timeOfLastChange:name
    ].
    ^ OperatingSystem timeOfLastChange:(pathName , '/' , name)
!

timeOfLastChange
    "return the timeStamp of myself"

    ^ OperatingSystem timeOfLastChange:pathName
!

accessModeOf:aFileName
    "return the access-mode bits (rwxrwxrwx) of a file in myself"

    (aFileName startsWith:'/') ifTrue:[
        ^ OperatingSystem accessModeOf:aFileName
    ].
    ^ OperatingSystem accessModeOf:(pathName , '/' , aFileName)
!

changeAccessModeOf:aFileName to:modeBits
    "set the access-mode bits (rwxrwxrwx) of a file in myself"

    (aFileName startsWith:'/') ifTrue:[
        ^ OperatingSystem changeAccessModeOf:aFileName
                                          to:modeBits
    ].
    ^ OperatingSystem changeAccessModeOf:(pathName , '/' , aFileName)
                                      to:modeBits
!

typeOf:aFileName
    "return the symbolic type of a file in myself"

    (aFileName startsWith:'/') ifTrue:[
        ^ OperatingSystem typeOf:aFileName
    ].
    ^ OperatingSystem typeOf:(pathName , '/' , aFileName)
!

isDirectory:name
    "return true, if the given name is that of a directory in myself"

    (name startsWith:'/') ifTrue:[
        ^ OperatingSystem isDirectory:name
    ].
    ^ OperatingSystem isDirectory:(pathName , '/' , name)
!

isReadable:name
    "return true, if the given file is readable"

    (name startsWith:'/') ifTrue:[
        ^ OperatingSystem isReadable:name
    ].
    ^ OperatingSystem isReadable:(pathName , '/' , name)
!

isWritable:name
    "return true, if the given file is readable"

    (name startsWith:'/') ifTrue:[
        ^ OperatingSystem isWritable:name
    ].
    ^ OperatingSystem isWritable:(pathName , '/' , name)
!

isExecutable:name
    "return true, if the given file is executable"

    (name startsWith:'/') ifTrue:[
        ^ OperatingSystem isExecutable:name
    ].
    ^ OperatingSystem isExecutable:(pathName , '/' , name)
! !

!FileDirectory methodsFor:'printing & storing'!

printString
    lazy ifTrue:[self getFullPathName].
    ^ '(a FileDirectory pathName:' , pathName, ')'
!

storeOn:aStream
    lazy ifTrue:[self getFullPathName].
    aStream nextPutAll:'(FileDirectory directoryNamed:'.
    aStream nextPutAll:pathName.
    aStream nextPut:$)
! !

!FileDirectory methodsFor:'more instance creation'!

directoryNamed:aName
    ^ self class directoryNamed:aName in:self pathName
! !

!FileDirectory methodsFor:'enumerating'!

where:testBlock do:aBlock
    "evaluate the argument, aBlock for every object in the directory
     for which testBlock evaluates to true."

    |aStream name|

    aStream := DirectoryStream directoryNamed:pathName.
    aStream isNil ifTrue:[^ nil].
    [aStream atEnd] whileFalse:[
        name := aStream nextLine.
        name notNil ifTrue:[
            (testBlock value:name) ifTrue:[
                aBlock value:name
            ]
        ]
    ].
    aStream close
!

do:aBlock
    "evaluate the argument, aBlock for every name in the directory"

    self where:[:name | true] do:aBlock
!

namesDo:aBlock
    "evaluate the argument, aBlock for every name in the directory.
     for ST-80 compatibility"

    self do:aBlock
!

filesDo:aBlock
    "evaluate the argument, aBlock for every plain file name in the directory"

    self where:[:name | (self isDirectory:name) not] do:aBlock
!

directoriesDo:aBlock
    "evaluate the argument, aBlock for every subdirectory name in the directory"

    self where:[:name | (self isDirectory:name) ifTrue:[
                            ((name ~= '.') and:[name ~= '..'])
                        ] ifFalse:[
                            false
                        ]
               ] do:aBlock
!

allFilesDo:aBlock
    "evaluate the argument, aBlock for every file name in the directory and in all
     subdirectories"

    |aStream command line|

    lazy ifTrue:[self getFullPathName].
    command := 'cd ' , pathName , '; find . -print'.
    aStream := PipeStream readingFrom:command.
    aStream isNil ifTrue:[^ nil].
    [aStream atEnd] whileFalse:[
        line := aStream nextLine.
        line notNil ifTrue:[
            (line = '.') ifFalse:[
                "cut off initial ./"
                line := line copyFrom:3 to:(line size)
            ].
            aBlock value:line
        ]
    ].
    aStream close
!

allDirectoriesDo:aBlock
    "evaluate the argument, aBlock for every directory name
     in the directory and in all subdirectories"

    |aStream command line|

    lazy ifTrue:[self getFullPathName].
    command := 'cd ' , pathName , '; find . -type d -print'.
    aStream := PipeStream readingFrom:command.
    aStream isNil ifTrue:[^ nil].
    [aStream atEnd] whileFalse:[
        line := aStream nextLine.
        line notNil ifTrue:[
            (line = '.') ifFalse:[
                "cut off initial ./"
                line := line copyFrom:3 to:(line size)
            ].
            aBlock value:line
        ]
    ].
    aStream close
! !