ExtStream.st
changeset 22 847106305963
parent 12 8e03bd717355
child 23 643f36fb4afe
--- 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;