<> <> <> DIRECTORY IO USING [CreateStream, CreateStreamProcs, EndOfStream, Error, GetBlock, GetInfo, PutBlock, STREAM, StreamProcs], RefText USING [New, ObtainScratch, ReleaseScratch], RemoteFile USING [Error, GetProcs, GetServer, ServerHandle], Rope USING [ROPE], UnixRemoteFile USING [Close, Create, Mode, Open, Read, UnixServerHandle, UnixServerObject, Write] ; UnixRemoteFileImpl: CEDAR PROGRAM IMPORTS IO, RefText, RemoteFile, UnixRemoteFile EXPORTS UnixRemoteFile ~ { OPEN UnixRemoteFile; <> FileDescriptor: TYPE ~ REF; Handle: TYPE ~ UnixServerHandle; Object: TYPE ~ UnixServerObject; ROPE: TYPE ~ Rope.ROPE; ServerHandle: TYPE ~ RemoteFile.ServerHandle; STREAM: TYPE ~ IO.STREAM; <> bufferBytes: CARDINAL ~ 2048; viewUnix: PUBLIC ATOM _ $Unix; <> 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] }; <> 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]; }; <> 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; }; <> Error: PUBLIC ERROR [code: ATOM, msg: ROPE] ~ CODE; ReportError: PROC [code: ATOM, msg: ROPE _ NIL] ~ { ERROR Error[code, msg]; }; }...