DIRECTORY Ascii USING [DEL], Commander USING [CommandProc, Register], Convert, EditedStream USING [Rubout], FS USING [StreamOpen], Icons USING [IconFlavor, NewIconFromFile], InputFocus USING [Focus, GetInputFocus, PopInputFocus, PushInputFocus], IO, PopUpSelection USING [Request], Process USING [Detach, Pause, MsecToTicks, SetTimeout, SecondsToTicks, Abort, CheckForAbort, DisableTimeout], Rope USING [ROPE, Fetch, FromChar, Find, Length, Match, Equal], RuntimeError USING [UNCAUGHT], TerminalIO, TerminalIOExtras, TypeScript USING [Create, PutRope], ViewerClasses USING [Viewer], ViewerIO USING [CreateViewerStreams, GetViewerFromStream], ViewerOps USING [PaintViewer, OpenIcon, EnumerateViewers, EnumProc], ViewerTools USING [SetSelection]; TerminalIOImpl: CEDAR MONITOR IMPORTS Commander, Convert, EditedStream, FS, Icons, InputFocus, IO, PopUpSelection, Process, Rope, RuntimeError, TypeScript, ViewerIO, ViewerOps, ViewerTools EXPORTS TerminalIO, TerminalIOExtras = BEGIN ROPE: TYPE = Rope.ROPE; UserAbort: PUBLIC SIGNAL = CODE; TimedOut: PUBLIC SIGNAL = CODE; viewer: ViewerClasses.Viewer _ NIL; in, out: IO.STREAM _ NIL; normalName: ROPE = "Terminal"; inputName: ROPE = "Terminal [Input Expected]"; normalIcon: Icons.IconFlavor; inputIcon: Icons.IconFlavor; del: ROPE = Rope.FromChar[Ascii.DEL]; log: IO.STREAM; shouldFlush: BOOL _ FALSE; occupied: BOOL _ FALSE; --module occupied next: CONDITION _ [timeout: Process.MsecToTicks[4000]]; --another client might try entering Enter: ENTRY PROC [] = { ENABLE UNWIND => NULL; WHILE occupied DO WAIT next ENDLOOP; occupied _ TRUE; }; Leave: ENTRY PROC [] = { ENABLE UNWIND => NULL; occupied _ FALSE; BROADCAST next; }; EnterReader: PROC [] = { v: ViewerClasses.Viewer _ viewer; WHILE v=NIL OR v.destroyed DO InternalCreate[FALSE]; v _ viewer ENDLOOP; v.inhibitDestroy _ TRUE; WHILE v=NIL OR v.destroyed DO InternalCreate[TRUE]; v _ viewer ENDLOOP; v.icon _ inputIcon; v.name _ inputName; ViewerOps.PaintViewer[v, caption]; InputFocus.PushInputFocus[v, $TerminalIO]; }; LeaveReader: PROC [] = { v: ViewerClasses.Viewer _ viewer; foc: InputFocus.Focus _ InputFocus.GetInputFocus[]; IF foc#NIL AND foc.info=$TerminalIO THEN InputFocus.PopInputFocus[]; IF v#NIL THEN { v.icon _ normalIcon; v.name _ normalName; v.inhibitDestroy _ FALSE; --conveniant here; usefull in all places where called ViewerOps.PaintViewer[v, caption]; }; }; ProcList: TYPE = LIST OF PROC; lockList: ProcList _ NIL; unLockList: ProcList _ NIL; AddLock: PUBLIC ENTRY PROC [lock, unLock: PROC] = { ENABLE UNWIND => NULL; IF unLock#NIL THEN unLockList _ CONS[unLock, unLockList]; IF lock#NIL THEN IF lockList=NIL THEN lockList _ LIST[lock] ELSE FOR l: ProcList _ lockList, l.rest DO IF l.rest=NIL THEN {l.rest _ LIST[lock]; EXIT} ENDLOOP; }; Lock: PROC = { FOR l: ProcList _ lockList, l.rest WHILE l#NIL DO l.first[! RuntimeError.UNCAUGHT => CONTINUE] ENDLOOP }; UnLock: PROC = { FOR l: ProcList _ unLockList, l.rest WHILE l#NIL DO l.first[! RuntimeError.UNCAUGHT => CONTINUE] ENDLOOP }; Create: PROC [protected: BOOL] = { InternalCreate[protected] }; InternalCreate: PROC [protected: BOOL] = { FindOldGuys: ViewerOps.EnumProc = { MakeOrphan: PROC [v: ViewerClasses.Viewer] = { IF v#NIL THEN { v.inhibitDestroy _ FALSE; v.icon _ typescript; v.name _ "orphaned split log viewer"; IO.PutRope[log, "log viewer orphaned\n"]; IO.Flush[log]; ViewerOps.PaintViewer[v, caption]; TypeScript.PutRope[v, "\n... log viewer orphaned ...\n"]; }; }; IF ~v.destroyed AND v.file=NIL AND v.class.flavor=$Typescript THEN IF Rope.Equal[v.name, normalName] OR Rope.Equal[v.name, inputName] THEN MakeOrphan[v ! RuntimeError.UNCAUGHT => CONTINUE]; }; ReallyCreate: PROC [] = { v: ViewerClasses.Viewer _ TypeScript.Create[info: [name: normalName, inhibitDestroy: protected, iconic: FALSE, icon: normalIcon, column: right]]; [in: in, out: out] _ ViewerIO.CreateViewerStreams[name: normalName, viewer: v, editedStream: TRUE]; viewer _ v; IO.PutRope[log, "log viewer created\n"]; IO.Flush[log]; }; oldOut: IO.STREAM _ out; oldIn: IO.STREAM _ in; IF oldOut#NIL AND oldIn#NIL AND ~protected THEN { v: ViewerClasses.Viewer; err: BOOL _ FALSE; v _ ViewerIO.GetViewerFromStream[oldOut]; IF v#NIL AND ~v.destroyed AND (v=ViewerIO.GetViewerFromStream[oldIn]) THEN { IO.Flush[oldOut ! RuntimeError.UNCAUGHT => {err _ TRUE; CONTINUE}]; IF ~err THEN IO.Reset[oldOut ! RuntimeError.UNCAUGHT => {err _ TRUE; CONTINUE}]; IF ~err THEN IO.Reset[oldIn ! RuntimeError.UNCAUGHT => {err _ TRUE; CONTINUE}]; IF ~err THEN { v.icon _ normalIcon; IF ~v.destroyed THEN { [in: in, out: out] _ ViewerIO.CreateViewerStreams[name: normalName, viewer: v, editedStream: TRUE]; viewer _ v; IO.PutRope[log, "usage of different LogViewer viewer\n"]; IO.Flush[log]; RETURN; }; }; }; }; ViewerOps.EnumerateViewers[FindOldGuys ! RuntimeError.UNCAUGHT => CONTINUE]; ReallyCreate[] }; IOState: TYPE = {ok, repeat, abort, undef}; InternalGetLine: PROC [promp: ROPE] RETURNS [line: ROPE, state: IOState_ok] = { ENABLE { IO.Error => IF ec=StreamClosed THEN {state_repeat; CONTINUE}; EditedStream.Rubout => {state_abort; IO.Reset[in]; CONTINUE}; RuntimeError.UNCAUGHT => {state_repeat; CONTINUE}; }; IF viewer.iconic THEN ViewerOps.OpenIcon[viewer]; InternalWrite[promp]; IO.Flush[out ! RuntimeError.UNCAUGHT => CONTINUE]; ViewerTools.SetSelection[viewer]; line _ IO.GetLineRope[in]; IF Rope.Find[line, del]>=0 THEN state_abort; }; RequestRope: PUBLIC PROC [text: ROPE _ NIL] RETURNS [r: ROPE] = { DoIt: PROC [] = {[r, state] _ InternalGetLine[text]}; state: IOState; DO ReaderProtected[DoIt]; SELECT state FROM ok => RETURN; repeat => WriteRope["XXX\n"]; abort => {WriteRope[" ..user abort\n"]; SIGNAL UserAbort} ; ENDCASE => {WriteRope[" ..??\n"]; SIGNAL UserAbort}; ENDLOOP; }; RequestChar: PUBLIC PROC [text: ROPE _ NIL] RETURNS [ch: CHAR] = { DO r: ROPE _ RequestRope[text]; i: INT _ Rope.Length[r]; IF i=0 THEN RETURN[15C]; IF i=1 THEN RETURN[Rope.Fetch[r, 0]]; WriteRope[" ?? (single char), please repeat: \n"]; ENDLOOP; }; RequestInt: PUBLIC PROC [text: ROPE _ NIL] RETURNS [i: INT] = { DO ok: BOOL _ TRUE; r: ROPE _ RequestRope[text]; i _ Convert.IntFromRope[r ! RuntimeError.UNCAUGHT => {ok_FALSE; CONTINUE}]; IF ok THEN RETURN; WriteRope[" ?? (integer), please repeat: \n"]; ENDLOOP; }; RequestSelection: PUBLIC PROC [label: ROPE, choice: LIST OF ROPE, text: ROPE _ NIL, default: NAT _ 0, timeOut: NAT] RETURNS [select: INT _ 0] = { DoIt: PROC [] = { IF text#NIL THEN InternalWrite[text]; IO.Flush[out ! RuntimeError.UNCAUGHT => CONTINUE]; Process.Pause[Process.MsecToTicks[5]]; --SILLY FLUSH DIDN'T WORK select _ PopUpSelection.Request[label, choice, NIL, NIL, default, timeOut]; }; IF (choice=NIL) OR (choice.first=NIL) THEN ERROR; ReaderProtected[DoIt]; }; Confirm: PUBLIC PROC [choice: ROPE, label: ROPE _ NIL, timeOut: NAT _ 0, onTimeOut: BOOL _ FALSE] RETURNS [BOOL] = { RETURN [ SELECT RequestSelection[label: label, choice: LIST[choice], timeOut: timeOut] FROM 1 => TRUE, -1 => onTimeOut, ENDCASE => FALSE ] }; xProcess: PROCESS; xStateCondition: CONDITION; xState: IOState _ undef; xResult: ROPE; RequestRopeWithTimeout: PUBLIC PROC [text: ROPE _ NIL, timeOut: NAT _ 0] RETURNS [ROPE] = { IF timeOut=0 THEN RETURN [RequestRope[text]]; RETURN [MyRequestRopeWithTimeout[text, timeOut]]; }; ReaderProtected: PROC [p: PROC] = { ENABLE UNWIND => UnLock[]; Lock[]; p[]; UnLock[]; }; MyRequestRopeWithTimeout: PROC [prompt: ROPE _ NIL, timeOut: NAT] RETURNS [result: ROPE_NIL] = { state: IOState; DoIt: PROC [] = { xState _ undef; IF timeOut=0 THEN TRUSTED {Process.DisableTimeout[@xStateCondition]} ELSE TRUSTED { Process.SetTimeout[@xStateCondition, Process.SecondsToTicks[timeOut]] }; ForkRopeGetter[prompt]; TRUSTED {JOIN xProcess}; state _ xState; result _ xResult; }; DO ReaderProtected[DoIt]; IF state=abort THEN SIGNAL UserAbort ELSE IF Rope.Find[result, del]>=0 THEN SIGNAL UserAbort ELSE IF state=undef THEN SIGNAL TimedOut ELSE EXIT ENDLOOP }; ForkRopeGetter: ENTRY PROC [prompt: ROPE_NIL] = { ENABLE UNWIND => NULL; xProcess _ FORK ForkedTryToGetRope[prompt]; WAIT xStateCondition; IF xState#ok AND xState#abort THEN TRUSTED {Process.Abort[xProcess]}; }; SetGlobalState: ENTRY PROC [s: IOState] = { ENABLE UNWIND => NULL; Process.CheckForAbort[]; xState _ s; BROADCAST xStateCondition; }; ForkedTryToGetRope: PROC [prompt: ROPE_NIL] = { ReallyTryToGetRope: PROC [prompt: ROPE] = { d: IOState; DoIt: PROC [] = { ENABLE { UNWIND => IO.Reset[in]; IO.Error => IF ec=StreamClosed THEN {d_repeat; IO.Reset[in]; CONTINUE}; EditedStream.Rubout => {d_abort; IO.Reset[in]; CONTINUE}; }; InternalWrite[prompt]; IF viewer.iconic THEN ViewerOps.OpenIcon[viewer]; xResult _ IO.GetLineRope[in]; d _ ok; }; DO d _ undef; DoIt[]; IF d=ok OR d=abort THEN {SetGlobalState[d]; RETURN}; IF d=repeat THEN InternalWrite["XXX\n"] ENDLOOP; }; ReallyTryToGetRope[prompt ! ABORTED => GOTO NeverMind] EXITS NeverMind => NULL; }; InternalWrite: PROC [text: ROPE] = { IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; IO.PutRope[out, text ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.PutRope[out, text]; CONTINUE } }]; IO.PutRope[log, text]; shouldFlush _ TRUE; }; WriteRope: PUBLIC PROC [text: ROPE] = { ENABLE UNWIND => Leave[]; Enter[]; InternalWrite[text]; Leave[] }; WriteRopes: PUBLIC PROC [t1, t2, t3: ROPE_NIL] = { ENABLE UNWIND => Leave[]; IF t1#NIL THEN InternalWrite[t1]; IF t2#NIL THEN InternalWrite[t2]; IF t3#NIL THEN InternalWrite[t3]; Leave[] }; WriteLn: PUBLIC PROC [] = { WriteRope["\n"]; }; WriteChar: PUBLIC PROC [ch: CHAR] = { WriteRope[Rope.FromChar[ch]]; }; WriteInt: PUBLIC PROC [i: INT] = { WriteRope[IO.PutFR1[format: " %g", value: IO.int[i]]]; }; Write: PUBLIC PROC [v1, v2, v3: IO.Value] = { WriteRope[IO.PutR[v1, v2, v3]]; }; Write1: PUBLIC PROC [value: IO.Value] = { WriteRope[IO.PutR1[value]]; }; WriteF: PUBLIC PROC [format: ROPE _ NIL, v1, v2, v3, v4, v5: IO.Value] = { ENABLE UNWIND => Leave[]; Enter[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; IO.PutF[out, format, v1, v2, v3, v4, v5 ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.PutF[out, format, v1, v2, v3, v4, v5]; CONTINUE } } ]; IO.PutF[log, format, v1, v2, v3, v4, v5]; shouldFlush _ TRUE; Leave[] }; WriteF1: PUBLIC PROC [format: ROPE _ NIL, value: IO.Value] = { WriteRope[IO.PutFR[format, value]]; }; TOS: PUBLIC PROC [] RETURNS [stream: IO.STREAM] = { IF viewer=NIL OR viewer.destroyed THEN Create[FALSE]; RETURN[IO.CreateStream[streamProcs: myStreamProcs, streamData: NIL]]; }; myStreamProcs: REF IO.StreamProcs _ IO.CreateStreamProcs[ variety: $output, class: $LogViewer, putChar: MyPutChar, putBlock: MyPutBlock, unsafePutBlock: MyUnsafePutBlock, flush: MyFlush, eraseChar: MyEraseChar, reset: MyFlush, close: MyClose ]; MyPutChar: PROC [self: IO.STREAM, char: CHAR] = { ENABLE UNWIND => Leave[]; Enter[]; IO.PutChar[out, char ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.PutChar[out, char]; CONTINUE } }]; IO.PutChar[log, char]; shouldFlush _ TRUE; Leave[]; }; MyPutBlock: PROC [self: IO.STREAM, block: REF READONLY TEXT, startIndex: NAT, count: NAT] = { ENABLE UNWIND => Leave[]; Enter[]; IO.PutBlock[out, block, startIndex, count ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.PutBlock[out, block, startIndex, count]; CONTINUE } }]; IO.PutBlock[log, block, startIndex, count]; shouldFlush _ TRUE; Leave[]; }; MyUnsafePutBlock: PROC [self: IO.STREAM, block: IO.UnsafeBlock] = { ENABLE UNWIND => Leave[]; Enter[]; IO.UnsafePutBlock[out, block ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.UnsafePutBlock[out, block]; CONTINUE } }]; IO.UnsafePutBlock[log, block]; shouldFlush _ TRUE; Leave[]; }; MyFlush: PROC [self: IO.STREAM] = { IO.Flush[log]; }; MyEraseChar: PROC [self: IO.STREAM, char: CHAR] = { }; MyClose: PROC [self: IO.STREAM, abort: BOOL] = { IO.Flush[log]; }; CreateLog: PROC [] = { log _ FS.StreamOpen[fileName: "TerminalIO.log", accessOptions: create, keep: 5]; IO.PutF1[log, "log file for TerminalIO created %g\n", IO.time[]] }; Command: Commander.CommandProc = { IF Rope.Match["*op*", cmd.commandLine, FALSE] THEN { WriteRope["open TerminalIO viewer\n"]; ViewerOps.OpenIcon[viewer]; }; IF Rope.Match["*log*", cmd.commandLine, FALSE] THEN { oldLog: IO.STREAM _ log; WriteRope["terminate and start new TerminalIO.log file\n"]; CreateLog[]; IO.Close[oldLog]; }; }; CheckInhibitDestroy: ENTRY PROC [] = { ENABLE UNWIND => NULL; v: ViewerClasses.Viewer _ viewer; IF v#NIL AND ~occupied THEN v.inhibitDestroy _ FALSE; }; Flusher: PROC [] = { flush: BOOL; DO Process.Pause[Process.SecondsToTicks[60]]; IF flush _ shouldFlush THEN { shouldFlush _ FALSE; IO.Flush[log ! RuntimeError.UNCAUGHT => CONTINUE]; }; CheckInhibitDestroy[]; ENDLOOP }; CreateLog[]; normalIcon _ Icons.NewIconFromFile["TerminalIO.icons", 0 ! RuntimeError.UNCAUGHT => CONTINUE]; inputIcon _ Icons.NewIconFromFile["TerminalIO.icons", 1 ! RuntimeError.UNCAUGHT => CONTINUE]; AddLock[Enter, Leave]; AddLock[EnterReader, LeaveReader]; Commander.Register["///Commands/TerminalIO", Command, "usage {~ | open | newlog}"]; TRUSTED {Process.Detach[FORK Flusher]}; END. .TerminalIOImpl.mesa Copyright c 1983, 1986 by Xerox Corporation. All rights reserved. Created by Christian Jacobi August 3, 1983 9:54 am Last edited by: Christian Jacobi, September 2, 1986 1:47:51 pm PDT --using a backing file for viewer makes log unaccessible --whether log file should be flushed -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --Terminals own locking procedures -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --Lock, UnLock mechanism -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --creation of viewer --its ok to risk creating accidently too many streams or viewer's --since this won't cause any wedge but only discomfort [orphans lying around] --afterwards in any case: viewer #NIL (doesnt really mean good, but certainly not NIL) --try re-using old pieces, but only if not from input --we dare trying to reuse a split viewer, --but first we try whether it wont cause errors --try to mark other [split] viewers as bad --the actual creation -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --input procedures -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --timed out input -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --output procedures --asking for viewer.destroyed helps to discover bad splits, but is not mandatory --actual inline usage of WriteF to allow client usage of formatting --an output stream which writes its output into the terminal -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --directly flushing after each output would make LogViewer far to slow 7e "cedar" stylecode "cedar" styleK "cedar" style m7BK "cedar" style22K "cedar" styleBK "cedar" style "cedar" stylek K "cedar" styleK "cedar" style (K "cedar" styleK "cedar" style  K "cedar" styleK "cedar" style"-K "cedar" style 7GK "cedar" styleK "cedar" style K "cedar" style`mK "cedar" style2BK "cedar" style K "cedar" style K "cedar" styleK "cedar" style #K "cedar" style K "cedar" style ,:K "cedar" style 8GK "cedar" style !K "cedar" style "cedar" stylenK "cedar" style#[K "cedar" style&K "cedar" styleK "cedar" styleK "cedar" styleK "cedar" styleK "cedar" style !K "cedar" style  K "cedar" styleK "cedar" style#K "cedar" style K "cedar" styleK "cedar" style K "cedar" style "1K "cedar" styleK "cedar" styleK "cedar" style%K "cedar" style "cedar" styleK "cedar" style8 "cedar" style K "cedar" style$K "cedar" styleK "cedar" style;;K "cedar" style""K "cedar" styleK "cedar" style c)K "cedar" style +#]K "cedar" styleK "cedar" style "cedar" styleK "cedar" styleK "cedar" style $K "cedar" style K "cedar" styleK "cedar" style "cedar" styleK "cedar" styleK "cedar" style K "cedar" style K "cedar" styleK "cedar" style "cedar" style K "cedar" style!!K "cedar" style  HK "cedar" styleK "cedar" style  GK "cedar" styleK "cedar" styleK "cedar" style""K "cedar" style**K "cedar" styleK "cedar" style "cedar" style K "cedar" style!!K "cedar" style33K "cedar" styleD "cedar" styleK "cedar" styleK "cedar" styleK "cedar" style5OK "cedar" style"K "cedar" styleK "cedar" styleK "cedar" styleK "cedar" style88K "cedar" styleK "cedar" styleK "cedar" style K "cedar" styleK "cedar" styleK "cedar" style "cedar" style 3K "cedar" styleK "cedar" style9 "cedar" styleK "cedar" style  * "cedar" style "cedar" style %K "cedar" style  .K "cedar" styleK "cedar" styleK "cedar" style "cedar" style "cedar" style 1K "cedar" style,K "cedar" styleK "cedar" styleK "cedar" style "cedar" style "cedar" style"3K "cedar" style,K "cedar" styleK "cedar" styleK "cedar" styleK "cedar" style;;K "cedar" styleK "cedar" style "cedar" style "K "cedar" styleBBK "cedar" styleNNK "cedar" styleK "cedar" styleK "cedar" style "cedar" style *K "cedar" styleNbW "cedar" style # "cedar" style . "cedar" styleK "cedar" styleK "cedar" styleK "cedar" style%%K "cedar" style( 8K "cedar" style""K "cedar" style99K "cedar" styleK "cedar" style "cedar" style B "cedar" style GK "cedar" style2K "cedar" style "cedar" style K "cedar" styleh$K "cedar" style]cK "cedar" style K "cedar" style&(K "cedar" style K "cedar" styleK "cedar" style5K "cedar" styleK "cedar" style "cedar" style  2K "cedar" styleK "cedar" styleK "cedar" style)) "cedar" style )MK "cedar" style**K "cedar" style//K "cedar" style CK "cedar" style  PK "cedar" style  O "cedar" styleK "cedar" style "cedar" styleK "cedar" style]cK "cedar" style K "cedar" style8 HK "cedar" styleK "cedar" styleK "cedar" styleK "cedar" styleK "cedar" styleK "cedar" style*K "cedar" style6LK "cedar" styleK "cedar" styleK "cedar" styleK "cedar" styleK "cedar" style88K "cedar" styleK "cedar" styleK "cedar" style +K "cedar" style "cedar" style  O "cedar" styleK "cedar" style =K "cedar" style% =K "cedar" style 2K "cedar" styleK "cedar" style1K "cedar" styleK "cedar" style2K "cedar" style!!K "cedar" styleK "cedar" style ,K "cedar" styleK "cedar" style "cedar" style AK "cedar" style+5K "cedar" style "cedar" styleK "cedar" style "cedar" styleK "cedar" style K "cedar" styleK "cedar" style) K "cedar" style #K "cedar" style "cedar" style bk 3K "cedar" style<