--- a/ExtStream.st Mon Dec 13 12:40:31 1993 +0100
+++ b/ExtStream.st Thu Dec 16 11:53:27 1993 +0100
@@ -11,7 +11,7 @@
"
ReadWriteStream subclass:#ExternalStream
- instanceVariableNames:'filePointer mode buffered binary useCRLF'
+ instanceVariableNames:'filePointer mode buffered binary useCRLF hitEOF'
classVariableNames:'lobby'
poolDictionaries:''
category:'Streams-External'
@@ -22,14 +22,7 @@
COPYRIGHT (c) 1988 by Claus Gittinger
All Rights Reserved
-ExternalStream defines protocol common to Streams which have a file-descriptor and
-represent some file or communicationChannel of the underlying OperatingSystem.
-ExternalStream is abstract; concrete classes are FileStream, PipeStream etc.
-ExternalStreams can be in two modes: text (the default) and binary.
-In text-mode, the elements read/written are characters; while in binary-mode the basic
-elements are bytes which read/write as SmallIntegers in the range 0..255.
-
-$Header: /cvs/stx/stx/libbasic/Attic/ExtStream.st,v 1.6 1993-12-11 00:46:55 claus Exp $
+$Header: /cvs/stx/stx/libbasic/Attic/ExtStream.st,v 1.7 1993-12-16 10:52:46 claus Exp $
written 88 by claus
'!
@@ -40,6 +33,37 @@
#include <errno.h>
%}
+!ExternalStream class methodsFor:'documentation'!
+
+documentation
+"
+ExternalStream defines protocol common to Streams which have a file-descriptor and
+represent some file or communicationChannel of the underlying OperatingSystem.
+ExternalStream is abstract; concrete classes are FileStream, PipeStream etc.
+ExternalStreams can be in two modes: text (the default) and binary.
+In text-mode, the elements read/written are characters;
+while in binary-mode the basic elements are bytes which read/write as SmallIntegers
+in the range 0..255.
+
+Also, the stream can be either in buffered or unbuffered mode. In buffered mode,
+data is not written until either a cr is written (in text mode) or a synchronizeOutput
+is sent (in both modes).
+
+The underlying OperatingSystem streams may either be close explicitely (sending a close)
+or just forgotten - in this case, the garbage collector will eventually collect the
+object AND a close be performed automatically (but you will NOT know when this happens).
+Closing is also suggested - since all streams understand it (it is defined as a noop in
+one of the superclasses).
+
+Most of the methods found here redefine inherited methods for better performance,
+since I/O from/to files should be fast.
+
+Recovering a snapshot:
+not all streams can be restored to the state they had before - see the implementation of
+reOpen in subclasses for more information.
+"
+! !
+
!ExternalStream class methodsFor:'initialization'!
initialize
@@ -73,7 +97,7 @@
|newStream|
newStream := self basicNew.
- newStream text; buffered:true; useCRLF:false.
+ newStream text; buffered:true; useCRLF:false; clearEOF.
^ newStream
! !
@@ -104,6 +128,10 @@
!ExternalStream methodsFor:'private'!
+clearEOF
+ hitEOF := false
+!
+
reOpen
"sent after snapin to reopen streams.
cannot reopen here since I am abstract and have no device knowledge"
@@ -260,12 +288,13 @@
[self atEnd] whileFalse:[
chunk := ByteArray uninitializedNew:4096.
cnt := self nextBytes:(chunk size) into:chunk.
- cnt isNil ifFalse:[
+ cnt notNil ifTrue:[
chunks add:chunk.
sizes add:cnt.
byteCount := byteCount + cnt
]
].
+
"now, create one big ByteArray"
bytes := ByteArray uninitializedNew:byteCount.
offset := 1.
@@ -475,6 +504,7 @@
if (cnt < 0) {
ErrorNumber = _MKSMALLINT(errno);
}
+ _INST(hitEOF) = true;
RETURN ( nil );
}
%}
@@ -485,36 +515,50 @@
nextBytes:count into:anObject
"read the next count bytes into an object and return the number of
- bytes read or nil on error.
- Use with care - non object oriented i/o.
- You can create bad crashes with this method."
+ bytes read or nil on error. The object must have non-pointer indexed
+ instvars (i.e. be a ByteArray, String, Float- or DoubleArray).
+ Use with care - non object oriented i/o."
^ self nextBytes:count into:anObject startingAt:1
!
nextBytes:count into:anObject startingAt:start
"read the next count bytes into an object and return the number of
- bytes read or nil on error.
- Use with care - non object oriented i/o.
- You can create bad crashes with this method."
+ bytes read or nil on error. The object must have non-pointer indexed
+ instvars (i.e. be a ByteArray, String, Float- or DoubleArray).
+ Use with care - non object oriented i/o."
%{ /* NOCONTEXT */
FILE *f;
int cnt, offs;
- int objSize, savInt;
+ int objSize, nInstVars, nInstBytes, savInt;
char *cp;
extern OBJ ErrorNumber;
extern errno;
OBJ pos;
extern int _immediateInterrupt;
+ OBJ oClass;
if ((_INST(filePointer) != nil) && (_INST(mode) != _writeonly)) {
if (_isSmallInteger(count) && _isSmallInteger(start)) {
+ oClass = _Class(anObject);
+ switch (_intVal(_ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
+ case BYTEARRAY:
+ case WORDARRAY:
+ case LONGARRAY:
+ case FLOATARRAY:
+ case DOUBLEARRAY:
+ break;
+ default:
+ goto bad;
+ }
cnt = _intVal(count);
offs = _intVal(start) - 1;
f = MKFD(_INST(filePointer));
- objSize = _Size(anObject) - OHDR_SIZE;
+ nInstVars = _intVal(_ClassInstPtr(oClass)->c_ninstvars);
+ nInstBytes = OHDR_SIZE + nInstVars * sizeof(OBJ);
+ objSize = _Size(anObject) - nInstBytes;
if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
savInt = _immediateInterrupt;
@@ -533,6 +577,8 @@
pos = _INST(position);
if (pos != nil)
_INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+ if (cnt == 0)
+ _INST(hitEOF) = true;
RETURN ( _MKSMALLINT(cnt) );
}
ErrorNumber = _MKSMALLINT(errno);
@@ -540,6 +586,7 @@
}
}
}
+bad: ;
%}
.
filePointer isNil ifTrue:[^ self errorNotOpen].
@@ -557,6 +604,7 @@
%{ /* NOCONTEXT */
extern int _immediateInterrupt;
+ extern OBJ ErrorNumber;
int savInt;
if (_INST(binary) == true) {
@@ -578,8 +626,12 @@
}
} while ((cnt < 0) && (errno == EINTR));
- if (cnt < 0) {
+ if (cnt <= 0) {
_immediateInterrupt = savInt;
+ if (cnt == 0)
+ _INST(hitEOF) = true;
+ else
+ ErrorNumber = _MKSMALLINT(errno);
RETURN ( nil );
}
do {
@@ -593,10 +645,14 @@
} while ((cnt < 0) && (errno == EINTR));
_immediateInterrupt = savInt;
- if (cnt < 0) {
+ if (cnt <= 0) {
if (_INST(position) != nil) {
_INST(position) = _MKSMALLINT(_intVal(_INST(position)) + 1);
}
+ if (cnt == 0)
+ _INST(hitEOF) = true;
+ else
+ ErrorNumber = _MKSMALLINT(errno);
RETURN ( _MKSMALLINT(hi & 0xFF) );
}
if (_INST(position) != nil) {
@@ -692,12 +748,14 @@
first = getc(f);
if (first == EOF) {
_immediateInterrupt = savInt;
+ _INST(hitEOF) = true;
RETURN ( nil );
}
second = getc(f);
_immediateInterrupt = savInt;
if (second == EOF) {
+ _INST(hitEOF) = true;
RETURN ( nil );
}
if (_INST(position) != nil) {
@@ -739,12 +797,14 @@
first = getc(f);
if (first == EOF) {
_immediateInterrupt = savInt;
+ _INST(hitEOF) = true;
RETURN ( nil );
}
second = getc(f);
_immediateInterrupt = savInt;
if (second == EOF) {
+ _INST(hitEOF) = true;
RETURN ( nil );
}
if (_INST(position) != nil) {
@@ -787,22 +847,26 @@
f = MKFD(_INST(filePointer));
first = getc(f);
if (first == EOF) {
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN ( nil );
}
second = getc(f);
if (second == EOF) {
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN ( nil );
}
third = getc(f);
if (third == EOF) {
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN ( nil );
}
fourth = getc(f);
_immediateInterrupt = savInt;
if (fourth == EOF) {
+ _INST(hitEOF) = true;
RETURN ( nil );
}
if (_INST(position) != nil) {
@@ -857,22 +921,26 @@
f = MKFD(_INST(filePointer));
first = getc(f);
if (first == EOF) {
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN ( nil );
}
second = getc(f);
if (second == EOF) {
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN ( nil );
}
third = getc(f);
if (third == EOF) {
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN ( nil );
}
fourth = getc(f);
_immediateInterrupt = savInt;
if (fourth == EOF) {
+ _INST(hitEOF) = true;
RETURN ( nil );
}
if (_INST(position) != nil) {
@@ -977,60 +1045,75 @@
nextPutBytes:count from:anObject startingAt:start
"write count bytes from an object starting at index start.
return the number of bytes written or nil on error.
+ The object must have non-pointer indexed instvars
+ (i.e. be a ByteArray, String, Float- or DoubleArray).
Use with care - non object oriented i/o"
%{ /* NOCONTEXT */
FILE *f;
int cnt, offs;
- int objSize;
+ int objSize, nInstVars, nInstBytes;
char *cp;
extern OBJ ErrorNumber;
extern errno;
+ OBJ oClass;
OBJ pos;
int savInt;
extern int _immediateInterrupt;
- if (_INST(filePointer) != nil) {
- if (_INST(mode) != _readonly) {
- if (_isSmallInteger(count) && _isSmallInteger(start)) {
- cnt = _intVal(count);
- offs = _intVal(start) - 1;
- f = MKFD(_INST(filePointer));
+ if ((_INST(filePointer) != nil) && (_INST(mode) != _readonly)) {
+ if (_isSmallInteger(count) && _isSmallInteger(start)) {
+ oClass = _Class(anObject);
+ switch (_intVal(_ClassInstPtr(oClass)->c_flags) & ARRAYMASK) {
+ case BYTEARRAY:
+ case WORDARRAY:
+ case LONGARRAY:
+ case FLOATARRAY:
+ case DOUBLEARRAY:
+ break;
+ default:
+ goto bad;
+ }
+ cnt = _intVal(count);
+ offs = _intVal(start) - 1;
+ f = MKFD(_INST(filePointer));
- objSize = _Size(anObject) - OHDR_SIZE;
- if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
- cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
- savInt = _immediateInterrupt;
- _immediateInterrupt = 1;
+ nInstVars = _intVal(_ClassInstPtr(oClass)->c_ninstvars);
+ nInstBytes = OHDR_SIZE + nInstVars * sizeof(OBJ);
+ objSize = _Size(anObject) - nInstBytes;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ cp = (char *)_InstPtr(anObject) + OHDR_SIZE + offs;
+ savInt = _immediateInterrupt;
+ _immediateInterrupt = 1;
#ifdef OLD
- if (_INST(buffered) == false) {
- cnt = write(fileno(f), cp, cnt);
- } else
+ if (_INST(buffered) == false) {
+ cnt = write(fileno(f), cp, cnt);
+ } else
#endif
- {
- if (_INST(mode) == _readwrite)
- fseek(f, 0L, 1); /* needed in stdio */
- cnt = fwrite(cp, 1, cnt, f);
- }
+ {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ cnt = fwrite(cp, 1, cnt, f);
+ }
#ifndef OLD
- if (_INST(buffered) == false) {
- fflush(f);
- }
+ if (_INST(buffered) == false) {
+ fflush(f);
+ }
#endif
- _immediateInterrupt = savInt;
- if (cnt >= 0) {
- pos = _INST(position);
- if (pos != nil)
- _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
- RETURN ( _MKSMALLINT(cnt) );
- }
- ErrorNumber = _MKSMALLINT(errno);
- RETURN ( nil );
+ _immediateInterrupt = savInt;
+ if (cnt >= 0) {
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = _MKSMALLINT(_intVal(pos) + cnt);
+ RETURN ( _MKSMALLINT(cnt) );
}
+ ErrorNumber = _MKSMALLINT(errno);
+ RETURN ( nil );
}
}
}
+bad: ;
%}
.
filePointer isNil ifTrue:[^ self errorNotOpen].
@@ -1223,6 +1306,7 @@
}
RETURN ( _MKCHARACTER(c & 0xFF) );
}
+ _INST(hitEOF) = true;
RETURN ( nil );
}
}
@@ -1283,6 +1367,7 @@
}
RETURN ( _MKCHARACTER(c & 0xFF) );
}
+ _INST(hitEOF) = true;
_INST(position) = nil;
RETURN ( nil );
}
@@ -1394,7 +1479,7 @@
!
nextPutAll:aCollection
- "write all elements of the argument, aCollection"
+ "write all elements of the argument, aCollection."
%{ /* NOCONTEXT */
@@ -1606,7 +1691,7 @@
int savInt;
char *rslt;
extern errno;
- int fd;
+ int fd, ch;
if ((_INST(filePointer) != nil) && (_INST(mode) != _writeonly)) {
if (_INST(binary) != true) {
@@ -1615,41 +1700,53 @@
_immediateInterrupt = 1;
buffer[0] = 0;
-#ifndef OLD
+ /*
+ * mhmh - the following code looks ok to me,
+ * but seems not to work for sockets
+ */
+#ifdef DOES_NOT_WORK
if (_INST(mode) == _readwrite)
fseek(f, 0L, 1); /* needed in stdio */
-#endif
-#ifdef OLD
+ do {
+ rslt = fgets(buffer, sizeof(buffer), f);
+ } while ((rslt == NULL) && (errno == EINTR));
+#else
if (_INST(buffered) == true) {
-#endif
- do {
- rslt = fgets(buffer, sizeof(buffer), f);
- } while ((rslt == NULL) && (errno == EINTR));
-#ifdef OLD
- } else {
- rslt = buffer;
- fd = fileno(f);
- for (;;) {
+ if (_INST(mode) == _readwrite)
+ fseek(f, 0L, 1); /* needed in stdio */
+ }
+ rslt = buffer;
+ fd = fileno(f);
+ for (;;) {
+ if (_INST(buffered) == true) {
+ ch = getc(f);
+ if (ch == EOF)
+ len = 0;
+ else {
+ len = 1;
+ *rslt = ch;
+ }
+ } else {
do {
len = read(fd, rslt, 1);
} while ((len < 0) && (errno == EINTR));
- if (len <= 0) {
- if (rslt == buffer) {
- rslt = NULL;
- } else {
- *rslt = '\0';
- }
- break;
+ }
+ if (len <= 0) {
+ if (rslt == buffer) {
+ rslt = NULL;
+ } else {
+ *rslt = '\0';
}
- rslt++;
- if (*(rslt-1) == '\n') {
- *rslt = '\0';
- break;
- }
- if (rslt == (buffer + sizeof(buffer) - 1)) {
- *rslt = '\0';
- break;
- }
+ break;
+ }
+ rslt++;
+ if (*(rslt-1) == '\n') {
+ *rslt = '\0';
+ break;
+ }
+ if (rslt == (buffer + sizeof(buffer) - 1)) {
+ *rslt = '\0';
+ break;
}
}
#endif
@@ -1657,7 +1754,7 @@
if (rslt != NULL) {
len = strlen(buffer);
if (_INST(position) != nil) {
- _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len);
+ _INST(position) = _MKSMALLINT(_intVal(_INST(position)) + len + 1);
}
/* remove EOL character */
if ((len != 0) && (buffer[len-1] == '\n')) {
@@ -1668,6 +1765,7 @@
}
RETURN ( _MKSTRING(buffer COMMA_CON) );
}
+ _INST(hitEOF) = true;
RETURN ( nil );
}
}
@@ -1681,6 +1779,11 @@
nextPutLine:aString
"write the characters in aString and append a newline"
+"
+ self nextPutAll:aString.
+ self cr.
+ ^ self
+"
%{ /* NOCONTEXT */
FILE *f;
@@ -1749,10 +1852,12 @@
!
nextPutLinesFrom:aStream upToLineStartingWith:aStringOrNil
- "used to copy large files
+ "used to copy/create large files
- read from aStream up to and including a line starting with aStringOrNil
- and append it to self. If aStringOrNil is nil or not matched,
- copy preceeds to the end"
+ and append it to self.
+ If aStringOrNil is nil or not matched, copy preceeds to the end.
+ (this allows for example to read a Socket and transfer the data quickly
+ into a file - without creating zillions of temporary strings)"
|srcFilePointer|
@@ -1762,6 +1867,7 @@
srcFilePointer isNil ifTrue:[^ aStream errorNotOpen].
%{ /* STACK:2000 */
+
FILE *dst, *src;
char *matchString;
int matchLen = 0;
@@ -1900,6 +2006,9 @@
FILE *f;
+ if (_INST(hitEOF) == true) {
+ RETURN (true);
+ }
if (_INST(filePointer) != nil) {
f = MKFD(_INST(filePointer));
RETURN ( feof(f) ? true : false );
@@ -1982,7 +2091,11 @@
_INST(position) = nil;
RETURN ( true );
}
- ungetc(c, f);
+ if (c == EOF) {
+ _INST(hitEOF) = true;
+ } else {
+ ungetc(c, f);
+ }
RETURN ( false );
}
}
@@ -2025,6 +2138,7 @@
for (;;) {
c = getc(f);
if (c == EOF) {
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN (nil);
}
@@ -2065,6 +2179,7 @@
_immediateInterrupt = savInt;
RETURN ( self );
}
+ _INST(hitEOF) = true;
_immediateInterrupt = savInt;
RETURN ( nil );
}
@@ -2132,6 +2247,7 @@
c = getc(f);
_immediateInterrupt = savInt;
if (c < 0) {
+ _INST(hitEOF) = true;
RETURN ( nil );
}
switch (c) {
@@ -2183,6 +2299,7 @@
c = getc(f);
_immediateInterrupt = savInt;
if (c < 0) {
+ _INST(hitEOF) = true;
RETURN ( nil );
}
switch (c) {
@@ -2208,6 +2325,7 @@
"return the next chunk, i.e. all characters up to the next
non-doubled exclamation mark; undouble doubled exclamation marks.
- reimplemented for speed"
+
|retVal|
filePointer isNil ifTrue:[
@@ -2216,7 +2334,8 @@
binary ifTrue:[
^ self errorBinary
].
-%{
+
+%{ /* NXXOREGISTER */
FILE *f;
int done = 0;
REGISTER int c;
@@ -2255,6 +2374,7 @@
case EOF:
_immediateInterrupt = savInt;
+ _INST(hitEOF) = true;
RETURN ( nil );
default:
@@ -2301,7 +2421,10 @@
}
}
}
- if (c == EOF) break;
+ if (c == EOF) {
+ _INST(hitEOF) = true;
+ break;
+ }
buffer[index++] = c;
}
_immediateInterrupt = savInt;