"
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:$)
! !