DIRECTORY Ascii, CedarConsole, Commander, CommanderOps, Feedback, IO, Process, PseudoTerminal, RefText, Rope, UnixErrno, UnixSysCalls, UnixTypes; CedarConsoleImpl: CEDAR MONITOR IMPORTS Commander, CommanderOps, Feedback, IO, Process, PseudoTerminal, RefText, Rope, UnixErrno, UnixSysCalls EXPORTS CedarConsole = BEGIN ROPE: TYPE ~ Rope.ROPE; Console: TYPE ~ RECORD [ stopped: BOOL ¬ TRUE, pty: PseudoTerminal.PseudoTerminal ¬ NIL, slaveTty: UnixTypes.FileDescriptor, router: Feedback.MsgRouter, process: PROCESS ]; console: Console; -- Global record containing console state IOParamType: TYPE ~ MACHINE DEPENDENT {(0), void(20H), R(40H), W(80H), WR(40H+80H), (255)}; IORequest: TYPE ~ MACHINE DEPENDENT RECORD [ paramType: IOParamType, paramBytes: BYTE, code: CHAR, loByte: BYTE ¬ 0 ]; TIOCCONS: IORequest = [void, 0, 't, 36]; -- ¬IO(t, 36) Cmd: Commander.CommandProc ~ { arg: Rope.ROPE ~ CommanderOps.NextArgument[cmd]; SELECT TRUE FROM Rope.Equal[arg, "on", FALSE] => Start[]; Rope.Equal[arg, "off", FALSE] => Stop[]; ENDCASE => CommanderOps.Failed[cmd.procData.doc]; }; Start: PUBLIC ENTRY PROC [] RETURNS [] = { res: UnixTypes.SysCallResult; IF console.pty = NIL THEN { console.pty ¬ PseudoTerminal.Allocate[]; -- opens master end console.slaveTty ¬ UnixSysCalls.Open[path: CHARPtrFromRefText[Rope.ToRefText[console.pty.slaveName]], flags: [access: RDWR], mode: []]; TRUSTED { res ¬ UnixSysCalls.IOCtl[d: console.pty.controllerFD, request: LOOPHOLE[TIOCCONS], argp: LOOPHOLE[0]]; IF res # success THEN { err: UnixErrno.Errno = UnixErrno.GetErrno[]; Feedback.PutF[console.router, oneLiner, $Debug, "CedarConsole: Unix Ioctl TIOCCONS error = %g", IO.int[ORD[err]]]; RETURN; }; }; console.router ¬ Feedback.CreateRouter[]; [] ¬ Feedback.RegisterRouter[console.router, $Console]; console.stopped ¬ FALSE; TRUSTED { console.process ¬ FORK CopyConsoleToFeedback[]; Process.Detach[console.process]; }; }; }; Stop: PUBLIC ENTRY PROC [] RETURNS [] = { IF console.pty # NIL THEN { console.stopped ¬ TRUE; Process.Abort[console.process]; PseudoTerminal.Close[console.pty]; [] ¬ UnixSysCalls.Close[console.slaveTty]; console.pty ¬ NIL; }; }; CHARPtrFromRefText: PROCEDURE [refText: REF TEXT] RETURNS [UnixTypes.CHARPtr] ~ TRUSTED { charPtr: UnixTypes.CHARPtr ~ LOOPHOLE[LOOPHOLE[refText, LONG POINTER]+SIZE[TEXT[0]]]; RETURN [charPtr]; }; CopyConsoleToFeedback: PROC [] ~{ buffer: REF TEXT ~ RefText.ObtainScratch[256]; bufPtr: UnixTypes.CHARPtr ~ CHARPtrFromRefText[refText: buffer]; inputRope: Rope.ROPE; ReadLoop: PROC [in: UnixTypes.FileDescriptor] ~ { length: INT; position, start: INT; inTheMiddle: BOOL; -- dangling output? outStyle: Feedback.MsgType; inTheMiddle ¬ FALSE; outStyle ¬ oneLiner; WHILE TRUE DO { TRUSTED { length ¬ UnixSysCalls.Read[d: in, buf: bufPtr, nBytes: buffer.maxLength]; }; IF length <= 0 THEN { IF NOT console.stopped THEN { err: UnixErrno.Errno = UnixErrno.GetErrno[]; Feedback.PutF[console.router, oneLiner, $Debug, "CedarConsole: Unix Read error = %g", IO.int[ORD[err]]]; }; RETURN; }; buffer.length ¬ length; -- this fixes up the RefText so we can treat it like a Rope inputRope ¬ Rope.FromRefText[buffer]; start ¬ 0; position ¬ 0; WHILE position < length DO IF buffer[position] = Ascii.LF OR buffer[position] = Ascii.CR THEN { IF position - start > 0 THEN { Feedback.Append[console.router, outStyle, $Error, Rope.Substr[base: inputRope, start: start, len: position-start]]; }; outStyle ¬ oneLiner; start ¬ position + 1; }; position ¬ position + 1; ENDLOOP; IF position - start > 0 THEN { outStyle ¬ middle ; Feedback.Append[console.router, outStyle, $Error, Rope.Substr[base: inputRope, start: start, len: position-start]]; }; Process.CheckForAbort[]; } ENDLOOP; }; Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole on"]; ReadLoop[in: console.pty.controllerFD ! IO.Error => { Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole IO.error"]; CONTINUE; }; ABORTED => { Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole aborted"]; CONTINUE; }; ]; Feedback.Append[console.router, oneLiner, $Debug, "CedarConsole off"]; RefText.ReleaseScratch[buffer]; }; Commander.Register[ key: "Console", proc: Cmd, doc: "Start/Stop diverting UNIX errors to Feedback, [on | off]"]; END. ¬ CedarConsoleImpl.mesa Copyright Σ 1990, 1991 by Xerox Corporation. All rights reserved. Brent Welch December 4, 1990 12:26 pm PST Michael Plass, October 22, 1991 4:22 pm PDT In order to divert UNIX error messages it is necessary to open a pseudo-terminal and declare it as the console using an ioctl(). Closing the pseudo-terminal reverts the console to its default (i.e. the physical console). The Console record holds the master end of the pty, which is opened by PseudoTerminal.Allocate, the slave end, which we open and must be open for things to work, and a Feedback router to which we dump the error messages. Copying from the pty to Feedback is done in the background by a detached process. Structures for using UNIX ioctl(). The command word cleverly encodes the size of the argument and whether it is read, write, or read-write to the system call Usage:"Console on" turns on the diversion of /dev/console to Feedback "Console off" stops the diversion. /dev/console reverts to the physical console. Allocate a name for the pty, open both ends, and turn off CR to CR-LF mapping. This ioctl() declares the PseudoTerminal to be the system console. Create a router and let its output go to the default place. Currently this is the message window as well as the SystemScript typescript This procedure reads from the pty into a reftext, converts that to a rope, exracts substrings that don't contain CR or LF, and then passes the substrings to Feedback Loop through the Rope eliminating embedded CR and NL Output anything left over Start code registers a Console command, which must be invoked for this program to do anything. The RawViewers.command script makes a call to "Console on" Κζ•NewlineDelimiter –(cedarcode) style™codešœ™Kšœ Οeœ7™BK™)K™+—K˜K™έK˜šΟk ˜ K˜Kšœ ˜ K˜ K˜ K˜ Kšžœ˜K˜Kšœ˜K˜K˜Kšœ ˜ Kšœ ˜ Kšœ ˜ —K˜šΟnœžœž˜Kšžœ$žœA˜nšžœž˜K˜—šžœžœžœ˜K™—KšœΟbœ€™―šœ žœžœ˜Kšœ žœžœ˜Kšœ%žœ˜)K˜#K˜Kšœ ž˜K˜K˜—šœΟc)˜;K˜—KšœŸ™ŸKš œ žœžœž œ"žœ˜[š œ žœžœž œžœ˜,Kšœ˜Kšœ žœ˜Kšœžœ˜ Kšœžœ˜Kšœ˜K˜—šžœ"‘˜GK˜—šŸœ˜Kš‘E™EKšœQ™QKšœ žœ"˜0šžœžœž˜Kšœžœ ˜(Kšœžœ ˜(Kšžœ*˜1—K˜K˜—š Ÿœžœžœžœžœ˜*Kšœ˜šžœžœžœ˜Kšœ:žœžœžœ ™NKšœ)‘˜<šœvžœ ˜‡šžœ˜ KšœB™BKšœ?žœžœ žœ˜fšžœžœ˜Kšœ,˜,Kšœ`žœžœ˜rKšžœ˜Kšœ˜—K˜—K™ˆK˜)K˜7Kšœžœ˜šžœ˜ Kšœžœ˜/K˜ —Kšœ˜—K˜—K˜K˜—š Ÿœžœžœžœžœ˜*šžœžœžœ˜Kšœžœ˜Kšœ˜Kšœ"˜"K˜*Kšœžœ˜K˜—K˜K˜—š Ÿœž œ žœžœžœžœ˜Yšœ˜Kš žœžœ žœžœžœžœ˜8—Kšžœ ˜Kšœ˜K˜—šŸœžœ˜!K™₯Kšœžœžœ˜.Kšœ@˜@Kšœžœ˜šŸœžœ#˜1Kšœžœ˜ Kšœžœ˜Kšœ žœ‘˜'K˜Kšœžœ˜K˜šžœžœžœ˜KšžœO˜Všžœ žœ˜šžœžœžœ˜Kšœ,˜,KšœVžœžœ˜hK˜—Kšžœ˜Kšœ˜—Kšœ‘;˜SK˜%Kšœ4™4K˜ K˜ šžœž˜š žœžœžœžœžœ˜Dšžœžœ˜Kšœs˜sK˜—K˜K˜K˜—K˜Kšžœ˜—K™K™K™šžœž˜K˜Kšœs˜sKšœ˜—Kšœ˜Kšœžœ˜ —Kšœ˜K˜—KšœE˜Ešœ(˜(šžœ ˜ KšœK˜KKšžœ˜ Kšœ˜—šžœ˜ KšœJ˜JKšžœ˜ Kšœ˜—K˜—KšœF˜FKšœ˜K˜K˜K˜K˜—š  œ™šK™—Kšœp˜p—˜Kšžœ˜—K˜—…—ΆH