ExternalStream.st
changeset 77 6c38ca59927f
parent 68 59faa75185ba
child 85 1343af456e28
--- a/ExternalStream.st	Thu May 12 04:07:15 1994 +0200
+++ b/ExternalStream.st	Tue May 17 12:09:46 1994 +0200
@@ -22,7 +22,7 @@
 COPYRIGHT (c) 1988 by Claus Gittinger
               All Rights Reserved
 
-$Header: /cvs/stx/stx/libbasic/ExternalStream.st,v 1.15 1994-03-30 09:31:24 claus Exp $
+$Header: /cvs/stx/stx/libbasic/ExternalStream.st,v 1.16 1994-05-17 10:07:23 claus Exp $
 
 written 88 by claus
 '!
@@ -35,7 +35,6 @@
 #ifdef hpux
 # define fileno(f)      ((f->__fileH << 8) | (f->__fileL))
 #endif
-
 %}
 
 !ExternalStream class methodsFor:'documentation'!
@@ -45,6 +44,7 @@
     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 
@@ -54,11 +54,14 @@
     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)
+    The underlying OperatingSystem streams may either be closed 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).
+    object AND a close will be performed automatically (but you will NOT know when this 
+    happens - so it is recommended, that you close your files when no longer needed).
+    Closing is also suggested since if smalltalk is finished (be it by purpose, or due to
+    some crash) the data will not be in the file, if unclosed. 
+    All streams understand the close message, so it never hurts to use 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.
@@ -66,6 +69,8 @@
     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.
+    For streams sitting on some communication channel (i.e. Pipes and Sockets) you should
+    reestablish the stream upon image restart (make someone dependent on ObjectMemory).
 "
 ! !
 
@@ -486,8 +491,8 @@
 
 nextByte
     "read the next byte and return it as an Integer; return nil on error.
-     This is allowed in both text and binary modes, always returning a 
-     byte binary value."
+     This is allowed in both text and binary modes, always returning the
+     bytes binary value as an integer in 0..255."
 
 %{  /* NOCONTEXT */
 
@@ -529,6 +534,17 @@
     self errorWriteOnly
 !
 
+nextBytesInto:anObject
+    "read bytes into an object; the number of bytes read is defined
+     by the objects size.
+     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"
+
+    ^ self nextBytes:(anObject size) into:anObject startingAt:1
+!
+
 nextBytes:count into:anObject
     "read the next count bytes into an object and return the number of
      bytes read or nil on error. The object must have non-pointer indexed
@@ -576,7 +592,7 @@
             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;
+                cp = (char *)_InstPtr(anObject) + nInstBytes + offs;
                 savInt = _immediateInterrupt;
                 _immediateInterrupt = 1;
                 do {
@@ -1038,9 +1054,20 @@
     self primitiveFailed
 !
 
+nextPutBytesFrom:anObject
+    "write bytes from an object; the number of bytes is defined by
+     the objects size.
+     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"
+
+    ^ self nextPutBytes:(anObject size) from:anObject startingAt:1
+!
+
 nextPutBytes:count from:anObject
     "write count bytes from an object.
-     return the number of bytes written or nil on error.
+     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"
@@ -1089,7 +1116,7 @@
             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;
+                cp = (char *)_InstPtr(anObject) + nInstBytes + offs;
                 savInt = _immediateInterrupt;
                 _immediateInterrupt = 1;
 #ifdef OLD
@@ -1127,33 +1154,6 @@
     self primitiveFailed
 !
 
-nextPutWord:aNumber
-    "write the argument, aNumber as a signed short (two bytes);
-     write msb-first for compatibility with other smalltalks.
-     I dont know if it should be named nextPutWord: or nextWordPut:;
-     one of them will vanish ..."
-
-    ^ self nextShortPut:aNumber MSB:true
-!
-
-nextWordPut:aNumber
-    "for compatibility - this will vanish"
-
-    ^ self nextPutShort:aNumber MSB:true
-!
-
-nextShortPut:aNumber MSB:msbFlag
-    "for compatibility - this will vanish"
-
-    ^ self nextPutShort:aNumber MSB:msbFlag
-!
-
-nextLongPut:aNumber MSB:msbFlag
-    "for compatibility - this will vanish"
-
-    ^ self nextPutLong:aNumber MSB:msbFlag
-!
-
 nextPutShort:aNumber MSB:msbFlag
     "Write the argument, aNumber as a short (two bytes). If msbFlag is
      true, data is written most-significant byte first; otherwise least
@@ -1284,12 +1284,12 @@
     aNumber isInteger ifTrue:[
         msbFlag ifTrue:[
             "high word first"
-            (self nextShortPut:(aNumber // 16r10000) MSB:true) idNil ifTrue:[^ nil].
-            ^ self nextShortPut:(aNumber // 16r10000) MSB:true
+            (self nextShortPut:(aNumber // 16r10000) MSB:true) isNil ifTrue:[^ nil].
+            ^ self nextShortPut:(aNumber \\ 16r10000) MSB:true
         ].
         "low word first"
-        (self nextShortPut:(aNumber // 16r10000) MSB:false) isNil ifTrue:[^ nil].
-        ^ self nextShortPut:(aNumber \\ 16r10000) MSB:false.
+        (self nextShortPut:(aNumber \\ 16r10000) MSB:false) isNil ifTrue:[^ nil].
+        ^ self nextShortPut:(aNumber // 16r10000) MSB:false.
     ].
     self argumentMustBeInteger
 ! !
@@ -1525,6 +1525,16 @@
                 if (__isByteArray(aCollection)) {
                     cp = _ByteArrayInstPtr(aCollection)->ba_element;
                     len = _byteArraySize(aCollection);
+                } else {
+                    if (__isBytes(aCollection)) {
+                        int nInst;
+
+                        cp = _ByteArrayInstPtr(aCollection)->ba_element;
+                        len = _byteArraySize(aCollection);
+                        nInst = _intVal(_ClassInstPtr(_qClass(aCollection))->c_ninstvars);
+                        cp += nInst * sizeof(OBJ);
+                        len -= nInst * sizeof(OBJ);
+                    }
                 }
             }
         }
@@ -1592,6 +1602,16 @@
                 if (__isByteArray(aCollection)) {
                     cp = _ByteArrayInstPtr(aCollection)->ba_element;
                     len = _byteArraySize(aCollection);
+                } else {
+                    if (__isBytes(aCollection)) {
+                        int nInst;
+
+                        cp = _ByteArrayInstPtr(aCollection)->ba_element;
+                        len = _byteArraySize(aCollection);
+                        nInst = _intVal(_ClassInstPtr(_qClass(aCollection))->c_ninstvars);
+                        cp += nInst * sizeof(OBJ);
+                        len -= nInst * sizeof(OBJ);
+                    }
                 }
             }
             if (cp != NULL) {
@@ -2015,13 +2035,24 @@
 %{  /* NOCONTEXT */
 
     FILE *f;
+    OBJ t;
+    int c;
 
     if (_INST(hitEOF) == true) {
         RETURN (true);
     }
-    if (_INST(filePointer) != nil) {
-        f = MKFD(_INST(filePointer));
+    if ((t = _INST(filePointer)) != nil) {
+        f = MKFD(t);
+#ifdef OLD
         RETURN ( feof(f) ? true : false );
+#else
+        c = getc(f);
+        if (c == EOF) {
+            RETURN (true);
+        }
+        ungetc(c, f);
+        RETURN (false);
+#endif
     }
 %}
 .
@@ -2201,34 +2232,137 @@
     self errorWriteOnly
 !
 
+skipThroughAll:aString
+    "search & skip for the sequence given by the argument, aCollection;
+     return nil if not found, self otherwise. If successfull, the next read 
+     will return the character after the searchstring."
+
+    |buffer len first|
+
+    (aString isString and:[binary not]) ifTrue:[
+        len := aString size.
+        first := aString at:1.
+        buffer := String new:len.
+        buffer at:1 put:first.
+        len := len - 1.
+        [true] whileTrue:[
+            (self skipThrough:first) isNil ifTrue:[
+                ^ nil.
+            ].
+            (self nextBytes:len into:buffer startingAt:2) == len ifFalse:[
+                ^ nil
+            ].
+            buffer = aString ifTrue:[
+                "
+                 position back, before string
+                "
+                ^ self
+            ].
+        ].
+        "NOT REACHED"
+    ].
+    ^ super skipThroughAll:aString
+
+    "
+     |s|
+     s := 'Makefile' asFilename readStream.
+     s skipThroughAll:'are'.
+     s next:10
+    "
+!
+
 skipToAll:aString
     "skip for the sequence given by the argument, aCollection;
      return nil if not found, self otherwise. On a successful match, next read
      will return characters of aString."
 
-    |oldPos buffer l first idx|
+    |oldPos|
+
+    oldPos := self position.
+    (self skipThroughAll:aString) isNil ifTrue:[
+        "
+         restore position
+        "
+        self position:oldPos.
+        ^ nil
+    ].
+    "
+     position before match-string
+    "
+    self position:(self position - aString size).
+    ^ self
+
+    "
+     |s|
+     s := 'Makefile' asFilename readStream.
+     s skipToAll:'are'.
+     s next:10
+    "
+!
+
+skipThrough:aCharacter
+    "skip all characters up-to and including aCharacter. Return the receiver if
+     skip was successfull, otherwise (i.e. if not found) return nil.
+     The next read operation will return the character after aCharacter.
+     The argument, aCharacter must be character, or integer wehn in binary mode."
+
+%{  /* NOCONTEXT */
+
+    FILE *f;
+    REGISTER int c, cSearch;
+    extern int _immediateInterrupt;
+    int savInt;
 
-    (aString isKindOf:String) ifTrue:[
-        oldPos := self position.
-        l := aString size.
-        first := aString at:1.
-        buffer := String new:l.
-        [true] whileTrue:[
-            (self nextBytes:l into:buffer) == l ifFalse:[
-                self position:oldPos.
-                ^ nil
-            ].
-            buffer = aString ifTrue:[
-                self position:(self position - l).
-                ^ self
-            ].
-            idx := buffer indexOf:first startingAt:2.
-            idx == 0 ifFalse:[
-                self position:(self position - l + idx - 1)
-            ]
-        ]
-    ].
-    ^ super skipFor:aString
+    if ((_INST(filePointer) != nil) && (_INST(mode) != _writeonly)) {
+        if (_INST(binary) == true) {
+            /* searched for object must be a smallInteger */
+            if (! _isSmallInteger(aCharacter)) goto badArgument;
+            cSearch = _intVal(aCharacter);
+        } else {
+            /* searched for object must be a character */
+            if (! __isCharacter(aCharacter)) goto badArgument;
+            cSearch = _intVal(_characterVal(aCharacter));
+        }
+        /* Q: should we just say: "not found" ? */
+        if ((cSearch < 0) || (cSearch > 255)) goto badArgument;
+
+        f = MKFD(_INST(filePointer));
+        if (_INST(mode) == _readwrite)
+            fseek(f, 0L, 1); /* needed in stdio */
+        while (1) {
+            if (feof(f)) {
+                RETURN ( nil );
+            }
+            savInt = _immediateInterrupt;
+            _immediateInterrupt = 1;
+            c = getc(f);
+            _immediateInterrupt = savInt;
+            if (c < 0) {
+                _INST(hitEOF) = true;
+                RETURN ( nil );
+            }
+            if (c == cSearch) {
+                RETURN (self);
+            }
+        }
+    }
+badArgument: ;
+%}
+.
+    filePointer isNil ifTrue:[^ self errorNotOpen].
+    (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+    "
+     argument must be integer/character in binary mode, 
+     character in text mode
+    "
+    ^ self error:'invalid argument'.
+
+    "
+     |s|
+     s := 'Makefile' asFilename readStream.
+     s skipThrough:$=.
+     s next:10
+    "
 !
 
 skipSeparators