"======================================================================
|
|   UnixStream (raw file access)
|
|   $Revision: 1.7.5$
|   $Date: 2000/05/28 16:56:52$
|   $Author: pb$
|
 ======================================================================"


"======================================================================
|
| Copyright 1988-92, 1994-95, 1999, 2000 Free Software Foundation, Inc.
| Written by Steve Byrne and Paolo Bonzini.
|
| This file is part of the GNU Smalltalk class library.
|
| The GNU Smalltalk class library is free software; you can redistribute it
| and/or modify it under the terms of the GNU Lesser General Public License
| as published by the Free Software Foundation; either version 2.1, or (at
| your option) any later version.
| 
| The GNU Smalltalk class library is distributed in the hope that it will be
| useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
| General Public License for more details.
| 
| You should have received a copy of the GNU Lesser General Public License
| along with the GNU Smalltalk class library; see the file COPYING.LESSER.
| If not, write to the Free Software Foundation, 59 Temple Place - Suite
| 330, Boston, MA 02111-1307, USA.  
|
 ======================================================================"


PositionableStream subclass: #UnixStream
		   instanceVariableNames: 'fd'
		   classVariableNames: ''
		   poolDictionaries: ''
		   category: 'Sockets-Protocols'
!
    
UnixStream comment:
'I provide an interface to Unix file descriptors, so that Smalltalk methods
may perform most of the I/O operations that C programs can.  I am quite proud
of my contribution to the GNU Smalltalk project, and look forward to serving
the project better in the future.'
!


UnixStream
	defineCFunc: 'open'
	withSelectorArgs: 'open: aFileName flags: flagsInteger 
			   mode: anInteger'
	returning: #int
	args: #(string int int)
!

UnixStream
	defineCFunc: 'available'
	withSelectorArgs: 'available: socket reading: forReading'
	returning: #int
	args: #(int int)
!

UnixStream
	defineCFunc: 'close'
	withSelectorArgs: 'close: fileDescriptor'
	returning: #int
	args: #(int)
!

UnixStream
	defineCFunc: 'read'
	withSelectorArgs: 'read: fileDescriptor into: buf bytes: anInteger'
	returning: #int
	args: #(int byteArrayOut int)
!

UnixStream
	defineCFunc: 'read'
	withSelectorArgs: 'read: fileDescriptor into: buf chars: anInteger'
	returning: #int
	args: #(int stringOut int)
!

UnixStream
	defineCFunc: 'write'
	withSelectorArgs: 'write: fileDescriptor from: buf bytes: anInteger'
	returning: #int
	args: #(int byteArray int)
!

UnixStream
	defineCFunc: 'lseek'
	withSelectorArgs: 'lseek: fileDescriptor offset: anInteger 
			   whence: cObject'
	returning: #int
	args: #(int int int)
!

"======================================================================
|
|   Type specific I/O routines
|
 ======================================================================"

UnixStream
	defineCFunc: 'readChar'
	withSelectorArgs: 'readChar: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!

UnixStream
	defineCFunc: 'readUChar'
	withSelectorArgs: 'readUChar: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!

UnixStream
	defineCFunc: 'readShort'
	withSelectorArgs: 'readShort: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!

UnixStream
	defineCFunc: 'readUShort'
	withSelectorArgs: 'readUShort: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!

UnixStream
	defineCFunc: 'readLong'
	withSelectorArgs: 'readLong: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!

UnixStream
	defineCFunc: 'readULong'
	withSelectorArgs: 'readULong: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!

UnixStream
	defineCFunc: 'readFloat'
	withSelectorArgs: 'readFloat: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!

UnixStream
	defineCFunc: 'readDouble'
	withSelectorArgs: 'readDouble: fileDescriptor'
	returning: #smalltalk
	args: #(int)
!


UnixStream
	defineCFunc: 'writeChar'
	withSelectorArgs: 'write: fileDescriptor char: aChar'
	returning: #void
	args: #(int char)
!

UnixStream
	defineCFunc: 'writeShort'
	withSelectorArgs: 'write: fileDescriptor short: aShort'
	returning: #void
	args: #(int int)
!

UnixStream
	defineCFunc: 'writeLong'
	withSelectorArgs: 'write: fileDescriptor long: aLong'
	returning: #void
	args: #(int long)
!

UnixStream
	defineCFunc: 'writeDouble'
	withSelectorArgs: 'write: fileDescriptor double: aDouble'
	returning: #void
	args: #(int double)
!





!UnixStream class methodsFor: 'instance creation'!

open: fileName dir: anInteger
    ^self open: fileName dir: anInteger mode: 0
!

open: fileName dir: anInteger mode: intMode
    ^self new init: fileName dir: anInteger mode: intMode
!

on: fd
    ^self new initFd: fd
! !



!UnixStream class methodsFor: 'constants'!

seekSet
    ^0
!

seekCurr
    ^1
!

seekEnd
    ^2
!

readOnly
    ^0
!

writeOnly
    ^1
!

readWrite
    ^2
!

"Other modifiers (like O_APPEND) may be added in the future as needs warrant"
!



!UnixStream methodsFor: 'basic accessing'!

close
    | result |
    result := self close: fd.
    fd := -1.
    ^result
!

canRead
    | result |
    self isOpen ifFalse: [ ^false ].
    result := self available: self fd reading: 1.
    result == -1 ifTrue: [ self nowDead; close ].
    ^result == 1
!

canWrite
    | result |
    self isOpen ifFalse: [ ^false ].
    result := self available: self fd reading: 0.
    result == -1 ifTrue: [ self nowDead; close ].
    ^result == 1
!

isOpen
    ^fd isInteger and: [ fd positive ]
!

read: byteArray
    | result |
    result := self read: fd into: byteArray bytes: byteArray size.
    result == -1 ifTrue: [ self nowDead; close ].
    ^result
!

read: byteArray numBytes: anInteger
    | result |
    result := self read: fd into: byteArray bytes: anInteger.
    result == -1 ifTrue: [ self nowDead; close ].
    ^result
!

write: byteArray
    | result |
    result := self write: byteArray numBytes: byteArray size.
    result == -1 ifTrue: [ self nowDead; close ].
    ^result
!

write: byteArray numBytes: anInteger
    | result |
    result := self write: fd from: byteArray bytes: anInteger.
    result == -1 ifTrue: [ self nowDead; close ].
    ^result
!

tell
    ^self lseek: fd offset: 0 whence: 1   "Curr"
!

ioctl: number arg: randomArg
    ^self ioctl: fd request: number arg: randomArg
!

readChar
    ^self readChar: fd
!

readUChar
    ^self readUChar: fd
!

readShort
    ^self readShort: fd
!

readUShort
    ^self readUShort: fd
!

readLong
    ^self readLong: fd
!

readULong
    ^self readULong: fd
!

readFloat
    ^self readFloat: fd
!

readDouble
    ^self readDouble: fd
!

writeChar: aChar
    ^self write: fd char: aChar
!

writeShort: aShort		"really an integer"
    ^self write: fd short: aShort
!

writeLong: aLong
    ^self write: fd long: aLong
!

writeDouble: aDouble
    ^self write: fd double: aDouble
!

readString: numChars
    | string numRead str |
    string := String new: numChars.
    numRead := self read: string.

    ^numRead = numChars
	ifTrue: [ string ]
    	ifFalse: [ string copyFrom: 1 to: numRead ]
!

readBytes: numBytes
    | byteArray numRead str |
    byteArray := ByteArray new: numBytes.
    numRead := self read: byteArray.

    ^numRead = numBytes
	ifTrue: [ byteArray ]
    	ifFalse: [ byteArray copyFrom: 1 to: numRead ]
! !



!UnixStream methodsFor: 'call back'!

nowDead
    File checkError.
! !


!UnixStream methodsFor: 'accessing'!

position
    ^1 + (self lseek: fd offset: 0 whence: 1)   "Curr"
!

position: anInteger
    ^self lseek: fd offset: anInteger - 1 whence: 0 "Set"
!

next
    ^self readChar
!

next: anInteger
    ^self readString: anInteger
!


nextPut: aValue
    (self writeChar: aValue) == -1 ifTrue: [ self nowDead; close ].
!

nextPutAll: aString
    (self write: aString) == -1 ifTrue: [ self nowDead; close ].
!

contents
    | curPos result size |
    curPos := self tell.
    size := self lseek: fd offset: 0 whence: 2. "To end"
    self lseek: fd offset: 0 whence: 0.
    result := self readString: size.
    self lseek: fd offset: curPos whence: 0.
    ^curPos
!

atEnd
    "Poor man's atEnd function"
    | curPos size |
    curPos := self tell.
    size := self lseek: fd offset: 0 whence: 2. "To end"
    self lseek: fd offset: curPos whence: 0.
    ^curPos >= size
!

size
    "Poor man's size function"
    | curPos size |
    curPos := self tell.
    size := self lseek: fd offset: 0 whence: 2. "To end"
    self lseek: fd offset: curPos whence: 0.
    ^size
!

skip: anInteger
    "Skip n bytes on the file.  N can be positive or negative"
    ^self lseek: fd offset: anInteger whence: 1 "Cur"
! !



!UnixStream methodsFor: 'private'!

fd
    ^fd
!

init: fileName dir: anInteger mode: intMode
    fd := self open: fileName flags: anInteger mode: intMode.
    access := anInteger + 1.
    fd < 0 ifTrue: [ ^nil ]
!

initFd: anFd
    fd := anFd.
    access := 3.
! !

