DIRECTORY Ascii USING [BS, ControlA, ControlW, ControlQ, ControlR, CR, ESC, DEL, NUL], IO USING [ChangeDeliverWhen, CreateProcsStream, CreateRefStreamProcs, DeliverWhenProc, EndOf, EndOfStream, EraseChar, Error, Flush, GetChar, GetBufferContents, PeekChar, PutChar, PutBlock, Reset, ROPE, SetEcho, Signal, STREAM, StreamProcs, UncheckedImplements, Zone], IOExtras USING [], RefText USING [ObtainScratch, ReleaseScratch], Rope USING [Fetch, Concat, Length, FromRefText] ; SomeStreamsImpl: CEDAR MONITOR LOCKS bufferedStreamData USING bufferedStreamData: REF BufferedOutputStreamRecord IMPORTS IO, RefText, Rope EXPORTS IO, IOExtras SHARES IO = BEGIN OPEN Ascii, IO; BufferOverFlow: PUBLIC SIGNAL[text: REF TEXT] RETURNS[REF TEXT] = CODE; -- e.g. line buffering and type more characters before CR then there is room in buffer. value returned can be a bigger text, with characters copied over, or else can be same size, with length having been reset, etc. EnlargeBuffer: PROC [text: REF TEXT] RETURNS[newBuffer: REF TEXT] = { newBuffer _ NEW[TEXT[2 * text.maxLength]]; FOR i: NAT IN [0..text.maxLength) DO newBuffer[i] _ text[i]; ENDLOOP; newBuffer.length _ text.maxLength; RETURN[newBuffer]; }; noWhereStream: PUBLIC IO.STREAM _ CreateProcsStream[ CreateRefStreamProcs[putChar: NoWherePutChar, name: "NoWhere"], NIL]; NoWherePutChar: PROCEDURE[self: STREAM, char: CHARACTER] = {}; noInputStream: PUBLIC STREAM _ CreateProcsStream[streamProcs: CreateRefStreamProcs[ getChar: NoInputGetChar, endOf: NoInputEndOf, name: "NoInput" ], streamData: NIL]; NoInputGetChar: PROC [self: IO.STREAM] RETURNS[CHAR] = {ERROR IO.Error[StreamClosed, self]; }; NoInputEndOf: PROC [self: IO.STREAM] RETURNS[BOOLEAN] = {RETURN[TRUE]}; RIScount: INT _ 0; InputRopeStreamData: TYPE = REF InputRopeStreamRecord; InputRopeStreamRecord: TYPE = RECORD[ rope: ROPE _ NIL, pos: INT _ 0 ]; InputRopeStreamProcs: REF StreamProcs _ NIL; RIS, CreateInputStreamFromRope: PUBLIC PROCEDURE [rope: ROPE, oldStream: STREAM _ NIL] RETURNS [h: STREAM] = { IF oldStream # NIL THEN BEGIN data: InputRopeStreamData = NARROW[oldStream.streamData]; data.rope _ rope; data.pos _ 0; RETURN[oldStream]; END; IF InputRopeStreamProcs = NIL THEN InputRopeStreamProcs _ CreateRefStreamProcs[ getChar: InputRopeStreamGetChar, endOf: InputRopeStreamEndOf, reset: InputRopeStreamReset, getIndex: InputRopeStreamGetIndex, setIndex: InputRopeStreamSetIndex, getLength: InputRopeStreamGetLength, backup: InputRopeStreamBackup, -- because most applications will want to do formatted input, e.g. getint, etc. name: "Input From Rope" ]; h _ CreateProcsStream[InputRopeStreamProcs, Zone.NEW[InputRopeStreamRecord _ [rope: rope, pos: 0]]]; RIScount _ RIScount + 1; }; -- of CreateStreamFromRope InputRopeStreamGetChar: PROC[self: STREAM] RETURNS [char: CHARACTER] = { data: InputRopeStreamData = NARROW[self.streamData]; IF data.pos >= Rope.Length[data.rope] THEN ERROR EndOfStream[self]; char _ Rope.Fetch[data.rope, data.pos]; data.pos _ data.pos + 1; }; -- of InputRopeStreamGetChar InputRopeStreamEndOf: PROC[self: STREAM] RETURNS [BOOLEAN] = { data: InputRopeStreamData = NARROW[self.streamData]; RETURN[data.pos >= Rope.Length[data.rope]]; }; -- of InputRopeStreamEndOf InputRopeStreamReset: PROC[self: STREAM] = { data: InputRopeStreamData = NARROW[self.streamData]; data.pos _ 0; }; -- of InputRopeStreamReset InputRopeStreamGetIndex: PROC[self: STREAM] RETURNS[INT] = { data: InputRopeStreamData = NARROW[self.streamData]; RETURN[data.pos]; }; -- of InputRopeStreamGetIndex InputRopeStreamSetIndex: PROC[self: STREAM, index: INT] = { data: InputRopeStreamData = NARROW[self.streamData]; data.pos _ index; }; -- of InputRopeStreamSetIndex InputRopeStreamGetLength: PROC[self: STREAM] RETURNS[length: INT] = { data: InputRopeStreamData = NARROW[self.streamData]; RETURN[Rope.Length[data.rope]]; }; -- of InputRopeStreamGetLength InputRopeStreamBackup: PROC[self: STREAM, char: CHARACTER] = { data: InputRopeStreamData = NARROW[self.streamData]; IF data.pos = 0 OR Rope.Fetch[data.rope, data.pos - 1] # char THEN Error[IllegalBackup, self]; data.pos _ data.pos - 1; }; -- of InputRopeStreamBackup ROSCount: INT _ 0; ROSNumChars: INT _ 0; OutputRopeStreamData: TYPE = REF OutputRopeStreamRecord; -- describes the streamData field for streams that read from ropes. OutputRopeStreamRecord: TYPE = RECORD[rope: ROPE _ NIL, text: REF TEXT]; OutputRopeStreamProcs: REF StreamProcs _ NIL; ROS, CreateOutputStreamToRope: PUBLIC PROC RETURNS [h: STREAM] = { IF OutputRopeStreamProcs = NIL THEN OutputRopeStreamProcs _ CreateRefStreamProcs[ putChar: OutputRopeStreamPutChar, reset: OutputRopeStreamReset, flush: OutputRopeStreamFlush, close: OutputRopeStreamClose, getLength: OutputRopeStreamLength, name: "Output To Rope" ]; ROSCount _ ROSCount + 1; RETURN[CreateProcsStream[OutputRopeStreamProcs, Zone.NEW[OutputRopeStreamRecord _ [rope: NIL, text: RefText.ObtainScratch[100]]]]]; }; -- of CreateStreamFromRope OutputRopeStreamPutChar: PROC[self: STREAM, char: CHARACTER] = { data: OutputRopeStreamData = NARROW[self.streamData]; text: REF TEXT _ data.text; IF text.length = text.maxLength THEN {data.rope _ Rope.Concat[data.rope, Rope.FromRefText[text]]; text.length _ 0}; text[text.length] _ char; text.length _ text.length + 1; }; -- of OutputRopeStreamPutChar OutputRopeStreamReset: PROC[self: STREAM] = { data: OutputRopeStreamData = NARROW[self.streamData]; data.rope _ NIL; data.text.length _ 0; }; -- of OutputRopeStreamReset OutputRopeStreamFlush: PROC[self: STREAM] = { data: OutputRopeStreamData = NARROW[self.streamData]; text: REF TEXT _ data.text; IF data.text # NIL AND text.length # 0 THEN {data.rope _ Rope.Concat[data.rope, Rope.FromRefText[text]]; text.length _ 0}; }; -- of OutputRopeStreamFlush OutputRopeStreamLength: PROC[self: STREAM] RETURNS [length: INT] = { data: OutputRopeStreamData = NARROW[self.streamData]; IF data.text # NIL THEN length _ data.text.length ELSE length _ 0; length _ length + Rope.Length[data.rope]; }; -- of OutputRopeStreamReset OutputRopeStreamClose: PROC[self: STREAM, abort: BOOLEAN _ FALSE] = { data: OutputRopeStreamData = NARROW[self.streamData]; RefText.ReleaseScratch[data.text]; data.text _ NIL; }; -- of OutputRopeStreamReset GetOutputStreamRope: PUBLIC PROC [self: STREAM] RETURNS [ROPE] = { data: OutputRopeStreamData = NARROW[self.streamData]; OutputRopeStreamFlush[self]; ROSNumChars _ ROSNumChars + Rope.Length[data.rope]; RETURN[data.rope]; }; -- of GetOutputStreamRope TextStreamData: TYPE = REF TextStreamRecord; TextStreamRecord: TYPE = RECORD[text: REF READONLY TEXT _ NIL, i: CARDINAL _ 0]; TextStreamProcs: REF StreamProcs _ NIL; CreateInputStreamFromText: PUBLIC PROCEDURE [text: REF READONLY TEXT _ NIL, oldStream: STREAM _ NIL] RETURNS [STREAM] = { IF TextStreamProcs = NIL THEN TextStreamProcs _ CreateRefStreamProcs[ getChar: TextStreamGetChar, reset: TextStreamReset, endOf: TextStreamEndOf, getIndex: TextStreamGetIndex, setIndex: TextStreamSetIndex, getLength: TextStreamGetLength, name: "Input From Text" ]; IF oldStream # NIL THEN BEGIN data: TextStreamData = NARROW[oldStream.streamData]; data.text _ text; data.i _ 0; RETURN[oldStream]; END; oldStream _ CreateProcsStream[TextStreamProcs, Zone.NEW[TextStreamRecord _ [text: text, i: 0]]]; RETURN[oldStream]; }; -- of CreateInputStreamFromText TextStreamGetChar: PROC[self: STREAM] RETURNS [char: CHARACTER] = { data: TextStreamData = NARROW[self.streamData]; IF data.i >= data.text.length THEN ERROR EndOfStream[self]; char _ data.text[data.i]; data.i _ data.i + 1; }; -- of TextStreamGetChar TextStreamReset: PROC[self: STREAM] = { data: TextStreamData = NARROW[self.streamData]; data.i _ 0; }; -- of TextStreamReset TextStreamEndOf: PROC[self: STREAM] RETURNS [BOOLEAN] = { data: TextStreamData = NARROW[self.streamData]; RETURN[data.i >= data.text.length]; }; -- of TextStreamEndOf TextStreamGetIndex: PROC[self: STREAM] RETURNS[INT] = { data: TextStreamData = NARROW[self.streamData]; RETURN[data.i]; }; -- of TextStreamGetIndex TextStreamSetIndex: PROC[self: STREAM, index: INT] = { data: TextStreamData = NARROW[self.streamData]; data.i _ index; }; -- of TextStreamSetIndex TextStreamGetLength: PROC[self: STREAM] RETURNS[length: INT] = { data: TextStreamData = NARROW[self.streamData]; RETURN[data.text.length]; }; -- of TextStreamGetLength StreamToTextProcs: REF StreamProcs _ NIL; CreateOutputStreamToText: PUBLIC PROCEDURE [text: REF TEXT, oldStream: STREAM _ NIL] RETURNS [STREAM] = { IF StreamToTextProcs = NIL THEN StreamToTextProcs _ CreateRefStreamProcs[ putChar: StreamToTextPutChar, reset: StreamToTextReset, getIndex: StreamToTextGetIndex, setIndex: StreamToTextSetIndex, eraseChar: StreamToTextEraseChar, name: "Output To Text" ]; IF oldStream = NIL THEN oldStream _ CreateProcsStream[StreamToTextProcs, text] ELSE {text: REF TEXT _ NARROW[oldStream.streamData]; -- just to make sure is of right type. oldStream.streamData _ text}; Reset[oldStream]; RETURN[oldStream]; }; -- of CreateOutputStreamToText StreamToTextPutChar: PROC[self: STREAM, char: CHARACTER] = { text: REF TEXT _ NARROW[self.streamData, REF TEXT]; WHILE text.length >= text.maxLength DO text _ -- SIGNAL BufferOverFlow -- EnlargeBuffer[text]; self.streamData _ text; ENDLOOP; text[text.length] _ char; text.length _ text.length + 1; }; -- of StreamToTextPutChar StreamToTextEraseChar: PROC[self: STREAM, char: CHARACTER] = { text: REF TEXT _ NARROW[self.streamData, REF TEXT]; IF text.length > 0 THEN text.length _ text.length - 1; }; -- of StreamToTextEraseChar StreamToTextReset: PROC[self: STREAM] = { text: REF TEXT _ NARROW[self.streamData, REF TEXT]; text.length _ 0; }; -- of StreamToTextReset StreamToTextGetIndex: PROC[self: STREAM] RETURNS[INT] = { text: REF TEXT _ NARROW[self.streamData, REF TEXT]; RETURN[text.length]; }; -- of StreamToTextReset StreamToTextSetIndex: PROC[self: STREAM, index: INT] = { text: REF TEXT _ NARROW[self.streamData, REF TEXT]; text.length _ index; }; -- of StreamToTextSetIndex EditedStreamData: TYPE = REF EditedStreamRecord; EditedStreamRecord: TYPE = RECORD[ buffer: REF TEXT, ready: REF TEXT, readyPos: INT _ 0, lastReadyLength: NAT, -- for ESC deliverWhen: DeliverWhenProc, echoTo: STREAM _ NIL ]; EditedStreamProcs: REF StreamProcs _ NIL; IsACR: PUBLIC DeliverWhenProc = {RETURN[char = CR]}; CreateEditedStream: PUBLIC PROCEDURE [in: STREAM, echoTo: STREAM, deliverWhen: DeliverWhenProc _ IsACR] RETURNS [h: STREAM] = { IF EditedStreamProcs = NIL THEN EditedStreamProcs _ CreateRefStreamProcs[ getChar: EditedStreamGetChar, reset: EditedStreamReset, endOf: EditedStreamEndOf, charsAvail: EditedStreamAvail, getIndex: EditedStreamGetIndex, setIndex: EditedStreamSetIndex, setEcho: EditedStreamSetEcho, backup: EditedStreamBackup, eraseChar: EditedStreamEraseChar, name: "Edited" ]; h _ CreateProcsStream[ streamProcs: EditedStreamProcs, streamData: Zone.NEW[EditedStreamRecord _ [ buffer: NEW[TEXT[256]], ready: NEW[TEXT[256]], lastReadyLength: 0, deliverWhen: deliverWhen]], backingStream: in ]; UncheckedImplements[ self: h, operation: ChangeDeliverWhen, via: ChangeDeliverWhen1, procRef: Zone.NEW[PROC [self: STREAM, proc: DeliverWhenProc] RETURNS [oldProc: DeliverWhenProc] _ ChangeDeliverWhen1], key: $ChangeDeliverWhen]; UncheckedImplements[ self: h, operation: GetBufferContents, via: GetBufferContents1, procRef: Zone.NEW[PROC [self: STREAM] RETURNS [buffer: ROPE] _ GetBufferContents1], key: $GetBufferContents]; [] _ SetEcho[in, NIL ! Error => CONTINUE]; [] _ SetEcho[h, echoTo]; -- echoing done at this level since dont want control-a's w's etc to be echoed, and also characters should be echoed when they are inserted in the buffer, not when they are returned as value of getchar. }; -- of CreateEditedStream ChangeDeliverWhen1: PROC [self: STREAM, proc: DeliverWhenProc] RETURNS [oldProc: DeliverWhenProc] = { data: EditedStreamData = NARROW[self.streamData]; oldProc _ data.deliverWhen; data.deliverWhen _ proc; }; GetBufferContents1: PROC [self: STREAM] RETURNS [buffer: ROPE] = { data: EditedStreamData = NARROW[self.streamData]; RETURN[Rope.FromRefText[IF data.buffer.length # 0 THEN data.buffer ELSE data.ready]]; }; EditedStreamReset: PROCEDURE[self: STREAM] = { data: EditedStreamData = NARROW[self.streamData]; data.buffer.length _ 0; data.ready.length _ 0; data.readyPos _ 0; Reset[self.backingStream]; IF data.echoTo # NIL THEN Reset[data.echoTo]; }; -- of EditedStreamReset EditedStreamFlush: PROCEDURE[self: STREAM] = { data: EditedStreamData = NARROW[self.streamData]; data.buffer.length _ 0; data.ready.length _ 0; data.readyPos _ 0; IF data.echoTo # NIL THEN Flush[data.echoTo]; }; -- of EditedStreamFlush EditedStreamEndOf: PROCEDURE[self: STREAM] RETURNS [BOOLEAN] = { data: EditedStreamData = NARROW[self.streamData]; RETURN[data.readyPos >= data.ready.length AND EndOf[self.backingStream]]; }; -- of EditedStreamEndOf EditedStreamGetIndex: PROCEDURE[self: STREAM] RETURNS [INT] = { data: EditedStreamData = NARROW[self.streamData]; RETURN[data.readyPos]; }; -- of EditedStreamGetIndex EditedStreamSetIndex: PROCEDURE[self: STREAM, index: INT] = { data: EditedStreamData = NARROW[self.streamData]; IF index > data.ready.length OR index < 0 THEN ERROR IO.Error[BadIndex, self]; data.readyPos _ index; }; -- of EditedStreamSetIndex EditedStreamSetEcho: PROCEDURE[self: STREAM, echoTo: STREAM] RETURNS [oldEcho: STREAM] = { data: EditedStreamData = NARROW[self.streamData]; oldEcho _ data.echoTo; data.echoTo _ echoTo; }; -- of EditedStreamSetEcho EditedStreamAvail: PROCEDURE[self: STREAM] RETURNS [BOOLEAN] = { data: EditedStreamData = NARROW[self.streamData]; RETURN[data.readyPos < data.ready.length -- OR CharsAvail[self.backingStream] --]; -- CharsAvail means available now }; -- of EditedStreamAvail EditedStreamEraseChar: PROCEDURE[self: STREAM, char: CHARACTER] = { data: EditedStreamData = NARROW[self.streamData]; IF data.buffer.length > 0 THEN BEGIN char _ data.buffer[data.buffer.length - 1]; data.buffer.length _ data.buffer.length - 1; IF data.echoTo # NIL THEN data.echoTo.EraseChar[char]; END ELSE NULL; -- Signal[EmptyBuffer, self]; }; -- of EditedStreamEraseChar EditedStreamGetChar: PROCEDURE[self: STREAM] RETURNS[char: CHARACTER] = { data: EditedStreamData = NARROW[self.streamData]; GetCharFromBuffer: PROC = INLINE { char _ data.ready[data.readyPos]; data.readyPos _ data.readyPos + 1; }; -- of GetCharFromBuffer DO IF data.readyPos < data.ready.length THEN {GetCharFromBuffer[]; RETURN}; char _ GetChar[self.backingStream]; -- gets character from stream underneath. Don't want to be in monitor at this point. IF EditedStreamAppend[self, char] AND data.deliverWhen[char, self] THEN EditedStreamDeliver[self]; -- EditedStreamAppend appends it to the buffer, checking for control-a etc., echoes the character. Value of FALSE means doesnt make any sense to check this char, e.g. was ^A or whatever. ENDLOOP; }; -- of EditedStreamGetChar EditedStreamBackup: PROCEDURE[self: STREAM, char: CHARACTER] = { data: EditedStreamData = NARROW[self.streamData]; IF data.readyPos <= 0 OR data.ready[data.readyPos - 1] # char THEN Error[IllegalBackup, self]; data.readyPos _ data.readyPos - 1; }; -- of EditedStreamBackup EditedStreamDeliver: PROC [self: STREAM] = { data: EditedStreamData = NARROW[self.streamData]; bufferLength: NAT _ data.buffer.length; readyLength: NAT _ data.ready.length; readyPos: INT _ data.readyPos; FOR i: NAT IN [readyPos..readyLength) DO data.ready[i - readyPos] _ data.ready[i]; ENDLOOP; readyLength _ readyLength - readyPos; data.readyPos _ readyPos _ 0; WHILE bufferLength + readyLength >= data.ready.maxLength DO data.ready _ -- SIGNAL BufferOverFlow -- EnlargeBuffer[data.ready]; ENDLOOP; FOR i: NAT IN [0..bufferLength) DO data.ready[readyLength + i] _ data.buffer[i]; ENDLOOP; data.buffer.length _ 0; data.ready.length _ readyLength + bufferLength; data.lastReadyLength _ data.ready.length; }; -- of EditedStreamDeliver EditedStreamAppend: PROCEDURE[self: STREAM, char: CHARACTER] RETURNS[BOOLEAN _ FALSE] = { data: EditedStreamData = NARROW[self.streamData]; EraseWord: PROC = INLINE{ state: {between, alpha, done} _ between; IF data.buffer.length = 0 THEN {-- Signal[EmptyBuffer, self]; -- RETURN}; UNTIL data.buffer.length = 0 DO char: CHARACTER _ data.buffer[data.buffer.length-1]; SELECT char FROM IN ['A..'Z], IN ['a..'z], IN ['0..'9] => state _ alpha; ENDCASE => IF state = alpha THEN state _ done; IF state = done THEN EXIT; data.buffer.length _ data.buffer.length -1; IF data.echoTo # NIL THEN data.echoTo.EraseChar[char]; ENDLOOP; }; -- of EraseWord EraseLine: PROC = INLINE{ IF data.buffer.length = 0 THEN {-- Signal[EmptyBuffer, self]; -- RETURN}; UNTIL data.buffer.length = 0 DO -- deletes entire line. for buffered streams that accept more than one line of input. char: CHARACTER _ data.buffer[data.buffer.length - 1]; IF char = CR THEN EXIT; data.buffer.length _ data.buffer.length - 1; IF data.echoTo # NIL THEN data.echoTo.EraseChar[char]; ENDLOOP; }; -- of EraseLine PrintLine: PROC = INLINE{ IF data.echoTo = NIL THEN RETURN; PutChar[data.echoTo, CR]; FOR i: NAT IN [0..data.buffer.length) DO IF data.echoTo # NIL THEN PutChar[data.echoTo, data.buffer[i]]; ENDLOOP; }; -- of PrintLine AppendChar: PROC [self: STREAM] = { WHILE data.buffer.length >= data.buffer.maxLength DO data.buffer _ -- SIGNAL BufferOverFlow -- EnlargeBuffer[data.buffer]; ENDLOOP; data.buffer[data.buffer.length] _ char; data.buffer.length _ data.buffer.length + 1; IF data.echoTo # NIL THEN PutChar[data.echoTo, char]; }; -- of AppendChar SELECT char FROM Ascii.DEL => {EditedStreamDeliver[self]; Signal[Rubout, self]}; Ascii.ControlA, Ascii.BS => EditedStreamEraseChar[self, char -- char argument ignored -- ] ; Ascii.ControlW => EraseWord[]; Ascii.ControlQ => EraseLine[]; Ascii.ControlR => PrintLine[]; ENDCASE => IF char = ESC AND data.buffer.length = 0 THEN FOR i: NAT IN [0..data.lastReadyLength-1) DO -- skip the character that caused the deliver, typically CR. char _ data.ready[i]; AppendChar[self]; ENDLOOP ELSE {AppendChar[self]; RETURN[TRUE]; }; RETURN[FALSE]; }; -- of EditedStreamAppend BufferedOutputStreamData: TYPE = REF BufferedOutputStreamRecord; -- describes the streamData field for streams that read from ropes. BufferedOutputStreamRecord: TYPE = MONITORED RECORD[ deliverWhen: DeliverWhenProc, buffer: REF TEXT, howOften: INT, pos: INT _ 0 ]; BufferedOutputStreamProcs: REF StreamProcs _ NIL; CreateBufferedOutputStream: PUBLIC PROC [stream: STREAM, deliverWhen: DeliverWhenProc _ IsACR, bufferSize: NAT _ 256] RETURNS [s: STREAM] = { IF BufferedOutputStreamProcs = NIL THEN BufferedOutputStreamProcs _ CreateRefStreamProcs[ putChar: BufferedOutputStreamPutChar, eraseChar: BufferedOutputStreamEraseChar, reset: BufferedOutputStreamReset, flush: BufferedOutputStreamFlush, currentPosition: BufferedOutputStreamPosition, -- mostly for performance, doesn't cost since we have to look at each character anyway. name: "Buffered" ]; s _ CreateProcsStream[ streamProcs: BufferedOutputStreamProcs, streamData: Zone.NEW[BufferedOutputStreamRecord _ [ deliverWhen: deliverWhen, howOften: bufferSize, -- change name of this to howOften buffer: NEW[TEXT[100]] ]], backingStream: stream]; }; -- of CreateBufferedOutputStream maxBufferSize: INT _ 16384; BufferedOutputStreamPutChar: PROC[self: STREAM, char: CHARACTER] = { data: BufferedOutputStreamData = NARROW[self.streamData]; buffer: REF TEXT _ data.buffer; IF buffer.length = buffer.maxLength THEN { IF buffer.length < maxBufferSize THEN data.buffer _ buffer _ EnlargeBuffer[buffer] -- dont confuse buffer size overflowing with desire to flush ELSE self.Flush[]; -- can't have more than 2^15 characters in a ref text. }; buffer[buffer.length] _ char; buffer.length _ buffer.length + 1; IF char = CR THEN data.pos _ 0 ELSE data.pos _ data.pos + 1; IF (data.howOften > 0 AND buffer.length >= data.howOften) OR (data.deliverWhen # NIL AND data.deliverWhen[char, self]) THEN self.Flush[]; }; -- of BufferedOutputStreamPutChar BufferedOutputStreamEraseChar: PROC[self: STREAM, char: CHARACTER] = { data: BufferedOutputStreamData = NARROW[self.streamData]; buffer: REF TEXT _ data.buffer; IF buffer.length = 0 THEN EraseChar[self.backingStream, char] ELSE IF char # buffer[buffer.length - 1] THEN ERROR IO.Error[IllegalBackup, self] ELSE buffer.length _ buffer.length - 1; }; -- of BufferedOutputStreamEraseChar BufferedOutputStreamReset: PROC[self: STREAM] = { data: BufferedOutputStreamData = NARROW[self.streamData]; data.buffer.length _ 0; data.pos _ 0; self.backingStream.Reset[]; }; -- of BufferedOutputStreamReset BufferedOutputStreamFlush: PROC[self: STREAM] = { data: BufferedOutputStreamData = NARROW[self.streamData]; self.backingStream.PutBlock[data.buffer]; data.buffer.length _ 0; }; -- of BufferedOutputStreamFlush BufferedOutputStreamPosition: PROC[self: STREAM] RETURNS[position: INT] = { data: BufferedOutputStreamData _ NARROW[self.streamData]; RETURN[data.pos]; }; -- of BufferedOutputStreamPosition DribbleStreamData: TYPE = REF DribbleStreamRecord; DribbleStreamRecord: TYPE = RECORD[ dribbleTo: STREAM, echoTo: STREAM, singleCharacter: CHARACTER _ NUL, autoFlush, singleFlg: BOOLEAN _ FALSE, flushCount, pos: INT _ 0, flushEveryNChars: INT ]; DribbleStreamProcs: REF StreamProcs _ NIL; CreateDribbleStream: PUBLIC PROCEDURE [stream: STREAM, dribbleTo: STREAM, flushEveryNChars: INT _ -1] RETURNS [s: STREAM] = { IF DribbleStreamProcs = NIL THEN DribbleStreamProcs _ CreateRefStreamProcs[ getChar: DribbleStreamGetChar, putChar: DribbleStreamPutChar, putBlock: DribbleStreamPutBlock, -- cant default, since default putblock calls backing stream which may have a putBlock, but which will not cause characters to be dribbled. flush: DribbleStreamFlush, eraseChar: DribbleStreamEraseChar, backup: DribbleStreamBackup, -- backup implemented here, rather than defaulting to stream below so that character not dribbled twice setEcho: DribbleStreamSetEcho, currentPosition: DribbleStreamPosition, name: "Dribble" ]; s _ CreateProcsStream[ streamProcs: DribbleStreamProcs, streamData: Zone.NEW[DribbleStreamRecord _ [ dribbleTo: dribbleTo, echoTo: dribbleTo, autoFlush: flushEveryNChars > 0, flushEveryNChars: flushEveryNChars]], backingStream: stream ]; }; -- of CreateDribbleStream DribbleStreamGetChar: PROC[self: STREAM] RETURNS[char: CHARACTER] = { data: DribbleStreamData = NARROW[self.streamData]; IF data.singleFlg THEN {data.singleFlg _ FALSE; RETURN[data.singleCharacter]}; char _ GetChar[self.backingStream]; data.singleCharacter _ char; IF data.echoTo # NIL THEN PutChar[data.echoTo, char]; }; -- of DribbleStreamGetChar DribbleStreamPutChar: PROC[self: STREAM, char: CHARACTER] = { data: DribbleStreamData = NARROW[self.streamData]; IF char = CR THEN data.pos _ 0 ELSE data.pos _ data.pos + 1; PutChar[data.dribbleTo, char]; PutChar[self.backingStream, char]; IF data.autoFlush THEN {data.flushCount _ data.flushCount + 1; IF data.flushCount >= data.flushEveryNChars THEN {data.dribbleTo.Flush[]; data.flushCount _ 0}; }; }; -- of DribbleStreamPutChar DribbleStreamPutBlock: PROC[self: STREAM, block: REF READONLY TEXT, startIndex: NAT, stopIndexPlusOne: NAT] = { data: DribbleStreamData = NARROW[self.streamData]; IF block # NIL THEN {end: NAT _ MIN[block.length, stopIndexPlusOne]; FOR i: NAT IN [startIndex..end) DO PutChar[self, block[i]]; ENDLOOP; }; }; -- of DribbleStreamPutBlock DribbleStreamBackup: PROCEDURE[self: STREAM, char: CHARACTER] = { data: DribbleStreamData = NARROW[self.streamData]; IF data.singleFlg OR data.singleCharacter # char THEN Error[IllegalBackup, self]; data.singleCharacter _ char; data.singleFlg _ TRUE; }; -- of DribbleStreamBackup DribbleStreamEraseChar: PROC[self: STREAM, char: CHARACTER] = { data: DribbleStreamData = NARROW[self.streamData]; EraseChar[data.dribbleTo, char]; EraseChar[self.backingStream, char]; }; -- of DribbleStreamEraseChar DribbleStreamSetEcho: PROC[self: STREAM, echoTo: STREAM] RETURNS[oldEcho: STREAM] = { data: DribbleStreamData = NARROW[self.streamData]; oldEcho _ data.echoTo; data.echoTo _ echoTo; }; -- of DribbleStreamSetEcho DribbleStreamFlush: PROC[self: STREAM] = { data: DribbleStreamData = NARROW[self.streamData]; Flush[data.dribbleTo]; Flush[self.backingStream]; }; -- of DribbleStreamFlush DribbleStreamReset: PROC[self: STREAM] = { data: DribbleStreamData = NARROW[self.streamData]; Reset[data.dribbleTo]; Reset[self.backingStream]; }; -- of DribbleStreamReset DribbleStreamPosition: PROC [self: STREAM] RETURNS[position: INT] = { data: DribbleStreamData = NARROW[self.streamData]; RETURN[data.pos]; }; -- of DribbleStreamPosition FilterCommentsStreamData: TYPE = REF FilterCommentsStreamRecord; FilterCommentsStreamRecord: TYPE = RECORD[ lastChar: CHARACTER _ NULL ]; FilterCommentsStreamProcs: REF IO.StreamProcs _ NIL; CreateFilterCommentsStream: PUBLIC PROCEDURE [onStream: IO.STREAM] RETURNS [IO.STREAM] = { IF FilterCommentsStreamProcs = NIL THEN FilterCommentsStreamProcs _ IO.CreateRefStreamProcs[ getChar: FilterCommentsStreamGetChar, name: "Filter Comments" ]; RETURN[IO.CreateProcsStream[streamProcs: FilterCommentsStreamProcs, backingStream: onStream, streamData: NEW[FilterCommentsStreamRecord _ []]]]; }; -- of CreateFilterCommentsStream FilterCommentsStreamGetChar: PROC[self: IO.STREAM] RETURNS [char: CHARACTER] = { data: FilterCommentsStreamData = NARROW[self.streamData]; SELECT char _ self.backingStream.GetChar[] FROM '- => IF data.lastChar # '\\ AND NOT self.backingStream.EndOf[] AND self.backingStream.PeekChar[] = '- THEN -- is a comment. skip to end of comment. {[] _ self.backingStream.GetChar[]; -- discard the -. DO SELECT (char _ self.backingStream.GetChar[]) FROM '\n => EXIT; -- and return the CR. '\\ => [] _ self.backingStream.GetChar[]; -- skip the next character. '- => IF self.backingStream.GetChar[] = '- THEN -- end of comment {char _ self.backingStream.GetChar[]; EXIT}; ENDCASE; ENDLOOP; }; ENDCASE; data.lastChar _ char; }; -- of FilterCommentsStreamGetChar END. 11-Apr-82 20:47:14 implemented erasechar for editedstream, primarily so could do ESC right in userexec. August 23, 1982 11:36 am removed CreateOutputStreamtoSTRING. moved it to CoPilotIO. October 4, 1982 8:52 pm fixed dribble stream so that flush will flush the backing stream in the case that autoFlush has been specified. April 13, 1983 11:18 am fixed dribble stream so that an explicit call to flush flushes both streams., October ”edited by Teitelman April 19, 1983 4:09 pm implements various streams. decided not to raise signal but to call this procedure instead. Nowhere Stream noWhereStream is a convenient output stream which discards its characters NoInput Stream noInputStream is a input stream for which CharsAvail is FALSE, EndOf is TRUE, but any attempt to get characters raises IO.StreamClosed Input Stream From a Rope, RIS CreateInputStreamFromRope is used when user has a rope in hand and wants to read characters from it. Characters can be loaded into an already existing stream by specifying a non-Null argument for oldStream. Note that no echoing of input is performed. Output Stream To a Rope, ROS h puts chars into a rope which can be retrieved via GetOutputStreamRope. Input Stream From Ref Text CreateInputStreamFromText is used when user has a string in hand and wants to read characters from it. Characters can be loaded into an already existing stream by specifying a non-Null argument for oldStream. Note that no echoing of input is performed. Output Stream to Ref Text h puts characters into text. If oldStream is non-NIL, it is reused rather than allocating space for a new handle. Edited Stream creates an edited stream on top of a given stream. implements ^A, ^W etc. used to use ready.length and get the characters from the end, and reset the length, i.e. in reverse order. This change was made so that could define a GetIndex and SetIndex, which was done in order to allow calls to MesaScanner on an edited stream. Note: for the purposes of get and set index, think of the editedstream as a rope input stream for which the rope is the characters currently available to the stream, i.e. those already delivered.. erases last character from buffer, and if being echoed, from echo stream move any unread characters in ready over. (Can this ever happen?) Buffered Stream creates an output stream on top of out such that characters are buffered until (a) the bufferSize is exceeded (actually there is no buffer now, and bufferSize will be changed to howOften), or (b) deliverWhen[char] is TRUE, or (c) an explicit Flush is performed, at which point the characters are output to out using PutBlock, while out is effectively locked until the flush is finished. IF deliverWhen = NIL is equivalent to a procedure which always returns FALSE. IF bufferSize is 0, then buffer is effectively infinite, and a rope output stream is used to store the characters. Dribble Stream Dribble Streams. Characters input from or output to handle will also be output to the indicated handle. Filter Comments Stream Edited on April 13, 1983 11:17 am, by Teitelman activate on DEL so client can flush unwanted characters by doing a WHILE CharsAvail DO GetChar. changes to: EditedStreamAppend Edited on April 19, 1983 4:07 pm, by Teitelman "I would like to be able to do a GetLength[] on a strm created by ROS". changes to: ROS, OutputRopeStreamLength, OutputRopeStreamClose Κ°– "Cedar" style˜J˜JšΟc+™+Jš™J˜šΟk ˜ Jš œžœžœ*žœžœžœ˜LJšžœžœΌžœžœ+˜ŒJšœ žœ˜Jšœžœ!˜.Jšœžœ%˜/J˜J˜—J˜Jš Ρblnœžœž œžœžœ˜qJ˜Jšžœžœ˜J˜Jšžœžœ ˜J˜šžœž˜ J˜—Jšœžœžœžœ˜J˜JšΟnœžœžœžœžœžœžœžœžœΧ˜ŸJ˜J™?š  œžœžœžœžœ žœžœ˜EJšœ žœžœ˜*šžœžœžœž˜$J˜Jšžœ˜—J˜"Jšžœ ˜J˜—head™JšI™Iš  œžœžœžœ˜4Jšœ@žœ˜E—Jš œž œžœž œ˜>—™Jš†™†J˜š  œžœ4˜SJšœ˜Jšœ˜J˜Jšœžœ˜—J˜š œžœžœžœžœžœžœ˜[Jšœ˜—J˜Jš  œžœž œžœžœžœžœ˜G—™Jšϋ™ϋJ˜Jš œžœ˜Jš œžœžœ˜7š œžœžœ˜%Jšœžœžœ˜Jšœžœ˜ J˜—J˜Jš œžœžœ˜,J˜š œ œžœž œžœ žœžœžœžœ˜nšžœ žœž˜Jšž˜Jšœžœ˜9J˜J˜ Jšžœ ˜Jšžœ˜—šžœžœžœ-˜OJ˜ J˜J˜J˜"J˜"J˜$Jšœ!O˜pJ˜J˜—Jšœ1žœ0˜dJšœ˜Jšœ˜J˜—š  œžœžœžœž œ˜HJšœžœ˜4Jšžœ$žœžœ˜CJ˜'J˜Jšœ˜J˜—š  œžœžœžœžœ˜>Jšœžœ˜4Jšžœ%˜+Jšœ˜J˜—š œžœžœ˜,Jšœžœ˜4J˜ Jšœ˜J˜—š  œžœžœžœžœ˜Jšœžœ˜4Jšžœžœ,žœ˜^J˜Jšœ˜——™JšH™HJ˜Jš œžœ˜Jš  œžœ˜Jš œžœžœD˜}š œžœžœžœžœžœžœ˜HJ˜—Jš œžœžœ˜-J˜š  œžœžœžœžœ˜Bšžœžœžœ.˜QJ˜!J˜J˜Jšœ˜J˜"J˜J˜—Jšœ˜Jšžœ0žœ!žœ'˜„Jšœ˜J˜—š œžœžœž œ˜@Jšœžœ˜5Jšœžœžœ ˜JšžœžœO˜sJ˜J˜Jšœ˜ J˜—š œžœžœ˜-Jšœžœ˜5Jšœ žœ˜J˜Jšœ˜J˜—š œžœžœ˜-Jšœžœ˜5Jšœžœžœ ˜Jšžœ žœžœžœO˜zJšœ˜J˜—š  œžœžœžœ žœ˜DJšœžœ˜5Jšžœ žœžœžœ ˜BJšœ)˜)Jšœ˜J˜—š  œžœžœ žœžœ˜EJšœžœ˜5J˜"Jšœ žœ˜Jšœ˜J˜—š  œžœžœžœžœžœ˜BJšœžœ˜5J˜Jšœ3˜3Jšžœ ˜Jšœ˜——™Jšύ™ύJš œžœžœ˜-Jš œžœžœžœžœžœžœžœ˜PJš œžœžœ˜'J˜š œžœž œžœžœžœžœ žœžœžœžœ˜yšžœžœžœ(˜EJ˜J˜J˜J˜J˜J˜J˜J˜—šžœ žœž˜Jšžœ˜Jšœžœ˜4J˜J˜ Jšžœ ˜Jšžœ˜—Jšœ5žœ)˜aJšžœ ˜Jšœ˜"J˜—š  œžœžœžœž œ˜CJšœžœ˜/Jšžœžœžœ˜;J˜J˜Jšœ˜J˜—š œžœžœ˜'Jšœžœ˜/J˜ Jšœ˜J˜—š  œžœžœžœžœ˜9Jšœžœ˜/Jšžœ˜#Jšœ˜J˜—š  œžœžœžœžœ˜7Jšœžœ˜/Jšžœ ˜Jšœ˜J˜—š œžœžœ žœ˜6Jšœžœ˜/J˜Jšœ˜J˜—š  œžœžœžœ žœ˜@Jšœžœ˜/Jšžœ˜Jšœ˜——™Jšq™qJš œžœžœ˜)J˜š œžœž œžœžœ žœžœžœžœ˜išžœžœžœ*˜IJ˜J˜J˜J˜J˜!J˜J˜—Jšžœ žœžœ7˜Nš žœžœžœžœ&˜[J˜—J˜Jšžœ ˜Jšœ˜!J˜—š œžœžœž œ˜Jš œžœžœžœžœžœ˜3Jšžœžœ˜6Jšœ˜J˜—š œžœžœ˜)Jš œžœžœžœžœžœ˜3J˜Jšœ˜J˜—š  œžœžœžœžœ˜9Jš œžœžœžœžœžœ˜3Jšžœ˜Jšœ˜J˜—š œžœžœ žœ˜8Jš œžœžœžœžœžœ˜3J˜Jšœ˜——™JšI™IJ˜Jš œžœžœ˜0š œžœžœ˜"Jšœžœžœ˜Jšœžœžœ˜šœ žœ˜Jšœω™ω—Jšœžœ ˜ J˜Jšœžœž˜J˜J˜—Jš œžœžœ˜)J˜Jš œžœžœžœ˜4J˜š œžœž œžœ žœ(žœžœ˜šžœžœžœ*˜IJ˜J˜J˜J˜J˜J˜J˜J˜J˜!J˜J˜—˜J˜šœžœ˜-Jšœžœžœ˜Jšœžœžœ˜J˜J˜—J˜J˜—šœ˜Jšœ˜Jšœ˜Jšœ˜Jš œžœžœžœžœ2˜vJšœ˜—šœ˜Jšœ˜Jšœ˜Jšœ˜Jš œžœžœžœžœ žœ˜SJšœ˜—Jšœžœ žœ˜+JšœΛ˜δJšœ˜J˜—š œžœžœžœ ˜fJšœžœ˜1J˜J˜J˜—J˜š  œžœžœžœ žœ˜CJšœžœ˜1Jšžœžœžœ žœ˜UJ˜—J˜š œž œžœ˜.Jšœžœ˜1J˜J˜J˜J˜Jšžœžœžœ˜-Jšœ˜J˜—š œž œžœ˜.Jšœžœ˜1J˜J˜J˜Jšžœžœžœ˜-Jšœ˜J˜—š  œž œžœžœžœ˜@Jšœžœ˜1Jšžœ$žœ˜JJšœ˜J˜—J™Δš  œž œžœžœžœ˜?Jšœžœ˜1Jšžœ˜Jšœ˜J˜—š œž œžœ žœ˜=Jšœžœ˜1Jš žœžœ žœžœžœ˜NJšœ˜Jšœ˜J˜—š  œž œžœ žœžœ žœ˜ZJšœžœ˜1J˜J˜Jšœ˜J˜—š  œž œžœžœžœ˜@Jšœžœ˜1Jšžœ#'œ!˜vJšœ˜J˜JšH™H—š œž œžœž œ˜CJšœžœ˜1šžœžœ˜Jšž˜J˜+J˜,Jšžœžœžœ˜7Jšž˜—Jšžœžœ˜(Jšœ˜J˜—š  œž œžœžœž œ˜IJšœžœ˜1š œžœžœ˜"J˜!J˜"Jšœ˜J˜—šž˜Jšžœ#žœžœ˜HJšœ$T˜xJšžœ žœžœΊ˜Jšžœ˜—šœ˜J˜——š œž œžœž œ˜@Jšœžœ˜1Jšžœžœ&žœ˜^Jšœ"˜"Jšœ˜J˜—š œžœžœ˜,Jšœžœ˜1Jšœžœ˜'Jšœ žœ˜%Jšœ žœ˜JšœA™Ašžœžœžœžœ˜)J˜)Jšžœ˜—Jšœ%˜%Jšœ˜šžœ4ž˜;Jšœ ž œ-˜CJšžœ˜—šžœžœžœžœ˜#J˜-Jšžœ˜—J˜J˜/J˜)Jšœ˜J˜—š œž œžœž œžœžœžœ˜YJšœžœ˜1J˜š  œžœžœ˜J˜*Jšžœžœ ž œ˜Išžœž˜Jšœž œ%˜4šžœž˜šžœ žœ žœ ˜(J˜—Jšžœžœžœ˜.—Jšžœžœžœ˜J˜+Jšžœžœžœ˜6—Jšžœ˜Jšœ˜J˜—š  œžœžœ˜Jšžœžœ ž œ˜IšžœžœU˜uJšœž œ'˜6Jšžœžœžœžœ˜J˜,Jšžœžœžœ˜6Jšžœ˜—Jšœ˜J˜—š  œžœžœ˜Jšžœžœžœžœ˜!Jšœžœ˜Jšžœžœžœž˜(Jšžœžœžœ&˜?Jšžœ˜Jšœ˜J˜—š  œžœžœ˜#šžœ-ž˜4Jšœž œ.˜EJšžœ˜—J˜'J˜,Jšžœžœžœ˜5Jšœ˜J˜—šžœž˜J˜Jšœžœ6˜?—˜Jšœžœ&œ˜]—˜J˜—˜J˜—˜J˜—˜šžœ˜ šžœžœžœž˜-š žœžœžœžœ<˜iJ˜J˜Jšž˜—šž˜J˜Jšžœžœ˜ J˜J˜————Jšžœžœ˜Jšœ˜——™Jš#ΠcrŒ‘‘3™‚JšM™MJ™rJ˜Jš œžœžœD˜…š œžœžœ˜4J˜Jšœžœžœ˜Jšœ žœ˜Jšœžœ˜ J˜J˜—Jš œžœžœ˜1J˜š œžœžœ žœ4žœžœžœ˜šžœžœžœ2˜YJ˜%J˜)J˜!J˜!Jšœ0W˜‡J˜J˜—˜J˜(šœžœ˜4J˜Jšœ9˜9Jšœžœžœ ˜—J˜—Jšœ ˜#J˜—Jšœžœ ˜š œžœžœž œ˜DJšœ!žœ˜9Jšœžœžœ˜šžœ"ž˜*Jšžœžœ/<˜‘Jšžœ6˜IJ˜—J˜J˜"Jšžœžœžœžœ˜<š žœžœ!žœžœžœž˜{J˜ —Jšœ!˜$J˜—š œžœžœž œ˜FJšœ!žœ˜9Jšœžœžœ˜Jšžœžœ$˜=Jš žœžœ"žœžœžœ˜QJšžœ#˜'Jšœ#˜&J˜—š œžœžœ˜1Jšœ!žœ˜9Jšœ˜Jšœ ˜ Jšœ˜Jšœ˜"J˜—š œžœžœ˜1Jšœ!žœ˜9Jšœ)˜)J˜Jšœ˜"—J˜š  œžœžœžœ žœ˜LJšœ!žœ˜9Jšžœ ˜Jšœ"˜%——™Jšg™gJš œžœžœ˜2š œžœžœ˜#Jšœ žœ˜Jšœžœ˜Jšœž œžœ˜!Jšœžœžœ˜&Jšœžœ˜Jšœž˜J˜J˜—Jš œžœžœ˜*J˜š œžœž œ žœ žœžœžœžœ˜}šžœžœžœ+˜KJ˜J˜Jšœ!‹˜¬J˜J˜"Jšœg˜„J˜J˜'J˜J˜—˜J˜ šœžœ˜-J˜J˜J˜ J˜%—J˜J˜—Jšœ˜J˜—š  œžœžœžœž œ˜EJšœžœ˜2Jšžœžœžœžœ˜NJ˜#J˜Jšžœžœžœ˜5Jšœ˜J˜—š œžœžœž œ˜=Jšœžœ˜2Jšžœžœžœžœ˜—J™——…—gžβ