<> <> <> <> <> <> <> <> DIRECTORY ConvertUnsafe USING [ToRope], FS USING [AccessOptions, Error, StreamOpen], IO USING [Close, EndOf, GetChar, PutChar, SetIndex, STREAM], Rope USING [ROPE], TJaMStorage USING [Zone], TJaMStreamDefs USING [AccessOptions, Append, DefaultAccess, Read, StreamErrorCode, StreamHandle, StreamObject]; TJaMStreamDefsImpl: MONITOR IMPORTS ConvertUnsafe, FS, TJaMStorage, IO EXPORTS TJaMStreamDefs = {OPEN TJaMStreamDefs; ROPE: TYPE = Rope.ROPE; zone: UNCOUNTED ZONE = TJaMStorage.Zone[]; openList: LIST OF IO.STREAM _ NIL; -- here to keep the streams from being collected StreamError: PUBLIC SIGNAL[stream: StreamHandle, error: StreamErrorCode] = CODE; FileNameError: PUBLIC SIGNAL[name: LONG STRING] = CODE; NewByteStream: PUBLIC PROC [name: LONG STRING, access: AccessOptions] RETURNS [StreamHandle] = { data: Data _ NIL; s: StreamHandle _ NIL; data _ NewStream[name,access]; s _ zone.NEW[StreamObject _ [reset: FileReset, get: FileGetChar, putback: FilePutback, put: FilePutChar, endof: FileEndof, destroy: FileDestroy, data: data]]; RETURN[s]; }; NewWordStream: PUBLIC PROC [name: LONG STRING, access: AccessOptions] RETURNS [StreamHandle] = { s: StreamHandle _ NewByteStream[name,access]; s.get _ FileGetWord; s.put _ FilePutWord; RETURN[s]; }; Data: TYPE = LONG POINTER TO IO.STREAM; AddStream: ENTRY PROC [stream: IO.STREAM] RETURNS [Data] = { openList _ CONS[stream, openList]; RETURN [@openList.first]; }; RemStream: ENTRY PROC [data: Data] RETURNS [IO.STREAM] = { lag: LIST OF IO.STREAM _ NIL; FOR list: LIST OF IO.STREAM _ openList, list.rest UNTIL list = NIL DO IF @list.first = data THEN { IF lag = NIL THEN openList _ list.rest ELSE lag.rest _ list.rest; RETURN [list.first]; }; lag _ list; ENDLOOP; RETURN [NIL]; }; NewStream: PROC [name: LONG STRING, access: AccessOptions] RETURNS [Data] = { new: BOOL _ FALSE; stream: IO.STREAM _ NIL; rope: ROPE _ ConvertUnsafe.ToRope[name]; accessOptions: FS.AccessOptions _ SELECT access FROM DefaultAccess, Read => read, Append => append, ENDCASE => create; stream _ FS.StreamOpen[ fileName: rope, accessOptions: accessOptions ! FS.Error => GO TO NotFound; ]; RETURN [AddStream[stream]]; EXITS NotFound => ERROR FileNameError[name]; }; <> FileReset: PROC[s: StreamHandle] = { data: Data _ s.data; IO.SetIndex[data^,0]; }; FileGetChar: PROC[s: StreamHandle] RETURNS [CHAR] = { data: Data _ s.data; RETURN[IO.GetChar[data^]]; }; FileGetWord: PROC[s: StreamHandle] RETURNS [WORD] = { data: Data _ s.data; card: CARDINAL _ (IO.GetChar[data^]-0C) * 256; card _ card + (IO.GetChar[data^]-0C); RETURN[LOOPHOLE[card, WORD]]; }; FilePutback: PROC[s: StreamHandle, c: CHAR] = { ERROR StreamError[s,StreamOperation]; }; FilePutChar: PROC[s: StreamHandle, c: CHAR] = { data: Data _ s.data; IO.PutChar[data^, c]; }; FilePutWord: PROC[s: StreamHandle, w: WORD] = { data: Data _ s.data; card: CARDINAL _ LOOPHOLE[w, CARDINAL]; IO.PutChar[data^, (card / 256) + 0C]; IO.PutChar[data^, (card MOD 256) + 0C]; }; FileEndof: PROC[s: StreamHandle] RETURNS [BOOL] = { data: Data _ s.data; RETURN[IO.EndOf[data^]]; }; FileDestroy: PROC[s: StreamHandle] = { data: Data _ s.data; stream: IO.STREAM _ RemStream[data]; IF stream # NIL THEN { IO.Close[stream]; zone.FREE[@s]; }; }; }.