FileStr.st
changeset 1 a27a279701f8
child 2 6526dde5f3ac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/FileStr.st	Fri Jul 16 11:39:45 1993 +0200
@@ -0,0 +1,467 @@
+"
+ COPYRIGHT (c) 1989-93 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.
+"
+
+ExternalStream subclass:#FileStream
+       instanceVariableNames:'pathName'
+       classVariableNames:''
+       poolDictionaries:''
+       category:'Streams-External'
+!
+
+FileStream comment:'
+COPYRIGHT (c) 1989-93 by Claus Gittinger
+              All Rights Reserved
+
+This class provides access to the operating systems underlying file
+system (i.e. its an interface to the stdio library).
+
+%W% %E%
+'!
+
+%{
+#include <stdio.h>
+
+#ifdef transputer
+# include <iocntrl.h>
+# ifndef fileno
+   /* kludge: inmos forgot fileno */
+#  define fileno(f)     ((f)->__file)
+# endif
+#else
+# include <sys/types.h>
+# include <sys/stat.h>
+#endif
+%}
+
+!FileStream class methodsFor:'instance creation'!
+
+newFileNamed:filename
+    "return a FileStream for new file named filename, aString.
+     If the file exists, it is truncated, otherwise created.
+     The file is opened for write access only."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename.
+    newStream createForWriting isNil ifTrue:[^nil].
+    ^ newStream
+!
+
+newFileNamed:filename in:aDirectory
+    "return a FileStream for new file named filename, aString
+     in aDirectory, a FileDirectory.
+     If the file exists, it is truncated, otherwise created.
+     The file is opened for write access only."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream createForWriting isNil ifTrue:[^nil].
+    ^ newStream
+!
+
+oldFileNamed:filename
+    "return a FileStream for existing file named filename, aString.
+     The file is opened for read/write access."
+
+    |newStream|
+
+    (OperatingSystem isReadable:filename) ifFalse:[^nil].
+    newStream := (self basicNew) pathName:filename.
+    newStream readwrite.
+    newStream openForReadWrite isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+oldFileNamed:filename in:aDirectory
+    "return a FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory.
+     The file is opened for read/write access."
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForReadWrite isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+fileNamed:filename
+    "return a stream on file filename - if the file does not
+     already exist, create it."
+
+    |stream|
+
+    stream := self oldFileNamed:filename.
+    stream isNil ifTrue:[
+        stream := self newFileNamed:filename
+    ].
+    ^ stream
+!
+
+fileNamed:filename in:aDirectory
+    "return a stream on file filename - if the file does not
+     already exist, create it."
+
+    |stream|
+
+    stream := self oldFileNamed:filename in:aDirectory.
+    stream isNil ifTrue:[
+        stream := self newFileNamed:filename in:aDirectory
+    ].
+    ^ stream
+!
+
+readonlyFileNamed:filename
+    "return a readonly FileStream for existing file named filename, aString"
+
+    |newStream|
+
+    (OperatingSystem isReadable:filename) ifFalse:[^nil].
+
+    newStream := (self basicNew) pathName:filename.
+    newStream openForReading isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+readonlyFileNamed:filename in:aDirectory
+    "return a readonly FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForReading isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+appendingOldFileNamed:filename
+    "return a FileStream for existing file named filename, aString"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename.
+    newStream openForAppending isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+!
+
+appendingOldFileNamed:filename in:aDirectory
+    "return a FileStream for existing file named filename, aString
+     in aDirectory, a FileDirectory"
+
+    |newStream|
+    newStream := (self basicNew) pathName:filename in:aDirectory.
+    newStream openForAppending isNil ifTrue:[^nil].
+    newStream readLimit:(newStream size).
+    ^ newStream
+! !
+
+!FileStream methodsFor:'accessing'!
+
+store:something
+    "what really should this do"
+
+    self nextPutAll:something
+!
+
+directoryName
+    "return the name of the directory I'm in"
+
+    |path lastIndex index|
+
+    path := pathName.
+    lastIndex := 0.
+    index := path indexOf:$/.
+    [index ~~ 0] whileTrue:[
+        lastIndex := index.
+        index := path indexOf:$/ startingAt:(index + 1)
+    ].
+    (lastIndex == 0) ifTrue:[^ '.'].
+    (lastIndex == 1) ifTrue:[^ '/'].
+    ^ path copyFrom:1 to:(lastIndex - 1)
+!
+
+name
+    "return my name without leading direcory-path"
+
+    |lastIndex index|
+
+    lastIndex := 1.
+    [true] whileTrue:[
+        index := pathName indexOf:$/ startingAt:lastIndex.
+        (index == 0) ifTrue:[
+            ^ pathName copyFrom:lastIndex
+        ].
+        lastIndex := index + 1
+    ]
+!
+
+pathName
+    "return the pathname"
+
+    ^ pathName
+! !
+
+!FileStream methodsFor:'private'!
+
+pathName:filename
+    "set the pathname"
+
+    pathName := filename
+!
+
+pathName:filename in:aDirectory
+    "set the pathname starting at aDirectory, a FileDirectory"
+
+    ((filename at:1) == $/) ifTrue:[
+        "filename may not start with a '/'"
+        pathName := nil
+    ] ifFalse:[
+        pathName := aDirectory pathName.
+        (pathName endsWith:'/') ifFalse:[
+            pathName := pathName , '/'
+        ].
+        pathName := pathName , filename
+    ]
+!
+
+open
+    "open the file"
+
+    pathName isNil ifTrue:[^nil].
+    (mode == #readonly) ifTrue: [
+        ^ self openForReading
+    ].
+    (mode == #writeonly) ifTrue: [
+        ^ self openForWriting
+    ].
+    ^ self openForReadWrite
+!
+
+openWithMode:openmode
+    "open the file; openmode is the string defining the way to open"
+
+    |retVal|
+%{
+    FILE *f;
+    OBJ path;
+    extern OBJ ErrorNumber, Filename;
+    extern errno;
+
+    if (_INST(filePointer) == nil) {
+        path = _INST(pathName);
+        if (_isString(path) || (_Class(path) == Filename)) {
+            f = (FILE *)fopen((char *) _stringVal(path), (char *) _stringVal(openmode));
+            if (f == NULL) {
+                ErrorNumber = _MKSMALLINT(errno);
+                _INST(position) = nil;
+            } else {
+                _INST(filePointer) = _MKSMALLINT((int)f);
+                _INST(position) = _MKSMALLINT(1);
+                retVal = self;
+            }
+        }
+    }
+%}
+.
+    retVal notNil ifTrue:[
+        lobby register:self
+    ].
+    ^ retVal
+!
+
+openForReading
+    "open the file for readonly"
+
+    mode := #readonly.
+    ^ self openWithMode:'r'
+!
+
+openForWriting
+    "open the file for writeonly"
+
+    mode := #writeonly.
+    ^ self openWithMode:'w'
+!
+
+openForAppending
+    "open the file for writeonly appending to the end"
+
+    mode := #writeonly.
+    ^ self openWithMode:'a+'
+!
+
+createForWriting
+    "create/truncate the file for writeonly"
+
+    mode := #writeonly.
+    ^ self openWithMode:'w+'
+!
+
+openForReadWrite
+    "open the file for read/write"
+
+    mode := #readwrite.
+    ^ self openWithMode:'r+w'
+!
+
+createForReadWrite
+    "create/truncate the file for read/write"
+
+    mode := #readwrite.
+    ^ self openWithMode:'rw+'
+!
+
+reOpen
+    "sent after snapin to reopen streams"
+
+    filePointer notNil ifTrue:[
+        "it was open, when snapped-out"
+        filePointer := nil.
+        self open.
+        filePointer isNil ifTrue:[
+            Transcript showCr:('could not reopen file: ', pathName)
+        ] ifFalse:[
+            self position:position
+        ]
+    ]
+!
+
+size
+    "return the size in bytes of the file"
+
+%{  /* NOCONTEXT */
+
+#ifdef transputer
+    FILE *f;
+    int size;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        if ((size = filesize(fileno(f))) >= 0) {
+            RETURN ( _MKSMALLINT(size) );
+        }
+    }
+#else
+    FILE *f;
+    struct stat buf;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        if (fstat(fileno(f), &buf) >= 0) {
+            RETURN ( _MKSMALLINT(buf.st_size) );
+        }
+    }
+#endif
+%}
+.
+    "could add a fall-back here:
+
+        oldPosition := self position.
+        self setToEnd.
+        sz := self position.
+        self position:oldPosition.
+        ^ sz
+    "
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+position
+    "return the read/write position in the file -
+     notice, in smalltalk indices start at 1 so begin of file is 1""
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    long currentPosition;
+
+    if (_INST(filePointer) != nil) {
+        f = (FILE *)_intVal(_INST(filePointer));
+        currentPosition = ftell(f);
+        if (currentPosition >= 0) {
+            /*
+             * notice: Smalltalk index starts at 1
+             */
+            RETURN ( _MKSMALLINT(currentPosition + 1) );
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+position:newPos
+    "set the read/write position in the file"
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    if (_INST(filePointer) != nil) {
+        if (_isSmallInteger(newPos)) {
+            f = (FILE *)_intVal(_INST(filePointer));
+            /*
+             * notice: Smalltalk index starts at 1
+             */
+            if (fseek(f, _intVal(newPos) - 1, 0) >= 0) {
+                _INST(position) = newPos;
+                RETURN ( self );
+            }
+            ErrorNumber = _MKSMALLINT(errno);
+        }
+    }
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    ^ self primitiveFailed
+!
+
+setToEnd
+    "set the read/write position in the file to be at the end of the file"
+
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+%{
+    FILE *f;
+    extern OBJ ErrorNumber;
+    extern errno;
+
+    f = (FILE *)_intVal(_INST(filePointer));
+    _INST(position) = nil;
+    if (fseek(f, 0, 2) >= 0) {
+        RETURN ( self );
+    }
+    ErrorNumber = _MKSMALLINT(errno);
+%}
+.
+    ^ self primitiveFailed
+! !
+
+!FileStream methodsFor:'printing & storing'!
+
+printOn:aStream
+    aStream nextPutAll:'(a FileStream for:'.
+    aStream nextPutAll:pathName.
+    aStream nextPut:$)
+!
+
+storeOn:aStream
+    aStream nextPutAll:'(FileStream oldFileNamed:'.
+    aStream nextPutAll:pathName.
+    (self position ~~ 1) ifTrue:[
+        aStream nextPutAll:'; position:'.
+        self position storeOn:aStream
+    ].
+    aStream nextPut:$)
+! !