DIRECTORY BasicTime USING [earliestGMT, GMT, Now, Period], Commander USING [CommandProc, Register], Convert USING [IntFromRope, Error], Icons USING [IconFlavor, NewIconFromFile], InputFocus, IO, Menus, PFS, PFSCanonicalNames, PFSNames, PopUpSelection USING [Request], Process, Rope, RuntimeError USING [UNCAUGHT], SystemNames, TerminalIO, TiogaMenuOps USING [Open], TypeScript USING [Create, PutRope], UserProfile USING [Number], ViewerClasses USING [Viewer], ViewerIO USING [CreateViewerStreams, GetViewerFromStream], ViewerOps USING [PaintViewer, OpenIcon, EnumerateViewers, EnumProc], ViewerTools USING [SetSelection]; TerminalIOImpl: CEDAR MONITOR IMPORTS BasicTime, <> Commander, Convert, Icons, InputFocus, IO, Menus, PFS, PFSCanonicalNames, PFSNames, PopUpSelection, Process, Rope, RuntimeError, SystemNames, TiogaMenuOps, TypeScript, UserProfile, ViewerIO, ViewerOps, ViewerTools EXPORTS TerminalIO = 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 Viewer"; inputName: ROPE = "Terminal Viewer [Input Expected]"; normalIcon, inputIcon: Icons.IconFlavor; del: ROPE = "\177"; log: IO.STREAM; shouldFlush: BOOL _ FALSE; occupied: BOOL _ FALSE; --module occupied next: CONDITION; -- 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; Lock[]; Enter[]; v _ 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]; ViewerTools.SetSelection[v];--order! SetSelection fools with the InputFocus! a feature }; LeaveReader: PROC [] = { v: ViewerClasses.Viewer _ viewer; foc: InputFocus.Focus _ InputFocus.GetInputFocus[]; 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]; }; IF foc#NIL AND foc.info = $TerminalIO THEN InputFocus.PopInputFocus[]; Leave[]; Free[]; }; ProcList: TYPE = LIST OF PROC; lockList: ProcList _ NIL; freeList: ProcList _ NIL; AddLock: PUBLIC ENTRY PROC [lock, free: PROC] = { ENABLE UNWIND => NULL; IF free#NIL THEN freeList _ CONS[free, freeList]; 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 }; Free: PROC = { FOR l: ProcList _ freeList, l.rest WHILE l#NIL DO l.first[! RuntimeError.UNCAUGHT => CONTINUE] ENDLOOP }; ReaderProtected: PROC [p: PROC] = { ENABLE UNWIND => LeaveReader[]; EnterReader[]; p[]; LeaveReader[]; }; 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 terminal viewer"; IO.PutRope[log, "terminal viewer orphaned\n"]; IO.Flush[log]; ViewerOps.PaintViewer[v, caption]; TypeScript.PutRope[v, "\n... terminal 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]]; Menus.AppendMenuEntry[v.menu, Menus.CreateEntry[ name: "Previous", fork: TRUE, proc: PreviousLog ]]; [in: in, out: out] _ ViewerIO.CreateViewerStreams[name: normalName, viewer: v, editedStream: TRUE]; viewer _ v; IO.PutRope[log, "terminal viewer created\n"]; IO.Flush[log]; lastTimeMsg _ BasicTime.earliestGMT; --so the time will be printed }; 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 { BEGIN IO.Flush[oldOut ! RuntimeError.UNCAUGHT => GOTO oops]; IO.Reset[oldOut ! RuntimeError.UNCAUGHT => GOTO oops]; IO.Reset[oldIn ! RuntimeError.UNCAUGHT => GOTO oops]; 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 terminal viewer\n"]; IO.Flush[log]; RETURN; }; EXITS oops => {} END; }; }; ViewerOps.EnumerateViewers[FindOldGuys ! RuntimeError.UNCAUGHT => CONTINUE]; ReallyCreate[] }; InternalPut: 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; }; PutRope: PUBLIC PROC [text: ROPE] = { ENABLE UNWIND => Leave[]; Enter[]; InternalPut[text]; Leave[] }; PutRopes: PUBLIC PROC [t1, t2, t3: ROPE] = { ENABLE UNWIND => Leave[]; Enter[]; InternalPut[t1]; InternalPut[t2]; InternalPut[t3]; Leave[] }; PutF: PUBLIC PROC [format: ROPE _ NIL, v1, v2, v3: IO.Value_[null[]]] = { ENABLE UNWIND => Leave[]; Enter[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; IO.PutF[out, format, v1, v2, v3 ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.PutF[out, format, v1, v2, v3]; CONTINUE } } ]; IO.PutF[log, format, v1, v2, v3]; shouldFlush _ TRUE; Leave[] }; PutFL: PUBLIC PROC [format: ROPE _ NIL, list: LIST OF IO.Value] = { ENABLE UNWIND => Leave[]; Enter[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; IO.PutFL[out, format, list ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.PutFL[out, format, list]; CONTINUE } } ]; IO.PutFL[log, format, list]; shouldFlush _ TRUE; Leave[] }; PutF1: PUBLIC PROC [format: ROPE _ NIL, value: IO.Value] = { ENABLE UNWIND => Leave[]; Enter[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; IO.PutF1[out, format, value ! IO.Error => { IF ec=StreamClosed THEN { InternalCreate[TRUE]; IO.PutF1[out, format, value]; CONTINUE } } ]; IO.PutF1[log, format, value]; shouldFlush _ TRUE; Leave[] }; RequestRope: PUBLIC PROC [prompt: ROPE _ NIL, timeOut: NAT] RETURNS [ROPE] = { RETURN [IF timeOut=0 THEN RequestRope0[prompt] ELSE RequestRope1[prompt, timeOut]] }; 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}; IO.Rubout => {state_abort; IO.Reset[in]; CONTINUE}; RuntimeError.UNCAUGHT => {state_repeat; CONTINUE}; }; IF viewer.iconic THEN ViewerOps.OpenIcon[viewer]; InternalPut[promp]; IO.Flush[out ! RuntimeError.UNCAUGHT => CONTINUE]; ViewerTools.SetSelection[viewer]; line _ IO.GetLineRope[in]; IF Rope.Find[line, del]>=0 THEN state_abort; }; RequestRope0: PROC [prompt: ROPE _ NIL] RETURNS [r: ROPE] = { state: IOState _ ok; DoIt: PROC [] = {[r, state] _ InternalGetLine[prompt]}; DO ReaderProtected[DoIt]; SELECT state FROM ok => RETURN; repeat => PutRope["XXX\n"]; abort => {PutRope[" ..user abort\n"]; SIGNAL UserAbort} ; ENDCASE => {PutRope[" ..??\n"]; SIGNAL UserAbort}; ENDLOOP; }; xProcess: PROCESS; xStateCondition: CONDITION; xState: IOState _ undef; xResult: ROPE; RequestRope1: PUBLIC PROC [prompt: ROPE, timeOut: NAT] RETURNS [result: ROPE] = { state: IOState _ ok; 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; IF state=ok THEN IF Rope.Find[result, del]>=0 THEN state _ abort }; DO ReaderProtected[DoIt]; SELECT state FROM ok => RETURN; repeat => PutRope["XXX\n"]; abort => {PutRope[" ..user abort\n"]; SIGNAL UserAbort} ; undef => SIGNAL TimedOut ENDCASE => {PutRope[" ..??\n"]; SIGNAL UserAbort}; 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}; IO.Rubout => {d_abort; IO.Reset[in]; CONTINUE}; }; InternalPut[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 InternalPut["XXX\n"] ENDLOOP; }; ReallyTryToGetRope[prompt ! ABORTED => GOTO NeverMind] EXITS NeverMind => NULL; }; TIS: PUBLIC PROC [] RETURNS [stream: IO.STREAM] = { RETURN[IO.CreateStream[streamProcs: myinputStreamProcs, streamData: NIL]]; }; myinputStreamProcs: REF IO.StreamProcs _ IO.CreateStreamProcs[ variety: $input, class: $TerminalIO, getChar: MyGetChar, endOf: MyEndOf, charsAvail: MyCharsAvail, close: MyClose ]; MyGetChar: PROC [self: IO.STREAM] RETURNS [char: CHAR] = { ENABLE UNWIND => Leave[]; Enter[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; IF viewer.iconic THEN ViewerOps.OpenIcon[viewer]; IO.Flush[out ! RuntimeError.UNCAUGHT => CONTINUE]; ViewerTools.SetSelection[viewer]; char _ IO.GetChar[in]; Leave[]; }; MyEndOf: PROC [self: IO.STREAM] RETURNS [endOf: BOOL _ TRUE] = { endOf _ FALSE; }; MyCharsAvail: PROC [self: IO.STREAM, wait: BOOL _ FALSE] RETURNS [INT] = { RETURN[INT.LAST]; }; TOS: PUBLIC PROC [] RETURNS [stream: IO.STREAM] = { RETURN[IO.CreateStream[streamProcs: myStreamProcs, streamData: NIL]]; }; myStreamProcs: REF IO.StreamProcs _ IO.CreateStreamProcs[ variety: $output, class: $TerminalIO, 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[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; 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[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; 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[]; IF viewer=NIL OR viewer.destroyed THEN InternalCreate[FALSE]; 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] = { IO.EraseChar[out, char]; IO.EraseChar[log, char]; }; MyClose: PROC [self: IO.STREAM, abort: BOOL] = { IO.Flush[log]; }; RequestInt: PUBLIC PROC [prompt: Rope.ROPE _ NIL, timeOut: NAT _ 0] RETURNS [i: INT] = { DO ok: BOOL _ TRUE; r: Rope.ROPE _ RequestRope[prompt, timeOut]; i _ Convert.IntFromRope[r ! RuntimeError.UNCAUGHT => {ok_FALSE; CONTINUE}]; IF ok THEN RETURN; PutRope[" ?? (integer), please repeat: \n"]; ENDLOOP; }; Confirm: PUBLIC PROC [text: Rope.ROPE, help: Rope.ROPE, timeOut: NAT, dontLog: BOOL] RETURNS [yes: BOOL] = { Ask: PROC RETURNS [BOOL] = { RETURN [ PopUpSelection.Request[ header: "Confirm", headerDoc: "Confirm by hitting entry; discard by hitting outside", choice: LIST[text], choiceDoc: LIST[help], timeOut: timeOut ]=1 ]; }; AskLogged: PROC = { InternalPut["confirm: "]; InternalPut[text]; InternalPut[": "]; yes _ Ask[]; InternalPut[(IF yes THEN "yes\n" ELSE "no\n")]; }; IF dontLog THEN yes _ Ask[] ELSE ReaderProtected[AskLogged] }; RequestSelection: PUBLIC PROC [ header: Rope.ROPE _ NIL, choice: LIST OF Rope.ROPE, headerDoc: Rope.ROPE _ NIL, choiceDoc: LIST OF Rope.ROPE _ NIL, default: NAT _ 0, timeOut: NAT _ 0, position: REF _ NIL, dontLog:BOOL _ FALSE ] RETURNS [n: INT _ 0] = { Request: PROC = { IF header#NIL AND ~dontLog THEN { InternalPut["pop up selection: "]; InternalPut[header]; InternalPut["\n "]; }; n _ PopUpSelection.Request[header: header, headerDoc: headerDoc, choice: choice, choiceDoc: choiceDoc, timeOut: timeOut, default: default, position: position]; IF ~dontLog THEN { IF n<0 THEN InternalPut["timed out\n"] ELSE IF n=0 THEN InternalPut["skipped\n"] ELSE {c: LIST OF Rope.ROPE _ choice; WHILE n>1 DO c _ c.rest; IF c=NIL THEN RETURN; ENDLOOP; InternalPut["select: "]; InternalPut[c.first]; InternalPut["\n"]; }; }; }; IF (choice=NIL) OR (choice.first=NIL) THEN ERROR; ReaderProtected[Request] }; LogFileName: PROC [] RETURNS [Rope.ROPE] = { RETURN [PFS.RopeFromPath[PFS.GetInfo[PFS.OpenFileFromStream[log]].fullFName]] }; CreateLog: PROC [] = { log _ PFS.StreamOpen[fileName: PFS.PathFromRope["-ux:/tmp/TerminalIO.log"], accessOptions: append, createOptions: [keep: 5, mutability: mutable]]; IO.SetIndex[log, 0]; IO.SetLength[log, 0]; IO.PutF1[log, "log file for terminal created %g\n", IO.time[]] }; PreviousLog: Menus.ClickProc = { fullName: Rope.ROPE _ LogFileName[]; filePath: PFSNames.PATH _ PFS.PathFromRope[fullName]; name: Rope.ROPE _ PFS.RopeFromPath[PFSNames.StripVersionNumber[filePath]]; versionRope: Rope.ROPE _ PFSCanonicalNames.UnparseVersion[PFSCanonicalNames.ParseVersion[fullName].version]; version: INT _ Convert.IntFromRope[versionRope ! Convert.Error => GOTO oops]; IF version<2 THEN GOTO oops; name _ IO.PutFR["%g!%g", [rope[name]], [integer[version-1]]]; [] _ TiogaMenuOps.Open[fileName: name]; PutRope["previous log file opened\n"]; EXITS oops => PutRope["opening previous log file failed\n"]; }; Command: Commander.CommandProc = { ENABLE Convert.Error => GOTO convertError; IF Rope.Match["*open*", cmd.commandLine, FALSE] THEN { PutRope["open terminal viewer\n"]; --side effect: creates viewer ViewerOps.OpenIcon[viewer]; }; IF Rope.Match["*new*", cmd.commandLine, FALSE] OR Rope.Match["*close*", cmd.commandLine, FALSE] THEN { oldLog: IO.STREAM _ log; oldName: Rope.ROPE _ LogFileName[]; PutF1["start new terminal log file [%g]\n", IO.time[]]; CreateLog[]; PutF1["previous log file ""%g"" closed\n", IO.rope[oldName]]; IO.Close[oldLog]; IO.PutF1[cmd.out, "previous log file ""%g"" closed\n", IO.rope[oldName]]; }; IF Rope.Match["*time*", cmd.commandLine, FALSE] THEN { time: INT; timePos: INT _ Rope.SkipTo[ s: cmd.commandLine, pos: Rope.Index[s1: cmd.commandLine, s2: "time", case: FALSE], skip: " " ]; time _ Convert.IntFromRope[Rope.Substr[base: cmd.commandLine, start: timePos]]; timeMsgInterval _ IF time<0 OR time>600 THEN 0 ELSE time*60; lastTimeMsg _ BasicTime.earliestGMT; }; EXITS convertError => IO.PutRope[cmd.err, "syntax error in time\n"]; }; CheckInhibitDestroy: ENTRY PROC [] = { v: ViewerClasses.Viewer _ viewer; IF v#NIL AND ~occupied THEN v.inhibitDestroy _ FALSE; }; lastTimeMsg: BasicTime.GMT _ BasicTime.earliestGMT; timeMsgInterval: INT _ 60*UserProfile.Number["TerminalIO.TimeMsg", 30]; Flusher: PROC [] = { flush: BOOL; DO Process.PauseMsec[32000]; --a little bit more than 30 seconds [so doesn't skip time msg] IF flush _ shouldFlush THEN { shouldFlush _ FALSE; IO.Flush[log ! RuntimeError.UNCAUGHT => CONTINUE]; }; CheckInhibitDestroy[]; IF viewer#NIL AND ~occupied AND ~viewer.destroyed AND timeMsgInterval>0 AND (BasicTime.Period[from: lastTimeMsg, to: BasicTime.Now[]]>timeMsgInterval) THEN { lastTimeMsg _ BasicTime.Now[]; PutF["%l%g%l\n", [rope["i"]], [time[lastTimeMsg]], [rope[" "]]]; }; ENDLOOP }; CreateLog[]; normalIcon _ Icons.NewIconFromFile["TerminalIO.icons", 0 ! RuntimeError.UNCAUGHT => CONTINUE]; inputIcon _ Icons.NewIconFromFile["TerminalIO.icons", 1 ! RuntimeError.UNCAUGHT => CONTINUE]; Commander.Register[Rope.Cat["///", SystemNames.ReleaseName[], "/Commands/TerminalIO"], Command, "usage {TerminalIO ~ | open | new}"]; TRUSTED {Process.Detach[FORK Flusher]}; END. ’TerminalIOImpl.mesa Copyright c 1983, 1986, 1987, 1988, 1992 by Xerox Corporation. All rights reserved. Created by Christian Jacobi August 3, 1983 9:54 am Last edited by: Christian Jacobi, March 27, 1992 3:15 pm PST Sampath Rangarajan June 16, 1989 4:03:08 pm PDT Bob Krivacic February 18, 1991 2:03 pm PST Booting USING [CheckpointProc, RegisterProcs, RollbackProc], --using a backing file for viewer makes log unaccessible --whether log file should be flushed -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --locking procedures -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --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 [orphaned viewers 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 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --short cuts --asking for viewer.destroyed helps to discover bad splits, but is not mandatory --actual usage of IO.PutF inside lock allows clients to use looks --actual usage of IO.PutFL inside lock allows clients to use looks --actual usage of IO.PutF1 inside lock allows clients to use looks -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --input procedures -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --stream input stuff -- This is unprotected code, so you should be sure that no one else will be doing terminalIO while inputing from the stream -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --stream output stuff -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --input conveniances -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --log and commands --directly flushing after each output would make TerminalIO far to slow --process also used to write periodic time mesages --flushing --time message Checkpoint: Booting.CheckpointProc = { stream: IO.STREAM _ log; log _ IO.noWhereStream; IF stream#NIL AND stream#IO.noWhereStream THEN IO.Close[stream]; }; Rollback: Booting.RollbackProc = { CreateLog[]; IO.PutRope[log, "Rollback!\n"] }; Booting.RegisterProcs[c: Checkpoint, r: Rollback]; ΚBσ•NewlineDelimiter – "cedar" style˜code– "cedar" stylešœ™K– "cedar" stylešœ ΟmœI™TK– "cedar" stylešœ2™2K– "cedar" style™˜>K– "cedar" stylešœ˜—K– "cedar" style˜—– "cedar" styleš žœžœžœžœž˜B– "cedar" stylešžœ žœž˜GK– "cedar" stylešœžœžœ˜2——K– "cedar" stylešœ˜—– "cedar" stylešŸ œžœ˜K– "cedar" stylešœhžœ$˜‘– "cedar" stylešœ0˜0K– "cedar" stylešœ˜K– "cedar" stylešœžœ˜ K– "cedar" stylešœ˜K– "cedar" style˜—K– "cedar" stylešœ]žœ˜cK– "cedar" stylešœ ˜ K– "cedar" stylešžœ+˜-K– "cedar" stylešžœ ˜K– "cedar" stylešœ% ˜BK– "cedar" stylešœ˜—K– "cedar" style™5K– "cedar" stylešœžœžœ˜K– "cedar" stylešœžœžœ˜– "cedar" styleš žœžœžœžœžœ žœ˜2K– "cedar" stylešœ˜K– "cedar" stylešœžœžœ˜K– "cedar" stylešœ)˜)– "cedar" styleš žœžœžœžœ)žœ˜MK– "cedar" stylešœ*™*K– "cedar" stylešœ/™/– "cedar" stylešž˜K– "cedar" stylešžœžœžœ˜6K– "cedar" stylešžœžœžœ˜6K– "cedar" stylešžœžœžœ˜5K– "cedar" stylešœ˜– "cedar" stylešžœžœ˜K– "cedar" stylešœ]žœ˜cK– "cedar" stylešœ ˜ K– "cedar" stylešžœ7žœ ˜GK– "cedar" stylešžœ˜K– "cedar" style˜—K– "cedar" stylešžœ ˜K– "cedar" stylešžœ˜—K– "cedar" stylešœ˜—K– "cedar" style˜—K– "cedar" style™*K– "cedar" stylešœ6žœžœ˜LK– "cedar" style™K– "cedar" stylešœ˜K– "cedar" stylešœ˜—K– "cedar" style˜K– "cedar" stylešœ8™8K– "cedar" stylešœ ™ K– "cedar" style˜– "cedar" stylešŸ œžœžœ˜"– "cedar" styleš žœžœžœžœžœ˜=K– "cedar" stylešœQ™Q—– "cedar" stylešžœžœ ˜$– "cedar" stylešžœžœ˜K– "cedar" stylešœžœ˜K– "cedar" stylešžœ˜K– "cedar" stylešž˜K– "cedar" style˜—K– "cedar" stylešœ˜—K– "cedar" stylešžœ#žœ˜*K– "cedar" stylešœ˜—K– "cedar" style˜– "cedar" stylešŸœž œžœ˜%K– "cedar" stylešžœžœ ˜K– "cedar" stylešœ#˜#K– "cedar" stylešœ˜—K– "cedar" style˜– "cedar" stylešŸœž œžœ˜,K– "cedar" stylešžœžœ ˜K– "cedar" stylešœC˜CK– "cedar" stylešœ˜—K– "cedar" style˜– "cedar" styleš Ÿœžœžœ žœžœžœ˜IK– "cedar" styleš  œ žœ (™AK– "cedar" stylešžœžœ ˜K– "cedar" stylešœ˜K– "cedar" styleš žœžœžœžœžœ˜=– "cedar" stylešžœ˜ – "cedar" stylešœžœ ˜– "cedar" stylešžœžœ˜K– "cedar" stylešœžœ˜K– "cedar" stylešžœ˜!K– "cedar" stylešž˜K– "cedar" style˜—K– "cedar" style˜—K– "cedar" stylešœ˜—K– "cedar" stylešžœ.žœ˜5K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" stylešŸœžœžœ žœžœžœžœžœœ˜CK– "cedar" styleš  œ žœ (™BK– "cedar" stylešžœžœ ˜K– "cedar" stylešœ˜K– "cedar" styleš žœžœžœžœžœ˜=– "cedar" stylešžœ˜– "cedar" stylešœžœ ˜– "cedar" stylešžœžœ˜K– "cedar" stylešœžœ˜K– "cedar" stylešžœ˜K– "cedar" stylešž˜K– "cedar" style˜—K– "cedar" style˜—K– "cedar" stylešœ˜—K– "cedar" stylešžœ)žœ˜0K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" styleš Ÿœžœžœ žœžœ žœ ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜—K– "cedar" style˜– "cedar" styleš Ÿ œžœžœžœžœžœ˜:K– "cedar" stylešžœžœ ˜K– "cedar" stylešœ˜K– "cedar" styleš žœžœžœžœžœ˜=K– "cedar" stylešžœžœ˜1K– "cedar" stylešžœžœžœ˜2K– "cedar" stylešœ!˜!K– "cedar" stylešœžœ ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" stylešŸœžœžœžœžœ žœžœ˜@K– "cedar" stylešœžœ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" stylešŸ œžœžœžœžœžœžœžœ˜JK– "cedar" stylešžœžœ˜K– "cedar" stylešœ˜K– "cedar" style˜—K– "cedar" style˜K– "cedar" stylešœ;™;K– "cedar" stylešœ;™;K– "cedar" stylešœ™K– "cedar" style˜– "cedar" styleš Ÿœžœžœžœ žœžœ˜3K– "cedar" stylešžœžœ6žœ˜EK– "cedar" stylešœ˜—– "cedar" stylešœžœžœžœ˜9K– "cedar" style˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ!˜!K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" stylešœ˜—K– "cedar" style˜– "cedar" styleš Ÿ œžœžœžœžœ˜1K– "cedar" stylešžœžœ ˜K– "cedar" stylešœ˜K– "cedar" styleš žœžœžœžœžœ˜=– "cedar" stylešžœžœ ˜$– "cedar" stylešžœžœ˜K– "cedar" stylešœžœ˜K– "cedar" stylešžœ˜K– "cedar" stylešž˜K– "cedar" style˜—K– "cedar" stylešœ˜—K– "cedar" stylešžœ#žœ˜*K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" stylešŸ œžœžœžœ žœžœžœžœ žœ˜]K– "cedar" stylešžœžœ ˜K– "cedar" stylešœ˜K– "cedar" styleš žœžœžœžœžœ˜=– "cedar" stylešžœ*žœ ˜9– "cedar" stylešžœžœ˜K– "cedar" stylešœžœ˜K– "cedar" stylešžœ)˜+K– "cedar" stylešž˜K– "cedar" style˜—K– "cedar" stylešœ˜—K– "cedar" stylešžœ8žœ˜?K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" styleš Ÿœžœžœžœ žœ˜CK– "cedar" stylešžœžœ ˜K– "cedar" stylešœ˜K– "cedar" styleš žœžœžœžœžœ˜=– "cedar" stylešžœžœ ˜,– "cedar" stylešžœžœ˜K– "cedar" stylešœžœ˜K– "cedar" stylešžœ˜K– "cedar" stylešž˜K– "cedar" style˜—K– "cedar" stylešœ˜—K– "cedar" stylešžœ+žœ˜2K– "cedar" stylešœ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" stylešŸœžœžœžœ˜#K– "cedar" stylešžœ ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" styleš Ÿ œžœžœžœžœ˜3K– "cedar" stylešžœ˜K– "cedar" stylešžœ˜K– "cedar" stylešœ˜K– "cedar" style˜—– "cedar" styleš Ÿœžœžœžœ žœ˜0K– "cedar" stylešžœ ˜K– "cedar" stylešœ˜—K– "cedar" style˜K– "cedar" stylešœ;™;K– "cedar" stylešœ™K˜– "cedar" stylešŸ œžœžœžœžœ žœžœžœ˜X– "cedar" stylešž˜K– "cedar" stylešœžœžœ˜K– "cedar" stylešœžœ ˜,K– "cedar" stylešœ)žœžœžœ˜KK– "cedar" stylešžœžœžœ˜K– "cedar" stylešœ-˜-K– "cedar" stylešžœ˜—K– "cedar" stylešœ˜—K˜šŸœžœžœ žœ žœ žœ žœžœžœ˜lšŸœžœžœžœ˜šžœ˜ šœ˜KšœU˜UKšœžœ˜Kšœ žœ˜Kšœ˜Kšœ˜—Kšœ˜—Kšœ˜—šŸ œžœ˜K– "cedar" stylešœ?˜?Kšœ ˜ Kšœ žœžœ žœ ˜/Kšœ˜—Kšžœ žœ žœ˜;Kšœ˜K˜—š,Ÿœžœžœžœžœ žœžœžœžœžœ žœžœžœžœ žœžœžœžœ žœžœžœžœ ˜ώšŸœžœ˜– "cedar" stylešžœžœžœ žœ˜!K– "cedar" stylešœL˜LK– "cedar" stylešœ˜—KšœŸ˜Ÿ– "cedar" stylešžœ žœ˜K– "cedar" stylešžœžœ˜&K– "cedar" stylešžœžœžœ˜)– "cedar" stylešžœžœžœžœ ˜$– "cedar" stylešžœž˜ K– "cedar" styleš œ žœžœžœžœ˜!K– "cedar" stylešžœ˜ —K– "cedar" stylešœA˜AK– "cedar" style˜—K– "cedar" stylešœ˜—Kšœ˜—K– "cedar" styleš žœ žœžœžœžœžœ˜1Kšœ˜K˜—K– "cedar" style™K– "cedar" stylešœ;™;K– "cedar" stylešœ™K– "cedar" style˜– "cedar" stylešŸ œžœžœžœ˜,Jšžœžœžœ žœ%˜MK– "cedar" stylešœ˜—K– "cedar" style˜– "cedar" stylešŸ œžœ˜Kšœžœžœp˜’Kšžœ˜Kšžœ˜K– "cedar" stylešžœ2žœ˜>K– "cedar" stylešœ˜—K– "cedar" style˜– "cedar" stylešŸ œ˜ K– "cedar" stylešœ$˜$K– "cedar" stylešœžœžœ˜6K– "cedar" stylešœ žœžœ6˜KK– "cedar" stylešœžœW˜mK– "cedar" stylešœ žœ6žœ˜NK– "cedar" stylešžœ žœžœ˜K– "cedar" stylešœžœ4˜=Kšœ'˜'K– "cedar" stylešœ&˜&K– "cedar" stylešžœ7˜