Types
FileDescriptor: TYPE ~ REF;
Handle: TYPE ~ UnixServerHandle;
Object: TYPE ~ UnixServerObject;
ROPE: TYPE ~ Rope.ROPE;
ServerHandle: TYPE ~ RemoteFile.ServerHandle;
STREAM: TYPE ~ IO.STREAM;
Handle Create / Destroy
CreateHandle:
PUBLIC
PROC [serverName:
ROPE]
RETURNS [h: Handle] ~ {
sH: ServerHandle;
tProcs: REF;
sH ← RemoteFile.GetServer[serverName
! RemoteFile.Error => ReportError[$unknownServer, "can't find server"] ];
tProcs ← RemoteFile.GetProcs[sH, viewUnix
! RemoteFile.Error =>
IF code = $notImplemented
THEN CONTINUE
ELSE ReportError[$unreachable, "can't contact server"]
];
IF tProcs = NIL THEN ReportError[$viewNotImplemented, "Unix(tm) view not implemented by server"];
h ← NEW[Object ← [sH, NARROW[tProcs]]];
};
DestroyHandle: PUBLIC PROC [h: Handle] ~ { h^ ← [NIL, NIL] };
Creature Comforts
RetrieveFile:
PUBLIC
PROC [h: Handle, path:
ROPE, into:
STREAM] ~ {
offset: CARD ← 0;
buffer: REF TEXT ← RefText.ObtainScratch[bufferBytes];
fD: FileDescriptor ← NIL;
{
ENABLE
UNWIND => {
RefText.ReleaseScratch[buffer];
IF fD # NIL THEN Close[h, fD ! Error => CONTINUE];
};
fD ← Open[h, path];
DO
[] ← Read[h, fD, offset, bufferBytes, 0, buffer];
IO.PutBlock[into, buffer];
offset ← offset + buffer.length;
IF buffer.length < bufferBytes THEN EXIT;
ENDLOOP;
};
Close[h, fD];
RefText.ReleaseScratch[buffer];
};
StoreFile:
PUBLIC
PROC [h: Handle, path:
ROPE, mode: Mode, from:
STREAM] ~ {
offset: CARD ← 0;
buffer: REF TEXT ← RefText.ObtainScratch[bufferBytes];
fD: FileDescriptor ← NIL;
{
ENABLE
UNWIND => {
RefText.ReleaseScratch[buffer];
IF fD # NIL THEN Close[h, fD ! Error => CONTINUE];
};
fD ← Create[h, path, mode];
DO
bytesRead, bytesWritten: CARD;
bytesRead ← IO.GetBlock[from, buffer, 0, bufferBytes];
IF bytesRead = 0 THEN EXIT;
bytesWritten ← Write[h, fD, offset, bytesRead, 0, buffer];
offset ← offset + bytesWritten;
IF bytesWritten # bytesRead THEN ReportError[$io, "short write error"];
ENDLOOP;
};
Close[h, fD];
RefText.ReleaseScratch[buffer];
};
Stream Implementation
StreamData: TYPE ~ REF StreamDataObject;
StreamDataObject:
TYPE ~
RECORD [
h: Handle,
fD: FileDescriptor,
fileOffset: CARD,
buffer: REF TEXT,
index: CARDINAL,
closed: BOOL
];
inputStreamProcs:
REF
IO.StreamProcs ~
IO.CreateStreamProcs[
variety~input,
class~$UnixRemoteFile,
getChar~MyGetChar,
endOf~MyEndOf,
close~MyClose
];
outputStreamProcs:
REF
IO.StreamProcs ~
IO.CreateStreamProcs[
variety~output,
class~$UnixRemoteFile,
putChar~MyPutChar,
flush~MyFlush,
close~MyClose
];
OpenReadStream:
PUBLIC
PROC [h: Handle, path:
ROPE]
RETURNS [str:
STREAM] ~ {
fD: FileDescriptor ← Open[h, path];
streamData: StreamData ← NEW[StreamDataObject ← [h, fD, 0, RefText.New[bufferBytes], 0, FALSE]];
str ← IO.CreateStream[inputStreamProcs, streamData, NIL];
};
OpenWriteStream:
PUBLIC
PROC [h: Handle, path:
ROPE, mode: Mode]
RETURNS [str:
STREAM] ~ {
fD: FileDescriptor ← Create[h, path, mode];
streamData: StreamData ← NEW[StreamDataObject ← [h, fD, 0, RefText.New[bufferBytes], 0, FALSE]];
str ← IO.CreateStream[outputStreamProcs, streamData, NIL];
};
CheckStreamClosed:
PROC [d: StreamData, self:
STREAM] ~
INLINE {
IF d.closed THEN ERROR IO.Error[StreamClosed, self] };
MyEndOf:
PROC [self:
STREAM]
RETURNS [
BOOL] ~ {
streamData: StreamData ← NARROW[self.streamData];
b: REF TEXT ← streamData.buffer;
CheckStreamClosed[streamData, self];
IF streamData.index >= b.length
THEN {
bytesRead: CARD ← Read[streamData.h, streamData.fD, streamData.fileOffset, b.maxLength, 0, b];
IF bytesRead = 0 THEN RETURN[TRUE];
streamData.index ← 0;
streamData.fileOffset ← streamData.fileOffset + bytesRead;
};
RETURN[FALSE];
};
MyGetChar:
PROC [self:
STREAM]
RETURNS [c:
CHAR] ~ {
streamData: StreamData ← NARROW[self.streamData];
b: REF TEXT ← streamData.buffer;
i: CARDINAL ← streamData.index;
CheckStreamClosed[streamData, self];
IF i >= b.length
THEN {
bytesRead: CARD ← Read[streamData.h, streamData.fD, streamData.fileOffset, b.maxLength, 0, b];
IF bytesRead = 0 THEN ERROR IO.EndOfStream[self];
streamData.fileOffset ← streamData.fileOffset + bytesRead;
i ← 0;
};
c ← b[i];
i ← i + 1;
streamData.index ← i;
};
MyPutChar:
PROC [self:
STREAM, char:
CHAR] ~ {
streamData: StreamData ← NARROW[self.streamData];
b: REF TEXT ← streamData.buffer;
i: CARDINAL ← streamData.index;
CheckStreamClosed[streamData, self];
IF i >= b.maxLength
THEN {
bytesWritten: CARD;
b.length ← i;
bytesWritten ← Write[streamData.h, streamData.fD, streamData.fileOffset, i, 0, b];
IF bytesWritten # i THEN ERROR; -- BUG: should have raised Error[...]
streamData.fileOffset ← streamData.fileOffset + bytesWritten;
i ← 0;
};
b[i] ← char;
i ← i + 1;
streamData.index ← i;
};
MyFlush:
PROC [self:
STREAM] ~ {
streamData: StreamData ← NARROW[self.streamData];
b: REF TEXT ← streamData.buffer;
i: CARDINAL ← streamData.index;
CheckStreamClosed[streamData, self];
IF i > 0
THEN {
bytesWritten: CARD;
b.length ← i;
bytesWritten ← Write[streamData.h, streamData.fD, streamData.fileOffset, i, 0, b];
IF bytesWritten # i THEN ERROR; -- BUG: should have raised Error[...]
streamData.fileOffset ← streamData.fileOffset + bytesWritten;
i ← 0;
};
streamData.index ← i;
};
MyClose:
PROC [self:
STREAM, abort:
BOOL] ~ {
streamData: StreamData ← NARROW[self.streamData];
IF streamData.closed THEN RETURN;
IF (IO.GetInfo[self].variety = output) AND (NOT abort) THEN MyFlush[self];
Close[streamData.h, streamData.fD];
streamData.fD ← NIL;
streamData.buffer ← NIL;
streamData.closed ← TRUE;
};
}...