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
CedarConsoleImpl:
CEDAR
MONITOR
IMPORTS Commander, CommanderOps, Feedback, IO, Process, PseudoTerminal, RefText, Rope, UnixErrno, UnixSysCalls
EXPORTS CedarConsole =
BEGIN
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.
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
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
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];
-- <sys/ttycom.h> ¬IO(t, 36)
Cmd: Commander.CommandProc ~ {
Usage:"Console on" turns on the diversion of /dev/console to Feedback
"Console off" stops the diversion. /dev/console reverts to the physical console.
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 {
Allocate a name for the pty, open both ends, and turn off CR to CR-LF mapping.
console.pty ¬ PseudoTerminal.Allocate[]; -- opens master end
console.slaveTty ¬ UnixSysCalls.Open[path: CHARPtrFromRefText[Rope.ToRefText[console.pty.slaveName]], flags: [access:
RDWR], mode: []];
TRUSTED {
This ioctl() declares the PseudoTerminal to be the system console.
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;
};
};
Create a router and let its output go to the default place. Currently this is the message window as well as the SystemScript typescript
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 [] ~{
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
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];
Loop through the Rope eliminating embedded CR and NL
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;
Output anything left over
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];
};
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"
Commander.Register[
key: "Console", proc: Cmd, doc: "Start/Stop diverting UNIX errors to Feedback, [on | off]"];