--- a/ExtStream.st Thu Nov 23 02:32:01 1995 +0100
+++ b/ExtStream.st Thu Nov 23 02:48:32 1995 +0100
@@ -11,17 +11,16 @@
"
ReadWriteStream subclass:#ExternalStream
- instanceVariableNames:'filePointer mode buffered binary useCRLF hitEOF didWrite lastErrorNumber'
- classVariableNames:'Lobby LastErrorNumber
- ReadErrorSignal WriteErrorSignal
- InvalidReadSignal InvalidWriteSignal InvalidModeSignal
- OpenErrorSignal StreamNotOpenSignal'
- poolDictionaries:''
- category:'Streams-External'
+ instanceVariableNames:'filePointer mode buffered binary useCRLF hitEOF didWrite
+ lastErrorNumber'
+ classVariableNames:'Lobby LastErrorNumber ReadErrorSignal WriteErrorSignal
+ InvalidReadSignal InvalidWriteSignal InvalidModeSignal
+ OpenErrorSignal StreamNotOpenSignal'
+ poolDictionaries:''
+ category:'Streams-External'
!
!ExternalStream primitiveDefinitions!
-
%{
#include <stdio.h>
#define _STDIO_H_INCLUDED_
@@ -82,10 +81,6 @@
"
!
-version
- ^ '$Header: /cvs/stx/stx/libbasic/Attic/ExtStream.st,v 1.69 1995-11-22 15:08:34 cg Exp $'
-!
-
documentation
"
ExternalStream defines protocol common to Streams which have a file-descriptor and
@@ -179,6 +174,10 @@
Two macros (__READING__ and __WRITING__) have been defined to be used before every
fread/fgetc and fwrite/putc respectively.
"
+!
+
+version
+ ^ '$Header: /cvs/stx/stx/libbasic/Attic/ExtStream.st,v 1.70 1995-11-23 01:48:32 cg Exp $'
! !
!ExternalStream class methodsFor:'initialization'!
@@ -238,18 +237,23 @@
]
! !
+!ExternalStream class methodsFor:'instance creation'!
+
+new
+ |newStream|
+
+ newStream := self basicNew.
+ newStream text; buffered:true; useCRLF:false; clearEOF.
+ ^ newStream
+! !
+
!ExternalStream class methodsFor:'Signal constants'!
-readErrorSignal
- "return the signal raised on read errors"
-
- ^ ReadErrorSignal
-!
-
-writeErrorSignal
- "return the signal raised on write errors"
-
- ^ WriteErrorSignal
+invalidModeSignal
+ "return the signal raised when doing text-I/O with a binary stream
+ or binary-I/O with a text stream"
+
+ ^ InvalidModeSignal
!
invalidReadSignal
@@ -264,11 +268,16 @@
^ InvalidWriteSignal
!
-invalidModeSignal
- "return the signal raised when doing text-I/O with a binary stream
- or binary-I/O with a text stream"
-
- ^ InvalidModeSignal
+openErrorSignal
+ "return the signal raised when a file open failed"
+
+ ^ OpenErrorSignal
+!
+
+readErrorSignal
+ "return the signal raised on read errors"
+
+ ^ ReadErrorSignal
!
streamNotOpenSignal
@@ -277,20 +286,10 @@
^ StreamNotOpenSignal
!
-openErrorSignal
- "return the signal raised when a file open failed"
-
- ^ OpenErrorSignal
-! !
-
-!ExternalStream class methodsFor:'instance creation'!
-
-new
- |newStream|
-
- newStream := self basicNew.
- newStream text; buffered:true; useCRLF:false; clearEOF.
- ^ newStream
+writeErrorSignal
+ "return the signal raised on write errors"
+
+ ^ WriteErrorSignal
! !
!ExternalStream class methodsFor:'error handling'!
@@ -315,278 +314,12 @@
"
! !
-!ExternalStream methodsFor:'instance release'!
-
-shallowCopyForFinalization
- "return a copy for finalization-registration;
- since all we need at finalization time is the fileDescriptor,
- a cheaper copy is possible."
-
- ^ self class basicNew setFilePointer:filePointer
-!
-
-setFilePointer:anInteger
- filePointer := anInteger
-!
-
-disposed
- "some Stream has been collected - close the file if not already done"
-
- self closeFile
-!
-
-shutDown
- "close the stream - added for protocol compatibility with PipeStream.
- see comment there"
-
- self closeFile
-!
-
-closeFile
- "low level close - may be redefined in subclasses"
-
-%{ /* NOCONTEXT */
-
- OBJ fp;
-
- if ((fp = _INST(filePointer)) != nil) {
- _INST(filePointer) = nil;
- __BEGIN_INTERRUPTABLE__
- fclose(__FILEVal(fp));
- __END_INTERRUPTABLE__
- }
-%}
-! !
-
-!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"
-
- self class name errorPrint. ': cannot reOpen stream - stream closed' errorPrintNL.
- filePointer := nil.
- Lobby unregister:self.
-!
-
-setLastError:aNumber
- lastErrorNumber := aNumber
-! !
-
-!ExternalStream methodsFor:'error handling'!
-
-lastErrorNumber
- "return the last error"
-
- ^ lastErrorNumber
-!
-
-lastErrorString
- "return a message string describing the last error"
-
- (lastErrorNumber isNil or:[lastErrorNumber == 0]) ifTrue:[
- ^ 'I/O error'
- ].
- ^ OperatingSystem errorTextForNumber:lastErrorNumber
-!
-
-errorOpen
- "report an error, that the stream is already opened"
-
- ^ OpenErrorSignal
- raiseRequestWith:self
- errorString:(self class name , ' is already open')
- in:thisContext sender
-!
-
-openError
- "report an error, that the open failed"
-
- ^ OpenErrorSignal
- raiseRequestWith:self
- errorString:('error on open: ' , self lastErrorString)
- in:thisContext sender
-!
-
-errorNotOpen
- "report an error, that the stream has not been opened"
-
- ^ StreamNotOpenSignal
- raiseRequestWith:self
- errorString:(self class name , ' not open')
- in:thisContext sender
-!
-
-errorReadOnly
- "report an error, that the stream is a readOnly stream"
-
- ^ InvalidWriteSignal
- raiseRequestWith:self
- errorString:(self class name , ' is readonly')
- in:thisContext sender
-!
-
-errorWriteOnly
- "report an error, that the stream is a writeOnly stream"
-
- ^ InvalidReadSignal
- raiseRequestWith:self
- errorString:(self class name , ' is writeonly')
- in:thisContext sender
-!
-
-errorNotBinary
- "report an error, that the stream is not in binary mode"
-
- ^ InvalidModeSignal
- raiseRequestWith:self
- errorString:(self class name , ' is not in binary mode')
- in:thisContext sender
-!
-
-errorBinary
- "report an error, that the stream is in binary mode"
-
- ^ InvalidModeSignal
- raiseRequestWith:self
- errorString:(self class name , ' is in binary mode')
- in:thisContext sender
-!
-
-errorNotBuffered
- "report an error, that the stream is not in buffered mode"
-
- ^ StreamErrorSignal
- raiseRequestWith:self
- errorString:(self class name , ' is unbuffered - operation not allowed')
- in:thisContext sender
-!
-
-ioError
- "report an error, that some I/O error occured"
-
- ^ StreamErrorSignal
- raiseRequestWith:self
- errorString:('I/O error: ' , self lastErrorString)
- in:thisContext sender
-!
-
-readError
- "report an error, that some read error occured"
-
- ^ ReadErrorSignal
- raiseRequestWith:self
- errorString:('read error: ' , self lastErrorString)
- in:thisContext sender
-!
-
-writeError
- "report an error, that some write error occured"
-
- ^ WriteErrorSignal
- raiseRequestWith:self
- errorString:('write error: ' , self lastErrorString)
- in:thisContext sender
-!
-
-argumentMustBeInteger
- "report an error, that the argument must be an integer"
-
- ^ self error:'argument must be an integer'
-!
-
-argumentMustBeCharacter
- "report an error, that the argument must be a character"
-
- ^ self error:'argument must be a character'
-!
-
-argumentMustBeString
- "report an error, that the argument must be a string"
-
- ^ self error:'argument must be a string'
-! !
-
-!ExternalStream methodsFor:'queries'!
-
-isExternalStream
- "return true, if the receiver is some kind of externalStream;
- true is returned here - the method redefined from Object."
-
- ^ true
-!
-
-isReadable
- "return true, if this stream can be read from"
-
- ^ (mode ~~ #writeonly)
-!
-
-isWritable
- "return true, if this stream can be written to"
-
- ^ (mode ~~ #readonly)
-!
-
-isBinary
- "return true, if the stream is in binary (as opposed to text-) mode.
- The default when created is false."
-
- ^ binary
-! !
-
!ExternalStream methodsFor:'accessing'!
-readonly
- "set access mode to readonly"
-
- mode := #readonly
-!
-
-writeonly
- "set access mode to writeonly"
-
- mode := #writeonly
-!
-
-readwrite
- "set access mode to readwrite"
-
- mode := #readwrite
-!
-
-filePointer
- "return the filePointer of the receiver -
- notice: for portability stdio is used; this means you will get
- a FILE * - not a fileDescriptor.
- (what you really get is a corresponding integer).
- You cannot do much with the returned value
- - except passing it to a primitive, for example."
-
- ^ filePointer
-!
-
-fileDescriptor
- "return the fileDescriptor of the receiver -
- notice: this one returns the underlying OSs fileDescriptor -
- this may not be available on all platforms (i.e. non unix systems)."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- OBJ fp;
-
- if ((fp = _INST(filePointer)) != nil) {
- f = __FILEVal(fp);
- RETURN ( __MKSMALLINT(fileno(f)) );
- }
-%}
-.
- ^ self errorNotOpen
+binary
+ "switch to binary mode - default is text"
+
+ binary := true
!
buffered:aBoolean
@@ -595,40 +328,6 @@
buffered := aBoolean
!
-useCRLF:aBoolean
- "turn on or off CRLF sending (instead of LF only) - default is off"
-
- useCRLF := aBoolean
-!
-
-binary
- "switch to binary mode - default is text"
-
- binary := true
-!
-
-text
- "switch to text mode - default is text"
-
- binary := false
-!
-
-contentsSpecies
- "return the kind of object to be returned by sub-collection builders
- (such as upTo)"
-
- binary ifTrue:[
- ^ ByteArray
- ].
- ^ String
-!
-
-contentsOfEntireFile
- "ST-80 compatibility"
-
- ^ self contents
-!
-
contents
"return the contents of the file from the current position up-to
the end. If the stream is in binary mode, a ByteArray containing
@@ -677,1422 +376,259 @@
text add:l
].
^ text
-! !
-
-!ExternalStream methodsFor:'misc functions'!
-
-open
- "open the stream
- - this must be redefined in subclass"
-
- ^ self subclassResponsibility
-!
-
-close
- "close the stream - tell operating system"
-
- filePointer isNil ifTrue:[^ self].
- Lobby unregister:self.
- self closeFile.
- filePointer := nil
-!
-
-create
- "create the stream
- - this must be redefined in subclass"
-
- ^ self subclassResponsibility
-!
-
-position
- "return the position
- - this must be redefined in subclass"
-
- ^ self subclassResponsibility
-!
-
-position:anInteger
- "set the position
- - this must be redefined in subclass"
-
- ^ self subclassResponsibility
-!
-
-backStep
- "step back one element -
- redefined, since position is redefined here"
-
- self position:(self position - 1)
-!
-
-reset
- "set the read position to the beginning of the collection"
-
- self position:"0" 1
-!
-
-setToEnd
- "redefined since it must be implemented differently"
-
- ^ self subclassResponsibility
-!
-
-blocking:aBoolean
- "set/clear the blocking attribute - if set (which is the default)
- a read (using next) on the receiver will block until data is available.
- If cleared, a read operation will immediately return with a value of
- nil."
-
- filePointer isNil ifTrue:[^ self errorNotOpen].
- ^ OperatingSystem setBlocking:aBoolean on:(self fileDescriptor)
-!
-
-async:aBoolean
- "set/clear the async attribute - if set the availability of data on
- the receiver will trigger an ioInterrupt.
- If cleared (which is the default) no special notification is made."
-
- |fd|
-
- filePointer isNil ifTrue:[^ self errorNotOpen].
- fd := self fileDescriptor.
- aBoolean ifTrue:[
- ^ OperatingSystem enableIOInterruptsOn:fd
- ].
- ^ OperatingSystem disableIOInterruptsOn:fd
-!
-
-ioctl:ioctlNumber
- "to provide a simple ioctl facility - an ioctl is performed
- on the underlying file; no arguments are passed."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- int ret, ioNum, ioArg;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if ((fp = _INST(filePointer)) != nil) {
- if (__isSmallInteger(ioctlNumber)) {
- ioNum = __intVal(ioctlNumber);
- f = __FILEVal(fp);
-
- __BEGIN_INTERRUPTABLE__
- do {
- ret = ioctl(fileno(f), ioNum);
- } while ((ret < 0) && (errno == EINTR));
- __END_INTERRUPTABLE__
-
- if (ret >= 0) {
- RETURN ( __MKSMALLINT(ret) );
- }
- _INST(position) = nil;
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self ioError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- "
- ioctl-number is not an integer
- "
- ^ self primitiveFailed
-!
-
-ioctl:ioctlNumber with:arg
- "to provide a simple ioctl facility - an ioctl is performed
- on the underlying file; the argument is passed as argument.
- If the argument is a number, its directly passed; if its a
- kind of ByteArray (ByteArray, String or Structure) a pointer to
- the data is passed. This allows performing most ioctls - however,
- it might be tricky to setup the buffer."
-
-%{ /* NOCONTEXT */
- FILE *f;
- int ret, ioNum;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if ((fp = _INST(filePointer)) != nil) {
- if (__isSmallInteger(ioctlNumber)
- && (__isSmallInteger(arg) || __isBytes(arg))) {
- f = __FILEVal(fp);
- ioNum = __intVal(ioctlNumber);
-
- __BEGIN_INTERRUPTABLE__
- do {
- if (__isSmallInteger(arg)) {
- ret = ioctl(fileno(f), ioNum, __intVal(arg));
- } else {
- ret = ioctl(fileno(f), ioNum, __ByteArrayInstPtr(arg)->ba_element);
- }
- } while ((ret < 0) && (errno == EINTR));
- __END_INTERRUPTABLE__
-
- if (ret >= 0) {
- RETURN ( __MKSMALLINT(ret) );
- }
- _INST(position) = nil;
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self ioError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- "
- ioctl-number is not an integer or argument is not byteArray-like
- "
- ^ self primitiveFailed
-! !
-
-!ExternalStream methodsFor:'non homogenous reading'!
-
-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 the
- bytes binary value as an integer in 0..255."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- unsigned char byte;
- int cnt;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- f = __FILEVal(fp);
-
- if (_INST(buffered) == true) {
- __READING__(f)
- }
-
- __BEGIN_INTERRUPTABLE__
- do {
- if (_INST(buffered) == false) {
- cnt = read(fileno(f), &byte, 1);
- } else {
- cnt = fread(&byte, 1, 1, f);
- }
- } while ((cnt < 0) && (errno == EINTR));
- __END_INTERRUPTABLE__
-
- if (cnt == 1) {
- if (_INST(position) != nil)
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 1);
- RETURN ( __MKSMALLINT(byte) );
- }
- if (cnt == 0) {
- _INST(hitEOF) = true;
-/*
- if (errno == EWOULDBLOCK) {
- RETURN (nil);
- }
-*/
- }
- _INST(position) = nil;
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- ^ self errorWriteOnly
-!
-
-nextBytesInto:anObject
- "read bytes into an object, regardless of binary/text mode.
- The number of bytes to read is defined by the objects size.
- Return the number of bytes read. On EOF, 0 is returned.
- If the receiver is some socket/pipe-like stream, an exception
- is raised if the connection is broken.
-
- The object to read into must have non-pointer indexed instvars
- (i.e. it must be a ByteArray, String, Float- or DoubleArray).
- If anObject is a string or byteArray and reused, this provides the
- fastest possible physical I/O (since no new objects are allocated).
-
- Use with care - non object oriented i/o.
- Warning: in general, you cannot use this method to pass data from other
- architectures since it does not care for byte order or float representation."
-
- ^ self nextBytes:(anObject size) into:anObject startingAt:1
-
- " to read 100 bytes from a stream:
-
- |b aStream|
-
- aStream := 'smalltalk.rc' asFilename readStream.
- b := ByteArray new:100.
- aStream nextBytesInto:b.
- aStream close.
- b inspect
- "
-
- "
- |s aStream|
- aStream := 'smalltalk.rc' asFilename readStream.
- s := String new:100.
- aStream nextBytesInto:s.
- aStream close.
- s inspect
- "
-!
-
-nextBytes:count into:anObject
- "read the next count bytes into an object and return the number of
- bytes read. On EOF, 0 is returned.
- If the receiver is some socket/pipe-like stream, an exception
- is raised if the connection is broken.
-
- The object must have non-pointer indexed instvars (i.e. it must be
- a ByteArray, String, Float- or DoubleArray).
- If anObject is a string or byteArray and reused, this provides the
- fastest possible physical I/O (since no new objects are allocated).
-
- Use with care - non object oriented i/o.
- Warning: in general, you cannot use this method to pass data from other
- architectures since it does not care for byte order or float representation."
-
- ^ 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 0 on EOF.
- If the receiver is some socket/pipe-like stream, an exception
- is raised if the connection is broken.
- Notice, that in contrast to other methods
- here, this does NOT return nil on EOF, but the actual count.
- Thus allowing read of partial blocks.
-
- The object must have non-pointer indexed instvars
- (i.e. it must be a ByteArray, String, Float- or DoubleArray).
- If anObject is a string or byteArray and reused, this provides the
- fastest possible physical I/O (since no new objects are allocated).
-
- Use with care - non object oriented I/O.
- Warning: in general, you cannot use this method to pass data from other
- architectures since it does not care for byte order or float representation."
-
-%{
- FILE *f;
- int cnt, offs, ret;
- int objSize, nInstVars, nInstBytes;
- char *cp;
- OBJ pos, fp, oClass;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- if (__bothSmallInteger(count, start)) {
- f = __FILEVal(fp);
-
- 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;
- nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
- nInstBytes = OHDR_SIZE + __OBJS2BYTES__(nInstVars);
- objSize = __Size(anObject) - nInstBytes;
- if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
- /*
- * mhmh - since we are interruptable, anObject may move.
- * therefore, fetch the cp-pointer within the loop
- */
- if (_INST(buffered) == true) {
- __READING__(f)
- }
-
- __BEGIN_INTERRUPTABLE__
- do {
- errno = 0;
- /*
- * because we are interruptable, refetch pointer
- */
- cp = (char *)__InstPtr(anObject) + nInstBytes + offs;
- if (_INST(buffered) == false) {
- ret = read(fileno(f), cp, cnt);
- } else {
- if (feof(f)) {
- ret = 0;
- break;
- }
- ret = fread(cp, 1, cnt, f);
- }
- } while ((ret < 0) && (errno == EINTR));
-
- __END_INTERRUPTABLE__
- cnt = ret;
-
- if (cnt > 0) {
- pos = _INST(position);
- if (pos != nil) {
- _INST(position) = __MKSMALLINT(__intVal(pos) + cnt);
- }
- RETURN (__MKSMALLINT(cnt));
- }
- if (cnt == 0) {
- _INST(hitEOF) = true;
- RETURN (__MKSMALLINT(cnt));
- }
-
- _INST(position) = nil;
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
- }
-bad: ;
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- "
- count not integer or arg not bit-like (String, ByteArray etc)
- "
- ^ self primitiveFailed
-!
-
-nextShortMSB:msbFlag
- "Read two bytes and return the value as a 16-bit signed Integer.
- If msbFlag is true, value is read with most-significant byte first,
- otherwise least-significant byte comes first.
- A nil is returned if EOF is reached (also when EOF is hit after the first byte).
- Works in both binary and text modes."
-
-%{ /* NOCONTEXT */
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- FILE *f;
- int first, second, err;
- short value;
-
- f = __FILEVal(fp);
- __BEGIN_INTERRUPTABLE__
- __READING__(f)
-
- do {
- first = getc(f);
- } while ((first < 0) && (errno == EINTR));
-
- if (first != EOF) {
- do {
- second = getc(f);
- } while ((second < 0) && (errno == EINTR));
-
- __END_INTERRUPTABLE__
- if (second != EOF) {
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 2);
- }
- if (msbFlag == true) {
- value = ((first & 0xFF) << 8) | (second & 0xFF);
- } else {
- value = ((second & 0xFF) << 8) | (first & 0xFF);
- }
- RETURN (__MKSMALLINT(value));
- }
- }
- __END_INTERRUPTABLE__
-
- if (ferror(f) && (errno != 0)) {
- _INST(position) = nil;
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- } else {
- _INST(hitEOF) = true;
- RETURN (nil);
- }
- }
-%}.
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- ^ self readError.
+contentsOfEntireFile
+ "ST-80 compatibility"
+
+ ^ self contents
!
-nextUnsignedShortMSB:msbFlag
- "Read two bytes and return the value as a 16-bit unsigned Integer.
- If msbFlag is true, value is read with most-significant byte first, otherwise
- least-significant byte comes first.
- A nil is returned if EOF is reached (also when EOF is hit after the first byte).
- Works in both binary and text modes."
-
-%{ /* NOCONTEXT */
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- FILE *f;
- int first, second;
- unsigned value;
-
- f = __FILEVal(fp);
- __BEGIN_INTERRUPTABLE__
- __READING__(f)
-
- do {
- first = getc(f);
- } while ((first < 0) && (errno == EINTR));
-
- if (first != EOF) {
- do {
- second = getc(f);
- } while ((second < 0) && (errno == EINTR));
-
- __END_INTERRUPTABLE__
- if (second != EOF) {
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 2);
- }
- if (msbFlag == true) {
- value = ((first & 0xFF) << 8) | (second & 0xFF);
- } else {
- value = ((second & 0xFF) << 8) | (first & 0xFF);
- }
- RETURN (__MKSMALLINT(value));
- }
- }
- __END_INTERRUPTABLE__
-
- if (ferror(f) && (errno != 0)) {
- _INST(position) = nil;
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- } else {
- _INST(hitEOF) = true;
- RETURN (nil);
- }
- }
-%}.
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- ^ self readError.
-!
-
-nextLongMSB:msbFlag
- "Read four bytes and return the value as a 32-bit signed Integer,
- which may be a LargeInteger.
- If msbFlag is true, value is read with most-significant byte first,
- otherwise least-significant byte comes first.
- A nil is returned, if EOF is hit before all 4 bytes have been read.
- Works in both binary and text modes."
-
-%{ /* NOCONTEXT */
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- FILE *f;
- int first, second, third, fourth;
- int value;
-
- f = __FILEVal(fp);
- __BEGIN_INTERRUPTABLE__
- __READING__(f)
-
- do {
- first = getc(f);
- } while ((first < 0) && (errno == EINTR));
- if (first != EOF) {
- do {
- second = getc(f);
- } while ((second < 0) && (errno == EINTR));
- if (second != EOF) {
- do {
- third = getc(f);
- } while ((third < 0) && (errno == EINTR));
- if (third != EOF) {
- do {
- fourth = getc(f);
- } while ((fourth < 0) && (errno == EINTR));
- if (fourth != EOF) {
- __END_INTERRUPTABLE__
-
- if (msbFlag == true) {
- value = (first & 0xFF);
- value = (value<<8) | (second & 0xFF);
- value = (value<<8) | (third & 0xFF);
- value = (value<<8) | (fourth & 0xFF);
- } else {
- value = (fourth & 0xFF);
- value = (value<<8) | (third & 0xFF);
- value = (value<<8) | (second & 0xFF);
- value = (value<<8) | (first & 0xFF);
- }
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 4);
- }
- if ((value >= _MIN_INT) && (value <= _MAX_INT)) {
- RETURN ( __MKSMALLINT(value));
- }
- RETURN ( _MKLARGEINT(value) );
- }
- }
- }
- }
- __END_INTERRUPTABLE__
-
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- _INST(position) = nil;
- } else {
- _INST(hitEOF) = true;
- RETURN (nil);
- }
- }
-%}.
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- ^ self readError.
-!
-
-nextUnsignedLongMSB:msbFlag
- "Read four bytes and return the value as a 32-bit unsigned Integer, which may be
- a LargeInteger.
- If msbFlag is true, value is read with most-significant byte first, otherwise
- least-significant byte comes first.
- A nil is returned, if endOfFile occurs before all 4 bytes have been read.
- Works in both binary and text modes."
-
-%{ /* NOCONTEXT */
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- FILE *f;
- int first, second, third, fourth;
- unsigned int value;
-
- f = __FILEVal(fp);
- __BEGIN_INTERRUPTABLE__
- __READING__(f)
-
- do {
- first = getc(f);
- } while ((first < 0) && (errno == EINTR));
- if (first != EOF) {
- do {
- second = getc(f);
- } while ((second < 0) && (errno == EINTR));
- if (second != EOF) {
- do {
- third = getc(f);
- } while ((third < 0) && (errno == EINTR));
- if (third != EOF) {
- do {
- fourth = getc(f);
- } while ((fourth < 0) && (errno == EINTR));
- if (fourth != EOF) {
- __END_INTERRUPTABLE__
-
- if (msbFlag == true) {
- value = (first & 0xFF);
- value = (value<<8) | (second & 0xFF);
- value = (value<<8) | (third & 0xFF);
- value = (value<<8) | (fourth & 0xFF);
- } else {
- value = (fourth & 0xFF);
- value = (value<<8) | (third & 0xFF);
- value = (value<<8) | (second & 0xFF);
- value = (value<<8) | (first & 0xFF);
- }
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 4);
- }
- if (value <= _MAX_INT) {
- RETURN ( __MKSMALLINT(value));
- }
- RETURN ( _MKULARGEINT(value) );
- }
- }
- }
- }
- __END_INTERRUPTABLE__
-
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- _INST(position) = nil;
- } else {
- _INST(hitEOF) = true;
- RETURN (nil);
- }
- }
-%}.
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- ^ self readError.
-!
-
-nextWord
- "in text-mode:
- read the alphaNumeric next word (i.e. up to non letter-or-digit).
- return a string containing those characters.
- in binary-mode:
- read two bytes (msb-first) and return the value as a 16-bit
- unsigned Integer (for compatibility with other smalltalks)"
+contentsSpecies
+ "return the kind of object to be returned by sub-collection builders
+ (such as upTo)"
binary ifTrue:[
- ^ self nextUnsignedShortMSB:true
+ ^ ByteArray
].
- ^ self nextAlphaNumericWord
+ ^ String
!
-nextLong
- "Read four bytes (msb-first) and return the value as a 32-bit signed Integer.
- The returned value may be a LargeInteger.
- (msb-first for compatibility with other smalltalks)"
-
- ^ self nextUnsignedLongMSB:true
-! !
-
-!ExternalStream methodsFor:'non homogenous writing'!
-
-nextPutByte:aByteValue
- "write a byte.
- Works in both binary and text modes."
+fileDescriptor
+ "return the fileDescriptor of the receiver -
+ notice: this one returns the underlying OSs fileDescriptor -
+ this may not be available on all platforms (i.e. non unix systems)."
%{ /* NOCONTEXT */
FILE *f;
- char c;
- OBJ pos, fp;
- int cnt;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))) {
- if (__isSmallInteger(aByteValue)) {
- c = __intVal(aByteValue);
- f = __FILEVal(fp);
- __BEGIN_INTERRUPTABLE__
-/*
- *#ifdef OLD
- * if (_INST(buffered) == false) {
- * cnt = write(fileno(f), &c, 1);
- * } else
- *#endif
- * {
- * __WRITING__(f)
- * cnt = fwrite(&c, 1, 1, f);
- *#ifndef OLD
- * if (_INST(buffered) == false) {
- * fflush(f);
- * }
- *#endif
- * }
- */
- if (_INST(buffered) == true) {
- __WRITING__(f)
- cnt = fwrite(&c, 1, 1, f);
- } else {
- do {
- cnt = write(fileno(f), &c, 1);
- } while ((cnt < 0) && (errno == EINTR));
- }
- __END_INTERRUPTABLE__
-
- if (cnt == 1) {
- pos = _INST(position);
- if (pos != nil)
- _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
- RETURN (self);
- }
- if (cnt < 0) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
+ OBJ fp;
+
+ if ((fp = _INST(filePointer)) != nil) {
+ f = __FILEVal(fp);
+ RETURN ( __MKSMALLINT(fileno(f)) );
}
-%}.
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #readonly) ifTrue:[^ self errorReadOnly].
- ^ self writeError.
+%}
+.
+ ^ self errorNotOpen
+!
+
+filePointer
+ "return the filePointer of the receiver -
+ notice: for portability stdio is used; this means you will get
+ a FILE * - not a fileDescriptor.
+ (what you really get is a corresponding integer).
+ You cannot do much with the returned value
+ - except passing it to a primitive, for example."
+
+ ^ filePointer
+!
+
+readonly
+ "set access mode to readonly"
+
+ mode := #readonly
+!
+
+readwrite
+ "set access mode to readwrite"
+
+ mode := #readwrite
+!
+
+text
+ "switch to text mode - default is text"
+
+ binary := false
+!
+
+useCRLF:aBoolean
+ "turn on or off CRLF sending (instead of LF only) - default is off"
+
+ useCRLF := aBoolean
!
-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.
- Warning: in general, you cannot use this method to pass data to other
- architectures since it does not care for byte order or float representation."
-
- ^ self nextPutBytes:(anObject size) from:anObject startingAt:1
+writeonly
+ "set access mode to writeonly"
+
+ mode := #writeonly
+! !
+
+!ExternalStream methodsFor:'error handling'!
+
+argumentMustBeCharacter
+ "report an error, that the argument must be a character"
+
+ ^ self error:'argument must be a character'
+!
+
+argumentMustBeInteger
+ "report an error, that the argument must be an integer"
+
+ ^ self error:'argument must be an integer'
!
-nextPutBytes:count from:anObject
- "write count bytes from an object.
- 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.
- Warning: in general, you cannot use this method to pass data to other
- architectures since it does not care for byte order or float representation."
-
- ^ self nextPutBytes:count from:anObject startingAt:1
+argumentMustBeString
+ "report an error, that the argument must be a string"
+
+ ^ self error:'argument must be a string'
+!
+
+errorBinary
+ "report an error, that the stream is in binary mode"
+
+ ^ InvalidModeSignal
+ raiseRequestWith:self
+ errorString:(self class name , ' is in binary mode')
+ in:thisContext sender
+!
+
+errorNotBinary
+ "report an error, that the stream is not in binary mode"
+
+ ^ InvalidModeSignal
+ raiseRequestWith:self
+ errorString:(self class name , ' is not in binary mode')
+ in:thisContext sender
!
-nextPutBytes:count from:anObject startingAt:start
- "write count bytes from an object starting at index start.
- return the number of bytes written - which could be 0.
- 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.
- Warning: in general, you cannot use this method to pass data to other
- architectures since it does not care for byte order or float representation."
-
-%{ /* NOCONTEXT */
- FILE *f;
- int cnt, offs;
- int objSize, nInstVars, nInstBytes;
- char *cp;
- OBJ oClass, pos, fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))) {
- if (__bothSmallInteger(count, 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 = __FILEVal(fp);
-
- nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
- nInstBytes = OHDR_SIZE + __OBJS2BYTES__(nInstVars);
- objSize = __Size(anObject) - nInstBytes;
- if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
- cp = (char *)__InstPtr(anObject) + nInstBytes + offs;
- __BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == true) {
- __WRITING__(f)
- cnt = fwrite(cp, 1, cnt, f);
- } else {
- do {
- cp = (char *)__InstPtr(anObject) + nInstBytes + offs;
- cnt = write(fileno(f), cp, cnt);
- } while ((cnt < 0) && (errno == EINTR));
- }
-
- __END_INTERRUPTABLE__
-
- if (cnt >= 0) {
- pos = _INST(position);
- if (pos != nil)
- _INST(position) = __MKSMALLINT(__intVal(pos) + cnt);
- RETURN ( __MKSMALLINT(cnt) );
- }
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
- }
-bad: ;
-%}.
- lastErrorNumber notNil ifTrue:[^ self writeError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #readonly) ifTrue:[^ self errorReadOnly].
- ^ self primitiveFailed
+errorNotBuffered
+ "report an error, that the stream is not in buffered mode"
+
+ ^ StreamErrorSignal
+ raiseRequestWith:self
+ errorString:(self class name , ' is unbuffered - operation not allowed')
+ in:thisContext sender
+!
+
+errorNotOpen
+ "report an error, that the stream has not been opened"
+
+ ^ StreamNotOpenSignal
+ raiseRequestWith:self
+ errorString:(self class name , ' not open')
+ in:thisContext sender
+!
+
+errorOpen
+ "report an error, that the stream is already opened"
+
+ ^ OpenErrorSignal
+ raiseRequestWith:self
+ errorString:(self class name , ' is already open')
+ in:thisContext sender
+!
+
+errorReadOnly
+ "report an error, that the stream is a readOnly stream"
+
+ ^ InvalidWriteSignal
+ raiseRequestWith:self
+ errorString:(self class name , ' is readonly')
+ in:thisContext sender
+!
+
+errorWriteOnly
+ "report an error, that the stream is a writeOnly stream"
+
+ ^ InvalidReadSignal
+ raiseRequestWith:self
+ errorString:(self class name , ' is writeonly')
+ in:thisContext sender
!
-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
- first.
- Works in both binary and text modes."
-
-%{ /* NOCONTEXT */
-
- int num;
- char bytes[2];
- FILE *f;
- int cnt;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))
- && __isSmallInteger(aNumber)) {
- num = __intVal(aNumber);
- if (msbFlag == true) {
- bytes[0] = (num >> 8) & 0xFF;
- bytes[1] = num & 0xFF;
- } else {
- bytes[1] = (num >> 8) & 0xFF;
- bytes[0] = num & 0xFF;
- }
-
- f = __FILEVal(fp);
- __BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == true) {
- __WRITING__(f)
- cnt = fwrite(bytes, 1, 2, f);
- } else {
- do {
- cnt = write(fileno(f), bytes, 2);
- } while ((cnt < 0) && (errno == EINTR));
- }
-
- __END_INTERRUPTABLE__
-
- if (cnt == 2) {
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 2);
- }
- RETURN ( self );
- }
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self writeError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #readonly) ifTrue:[^ self errorReadOnly].
- self argumentMustBeInteger
+ioError
+ "report an error, that some I/O error occured"
+
+ ^ StreamErrorSignal
+ raiseRequestWith:self
+ errorString:('I/O error: ' , self lastErrorString)
+ in:thisContext sender
+!
+
+lastErrorNumber
+ "return the last error"
+
+ ^ lastErrorNumber
+!
+
+lastErrorString
+ "return a message string describing the last error"
+
+ (lastErrorNumber isNil or:[lastErrorNumber == 0]) ifTrue:[
+ ^ 'I/O error'
+ ].
+ ^ OperatingSystem errorTextForNumber:lastErrorNumber
!
-nextPutLong:aNumber MSB:msbFlag
- "Write the argument, aNumber as a long (four bytes). If msbFlag is
- true, data is written most-significant byte first; otherwise least
- first.
- Works in both binary and text modes."
+openError
+ "report an error, that the open failed"
+
+ ^ OpenErrorSignal
+ raiseRequestWith:self
+ errorString:('error on open: ' , self lastErrorString)
+ in:thisContext sender
+!
+
+readError
+ "report an error, that some read error occured"
+
+ ^ ReadErrorSignal
+ raiseRequestWith:self
+ errorString:('read error: ' , self lastErrorString)
+ in:thisContext sender
+!
+
+writeError
+ "report an error, that some write error occured"
+
+ ^ WriteErrorSignal
+ raiseRequestWith:self
+ errorString:('write error: ' , self lastErrorString)
+ in:thisContext sender
+! !
+
+!ExternalStream methodsFor:'instance release'!
+
+closeFile
+ "low level close - may be redefined in subclasses"
%{ /* NOCONTEXT */
- int num;
- char bytes[4];
- FILE *f;
- int cnt;
OBJ fp;
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))
- && __isSmallInteger(aNumber)) {
- num = __intVal(aNumber);
- if (msbFlag == true) {
- bytes[0] = (num >> 24) & 0xFF;
- bytes[1] = (num >> 16) & 0xFF;
- bytes[2] = (num >> 8) & 0xFF;
- bytes[3] = num & 0xFF;
- } else {
- bytes[3] = (num >> 24) & 0xFF;
- bytes[2] = (num >> 16) & 0xFF;
- bytes[1] = (num >> 8) & 0xFF;
- bytes[0] = num & 0xFF;
- }
-
- f = __FILEVal(fp);
+ if ((fp = _INST(filePointer)) != nil) {
+ _INST(filePointer) = nil;
__BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == true) {
- __WRITING__(f)
- cnt = fwrite(bytes, 1, 4, f);
- } else {
- do {
- cnt = write(fileno(f), bytes, 4);
- } while ((cnt < 0) && (errno == EINTR));
- }
-
+ fclose(__FILEVal(fp));
__END_INTERRUPTABLE__
-
- if (cnt == 4) {
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 4);
- }
- RETURN ( self );
- }
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
-%}.
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #readonly) ifTrue:[^ self errorReadOnly].
- lastErrorNumber notNil ifTrue:[^ self writeError].
-
- aNumber isInteger ifTrue:[
- msbFlag ifTrue:[
- "high word first"
- (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 argumentMustBeInteger
-! !
-
-!ExternalStream methodsFor:'reading'!
-
-peek
- "return the element to be read next without advancing read position.
- In binary mode, an integer is returned, otherwise a character.
- If there are no more elements, nil is returned.
- Not allowed in unbuffered mode."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- REGISTER int c;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
-#ifdef OLD
- if (_INST(buffered) == true)
-#endif
- {
- f = __FILEVal(fp);
-
- __BEGIN_INTERRUPTABLE__
- __READING__(f)
-
- do {
- c = getc(f);
- } while ((c < 0) && (errno == EINTR));
- __END_INTERRUPTABLE__
-
- if (c != EOF) {
- ungetc(c, f);
- if (_INST(binary) == true) {
- RETURN ( __MKSMALLINT(c & 0xFF) );
- }
- RETURN ( _MKCHARACTER(c & 0xFF) );
- }
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- } else {
- _INST(hitEOF) = true;
- RETURN ( nil );
- }
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- buffered ifFalse:[^ self errorNotBuffered].
- ^ self errorWriteOnly
-!
-
-next
- "return the next element; advance read position.
- In binary mode, an integer is returned, otherwise a character.
- If there are no more elements, nil is returned."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- int c;
- OBJ pos, fp;
- unsigned char ch;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- f = __FILEVal(fp);
- __BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == true) {
- __READING__(f)
- }
-
- do {
- errno = 0;
- if (_INST(buffered) == false) {
- if (read(fileno(f), &ch, 1) != 1)
- c = EOF;
- else
- c = ch;
- } else
- {
- c = getc(f);
- }
- } while ((c < 0) && (errno == EINTR));
-
- __END_INTERRUPTABLE__
-
- if (c != EOF) {
- pos = _INST(position);
- if (__isSmallInteger(pos)) {
- _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
- } else {
- _INST(position) = nil;
- }
- if (_INST(binary) == true) {
- RETURN ( __MKSMALLINT(c & 0xFF) );
- }
- RETURN ( _MKCHARACTER(c & 0xFF) );
- }
-/*
- if (errno == EWOULDBLOCK) {
- _INST(hitEOF) = true;
- RETURN(nil);
- }
-*/
- _INST(position) = nil;
-
- if ((_INST(buffered) == false)
- || (ferror(f) && (errno != 0))) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- } else {
- _INST(hitEOF) = true;
- RETURN ( nil );
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- self errorWriteOnly
-!
-
-next:count
- "return the next count elements of the stream as a collection.
- Redefined to return a String or ByteArray instead of the default: Array."
-
- |coll|
-
- binary ifTrue:[
- coll := ByteArray uninitializedNew:count
- ] ifFalse:[
- coll := String new:count
- ].
- "/
- "/ Q: should we use:
- "/ self nextBytes:count into:coll startingAt:1
- "/
- 1 to:count do: [:index |
- coll at:index put:(self next)
- ].
- ^ coll
-! !
-
-!ExternalStream methodsFor:'writing'!
-
-commit
- "write all buffered date - ignored if unbuffered"
-
- self synchronizeOutput
-!
-
-synchronizeOutput
- "write all buffered data - ignored if unbuffered"
-
-%{ /* NOCONTEXT */
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if ((fp = _INST(filePointer)) != nil) {
- if (_INST(mode) != @symbol(readonly)) {
- if (_INST(buffered) == true) {
- __BEGIN_INTERRUPTABLE__
- fflush( __FILEVal(fp) );
- __END_INTERRUPTABLE__
- }
- }
}
%}
!
-nextPut:aCharacter
- "write the argument, aCharacter - return nil if failed, self if ok.
- Only single-byte characters are currently supported"
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- char c;
- int cnt;
- OBJ pos, fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))) {
- if (_INST(binary) != true) {
- if (__isCharacter(aCharacter)) {
- c = __intVal(__characterVal(aCharacter)) & 0xFF;
- doWrite:
- f = __FILEVal(fp);
-
- __BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == true) {
- __WRITING__(f)
-
- do {
- cnt = fwrite(&c, 1, 1, f);
- } while ((cnt != 1) && (errno == EINTR));
- } else {
- do {
- cnt = write(fileno(f), &c, 1);
- } while ((cnt != 1) && (errno == EINTR));
- }
-
- __END_INTERRUPTABLE__
-
- if (cnt == 1) {
- pos = _INST(position);
- if (pos != nil) {
- _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
- }
- RETURN ( self );
- }
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- } else {
- if (__isSmallInteger(aCharacter)) {
- c = __intVal(aCharacter);
- goto doWrite;
- }
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self writeError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #readonly) ifTrue:[^ self errorReadOnly].
- binary ifFalse:[^ self argumentMustBeCharacter].
- ^ self argumentMustBeInteger.
+disposed
+ "some Stream has been collected - close the file if not already done"
+
+ self closeFile
+!
+
+setFilePointer:anInteger
+ filePointer := anInteger
!
-nextPutAll:aCollection
- "write all elements of the argument, aCollection.
- Reimplemented for speed when writing strings or byteArrays.
- For others, falls back to general method in superclass."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- unsigned char *cp;
- int len, cnt;
- OBJ pos, fp;
- int offs;
-
- _INST(lastErrorNumber) = nil;
-
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))) {
- cp = NULL;
- offs = 0;
- if (__isString(aCollection) || __isSymbol(aCollection)) {
- cp = _stringVal(aCollection);
- len = _stringSize(aCollection);
- } else {
- if (_INST(binary) == true) {
- 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);
- offs = __OBJS2BYTES__(nInst);
- cp += offs;
- len -= offs;
- }
- }
- }
- }
- if (cp != NULL) {
- f = __FILEVal(fp);
-
- __BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == false) {
- int cc, rest;
-
- cnt = 0;
- do {
- /*
- * refetch pointer in case of an interrupt
- */
- cp = __ByteArrayInstPtr(aCollection)->ba_element + offs;
- cnt = write(fileno(f), cp, len);
- } while ((cnt < 0) && (errno == EINTR));
- } else {
- __WRITING__(f)
- cnt = fwrite(cp, 1, len, f);
- if (errno == EINTR) errno = 0;
- }
-
- __END_INTERRUPTABLE__
-
- if (cnt == len) {
- pos = _INST(position);
- if (pos != nil) {
- _INST(position) = __MKSMALLINT(__intVal(pos) + len);
- }
- RETURN ( self );
- }
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self writeError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #readonly) ifTrue:[^ self errorReadOnly].
-
- ^ super nextPutAll:aCollection
+shallowCopyForFinalization
+ "return a copy for finalization-registration;
+ since all we need at finalization time is the fileDescriptor,
+ a cheaper copy is possible."
+
+ ^ self class basicNew setFilePointer:filePointer
!
-nextPutAll:aCollection startingAt:start to:stop
- "write a range of elements of the argument, aCollection.
- Reimplemented for speed when writing strings or byteArrays.
- For others, falls back to general method in superclass."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- unsigned char *cp;
- int offs, len, cnt, index1, index2;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))) {
- if (__bothSmallInteger(start, stop)) {
- cp = NULL;
- offs = 0;
- if (_INST(binary) != true) {
- if (__isString(aCollection) || __isSymbol(aCollection)) {
- cp = _stringVal(aCollection);
- len = _stringSize(aCollection);
- }
- } else {
- 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);
- offs = __OBJS2BYTES__(nInst);
- cp += offs;
- len -= offs;
- }
- }
- }
-
- if (cp != NULL) {
- f = __FILEVal(fp);
-
- index1 = __intVal(start);
- index2 = __intVal(stop);
- if ((index1 < 1) || (index2 > len) || (index2 < index1)) {
- RETURN ( self );
- }
- if (index2 > len)
- index2 = len;
-
- len = index2 - index1 + 1;
-
- __BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == true) {
- __WRITING__(f)
- cnt = fwrite(cp+index1-1, 1, len, f);
- } else {
- do {
- cp = __ByteArrayInstPtr(aCollection)->ba_element + offs;
- cnt = write(fileno(f), cp+offs, len);
- } while ((cnt < 0) && (errno == EINTR));
- }
-
- __END_INTERRUPTABLE__
-
- if (cnt == len) {
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + len);
- }
- RETURN ( self );
- }
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self writeError].
- ^ super nextPutAll:aCollection startingAt:start to:stop
-!
-
-cr
- "append an end-of-line character (or CRLF if in crlf mode).
- reimplemented for speed"
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- int len, cnt;
- OBJ fp;
- char *cp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(readonly))) {
- if (_INST(binary) != true) {
- f = __FILEVal(fp);
-
- if (_INST(useCRLF) == true) {
- cp = "\r\n"; len = 2;
- } else {
- cp = "\n"; len = 1;
- }
- __BEGIN_INTERRUPTABLE__
-
- if (_INST(buffered) == true) {
- __WRITING__(f)
- cnt = fwrite(cp, 1, len, f);
- } else {
- do {
- cnt = write(fileno(f), cp, len);
- } while ((cnt < 0) && (errno == EINTR));
- }
-
- __END_INTERRUPTABLE__
-
- if (cnt == len) {
- if (_INST(position) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + len);
- }
- RETURN ( self );
- }
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self writeError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #readonly) ifTrue:[^ self errorReadOnly].
- self errorBinary
+shutDown
+ "close the stream - added for protocol compatibility with PipeStream.
+ see comment there"
+
+ self closeFile
! !
!ExternalStream methodsFor:'line reading/writing'!
@@ -2484,381 +1020,1150 @@
^ nil
! !
-!ExternalStream methodsFor:'testing'!
-
-atEnd
- "return true, if position is at end"
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- OBJ fp;
- int c;
-
- if (_INST(hitEOF) == true) {
- RETURN (true);
- }
- if (_INST(buffered) == false) {
- RETURN (false);
- }
-
- _INST(lastErrorNumber) = nil;
-
- if ((fp = _INST(filePointer)) != nil) {
- f = __FILEVal(fp);
-#ifdef OLD
- RETURN ( feof(f) ? true : false );
-#else
- __READING__(f)
-
- __BEGIN_INTERRUPTABLE__
- do {
- c = getc(f);
- } while ((c < 0) && (errno == EINTR) && (clearerr(f), 1));
- __END_INTERRUPTABLE__
-
- if (c != EOF) {
- ungetc(c, f);
- RETURN (false);
- }
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- } else {
- _INST(hitEOF) = true;
- RETURN (true);
- }
-#endif
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- ^ self errorNotOpen
-!
-
-canReadWithoutBlocking
- "return true, if any data is available for reading (i.e.
- a read operation will not block the smalltalk process), false otherwise."
-
- |fd|
-
- filePointer isNil ifTrue:[^ self errorNotOpen].
- mode == #writeonly ifTrue:[^ self errorWriteOnly].
-
- fd := self fileDescriptor.
- ^ OperatingSystem readCheck:fd
-
- "
- |pipe|
-
- pipe := PipeStream readingFrom:'(sleep 10; echo hello)'.
- pipe canReadWithoutBlocking ifTrue:[
- Transcript showCr:'data available'
- ] ifFalse:[
- Transcript showCr:'no data available'
- ].
- pipe close
- "
-!
-
-canWriteWithoutBlocking
- "return true, if data can be written into the stream
- (i.e. a write operation will not block the smalltalk process)."
+!ExternalStream methodsFor:'misc functions'!
+
+async:aBoolean
+ "set/clear the async attribute - if set the availability of data on
+ the receiver will trigger an ioInterrupt.
+ If cleared (which is the default) no special notification is made."
|fd|
filePointer isNil ifTrue:[^ self errorNotOpen].
- mode == #readonly ifTrue:[^ self errorReadOnly].
-
fd := self fileDescriptor.
- ^ OperatingSystem writeCheck:fd
-! !
-
-!ExternalStream methodsFor:'waiting for I/O'!
-
-readWait
- "suspend the current process, until the receiver
- becomes ready for reading. If data is already available,
- return immediate.
- The other threads are not affected by the wait."
-
- self readWaitWithTimeoutMs:nil
+ aBoolean ifTrue:[
+ ^ OperatingSystem enableIOInterruptsOn:fd
+ ].
+ ^ OperatingSystem disableIOInterruptsOn:fd
!
-readWaitWithTimeout:timeout
- "suspend the current process, until the receiver
- becomes ready for reading or a timeout (in seconds) expired.
- If data is already available, return immediate.
- Return true if a timeout occured (i.e. false, if data is available).
- The other threads are not affected by the wait."
-
- ^ self readWaitWithTimeoutMs:timeout * 1000
+backStep
+ "step back one element -
+ redefined, since position is redefined here"
+
+ self position:(self position - 1)
!
-readWaitWithTimeoutMs:timeout
- "suspend the current process, until the receiver
- becomes ready for reading or a timeout (in milliseconds) expired.
- If data is already available, return immediate.
- Return true if a timeout occured (i.e. false, if data is available).
- The other threads are not affected by the wait."
-
- |fd inputSema hasData wasBlocked|
+blocking:aBoolean
+ "set/clear the blocking attribute - if set (which is the default)
+ a read (using next) on the receiver will block until data is available.
+ If cleared, a read operation will immediately return with a value of
+ nil."
filePointer isNil ifTrue:[^ self errorNotOpen].
- mode == #writeonly ifTrue:[^ self errorWriteOnly].
-
- fd := self fileDescriptor.
- (OperatingSystem readCheck:fd) ifTrue:[^ false].
-
- wasBlocked := OperatingSystem blockInterrupts.
- hasData := OperatingSystem readCheck:fd.
- hasData ifFalse:[
- inputSema := Semaphore new.
- [
- timeout notNil ifTrue:[
- Processor signal:inputSema afterMilliseconds:timeout
- ].
- Processor signal:inputSema onInput:fd.
- Processor activeProcess state:#ioWait.
- inputSema wait.
- Processor disableSemaphore:inputSema.
- hasData := OperatingSystem readCheck:fd
- ] valueOnUnwindDo:[
- Processor disableSemaphore:inputSema.
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
- ]
- ].
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
- ^ hasData not
+ ^ OperatingSystem setBlocking:aBoolean on:(self fileDescriptor)
+!
+
+close
+ "close the stream - tell operating system"
+
+ filePointer isNil ifTrue:[^ self].
+ Lobby unregister:self.
+ self closeFile.
+ filePointer := nil
+!
+
+create
+ "create the stream
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+ioctl:ioctlNumber
+ "to provide a simple ioctl facility - an ioctl is performed
+ on the underlying file; no arguments are passed."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int ret, ioNum, ioArg;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if ((fp = _INST(filePointer)) != nil) {
+ if (__isSmallInteger(ioctlNumber)) {
+ ioNum = __intVal(ioctlNumber);
+ f = __FILEVal(fp);
+
+ __BEGIN_INTERRUPTABLE__
+ do {
+ ret = ioctl(fileno(f), ioNum);
+ } while ((ret < 0) && (errno == EINTR));
+ __END_INTERRUPTABLE__
+
+ if (ret >= 0) {
+ RETURN ( __MKSMALLINT(ret) );
+ }
+ _INST(position) = nil;
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self ioError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ "
+ ioctl-number is not an integer
+ "
+ ^ self primitiveFailed
+!
+
+ioctl:ioctlNumber with:arg
+ "to provide a simple ioctl facility - an ioctl is performed
+ on the underlying file; the argument is passed as argument.
+ If the argument is a number, its directly passed; if its a
+ kind of ByteArray (ByteArray, String or Structure) a pointer to
+ the data is passed. This allows performing most ioctls - however,
+ it might be tricky to setup the buffer."
+
+%{ /* NOCONTEXT */
+ FILE *f;
+ int ret, ioNum;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if ((fp = _INST(filePointer)) != nil) {
+ if (__isSmallInteger(ioctlNumber)
+ && (__isSmallInteger(arg) || __isBytes(arg))) {
+ f = __FILEVal(fp);
+ ioNum = __intVal(ioctlNumber);
+
+ __BEGIN_INTERRUPTABLE__
+ do {
+ if (__isSmallInteger(arg)) {
+ ret = ioctl(fileno(f), ioNum, __intVal(arg));
+ } else {
+ ret = ioctl(fileno(f), ioNum, __ByteArrayInstPtr(arg)->ba_element);
+ }
+ } while ((ret < 0) && (errno == EINTR));
+ __END_INTERRUPTABLE__
+
+ if (ret >= 0) {
+ RETURN ( __MKSMALLINT(ret) );
+ }
+ _INST(position) = nil;
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self ioError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ "
+ ioctl-number is not an integer or argument is not byteArray-like
+ "
+ ^ self primitiveFailed
+!
+
+open
+ "open the stream
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+position
+ "return the position
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+position:anInteger
+ "set the position
+ - this must be redefined in subclass"
+
+ ^ self subclassResponsibility
+!
+
+reset
+ "set the read position to the beginning of the collection"
+
+ self position:"0" 1
+!
+
+setToEnd
+ "redefined since it must be implemented differently"
+
+ ^ self subclassResponsibility
+! !
+
+!ExternalStream methodsFor:'non homogenous reading'!
+
+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 the
+ bytes binary value as an integer in 0..255."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char byte;
+ int cnt;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ f = __FILEVal(fp);
+
+ if (_INST(buffered) == true) {
+ __READING__(f)
+ }
+
+ __BEGIN_INTERRUPTABLE__
+ do {
+ if (_INST(buffered) == false) {
+ cnt = read(fileno(f), &byte, 1);
+ } else {
+ cnt = fread(&byte, 1, 1, f);
+ }
+ } while ((cnt < 0) && (errno == EINTR));
+ __END_INTERRUPTABLE__
+
+ if (cnt == 1) {
+ if (_INST(position) != nil)
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 1);
+ RETURN ( __MKSMALLINT(byte) );
+ }
+ if (cnt == 0) {
+ _INST(hitEOF) = true;
+/*
+ if (errno == EWOULDBLOCK) {
+ RETURN (nil);
+ }
+*/
+ }
+ _INST(position) = nil;
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ self errorWriteOnly
+!
+
+nextBytes:count into:anObject
+ "read the next count bytes into an object and return the number of
+ bytes read. On EOF, 0 is returned.
+ If the receiver is some socket/pipe-like stream, an exception
+ is raised if the connection is broken.
+
+ The object must have non-pointer indexed instvars (i.e. it must be
+ a ByteArray, String, Float- or DoubleArray).
+ If anObject is a string or byteArray and reused, this provides the
+ fastest possible physical I/O (since no new objects are allocated).
+
+ Use with care - non object oriented i/o.
+ Warning: in general, you cannot use this method to pass data from other
+ architectures since it does not care for byte order or float representation."
+
+ ^ self nextBytes:count into:anObject startingAt:1
!
-writeWait
- "suspend the current process, until the receiver
- becomes ready for writing.
- Return immediate if the receiver is already ready.
- The other threads are not affected by the wait."
-
- self writeWaitWithTimeoutMs:nil
+nextBytes:count into:anObject startingAt:start
+ "read the next count bytes into an object and return the number of
+ bytes read or 0 on EOF.
+ If the receiver is some socket/pipe-like stream, an exception
+ is raised if the connection is broken.
+ Notice, that in contrast to other methods
+ here, this does NOT return nil on EOF, but the actual count.
+ Thus allowing read of partial blocks.
+
+ The object must have non-pointer indexed instvars
+ (i.e. it must be a ByteArray, String, Float- or DoubleArray).
+ If anObject is a string or byteArray and reused, this provides the
+ fastest possible physical I/O (since no new objects are allocated).
+
+ Use with care - non object oriented I/O.
+ Warning: in general, you cannot use this method to pass data from other
+ architectures since it does not care for byte order or float representation."
+
+%{
+ FILE *f;
+ int cnt, offs, ret;
+ int objSize, nInstVars, nInstBytes;
+ char *cp;
+ OBJ pos, fp, oClass;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ if (__bothSmallInteger(count, start)) {
+ f = __FILEVal(fp);
+
+ 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;
+ nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
+ nInstBytes = OHDR_SIZE + __OBJS2BYTES__(nInstVars);
+ objSize = __Size(anObject) - nInstBytes;
+ if ((offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs))) {
+ /*
+ * mhmh - since we are interruptable, anObject may move.
+ * therefore, fetch the cp-pointer within the loop
+ */
+ if (_INST(buffered) == true) {
+ __READING__(f)
+ }
+
+ __BEGIN_INTERRUPTABLE__
+ do {
+ errno = 0;
+ /*
+ * because we are interruptable, refetch pointer
+ */
+ cp = (char *)__InstPtr(anObject) + nInstBytes + offs;
+ if (_INST(buffered) == false) {
+ ret = read(fileno(f), cp, cnt);
+ } else {
+ if (feof(f)) {
+ ret = 0;
+ break;
+ }
+ ret = fread(cp, 1, cnt, f);
+ }
+ } while ((ret < 0) && (errno == EINTR));
+
+ __END_INTERRUPTABLE__
+ cnt = ret;
+
+ if (cnt > 0) {
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(pos) + cnt);
+ }
+ RETURN (__MKSMALLINT(cnt));
+ }
+ if (cnt == 0) {
+ _INST(hitEOF) = true;
+ RETURN (__MKSMALLINT(cnt));
+ }
+
+ _INST(position) = nil;
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+ }
+bad: ;
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ "
+ count not integer or arg not bit-like (String, ByteArray etc)
+ "
+ ^ self primitiveFailed
!
-writeWaitWithTimeout:timeout
- "suspend the current process, until the receiver
- becomes ready for writing or a timeout (in seconds) expired.
- Return true if a timeout occured (i.e. false, if data is available).
- Return immediate if the receiver is already ready.
- The other threads are not affected by the wait."
-
- ^ self writeWaitWithTimeoutMs:timeout * 1000
+nextBytesInto:anObject
+ "read bytes into an object, regardless of binary/text mode.
+ The number of bytes to read is defined by the objects size.
+ Return the number of bytes read. On EOF, 0 is returned.
+ If the receiver is some socket/pipe-like stream, an exception
+ is raised if the connection is broken.
+
+ The object to read into must have non-pointer indexed instvars
+ (i.e. it must be a ByteArray, String, Float- or DoubleArray).
+ If anObject is a string or byteArray and reused, this provides the
+ fastest possible physical I/O (since no new objects are allocated).
+
+ Use with care - non object oriented i/o.
+ Warning: in general, you cannot use this method to pass data from other
+ architectures since it does not care for byte order or float representation."
+
+ ^ self nextBytes:(anObject size) into:anObject startingAt:1
+
+ " to read 100 bytes from a stream:
+
+ |b aStream|
+
+ aStream := 'smalltalk.rc' asFilename readStream.
+ b := ByteArray new:100.
+ aStream nextBytesInto:b.
+ aStream close.
+ b inspect
+ "
+
+ "
+ |s aStream|
+ aStream := 'smalltalk.rc' asFilename readStream.
+ s := String new:100.
+ aStream nextBytesInto:s.
+ aStream close.
+ s inspect
+ "
+!
+
+nextLong
+ "Read four bytes (msb-first) and return the value as a 32-bit signed Integer.
+ The returned value may be a LargeInteger.
+ (msb-first for compatibility with other smalltalks)"
+
+ ^ self nextUnsignedLongMSB:true
+!
+
+nextLongMSB:msbFlag
+ "Read four bytes and return the value as a 32-bit signed Integer,
+ which may be a LargeInteger.
+ If msbFlag is true, value is read with most-significant byte first,
+ otherwise least-significant byte comes first.
+ A nil is returned, if EOF is hit before all 4 bytes have been read.
+ Works in both binary and text modes."
+
+%{ /* NOCONTEXT */
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ FILE *f;
+ int first, second, third, fourth;
+ int value;
+
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+ __READING__(f)
+
+ do {
+ first = getc(f);
+ } while ((first < 0) && (errno == EINTR));
+ if (first != EOF) {
+ do {
+ second = getc(f);
+ } while ((second < 0) && (errno == EINTR));
+ if (second != EOF) {
+ do {
+ third = getc(f);
+ } while ((third < 0) && (errno == EINTR));
+ if (third != EOF) {
+ do {
+ fourth = getc(f);
+ } while ((fourth < 0) && (errno == EINTR));
+ if (fourth != EOF) {
+ __END_INTERRUPTABLE__
+
+ if (msbFlag == true) {
+ value = (first & 0xFF);
+ value = (value<<8) | (second & 0xFF);
+ value = (value<<8) | (third & 0xFF);
+ value = (value<<8) | (fourth & 0xFF);
+ } else {
+ value = (fourth & 0xFF);
+ value = (value<<8) | (third & 0xFF);
+ value = (value<<8) | (second & 0xFF);
+ value = (value<<8) | (first & 0xFF);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 4);
+ }
+ if ((value >= _MIN_INT) && (value <= _MAX_INT)) {
+ RETURN ( __MKSMALLINT(value));
+ }
+ RETURN ( _MKLARGEINT(value) );
+ }
+ }
+ }
+ }
+ __END_INTERRUPTABLE__
+
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ _INST(position) = nil;
+ } else {
+ _INST(hitEOF) = true;
+ RETURN (nil);
+ }
+ }
+%}.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ ^ self readError.
!
-writeWaitWithTimeoutMs:timeout
- "suspend the current process, until the receiver
- becomes ready for writing or a timeout (in seconds) expired.
- Return true if a timeout occured (i.e. false, if data is available).
- Return immediate if the receiver is already ready.
- The other threads are not affected by the wait."
-
- |fd outputSema canWrite wasBlocked|
-
- filePointer isNil ifTrue:[
- ^ self errorNotOpen
- ].
- mode == #readonly ifTrue:[
- ^ self errorReadOnly
+nextShortMSB:msbFlag
+ "Read two bytes and return the value as a 16-bit signed Integer.
+ If msbFlag is true, value is read with most-significant byte first,
+ otherwise least-significant byte comes first.
+ A nil is returned if EOF is reached (also when EOF is hit after the first byte).
+ Works in both binary and text modes."
+
+%{ /* NOCONTEXT */
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ FILE *f;
+ int first, second, err;
+ short value;
+
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+ __READING__(f)
+
+ do {
+ first = getc(f);
+ } while ((first < 0) && (errno == EINTR));
+
+ if (first != EOF) {
+ do {
+ second = getc(f);
+ } while ((second < 0) && (errno == EINTR));
+
+ __END_INTERRUPTABLE__
+ if (second != EOF) {
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 2);
+ }
+ if (msbFlag == true) {
+ value = ((first & 0xFF) << 8) | (second & 0xFF);
+ } else {
+ value = ((second & 0xFF) << 8) | (first & 0xFF);
+ }
+ RETURN (__MKSMALLINT(value));
+ }
+ }
+ __END_INTERRUPTABLE__
+
+ if (ferror(f) && (errno != 0)) {
+ _INST(position) = nil;
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ } else {
+ _INST(hitEOF) = true;
+ RETURN (nil);
+ }
+ }
+%}.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ ^ self readError.
+!
+
+nextUnsignedLongMSB:msbFlag
+ "Read four bytes and return the value as a 32-bit unsigned Integer, which may be
+ a LargeInteger.
+ If msbFlag is true, value is read with most-significant byte first, otherwise
+ least-significant byte comes first.
+ A nil is returned, if endOfFile occurs before all 4 bytes have been read.
+ Works in both binary and text modes."
+
+%{ /* NOCONTEXT */
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ FILE *f;
+ int first, second, third, fourth;
+ unsigned int value;
+
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+ __READING__(f)
+
+ do {
+ first = getc(f);
+ } while ((first < 0) && (errno == EINTR));
+ if (first != EOF) {
+ do {
+ second = getc(f);
+ } while ((second < 0) && (errno == EINTR));
+ if (second != EOF) {
+ do {
+ third = getc(f);
+ } while ((third < 0) && (errno == EINTR));
+ if (third != EOF) {
+ do {
+ fourth = getc(f);
+ } while ((fourth < 0) && (errno == EINTR));
+ if (fourth != EOF) {
+ __END_INTERRUPTABLE__
+
+ if (msbFlag == true) {
+ value = (first & 0xFF);
+ value = (value<<8) | (second & 0xFF);
+ value = (value<<8) | (third & 0xFF);
+ value = (value<<8) | (fourth & 0xFF);
+ } else {
+ value = (fourth & 0xFF);
+ value = (value<<8) | (third & 0xFF);
+ value = (value<<8) | (second & 0xFF);
+ value = (value<<8) | (first & 0xFF);
+ }
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 4);
+ }
+ if (value <= _MAX_INT) {
+ RETURN ( __MKSMALLINT(value));
+ }
+ RETURN ( _MKULARGEINT(value) );
+ }
+ }
+ }
+ }
+ __END_INTERRUPTABLE__
+
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ _INST(position) = nil;
+ } else {
+ _INST(hitEOF) = true;
+ RETURN (nil);
+ }
+ }
+%}.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ ^ self readError.
+!
+
+nextUnsignedShortMSB:msbFlag
+ "Read two bytes and return the value as a 16-bit unsigned Integer.
+ If msbFlag is true, value is read with most-significant byte first, otherwise
+ least-significant byte comes first.
+ A nil is returned if EOF is reached (also when EOF is hit after the first byte).
+ Works in both binary and text modes."
+
+%{ /* NOCONTEXT */
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ FILE *f;
+ int first, second;
+ unsigned value;
+
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+ __READING__(f)
+
+ do {
+ first = getc(f);
+ } while ((first < 0) && (errno == EINTR));
+
+ if (first != EOF) {
+ do {
+ second = getc(f);
+ } while ((second < 0) && (errno == EINTR));
+
+ __END_INTERRUPTABLE__
+ if (second != EOF) {
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 2);
+ }
+ if (msbFlag == true) {
+ value = ((first & 0xFF) << 8) | (second & 0xFF);
+ } else {
+ value = ((second & 0xFF) << 8) | (first & 0xFF);
+ }
+ RETURN (__MKSMALLINT(value));
+ }
+ }
+ __END_INTERRUPTABLE__
+
+ if (ferror(f) && (errno != 0)) {
+ _INST(position) = nil;
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ } else {
+ _INST(hitEOF) = true;
+ RETURN (nil);
+ }
+ }
+%}.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ ^ self readError.
+!
+
+nextWord
+ "in text-mode:
+ read the alphaNumeric next word (i.e. up to non letter-or-digit).
+ return a string containing those characters.
+ in binary-mode:
+ read two bytes (msb-first) and return the value as a 16-bit
+ unsigned Integer (for compatibility with other smalltalks)"
+
+ binary ifTrue:[
+ ^ self nextUnsignedShortMSB:true
].
-
- fd := self fileDescriptor.
- (OperatingSystem writeCheck:fd) ifTrue:[^ false].
-
- wasBlocked := OperatingSystem blockInterrupts.
- canWrite := OperatingSystem writeCheck:fd.
- canWrite ifFalse:[
- outputSema := Semaphore new.
- [
- timeout notNil ifTrue:[
- Processor signal:outputSema afterMilliseconds:timeout
- ].
- Processor signal:outputSema onOutput:fd.
- Processor activeProcess state:#ioWait.
- outputSema wait.
- Processor disableSemaphore:outputSema.
- canWrite := OperatingSystem writeCheck:fd
- ] valueOnUnwindDo:[
- Processor disableSemaphore:outputSema.
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
- ]
+ ^ self nextAlphaNumericWord
+! !
+
+!ExternalStream methodsFor:'non homogenous writing'!
+
+nextPutByte:aByteValue
+ "write a byte.
+ Works in both binary and text modes."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char c;
+ OBJ pos, fp;
+ int cnt;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))) {
+ if (__isSmallInteger(aByteValue)) {
+ c = __intVal(aByteValue);
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+/*
+ *#ifdef OLD
+ * if (_INST(buffered) == false) {
+ * cnt = write(fileno(f), &c, 1);
+ * } else
+ *#endif
+ * {
+ * __WRITING__(f)
+ * cnt = fwrite(&c, 1, 1, f);
+ *#ifndef OLD
+ * if (_INST(buffered) == false) {
+ * fflush(f);
+ * }
+ *#endif
+ * }
+ */
+ if (_INST(buffered) == true) {
+ __WRITING__(f)
+ cnt = fwrite(&c, 1, 1, f);
+ } else {
+ do {
+ cnt = write(fileno(f), &c, 1);
+ } while ((cnt < 0) && (errno == EINTR));
+ }
+ __END_INTERRUPTABLE__
+
+ if (cnt == 1) {
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
+ RETURN (self);
+ }
+ if (cnt < 0) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+ }
+%}.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ ^ self writeError.
+!
+
+nextPutBytes:count from:anObject
+ "write count bytes from an object.
+ 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.
+ Warning: in general, you cannot use this method to pass data to other
+ architectures since it does not care for byte order or float representation."
+
+ ^ self nextPutBytes:count from:anObject startingAt:1
+!
+
+nextPutBytes:count from:anObject startingAt:start
+ "write count bytes from an object starting at index start.
+ return the number of bytes written - which could be 0.
+ 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.
+ Warning: in general, you cannot use this method to pass data to other
+ architectures since it does not care for byte order or float representation."
+
+%{ /* NOCONTEXT */
+ FILE *f;
+ int cnt, offs;
+ int objSize, nInstVars, nInstBytes;
+ char *cp;
+ OBJ oClass, pos, fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))) {
+ if (__bothSmallInteger(count, 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 = __FILEVal(fp);
+
+ nInstVars = __intVal(__ClassInstPtr(oClass)->c_ninstvars);
+ nInstBytes = OHDR_SIZE + __OBJS2BYTES__(nInstVars);
+ objSize = __Size(anObject) - nInstBytes;
+ if ( (offs >= 0) && (cnt >= 0) && (objSize >= (cnt + offs)) ) {
+ cp = (char *)__InstPtr(anObject) + nInstBytes + offs;
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == true) {
+ __WRITING__(f)
+ cnt = fwrite(cp, 1, cnt, f);
+ } else {
+ do {
+ cp = (char *)__InstPtr(anObject) + nInstBytes + offs;
+ cnt = write(fileno(f), cp, cnt);
+ } while ((cnt < 0) && (errno == EINTR));
+ }
+
+ __END_INTERRUPTABLE__
+
+ if (cnt >= 0) {
+ pos = _INST(position);
+ if (pos != nil)
+ _INST(position) = __MKSMALLINT(__intVal(pos) + cnt);
+ RETURN ( __MKSMALLINT(cnt) );
+ }
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+ }
+bad: ;
+%}.
+ lastErrorNumber notNil ifTrue:[^ self writeError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ ^ 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.
+ Warning: in general, you cannot use this method to pass data to other
+ architectures since it does not care for byte order or float representation."
+
+ ^ self nextPutBytes:(anObject size) from:anObject startingAt:1
+!
+
+nextPutLong:aNumber MSB:msbFlag
+ "Write the argument, aNumber as a long (four bytes). If msbFlag is
+ true, data is written most-significant byte first; otherwise least
+ first.
+ Works in both binary and text modes."
+
+%{ /* NOCONTEXT */
+
+ int num;
+ char bytes[4];
+ FILE *f;
+ int cnt;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))
+ && __isSmallInteger(aNumber)) {
+ num = __intVal(aNumber);
+ if (msbFlag == true) {
+ bytes[0] = (num >> 24) & 0xFF;
+ bytes[1] = (num >> 16) & 0xFF;
+ bytes[2] = (num >> 8) & 0xFF;
+ bytes[3] = num & 0xFF;
+ } else {
+ bytes[3] = (num >> 24) & 0xFF;
+ bytes[2] = (num >> 16) & 0xFF;
+ bytes[1] = (num >> 8) & 0xFF;
+ bytes[0] = num & 0xFF;
+ }
+
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == true) {
+ __WRITING__(f)
+ cnt = fwrite(bytes, 1, 4, f);
+ } else {
+ do {
+ cnt = write(fileno(f), bytes, 4);
+ } while ((cnt < 0) && (errno == EINTR));
+ }
+
+ __END_INTERRUPTABLE__
+
+ if (cnt == 4) {
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 4);
+ }
+ RETURN ( self );
+ }
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+%}.
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ lastErrorNumber notNil ifTrue:[^ self writeError].
+
+ aNumber isInteger ifTrue:[
+ msbFlag ifTrue:[
+ "high word first"
+ (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.
].
- wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
- ^ canWrite not
+ self argumentMustBeInteger
+!
+
+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
+ first.
+ Works in both binary and text modes."
+
+%{ /* NOCONTEXT */
+
+ int num;
+ char bytes[2];
+ FILE *f;
+ int cnt;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))
+ && __isSmallInteger(aNumber)) {
+ num = __intVal(aNumber);
+ if (msbFlag == true) {
+ bytes[0] = (num >> 8) & 0xFF;
+ bytes[1] = num & 0xFF;
+ } else {
+ bytes[1] = (num >> 8) & 0xFF;
+ bytes[0] = num & 0xFF;
+ }
+
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == true) {
+ __WRITING__(f)
+ cnt = fwrite(bytes, 1, 2, f);
+ } else {
+ do {
+ cnt = write(fileno(f), bytes, 2);
+ } while ((cnt < 0) && (errno == EINTR));
+ }
+
+ __END_INTERRUPTABLE__
+
+ if (cnt == 2) {
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + 2);
+ }
+ RETURN ( self );
+ }
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self writeError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self argumentMustBeInteger
! !
-
-!ExternalStream methodsFor:'reimplemented for speed'!
-
-peekFor:anObject
- "return true and move past next element, if next == something.
- Otherwise, stay and return false. False is also returned
- when EOF is encountered.
- The argument must be an integer if in binary, a character if in
- text mode; only single byte characters are currently supported."
+
+!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"
+
+ self class name errorPrint. ': cannot reOpen stream - stream closed' errorPrintNL.
+ filePointer := nil.
+ Lobby unregister:self.
+!
+
+setLastError:aNumber
+ lastErrorNumber := aNumber
+! !
+
+!ExternalStream methodsFor:'queries'!
+
+isBinary
+ "return true, if the stream is in binary (as opposed to text-) mode.
+ The default when created is false."
+
+ ^ binary
+!
+
+isExternalStream
+ "return true, if the receiver is some kind of externalStream;
+ true is returned here - the method redefined from Object."
+
+ ^ true
+!
+
+isReadable
+ "return true, if this stream can be read from"
+
+ ^ (mode ~~ #writeonly)
+!
+
+isWritable
+ "return true, if this stream can be written to"
+
+ ^ (mode ~~ #readonly)
+! !
+
+!ExternalStream methodsFor:'reading'!
+
+next
+ "return the next element; advance read position.
+ In binary mode, an integer is returned, otherwise a character.
+ If there are no more elements, nil is returned."
%{ /* NOCONTEXT */
FILE *f;
int c;
- int peekValue;
+ OBJ pos, fp;
+ unsigned char ch;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == true) {
+ __READING__(f)
+ }
+
+ do {
+ errno = 0;
+ if (_INST(buffered) == false) {
+ if (read(fileno(f), &ch, 1) != 1)
+ c = EOF;
+ else
+ c = ch;
+ } else
+ {
+ c = getc(f);
+ }
+ } while ((c < 0) && (errno == EINTR));
+
+ __END_INTERRUPTABLE__
+
+ if (c != EOF) {
+ pos = _INST(position);
+ if (__isSmallInteger(pos)) {
+ _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
+ } else {
+ _INST(position) = nil;
+ }
+ if (_INST(binary) == true) {
+ RETURN ( __MKSMALLINT(c & 0xFF) );
+ }
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+/*
+ if (errno == EWOULDBLOCK) {
+ _INST(hitEOF) = true;
+ RETURN(nil);
+ }
+*/
+ _INST(position) = nil;
+
+ if ((_INST(buffered) == false)
+ || (ferror(f) && (errno != 0))) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ } else {
+ _INST(hitEOF) = true;
+ RETURN ( nil );
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ self errorWriteOnly
+!
+
+next:count
+ "return the next count elements of the stream as a collection.
+ Redefined to return a String or ByteArray instead of the default: Array."
+
+ |coll|
+
+ binary ifTrue:[
+ coll := ByteArray uninitializedNew:count
+ ] ifFalse:[
+ coll := String new:count
+ ].
+ "/
+ "/ Q: should we use:
+ "/ self nextBytes:count into:coll startingAt:1
+ "/
+ 1 to:count do: [:index |
+ coll at:index put:(self next)
+ ].
+ ^ coll
+!
+
+peek
+ "return the element to be read next without advancing read position.
+ In binary mode, an integer is returned, otherwise a character.
+ If there are no more elements, nil is returned.
+ Not allowed in unbuffered mode."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ REGISTER int c;
OBJ fp;
_INST(lastErrorNumber) = nil;
if (((fp = _INST(filePointer)) != nil)
&& (_INST(mode) != @symbol(writeonly))) {
- if (_INST(binary) == true) {
- if (__isSmallInteger(anObject)) {
- peekValue = __intVal(anObject) & 0xFF;
- } else {
- goto bad;
- }
- } else {
- if (__isCharacter(anObject)) {
- peekValue = __intVal(_characterVal(anObject)) & 0xFF;
- } else {
- goto bad;
- }
- }
-
- f = __FILEVal(fp);
-
- if (feof(f)) {
- _INST(hitEOF) = true;
- RETURN (false);
- }
-
- __READING__(f)
-
- errno = 0;
- __BEGIN_INTERRUPTABLE__
- do {
- if (feof(f)) {
- break;
- }
- c = getc(f);
- } while ((c < 0) && (errno == EINTR));
- __END_INTERRUPTABLE__
-
- if (feof(f)) {
- _INST(hitEOF) = true;
- }
-
- if (c == peekValue) {
- OBJ pos;
-
- if ((pos = _INST(position)) != nil) {
- _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
- }
- RETURN (true);
- }
-
- if (c != EOF) {
- ungetc(c, f);
- RETURN (false);
- }
-
- _INST(hitEOF) = true;
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- } else {
- RETURN (false);
- }
- }
-bad: ;
-%}.
- mode == #writeonly ifTrue:[^ self errorWriteOnly].
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- ^ super peekFor:anObject
-!
-
-nextMatchFor:anObject
- "skip all objects up-to and including anObject, return anObject on success,
- nil if end-of-file is reached before. The next read operation will return
- the element after anObject.
- Only single byte characters are currently supported."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- int peekValue, c;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- if ((_INST(binary) == true) && __isSmallInteger(anObject)) {
- peekValue = __intVal(anObject) & 0xFF;
- } else {
- if ((_INST(binary) != true) && __isCharacter(anObject)) {
- peekValue = __intVal(_characterVal(anObject)) & 0xFF;
- } else {
- peekValue = -1;
- }
- }
-
- if (peekValue >= 0) {
- _INST(position) = nil;
+#ifdef OLD
+ if (_INST(buffered) == true)
+#endif
+ {
f = __FILEVal(fp);
+
__BEGIN_INTERRUPTABLE__
__READING__(f)
- for (;;) {
- do {
- c = getc(f);
- } while ((c < 0) && (errno == EINTR));
-
- if (c == EOF) {
- __END_INTERRUPTABLE__
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- break;
- }
- _INST(hitEOF) = true;
- RETURN (nil);
+ do {
+ c = getc(f);
+ } while ((c < 0) && (errno == EINTR));
+ __END_INTERRUPTABLE__
+
+ if (c != EOF) {
+ ungetc(c, f);
+ if (_INST(binary) == true) {
+ RETURN ( __MKSMALLINT(c & 0xFF) );
}
- if (c == peekValue) {
- __END_INTERRUPTABLE__
- RETURN (anObject);
- }
+ RETURN ( _MKCHARACTER(c & 0xFF) );
}
- }
- }
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- ^ super nextMatchFor:anObject
-!
-
-skipLine
- "read the next line (characters up to newline) skip only;
- return nil if EOF reached, self otherwise.
- Not allowed in binary mode."
-
-%{ /* STACK:2000 */
-
- FILE *f;
- char buffer[1024];
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- if (_INST(binary) != true) {
- f = __FILEVal(fp);
-
- __READING__(f)
-
- __BEGIN_INTERRUPTABLE__
- if (fgets(buffer, sizeof(buffer)-1, f) != NULL) {
- __END_INTERRUPTABLE__
- RETURN ( self );
- }
- __END_INTERRUPTABLE__
-
if (ferror(f) && (errno != 0)) {
_INST(lastErrorNumber) = __MKSMALLINT(errno);
} else {
@@ -2870,285 +2175,11 @@
%}.
lastErrorNumber notNil ifTrue:[^ self readError].
filePointer isNil ifTrue:[^ self errorNotOpen].
- binary ifTrue:[^ self errorBinary].
+ buffered ifFalse:[^ self errorNotBuffered].
^ 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|
-
- 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 when in binary mode.
- Only single byte characters are currently supported."
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- REGISTER int c, cSearch;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(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 = __FILEVal(fp);
- __READING__(f)
-
- __BEGIN_INTERRUPTABLE__
- while (1) {
-#ifdef NOTNEEDED
- if (feof(f)) {
- __END_INTERRUPTABLE__
- RETURN ( nil );
- }
-#endif
- do {
- c = getc(f);
- } while ((c < 0) && (errno == EINTR));
-
- if (c == cSearch) {
- __END_INTERRUPTABLE__
- RETURN (self);
- }
- if (c < 0) {
- __END_INTERRUPTABLE__
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- break;
- } else {
- _INST(hitEOF) = true;
- RETURN (nil);
- }
- }
- }
- }
-badArgument: ;
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- 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
- "skip all whitespace; next will return next non-white-space character
- or nil if endOfFile reached. Not allowed in binary mode.
- - reimplemented for speed"
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- REGISTER int c;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- if (_INST(binary) != true) {
- f = __FILEVal(fp);
-
- if (feof(f)) {
- _INST(hitEOF) = true;
- RETURN ( nil );
- }
-
- __READING__(f)
-
- __BEGIN_INTERRUPTABLE__
- while (1) {
- do {
- if (feof(f)) {
- __END_INTERRUPTABLE__
- _INST(hitEOF) = true;
- RETURN ( nil );
- }
- c = getc(f);
- } while ((c < 0) && (errno == EINTR));
-
- switch (c) {
- case ' ':
- case '\t':
- case '\n':
- case '\r':
- case '\b':
- case '\014':
- break;
-
- default:
- __END_INTERRUPTABLE__
- if (c < 0) {
- _INST(hitEOF) = true;
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- goto err;
- }
- RETURN ( nil );
- }
- ungetc(c, f);
- RETURN ( _MKCHARACTER(c & 0xFF) );
- }
- }
- }
- }
-err: ;
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- ^ self errorBinary.
-!
-
-skipSeparatorsExceptCR
- "skip all whitespace but no newlines;
- next will return next non-white-space character
- or nil if endOfFile reached. Not allowed in binary mode.
- - reimplemented for speed"
-
-%{ /* NOCONTEXT */
-
- FILE *f;
- int c;
- OBJ fp;
-
- _INST(lastErrorNumber) = nil;
- if (((fp = _INST(filePointer)) != nil)
- && (_INST(mode) != @symbol(writeonly))) {
- if (_INST(binary) != true) {
- f = __FILEVal(fp);
- __READING__(f)
-
- __BEGIN_INTERRUPTABLE__
- while (1) {
-
- if (feof(f)) {
- __END_INTERRUPTABLE__
- _INST(hitEOF) = true;
- RETURN ( nil );
- }
-
- do {
- c = getc(f);
- } while ((c < 0) && (errno == EINTR));
-
- switch (c) {
- case ' ':
- case '\t':
- case '\b':
- break;
-
- default:
- __END_INTERRUPTABLE__
- if (c < 0) {
- if (ferror(f) && (errno != 0)) {
- _INST(lastErrorNumber) = __MKSMALLINT(errno);
- goto err;
- }
- _INST(hitEOF) = true;
- RETURN ( nil );
- }
- ungetc(c, f);
- RETURN ( _MKCHARACTER(c & 0xFF) );
- }
- }
- }
- }
-err: ;
-%}.
- lastErrorNumber notNil ifTrue:[^ self readError].
- filePointer isNil ifTrue:[^ self errorNotOpen].
- (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
- ^ self errorBinary
-!
+! !
+
+!ExternalStream methodsFor:'reimplemented for speed'!
nextAlphaNumericWord
"read the next word (i.e. up to non letter-or-digit) after first
@@ -3436,4 +2467,974 @@
]
].
^ retVal
+!
+
+nextMatchFor:anObject
+ "skip all objects up-to and including anObject, return anObject on success,
+ nil if end-of-file is reached before. The next read operation will return
+ the element after anObject.
+ Only single byte characters are currently supported."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int peekValue, c;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ if ((_INST(binary) == true) && __isSmallInteger(anObject)) {
+ peekValue = __intVal(anObject) & 0xFF;
+ } else {
+ if ((_INST(binary) != true) && __isCharacter(anObject)) {
+ peekValue = __intVal(_characterVal(anObject)) & 0xFF;
+ } else {
+ peekValue = -1;
+ }
+ }
+
+ if (peekValue >= 0) {
+ _INST(position) = nil;
+ f = __FILEVal(fp);
+ __BEGIN_INTERRUPTABLE__
+ __READING__(f)
+
+ for (;;) {
+ do {
+ c = getc(f);
+ } while ((c < 0) && (errno == EINTR));
+
+ if (c == EOF) {
+ __END_INTERRUPTABLE__
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ break;
+ }
+ _INST(hitEOF) = true;
+ RETURN (nil);
+ }
+ if (c == peekValue) {
+ __END_INTERRUPTABLE__
+ RETURN (anObject);
+ }
+ }
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ super nextMatchFor:anObject
+!
+
+peekFor:anObject
+ "return true and move past next element, if next == something.
+ Otherwise, stay and return false. False is also returned
+ when EOF is encountered.
+ The argument must be an integer if in binary, a character if in
+ text mode; only single byte characters are currently supported."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ int peekValue;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ if (_INST(binary) == true) {
+ if (__isSmallInteger(anObject)) {
+ peekValue = __intVal(anObject) & 0xFF;
+ } else {
+ goto bad;
+ }
+ } else {
+ if (__isCharacter(anObject)) {
+ peekValue = __intVal(_characterVal(anObject)) & 0xFF;
+ } else {
+ goto bad;
+ }
+ }
+
+ f = __FILEVal(fp);
+
+ if (feof(f)) {
+ _INST(hitEOF) = true;
+ RETURN (false);
+ }
+
+ __READING__(f)
+
+ errno = 0;
+ __BEGIN_INTERRUPTABLE__
+ do {
+ if (feof(f)) {
+ break;
+ }
+ c = getc(f);
+ } while ((c < 0) && (errno == EINTR));
+ __END_INTERRUPTABLE__
+
+ if (feof(f)) {
+ _INST(hitEOF) = true;
+ }
+
+ if (c == peekValue) {
+ OBJ pos;
+
+ if ((pos = _INST(position)) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
+ }
+ RETURN (true);
+ }
+
+ if (c != EOF) {
+ ungetc(c, f);
+ RETURN (false);
+ }
+
+ _INST(hitEOF) = true;
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ } else {
+ RETURN (false);
+ }
+ }
+bad: ;
+%}.
+ mode == #writeonly ifTrue:[^ self errorWriteOnly].
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ ^ super peekFor:anObject
+!
+
+skipLine
+ "read the next line (characters up to newline) skip only;
+ return nil if EOF reached, self otherwise.
+ Not allowed in binary mode."
+
+%{ /* STACK:2000 */
+
+ FILE *f;
+ char buffer[1024];
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ if (_INST(binary) != true) {
+ f = __FILEVal(fp);
+
+ __READING__(f)
+
+ __BEGIN_INTERRUPTABLE__
+ if (fgets(buffer, sizeof(buffer)-1, f) != NULL) {
+ __END_INTERRUPTABLE__
+ RETURN ( self );
+ }
+ __END_INTERRUPTABLE__
+
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ } else {
+ _INST(hitEOF) = true;
+ RETURN ( nil );
+ }
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ binary ifTrue:[^ self errorBinary].
+ ^ self errorWriteOnly
+!
+
+skipSeparators
+ "skip all whitespace; next will return next non-white-space character
+ or nil if endOfFile reached. Not allowed in binary mode.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ REGISTER int c;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ if (_INST(binary) != true) {
+ f = __FILEVal(fp);
+
+ if (feof(f)) {
+ _INST(hitEOF) = true;
+ RETURN ( nil );
+ }
+
+ __READING__(f)
+
+ __BEGIN_INTERRUPTABLE__
+ while (1) {
+ do {
+ if (feof(f)) {
+ __END_INTERRUPTABLE__
+ _INST(hitEOF) = true;
+ RETURN ( nil );
+ }
+ c = getc(f);
+ } while ((c < 0) && (errno == EINTR));
+
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\n':
+ case '\r':
+ case '\b':
+ case '\014':
+ break;
+
+ default:
+ __END_INTERRUPTABLE__
+ if (c < 0) {
+ _INST(hitEOF) = true;
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ goto err;
+ }
+ RETURN ( nil );
+ }
+ ungetc(c, f);
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ }
+ }
+ }
+err: ;
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ ^ self errorBinary.
+!
+
+skipSeparatorsExceptCR
+ "skip all whitespace but no newlines;
+ next will return next non-white-space character
+ or nil if endOfFile reached. Not allowed in binary mode.
+ - reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int c;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(writeonly))) {
+ if (_INST(binary) != true) {
+ f = __FILEVal(fp);
+ __READING__(f)
+
+ __BEGIN_INTERRUPTABLE__
+ while (1) {
+
+ if (feof(f)) {
+ __END_INTERRUPTABLE__
+ _INST(hitEOF) = true;
+ RETURN ( nil );
+ }
+
+ do {
+ c = getc(f);
+ } while ((c < 0) && (errno == EINTR));
+
+ switch (c) {
+ case ' ':
+ case '\t':
+ case '\b':
+ break;
+
+ default:
+ __END_INTERRUPTABLE__
+ if (c < 0) {
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ goto err;
+ }
+ _INST(hitEOF) = true;
+ RETURN ( nil );
+ }
+ ungetc(c, f);
+ RETURN ( _MKCHARACTER(c & 0xFF) );
+ }
+ }
+ }
+ }
+err: ;
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #writeonly) ifTrue:[^ self errorWriteOnly].
+ ^ self errorBinary
+!
+
+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 when in binary mode.
+ Only single byte characters are currently supported."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ REGISTER int c, cSearch;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(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 = __FILEVal(fp);
+ __READING__(f)
+
+ __BEGIN_INTERRUPTABLE__
+ while (1) {
+#ifdef NOTNEEDED
+ if (feof(f)) {
+ __END_INTERRUPTABLE__
+ RETURN ( nil );
+ }
+#endif
+ do {
+ c = getc(f);
+ } while ((c < 0) && (errno == EINTR));
+
+ if (c == cSearch) {
+ __END_INTERRUPTABLE__
+ RETURN (self);
+ }
+ if (c < 0) {
+ __END_INTERRUPTABLE__
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ break;
+ } else {
+ _INST(hitEOF) = true;
+ RETURN (nil);
+ }
+ }
+ }
+ }
+badArgument: ;
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ 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
+ "
+!
+
+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|
+
+ 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
+ "
! !
+
+!ExternalStream methodsFor:'testing'!
+
+atEnd
+ "return true, if position is at end"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ OBJ fp;
+ int c;
+
+ if (_INST(hitEOF) == true) {
+ RETURN (true);
+ }
+ if (_INST(buffered) == false) {
+ RETURN (false);
+ }
+
+ _INST(lastErrorNumber) = nil;
+
+ if ((fp = _INST(filePointer)) != nil) {
+ f = __FILEVal(fp);
+#ifdef OLD
+ RETURN ( feof(f) ? true : false );
+#else
+ __READING__(f)
+
+ __BEGIN_INTERRUPTABLE__
+ do {
+ c = getc(f);
+ } while ((c < 0) && (errno == EINTR) && (clearerr(f), 1));
+ __END_INTERRUPTABLE__
+
+ if (c != EOF) {
+ ungetc(c, f);
+ RETURN (false);
+ }
+ if (ferror(f) && (errno != 0)) {
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ } else {
+ _INST(hitEOF) = true;
+ RETURN (true);
+ }
+#endif
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self readError].
+ ^ self errorNotOpen
+!
+
+canReadWithoutBlocking
+ "return true, if any data is available for reading (i.e.
+ a read operation will not block the smalltalk process), false otherwise."
+
+ |fd|
+
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ mode == #writeonly ifTrue:[^ self errorWriteOnly].
+
+ fd := self fileDescriptor.
+ ^ OperatingSystem readCheck:fd
+
+ "
+ |pipe|
+
+ pipe := PipeStream readingFrom:'(sleep 10; echo hello)'.
+ pipe canReadWithoutBlocking ifTrue:[
+ Transcript showCr:'data available'
+ ] ifFalse:[
+ Transcript showCr:'no data available'
+ ].
+ pipe close
+ "
+!
+
+canWriteWithoutBlocking
+ "return true, if data can be written into the stream
+ (i.e. a write operation will not block the smalltalk process)."
+
+ |fd|
+
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ mode == #readonly ifTrue:[^ self errorReadOnly].
+
+ fd := self fileDescriptor.
+ ^ OperatingSystem writeCheck:fd
+! !
+
+!ExternalStream methodsFor:'waiting for I/O'!
+
+readWait
+ "suspend the current process, until the receiver
+ becomes ready for reading. If data is already available,
+ return immediate.
+ The other threads are not affected by the wait."
+
+ self readWaitWithTimeoutMs:nil
+!
+
+readWaitWithTimeout:timeout
+ "suspend the current process, until the receiver
+ becomes ready for reading or a timeout (in seconds) expired.
+ If data is already available, return immediate.
+ Return true if a timeout occured (i.e. false, if data is available).
+ The other threads are not affected by the wait."
+
+ ^ self readWaitWithTimeoutMs:timeout * 1000
+!
+
+readWaitWithTimeoutMs:timeout
+ "suspend the current process, until the receiver
+ becomes ready for reading or a timeout (in milliseconds) expired.
+ If data is already available, return immediate.
+ Return true if a timeout occured (i.e. false, if data is available).
+ The other threads are not affected by the wait."
+
+ |fd inputSema hasData wasBlocked|
+
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ mode == #writeonly ifTrue:[^ self errorWriteOnly].
+
+ fd := self fileDescriptor.
+ (OperatingSystem readCheck:fd) ifTrue:[^ false].
+
+ wasBlocked := OperatingSystem blockInterrupts.
+ hasData := OperatingSystem readCheck:fd.
+ hasData ifFalse:[
+ inputSema := Semaphore new.
+ [
+ timeout notNil ifTrue:[
+ Processor signal:inputSema afterMilliseconds:timeout
+ ].
+ Processor signal:inputSema onInput:fd.
+ Processor activeProcess state:#ioWait.
+ inputSema wait.
+ Processor disableSemaphore:inputSema.
+ hasData := OperatingSystem readCheck:fd
+ ] valueOnUnwindDo:[
+ Processor disableSemaphore:inputSema.
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ]
+ ].
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ^ hasData not
+!
+
+writeWait
+ "suspend the current process, until the receiver
+ becomes ready for writing.
+ Return immediate if the receiver is already ready.
+ The other threads are not affected by the wait."
+
+ self writeWaitWithTimeoutMs:nil
+!
+
+writeWaitWithTimeout:timeout
+ "suspend the current process, until the receiver
+ becomes ready for writing or a timeout (in seconds) expired.
+ Return true if a timeout occured (i.e. false, if data is available).
+ Return immediate if the receiver is already ready.
+ The other threads are not affected by the wait."
+
+ ^ self writeWaitWithTimeoutMs:timeout * 1000
+!
+
+writeWaitWithTimeoutMs:timeout
+ "suspend the current process, until the receiver
+ becomes ready for writing or a timeout (in seconds) expired.
+ Return true if a timeout occured (i.e. false, if data is available).
+ Return immediate if the receiver is already ready.
+ The other threads are not affected by the wait."
+
+ |fd outputSema canWrite wasBlocked|
+
+ filePointer isNil ifTrue:[
+ ^ self errorNotOpen
+ ].
+ mode == #readonly ifTrue:[
+ ^ self errorReadOnly
+ ].
+
+ fd := self fileDescriptor.
+ (OperatingSystem writeCheck:fd) ifTrue:[^ false].
+
+ wasBlocked := OperatingSystem blockInterrupts.
+ canWrite := OperatingSystem writeCheck:fd.
+ canWrite ifFalse:[
+ outputSema := Semaphore new.
+ [
+ timeout notNil ifTrue:[
+ Processor signal:outputSema afterMilliseconds:timeout
+ ].
+ Processor signal:outputSema onOutput:fd.
+ Processor activeProcess state:#ioWait.
+ outputSema wait.
+ Processor disableSemaphore:outputSema.
+ canWrite := OperatingSystem writeCheck:fd
+ ] valueOnUnwindDo:[
+ Processor disableSemaphore:outputSema.
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ]
+ ].
+ wasBlocked ifFalse:[OperatingSystem unblockInterrupts].
+ ^ canWrite not
+! !
+
+!ExternalStream methodsFor:'writing'!
+
+commit
+ "write all buffered date - ignored if unbuffered"
+
+ self synchronizeOutput
+!
+
+cr
+ "append an end-of-line character (or CRLF if in crlf mode).
+ reimplemented for speed"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ int len, cnt;
+ OBJ fp;
+ char *cp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))) {
+ if (_INST(binary) != true) {
+ f = __FILEVal(fp);
+
+ if (_INST(useCRLF) == true) {
+ cp = "\r\n"; len = 2;
+ } else {
+ cp = "\n"; len = 1;
+ }
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == true) {
+ __WRITING__(f)
+ cnt = fwrite(cp, 1, len, f);
+ } else {
+ do {
+ cnt = write(fileno(f), cp, len);
+ } while ((cnt < 0) && (errno == EINTR));
+ }
+
+ __END_INTERRUPTABLE__
+
+ if (cnt == len) {
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + len);
+ }
+ RETURN ( self );
+ }
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self writeError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ self errorBinary
+!
+
+nextPut:aCharacter
+ "write the argument, aCharacter - return nil if failed, self if ok.
+ Only single-byte characters are currently supported"
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ char c;
+ int cnt;
+ OBJ pos, fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))) {
+ if (_INST(binary) != true) {
+ if (__isCharacter(aCharacter)) {
+ c = __intVal(__characterVal(aCharacter)) & 0xFF;
+ doWrite:
+ f = __FILEVal(fp);
+
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == true) {
+ __WRITING__(f)
+
+ do {
+ cnt = fwrite(&c, 1, 1, f);
+ } while ((cnt != 1) && (errno == EINTR));
+ } else {
+ do {
+ cnt = write(fileno(f), &c, 1);
+ } while ((cnt != 1) && (errno == EINTR));
+ }
+
+ __END_INTERRUPTABLE__
+
+ if (cnt == 1) {
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(pos) + 1);
+ }
+ RETURN ( self );
+ }
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ } else {
+ if (__isSmallInteger(aCharacter)) {
+ c = __intVal(aCharacter);
+ goto doWrite;
+ }
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self writeError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+ binary ifFalse:[^ self argumentMustBeCharacter].
+ ^ self argumentMustBeInteger.
+!
+
+nextPutAll:aCollection
+ "write all elements of the argument, aCollection.
+ Reimplemented for speed when writing strings or byteArrays.
+ For others, falls back to general method in superclass."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char *cp;
+ int len, cnt;
+ OBJ pos, fp;
+ int offs;
+
+ _INST(lastErrorNumber) = nil;
+
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))) {
+ cp = NULL;
+ offs = 0;
+ if (__isString(aCollection) || __isSymbol(aCollection)) {
+ cp = _stringVal(aCollection);
+ len = _stringSize(aCollection);
+ } else {
+ if (_INST(binary) == true) {
+ 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);
+ offs = __OBJS2BYTES__(nInst);
+ cp += offs;
+ len -= offs;
+ }
+ }
+ }
+ }
+ if (cp != NULL) {
+ f = __FILEVal(fp);
+
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == false) {
+ int cc, rest;
+
+ cnt = 0;
+ do {
+ /*
+ * refetch pointer in case of an interrupt
+ */
+ cp = __ByteArrayInstPtr(aCollection)->ba_element + offs;
+ cnt = write(fileno(f), cp, len);
+ } while ((cnt < 0) && (errno == EINTR));
+ } else {
+ __WRITING__(f)
+ cnt = fwrite(cp, 1, len, f);
+ if (errno == EINTR) errno = 0;
+ }
+
+ __END_INTERRUPTABLE__
+
+ if (cnt == len) {
+ pos = _INST(position);
+ if (pos != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(pos) + len);
+ }
+ RETURN ( self );
+ }
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self writeError].
+ filePointer isNil ifTrue:[^ self errorNotOpen].
+ (mode == #readonly) ifTrue:[^ self errorReadOnly].
+
+ ^ super nextPutAll:aCollection
+!
+
+nextPutAll:aCollection startingAt:start to:stop
+ "write a range of elements of the argument, aCollection.
+ Reimplemented for speed when writing strings or byteArrays.
+ For others, falls back to general method in superclass."
+
+%{ /* NOCONTEXT */
+
+ FILE *f;
+ unsigned char *cp;
+ int offs, len, cnt, index1, index2;
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if (((fp = _INST(filePointer)) != nil)
+ && (_INST(mode) != @symbol(readonly))) {
+ if (__bothSmallInteger(start, stop)) {
+ cp = NULL;
+ offs = 0;
+ if (_INST(binary) != true) {
+ if (__isString(aCollection) || __isSymbol(aCollection)) {
+ cp = _stringVal(aCollection);
+ len = _stringSize(aCollection);
+ }
+ } else {
+ 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);
+ offs = __OBJS2BYTES__(nInst);
+ cp += offs;
+ len -= offs;
+ }
+ }
+ }
+
+ if (cp != NULL) {
+ f = __FILEVal(fp);
+
+ index1 = __intVal(start);
+ index2 = __intVal(stop);
+ if ((index1 < 1) || (index2 > len) || (index2 < index1)) {
+ RETURN ( self );
+ }
+ if (index2 > len)
+ index2 = len;
+
+ len = index2 - index1 + 1;
+
+ __BEGIN_INTERRUPTABLE__
+
+ if (_INST(buffered) == true) {
+ __WRITING__(f)
+ cnt = fwrite(cp+index1-1, 1, len, f);
+ } else {
+ do {
+ cp = __ByteArrayInstPtr(aCollection)->ba_element + offs;
+ cnt = write(fileno(f), cp+offs, len);
+ } while ((cnt < 0) && (errno == EINTR));
+ }
+
+ __END_INTERRUPTABLE__
+
+ if (cnt == len) {
+ if (_INST(position) != nil) {
+ _INST(position) = __MKSMALLINT(__intVal(_INST(position)) + len);
+ }
+ RETURN ( self );
+ }
+ _INST(lastErrorNumber) = __MKSMALLINT(errno);
+ }
+ }
+ }
+%}.
+ lastErrorNumber notNil ifTrue:[^ self writeError].
+ ^ super nextPutAll:aCollection startingAt:start to:stop
+!
+
+synchronizeOutput
+ "write all buffered data - ignored if unbuffered"
+
+%{ /* NOCONTEXT */
+ OBJ fp;
+
+ _INST(lastErrorNumber) = nil;
+ if ((fp = _INST(filePointer)) != nil) {
+ if (_INST(mode) != @symbol(readonly)) {
+ if (_INST(buffered) == true) {
+ __BEGIN_INTERRUPTABLE__
+ fflush( __FILEVal(fp) );
+ __END_INTERRUPTABLE__
+ }
+ }
+ }
+%}
+! !
+
+ExternalStream initialize!